核心业务9:还款业务实现和首页信息展示的完善

核心业务9:还款业务实现

1.还款业务前端

2.还款业务流程图

3.汇付宝接口调用

4.尚融宝接口调用

5.首页信息展示的完善

6.资金记录展示

核心业务9:还款业务实现

1.还款业务前端

  • 按钮
    pages/lend/_id.vue
<td>
    <a href="javascript:" @click="commitReturn(lendReturn.id)">
        {{ lendReturn.status === 0 ? '还款' : '' }}
    </a>
</td>
  • 页面脚本
    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)
            })
        }
      },
    }
  )
}

2.还款业务流程图

①还款业务流程图

在这里插入图片描述

3.汇付宝接口调用

①提交表单

在这里插入图片描述

②返回数据

在这里插入图片描述

③汇付宝数据库

  • user_item_return
    在这里插入图片描述

  • user_return
    在这里插入图片描述

  • 用户提交表单后
    对对应借款人账户和投资人账户进行扣钱和加钱。
    生成还款流水和借款流水。

4.尚融宝接口调用

①接口

在这里插入图片描述

②业务代码

  • controller
    com/atguigu/srb/core/controller/api/LendReturnController.java
@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);
    }

    @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";
    }
  • service
    com/atguigu/srb/core/service/LendReturnService.java
    String commitReturn(Long lendReturnId, Long userId);

    void notify(Map<String, Object> paramMap);
  /**
     * @param lendReturnId:
     * @param userId:
     * @return String
     * @author Likejin
     * @description 还款操作
     * @date 2023/4/19 15:51
     */

    @Override
    public String commitReturn(Long lendReturnId, Long userId) {


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

        //校验账户余额是否够
        BigDecimal account = userAccountService.getAccount(userId);
        Assert.isTrue(account.doubleValue()>=lendReturn.getTotal().doubleValue(), ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);
        //标的记录
        Lend lend = lendMapper.selectById(lendReturn.getLendId());

        String bindCode = userBindService.getBindCodeByUserId(userId);

        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);
        //封装为json对象
        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;

    }

    /**
     * @param paramMap:
     * @return void
     * @author Likejin
     * @description 还款回调
     * @date 2023/4/19 16:25
     */
    @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;
        }
        //获取还款记录
        QueryWrapper<LendReturn> lendReturnQueryWrapper = new QueryWrapper<>();
        lendReturnQueryWrapper.eq("return_no",agentBatchNo);
        LendReturn lendReturn = baseMapper.selectOne(lendReturnQueryWrapper);
        //更新还款状态
        lendReturn.setStatus(1);
        String voteFeeAmt = (String)paramMap.get("voteFeeAmt");
        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(lendReturn.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);
            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);
        });

        // }



    }
  • 其他service
    com/atguigu/srb/core/service/impl/LendItemReturnServiceImpl.java

	List<Map<String,Object>> addReturnDetail(Long lendReturnId);
    /**
     * @param lendReturnId:
     * @return List<LendItemReturn>
     * @author Likejin
     * @description 根据还款记录的id查询对应的回款记录
     * @date 2023/4/19 16:07
     */

    List<LendItemReturn> selectLendItemReturnList(long lendReturnId);
 /**
     * @param lendReturnId:
     * @return List<Map<String,Object>>
     * @author Likejin
     * @description 组装汇付宝需要的还款对象(回款)
     * @date 2023/4/19 16:03
     */

    @Override
    public List<Map<String, Object>> addReturnDetail(Long lendReturnId) {

        //通过还款id找到对应的回款计划数据,组装data参数需要的listmap

        //获取标的
        //获取还款记录
        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){
            //获取投资记录
            Long lendItemId = lendItemReturn.getLendItemId();
            LendItem lendItem = lendItemMapper.selectById(lendItemId);
            //获取投资人id
            Long investUserId = lendItem.getInvestUserId();
            String bindCode = userBindService.getBindCodeByUserId(investUserId);
            HashMap<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;
    }

    /**
     * @param lendReturnId:
     * @return List<LendItemReturn>
     * @author Likejin
     * @description 根据还款id查询对应的回款列表
     * @date 2023/4/19 16:08
     */
    @Override
    public List<LendItemReturn> selectLendItemReturnList(long lendReturnId) {
        QueryWrapper<LendItemReturn> lendItemReturnQueryWrapper = new QueryWrapper<>();
        lendItemReturnQueryWrapper.eq("lend_return_id",lendReturnId);
        return baseMapper.selectList(lendItemReturnQueryWrapper);
    }

③接口逻辑1 组装表单

  • 前端传来还款计划的id lend_return表的id
  • 通过校验token获取user_id
  • 组装表单
    • 校验账户余额,根据user_id和lend_return的还款金额校验
    • 组装表单中其他参数
    • 组装表单中需要的data(还款对应的所有回款记录)

④接口逻辑20 异步回调

  • 校验签名,判断结果是否正确
  • 幂等性返回检验
  • 更新当前还款记录 状态为1
  • 如果是最后一次还款则更新标的信息
  • 借款账号转出金额
  • 借款流水保存
  • 更新所有对应的回款信息
    • 更新回款状态
    • 更新借款信息的实际收益(lend_item),累加
    • 更新回款对应的账号的金额
    • 回款金额流水

5.首页信息展示的完善

①前端

srb-site\pages\user\index.vue

<template>
  <div class="personal-main">
    <div class="pmain-profile">
      <div class="pmain-welcome">
        <span class="fr">上次登录时间: {{ userIndexVO.lastLoginTime }} </span>
      </div>
      <div class="pmain-user">
        <div class="user-head">
          <span class="head-img">
            <span>
              <img
                :src="userIndexVO.headImg"
                style="width:88px;height:88px;z-index:0;"
              />
              <i class="headframe" style="z-index:0;"></i>
            </span>
          </span>
        </div>
        <div class="user-info">
          <ul>
            <li>
              用户名<span>{{ userIndexVO.name }}</span>
              <NuxtLink
                v-if="
                  userIndexVO.userType === 2 && userIndexVO.bindStatus === 1
                "
                to="/user/borrower"
              >
                立即借款
              </NuxtLink>
            </li>
            <li v-if="userIndexVO.bindStatus !== 1">
              您还未开通第三方支付账户,请
              <NuxtLink to="/user/bind">立即开通</NuxtLink>
              以确保您的正常使用和资金安全。
            </li>
          </ul>
        </div>
      </div>

      <div v-if="userIndexVO.bindStatus === 1" class="pmain-money">
        <ul>
          <li class="none">
            <span>
              <em>账户余额</em>
              <i class="markicon"></i>
            </span>
            <span class="truemoney">
              <i class="f26 fb">{{ userIndexVO.amount }}</i></span>
          </li>
          <li>
            <span>
              <em>冻结金额</em>
              <i class="markicon"></i>
            </span>
            <span class="truemoney">
              <i class="f26 fb">{{ userIndexVO.freezeAmount }}</i
              ></span>
          </li>
          <li>
            <NuxtLink to="/user/recharge">
              <el-button size="mini" type="danger">充值</el-button>
            </NuxtLink>
            <NuxtLink to="/user/withdraw">
              <el-button size="mini" type="success">提现</el-button>
            </NuxtLink>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userIndexVO: {},
    }
  },

  created() {
    this.fetchUserData()
  },

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

②后端

  • vo
    com/atguigu/srb/core/pojo/vo/UserIndexVO.java
package com.atguigu.srb.core.pojo.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

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

}
  • controller
    com/atguigu/srb/core/controller/api/UserInfoController.java
    @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);
    }
  • service
 UserIndexVO getIndexUserInfo(Long userId);
    /**
     * @param userId:
     * @return UserIndexVO
     * @author Likejin
     * @description 返回页面的展示结果
     * @date 2023/4/19 17:38
     */

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

6.资金记录展示

①前端

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

②后端

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

import com.atguigu.common.result.R;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.service.TransFlowService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

@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);
    }
}
  • service
    com/atguigu/srb/core/service/TransFlowService.java
List<TransFlow> selectByUserId(Long userId);
    /**
     * @param userId:
     * @return List<TransFlow>
     * @author Likejin
     * @description 获取流水列表
     * @date 2023/4/19 17:36
     */

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

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值