当前位置: 首页 > news >正文

微服务项目:尚融宝(59)(核心业务流程:提现和还款(2))

认清现实,放弃幻想,准备斗争

需求

放款成功后,会生成借款人的还款计划与出借人的回款计划,然后借款人按照还款计划日期操作还款即可。

参考《汇付宝商户账户技术文档》3.14还款扣款,处理业务即可

 

一、前端整合

1、还款按钮

pages/lend/_id.vue

<td>
    <a href="javascript:" @click="commitReturn(lendReturn.id)">
        {{ lendReturn.status === 0 ? '还款' : '' }}
    </a>
</td>

2、脚本

pages/lend/_id.vue

commitReturn(lendReturnId) {
  this.$alert(
    '<div style="size: 18px;color: red;">您即将前往汇付宝确认还款</div>',
    '前往汇付宝资金托管平台',
    {
      dangerouslyUseHTMLString: true,
      confirmButtonText: '立即前往',
      callback: (action) => {
        if (action === 'confirm') {
          this.$axios
            .$post('/api/core/lendReturn/auth/commitReturn/' + lendReturnId)
            .then((response) => {
              document.write(response.data.formStr)
            })
        }
      },
    }
  )

二、还款接口

1、Controller

LendReturnController

@ApiOperation("用户还款")
@PostMapping("/auth/commitReturn/{lendReturnId}")
public R commitReturn(
    @ApiParam(value = "还款计划id", required = true)
    @PathVariable Long lendReturnId, HttpServletRequest request) {

    String token = request.getHeader("token");
    Long userId = JwtUtils.getUserId(token);
    String formStr = lendReturnService.commitReturn(lendReturnId, userId);
    return R.ok().data("formStr", formStr);
}

2、Service

接口:LendReturnService

String commitReturn(Long lendReturnId, Long userId);

实现:LendReturnServiceImpl 

@Resource
private UserAccountService userAccountService;

@Resource
private LendMapper lendMapper;

@Resource
private UserBindService userBindService;

@Resource
private LendItemReturnService lendItemReturnService;

@Transactional(rollbackFor = Exception.class)
@Override
public String commitReturn(Long lendReturnId, Long userId) {

    //获取还款记录
    LendReturn lendReturn = baseMapper.selectById(lendReturnId);

    //判断账号余额是否充足
    BigDecimal amount = userAccountService.getAccount(userId);
    Assert.isTrue(amount.doubleValue() >= lendReturn.getTotal().doubleValue(),
                  ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);

    //获取借款人code
    String bindCode = userBindService.getBindCodeByUserId(userId);
    //获取lend
    Long lendId = lendReturn.getLendId();
    Lend lend = lendMapper.selectById(lendId);

    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("agentId", HfbConst.AGENT_ID);
    //商户商品名称
    paramMap.put("agentGoodsName", lend.getTitle());
    //批次号
    paramMap.put("agentBatchNo",lendReturn.getReturnNo());
    //还款人绑定协议号
    paramMap.put("fromBindCode", bindCode);
    //还款总额
    paramMap.put("totalAmt", lendReturn.getTotal());
    paramMap.put("note", "");
    //还款明细
    List<Map<String, Object>> lendItemReturnDetailList = lendItemReturnService.addReturnDetail(lendReturnId);
    paramMap.put("data", JSONObject.toJSONString(lendItemReturnDetailList));
    paramMap.put("voteFeeAmt", new BigDecimal(0));
    paramMap.put("notifyUrl", HfbConst.BORROW_RETURN_NOTIFY_URL);
    paramMap.put("returnUrl", HfbConst.BORROW_RETURN_RETURN_URL);
    paramMap.put("timestamp", RequestHelper.getTimestamp());
    String sign = RequestHelper.getSign(paramMap);
    paramMap.put("sign", sign);

    //构建自动提交表单
    String formStr = FormHelper.buildForm(HfbConst.BORROW_RETURN_URL, paramMap);
    return formStr;
}

3、还款明细Service

根据还款id获取回款列表

LendReturnService接口:

List<Map<String, Object>> addReturnDetail(Long lendReturnId);

LendReturnServiceImpl实现: 

@Resource
private UserBindService userBindService;

@Resource
private LendItemMapper lendItemMapper;

@Resource
private LendMapper lendMapper;

@Resource
private LendReturnMapper lendReturnMapper;

/**
* 添加还款明细
* @param lendReturnId
*/
@Override
public List<Map<String, Object>> addReturnDetail(Long lendReturnId) {

    //获取还款记录
    LendReturn lendReturn = lendReturnMapper.selectById(lendReturnId);
    //获取标的信息
    Lend lend = lendMapper.selectById(lendReturn.getLendId());

    //根据还款id获取回款列表
    List<LendItemReturn> lendItemReturnList = this.selectLendItemReturnList(lendReturnId);
    List<Map<String, Object>> lendItemReturnDetailList = new ArrayList<>();
    for(LendItemReturn lendItemReturn : lendItemReturnList) {
        LendItem lendItem = lendItemMapper.selectById(lendItemReturn.getLendItemId());
        String bindCode = userBindService.getBindCodeByUserId(lendItem.getInvestUserId());

        Map<String, Object> map = new HashMap<>();
        //项目编号
        map.put("agentProjectCode", lend.getLendNo());
        //出借编号
        map.put("voteBillNo", lendItem.getLendItemNo());
        //收款人(出借人)
        map.put("toBindCode", bindCode);
        //还款金额
        map.put("transitAmt", lendItemReturn.getTotal());
        //还款本金
        map.put("baseAmt", lendItemReturn.getPrincipal());
        //还款利息
        map.put("benifitAmt", lendItemReturn.getInterest());
        //商户手续费
        map.put("feeAmt", new BigDecimal("0"));

        lendItemReturnDetailList.add(map);
    }
    return lendItemReturnDetailList;
}

根据还款计划id获取对应的回款计划列表

接口:

List<LendItemReturn> selectLendItemReturnList(Long lendReturnId);

实现: 

@Override
public List<LendItemReturn> selectLendItemReturnList(Long lendReturnId) {
    QueryWrapper<LendItemReturn> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("lend_return_id", lendReturnId);
    List<LendItemReturn> lendItemReturnList = baseMapper.selectList(queryWrapper);
    return lendItemReturnList;
}

三、回调接口

1、Controller

LendReturnController

@ApiOperation("还款异步回调")
@PostMapping("/notifyUrl")
public String notifyUrl(HttpServletRequest request) {

    Map<String, Object> paramMap = RequestHelper.switchMap(request.getParameterMap());
    log.info("还款异步回调:" + JSON.toJSONString(paramMap));

    //校验签名
    if(RequestHelper.isSignEquals(paramMap)) {
        if("0001".equals(paramMap.get("resultCode"))) {
            lendReturnService.notify(paramMap);
        } else {
            log.info("还款异步回调失败:" + JSON.toJSONString(paramMap));
            return "fail";
        }
    } else {
        log.info("还款异步回调签名错误:" + JSON.toJSONString(paramMap));
        return "fail";
    }
    return "success";
}

2、Service

接口:LendReturnService

void notify(Map<String, Object> paramMap);

实现:LendReturnServiceImpl 

@Resource
private TransFlowService transFlowService;

@Resource
private UserAccountMapper userAccountMapper;

@Resource
private LendItemReturnMapper lendItemReturnMapper;

@Resource
private LendItemMapper lendItemMapper;

@Transactional(rollbackFor = Exception.class)
@Override
public void notify(Map<String, Object> paramMap) {

    log.info("还款成功");

    //还款编号
    String agentBatchNo = (String)paramMap.get("agentBatchNo");

    boolean result = transFlowService.isSaveTransFlow(agentBatchNo);
    if(result){
        log.warn("幂等性返回");
        return;
    }

    //获取还款数据
    String voteFeeAmt = (String)paramMap.get("voteFeeAmt");
    QueryWrapper lendReturnQueryWrapper = new QueryWrapper<LendReturn>();
    lendReturnQueryWrapper.eq("return_no", agentBatchNo);
    LendReturn lendReturn = baseMapper.selectOne(lendReturnQueryWrapper);;

    //更新还款状态
    lendReturn.setStatus(1);
    lendReturn.setFee(new BigDecimal(voteFeeAmt));
    lendReturn.setRealReturnTime(LocalDateTime.now());
    baseMapper.updateById(lendReturn);

    //更新标的信息
    Lend lend = lendMapper.selectById(lendReturn.getLendId());
    //最后一次还款更新标的状态
    if(lendReturn.getLast()) {
        lend.setStatus(LendStatusEnum.PAY_OK.getStatus());
        lendMapper.updateById(lend);
    }

    //借款账号转出金额
    BigDecimal totalAmt = new BigDecimal((String)paramMap.get("totalAmt"));//还款金额
    String bindCode = userBindService.getBindCodeByUserId(lend.getUserId());
    userAccountMapper.updateAccount(bindCode, totalAmt.negate(), new BigDecimal(0));

    //借款人交易流水
    TransFlowBO transFlowBO = new TransFlowBO(
        agentBatchNo,
        bindCode,
        totalAmt,
        TransTypeEnum.RETURN_DOWN,
        "借款人还款扣减,项目编号:" + lend.getLendNo() + ",项目名称:" + lend.getTitle());
    transFlowService.saveTransFlow(transFlowBO);

    //获取回款明细
    List<LendItemReturn> lendItemReturnList = lendItemReturnService.selectLendItemReturnList(lendReturn.getId());
    lendItemReturnList.forEach(item -> {
        //更新回款状态
        item.setStatus(1);
        item.setRealReturnTime(LocalDateTime.now());
        lendItemReturnMapper.updateById(item);

        //更新出借信息
        LendItem lendItem = lendItemMapper.selectById(item.getLendItemId());
        lendItem.setRealAmount(item.getInterest());
        lendItemMapper.updateById(lendItem);

        //投资账号转入金额
        String investBindCode = userBindService.getBindCodeByUserId(item.getInvestUserId());
        userAccountMapper.updateAccount(investBindCode, item.getTotal(), new BigDecimal(0));

        //投资账号交易流水
        TransFlowBO investTransFlowBO = new TransFlowBO(
            LendNoUtils.getReturnItemNo(),
            investBindCode,
            item.getTotal(),
            TransTypeEnum.INVEST_BACK,
            "还款到账,项目编号:" + lend.getLendNo() + ",项目名称:" + lend.getTitle());
        transFlowService.saveTransFlow(investTransFlowBO);

    });
}

 上篇链接:

微服务项目:尚融宝(58)(核心业务流程:提现和还款(1))_一个风轻云淡的博客-CSDN博客icon-default.png?t=M85Bhttps://blog.csdn.net/m0_62436868/article/details/127036047?spm=1001.2014.3001.5501

相关文章:

  • jetson nano补充:根目录/usr刷机扩容 瘦身
  • Java工程师面试题
  • 网课查题接口使用
  • 算法练习(堆/栈/队列)
  • 大数据-ClickHouse技术一(安装部署)
  • 【Android入门】4、数据持久化:文件、SharedPreferences 和 Sqlite
  • style样式优先级问题【display:block依旧无法显示DOM元素】
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • 面试宝典------经典
  • node.js环境搭建
  • 【5G核心网】手把手教你将Open5gs托管到k8s(KubeSphere)
  • 空城机在CSDN的四周年创作纪念日
  • C++ Reference: Standard C++ Library reference: C Library: clocale: struct lconv
  • JavaSE进阶--集合(2万字总结)
  • CKA考题 [k8s1.21]
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 「面试题」如何实现一个圣杯布局?
  • 【node学习】协程
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • cookie和session
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • Node项目之评分系统(二)- 数据库设计
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Redis学习笔记 - pipline(流水线、管道)
  • SpingCloudBus整合RabbitMQ
  • 对JS继承的一点思考
  • 飞驰在Mesos的涡轮引擎上
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 深度学习中的信息论知识详解
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (06)金属布线——为半导体注入生命的连接
  • (11)MSP430F5529 定时器B
  • (4) PIVOT 和 UPIVOT 的使用
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (南京观海微电子)——COF介绍
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)80c52学习之旅-起始篇
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • @RequestMapping 的作用是什么?
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [C#]C# winform实现imagecaption图像生成描述图文描述生成
  • [C++] sqlite3_get_table 的使用
  • [CodeForces-759D]Bacterial Melee
  • [EFI]Dell Inspiron 15 5567 电脑 Hackintosh 黑苹果efi引导文件
  • [EFI]Dell Latitude-7400电脑 Hackintosh 黑苹果efi引导文件
  • [hive] 窗口函数 ROW_NUMBER()
  • [JS] node.js 入门
  • [LeetCode]—Permutations 求全排列
  • [MT8766][Android12] 增加应用安装白名单或者黑名单