项目背景
本系统主要用于地图站位小程序,通过地图站位展示贷款相关的地理位置信息,支持按不同条件筛选和统计,帮助业务人员更好地进行贷后管理。
核心功能分析
1. 功能架构
系统支持以下核心功能:
- 地图标记点展示(客户、担保人、抵押物)
- 表内外数据区分
- 距离范围筛选
- 多维度条件查询
- 贷款状态统计
2. Controller层实现
@RestController
@RequestMapping("/bank/app/mapwork")
public class AppMapworkController {
@Autowired
private AppMapworkService appMapworkService;
@GetMapping("/index")
@Operation(summary = "首页")
public CommonResult<?> getIndex(AppMapworkReqVO req) {
Map<String, Object> data = new HashMap<>();
data.put("normal",0);
data.put("overdue",0);
data.put("lawsuit",0);
List<AppMapworkRespVO> result = new ArrayList<>();
// IO标志处理逻辑
boolean inFlag = false;
boolean outFlag = false;
if(StringUtils.isEmpty(req.getIoFlag())){
inFlag = true;
outFlag = true;
}else{
if("in".equals(req.getIoFlag())){
inFlag = true;
}else if("out".equals(req.getIoFlag())){
outFlag = true;
}else if("all".equals(req.getIoFlag())){
inFlag = true;
outFlag = true;
}
}
// 表内数据查询
if(inFlag){
if(StringUtils.isEmpty(req.getOther())){
// 查询全部类型
List<AppMapworkRespVO> custList = appMapworkService.getCustomerInMark(req);
List<AppMapworkRespVO> guarantorList = appMapworkService.getGuarantorInMark(req);
List<AppMapworkRespVO> mortgageList = appMapworkService.getMortgageInMark(req);
result.addAll(custList);
result.addAll(guarantorList);
result.addAll(mortgageList);
}else{
// 按类型查询
if("cust".equals(req.getOther())){
// 客户查询,特殊处理地址标记点
List<AppMapworkRespVO> custList = appMapworkService.getCustomerInMark(req);
if(UxUtil.isListNotEmpty(custList) && custList.size() == 1){
AppMapworkRespVO cust = custList.get(0);
List<AppMapworkRespVO> addressList = appMapworkService.getCustomerInAddressMark(req,cust.getMarkId());
if(UxUtil.isListNotEmpty(addressList)){
for(AppMapworkRespVO c : addressList){
c.setLoanId(cust.getLoanId());
c.setCustName(cust.getCustName());
c.setContractCode(cust.getContractCode());
c.setAmount(cust.getAmount());
}
result.addAll(addressList);
}
}
result.addAll(custList);
}
// ... 其他类型处理
}
}
// 表外数据查询(逻辑类似)
if(outFlag){
// ... 同表内处理逻辑
}
data.put("marks",result);
data.put("total",result.size());
// 贷款状态统计
if(StringUtils.isNotEmpty(req.getOther()) && "cust".equals(req.getOther()) && UxUtil.isListNotEmpty(result)){
Long normalCount = result.stream().filter(o -> (o.getOverdueFlag() == 0 && o.getLawsuitFlag() == 0)).count();
Long overdueCount = result.stream().filter(o -> (o.getOverdueFlag() == 1 && o.getLawsuitFlag() == 0)).count();
Long lawsuitCount = result.stream().filter(o -> (o.getLawsuitFlag() == 1)).count();
data.put("normal",normalCount);
data.put("overdue",overdueCount);
data.put("lawsuit",lawsuitCount);
}
return success(data);
}
}
Service层设计
@Service
@Validated
public class AppMapworkServiceImpl implements AppMapworkService {
@Autowired
private MapworkMapper mapworkMapper;
@Override
public List<AppMapworkRespVO> getCustomerInMark(AppMapworkReqVO req) {
LambdaQueryWrapper<LoanInDO> queryWrapper = new LambdaQueryWrapper<>();
// 模糊查询支持(客户名/代码/合同号)
if(StringUtils.isNotEmpty(req.getCustName())){
queryWrapper.apply(" (m.cust_name like {0} or m.cust_code like {1} or m.contract_code like {2} )",
UxUtil.likeForSql(req.getCustName()),UxUtil.likeForSql(req.getCustName()),UxUtil.likeForSql(req.getCustName()));
}
// 状态筛选
if(StringUtils.isNotEmpty(req.getStatus())){
if("normal".equals(req.getStatus())){
queryWrapper.apply(" (m.overdue_flag = {0} and m.lawsuit_flag = {1})", 0,0);
}else if("overdue".equals(req.getStatus())){
queryWrapper.apply(" (m.overdue_flag = {0} and m.lawsuit_flag = {1}) ", 1,0);
}else if("lawsuit".equals(req.getStatus())){
queryWrapper.apply(" m.lawsuit_flag = {0} ", 1);
}
}
return mapworkMapper.getCustomerInMark(req,queryWrapper);
}
// 其他方法实现类似...
}
Mapper层核心技术
1. 站位计算
MapworkMapper.java
public interface MapworkMapper {
List<AppMapworkRespVO> getCustomerInMark(@Param("req") AppMapworkReqVO req, @Param("ew") LambdaQueryWrapper<LoanInDO> queryWrapper);
...
}
MapworkMapper.xml
<select id="getCustomerInMark" resultType="com.mxx.module.bank.controller.admin.app.vo.AppMapworkRespVO">
SELECT
cu.id * 10 + 1 as id,
cu.id as mark_id,
'ux_customer_in' as mark_type,
cu.latitude as lat,
cu.longitude as lng,
m.overdue_flag,
m.lawsuit_flag,
1 as in_out_flag,
m.id as loan_id,
m.cust_code,
m.cust_name,
m.cust_id,
m.contract_code,
m.amount,
cu.contact_way,
cu.detail_address as address,
<!-- 核心距离计算公式 -->
ROUND(111.111 * sqrt(pow(cu.latitude - #{req.latitude}, 2) +
pow(cos(radians(#{req.latitude})) * (cu.longitude - #{req.longitude}),2)), 2) AS distance
FROM
ux_loan_in m
LEFT JOIN ux_customer_in cu ON cu.id = m.cust_id
WHERE 1 = 1
<if test="ew != null">
<if test="ew.sqlSegment != null">
<if test="ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</if>
</if>
AND m.deleted = 0
AND cu.deleted = 0
AND cu.latitude is not null
AND cu.longitude is not null
HAVING #{req.radius} > distance
</select>
2. 唯一ID生成策略
-- 不同类型标记点的唯一ID生成,避免ID冲突
cu.id * 10 + 1 as id, -- 客户表内
cu.id * 10 + 2 as id, -- 客户表外
cu.id * 10 + 3 as id, -- 担保人表内
cu.id * 10 + 4 as id, -- 担保人表外
技术亮点解析
1. 灵活的查询策略
系统支持多种组合查询:
ioFlag
: in/out/all(表内/表外/全部)other
: cust/guarant/mortgage/all(客户/担保人/抵押物/全部)status
: normal/overdue/lawsuit(正常/逾期/诉讼)
2. 地理位置计算优化
-- 基于经纬度的距离计算,考虑地球曲率
ROUND(111.111 * sqrt(pow(cu.latitude - #{req.latitude}, 2) +
pow(cos(radians(#{req.latitude})) * (cu.longitude - #{req.longitude}),2)), 2) AS distance
3. 数据聚合设计
// 多种业务类型统一返回VO
List<AppMapworkRespVO> result = new ArrayList<>();
result.addAll(custList); // 客户
result.addAll(guarantorList); // 担保人
result.addAll(mortgageList); // 抵押物
总结
本文介绍的银行贷款地理位置标记系统具有以下优势:
- 架构清晰:Controller-Service-Mapper三层架构
- 扩展性强:易于添加新的标记点类型
- 性能优化:地理位置计算和查询优化
- 用户体验:丰富的筛选和统计功能
这套解决方案在实际项目中表现良好,为银行的贷后管理提供了有力的技术支撑。