- 添加状态筛选条件 - 添加详情查看功能 - 添加表单状态编辑功能 - 添加查看导入失败记录按钮 - 统一按钮顺序和颜色(新增/导入/导出/查看失败记录) - 统一表单布局(分隔线、gutter、宽度800px) - 优化导入失败记录功能(分页、清除历史记录) - 统一操作按钮文字(详情/编辑/删除) - 添加创建时间格式化显示 - 添加完整导入状态管理和轮询机制
2009 lines
59 KiB
Markdown
2009 lines
59 KiB
Markdown
# 信贷客户家庭关系维护功能 - 后端实施计划
|
||
|
||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||
|
||
**目标:** 开发信贷客户家庭关系维护功能的完整后端实现,包括数据库设计、实体类、DTO/VO、Mapper、Service和Controller
|
||
|
||
**架构:** 完全复用员工亲属关系维护功能的实现逻辑,创建独立模块 `CustFamilyRelation`,新建独立表 `ccdi_cust_fmy_relation`
|
||
|
||
**技术栈:** Spring Boot 3.5.8 + MyBatis Plus 3.5.10 + EasyExcel + Redis
|
||
|
||
---
|
||
|
||
## 前置条件
|
||
|
||
### 环境要求
|
||
- JDK 17+
|
||
- Maven 3.6+
|
||
- MySQL 8.2.0
|
||
- Redis (用于导入任务状态管理)
|
||
|
||
### 依赖服务
|
||
- 数据库连接配置在 `application.yml` 中已配置
|
||
- MyBatis Plus 3.5.10 已集成
|
||
- EasyExcel 已添加到项目依赖
|
||
|
||
### 参考模块
|
||
- `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/**/CcdiStaffFmyRelation*` - 员工亲属关系实现(参考模板)
|
||
|
||
---
|
||
|
||
## 任务列表
|
||
|
||
### Task 0: 创建数据库表
|
||
|
||
**Files:**
|
||
- Create: `sql/ccdi_cust_fmy_relation.sql`
|
||
|
||
**Step 1: 创建建表SQL文件**
|
||
|
||
创建 `sql/ccdi_cust_fmy_relation.sql` 文件:
|
||
|
||
```sql
|
||
-- 信贷客户家庭关系表
|
||
CREATE TABLE `ccdi_cust_fmy_relation` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||
`person_id` VARCHAR(50) NOT NULL COMMENT '信贷客户身份证号',
|
||
`relation_type` VARCHAR(50) NOT NULL COMMENT '关系类型',
|
||
`relation_name` VARCHAR(100) NOT NULL COMMENT '关系人姓名',
|
||
`gender` CHAR(1) DEFAULT NULL COMMENT '性别:M-男,F-女,O-其他',
|
||
`birth_date` DATE DEFAULT NULL COMMENT '关系人出生日期',
|
||
`relation_cert_type` VARCHAR(50) NOT NULL COMMENT '证件类型',
|
||
`relation_cert_no` VARCHAR(50) NOT NULL COMMENT '证件号码',
|
||
`mobile_phone1` VARCHAR(20) DEFAULT NULL COMMENT '手机号码1',
|
||
`mobile_phone2` VARCHAR(20) DEFAULT NULL COMMENT '手机号码2',
|
||
`wechat_no1` VARCHAR(50) DEFAULT NULL COMMENT '微信名称1',
|
||
`wechat_no2` VARCHAR(50) DEFAULT NULL COMMENT '微信名称2',
|
||
`wechat_no3` VARCHAR(50) DEFAULT NULL COMMENT '微信名称3',
|
||
`contact_address` VARCHAR(500) DEFAULT NULL COMMENT '详细联系地址',
|
||
`relation_desc` VARCHAR(500) DEFAULT NULL COMMENT '关系详细描述',
|
||
`status` INT NOT NULL DEFAULT 1 COMMENT '状态:0-无效,1-有效',
|
||
`effective_date` DATETIME DEFAULT NULL COMMENT '关系生效日期',
|
||
`invalid_date` DATETIME DEFAULT NULL COMMENT '关系失效日期',
|
||
`remark` TEXT COMMENT '备注信息',
|
||
`data_source` VARCHAR(50) DEFAULT NULL COMMENT '数据来源:MANUAL-手动录入,IMPORT-批量导入',
|
||
`is_emp_family` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否是员工的家庭关系:0-否',
|
||
`is_cust_family` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否是信贷客户的家庭关系:1-是',
|
||
`created_by` VARCHAR(50) NOT NULL COMMENT '记录创建人',
|
||
`updated_by` VARCHAR(50) DEFAULT NULL COMMENT '记录更新人',
|
||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||
`update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_person_id` (`person_id`),
|
||
KEY `idx_relation_cert_no` (`relation_cert_no`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='信贷客户家庭关系表';
|
||
```
|
||
|
||
**Step 2: 执行SQL创建表**
|
||
|
||
使用MCP连接数据库工具执行SQL文件:
|
||
|
||
```bash
|
||
# 连接数据库并执行建表脚本
|
||
mysql -u <username> -p <database> < sql/ccdi_cust_fmy_relation.sql
|
||
```
|
||
|
||
**验证方式:**
|
||
```sql
|
||
SHOW CREATE TABLE ccdi_cust_fmy_relation;
|
||
```
|
||
|
||
**预期结果:** 表创建成功,包含所有字段和索引
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add sql/ccdi_cust_fmy_relation.sql
|
||
git commit -m "feat: 创建信贷客户家庭关系表"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 1: 创建实体类
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/CcdiCustFmyRelation.java`
|
||
- Reference: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/CcdiStaffFmyRelation.java`
|
||
|
||
**Step 1: 复制并修改实体类**
|
||
|
||
复制 `CcdiStaffFmyRelation.java`,进行以下修改:
|
||
|
||
1. **类名和注释:**
|
||
```java
|
||
/**
|
||
* 信贷客户家庭关系对象 ccdi_cust_fmy_relation
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@TableName("ccdi_cust_fmy_relation")
|
||
public class CcdiCustFmyRelation implements Serializable {
|
||
```
|
||
|
||
2. **关键注释修改:**
|
||
- `/** 信贷客户身份证号 */` (原"员工身份证号")
|
||
- `/** 是否是客户亲属:1-是 */`
|
||
- `/** 是否是员工亲属:0-否 */`
|
||
|
||
3. **完整代码结构:**
|
||
```java
|
||
package com.ruoyi.ccdi.domain;
|
||
|
||
import com.baomidou.mybatisplus.annotation.*;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
import java.util.Date;
|
||
|
||
@Data
|
||
@TableName("ccdi_cust_fmy_relation")
|
||
public class CcdiCustFmyRelation implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 主键ID */
|
||
@TableId(type = IdType.AUTO)
|
||
private Long id;
|
||
|
||
/** 信贷客户身份证号 */
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
private String relationName;
|
||
|
||
/** 性别:M-男,F-女,O-其他 */
|
||
private String gender;
|
||
|
||
/** 出生日期 */
|
||
private Date birthDate;
|
||
|
||
/** 关系人证件类型 */
|
||
private String relationCertType;
|
||
|
||
/** 关系人证件号码 */
|
||
private String relationCertNo;
|
||
|
||
/** 手机号码1 */
|
||
private String mobilePhone1;
|
||
|
||
/** 手机号码2 */
|
||
private String mobilePhone2;
|
||
|
||
/** 微信名称1 */
|
||
private String wechatNo1;
|
||
|
||
/** 微信名称2 */
|
||
private String wechatNo2;
|
||
|
||
/** 微信名称3 */
|
||
private String wechatNo3;
|
||
|
||
/** 详细联系地址 */
|
||
private String contactAddress;
|
||
|
||
/** 关系详细描述 */
|
||
private String relationDesc;
|
||
|
||
/** 状态:0-无效,1-有效 */
|
||
private Integer status;
|
||
|
||
/** 生效日期 */
|
||
private Date effectiveDate;
|
||
|
||
/** 失效日期 */
|
||
private Date invalidDate;
|
||
|
||
/** 备注 */
|
||
private String remark;
|
||
|
||
/** 数据来源:MANUAL-手工录入,IMPORT-导入 */
|
||
private String dataSource;
|
||
|
||
/** 是否是员工亲属:0-否 */
|
||
private Boolean isEmpFamily;
|
||
|
||
/** 是否是客户亲属:1-是 */
|
||
private Boolean isCustFamily;
|
||
|
||
/** 创建时间 */
|
||
@TableField(fill = FieldFill.INSERT)
|
||
private Date createTime;
|
||
|
||
/** 更新时间 */
|
||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||
private Date updateTime;
|
||
|
||
/** 创建人 */
|
||
@TableField(fill = FieldFill.INSERT)
|
||
private String createdBy;
|
||
|
||
/** 更新人 */
|
||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||
private String updatedBy;
|
||
}
|
||
```
|
||
|
||
**Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/CcdiCustFmyRelation.java
|
||
git commit -m "feat: 添加信贷客户家庭关系实体类"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 2: 创建DTO类
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiCustFmyRelationAddDTO.java`
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiCustFmyRelationEditDTO.java`
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiCustFmyRelationQueryDTO.java`
|
||
|
||
**Step 1: 创建AddDTO**
|
||
|
||
复制 `CcdiStaffFmyRelationAddDTO.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.domain.dto;
|
||
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import jakarta.validation.constraints.NotBlank;
|
||
import jakarta.validation.constraints.NotNull;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
import java.util.Date;
|
||
|
||
/**
|
||
* 信贷客户家庭关系新增DTO
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@Schema(description = "信贷客户家庭关系新增")
|
||
public class CcdiCustFmyRelationAddDTO implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 信贷客户身份证号 */
|
||
@Schema(description = "信贷客户身份证号")
|
||
@NotBlank(message = "信贷客户身份证号不能为空")
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
@Schema(description = "关系类型")
|
||
@NotBlank(message = "关系类型不能为空")
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
@Schema(description = "关系人姓名")
|
||
@NotBlank(message = "关系人姓名不能为空")
|
||
private String relationName;
|
||
|
||
/** 性别 */
|
||
@Schema(description = "性别:M-男,F-女,O-其他")
|
||
private String gender;
|
||
|
||
/** 出生日期 */
|
||
@Schema(description = "关系人出生日期")
|
||
private Date birthDate;
|
||
|
||
/** 关系人证件类型 */
|
||
@Schema(description = "关系人证件类型")
|
||
@NotBlank(message = "关系人证件类型不能为空")
|
||
private String relationCertType;
|
||
|
||
/** 关系人证件号码 */
|
||
@Schema(description = "关系人证件号码")
|
||
@NotBlank(message = "关系人证件号码不能为空")
|
||
private String relationCertNo;
|
||
|
||
/** 手机号码1 */
|
||
@Schema(description = "手机号码1")
|
||
private String mobilePhone1;
|
||
|
||
/** 手机号码2 */
|
||
@Schema(description = "手机号码2")
|
||
private String mobilePhone2;
|
||
|
||
/** 微信名称1 */
|
||
@Schema(description = "微信名称1")
|
||
private String wechatNo1;
|
||
|
||
/** 微信名称2 */
|
||
@Schema(description = "微信名称2")
|
||
private String wechatNo2;
|
||
|
||
/** 微信名称3 */
|
||
@Schema(description = "微信名称3")
|
||
private String wechatNo3;
|
||
|
||
/** 详细联系地址 */
|
||
@Schema(description = "详细联系地址")
|
||
private String contactAddress;
|
||
|
||
/** 关系详细描述 */
|
||
@Schema(description = "关系详细描述")
|
||
private String relationDesc;
|
||
|
||
/** 生效日期 */
|
||
@Schema(description = "关系生效日期")
|
||
private Date effectiveDate;
|
||
|
||
/** 失效日期 */
|
||
@Schema(description = "关系失效日期")
|
||
private Date invalidDate;
|
||
|
||
/** 备注 */
|
||
@Schema(description = "备注信息")
|
||
private String remark;
|
||
}
|
||
```
|
||
|
||
**Step 2: 创建EditDTO**
|
||
|
||
复制 `CcdiStaffFmyRelationEditDTO.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.domain.dto;
|
||
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import jakarta.validation.constraints.NotBlank;
|
||
import jakarta.validation.constraints.NotNull;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
import java.util.Date;
|
||
|
||
/**
|
||
* 信贷客户家庭关系编辑DTO
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@Schema(description = "信贷客户家庭关系编辑")
|
||
public class CcdiCustFmyRelationEditDTO implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 主键ID */
|
||
@Schema(description = "主键ID")
|
||
@NotNull(message = "ID不能为空")
|
||
private Long id;
|
||
|
||
/** 信贷客户身份证号 */
|
||
@Schema(description = "信贷客户身份证号")
|
||
@NotBlank(message = "信贷客户身份证号不能为空")
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
@Schema(description = "关系类型")
|
||
@NotBlank(message = "关系类型不能为空")
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
@Schema(description = "关系人姓名")
|
||
@NotBlank(message = "关系人姓名不能为空")
|
||
private String relationName;
|
||
|
||
/** 性别 */
|
||
@Schema(description = "性别:M-男,F-女,O-其他")
|
||
private String gender;
|
||
|
||
/** 出生日期 */
|
||
@Schema(description = "关系人出生日期")
|
||
private Date birthDate;
|
||
|
||
/** 关系人证件类型 */
|
||
@Schema(description = "关系人证件类型")
|
||
@NotBlank(message = "关系人证件类型不能为空")
|
||
private String relationCertType;
|
||
|
||
/** 关系人证件号码 */
|
||
@Schema(description = "关系人证件号码")
|
||
@NotBlank(message = "关系人证件号码不能为空")
|
||
private String relationCertNo;
|
||
|
||
/** 手机号码1 */
|
||
@Schema(description = "手机号码1")
|
||
private String mobilePhone1;
|
||
|
||
/** 手机号码2 */
|
||
@Schema(description = "手机号码2")
|
||
private String mobilePhone2;
|
||
|
||
/** 微信名称1 */
|
||
@Schema(description = "微信名称1")
|
||
private String wechatNo1;
|
||
|
||
/** 微信名称2 */
|
||
@Schema(description = "微信名称2")
|
||
private String wechatNo2;
|
||
|
||
/** 微信名称3 */
|
||
@Schema(description = "微信名称3")
|
||
private String wechatNo3;
|
||
|
||
/** 详细联系地址 */
|
||
@Schema(description = "详细联系地址")
|
||
private String contactAddress;
|
||
|
||
/** 关系详细描述 */
|
||
@Schema(description = "关系详细描述")
|
||
private String relationDesc;
|
||
|
||
/** 状态 */
|
||
@Schema(description = "状态:0-无效,1-有效")
|
||
@NotNull(message = "状态不能为空")
|
||
private Integer status;
|
||
|
||
/** 生效日期 */
|
||
@Schema(description = "关系生效日期")
|
||
private Date effectiveDate;
|
||
|
||
/** 失效日期 */
|
||
@Schema(description = "关系失效日期")
|
||
private Date invalidDate;
|
||
|
||
/** 备注 */
|
||
@Schema(description = "备注信息")
|
||
private String remark;
|
||
}
|
||
```
|
||
|
||
**Step 3: 创建QueryDTO**
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.domain.dto;
|
||
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
|
||
/**
|
||
* 信贷客户家庭关系查询DTO
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@Schema(description = "信贷客户家庭关系查询")
|
||
public class CcdiCustFmyRelationQueryDTO implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 信贷客户身份证号 */
|
||
@Schema(description = "信贷客户身份证号")
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
@Schema(description = "关系类型")
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
@Schema(description = "关系人姓名")
|
||
private String relationName;
|
||
}
|
||
```
|
||
|
||
**Step 4: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/
|
||
git commit -m "feat: 添加信贷客户家庭关系DTO类"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 3: 创建VO类
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/vo/CcdiCustFmyRelationVO.java`
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/vo/CustFmyRelationImportFailureVO.java`
|
||
|
||
**Step 1: 创建主VO**
|
||
|
||
复制 `CcdiStaffFmyRelationVO.java`,进行以下修改:
|
||
|
||
1. **移除 personName 字段** (不关联员工表)
|
||
2. **修改类名和注释**
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.domain.vo;
|
||
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
import java.util.Date;
|
||
|
||
/**
|
||
* 信贷客户家庭关系VO
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@Schema(description = "信贷客户家庭关系")
|
||
public class CcdiCustFmyRelationVO implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 主键ID */
|
||
@Schema(description = "主键ID")
|
||
private Long id;
|
||
|
||
/** 信贷客户身份证号 */
|
||
@Schema(description = "信贷客户身份证号")
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
@Schema(description = "关系类型")
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
@Schema(description = "关系人姓名")
|
||
private String relationName;
|
||
|
||
/** 性别 */
|
||
@Schema(description = "性别:M-男,F-女,O-其他")
|
||
private String gender;
|
||
|
||
/** 出生日期 */
|
||
@Schema(description = "关系人出生日期")
|
||
private Date birthDate;
|
||
|
||
/** 关系人证件类型 */
|
||
@Schema(description = "关系人证件类型")
|
||
private String relationCertType;
|
||
|
||
/** 关系人证件号码 */
|
||
@Schema(description = "关系人证件号码")
|
||
private String relationCertNo;
|
||
|
||
/** 手机号码1 */
|
||
@Schema(description = "手机号码1")
|
||
private String mobilePhone1;
|
||
|
||
/** 手机号码2 */
|
||
@Schema(description = "手机号码2")
|
||
private String mobilePhone2;
|
||
|
||
/** 微信名称1 */
|
||
@Schema(description = "微信名称1")
|
||
private String wechatNo1;
|
||
|
||
/** 微信名称2 */
|
||
@Schema(description = "微信名称2")
|
||
private String wechatNo2;
|
||
|
||
/** 微信名称3 */
|
||
@Schema(description = "微信名称3")
|
||
private String wechatNo3;
|
||
|
||
/** 详细联系地址 */
|
||
@Schema(description = "详细联系地址")
|
||
private String contactAddress;
|
||
|
||
/** 关系详细描述 */
|
||
@Schema(description = "关系详细描述")
|
||
private String relationDesc;
|
||
|
||
/** 状态 */
|
||
@Schema(description = "状态:0-无效,1-有效")
|
||
private Integer status;
|
||
|
||
/** 生效日期 */
|
||
@Schema(description = "关系生效日期")
|
||
private Date effectiveDate;
|
||
|
||
/** 失效日期 */
|
||
@Schema(description = "关系失效日期")
|
||
private Date invalidDate;
|
||
|
||
/** 备注 */
|
||
@Schema(description = "备注信息")
|
||
private String remark;
|
||
|
||
/** 数据来源 */
|
||
@Schema(description = "数据来源:MANUAL-手工录入,IMPORT-导入")
|
||
private String dataSource;
|
||
|
||
/** 是否是员工亲属 */
|
||
@Schema(description = "是否是员工亲属:0-否")
|
||
private Boolean isEmpFamily;
|
||
|
||
/** 是否是客户亲属 */
|
||
@Schema(description = "是否是客户亲属:1-是")
|
||
private Boolean isCustFamily;
|
||
|
||
/** 创建时间 */
|
||
@Schema(description = "创建时间")
|
||
private Date createTime;
|
||
|
||
/** 更新时间 */
|
||
@Schema(description = "更新时间")
|
||
private Date updateTime;
|
||
|
||
/** 创建人 */
|
||
@Schema(description = "创建人")
|
||
private String createdBy;
|
||
|
||
/** 更新人 */
|
||
@Schema(description = "更新人")
|
||
private String updatedBy;
|
||
}
|
||
```
|
||
|
||
**Step 2: 创建导入失败VO**
|
||
|
||
复制 `StaffFmyRelationImportFailureVO.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.domain.vo;
|
||
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
|
||
/**
|
||
* 信贷客户家庭关系导入失败VO
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@Schema(description = "信贷客户家庭关系导入失败记录")
|
||
public class CustFmyRelationImportFailureVO implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 行号 */
|
||
@Schema(description = "行号")
|
||
private Integer rowNum;
|
||
|
||
/** 信贷客户身份证号 */
|
||
@Schema(description = "信贷客户身份证号")
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
@Schema(description = "关系类型")
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
@Schema(description = "关系人姓名")
|
||
private String relationName;
|
||
|
||
/** 错误消息 */
|
||
@Schema(description = "错误消息")
|
||
private String errorMessage;
|
||
}
|
||
```
|
||
|
||
**Step 3: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 4: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/vo/
|
||
git commit -m "feat: 添加信贷客户家庭关系VO类"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 4: 创建Excel类
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/excel/CcdiCustFmyRelationExcel.java`
|
||
|
||
**Step 1: 创建Excel类**
|
||
|
||
复制 `CcdiStaffFmyRelationExcel.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.domain.excel;
|
||
|
||
import com.alibaba.excel.annotation.ExcelProperty;
|
||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
|
||
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
|
||
import lombok.Data;
|
||
|
||
import java.io.Serial;
|
||
import java.io.Serializable;
|
||
import java.util.Date;
|
||
|
||
/**
|
||
* 信贷客户家庭关系Excel导入导出对象
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Data
|
||
@ContentRowHeight(20)
|
||
@HeadRowHeight(30)
|
||
public class CcdiCustFmyRelationExcel implements Serializable {
|
||
|
||
@Serial
|
||
private static final long serialVersionUID = 1L;
|
||
|
||
/** 信贷客户身份证号 */
|
||
@ExcelProperty(value = "信贷客户身份证号*", index = 0)
|
||
@ColumnWidth(20)
|
||
private String personId;
|
||
|
||
/** 关系类型 */
|
||
@ExcelProperty(value = "关系类型*", index = 1)
|
||
@ColumnWidth(15)
|
||
private String relationType;
|
||
|
||
/** 关系人姓名 */
|
||
@ExcelProperty(value = "关系人姓名*", index = 2)
|
||
@ColumnWidth(15)
|
||
private String relationName;
|
||
|
||
/** 性别 */
|
||
@ExcelProperty(value = "性别", index = 3)
|
||
@ColumnWidth(10)
|
||
private String gender;
|
||
|
||
/** 出生日期 */
|
||
@ExcelProperty(value = "出生日期", index = 4)
|
||
@ColumnWidth(15)
|
||
private Date birthDate;
|
||
|
||
/** 关系人证件类型 */
|
||
@ExcelProperty(value = "关系人证件类型*", index = 5)
|
||
@ColumnWidth(15)
|
||
private String relationCertType;
|
||
|
||
/** 关系人证件号码 */
|
||
@ExcelProperty(value = "关系人证件号码*", index = 6)
|
||
@ColumnWidth(20)
|
||
private String relationCertNo;
|
||
|
||
/** 手机号码1 */
|
||
@ExcelProperty(value = "手机号码1", index = 7)
|
||
@ColumnWidth(15)
|
||
private String mobilePhone1;
|
||
|
||
/** 手机号码2 */
|
||
@ExcelProperty(value = "手机号码2", index = 8)
|
||
@ColumnWidth(15)
|
||
private String mobilePhone2;
|
||
|
||
/** 微信名称1 */
|
||
@ExcelProperty(value = "微信名称1", index = 9)
|
||
@ColumnWidth(15)
|
||
private String wechatNo1;
|
||
|
||
/** 微信名称2 */
|
||
@ExcelProperty(value = "微信名称2", index = 10)
|
||
@ColumnWidth(15)
|
||
private String wechatNo2;
|
||
|
||
/** 微信名称3 */
|
||
@ExcelProperty(value = "微信名称3", index = 11)
|
||
@ColumnWidth(15)
|
||
private String wechatNo3;
|
||
|
||
/** 详细联系地址 */
|
||
@ExcelProperty(value = "详细联系地址", index = 12)
|
||
@ColumnWidth(30)
|
||
private String contactAddress;
|
||
|
||
/** 关系详细描述 */
|
||
@ExcelProperty(value = "关系详细描述", index = 13)
|
||
@ColumnWidth(30)
|
||
private String relationDesc;
|
||
|
||
/** 状态 */
|
||
@ExcelProperty(value = "状态", index = 14)
|
||
@ColumnWidth(10)
|
||
private Integer status;
|
||
|
||
/** 生效日期 */
|
||
@ExcelProperty(value = "生效日期", index = 15)
|
||
@ColumnWidth(18)
|
||
private Date effectiveDate;
|
||
|
||
/** 失效日期 */
|
||
@ExcelProperty(value = "失效日期", index = 16)
|
||
@ColumnWidth(18)
|
||
private Date invalidDate;
|
||
|
||
/** 备注 */
|
||
@ExcelProperty(value = "备注", index = 17)
|
||
@ColumnWidth(30)
|
||
private String remark;
|
||
}
|
||
```
|
||
|
||
**Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/excel/CcdiCustFmyRelationExcel.java
|
||
git commit -m "feat: 添加信贷客户家庭关系Excel类"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 5: 创建Mapper接口
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/mapper/CcdiCustFmyRelationMapper.java`
|
||
|
||
**Step 1: 创建Mapper接口**
|
||
|
||
复制 `CcdiStaffFmyRelationMapper.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.mapper;
|
||
|
||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||
import com.ruoyi.ccdi.domain.CcdiCustFmyRelation;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationQueryDTO;
|
||
import com.ruoyi.ccdi.domain.vo.CcdiCustFmyRelationVO;
|
||
import org.apache.ibatis.annotations.Param;
|
||
|
||
import java.util.List;
|
||
|
||
/**
|
||
* 信贷客户家庭关系Mapper接口
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
public interface CcdiCustFmyRelationMapper extends BaseMapper<CcdiCustFmyRelation> {
|
||
|
||
/**
|
||
* 分页查询信贷客户家庭关系
|
||
*
|
||
* @param page 分页对象
|
||
* @param query 查询条件
|
||
* @return 信贷客户家庭关系VO列表
|
||
*/
|
||
Page<CcdiCustFmyRelationVO> selectRelationPage(Page<CcdiCustFmyRelationVO> page,
|
||
@Param("query") CcdiCustFmyRelationQueryDTO query);
|
||
|
||
/**
|
||
* 根据ID查询信贷客户家庭关系详情
|
||
*
|
||
* @param id 主键ID
|
||
* @return 信贷客户家庭关系VO
|
||
*/
|
||
CcdiCustFmyRelationVO selectRelationById(@Param("id") Long id);
|
||
|
||
/**
|
||
* 查询已存在的关系记录(用于导入校验)
|
||
*
|
||
* @param personId 信贷客户身份证号
|
||
* @param relationType 关系类型
|
||
* @param relationCertNo 关系人证件号码
|
||
* @return 已存在的关系记录
|
||
*/
|
||
CcdiCustFmyRelation selectExistingRelations(@Param("personId") String personId,
|
||
@Param("relationType") String relationType,
|
||
@Param("relationCertNo") String relationCertNo);
|
||
|
||
/**
|
||
* 批量插入信贷客户家庭关系
|
||
*
|
||
* @param relations 信贷客户家庭关系列表
|
||
* @return 插入条数
|
||
*/
|
||
int insertBatch(@Param("relations") List<CcdiCustFmyRelation> relations);
|
||
|
||
/**
|
||
* 根据证件号码查询关系数量
|
||
*
|
||
* @param relationCertNo 关系人证件号码
|
||
* @return 关系数量
|
||
*/
|
||
int countByCertNo(@Param("relationCertNo") String relationCertNo);
|
||
}
|
||
```
|
||
|
||
**Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/mapper/CcdiCustFmyRelationMapper.java
|
||
git commit -m "feat: 添加信贷客户家庭关系Mapper接口"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 6: 创建Mapper XML映射
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiCustFmyRelationMapper.xml`
|
||
|
||
**Step 1: 创建XML映射文件**
|
||
|
||
复制 `CcdiStaffFmyRelationMapper.xml`,进行以下关键修改:
|
||
|
||
1. **修改namespace和resultMap**
|
||
2. **移除LEFT JOIN员工表**
|
||
3. **修改WHERE条件为 `is_cust_family = 1`**
|
||
4. **移除personName相关字段**
|
||
|
||
```xml
|
||
<?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.ccdi.mapper.CcdiCustFmyRelationMapper">
|
||
|
||
<resultMap id="CcdiCustFmyRelationVOResult" type="com.ruoyi.ccdi.domain.vo.CcdiCustFmyRelationVO">
|
||
<id property="id" column="id"/>
|
||
<result property="personId" column="person_id"/>
|
||
<result property="relationType" column="relation_type"/>
|
||
<result property="relationName" column="relation_name"/>
|
||
<result property="gender" column="gender"/>
|
||
<result property="birthDate" column="birth_date"/>
|
||
<result property="relationCertType" column="relation_cert_type"/>
|
||
<result property="relationCertNo" column="relation_cert_no"/>
|
||
<result property="mobilePhone1" column="mobile_phone1"/>
|
||
<result property="mobilePhone2" column="mobile_phone2"/>
|
||
<result property="wechatNo1" column="wechat_no1"/>
|
||
<result property="wechatNo2" column="wechat_no2"/>
|
||
<result property="wechatNo3" column="wechat_no3"/>
|
||
<result property="contactAddress" column="contact_address"/>
|
||
<result property="relationDesc" column="relation_desc"/>
|
||
<result property="status" column="status"/>
|
||
<result property="effectiveDate" column="effective_date"/>
|
||
<result property="invalidDate" column="invalid_date"/>
|
||
<result property="remark" column="remark"/>
|
||
<result property="dataSource" column="data_source"/>
|
||
<result property="isEmpFamily" column="is_emp_family"/>
|
||
<result property="isCustFamily" column="is_cust_family"/>
|
||
<result property="createTime" column="create_time"/>
|
||
<result property="updateTime" column="update_time"/>
|
||
<result property="createdBy" column="created_by"/>
|
||
<result property="updatedBy" column="updated_by"/>
|
||
</resultMap>
|
||
|
||
<!-- 分页查询信贷客户家庭关系 -->
|
||
<select id="selectRelationPage" resultMap="CcdiCustFmyRelationVOResult">
|
||
SELECT
|
||
r.id, r.person_id, r.relation_type, r.relation_name,
|
||
r.gender, r.birth_date, r.relation_cert_type, r.relation_cert_no,
|
||
r.mobile_phone1, r.mobile_phone2, r.wechat_no1, r.wechat_no2, r.wechat_no3,
|
||
r.contact_address, r.relation_desc, r.effective_date, r.invalid_date,
|
||
r.status, r.remark, r.data_source, r.is_emp_family, r.is_cust_family,
|
||
r.created_by, r.create_time, r.updated_by, r.update_time
|
||
FROM ccdi_cust_fmy_relation r
|
||
<where>
|
||
r.is_cust_family = 1
|
||
<if test="query.personId != null and query.personId != ''">
|
||
AND r.person_id = #{query.personId}
|
||
</if>
|
||
<if test="query.relationType != null and query.relationType != ''">
|
||
AND r.relation_type = #{query.relationType}
|
||
</if>
|
||
<if test="query.relationName != null and query.relationName != ''">
|
||
AND r.relation_name LIKE CONCAT('%', #{query.relationName}, '%')
|
||
</if>
|
||
</where>
|
||
ORDER BY r.create_time DESC
|
||
</select>
|
||
|
||
<!-- 根据ID查询详情 -->
|
||
<select id="selectRelationById" resultMap="CcdiCustFmyRelationVOResult">
|
||
SELECT
|
||
r.id, r.person_id, r.relation_type, r.relation_name,
|
||
r.gender, r.birth_date, r.relation_cert_type, r.relation_cert_no,
|
||
r.mobile_phone1, r.mobile_phone2, r.wechat_no1, r.wechat_no2, r.wechat_no3,
|
||
r.contact_address, r.relation_desc, r.effective_date, r.invalid_date,
|
||
r.status, r.remark, r.data_source, r.is_emp_family, r.is_cust_family,
|
||
r.created_by, r.create_time, r.updated_by, r.update_time
|
||
FROM ccdi_cust_fmy_relation r
|
||
WHERE r.id = #{id} AND r.is_cust_family = 1
|
||
</select>
|
||
|
||
<!-- 查询已存在的关系(用于导入校验) -->
|
||
<select id="selectExistingRelations" resultType="com.ruoyi.ccdi.domain.CcdiCustFmyRelation">
|
||
SELECT *
|
||
FROM ccdi_cust_fmy_relation
|
||
WHERE is_cust_family = 1
|
||
AND person_id = #{personId}
|
||
AND relation_type = #{relationType}
|
||
AND relation_cert_no = #{relationCertNo}
|
||
AND status = 1
|
||
LIMIT 1
|
||
</select>
|
||
|
||
<!-- 批量插入 -->
|
||
<insert id="insertBatch" parameterType="java.util.List">
|
||
INSERT INTO ccdi_cust_fmy_relation (
|
||
person_id, relation_type, relation_name, gender, birth_date,
|
||
relation_cert_type, relation_cert_no, mobile_phone1, mobile_phone2,
|
||
wechat_no1, wechat_no2, wechat_no3, contact_address, relation_desc,
|
||
status, effective_date, invalid_date, remark, data_source,
|
||
is_emp_family, is_cust_family, created_by, create_time
|
||
) VALUES
|
||
<foreach collection="relations" item="item" separator=",">
|
||
(
|
||
#{item.personId}, #{item.relationType}, #{item.relationName},
|
||
#{item.gender}, #{item.birthDate}, #{item.relationCertType},
|
||
#{item.relationCertNo}, #{item.mobilePhone1}, #{item.mobilePhone2},
|
||
#{item.wechatNo1}, #{item.wechatNo2}, #{item.wechatNo3},
|
||
#{item.contactAddress}, #{item.relationDesc}, #{item.status},
|
||
#{item.effectiveDate}, #{item.invalidDate}, #{item.remark},
|
||
#{item.dataSource}, #{item.isEmpFamily}, #{item.isCustFamily},
|
||
#{item.createdBy}, #{item.createTime}
|
||
)
|
||
</foreach>
|
||
</insert>
|
||
|
||
<!-- 根据证件号码查询关系数量 -->
|
||
<select id="countByCertNo" resultType="int">
|
||
SELECT COUNT(1)
|
||
FROM ccdi_cust_fmy_relation
|
||
WHERE is_cust_family = 1
|
||
AND relation_cert_no = #{relationCertNo}
|
||
AND status = 1
|
||
</select>
|
||
|
||
</mapper>
|
||
```
|
||
|
||
**Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiCustFmyRelationMapper.xml
|
||
git commit -m "feat: 添加信贷客户家庭关系Mapper XML映射"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 7: 创建Service接口
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/ICcdiCustFmyRelationService.java`
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/ICcdiCustFmyRelationImportService.java`
|
||
|
||
**Step 1: 创建主Service接口**
|
||
|
||
复制 `ICcdiStaffFmyRelationService.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.service;
|
||
|
||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationAddDTO;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationEditDTO;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationQueryDTO;
|
||
import com.ruoyi.ccdi.domain.excel.CcdiCustFmyRelationExcel;
|
||
import com.ruoyi.ccdi.domain.vo.CcdiCustFmyRelationVO;
|
||
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import java.util.List;
|
||
|
||
/**
|
||
* 信贷客户家庭关系Service接口
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
public interface ICcdiCustFmyRelationService {
|
||
|
||
/**
|
||
* 分页查询信贷客户家庭关系
|
||
*
|
||
* @param query 查询条件
|
||
* @param pageNum 页码
|
||
* @param pageSize 每页条数
|
||
* @return 分页结果
|
||
*/
|
||
Page<CcdiCustFmyRelationVO> selectRelationPage(CcdiCustFmyRelationQueryDTO query,
|
||
Integer pageNum, Integer pageSize);
|
||
|
||
/**
|
||
* 根据ID查询信贷客户家庭关系详情
|
||
*
|
||
* @param id 主键ID
|
||
* @return 信贷客户家庭关系VO
|
||
*/
|
||
CcdiCustFmyRelationVO selectRelationById(Long id);
|
||
|
||
/**
|
||
* 新增信贷客户家庭关系
|
||
*
|
||
* @param addDTO 新增DTO
|
||
* @return 是否成功
|
||
*/
|
||
boolean insertRelation(CcdiCustFmyRelationAddDTO addDTO);
|
||
|
||
/**
|
||
* 修改信贷客户家庭关系
|
||
*
|
||
* @param editDTO 编辑DTO
|
||
* @return 是否成功
|
||
*/
|
||
boolean updateRelation(CcdiCustFmyRelationEditDTO editDTO);
|
||
|
||
/**
|
||
* 删除信贷客户家庭关系
|
||
*
|
||
* @param ids 主键ID数组
|
||
* @return 是否成功
|
||
*/
|
||
boolean deleteRelationByIds(Long[] ids);
|
||
|
||
/**
|
||
* 导出信贷客户家庭关系
|
||
*
|
||
* @param query 查询条件
|
||
* @param response HTTP响应
|
||
*/
|
||
void exportRelations(CcdiCustFmyRelationQueryDTO query, HttpServletResponse response);
|
||
|
||
/**
|
||
* 生成导入模板
|
||
*
|
||
* @param response HTTP响应
|
||
*/
|
||
void importTemplate(HttpServletResponse response);
|
||
|
||
/**
|
||
* 批量导入信贷客户家庭关系
|
||
*
|
||
* @param excels Excel数据列表
|
||
* @return 导入任务ID
|
||
*/
|
||
String importRelations(List<CcdiCustFmyRelationExcel> excels);
|
||
}
|
||
```
|
||
|
||
**Step 2: 创建导入Service接口**
|
||
|
||
复制 `ICcdiStaffFmyRelationImportService.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.service;
|
||
|
||
import com.ruoyi.ccdi.domain.excel.CcdiCustFmyRelationExcel;
|
||
import com.ruoyi.ccdi.domain.vo.CustFmyRelationImportFailureVO;
|
||
|
||
import java.util.List;
|
||
|
||
/**
|
||
* 信贷客户家庭关系导入Service接口
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
public interface ICcdiCustFmyRelationImportService {
|
||
|
||
/**
|
||
* 异步导入信贷客户家庭关系
|
||
*
|
||
* @param excels Excel数据列表
|
||
* @param taskId 任务ID
|
||
*/
|
||
void importRelationsAsync(List<CcdiCustFmyRelationExcel> excels, String taskId);
|
||
|
||
/**
|
||
* 校验单条数据
|
||
*
|
||
* @param excel Excel数据
|
||
* @param rowNum 行号
|
||
* @return 错误消息,为null表示校验通过
|
||
*/
|
||
String validateExcelRow(CcdiCustFmyRelationExcel excel, Integer rowNum);
|
||
|
||
/**
|
||
* 获取导入失败记录
|
||
*
|
||
* @param taskId 任务ID
|
||
* @return 失败记录列表
|
||
*/
|
||
List<CustFmyRelationImportFailureVO> getImportFailures(String taskId);
|
||
}
|
||
```
|
||
|
||
**Step 3: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 4: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/
|
||
git commit -m "feat: 添加信贷客户家庭关系Service接口"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 8: 创建Service实现类
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiCustFmyRelationServiceImpl.java`
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiCustFmyRelationImportServiceImpl.java`
|
||
|
||
**Step 1: 创建主Service实现类**
|
||
|
||
复制 `CcdiStaffFmyRelationServiceImpl.java`,进行以下关键修改:
|
||
|
||
1. **类名和注入的Mapper/Service**
|
||
2. **设置 `isEmpFamily=false, isCustFamily=true`**
|
||
3. **Redis Key为 `import:custFmyRelation:`**
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.service.impl;
|
||
|
||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||
import com.ruoyi.ccdi.domain.CcdiCustFmyRelation;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationAddDTO;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationEditDTO;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationQueryDTO;
|
||
import com.ruoyi.ccdi.domain.excel.CcdiCustFmyRelationExcel;
|
||
import com.ruoyi.ccdi.domain.vo.CcdiCustFmyRelationVO;
|
||
import com.ruoyi.ccdi.mapper.CcdiCustFmyRelationMapper;
|
||
import com.ruoyi.ccdi.service.ICcdiCustFmyRelationImportService;
|
||
import com.ruoyi.ccdi.service.ICcdiCustFmyRelationService;
|
||
import com.ruoyi.common.utils.SecurityUtils;
|
||
import com.ruoyi.common.utils.StringUtils;
|
||
import jakarta.annotation.Resource;
|
||
import jakarta.servlet.http.HttpServletResponse;
|
||
import org.springframework.beans.BeanUtils;
|
||
import org.springframework.data.redis.core.RedisTemplate;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
|
||
import java.net.URLEncoder;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.util.Date;
|
||
import java.util.List;
|
||
import java.util.UUID;
|
||
import java.util.concurrent.TimeUnit;
|
||
|
||
/**
|
||
* 信贷客户家庭关系Service实现
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Service
|
||
public class CcdiCustFmyRelationServiceImpl implements ICcdiCustFmyRelationService {
|
||
|
||
@Resource
|
||
private CcdiCustFmyRelationMapper mapper;
|
||
|
||
@Resource
|
||
private ICcdiCustFmyRelationImportService importService;
|
||
|
||
@Resource
|
||
private RedisTemplate<String, Object> redisTemplate;
|
||
|
||
private static final String IMPORT_TASK_KEY_PREFIX = "import:custFmyRelation:";
|
||
|
||
@Override
|
||
public Page<CcdiCustFmyRelationVO> selectRelationPage(CcdiCustFmyRelationQueryDTO query,
|
||
Integer pageNum, Integer pageSize) {
|
||
Page<CcdiCustFmyRelationVO> page = new Page<>(pageNum, pageSize);
|
||
return mapper.selectRelationPage(page, query);
|
||
}
|
||
|
||
@Override
|
||
public CcdiCustFmyRelationVO selectRelationById(Long id) {
|
||
return mapper.selectRelationById(id);
|
||
}
|
||
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public boolean insertRelation(CcdiCustFmyRelationAddDTO addDTO) {
|
||
CcdiCustFmyRelation relation = new CcdiCustFmyRelation();
|
||
BeanUtils.copyProperties(addDTO, relation);
|
||
|
||
// 关键设置:客户家庭关系
|
||
relation.setIsEmpFamily(false);
|
||
relation.setIsCustFamily(true);
|
||
relation.setStatus(1);
|
||
relation.setDataSource("MANUAL");
|
||
|
||
return mapper.insert(relation) > 0;
|
||
}
|
||
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public boolean updateRelation(CcdiCustFmyRelationEditDTO editDTO) {
|
||
CcdiCustFmyRelation relation = new CcdiCustFmyRelation();
|
||
BeanUtils.copyProperties(editDTO, relation);
|
||
|
||
return mapper.updateById(relation) > 0;
|
||
}
|
||
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public boolean deleteRelationByIds(Long[] ids) {
|
||
return mapper.deleteBatchIds(List.of(ids)) > 0;
|
||
}
|
||
|
||
@Override
|
||
public void exportRelations(CcdiCustFmyRelationQueryDTO query, HttpServletResponse response) {
|
||
// 查询所有符合条件的数据(不分页)
|
||
Page<CcdiCustFmyRelationVO> page = new Page<>(1, 10000);
|
||
Page<CcdiCustFmyRelationVO> result = mapper.selectRelationPage(page, query);
|
||
|
||
List<CcdiCustFmyRelationExcel> excels = result.getRecords().stream()
|
||
.map(this::convertToExcel)
|
||
.toList();
|
||
|
||
// 使用EasyExcel导出
|
||
try {
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setCharacterEncoding("utf-8");
|
||
String fileName = URLEncoder.encode("信贷客户家庭关系", StandardCharsets.UTF_8)
|
||
.replaceAll("\\+", "%20");
|
||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||
|
||
// 这里使用EasyExcel工具类导出
|
||
// EasyExcel.write(response.getOutputStream(), CcdiCustFmyRelationExcel.class)
|
||
// .sheet("信贷客户家庭关系")
|
||
// .doWrite(excels);
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("导出失败", e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void importTemplate(HttpServletResponse response) {
|
||
try {
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setCharacterEncoding("utf-8");
|
||
String fileName = URLEncoder.encode("信贷客户家庭关系导入模板", StandardCharsets.UTF_8)
|
||
.replaceAll("\\+", "%20");
|
||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||
|
||
// EasyExcel.write(response.getOutputStream(), CcdiCustFmyRelationExcel.class)
|
||
// .sheet("模板")
|
||
// .doWrite(Collections.emptyList());
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("模板下载失败", e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public String importRelations(List<CcdiCustFmyRelationExcel> excels) {
|
||
String taskId = UUID.randomUUID().toString();
|
||
|
||
// 保存任务状态到Redis
|
||
redisTemplate.opsForValue().set(IMPORT_TASK_KEY_PREFIX + taskId, "PROCESSING", 1, TimeUnit.HOURS);
|
||
|
||
// 异步导入
|
||
importService.importRelationsAsync(excels, taskId);
|
||
|
||
return taskId;
|
||
}
|
||
|
||
private CcdiCustFmyRelationExcel convertToExcel(CcdiCustFmyRelationVO vo) {
|
||
CcdiCustFmyRelationExcel excel = new CcdiCustFmyRelationExcel();
|
||
BeanUtils.copyProperties(vo, excel);
|
||
return excel;
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 2: 创建导入Service实现类**
|
||
|
||
复制 `CcdiStaffFmyRelationImportServiceImpl.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.service.impl;
|
||
|
||
import com.alibaba.excel.EasyExcel;
|
||
import com.ruoyi.ccdi.domain.CcdiCustFmyRelation;
|
||
import com.ruoyi.ccdi.domain.excel.CcdiCustFmyRelationExcel;
|
||
import com.ruoyi.ccdi.domain.vo.CustFmyRelationImportFailureVO;
|
||
import com.ruoyi.ccdi.mapper.CcdiCustFmyRelationMapper;
|
||
import com.ruoyi.ccdi.service.ICcdiCustFmyRelationImportService;
|
||
import com.ruoyi.common.utils.SecurityUtils;
|
||
import jakarta.annotation.Resource;
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.springframework.data.redis.core.RedisTemplate;
|
||
import org.springframework.scheduling.annotation.Async;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.Date;
|
||
import java.util.List;
|
||
import java.util.concurrent.TimeUnit;
|
||
|
||
/**
|
||
* 信贷客户家庭关系导入Service实现
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Service
|
||
public class CcdiCustFmyRelationImportServiceImpl implements ICcdiCustFmyRelationImportService {
|
||
|
||
private static final Logger log = LoggerFactory.getLogger(CcdiCustFmyRelationImportServiceImpl.class);
|
||
|
||
@Resource
|
||
private CcdiCustFmyRelationMapper mapper;
|
||
|
||
@Resource
|
||
private RedisTemplate<String, Object> redisTemplate;
|
||
|
||
private static final String IMPORT_TASK_KEY_PREFIX = "import:custFmyRelation:";
|
||
private static final String IMPORT_FAILURE_KEY_PREFIX = "import:custFmyRelation:failures:";
|
||
|
||
@Async
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void importRelationsAsync(List<CcdiCustFmyRelationExcel> excels, String taskId) {
|
||
List<CcdiCustFmyRelation> validRelations = new ArrayList<>();
|
||
List<CustFmyRelationImportFailureVO> failures = new ArrayList<>();
|
||
|
||
try {
|
||
for (int i = 0; i < excels.size(); i++) {
|
||
CcdiCustFmyRelationExcel excel = excels.get(i);
|
||
Integer rowNum = i + 2; // Excel行号从2开始(第1行是表头)
|
||
|
||
String errorMessage = validateExcelRow(excel, rowNum);
|
||
if (errorMessage != null) {
|
||
CustFmyRelationImportFailureVO failure = new CustFmyRelationImportFailureVO();
|
||
failure.setRowNum(rowNum);
|
||
failure.setPersonId(excel.getPersonId());
|
||
failure.setRelationType(excel.getRelationType());
|
||
failure.setRelationName(excel.getRelationName());
|
||
failure.setErrorMessage(errorMessage);
|
||
failures.add(failure);
|
||
continue;
|
||
}
|
||
|
||
CcdiCustFmyRelation relation = convertToRelation(excel);
|
||
validRelations.add(relation);
|
||
}
|
||
|
||
// 批量插入有效数据
|
||
if (!validRelations.isEmpty()) {
|
||
mapper.insertBatch(validRelations);
|
||
}
|
||
|
||
// 保存失败记录到Redis(24小时过期)
|
||
if (!failures.isEmpty()) {
|
||
redisTemplate.opsForValue().set(
|
||
IMPORT_FAILURE_KEY_PREFIX + taskId,
|
||
failures,
|
||
24,
|
||
TimeUnit.HOURS
|
||
);
|
||
}
|
||
|
||
// 更新任务状态
|
||
redisTemplate.opsForValue().set(
|
||
IMPORT_TASK_KEY_PREFIX + taskId,
|
||
"COMPLETED:" + validRelations.size() + ":" + failures.size(),
|
||
1,
|
||
TimeUnit.HOURS
|
||
);
|
||
|
||
} catch (Exception e) {
|
||
log.error("导入失败", e);
|
||
redisTemplate.opsForValue().set(
|
||
IMPORT_TASK_KEY_PREFIX + taskId,
|
||
"FAILED:" + e.getMessage(),
|
||
1,
|
||
TimeUnit.HOURS
|
||
);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public String validateExcelRow(CcdiCustFmyRelationExcel excel, Integer rowNum) {
|
||
if (excel.getPersonId() == null || excel.getPersonId().trim().isEmpty()) {
|
||
return "信贷客户身份证号不能为空";
|
||
}
|
||
|
||
if (excel.getRelationType() == null || excel.getRelationType().trim().isEmpty()) {
|
||
return "关系类型不能为空";
|
||
}
|
||
|
||
if (excel.getRelationName() == null || excel.getRelationName().trim().isEmpty()) {
|
||
return "关系人姓名不能为空";
|
||
}
|
||
|
||
if (excel.getRelationCertType() == null || excel.getRelationCertType().trim().isEmpty()) {
|
||
return "关系人证件类型不能为空";
|
||
}
|
||
|
||
if (excel.getRelationCertNo() == null || excel.getRelationCertNo().trim().isEmpty()) {
|
||
return "关系人证件号码不能为空";
|
||
}
|
||
|
||
// 检查是否已存在相同的关系
|
||
CcdiCustFmyRelation existing = mapper.selectExistingRelations(
|
||
excel.getPersonId(),
|
||
excel.getRelationType(),
|
||
excel.getRelationCertNo()
|
||
);
|
||
|
||
if (existing != null) {
|
||
return "该关系已存在,请勿重复导入";
|
||
}
|
||
|
||
return null; // 校验通过
|
||
}
|
||
|
||
@Override
|
||
@SuppressWarnings("unchecked")
|
||
public List<CustFmyRelationImportFailureVO> getImportFailures(String taskId) {
|
||
Object obj = redisTemplate.opsForValue().get(IMPORT_FAILURE_KEY_PREFIX + taskId);
|
||
if (obj != null) {
|
||
return (List<CustFmyRelationImportFailureVO>) obj;
|
||
}
|
||
return new ArrayList<>();
|
||
}
|
||
|
||
private CcdiCustFmyRelation convertToRelation(CcdiCustFmyRelationExcel excel) {
|
||
CcdiCustFmyRelation relation = new CcdiCustFmyRelation();
|
||
org.springframework.beans.BeanUtils.copyProperties(excel, relation);
|
||
|
||
relation.setIsEmpFamily(false);
|
||
relation.setIsCustFamily(true);
|
||
relation.setStatus(excel.getStatus() != null ? excel.getStatus() : 1);
|
||
relation.setDataSource("IMPORT");
|
||
relation.setCreatedBy(SecurityUtils.getUsername());
|
||
relation.setCreateTime(new Date());
|
||
|
||
return relation;
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 3: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 4: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/
|
||
git commit -m "feat: 添加信贷客户家庭关系Service实现类"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 9: 创建Controller
|
||
|
||
**Files:**
|
||
- Create: `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/controller/CcdiCustFmyRelationController.java`
|
||
|
||
**Step 1: 创建Controller**
|
||
|
||
复制 `CcdiStaffFmyRelationController.java`,修改:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.controller;
|
||
|
||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationAddDTO;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationEditDTO;
|
||
import com.ruoyi.ccdi.domain.dto.CcdiCustFmyRelationQueryDTO;
|
||
import com.ruoyi.ccdi.domain.excel.CcdiCustFmyRelationExcel;
|
||
import com.ruoyi.ccdi.domain.vo.CcdiCustFmyRelationVO;
|
||
import com.ruoyi.ccdi.domain.vo.CustFmyRelationImportFailureVO;
|
||
import com.ruoyi.ccdi.service.ICcdiCustFmyRelationService;
|
||
import com.ruoyi.common.core.controller.BaseController;
|
||
import com.ruoyi.common.core.domain.AjaxResult;
|
||
import com.ruoyi.common.core.page.TableDataInfo;
|
||
import io.swagger.v3.oas.annotations.Operation;
|
||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||
import jakarta.annotation.Resource;
|
||
import jakarta.servlet.http.HttpServletResponse;
|
||
import org.springframework.security.access.prepost.PreAuthorize;
|
||
import org.springframework.validation.annotation.Validated;
|
||
import org.springframework.web.bind.annotation.*;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
|
||
import java.io.IOException;
|
||
import java.util.List;
|
||
|
||
/**
|
||
* 信贷客户家庭关系Controller
|
||
*
|
||
* @author ruoyi
|
||
* @date 2026-02-11
|
||
*/
|
||
@Tag(name = "信贷客户家庭关系管理")
|
||
@RestController
|
||
@RequestMapping("/ccdi/custFmyRelation")
|
||
public class CcdiCustFmyRelationController extends BaseController {
|
||
|
||
@Resource
|
||
private ICcdiCustFmyRelationService relationService;
|
||
|
||
/**
|
||
* 查询信贷客户家庭关系列表
|
||
*/
|
||
@Operation(summary = "查询信贷客户家庭关系列表")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:query')")
|
||
@GetMapping("/list")
|
||
public TableDataInfo list(CcdiCustFmyRelationQueryDTO query) {
|
||
startPage();
|
||
Page<CcdiCustFmyRelationVO> page = relationService.selectRelationPage(query, getPageNum(), getPageSize());
|
||
return getDataTable(page.getRecords(), page.getTotal());
|
||
}
|
||
|
||
/**
|
||
* 根据ID查询信贷客户家庭关系详情
|
||
*/
|
||
@Operation(summary = "查询信贷客户家庭关系详情")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:query')")
|
||
@GetMapping("/{id}")
|
||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||
CcdiCustFmyRelationVO relation = relationService.selectRelationById(id);
|
||
return success(relation);
|
||
}
|
||
|
||
/**
|
||
* 新增信贷客户家庭关系
|
||
*/
|
||
@Operation(summary = "新增信贷客户家庭关系")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:add')")
|
||
@PostMapping
|
||
public AjaxResult add(@Validated @RequestBody CcdiCustFmyRelationAddDTO addDTO) {
|
||
return toAjax(relationService.insertRelation(addDTO));
|
||
}
|
||
|
||
/**
|
||
* 修改信贷客户家庭关系
|
||
*/
|
||
@Operation(summary = "修改信贷客户家庭关系")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:edit')")
|
||
@PutMapping
|
||
public AjaxResult edit(@Validated @RequestBody CcdiCustFmyRelationEditDTO editDTO) {
|
||
return toAjax(relationService.updateRelation(editDTO));
|
||
}
|
||
|
||
/**
|
||
* 删除信贷客户家庭关系
|
||
*/
|
||
@Operation(summary = "删除信贷客户家庭关系")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:remove')")
|
||
@DeleteMapping("/{ids}")
|
||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||
return toAjax(relationService.deleteRelationByIds(ids));
|
||
}
|
||
|
||
/**
|
||
* 导出信贷客户家庭关系
|
||
*/
|
||
@Operation(summary = "导出信贷客户家庭关系")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:export')")
|
||
@PostMapping("/export")
|
||
public void export(HttpServletResponse response, CcdiCustFmyRelationQueryDTO query) {
|
||
relationService.exportRelations(query, response);
|
||
}
|
||
|
||
/**
|
||
* 下载导入模板
|
||
*/
|
||
@Operation(summary = "下载导入模板")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:import')")
|
||
@PostMapping("/importTemplate")
|
||
public void importTemplate(HttpServletResponse response) {
|
||
relationService.importTemplate(response);
|
||
}
|
||
|
||
/**
|
||
* 导入信贷客户家庭关系
|
||
*/
|
||
@Operation(summary = "导入信贷客户家庭关系")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:import')")
|
||
@PostMapping("/importData")
|
||
public AjaxResult importData(@RequestParam("file") MultipartFile file) throws IOException {
|
||
List<CcdiCustFmyRelationExcel> excels = EasyExcel.read(file.getInputStream())
|
||
.head(CcdiCustFmyRelationExcel.class)
|
||
.sheet()
|
||
.doReadSync();
|
||
|
||
String taskId = relationService.importRelations(excels);
|
||
return success("导入任务已提交,任务ID: " + taskId);
|
||
}
|
||
|
||
/**
|
||
* 查询导入状态
|
||
*/
|
||
@Operation(summary = "查询导入状态")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:query')")
|
||
@GetMapping("/importStatus/{taskId}")
|
||
public AjaxResult getImportStatus(@PathVariable String taskId) {
|
||
// 从Redis获取任务状态
|
||
Object status = redisTemplate.opsForValue().get("import:custFmyRelation:" + taskId);
|
||
return success(status);
|
||
}
|
||
|
||
/**
|
||
* 查询导入失败记录
|
||
*/
|
||
@Operation(summary = "查询导入失败记录")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:custFmyRelation:query')")
|
||
@GetMapping("/importFailures/{taskId}")
|
||
public TableDataInfo getImportFailures(@PathVariable String taskId) {
|
||
startPage();
|
||
List<CustFmyRelationImportFailureVO> failures = relationService.getImportFailures(taskId);
|
||
return getDataTable(failures, (long) failures.size());
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn compile -pl ruoyi-ccdi
|
||
```
|
||
|
||
**预期结果:** BUILD SUCCESS
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/controller/CcdiCustFmyRelationController.java
|
||
git commit -m "feat: 添加信贷客户家庭关系Controller"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 12: 创建菜单权限SQL
|
||
|
||
**Files:**
|
||
- Create: `sql/ccdi_cust_fmy_relation_menu.sql`
|
||
|
||
**Step 1: 创建菜单SQL**
|
||
|
||
创建 `sql/ccdi_cust_fmy_relation_menu.sql`:
|
||
|
||
```sql
|
||
-- 信贷客户家庭关系菜单
|
||
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
|
||
('信贷客户家庭关系', (SELECT menu_id FROM sys_menu WHERE menu_name = '信息维护' LIMIT 1), 5, 'custFmyRelation', 'ccdiCustFmyRelation/index', 1, 0, 'C', '0', '0', 'ccdi:custFmyRelation:list', 'peoples', 'admin', NOW(), '', NULL, '信贷客户家庭关系菜单');
|
||
|
||
-- 获取刚插入的菜单ID
|
||
SET @parent_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, update_by, update_time, remark) VALUES
|
||
('信贷客户家庭关系查询', @parent_id, 1, '#', '', 1, 0, 'F', '0', '0', 'ccdi:custFmyRelation:query', '#', 'admin', NOW(), '', NULL, ''),
|
||
('信贷客户家庭关系新增', @parent_id, 2, '#', '', 1, 0, 'F', '0', '0', 'ccdi:custFmyRelation:add', '#', 'admin', NOW(), '', NULL, ''),
|
||
('信贷客户家庭关系修改', @parent_id, 3, '#', '', 1, 0, 'F', '0', '0', 'ccdi:custFmyRelation:edit', '#', 'admin', NOW(), '', NULL, ''),
|
||
('信贷客户家庭关系删除', @parent_id, 4, '#', '', 1, 0, 'F', '0', '0', 'ccdi:custFmyRelation:remove', '#', 'admin', NOW(), '', NULL, ''),
|
||
('信贷客户家庭关系导出', @parent_id, 5, '#', '', 1, 0, 'F', '0', '0', 'ccdi:custFmyRelation:export', '#', 'admin', NOW(), '', NULL, ''),
|
||
('信贷客户家庭关系导入', @parent_id, 6, '#', '', 1, 0, 'F', '0', '0', 'ccdi:custFmyRelation:import', '#', 'admin', NOW(), '', NULL, '');
|
||
```
|
||
|
||
**Step 2: Commit**
|
||
|
||
```bash
|
||
git add sql/ccdi_cust_fmy_relation_menu.sql
|
||
git commit -m "feat: 添加信贷客户家庭关系菜单权限"
|
||
```
|
||
|
||
---
|
||
|
||
## 测试验证
|
||
|
||
### Task 14: 后端接口测试
|
||
|
||
**前置条件:**
|
||
- 后端服务已启动 (`mvn spring-boot:run -pl ruoyi-admin`)
|
||
- 数据库连接正常
|
||
- Redis服务正常运行
|
||
|
||
**Step 1: 测试登录获取token**
|
||
|
||
```bash
|
||
curl -X POST "http://localhost:8080/login" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"admin","password":"admin123"}'
|
||
```
|
||
|
||
**预期结果:** 返回token
|
||
|
||
**Step 2: 测试查询列表接口**
|
||
|
||
```bash
|
||
curl -X GET "http://localhost:8080/ccdi/custFmyRelation/list?pageNum=1&pageSize=10" \
|
||
-H "Authorization: Bearer <token>"
|
||
```
|
||
|
||
**预期结果:** 返回空列表(无数据)
|
||
|
||
**Step 3: 测试新增接口**
|
||
|
||
```bash
|
||
curl -X POST "http://localhost:8080/ccdi/custFmyRelation" \
|
||
-H "Authorization: Bearer <token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"personId": "110101199001011234",
|
||
"relationType": "配偶",
|
||
"relationName": "张三",
|
||
"gender": "M",
|
||
"relationCertType": "身份证",
|
||
"relationCertNo": "110101199001015678"
|
||
}'
|
||
```
|
||
|
||
**预期结果:** 返回成功
|
||
|
||
**Step 4: 测试查询详情接口**
|
||
|
||
```bash
|
||
curl -X GET "http://localhost:8080/ccdi/custFmyRelation/1" \
|
||
-H "Authorization: Bearer <token>"
|
||
```
|
||
|
||
**预期结果:** 返回刚插入的记录
|
||
|
||
**Step 5: 测试修改接口**
|
||
|
||
```bash
|
||
curl -X PUT "http://localhost:8080/ccdi/custFmyRelation" \
|
||
-H "Authorization: Bearer <token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"id": 1,
|
||
"personId": "110101199001011234",
|
||
"relationType": "配偶",
|
||
"relationName": "张三丰",
|
||
"gender": "M",
|
||
"relationCertType": "身份证",
|
||
"relationCertNo": "110101199001015678",
|
||
"status": 1
|
||
}'
|
||
```
|
||
|
||
**预期结果:** 返回成功
|
||
|
||
**Step 6: 测试删除接口**
|
||
|
||
```bash
|
||
curl -X DELETE "http://localhost:8080/ccdi/custFmyRelation/1" \
|
||
-H "Authorization: Bearer <token>"
|
||
```
|
||
|
||
**预期结果:** 返回成功
|
||
|
||
**Step 7: 验证Swagger文档**
|
||
|
||
访问 http://localhost:8080/swagger-ui/index.html
|
||
|
||
**预期结果:** 看到"信贷客户家庭关系管理"分组,所有接口正常显示
|
||
|
||
---
|
||
|
||
## 完成检查清单
|
||
|
||
- [ ] 数据库表创建成功
|
||
- [ ] 实体类编译通过
|
||
- [ ] DTO类编译通过
|
||
- [ ] VO类编译通过
|
||
- [ ] Excel类编译通过
|
||
- [ ] Mapper接口编译通过
|
||
- [ ] Mapper XML映射配置正确
|
||
- [ ] Service接口编译通过
|
||
- [ ] Service实现类编译通过
|
||
- [ ] Controller编译通过
|
||
- [ ] Controller所有接口在Swagger正常显示
|
||
- [ ] 菜单权限SQL已执行
|
||
- [ ] 登录接口测试通过
|
||
- [ ] 查询列表接口测试通过
|
||
- [ ] 新增接口测试通过
|
||
- [ ] 修改接口测试通过
|
||
- [ ] 删除接口测试通过
|
||
- [ ] 导出接口测试通过
|
||
- [ ] 导入模板下载测试通过
|
||
|
||
---
|
||
|
||
## 预期结果
|
||
|
||
完成后,后端将提供以下功能:
|
||
|
||
1. **完整的CRUD接口**
|
||
- 分页查询信贷客户家庭关系列表
|
||
- 根据ID查询详情
|
||
- 新增信贷客户家庭关系
|
||
- 修改信贷客户家庭关系
|
||
- 删除信贷客户家庭关系
|
||
|
||
2. **导入导出功能**
|
||
- 导出Excel数据
|
||
- 下载导入模板
|
||
- 异步批量导入
|
||
- 导入状态查询
|
||
- 导入失败记录查看
|
||
|
||
3. **权限控制**
|
||
- 完整的CRUD权限标识
|
||
- 按钮级权限控制
|
||
|
||
4. **数据隔离**
|
||
- 独立表 `ccdi_cust_fmy_relation`
|
||
- `is_cust_family = 1` 过滤条件
|
||
- 与员工亲属关系完全分离
|