36. 尚融宝放款

2023-11-22 11:10
文章标签 36 放款 尚融宝

本文主要是介绍36. 尚融宝放款,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

后台放款

需求

管理员在后台对标的进行放款

在这里插入图片描述
在这里插入图片描述

相关数据库表

还款人还款记录:lend_return

投资人回款记录:lend_item_return

参考文档

参考《汇付宝商户账户技术文档》3.11满标放款

后端

controller

AdminLendController.java

    @ApiOperation("放款")@GetMapping("/makeLoan/{id}")public R makeLoan(@ApiParam(value = "标的id",required = true)@PathVariable Long id){lendService.makeLoan(id);return R.ok().setMessage("放款成功");}

service

LendService.java

    void makeLoan(Long id);

LendServiceImpl.java

    @Resourceprivate UserAccountMapper userAccountMapper;@Resourceprivate UserBindService userBindService;@Resourceprivate TransFlowService transFlowService;@Resourceprivate LendItemService lendItemService;@Override@Transactional(rollbackFor = Exception.class)public void makeLoan(Long id) {Lend lend = baseMapper.selectById(id);String agentBillNo = LendNoUtils.getLoanNo();Map<String, Object> map = new HashMap<>();map.put("agentId", HfbConst.AGENT_ID);map.put("agentProjectCode", lend.getLendNo());map.put("agentBillNo", agentBillNo);BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);// 平台实际收益:已投金额 * 平台月化率 * 投资时长BigDecimal realAmount = lend.getInvestAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));map.put("mchFee", realAmount);    map.put("timestamp", RequestHelper.getTimestamp());map.put("sign", RequestHelper.getSign(map));// 放款是一个同步接口,如果放款返回成功,那么我们要处理平台业务JSONObject result = RequestHelper.sendRequest(map, HfbConst.MAKE_LOAN_URL);BigDecimal voteAmt = result.getBigDecimal("voteAmt");   // 投资金额if (!"0000".equals(result.get("resultCode"))) {throw new BusinessException(result.getString("resultMsg"));}// 1、更新标的:平台实际收益、状态、支付时间lend.setRealAmount(realAmount);lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());lend.setPaymentTime(LocalDateTime.now());baseMapper.updateById(lend);// 2、更新借款人账户String benefitBindCode = userBindService.getBindCodeByUserId(lend.getUserId());userAccountMapper.updateAccount(benefitBindCode, voteAmt, new BigDecimal(0));// 3、添加交易流水TransFlowBO transFlowBO = new TransFlowBO(agentBillNo,benefitBindCode,TransTypeEnum.BORROW_BACK,voteAmt,"放款到账,项目编号:" + lend.getLendNo());transFlowService.saveTransFlow(transFlowBO);// 4、解冻并扣除投资人资金List<LendItem> lendItemList = lendItemService.listByIdAndStatus(lend.getId(), LendItemEnum.PAY_OK.getStatus());lendItemList.forEach(lendItem -> {String voteBindCode = userBindService.getBindCodeByUserId(lendItem.getInvestUserId());userAccountMapper.updateAccount(voteBindCode, new BigDecimal(0), lendItem.getInvestAmount().negate());// 5、增加投资人交易流水TransFlowBO investTransFlow = new TransFlowBO(LendNoUtils.getLendNo(),voteBindCode,TransTypeEnum.INVEST_UNLOCK,lendItem.getInvestAmount(),"冻结资金转出放款,项目编号:" + lend.getLendNo() + "项目名称:" + lend.getTitle());transFlowService.saveTransFlow(investTransFlow);});// 6、生成借款人还款计划、投资人回款计划repaymentPlan(lend);}       
    @Resourceprivate LendReturnService lendReturnService;/*** 生成借款人还款计划	*      * @param lend 标的*/private void repaymentPlan(Lend lend) {// 一、创建还款计划列表List<LendReturn> lendReturnList = new ArrayList<>();// 根据标的还款期限(period)生成还款计划// 你借多少期,我就给你生成多少条还款计划int period = lend.getPeriod().intValue();// 遍历还款期限,从第1期开始for (int i = 1; i <= period; i++) {// 创建还款计划LendReturn lendReturn = new LendReturn();// 填充属性lendReturn.setLendId(lend.getId());lendReturn.setBorrowInfoId(lend.getBorrowInfoId());lendReturn.setReturnNo(LendNoUtils.getReturnNo());lendReturn.setUserId(lend.getUserId());lendReturn.setAmount(lend.getAmount());lendReturn.setBaseAmount(lend.getInvestAmount());// 计算利息所需要用到的本金:根据已投金额,得出利息lendReturn.setCurrentPeriod(i);lendReturn.setLendYearRate(lend.getLendYearRate());lendReturn.setReturnMethod(lend.getReturnMethod());lendReturn.setFee(BigDecimal.ZERO);lendReturn.setReturnDate(lend.getLendStartDate().plusMonths(i)); // 开始日期往后i个月开始还款lendReturn.setOverdue(false);   // 不逾期lendReturn.setLast(i == period);    // 是否为最后一期lendReturn.setStatus(0);    // 设置还款状态lendReturnList.add(lendReturn); // 将还款计划加入还款计划列表}// 批量保存还款计划lendReturnService.saveBatch(lendReturnList);// 将还款计划列表的还款期数、还款计划id提取出来,转换成一个还款计划mapMap<Integer, Long> lendReturnMap = lendReturnList.stream().collect(Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId));// ------------------------------------------------------------------// ---------------------------获取回款计划----------------------------// ------------------------------------------------------------------// 创建当前标的下所有投资人的回款计划列表List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();// 获取当前标的下所有已支付的投资记录List<LendItem> lendItemList = lendItemService.listByIdAndStatus(lend.getId(), LendItemEnum.PAY_OK.getStatus());// 遍历投资记录for (LendItem lendItem : lendItemList) {// 三、根据投资记录、还款计划map、标的,当前投资记录的所有回款计划List<LendItemReturn> lendItemReturnList = this.returnInvest(lendItem, lendReturnMap, lend);// 将每个投资记录的回款计划添加至回款计划列表中lendItemReturnAllList.addAll(lendItemReturnList);}// 四、遍历还款计划列表for (LendReturn lendReturn : lendReturnList) {// 计算总利息、总本金、总本息// 把回款计划中同一期所有的利息/本金/本息加起来// 计算出总利息、总本金、总本息BigDecimal principal = lendItemReturnAllList.stream().filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue()).map(LendItemReturn::getPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);BigDecimal interest = lendItemReturnAllList.stream().filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue()).map(LendItemReturn::getInterest).reduce(BigDecimal.ZERO, BigDecimal::add);BigDecimal total = lendItemReturnAllList.stream().filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue()).map(LendItemReturn::getTotal).reduce(BigDecimal.ZERO, BigDecimal::add);lendReturn.setPrincipal(principal);lendReturn.setInterest(interest);lendReturn.setTotal(total);}// 批量更新lendReturnService.updateBatchById(lendReturnList);}
    @Resourceprivate LendItemReturnService lendItemReturnService;/*** 投资人还款计划** @param lend* @param lendReturnMap* @param lendItem* @return*/private List<LendItemReturn> returnInvest(LendItem lendItem, Map<Integer, Long> lendReturnMap, Lend lend) {// 投资金额BigDecimal investAmount = lendItem.getInvestAmount();// 年化利率BigDecimal lendYearRate = lendItem.getLendYearRate();// 投资期数Integer period = lend.getPeriod();// 创建还款期数、利息的mapMap<Integer, BigDecimal> mapInterest;// 创建还款期数、本金的mapMap<Integer, BigDecimal> mapPrincipal;// 二、根据还款方式计算本金和利息switch (ReturnMethodEnum.getByValue(lend.getReturnMethod().intValue())) {case ONE:mapInterest = Amount1Helper.getPerMonthInterest(investAmount, lendYearRate, period);mapPrincipal = Amount1Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);break;case TWO:mapInterest = Amount2Helper.getPerMonthInterest(investAmount, lendYearRate, period);mapPrincipal = Amount2Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);break;case THREE:mapInterest = Amount3Helper.getPerMonthInterest(investAmount, lendYearRate, period);mapPrincipal = Amount3Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);break;default:mapInterest = Amount4Helper.getPerMonthInterest(investAmount, lendYearRate, period);mapPrincipal = Amount4Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);break;}// 创建回款计划列表List<LendItemReturn> lendItemReturnList = new ArrayList<>();// 遍历还款期数、利息mapfor (Entry<Integer, BigDecimal> entry : mapInterest.entrySet()) {Integer currentPeriod = entry.getKey();    // 当前期数// 通过当前期数获取到还款计划的idLong lendReturnId = lendReturnMap.get(currentPeriod);// 创建回款计划LendItemReturn lendItemReturn = new LendItemReturn();// 填充数据lendItemReturn.setLendReturnId(lendReturnId);lendItemReturn.setLendItemId(lendItem.getId());lendItemReturn.setLendId(lendItem.getLendId());lendItemReturn.setInvestUserId(lendItem.getInvestUserId());lendItemReturn.setInvestAmount(lendItem.getInvestAmount());lendItemReturn.setCurrentPeriod(currentPeriod);lendItemReturn.setLendYearRate(lend.getLendYearRate());lendItemReturn.setReturnMethod(lend.getReturnMethod());// filter做过滤 map取属性 reduce做处理if (currentPeriod.intValue() == lend.getPeriod().intValue()) {// 最后一期// 前几期要还的本金之和BigDecimal sumPrincipal = lendItemReturnList.stream().map(LendItemReturn::getPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);// 最后一次还款本金 = 投资人的总投资 - 前几次还款金额之和// 因为数据库只保留两位,所以此处需要四舍五入进位// 否则会导致还款计划精度不一致的问题BigDecimal lastPrincipal = lendItem.getInvestAmount().subtract(sumPrincipal);lendItemReturn.setPrincipal(lastPrincipal.setScale(2, BigDecimal.ROUND_HALF_UP));// 前几期要还的利息之和BigDecimal sumInterest = lendItemReturnList.stream().map(LendItemReturn::getInterest).reduce(BigDecimal.ZERO, BigDecimal::add);// 最后一次还款利息 = 投资人的预期收益 - 前几次还款利息之和BigDecimal lastInterest = lendItem.getExpectAmount().subtract(sumInterest);lendItemReturn.setInterest(lastInterest.setScale(2, BigDecimal.ROUND_HALF_UP));} else {// 非最后一期,直接从map中获取对应当前期数的本金/利息lendItemReturn.setPrincipal(mapPrincipal.get(currentPeriod).setScale(2, BigDecimal.ROUND_HALF_UP));lendItemReturn.setInterest(mapInterest.get(currentPeriod).setScale(2, BigDecimal.ROUND_HALF_UP));}// 回款总金额lendItemReturn.setTotal(lendItemReturn.getPrincipal().add(lendItemReturn.getInterest()));// 设置其他属性lendItemReturn.setFee(BigDecimal.ZERO);lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(currentPeriod));lendItemReturn.setOverdue(false);lendItemReturn.setStatus(0);// 添加到回款计划列表中lendItemReturnList.add(lendItemReturn);}// 批量保存lendItemReturnService.saveBatch(lendItemReturnList);// 返回回款计划列表return lendItemReturnList;}

LendItemService

LendItemService.java

    // 根据标的id和状态获取投资人列表List<LendItem> listByIdAndStatus(Long lendId, Integer status)

LendItemServiceImpl.java

    @Overridepublic List<LendItem> listByIdAndStatus(Long lendId, Integer status) {QueryWrapper<LendItem> queryWrapper = new QueryWrapper<>();queryWrapper.eq("lend_id",lendId).eq("status",status);return baseMapper.selectList(queryWrapper);}

前端

api

创建 src/api/core/lend.js

  makeLoan(id) {return request({url: `/admin/core/lend/makeLoan/${id}`,method: 'get'})}

script

src/views/core/lend/list.vue

methods:

    makeLoan(id) {this.$confirm('确定放款吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {return lendApi.makeLoan(id)}).then(response => {//放款成功则重新获取数据列表this.fetchData()this.$message({type: 'success',message: response.message})}).catch(error => {console.log('取消', error)if (error === 'cancel') {this.$message({type: 'info',message: '已取消放款'})}})}

测试各个表的变化

还款方式为等额本息,借3个月,标的金额为95000,实际募集2000,有两个人分别投资了1000元

测试时,注意多个表之间的数据关系,尤其是汇付宝的数据,一旦测试失败,需要重新投资的话,需要清空user_invest表。

尚融宝

lend

标的表
在这里插入图片描述

user_account

账户表
在这里插入图片描述

trans_flow

流水表
在这里插入图片描述
lend_return

还款计划表
在这里插入图片描述
在这里插入图片描述

lend_item_return

回款计划表
在这里插入图片描述
在这里插入图片描述

汇付宝

user_account

账户表
在这里插入图片描述

这篇关于36. 尚融宝放款的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/409721

相关文章

关于大模型和AIGC的36条笔记和真话

行业到底有多卷? 最新统计,中国已有130多个大模型问世,在网信办备案的算法模型也超过70多家。BAT等互联网巨头悉数下场发布AI大模型,仅2023年就有超60家创业公司拿到融资,产品更是布满了基础层、模型层和应用层。新一代生成式AI,可能要回头看看上一代AI趟过的坑,不要行业自嗨,避免上一个冬天的轮回。在这个领域的从业者,更要清晰地看到行业的内卷和客户的痛点,别被大佬的鸡汤迷了眼。 1、

itoa()函数,10进制转换到(2~36)进制

先看下itoa()的函数说明吧: 功 能:把一整数转换为字符串   用 法:char *itoa(int value, char *string, int radix);    详细解释:itoa是英文integer to array(将int整型数转化为一个字符串,并将值保存在数组string中)的缩写.    参数:  value: 待转化的整数。            radix:

代码随想录Day 36|滑铁卢了,leetcode题目:1049.最后一块石头的重量、494.目标和、474.一和零

提示:DDU,供自己复习使用。欢迎大家前来讨论~ 文章目录 动态规划一、题目题目一:1049.最后一块石头的重量II解题思路: 题目二:494.目标和动态规划 (二维dp数组)#动态规划 (一维dp数组) 题目三: 474.一和零解题思路: 总结 动态规划 有点难了,之前差的有点多,找时间补 一、题目 题目一:1049.最后一块石头的重量II leetcode题目链接

LeetCode - 36. Valid Sudoku

36. Valid Sudoku  Problem's Link  ---------------------------------------------------------------------------- Mean:  给定一个数独,判断这个数独是否合法. analyse: 略. Time complexity: O(N)   view

leetcode解题思路分析(五)29-36题

两数相除 给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。 返回被除数 dividend 除以除数 divisor 得到的商。 本题思路倒是不难,既然不能用乘除法和mod,那使用减法是理所当然的,唯一需要考虑的是边界溢出情况 class Solution {public:int divide(int dividend, in

LeetCode 36 Valid Sudoku

题意: 判断一个填了一部分的数独有没有解。 思路: 按照数独规则判断即可,即同一行、同一列、同一个3*3的方格内没有数字重复出现。 代码: class Solution {public:bool isValidSudoku(vector <vector<char>> &board) {const int step = 3;bool app[step * step];fo

代码随想录算法训练营第36天|1049. 最后一块石头的重量、494. 目标和、474.一和零

目录 1049. 最后一块石头的重量 II1、题目描述2、思路3、code4、复杂度分析 494. 目标和1、题目描述2、思路3、code4、复杂度分析 474. 一和零1、题目描述2、思路3、code4、复杂度分析 1049. 最后一块石头的重量 II 题目链接:link 1、题目描述 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块

MySQL5.7.36之高可用架构部署-MHA-VIP漂移

1、创建文件 vi /usr/local/bin/master_vip_mysql_mha #!/usr/bin/env perluse strict;use warnings FATAL => 'all';use Getopt::Long;my ($command, $ssh_user, $orig_master_host, $orig_master_i

认知杂谈36

今天分享 有人说的一段争议性的话 《爷们儿,高手避坑指南》 嘿,爷们儿!人生处处有陷阱,稍不留意就可能让你的人生轨迹严重跑偏。 I 今天咱就从高手的视角聊聊这些硬派的人生感悟,这可都是无数前辈爷们用血汗换来的教训。 I 咱分享给你,就是希望你能避开那些坑,让你的黄金岁月走在正道上,闪闪发光。 I 要知道,男人的青春无比珍贵,是在烂泥里挣扎还是在康庄大道上飞驰,结果天差地别。 I 就像有的陷

数据库系统 第36节 数据库镜像

数据库镜像(Database Mirroring)是一种在数据库系统中用于确保数据高可用性和灾难恢复的技术。在MySQL中,虽然它不像SQL Server那样直接支持数据库镜像,但可以通过复制(Replication)来实现类似的功能。MySQL的复制分为两种类型:异步复制和半同步复制。 异步复制(Asynchronous Replication):在这种模式下,主服务器(Master)上的数