本文主要是介绍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. 尚融宝投标的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!