Explorar el Código

Merge branch 'dev' into 'mp-server-test'

Dev

See merge request quanshu/mp-server!1056
Evan hace 2 años
padre
commit
04942a3d32

+ 72 - 1
mp-admin/src/main/java/com/qs/mp/web/controller/api/callback/YsPayCallBackController.java

@@ -12,9 +12,12 @@ import com.eptok.yspay.opensdkjava.pojo.vo.OnlineReqDataVo;
 import com.eptok.yspay.opensdkjava.util.DateUtil;
 import com.eptok.yspay.opensdkjava.util.YsOnlineSignUtils;
 import com.eptok.yspay.opensdkjava.util.YsfSignUtil;
+import com.qs.mp.channel.domain.ChannelWithdraw;
+import com.qs.mp.channel.service.IChannelWithdrawService;
 import com.qs.mp.common.constant.DivisionLogStatusConstants;
 import com.qs.mp.common.constant.YsServerApiConstants;
 import com.qs.mp.common.domain.YsCallLog;
+import com.qs.mp.common.enums.ChannelWithdrawStatusEnum;
 import com.qs.mp.common.enums.DivisionStatusEnum;
 import com.qs.mp.common.enums.PayOrderStatusEnum;
 import com.qs.mp.common.enums.YsCallBizTypeEnum;
@@ -32,6 +35,7 @@ import java.util.HashMap;
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.bouncycastle.util.test.TestRandomBigInteger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -87,6 +91,9 @@ public class YsPayCallBackController {
     @Autowired
     private IDivisionLogService divisionLogService;
 
+    @Autowired
+    private IChannelWithdrawService channelWithdrawService;
+
 
     @RequestMapping(value = "/ysCallback", method = RequestMethod.GET)
     public void payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
@@ -258,7 +265,7 @@ public class YsPayCallBackController {
             params.put(key, valueStr);
         }
 
-        System.out.println("分账回调的数据 = " + JSONObject.toJSONString(params));
+        logger.info("分账回调的数据 = " + JSONObject.toJSONString(params));
         String divTradeNo = params.get("out_trade_no");
         String divisionStatusCode = params.get("division_status_code");
         YsCallLog ysCallLog = ysCallLogService.getOne(new LambdaQueryWrapper<YsCallLog>()
@@ -289,6 +296,70 @@ public class YsPayCallBackController {
         responseWrite(response, "success");
     }
 
+    /**
+     * 提现回调
+     * @param request
+     * @param response
+     */
+    @RequestMapping(value = "/ysWithdrawCallback", method = RequestMethod.GET)
+    public void withdrawCallback(HttpServletRequest request, HttpServletResponse response) {
+        Map<String, String[]> reqParams = request.getParameterMap();
+
+        Map<String, String> params = new HashMap<>();
+        for (String key : reqParams.keySet()) {
+            String[] values = reqParams.get(key);
+            String valueStr = "";
+            for (int i = 0; i < values.length; i++) {
+                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
+            }
+            params.put(key, valueStr);
+        }
+
+        logger.info("提现回调的数据 = " + JSONObject.toJSONString(params));
+        String tradeNo = params.get("out_trade_no");
+
+        // 记录回调数据
+        // TODO: 类型,一般账户 和 待结算账户
+        YsCallLog ysCallLog = ysCallLogService.getOne(new LambdaQueryWrapper<YsCallLog>()
+            .eq(YsCallLog::getBizId, tradeNo)
+            .eq(YsCallLog::getBizType, YsCallBizTypeEnum.WITHDRAW));
+        if (ysCallLog != null) {
+            ysCallLogService.update(new LambdaUpdateWrapper<YsCallLog>()
+                .set(YsCallLog::getCallbackJson, JSONObject.toJSONString(params))
+                .eq(YsCallLog::getId, ysCallLog.getId()));
+        }
+
+        ChannelWithdraw channelWithdraw = channelWithdrawService.getOne(new LambdaQueryWrapper<ChannelWithdraw>()
+            .eq(ChannelWithdraw::getTradeNo, tradeNo));
+
+
+        if (!ChannelWithdrawStatusEnum.WITHDRAW_PROCESS.equals(channelWithdraw.getStatus())) {
+            logger.error("提现状态不是银盛提现处理中,不处理回调,tradeNo:" + tradeNo);
+            responseWrite(response, "fail");
+            return;
+        }
+
+        String tradeStatus = params.get("trade_status");
+        if (!"TRADE_SUCCESS".equals(tradeStatus)) {
+            logger.error("提现处理失败,tradeNo:{},desc:{}",tradeNo, params.get("trade_status_description"));
+            channelWithdrawService.update(new LambdaUpdateWrapper<ChannelWithdraw>()
+                .set(ChannelWithdraw::getStatus, ChannelWithdrawStatusEnum.YS_WITHDRAW_FAILED)
+                .eq(ChannelWithdraw::getId, channelWithdraw.getId())
+                .eq(ChannelWithdraw::getStatus, ChannelWithdrawStatusEnum.WITHDRAW_PROCESS));
+            responseWrite(response, "fail");
+            return;
+        }
+
+        // 提现成功,状态修改
+        boolean res = channelWithdrawService.update(new LambdaUpdateWrapper<ChannelWithdraw>()
+            .set(ChannelWithdraw::getStatus, ChannelWithdrawStatusEnum.FINISHED)
+            .eq(ChannelWithdraw::getId, channelWithdraw.getId())
+            .eq(ChannelWithdraw::getStatus, ChannelWithdrawStatusEnum.WITHDRAW_PROCESS));
+        if (!res) {
+            logger.error("更新提现记录状态失败,tradeNo:{}", tradeNo);
+        }
+        responseWrite(response, "success");
+    }
 
 
     private void responseWrite(HttpServletResponse response,String echostr){

+ 37 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserMineController.java

@@ -13,11 +13,14 @@ import com.qs.mp.admin.service.ICouponService;
 import com.qs.mp.admin.service.IGoodsService;
 import com.qs.mp.channel.domain.Channel;
 import com.qs.mp.channel.service.IChannelService;
+import com.qs.mp.common.annotation.Log;
 import com.qs.mp.common.core.domain.AjaxResult;
 import com.qs.mp.common.core.page.TableDataInfo;
 import com.qs.mp.common.enums.*;
 import com.qs.mp.common.utils.DateUtils;
 import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.system.domain.SysUser;
+import com.qs.mp.system.service.ISysUserService;
 import com.qs.mp.user.domain.UserCoin;
 import com.qs.mp.user.domain.UserCoinLog;
 import com.qs.mp.user.domain.UserCoupon;
@@ -93,6 +96,40 @@ public class UserMineController extends BaseApiController {
     @Autowired
     private IChannelService channelService;
 
+    @Autowired
+    private ISysUserService userService;
+
+    @Log(title = "昵称和头像修改", businessType = BusinessType.UPDATE)
+    @PostMapping("/updateUserInfo")
+    public AjaxResult updateUserInfo(@RequestBody SysUser user) {
+        Long userId = SecurityUtils.getLoginUser().getUserId();
+        String avatar = user.getAvatar();
+        String nickName = user.getNickName();
+
+        if (userId == null || userId <= 0L) {
+            return AjaxResult.error("用户ID不正确");
+        }
+        SysUser sysUser = userService.selectUserById(userId);
+        if (sysUser == null) {
+            return AjaxResult.error("用户不存在");
+        }
+
+        if (StringUtils.isBlank(nickName)) {
+            return AjaxResult.error("用户昵称不能为空");
+        }
+
+        if (StringUtils.isBlank(avatar)) {
+            return AjaxResult.error("用户头像不能为空");
+        }
+
+        user.setUserId(userId);
+        // 更新用户昵称和头像
+        userService.updateUserProfile(user);
+
+        return AjaxResult.success();
+    }
+
+
     /**
      * 我的票包
      */

+ 61 - 20
mp-admin/src/test/java/com/qs/mp/task/MyTest.java

@@ -137,12 +137,53 @@ public class MyTest {
     private String publicKeyPath;
 
 
+    @Value("${ys-pay.withdraw-notify-url}")
+    private String ysWithdrawNotifyUrl;
+
+
     /**
      * 提现测试方法
      */
     @Test
     void test20() {
+        String tradeNo = DateUtil.getDateNowYmd() + DateUtil.getRandom(14);
+
+
+        OnlineReqDataVo req = new OnlineReqDataVo();
+        req.setReqUrl(YsServerApiConstants.COMMON_API);
+        req.setPrivateKeyFilePath(privateKeyPath);
+        req.setYsPublicKeyFilePath(publicKeyPath);
+        req.setPrivateKeyPassword(privateKeyPass);
+        req.setNotifyUrl(ysWithdrawNotifyUrl);
+        req.setPartnerId(partnerId);
+
+        //实时提现(一般户到银行卡)业务参数
+        Map<String, Object> bizContent = new HashMap<>();
+        //商户生成的订单号,生成规则前8位必须为交易日期,如20180525,范围跨度支持包含当天在内的前后一天
+        bizContent.put("out_trade_no", tradeNo);
+        //商户号
+        bizContent.put("merchant_usercode", partnerId);
+        //暂时只支持币种:CNY(人民币)
+        bizContent.put("currency", "CNY");
+        //提现的总金额。单位为:RMB Yuan。取值范围为[0.01,99999999.99],精确到小数点后两位。Number(10,2)指10位长度,2位精度
+        bizContent.put("total_amount", "0.04");
+        //订单说明
+        bizContent.put("subject", "提现了");
+        //商户日期(该参数做交易与查询时需要一致) 该日期需在当日的前后一天时间范围之内
+        bizContent.put("shopdate", DateUtil.getDateNowYmd());
+        //银行帐号
+        bizContent.put("bank_account_no", "6212260488888888888");
+        req.setParamData(bizContent);
+        String result = null;
+        try{
+            logger.info("实时提现(一般户到银行卡)调用sdk接口addScanMerc请求入参为:"+ JSONObject.toJSONString(req));
+            //根据返回结果处理自己的业务逻辑,result内容详见接口文档
+            result = MercFundApi.withdrawQuick(req);
+        }catch (Exception e){
+            logger.info("实时提现(一般户到银行卡)失败:"+ e.getMessage());
+        }
 
+        System.out.println("result = " + result);
     }
 
     @Test
@@ -186,36 +227,36 @@ public class MyTest {
         walletService.h5Pay(null, "0239021091224323", 100, "测试测试测试");
     }
 
+    /**
+     * 实名认证方法
+     */
     @Test
     void test17() {
         OnlineReqDataVo req = new OnlineReqDataVo();
-        //请求路径,建议配置在项目的配置文件里面
-        String reqUrl = "https://commonapi.ysepay.com/gateway.do";
-        //私钥证书存放路径,建议配置在项目的配置文件里面
-        String privateKeyFilePath = "/Users/cup/Downloads/ysPayPre.pfx";
-        //ys公钥证书存放地址 建议配置在项目的配置文件里面
-        String publicKeyFilePath = "/Users/cup/Downloads/ysPayPub.cer";
-        //私钥证书密钥,建议配置在项目的配置文件里面
-        String privateKeyPassworde = "FAl9S8TmGB80";
-
         //商户在银盛支付平台开设的用户号[商户号],接入时需要替换成自己的
         req.setPartnerId(partnerId);
-        req.setReqUrl(reqUrl);
-        req.setPrivateKeyFilePath(privateKeyFilePath);
-        req.setPrivateKeyPassword(privateKeyPassworde);
-        req.setYsPublicKeyFilePath(publicKeyFilePath);
-        //商户余额查询业务参数
+        req.setReqUrl(YsServerApiConstants.OPEN_API);
+        req.setPrivateKeyFilePath(privateKeyPath);
+        req.setPrivateKeyPassword(privateKeyPass);
+        req.setYsPublicKeyFilePath(publicKeyPath);
+        //银行实名认证三要素下单业务参数
         Map<String, Object> bizContent = new HashMap<>();
-        bizContent.put("merchant_usercode", partnerId);//商户号
+        bizContent.put("out_trade_no", DateUtil.getDateNowYmd() + DateUtil.getRandom(6));//商户生成的订单号,生成规则前8位必须为交易日期,如20180525,范围跨度支持包含当天在内的前后一天
+        bizContent.put("shopdate", DateUtil.getDateNowYmd());//商户日期(该参数做交易与查询时需要一致)该日期需在当日的前后一天时间范围之内
+        bizContent.put("bank_account_name", "李四");//实名认证姓名--请填写真实信息
+        bizContent.put("bank_account_no", "6212260488888888888");//银行帐号,支持对公对私
+        bizContent.put("id_card","41022319940916602X");//证件号码,目前只支持身份证
         req.setParamData(bizContent);
         String result = null;
-        try {
-            logger.info("账户余额查询调用sdk接口addScanMerc请求入参为:" + JSONObject.toJSONString(req));
+        try{
+            logger.info("银行实名认证三要素调用sdk接口addScanMerc请求入参为:"+ JSONObject.toJSONString(req));
             //根据返回结果处理自己的业务逻辑,result内容详见接口文档
-            result = MercFundApi.getFundAccount(req);
-        } catch (Exception e) {
-            logger.info("账户余额查询失败:" + e.getMessage());
+            result = BankAuthorityApi.sendBankAuthThreePrecise(req);
+        }catch (Exception e){
+            logger.info("银行实名认证三要素失败:"+ e.getMessage());
         }
+        System.out.println("result = " + result);
+
     }
 
     /**

+ 7 - 0
mp-common/src/main/java/com/qs/mp/common/constant/YsPayMethodConstants.java

@@ -18,4 +18,11 @@ public class YsPayMethodConstants {
 
     public static String BANK_CARD_AUTH_METHOD = "ysepay.authenticate.three.key.element.precise";
 
+
+    /** 一般账户提现方法 */
+    public static String GENERAL_ACCOUNT_WITHDRAW_METHOD = "ysepay.merchant.withdraw.quick.accept";
+
+    /** 待结算账户提现方法 */
+    public static String SETTLED_ACCOUNT_WITHDRAW_METHOD = "ysepay.merchant.withdraw.d0.accept";
+
 }

+ 6 - 1
mp-common/src/main/java/com/qs/mp/common/enums/ChannelWithdrawStatusEnum.java

@@ -15,7 +15,12 @@ public enum ChannelWithdrawStatusEnum implements IEnum<Integer> {
 
   WITHDRAWING(1, "待提现"),
   FINISHED(2, "已完成"),
-  FAILED(3, "提现失败");
+  FAILED(3, "提现失败"),
+
+  WITHDRAW_PROCESS(4,"提现处理中"),
+
+  YS_WITHDRAW_FAILED(5, "银盛提现失败"),
+  ;
 
 
   private final int value;

+ 2 - 0
mp-common/src/main/java/com/qs/mp/common/enums/YsCallBizTypeEnum.java

@@ -20,6 +20,8 @@ public enum YsCallBizTypeEnum implements IEnum<Integer> {
 
     BANK_CARD_AUTH(3, "银行实名认证三要素"),
 
+    WITHDRAW(4,"提现")
+
     ;
 
 

+ 4 - 0
mp-service/src/main/java/com/qs/mp/channel/domain/ChannelWithdraw.java

@@ -38,6 +38,10 @@ public class ChannelWithdraw implements Serializable {
   @TableField("channel_id")
   private Long channelId;
 
+  @ApiModelProperty("提现交易单号")
+  @TableField("trade_no")
+  private String tradeNo;
+
   /**
    * 提现金额
    */

+ 1 - 1
mp-service/src/main/java/com/qs/mp/channel/service/impl/ChannelWithdrawServiceImpl.java

@@ -107,7 +107,7 @@ public class ChannelWithdrawServiceImpl extends ServiceImpl<ChannelWithdrawMappe
   }
 
   @Override
-  @Transactional
+  @Transactional(rollbackFor = Exception.class)
   public boolean verify(ChannelWithdraw withdraw, ChannelWithdrawStatusEnum status, String verifyContent) {
     if (status == ChannelWithdrawStatusEnum.FINISHED) {
       boolean rtn = update(new LambdaUpdateWrapper<ChannelWithdraw>()

+ 7 - 0
mp-service/src/main/java/com/qs/mp/pay/service/IWalletService.java

@@ -71,6 +71,13 @@ public interface IWalletService {
    */
   boolean refund(String bizId, Integer refundAmount, String remark);
 
+  /**
+   * 银盛提现
+   * @param bizId
+   * @return
+   */
+  boolean withdrawal(String bizId);
+
 
   /**
    *

+ 92 - 0
mp-service/src/main/java/com/qs/mp/pay/service/impl/WalletServiceImpl.java

@@ -10,18 +10,23 @@ import com.alipay.api.response.AlipayTradeCreateResponse;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.eptok.yspay.opensdkjava.common.Constants;
+import com.eptok.yspay.opensdkjava.fund.MercFundApi;
 import com.eptok.yspay.opensdkjava.orderpay.AppPayApi;
+import com.eptok.yspay.opensdkjava.pojo.vo.OnlineReqDataVo;
 import com.eptok.yspay.opensdkjava.util.DateUtil;
 import com.eptok.yspay.opensdkjava.util.HttpClientUtil;
 import com.eptok.yspay.opensdkjava.util.StringUtil;
 import com.eptok.yspay.opensdkjava.util.YsOnlineSignUtils;
+import com.qs.mp.channel.domain.ChannelWithdraw;
 import com.qs.mp.channel.service.IChannelOrderService;
+import com.qs.mp.channel.service.IChannelWithdrawService;
 import com.qs.mp.common.constant.YsPayMethodConstants;
 import com.qs.mp.common.constant.YsServerApiConstants;
 import com.qs.mp.common.domain.YsCallLog;
 import com.qs.mp.common.enums.AppSourceEnum;
 import com.qs.mp.common.enums.AsyncTaskTypeEnum;
 import com.qs.mp.common.enums.BizTypeEnum;
+import com.qs.mp.common.enums.ChannelWithdrawStatusEnum;
 import com.qs.mp.common.enums.MqTopicType;
 import com.qs.mp.common.enums.PayOrderStatusEnum;
 import com.qs.mp.common.enums.YsCallBizTypeEnum;
@@ -55,6 +60,7 @@ import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 
 import java.util.Objects;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * @auther duota
@@ -87,6 +93,9 @@ public class WalletServiceImpl implements IWalletService {
   @Autowired
   private IAsyncTaskService asyncTaskService;
 
+  @Autowired
+  private IChannelWithdrawService channelWithdrawService;
+
   @Autowired
   private IYsCallLogService ysCallLogService;
 
@@ -167,6 +176,9 @@ public class WalletServiceImpl implements IWalletService {
   @Value("${ys-pay.pay-notify-url}")
   private String ysPayNotifyUrl;
 
+  @Value("${ys-pay.withdraw-notify-url}")
+  private String ysWithdrawNotifyUrl;
+
   @Override
   public JSONObject h5Pay(BizTypeEnum bizType, String bizId, int money, String orderRemark) {
     Map<String, String> params = new HashMap<>();
@@ -451,6 +463,86 @@ public class WalletServiceImpl implements IWalletService {
   }
 
 
+  @Override
+  @Transactional(rollbackFor = Exception.class)
+  public boolean withdrawal(String bizId) {
+    ChannelWithdraw channelWithdraw = channelWithdrawService.getById(bizId);
+
+    if (!ChannelWithdrawStatusEnum.WITHDRAWING.equals(channelWithdraw.getStatus())) {
+        throw new ServiceException("提现状态不正确");
+    }
+
+    String tradeNo = DateUtil.getDateNowYmd() + DateUtil.getRandom(14);
+
+      boolean res = channelWithdrawService.update(new LambdaUpdateWrapper<ChannelWithdraw>()
+          .set(ChannelWithdraw::getTradeNo, tradeNo)
+          .set(ChannelWithdraw::getStatus, ChannelWithdrawStatusEnum.WITHDRAW_PROCESS)
+          .eq(ChannelWithdraw::getId, channelWithdraw.getId())
+          .eq(ChannelWithdraw::getStatus, ChannelWithdrawStatusEnum.WITHDRAWING));
+      if (!res) {
+          throw new ServiceException("提现状态更新失败");
+      }
+
+
+    OnlineReqDataVo req = new OnlineReqDataVo();
+    req.setReqUrl(YsServerApiConstants.COMMON_API);
+    req.setPrivateKeyFilePath(privateKeyPath);
+    req.setYsPublicKeyFilePath(publicKeyPath);
+    req.setPrivateKeyPassword(privateKeyPass);
+    req.setNotifyUrl(ysWithdrawNotifyUrl);
+    req.setPartnerId(partnerId);
+
+    //实时提现(一般户到银行卡)业务参数
+    Map<String, Object> bizContent = new HashMap<>();
+    //商户生成的订单号,生成规则前8位必须为交易日期,如20180525,范围跨度支持包含当天在内的前后一天
+    bizContent.put("out_trade_no", tradeNo);
+    //商户号
+    bizContent.put("merchant_usercode", partnerId);
+    //暂时只支持币种:CNY(人民币)
+    bizContent.put("currency", "CNY");
+    //提现的总金额。单位为:RMB Yuan。取值范围为[0.01,99999999.99],精确到小数点后两位。Number(10,2)指10位长度,2位精度
+    bizContent.put("total_amount", new BigDecimal(channelWithdraw.getMoney()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+    //订单说明
+    bizContent.put("subject", "提现备注");
+    //商户日期(该参数做交易与查询时需要一致) 该日期需在当日的前后一天时间范围之内
+    bizContent.put("shopdate", DateUtil.getDateNowYmd());
+    //银行帐号
+    bizContent.put("bank_account_no", channelWithdraw.getCardNo());
+    req.setParamData(bizContent);
+    String result = null;
+    try{
+      logger.info("实时提现(一般户到银行卡)调用sdk接口addScanMerc请求入参为:"+ JSONObject.toJSONString(req));
+      //根据返回结果处理自己的业务逻辑,result内容详见接口文档
+      result = MercFundApi.withdrawQuick(req);
+    }catch (Exception e){
+      logger.info("实时提现(一般户到银行卡)失败:"+ e.getMessage());
+    }
+
+    logger.info("提现提交result = " + result);
+
+    // 保存调用日志
+      YsCallLog ysCallLog = new YsCallLog();
+      ysCallLog.setInterfaceId(YsPayMethodConstants.SETTLED_ACCOUNT_WITHDRAW_METHOD);
+      ysCallLog.setBizId(tradeNo);
+      ysCallLog.setBizType(YsCallBizTypeEnum.WITHDRAW);
+      ysCallLog.setReqJson(JSONObject.toJSONString(bizContent));
+      ysCallLog.setResJson(result);
+      ysCallLogService.save(ysCallLog);
+
+      if (StringUtils.isBlank(result)) {
+            throw new ServiceException("提现交易处理失败");
+    }
+
+    JSONObject response = JSONObject.parseObject(result);
+    String tradeStatus = (String) response.get("trade_status");
+    if (StringUtils.isBlank(tradeStatus) &&
+        (!"TRADE_ACCEPT_SUCCESS".equals(tradeStatus) || !"TRADE_SUCCESS".equals(tradeStatus))) {
+      logger.error("提现交易处理失败,result = " + result);
+        throw new ServiceException("提现交易处理失败");
+    }
+    return true;
+  }
+
   @Override
   public void payOrderStatusHandle(PayOrder payOrder) {
     String orderNo = payOrder.getOrderId();