借款信息列表
需求
一、后端实现
1、entity
列表的结果需要关联查询,数据字典的数据也需要展示对应的文本内容而不是值,除了定义VO的方式,我们也可以使用扩展实体类的方式
在BorrowInfo.java
类中扩展以下字段
//扩展字段
@ApiModelProperty(value = "姓名")
@TableField(exist = false)
private String name;
@ApiModelProperty(value = "手机")
@TableField(exist = false)
private String mobile;
@ApiModelProperty(value = "其他参数")
@TableField(exist = false)
private Map<String, Object> param = new HashMap<>();
2、controller
添加 AdminBorrowInfoController.java
package com.indi.srb.core.controller.admin;
@Api(tags = "借款管理")
@RestController
@RequestMapping("/admin/core/borrowInfo")
public class AdminBorrowInfoController {
@Resource
private BorrowInfoService borrowInfoService;
@ApiOperation("借款列表")
@GetMapping("/list")
public R selectList() {
List<BorrowInfo> borrowInfoList = borrowInfoService.selectList();
return R.ok().setData("list", borrowInfoList);
}
}
3、service
BorrowInfoService.java
List<BorrowInfo> selectList();
BorrowInfoServiceImpl.java
@Resource
private DictService dictService;
@Override
public List<BorrowInfo> selectList() {
List<BorrowInfo> borrowInfoList = baseMapper.selectBorrowInfoList();
borrowInfoList.forEach(borrowInfo -> {
setExtensionBorrowInfo(borrowInfo);
});
return borrowInfoList;
}
/**
* 设置BorrowInfo扩展的字段
* @param borrowInfo
*/
private void setExtensionBorrowInfo(BorrowInfo borrowInfo) {
String returnMethod = dictService.getNameByDictCodeAndValue("returnMethod", borrowInfo.getReturnMethod());
String moneyUse = dictService.getNameByDictCodeAndValue("moneyUse", borrowInfo.getMoneyUse());
String status = BorrowInfoStatusEnum.getMsgByStatus(borrowInfo.getStatus());
borrowInfo.getParam().put("returnMethod", returnMethod);
borrowInfo.getParam().put("moneyUse", moneyUse);
borrowInfo.getParam().put("status", status);
}
4、mapper
BorrowInfoMapper.java
List<BorrowInfo> selectBorrowInfoList();
BorrowInfoMapper.xml
<select id="selectBorrowInfoList" resultType="com.indi.srb.core.pojo.entity.BorrowInfo">
select bi.*,b.`name`,b.mobile
from borrow_info as bi
left join borrower as b
on bi.user_id = b.user_id
where b.is_deleted = 0
</select>
二、前端实现
1、创建页面组件
创建 src/views/core/borrow-info/list.vue
创建 src/views/core/borrow-info/detail.vue
2、配置路由
src/router/index.js
在“借款管理”下添加子路由
{
path: 'info-list',
name: 'coreBorrowInfoList',
component: () => import('@/views/core/borrow-info/list'),
meta: { title: '借款列表' }
},
{
path: 'info-detail/:id',
name: 'coreBorrowInfoDetail',
component: () => import('@/views/core/borrow-info/detail'),
meta: { title: '借款详情' },
hidden: true
}
3、定义api
创建 src/api/core/borrow-info.js
import request from '@/utils/request'
export default {
getList() {
return request({
url: `/admin/core/borrowInfo/list`,
method: 'get'
})
}
}
4、template
src/views/core/borrow-info/list.vue
<template>
<div class="app-container">
<!-- 列表 -->
<el-table :data="list" stripe>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="name" label="借款人姓名" width="90" />
<el-table-column prop="mobile" label="手机" />
<el-table-column prop="amount" label="借款金额" />
<el-table-column label="借款期限" width="90">
<template slot-scope="scope">{{ scope.row.period }}个月</template>
</el-table-column>
<el-table-column prop="param.returnMethod" label="还款方式" width="150" />
<el-table-column prop="param.moneyUse" label="资金用途" width="100" />
<el-table-column label="年化利率" width="90">
<template slot-scope="scope">
{{ scope.row.borrowYearRate * 100 }}%
</template>
</el-table-column>
<el-table-column prop="param.status" label="状态" width="100" />
<el-table-column prop="createTime" label="申请时间" width="150" />
<el-table-column label="操作" width="150" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini">
<router-link :to="'/core/borrower/info-detail/' + scope.row.id">
查看
</router-link>
</el-button>
<el-button
v-if="scope.row.status === 1"
type="warning"
size="mini"
@click="approvalShow(scope.row)"
>
审批
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
5、script
src/views/core/borrow-info/list.vue
<script>
import borrowInfoApi from '@/api/core/borrow-info'
export default {
data() {
return {
list: null // 列表
}
},
created() {
this.fetchData()
},
methods: {
// 加载列表数据
fetchData() {
borrowInfoApi.getList().then(response => {
this.list = response.data.list
})
}
}
}
</script>
借款详情
需求
借款信息列表点击查看,进入借款详情展示借款信息与借款人信息
一、后端实现
1、controller
AdminBorrowInfoController.java
@ApiOperation("借款详情")
@GetMapping("/getBorrowInfoDetail/{id}")
public R getBorrowInfoDetail(
@ApiParam(value = "借款信息的id", required = true)
@PathVariable Long id) {
Map<String, Object> map = borrowInfoService.getBorrowInfoDetail(id);
return R.ok().setData("borrowInfoDetail", map);
}
2、service
BorrowInfoService.java
Map<String, Object> getBorrowInfoDetail(Long id);
BorrowInfoServiceImpl.java
@Resource
private BorrowerService borrowerService;
@Override
public Map<String, Object> getBorrowInfoDetail(Long id) {
// 借款信息
BorrowInfo borrowInfo = baseMapper.selectById(id);
setExtensionBorrowInfo(borrowInfo);
Long userId = borrowInfo.getUserId();
QueryWrapper<Borrower> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id").eq("user_id", userId);
Borrower borrower = borrowerService.getBaseMapper().selectOne(queryWrapper);
// 借款人信息
BorrowerDetailVO borrowerDetailVO = borrowerService.getBorrowerDetailVO(borrower.getId());
Map<String, Object> map = new HashMap<>();
map.put("borrowInfo", borrowInfo);
map.put("borrower", borrowerDetailVO);
return map;
}
二、前端
1、定义api
src/api/core/borrow-info.js
show(id) {
return request({
url: `/admin/core/borrowInfo/show/${id}`,
method: 'get'
})
}
2、添加自定义css
src/styles/show.css
.app-container{
background: #fff;
position: absolute;
width:100%;
height: 100%;
overflow: auto;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
.table-bordered {
border: 1px solid #ddd;
}
.table-striped>tbody>tr:nth-child(odd)>td, .table-striped>tbody>tr:nth-child(odd)>th {
background-color: #f9f9f9;
}
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th,
.table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
font-size: 14px;
color: #333;
padding: 8px;
line-height: 1.42857143;
vertical-align: top;
border-top: 1px solid #ddd;
}
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th{
text-align: right;
width: 120px;
}
.table-bordered>thead>tr>th, .table-bordered>tbody>tr>th, .table-bordered>tfoot>tr>th, .table-bordered>thead>tr>td, .table-bordered>tbody>tr>td, .table-bordered>tfoot>tr>td {
border: 1px solid #ddd;
}
.active_content{
padding: 0 20px ;
border-radius: 4px;
border: 1px solid #ebeef5;
background-color: #fff;
overflow: hidden;
color: #303133;
transition: .3s;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
.active_content h4{
/*line-height: 0;*/
}
.active_content span{
font-size: 12px;
color: #999;
}
4、template
src/views/core/borrow-info/detail.vue
<template>
<div class="app-container">
<h4>借款信息</h4>
<table
class="table table-striped table-condenseda table-bordered"
width="100%"
>
<tbody>
<tr>
<th width="15%">借款金额</th>
<td width="35%">{{ borrowInfoDetail.borrowInfo.amount }}元</td>
<th width="15%">借款期限</th>
<td width="35%">{{ borrowInfoDetail.borrowInfo.period }}个月</td>
</tr>
<tr>
<th>年化利率</th>
<td>{{ borrowInfoDetail.borrowInfo.borrowYearRate * 100 }}%</td>
<th>还款方式</th>
<td>{{ borrowInfoDetail.borrowInfo.param.returnMethod }}</td>
</tr>
<tr>
<th>资金用途</th>
<td>{{ borrowInfoDetail.borrowInfo.param.moneyUse }}</td>
<th>状态</th>
<td>{{ borrowInfoDetail.borrowInfo.param.status }}</td>
</tr>
<tr>
<th>创建时间</th>
<td>{{ borrowInfoDetail.borrowInfo.createTime }}</td>
<th></th>
<td></td>
</tr>
</tbody>
</table>
<h4>借款人信息</h4>
<table
class="table table-striped table-condenseda table-bordered"
width="100%"
>
<tbody>
<tr>
<th width="15%">借款人</th>
<td width="35%">
<b>{{ borrowInfoDetail.borrower.name }}</b>
</td>
<th width="15%">手机</th>
<td width="35%">{{ borrowInfoDetail.borrower.mobile }}</td>
</tr>
<tr>
<th>身份证</th>
<td>{{ borrowInfoDetail.borrower.idCard }}</td>
<th>性别</th>
<td>{{ borrowInfoDetail.borrower.sex }}</td>
</tr>
<tr>
<th>年龄</th>
<td>{{ borrowInfoDetail.borrower.age }}</td>
<th>是否结婚</th>
<td>{{ borrowInfoDetail.borrower.marry }}</td>
</tr>
<tr>
<th>学历</th>
<td>{{ borrowInfoDetail.borrower.education }}</td>
<th>行业</th>
<td>{{ borrowInfoDetail.borrower.industry }}</td>
</tr>
<tr>
<th>月收入</th>
<td>{{ borrowInfoDetail.borrower.income }}</td>
<th>还款来源</th>
<td>{{ borrowInfoDetail.borrower.returnSource }}</td>
</tr>
<tr>
<th>创建时间</th>
<td>{{ borrowInfoDetail.borrower.createTime }}</td>
<th>状态</th>
<td>{{ borrowInfoDetail.borrower.status }}</td>
</tr>
</tbody>
</table>
<el-row style="text-align:center;margin-top: 40px;">
<el-button @click="back">
返回
</el-button>
</el-row>
</div>
</template>
3、script
src/views/core/borrow-info/detail.vue
<script>
import borrowInfoApi from '@/api/core/borrow-info'
import '@/styles/show.css'
export default {
data() {
return {
borrowInfoDetail: {
borrowInfo: {
param: {}
},
borrower: {}
}
}
},
created() {
if (this.$route.params.id) {
this.fetchDataById()
}
},
methods: {
fetchDataById() {
borrowInfoApi.show(this.$route.params.id).then(response => {
this.borrowInfoDetail = response.data.borrowInfoDetail
})
},
back() {
this.$router.push({ path: '/core/borrower/info-list' })
}
}
}
</script>
借款审批
需求
管理平台借款审批,审批通过后产生标的,审批前我们需要跟借款人进行电话沟通,确定借款年化和平台服务费率(平台收益),借款年化可能根据实际情况调高或调低;起息日通常我们把它确定为放款时间(或募集结束时间)
一、后端实现
1、vo
BorrowInfoApprovalVO .java
package com.indi.srb.core.pojo.vo;
@Data
@ApiModel(description = "借款信息审批")
public class BorrowInfoApprovalVO {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "审批内容")
private String content;
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "年化利率")
private BigDecimal lendYearRate;
@ApiModelProperty(value = "平台服务费率")
private BigDecimal serviceRate;
@ApiModelProperty(value = "开始日期")
private String lendStartDate;
@ApiModelProperty(value = "描述信息")
private String lendInfo;
}
2、controller
AdminBorrowInfoController.java
@ApiOperation("审批借款信息")
@PostMapping("/approval")
public R approval(@RequestBody BorrowInfoApprovalVO borrowInfoApprovalVO){
borrowInfoService.approval(borrowInfoApprovalVO);
return R.ok().setMessage("审批完成");
}
3、BorrowInfoService
BorrowInfoService.java
void approval(BorrowInfoApprovalVO borrowInfoApprovalVO);
BorrowInfoServiceImpl.java
@Resource
private LendService lendService;
@Transactional(rollbackFor = Exception.class)
@Override
public void approval(BorrowInfoApprovalVO borrowInfoApprovalVO) {
// 修改借款信息状态
Long id = borrowInfoApprovalVO.getId();
BorrowInfo borrowInfo = baseMapper.selectById(id);
borrowInfo.setStatus(borrowInfoApprovalVO.getStatus());
baseMapper.updateById(borrowInfo);
// 如果借款状态为审核通过的话,则生成一个标的
if (borrowInfoApprovalVO.getStatus().intValue() == BorrowInfoStatusEnum.CHECK_OK.getStatus().intValue()) {
//TODO
lendService.createLend(borrowInfoApprovalVO, borrowInfo);
}
}
3、编号辅助类
创建util
包
LendNoUtils.java
package com.indi.srb.core.util;
public class LendNoUtils {
public static String getNo() {
LocalDateTime time=LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String strDate = dtf.format(time);
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
return strDate + result;
}
public static String getLendNo() {
return "LEND" + getNo();
}
public static String getLendItemNo() {
return "INVEST" + getNo();
}
public static String getLoanNo() {
return "LOAN" + getNo();
}
public static String getReturnNo() {
return "RETURN" + getNo();
}
public static Object getWithdrawNo() {
return "WITHDRAW" + getNo();
}
public static String getReturnItemNo() {
return "RETURNITEM" + getNo();
}
public static String getChargeNo() {
return "CHARGE" + getNo();
}
/**
* 获取交易编码
*/
public static String getTransNo() {
return "TRANS" + getNo();
}
}
4、LendService
LendService.java
void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo);
LendServiceImpl.java
建议在idea中添加一个插件,GenerateAllSetter,可以快速生成对象的全部set方法。
@Override
public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
Lend lend = new Lend();
lend.setUserId(borrowInfo.getUserId());
lend.setBorrowInfoId(borrowInfo.getId());
lend.setLendNo(LendNoUtils.getLendNo());
lend.setTitle(borrowInfoApprovalVO.getTitle());
lend.setAmount(borrowInfo.getAmount());
lend.setPeriod(borrowInfo.getPeriod());
// 年化率(转小数)
lend.setLendYearRate(borrowInfoApprovalVO.getLendYearRate().divide(new BigDecimal(100)));
// 平台服务费率(转小数)
lend.setServiceRate(borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal(100)));
lend.setReturnMethod(borrowInfo.getReturnMethod());
lend.setLowestAmount(new BigDecimal(100));
lend.setInvestAmount(new BigDecimal(0));
lend.setInvestNum(0);
lend.setPublishDate(LocalDateTime.now());
// 起息日期
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate lendStartDate = LocalDate.parse(borrowInfoApprovalVO.getLendStartDate(), dateTimeFormatter);
lend.setLendStartDate(lendStartDate);
// 结束日期
lend.setLendEndDate(lendStartDate.plusMonths(borrowInfo.getPeriod()));
lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());
// 平台预期收益
// 每月年化利率 = 平台服务费率 / 12,保留小数点后8位,去除最后一位
BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);
// 平台预期收益:借款金额 * 月年化 * 借款期限
BigDecimal exceptAmount = borrowInfo.getAmount().multiply(monthRate).multiply(new BigDecimal(borrowInfo.getPeriod()));
lend.setExpectAmount(exceptAmount);
lend.setRealAmount(new BigDecimal(0));
lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());
lend.setCheckTime(LocalDateTime.now());
lend.setCheckAdminId(1L);
baseMapper.insert(lend);
}
二、前端实现
1、定义api
src/api/core/borrow-info.js
approval(borrowInfoApproval) {
return request({
url: '/admin/core/borrowInfo/approval',
method: 'post',
data: borrowInfoApproval
})
}
2、template
src/views/core/borrow-info/list.vue
<!-- 审批对话框 -->
<el-dialog title="审批" :visible.sync="dialogVisible" width="490px">
<el-form label-position="right" label-width="100px">
<el-form-item label="是否通过">
<el-radio-group v-model="borrowInfoApproval.status">
<el-radio :label="2">通过</el-radio>
<el-radio :label="-1">不通过</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="borrowInfoApproval.status == 2" label="标的名称">
<el-input v-model="borrowInfoApproval.title" />
</el-form-item>
<el-form-item v-if="borrowInfoApproval.status == 2" label="起息日">
<el-date-picker
v-model="borrowInfoApproval.lendStartDate"
type="date"
placeholder="选择开始时间"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item v-if="borrowInfoApproval.status == 2" label="年化收益率">
<el-input v-model="borrowInfoApproval.lendYearRate">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item v-if="borrowInfoApproval.status == 2" label="服务费率">
<el-input v-model="borrowInfoApproval.serviceRate">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item v-if="borrowInfoApproval.status == 2" label="标的描述">
<el-input v-model="borrowInfoApproval.lendInfo" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">
取消
</el-button>
<el-button type="primary" @click="approvalSubmit">
确定
</el-button>
</div>
</el-dialog>
3、script
src/views/core/borrow-info/list.vue
data
dialogVisible: false, //审批对话框
borrowInfoApproval: {
status: 2,
serviceRate: 5,
lendYearRate: 0 //初始化,解决表单中数据修改时无法及时渲染的问题
} //审批对象
methods
approvalShow(row) {
this.dialogVisible = true
this.borrowInfoApproval.id = row.id
this.borrowInfoApproval.lendYearRate = row.borrowYearRate * 100
},
approvalSubmit() {
borrowInfoApi.approval(this.borrowInfoApproval).then(response => {
this.dialogVisible = false
this.$message.success(response.message)
this.fetchData()
})
}