【金融项目】尚融宝项目(十五)

29、提现和还款

29.1、提现

29.1.1、需求

在这里插入图片描述

放款成功后,借款人可以申请提现。

参考《汇付宝商户账户技术文档》3.15用户申请提现

在这里插入图片描述

29.1.2、前端整合

pages/user/withdraw.vue

<script>
export default {
  data() {
    return {
      fetchAmt: 0,
    }
  },
  methods: {
    commitWithdraw() {
      this.$alert(
        '<div style="size: 18px;color: red;">您即将前往汇付宝提现</div>',
        '前往汇付宝资金托管平台',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '立即前往',
          callback: (action) => {
            if (action === 'confirm') {
              this.$axios
                .$post(
                  '/api/core/userAccount/auth/commitWithdraw/' + this.fetchAmt
                )
                .then((response) => {
                  document.write(response.data.formStr)
                })
            }
          },
        }
      )
    },
  },
}
</script>

29.1.3、提现接口

1、Controller

UserAccountController

@ApiOperation("用户提现")
@PostMapping("/auth/commitWithdraw/{fetchAmt}")
public R commitWithdraw(
    @ApiParam(value = "金额", required = true)
    @PathVariable BigDecimal fetchAmt, HttpServletRequest request) {
    String token = request.getHeader("token");
    Long userId = JwtUtils.getUserId(token);
    String formStr = userAccountService.commitWithdraw(fetchAmt, userId);
    return R.ok().data("formStr", formStr);
}

2、Service

接口:UserAccountService

String commitWithdraw(BigDecimal fetchAmt, Long userId);

实现:UserAccountServiceImpl

@Resource
private UserBindService userBindService;
@Resource
private UserAccountService userAccountService;

@Override
public String commitWithdraw(BigDecimal fetchAmt, Long userId) {
    //账户可用余额充足:当前用户的余额 >= 当前用户的提现金额
    BigDecimal amount = userAccountService.getAccount(userId);//获取当前用户的账户余额
    Assert.isTrue(amount.doubleValue() >= fetchAmt.doubleValue(),
                  ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);
    String bindCode = userBindService.getBindCodeByUserId(userId);
    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("agentId", HfbConst.AGENT_ID);
    paramMap.put("agentBillNo", LendNoUtils.getWithdrawNo());
    paramMap.put("bindCode", bindCode);
    paramMap.put("fetchAmt", fetchAmt);
    paramMap.put("feeAmt", new BigDecimal(0));
    paramMap.put("notifyUrl", HfbConst.WITHDRAW_NOTIFY_URL);
    paramMap.put("returnUrl", HfbConst.WITHDRAW_RETURN_URL);
    paramMap.put("timestamp", RequestHelper.getTimestamp());
    String sign = RequestHelper.getSign(paramMap);
    paramMap.put("sign", sign);
    //构建自动提交表单
    String formStr = FormHelper.buildForm(HfbConst.WITHDRAW_URL, paramMap);
    return formStr;
}

29.1.4、回调接口

1、Controller

UserAccountController

@ApiOperation("用户提现异步回调")
@PostMapping("/notifyWithdraw")
public String notifyWithdraw(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"))) {
            userAccountService.notifyWithdraw(paramMap);
        } else {
            log.info("提现异步回调充值失败:" + JSON.toJSONString(paramMap));
            return "fail";
        }
    } else {
        log.info("提现异步回调签名错误:" + JSON.toJSONString(paramMap));
        return "fail";
    }
    return "success";
}

2、Service

接口:UserAccountService

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

实现:UserAccountServiceImpl

@Transactional(rollbackFor = Exception.class)
@Override
public void notifyWithdraw(Map<String, Object> paramMap) {
    log.info("提现成功");
    String agentBillNo = (String)paramMap.get("agentBillNo");
    boolean result = transFlowService.isSaveTransFlow(agentBillNo);
    if(result){
        log.warn("幂等性返回");
        return;
    }
    String bindCode = (String)paramMap.get("bindCode");
    String fetchAmt = (String)paramMap.get("fetchAmt");
    //根据用户账户修改账户金额
    baseMapper.updateAccount(bindCode, new BigDecimal("-" + fetchAmt), new BigDecimal(0));
    //增加交易流水
    TransFlowBO transFlowBO = new TransFlowBO(
        agentBillNo,
        bindCode,
        new BigDecimal(fetchAmt),
        TransTypeEnum.WITHDRAW,
        "提现");
    transFlowService.saveTransFlow(transFlowBO);
}

29.2、还款

29.2.1、需求

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

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

在这里插入图片描述

29.2.2、前端整合

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)
            })
        }
      },
    }
  )
}

29.2.3、还款接口

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;
}

29.2.4、回调接口

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<LendReturn> lendReturnQueryWrapper = new QueryWrapper<>();
    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(lendItem.getRealAmount().add(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);
    });
}

30、个人中心

30.1、资金记录

30.1.1、资金列表接口

1、Controller

TransFlowController

package com.atguigu.srb.core.controller.api;

@Api(tags = "资金记录")
@RestController
@RequestMapping("/api/core/transFlow")
@Slf4j
public class TransFlowController {
    @Resource
    private TransFlowService transFlowService;
    @ApiOperation("获取列表")
    @GetMapping("/list")
    public R list(HttpServletRequest request) {
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        List<TransFlow> list = transFlowService.selectByUserId(userId);
        return R.ok().data("list", list);
    }
}

2、Service

接口:TransFlowService

List<TransFlow> selectByUserId(Long userId);

实现:TransFlowServiceImpl

@Override
public List<TransFlow> selectByUserId(Long userId) {
    QueryWrapper<TransFlow> queryWrapper = new QueryWrapper<>();
    queryWrapper
        .eq("user_id", userId)
        .orderByDesc("id");
    return baseMapper.selectList(queryWrapper);
}

30.1.2、前端整合

脚本

pages/user/fund.vue

fetchTransFlowList() {
  this.$axios.$get('/api/core/transFlow/list').then((response) => {
    this.transFlowList = response.data.list
  })
},

30.2、个人中心首页

30.2.1、后端接口

1、创建VO

package com.atguigu.srb.core.pojo.vo;

@Data
@ApiModel(description = "首页用户信息")
public class UserIndexVO {
    @ApiModelProperty(value = "用户id")
    private Long userId;
    @ApiModelProperty(value = "用户姓名")
    private String name;
    @ApiModelProperty(value = "用户昵称")
    private String nickName;
    @ApiModelProperty(value = "1:出借人 2:借款人")
    private Integer userType;
    @ApiModelProperty(value = "用户头像")
    private String headImg;
    @ApiModelProperty(value = "绑定状态(0:未绑定,1:绑定成功 -1:绑定失败)")
    private Integer bindStatus;
    @ApiModelProperty(value = "帐户可用余额")
    private BigDecimal amount;
    @ApiModelProperty(value = "冻结金额")
    private BigDecimal freezeAmount;
    @ApiModelProperty(value = "上次登录时间")
    private LocalDateTime lastLoginTime;
}

2、Controller

UserInfoController

@ApiOperation("获取个人空间用户信息")
@GetMapping("/auth/getIndexUserInfo")
public R getIndexUserInfo(HttpServletRequest request) {
    String token = request.getHeader("token");
    Long userId = JwtUtils.getUserId(token);
    UserIndexVO userIndexVO = userInfoService.getIndexUserInfo(userId);
    return R.ok().data("userIndexVO", userIndexVO);
}

3、Service

接口:UserInfoService

UserIndexVO getIndexUserInfo(Long userId);

实现:UserInfoServiceImpl

@Override
public UserIndexVO getIndexUserInfo(Long userId) {
    
    //用户信息
    UserInfo userInfo = baseMapper.selectById(userId);
    //账户信息
    QueryWrapper<UserAccount> userAccountQueryWrapper = new QueryWrapper<>();
    userAccountQueryWrapper.eq("user_id", userId);
    UserAccount userAccount = userAccountMapper.selectOne(userAccountQueryWrapper);
    //登录信息
    QueryWrapper<UserLoginRecord> userLoginRecordQueryWrapper = new QueryWrapper<>();
    userLoginRecordQueryWrapper
        .eq("user_id", userId)
        .orderByDesc("id")
        .last("limit 1");
    UserLoginRecord userLoginRecord = userLoginRecordMapper.selectOne(userLoginRecordQueryWrapper);
    //组装结果数据
    UserIndexVO userIndexVO = new UserIndexVO();
    userIndexVO.setUserId(userInfo.getId());
    userIndexVO.setUserType(userInfo.getUserType());
    userIndexVO.setName(userInfo.getName());
    userIndexVO.setNickName(userInfo.getNickName());
    userIndexVO.setHeadImg(userInfo.getHeadImg());
    userIndexVO.setBindStatus(userInfo.getBindStatus());
    userIndexVO.setAmount(userAccount.getAmount());
    userIndexVO.setFreezeAmount(userAccount.getFreezeAmount());
    userIndexVO.setLastLoginTime(userLoginRecord.getCreateTime());
    return userIndexVO;
}

30.2.2.、前端整合

脚本

pages/user/index.vue

fetchUserData() {
  this.$axios
    .$get('/api/core/userInfo/auth/getIndexUserInfo')
    .then((response) => {
      this.userIndexVO = response.data.userIndexVO
    })
},

本文章参考B站 尚硅谷《尚融宝》Java微服务分布式金融项目,仅供个人学习使用,部分内容为本人自己见解,与尚硅谷无关。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值