From fc78c2f3d94bc83cbf4a368a630f9bd314cff601 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 24 Mar 2026 09:01:10 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=81=E4=BF=A1?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E5=BB=BA=E8=A1=A8=E4=B8=8E=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/ccdi_credit_info_menu.sql | 17 ++++++++ .../2026-03-23-create-credit-info-tables.sql | 40 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 sql/ccdi_credit_info_menu.sql create mode 100644 sql/migration/2026-03-23-create-credit-info-tables.sql diff --git a/sql/ccdi_credit_info_menu.sql b/sql/ccdi_credit_info_menu.sql new file mode 100644 index 00000000..b19c33c1 --- /dev/null +++ b/sql/ccdi_credit_info_menu.sql @@ -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(), ''); diff --git a/sql/migration/2026-03-23-create-credit-info-tables.sql b/sql/migration/2026-03-23-create-credit-info-tables.sql new file mode 100644 index 00000000..974ac206 --- /dev/null +++ b/sql/migration/2026-03-23-create-credit-info-tables.sql @@ -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='员工征信负面信息'; From 6959c7a49d91eaea8613d5e0a3cef28057280c53 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 24 Mar 2026 09:03:23 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=81=E4=BF=A1?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E5=AF=B9=E8=B1=A1=E4=B8=8E=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E9=AA=A8=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ccdi-info-collection/pom.xml | 6 +++ .../domain/CcdiCreditNegativeInfo.java | 39 ++++++++++++++++++ .../info/collection/domain/CcdiDebtsInfo.java | 40 +++++++++++++++++++ .../domain/dto/CcdiCreditInfoQueryDTO.java | 21 ++++++++++ .../domain/vo/CreditInfoDetailVO.java | 24 +++++++++++ .../domain/vo/CreditInfoListVO.java | 29 ++++++++++++++ .../domain/vo/CreditInfoNegativeVO.java | 28 +++++++++++++ .../domain/vo/CreditInfoUploadFailureVO.java | 21 ++++++++++ .../domain/vo/CreditInfoUploadResultVO.java | 23 +++++++++++ .../mapper/CcdiCreditInfoQueryMapper.java | 27 +++++++++++++ .../mapper/CcdiCreditNegativeInfoMapper.java | 15 +++++++ .../mapper/CcdiDebtsInfoMapper.java | 19 +++++++++ .../CcdiCreditInfoServiceImplTest.java | 18 +++++++++ 13 files changed, 310 insertions(+) create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java create mode 100644 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java diff --git a/ccdi-info-collection/pom.xml b/ccdi-info-collection/pom.xml index 34473cc6..8158c290 100644 --- a/ccdi-info-collection/pom.xml +++ b/ccdi-info-collection/pom.xml @@ -29,6 +29,12 @@ ruoyi-system + + com.ruoyi + ccdi-lsfx + 3.9.1 + + com.alibaba diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java new file mode 100644 index 00000000..c490fd28 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java @@ -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; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java new file mode 100644 index 00000000..eff8bd56 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java @@ -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; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java new file mode 100644 index 00000000..3554cb93 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java @@ -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; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java new file mode 100644 index 00000000..6983eb09 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java @@ -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 debtList; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java new file mode 100644 index 00000000..8c7017e1 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java @@ -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; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java new file mode 100644 index 00000000..aca1233b --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java @@ -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; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java new file mode 100644 index 00000000..7085fd11 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java @@ -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; +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java new file mode 100644 index 00000000..8920214f --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java @@ -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 failures = new ArrayList<>(); +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java new file mode 100644 index 00000000..aa610a98 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java @@ -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 selectCreditInfoPage(@Param("page") Page page, + @Param("query") CcdiCreditInfoQueryDTO queryDTO); + + CreditInfoListVO selectCreditInfoSummaryByPersonId(@Param("personId") String personId); + + CreditInfoDetailVO selectCreditInfoDetailByPersonId(@Param("personId") String personId); + + List selectCreditInfoList(@Param("query") CcdiCreditInfoQueryDTO queryDTO); + + LocalDate selectLatestQueryDate(@Param("personId") String personId); +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java new file mode 100644 index 00000000..ce399847 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java @@ -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 selectByPersonId(@Param("personId") String personId); + + int deleteByPersonId(@Param("personId") String personId); +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java new file mode 100644 index 00000000..9ceaf8b0 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java @@ -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 { + + List selectByPersonId(@Param("personId") String personId); + + int deleteByPersonId(@Param("personId") String personId); + + int insertBatch(@Param("list") List list); +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java new file mode 100644 index 00000000..323c8480 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java @@ -0,0 +1,18 @@ +package com.ruoyi.info.collection.service; + +import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO; +import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class CcdiCreditInfoServiceImplTest { + + @Test + void shouldCompileCreditInfoContracts() { + CcdiCreditInfoQueryDTO queryDTO = new CcdiCreditInfoQueryDTO(); + CreditInfoUploadResultVO resultVO = new CreditInfoUploadResultVO(); + assertNotNull(queryDTO); + assertNotNull(resultVO); + } +} From d2e3388a0859054735de28cb00126dfdb25e772d Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 24 Mar 2026 09:05:04 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=81=E4=BF=A1?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E7=BB=93=E6=9E=9C=E8=A3=85=E9=85=8D=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/CreditInfoPayloadAssembler.java | 124 ++++++++++++++++++ .../CreditInfoPayloadAssemblerTest.java | 61 +++++++++ 2 files changed, 185 insertions(+) create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java create mode 100644 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java new file mode 100644 index 00000000..607c932a --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java @@ -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 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 buildDebts(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) { + Map source = payload == null ? null : payload.getLxDebt(); + if (source == null || source.isEmpty()) { + return List.of(); + } + List 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 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 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) { + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java new file mode 100644 index 00000000..e0316190 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java @@ -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 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 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 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()); + } +} From c22e3793342fc86aef2b0350cd938de93742acb4 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 24 Mar 2026 09:08:40 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=81=E4=BF=A1?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E4=B8=8A=E4=BC=A0=E4=B8=8E=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ICcdiCreditInfoService.java | 28 ++ .../impl/CcdiCreditInfoServiceImpl.java | 252 ++++++++++++++++++ .../CcdiCreditInfoServiceImplTest.java | 134 +++++++++- 3 files changed, 407 insertions(+), 7 deletions(-) create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java new file mode 100644 index 00000000..3a0f6d83 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java @@ -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 files); + + Page selectCreditInfoPage(Page page, CcdiCreditInfoQueryDTO queryDTO); + + CreditInfoDetailVO selectDetailByPersonId(String personId); + + int deleteByPersonId(String personId); + + void replaceEmployeeCredit(String personId, List debts, CcdiCreditNegativeInfo negative, String userName); +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java new file mode 100644 index 00000000..e4d9f598 --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java @@ -0,0 +1,252 @@ +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.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 files) { + CreditInfoUploadResultVO result = new CreditInfoUploadResultVO(); + List 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 selectCreditInfoPage(Page page, CcdiCreditInfoQueryDTO queryDTO) { + return queryMapper.selectCreditInfoPage(page, queryDTO); + } + + @Override + public CreditInfoDetailVO selectDetailByPersonId(String personId) { + return queryMapper.selectCreditInfoDetailByPersonId(personId); + } + + @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 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 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 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 requireHeader(CreditParsePayload payload) { + Map 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() + .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 String currentUserName() { + try { + return SecurityUtils.getUsername(); + } catch (Exception e) { + return "system"; + } + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java index 323c8480..59cdb105 100644 --- a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java @@ -1,18 +1,138 @@ package com.ruoyi.info.collection.service; -import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO; +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 static org.junit.jupiter.api.Assertions.assertNotNull; +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 shouldCompileCreditInfoContracts() { - CcdiCreditInfoQueryDTO queryDTO = new CcdiCreditInfoQueryDTO(); - CreditInfoUploadResultVO resultVO = new CreditInfoUploadResultVO(); - assertNotNull(queryDTO); - assertNotNull(resultVO); + void uploadHtmlFiles_shouldIsolateFailuresPerEmployee() { + MockMultipartFile successFile = new MockMultipartFile("files", "a.html", "text/html", "a".getBytes(StandardCharsets.UTF_8)); + MockMultipartFile failFile = new MockMultipartFile("files", "b.html", "text/html", "b".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", "a".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 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; } } From 155adbee7b286235cc4de52228e3eed93d5df00e Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 24 Mar 2026 09:13:08 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=81=E4=BF=A1?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E6=9F=A5=E8=AF=A2=E4=B8=8E=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CcdiCreditInfoController.java | 72 ++++++++++++++++ .../impl/CcdiCreditInfoServiceImpl.java | 41 ++++++++- .../collection/CcdiCreditInfoQueryMapper.xml | 86 +++++++++++++++++++ .../CcdiCreditNegativeInfoMapper.xml | 38 ++++++++ .../info/collection/CcdiDebtsInfoMapper.xml | 50 +++++++++++ .../CcdiCreditInfoControllerTest.java | 53 ++++++++++++ 6 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java create mode 100644 ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml create mode 100644 ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditNegativeInfoMapper.xml create mode 100644 ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiDebtsInfoMapper.xml create mode 100644 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java new file mode 100644 index 00000000..4d447ffd --- /dev/null +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java @@ -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 page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize()); + Page 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)); + } +} diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java index e4d9f598..3aaaacba 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java @@ -9,6 +9,7 @@ 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; @@ -96,7 +97,28 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService { @Override public CreditInfoDetailVO selectDetailByPersonId(String personId) { - return queryMapper.selectCreditInfoDetailByPersonId(personId); + CreditInfoListVO summary = queryMapper.selectCreditInfoSummaryByPersonId(personId); + CcdiCreditNegativeInfo negative = negativeInfoMapper.selectByPersonId(personId); + List 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 @@ -242,6 +264,23 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService { 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(); diff --git a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml new file mode 100644 index 00000000..a8eeb4e9 --- /dev/null +++ b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + diff --git a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditNegativeInfoMapper.xml b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditNegativeInfoMapper.xml new file mode 100644 index 00000000..41d67334 --- /dev/null +++ b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditNegativeInfoMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM ccdi_credit_negative_info + WHERE person_id = #{personId} + + + diff --git a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiDebtsInfoMapper.xml b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiDebtsInfoMapper.xml new file mode 100644 index 00000000..2d6ce569 --- /dev/null +++ b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiDebtsInfoMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM ccdi_debts_info + WHERE person_id = #{personId} + + + + 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 + + (#{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()) + + + + diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java new file mode 100644 index 00000000..89a11801 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java @@ -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(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")); + } +} From aaec81f7f8bbb6fe2f2ef5337e4cca703c77991b Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 24 Mar 2026 09:23:24 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=BE=81=E4=BF=A1?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E5=90=8E=E7=AB=AF=E5=AE=9E=E6=96=BD=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=B9=B6=E4=BF=AE=E6=AD=A3=E8=B4=9F=E9=9D=A2=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E8=A1=A8=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...info-maintenance-backend-implementation.md | 214 ++++++++++++++++++ ...24-recreate-credit-negative-info-table.sql | 20 ++ 2 files changed, 234 insertions(+) create mode 100644 docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md create mode 100644 sql/migration/2026-03-24-recreate-credit-negative-info-table.sql diff --git a/docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md b/docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md new file mode 100644 index 00000000..7d5d8730 --- /dev/null +++ b/docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md @@ -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 " \ + -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 " \ + '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 " \ + 'http://127.0.0.1:62319/ccdi/creditInfo/558455197203132040' +``` + +结果摘要: + +- 返回 `code = 200` +- `negativeInfo` 正常返回 +- `debtList` 返回 7 条明细 + +删除验证: + +```bash +curl -X DELETE -H "Authorization: Bearer " \ + 'http://127.0.0.1:62319/ccdi/creditInfo/558455197203132040' + +curl -H "Authorization: Bearer " \ + '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` 新增征信维护查询与接口 diff --git a/sql/migration/2026-03-24-recreate-credit-negative-info-table.sql b/sql/migration/2026-03-24-recreate-credit-negative-info-table.sql new file mode 100644 index 00000000..49885f28 --- /dev/null +++ b/sql/migration/2026-03-24-recreate-credit-negative-info-table.sql @@ -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='员工征信负面信息';