35. 尚融宝投标

2023-11-22 11:10
文章标签 35 投标 尚融宝

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

简介

1、需求描述

平台发布标的,出借人充值就可以投资标的
在这里插入图片描述

2、相关数据库表

在这里插入图片描述

3、参考文档

参考《汇付宝商户账户技术文档》3.10满标投资,投资过程与账户绑定、用户充值过程一致

在这里插入图片描述

标的详情

需求

step1:点击标的,进入标的详情页面

在这里插入图片描述

后端

controller

LendController.java

    @ApiOperation("标的详情")@GetMapping("/getLendDetail/{id}")public R getLendDetail(@PathVariable Long id) {Map<String, Object> lendDetail = lendService.getLendDetail(id);return R.ok().setData("lendDetail", lendDetail);}

service

LendService.java

    Map<String, Object> getLendDetail(Long id);

LendServiceImpl.java

    @Resourceprivate BorrowerService borrowerService;@Overridepublic Map<String, Object> getLendDetail(Long id) {// 获取表的信息Lend lend = baseMapper.selectById(id);setExtensionLend(lend);QueryWrapper<Borrower> queryWrapper = new QueryWrapper<>();queryWrapper.eq("user_id", lend.getUserId());Borrower borrower = borrowerService.getBaseMapper().selectOne(queryWrapper);// 获取借款人信息BorrowerDetailVO borrowerDetailVO = borrowerService.getBorrowerDetailVO(borrower.getId());Map<String, Object> map = new HashMap<>();map.put("lend", lend);map.put("borrower", borrowerDetailVO);return map;}

前端

template

pages/lend/_id.vue

以后也会上传到码云…

script

pages/lend/_id.vue

在这里插入图片描述

  // 获取标的详情// 希望搜索引擎能收录,所以使用异步查询的方式async asyncData({ $axios, params }) {let lendId = params.idlet response = await $axios.$get('/api/core/lend/getLendDetail/' + lendId)return {lend: response.data.lendDetail.lend,borrower: response.data.lendDetail.borrower,}},
  //此时方法在客户端的浏览器中执行,可以获取到cookiemounted() {//查询账户余额this.fetchAmount()//判断登录人的用户类型this.fetchUserType()},methods: {//查询账户余额fetchAmount() {let userInfo = cookie.get('userInfo')if (userInfo) {this.$axios.$get('/api/core/userAccount/auth/getAmount').then((response) => {this.amount = response.data.amount})}},//获取登录人的用户类型fetchUserType() {let userInfo = cookie.get('userInfo')if (userInfo) {userInfo = JSON.parse(userInfo)this.userType = userInfo.userType}},},

计算收益

需求

step2:输入投资金额,计算获得收益

还款方式

1、等额本息

等额本息法最重要的一个特点是每月的还款额相同,从本质上来说是本金所占比例逐月递增,利息所占比例逐月递减,月还款数不变。

即在月供“本金与利息”的分配比例中,前半段时期所还的利息比例大、本金比例小,还款期限过半后逐步转为本金比例大、利息比例小。

计算公式为

每月利息 = 剩余本金 x 贷款月利率

每月还本付息金额 = 还款总额 / 贷款月数

每月本金 = 每月还本付息金额 - 每月利息

注意:在等额本息法中,银行一般先收剩余本金利息,后收本金,所以利息在月供款中的比例会随本金的减少而降低,本金在月供款中的比例因而升高,但月供总额保持不变。

2、等额本金

等额本金法最大的特点是每月的还款额不同,呈现逐月递减的状态;它是将贷款本金按还款的总月数均分,再加上上期剩余本金的利息,这样就形成月还款额,所以等额本金法第一个月的还款额最多 ,然后逐月减少,越还越少。

计算公式为:

每月利息 = 剩余本金 x 贷款月利率

每月本金 = 贷款额 / 贷款月数

每月还本付息金额 = 每月本金 + 每月利息

注意:在等额本金法中,人们每月归还的本金额始终不变,利息随剩余本金的减少而减少,因而其每月还款额逐渐减少。

3、按期付息到期还本

按期付息到期还本是借款人在贷款到期日一次性归还贷款本金,利息按期归还

计算公式为

每月利息 = 贷款额 x 贷款月利率

总利息 = 每月利息 x 贷款月数

4、一次还本付息

一次还本付息是贷款到期后一次性归还本金和利息

计算公式为

还款金额 = 贷款额 + 贷款额 x 月利率 x 贷款月数

后端

controller

LendController.java

    @ApiOperation("计算投资收益")@GetMapping("/getInterestCount/{invest}/{yearRate}/{totalMonth}/{returnMethod}")public R getInterestCount(@ApiParam(value = "投资金额", required = true)@PathVariable("invest") BigDecimal invest,@ApiParam(value = "年化收益", required = true)@PathVariable("yearRate") BigDecimal yearRate,@ApiParam(value = "期数", required = true)@PathVariable("totalMonth") Integer totalMonth,@ApiParam(value = "还款方式", required = true)@PathVariable("returnMethod") Integer returnMethod){BigDecimal interestCount = lendService.getInterestCount(invest, yearRate,totalMonth,returnMethod);return R.ok().setData("interestCount",interestCount);}

enums

ReturnMethodEnum.java

    public static ReturnMethodEnum getByValue(Integer value){for(ReturnMethodEnum transactType : values()){if (transactType.getMethod() == value) {return transactType;}}return null;}

util

根据我们的表设计,出借人需要知道每月回款的本金与利息,借款人也一样,他也要知道每月的还款本金与利息,还有我们需要计算投资人的投资收益等数据。
因此我们将四种还款方式工具类设计如下:

在这里插入图片描述
说明:还款方式计算复杂,仅做了解,这里不做详细介绍
后续会上传码云,有急需的可以私信

service

LendService.java

    // 根据用户的还款方式,来计算不同的收益BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalMonth, Integer returnMethod);

LendServiceImpl.java

    @Overridepublic BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalMonth, Integer returnMethod) {BigDecimal interestCount;switch (ReturnMethodEnum.getByValue(returnMethod.intValue())) {case ONE:interestCount = Amount1Helper.getInterestCount(invest, yearRate, totalMonth);break;case TWO:interestCount = Amount2Helper.getInterestCount(invest, yearRate, totalMonth);break;case THREE:interestCount = Amount3Helper.getInterestCount(invest, yearRate, totalMonth);break;default:interestCount = Amount4Helper.getInterestCount(invest, yearRate, totalMonth);break;}return interestCount;}

前端

script

pages/lend/_id.vue

    //计算收益getInterestCount() {this.$axios.$get(`/api/core/lend/getInterestCount/${this.invest.investAmount}/${this.lend.lendYearRate}/${this.lend.period}/${this.lend.returnMethod}`).then((response) => {this.interestCount = response.data.interestCount})},

投标

需求

step3:同意协议,点击立即投资

在这里插入图片描述
step4:跳转到汇付宝页面(资金托管接口调用)

step5:汇付宝验证用户交易密码

step6:汇付宝修改账号资金余额(更新user_account记录中的amount的值和freeze_amount的值)
汇付宝新增投资记录(新增user_invest记录)

后端

vo

InvestVO.java

@Data
public class InvestVO {private Long lendId;    // 标的idprivate Long investUserId;  // 投资人idprivate String investUserName;  // 投资人姓名private BigDecimal investAmount;    // 投资金额
}

controller

LendItemController.java

@Api("标的项")
@RestController
@RequestMapping("/api/core/lendItem")
@Slf4j
public class LendItemController {@Resourceprivate LendItemService lendItemService;@ApiOperation("我要投资")@PostMapping("/auth/invest")public R invest(@RequestBody InvestVO investVO, HttpServletRequest request) {String token = request.getHeader("token");Long userId = JwtUtils.getUserId(token);String userName = JwtUtils.getUserName(token);investVO.setInvestUserId(userId);investVO.setInvestUserName(userName);String formStr = lendItemService.invest(investVO);return R.ok().setData("formStr", formStr);}
}

enums

UserTypeEnum.java

@AllArgsConstructor
@Getter
public enum UserTypeEnum {INVESTOR(1, "投资人"),BORROWER(2, "借款人");private Integer status;private String msg;
}

LendItemEnum.java

@Getter
@AllArgsConstructor
public enum LendItemEnum {NO_PAY(0, "未支付"),PAY_OK(1, "已支付");Integer status;String msg;
}

LendItemService

LendItemService.java

    String invest(InvestVO investVO);

LendItemServiceImpl.java

    @Resourceprivate LendService lendService;@Resourceprivate UserInfoMapper userInfoMapper;@Resourceprivate LendMapper lendMapper;@Resourceprivate UserAccountService userAccountService;@Resourceprivate UserBindService userBindService;@Overridepublic String invest(InvestVO investVO) {// 标的状态必须是募资中Lend lend = lendMapper.selectById(investVO.getLendId());Assert.isTrue(lend.getStatus() == LendStatusEnum.INVEST_RUN.getStatus(), ResponseEnum.LEND_INVEST_ERROR);// 用户必须是投资人UserInfo userInfo = userInfoMapper.selectById(investVO.getInvestUserId());Assert.isTrue(userInfo.getUserType() == UserTypeEnum.INVESTOR.getStatus(), ResponseEnum.LEND_USER_TYPE_ERROR);// 账户余额必须大于等于投资金额BigDecimal amount = userAccountService.getAmount(investVO.getInvestUserId());Assert.isTrue(amount.doubleValue() >= investVO.getInvestAmount().doubleValue(), ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);// 投资金额必须是100的正整数倍Assert.isTrue(investVO.getInvestAmount().remainder(new BigDecimal(100)).equals(new BigDecimal(0)) && investVO.getInvestAmount().doubleValue() > 0,ResponseEnum.LEND_INVEST_AMOUNT_ERROR);// 投资金额 + 已投金额 <= 募资金额BigDecimal sum = investVO.getInvestAmount().add(lend.getInvestAmount());Assert.isTrue(sum.doubleValue() <= lend.getAmount().doubleValue(), ResponseEnum.LEND_FULL_SCALE_ERROR);// 生成投资项对应的投资信息LendItem lendItem = new LendItem();lendItem.setLendItemNo(LendNoUtils.getLendItemNo());lendItem.setLendId(investVO.getLendId());lendItem.setInvestUserId(investVO.getInvestUserId());lendItem.setInvestName(investVO.getInvestUserName());lendItem.setInvestAmount(investVO.getInvestAmount());lendItem.setLendYearRate(lend.getLendYearRate());lendItem.setInvestTime(LocalDateTime.now());lendItem.setLendStartDate(lend.getLendStartDate());lendItem.setLendEndDate(lend.getLendEndDate());BigDecimal exceptAmount = lendService.getInterestCount(investVO.getInvestAmount(),lend.getLendYearRate(),lend.getPeriod(),lend.getReturnMethod());lendItem.setExpectAmount(exceptAmount);lendItem.setRealAmount(BigDecimal.ZERO);lendItem.setStatus(LendItemEnum.NO_PAY.getStatus());baseMapper.insert(lendItem);// 组装汇付宝参数Map<String, Object> map = new HashMap<>();String voteBindCode = userBindService.getBindCodeByUserId(lendItem.getInvestUserId());String benefitBindCode = userBindService.getBindCodeByUserId(lend.getUserId());map.put("agentId", HfbConst.AGENT_ID);map.put("voteBindCode", voteBindCode);map.put("benefitBindCode", benefitBindCode);map.put("agentProjectCode", lend.getLendNo());map.put("agentProjectName", lend.getTitle());map.put("agentBillNo", lendItem.getLendItemNo());map.put("voteAmt", lendItem.getInvestAmount());map.put("votePrizeAmt", "0");map.put("voteFeeAmt", "0");map.put("projectAmt", lend.getAmount());map.put("note", "");map.put("notifyUrl", HfbConst.INVEST_NOTIFY_URL);map.put("returnUrl", HfbConst.INVEST_RETURN_URL);map.put("timestamp", RequestHelper.getTimestamp());map.put("sign", RequestHelper.getSign(map));String formStr = FormHelper.buildForm(HfbConst.INVEST_URL, map);return formStr;}

UserBindService

创建一个通用的Service方法,以方便调用,根据userId获取用户绑定账号

UserBindService.java

    String getBindCodeByUserId(Long userId);

UserBindServiceImpl.java

    @Overridepublic String getBindCodeByUserId(Long userId) {QueryWrapper<UserBind> queryWrapper = new QueryWrapper<>();queryWrapper.eq("user_id",userId);UserBind userBind = baseMapper.selectOne(queryWrapper);return userBind.getBindCode();}

前端

    //投资commitInvest() {//校验用户是否登录let userInfo = cookie.get('userInfo')// console.log(typeof userInfo)// console.log(!userInfo) //trueif (!userInfo) {window.location.href = '/login'return}//校验当前用户是否是投资人let userInfoObj = JSON.parse(userInfo)if (userInfoObj.userType == 2) {//借款人this.$message.error('借款人无法投资')return}console.log(this.lend.investAmount)console.log(this.invest.investAmount)console.log(this.lend.amount)//判断标的是否超卖:标的已投金额 + 本次投资金额 > 标的总金额if (this.lend.investAmount + Number(this.invest.investAmount) >this.lend.amount) {this.$message.error('标的可投资金额不足')return}//是否是100的整数倍// console.log(this.invest.investAmount)// console.log(Number(this.invest.investAmount))// console.log(typeof Number(this.invest.investAmount))// returnif (Number(this.invest.investAmount) <= 0 ||this.invest.investAmount % this.lend.lowestAmount != 0) {this.$message.error(`投资金额必须是${this.lend.lowestAmount}的整数倍`)return}//余额的判断if (this.invest.investAmount > this.amount) {this.$message.error('余额不足,请充值')return}//数据提交this.$alert('<div style="size: 18px;color: red;">您即将前往汇付宝确认标的</div>','前往汇付宝资金托管平台',{dangerouslyUseHTMLString: true,confirmButtonText: '立即前往',callback: (action) => {console.log('action', action)if (action === 'confirm') {this.invest.lendId = this.lend.idthis.$axios.$post('/api/core/lendItem/auth/invest', this.invest).then((response) => {// console.log(response.data.formStr)// debuggerdocument.write(response.data.formStr)})}},})},},

投资回调

需求

step7:异步回调

(1)账户金额更改(剩余金额和冻结金额)

(2)修改投资状态(lend_item表中的status)

(3)更新标的信息(lend表中的投资人数和已投金额)

(4)添加交易流水

step8:用户点击“返回平台”,返回尚融宝

后端

controller

LendItemController.java

    @ApiOperation("投资回调")@PostMapping("/notify")public String notify(HttpServletRequest request) {Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());// 校验签名if (RequestHelper.isSignEquals(map)) {if("0001".equals(map.get("resultCode"))){lendItemService.notify(map);}else{log.info("用户投资异步回调失败:" + JSON.toJSONString(map));return "fail";}} else {log.info("用户投资异步回调签名错误:" + JSON.toJSONString(map));return "fail";}return "success";}

service

LendItemService.java

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

LendItemServiceImpl.java

    @Resourceprivate UserAccountMapper userAccountMapper;@Resourceprivate TransFlowService transFlowService;@Overridepublic void notify(Map<String, Object> map) {String agentBillNo = (String) map.get("agentBillNo");if (transFlowService.haveTransFlow(agentBillNo)) {log.warn("幂等性返回");return;}String bindCode = (String) map.get("voteBindCode");BigDecimal amount = new BigDecimal((String) map.get("voteAmt"));// 更新账户余额、冻结金额userAccountMapper.updateAccount(bindCode, new BigDecimal("-" + map.get("voteAmt")), amount);// 更新标的项状态LendItem lendItem = this.getLendItem(agentBillNo);lendItem.setStatus(LendItemEnum.PAY_OK.getStatus());baseMapper.updateById(lendItem);// 更新标的人数、已筹金额Lend lend = lendMapper.selectById(lendItem.getLendId());lend.setInvestAmount(lend.getInvestAmount().add(amount));lend.setInvestNum(lend.getInvestNum() + 1);lendMapper.updateById(lend);// 添加流水TransFlowBO transFlowBO = new TransFlowBO(agentBillNo,bindCode,TransTypeEnum.INVEST_LOCK,amount,"项目编号:"+lend.getLendNo()+",项目名称:"+lend.getTitle());transFlowService.saveTransFlow(transFlowBO);}/*** 先提取出来,以后需要的话再公开* @param agentBillNo* @return*/	private LendItem getLendItem(String agentBillNo) {QueryWrapper<LendItem> queryWrapper = new QueryWrapper<>();queryWrapper.eq("lend_item_no", agentBillNo);return baseMapper.selectOne(queryWrapper);}

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



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

相关文章

『功能项目』战士的平A特效【35】

我们打开上一篇34武器的切换实例的项目, 本章要做的事情是在战士的每次按A键时在指定位置生成一个平A特效 首先将之前下载的技能拖拽至场景中 完全解压缩后重命名为AEffect 拖拽至预制体文件夹 进入主角动画的战士动画层级 双击第一次攻击 选择Animation 创建事件 创建的动画事件帧放在攻击动画挥剑指定处 命名为PerpetualAtt

NoSQL数据库的35个应用场景

现在我们站在各个用例的角度上来考虑那种系统适合于这些用例。   你的意见是?   首先,我们要纵览各种数据模型。这些模型的分类方法来自于Emil Eifrem和NoSQL databases。   文档数据库   源起:受Lotus Notes启发。   数据模型:包含了key-value的文档集合   例子:CouchDB, MongoDB   优点:数据模型自然,编

MySQL-35个DQL练手题(难)

第1题 取得每个部门最高薪水的人员名称 第一步:取得每个部门最高薪水 select max(sal) topsal, deptno from emp group by deptno; 第二步:将上面第一步的查询结果当做一张临时表t,进行表连接,条件是:t.deptno=e.deptno and t.maxsal=e.sal select e.ename, t.* from emp e

没有35类可以做特许经营加盟不!

前几天有个老客户找到普推知产商标老杨,没有35类可以做特许经营加盟不,在35类有个小类叫做“特许经营的商业管理”,但是35类这个主要指的为他人提供的,所以就是没有35类广告,照样就可以做特许经营加盟。 比如已经有43类餐饮行业的商标了,有44类美容店的商标,那也可以自营做加盟,35类的特许经营主要是指的是为他人的特许经营行为提供商业咨询、调查、管理等服务的主体,比如管理多个

「图」邻接矩阵|边集数组|邻接表 / LeetCode 35|33|81(C++)

概述 我们开始入门图论:图的存储。 图是一种高级数据结构:链表是一个节点由一条边指向下一个节点,二叉树是一个节点由两条边指向下两个节点,而图是由任意多个节点由任意多条边指向任意多个节点。 图由节点和边构成,边往往存在边权。边权在不同的问题中往往有不同含义,如在最短路径中表示边的长度,在AOE网中表示任务所需时间。 对于这种复杂的结构,如何存储在计算机的程序语言中呢? 我们先来介绍三种存储

代码随想录算法训练营第35天|背包问题基础、46. 携带研究材料(01背包二维解法)(01背包一维解法)(acm)、416. 分割等和子集

目录 0、背包问题基础01背包 46. 携带研究材料(01背包)1、题目描述2、思路3、code(二维解法)3-1、code(一维解法)4、复杂度分析 416. 分割等和子集1、题目描述2、思路3、code4、复杂度分析 0、背包问题基础 01背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能

【软件技巧】第35课,软件逆向安全工程师之汇编指令mov、ptr、xchg交换指令,每天5分钟学习逆向吧!

在x86汇编语言中,mov 指令用于将一个值从一个位置移动到另一个位置。这个值可以是立即数、寄存器中的值、内存中的值或者是一个指针。mov 指令是汇编语言中最常用的指令之一,因为它在数据传输和初始化操作中起着核心作用。 mov 指令的基本格式: mov 目标操作数, 源操作数 目标操作数:接收数据的操作数,通常是寄存器、内存地址或者是一个指针。源操作数:提供数据的操作数,可以是立即数、寄存器

【持续更新】Advanced Download Manager 14.0.35 Pro安卓ADM下载神器最新高级免费修改版

这个也算小有名气,名字和 idm 有点像。当程序从剪贴板中截取链接后,您可以将其复制并发送至ADM编辑器,或者使用“添加”按钮粘贴链接。 ▨ ADM 有以下特点: • 该应用支持同时下载最多三个文件 • 通过多线程技术(9个部分)加速下载过程 • 从安卓浏览器及剪贴板中拦截链接 • 后台下载文件,并在失败后自动恢复 • 支持图片、文档、压缩包及程序的加载 • 针对Lollipop和Ma

35~750kV 变电站无人值守与集中监控的智能运维模式

1、引言         电力行业的快速发展和电网规模的持续扩张,使得传统的变电站运维模式日渐不能满足现代电网对于安全性、可靠性和效率的更高要求。因此,向无人值守与集中监控过渡,已经逐渐成为了整个行业发展的主旋律。 2、关键技术支撑 2.1 自动化监控技术 为了实现对变电站的全面监控与管理,安装在变电站的高清摄像头、红外测温仪、烟雾探测器等设备扮演着至关重要的角色。它们可以实现对变电站

C++竞赛初阶L1-15-第六单元-多维数组(34~35课)551: T456501 计算矩阵边缘元素之和

题目内容 输入一个整数矩阵,计算位于矩阵边缘的元素之和。 所谓矩阵边缘的元素,就是第一行和最后一行的元素以及第一列和最后一列的元素。 输入格式 第 1 行包含两个整数,分别为行数 m 和列数 n,两个整数之间空格隔开。 第 2 行开始有 m 行数据,每行包含 n 个整数,整数之间空格隔开。 输出格式 对应矩阵的边缘元素和。 样例 1 输入 3 33 4 13 7 12