From c72910d2b90f74d23e770717d80921b4fd064d48 Mon Sep 17 00:00:00 2001 From: 无关风月 <443237572@qq.com> Date: 星期二, 16 九月 2025 16:24:08 +0800 Subject: [PATCH] 新增用户提现 --- ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/AppUserBank.java | 12 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/UserWithdrawServiceImpl.java | 20 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/HttpUtilWithdraw.java | 492 ++++++++++++++++ ruoyi-service/ruoyi-account/pom.xml | 5 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRequester.java | 204 ++++++ ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserPoint.java | 2 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/IUserWithdrawService.java | 17 ruoyi-service/ruoyi-account/src/main/resources/mapper/account/UserWithdrawMapper.xml | 31 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/WithdrawCallBackDTO.java | 36 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java | 88 ++ ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserWithdraw.java | 110 +++ ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/UUIDUtil.java | 103 +++ ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserBankController.java | 19 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/dto/SaveWithdrawalAccount.java | 25 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRespons.java | 184 ++++++ ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/UserWithdrawController.java | 169 +++++ ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/mapper/UserWithdrawMapper.java | 24 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/BankCode.java | 217 +++++++ 18 files changed, 1,751 insertions(+), 7 deletions(-) diff --git a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/AppUserBank.java b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/AppUserBank.java index 663845b..2400952 100644 --- a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/AppUserBank.java +++ b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/AppUserBank.java @@ -40,4 +40,16 @@ @TableField("bank_number") @ApiModelProperty(value = "银行卡号", required = true) private String bankNumber; + /** + * 持卡人名称 + */ + @TableField("user_name") + @ApiModelProperty(value = "持卡人名称", required = true) + private String userName; + @ApiModelProperty("账户类型(对私账户201,对公账户204)") + @TableField("receiverAccountType") + private Integer receiverAccountType; + @ApiModelProperty("收款方开户行编号") + @TableField("receiverBankChannelNo") + private String receiverBankChannelNo; } diff --git a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserPoint.java b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserPoint.java index 6785dc6..754bdac 100644 --- a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserPoint.java +++ b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserPoint.java @@ -39,7 +39,7 @@ private Long id; @ApiModelProperty(value = "变动类型(4=兑换商品,12=他人赠送,13=赠与他人,16=取消订单,17=充值 )") - @Excel(name = "变动类型", readConverterExp = "4=兑换商品,12=他人赠送,13=赠与他人,16=取消订单,17=充值 ") + @Excel(name = "变动类型", readConverterExp = "4=兑换商品,12=他人赠送,13=赠与他人,16=取消订单,17=充值 18=提现 19=提现失败回退积分 ") @TableField("type") private Integer type; diff --git a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserWithdraw.java b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserWithdraw.java new file mode 100644 index 0000000..88fa78f --- /dev/null +++ b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/UserWithdraw.java @@ -0,0 +1,110 @@ +package com.ruoyi.account.api.model; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * <p> + * 用户提现记录 + * </p> + * + * @author lingma + * @since 2025-04-05 + */ +@Data +@TableName("t_user_withdraw") +@ApiModel(value = "UserWithdraw对象", description = "用户提现记录") +public class UserWithdraw implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "主键") + @TableId("id") + private Long id; + + + @ApiModelProperty(value = "uid") + @TableField(exist = false) + + private String uid; + @ApiModelProperty(value = "用户id") + @TableField("app_user_id") + private Long appUserId; + + @ApiModelProperty(value = "提现金额") + @TableField("money") + private BigDecimal money; + + @ApiModelProperty(value = "消耗积分") + @TableField("integral") + private Integer integral; + + @ApiModelProperty(value = "审核状态(0=待审核,1=审核通过,2=审核失败)") + @TableField("audit_status") + private Integer auditStatus; + + @ApiModelProperty(value = "审核人id") + @TableField("audit_user_id") + private Long auditUserId; + + @ApiModelProperty(value = "审核时间") + @TableField("audit_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime auditTime; + + @ApiModelProperty(value = "审核结果") + @TableField("audit_msg") + private String auditMsg; + + @ApiModelProperty(value = "状态(1=申请中,2=已到账)") + @TableField("status") + private Integer status; + + @ApiModelProperty(value = "到账时间") + @TableField("arrival_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime arrivalTime; + + @ApiModelProperty(value = "删除(0=否,1=是)") + @TableField("del_flag") + private Integer delFlag; + + @ApiModelProperty(value = "添加时间") + @TableField("create_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + @TableField(exist = false) + @ApiModelProperty(value = "联系电话") + private String phone; + @TableField(exist = false) + @ApiModelProperty(value = "用户名称") + private String name; + @ApiModelProperty("收款银行卡号") + @TableField("receiverAccountNoEnc") + private String receiverAccountNoEnc; + + @ApiModelProperty("收款银行卡持卡人名称") + @TableField("receiverNameEnc") + private String receiverNameEnc; + + @ApiModelProperty("账户类型(对私账户201,对公账户204)") + @TableField("receiverAccountType") + private Integer receiverAccountType; + @ApiModelProperty(value = "提现单号") + @TableField("code") + private String code; + @ApiModelProperty(value = "提现流水号") + @TableField("order_number") + private String orderNumber; + @ApiModelProperty("收款方开户行编号") + @TableField("receiverBankChannelNo") + private String receiverBankChannelNo; +} diff --git a/ruoyi-service/ruoyi-account/pom.xml b/ruoyi-service/ruoyi-account/pom.xml index 256bf58..9a57a28 100644 --- a/ruoyi-service/ruoyi-account/pom.xml +++ b/ruoyi-service/ruoyi-account/pom.xml @@ -16,6 +16,11 @@ <dependencies> <dependency> + <groupId>commons-httpclient</groupId> + <artifactId>commons-httpclient</artifactId> + <version>3.1</version> + </dependency> + <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-api-order</artifactId> </dependency> diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserBankController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserBankController.java index e242c63..6727744 100644 --- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserBankController.java +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserBankController.java @@ -2,6 +2,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.account.api.model.AppUserBank; +import com.ruoyi.account.dto.SaveWithdrawalAccount; import com.ruoyi.account.service.AppUserBankService; import com.ruoyi.common.core.utils.bean.BeanUtils; import com.ruoyi.common.core.web.domain.AjaxResult; @@ -33,18 +34,24 @@ @ResponseBody @PostMapping("/saveAppUserBank") @ApiOperation(value = "保存银行卡") - public AjaxResult saveAppUserBank(@RequestBody AppUserBank appUserBank){ + public AjaxResult saveAppUserBank(@RequestBody SaveWithdrawalAccount appUserBank){ LoginUser loginUserApplet = tokenService.getLoginUserApplet(); AppUserBank bank = appUserBankService.getOne(new LambdaQueryWrapper<AppUserBank>() .eq(AppUserBank::getAppUserId, loginUserApplet.getUserid())); if (bank == null){ - appUserBank.setAppUserId(loginUserApplet.getUserid()); - appUserBankService.saveOrUpdate(appUserBank); + AppUserBank bank1 = new AppUserBank(); + bank1.setAppUserId(loginUserApplet.getUserid()); + bank1.setBankNumber(appUserBank.getReceiverAccountNoEnc()); + bank1.setUserName(appUserBank.getReceiverNameEnc()); + bank1.setReceiverAccountType(appUserBank.getReceiverAccountType()); + appUserBankService.saveOrUpdate(bank1); }else { - appUserBank.setId(bank.getId()); - appUserBank.setAppUserId(bank.getAppUserId()); - appUserBankService.updateById(appUserBank); + bank.setAppUserId(loginUserApplet.getUserid()); + bank.setBankNumber(appUserBank.getReceiverAccountNoEnc()); + bank.setUserName(appUserBank.getReceiverNameEnc()); + bank.setReceiverAccountType(appUserBank.getReceiverAccountType()); + appUserBankService.updateById(bank); } return AjaxResult.success(); } diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java index 24dab3c..b4ceb83 100644 --- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java @@ -14,11 +14,13 @@ import com.ruoyi.account.mapper.AppUserMapper; import com.ruoyi.account.service.*; import com.ruoyi.account.util.ObsUploadUtil; +import com.ruoyi.account.util.UUIDUtil; import com.ruoyi.account.util.weChat.EnvVersion; import com.ruoyi.account.util.weChat.WeChatUtil; import com.ruoyi.account.vo.*; import com.ruoyi.common.core.constant.CacheConstants; import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.poi.ExcelUtil; import com.ruoyi.common.core.web.controller.BaseController; @@ -29,6 +31,8 @@ import com.ruoyi.order.feignClient.RemoteOrderGoodsClient; import com.ruoyi.order.model.Order; import com.ruoyi.other.api.domain.Shop; +import com.ruoyi.other.api.domain.ShopBalanceStatement; +import com.ruoyi.other.api.domain.ShopWithdraw; import com.ruoyi.other.api.feignClient.ShopClient; import com.ruoyi.system.api.domain.SysConfig; import com.ruoyi.system.api.domain.SysUser; @@ -53,8 +57,10 @@ import java.io.File; import java.io.IOException; import java.math.BigDecimal; +import java.math.RoundingMode; import java.net.URLDecoder; import java.net.URLEncoder; +import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -81,6 +87,12 @@ @Resource private AppUserService appUserService; @Resource + private IUserWithdrawService userWithdrawService; + @Resource + private UserPointService userPointService; + @Resource + private AppUserBankService appUserBankService; + @Resource private AppUserMapper appUserMapper; @Resource private ShopClient shopClient; @@ -106,6 +118,82 @@ @Value("${file.upload.location}") private String filePath; + @PostMapping("/verifyBankInfo") + @ApiOperation(value = "用户提现前校验银行卡信息") + public R<Boolean> mobileLogin() { + LoginUser loginUserApplet = tokenService.getLoginUserApplet(); + AppUser appUser = appUserService.getById(loginUserApplet.getUserid()); + AppUserBank bank = appUserBankService.lambdaQuery().eq(AppUserBank::getAppUserId, appUser.getId()).last("limit 1") + .one(); + if (bank == null){ + return R.ok(false); + }else{ + return R.ok(true); + } + } + @ApiOperation(value = "提现申请") + @GetMapping("/withdrawalApplication") + @ResponseBody + public R withdrawalApplication(@ApiParam("提现金额") @RequestParam BigDecimal money) { + LoginUser loginUser = tokenService.getLoginUserApplet(); + SysConfig data = sysConfigClient.getInfo(8L).getData(); + AppUser appUser = appUserService.getById(loginUser.getUserid()); + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5); + money=money.setScale(2, BigDecimal.ROUND_HALF_DOWN); + if (money.compareTo(BigDecimal.ZERO)==0){ + throw new ServiceException("提现金额必须大于零"); + } + Integer rate = Integer.valueOf(data.getConfigValue()); + BigDecimal availablePoint = new BigDecimal(appUser.getAvailablePoint()); + BigDecimal unMoney = availablePoint.divide(BigDecimal.valueOf(rate), 2, RoundingMode.HALF_DOWN); + if (unMoney.compareTo( money)<0){ + throw new ServiceException("提现金额不能大于可提现金额,当前可提现金额为:"+unMoney); + } + AppUserBank bank = appUserBankService.lambdaQuery().eq(AppUserBank::getAppUserId, appUser.getId()).last("limit 1") + .one(); + if(bank==null){ + throw new ServiceException("请完善账户信息后再申请提现!"); + } + if(!org.springframework.util.StringUtils.hasLength(bank.getBankNumber())){ + throw new ServiceException("请完善银行卡后再申请提现!"); + } + // 增加用户提现积分变动记录 + UserPoint one = userPointService.lambdaQuery().eq(UserPoint::getAppUserId, appUser.getId()) + .orderByDesc(UserPoint::getCreateTime).last("limit 1").one(); + UserWithdraw userWithdraw = new UserWithdraw(); + userWithdraw.setAppUserId(appUser.getId()); + userWithdraw.setMoney(money); + BigDecimal multiply = money.multiply(BigDecimal.valueOf(rate)); + appUser.setAvailablePoint(appUser.getAvailablePoint()-multiply.setScale(0, RoundingMode.HALF_UP).intValue()); + if (multiply.compareTo(new BigDecimal(appUser.getAvailablePoint()))>0){ + throw new ServiceException("积分不足!"); + } + userWithdraw.setIntegral(Integer.valueOf(multiply.setScale(0, RoundingMode.HALF_UP).toString())); + userWithdraw.setAuditStatus(0); + userWithdraw.setStatus(1); + userWithdraw.setCode(code); + userWithdraw.setCreateTime(LocalDateTime.now()); + if (one.getBalance() - Integer.valueOf(multiply.setScale(0, RoundingMode.HALF_UP).toString())<0){ + throw new ServiceException("积分不足!"); + } + userWithdraw.setReceiverAccountNoEnc(bank.getBankNumber()); + userWithdraw.setReceiverNameEnc(bank.getUserName()); + userWithdraw.setReceiverBankChannelNo(bank.getReceiverBankChannelNo()); + userWithdraw.setReceiverAccountType(201); + userWithdrawService.save(userWithdraw); + UserPoint userPoint = new UserPoint(); + userPoint.setType(18); + userPoint.setVariablePoint(Integer.valueOf(multiply.setScale(0, RoundingMode.HALF_UP).toString())); + userPoint.setHistoricalPoint(one.getBalance()); + userPoint.setBalance(one.getBalance() - Integer.valueOf(multiply.setScale(0, RoundingMode.HALF_UP).toString())); + userPoint.setCreateTime(LocalDateTime.now()); + userPoint.setAppUserId(appUser.getId()); + userPoint.setObjectId(userWithdraw.getId()); + userPointService.save(userPoint); + appUserService.updateById(appUser); + return R.ok(); + } @ResponseBody @PostMapping("/mobileLogin") diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/UserWithdrawController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/UserWithdrawController.java new file mode 100644 index 0000000..5b9499f --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/UserWithdrawController.java @@ -0,0 +1,169 @@ +package com.ruoyi.account.controller; + + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.account.api.model.AppUser; +import com.ruoyi.account.api.model.UserPoint; +import com.ruoyi.account.api.model.UserWithdraw; +import com.ruoyi.account.mapper.UserPointMapper; +import com.ruoyi.account.mapper.UserWithdrawMapper; +import com.ruoyi.account.service.AppUserService; +import com.ruoyi.account.service.IUserWithdrawService; +import com.ruoyi.account.service.UserPointService; +import com.ruoyi.account.util.BankCode; +import com.ruoyi.account.util.HttpRequester; +import com.ruoyi.account.util.HttpRespons; +import com.ruoyi.account.util.withdraw.HttpUtilWithdraw; +import com.ruoyi.account.vo.*; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.poi.ExcelUtil; +import com.ruoyi.common.core.web.controller.BaseController; +import com.ruoyi.common.core.web.page.PageInfo; +import com.ruoyi.common.security.service.TokenService; +import com.ruoyi.common.security.utils.SecurityUtils; +import com.ruoyi.order.feignClient.OrderClient; +import com.ruoyi.order.model.Order; +import com.ruoyi.other.api.domain.Shop; +import com.ruoyi.other.api.domain.ShopBalanceStatement; +import com.ruoyi.other.api.domain.ShopWithdraw; +import com.ruoyi.system.api.model.LoginUser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +/** + * <p> + * 前端控制器 + * </p> + * + * @author luodangjia + * @since 2024-11-21 + */ +@RestController +@RequestMapping("/user-point") +@Api(tags = "用户提现列表") +public class UserWithdrawController extends BaseController { + @Resource + private IUserWithdrawService userWithdrawService; + @Resource + private UserPointService userPointService; + @Resource + private UserWithdrawMapper userWithdrawMapper; + @Resource + private AppUserService appUserService; + @Resource + private TokenService tokenService; + @GetMapping("/shop/list") + @ApiOperation(value = "用户提现申请列表") + public R<IPage<UserWithdraw>> shoplist(@ApiParam("页码") @RequestParam Integer pageNum, + @ApiParam("每一页数据大小") Integer pageSize, + @ApiParam("用户手机号") String phone, + @ApiParam("审核状态(0=待审核,1=审核通过,2=审核失败)")Integer auditStatus) { + //模糊查询手机号 + List<Long> appUserIds=new ArrayList<>(); + if (StringUtils.isNotEmpty(phone)) { + QueryWrapper<AppUser> queryWrapper=new QueryWrapper<>(); + queryWrapper.like(StringUtils.isNotEmpty(phone),"phone", phone); + appUserIds=appUserService.list(queryWrapper).stream().map(AppUser::getId).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(appUserIds)) { + return R.ok(new PageInfo<>()); + } + } + PageInfo<UserWithdraw> page=new PageInfo<>(pageNum,pageSize); + List<UserWithdraw> list =userWithdrawMapper.page(page,appUserIds,auditStatus); + for (UserWithdraw userWithdraw : list) { + userWithdraw.setUid(userWithdraw.getId().toString()); + } + page.setRecords(list); + return R.ok(page); + } + /** + * 审核 + */ + @GetMapping("/audit") + @ApiOperation("提现审核") + public R<Void> audit(@ApiParam("审核状态 1=通过 2=失败") @RequestParam Integer auditstatus, + @ApiParam("提现id") Long id, + @ApiParam("审核失败备注") String auditMsg + ) throws IOException { + LoginUser loginUser = tokenService.getLoginUser(); + + UserWithdraw userWithdraw = userWithdrawService.getById(id); + if(0 != userWithdraw.getAuditStatus()){ + return R.fail("不能重复审核"); + } + AppUser appUser = appUserService.getById(userWithdraw.getAppUserId()); + if(1 == auditstatus){ + // 打款 + String url = "https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&" + + "cardNo="+userWithdraw.getReceiverAccountNoEnc()+"&cardBinCheck=true"; + HashMap<String, String> hashMap = new HashMap<>(); + HttpRequester hr = new HttpRequester(); + HttpRespons HP = hr.sendPost(url, hashMap); + System.out.println("接收返回参数:" + HP.getContent()); + com.alibaba.fastjson.JSONObject resPay = com.alibaba.fastjson.JSONObject.parseObject(HP.getContent()); + System.err.println(resPay); + if (resPay.getString("validated")==null){ + System.err.println("不合法的银行卡号"); + } + String bankCode = resPay.getString("bank"); + String bankName = BankCode.getBankNameByCode(bankCode); // 返回ABC枚举实例 + System.err.println(bankName); + String withdraw = HttpUtilWithdraw.userWithdraw(userWithdraw, bankName); + if (!withdraw.equals("success")){ + return R.fail("打款失败,原因:"+withdraw); + } + + userWithdraw.setStatus(2); + userWithdraw.setArrivalTime(LocalDateTime.now()); + userWithdrawService.updateById(userWithdraw); + } + if(2 == auditstatus){ + //审核不通过 + //回退积分和添加变动明细 + UserPoint userPoint = new UserPoint(); + userPoint.setType(19); + UserPoint one = userPointService.lambdaQuery().eq(UserPoint::getAppUserId, appUser.getId()) + .orderByDesc(UserPoint::getCreateTime).last("limit 1").one(); + userPoint.setVariablePoint(userWithdraw.getIntegral()); + userPoint.setHistoricalPoint(one.getBalance()); + userPoint.setBalance(one.getBalance() + userWithdraw.getIntegral()); + userPoint.setCreateTime(LocalDateTime.now()); + userPoint.setAppUserId(appUser.getId()); + userPoint.setObjectId(userWithdraw.getId()); + userPointService.save(userPoint); + // 回退积分 + appUser.setAvailablePoint(appUser.getAvailablePoint()+userWithdraw.getIntegral()); + appUserService.updateById(appUser); + } + userWithdraw.setAuditStatus(auditstatus); + userWithdraw.setAuditUserId(loginUser.getUserid()); + userWithdraw.setAuditTime(LocalDateTime.now()); + userWithdraw.setAuditMsg(auditMsg); + userWithdrawService.updateById(userWithdraw); + return R.ok(); + } +} + diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/dto/SaveWithdrawalAccount.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/dto/SaveWithdrawalAccount.java new file mode 100644 index 0000000..a42c1ef --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/dto/SaveWithdrawalAccount.java @@ -0,0 +1,25 @@ +package com.ruoyi.account.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2025/1/1 11:54 + */ +@Data +public class SaveWithdrawalAccount { + + + @ApiModelProperty("收款银行卡号") + private String receiverAccountNoEnc; + + @ApiModelProperty("收款银行卡持卡人名称") + private String receiverNameEnc; + + @ApiModelProperty("账户类型(对私账户201,对公账户204)") + private Integer receiverAccountType; + + @ApiModelProperty("收款账户联行号") + private String receiverBankChannelNo; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/mapper/UserWithdrawMapper.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/mapper/UserWithdrawMapper.java new file mode 100644 index 0000000..561fc14 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/mapper/UserWithdrawMapper.java @@ -0,0 +1,24 @@ +package com.ruoyi.account.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.account.api.model.UserWithdraw; +import com.ruoyi.common.core.web.page.PageInfo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * <p> + * 用户提现记录 Mapper 接口 + * </p> + * + * @author lingma + * @since 2025-04-05 + */ +@Mapper +public interface UserWithdrawMapper extends BaseMapper<UserWithdraw> { + + List<UserWithdraw> page(PageInfo<UserWithdraw> page, @Param("appUserIds")List<Long> appUserIds, @Param("auditStatus")Integer auditStatus); + +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/IUserWithdrawService.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/IUserWithdrawService.java new file mode 100644 index 0000000..3c34d57 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/IUserWithdrawService.java @@ -0,0 +1,17 @@ +package com.ruoyi.account.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.account.api.model.UserWithdraw; + +/** + * <p> + * 用户提现记录 服务类 + * </p> + * + * @author lingma + * @since 2025-04-05 + */ +public interface IUserWithdrawService extends IService<UserWithdraw> { + +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/UserWithdrawServiceImpl.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/UserWithdrawServiceImpl.java new file mode 100644 index 0000000..b673808 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/UserWithdrawServiceImpl.java @@ -0,0 +1,20 @@ +package com.ruoyi.account.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.account.api.model.UserWithdraw; +import com.ruoyi.account.mapper.UserWithdrawMapper; +import com.ruoyi.account.service.IUserWithdrawService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 用户提现记录 服务实现类 + * </p> + * + * @author lingma + * @since 2025-04-05 + */ +@Service +public class UserWithdrawServiceImpl extends ServiceImpl<UserWithdrawMapper, UserWithdraw> implements IUserWithdrawService { + +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/BankCode.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/BankCode.java new file mode 100644 index 0000000..d2279d9 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/BankCode.java @@ -0,0 +1,217 @@ +package com.ruoyi.account.util; + +/** + * 银行代码枚举类 + * 用于通过银行简称获取对应的银行名称 + */ +public enum BankCode { + SRCB("SRCB", "深圳农村商业银行"), + BGB("BGB", "广西北部湾银行"), + SHRCB("SHRCB", "上海农村商业银行"), + BJBANK("BJBANK", "北京银行"), + WHCCB("WHCCB", "威海市商业银行"), + BOZK("BOZK", "周口银行"), + KORLABANK("KORLABANK", "库尔勒市商业银行"), + SPABANK("SPABANK", "平安银行"), + SDEB("SDEB", "顺德农商银行"), + HURCB("HURCB", "湖北省农村信用社"), + WRCB("WRCB", "无锡农村商业银行"), + BOCY("BOCY", "朝阳银行"), + CZBANK("CZBANK", "浙商银行"), + HDBANK("HDBANK", "邯郸银行"), + BOC("BOC", "中国银行"), + BOD("BOD", "东莞银行"), + CCB("CCB", "中国建设银行"), + ZYCBANK("ZYCBANK", "遵义市商业银行"), + SXCB("SXCB", "绍兴银行"), + GZRCU("GZRCU", "贵州省农村信用社"), + ZJKCCB("ZJKCCB", "张家口市商业银行"), + BOJZ("BOJZ", "锦州银行"), + BOP("BOP", "平顶山银行"), + HKB("HKB", "汉口银行"), + SPDB("SPDB", "上海浦东发展银行"), + NXRCU("NXRCU", "宁夏黄河农村商业银行"), + NYNB("NYNB", "广东南粤银行"), + GRCB("GRCB", "广州农商银行"), + BOSZ("BOSZ", "苏州银行"), + HZCB("HZCB", "杭州银行"), + HSBK("HSBK", "衡水银行"), + HBC("HBC", "湖北银行"), + JXBANK("JXBANK", "嘉兴银行"), + HRXJB("HRXJB", "华融湘江银行"), + BODD("BODD", "丹东银行"), + AYCB("AYCB", "安阳银行"), + EGBANK("EGBANK", "恒丰银行"), + CDB("CDB", "国家开发银行"), + TCRCB("TCRCB", "江苏太仓农村商业银行"), + NJCB("NJCB", "南京银行"), + ZZBANK("ZZBANK", "郑州银行"), + DYCB("DYCB", "德阳商业银行"), + YBCCB("YBCCB", "宜宾市商业银行"), + SCRCU("SCRCU", "四川省农村信用"), + KLB("KLB", "昆仑银行"), + LSBANK("LSBANK", "莱商银行"), + YDRCB("YDRCB", "尧都农商行"), + CCQTGB("CCQTGB", "重庆三峡银行"), + FDB("FDB", "富滇银行"), + JSRCU("JSRCU", "江苏省农村信用联合社"), + JNBANK("JNBANK", "济宁银行"), + CMB("CMB", "招商银行"), + JINCHB("JINCHB", "晋城银行JCBANK"), + FXCB("FXCB", "阜新银行"), + WHRCB("WHRCB", "武汉农村商业银行"), + HBYCBANK("HBYCBANK", "湖北银行宜昌分行"), + TZCB("TZCB", "台州银行"), + TACCB("TACCB", "泰安市商业银行"), + XCYH("XCYH", "许昌银行"), + CEB("CEB", "中国光大银行"), + NXBANK("NXBANK", "宁夏银行"), + HSBANK("HSBANK", "徽商银行"), + JJBANK("JJBANK", "九江银行"), + NHQS("NHQS", "农信银清算中心"), + MTBANK("MTBANK", "浙江民泰商业银行"), + LANGFB("LANGFB", "廊坊银行"), + ASCB("ASCB", "鞍山银行"), + KSRB("KSRB", "昆山农村商业银行"), + YXCCB("YXCCB", "玉溪市商业银行"), + DLB("DLB", "大连银行"), + DRCBCL("DRCBCL", "东莞农村商业银行"), + GCB("GCB", "广州银行"), + NBBANK("NBBANK", "宁波银行"), + BOYK("BOYK", "营口银行"), + SXRCCU("SXRCCU", "陕西信合"), + GLBANK("GLBANK", "桂林银行"), + BOQH("BOQH", "青海银行"), + CDRCB("CDRCB", "成都农商银行"), + QDCCB("QDCCB", "青岛银行"), + HKBEA("HKBEA", "东亚银行"), + HBHSBANK("HBHSBANK", "湖北银行黄石分行"), + WZCB("WZCB", "温州银行"), + TRCB("TRCB", "天津农商银行"), + QLBANK("QLBANK", "齐鲁银行"), + GDRCC("GDRCC", "广东省农村信用社联合社"), + ZJTLCB("ZJTLCB", "浙江泰隆商业银行"), + GZB("GZB", "赣州银行"), + GYCB("GYCB", "贵阳市商业银行"), + CQBANK("CQBANK", "重庆银行"), + DAQINGB("DAQINGB", "龙江银行"), + CGNB("CGNB", "南充市商业银行"), + SCCB("SCCB", "三门峡银行"), + CSRCB("CSRCB", "常熟农村商业银行"), + SHBANK("SHBANK", "上海银行"), + JLBANK("JLBANK", "吉林银行"), + CZRCB("CZRCB", "常州农村信用联社"), + BANKWF("BANKWF", "潍坊银行"), + ZRCBANK("ZRCBANK", "张家港农村商业银行"), + FJHXBC("FJHXBC", "福建海峡银行"), + ZJNX("ZJNX", "浙江省农村信用社联合社"), + LZYH("LZYH", "兰州银行"), + JSB("JSB", "晋商银行"), + BOHAIB("BOHAIB", "渤海银行"), + CZCB("CZCB", "浙江稠州商业银行"), + YQCCB("YQCCB", "阳泉银行"), + SJBANK("SJBANK", "盛京银行"), + XABANK("XABANK", "西安银行"), + BSB("BSB", "包商银行"), + JSBANK("JSBANK", "江苏银行"), + FSCB("FSCB", "抚顺银行"), + HNRCU("HNRCU", "河南省农村信用"), + COMM("COMM", "交通银行"), + XTB("XTB", "邢台银行"), + CITIC("CITIC", "中信银行"), + HXBANK("HXBANK", "华夏银行"), + HNRCC("HNRCC", "湖南省农村信用社"), + DYCCB("DYCCB", "东营市商业银行"), + ORBANK("ORBANK", "鄂尔多斯银行"), + BJRCB("BJRCB", "北京农村商业银行"), + XYBANK("XYBANK", "信阳银行"), + ZGCCB("ZGCCB", "自贡市商业银行"), + CDCB("CDCB", "成都银行"), + HANABANK("HANABANK", "韩亚银行"), + CMBC("CMBC", "中国民生银行"), + LYBANK("LYBANK", "洛阳银行"), + GDB("GDB", "广东发展银行"), + ZBCB("ZBCB", "齐商银行"), + CBKF("CBKF", "开封市商业银行"), + H3CB("H3CB", "内蒙古银行"), + CIB("CIB", "兴业银行"), + CRCBANK("CRCBANK", "重庆农村商业银行"), + SZSBK("SZSBK", "石嘴山银行"), + DZBANK("DZBANK", "德州银行"), + SRBANK("SRBANK", "上饶银行"), + LSCCB("LSCCB", "乐山市商业银行"), + JXRCU("JXRCU", "江西省农村信用"), + ICBC("ICBC", "中国工商银行"), + JZBANK("JZBANK", "晋中市商业银行"), + HZCCB("HZCCB", "湖州市商业银行"), + NHB("NHB", "南海农村信用联社"), + XXBANK("XXBANK", "新乡银行"), + JRCB("JRCB", "江苏江阴农村商业银行"), + YNRCC("YNRCC", "云南省农村信用社"), + ABC("ABC", "中国农业银行"), + GXRCU("GXRCU", "广西省农村信用"), + PSBC("PSBC", "中国邮政储蓄银行"), + BZMD("BZMD", "驻马店银行"), + ARCU("ARCU", "安徽省农村信用社"), + GSRCU("GSRCU", "甘肃省农村信用"), + LYCB("LYCB", "辽阳市商业银行"), + JLRCU("JLRCU", "吉林农信"), + URMQCCB("URMQCCB", "乌鲁木齐市商业银行"), + XLBANK("XLBANK", "中山小榄村镇银行"), + CSCB("CSCB", "长沙银行"), + JHBANK("JHBANK", "金华银行"), + BHB("BHB", "河北银行"), + NBYZ("NBYZ", "鄞州银行"), + LSBC("LSBC", "临商银行"), + BOCD("BOCD", "承德银行"), + SDRCU("SDRCU", "山东农信"), + NCB("NCB", "南昌银行"), + TCCB("TCCB", "天津银行"), + WJRCB("WJRCB", "吴江农商银行"), + CBBQS("CBBQS", "城市商业银行资金清算中心"), + HBRCU("HBRCU", "河北省农村信用社"); + + private final String code; + private final String name; + + BankCode(String code, String name) { + this.code = code; + this.name = name; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + /** + * 根据银行简称获取银行名称 + * @param code 银行简称 + * @return 银行名称,如果未找到返回null + */ + public static String getBankNameByCode(String code) { + for (BankCode bankCode : values()) { + if (bankCode.getCode().equals(code)) { + return bankCode.getName(); + } + } + return null; + } + + /** + * 根据银行简称获取枚举实例 + * @param code 银行简称 + * @return BankCode枚举实例,如果未找到返回null + */ + public static BankCode getBankByCode(String code) { + for (BankCode bankCode : values()) { + if (bankCode.getCode().equals(code)) { + return bankCode; + } + } + return null; + } +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRequester.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRequester.java new file mode 100644 index 0000000..5737349 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRequester.java @@ -0,0 +1,204 @@ +package com.ruoyi.account.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.Vector; + +public class HttpRequester { + private String defaultContentEncoding; + + public HttpRequester() { + this.defaultContentEncoding = Charset.defaultCharset().name(); + } + + /** + * 发送POST请求 + * + * @param urlString + * URL地址 + * @return 响应对象 + * @throws IOException + */ + public HttpRespons sendPost(String urlString) throws IOException { + return this.send(urlString, "POST", null, null); + } + + /** + * 发送POST请求 + * + * @param urlString + * URL地址 + * @param params + * 参数集合 + * @return 响应对象 + * @throws IOException + */ + public HttpRespons sendPost(String urlString, Map<String, String> params) + throws IOException { + + for(String key : params.keySet()) { + if(params.get(key)!=null&&!"".equals(params.get(key))) { + params.put(key, (String)URLEncoder.encode(params.get(key),"utf-8")) ;//(String) 强制类型转换 + } + } + + + + return this.send(urlString, "POST", params, null); + } + + /** + * 发送POST请求 + * + * @param urlString + * URL地址 + * @param params + * 参数集合 + * @param propertys + * 请求属性 + * @return 响应对象 + * @throws IOException + */ + public HttpRespons sendPost(String urlString, Map<String, String> params, + Map<String, String> propertys) throws IOException { + return this.send(urlString, "POST", params, propertys); + } + + /** + * 发送HTTP请求 + * + * @param urlString + * @return 响映对象 + * @throws IOException + */ + private HttpRespons send(String urlString, String method, + Map<String, String> parameters, Map<String, String> propertys) + throws IOException { + HttpURLConnection urlConnection = null; + + if (method.equalsIgnoreCase("GET") && parameters != null) { + StringBuffer param = new StringBuffer(); + int i = 0; + for (String key : parameters.keySet()) { + if (i == 0) + param.append("?"); + else + param.append("&"); + param.append(key).append("=").append(parameters.get(key)); + i++; + } + urlString += param; + } + URL url = new URL(urlString); + urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod(method); + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + urlConnection.setUseCaches(false); + + if (propertys != null) + for (String key : propertys.keySet()) { + urlConnection.addRequestProperty(key, propertys.get(key)); + } + + if (method.equalsIgnoreCase("POST") && parameters != null) { + StringBuffer param = new StringBuffer(); + for (String key : parameters.keySet()) { + param.append("&"); + param.append(key).append("=").append(parameters.get(key)); + } + urlConnection.getOutputStream().write(param.toString().getBytes()); + urlConnection.getOutputStream().flush(); + urlConnection.getOutputStream().close(); + } + + return this.makeContent(urlString, urlConnection); + } + + /** + * 得到响应对象 + * + * @param urlConnection + * @return 响应对象 + * @throws IOException + */ + private HttpRespons makeContent(String urlString, + HttpURLConnection urlConnection) throws IOException { + HttpRespons httpResponser = new HttpRespons(); + try { + InputStream in = urlConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(in)); + httpResponser.contentCollection = new Vector<String>(); + StringBuffer temp = new StringBuffer(); + String line = bufferedReader.readLine(); + while (line != null) { + httpResponser.contentCollection.add(line); + temp.append(line).append("\r\n"); + line = bufferedReader.readLine(); + } + bufferedReader.close(); + + String ecod = urlConnection.getContentEncoding(); + if (ecod == null) + ecod = this.defaultContentEncoding; + + httpResponser.urlString = urlString; + + httpResponser.defaultPort = urlConnection.getURL().getDefaultPort(); + httpResponser.file = urlConnection.getURL().getFile(); + httpResponser.host = urlConnection.getURL().getHost(); + httpResponser.path = urlConnection.getURL().getPath(); + httpResponser.port = urlConnection.getURL().getPort(); + httpResponser.protocol = urlConnection.getURL().getProtocol(); + httpResponser.query = urlConnection.getURL().getQuery(); + httpResponser.ref = urlConnection.getURL().getRef(); + httpResponser.userInfo = urlConnection.getURL().getUserInfo(); + + httpResponser.content = new String(temp.toString().getBytes(), ecod); + httpResponser.contentEncoding = ecod; + httpResponser.code = urlConnection.getResponseCode(); + httpResponser.message = urlConnection.getResponseMessage(); + httpResponser.contentType = urlConnection.getContentType(); + httpResponser.method = urlConnection.getRequestMethod(); + httpResponser.connectTimeout = urlConnection.getConnectTimeout(); + httpResponser.readTimeout = urlConnection.getReadTimeout(); + + return httpResponser; + } catch (IOException e) { + throw e; + } finally { + if (urlConnection != null) + urlConnection.disconnect(); + } + } + + /** + * 默认的响应字符集 + */ + public String getDefaultContentEncoding() { + return this.defaultContentEncoding; + } + + /** + * 设置默认的响应字符集 + */ + public void setDefaultContentEncoding(String defaultContentEncoding) { + this.defaultContentEncoding = defaultContentEncoding; + } + +} + + + + + + diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRespons.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRespons.java new file mode 100644 index 0000000..0ef4d5a --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/HttpRespons.java @@ -0,0 +1,184 @@ +package com.ruoyi.account.util; + + + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.InputStreamRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.RequestEntity; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + + + +public class HttpRespons { + String urlString; + + int defaultPort; + + String file; + + String host; + + String path; + + int port; + + String protocol; + + String query; + + String ref; + + String userInfo; + + String contentEncoding; + + String content; + + String contentType; + + int code; + + String message; + + String method; + + int connectTimeout; + + int readTimeout; + + Vector<String> contentCollection; + + public String getContent() { + return content; + } + + public String getContentType() { + return contentType; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public Vector<String> getContentCollection() { + return contentCollection; + } + + public String getContentEncoding() { + return contentEncoding; + } + + public String getMethod() { + return method; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public int getReadTimeout() { + return readTimeout; + } + + public String getUrlString() { + return urlString; + } + + public int getDefaultPort() { + return defaultPort; + } + + public String getFile() { + return file; + } + + public String getHost() { + return host; + } + + public String getPath() { + return path; + } + + public int getPort() { + return port; + } + + public String getProtocol() { + return protocol; + } + + public String getQuery() { + return query; + } + + public String getRef() { + return ref; + } + + public String getUserInfo() { + return userInfo; + } + + + public static String post(String params,String requestUrl) throws IOException { +// try { + //HttpRequester request = new HttpRequester(); + // request.setDefaultContentEncoding("utf-8"); + byte[] requestBytes = params.getBytes("utf-8"); // 将参数转为二进制流 + HttpClient httpClient = new HttpClient(); // 客户端实例化 + PostMethod postMethod = new PostMethod(requestUrl); + //设置请求头Authorization +// postMethod.setRequestHeader("Authorization", "Basic " + authorization); + // 设置请求头 Content-Type + postMethod.setRequestHeader("Content-Type", "application/json"); + InputStream inputStream = new ByteArrayInputStream(requestBytes, 0,requestBytes.length); + RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, + requestBytes.length, "application/json; charset=utf-8"); // 请求体 + postMethod.setRequestEntity(requestEntity); + httpClient.executeMethod(postMethod);// 执行请求 + InputStream soapResponseStream = postMethod.getResponseBodyAsStream();// 获取返回的流 + byte[] datas = null; + try { + datas = readInputStream(soapResponseStream);// 从输入流中读取数据 + } catch (Exception e) { + e.printStackTrace(); + } + String result = new String(datas, "UTF-8");// 将二进制流转为String + // 打印返回结果 + // System.out.println(result); + + return result; + } + + + /** + * 从输入流中读取数据 + * + * @param inStream + * @return + * @throws Exception + */ + public static byte[] readInputStream(InputStream inStream) throws Exception { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len = 0; + while ((len = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + byte[] data = outStream.toByteArray(); + outStream.close(); + inStream.close(); + return data; + } +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/UUIDUtil.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/UUIDUtil.java new file mode 100644 index 0000000..bbd739f --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/UUIDUtil.java @@ -0,0 +1,103 @@ +package com.ruoyi.account.util; + + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +/** + * 定义生成随机码的工具类 + */ +public class UUIDUtil { + + private int i = 1; + + + /** + * 定义生成原生的UUID随机码 + * + * @return + */ + public static String getNativeUUID() { + return UUID.randomUUID().toString(); + } + + + /** + * 生成32位随机码 + * + * @return + */ + public static String getRandomCode() { + return UUIDUtil.getNativeUUID().replaceAll("-", ""); + } + + + /** + * 获取给定长度的随机码 + * + * @param num + * @return + * @throws Exception + */ + public static String getRandomCode(Integer num) throws Exception { + String str = null; + if (0 < num) { + if (num % 32 > 0) { + Integer s = num / 32; + Integer l = num % 32; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s; i++) { + sb.append(UUIDUtil.getRandomCode()); + } + sb.append(UUIDUtil.getRandomCode().substring(0, l)); + str = sb.toString(); + } else if (num % 32 == 0) { + Integer s = num / 32; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s; i++) { + sb.append(UUIDUtil.getRandomCode()); + } + str = sb.toString(); + } else { + str = UUIDUtil.getRandomCode().substring(0, num); + } + } else { + throw new Exception("参数只能大于0"); + } + return str; + } + + + /** + * 获取根据当前时间的字符串数据 + * + * @return + */ + public synchronized static String getTimeStr() { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmssS"); + return simpleDateFormat.format(new Date()); + } + + + /** + * @Description: 获取数字随机码 + * @Author pzb + * @Date 2021/8/11 16:52 + * @Param + * @Return + * @Exception + */ + public static String getNumberRandom(Integer num) { + if (null == num) { + num = 32; + } + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < num; i++) { + sb.append(Double.valueOf(Math.random() * 10).intValue()); + } + return sb.toString(); + } + + +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/HttpUtilWithdraw.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/HttpUtilWithdraw.java new file mode 100644 index 0000000..2a17261 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/HttpUtilWithdraw.java @@ -0,0 +1,492 @@ +package com.ruoyi.account.util.withdraw; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; +import com.ruoyi.account.api.model.UserWithdraw; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.other.api.domain.ShopWithdraw; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.client.HttpClients; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 三方提现工具类 + */ +public class HttpUtilWithdraw { + + //设置请求和传输超时时间 + private static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(60000).setConnectTimeout(60000).build(); + //初始化需要用到的参数 + private static final String CLIENT_TYPE = "1"; + private static final String CLIENT_VERSION = "1.0.0"; + private static final String APP_CONFIG_ID = "8597918391594056444"; + private static final String API_KEY = "jo_pay_S4UIWYjWmy46d9yX9N2QWZffD"; + private static final String API_SECRET = "cad03aaf2019530090d642aa9b2f0cbb"; + + private static final String STRING_KEY = "pay_open_api_vwHXa2ay1Dv7E${clientType}${clientVersion}${apiKey}${apiSecret}${timestamp}"; + private static final String URL = "https://pay.new.jieoukeji.cn/jo-pay-open-api"; + private static final String PATH = "/v1/safe/withdraw";//示例接口:获取银行卡信息 + private static final Pattern VALUE_PATTERN = Pattern.compile("\\$\\{([^}]*)\\}"); + + public static void main(String[] args) throws IOException { + long timestamp = System.currentTimeMillis(); + Map<String,String> headers = new HashMap<>(6); + headers.put("client-type",CLIENT_TYPE); + headers.put("client-version",CLIENT_VERSION); + headers.put("api-secret",API_SECRET); + headers.put("api-key",API_KEY); + headers.put("access-timestamp",String.valueOf(timestamp)); + headers.put("Content-Type","application/json"); + //拼接字符串,签名开头保留,${} 全替换为header参数 + headers.put("access-sign",getSign(STRING_KEY,timestamp)); + //开始请求API接口 + Map<String, Object> param = new HashMap<>(); + param.put("orderCode", "09127389681694"); + param.put("cardNo", "6228480469852935177"); + param.put("name", "周帅"); + param.put("withdrawType", "1"); + param.put("accountType", "00"); + param.put("bankName", "中国农业银行"); +// param.put("brachBankCode", "123546345674573457"); + param.put("money", "0.1"); + param.put("appConfigId", APP_CONFIG_ID); + try { + String results = post(URL+PATH,headers, param); + //返回结果JSON字符串 + System.out.println(results); + } catch (Exception e) { + e.printStackTrace(); + } + + +// String url = "https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&" + +// "cardNo=" + "6228480469852935177" + "&cardBinCheck=true"; +// HashMap<String, String> hashMap = new HashMap<>(); +// HttpRequester hr = new HttpRequester(); +// HttpRespons HP = hr.sendPost(url, hashMap); +// System.out.println("接收返回参数:" + HP.getContent()); +// JSONObject resPay = JSONObject.parseObject(HP.getContent()); +// System.err.println(resPay); +// if (resPay.getString("validated") == null) { +// System.err.println("不合法的银行卡号"); +// } +// String bankName = BankCode.getBankNameByCode("ABC"); // 返回ABC枚举实例 +// System.err.println(bankName); + } + + public static String withdraw(ShopWithdraw shopWithdraw, String bankName) { + long timestamp = System.currentTimeMillis(); + Map<String, String> headers = new HashMap<>(6); + headers.put("client-type", CLIENT_TYPE); + headers.put("client-version", CLIENT_VERSION); + headers.put("api-secret", API_SECRET); + headers.put("api-key", API_KEY); + headers.put("access-timestamp", String.valueOf(timestamp)); + headers.put("Content-Type", "application/json"); + //拼接字符串,签名开头保留,${} 全替换为header参数 + headers.put("access-sign", getSign(STRING_KEY, timestamp)); + //开始请求API接口 + Map<String, Object> param = new HashMap<>(); + param.put("orderCode", shopWithdraw.getCode()); + param.put("cardNo", shopWithdraw.getReceiverAccountNoEnc()); + param.put("name", shopWithdraw.getReceiverNameEnc()); + param.put("withdrawType", "1"); + if (shopWithdraw.getReceiverAccountType().equals(204)) { + param.put("accountType", "01"); + param.put("bankName", bankName); + param.put("brachBankCode", shopWithdraw.getReceiverBankChannelNo()); + } else { + param.put("accountType", "00"); + } + param.put("money", shopWithdraw.getMoney()); + param.put("appConfigId", APP_CONFIG_ID); + try { + String results = post(URL + PATH, headers, param); + //返回结果JSON字符串 + System.out.println(results); + JSONObject jsonObject = JSONObject.parseObject(results); + if (jsonObject.getString("result").equals("true")){ + return "success"; + }else{ + return jsonObject.getString("message"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return "打款失败"; + } + public static String userWithdraw(UserWithdraw userWithdraw, String bankName) { + long timestamp = System.currentTimeMillis(); + Map<String, String> headers = new HashMap<>(6); + headers.put("client-type", CLIENT_TYPE); + headers.put("client-version", CLIENT_VERSION); + headers.put("api-secret", API_SECRET); + headers.put("api-key", API_KEY); + headers.put("access-timestamp", String.valueOf(timestamp)); + headers.put("Content-Type", "application/json"); + //拼接字符串,签名开头保留,${} 全替换为header参数 + headers.put("access-sign", getSign(STRING_KEY, timestamp)); + //开始请求API接口 + Map<String, Object> param = new HashMap<>(); + param.put("orderCode", userWithdraw.getCode()); + param.put("cardNo", userWithdraw.getReceiverAccountNoEnc()); + param.put("name", userWithdraw.getReceiverNameEnc()); + param.put("withdrawType", "1"); + if (userWithdraw.getReceiverAccountType().equals(204)) { + param.put("accountType", "01"); + param.put("bankName", bankName); + param.put("brachBankCode", userWithdraw.getReceiverBankChannelNo()); + } else { + param.put("accountType", "00"); + } + param.put("money", userWithdraw.getMoney()); + param.put("appConfigId", APP_CONFIG_ID); + try { + String results = post(URL + PATH, headers, param); + //返回结果JSON字符串 + System.out.println(results); + JSONObject jsonObject = JSONObject.parseObject(results); + if (jsonObject.getString("result").equals("true")){ + return "success"; + }else{ + return jsonObject.getString("message"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return "打款失败"; + } + + /** + * 发起post请求,请求参数以Map集合形式传入 + * + * @param httpUrl + * @param body + * @return + * @throws Exception + */ + public static String post(String httpUrl, Map<String, String> headers, Map<String, Object> body) { + CloseableHttpClient httpclient = createSSLClientDefault(); + BufferedReader in; + HttpPost httpPost = new HttpPost(httpUrl); + try { + httpPost.setHeader("Accept", "application/json"); + for (Map.Entry<String, String> headerMap : headers.entrySet()) { + httpPost.addHeader(headerMap.getKey(), headerMap.getValue()); + } + httpPost.setConfig(requestConfig); + StringEntity entity = new StringEntity(JSON.toJSONString(body), StandardCharsets.UTF_8); + entity.setContentType("application/json"); + httpPost.setEntity(entity); + + CloseableHttpResponse response = httpclient.execute(httpPost); + InputStream content = response.getEntity().getContent(); + in = new BufferedReader(new InputStreamReader(content, StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + sb.append(line); + } + return sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + httpclient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + /** + * 发起get请求,请求参数以Map集合形式传入 + * + * @param httpUrl + * @param params + * @return + * @throws Exception + */ + public static String get(String httpUrl, Map<String, String> headers, Map<String, String> params) { + CloseableHttpClient httpclient = createSSLClientDefault(); + BufferedReader in; + List<String> paramList = new ArrayList<>(); + for (Map.Entry<String, String> param : params.entrySet()) { + paramList.add(param.getKey() + "=" + param.getValue()); + } + HttpGet httpGet = new HttpGet(httpUrl + "?" + String.join("&", paramList)); + try { + httpGet.setHeader("Accept", "application/json"); + for (Map.Entry<String, String> headerMap : headers.entrySet()) { + httpGet.addHeader(headerMap.getKey(), headerMap.getValue()); + } + httpGet.setConfig(requestConfig); + + CloseableHttpResponse response = httpclient.execute(httpGet); + InputStream content = response.getEntity().getContent(); + in = new BufferedReader(new InputStreamReader(content, StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + sb.append(line); + } + return sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + httpclient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + public static CloseableHttpClient createSSLClientDefault() { + try { + // SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { + // 在JSSE中,证书信任管理器类就是实现了接口X509TrustManager的类。我们可以自己实现该接口,让它信任我们指定的证书。 + // 创建SSLContext对象,并使用我们指定的信任管理器初始化 + // 信任所有 + X509TrustManager x509mgr = new X509TrustManager() { + // 该方法检查客户端的证书,若不信任该证书则抛出异常 + @Override + public void checkClientTrusted(X509Certificate[] xcs, String string) { + } + + // 该方法检查服务端的证书,若不信任该证书则抛出异常 + @Override + public void checkServerTrusted(X509Certificate[] xcs, String string) { + } + + // 返回受信任的X509证书数组。 + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + SSLContext sslContext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS); + sslContext.init(null, new TrustManager[]{x509mgr}, null); + // 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象 + SSLConnectionSocketFactory sslSf = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier()); + // HttpsURLConnection对象就可以正常连接HTTPS了,无论其证书是否经权威机构的验证,只要实现了接口X509TrustManager的类MyX509TrustManager信任该证书。 + return HttpClients.custom().setSSLSocketFactory(sslSf).build(); + } catch (Exception e) { + e.printStackTrace(); + } + // 创建默认的httpClient实例. + return HttpClients.createDefault(); + } + + /** + * 获取签名前准备参数 + * + * @param format + * @param timestamp + * @return + */ + public static String getSign(String format, long timestamp) { + JSONObject dataSign = new JSONObject(); + dataSign.put("clientType", CLIENT_TYPE); + dataSign.put("clientVersion", CLIENT_VERSION); + dataSign.put("apiSecret", API_SECRET); + dataSign.put("apiKey", API_KEY); + dataSign.put("timestamp", timestamp); + String formatData = getFormatData(dataSign, format); + return md5(formatData); + } + + /** + * 拼接替换生成签名前字符串 + * + * @return + */ + public static String getFormatData(JSONObject data, String format) { + + System.err.println(data); + System.err.println(format); + Matcher matcher = VALUE_PATTERN.matcher(format); + while (true) { + while (matcher.find()) { + String sign = matcher.group(); + String key = matcher.group(1); + if (ObjectUtils.isNotEmpty(key)) { + format = format.replace(sign, data.containsKey(key) && ObjectUtils.isNotEmpty(data.getString(key)) && !"null".equalsIgnoreCase(data.getString(key)) ? data.getString(key) : ""); + } else { + format = format.replace(sign, ""); + } + } + return format; + } + } + + /** + * 生成md5,小写 + * + * @param message + * @return + */ + public static String md5(String message) { + try { + // 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象 + MessageDigest md = MessageDigest.getInstance("MD5"); + + // 2 将消息变成byte数组 + byte[] input = message.getBytes(); + + // 3 计算后获得字节数组,这就是那128位了 + byte[] buff = md.digest(input); + + // 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串 + return byte2hex(buff); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 二进制转十六进制字符串 + * + * @param bytes + * @return + */ + private static String byte2hex(byte[] bytes) { + StringBuilder sign = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(bytes[i] & 0xFF); + if (hex.length() == 1) { + sign.append("0"); + } + sign.append(hex.toLowerCase()); + } + return sign.toString(); + } + + /** + * 调用apiPost方法 + * + * @return + */ + public static HttpResponse doPost(String host, String path, String method, + Map<String, String> headers, + Map<String, String> querys, + String body) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry<String, String> e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (StringUtils.isNotBlank(body)) { + request.setEntity(new StringEntity(body, "utf-8")); + } + return httpClient.execute(request); + } + + public static HttpClient wrapClient(String host) { + HttpClient httpClient = new DefaultHttpClient(); + if (host.startsWith("https://")) { + sslClient(httpClient); + } + + return httpClient; + } + + public static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException { + StringBuilder sbUrl = new StringBuilder(); + sbUrl.append(host); + if (!StringUtils.isBlank(path)) { + sbUrl.append(path); + } + if (null != querys) { + StringBuilder sbQuery = new StringBuilder(); + for (Map.Entry<String, String> query : querys.entrySet()) { + if (0 < sbQuery.length()) { + sbQuery.append("&"); + } + if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) { + sbQuery.append(query.getValue()); + } + if (!StringUtils.isBlank(query.getKey())) { + sbQuery.append(query.getKey()); + if (!StringUtils.isBlank(query.getValue())) { + sbQuery.append("="); + sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8")); + } + } + } + if (0 < sbQuery.length()) { + sbUrl.append("?").append(sbQuery); + } + } + + return sbUrl.toString(); + } + + private static void sslClient(HttpClient httpClient) { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] xcs, String str) { + + } + + @Override + public void checkServerTrusted(X509Certificate[] xcs, String str) { + + } + }; + ctx.init(null, new TrustManager[]{tm}, null); + SSLSocketFactory ssf = new SSLSocketFactory(ctx); + ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + ClientConnectionManager ccm = httpClient.getConnectionManager(); + SchemeRegistry registry = ccm.getSchemeRegistry(); + registry.register(new Scheme("https", 443, ssf)); + } catch (KeyManagementException ex) { + throw new RuntimeException(ex); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/WithdrawCallBackDTO.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/WithdrawCallBackDTO.java new file mode 100644 index 0000000..a6d56f8 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/withdraw/WithdrawCallBackDTO.java @@ -0,0 +1,36 @@ +package com.ruoyi.account.util.withdraw; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 三方提现回调DTO + */ +@Data +public class WithdrawCallBackDTO { + + @ApiModelProperty(value = "数据签名,") + private String sign; + @ApiModelProperty(value = "打款订单号") + private String orderCode; + @ApiModelProperty(value = "交易流水号") + private String tradeNo; + @ApiModelProperty(value = "打款金额") + private BigDecimal money; + @ApiModelProperty(value = "订单状态 0待支付 1已支付 2处理中 4交易失败") + private Integer status; + @ApiModelProperty(value = "打款时间") + private LocalDateTime time; + @ApiModelProperty(value = "订单状态信息") + private String message; + + + + + + + +} diff --git a/ruoyi-service/ruoyi-account/src/main/resources/mapper/account/UserWithdrawMapper.xml b/ruoyi-service/ruoyi-account/src/main/resources/mapper/account/UserWithdrawMapper.xml new file mode 100644 index 0000000..87b0491 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/resources/mapper/account/UserWithdrawMapper.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.account.mapper.UserWithdrawMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.ruoyi.account.api.model.UserWithdraw"> + <id column="id" property="id" /> + <result column="app_user_id" property="appUserId" /> + <result column="money" property="money" /> + <result column="integral" property="integral" /> + <result column="audit_status" property="auditStatus" /> + <result column="audit_user_id" property="auditUserId" /> + <result column="audit_time" property="auditTime" /> + <result column="audit_msg" property="auditMsg" /> + <result column="status" property="status" /> + <result column="arrival_time" property="arrivalTime" /> + <result column="del_flag" property="delFlag" /> + <result column="create_time" property="createTime" /> + </resultMap> + + <!-- 通用查询结果列 --> + <sql id="Base_Column_List"> + id, app_user_id, money, integral, audit_status, audit_user_id, audit_time, audit_msg, status, arrival_time, del_flag, create_time + </sql> + <select id="page" resultType="com.ruoyi.account.api.model.UserWithdraw"> + select t1.*,t2.phone,t2.name from + t_user_withdraw t1 + left join t_app_user t2 on t1.app_user_id = t2.id + </select> + +</mapper> -- Gitblit v1.7.1