核心业务3:借款人借款申请
1.借款人借款申请业务流程图
2.借款项数据库绑定
---------------------借款申请流程----------------------
3.借款申请流程
4.前端代码逻辑
5.后端代码逻辑
---------------------借款申请流程----------------------
核心业务3:借款人借款申请
1.借款人借款申请业务流程图
①此时用户已经获得了积分,可以在额度申请页面点击我要借款
②点击我要借款后展示借款信息填写页
③借款信息提交后显示等待审核
④后台管理员审核技术后获取审核结果
2.借款项数据库绑定
①数据库表
borrow_info
integral_grade
②关联
- borrow_info表通过user_id与user_info关联,即一个user只能发起一次借款
- user_info的积分字段可查integral_grade表获取对应额度,然后borrow_info根据user_info的额度来发起借款
- borrow表和borrow_info表无关,两者都只是通过user_id与user_info关联,一个是借款额度申请表单,一个是借款申请表单
3.借款申请流程
①前端
- 用户在注册登陆绑定数据和获得额度之后,可以发起我要借款
- 进入界面判断借款的状态,如果未借款进入表单界面,如果其他状态跳到响应的界面
- 表单数据的填写,用户可以看到自己的额度(调用api),用户也可以再下拉列表里填写数据(调用api)
- 用户填写完表单后提交后台
②后端
- 用户刚进入借款页面判断用户借款状态(通过user_id查询borrow_info看用户借款状态)
- 用户未发起借款,需要返回数据字典数据初始化下拉列表,需要返回用户的借款额度
- 用户提交表单,需要校验(防止恶意用户跳过入口直接访问后台),然后保存数据到borrow_info表
4.前端代码逻辑
①前端封装表单对象
②前端调用接口
③前端代码
- 增加按钮到借款额度申请成功页
srb-site\pages\user\borrower.vue
添加按钮到审批通过组件下
<!--在审批通过后,增加我要借款按钮,进入借款信息页面-->
<NuxtLink to="/user/apply" v-if="borrowerStatus === 2">
<el-button style="margin-top:20px;" type="success">
我要借款
</el-button>
</NuxtLink>
- 借款申请页面
srb-site\pages\user\apply.vue
<template>
<div class="personal-main">
<div class="personal-pay">
<h3><i>借款申请</i></h3>
<el-steps :active="active" style="margin: 40px">
<el-step title="提交借款信息"></el-step>
<el-step title="审核"></el-step>
<el-step title="等待审核结果"></el-step>
</el-steps>
<div v-if="active === 0" class="user-borrower">
<el-form label-width="120px">
<el-form-item label="借款金额">
<el-col :span="6">
<el-input v-model="borrowInfo.amount" />
</el-col>
<el-col :span="6">
您最多可借款{{ borrowAmount }}元
</el-col>
</el-form-item>
<el-form-item label="期数">
<el-select v-model="borrowInfo.period">
<el-option :value="1" label="1个月" />
<el-option :value="3" label="3个月" />
<el-option :value="6" label="6个月" />
<el-option :value="9" label="9个月" />
<el-option :value="12" label="12个月" />
<el-option :value="24" label="24个月" />
</el-select>
</el-form-item>
<el-form-item label="还款方式">
<el-select v-model="borrowInfo.returnMethod">
<el-option
v-for="item in returnMethodList"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="资金用途">
<el-select v-model="borrowInfo.moneyUse">
<el-option
v-for="item in moneyUseList"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="年利率">
<el-col :span="6">
<el-input v-model="borrowInfo.borrowYearRate">
<template slot="append">%</template>
</el-input>
</el-col>
<el-col :span="8">
年利率越高,借款越容易成功。
</el-col>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:disabled="submitBtnDisabled"
@click="save"
>
提交
</el-button>
</el-form-item>
</el-form>
<el-alert
title="您提供的任何信息尚融宝都承诺予以保护,不会挪作他用。"
type="warning"
:closable="false"
>
</el-alert>
</div>
<div v-if="active === 1">
<div style="margin-top:40px;">
<el-alert
title="您的借款申请已成功提交,请耐心等待"
type="warning"
show-icon
:closable="false"
>
我们将在10分钟内完成审核,审核时间为周一至周五8:00至20:00。
</el-alert>
</div>
</div>
<div v-if="active === 2">
<div style="margin-top:40px;">
<el-alert
v-if="borrowInfoStatus === 2"
title="您的借款申请审批已通过"
type="success"
show-icon
:closable="false"
>
</el-alert>
<el-alert
v-if="borrowInfoStatus === -1"
title="您的借款申请审批未通过"
type="error"
show-icon
:closable="false"
>
</el-alert>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
active: null, //步骤
borrowInfoStatus: null, //审批状态
//借款申请
borrowInfo: {
borrowYearRate: '12',
},
borrowAmount: 0, //借款额度
submitBtnDisabled: false,
returnMethodList: [], //还款方式列表
moneyUseList: [], //资金用途列表
}
},
methods: {
//初始化下拉列表的数据
initSelected() {
//还款方式列表
this.$axios
.$get('/api/core/dict/findByDictCode/returnMethod')
.then((response) => {
this.returnMethodList = response.data.dictList
})
//资金用途列表
this.$axios
.$get('/api/core/dict/findByDictCode/moneyUse')
.then((response) => {
this.moneyUseList = response.data.dictList
})
},
//获取借款额度
getBorrowAmount() {
this.$axios
.$get('/api/core/borrowInfo/auth/getBorrowAmount')
.then((response) => {
this.borrowAmount = response.data.borrowAmount
})
},
//提交借款申请
save() {
// this.submitBtnDisabled = true
this.$axios
.$post('/api/core/borrowInfo/auth/save', this.borrowInfo)
.then((response) => {
this.active = 1
})
},
//获取借款审批状态
getBorrowInfoStatus() {
this.$axios
.$get('/api/core/borrowInfo/auth/getBorrowInfoStatus')
.then((response) => {
this.borrowInfoStatus = response.data.borrowInfoStatus
if (this.borrowInfoStatus === 0) {
//未认证
this.active = 0
//获取借款额度
this.getBorrowAmount()
//初始化下拉列表
this.initSelected()
} else if (this.borrowInfoStatus === 1) {
//审批中
this.active = 1
} else if (this.borrowInfoStatus === 2) {
//审批成功
this.active = 2
} else if (this.borrowInfoStatus === -1) {
//审批失败
this.active = 2
}
})
},
},
created() {
//created方法默认情况下在前端服务器执行,而不是在浏览器执行,前端服务器端执行失败,在浏览器端还会再发起请求(浏览器端成功)
//即在nuxt前端服务器初始化时候并没有带token,故可以用mounted渲染,mounted一定在浏览器端执行(渲染完成后执行)
//获取借款额度
//this.getBorrowAmount()
//初始化下拉列表
//this.initSelected()
//获取审批状态
this.getBorrowInfoStatus()
},
//监听某一个值,如果这一值一旦改变则调用
watch: {
'borrowInfo.amount'(value) {
if (value > this.borrowAmount) {
let _this = this
this.$alert('您的借款额度不足!', {
type: 'error',
//当错误信息弹出完毕之后点击确定,然后执行回调函数
callback() {
//this的作用域和外部的作用域不一样(callback的作用域和外面的this不一样,需要传入this)
_this.borrowInfo.amount = _this.borrowAmount
},
})
}
},
},
}
</script>
④前端代码逻辑难点
- 需要监听额度字段如果超过最大额度设置其值为最大额度
用watch方法监听字段
5.后端代码逻辑
①后端对象
- borrow_info对象即可不需要专门创建vo对象
②后端接口
③后端代码
- 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.BorrowInfo;
import com.atguigu.srb.core.service.BorrowInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
@Api(tags = "借款信息")
@RestController
@RequestMapping("/api/core/borrowInfo")
@Slf4j
public class BorrowInfoController {
@Resource
private BorrowInfoService borrowInfoService;
@ApiOperation("获取借款额度")
@GetMapping("/auth/getBorrowAmount")
public R getBorrowAmount(HttpServletRequest request) {
String token = request.getHeader("token");
Long userId = JwtUtils.getUserId(token);
BigDecimal borrowAmount = borrowInfoService.getBorrowAmount(userId);
return R.ok().data("borrowAmount", borrowAmount);
}
@ApiOperation("提交借款申请")
@PostMapping("/auth/save")
public R save(@RequestBody BorrowInfo borrowInfo, HttpServletRequest request) {
//目标就是把用户提交的数据保存到数据表borrow_info中
//该接口防止恶意攻击
//前端不提供入口 + 后端进行校验(是否绑定,是否审批借款申请)
String token = request.getHeader("token");
Long userId = JwtUtils.getUserId(token);
//将借款信息存储数据库,校验用户是否绑定和审批
borrowInfoService.saveBorrowInfo(borrowInfo, userId);
return R.ok().message("提交成功");
}
@ApiOperation("获取借款申请审批状态")
@GetMapping("/auth/getBorrowInfoStatus")
public R getBorrowerStatus(HttpServletRequest request){
String token = request.getHeader("token");
Long userId = JwtUtils.getUserId(token);
Integer status = borrowInfoService.getStatusByUserId(userId);
return R.ok().data("borrowInfoStatus", status);
}
}
- service
package com.atguigu.srb.core.service;
import com.atguigu.srb.core.pojo.entity.BorrowInfo;
import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal;
/**
* <p>
* 借款信息表 服务类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
public interface BorrowInfoService extends IService<BorrowInfo> {
BigDecimal getBorrowAmount(Long userId);
void saveBorrowInfo(BorrowInfo borrowInfo, Long userId);
Integer getStatusByUserId(Long userId);
}
package com.atguigu.srb.core.service.impl;
import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.srb.core.enums.BorrowInfoStatusEnum;
import com.atguigu.srb.core.enums.BorrowerStatusEnum;
import com.atguigu.srb.core.enums.UserBindEnum;
import com.atguigu.srb.core.mapper.BorrowInfoMapper;
import com.atguigu.srb.core.mapper.IntegralGradeMapper;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.BorrowInfo;
import com.atguigu.srb.core.pojo.entity.IntegralGrade;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.BorrowInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 借款信息表 服务实现类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Service
public class BorrowInfoServiceImpl extends ServiceImpl<BorrowInfoMapper, BorrowInfo> implements BorrowInfoService {
@Resource
private UserInfoMapper userInfoMapper;
@Resource
private IntegralGradeMapper integralGradeMapper;
/**
* @param userId:
* @return BigDecimal
* @author Likejin
* @description 根据用户id查询用户积分,根据积分获取用户额度
* @date 2023/4/16 14:30
*/
@Override
public BigDecimal getBorrowAmount(Long userId) {
//获取用户积分
UserInfo userInfo = userInfoMapper.selectById(userId);
Assert.notNull(userInfo, ResponseEnum.LOGIN_MOBILE_ERROR);
Integer integral = userInfo.getIntegral();
//根据积分获取用户额度
QueryWrapper<IntegralGrade> integralGradeQueryWrapper = new QueryWrapper<>();
//做一个区间比较,le为大于左边,ge为小于右边
integralGradeQueryWrapper
.le("integral_start",integral)
.ge("integral_end",integral);
IntegralGrade integralGrade = integralGradeMapper.selectOne(integralGradeQueryWrapper);
if(integralGrade == null){
return new BigDecimal("0");
}
return integralGrade.getBorrowAmount();
}
/**
* @param borrowInfo:
* @param userId:
* @return void
* @author Likejin
* @description 将借款信息存储数据库,校验用户是否绑定和审批
* @date 2023/4/16 15:11
*/
@Override
public void saveBorrowInfo(BorrowInfo borrowInfo, Long userId) {
//校验绑定状态和借款人额度审批状态
UserInfo userInfo = userInfoMapper.selectById(userId);
Assert.isTrue(userInfo.getBindStatus().intValue() == UserBindEnum.BIND_OK.getStatus().intValue(),
ResponseEnum.USER_NO_BIND_ERROR);
Assert.isTrue(userInfo.getBorrowAuthStatus().intValue() == BorrowerStatusEnum.AUTH_OK.getStatus().intValue(),
ResponseEnum.USER_NO_AMOUNT_ERROR);
//判断借款人额度是否充足
BigDecimal borrowAmount = this.getBorrowAmount(userId);
Assert.isTrue(borrowInfo.getAmount().doubleValue() <=borrowAmount.doubleValue(),
ResponseEnum.USER_AMOUNT_LESS_ERROR);
//存储borrower_info数据
borrowInfo.setUserId(userId);
//百分比转小数
borrowInfo.setBorrowYearRate(borrowInfo.getBorrowYearRate().divide(new BigDecimal(100)));
//设置借款申请的审核状态
borrowInfo.setStatus(BorrowInfoStatusEnum.CHECK_RUN.getStatus());
baseMapper.insert(borrowInfo);
}
/**
* @param userId:
* @return Integer
* @author Likejin
* @description 获取用户申请借款的状态
* @date 2023/4/16 15:35
*/
@Override
public Integer getStatusByUserId(Long userId) {
//根据user_id查询用户的借款申请状态
QueryWrapper<BorrowInfo> borrowInfoQueryWrapper = new QueryWrapper<>();
borrowInfoQueryWrapper.select("status").eq("user_id", userId);
List<Object> objects = baseMapper.selectObjs(borrowInfoQueryWrapper);
if(objects.size() == 0){
//借款人尚未提交信息
return BorrowInfoStatusEnum.NO_AUTH.getStatus();
}
Integer status = (Integer)objects.get(0);
return status;
}
}
④后端代码逻辑难点
- 获取借款额度
通过token获得user_id,然后通过user_id查询user_info获得积分,在通过查表integral_grade表的的区间查询(根据积分所在的区间查询对应的额度)获取额度 - 提交借贷申请
需要从token中拿到user_id,通过user_id校验user是否绑定和是否申请借款额度(防止恶意攻击),还需要通过user_id校验该user的额度是否没被申请的额度超过,封装数据(年利率前端传整型,后端小数,除以100),设置借款流程的状态borrow_info的status,保存数据到数据库。 - 获取借款流程状态
直接根据user_id查询borrow_info中的status即可。