核心业务4:标的管理
1.标的管理流程图
2.数据库表设计
3.前端逻辑设计
4.后端逻辑设计
5.标的放款TODO
核心业务4:标的管理
1.标的管理流程图
①上一个核心业务通过审核借款申请结束之后产生新标的
②该标的在后台管理系统展示
③可以查看标的详情
④注意略过审核标的步骤,产生标的直接审核通过,进入状态募资中
2.数据库表设计
①标的表
- lend
②标的表关联
- 标的表通过user_id与user_info表关联(用户表)
- 标的表通过borrower_info_id与borrow_info表关联(借款信息表)
3.前端逻辑设计
①表单对象
-
标的列表
-
标的详情
②调用接口
- 前端内部跳转
- 调用后端api
③后端代码详细
- 路由定义(前端内部)
srb-admin\src\router\index.js
//标的管理路由
{
path: '/core/lend',
component: Layout,
name: 'coreLend',
meta: { title: '标的管理', icon: 'el-icon-s-flag' },
alwaysShow: true,
children: [
{
path: 'list',
name: 'coreLendList',
component: () => import('@/views/core/lend/list'),
meta: { title: '标的列表' },
},
{
path: 'detail/:id',
name: 'coreLendDetail',
component: () => import('@/views/core/lend/detail'),
meta: { title: '标的详情' },
hidden: true,
},
],
},
- 调用接口定义
srb-admin\src\api\core\lend.js
import request from '@/utils/request'
export default {
//标的列表
getList() {
return request({
url: `/admin/core/lend/list`,
method: 'get',
})
},
//标的详情
show(id) {
return request({
url: `/admin/core/lend/show/${id}`,
method: 'get',
})
},
}
- 列表页
srb-admin\src\views\core\lend\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="lendNo" label="标的编号" width="160" />
<el-table-column prop="amount" label="标的金额" />
<el-table-column prop="period" label="投资期数" />
<el-table-column label="年化利率">
<template slot-scope="scope">
{{ scope.row.lendYearRate * 100 }}%
</template>
</el-table-column>
<el-table-column prop="investAmount" label="已投金额" />
<el-table-column prop="investNum" label="投资人数" />
<el-table-column prop="publishDate" label="发布时间" width="150" />
<el-table-column prop="lendStartDate" label="开始日期" />
<el-table-column prop="lendEndDate" label="结束日期" />
<el-table-column prop="param.returnMethod" label="还款方式" />
<el-table-column prop="param.status" label="状态" />
<el-table-column label="操作" width="150" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini">
<router-link :to="'/core/lend/detail/' + scope.row.id">
查看
</router-link>
</el-button>
<el-button
v-if="scope.row.status == 1"
type="warning"
size="mini"
@click="makeLoan(scope.row.id)"
>
放款
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import lendApi from '@/api/core/lend'
export default {
data() {
return {
list: null, // 列表
}
},
created() {
this.fetchData()
},
methods: {
// 加载列表数据
fetchData() {
lendApi.getList().then((response) => {
this.list = response.data.list
})
},
},
}
</script>
- 详情页
srb-admin\src\views\core\lend\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%">
<b>{{ lendDetail.lend.lendNo }}</b>
</td>
<th width="15%">标题</th>
<td width="35%">{{ lendDetail.lend.title }}</td>
</tr>
<tr>
<th>标的金额</th>
<td>{{ lendDetail.lend.amount }}元</td>
<th>投资期数</th>
<td>{{ lendDetail.lend.period }}个月</td>
</tr>
<tr>
<th>年化利率</th>
<td>
标的:{{ lendDetail.lend.lendYearRate * 100 }}%
<br />
平台:{{ lendDetail.lend.serviceRate * 100 }}%
</td>
<th>还款方式</th>
<td>{{ lendDetail.lend.param.returnMethod }}</td>
</tr>
<tr>
<th>最低投资金额</th>
<td>{{ lendDetail.lend.lowestAmount }}元</td>
<th>发布日期</th>
<td>{{ lendDetail.lend.publishDate }}</td>
</tr>
<tr>
<th>开始日期</th>
<td>{{ lendDetail.lend.lendStartDate }}</td>
<th>结束日期</th>
<td>{{ lendDetail.lend.lendEndDate }}</td>
</tr>
<tr>
<th>已投金额</th>
<td>
<b>{{ lendDetail.lend.investAmount }}元</b>
</td>
<th>投资人数</th>
<td>{{ lendDetail.lend.investNum }}人</td>
</tr>
<tr>
<th>状态</th>
<td>
<b>{{ lendDetail.lend.param.status }}</b>
</td>
<th>创建时间</th>
<td>{{ lendDetail.lend.createTime }}</td>
</tr>
<tr>
<th>说明</th>
<td colspan="3">
<b>{{ lendDetail.lend.lendInfo }}</b>
</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>{{ lendDetail.lend.expectAmount }}元</b>
</td>
<th width="15%">标的已获取收益</th>
<td width="35%">{{ lendDetail.lend.realAmount }}元</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>{{ lendDetail.borrower.name }}</b>
</td>
<th width="15%">手机</th>
<td width="35%">{{ lendDetail.borrower.mobile }}</td>
</tr>
<tr>
<th>身份证</th>
<td>{{ lendDetail.borrower.idCard }}</td>
<th>性别</th>
<td>{{ lendDetail.borrower.sex }}</td>
</tr>
<tr>
<th>年龄</th>
<td>{{ lendDetail.borrower.age }}</td>
<th>是否结婚</th>
<td>{{ lendDetail.borrower.marry }}</td>
</tr>
<tr>
<th>学历</th>
<td>{{ lendDetail.borrower.education }}</td>
<th>行业</th>
<td>{{ lendDetail.borrower.industry }}</td>
</tr>
<tr>
<th>月收入</th>
<td>{{ lendDetail.borrower.income }}</td>
<th>还款来源</th>
<td>{{ lendDetail.borrower.returnSource }}</td>
</tr>
<tr>
<th>创建时间</th>
<td>{{ lendDetail.borrower.createTime }}</td>
<th>状态</th>
<td>{{ lendDetail.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>
<script>
import lendApi from '@/api/core/lend'
import '@/styles/show.css'
export default {
data() {
return {
lendDetail: {
lend: {
param: {},
},
borrower: {},
},
}
},
created() {
if (this.$route.params.id) {
this.fetchDataById()
}
},
methods: {
fetchDataById() {
lendApi.show(this.$route.params.id).then((response) => {
this.lendDetail = response.data.lendDetail
})
},
back() {
this.$router.push({ path: '/core/lend/list' })
},
},
}
</script>
4.后端逻辑设计
①后端对象
- 列表对象
com.atguigu.srb.core.pojo.entity.Lend
//扩充的其他参数
@ApiModelProperty(value = "其他参数")
@TableField(exist = false)
private Map<String,Object> param = new HashMap<>();
- 详情对象
封装为map
②后端接口
③后端代码(完成核心业务3中最后的10.TODO创建标的)
- 调用位置
com.atguigu.srb.core.service.impl.BorrowInfoServiceImpl
if(borrowInfoApprovalVO.getStatus().intValue() == BorrowInfoStatusEnum.CHECK_OK.getStatus().intValue()){
//创建新标的
//TODO
lendService.createLend(borrowInfoApprovalVO,borrowInfo);
}
- service编写
com.atguigu.srb.core.service.LendService
void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo);
/**
* @param borrowInfoApprovalVO:
* @param borrowInfo:
* @return void
* @author Likejin
* @description 审批后生成新标的的流程
* @date 2023/4/17 9:18
*/
@Override
public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
Lend lend = new Lend();
//填充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));//初始化最低投款金额为100
lend.setInvestAmount(new BigDecimal(0));//初始化已投金额为0
lend.setInvestNum(0);//初始化已投人为0
lend.setPublishDate(LocalDateTime.now());//初始化表弟发布时间
//计算开始日期和结束日期
String lendStartDateVo = borrowInfoApprovalVO.getLendStartDate();
//计算起息日期
//转换时间(字符串到LocalDate)
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate lendStartDate = LocalDate.parse(lendStartDateVo, dateTimeFormatter);
lend.setLendStartDate(lendStartDate);
//计算结束日期
LocalDate lendEndDate = lendStartDate.plusMonths(borrowInfo.getPeriod());
lend.setLendEndDate(lendEndDate);
//描述
lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());
//设置平台预期受益(年费费率转化为月化费率然后乘以借款月份)标的金额 * 年化/12 * 期数
BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);//小数点精确,抹零
BigDecimal expectMount = lend.getAmount().multiply(monthRate.multiply(new BigDecimal(lend.getPeriod())));
lend.setExpectAmount(expectMount);//预期受益
lend.setRealAmount(new BigDecimal(0));//实际收益
lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());//标的状态
//标的不需审核通过,生成标的直接通过,直接审核通过的时间
lend.setCheckTime(LocalDateTime.now());//审核通过时间
//硬编码
lend.setCheckAdminId(1L);//设置审核人
//存入数据库
baseMapper.insert(lend);
}
④后端代码(展示标的列表和标的详情)
- controller
package com.atguigu.srb.core.controller.admin;
import com.atguigu.common.result.R;
import com.atguigu.srb.core.pojo.entity.Lend;
import com.atguigu.srb.core.service.LendService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@Api(tags = "标的管理")
@RestController
@RequestMapping("/admin/core/lend")
@Slf4j
public class AdminLendController {
@Resource
private LendService lendService;
@ApiOperation("标的列表")
@GetMapping("/list")
public R list() {
List<Lend> lendList = lendService.selectList();
return R.ok().data("list", lendList);
}
@ApiOperation("获取标的信息")
@GetMapping("/show/{id}")
public R show(
@ApiParam(value = "标的id", required = true)
@PathVariable Long id) {
Map<String, Object> result = lendService.getLendDetail(id);
return R.ok().data("lendDetail", result);
}
}
- service
package com.atguigu.srb.core.service;
import com.atguigu.srb.core.pojo.entity.BorrowInfo;
import com.atguigu.srb.core.pojo.entity.Lend;
import com.atguigu.srb.core.pojo.vo.BorrowInfoApprovalVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
/**
* <p>
* 标的准备表 服务类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
public interface LendService extends IService<Lend> {
void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo);
List<Lend> selectList();
Map<String,Object> getLendDetail(Long id);
}
package com.atguigu.srb.core.service.impl;
import com.atguigu.srb.core.enums.LendStatusEnum;
import com.atguigu.srb.core.mapper.BorrowerMapper;
import com.atguigu.srb.core.mapper.LendMapper;
import com.atguigu.srb.core.pojo.entity.BorrowInfo;
import com.atguigu.srb.core.pojo.entity.Borrower;
import com.atguigu.srb.core.pojo.entity.Lend;
import com.atguigu.srb.core.pojo.vo.BorrowInfoApprovalVO;
import com.atguigu.srb.core.pojo.vo.BorrowerDetailVO;
import com.atguigu.srb.core.service.BorrowerService;
import com.atguigu.srb.core.service.DictService;
import com.atguigu.srb.core.service.LendService;
import com.atguigu.srb.core.util.LendNoUtils;
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.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 标的准备表 服务实现类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {
@Resource
private DictService dictService;
@Resource
private BorrowerMapper borrowerMapper;
@Resource
private BorrowerService borrowerService;
/**
* @param borrowInfoApprovalVO:
* @param borrowInfo:
* @return void
* @author Likejin
* @description 审批后生成新标的的流程
* @date 2023/4/17 9:18
*/
@Override
public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
Lend lend = new Lend();
//填充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));//初始化最低投款金额为100
lend.setInvestAmount(new BigDecimal(0));//初始化已投金额为0
lend.setInvestNum(0);//初始化已投人为0
lend.setPublishDate(LocalDateTime.now());//初始化表弟发布时间
//计算开始日期和结束日期
String lendStartDateVo = borrowInfoApprovalVO.getLendStartDate();
//计算起息日期
//转换时间(字符串到LocalDate)
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate lendStartDate = LocalDate.parse(lendStartDateVo, dateTimeFormatter);
lend.setLendStartDate(lendStartDate);
//计算结束日期
LocalDate lendEndDate = lendStartDate.plusMonths(borrowInfo.getPeriod());
lend.setLendEndDate(lendEndDate);
//描述
lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());
//设置平台预期受益(年费费率转化为月化费率然后乘以借款月份)标的金额 * 年化/12 * 期数
BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);//小数点精确,抹零
BigDecimal expectMount = lend.getAmount().multiply(monthRate.multiply(new BigDecimal(lend.getPeriod())));
lend.setExpectAmount(expectMount);//预期受益
lend.setRealAmount(new BigDecimal(0));//实际收益
lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());//标的状态
//标的不需审核通过,生成标的直接通过,直接审核通过的时间
lend.setCheckTime(LocalDateTime.now());//审核通过时间
//硬编码
lend.setCheckAdminId(1L);//设置审核人
//存入数据库
baseMapper.insert(lend);
}
/**
* @param :
* @return List<Lend>
* @author Likejin
* @description 后台管理系统展示标的
* @date 2023/4/17 9:50
*/
@Override
public List<Lend> selectList() {
List<Lend> lendList = baseMapper.selectList(null);
lendList.forEach(lend -> {
//转换编码为字符串
String returnMethod = dictService.getNameByParentDictCodeAndValue("returnMethod",lend.getReturnMethod());
String status = LendStatusEnum.getMsgByStatus(lend.getStatus());
lend.getParam().put("returnMethod",returnMethod);
lend.getParam().put("status",status);
});
return lendList;
}
/**
* @param id:
* @return Map<String,Object>
* @author Likejin
* @description 获取标的详情
* @date 2023/4/17 9:57
*/
@Override
public Map<String, Object> getLendDetail(Long id) {
//查询标的对象lend
Lend lend = baseMapper.selectById(id);
//将lend数据组装编码为字符串
String returnMethod = dictService.getNameByParentDictCodeAndValue("returnMethod", lend.getReturnMethod());
String status = LendStatusEnum.getMsgByStatus(lend.getStatus());
lend.getParam().put("returnMethod", returnMethod);
lend.getParam().put("status", status);
//根据user_id获取借款人对象(写过,借用前面的数据)
QueryWrapper<Borrower> borrowerQueryWrapper = new QueryWrapper<Borrower>();
borrowerQueryWrapper.eq("user_id", lend.getUserId());
Borrower borrower = borrowerMapper.selectOne(borrowerQueryWrapper);
//组装借款人对象
BorrowerDetailVO borrowerDetailVO = borrowerService.getBorrowerDetailVOById(borrower.getId());
//组装数据
Map<String, Object> result = new HashMap<>();
result.put("lend", lend);
result.put("borrower", borrowerDetailVO);
return result;
}
}
- 调用工具类实现业务编号的生成
package com.atguigu.srb.core.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
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();
}
}
⑤后端业务逻辑
- 新增标的
标的对应的表为lend表。
在审批时增加lend表时可以获得审批表borrowInfoApprovalVO和具体的借款表BorrowInfo,通过这两个封装lend。注意其中计算预期受益,转换时间类型(前端传来字符串转换为时间类型) - 标的列表
在获取标的列表时需要转换编码为字符串(Dict表) - 获取标的详情
通过标的id获取标的id,先转换标的信息的编码,根据标的的user_id获取borrower借款人信息,获取借款人信息详情(之前做过),组装到map集合中