客户类型字段更新
This commit is contained in:
360
openspec/changes/split-pricing-creation-interface/design.md
Normal file
360
openspec/changes/split-pricing-creation-interface/design.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# Design: 拆分个人和企业利率定价发起接口
|
||||
|
||||
## 数据库变更
|
||||
|
||||
### 缺失字段分析
|
||||
|
||||
经过对比 `person.csv` 和 `corp.csv` 中定义的字段与现有数据库表 `loan_pricing_workflow`,发现以下字段缺失:
|
||||
|
||||
#### 个人客户缺失字段
|
||||
|
||||
| 字段名 | 中文名 | 类型 | 说明 |
|
||||
|-----------|------|--------------|-------------------------|
|
||||
| id_num | 证件号码 | varchar(100) | 存储个人身份证号或其他证件号码 |
|
||||
| loan_loop | 循环功能 | varchar(10) | 贷款合同是否开通循环功能,true/false |
|
||||
|
||||
#### 企业客户缺失字段
|
||||
|
||||
| 字段名 | 中文名 | 类型 | 说明 |
|
||||
|-----------------------|------------|--------------|----------------------------------|
|
||||
| id_num | 证件号码 | varchar(100) | 存储企业统一社会信用代码或其他证件号码 |
|
||||
| is_trade_construction | 贸易和建筑业企业标识 | varchar(10) | 抵(质)押类:贸易和建筑业企业上调20BP,true/false |
|
||||
| is_green_loan | 绿色贷款 | varchar(10) | 绿色贷款标识,true/false |
|
||||
| is_tech_ent | 科技型企业 | varchar(10) | 科技型企业标识,true/false |
|
||||
| loan_term | 贷款期限 | varchar(50) | 贷款期限,单位:月/年 |
|
||||
|
||||
### 数据库迁移 SQL
|
||||
|
||||
```sql
|
||||
-- 添加缺失的字段到 loan_pricing_workflow 表
|
||||
|
||||
-- 个人和企业共同需要的字段
|
||||
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `id_num` varchar(100) DEFAULT NULL COMMENT '证件号码' AFTER `id_type`;
|
||||
|
||||
-- 个人客户专用字段
|
||||
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `loan_loop` varchar(10) DEFAULT NULL COMMENT '循环功能: true/false' AFTER `biz_proof`;
|
||||
|
||||
-- 企业客户专用字段
|
||||
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `is_trade_construction` varchar(10) DEFAULT NULL COMMENT '贸易和建筑业企业标识: true/false(抵质押类上调20BP)' AFTER `is_agri_guar`;
|
||||
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `is_green_loan` varchar(10) DEFAULT NULL COMMENT '绿色贷款: true/false' AFTER `is_agri_guar`;
|
||||
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `is_tech_ent` varchar(10) DEFAULT NULL COMMENT '科技型企业: true/false' AFTER `is_agri_guar`;
|
||||
ALTER TABLE `loan_pricing_workflow` ADD COLUMN `loan_term` varchar(50) DEFAULT NULL COMMENT '贷款期限' AFTER `apply_amt`;
|
||||
```
|
||||
|
||||
### Entity 类更新
|
||||
|
||||
`LoanPricingWorkflow.java` 需要添加以下属性:
|
||||
|
||||
```java
|
||||
/** 证件号码 */
|
||||
private String idNum;
|
||||
|
||||
/** 循环功能: true/false */
|
||||
private String loanLoop;
|
||||
|
||||
/** 贸易和建筑业企业标识: true/false */
|
||||
private String isTradeConstruction;
|
||||
|
||||
/** 绿色贷款: true/false */
|
||||
private String isGreenLoan;
|
||||
|
||||
/** 科技型企业: true/false */
|
||||
private String isTechEnt;
|
||||
|
||||
/** 贷款期限 */
|
||||
private String loanTerm;
|
||||
```
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 1. DTO 结构设计
|
||||
|
||||
#### PersonalLoanPricingCreateDTO(个人客户发起DTO)
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class PersonalLoanPricingCreateDTO {
|
||||
@NotBlank(message = "客户内码不能为空")
|
||||
private String custIsn;
|
||||
|
||||
@NotBlank(message = "客户类型不能为空")
|
||||
private String custType; // 固定值 "个人"
|
||||
|
||||
@NotBlank(message = "担保方式不能为空")
|
||||
private String guarType; // 可选值:信用,保证,抵押,质押
|
||||
|
||||
private String custName;
|
||||
private String idType;
|
||||
private String idNum;
|
||||
|
||||
@NotBlank(message = "申请金额不能为空")
|
||||
private String applyAmt; // 单位:元
|
||||
|
||||
private String bizProof; // 是否有经营佐证
|
||||
private String loanLoop; // 循环功能
|
||||
|
||||
private String collType; // 抵质押类型
|
||||
private String collThirdParty; // 抵质押物是否三方所有
|
||||
}
|
||||
```
|
||||
|
||||
#### CorporateLoanPricingCreateDTO(企业客户发起DTO)
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class CorporateLoanPricingCreateDTO {
|
||||
@NotBlank(message = "客户内码不能为空")
|
||||
private String custIsn;
|
||||
|
||||
@NotBlank(message = "客户类型不能为空")
|
||||
private String custType; // 固定值 "企业"
|
||||
|
||||
@NotBlank(message = "担保方式不能为空")
|
||||
private String guarType; // 可选值:信用,保证,抵押,质押
|
||||
|
||||
private String custName;
|
||||
private String idType;
|
||||
private String idNum;
|
||||
|
||||
@NotBlank(message = "申请金额不能为空")
|
||||
private String applyAmt; // 单位:元
|
||||
|
||||
private String isAgriGuar; // 省农担担保贷款
|
||||
private String isGreenLoan; // 绿色贷款
|
||||
private String isTechEnt; // 科技型企业
|
||||
private String loanTerm; // 贷款期限
|
||||
|
||||
private String collType; // 抵质押类型
|
||||
private String collThirdParty; // 抵质押物是否三方所有
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 接口设计
|
||||
|
||||
#### Controller 层
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/loanPricing/workflow")
|
||||
public class LoanPricingWorkflowController extends BaseController {
|
||||
|
||||
// 原有接口(保留)
|
||||
@PostMapping("/create")
|
||||
public AjaxResult create(@Validated @RequestBody LoanPricingWorkflow loanPricingWorkflow)
|
||||
|
||||
// 新增:个人客户发起接口
|
||||
@PostMapping("/create/personal")
|
||||
public AjaxResult createPersonal(@Validated @RequestBody PersonalLoanPricingCreateDTO dto)
|
||||
|
||||
// 新增:企业客户发起接口
|
||||
@PostMapping("/create/corporate")
|
||||
public AjaxResult createCorporate(@Validated @RequestBody CorporateLoanPricingCreateDTO dto)
|
||||
}
|
||||
```
|
||||
|
||||
#### Service 层接口
|
||||
|
||||
```java
|
||||
public interface ILoanPricingWorkflowService {
|
||||
// 原有方法(保留兼容)
|
||||
LoanPricingWorkflow createLoanPricing(LoanPricingWorkflow loanPricingWorkflow);
|
||||
|
||||
// 新增:个人客户发起
|
||||
LoanPricingWorkflow createPersonalLoanPricing(PersonalLoanPricingCreateDTO dto);
|
||||
|
||||
// 新增:企业客户发起
|
||||
LoanPricingWorkflow createCorporateLoanPricing(CorporateLoanPricingCreateDTO dto);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 字段映射关系
|
||||
|
||||
| 字段类别 | 个人字段 | 企业字段 |
|
||||
|------|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| 共同字段 | custIsn, custType, guarType, custName, idType, idNum, applyAmt, collType, collThirdParty | |
|
||||
| 个人特有 | bizProof(是否有经营佐证), loanLoop(循环功能) | |
|
||||
| 企业特有 | | isAgriGuar(省农担担保贷款), isGreenLoan(绿色贷款), isTechEnt(科技型企业), isTradeConstruction(贸易和建筑业企业), loanTerm(贷款期限) |
|
||||
|
||||
### 4. 实现细节
|
||||
|
||||
#### DTO 转 Entity 转换器
|
||||
|
||||
创建一个转换工具类,将 DTO 转换为 `LoanPricingWorkflow` 实体:
|
||||
|
||||
```java
|
||||
public class LoanPricingConverter {
|
||||
public static LoanPricingWorkflow toEntity(PersonalLoanPricingCreateDTO dto) {
|
||||
LoanPricingWorkflow entity = new LoanPricingWorkflow();
|
||||
// 映射共同字段
|
||||
entity.setCustIsn(dto.getCustIsn());
|
||||
entity.setCustType("个人");
|
||||
entity.setCustName(dto.getCustName());
|
||||
entity.setIdType(dto.getIdType());
|
||||
entity.setIdNum(dto.getIdNum());
|
||||
entity.setGuarType(dto.getGuarType());
|
||||
entity.setApplyAmt(dto.getApplyAmt());
|
||||
entity.setCollType(dto.getCollType());
|
||||
entity.setCollThirdParty(dto.getCollThirdParty());
|
||||
// 映射个人特有字段
|
||||
entity.setBizProof(dto.getBizProof());
|
||||
entity.setLoanLoop(dto.getLoanLoop());
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static LoanPricingWorkflow toEntity(CorporateLoanPricingCreateDTO dto) {
|
||||
LoanPricingWorkflow entity = new LoanPricingWorkflow();
|
||||
// 映射共同字段
|
||||
entity.setCustIsn(dto.getCustIsn());
|
||||
entity.setCustType("企业");
|
||||
entity.setCustName(dto.getCustName());
|
||||
entity.setIdType(dto.getIdType());
|
||||
entity.setIdNum(dto.getIdNum());
|
||||
entity.setGuarType(dto.getGuarType());
|
||||
entity.setApplyAmt(dto.getApplyAmt());
|
||||
entity.setCollType(dto.getCollType());
|
||||
entity.setCollThirdParty(dto.getCollThirdParty());
|
||||
// 映射企业特有字段
|
||||
entity.setIsAgriGuar(dto.getIsAgriGuar());
|
||||
entity.setIsGreenLoan(dto.getIsGreenLoan());
|
||||
entity.setIsTechEnt(dto.getIsTechEnt());
|
||||
entity.setIsTradeConstruction(dto.getIsTradeConstruction());
|
||||
entity.setLoanTerm(dto.getLoanTerm());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 验证策略
|
||||
|
||||
#### 个人客户验证
|
||||
|
||||
- 必填字段:custIsn, custType, guarType, applyAmt
|
||||
- 枚举验证:guarType 必须是"信用/保证/抵押/质押"之一
|
||||
|
||||
#### 企业客户验证
|
||||
|
||||
- 必填字段:custIsn, custType, guarType, applyAmt
|
||||
- 枚举验证:guarType 必须是"信用/保证/抵押/质押"之一
|
||||
|
||||
### 6. 向后兼容性策略
|
||||
|
||||
1. 保留原有 `POST /loanPricing/workflow/create` 接口
|
||||
2. 新旧接口共存,前端可自由选择使用
|
||||
3. 不添加 @Deprecated 标记,保持接口的可用性
|
||||
|
||||
## 技术决策
|
||||
|
||||
### 为什么使用两个独立的 DTO 而不是一个带条件验证的 DTO?
|
||||
|
||||
1. **类型安全**:编译期就能发现字段错误
|
||||
2. **API 文档更清晰**:Swagger 只显示相关字段
|
||||
3. **验证更简单**:不需要在 DTO 内部根据类型做条件验证
|
||||
|
||||
### 为什么保留原有接口?
|
||||
|
||||
1. **多接口共存**:前端可以选择使用新接口或继续使用原有接口
|
||||
2. **降级方案**:如果新接口有问题,可以快速回退
|
||||
3. **兼容性**:可能有其他系统调用该接口
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 数据库相关
|
||||
|
||||
- **新增**: `sql/add_missing_fields.sql` - 数据库迁移脚本
|
||||
|
||||
### 新增文件
|
||||
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/CorporateLoanPricingCreateDTO.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java`
|
||||
|
||||
### 测试文件
|
||||
|
||||
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTOTest.java`
|
||||
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/domain/dto/CorporateLoanPricingCreateDTOTest.java`
|
||||
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/util/LoanPricingConverterTest.java`
|
||||
- `test_api/test_personal_create.http` - 个人客户发起接口 HTTP 测试脚本
|
||||
- `test_api/test_corporate_create.http` - 企业客户发起接口 HTTP 测试脚本
|
||||
- `test_api/test_backward_compatibility.http` - 向后兼容性测试脚本
|
||||
|
||||
### 修改文件
|
||||
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/LoanPricingWorkflow.java` - 添加缺失字段的属性
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/controller/LoanPricingWorkflowController.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/ILoanPricingWorkflowService.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImpl.java`
|
||||
|
||||
## 测试脚本示例
|
||||
|
||||
### HTTP 测试脚本格式
|
||||
|
||||
测试脚本使用 IntelliJ IDEA 的 .http 文件格式,示例如下:
|
||||
|
||||
```http
|
||||
### 获取测试 Token
|
||||
POST http://localhost:8080/login/test
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "admin123"
|
||||
}
|
||||
|
||||
> {%
|
||||
client.test("Request executed successfully", function() {
|
||||
client.assert(response.status === 200, "Response status is 200");
|
||||
client.assert(response.body.code === 200, "Response code is 200");
|
||||
client.global.set("token", response.body.data.token);
|
||||
});
|
||||
%}
|
||||
|
||||
### 个人客户发起 - 成功场景
|
||||
POST http://localhost:8080/loanPricing/workflow/create/personal
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"custIsn": "TEST001",
|
||||
"custName": "张三",
|
||||
"idType": "身份证",
|
||||
"idNum": "110101199001011234",
|
||||
"guarType": "信用",
|
||||
"applyAmt": "500000",
|
||||
"bizProof": "true",
|
||||
"loanLoop": "false"
|
||||
}
|
||||
|
||||
> {%
|
||||
client.test("Personal loan creation successful", function() {
|
||||
client.assert(response.status === 200, "Response status is 200");
|
||||
client.assert(response.body.code === 200, "Response code is 200");
|
||||
client.assert(response.body.data.custType === "个人", "Customer type is 个人");
|
||||
});
|
||||
%}
|
||||
|
||||
### 企业客户发起 - 成功场景
|
||||
POST http://localhost:8080/loanPricing/workflow/create/corporate
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"custIsn": "CORP001",
|
||||
"custName": "测试科技有限公司",
|
||||
"idType": "统一社会信用代码",
|
||||
"idNum": "91110000100000000X",
|
||||
"guarType": "抵押",
|
||||
"applyAmt": "1000000",
|
||||
"isAgriGuar": "false",
|
||||
"isGreenLoan": "true",
|
||||
"isTechEnt": "true",
|
||||
"loanTerm": "36"
|
||||
}
|
||||
|
||||
> {%
|
||||
client.test("Corporate loan creation successful", function() {
|
||||
client.assert(response.status === 200, "Response status is 200");
|
||||
client.assert(response.body.code === 200, "Response code is 200");
|
||||
client.assert(response.body.data.custType === "企业", "Customer type is 企业");
|
||||
});
|
||||
%}
|
||||
```
|
||||
Reference in New Issue
Block a user