Merge branch 'codex/credit-info-maintenance-backend' into dev
This commit is contained in:
@@ -29,6 +29,12 @@
|
||||
<artifactId>ruoyi-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ccdi-lsfx</artifactId>
|
||||
<version>3.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- easyexcel工具 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.ruoyi.info.collection.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.PageDomain;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.page.TableSupport;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
|
||||
import com.ruoyi.info.collection.service.ICcdiCreditInfoService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 征信维护 Controller
|
||||
*/
|
||||
@Tag(name = "征信维护")
|
||||
@RestController
|
||||
@RequestMapping("/ccdi/creditInfo")
|
||||
public class CcdiCreditInfoController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private ICcdiCreditInfoService creditInfoService;
|
||||
|
||||
@Operation(summary = "上传征信 HTML")
|
||||
@PreAuthorize("@ss.hasPermi('ccdi:creditInfo:upload')")
|
||||
@Log(title = "征信维护", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/upload")
|
||||
public AjaxResult upload(@RequestParam("files") MultipartFile[] files) {
|
||||
return AjaxResult.success("上传成功", creditInfoService.upload(Arrays.asList(files)));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询征信维护列表")
|
||||
@PreAuthorize("@ss.hasPermi('ccdi:creditInfo:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(CcdiCreditInfoQueryDTO queryDTO) {
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
Page<CreditInfoListVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
||||
Page<CreditInfoListVO> result = creditInfoService.selectCreditInfoPage(page, queryDTO);
|
||||
return getDataTable(result.getRecords(), result.getTotal());
|
||||
}
|
||||
|
||||
@Operation(summary = "查询征信维护详情")
|
||||
@PreAuthorize("@ss.hasPermi('ccdi:creditInfo:query')")
|
||||
@GetMapping("/{personId}")
|
||||
public AjaxResult detail(@PathVariable String personId) {
|
||||
return success(creditInfoService.selectDetailByPersonId(personId));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除征信维护数据")
|
||||
@PreAuthorize("@ss.hasPermi('ccdi:creditInfo:remove')")
|
||||
@Log(title = "征信维护", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{personId}")
|
||||
public AjaxResult remove(@PathVariable String personId) {
|
||||
return toAjax(creditInfoService.deleteByPersonId(personId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.info.collection.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 员工征信负面信息对象 ccdi_credit_negative_info
|
||||
*/
|
||||
@Data
|
||||
@TableName("ccdi_credit_negative_info")
|
||||
public class CcdiCreditNegativeInfo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long negativeId;
|
||||
|
||||
private String personId;
|
||||
private String personName;
|
||||
private Date queryDate;
|
||||
private Integer civilCnt;
|
||||
private Integer enforceCnt;
|
||||
private Integer admCnt;
|
||||
private BigDecimal civilLmt;
|
||||
private BigDecimal enforceLmt;
|
||||
private BigDecimal admLmt;
|
||||
private String createBy;
|
||||
private Date createTime;
|
||||
private String updateBy;
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.ruoyi.info.collection.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 员工征信负债明细对象 ccdi_debts_info
|
||||
*/
|
||||
@Data
|
||||
@TableName("ccdi_debts_info")
|
||||
public class CcdiDebtsInfo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long debtId;
|
||||
|
||||
private String personId;
|
||||
private String personName;
|
||||
private Date queryDate;
|
||||
private String debtMainType;
|
||||
private String debtSubType;
|
||||
private String creditorType;
|
||||
private String debtName;
|
||||
private BigDecimal principalBalance;
|
||||
private BigDecimal debtTotalAmount;
|
||||
private String debtStatus;
|
||||
private String createBy;
|
||||
private Date createTime;
|
||||
private String updateBy;
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ruoyi.info.collection.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 征信维护查询 DTO
|
||||
*/
|
||||
@Data
|
||||
public class CcdiCreditInfoQueryDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
private String staffId;
|
||||
private String idCard;
|
||||
private String maintained;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 征信详情聚合 VO
|
||||
*/
|
||||
@Data
|
||||
public class CreditInfoDetailVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String personId;
|
||||
private String personName;
|
||||
private String idCard;
|
||||
private CreditInfoNegativeVO negativeInfo;
|
||||
private List<CcdiDebtsInfo> debtList;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 征信维护列表 VO
|
||||
*/
|
||||
@Data
|
||||
public class CreditInfoListVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long staffId;
|
||||
private String name;
|
||||
private String idCard;
|
||||
private String deptName;
|
||||
private Date queryDate;
|
||||
private Long debtCount;
|
||||
private BigDecimal debtTotalAmount;
|
||||
private Integer civilCnt;
|
||||
private Integer enforceCnt;
|
||||
private Integer admCnt;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 征信负面信息展示 VO
|
||||
*/
|
||||
@Data
|
||||
public class CreditInfoNegativeVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String personId;
|
||||
private String personName;
|
||||
private Date queryDate;
|
||||
private Integer civilCnt;
|
||||
private Integer enforceCnt;
|
||||
private Integer admCnt;
|
||||
private BigDecimal civilLmt;
|
||||
private BigDecimal enforceLmt;
|
||||
private BigDecimal admLmt;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 征信上传失败结果 VO
|
||||
*/
|
||||
@Data
|
||||
public class CreditInfoUploadFailureVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String fileName;
|
||||
private String personId;
|
||||
private String personName;
|
||||
private String reason;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ruoyi.info.collection.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 征信上传结果 VO
|
||||
*/
|
||||
@Data
|
||||
public class CreditInfoUploadResultVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer totalCount;
|
||||
private Integer successCount;
|
||||
private Integer failureCount;
|
||||
private List<CreditInfoUploadFailureVO> failures = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.ruoyi.info.collection.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoDetailVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 征信维护聚合查询 Mapper
|
||||
*/
|
||||
public interface CcdiCreditInfoQueryMapper {
|
||||
|
||||
Page<CreditInfoListVO> selectCreditInfoPage(@Param("page") Page<CreditInfoListVO> page,
|
||||
@Param("query") CcdiCreditInfoQueryDTO queryDTO);
|
||||
|
||||
CreditInfoListVO selectCreditInfoSummaryByPersonId(@Param("personId") String personId);
|
||||
|
||||
CreditInfoDetailVO selectCreditInfoDetailByPersonId(@Param("personId") String personId);
|
||||
|
||||
List<CreditInfoListVO> selectCreditInfoList(@Param("query") CcdiCreditInfoQueryDTO queryDTO);
|
||||
|
||||
LocalDate selectLatestQueryDate(@Param("personId") String personId);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ruoyi.info.collection.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 员工征信负面信息 Mapper
|
||||
*/
|
||||
public interface CcdiCreditNegativeInfoMapper extends BaseMapper<CcdiCreditNegativeInfo> {
|
||||
|
||||
CcdiCreditNegativeInfo selectByPersonId(@Param("personId") String personId);
|
||||
|
||||
int deleteByPersonId(@Param("personId") String personId);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ruoyi.info.collection.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 员工征信负债明细 Mapper
|
||||
*/
|
||||
public interface CcdiDebtsInfoMapper extends BaseMapper<CcdiDebtsInfo> {
|
||||
|
||||
List<CcdiDebtsInfo> selectByPersonId(@Param("personId") String personId);
|
||||
|
||||
int deleteByPersonId(@Param("personId") String personId);
|
||||
|
||||
int insertBatch(@Param("list") List<CcdiDebtsInfo> list);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.info.collection.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoDetailVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 征信维护服务接口
|
||||
*/
|
||||
public interface ICcdiCreditInfoService {
|
||||
|
||||
CreditInfoUploadResultVO upload(List<MultipartFile> files);
|
||||
|
||||
Page<CreditInfoListVO> selectCreditInfoPage(Page<CreditInfoListVO> page, CcdiCreditInfoQueryDTO queryDTO);
|
||||
|
||||
CreditInfoDetailVO selectDetailByPersonId(String personId);
|
||||
|
||||
int deleteByPersonId(String personId);
|
||||
|
||||
void replaceEmployeeCredit(String personId, List<CcdiDebtsInfo> debts, CcdiCreditNegativeInfo negative, String userName);
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package com.ruoyi.info.collection.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoDetailVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoNegativeVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadFailureVO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
|
||||
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
||||
import com.ruoyi.info.collection.service.ICcdiCreditInfoService;
|
||||
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 征信维护服务实现
|
||||
*/
|
||||
@Service
|
||||
public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
||||
|
||||
@Resource
|
||||
private CreditParseClient creditParseClient;
|
||||
|
||||
@Resource
|
||||
private CreditInfoPayloadAssembler assembler;
|
||||
|
||||
@Resource
|
||||
private CcdiBaseStaffMapper baseStaffMapper;
|
||||
|
||||
@Resource
|
||||
private CcdiDebtsInfoMapper debtsInfoMapper;
|
||||
|
||||
@Resource
|
||||
private CcdiCreditNegativeInfoMapper negativeInfoMapper;
|
||||
|
||||
@Resource
|
||||
private CcdiCreditInfoQueryMapper queryMapper;
|
||||
|
||||
@Override
|
||||
public CreditInfoUploadResultVO upload(List<MultipartFile> files) {
|
||||
CreditInfoUploadResultVO result = new CreditInfoUploadResultVO();
|
||||
List<CreditInfoUploadFailureVO> failures = new ArrayList<>();
|
||||
int totalCount = files == null ? 0 : files.size();
|
||||
int successCount = 0;
|
||||
String userName = currentUserName();
|
||||
|
||||
if (files == null || files.isEmpty()) {
|
||||
result.setTotalCount(0);
|
||||
result.setSuccessCount(0);
|
||||
result.setFailureCount(0);
|
||||
result.setFailures(failures);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
try {
|
||||
validateHtmlFile(file);
|
||||
handleSingleFile(file, userName);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
failures.add(buildFailure(file, null, null, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
result.setTotalCount(totalCount);
|
||||
result.setSuccessCount(successCount);
|
||||
result.setFailureCount(failures.size());
|
||||
result.setFailures(failures);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<CreditInfoListVO> selectCreditInfoPage(Page<CreditInfoListVO> page, CcdiCreditInfoQueryDTO queryDTO) {
|
||||
return queryMapper.selectCreditInfoPage(page, queryDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreditInfoDetailVO selectDetailByPersonId(String personId) {
|
||||
CreditInfoListVO summary = queryMapper.selectCreditInfoSummaryByPersonId(personId);
|
||||
CcdiCreditNegativeInfo negative = negativeInfoMapper.selectByPersonId(personId);
|
||||
List<CcdiDebtsInfo> debts = debtsInfoMapper.selectByPersonId(personId);
|
||||
if (summary == null && negative == null && (debts == null || debts.isEmpty())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CreditInfoDetailVO detail = new CreditInfoDetailVO();
|
||||
detail.setPersonId(personId);
|
||||
detail.setIdCard(summary != null && !isBlank(summary.getIdCard()) ? summary.getIdCard() : personId);
|
||||
if (summary != null) {
|
||||
detail.setPersonName(summary.getName());
|
||||
}
|
||||
if (isBlank(detail.getPersonName()) && negative != null) {
|
||||
detail.setPersonName(negative.getPersonName());
|
||||
}
|
||||
if (isBlank(detail.getPersonName()) && debts != null && !debts.isEmpty()) {
|
||||
detail.setPersonName(debts.get(0).getPersonName());
|
||||
}
|
||||
detail.setNegativeInfo(toNegativeVO(negative));
|
||||
detail.setDebtList(debts == null ? List.of() : debts);
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteByPersonId(String personId) {
|
||||
int debtCount = debtsInfoMapper.deleteByPersonId(personId);
|
||||
int negativeCount = negativeInfoMapper.deleteByPersonId(personId);
|
||||
return debtCount + negativeCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void replaceEmployeeCredit(String personId, List<CcdiDebtsInfo> debts, CcdiCreditNegativeInfo negative, String userName) {
|
||||
debtsInfoMapper.deleteByPersonId(personId);
|
||||
negativeInfoMapper.deleteByPersonId(personId);
|
||||
if (debts != null && !debts.isEmpty()) {
|
||||
debts.forEach(item -> {
|
||||
item.setCreateBy(userName);
|
||||
item.setUpdateBy(userName);
|
||||
});
|
||||
debtsInfoMapper.insertBatch(debts);
|
||||
}
|
||||
if (negative != null) {
|
||||
negative.setCreateBy(userName);
|
||||
negative.setUpdateBy(userName);
|
||||
negativeInfoMapper.insert(negative);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSingleFile(MultipartFile multipartFile, String userName) throws IOException {
|
||||
File tempFile = createTempFile(multipartFile);
|
||||
try {
|
||||
CreditParseResponse response = creditParseClient.parse("LXCUSTALL", "PERSON", tempFile);
|
||||
CreditParsePayload payload = requireResponse(response).getPayload();
|
||||
Map<String, Object> header = requireHeader(payload);
|
||||
String personId = stringValue(header.get("query_cert_no"));
|
||||
String personName = stringValue(header.get("query_cust_name"));
|
||||
LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time")));
|
||||
ensureStaffExists(personId);
|
||||
ensureLatestQueryDate(personId, queryDate);
|
||||
|
||||
List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
|
||||
CcdiCreditNegativeInfo negative = assembler.buildNegative(personId, personName, queryDate, payload);
|
||||
replaceEmployeeCredit(personId, debts, negative, userName);
|
||||
} finally {
|
||||
if (tempFile.exists()) {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File createTempFile(MultipartFile multipartFile) throws IOException {
|
||||
String originalFilename = multipartFile.getOriginalFilename();
|
||||
String suffix = ".html";
|
||||
if (originalFilename != null && originalFilename.contains(".")) {
|
||||
suffix = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||
}
|
||||
File tempFile = File.createTempFile("credit-info-", suffix);
|
||||
multipartFile.transferTo(tempFile);
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
private void validateHtmlFile(MultipartFile file) {
|
||||
String originalFilename = file == null ? null : file.getOriginalFilename();
|
||||
if (originalFilename == null) {
|
||||
throw new RuntimeException("上传文件不能为空");
|
||||
}
|
||||
String lowerName = originalFilename.toLowerCase();
|
||||
if (!lowerName.endsWith(".html") && !lowerName.endsWith(".htm")) {
|
||||
throw new RuntimeException("仅支持上传.html或.htm征信文件");
|
||||
}
|
||||
}
|
||||
|
||||
private CreditParseResponse requireResponse(CreditParseResponse response) {
|
||||
if (response == null || response.getPayload() == null) {
|
||||
throw new RuntimeException("征信解析结果为空");
|
||||
}
|
||||
if (!"0".equals(response.getStatusCode())) {
|
||||
throw new RuntimeException(stringValue(response.getMessage(), "征信解析失败"));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private Map<String, Object> requireHeader(CreditParsePayload payload) {
|
||||
Map<String, Object> header = payload.getLxHeader();
|
||||
if (header == null || header.isEmpty()) {
|
||||
throw new RuntimeException("征信解析结果缺少头信息");
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
private void ensureStaffExists(String personId) {
|
||||
if (isBlank(personId)) {
|
||||
throw new RuntimeException("征信解析结果缺少员工身份证号");
|
||||
}
|
||||
CcdiBaseStaff staff = baseStaffMapper.selectOne(new LambdaQueryWrapper<CcdiBaseStaff>()
|
||||
.eq(CcdiBaseStaff::getIdCard, personId)
|
||||
.last("LIMIT 1"));
|
||||
if (staff == null) {
|
||||
throw new RuntimeException("未找到对应员工信息");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureLatestQueryDate(String personId, LocalDate queryDate) {
|
||||
LocalDate latestQueryDate = queryMapper.selectLatestQueryDate(personId);
|
||||
if (latestQueryDate != null && queryDate.isBefore(latestQueryDate)) {
|
||||
throw new RuntimeException("上传征信日期早于当前已维护最新记录");
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDate parseQueryDate(String reportTime) {
|
||||
if (isBlank(reportTime)) {
|
||||
throw new RuntimeException("征信解析结果缺少征信查询日期");
|
||||
}
|
||||
String normalized = reportTime.trim();
|
||||
if (normalized.length() > 10) {
|
||||
normalized = normalized.substring(0, 10);
|
||||
}
|
||||
return LocalDate.parse(normalized);
|
||||
}
|
||||
|
||||
private CreditInfoUploadFailureVO buildFailure(MultipartFile file, String personId, String personName, String reason) {
|
||||
CreditInfoUploadFailureVO failure = new CreditInfoUploadFailureVO();
|
||||
failure.setFileName(file == null ? null : file.getOriginalFilename());
|
||||
failure.setPersonId(personId);
|
||||
failure.setPersonName(personName);
|
||||
failure.setReason(reason);
|
||||
return failure;
|
||||
}
|
||||
|
||||
private String stringValue(Object value) {
|
||||
return stringValue(value, null);
|
||||
}
|
||||
|
||||
private String stringValue(Object value, String defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
String text = value.toString().trim();
|
||||
return text.isEmpty() ? defaultValue : text;
|
||||
}
|
||||
|
||||
private boolean isBlank(String text) {
|
||||
return text == null || text.trim().isEmpty();
|
||||
}
|
||||
|
||||
private CreditInfoNegativeVO toNegativeVO(CcdiCreditNegativeInfo negative) {
|
||||
if (negative == null) {
|
||||
return null;
|
||||
}
|
||||
CreditInfoNegativeVO negativeVO = new CreditInfoNegativeVO();
|
||||
negativeVO.setPersonId(negative.getPersonId());
|
||||
negativeVO.setPersonName(negative.getPersonName());
|
||||
negativeVO.setQueryDate(negative.getQueryDate());
|
||||
negativeVO.setCivilCnt(negative.getCivilCnt());
|
||||
negativeVO.setEnforceCnt(negative.getEnforceCnt());
|
||||
negativeVO.setAdmCnt(negative.getAdmCnt());
|
||||
negativeVO.setCivilLmt(negative.getCivilLmt());
|
||||
negativeVO.setEnforceLmt(negative.getEnforceLmt());
|
||||
negativeVO.setAdmLmt(negative.getAdmLmt());
|
||||
return negativeVO;
|
||||
}
|
||||
|
||||
private String currentUserName() {
|
||||
try {
|
||||
return SecurityUtils.getUsername();
|
||||
} catch (Exception e) {
|
||||
return "system";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.ruoyi.info.collection.service.support;
|
||||
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Date;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 征信解析结果装配器
|
||||
*/
|
||||
@Component
|
||||
public class CreditInfoPayloadAssembler {
|
||||
|
||||
private static final List<DebtMapping> DEBT_MAPPINGS = List.of(
|
||||
new DebtMapping("uncle_bank_house", "银行", "住房贷款", "银行", "未结清银行住房贷款"),
|
||||
new DebtMapping("uncle_bank_car", "银行", "汽车贷款", "银行", "未结清银行汽车贷款"),
|
||||
new DebtMapping("uncle_bank_manage", "银行", "经营贷款", "银行", "未结清银行经营贷款"),
|
||||
new DebtMapping("uncle_bank_consume", "银行", "消费贷款", "银行", "未结清银行消费贷款"),
|
||||
new DebtMapping("uncle_bank_other", "银行", "其他贷款", "银行", "未结清银行其他贷款"),
|
||||
new DebtMapping("uncle_not_bank", "非银", "非银行贷款", "非银", "未结清非银行贷款"),
|
||||
new DebtMapping("uncle_credit_cart", "银行", "信用卡", "银行", "未结清信用卡")
|
||||
);
|
||||
|
||||
public List<CcdiDebtsInfo> buildDebts(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) {
|
||||
Map<String, Object> source = payload == null ? null : payload.getLxDebt();
|
||||
if (source == null || source.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<CcdiDebtsInfo> rows = new ArrayList<>();
|
||||
for (DebtMapping mapping : DEBT_MAPPINGS) {
|
||||
CcdiDebtsInfo row = buildDebtRow(personId, personName, queryDate, source, mapping);
|
||||
if (row != null) {
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
public CcdiCreditNegativeInfo buildNegative(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) {
|
||||
Map<String, Object> source = payload == null ? null : payload.getLxPublictype();
|
||||
CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo();
|
||||
info.setPersonId(personId);
|
||||
info.setPersonName(personName);
|
||||
info.setQueryDate(queryDate == null ? null : Date.valueOf(queryDate));
|
||||
info.setCivilCnt(toInteger(source == null ? null : source.get("civil_cnt")));
|
||||
info.setEnforceCnt(toInteger(source == null ? null : source.get("enforce_cnt")));
|
||||
info.setAdmCnt(toInteger(source == null ? null : source.get("adm_cnt")));
|
||||
info.setCivilLmt(toBigDecimal(source == null ? null : source.get("civil_lmt")));
|
||||
info.setEnforceLmt(toBigDecimal(source == null ? null : source.get("enforce_lmt")));
|
||||
info.setAdmLmt(toBigDecimal(source == null ? null : source.get("adm_lmt")));
|
||||
return info;
|
||||
}
|
||||
|
||||
private CcdiDebtsInfo buildDebtRow(String personId, String personName, LocalDate queryDate,
|
||||
Map<String, Object> source, DebtMapping mapping) {
|
||||
BigDecimal principalBalance = toBigDecimal(source.get(mapping.prefix() + "_bal"));
|
||||
BigDecimal debtTotalAmount = toBigDecimal(source.get(mapping.prefix() + "_lmt"));
|
||||
String debtStatus = toStringValue(source.get(mapping.prefix() + "_state"));
|
||||
if (isEmptyMetrics(principalBalance, debtTotalAmount, debtStatus)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CcdiDebtsInfo row = new CcdiDebtsInfo();
|
||||
row.setPersonId(personId);
|
||||
row.setPersonName(personName);
|
||||
row.setQueryDate(queryDate == null ? null : Date.valueOf(queryDate));
|
||||
row.setDebtMainType(mapping.debtMainType());
|
||||
row.setDebtSubType(mapping.debtSubType());
|
||||
row.setCreditorType(mapping.creditorType());
|
||||
row.setDebtName(mapping.debtName());
|
||||
row.setPrincipalBalance(principalBalance);
|
||||
row.setDebtTotalAmount(debtTotalAmount);
|
||||
row.setDebtStatus(debtStatus);
|
||||
return row;
|
||||
}
|
||||
|
||||
private boolean isEmptyMetrics(BigDecimal principalBalance, BigDecimal debtTotalAmount, String debtStatus) {
|
||||
boolean principalEmpty = principalBalance == null || BigDecimal.ZERO.compareTo(principalBalance) == 0;
|
||||
boolean totalEmpty = debtTotalAmount == null || BigDecimal.ZERO.compareTo(debtTotalAmount) == 0;
|
||||
return principalEmpty && totalEmpty && isBlank(debtStatus);
|
||||
}
|
||||
|
||||
private Integer toInteger(Object value) {
|
||||
BigDecimal decimal = toBigDecimal(value);
|
||||
return decimal == null ? 0 : decimal.intValue();
|
||||
}
|
||||
|
||||
private BigDecimal toBigDecimal(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof BigDecimal decimal) {
|
||||
return decimal;
|
||||
}
|
||||
String text = Objects.toString(value, "").trim();
|
||||
if (text.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new BigDecimal(text);
|
||||
}
|
||||
|
||||
private String toStringValue(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String text = Objects.toString(value, "").trim();
|
||||
return text.isEmpty() ? null : text;
|
||||
}
|
||||
|
||||
private boolean isBlank(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
|
||||
private record DebtMapping(String prefix, String debtMainType, String debtSubType, String creditorType, String debtName) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper">
|
||||
|
||||
<select id="selectCreditInfoPage" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
|
||||
SELECT
|
||||
s.staff_id,
|
||||
s.name,
|
||||
s.id_card,
|
||||
d.dept_name,
|
||||
debt_agg.query_date,
|
||||
IFNULL(debt_agg.debt_count, 0) AS debt_count,
|
||||
IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount,
|
||||
IFNULL(neg.civil_cnt, 0) AS civil_cnt,
|
||||
IFNULL(neg.enforce_cnt, 0) AS enforce_cnt,
|
||||
IFNULL(neg.adm_cnt, 0) AS adm_cnt
|
||||
FROM ccdi_base_staff s
|
||||
LEFT JOIN sys_dept d ON s.dept_id = d.dept_id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
person_id,
|
||||
MAX(query_date) AS query_date,
|
||||
COUNT(*) AS debt_count,
|
||||
SUM(debt_total_amount) AS debt_total_amount
|
||||
FROM ccdi_debts_info
|
||||
GROUP BY person_id
|
||||
) debt_agg ON debt_agg.person_id = s.id_card
|
||||
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = s.id_card
|
||||
<where>
|
||||
<if test="query != null and query.name != null and query.name != ''">
|
||||
AND s.name LIKE CONCAT('%', #{query.name}, '%')
|
||||
</if>
|
||||
<if test="query != null and query.staffId != null and query.staffId != ''">
|
||||
AND CAST(s.staff_id AS CHAR) = #{query.staffId}
|
||||
</if>
|
||||
<if test="query != null and query.idCard != null and query.idCard != ''">
|
||||
AND s.id_card LIKE CONCAT('%', #{query.idCard}, '%')
|
||||
</if>
|
||||
<if test="query != null and query.maintained == '1'">
|
||||
AND (debt_agg.person_id IS NOT NULL OR neg.person_id IS NOT NULL)
|
||||
</if>
|
||||
<if test="query != null and query.maintained == '0'">
|
||||
AND debt_agg.person_id IS NULL
|
||||
AND neg.person_id IS NULL
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY debt_agg.query_date DESC, s.staff_id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectCreditInfoSummaryByPersonId" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
|
||||
SELECT
|
||||
s.staff_id,
|
||||
s.name,
|
||||
s.id_card,
|
||||
d.dept_name,
|
||||
debt_agg.query_date,
|
||||
IFNULL(debt_agg.debt_count, 0) AS debt_count,
|
||||
IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount,
|
||||
IFNULL(neg.civil_cnt, 0) AS civil_cnt,
|
||||
IFNULL(neg.enforce_cnt, 0) AS enforce_cnt,
|
||||
IFNULL(neg.adm_cnt, 0) AS adm_cnt
|
||||
FROM ccdi_base_staff s
|
||||
LEFT JOIN sys_dept d ON s.dept_id = d.dept_id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
person_id,
|
||||
MAX(query_date) AS query_date,
|
||||
COUNT(*) AS debt_count,
|
||||
SUM(debt_total_amount) AS debt_total_amount
|
||||
FROM ccdi_debts_info
|
||||
GROUP BY person_id
|
||||
) debt_agg ON debt_agg.person_id = s.id_card
|
||||
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = s.id_card
|
||||
WHERE s.id_card = #{personId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectLatestQueryDate" resultType="java.time.LocalDate">
|
||||
SELECT MAX(query_date)
|
||||
FROM ccdi_debts_info
|
||||
WHERE person_id = #{personId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper">
|
||||
|
||||
<resultMap id="CcdiCreditNegativeInfoResultMap" type="com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo">
|
||||
<id property="negativeId" column="negative_id"/>
|
||||
<result property="personId" column="person_id"/>
|
||||
<result property="personName" column="person_name"/>
|
||||
<result property="queryDate" column="query_date"/>
|
||||
<result property="civilCnt" column="civil_cnt"/>
|
||||
<result property="enforceCnt" column="enforce_cnt"/>
|
||||
<result property="admCnt" column="adm_cnt"/>
|
||||
<result property="civilLmt" column="civil_lmt"/>
|
||||
<result property="enforceLmt" column="enforce_lmt"/>
|
||||
<result property="admLmt" column="adm_lmt"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByPersonId" resultMap="CcdiCreditNegativeInfoResultMap">
|
||||
SELECT
|
||||
negative_id, person_id, person_name, query_date, civil_cnt, enforce_cnt, adm_cnt,
|
||||
civil_lmt, enforce_lmt, adm_lmt, create_by, create_time, update_by, update_time
|
||||
FROM ccdi_credit_negative_info
|
||||
WHERE person_id = #{personId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<delete id="deleteByPersonId">
|
||||
DELETE FROM ccdi_credit_negative_info
|
||||
WHERE person_id = #{personId}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper">
|
||||
|
||||
<resultMap id="CcdiDebtsInfoResultMap" type="com.ruoyi.info.collection.domain.CcdiDebtsInfo">
|
||||
<id property="debtId" column="debt_id"/>
|
||||
<result property="personId" column="person_id"/>
|
||||
<result property="personName" column="person_name"/>
|
||||
<result property="queryDate" column="query_date"/>
|
||||
<result property="debtMainType" column="debt_main_type"/>
|
||||
<result property="debtSubType" column="debt_sub_type"/>
|
||||
<result property="creditorType" column="creditor_type"/>
|
||||
<result property="debtName" column="debt_name"/>
|
||||
<result property="principalBalance" column="principal_balance"/>
|
||||
<result property="debtTotalAmount" column="debt_total_amount"/>
|
||||
<result property="debtStatus" column="debt_status"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByPersonId" resultMap="CcdiDebtsInfoResultMap">
|
||||
SELECT
|
||||
debt_id, person_id, person_name, query_date, debt_main_type, debt_sub_type, creditor_type,
|
||||
debt_name, principal_balance, debt_total_amount, debt_status, create_by, create_time, update_by, update_time
|
||||
FROM ccdi_debts_info
|
||||
WHERE person_id = #{personId}
|
||||
ORDER BY debt_id ASC
|
||||
</select>
|
||||
|
||||
<delete id="deleteByPersonId">
|
||||
DELETE FROM ccdi_debts_info
|
||||
WHERE person_id = #{personId}
|
||||
</delete>
|
||||
|
||||
<insert id="insertBatch">
|
||||
INSERT INTO ccdi_debts_info
|
||||
(person_id, person_name, query_date, debt_main_type, debt_sub_type, creditor_type,
|
||||
debt_name, principal_balance, debt_total_amount, debt_status, create_by, create_time, update_by, update_time)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.personId}, #{item.personName}, #{item.queryDate}, #{item.debtMainType}, #{item.debtSubType}, #{item.creditorType},
|
||||
#{item.debtName}, #{item.principalBalance}, #{item.debtTotalAmount}, #{item.debtStatus}, #{item.createBy}, NOW(), #{item.updateBy}, NOW())
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.ruoyi.info.collection.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
|
||||
import com.ruoyi.info.collection.service.ICcdiCreditInfoService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CcdiCreditInfoControllerTest {
|
||||
|
||||
@InjectMocks
|
||||
private CcdiCreditInfoController controller;
|
||||
|
||||
@Mock
|
||||
private ICcdiCreditInfoService service;
|
||||
|
||||
@Test
|
||||
void list_shouldDelegateWithPageRequest() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addParameter("pageNum", "1");
|
||||
request.addParameter("pageSize", "10");
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
|
||||
when(service.selectCreditInfoPage(any(), any())).thenReturn(new Page<CreditInfoListVO>(1, 10, 0));
|
||||
|
||||
TableDataInfo result = controller.list(new CcdiCreditInfoQueryDTO());
|
||||
|
||||
assertEquals(0L, result.getTotal());
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
void remove_shouldCallDeleteByPersonId() {
|
||||
when(service.deleteByPersonId("330101199001010011")).thenReturn(1);
|
||||
|
||||
AjaxResult result = controller.remove("330101199001010011");
|
||||
|
||||
assertEquals(200, result.get("code"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.ruoyi.info.collection.service;
|
||||
|
||||
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
|
||||
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
||||
import com.ruoyi.info.collection.service.impl.CcdiCreditInfoServiceImpl;
|
||||
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CcdiCreditInfoServiceImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private CcdiCreditInfoServiceImpl service;
|
||||
|
||||
@Mock
|
||||
private CreditParseClient creditParseClient;
|
||||
|
||||
@Mock
|
||||
private CreditInfoPayloadAssembler assembler;
|
||||
|
||||
@Mock
|
||||
private CcdiBaseStaffMapper baseStaffMapper;
|
||||
|
||||
@Mock
|
||||
private CcdiDebtsInfoMapper debtsInfoMapper;
|
||||
|
||||
@Mock
|
||||
private CcdiCreditNegativeInfoMapper negativeInfoMapper;
|
||||
|
||||
@Mock
|
||||
private CcdiCreditInfoQueryMapper queryMapper;
|
||||
|
||||
@Test
|
||||
void uploadHtmlFiles_shouldIsolateFailuresPerEmployee() {
|
||||
MockMultipartFile successFile = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
||||
MockMultipartFile failFile = new MockMultipartFile("files", "b.html", "text/html", "<html>b</html>".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
|
||||
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"))
|
||||
.thenThrow(new RuntimeException("征信解析失败"));
|
||||
when(baseStaffMapper.selectOne(any())).thenReturn(baseStaff("330101199001010011", "张三"));
|
||||
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||
.thenReturn(List.of(buildDebt()));
|
||||
when(assembler.buildNegative(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||
.thenReturn(buildNegative());
|
||||
|
||||
CreditInfoUploadResultVO result = service.upload(Arrays.asList(successFile, failFile));
|
||||
|
||||
assertEquals(1, result.getSuccessCount());
|
||||
assertEquals(1, result.getFailureCount());
|
||||
assertEquals("征信解析失败", result.getFailures().get(0).getReason());
|
||||
verify(debtsInfoMapper).deleteByPersonId("330101199001010011");
|
||||
verify(negativeInfoMapper).deleteByPersonId("330101199001010011");
|
||||
verify(debtsInfoMapper).insertBatch(any());
|
||||
verify(negativeInfoMapper).insert(any(CcdiCreditNegativeInfo.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void uploadHtmlFiles_shouldRejectOlderReportDate() {
|
||||
MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
|
||||
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
||||
when(baseStaffMapper.selectOne(any())).thenReturn(baseStaff("330101199001010011", "张三"));
|
||||
when(queryMapper.selectLatestQueryDate("330101199001010011"))
|
||||
.thenReturn(LocalDate.parse("2026-03-05"));
|
||||
|
||||
CreditInfoUploadResultVO result = service.upload(List.of(file));
|
||||
|
||||
assertEquals(0, result.getSuccessCount());
|
||||
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
|
||||
}
|
||||
|
||||
private CreditParseResponse successResponse(String personId, String personName, String reportTime) {
|
||||
CreditParsePayload payload = new CreditParsePayload();
|
||||
Map<String, Object> header = new HashMap<>();
|
||||
header.put("query_cert_no", personId);
|
||||
header.put("query_cust_name", personName);
|
||||
header.put("report_time", reportTime);
|
||||
payload.setLxHeader(header);
|
||||
payload.setLxDebt(Map.of("uncle_bank_house_bal", "1"));
|
||||
payload.setLxPublictype(Map.of("civil_cnt", 1));
|
||||
|
||||
CreditParseResponse response = new CreditParseResponse();
|
||||
response.setStatusCode("0");
|
||||
response.setPayload(payload);
|
||||
return response;
|
||||
}
|
||||
|
||||
private CcdiBaseStaff baseStaff(String idCard, String name) {
|
||||
CcdiBaseStaff staff = new CcdiBaseStaff();
|
||||
staff.setIdCard(idCard);
|
||||
staff.setName(name);
|
||||
return staff;
|
||||
}
|
||||
|
||||
private CcdiDebtsInfo buildDebt() {
|
||||
CcdiDebtsInfo debt = new CcdiDebtsInfo();
|
||||
debt.setPersonId("330101199001010011");
|
||||
debt.setDebtTotalAmount(new BigDecimal("100"));
|
||||
return debt;
|
||||
}
|
||||
|
||||
private CcdiCreditNegativeInfo buildNegative() {
|
||||
CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo();
|
||||
info.setPersonId("330101199001010011");
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.ruoyi.info.collection.service.support;
|
||||
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CreditInfoPayloadAssemblerTest {
|
||||
|
||||
private final CreditInfoPayloadAssembler assembler = new CreditInfoPayloadAssembler();
|
||||
|
||||
@Test
|
||||
void shouldConvertDebtPayloadToSevenTypedRows() {
|
||||
CreditParsePayload payload = new CreditParsePayload();
|
||||
Map<String, Object> debt = new HashMap<>();
|
||||
debt.put("uncle_bank_house_bal", "50000");
|
||||
debt.put("uncle_bank_house_lmt", "100000");
|
||||
debt.put("uncle_bank_house_state", "正常");
|
||||
debt.put("uncle_not_bank_bal", "2000");
|
||||
debt.put("uncle_not_bank_lmt", "3000");
|
||||
debt.put("uncle_not_bank_state", "逾期");
|
||||
payload.setLxDebt(debt);
|
||||
|
||||
List<CcdiDebtsInfo> rows = assembler.buildDebts("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
|
||||
|
||||
assertEquals(2, rows.size());
|
||||
assertEquals("住房贷款", rows.get(0).getDebtSubType());
|
||||
assertEquals("非银", rows.get(1).getCreditorType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBuildNegativeInfoFromPublicTypePayload() {
|
||||
CreditParsePayload payload = new CreditParsePayload();
|
||||
Map<String, Object> publictype = new HashMap<>();
|
||||
publictype.put("civil_cnt", 2);
|
||||
publictype.put("civil_lmt", "9800");
|
||||
payload.setLxPublictype(publictype);
|
||||
|
||||
CcdiCreditNegativeInfo info = assembler.buildNegative("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
|
||||
|
||||
assertEquals(2, info.getCivilCnt());
|
||||
assertEquals(new BigDecimal("9800"), info.getCivilLmt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipDebtRowWhenAllMetricsAreEmpty() {
|
||||
CreditParsePayload payload = new CreditParsePayload();
|
||||
payload.setLxDebt(new HashMap<>());
|
||||
|
||||
assertTrue(assembler.buildDebts("3301", "张三", LocalDate.parse("2026-03-01"), payload).isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
# 征信维护后端实施记录
|
||||
|
||||
## 1. 实施概述
|
||||
|
||||
- 实施日期:2026-03-24
|
||||
- 实施范围:`ccdi-info-collection` 征信维护后端能力、SQL 脚本、联调验证、环境修正记录
|
||||
- 实施结果:代码实现完成,目标测试通过,隔离端口联调通过
|
||||
|
||||
本次实现按设计文档完成以下能力:
|
||||
|
||||
- 新增 `ccdi_debts_info`、`ccdi_credit_negative_info` 两张业务表脚本
|
||||
- 新增“征信维护”菜单与按钮权限脚本
|
||||
- 新增征信维护实体、DTO、VO、Mapper、装配器、服务、控制器
|
||||
- 支持批量上传征信 HTML、按员工身份证号归户、按最新征信覆盖写入
|
||||
- 支持员工维度列表、详情、删除接口
|
||||
|
||||
## 2. 实际修改文件
|
||||
|
||||
### 2.1 SQL
|
||||
|
||||
- `sql/migration/2026-03-23-create-credit-info-tables.sql`
|
||||
- `sql/ccdi_credit_info_menu.sql`
|
||||
- `sql/migration/2026-03-24-recreate-credit-negative-info-table.sql`
|
||||
|
||||
### 2.2 后端代码
|
||||
|
||||
- `ccdi-info-collection/pom.xml`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java`
|
||||
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java`
|
||||
- `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml`
|
||||
- `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiDebtsInfoMapper.xml`
|
||||
- `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditNegativeInfoMapper.xml`
|
||||
|
||||
### 2.3 测试代码
|
||||
|
||||
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java`
|
||||
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java`
|
||||
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java`
|
||||
|
||||
## 3. 关键实施说明
|
||||
|
||||
### 3.1 解析与落库
|
||||
|
||||
- 上传接口调用 `CreditParseClient.parse("LXCUSTALL", "PERSON", tempFile)`
|
||||
- 从 `lx_header.query_cert_no` 归户到 `ccdi_base_staff.id_card`
|
||||
- 从 `lx_header.report_time` 提取 `query_date`
|
||||
- `lx_debt` 按 7 组固定前缀映射为负债明细
|
||||
- `lx_publictype` 直接映射为负面信息汇总
|
||||
- 同一员工在事务内执行“先删后插”,仅保留最新征信
|
||||
|
||||
### 3.2 实际环境偏差与处理
|
||||
|
||||
联调时发现开发库已存在同名旧表 `ccdi_credit_negative_info`,结构与本次设计不一致:
|
||||
|
||||
- 旧表字段:`id`、`credit_info_id` 等
|
||||
- 目标字段:`negative_id`、`person_id`、`person_name`、`query_date` 等
|
||||
|
||||
处理步骤:
|
||||
|
||||
1. 先核对旧表行数:`SELECT COUNT(*) FROM ccdi_credit_negative_info;`
|
||||
2. 确认旧表为空表后,执行重建脚本
|
||||
3. 重建后重新执行上传、列表、详情、删除联调
|
||||
|
||||
## 4. SQL 执行记录
|
||||
|
||||
按仓库约定,涉及中文与建表脚本的执行均使用 `bin/mysql_utf8_exec.sh`:
|
||||
|
||||
```bash
|
||||
bin/mysql_utf8_exec.sh sql/migration/2026-03-23-create-credit-info-tables.sql
|
||||
bin/mysql_utf8_exec.sh sql/migration/2026-03-24-recreate-credit-negative-info-table.sql
|
||||
```
|
||||
|
||||
执行结果:
|
||||
|
||||
- 首次执行建表脚本时,`ccdi_debts_info` 创建成功
|
||||
- 由于开发库已存在旧版 `ccdi_credit_negative_info`,脚本在第二张表处提示已存在
|
||||
- 通过重建脚本完成 `ccdi_credit_negative_info` 修正
|
||||
|
||||
## 5. 测试与验证
|
||||
|
||||
### 5.1 单元测试与编译
|
||||
|
||||
执行命令:
|
||||
|
||||
```bash
|
||||
mvn -pl ccdi-info-collection -Dtest=CreditInfoPayloadAssemblerTest test
|
||||
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest test
|
||||
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoControllerTest test
|
||||
mvn -pl ccdi-info-collection -Dtest=CreditInfoPayloadAssemblerTest,CcdiCreditInfoServiceImplTest,CcdiCreditInfoControllerTest test
|
||||
mvn -pl ccdi-info-collection -am compile
|
||||
mvn -pl ruoyi-admin -am package -DskipTests
|
||||
```
|
||||
|
||||
验证结果:
|
||||
|
||||
- `CreditInfoPayloadAssemblerTest` 通过
|
||||
- `CcdiCreditInfoServiceImplTest` 通过
|
||||
- `CcdiCreditInfoControllerTest` 通过
|
||||
- 目标测试集合 7 条全部通过
|
||||
- `ccdi-info-collection` 及上游依赖编译通过
|
||||
- `ruoyi-admin.jar` 打包成功
|
||||
|
||||
### 5.2 隔离端口联调
|
||||
|
||||
为避免影响本机已有开发进程,本次联调使用隔离端口:
|
||||
|
||||
- Mock 服务:`http://127.0.0.1:8001`
|
||||
- 后端服务:`http://127.0.0.1:62319`
|
||||
|
||||
登录获取 token:
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://127.0.0.1:62319/login/test' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"admin","password":"admin123"}'
|
||||
```
|
||||
|
||||
上传验证:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
-F 'files=@assets/征信解析员工样本/0001_徐伟_2040.html;type=text/html' \
|
||||
'http://127.0.0.1:62319/ccdi/creditInfo/upload'
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `code = 200`
|
||||
- `data.totalCount = 1`
|
||||
- `data.successCount = 1`
|
||||
- `data.failureCount = 0`
|
||||
|
||||
列表验证:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
'http://127.0.0.1:62319/ccdi/creditInfo/list?pageNum=1&pageSize=10'
|
||||
```
|
||||
|
||||
结果摘要:
|
||||
|
||||
- 返回 `code = 200`
|
||||
- 员工 `徐伟 / 558455197203132040` 可见
|
||||
- `queryDate = 2024-12-09`
|
||||
- `debtCount = 7`
|
||||
- `civilCnt = 2`
|
||||
- `enforceCnt = 13`
|
||||
- `admCnt = 3`
|
||||
|
||||
详情验证:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
'http://127.0.0.1:62319/ccdi/creditInfo/558455197203132040'
|
||||
```
|
||||
|
||||
结果摘要:
|
||||
|
||||
- 返回 `code = 200`
|
||||
- `negativeInfo` 正常返回
|
||||
- `debtList` 返回 7 条明细
|
||||
|
||||
删除验证:
|
||||
|
||||
```bash
|
||||
curl -X DELETE -H "Authorization: Bearer <token>" \
|
||||
'http://127.0.0.1:62319/ccdi/creditInfo/558455197203132040'
|
||||
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
'http://127.0.0.1:62319/ccdi/creditInfo/558455197203132040'
|
||||
```
|
||||
|
||||
结果摘要:
|
||||
|
||||
- 删除接口返回 `code = 200`
|
||||
- 删除后详情仍返回员工壳信息,但 `negativeInfo = null`、`debtList = []`
|
||||
- 满足“删除后详情为空或提示未维护征信”的预期
|
||||
|
||||
## 6. 进程启停记录
|
||||
|
||||
本次联调启动的进程:
|
||||
|
||||
- Mock 服务:
|
||||
`python3 -m uvicorn main:app --host 0.0.0.0 --port 8001`
|
||||
- 后端服务:
|
||||
`java -jar ruoyi-admin/target/ruoyi-admin.jar --server.port=62319 --credit-parse.api.url=http://127.0.0.1:8001/xfeature-mngs/conversation/htmlEval`
|
||||
|
||||
处理要求:
|
||||
|
||||
- 联调结束后已按仓库约定停止本次启动的后端与 Mock 进程
|
||||
- 未触碰本机原有 `8000`、`62318` 端口上的既有开发进程
|
||||
|
||||
## 7. 提交记录
|
||||
|
||||
本次实施包含以下提交:
|
||||
|
||||
- `fc78c2f` 新增征信维护建表与菜单脚本
|
||||
- `6959c7a` 新增征信维护对象与依赖骨架
|
||||
- `d2e3388` 新增征信解析结果装配器
|
||||
- `c22e379` 新增征信维护上传与覆盖服务
|
||||
- `155adbe` 新增征信维护查询与接口
|
||||
17
sql/ccdi_credit_info_menu.sql
Normal file
17
sql/ccdi_credit_info_menu.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- 添加征信维护菜单
|
||||
-- 注意: 执行前请确认已存在"信息维护"父菜单
|
||||
-- 如果不存在,请先执行以下语句创建父菜单:
|
||||
-- INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
|
||||
-- VALUES (2000, '信息维护', 0, 4, 'dpc', NULL, '', '', 1, 0, 'M', '0', '0', '', 'example', 'admin', NOW(), '信息维护目录');
|
||||
|
||||
SET @parent_menu_id = (SELECT menu_id FROM sys_menu WHERE menu_name = '信息维护' AND parent_id = 0 LIMIT 1);
|
||||
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES ('征信维护', @parent_menu_id, 4, 'creditInfo', 'ccdiCreditInfo/index', 1, 0, 'C', '0', '0', 'ccdi:creditInfo:list', 'document', 'admin', NOW(), '', NULL, '员工征信维护菜单');
|
||||
|
||||
SET @menu_id = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) VALUES
|
||||
('征信查询', @menu_id, 1, '', '', 1, 0, 'F', '0', '0', 'ccdi:creditInfo:query', '#', 'admin', NOW(), ''),
|
||||
('征信上传', @menu_id, 2, '', '', 1, 0, 'F', '0', '0', 'ccdi:creditInfo:upload', '#', 'admin', NOW(), ''),
|
||||
('征信删除', @menu_id, 3, '', '', 1, 0, 'F', '0', '0', 'ccdi:creditInfo:remove', '#', 'admin', NOW(), '');
|
||||
40
sql/migration/2026-03-23-create-credit-info-tables.sql
Normal file
40
sql/migration/2026-03-23-create-credit-info-tables.sql
Normal file
@@ -0,0 +1,40 @@
|
||||
CREATE TABLE `ccdi_debts_info` (
|
||||
`debt_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`person_id` VARCHAR(18) NOT NULL COMMENT '员工身份证号',
|
||||
`person_name` VARCHAR(100) DEFAULT NULL COMMENT '员工姓名',
|
||||
`query_date` DATE DEFAULT NULL COMMENT '征信查询日期',
|
||||
`debt_main_type` VARCHAR(50) DEFAULT NULL COMMENT '负债大类',
|
||||
`debt_sub_type` VARCHAR(50) DEFAULT NULL COMMENT '负债小类',
|
||||
`creditor_type` VARCHAR(50) DEFAULT NULL COMMENT '债权人类型',
|
||||
`debt_name` VARCHAR(100) DEFAULT NULL COMMENT '负债名称',
|
||||
`principal_balance` DECIMAL(18, 2) DEFAULT NULL COMMENT '负债本金余额',
|
||||
`debt_total_amount` DECIMAL(18, 2) DEFAULT NULL COMMENT '负债总额',
|
||||
`debt_status` VARCHAR(20) DEFAULT NULL COMMENT '负债状态',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`debt_id`),
|
||||
KEY `idx_person_id` (`person_id`),
|
||||
KEY `idx_query_date` (`query_date`),
|
||||
KEY `idx_person_query_date` (`person_id`, `query_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工征信负债明细';
|
||||
|
||||
CREATE TABLE `ccdi_credit_negative_info` (
|
||||
`negative_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`person_id` VARCHAR(18) NOT NULL COMMENT '员工身份证号',
|
||||
`person_name` VARCHAR(100) DEFAULT NULL COMMENT '员工姓名',
|
||||
`query_date` DATE DEFAULT NULL COMMENT '征信查询日期',
|
||||
`civil_cnt` INT DEFAULT 0 COMMENT '民事案件笔数',
|
||||
`enforce_cnt` INT DEFAULT 0 COMMENT '强制执行笔数',
|
||||
`adm_cnt` INT DEFAULT 0 COMMENT '行政处罚笔数',
|
||||
`civil_lmt` DECIMAL(18, 2) DEFAULT 0 COMMENT '民事案件金额',
|
||||
`enforce_lmt` DECIMAL(18, 2) DEFAULT 0 COMMENT '强制执行金额',
|
||||
`adm_lmt` DECIMAL(18, 2) DEFAULT 0 COMMENT '行政处罚金额',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`negative_id`),
|
||||
UNIQUE KEY `uk_person_id` (`person_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工征信负面信息';
|
||||
@@ -0,0 +1,20 @@
|
||||
DROP TABLE IF EXISTS `ccdi_credit_negative_info`;
|
||||
|
||||
CREATE TABLE `ccdi_credit_negative_info` (
|
||||
`negative_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`person_id` VARCHAR(18) NOT NULL COMMENT '员工身份证号',
|
||||
`person_name` VARCHAR(100) DEFAULT NULL COMMENT '员工姓名',
|
||||
`query_date` DATE DEFAULT NULL COMMENT '征信查询日期',
|
||||
`civil_cnt` INT DEFAULT 0 COMMENT '民事案件笔数',
|
||||
`enforce_cnt` INT DEFAULT 0 COMMENT '强制执行笔数',
|
||||
`adm_cnt` INT DEFAULT 0 COMMENT '行政处罚笔数',
|
||||
`civil_lmt` DECIMAL(18, 2) DEFAULT 0 COMMENT '民事案件金额',
|
||||
`enforce_lmt` DECIMAL(18, 2) DEFAULT 0 COMMENT '强制执行金额',
|
||||
`adm_lmt` DECIMAL(18, 2) DEFAULT 0 COMMENT '行政处罚金额',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`negative_id`),
|
||||
UNIQUE KEY `uk_person_id` (`person_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工征信负面信息';
|
||||
Reference in New Issue
Block a user