Compare commits
10 Commits
8e6eb5b382
...
892-withou
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a80653917 | |||
| bfe1b346d9 | |||
| ef40675422 | |||
| f1e4b26800 | |||
| 235672304a | |||
| ec4a7c09db | |||
| 5839a76f87 | |||
| fa0b446699 | |||
| f001047d0c | |||
| 09707d312e |
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": []
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(java:*)",
|
||||
"Bash(binrun.bat:*)",
|
||||
"Bash(mvn clean package:*)",
|
||||
"Bash(curl:*)",
|
||||
"Bash(pkill:*)",
|
||||
"Bash(bash:*)",
|
||||
"Bash(pip install:*)",
|
||||
"Bash(findstr:*)",
|
||||
"Bash(chcp:*)",
|
||||
"Bash(cmd.exe:*)",
|
||||
"Bash(powershell -Command:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(cd:*)",
|
||||
"mcp__zai-mcp-server__extract_text_from_screenshot",
|
||||
"Bash(mvn test:*)",
|
||||
"Bash(mvn install:*)",
|
||||
"Bash(mvn clean install:*)",
|
||||
"mcp__web-reader__webReader",
|
||||
"Skill(superpowers:brainstorming)",
|
||||
"Skill(superpowers:writing-plans)",
|
||||
"Skill(superpowers:executing-plans)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"d:\\利率定价\\loan-pricing-892\\loan-pricing-892-v2.0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"paths": {
|
||||
"specs": ".claude/specs",
|
||||
"steering": ".claude/steering",
|
||||
"settings": ".claude/settings"
|
||||
},
|
||||
"views": {
|
||||
"specs": {
|
||||
"visible": true
|
||||
},
|
||||
"steering": {
|
||||
"visible": true
|
||||
},
|
||||
"mcp": {
|
||||
"visible": true
|
||||
},
|
||||
"hooks": {
|
||||
"visible": true
|
||||
},
|
||||
"settings": {
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
}
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -48,4 +48,10 @@ nbdist/
|
||||
|
||||
|
||||
logs/
|
||||
ruoyi-ui/dist.zip
|
||||
ruoyi-ui/dist.zip
|
||||
*/src/test/
|
||||
ruoyi-ui/tests
|
||||
.playwright-cli
|
||||
|
||||
tongweb_63310.properties
|
||||
audit.log
|
||||
BIN
bin/.DS_Store
vendored
BIN
bin/.DS_Store
vendored
Binary file not shown.
@@ -71,7 +71,16 @@ collect_backend_pids() {
|
||||
fi
|
||||
fi
|
||||
|
||||
marker_pids=$(pgrep -f "$BACKEND_MARKER" 2>/dev/null || true)
|
||||
marker_pids=$(ps -ef | awk -v marker="$BACKEND_MARKER" -v jar="$BACKEND_JAR" '
|
||||
index($0, "<defunct>") == 0 && index($0, marker) > 0 {
|
||||
for (i = 1; i < NF; i++) {
|
||||
if ($i == "-jar" && $(i + 1) == jar) {
|
||||
print $2
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
' | xargs 2>/dev/null || true)
|
||||
if [ -n "${marker_pids:-}" ]; then
|
||||
for pid in $marker_pids; do
|
||||
if is_managed_backend_pid "$pid"; then
|
||||
|
||||
@@ -44,8 +44,8 @@ EOF
|
||||
prepare_script_env() {
|
||||
work_dir="$1"
|
||||
|
||||
mkdir -p "$work_dir/env/java/bin" "$work_dir/loan-pricing/backend" "$work_dir/loan-pricing/logs" "$work_dir/loan-pricing/run"
|
||||
create_fake_java "$work_dir/env/java/bin/java"
|
||||
mkdir -p "$work_dir/env/jdk/bin" "$work_dir/loan-pricing/backend" "$work_dir/loan-pricing/logs" "$work_dir/loan-pricing/run"
|
||||
create_fake_java "$work_dir/env/jdk/bin/java"
|
||||
printf 'fake-jar\n' > "$work_dir/loan-pricing/backend/ruoyi-admin.jar"
|
||||
cp "$SCRIPT_UNDER_TEST" "$work_dir/restart_java.sh"
|
||||
perl -0pi -e "s#WEBAPP_ROOT=\"/home/webapp\"#WEBAPP_ROOT=\"$work_dir\"#" "$work_dir/restart_java.sh"
|
||||
@@ -67,8 +67,10 @@ cleanup_work_dir() {
|
||||
}
|
||||
|
||||
test_script_contract() {
|
||||
assert_grep 'JAVA_HOME="\$ENV_ROOT/java"' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep 'JAVA_HOME="\$ENV_ROOT/jdk"' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep '--spring\.profiles\.active=pro' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep 'ps -ef' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'pgrep' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'mvn' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'require_root' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep '\b(ss|lsof|netstat)\b' "$SCRIPT_UNDER_TEST"
|
||||
|
||||
@@ -83,7 +83,16 @@ collect_pids() {
|
||||
fi
|
||||
fi
|
||||
|
||||
marker_pids=$(pgrep -f "$APP_MARKER" 2>/dev/null || true)
|
||||
marker_pids=$(ps -ef | awk -v marker="$APP_MARKER" -v jar="$JAR_NAME" '
|
||||
index($0, "<defunct>") == 0 && index($0, marker) > 0 {
|
||||
for (i = 1; i < NF; i++) {
|
||||
if ($i == "-jar" && $(i + 1) == jar) {
|
||||
print $2
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
' | xargs 2>/dev/null || true)
|
||||
if [ -n "${marker_pids:-}" ]; then
|
||||
for pid in $marker_pids; do
|
||||
if is_managed_backend_pid "$pid"; then
|
||||
@@ -225,7 +234,6 @@ restart_action() {
|
||||
main() {
|
||||
ensure_command mvn
|
||||
ensure_command lsof
|
||||
ensure_command pgrep
|
||||
ensure_command ps
|
||||
ensure_command tail
|
||||
|
||||
|
||||
42
bin/restart_java_backend_test.sh
Normal file
42
bin/restart_java_backend_test.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
SCRIPT_UNDER_TEST="$ROOT_DIR/bin/restart_java_backend.sh"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_grep() {
|
||||
pattern="$1"
|
||||
target="$2"
|
||||
if ! grep -Eq -- "$pattern" "$target"; then
|
||||
fail "expected pattern [$pattern] in $target"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_grep() {
|
||||
pattern="$1"
|
||||
target="$2"
|
||||
if grep -Eq -- "$pattern" "$target"; then
|
||||
fail "did not expect pattern [$pattern] in $target"
|
||||
fi
|
||||
}
|
||||
|
||||
test_script_contract() {
|
||||
assert_grep 'ps -ef' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'pgrep' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep 'APP_MARKER=' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep 'status_backend\(\)' "$SCRIPT_UNDER_TEST"
|
||||
}
|
||||
|
||||
main() {
|
||||
[ -f "$SCRIPT_UNDER_TEST" ] || fail "script under test not found: $SCRIPT_UNDER_TEST"
|
||||
test_script_contract
|
||||
printf 'PASS: restart_java_backend tests\n'
|
||||
}
|
||||
|
||||
main "$@"
|
||||
238
doc/2026-04-09-shangyu-retail-input-params-backend-plan.md
Normal file
238
doc/2026-04-09-shangyu-retail-input-params-backend-plan.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# 上虞个人利率测算输入参数后端 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 补齐个人创建 DTO、流程转换和模型调用参数,使个人模型请求完整覆盖 Excel 要求的输入字段。
|
||||
|
||||
**Architecture:** 维持现有“页面提交 -> DTO -> Workflow -> ModelInvokeDTO -> form-urlencoded 调用”的链路,只在个人创建与模型调用相关文件中补齐字段和转换规则。通过后端单元测试与真实接口联调覆盖必填、正常和分支场景。
|
||||
|
||||
**Tech Stack:** Spring Boot、MyBatis-Plus、Lombok、JUnit、Maven
|
||||
|
||||
---
|
||||
|
||||
## 后端模型输入参数确认
|
||||
|
||||
个人链路最终需要发给模型的 16 个参数如下:
|
||||
|
||||
- `serialNum`:服务层自动生成
|
||||
- `orgCode`:服务层默认值,当前代码为 `892000`
|
||||
- `runType`:服务层默认值 `1`
|
||||
- `custIsn`:页面输入透传
|
||||
- `custType`:个人链路固定 `个人`
|
||||
- `custName`:页面输入,调用模型前解密后透传
|
||||
- `idType`:页面输入透传
|
||||
- `idNum`:页面输入,调用模型前解密后透传
|
||||
- `guarType`:页面输入透传
|
||||
- `applyAmt`:页面输入透传
|
||||
- `loanPurpose`:页面输入透传
|
||||
- `loanTerm`:页面输入透传
|
||||
- `bizProof`:页面开关,调用模型前转 `0/1`
|
||||
- `loanLoop`:页面开关,调用模型前转 `0/1`
|
||||
- `collThirdParty`:页面开关,调用模型前转 `0/1`
|
||||
- `collType`:页面下拉透传
|
||||
|
||||
调用方式确认:
|
||||
|
||||
- 参数载体:`ModelInvokeDTO`
|
||||
- 组装方式:`BeanUtils.copyProperties(loanPricingWorkflow, modelInvokeDTO)`
|
||||
- 请求格式:`application/x-www-form-urlencoded`
|
||||
- 发送入口:`ModelService#invokeModel`
|
||||
|
||||
## 文件结构
|
||||
|
||||
- Modify: [ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java)
|
||||
- 增加 `loanPurpose`、`loanTerm`
|
||||
- Modify: [ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java)
|
||||
- 将新增字段映射到 `LoanPricingWorkflow`
|
||||
- Modify: [ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java)
|
||||
- 增加 `loanTerm`、`loanLoop`
|
||||
- Modify: [ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingModelService.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingModelService.java)
|
||||
- 在个人模型调用前规范化 `0/1`
|
||||
- Create: [ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java)
|
||||
- 覆盖字段存在与值转换
|
||||
|
||||
### Task 1: 为新增字段补失败测试
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java`
|
||||
|
||||
- [ ] **Step 1: 编写字段与转换测试**
|
||||
|
||||
```java
|
||||
@Test
|
||||
void shouldContainLoanPurposeLoanTermAndLoanLoop() {
|
||||
assertThat(ModelInvokeDTO.class.getDeclaredField("loanTerm")).isNotNull();
|
||||
assertThat(ModelInvokeDTO.class.getDeclaredField("loanLoop")).isNotNull();
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@Test
|
||||
void shouldConvertPersonalBooleanFlagsToZeroOne() {
|
||||
// 构造个人流程对象,断言模型请求中的 bizProof/loanLoop/collThirdParty 为 1/0
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试并确认先失败**
|
||||
|
||||
Run: `mvn -pl ruoyi-loan-pricing -Dtest=LoanPricingModelServicePersonalParamsTest test`
|
||||
|
||||
Expected: FAIL,提示字段不存在或转换逻辑未实现
|
||||
|
||||
- [ ] **Step 3: 保持测试只覆盖本次改动相关链路**
|
||||
|
||||
```java
|
||||
// 仅断言 PersonalLoanPricingCreateDTO / LoanPricingConverter / ModelInvokeDTO / LoanPricingModelService
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 增加最终模型请求参数断言**
|
||||
|
||||
```java
|
||||
// 断言 requestBody 包含 serialNum、orgCode、runType、custIsn、custType、custName、
|
||||
// idType、idNum、guarType、applyAmt、loanPurpose、loanTerm、bizProof、
|
||||
// loanLoop、collThirdParty、collType
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 再次运行测试确认失败原因稳定**
|
||||
|
||||
Run: `mvn -pl ruoyi-loan-pricing -Dtest=LoanPricingModelServicePersonalParamsTest test`
|
||||
|
||||
Expected: FAIL,失败点与新增字段缺失一致
|
||||
|
||||
- [ ] **Step 6: 提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java
|
||||
git commit -m "新增个人测算输入参数后端测试"
|
||||
```
|
||||
|
||||
### Task 2: 补齐个人创建 DTO 与流程映射
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java`
|
||||
- Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java`
|
||||
|
||||
- [ ] **Step 1: 在个人 DTO 中增加 `loanPurpose`**
|
||||
|
||||
```java
|
||||
@NotBlank(message = "贷款用途不能为空")
|
||||
@Pattern(regexp = "^(consumer|business)$", message = "贷款用途必须是 consumer 或 business")
|
||||
private String loanPurpose;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在个人 DTO 中增加 `loanTerm`**
|
||||
|
||||
```java
|
||||
@NotBlank(message = "借款期限不能为空")
|
||||
private String loanTerm;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 在转换器中映射新增字段**
|
||||
|
||||
```java
|
||||
entity.setLoanPurpose(dto.getLoanPurpose());
|
||||
entity.setLoanTerm(dto.getLoanTerm());
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 运行相关测试确认 DTO 与映射通过**
|
||||
|
||||
Run: `mvn -pl ruoyi-loan-pricing -Dtest=LoanPricingModelServicePersonalParamsTest test`
|
||||
|
||||
Expected: 仍可能 FAIL,但失败点已推进到模型调用层
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java
|
||||
git commit -m "补齐个人测算创建参数字段"
|
||||
```
|
||||
|
||||
### Task 3: 补齐模型调用 DTO 与个人参数规范化
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java`
|
||||
- Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingModelService.java`
|
||||
|
||||
- [ ] **Step 1: 在模型调用 DTO 中增加缺失字段**
|
||||
|
||||
```java
|
||||
private String loanTerm;
|
||||
private String loanLoop;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在个人模型调用前做 `0/1` 转换**
|
||||
|
||||
```java
|
||||
if ("个人".equals(loanPricingWorkflow.getCustType())) {
|
||||
modelInvokeDTO.setBizProof(toZeroOne(modelInvokeDTO.getBizProof()));
|
||||
modelInvokeDTO.setLoanLoop(toZeroOne(modelInvokeDTO.getLoanLoop()));
|
||||
modelInvokeDTO.setCollThirdParty(toZeroOne(modelInvokeDTO.getCollThirdParty()));
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 抽出最小辅助方法,避免散落重复逻辑**
|
||||
|
||||
```java
|
||||
private String toZeroOne(String value) {
|
||||
if ("true".equals(value) || "1".equals(value)) return "1";
|
||||
if ("false".equals(value) || "0".equals(value)) return "0";
|
||||
return value;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 运行测试确认通过**
|
||||
|
||||
Run: `mvn -pl ruoyi-loan-pricing -Dtest=LoanPricingModelServicePersonalParamsTest test`
|
||||
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingModelService.java
|
||||
git commit -m "规范个人测算模型调用参数"
|
||||
```
|
||||
|
||||
### Task 4: 后端联调与接口验证
|
||||
|
||||
**Files:**
|
||||
- Verify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/controller/LoanPricingWorkflowController.java`
|
||||
- Verify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImpl.java`
|
||||
|
||||
- [ ] **Step 1: 重新编译并重启后端进程**
|
||||
|
||||
Run: `mvn -pl ruoyi-admin -am package -DskipTests`
|
||||
|
||||
Expected: 打包成功,随后重启运行中的后端服务使最新代码生效
|
||||
|
||||
- [ ] **Step 2: 验证正常场景**
|
||||
|
||||
Run: 调用 `POST /loanPricing/workflow/create/personal`
|
||||
|
||||
Expected:
|
||||
- 返回创建成功
|
||||
- 请求体包含 `loanPurpose`、`loanTerm`
|
||||
- 模型请求中完整带出 16 个参数
|
||||
- 其中 `bizProof`、`loanLoop`、`collThirdParty` 为 `0/1`
|
||||
|
||||
- [ ] **Step 3: 验证必填缺失场景**
|
||||
|
||||
Run: 缺少 `loanPurpose` 或 `loanTerm` 分别调用接口
|
||||
|
||||
Expected: 返回参数校验失败
|
||||
|
||||
- [ ] **Step 4: 验证分支场景**
|
||||
|
||||
Run: 以不同开关组合调用接口
|
||||
|
||||
Expected:
|
||||
- `bizProof=true` -> 模型入参 `1`
|
||||
- `bizProof=false` -> 模型入参 `0`
|
||||
- `loanLoop=true/false` 与 `collThirdParty=true/false` 同理
|
||||
|
||||
- [ ] **Step 5: 验证结束后停止后端进程并提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingModelService.java ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java
|
||||
git commit -m "完成个人测算输入参数后端联调"
|
||||
```
|
||||
322
doc/2026-04-09-shangyu-retail-input-params-design.md
Normal file
322
doc/2026-04-09-shangyu-retail-input-params-design.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 上虞个人利率测算输入参数对齐设计文档
|
||||
|
||||
## 1. 背景
|
||||
|
||||
根据 [doc/上虞利率测算接口文档.xlsx](/Users/wkc/Desktop/loan-pricing/loan-pricing/doc/上虞利率测算接口文档.xlsx) 的 `入参` sheet,个人利率测算模型当前要求的输入参数为:
|
||||
|
||||
- `serialNum`
|
||||
- `orgCode`
|
||||
- `runType`
|
||||
- `custIsn`
|
||||
- `custType`
|
||||
- `custName`
|
||||
- `idType`
|
||||
- `idNum`
|
||||
- `guarType`
|
||||
- `applyAmt`
|
||||
- `loanPurpose`
|
||||
- `loanTerm`
|
||||
- `bizProof`
|
||||
- `loanLoop`
|
||||
- `collThirdParty`
|
||||
- `collType`
|
||||
|
||||
现有个人新增弹窗与模型调用链路未完全覆盖该输入集合:
|
||||
|
||||
- 页面缺少 `loanPurpose`
|
||||
- 页面缺少 `loanTerm`
|
||||
- `collType` 选项与 Excel 不一致
|
||||
- 页面开关字段当前提交的是 `true/false`
|
||||
- 模型调用 DTO 当前缺少 `loanTerm`、`loanLoop`
|
||||
|
||||
本次目标是按 Excel 的个人输入参数定义,对齐个人新增弹窗输入项和模型调用入参,不扩展到企业链路,不引入兜底或兼容分支。
|
||||
|
||||
## 2. 已确认范围
|
||||
|
||||
- 仅处理个人新增弹窗
|
||||
- 仅处理个人创建流程到模型调用的入参链路
|
||||
- 保持现有页面交互结构,不新增系统字段输入区
|
||||
- `loanTerm` 使用固定年限下拉,选项按 Excel 定义
|
||||
- 系统字段继续自动生成或默认赋值
|
||||
- 不修改企业新增弹窗
|
||||
- 不修改模型计算规则
|
||||
|
||||
## 3. 输入参数获取方式整理
|
||||
|
||||
### 3.1 系统自动带值
|
||||
|
||||
以下字段不放到新增弹窗中,由现有服务链路自动提供:
|
||||
|
||||
- `serialNum`
|
||||
- 由 `LoanPricingWorkflowServiceImpl#createLoanPricing` 按时间戳生成
|
||||
- `orgCode`
|
||||
- 由 `LoanPricingWorkflowServiceImpl#createLoanPricing` 在空值时补默认值
|
||||
- `runType`
|
||||
- 由 `LoanPricingWorkflowServiceImpl#createLoanPricing` 在空值时补默认值 `1`
|
||||
- `custType`
|
||||
- 由 `LoanPricingConverter#toEntity(PersonalLoanPricingCreateDTO)` 固定写为 `个人`
|
||||
|
||||
### 3.2 用户直接输入
|
||||
|
||||
- 文本输入:
|
||||
- `custIsn`
|
||||
- `custName`
|
||||
- `idNum`
|
||||
- `applyAmt`
|
||||
- 下拉选择:
|
||||
- `idType`
|
||||
- `guarType`
|
||||
- `loanPurpose`
|
||||
- `loanTerm`
|
||||
- `collType`
|
||||
- 开关选择:
|
||||
- `bizProof`
|
||||
- `loanLoop`
|
||||
- `collThirdParty`
|
||||
|
||||
## 4. 现状分析
|
||||
|
||||
### 4.1 前端现状
|
||||
|
||||
[PersonalCreateDialog.vue](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue) 当前已经提供:
|
||||
|
||||
- `custIsn`
|
||||
- `custName`
|
||||
- `idType`
|
||||
- `idNum`
|
||||
- `guarType`
|
||||
- `applyAmt`
|
||||
- `bizProof`
|
||||
- `loanLoop`
|
||||
- `collType`
|
||||
- `collThirdParty`
|
||||
|
||||
当前缺失或不一致点:
|
||||
|
||||
- 缺少 `loanPurpose`
|
||||
- 缺少 `loanTerm`
|
||||
- `collType` 选项为 `一线/一类/二类`,与 Excel 的 `一类/二类/三类` 不一致
|
||||
- 开关字段提交值为 `true/false`
|
||||
|
||||
### 4.2 后端现状
|
||||
|
||||
[PersonalLoanPricingCreateDTO.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java) 当前未定义:
|
||||
|
||||
- `loanPurpose`
|
||||
- `loanTerm`
|
||||
|
||||
[LoanPricingConverter.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java) 当前未把以上字段映射到 `LoanPricingWorkflow`。
|
||||
|
||||
[ModelInvokeDTO.java](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java) 当前未定义:
|
||||
|
||||
- `loanTerm`
|
||||
- `loanLoop`
|
||||
|
||||
这意味着即使页面补齐字段,当前模型调用也无法完整带出 Excel 要求的个人入参。
|
||||
|
||||
## 5. 方案对比
|
||||
|
||||
### 方案一:页面补齐用户输入,系统字段继续自动带值
|
||||
|
||||
做法:
|
||||
|
||||
- 在个人新增弹窗新增 `loanPurpose` 下拉
|
||||
- 在个人新增弹窗新增 `loanTerm` 固定年限下拉
|
||||
- 修正 `collType` 选项
|
||||
- 后端 DTO、转换器、模型调用 DTO 同步补字段
|
||||
- 模型调用前统一将开关字段转换为 Excel 要求的 `0/1`
|
||||
|
||||
优点:
|
||||
|
||||
- 与现有个人/企业创建方式一致
|
||||
- 改动最小
|
||||
- 页面输入、流程入库、模型调用职责边界清晰
|
||||
|
||||
缺点:
|
||||
|
||||
- 需要同时修改前后端
|
||||
|
||||
### 方案二:前端少改,后端在模型调用前兜底补参数
|
||||
|
||||
做法:
|
||||
|
||||
- 页面只补部分字段
|
||||
- 其余由后端按默认逻辑拼接模型参数
|
||||
|
||||
优点:
|
||||
|
||||
- 页面改动更少
|
||||
|
||||
缺点:
|
||||
|
||||
- 页面输入和真实模型入参不一致
|
||||
- 后续排查问题时难以定位参数来源
|
||||
- 不符合本次“按现有输入方式整理字段获取方式”的要求
|
||||
|
||||
### 方案三:Excel 全量字段全部暴露到弹窗
|
||||
|
||||
做法:
|
||||
|
||||
- 把 `serialNum`、`orgCode`、`runType`、`custType` 也作为页面字段给用户填写
|
||||
|
||||
优点:
|
||||
|
||||
- 页面可见字段与 Excel 完全一一对应
|
||||
|
||||
缺点:
|
||||
|
||||
- 与当前产品交互方式不一致
|
||||
- 增加误填风险
|
||||
- 不符合最短路径要求
|
||||
|
||||
## 6. 设计结论
|
||||
|
||||
采用方案一。
|
||||
|
||||
### 6.1 页面设计
|
||||
|
||||
个人新增弹窗保留现有分组结构,在“贷款信息”区域补齐:
|
||||
|
||||
- `loanPurpose`
|
||||
- 下拉选项:`consumer`、`business`
|
||||
- `loanTerm`
|
||||
- 固定年限下拉
|
||||
- 选项固定为 `1/2/3/4/5/6`
|
||||
|
||||
同时修正:
|
||||
|
||||
- `collType` 选项改为 `一类/二类/三类`
|
||||
|
||||
### 6.2 参数来源设计
|
||||
|
||||
- 系统带值:
|
||||
- `serialNum`
|
||||
- `orgCode`
|
||||
- `runType`
|
||||
- `custType`
|
||||
- 页面透传:
|
||||
- `custIsn`
|
||||
- `custName`
|
||||
- `idType`
|
||||
- `idNum`
|
||||
- `guarType`
|
||||
- `applyAmt`
|
||||
- `loanPurpose`
|
||||
- `loanTerm`
|
||||
- `collType`
|
||||
- 页面开关,经模型调用层转换:
|
||||
- `bizProof`
|
||||
- `loanLoop`
|
||||
- `collThirdParty`
|
||||
|
||||
### 6.3 链路设计
|
||||
|
||||
1. 前端提交个人创建请求时,补齐 `loanPurpose`、`loanTerm`
|
||||
2. 后端个人创建 DTO 接收新增字段
|
||||
3. 转换器将新增字段写入 `LoanPricingWorkflow`
|
||||
4. 模型调用 DTO 增加 `loanPurpose`、`loanTerm`、`loanLoop`
|
||||
5. `LoanPricingModelService` 在调用模型前,将个人链路中的开关字段转换为 `0/1`
|
||||
6. `ModelService` 继续以 `application/x-www-form-urlencoded` 方式调用模型接口
|
||||
|
||||
### 6.3.1 后端模型调用输入参数确认
|
||||
|
||||
后端最终发给模型的个人入参,按 Excel 要求确认为以下 16 个字段:
|
||||
|
||||
- `serialNum`
|
||||
- 来源:`LoanPricingWorkflowServiceImpl#createLoanPricing` 自动生成
|
||||
- `orgCode`
|
||||
- 来源:`LoanPricingWorkflowServiceImpl#createLoanPricing` 默认赋值
|
||||
- 当前代码值:`892000`
|
||||
- `runType`
|
||||
- 来源:`LoanPricingWorkflowServiceImpl#createLoanPricing`
|
||||
- 当前值:`1`
|
||||
- `custIsn`
|
||||
- 来源:页面输入,经个人创建 DTO 和转换器透传
|
||||
- `custType`
|
||||
- 来源:`LoanPricingConverter#toEntity(PersonalLoanPricingCreateDTO)`
|
||||
- 当前值:固定 `个人`
|
||||
- `custName`
|
||||
- 来源:页面输入
|
||||
- 说明:入库时加密,调用模型前解密
|
||||
- `idType`
|
||||
- 来源:页面输入
|
||||
- `idNum`
|
||||
- 来源:页面输入
|
||||
- 说明:入库时加密,调用模型前解密
|
||||
- `guarType`
|
||||
- 来源:页面输入
|
||||
- `applyAmt`
|
||||
- 来源:页面输入
|
||||
- `loanPurpose`
|
||||
- 来源:页面输入
|
||||
- 当前状态:需补齐到个人 DTO、流程实体映射和模型 DTO
|
||||
- `loanTerm`
|
||||
- 来源:页面输入
|
||||
- 当前状态:需补齐到个人 DTO、流程实体映射和模型 DTO
|
||||
- `bizProof`
|
||||
- 来源:页面开关
|
||||
- 模型值:调用模型前统一转换为 `0/1`
|
||||
- `loanLoop`
|
||||
- 来源:页面开关
|
||||
- 当前状态:需补齐到模型 DTO
|
||||
- 模型值:调用模型前统一转换为 `0/1`
|
||||
- `collThirdParty`
|
||||
- 来源:页面开关
|
||||
- 模型值:调用模型前统一转换为 `0/1`
|
||||
- `collType`
|
||||
- 来源:页面下拉
|
||||
- 模型值:按 `一类/二类/三类` 直接透传
|
||||
|
||||
后端调用方式确认如下:
|
||||
|
||||
- 参数载体:`ModelInvokeDTO`
|
||||
- 参数来源:`BeanUtils.copyProperties(loanPricingWorkflow, modelInvokeDTO)`
|
||||
- 请求构造:`ModelService#entityToMap`
|
||||
- 请求格式:`application/x-www-form-urlencoded`
|
||||
- 发送入口:`ModelService#invokeModel`
|
||||
|
||||
### 6.4 展示闭环
|
||||
|
||||
为保证输入项可在详情页回看,个人详情页同步补齐:
|
||||
|
||||
- `loanPurpose`
|
||||
|
||||
`loanTerm` 详情展示已存在,不需要新增区域。
|
||||
|
||||
## 7. 校验与错误处理
|
||||
|
||||
- 前端新增 `loanPurpose` 必选校验
|
||||
- 前端新增 `loanTerm` 必选校验
|
||||
- `loanTerm` 只能通过固定下拉选择,不提供自由输入
|
||||
- 后端 DTO 对 `loanPurpose`、`loanTerm` 增加必填约束
|
||||
- 保持现有创建失败与模型调用失败的错误提示方式
|
||||
- 不新增兼容逻辑、兜底逻辑或补丁式分支
|
||||
|
||||
## 8. 验证设计
|
||||
|
||||
- 前端源码断言个人新增弹窗已出现 `loanPurpose`、`loanTerm`
|
||||
- 前端源码断言 `loanTerm` 为固定下拉、`collType` 选项为 `一类/二类/三类`
|
||||
- 后端测试或源码断言 `PersonalLoanPricingCreateDTO`、`LoanPricingConverter`、`ModelInvokeDTO` 已补齐字段
|
||||
- 后端测试或日志断言调用模型前最终请求参数完整包含以上 16 个字段
|
||||
- 重启后端后,覆盖以下接口验证:
|
||||
- 正常场景:完整参数创建成功
|
||||
- 必填缺失场景:缺少 `loanPurpose` 或 `loanTerm` 被拦截
|
||||
- 分支场景:`bizProof`、`loanLoop`、`collThirdParty` 开关不同组合能正确转换为 `0/1`
|
||||
- 启动前端页面并通过浏览器检查:
|
||||
- 新增弹窗展示正确
|
||||
- 提交流程后详情页能回显 `loanPurpose`、`loanTerm`
|
||||
- 验证完成后停止本次启动的前后端进程
|
||||
|
||||
## 9. 已确认项
|
||||
|
||||
- `orgCode` 统一为 `892000`
|
||||
- `ModelInvokeDTO` 注释已统一为 `892000`
|
||||
- 数据库 `loan_pricing_workflow.org_code` 默认值已统一为 `892000`
|
||||
- 存量 `loan_pricing_workflow.org_code` 数据已通过迁移脚本统一为 `892000`
|
||||
|
||||
## 10. 非目标
|
||||
|
||||
- 不调整企业新增弹窗
|
||||
- 不修改企业模型调用参数
|
||||
- 不修改流程列表逻辑
|
||||
- 不改模型返回字段映射逻辑
|
||||
212
doc/2026-04-09-shangyu-retail-input-params-frontend-plan.md
Normal file
212
doc/2026-04-09-shangyu-retail-input-params-frontend-plan.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# 上虞个人利率测算输入参数前端 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 按 Excel 补齐个人新增弹窗输入项,确保页面提交字段与个人模型入参要求一致。
|
||||
|
||||
**Architecture:** 仅修改个人创建弹窗和个人详情页,沿用当前 Element UI 表单结构,不新增页面层级。通过前端源码断言覆盖新增字段、选项和值转换,确保页面输入与现有交互方式保持一致。
|
||||
|
||||
**Tech Stack:** Vue 2、Element UI、RuoYi 前端请求封装、Node 源码断言脚本
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
- 修改: [ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue)
|
||||
- 补齐 `loanPurpose`、`loanTerm`
|
||||
- 修正 `collType` 选项
|
||||
- 调整个人开关字段提交值
|
||||
- 修改: [ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue)
|
||||
- 补齐 `loanPurpose` 展示
|
||||
- 如有需要,扩展布尔格式化兼容 `0/1`
|
||||
- 新增: [ruoyi-ui/tests/personal-create-input-params.test.js](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-ui/tests/personal-create-input-params.test.js)
|
||||
- 断言新增弹窗字段、选项和值转换逻辑
|
||||
- 修改: [ruoyi-ui/package.json](/Users/wkc/Desktop/loan-pricing/loan-pricing/ruoyi-ui/package.json)
|
||||
- 新增针对本次改动的测试命令
|
||||
|
||||
### Task 1: 为个人新增弹窗补失败断言
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/tests/personal-create-input-params.test.js`
|
||||
- Modify: `ruoyi-ui/package.json`
|
||||
|
||||
- [ ] **Step 1: 编写失败断言脚本**
|
||||
|
||||
```js
|
||||
const requiredFields = ['form.loanPurpose', 'form.loanTerm']
|
||||
const requiredOptions = ['value="consumer"', 'value="business"', 'label="一类"', 'label="二类"', 'label="三类"']
|
||||
const requiredConversions = [
|
||||
"bizProof: this.form.bizProof ? '1' : '0'",
|
||||
"loanLoop: this.form.loanLoop ? '1' : '0'",
|
||||
"collThirdParty: this.form.collThirdParty ? '1' : '0'"
|
||||
]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行断言并确认先失败**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:personal-create-input-params`
|
||||
|
||||
Expected: FAIL,提示缺少 `loanPurpose` 或 `loanTerm` 或值转换未按 `1/0`
|
||||
|
||||
- [ ] **Step 3: 在 `package.json` 注册测试命令**
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test:personal-create-input-params": "node tests/personal-create-input-params.test.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 再次运行断言并确认仍处于失败态**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:personal-create-input-params`
|
||||
|
||||
Expected: FAIL,失败原因与新增字段缺失一致
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/tests/personal-create-input-params.test.js ruoyi-ui/package.json
|
||||
git commit -m "新增个人测算输入参数前端断言"
|
||||
```
|
||||
|
||||
### Task 2: 补齐个人新增弹窗字段与选项
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
|
||||
- [ ] **Step 1: 增加 `loanPurpose` 表单项**
|
||||
|
||||
```vue
|
||||
<el-form-item label="贷款用途" prop="loanPurpose">
|
||||
<el-select v-model="form.loanPurpose" placeholder="请选择贷款用途" style="width: 100%">
|
||||
<el-option label="消费" value="consumer" />
|
||||
<el-option label="经营" value="business" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 增加 `loanTerm` 固定年限下拉**
|
||||
|
||||
```vue
|
||||
<el-form-item label="借款期限(年)" prop="loanTerm">
|
||||
<el-select v-model="form.loanTerm" placeholder="请选择借款期限" style="width: 100%">
|
||||
<el-option v-for="item in ['1', '2', '3', '4', '5', '6']" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 修正 `collType` 选项为 Excel 定义**
|
||||
|
||||
```vue
|
||||
<el-option label="一类" value="一类" />
|
||||
<el-option label="二类" value="二类" />
|
||||
<el-option label="三类" value="三类" />
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 为新增字段补表单状态与校验**
|
||||
|
||||
```js
|
||||
form: {
|
||||
loanPurpose: undefined,
|
||||
loanTerm: undefined
|
||||
},
|
||||
rules: {
|
||||
loanPurpose: [{ required: true, message: '请选择贷款用途', trigger: 'change' }],
|
||||
loanTerm: [{ required: true, message: '请选择借款期限', trigger: 'change' }]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue
|
||||
git commit -m "补齐个人新增弹窗输入字段"
|
||||
```
|
||||
|
||||
### Task 3: 调整前端提交值与详情展示
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
- Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue`
|
||||
|
||||
- [ ] **Step 1: 在提交逻辑中改为 `1/0` 值**
|
||||
|
||||
```js
|
||||
const data = {
|
||||
...this.form,
|
||||
bizProof: this.form.bizProof ? '1' : '0',
|
||||
loanLoop: this.form.loanLoop ? '1' : '0',
|
||||
collThirdParty: this.form.collThirdParty ? '1' : '0'
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在详情页补齐 `loanPurpose` 展示**
|
||||
|
||||
```vue
|
||||
<el-descriptions-item label="贷款用途">{{ detailData.loanPurpose || '-' }}</el-descriptions-item>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 兼容详情页 `0/1` 布尔展示**
|
||||
|
||||
```js
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 运行前端源码断言并确认通过**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:personal-create-input-params`
|
||||
|
||||
Expected: PASS,输出断言通过信息
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue
|
||||
git commit -m "调整个人测算前端提交与展示"
|
||||
```
|
||||
|
||||
### Task 4: 页面联调与回归验证
|
||||
|
||||
**Files:**
|
||||
- Verify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
- Verify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue`
|
||||
|
||||
- [ ] **Step 1: 启动前端开发服务**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run dev`
|
||||
|
||||
Expected: 成功启动并输出本地访问地址
|
||||
|
||||
- [ ] **Step 2: 打开流程页面验证新增弹窗**
|
||||
|
||||
Run: 在浏览器中进入 `/loanPricing/workflow`
|
||||
|
||||
Expected:
|
||||
- 出现 `贷款用途`
|
||||
- 出现 `借款期限(年)` 固定下拉
|
||||
- `抵质押类型` 选项为 `一类/二类/三类`
|
||||
|
||||
- [ ] **Step 3: 结合后端联调创建个人流程**
|
||||
|
||||
Run: 在页面中填写完整参数并提交
|
||||
|
||||
Expected: 创建成功,不出现参数缺失报错
|
||||
|
||||
- [ ] **Step 4: 打开详情页验证回显**
|
||||
|
||||
Run: 打开刚创建的个人流程详情
|
||||
|
||||
Expected:
|
||||
- 页面展示 `贷款用途`
|
||||
- 页面展示 `借款期限`
|
||||
- 开关字段显示为“是/否”
|
||||
|
||||
- [ ] **Step 5: 验证结束后停止前端进程并提交**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue ruoyi-ui/tests/personal-create-input-params.test.js ruoyi-ui/package.json
|
||||
git commit -m "完成个人测算输入参数前端联调"
|
||||
```
|
||||
52
doc/2026-04-16-shangyu-corporate-alignment-backend-plan.md
Normal file
52
doc/2026-04-16-shangyu-corporate-alignment-backend-plan.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# 上虞对公利率测算字段对齐后端实施计划
|
||||
|
||||
## 目标
|
||||
- 对齐对公创建接口、模型调用入参、流程详情返回、mock 返回和 SQL 基线。
|
||||
|
||||
## 实施内容
|
||||
- 创建请求字段改为 Excel `上传指标` 口径:
|
||||
- 新增 `repayMethod`
|
||||
- `isTradeConstruction` 改为 `isTradeBuildEnt`
|
||||
- 移除对公创建链路中的 `isAgriGuar`、`isTechEnt`
|
||||
- 流程主表实体补 `repayMethod`,并将 `isTradeBuildEnt` 映射到数据库列 `is_trade_construction`
|
||||
- 对公模型输出实体补齐:
|
||||
- `repayMethod`
|
||||
- `isTradeBuildEnt`
|
||||
- `loanRateHistory`
|
||||
- `minRateProduct`
|
||||
- `smoothRange`
|
||||
- `finalCalculateRate`
|
||||
- `referenceRate`
|
||||
- 对公模型输出实体不再暴露:
|
||||
- `isAgriGuar`
|
||||
- `midEntTax`
|
||||
- `cardOverdue`
|
||||
- 企业模型入参统一值域:
|
||||
- `isGreenLoan`、`isTradeBuildEnt`、`collThirdParty` 发送 `0/1`
|
||||
- `repayMethod` 发送 `分期/不分期`
|
||||
- 企业流程详情主利率改为 `finalCalculateRate`
|
||||
- mock 继续保留 `data.mappingOutputFields` 包装层,只更新企业字段集合和值域
|
||||
|
||||
## SQL 调整
|
||||
- `loan_pricing_workflow` 新增 `repay_method`
|
||||
- `model_corp_output_fields` 新增:
|
||||
- `repay_method`
|
||||
- `is_trade_build_ent`
|
||||
- `loan_rate_history`
|
||||
- `min_rate_product`
|
||||
- `smooth_range`
|
||||
- `final_calculate_rate`
|
||||
- `reference_rate`
|
||||
- 已同步更新:
|
||||
- `sql/loan_pricing_workflow.sql`
|
||||
- `sql/model_corp.sql`
|
||||
- `sql/loan_pricing_schema_20260328.sql`
|
||||
- `sql/loan_pricing_prod_init_20260331.sql`
|
||||
- `sql/2026-04-16-shangyu-corporate-alignment.sql`
|
||||
|
||||
## 验证
|
||||
- 运行后端定向单测,确认对公字段和详情主利率断言通过
|
||||
- 使用 `/login/test` 获取 token 后调用对公创建和详情接口,确认:
|
||||
- 正常场景成功
|
||||
- 缺少 `repayMethod` 返回校验错误
|
||||
- 详情返回包含新增字段且 `loanRate = finalCalculateRate`
|
||||
48
doc/2026-04-16-shangyu-corporate-alignment-frontend-plan.md
Normal file
48
doc/2026-04-16-shangyu-corporate-alignment-frontend-plan.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 上虞对公利率测算字段对齐前端实施计划
|
||||
|
||||
## 目标
|
||||
- 对齐对公新增弹窗和企业流程详情页展示,严格跟随 Excel `上传指标` 与 `展示指标`。
|
||||
|
||||
## 实施内容
|
||||
- 对公新增弹窗调整为 Excel `上传指标`:
|
||||
- 新增 `repayMethod`
|
||||
- `isTradeConstruction` 改为 `isTradeBuildEnt`
|
||||
- 删除 `省农担担保贷款`、`科技型企业`
|
||||
- `loanTerm` 文案改为按年
|
||||
- `collType` 选项改为 `一类/二类/三类/四类`
|
||||
- `isGreenLoan`、`isTradeBuildEnt`、`collThirdParty` 提交值改为 `1/0`
|
||||
- 企业详情左侧关键信息:
|
||||
- 标签改为 `最终测算利率`
|
||||
- 读取 `corpOutput.finalCalculateRate`
|
||||
- 企业流程详情业务信息:
|
||||
- 新增展示 `repayMethod`
|
||||
- 新增展示 `isTradeBuildEnt`
|
||||
- 保留 `isGreenLoan`
|
||||
- 移除不在本次口径内的企业业务展示
|
||||
- 企业模型输出补齐展示:
|
||||
- `repayMethod`
|
||||
- `isTradeBuildEnt`
|
||||
- `loanRateHistory`
|
||||
- `minRateProduct`
|
||||
- `smoothRange`
|
||||
- `finalCalculateRate`
|
||||
- `referenceRate`
|
||||
- 企业模型输出移除展示:
|
||||
- `isAgriGuar`
|
||||
- `midEntTax`
|
||||
- `cardOverdue`
|
||||
|
||||
## 测试脚本
|
||||
- 新增:
|
||||
- `ruoyi-ui/tests/corporate-create-input-params.test.js`
|
||||
- `ruoyi-ui/tests/corporate-display-fields.test.js`
|
||||
- 更新 `ruoyi-ui/package.json`,补充对应 npm scripts
|
||||
|
||||
## 验证
|
||||
- `nvm use default` 后执行两个对公静态断言脚本
|
||||
- 执行前端生产构建
|
||||
- 启动前端页面并在浏览器中确认:
|
||||
- 对公新增弹窗字段和选项正确
|
||||
- 创建成功后列表刷新
|
||||
- 企业详情页显示 `最终测算利率`
|
||||
- 企业详情页和模型输出出现新增字段
|
||||
@@ -32,10 +32,12 @@
|
||||
| idNum | String | 否 | 证件号码 |
|
||||
| guarType | String | 是 | 担保方式,可选值: 信用/保证/抵押/质押 |
|
||||
| applyAmt | String | 是 | 申请金额,单位: 元 |
|
||||
| bizProof | String | 否 | 是否有经营佐证,值: true/false |
|
||||
| loanLoop | String | 否 | 循环功能,值: true/false |
|
||||
| collType | String | 否 | 抵质押类型,可选值: 一线/一类/二类 |
|
||||
| collThirdParty | String | 否 | 抵质押物是否三方所有,值: true/false |
|
||||
| loanPurpose | String | 是 | 贷款用途,可选值: consumer/business |
|
||||
| loanTerm | String | 是 | 借款期限(年),固定下拉选项按模型文档配置 |
|
||||
| bizProof | String | 否 | 是否有经营佐证,值: 0/1 |
|
||||
| loanLoop | String | 否 | 循环功能,值: 0/1 |
|
||||
| collType | String | 否 | 抵质押类型,可选值: 一类/二类/三类 |
|
||||
| collThirdParty | String | 否 | 抵质押物是否三方所有,值: 0/1 |
|
||||
|
||||
**请求示例:**
|
||||
|
||||
@@ -47,10 +49,12 @@
|
||||
"idNum": "110101199001011234",
|
||||
"guarType": "抵押",
|
||||
"applyAmt": "500000",
|
||||
"bizProof": "true",
|
||||
"loanLoop": "false",
|
||||
"loanPurpose": "business",
|
||||
"loanTerm": "3",
|
||||
"bizProof": "1",
|
||||
"loanLoop": "0",
|
||||
"collType": "一类",
|
||||
"collThirdParty": "false"
|
||||
"collThirdParty": "0"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -64,12 +68,14 @@
|
||||
"id": 1,
|
||||
"modelOutputId": 100,
|
||||
"serialNum": "20250119143025123",
|
||||
"orgCode": "931000",
|
||||
"orgCode": "892000",
|
||||
"runType": "1",
|
||||
"custIsn": "CUST001",
|
||||
"custType": "个人",
|
||||
"guarType": "抵押",
|
||||
"applyAmt": "500000",
|
||||
"loanPurpose": "business",
|
||||
"loanTerm": "3",
|
||||
"custName": "张三",
|
||||
"idType": "身份证",
|
||||
"createTime": "2025-01-19 14:30:25",
|
||||
@@ -136,7 +142,7 @@
|
||||
"id": 2,
|
||||
"modelOutputId": 101,
|
||||
"serialNum": "20250119143125456",
|
||||
"orgCode": "931000",
|
||||
"orgCode": "892000",
|
||||
"runType": "1",
|
||||
"custIsn": "CORP001",
|
||||
"custType": "企业",
|
||||
@@ -189,7 +195,7 @@ GET /loanPricing/workflow/list?pageNum=1&pageSize=10&custName=科技
|
||||
"id": 1,
|
||||
"modelOutputId": 100,
|
||||
"serialNum": "20250119143025123",
|
||||
"orgCode": "931000",
|
||||
"orgCode": "892000",
|
||||
"custIsn": "CUST001",
|
||||
"custType": "企业",
|
||||
"guarType": "抵押",
|
||||
@@ -240,7 +246,7 @@ GET /loanPricing/workflow/20250119143025123
|
||||
"id": 1,
|
||||
"modelOutputId": 100,
|
||||
"serialNum": "20250119143025123",
|
||||
"orgCode": "931000",
|
||||
"orgCode": "892000",
|
||||
"runType": "1",
|
||||
"custIsn": "CUST001",
|
||||
"custType": "企业",
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# 2026-04-09 默认切换 Node 25 以支持 Playwright 实施记录
|
||||
|
||||
## 变更内容
|
||||
- 将 `nvm` 默认别名从 `14` 调整为 `25`
|
||||
- 清理了本次验证过程中残留的 Playwright 浏览器安装进程
|
||||
|
||||
## 执行命令
|
||||
- `zsh -lic 'nvm alias default 25'`
|
||||
|
||||
## 变更结果
|
||||
- 新开的交互式 `zsh` 默认 Node 版本变为 `v25.9.0`
|
||||
- 默认 npm/npx 版本变为 `11.12.1`
|
||||
|
||||
## 验证结果
|
||||
- `zsh -lic 'node -v && npm -v && npx -v && nvm current && nvm alias default'`
|
||||
- `node v25.9.0`
|
||||
- `npm 11.12.1`
|
||||
- `npx 11.12.1`
|
||||
- `nvm current = v25.9.0`
|
||||
- `default -> 25 (-> v25.9.0 *)`
|
||||
- `zsh -lic '... playwright_cli.sh --help'`
|
||||
- Playwright CLI 帮助输出正常
|
||||
- `zsh -lic '... playwright_cli.sh --session verify-default-25 open https://example.com && snapshot && close && list'`
|
||||
- 页面成功打开
|
||||
- 页面标题为 `Example Domain`
|
||||
- 快照成功输出
|
||||
- 浏览器关闭后 `list` 返回 `(no browsers)`
|
||||
|
||||
## 结论
|
||||
- 默认 shell 环境下已可直接使用 Playwright,无需再先手动执行 `nvm use 25`
|
||||
@@ -0,0 +1,30 @@
|
||||
# 2026-04-09 安装 Node 25 与 Node 14 实施记录
|
||||
|
||||
## 变更内容
|
||||
- 使用 `nvm` 安装 `node v25.9.0`
|
||||
- 使用 `nvm` 安装 `node v14.21.3`
|
||||
- 调整 `/Users/wkc/.npmrc`,删除与 `nvm` 冲突的 `prefix=~/.npm-global`
|
||||
- 保留 npm 镜像配置:`registry=https://registry.npmmirror.com`
|
||||
|
||||
## 处理过程
|
||||
- `node 25` 通过 `nvm` 正常安装成功
|
||||
- `node 14` 在 Apple Silicon 原生环境下无法直接下载 `darwin-arm64` 安装包
|
||||
- 原生源码编译 `node 14` 失败,错误来自当前 macOS Command Line Tools/SDK 与旧版 `node 14` 源码不兼容
|
||||
- 改为通过 Rosetta 以 `x64` 方式安装 `node 14`,安装成功
|
||||
|
||||
## 验证结果
|
||||
- `zsh -lic 'nvm use 25 && node -v && npm -v'` 验证结果:
|
||||
- `node v25.9.0`
|
||||
- `npm 11.12.1`
|
||||
- `zsh -lic 'nvm use 14 && node -v && npm -v'` 验证结果:
|
||||
- `node v14.21.3`
|
||||
- `npm 6.14.18`
|
||||
- `arch -x86_64 /bin/zsh -lic 'nvm use 14 && node -v && npm -v'` 验证结果:
|
||||
- `node v14.21.3`
|
||||
- `npm 6.14.18`
|
||||
- 新开的交互式 `zsh` 默认版本:
|
||||
- `node v14.21.3`
|
||||
- `npm 6.14.18`
|
||||
|
||||
## 备注
|
||||
- `nvm` 当前默认别名已指向 `14`
|
||||
@@ -0,0 +1,25 @@
|
||||
# 2026-04-09 Node 卸载与 nvm 安装实施记录
|
||||
|
||||
## 变更内容
|
||||
- 卸载了 Homebrew 安装的 `node 25.8.1_1`
|
||||
- 删除了本地目录 `/Users/wkc/.local/node-v14.21.3-darwin-x64`
|
||||
- 更新了 `/Users/wkc/.zshrc`
|
||||
- 安装了 `nvm 0.40.4`
|
||||
|
||||
## shell 配置调整
|
||||
- 删除旧配置:`export PATH="/Users/wkc/.local/node-v14.21.3-darwin-x64/bin:$PATH"`
|
||||
- 新增 `nvm` 初始化配置:
|
||||
|
||||
```sh
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"
|
||||
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"
|
||||
```
|
||||
|
||||
## 验证结果
|
||||
- 交互式 `zsh` 下 `nvm --version` 返回 `0.40.4`
|
||||
- `node` 命令已不存在,说明当前环境中已无非 `nvm` 管理的 Node 版本
|
||||
- `nvm ls` 返回 `N/A`,说明尚未通过 `nvm` 安装任何 Node 版本
|
||||
|
||||
## 备注
|
||||
- `brew uninstall node` 过程中触发了 Homebrew 自动移除若干仅供该版本 Node 使用的依赖库
|
||||
@@ -0,0 +1,25 @@
|
||||
# 证件输入校验移除实施记录
|
||||
|
||||
## 实施时间
|
||||
- 2026-04-09
|
||||
|
||||
## 修改内容
|
||||
- 移除个人新增弹窗中的证件号码格式校验
|
||||
- 移除企业新增弹窗中的证件号码格式校验
|
||||
- 两个新增弹窗的证件号码规则统一保留为必填校验
|
||||
- 新增前端源码断言,约束后续不再恢复证件号码格式校验
|
||||
|
||||
## 修改文件
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/CorporateCreateDialog.vue`
|
||||
- `ruoyi-ui/tests/id-number-validation-removal.test.js`
|
||||
- `ruoyi-ui/package.json`
|
||||
- `doc/implementation-report-2026-04-09-remove-id-number-validation.md`
|
||||
|
||||
## 验证方式
|
||||
1. `npm --prefix ruoyi-ui run test:id-number-validation-removal`
|
||||
2. `npm --prefix ruoyi-ui run build:prod`
|
||||
|
||||
## 说明
|
||||
- 本次移除的是前端证件号码格式校验,不影响证件号码必填约束
|
||||
- 后端本次未新增或调整证件号码格式校验逻辑
|
||||
@@ -0,0 +1,26 @@
|
||||
# 上虞个人利率测算输入参数文档产出记录
|
||||
|
||||
## 实施时间
|
||||
- 2026-04-09
|
||||
|
||||
## 修改内容
|
||||
- 新增个人利率测算输入参数对齐设计文档
|
||||
- 新增个人利率测算输入参数前端实施计划
|
||||
- 新增个人利率测算输入参数后端实施计划
|
||||
- 按 Excel `入参` sheet 整理个人新增弹窗字段获取方式和模型调用参数来源
|
||||
- 明确 `loanTerm` 使用固定年限下拉,选项按 Excel 组织
|
||||
- 明确系统字段 `serialNum`、`orgCode`、`runType`、`custType` 继续自动带值
|
||||
- 明确个人开关字段在模型调用层转换为 `0/1`
|
||||
- 补充确认后端最终发给模型的 16 个输入参数、来源、请求格式与验证口径
|
||||
|
||||
## 修改文件
|
||||
- `doc/2026-04-09-shangyu-retail-input-params-design.md`
|
||||
- `doc/2026-04-09-shangyu-retail-input-params-frontend-plan.md`
|
||||
- `doc/2026-04-09-shangyu-retail-input-params-backend-plan.md`
|
||||
- `doc/implementation-report-2026-04-09-shangyu-retail-input-params-plans.md`
|
||||
|
||||
## 说明
|
||||
- 设计文档保存路径已核对为项目现有的 `doc/` 目录
|
||||
- 按项目要求,本次实施计划拆分为前端与后端两份文档
|
||||
- 由于仓库约束为“不开启 subagent”,文档评审环节未使用子代理,后续执行时将在当前会话内推进
|
||||
- 本次仅产出设计与计划文档,尚未进入代码实施阶段
|
||||
@@ -0,0 +1,93 @@
|
||||
# 上虞个人利率测算输入参数对齐实施记录
|
||||
|
||||
## 实施时间
|
||||
- 2026-04-09
|
||||
|
||||
## 修改内容
|
||||
- 个人新增弹窗补齐 `loanPurpose`、`loanTerm`
|
||||
- 个人新增弹窗 `loanTerm` 固定为 `1-6` 年
|
||||
- 个人新增弹窗 `collType` 选项统一为 `一类/二类/三类`
|
||||
- 个人新增弹窗开关字段提交值由 `true/false` 调整为 `1/0`
|
||||
- 个人详情页补齐 `贷款用途` 展示
|
||||
- 个人与企业详情、模型输出布尔展示兼容 `1/0`
|
||||
- 后端个人创建 DTO 补齐 `loanPurpose`、`loanTerm`
|
||||
- 后端个人 DTO 到流程实体映射补齐 `loanPurpose`、`loanTerm`
|
||||
- 后端模型调用 DTO 补齐 `loanTerm`、`loanLoop`
|
||||
- 后端个人模型调用前统一将 `bizProof`、`loanLoop`、`collThirdParty` 规范为 `0/1`
|
||||
- `orgCode` 统一为 `892000`
|
||||
- `ModelInvokeDTO` 注释、接口文档、SQL 基线和迁移脚本同步统一为 `892000`
|
||||
- 新增前端源码断言与后端单元测试
|
||||
|
||||
## 修改文件
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/ModelOutputDisplay.vue`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/CorporateCreateDialog.vue`
|
||||
- `ruoyi-ui/tests/personal-create-input-params.test.js`
|
||||
- `ruoyi-ui/package.json`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java`
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingModelService.java`
|
||||
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java`
|
||||
- `doc/api/loan-pricing-workflow-api.md`
|
||||
- `sql/loan_pricing_workflow.sql`
|
||||
- `sql/loan_pricing_schema_20260328.sql`
|
||||
- `sql/loan_pricing_prod_init_20260331.sql`
|
||||
- `sql/fix_comments.sql`
|
||||
- `sql/fix_all_comments.sql`
|
||||
- `sql/update_org_code_default_20260409.sql`
|
||||
- `doc/2026-04-09-shangyu-retail-input-params-design.md`
|
||||
- `doc/2026-04-09-shangyu-retail-input-params-frontend-plan.md`
|
||||
- `doc/implementation-report-2026-04-09-shangyu-retail-input-params.md`
|
||||
|
||||
## 数据库处理
|
||||
1. 执行 `sql/update_org_code_default_20260409.sql`
|
||||
2. 将 `loan_pricing_workflow.org_code` 默认值修改为 `892000`
|
||||
3. 将存量 `loan_pricing_workflow.org_code` 非 `892000` 的记录统一更新为 `892000`
|
||||
|
||||
## 验证方式
|
||||
1. 前端源码断言:
|
||||
- `npm --prefix ruoyi-ui run test:personal-create-input-params`
|
||||
- `npm --prefix ruoyi-ui run test:retail-display-fields`
|
||||
2. 后端单元测试:
|
||||
- `mvn -pl ruoyi-loan-pricing -Dtest=LoanPricingModelServiceTest,LoanPricingModelServicePersonalParamsTest test`
|
||||
3. 前端构建:
|
||||
- `npm --prefix ruoyi-ui run build:prod`
|
||||
4. 数据库验证:
|
||||
- 查询 `loan_pricing_workflow.org_code` 字段默认值
|
||||
- 查询存量数据中是否仍存在非 `892000` 记录
|
||||
5. 接口验证:
|
||||
- `/login/test` 获取 token
|
||||
- `POST /loanPricing/workflow/create/personal` 正常场景
|
||||
- `POST /loanPricing/workflow/create/personal` 缺少 `loanPurpose` 场景
|
||||
- `POST /loanPricing/workflow/create/personal` 分支值场景
|
||||
- `GET /loanPricing/workflow/{serialNum}` 验证回显
|
||||
6. 页面验证:
|
||||
- 启动前端 dev server
|
||||
- 使用浏览器打开流程列表页
|
||||
- 校验新增弹窗下拉选项
|
||||
- 页面创建个人流程并打开详情页确认回显
|
||||
|
||||
## 验证结果
|
||||
- `npm --prefix ruoyi-ui run test:personal-create-input-params` 通过
|
||||
- `npm --prefix ruoyi-ui run test:retail-display-fields` 通过
|
||||
- `mvn -pl ruoyi-loan-pricing -Dtest=LoanPricingModelServiceTest,LoanPricingModelServicePersonalParamsTest test` 通过
|
||||
- `npm --prefix ruoyi-ui run build:prod` 通过,输出 `Build complete.`
|
||||
- 数据库验证结果:
|
||||
- `loan_pricing_workflow.org_code` 默认值为 `892000`
|
||||
- 存量非 `892000` 记录数为 `0`
|
||||
- 接口验证结果:
|
||||
- 正常场景创建成功,返回 `orgCode=892000`,并持久化 `loanPurpose`、`loanTerm`
|
||||
- 缺少 `loanPurpose` 时返回 `贷款用途不能为空`
|
||||
- 分支场景详情回显 `bizProof=0`、`loanLoop=1`、`collThirdParty=0`
|
||||
- 页面验证结果:
|
||||
- 新增弹窗显示 `贷款用途`
|
||||
- 借款期限下拉仅包含 `1-6`
|
||||
- 抵质押类型下拉为 `一类/二类/三类`
|
||||
- 页面创建流程成功后,详情页展示 `贷款用途=经营`、`借款期限=6`
|
||||
|
||||
## 说明
|
||||
- 浏览器验证使用系统 `Google Chrome.app`
|
||||
- 本次验证期间启动的后端、前端和浏览器进程已在任务结束前关闭
|
||||
26
doc/implementation-report-2026-04-09-start-script-ps-ef.md
Normal file
26
doc/implementation-report-2026-04-09-start-script-ps-ef.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 启动脚本进程判断改为 ps -ef 实施记录
|
||||
|
||||
## 修改内容
|
||||
|
||||
- 将 `bin/prod/restart_java.sh` 中的后端进程收集逻辑由 `pgrep -f` 改为 `ps -ef | awk`
|
||||
- 将 `bin/restart_java_backend.sh` 中的后端进程收集逻辑由 `pgrep -f` 改为 `ps -ef | awk`
|
||||
- 删除 `bin/restart_java_backend.sh` 中对 `pgrep` 命令的依赖校验
|
||||
- 更新 `bin/prod/restart_java_test.sh`,补充 `ps -ef` / `pgrep` 约束校验,并修正测试夹具中的 JDK 目录
|
||||
- 新增 `bin/restart_java_backend_test.sh`,校验本地后端重启脚本已改用 `ps -ef`
|
||||
|
||||
## 实现说明
|
||||
|
||||
- 两份脚本都只在 `ps -ef` 结果中匹配同时满足“包含脚本标记参数”和“`-jar` 指向目标 jar”这两个条件的 Java 进程
|
||||
- 进程筛选时继续忽略 `<defunct>` 记录,避免误判僵尸进程
|
||||
- 现有 PID 文件校验逻辑保持不变,本次只收敛“扫描当前是否已有进程”的实现方式
|
||||
|
||||
## 路径检查
|
||||
|
||||
- 已确认本次实施记录保存路径为 `doc/implementation-report-2026-04-09-start-script-ps-ef.md`
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 已执行 `sh bin/prod/restart_java_test.sh`
|
||||
- 已执行 `sh bin/restart_java_backend_test.sh`
|
||||
- 已执行 `sh -n bin/prod/restart_java.sh && sh -n bin/restart_java_backend.sh`
|
||||
- 已确认测试中拉起的假 Java 进程在脚本收尾阶段自动停止并清理
|
||||
@@ -0,0 +1,37 @@
|
||||
# 2026-04-10 登录 Shell 默认使用 Node 25 实施记录
|
||||
|
||||
## 变更内容
|
||||
- 保持 `nvm` 默认别名为 `25`
|
||||
- 在 `~/.zprofile` 中补充 `nvm` 初始化,并在登录 shell 启动时自动执行 `nvm use default`
|
||||
|
||||
## 根因分析
|
||||
- `nvm alias default 25` 已经存在,但仅在交互式 shell 中可用
|
||||
- `zsh -lc` 启动的是登录非交互 shell,不会读取 `~/.zshrc`
|
||||
- 因此这类场景下 `node`、`npm`、`npx` 未进入 PATH,表现为 `npx` 启动失败
|
||||
|
||||
## 修改文件
|
||||
- `~/.zprofile`
|
||||
- `doc/implementation-report-2026-04-10-login-shell-default-node25.md`
|
||||
|
||||
## 验证项
|
||||
- 验证登录 shell 在不手动执行 `nvm use` 的情况下可直接识别 `node`
|
||||
- 验证登录 shell 在不手动执行 `nvm use` 的情况下可直接识别 `npx`
|
||||
- 验证 `nvm` 默认别名仍然指向 `25`
|
||||
|
||||
## 执行命令
|
||||
- `zsh -lc 'nvm alias default 25'`
|
||||
- `zsh -lc 'echo NODE=$(node -v); echo NPM=$(npm -v); echo NPX=$(npx -v); echo NODE_PATH=$(command -v node); echo NPX_PATH=$(command -v npx); echo NVM_CURRENT=$(nvm current); echo NVM_ALIAS=$(nvm alias default | tail -n 1)'`
|
||||
|
||||
## 验证结果
|
||||
- `nvm` 默认别名输出为 `default -> 25 (-> v25.9.0 *)`
|
||||
- 登录 shell 输出 `NODE=v25.9.0`
|
||||
- 登录 shell 输出 `NPM=11.12.1`
|
||||
- 登录 shell 输出 `NPX=11.12.1`
|
||||
- 登录 shell 输出 `NODE_PATH=/Users/wkc/.nvm/versions/node/v25.9.0/bin/node`
|
||||
- 登录 shell 输出 `NPX_PATH=/Users/wkc/.nvm/versions/node/v25.9.0/bin/npx`
|
||||
- 登录 shell 输出 `NVM_CURRENT=v25.9.0`
|
||||
- 登录 shell 输出 `NVM_ALIAS=default -> 25 (-> v25.9.0 *)`
|
||||
|
||||
## 结论
|
||||
- `zsh -lc` 场景下已默认切换到 Node `25.9.0`
|
||||
- `npx` 在登录 shell 中已可直接使用,无需先手动执行 `nvm use 25`
|
||||
@@ -0,0 +1,19 @@
|
||||
# 个人流程最终测算利率展示实施记录
|
||||
|
||||
## 修改时间
|
||||
- 2026-04-11
|
||||
|
||||
## 修改内容
|
||||
- 调整流程列表后端查询:个人客户列表“测算利率”改为读取 `model_retail_output_fields.final_calculate_rate`
|
||||
- 调整个人流程详情左侧展示:标签改为“最终测算利率”,显示字段改为 `retailOutput.finalCalculateRate`
|
||||
- 调整个人流程详情后端组装:`loanPricingWorkflow.loanRate` 改为取个人模型输出的 `finalCalculateRate`
|
||||
|
||||
## 影响范围
|
||||
- 个人流程列表
|
||||
- 个人流程详情左侧关键信息
|
||||
- 企业流程列表与详情保持现状,不做改动
|
||||
|
||||
## 验证计划
|
||||
- 后端单元测试验证个人详情取 `finalCalculateRate`
|
||||
- 后端静态测试验证列表 SQL 的个人分支查询 `mr.final_calculate_rate`
|
||||
- 前端静态测试验证个人详情展示 `finalCalculateRate`
|
||||
@@ -0,0 +1,17 @@
|
||||
# 流程详情卡片顺序调整实施记录
|
||||
|
||||
## 修改时间
|
||||
- 2026-04-11
|
||||
|
||||
## 修改内容
|
||||
- 调整个人流程详情页右侧卡片顺序,将模型输出卡片移动到流程详情卡片上方
|
||||
- 调整企业流程详情页右侧卡片顺序,将模型输出卡片移动到流程详情卡片上方
|
||||
- 新增前端静态校验,约束个人与企业详情组件的卡片顺序
|
||||
|
||||
## 影响范围
|
||||
- 个人流程详情页面布局
|
||||
- 企业流程详情页面布局
|
||||
|
||||
## 验证计划
|
||||
- 执行前端静态测试,确认卡片顺序断言通过
|
||||
- 启动前端页面并在浏览器中检查个人、企业详情页卡片顺序
|
||||
@@ -0,0 +1,38 @@
|
||||
# 上虞对公利率测算字段对齐实施记录
|
||||
|
||||
## 修改时间
|
||||
- 2026-04-16
|
||||
|
||||
## 修改内容
|
||||
- 对齐对公创建请求字段,新增 `repayMethod`,将 `isTradeConstruction` 统一为 `isTradeBuildEnt`
|
||||
- 对齐企业详情返回与页面展示,左侧主利率改为 `finalCalculateRate`
|
||||
- 对齐对公模型输出字段,补齐 `loanRateHistory`、`minRateProduct`、`smoothRange`、`finalCalculateRate`、`referenceRate`
|
||||
- 裁剪企业模型输出和页面展示,不再暴露 `isAgriGuar`、`midEntTax`、`cardOverdue`
|
||||
- 对公新增弹窗中的 `贷款期限(年)` 调整为下拉框,选项固定为 `1-10` 年
|
||||
- 更新企业 mock 返回和 SQL 基线、迁移脚本
|
||||
|
||||
## 文档与脚本
|
||||
- `doc/2026-04-16-shangyu-corporate-alignment-backend-plan.md`
|
||||
- `doc/2026-04-16-shangyu-corporate-alignment-frontend-plan.md`
|
||||
- `sql/2026-04-16-shangyu-corporate-alignment.sql`
|
||||
|
||||
## 验证记录
|
||||
- 后端单测:
|
||||
- `mvn -pl ruoyi-loan-pricing -Dtest=ModelCorpOutputFieldsTest,LoanPricingModelServiceTest,LoanPricingWorkflowServiceImplTest test`
|
||||
- 结果:13 个测试全部通过
|
||||
- 前端静态断言:
|
||||
- `zsh -lic 'nvm use default >/dev/null && npm --prefix ruoyi-ui run test:corporate-create-input-params'`
|
||||
- `zsh -lic 'nvm use default >/dev/null && npm --prefix ruoyi-ui run test:corporate-display-fields'`
|
||||
- 结果:两个脚本均通过
|
||||
- 前端构建:
|
||||
- `zsh -lic 'nvm use default >/dev/null && npm --prefix ruoyi-ui run build:prod'`
|
||||
- 结果:构建成功,仅有体积告警
|
||||
- 接口联调:
|
||||
- 使用 `/login/test` 获取 token
|
||||
- 验证了对公创建正常场景、缺少 `repayMethod` 的参数错误场景、`分期/不分期` 与 `1/0` 分支场景
|
||||
- 详情接口确认返回新增字段,且 `loanPricingWorkflow.loanRate = modelCorpOutputFields.finalCalculateRate`
|
||||
- 浏览器联调:
|
||||
- 启动前端开发服务并打开流程列表
|
||||
- 验证对公新增弹窗字段、选项、提交流程
|
||||
- 验证创建后列表新增记录
|
||||
- 验证企业详情页出现 `最终测算利率`、`还款方式`、`贸易和建筑业企业`、`历史利率`、`产品最低利率下限`、`平滑幅度`、`参考利率`
|
||||
@@ -0,0 +1,21 @@
|
||||
# 流程详情页模型输出平铺展示实施记录
|
||||
|
||||
## 改动日期
|
||||
- 2026-04-16
|
||||
|
||||
## 改动范围
|
||||
- 前端:`ruoyi-ui/src/views/loanPricing/workflow/components/ModelOutputDisplay.vue`
|
||||
- 前端测试:`ruoyi-ui/tests/model-output-flat-display.test.js`
|
||||
- 前端脚本:`ruoyi-ui/package.json`
|
||||
|
||||
## 改动内容
|
||||
- 取消流程详情页“模型输出”区域的 Tab 切换结构。
|
||||
- 保留原有分组顺序与字段内容,将“基本信息”“忠诚度分析”“贡献度分析”等分组改为自上而下平铺展示。
|
||||
- 按最新要求,将“测算结果”分组前移到“基本信息”下方,优先展示最终测算相关结果。
|
||||
- 按最新要求,将“测算结果”中的“最终测算利率”调整到最后一行展示。
|
||||
- 移除组件内仅用于 Tab 默认选中的 `activeTab` 和相关监听逻辑。
|
||||
- 新增最小回归测试,校验模型输出组件不再包含 `el-tabs`、`el-tab-pane`,并具备平铺分组区块,同时校验“基本信息”后紧跟“测算结果”。
|
||||
|
||||
## 验证计划
|
||||
- 使用 `nvm` 显式切换前端 Node 版本后执行 `npm run test:model-output-flat-display`。
|
||||
- 启动前端页面,在浏览器中打开流程详情页,确认模型输出区域已按分组平铺展示,且不再出现 Tab 切换。
|
||||
BIN
doc/~$上虞利率测算接口文档.xlsx
Normal file
BIN
doc/~$上虞利率测算接口文档.xlsx
Normal file
Binary file not shown.
BIN
doc/~$上虞对公利率测算_上传字段与展示字段 .xlsx
Normal file
BIN
doc/~$上虞对公利率测算_上传字段与展示字段 .xlsx
Normal file
Binary file not shown.
BIN
doc/上虞利率测算接口文档.xlsx
Normal file
BIN
doc/上虞利率测算接口文档.xlsx
Normal file
Binary file not shown.
BIN
doc/上虞对公利率测算_上传字段与展示字段 .xlsx
Normal file
BIN
doc/上虞对公利率测算_上传字段与展示字段 .xlsx
Normal file
Binary file not shown.
@@ -0,0 +1,110 @@
|
||||
# Personal Pricing Collateral Optional Frontend Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:executing-plans to implement this plan in this repository. Do not use subagents. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 让个人利率定价新增流程中的“抵质押类型”改为非必填,企业流程保持不变。
|
||||
|
||||
**Architecture:** 维持现有个人新增弹窗的数据结构、字段名称和提交报文不变,只移除前端表单对 `collType` 的必填限制。通过前端源码断言测试和页面联调共同验证“字段可空提交”的行为,避免扩大到企业流程或后端接口。
|
||||
|
||||
**Tech Stack:** Vue 2、Element UI、npm、Node.js 断言脚本
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 固化个人新增流程“抵质押类型非必填”的前端测试
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/tests/personal-create-input-params.test.js`
|
||||
- Inspect: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
|
||||
- [ ] **Step 1: 核对当前个人新增弹窗中的 `collType` 规则**
|
||||
|
||||
Run: `sed -n '150,220p' ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
Expected: 能看到 `rules.collType` 里仍有 `required: true`。
|
||||
|
||||
- [ ] **Step 2: 先写失败断言**
|
||||
|
||||
在 `ruoyi-ui/tests/personal-create-input-params.test.js` 中增加断言,明确个人新增弹窗不应再包含:
|
||||
|
||||
```js
|
||||
required: true, message: "请选择抵质押类型"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 运行测试确认红灯**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:personal-create-input-params`
|
||||
Expected: FAIL,提示个人新增弹窗仍将抵质押类型设为必填。
|
||||
|
||||
### Task 2: 移除个人新增弹窗中 `collType` 的必填限制
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalCreateDialog.vue`
|
||||
|
||||
- [ ] **Step 1: 删除 `collType` 的必填校验**
|
||||
|
||||
把:
|
||||
|
||||
```js
|
||||
collType: [
|
||||
{required: true, message: "请选择抵质押类型", trigger: "change"}
|
||||
]
|
||||
```
|
||||
|
||||
移除,使个人表单规则中不再声明 `collType` 为必填。
|
||||
|
||||
- [ ] **Step 2: 保持其它字段与提交逻辑不变**
|
||||
|
||||
确认以下内容不改动:
|
||||
|
||||
```js
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
```
|
||||
|
||||
以及提交时的:
|
||||
|
||||
```js
|
||||
collThirdParty: this.form.collThirdParty ? '1' : '0'
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 重新运行测试确认转绿**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:personal-create-input-params`
|
||||
Expected: PASS,输出包含 `personal create input params assertions passed`。
|
||||
|
||||
### Task 3: 页面联调并补实施记录
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/implementation-reports/2026-04-10-personal-pricing-collateral-optional-frontend.md`
|
||||
|
||||
- [ ] **Step 1: 启动前端页面**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run dev`
|
||||
Expected: 前端本地开发服务启动成功,可访问新增个人利率定价流程页面。
|
||||
|
||||
- [ ] **Step 2: 浏览器确认页面行为**
|
||||
|
||||
联调确认:
|
||||
- 个人新增弹窗“抵质押类型”字段可为空
|
||||
- 不选择“抵质押类型”时其余必填项填完整仍可点击提交
|
||||
- 企业新增弹窗规则不受影响
|
||||
|
||||
- [ ] **Step 3: 停止本次测试启动的前端进程**
|
||||
|
||||
Run: `ps -ef | rg 'vue-cli-service serve|npm --prefix ruoyi-ui run dev'`
|
||||
Expected: 仅停止本次联调启动的前端进程。
|
||||
|
||||
- [ ] **Step 4: 编写实施记录**
|
||||
|
||||
实施记录至少包含:
|
||||
|
||||
```markdown
|
||||
- 本次仅调整个人利率定价新增流程
|
||||
- 个人新增弹窗已移除抵质押类型必填校验
|
||||
- 企业新增流程未改动
|
||||
- 已执行前端断言测试与页面联调验证
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 核对实施记录保存路径**
|
||||
|
||||
Run: `ls docs/implementation-reports/2026-04-10-personal-pricing-collateral-optional-frontend.md`
|
||||
Expected: 文件位于仓库 `docs/implementation-reports` 目录。
|
||||
@@ -0,0 +1,27 @@
|
||||
# TongWeb 接入后端实施文档
|
||||
|
||||
## 目标
|
||||
|
||||
按照 `tongweb/2026-04-16-TongWeb接入全流程通用指南.md` 在当前后端工程中接入东方通 TongWeb,替换默认内嵌 Tomcat,并将 license 文件随 `ruoyi-admin` 启动模块一起打包。
|
||||
|
||||
## 实施内容
|
||||
|
||||
1. 在 `ruoyi-admin/pom.xml` 增加 TongWeb Maven 仓库。
|
||||
2. 在 `ruoyi-admin/pom.xml` 对 `ruoyi-framework`、`ruoyi-loan-pricing` 依赖排除 `spring-boot-starter-tomcat`。
|
||||
3. 在 `ruoyi-admin/pom.xml` 引入 `com.tongweb.springboot:tongweb-spring-boot-starter-3.x:7.0.E.7`。
|
||||
4. 将 TongWeb license 复制到 `ruoyi-admin/src/main/resources/license.dat`,并在 `application.yml` 中配置 `server.tongweb.license.path=classpath:license.dat`。
|
||||
5. 增加资源存在性测试,验证 `license.dat` 可从 classpath 加载。
|
||||
6. 执行后端构建、依赖树、打包产物和测试验证,确认 TongWeb 依赖与 license 已正确接入。
|
||||
|
||||
## 变更说明
|
||||
|
||||
- 当前项目基于 Spring Boot 3.5.x,因此实际接入使用 `tongweb-spring-boot-starter-3.x`,版本号延续指南中的 `7.0.E.7`。
|
||||
- license 按本次要求保持文件名为 `license.dat`,不改名为 `Tongweb_license.dat`。
|
||||
- 现有 `application-dev.yml`、`application-pro.yml`、`application-uat.yml` 中的 `server.tomcat` 参数暂时保留,后续以 TongWeb 实际启动结果为准决定是否继续清理。
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. `mvn -pl ruoyi-admin -Dtest=TongWebLicenseResourceTest test`
|
||||
2. `mvn -pl ruoyi-admin -am package -DskipTests`
|
||||
3. `jar tf ruoyi-admin/target/ruoyi-admin.jar | rg 'license.dat|tongweb'`
|
||||
4. `mvn -pl ruoyi-admin dependency:tree '-Dincludes=com.tongweb.springboot:*,com.tongweb:*,org.springframework.boot:spring-boot-starter-tomcat,org.apache.tomcat.embed:*'`
|
||||
BIN
ruoyi-admin/.DS_Store
vendored
Normal file
BIN
ruoyi-admin/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -15,6 +15,20 @@
|
||||
web服务入口
|
||||
</description>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>tongweb-releases</id>
|
||||
<name>TongWeb Maven Releases</name>
|
||||
<url>https://mvn.elitescloud.com/nexus/repository/maven-releases/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- spring-boot-devtools -->
|
||||
@@ -40,6 +54,12 @@
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-framework</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 定时任务-->
|
||||
@@ -58,6 +78,18 @@
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-loan-pricing</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tongweb.springboot</groupId>
|
||||
<artifactId>tongweb-spring-boot-starter-3.x</artifactId>
|
||||
<version>7.0.E.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
# 项目相关配置
|
||||
server:
|
||||
tongweb:
|
||||
license:
|
||||
path: classpath:license.dat
|
||||
|
||||
ruoyi:
|
||||
# 名称
|
||||
name: RuoYi
|
||||
|
||||
1
ruoyi-admin/src/main/resources/license.dat
Executable file
1
ruoyi-admin/src/main/resources/license.dat
Executable file
@@ -0,0 +1 @@
|
||||
uc3Y29XJfVtZtZTbmF72t3V405cxamrXBnM0P0vqrrLnJjQ0T0Mt93avL/euwcmvgpWN09qZhbWX25eO9U91ptOrcWNK1XJz6z9waqNC5L40d09ybfrmrDP352Ny76fqyPauv06+ru7f+bTwG99zvHOS8bQvJub/rL3JkoKbfbnZXJmVyVtYwMjPTIjEyQtMsaWMQpnNlNlbkPTX2lTE5EwNsaWOApnNlNlb5cGX3RmVsU9czZQZWFmVhpjcfZGdGVT0yF0Z0LTMDITEwEyLuZFCmVXRl9kYxClPS01ByRXX1Y3b2RmFtRfTUb2ZT12Vi5nVXX1ClRnNpZlcfTnb25mVyVtYuMCPTclRX5FCQVVX0N1VO9DTKYmVD0GlwluZUV1PQpXJk9IYyZVd2FD0K9JZfTWVFd051F4XlcjbWJQpU0tMFZGV19W9ul0atYmPUVk5FVkCWRVV19U9OJTSJQ0X0x0U9VOQLWUWFlTU1lLbSMmSmhkNHlBRrcVdG8kNtUxYCT2RHFTc5lperM2WUFkkvU3M3MzTDBldOlqeTb3YUVGx3VWT1WkaERHhilxOTM0T0l1FrdBS1aUWG4GE5FtaLMyUXZUlz8zM0UnSCs0lDM5RDVFRzJDBzZmOoRkNFdEt6YwNKTkTXA1ZFVXXJT0UlNElD5fTDRTRU5TdU1YdROEL2xUhvV3OLY3bTVmhMpZUJU2QXF2I1VYdxTjWVVm9jZKUPWUMXFHNrFJeJZDU29mM2hKQpUmUVJGIxwwOVSUaXFXQy9JU4cFdlkGJQY4SCYWYjFkJndiaCVFMlNk1QZQTwQWRDJ0th1YMwaHYmEzQrB2aWTTRmpgpOA5dfVkRVd0lPVSUMSUTl9kNFNFTyaWPU0G5mNwaieUdUJ0NiZjNVbnOC90tNYyb2S3djNmh4BidmNlRnBk1PdCShdDYUdS9mNMSiMnYzVml2pSameEY2NStCZtRvU3dloGgvQyU0TmcTlTlMJVYhc3VEp01EpYRwUGNUUWZ5daVxU3blZzg3dnR2UncnR2U4RkU2CkSUc19W5FVTSURVJ0xJ9OXOQ0Q0VG1iU9WFcDYWNktah2M3blbjNlZsZndjOTdXFTFHNQS0SzUE4FVpdqR1dEL2RWgvpMSabVZkJlhRJFRMZUTUJ0NGdwUGbGZitjFE01bZSlRzZHZ4RMUFd2cHhEZqtZbwR2clQUgxFsb0Z1ZFVW5tJvUwWEd2gjVog1eKYUaUdHYK5JUXX1TkVlNJZFUfTET05U5DlDRTVURV9jJi49SmbHR2pU5UFwVUK0ZTBFE5pmd3ZGOEdXppU1VTNEb09UtntCRycUaHUmR1ovVYNGUU1HFy1vVsZXYlBmt1lsNVZDVUNUNyJQeHUXZlF2pzVMVlVjVmNWx2VxWZaUSEh1FlJ0bmRmbENkRV9VWiRVT3R01apMUIMkWjdW8K5JTXX1TkVlNJZFUfTET05U5DlDRTVURV9Ulw49bhMWcW5HdRdZNaK0UEp3AyU0TwY0Wm1md6tDMpN0cDRVBK11aKR0VTZkI3RocKUXRm5Vl6llcpMWaXJXRLZOOaZDOGZ1R0gychWURVJk5JR5VNYXQTl250dGYheFOG1FZog4RFZWMkRmtK1QRCaUclJlNYNFRHSWTFpFUKxYRXX1TkVlNJZFUfTET05U5DlDRTVURV9Hho49T5aTempVMwFhU1b1Vi9HR4YzO1dDNks05HhEQxY2VUZXlMNybyVmVEl3dNlLbYSGbGlWxMVNWUcEZXpVN0w5NVZDVGFUw4NMSUOWbkRjV21QaMbESVhGx1w3MiY1WmJXB6o0NjS1T2tWxjNSeRY0UzV0g2VhR5Z0RWlzkKRMdXX1TkVlNJZFUfTET05U5DlDRTVURV9Hdt49apUHZVNnhlpxQ5MENGNnh1VYN3aDQ2QW5qRqd4K1cXYk9ZdHW4VzeE9XVHB6YmM3Wk1DYwVLdqS1aTNUtjhINicVeUV1JBZRZxTGYWdTVytuepR1QVVXZlNoSVOFdVlVkzRqdPcjOW9HBll6Ota2dHFGV6dtN6c1ekN2UKdwc
|
||||
@@ -41,24 +41,26 @@ public class CorporateLoanPricingCreateDTO implements Serializable {
|
||||
@NotBlank(message = "申请金额不能为空")
|
||||
private String applyAmt;
|
||||
|
||||
@Schema(description = "贷款期限", example = "36")
|
||||
@Schema(description = "还款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "分期", allowableValues = {"分期", "不分期"})
|
||||
@NotBlank(message = "还款方式不能为空")
|
||||
@Pattern(regexp = "^(分期|不分期)$", message = "还款方式必须是:分期、不分期之一")
|
||||
private String repayMethod;
|
||||
|
||||
@Schema(description = "借款期限(年)", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
@NotBlank(message = "贷款期限不能为空")
|
||||
private String loanTerm;
|
||||
|
||||
@Schema(description = "省农担担保贷款", example = "false")
|
||||
private String isAgriGuar;
|
||||
|
||||
@Schema(description = "绿色贷款", example = "true")
|
||||
@Schema(description = "绿色贷款", example = "0")
|
||||
private String isGreenLoan;
|
||||
|
||||
@Schema(description = "科技型企业", example = "true")
|
||||
private String isTechEnt;
|
||||
@Schema(description = "贸易和建筑业企业标识", example = "0")
|
||||
private String isTradeBuildEnt;
|
||||
|
||||
@Schema(description = "贸易和建筑业企业标识", example = "false")
|
||||
private String isTradeConstruction;
|
||||
|
||||
@Schema(description = "抵质押类型", example = "一类")
|
||||
@Schema(description = "抵质押类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "一类", allowableValues = {"一类", "二类", "三类", "四类"})
|
||||
@NotBlank(message = "抵质押类型不能为空")
|
||||
@Pattern(regexp = "^(一类|二类|三类|四类)$", message = "抵质押类型必须是:一类、二类、三类、四类之一")
|
||||
private String collType;
|
||||
|
||||
@Schema(description = "抵质押物是否三方所有", example = "false")
|
||||
@Schema(description = "抵质押物是否三方所有", example = "0")
|
||||
private String collThirdParty;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ModelInvokeDTO {
|
||||
|
||||
/**
|
||||
* 机构编码(必填)
|
||||
* 固定值:931000
|
||||
* 固定值:892000
|
||||
*/
|
||||
private String orgCode;
|
||||
|
||||
@@ -73,6 +73,18 @@ public class ModelInvokeDTO {
|
||||
*/
|
||||
private String applyAmt;
|
||||
|
||||
/**
|
||||
* 贷款期限(必填)
|
||||
* 单位:年
|
||||
*/
|
||||
private String loanTerm;
|
||||
|
||||
/**
|
||||
* 还款方式(非必填)
|
||||
* 可选值:分期/不分期
|
||||
*/
|
||||
private String repayMethod;
|
||||
|
||||
/**
|
||||
* 净身企业(非必填)
|
||||
* 可选值:true/false
|
||||
@@ -92,10 +104,10 @@ public class ModelInvokeDTO {
|
||||
private String isManufacturing;
|
||||
|
||||
/**
|
||||
* 省农担担保贷款(非必填)
|
||||
* 可选值:true/false
|
||||
* 绿色贷款(非必填)
|
||||
* 可选值:0/1
|
||||
*/
|
||||
private String isAgriGuar;
|
||||
private String isGreenLoan;
|
||||
|
||||
/**
|
||||
* 是否纳税信用等级A级(非必填)
|
||||
@@ -119,19 +131,25 @@ public class ModelInvokeDTO {
|
||||
|
||||
/**
|
||||
* 是否有经营佐证(非必填)
|
||||
* 可选值:true/false
|
||||
* 可选值:0/1
|
||||
*/
|
||||
private String bizProof;
|
||||
|
||||
/**
|
||||
* 循环功能(非必填)
|
||||
* 可选值:0/1
|
||||
*/
|
||||
private String loanLoop;
|
||||
|
||||
/**
|
||||
* 抵质押类型(非必填)
|
||||
* 可选值:抵押/质押
|
||||
* 可选值:一类/二类/三类/四类
|
||||
*/
|
||||
private String collType;
|
||||
|
||||
/**
|
||||
* 抵质押物是否三方所有(非必填)
|
||||
* 可选值:true/false
|
||||
* 可选值:0/1
|
||||
*/
|
||||
private String collThirdParty;
|
||||
|
||||
@@ -155,4 +173,10 @@ public class ModelInvokeDTO {
|
||||
*/
|
||||
private String idNum;
|
||||
|
||||
/**
|
||||
* 贸易和建筑业企业(非必填)
|
||||
* 可选值:0/1
|
||||
*/
|
||||
private String isTradeBuildEnt;
|
||||
|
||||
}
|
||||
|
||||
@@ -41,15 +41,24 @@ public class PersonalLoanPricingCreateDTO implements Serializable {
|
||||
@NotBlank(message = "申请金额不能为空")
|
||||
private String applyAmt;
|
||||
|
||||
@Schema(description = "是否有经营佐证", example = "true")
|
||||
@Schema(description = "贷款用途", requiredMode = Schema.RequiredMode.REQUIRED, example = "business", allowableValues = {"consumer", "business"})
|
||||
@NotBlank(message = "贷款用途不能为空")
|
||||
@Pattern(regexp = "^(consumer|business)$", message = "贷款用途必须是:consumer、business之一")
|
||||
private String loanPurpose;
|
||||
|
||||
@Schema(description = "借款期限(年)", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
@NotBlank(message = "借款期限不能为空")
|
||||
private String loanTerm;
|
||||
|
||||
@Schema(description = "是否有经营佐证", example = "1")
|
||||
private String bizProof;
|
||||
|
||||
@Schema(description = "循环功能", example = "false")
|
||||
@Schema(description = "循环功能", example = "0")
|
||||
private String loanLoop;
|
||||
|
||||
@Schema(description = "抵质押类型", example = "一类")
|
||||
private String collType;
|
||||
|
||||
@Schema(description = "抵质押物是否三方所有", example = "false")
|
||||
@Schema(description = "抵质押物是否三方所有", example = "0")
|
||||
private String collThirdParty;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ public class LoanPricingWorkflow implements Serializable
|
||||
*/
|
||||
private String loanTerm;
|
||||
|
||||
/**
|
||||
* 还款方式: 分期/不分期
|
||||
*/
|
||||
private String repayMethod;
|
||||
|
||||
/** 净身企业: true/false */
|
||||
private String isCleanEnt;
|
||||
|
||||
@@ -78,24 +83,17 @@ public class LoanPricingWorkflow implements Serializable
|
||||
/** 制造业企业: true/false */
|
||||
private String isManufacturing;
|
||||
|
||||
/** 省农担担保贷款: true/false */
|
||||
private String isAgriGuar;
|
||||
|
||||
/**
|
||||
* 贸易和建筑业企业标识: true/false
|
||||
*/
|
||||
private String isTradeConstruction;
|
||||
@TableField("is_trade_construction")
|
||||
private String isTradeBuildEnt;
|
||||
|
||||
/**
|
||||
* 绿色贷款: true/false
|
||||
*/
|
||||
private String isGreenLoan;
|
||||
|
||||
/**
|
||||
* 科技型企业: true/false
|
||||
*/
|
||||
private String isTechEnt;
|
||||
|
||||
/** 是否纳税信用等级A级: true/false */
|
||||
private String isTaxA;
|
||||
|
||||
@@ -111,7 +109,7 @@ public class LoanPricingWorkflow implements Serializable
|
||||
/** 循环功能: true/false */
|
||||
private String loanLoop;
|
||||
|
||||
/** 抵质押类型: 一线/一类/二类 */
|
||||
/** 抵质押类型: 一类/二类/三类/四类 */
|
||||
private String collType;
|
||||
|
||||
/** 抵质押物是否三方所有: true/false */
|
||||
|
||||
@@ -31,6 +31,8 @@ public class ModelCorpOutputFields {
|
||||
private String idType;
|
||||
// 证件号码
|
||||
private String idNum;
|
||||
// 还款方式
|
||||
private String repayMethod;
|
||||
// 基准利率
|
||||
private String baseLoanRate;
|
||||
// 我行首贷客户
|
||||
@@ -65,8 +67,6 @@ public class ModelCorpOutputFields {
|
||||
private String midEntEleDdc;
|
||||
// 中间业务_企业_水费代扣
|
||||
private String midEntWaterDdc;
|
||||
// 中间业务_企业_税务代扣
|
||||
private String midEntTax;
|
||||
// BP_中间业务
|
||||
private String bpMid;
|
||||
// 代发工资户数
|
||||
@@ -79,12 +79,12 @@ public class ModelCorpOutputFields {
|
||||
private String isCleanEnt;
|
||||
// 开立基本结算账户
|
||||
private String hasSettleAcct;
|
||||
// 省农担担保贷款
|
||||
private String isAgriGuar;
|
||||
// 绿色贷款
|
||||
private String isGreenLoan;
|
||||
// 科技型企业
|
||||
private String isTechEnt;
|
||||
// 贸易和建筑业企业
|
||||
private String isTradeBuildEnt;
|
||||
// BP_企业客户类别
|
||||
private String bpEntType;
|
||||
// TOTAL_BP_关联度
|
||||
@@ -109,8 +109,6 @@ public class ModelCorpOutputFields {
|
||||
private String prinOverdue;
|
||||
// 利息逾期
|
||||
private String interestOverdue;
|
||||
// 信用卡逾期
|
||||
private String cardOverdue;
|
||||
// BP_灰名单与逾期
|
||||
private String bpGreyOverdue;
|
||||
// TOTAL_BP_风险度
|
||||
@@ -119,6 +117,16 @@ public class ModelCorpOutputFields {
|
||||
private String totalBp;
|
||||
// 测算利率
|
||||
private String calculateRate;
|
||||
// 历史利率
|
||||
private String loanRateHistory;
|
||||
// 产品最低利率下限
|
||||
private String minRateProduct;
|
||||
// 平滑幅度
|
||||
private String smoothRange;
|
||||
// 最终测算利率
|
||||
private String finalCalculateRate;
|
||||
// 参考利率
|
||||
private String referenceRate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
|
||||
@@ -60,6 +60,14 @@ public class LoanPricingModelService {
|
||||
}
|
||||
ModelInvokeDTO modelInvokeDTO = new ModelInvokeDTO();
|
||||
BeanUtils.copyProperties(loanPricingWorkflow, modelInvokeDTO);
|
||||
if ("个人".equals(loanPricingWorkflow.getCustType()))
|
||||
{
|
||||
normalizePersonalModelInvokeDTO(modelInvokeDTO);
|
||||
}
|
||||
if ("企业".equals(loanPricingWorkflow.getCustType()))
|
||||
{
|
||||
normalizeCorporateModelInvokeDTO(modelInvokeDTO);
|
||||
}
|
||||
JSONObject response = modelService.invokeModel(modelInvokeDTO);
|
||||
if (loanPricingWorkflow.getCustType().equals("个人")){
|
||||
// 个人模型
|
||||
@@ -83,4 +91,31 @@ public class LoanPricingModelService {
|
||||
log.info("更新流程信息成功");
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizePersonalModelInvokeDTO(ModelInvokeDTO modelInvokeDTO)
|
||||
{
|
||||
modelInvokeDTO.setBizProof(toZeroOne(modelInvokeDTO.getBizProof()));
|
||||
modelInvokeDTO.setLoanLoop(toZeroOne(modelInvokeDTO.getLoanLoop()));
|
||||
modelInvokeDTO.setCollThirdParty(toZeroOne(modelInvokeDTO.getCollThirdParty()));
|
||||
}
|
||||
|
||||
private void normalizeCorporateModelInvokeDTO(ModelInvokeDTO modelInvokeDTO)
|
||||
{
|
||||
modelInvokeDTO.setCollThirdParty(toZeroOne(modelInvokeDTO.getCollThirdParty()));
|
||||
modelInvokeDTO.setIsGreenLoan(toZeroOne(modelInvokeDTO.getIsGreenLoan()));
|
||||
modelInvokeDTO.setIsTradeBuildEnt(toZeroOne(modelInvokeDTO.getIsTradeBuildEnt()));
|
||||
}
|
||||
|
||||
private String toZeroOne(String value)
|
||||
{
|
||||
if ("true".equals(value) || "1".equals(value))
|
||||
{
|
||||
return "1";
|
||||
}
|
||||
if ("false".equals(value) || "0".equals(value))
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
if (Objects.nonNull(modelRetailOutputFields))
|
||||
{
|
||||
maskModelRetailOutputBasicInfo(modelRetailOutputFields);
|
||||
loanPricingWorkflow.setLoanRate(modelRetailOutputFields.getCalculateRate());
|
||||
loanPricingWorkflow.setLoanRate(modelRetailOutputFields.getFinalCalculateRate());
|
||||
}
|
||||
loanPricingWorkflowVO.setModelRetailOutputFields(modelRetailOutputFields);
|
||||
}
|
||||
@@ -181,7 +181,7 @@ public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowServi
|
||||
if (Objects.nonNull(modelCorpOutputFields))
|
||||
{
|
||||
maskModelCorpOutputBasicInfo(modelCorpOutputFields);
|
||||
loanPricingWorkflow.setLoanRate(modelCorpOutputFields.getCalculateRate());
|
||||
loanPricingWorkflow.setLoanRate(modelCorpOutputFields.getFinalCalculateRate());
|
||||
}
|
||||
loanPricingWorkflowVO.setModelCorpOutputFields(modelCorpOutputFields);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ public class LoanPricingConverter {
|
||||
entity.setIdNum(dto.getIdNum());
|
||||
entity.setGuarType(dto.getGuarType());
|
||||
entity.setApplyAmt(dto.getApplyAmt());
|
||||
entity.setLoanPurpose(dto.getLoanPurpose());
|
||||
entity.setLoanTerm(dto.getLoanTerm());
|
||||
entity.setCollType(dto.getCollType());
|
||||
entity.setCollThirdParty(dto.getCollThirdParty());
|
||||
// 映射个人特有字段
|
||||
@@ -52,13 +54,11 @@ public class LoanPricingConverter {
|
||||
entity.setIdNum(dto.getIdNum());
|
||||
entity.setGuarType(dto.getGuarType());
|
||||
entity.setApplyAmt(dto.getApplyAmt());
|
||||
entity.setRepayMethod(dto.getRepayMethod());
|
||||
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.setIsTradeBuildEnt(dto.getIsTradeBuildEnt());
|
||||
entity.setLoanTerm(dto.getLoanTerm());
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -3,66 +3,70 @@
|
||||
"cost": 2267,
|
||||
"tokenId": "17364055486305E7F4722M8IPFWNL8TOBEB",
|
||||
"mappingOutputFields": {
|
||||
"custIsn": "CUST20260121001",
|
||||
"custType": "企业客户",
|
||||
"guarType": "抵押担保",
|
||||
"custName": "北京智联科技有限公司",
|
||||
"idType": "营业执照",
|
||||
"idNum": "91110108MA00XXXXXX",
|
||||
"baseLoanRate": "3.45",
|
||||
"isFirstLoan": "N",
|
||||
"faithDay": "730",
|
||||
"bpFirstLoan": "0",
|
||||
"bpAgeLoan": "5.2",
|
||||
"totalBpLoyalty": "8.5",
|
||||
"balanceAvg": "5000000.00",
|
||||
"loanAvg": "3000000.00",
|
||||
"derivationRate": "1.8",
|
||||
"totalBpContribution": "12.3",
|
||||
"midEntConnect": "100000.00",
|
||||
"midEntEffect": "50000.00",
|
||||
"midEntInter": "80000.00",
|
||||
"midEntAccept": "200000.00",
|
||||
"midEntDiscount": "150000.00",
|
||||
"midEntEleDdc": "30000.00",
|
||||
"midEntWaterDdc": "10000.00",
|
||||
"midEntTax": "40000.00",
|
||||
"bpMid": "6.8",
|
||||
"payroll": "200",
|
||||
"invLoanAmount": "2500000.00",
|
||||
"bpPayroll": "4.1",
|
||||
"isCleanEnt": "Y",
|
||||
"hasSettleAcct": "Y",
|
||||
"isAgriGuar": "N",
|
||||
"isGreenLoan": "Y",
|
||||
"isTechEnt": "Y",
|
||||
"bpEntType": "7.5",
|
||||
"totoalBpRelevance": "9.2",
|
||||
"loanTerm": "36",
|
||||
"bpLoanTerm": "3.3",
|
||||
"applyAmt": "5000000.00",
|
||||
"bpLoanAmount": "5.8",
|
||||
"collType": "房产抵押",
|
||||
"collThirdParty": "N",
|
||||
"bpCollateral": "4.5",
|
||||
"greyCust": "N",
|
||||
"prinOverdue": "N",
|
||||
"interestOverdue": "N",
|
||||
"cardOverdue": "N",
|
||||
"bpGreyOverdue": "0",
|
||||
"totoalBpRisk": "1.2",
|
||||
"totalBp": "48.2",
|
||||
"calculateRate": "3.932"
|
||||
"custIsn": "CUST20260121001",
|
||||
"custType": "企业",
|
||||
"guarType": "抵押",
|
||||
"custName": "北京智联科技有限公司",
|
||||
"idType": "统一社会信用代码",
|
||||
"idNum": "91110108MA00XXXXXX",
|
||||
"repayMethod": "分期",
|
||||
"loanTerm": "3",
|
||||
"isFirstLoan": "0",
|
||||
"faithDay": "730",
|
||||
"bpFirstLoan": "0",
|
||||
"bpAgeLoan": "5.2",
|
||||
"totalBpLoyalty": "8.5",
|
||||
"balanceAvg": "5000000.00",
|
||||
"loanAvg": "3000000.00",
|
||||
"derivationRate": "1.8",
|
||||
"totalBpContribution": "12.3",
|
||||
"midEntConnect": "1",
|
||||
"midEntEffect": "1",
|
||||
"midEntInter": "1",
|
||||
"midEntAccept": "1",
|
||||
"midEntDiscount": "1",
|
||||
"midEntEleDdc": "1",
|
||||
"midEntWaterDdc": "1",
|
||||
"bpMid": "6.8",
|
||||
"payroll": "200",
|
||||
"invLoanAmount": "2500000.00",
|
||||
"bpPayroll": "4.1",
|
||||
"isCleanEnt": "1",
|
||||
"hasSettleAcct": "1",
|
||||
"isTradeBuildEnt": "0",
|
||||
"isGreenLoan": "1",
|
||||
"isTechEnt": "1",
|
||||
"bpEntType": "7.5",
|
||||
"totoalBpRelevance": "9.2",
|
||||
"bpLoanTerm": "3.3",
|
||||
"applyAmt": "5000000.00",
|
||||
"bpLoanAmount": "5.8",
|
||||
"collType": "一类",
|
||||
"collThirdParty": "0",
|
||||
"bpCollateral": "4.5",
|
||||
"greyCust": "0",
|
||||
"prinOverdue": "0",
|
||||
"interestOverdue": "0",
|
||||
"bpGreyOverdue": "0",
|
||||
"totoalBpRisk": "1.2",
|
||||
"totalBp": "48.2",
|
||||
"baseLoanRate": "3.45",
|
||||
"calculateRate": "3.932",
|
||||
"loanRateHistory": "4.20",
|
||||
"minRateProduct": "3.10",
|
||||
"smoothRange": "-0.20",
|
||||
"finalCalculateRate": "3.732",
|
||||
"referenceRate": "3.432"
|
||||
},
|
||||
"extensionMap": {},
|
||||
"reasonMessage": "Running successfully",
|
||||
"bizTime": 1736405548630,
|
||||
"outputFields": {},
|
||||
"workflowCode": "TBKH",
|
||||
"orgCode": "802000",
|
||||
"orgCode": "892000",
|
||||
"bizId": "2025010914345",
|
||||
"reasonCode": 200,
|
||||
"workflowVersion": 14,
|
||||
"callTime": 1736405548630,
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
lpw.guar_type AS guarType,
|
||||
lpw.apply_amt AS applyAmt,
|
||||
CASE
|
||||
WHEN lpw.cust_type = '个人' THEN mr.calculate_rate
|
||||
WHEN lpw.cust_type = '个人' THEN mr.final_calculate_rate
|
||||
WHEN lpw.cust_type = '企业' THEN mc.calculate_rate
|
||||
ELSE NULL
|
||||
END AS calculateRate,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ruoyi.loanpricing.mapper;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
class LoanPricingWorkflowMapperXmlTest
|
||||
{
|
||||
@Test
|
||||
void shouldUseRetailFinalCalculateRateInWorkflowListQuery() throws IOException
|
||||
{
|
||||
ClassPathResource resource = new ClassPathResource("mapper/loanpricing/LoanPricingWorkflowMapper.xml");
|
||||
String xml = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
|
||||
|
||||
assertTrue(xml.contains("WHEN lpw.cust_type = '个人' THEN mr.final_calculate_rate"));
|
||||
assertTrue(xml.contains("WHEN lpw.cust_type = '企业' THEN mc.calculate_rate"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.ruoyi.loanpricing.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.loanpricing.domain.dto.ModelInvokeDTO;
|
||||
import com.ruoyi.loanpricing.domain.dto.PersonalLoanPricingCreateDTO;
|
||||
import com.ruoyi.loanpricing.domain.entity.LoanPricingWorkflow;
|
||||
import com.ruoyi.loanpricing.mapper.LoanPricingWorkflowMapper;
|
||||
import com.ruoyi.loanpricing.mapper.ModelCorpOutputFieldsMapper;
|
||||
import com.ruoyi.loanpricing.mapper.ModelRetailOutputFieldsMapper;
|
||||
import com.ruoyi.loanpricing.util.LoanPricingConverter;
|
||||
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 java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LoanPricingModelServicePersonalParamsTest {
|
||||
|
||||
@Mock
|
||||
private ModelService modelService;
|
||||
|
||||
@Mock
|
||||
private LoanPricingWorkflowMapper loanPricingWorkflowMapper;
|
||||
|
||||
@Mock
|
||||
private ModelRetailOutputFieldsMapper modelRetailOutputFieldsMapper;
|
||||
|
||||
@Mock
|
||||
private ModelCorpOutputFieldsMapper modelCorpOutputFieldsMapper;
|
||||
|
||||
@Mock
|
||||
private SensitiveFieldCryptoService sensitiveFieldCryptoService;
|
||||
|
||||
@InjectMocks
|
||||
private LoanPricingModelService loanPricingModelService;
|
||||
|
||||
@Test
|
||||
void shouldContainLoanPurposeAndLoanTermInPersonalCreateDto() throws NoSuchFieldException {
|
||||
assertNotNull(PersonalLoanPricingCreateDTO.class.getDeclaredField("loanPurpose"));
|
||||
assertNotNull(PersonalLoanPricingCreateDTO.class.getDeclaredField("loanTerm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMapLoanPurposeAndLoanTermFromPersonalDto() {
|
||||
PersonalLoanPricingCreateDTO dto = new PersonalLoanPricingCreateDTO();
|
||||
dto.setCustIsn("CUST001");
|
||||
dto.setCustName("张三");
|
||||
dto.setGuarType("信用");
|
||||
dto.setApplyAmt("100000");
|
||||
dto.setLoanPurpose("business");
|
||||
dto.setLoanTerm("3");
|
||||
|
||||
LoanPricingWorkflow workflow = LoanPricingConverter.toEntity(dto);
|
||||
|
||||
assertEquals("business", workflow.getLoanPurpose());
|
||||
assertEquals("3", workflow.getLoanTerm());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldContainLoanTermAndLoanLoopInModelInvokeDto() throws NoSuchFieldException {
|
||||
assertNotNull(ModelInvokeDTO.class.getDeclaredField("loanTerm"));
|
||||
assertNotNull(ModelInvokeDTO.class.getDeclaredField("loanLoop"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInvokePersonalModelWithExpectedParams() {
|
||||
LoanPricingWorkflow workflow = new LoanPricingWorkflow();
|
||||
workflow.setId(1L);
|
||||
workflow.setSerialNum("202604090001");
|
||||
workflow.setOrgCode("892000");
|
||||
workflow.setRunType("1");
|
||||
workflow.setCustIsn("CUST001");
|
||||
workflow.setCustType("个人");
|
||||
workflow.setCustName("cipher-name");
|
||||
workflow.setIdType("身份证");
|
||||
workflow.setIdNum("cipher-id");
|
||||
workflow.setGuarType("信用");
|
||||
workflow.setApplyAmt("100000");
|
||||
workflow.setLoanPurpose("business");
|
||||
workflow.setLoanTerm("3");
|
||||
workflow.setBizProof("true");
|
||||
workflow.setLoanLoop("false");
|
||||
workflow.setCollThirdParty("true");
|
||||
workflow.setCollType("一类");
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("calculateRate", "6.15");
|
||||
|
||||
when(loanPricingWorkflowMapper.selectById(1L)).thenReturn(workflow);
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("张三");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("110101199001011234");
|
||||
when(modelService.invokeModel(any())).thenReturn(response);
|
||||
|
||||
loanPricingModelService.invokeModelAsync(1L);
|
||||
|
||||
verify(modelService).invokeModel(argThat((ModelInvokeDTO dto) ->
|
||||
Objects.equals("202604090001", dto.getSerialNum())
|
||||
&& Objects.equals("892000", dto.getOrgCode())
|
||||
&& Objects.equals("1", dto.getRunType())
|
||||
&& Objects.equals("CUST001", dto.getCustIsn())
|
||||
&& Objects.equals("个人", dto.getCustType())
|
||||
&& Objects.equals("张三", dto.getCustName())
|
||||
&& Objects.equals("身份证", dto.getIdType())
|
||||
&& Objects.equals("110101199001011234", dto.getIdNum())
|
||||
&& Objects.equals("信用", dto.getGuarType())
|
||||
&& Objects.equals("100000", dto.getApplyAmt())
|
||||
&& Objects.equals("business", dto.getLoanPurpose())
|
||||
&& Objects.equals("3", dto.getLoanTerm())
|
||||
&& Objects.equals("1", dto.getBizProof())
|
||||
&& Objects.equals("0", dto.getLoanLoop())
|
||||
&& Objects.equals("1", dto.getCollThirdParty())
|
||||
&& Objects.equals("一类", dto.getCollType())));
|
||||
}
|
||||
}
|
||||
@@ -87,4 +87,34 @@ class LoanPricingModelServiceTest
|
||||
!Objects.equals("张三", entity.getCustName())
|
||||
&& !Objects.equals("110101199001011234", entity.getIdNum())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNormalizeCorporateUploadParamsBeforeInvokeModel()
|
||||
{
|
||||
LoanPricingWorkflow workflow = new LoanPricingWorkflow();
|
||||
workflow.setId(3L);
|
||||
workflow.setCustType("企业");
|
||||
workflow.setCustName("cipher-name");
|
||||
workflow.setIdNum("cipher-id");
|
||||
workflow.setRepayMethod("分期");
|
||||
workflow.setIsGreenLoan("true");
|
||||
workflow.setIsTradeBuildEnt("false");
|
||||
workflow.setCollThirdParty("true");
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("calculateRate", "4.15");
|
||||
|
||||
when(loanPricingWorkflowMapper.selectById(3L)).thenReturn(workflow);
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-name")).thenReturn("测试公司");
|
||||
when(sensitiveFieldCryptoService.decrypt("cipher-id")).thenReturn("91330000123456789X");
|
||||
when(modelService.invokeModel(any())).thenReturn(response);
|
||||
|
||||
loanPricingModelService.invokeModelAsync(3L);
|
||||
|
||||
verify(modelService).invokeModel(argThat((ModelInvokeDTO dto) ->
|
||||
Objects.equals("分期", dto.getRepayMethod())
|
||||
&& Objects.equals("1", dto.getIsGreenLoan())
|
||||
&& Objects.equals("0", dto.getIsTradeBuildEnt())
|
||||
&& Objects.equals("1", dto.getCollThirdParty())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class LoanPricingWorkflowServiceImplTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseRetailModelOutputCalculateRateForWorkflowDetail()
|
||||
void shouldUseRetailModelOutputFinalCalculateRateForWorkflowDetail()
|
||||
{
|
||||
LoanPricingWorkflow workflow = new LoanPricingWorkflow();
|
||||
workflow.setSerialNum("P20260328001");
|
||||
@@ -144,14 +144,16 @@ class LoanPricingWorkflowServiceImplTest
|
||||
|
||||
ModelRetailOutputFields retailOutputFields = new ModelRetailOutputFields();
|
||||
retailOutputFields.setCalculateRate("6.15");
|
||||
retailOutputFields.setFinalCalculateRate("6.05");
|
||||
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
when(modelRetailOutputFieldsMapper.selectById(11L)).thenReturn(retailOutputFields);
|
||||
|
||||
LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("P20260328001");
|
||||
|
||||
assertEquals("6.15", result.getLoanPricingWorkflow().getLoanRate());
|
||||
assertEquals("6.05", result.getLoanPricingWorkflow().getLoanRate());
|
||||
assertEquals("6.15", result.getModelRetailOutputFields().getCalculateRate());
|
||||
assertEquals("6.05", result.getModelRetailOutputFields().getFinalCalculateRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,14 +216,16 @@ class LoanPricingWorkflowServiceImplTest
|
||||
|
||||
ModelCorpOutputFields corpOutputFields = new ModelCorpOutputFields();
|
||||
corpOutputFields.setCalculateRate("3.932");
|
||||
corpOutputFields.setFinalCalculateRate("3.652");
|
||||
|
||||
when(loanPricingWorkflowMapper.selectOne(any())).thenReturn(workflow);
|
||||
when(modelCorpOutputFieldsMapper.selectById(22L)).thenReturn(corpOutputFields);
|
||||
|
||||
LoanPricingWorkflowVO result = loanPricingWorkflowService.selectLoanPricingBySerialNum("C20260328001");
|
||||
|
||||
assertEquals("3.932", result.getLoanPricingWorkflow().getLoanRate());
|
||||
assertEquals("3.652", result.getLoanPricingWorkflow().getLoanRate());
|
||||
assertEquals("3.932", result.getModelCorpOutputFields().getCalculateRate());
|
||||
assertEquals("3.652", result.getModelCorpOutputFields().getFinalCalculateRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Binary file not shown.
@@ -10,7 +10,12 @@
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview",
|
||||
"test:password-transfer": "node tests/password-transfer-api.test.js",
|
||||
"test:retail-display-fields": "node tests/retail-display-fields.test.js"
|
||||
"test:retail-display-fields": "node tests/retail-display-fields.test.js",
|
||||
"test:model-output-flat-display": "node tests/model-output-flat-display.test.js",
|
||||
"test:personal-create-input-params": "node tests/personal-create-input-params.test.js",
|
||||
"test:id-number-validation-removal": "node tests/id-number-validation-removal.test.js",
|
||||
"test:corporate-create-input-params": "node tests/corporate-create-input-params.test.js",
|
||||
"test:corporate-display-fields": "node tests/corporate-display-fields.test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
|
||||
@@ -53,8 +53,18 @@
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="贷款期限(月)" prop="loanTerm">
|
||||
<el-input v-model.number="form.loanTerm" type="number" placeholder="请输入贷款期限"/>
|
||||
<el-form-item label="贷款期限(年)" prop="loanTerm">
|
||||
<el-select v-model="form.loanTerm" placeholder="请选择贷款期限" style="width: 100%">
|
||||
<el-option v-for="item in loanTermOptions" :key="item" :label="item" :value="item"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="还款方式" prop="repayMethod">
|
||||
<el-select v-model="form.repayMethod" placeholder="请选择还款方式" style="width: 100%">
|
||||
<el-option label="分期" value="分期"/>
|
||||
<el-option label="不分期" value="不分期"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -62,26 +72,14 @@
|
||||
<!-- 企业标识 -->
|
||||
<el-divider content-position="left">企业标识</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="省农担担保贷款" prop="isAgriGuar">
|
||||
<el-switch v-model="form.isAgriGuar"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="绿色贷款" prop="isGreenLoan">
|
||||
<el-switch v-model="form.isGreenLoan"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="科技型企业" prop="isTechEnt">
|
||||
<el-switch v-model="form.isTechEnt"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="贸易和建筑业企业" prop="isTradeConstruction">
|
||||
<el-switch v-model="form.isTradeConstruction"/>
|
||||
<el-form-item label="贸易和建筑业企业" prop="isTradeBuildEnt">
|
||||
<el-switch v-model="form.isTradeBuildEnt"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -92,9 +90,10 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抵质押类型" prop="collType">
|
||||
<el-select v-model="form.collType" placeholder="请选择抵质押类型" style="width: 100%">
|
||||
<el-option label="一线" value="一线"/>
|
||||
<el-option label="一类" value="一类"/>
|
||||
<el-option label="二类" value="二类"/>
|
||||
<el-option label="三类" value="三类"/>
|
||||
<el-option label="四类" value="四类"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -124,22 +123,6 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
// 统一社会信用代码验证
|
||||
const validateIdNum = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入证件号码'))
|
||||
} else if (this.form.idType === '统一社会信用代码') {
|
||||
const reg = /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error('请输入正确的统一社会信用代码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 金额验证
|
||||
const validateApplyAmt = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
@@ -164,8 +147,6 @@ export default {
|
||||
const num = parseInt(value)
|
||||
if (isNaN(num) || num <= 0) {
|
||||
callback(new Error('请输入有效的贷款期限'))
|
||||
} else if (num > 360) {
|
||||
callback(new Error('贷款期限不能超过 360 个月'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
@@ -173,9 +154,12 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
loanTermOptions: [
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10'
|
||||
],
|
||||
submitting: false,
|
||||
form: {
|
||||
orgCode: '',
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
@@ -184,10 +168,9 @@ export default {
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanTerm: undefined,
|
||||
isAgriGuar: false,
|
||||
repayMethod: undefined,
|
||||
isGreenLoan: false,
|
||||
isTechEnt: false,
|
||||
isTradeConstruction: false,
|
||||
isTradeBuildEnt: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
},
|
||||
@@ -204,7 +187,7 @@ export default {
|
||||
{required: true, message: "请选择证件类型", trigger: "change"}
|
||||
],
|
||||
idNum: [
|
||||
{required: true, validator: validateIdNum, trigger: "blur"}
|
||||
{required: true, message: "证件号码不能为空", trigger: "blur"}
|
||||
],
|
||||
guarType: [
|
||||
{required: true, message: "请选择担保方式", trigger: "change"}
|
||||
@@ -213,7 +196,10 @@ export default {
|
||||
{required: true, validator: validateApplyAmt, trigger: "blur"}
|
||||
],
|
||||
loanTerm: [
|
||||
{required: true, validator: validateLoanTerm, trigger: "blur"}
|
||||
{required: true, validator: validateLoanTerm, trigger: "change"}
|
||||
],
|
||||
repayMethod: [
|
||||
{required: true, message: "请选择还款方式", trigger: "change"}
|
||||
],
|
||||
collType: [
|
||||
{required: true, message: "请选择抵质押类型", trigger: "change"}
|
||||
@@ -242,7 +228,7 @@ export default {
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
orgCode: '',
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
@@ -251,10 +237,9 @@ export default {
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanTerm: undefined,
|
||||
isAgriGuar: false,
|
||||
repayMethod: undefined,
|
||||
isGreenLoan: false,
|
||||
isTechEnt: false,
|
||||
isTradeConstruction: false,
|
||||
isTradeBuildEnt: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
}
|
||||
@@ -278,11 +263,9 @@ export default {
|
||||
// 转换开关值为字符串
|
||||
const data = {
|
||||
...this.form,
|
||||
isAgriGuar: this.form.isAgriGuar ? 'true' : 'false',
|
||||
isGreenLoan: this.form.isGreenLoan ? 'true' : 'false',
|
||||
isTechEnt: this.form.isTechEnt ? 'true' : 'false',
|
||||
isTradeConstruction: this.form.isTradeConstruction ? 'true' : 'false',
|
||||
collThirdParty: this.form.collThirdParty ? 'true' : 'false'
|
||||
isGreenLoan: this.form.isGreenLoan ? '1' : '0',
|
||||
isTradeBuildEnt: this.form.isTradeBuildEnt ? '1' : '0',
|
||||
collThirdParty: this.form.collThirdParty ? '1' : '0'
|
||||
}
|
||||
|
||||
createCorporateWorkflow(data).then(response => {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<el-descriptions-item label="浮动BP">
|
||||
<span class="total-bp-value">{{ getTotalBp() }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率">
|
||||
<el-descriptions-item label="最终测算利率">
|
||||
<span class="calculate-rate">{{ getCalculateRate() }}</span> %
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行利率">
|
||||
@@ -46,6 +46,13 @@
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<div class="right-panel">
|
||||
<!-- 模型输出卡片 -->
|
||||
<ModelOutputDisplay
|
||||
:cust-type="detailData.custType"
|
||||
:retail-output="null"
|
||||
:corp-output="corpOutput"
|
||||
/>
|
||||
|
||||
<!-- 流程详情卡片 -->
|
||||
<el-card class="detail-card">
|
||||
<div slot="header" class="card-header">
|
||||
@@ -73,12 +80,9 @@
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="担保方式">{{ detailData.guarType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} 元</el-descriptions-item>
|
||||
<el-descriptions-item label="省农担担保贷款">{{
|
||||
formatBoolean(detailData.isAgriGuar)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="还款方式">{{ detailData.repayMethod || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="绿色贷款">{{ formatBoolean(detailData.isGreenLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="科技型企业">{{ formatBoolean(detailData.isTechEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贸易和建筑业企业">{{ formatBoolean(detailData.isTradeBuildEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押类型">{{ detailData.collType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押物是否三方所有">{{
|
||||
formatBoolean(detailData.collThirdParty)
|
||||
@@ -88,13 +92,6 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 模型输出卡片 -->
|
||||
<ModelOutputDisplay
|
||||
:cust-type="detailData.custType"
|
||||
:retail-output="null"
|
||||
:corp-output="corpOutput"
|
||||
/>
|
||||
|
||||
<!-- 议价池卡片 -->
|
||||
<BargainingPoolDisplay
|
||||
v-if="bargainingPool"
|
||||
@@ -150,8 +147,8 @@ export default {
|
||||
methods: {
|
||||
/** 格式化布尔值为中文 */
|
||||
formatBoolean(value) {
|
||||
if (value === 'true' || value === true) return '是'
|
||||
if (value === 'false' || value === false) return '否'
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
return value || '-'
|
||||
},
|
||||
/** 获取基准利率 */
|
||||
@@ -162,9 +159,9 @@ export default {
|
||||
getTotalBp() {
|
||||
return this.corpOutput?.totalBp || '-'
|
||||
},
|
||||
/** 获取测算利率 */
|
||||
/** 获取最终测算利率 */
|
||||
getCalculateRate() {
|
||||
return this.corpOutput?.calculateRate || '-'
|
||||
return this.corpOutput?.finalCalculateRate || '-'
|
||||
},
|
||||
/** 设定执行利率 */
|
||||
handleSetExecuteRate() {
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">模型输出</span>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab">
|
||||
<div class="output-sections">
|
||||
<!-- 个人客户模型输出 -->
|
||||
<template v-if="custType === '个人' && retailOutput">
|
||||
<!-- 基本信息 -->
|
||||
<el-tab-pane label="基本信息" name="retail-basic">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">基本信息</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="客户内码">{{ retailOutput.custIsn || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ retailOutput.custName || '-' }}</el-descriptions-item>
|
||||
@@ -15,10 +15,23 @@
|
||||
<el-descriptions-item label="证件号码">{{ retailOutput.idNum || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="基准利率"><span class="rate-value">{{ retailOutput.baseLoanRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 忠诚度分析 -->
|
||||
<el-tab-pane label="忠诚度分析" name="retail-loyalty">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">测算结果</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="浮动BP"><span class="total-bp-value">{{ retailOutput.totalBp || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率"><span class="calculate-rate">{{ retailOutput.calculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="历史利率">{{ retailOutput.loanRateHistory || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品最低利率下限">{{ retailOutput.minRateProduct || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平滑幅度">{{ retailOutput.smoothRange || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="参考利率"><span class="calculate-rate">{{ retailOutput.referenceRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="最终测算利率"><span class="calculate-rate">{{ retailOutput.finalCalculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">忠诚度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="我行首贷客户">{{ formatBoolean(retailOutput.isFirstLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用信天数">{{ retailOutput.faithDay || '-' }}</el-descriptions-item>
|
||||
@@ -28,20 +41,20 @@
|
||||
<el-descriptions-item label="BP_年龄"><span class="bp-value">{{ retailOutput.bpAge || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_忠诚度" :span="2"><span class="total-bp-value">{{ retailOutput.totalBpLoyalty || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 贡献度分析 -->
|
||||
<el-tab-pane label="贡献度分析" name="retail-contribution">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">贡献度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="存款年日均">{{ retailOutput.balanceAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款年日均">{{ retailOutput.loanAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="派生率">{{ retailOutput.derivationRate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_贡献度"><span class="total-bp-value">{{ retailOutput.totalBpContribution || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 关联度分析 -->
|
||||
<el-tab-pane label="关联度分析" name="retail-relevance">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">关联度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="中间业务_个人_信用卡">{{ formatBoolean(retailOutput.midPerCard) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_一码通">{{ formatBoolean(retailOutput.midPerPass) }}</el-descriptions-item>
|
||||
@@ -58,10 +71,10 @@
|
||||
<el-descriptions-item label="BP_中间业务"><span class="bp-value">{{ retailOutput.bpMid || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_关联度"><span class="total-bp-value">{{ retailOutput.totoalBpRelevance || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 贷款特征 -->
|
||||
<el-tab-pane label="贷款特征" name="retail-loan">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">贷款特征</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="申请金额">{{ retailOutput.applyAmt || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款额度"><span class="bp-value">{{ retailOutput.bpLoanAmount || '-' }}</span></el-descriptions-item>
|
||||
@@ -74,10 +87,10 @@
|
||||
<el-descriptions-item label="抵质押物三方所有">{{ formatBoolean(retailOutput.collThirdParty) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_抵押物"><span class="bp-value">{{ retailOutput.bpCollateral || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 风险度分析 -->
|
||||
<el-tab-pane label="风险度分析" name="retail-risk">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">风险度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="灰名单客户">{{ formatBoolean(retailOutput.greyCust) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="本金逾期">{{ formatBoolean(retailOutput.prinOverdue) }}</el-descriptions-item>
|
||||
@@ -86,26 +99,13 @@
|
||||
<el-descriptions-item label="BP_灰名单与逾期"><span class="bp-value">{{ retailOutput.bpGreyOverdue || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_风险度"><span class="total-bp-value">{{ retailOutput.totoalBpRisk || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 测算结果 -->
|
||||
<el-tab-pane label="测算结果" name="retail-result">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="浮动BP"><span class="total-bp-value">{{ retailOutput.totalBp || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率"><span class="calculate-rate">{{ retailOutput.calculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="历史利率">{{ retailOutput.loanRateHistory || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品最低利率下限">{{ retailOutput.minRateProduct || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平滑幅度">{{ retailOutput.smoothRange || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最终测算利率"><span class="calculate-rate">{{ retailOutput.finalCalculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="参考利率"><span class="calculate-rate">{{ retailOutput.referenceRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 企业客户模型输出 -->
|
||||
<template v-else-if="custType === '企业' && corpOutput">
|
||||
<!-- 基本信息 -->
|
||||
<el-tab-pane label="基本信息" name="corp-basic">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">基本信息</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="客户内码">{{ corpOutput.custIsn || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ corpOutput.custName || '-' }}</el-descriptions-item>
|
||||
@@ -113,10 +113,23 @@
|
||||
<el-descriptions-item label="证件号码">{{ corpOutput.idNum || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="基准利率"><span class="rate-value">{{ corpOutput.baseLoanRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 忠诚度分析 -->
|
||||
<el-tab-pane label="忠诚度分析" name="corp-loyalty">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">测算结果</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="浮动BP"><span class="total-bp-value">{{ corpOutput.totalBp || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率"><span class="calculate-rate">{{ corpOutput.calculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="历史利率">{{ corpOutput.loanRateHistory || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品最低利率下限">{{ corpOutput.minRateProduct || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平滑幅度">{{ corpOutput.smoothRange || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="参考利率"><span class="calculate-rate">{{ corpOutput.referenceRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="最终测算利率"><span class="calculate-rate">{{ corpOutput.finalCalculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">忠诚度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="我行首贷客户">{{ formatBoolean(corpOutput.isFirstLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用信天数">{{ corpOutput.faithDay || '-' }}</el-descriptions-item>
|
||||
@@ -124,20 +137,20 @@
|
||||
<el-descriptions-item label="BP_贷龄"><span class="bp-value">{{ corpOutput.bpAgeLoan || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_忠诚度" :span="2"><span class="total-bp-value">{{ corpOutput.totalBpLoyalty || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 贡献度分析 -->
|
||||
<el-tab-pane label="贡献度分析" name="corp-contribution">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">贡献度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="存款年日均">{{ corpOutput.balanceAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款年日均">{{ corpOutput.loanAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="派生率">{{ corpOutput.derivationRate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_贡献度"><span class="total-bp-value">{{ corpOutput.totalBpContribution || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 关联度分析 -->
|
||||
<el-tab-pane label="关联度分析" name="corp-relevance">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">关联度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="中间业务_企业_企业互联">{{ formatBoolean(corpOutput.midEntConnect) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_有效价值客户">{{ formatBoolean(corpOutput.midEntEffect) }}</el-descriptions-item>
|
||||
@@ -146,31 +159,31 @@
|
||||
<el-descriptions-item label="中间业务_企业_贴现">{{ formatBoolean(corpOutput.midEntDiscount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_电费代扣">{{ formatBoolean(corpOutput.midEntEleDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_水费代扣">{{ formatBoolean(corpOutput.midEntWaterDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_税务代扣">{{ formatBoolean(corpOutput.midEntTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_中间业务"><span class="bp-value">{{ corpOutput.bpMid || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="代发工资户数">{{ corpOutput.payroll || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="存量贷款余额">{{ corpOutput.invLoanAmount || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_代发工资"><span class="bp-value">{{ corpOutput.bpPayroll || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_关联度"><span class="total-bp-value">{{ corpOutput.totoalBpRelevance || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 企业类别 -->
|
||||
<el-tab-pane label="企业类别" name="corp-category">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">企业类别</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="净身企业">{{ formatBoolean(corpOutput.isCleanEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开立基本结算账户">{{ formatBoolean(corpOutput.hasSettleAcct) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="省农担担保贷款">{{ formatBoolean(corpOutput.isAgriGuar) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="绿色贷款">{{ formatBoolean(corpOutput.isGreenLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="科技型企业">{{ formatBoolean(corpOutput.isTechEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贸易和建筑业企业">{{ formatBoolean(corpOutput.isTradeBuildEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_企业客户类别"><span class="bp-value">{{ corpOutput.bpEntType || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 贷款特征 -->
|
||||
<el-tab-pane label="贷款特征" name="corp-loan">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">贷款特征</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="贷款期限">{{ corpOutput.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="还款方式">{{ corpOutput.repayMethod || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="借款期限">{{ corpOutput.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款期限"><span class="bp-value">{{ corpOutput.bpLoanTerm || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ corpOutput.applyAmt || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款额度"><span class="bp-value">{{ corpOutput.bpLoanAmount || '-' }}</span></el-descriptions-item>
|
||||
@@ -178,29 +191,20 @@
|
||||
<el-descriptions-item label="抵质押物三方所有">{{ formatBoolean(corpOutput.collThirdParty) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_抵押物"><span class="bp-value">{{ corpOutput.bpCollateral || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
|
||||
<!-- 风险度分析 -->
|
||||
<el-tab-pane label="风险度分析" name="corp-risk">
|
||||
<div class="output-section">
|
||||
<h4 class="section-title">风险度分析</h4>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="灰名单客户">{{ formatBoolean(corpOutput.greyCust) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="本金逾期">{{ formatBoolean(corpOutput.prinOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="利息逾期">{{ formatBoolean(corpOutput.interestOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="信用卡逾期">{{ formatBoolean(corpOutput.cardOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_灰名单与逾期"><span class="bp-value">{{ corpOutput.bpGreyOverdue || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_风险度"><span class="total-bp-value">{{ corpOutput.totoalBpRisk || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 测算结果 -->
|
||||
<el-tab-pane label="测算结果" name="corp-result">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="浮动BP"><span class="total-bp-value">{{ corpOutput.totalBp || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率"><span class="calculate-rate">{{ corpOutput.calculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</div>
|
||||
</template>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
@@ -221,29 +225,11 @@ export default {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
custType: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
// 根据客户类型设置默认 tab
|
||||
if (val === '个人') {
|
||||
this.activeTab = 'retail-basic'
|
||||
} else if (val === '企业') {
|
||||
this.activeTab = 'corp-basic'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 格式化布尔值为中文 */
|
||||
formatBoolean(value) {
|
||||
if (value === 'true' || value === true) return '是'
|
||||
if (value === 'false' || value === false) return '否'
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
return value || '-'
|
||||
},
|
||||
/** 格式化贷款用途 */
|
||||
@@ -279,8 +265,15 @@ export default {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__header {
|
||||
margin-bottom: 20px;
|
||||
.output-section + .output-section {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
// BP 值样式
|
||||
|
||||
@@ -51,6 +51,23 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="贷款用途" prop="loanPurpose">
|
||||
<el-select v-model="form.loanPurpose" placeholder="请选择贷款用途" style="width: 100%">
|
||||
<el-option label="消费" value="consumer"/>
|
||||
<el-option label="经营" value="business"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="借款期限(年)" prop="loanTerm">
|
||||
<el-select v-model="form.loanTerm" placeholder="请选择借款期限" style="width: 100%">
|
||||
<el-option v-for="item in loanTermOptions" :key="item" :label="item" :value="item"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否有经营佐证" prop="bizProof">
|
||||
@@ -70,9 +87,9 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抵质押类型" prop="collType">
|
||||
<el-select v-model="form.collType" placeholder="请选择抵质押类型" style="width: 100%">
|
||||
<el-option label="一线" value="一线"/>
|
||||
<el-option label="一类" value="一类"/>
|
||||
<el-option label="二类" value="二类"/>
|
||||
<el-option label="三类" value="三类"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -102,22 +119,6 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
// 身份证验证
|
||||
const validateIdNum = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入证件号码'))
|
||||
} else if (this.form.idType === '身份证') {
|
||||
const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error('请输入正确的身份证号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// 金额验证
|
||||
const validateApplyAmt = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
@@ -135,9 +136,12 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
loanTermOptions: [
|
||||
'1', '2', '3', '4', '5', '6'
|
||||
],
|
||||
submitting: false,
|
||||
form: {
|
||||
orgCode: '',
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
@@ -145,6 +149,8 @@ export default {
|
||||
idNum: undefined,
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanPurpose: undefined,
|
||||
loanTerm: undefined,
|
||||
bizProof: false,
|
||||
loanLoop: false,
|
||||
collType: undefined,
|
||||
@@ -163,7 +169,7 @@ export default {
|
||||
{required: true, message: "请选择证件类型", trigger: "change"}
|
||||
],
|
||||
idNum: [
|
||||
{required: true, validator: validateIdNum, trigger: "blur"}
|
||||
{required: true, message: "证件号码不能为空", trigger: "blur"}
|
||||
],
|
||||
guarType: [
|
||||
{required: true, message: "请选择担保方式", trigger: "change"}
|
||||
@@ -171,8 +177,11 @@ export default {
|
||||
applyAmt: [
|
||||
{required: true, validator: validateApplyAmt, trigger: "blur"}
|
||||
],
|
||||
collType: [
|
||||
{required: true, message: "请选择抵质押类型", trigger: "change"}
|
||||
loanPurpose: [
|
||||
{required: true, message: "请选择贷款用途", trigger: "change"}
|
||||
],
|
||||
loanTerm: [
|
||||
{required: true, message: "请选择借款期限", trigger: "change"}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -198,7 +207,7 @@ export default {
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
orgCode: '',
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
@@ -206,6 +215,8 @@ export default {
|
||||
idNum: undefined,
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanPurpose: undefined,
|
||||
loanTerm: undefined,
|
||||
bizProof: false,
|
||||
loanLoop: false,
|
||||
collType: undefined,
|
||||
@@ -231,9 +242,9 @@ export default {
|
||||
// 转换开关值为字符串
|
||||
const data = {
|
||||
...this.form,
|
||||
bizProof: this.form.bizProof ? 'true' : 'false',
|
||||
loanLoop: this.form.loanLoop ? 'true' : 'false',
|
||||
collThirdParty: this.form.collThirdParty ? 'true' : 'false'
|
||||
bizProof: this.form.bizProof ? '1' : '0',
|
||||
loanLoop: this.form.loanLoop ? '1' : '0',
|
||||
collThirdParty: this.form.collThirdParty ? '1' : '0'
|
||||
}
|
||||
|
||||
createPersonalWorkflow(data).then(response => {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<el-descriptions-item label="浮动BP">
|
||||
<span class="total-bp-value">{{ getTotalBp() }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率">
|
||||
<el-descriptions-item label="最终测算利率">
|
||||
<span class="calculate-rate">{{ getCalculateRate() }}</span> %
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行利率">
|
||||
@@ -46,6 +46,13 @@
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<div class="right-panel">
|
||||
<!-- 模型输出卡片 -->
|
||||
<ModelOutputDisplay
|
||||
:cust-type="detailData.custType"
|
||||
:retail-output="retailOutput"
|
||||
:corp-output="null"
|
||||
/>
|
||||
|
||||
<!-- 流程详情卡片 -->
|
||||
<el-card class="detail-card">
|
||||
<div slot="header" class="card-header">
|
||||
@@ -72,6 +79,7 @@
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="担保方式">{{ detailData.guarType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} 元</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款用途">{{ formatLoanPurpose(detailData.loanPurpose) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="借款期限">{{ detailData.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否有经营佐证">{{
|
||||
formatBoolean(detailData.bizProof)
|
||||
@@ -87,13 +95,6 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 模型输出卡片 -->
|
||||
<ModelOutputDisplay
|
||||
:cust-type="detailData.custType"
|
||||
:retail-output="retailOutput"
|
||||
:corp-output="null"
|
||||
/>
|
||||
|
||||
<!-- 议价池卡片 -->
|
||||
<BargainingPoolDisplay
|
||||
v-if="bargainingPool"
|
||||
@@ -149,8 +150,14 @@ export default {
|
||||
methods: {
|
||||
/** 格式化布尔值为中文 */
|
||||
formatBoolean(value) {
|
||||
if (value === 'true' || value === true) return '是'
|
||||
if (value === 'false' || value === false) return '否'
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
return value || '-'
|
||||
},
|
||||
/** 格式化贷款用途 */
|
||||
formatLoanPurpose(value) {
|
||||
if (value === 'consumer') return '消费'
|
||||
if (value === 'business') return '经营'
|
||||
return value || '-'
|
||||
},
|
||||
/** 获取基准利率 */
|
||||
@@ -161,9 +168,9 @@ export default {
|
||||
getTotalBp() {
|
||||
return this.retailOutput?.totalBp || '-'
|
||||
},
|
||||
/** 获取测算利率 */
|
||||
/** 获取最终测算利率 */
|
||||
getCalculateRate() {
|
||||
return this.retailOutput?.calculateRate || '-'
|
||||
return this.retailOutput?.finalCalculateRate || '-'
|
||||
},
|
||||
/** 设定执行利率 */
|
||||
handleSetExecuteRate() {
|
||||
|
||||
34
ruoyi-ui/tests/id-number-validation-removal.test.js
Normal file
34
ruoyi-ui/tests/id-number-validation-removal.test.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalCreateDialog = read('src/views/loanPricing/workflow/components/PersonalCreateDialog.vue')
|
||||
const corporateCreateDialog = read('src/views/loanPricing/workflow/components/CorporateCreateDialog.vue')
|
||||
|
||||
assert(
|
||||
!personalCreateDialog.includes('const validateIdNum ='),
|
||||
'个人新增弹窗仍包含证件号码格式校验函数'
|
||||
)
|
||||
|
||||
assert(
|
||||
!corporateCreateDialog.includes('const validateIdNum ='),
|
||||
'企业新增弹窗仍包含证件号码格式校验函数'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes("idNum: [") &&
|
||||
personalCreateDialog.includes('{required: true, message: "证件号码不能为空", trigger: "blur"}'),
|
||||
'个人新增弹窗证件号码规则应仅保留必填'
|
||||
)
|
||||
|
||||
assert(
|
||||
corporateCreateDialog.includes("idNum: [") &&
|
||||
corporateCreateDialog.includes('{required: true, message: "证件号码不能为空", trigger: "blur"}'),
|
||||
'企业新增弹窗证件号码规则应仅保留必填'
|
||||
)
|
||||
|
||||
console.log('id number validation removal assertions passed')
|
||||
65
ruoyi-ui/tests/personal-create-input-params.test.js
Normal file
65
ruoyi-ui/tests/personal-create-input-params.test.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalCreateDialog = read('src/views/loanPricing/workflow/components/PersonalCreateDialog.vue')
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('label="贷款用途"') && personalCreateDialog.includes('prop="loanPurpose"'),
|
||||
'个人新增弹窗缺少贷款用途字段'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('label="借款期限(年)"') && personalCreateDialog.includes('prop="loanTerm"'),
|
||||
'个人新增弹窗缺少借款期限字段'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes("value=\"consumer\"") && personalCreateDialog.includes("value=\"business\""),
|
||||
'个人新增弹窗缺少贷款用途选项'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('loanTermOptions') &&
|
||||
personalCreateDialog.includes("'1'") &&
|
||||
personalCreateDialog.includes("'6'") &&
|
||||
!personalCreateDialog.includes("'7'"),
|
||||
'个人新增弹窗借款期限选项应限制为 1-6 年'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('label="一类"') &&
|
||||
personalCreateDialog.includes('label="二类"') &&
|
||||
personalCreateDialog.includes('label="三类"') &&
|
||||
!personalCreateDialog.includes('label="一线"'),
|
||||
'个人新增弹窗抵质押类型选项未按 Excel 对齐'
|
||||
)
|
||||
|
||||
assert(
|
||||
!personalCreateDialog.includes('{required: true, message: "请选择抵质押类型", trigger: "change"}'),
|
||||
'个人新增弹窗仍将抵质押类型设为必填'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes("bizProof: this.form.bizProof ? '1' : '0'") &&
|
||||
personalCreateDialog.includes("loanLoop: this.form.loanLoop ? '1' : '0'") &&
|
||||
personalCreateDialog.includes("collThirdParty: this.form.collThirdParty ? '1' : '0'"),
|
||||
'个人新增弹窗开关字段未按 1/0 提交'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalDetail.includes('label="贷款用途"') && personalDetail.includes('detailData.loanPurpose'),
|
||||
'个人详情页缺少贷款用途展示'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalDetail.includes("value === '1'") && personalDetail.includes("value === '0'"),
|
||||
'个人详情页布尔格式化未兼容 1/0'
|
||||
)
|
||||
|
||||
console.log('personal create input params assertions passed')
|
||||
21
ruoyi-ui/tests/personal-final-calculate-rate-display.test.js
Normal file
21
ruoyi-ui/tests/personal-final-calculate-rate-display.test.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
|
||||
assert(
|
||||
/label="最终测算利率"/.test(personalDetail),
|
||||
'个人流程详情左侧缺少“最终测算利率”标签'
|
||||
)
|
||||
|
||||
assert(
|
||||
/return this\.retailOutput\?\.finalCalculateRate \|\| '-'/.test(personalDetail),
|
||||
'个人流程详情没有使用 finalCalculateRate 展示最终测算利率'
|
||||
)
|
||||
|
||||
console.log('personal final calculate rate display assertions passed')
|
||||
27
ruoyi-ui/tests/workflow-detail-card-order.test.js
Normal file
27
ruoyi-ui/tests/workflow-detail-card-order.test.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
function assertModelOutputBeforeDetailCard(source, label) {
|
||||
const modelOutputIndex = source.indexOf('<ModelOutputDisplay')
|
||||
const detailCardIndex = source.indexOf('<el-card class="detail-card">')
|
||||
|
||||
assert(modelOutputIndex !== -1, `${label} 缺少模型输出卡片`)
|
||||
assert(detailCardIndex !== -1, `${label} 缺少流程详情卡片`)
|
||||
assert(
|
||||
modelOutputIndex < detailCardIndex,
|
||||
`${label} 的模型输出卡片应位于流程详情卡片上方`
|
||||
)
|
||||
}
|
||||
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
const corporateDetail = read('src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue')
|
||||
|
||||
assertModelOutputBeforeDetailCard(personalDetail, '个人流程详情')
|
||||
assertModelOutputBeforeDetailCard(corporateDetail, '企业流程详情')
|
||||
|
||||
console.log('workflow detail card order assertions passed')
|
||||
13
sql/2026-04-16-shangyu-corporate-alignment.sql
Normal file
13
sql/2026-04-16-shangyu-corporate-alignment.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- 上虞对公利率测算字段对齐
|
||||
|
||||
ALTER TABLE `loan_pricing_workflow`
|
||||
ADD COLUMN `repay_method` varchar(20) DEFAULT NULL COMMENT '还款方式: 分期/不分期' AFTER `loan_term`;
|
||||
|
||||
ALTER TABLE `model_corp_output_fields`
|
||||
ADD COLUMN `repay_method` varchar(100) DEFAULT NULL COMMENT '还款方式' AFTER `id_num`,
|
||||
ADD COLUMN `is_trade_build_ent` varchar(100) DEFAULT NULL COMMENT '贸易和建筑业企业' AFTER `is_tech_ent`,
|
||||
ADD COLUMN `loan_rate_history` varchar(100) DEFAULT NULL COMMENT '历史利率' AFTER `calculate_rate`,
|
||||
ADD COLUMN `min_rate_product` varchar(100) DEFAULT NULL COMMENT '产品最低利率下限' AFTER `loan_rate_history`,
|
||||
ADD COLUMN `smooth_range` varchar(100) DEFAULT NULL COMMENT '平滑幅度' AFTER `min_rate_product`,
|
||||
ADD COLUMN `final_calculate_rate` varchar(100) DEFAULT NULL COMMENT '最终测算利率' AFTER `smooth_range`,
|
||||
ADD COLUMN `reference_rate` varchar(100) DEFAULT NULL COMMENT '参考利率' AFTER `final_calculate_rate`;
|
||||
15
sql/2026-04-17-corporate.sql
Normal file
15
sql/2026-04-17-corporate.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- 对公展示指标字段对齐:补充还款方式与模型输出缺失字段
|
||||
-- 执行库:loan-pricing
|
||||
|
||||
ALTER TABLE `loan_pricing_workflow`
|
||||
ADD COLUMN `repay_method` varchar(20) DEFAULT NULL COMMENT '还款方式: 分期/不分期'
|
||||
AFTER `guar_type`;
|
||||
|
||||
ALTER TABLE `model_corp_output_fields`
|
||||
ADD COLUMN `repay_method` varchar(100) DEFAULT NULL COMMENT '还款方式' AFTER `id_num`,
|
||||
ADD COLUMN `is_trade_build_ent` varchar(100) DEFAULT NULL COMMENT '贸易和建筑业企业' AFTER `has_settle_acct`,
|
||||
ADD COLUMN `loan_rate_history` varchar(100) DEFAULT NULL COMMENT '历史利率' AFTER `calculate_rate`,
|
||||
ADD COLUMN `min_rate_product` varchar(100) DEFAULT NULL COMMENT '产品最低利率下限' AFTER `loan_rate_history`,
|
||||
ADD COLUMN `smooth_range` varchar(100) DEFAULT NULL COMMENT '平滑幅度' AFTER `min_rate_product`,
|
||||
ADD COLUMN `final_calculate_rate` varchar(100) DEFAULT NULL COMMENT '最终测算利率' AFTER `smooth_range`,
|
||||
ADD COLUMN `reference_rate` varchar(100) DEFAULT NULL COMMENT '参考利率' AFTER `final_calculate_rate`;
|
||||
@@ -8,7 +8,7 @@ ALTER TABLE loan_pricing_workflow
|
||||
MODIFY COLUMN `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
MODIFY COLUMN `serial_num` varchar(50) NOT NULL COMMENT '业务方流水号',
|
||||
MODIFY COLUMN `model_output_id` bigint(20) DEFAULT NULL COMMENT '模型输出ID',
|
||||
MODIFY COLUMN `org_code` varchar(20) NOT NULL DEFAULT '' COMMENT '机构编码',
|
||||
MODIFY COLUMN `org_code` varchar(20) NOT NULL DEFAULT '892000' COMMENT '机构编码(统一值892000)',
|
||||
MODIFY COLUMN `run_type` varchar(10) NOT NULL DEFAULT '1' COMMENT '运行模式: 1-同步',
|
||||
MODIFY COLUMN `cust_isn` varchar(50) NOT NULL COMMENT '客户内码',
|
||||
MODIFY COLUMN `cust_type` varchar(20) NOT NULL COMMENT '客户类型: 个人/企业',
|
||||
|
||||
@@ -14,7 +14,7 @@ ALTER TABLE loan_pricing_workflow
|
||||
MODIFY COLUMN `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
MODIFY COLUMN `serial_num` varchar(50) NOT NULL COMMENT '业务方流水号',
|
||||
MODIFY COLUMN `model_output_id` bigint(20) DEFAULT NULL COMMENT '模型输出ID',
|
||||
MODIFY COLUMN `org_code` varchar(20) NOT NULL DEFAULT '' COMMENT '机构编码',
|
||||
MODIFY COLUMN `org_code` varchar(20) NOT NULL DEFAULT '892000' COMMENT '机构编码(统一值892000)',
|
||||
MODIFY COLUMN `run_type` varchar(10) NOT NULL DEFAULT '1' COMMENT '运行模式: 1-同步',
|
||||
MODIFY COLUMN `cust_isn` varchar(50) NOT NULL COMMENT '客户内码',
|
||||
MODIFY COLUMN `cust_type` varchar(20) NOT NULL COMMENT '客户类型: 个人/企业',
|
||||
|
||||
@@ -741,7 +741,7 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`serial_num` varchar(50) NOT NULL COMMENT '业务方流水号',
|
||||
`model_output_id` bigint(20) DEFAULT NULL COMMENT '模型输出ID',
|
||||
`org_code` varchar(20) NOT NULL DEFAULT '' COMMENT '机构编码',
|
||||
`org_code` varchar(20) NOT NULL DEFAULT '892000' COMMENT '机构编码(统一值892000)',
|
||||
`run_type` varchar(10) NOT NULL DEFAULT '1' COMMENT '运行模式: 1-同步',
|
||||
`cust_isn` varchar(50) NOT NULL COMMENT '客户内码',
|
||||
`cust_type` varchar(20) NOT NULL COMMENT '客户类型: 个人/企业',
|
||||
@@ -751,7 +751,8 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`mid_ent_ele_ddc` varchar(10) DEFAULT NULL COMMENT '中间业务_企业_电费代扣: true/false',
|
||||
`mid_ent_water_ddc` varchar(10) DEFAULT NULL COMMENT '中间业务_企业_水费代扣: true/false',
|
||||
`apply_amt` varchar(50) NOT NULL COMMENT '申请金额(元)',
|
||||
`loan_term` varchar(50) DEFAULT NULL COMMENT '贷款期限',
|
||||
`loan_term` varchar(50) DEFAULT NULL COMMENT '借款期限(年)',
|
||||
`repay_method` varchar(20) DEFAULT NULL COMMENT '还款方式: 分期/不分期',
|
||||
`is_clean_ent` varchar(10) DEFAULT NULL COMMENT '净身企业: true/false',
|
||||
`has_settle_acct` varchar(10) DEFAULT NULL COMMENT '开立基本结算账户: true/false',
|
||||
`is_manufacturing` varchar(10) DEFAULT NULL COMMENT '制造业企业: true/false',
|
||||
@@ -764,7 +765,7 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`loan_purpose` varchar(20) DEFAULT NULL COMMENT '贷款用途: consumer-消费/business-经营',
|
||||
`biz_proof` varchar(10) DEFAULT NULL COMMENT '是否有经营佐证: true/false',
|
||||
`loan_loop` varchar(10) DEFAULT NULL COMMENT '循环功能: true/false(贷款合同是否开通循环功能)',
|
||||
`coll_type` varchar(20) DEFAULT NULL COMMENT '抵质押类型: 一线/一类/二类',
|
||||
`coll_type` varchar(20) DEFAULT NULL COMMENT '抵质押类型: 一类/二类/三类/四类',
|
||||
`coll_third_party` varchar(10) DEFAULT NULL COMMENT '抵质押物是否三方所有: true/false',
|
||||
`loan_rate` varchar(20) DEFAULT NULL COMMENT '贷款利率',
|
||||
`execute_rate` varchar(20) DEFAULT NULL COMMENT '执行利率(%)',
|
||||
@@ -800,6 +801,7 @@ CREATE TABLE `model_corp_output_fields` (
|
||||
`cust_name` varchar(100) DEFAULT NULL COMMENT '客户名称',
|
||||
`id_type` varchar(100) DEFAULT NULL COMMENT '证件类型',
|
||||
`id_num` varchar(100) DEFAULT NULL COMMENT '证件号码',
|
||||
`repay_method` varchar(100) DEFAULT NULL COMMENT '还款方式',
|
||||
`base_loan_rate` varchar(100) DEFAULT NULL COMMENT '基准利率',
|
||||
`is_first_loan` varchar(100) DEFAULT NULL COMMENT '我行首贷客户',
|
||||
`faith_day` varchar(100) DEFAULT NULL COMMENT '用信天数',
|
||||
@@ -827,6 +829,7 @@ CREATE TABLE `model_corp_output_fields` (
|
||||
`is_agri_guar` varchar(100) DEFAULT NULL COMMENT '省农担担保贷款',
|
||||
`is_green_loan` varchar(100) DEFAULT NULL COMMENT '绿色贷款',
|
||||
`is_tech_ent` varchar(100) DEFAULT NULL COMMENT '科技型企业',
|
||||
`is_trade_build_ent` varchar(100) DEFAULT NULL COMMENT '贸易和建筑业企业',
|
||||
`bp_ent_type` varchar(100) DEFAULT NULL COMMENT 'BP_企业客户类别',
|
||||
`totoal_bp_relevance` varchar(100) DEFAULT NULL COMMENT 'TOTAL_BP_关联度',
|
||||
`loan_term` varchar(100) DEFAULT NULL COMMENT '贷款期限',
|
||||
@@ -844,6 +847,11 @@ CREATE TABLE `model_corp_output_fields` (
|
||||
`totoal_bp_risk` varchar(100) DEFAULT NULL COMMENT 'TOTAL_BP_风险度',
|
||||
`total_bp` varchar(100) DEFAULT NULL COMMENT '浮动BP',
|
||||
`calculate_rate` varchar(100) DEFAULT NULL COMMENT '测算利率',
|
||||
`loan_rate_history` varchar(100) DEFAULT NULL COMMENT '历史利率',
|
||||
`min_rate_product` varchar(100) DEFAULT NULL COMMENT '产品最低利率下限',
|
||||
`smooth_range` varchar(100) DEFAULT NULL COMMENT '平滑幅度',
|
||||
`final_calculate_rate` varchar(100) DEFAULT NULL COMMENT '最终测算利率',
|
||||
`reference_rate` varchar(100) DEFAULT NULL COMMENT '参考利率',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户贷款利率测算表';
|
||||
|
||||
@@ -323,7 +323,7 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`serial_num` varchar(50) NOT NULL COMMENT '业务方流水号',
|
||||
`model_output_id` bigint(20) DEFAULT NULL COMMENT '模型输出ID',
|
||||
`org_code` varchar(20) NOT NULL DEFAULT '' COMMENT '机构编码',
|
||||
`org_code` varchar(20) NOT NULL DEFAULT '892000' COMMENT '机构编码(统一值892000)',
|
||||
`run_type` varchar(10) NOT NULL DEFAULT '1' COMMENT '运行模式: 1-同步',
|
||||
`cust_isn` varchar(50) NOT NULL COMMENT '客户内码',
|
||||
`cust_type` varchar(20) NOT NULL COMMENT '客户类型: 个人/企业',
|
||||
@@ -333,7 +333,8 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`mid_ent_ele_ddc` varchar(10) DEFAULT NULL COMMENT '中间业务_企业_电费代扣: true/false',
|
||||
`mid_ent_water_ddc` varchar(10) DEFAULT NULL COMMENT '中间业务_企业_水费代扣: true/false',
|
||||
`apply_amt` varchar(50) NOT NULL COMMENT '申请金额(元)',
|
||||
`loan_term` varchar(50) DEFAULT NULL COMMENT '贷款期限',
|
||||
`loan_term` varchar(50) DEFAULT NULL COMMENT '借款期限(年)',
|
||||
`repay_method` varchar(20) DEFAULT NULL COMMENT '还款方式: 分期/不分期',
|
||||
`is_clean_ent` varchar(10) DEFAULT NULL COMMENT '净身企业: true/false',
|
||||
`has_settle_acct` varchar(10) DEFAULT NULL COMMENT '开立基本结算账户: true/false',
|
||||
`is_manufacturing` varchar(10) DEFAULT NULL COMMENT '制造业企业: true/false',
|
||||
@@ -346,7 +347,7 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`loan_purpose` varchar(20) DEFAULT NULL COMMENT '贷款用途: consumer-消费/business-经营',
|
||||
`biz_proof` varchar(10) DEFAULT NULL COMMENT '是否有经营佐证: true/false',
|
||||
`loan_loop` varchar(10) DEFAULT NULL COMMENT '循环功能: true/false(贷款合同是否开通循环功能)',
|
||||
`coll_type` varchar(20) DEFAULT NULL COMMENT '抵质押类型: 一线/一类/二类',
|
||||
`coll_type` varchar(20) DEFAULT NULL COMMENT '抵质押类型: 一类/二类/三类/四类',
|
||||
`coll_third_party` varchar(10) DEFAULT NULL COMMENT '抵质押物是否三方所有: true/false',
|
||||
`loan_rate` varchar(20) DEFAULT NULL COMMENT '贷款利率',
|
||||
`execute_rate` varchar(20) DEFAULT NULL COMMENT '执行利率(%)',
|
||||
@@ -382,6 +383,7 @@ CREATE TABLE `model_corp_output_fields` (
|
||||
`cust_name` varchar(100) DEFAULT NULL COMMENT '客户名称',
|
||||
`id_type` varchar(100) DEFAULT NULL COMMENT '证件类型',
|
||||
`id_num` varchar(100) DEFAULT NULL COMMENT '证件号码',
|
||||
`repay_method` varchar(100) DEFAULT NULL COMMENT '还款方式',
|
||||
`base_loan_rate` varchar(100) DEFAULT NULL COMMENT '基准利率',
|
||||
`is_first_loan` varchar(100) DEFAULT NULL COMMENT '我行首贷客户',
|
||||
`faith_day` varchar(100) DEFAULT NULL COMMENT '用信天数',
|
||||
@@ -409,6 +411,7 @@ CREATE TABLE `model_corp_output_fields` (
|
||||
`is_agri_guar` varchar(100) DEFAULT NULL COMMENT '省农担担保贷款',
|
||||
`is_green_loan` varchar(100) DEFAULT NULL COMMENT '绿色贷款',
|
||||
`is_tech_ent` varchar(100) DEFAULT NULL COMMENT '科技型企业',
|
||||
`is_trade_build_ent` varchar(100) DEFAULT NULL COMMENT '贸易和建筑业企业',
|
||||
`bp_ent_type` varchar(100) DEFAULT NULL COMMENT 'BP_企业客户类别',
|
||||
`totoal_bp_relevance` varchar(100) DEFAULT NULL COMMENT 'TOTAL_BP_关联度',
|
||||
`loan_term` varchar(100) DEFAULT NULL COMMENT '贷款期限',
|
||||
@@ -426,6 +429,11 @@ CREATE TABLE `model_corp_output_fields` (
|
||||
`totoal_bp_risk` varchar(100) DEFAULT NULL COMMENT 'TOTAL_BP_风险度',
|
||||
`total_bp` varchar(100) DEFAULT NULL COMMENT '浮动BP',
|
||||
`calculate_rate` varchar(100) DEFAULT NULL COMMENT '测算利率',
|
||||
`loan_rate_history` varchar(100) DEFAULT NULL COMMENT '历史利率',
|
||||
`min_rate_product` varchar(100) DEFAULT NULL COMMENT '产品最低利率下限',
|
||||
`smooth_range` varchar(100) DEFAULT NULL COMMENT '平滑幅度',
|
||||
`final_calculate_rate` varchar(100) DEFAULT NULL COMMENT '最终测算利率',
|
||||
`reference_rate` varchar(100) DEFAULT NULL COMMENT '参考利率',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户贷款利率测算表';
|
||||
|
||||
@@ -4,7 +4,7 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`serial_num` varchar(50) NOT NULL COMMENT '业务方流水号',
|
||||
`model_output_id` bigint(20) NULL COMMENT '模型输出id',
|
||||
`org_code` varchar(20) NOT NULL DEFAULT '' COMMENT '机构编码',
|
||||
`org_code` varchar(20) NOT NULL DEFAULT '892000' COMMENT '机构编码(统一值892000)',
|
||||
`run_type` varchar(10) NOT NULL DEFAULT '1' COMMENT '运行模式: 1-同步',
|
||||
`cust_isn` varchar(50) NOT NULL COMMENT '客户内码',
|
||||
`cust_type` varchar(20) NOT NULL COMMENT '客户类型: 个人/企业',
|
||||
@@ -14,20 +14,27 @@ CREATE TABLE `loan_pricing_workflow` (
|
||||
`mid_ent_ele_ddc` varchar(10) DEFAULT NULL COMMENT '中间业务_企业_电费代扣: true/false',
|
||||
`mid_ent_water_ddc` varchar(10) DEFAULT NULL COMMENT '中间业务_企业_水费代扣: true/false',
|
||||
`apply_amt` varchar(50) NOT NULL COMMENT '申请金额(元)',
|
||||
`loan_term` varchar(50) DEFAULT NULL COMMENT '借款期限(年)',
|
||||
`repay_method` varchar(20) DEFAULT NULL COMMENT '还款方式: 分期/不分期',
|
||||
`is_clean_ent` varchar(10) DEFAULT NULL COMMENT '净身企业: true/false',
|
||||
`has_settle_acct` varchar(10) DEFAULT NULL COMMENT '开立基本结算账户: true/false',
|
||||
`is_manufacturing` varchar(10) DEFAULT NULL COMMENT '制造业企业: true/false',
|
||||
`is_agri_guar` varchar(10) DEFAULT NULL COMMENT '省农担担保贷款: true/false',
|
||||
`is_tech_ent` varchar(10) DEFAULT NULL COMMENT '科技型企业: true/false(科技型企业最多下降5BP)',
|
||||
`is_green_loan` varchar(10) DEFAULT NULL COMMENT '绿色贷款: true/false(绿色贷款最多下降5BP)',
|
||||
`is_trade_construction` varchar(10) DEFAULT NULL COMMENT '贸易和建筑业企业标识: true/false(抵质押类上调20BP)',
|
||||
`is_tax_a` varchar(10) DEFAULT NULL COMMENT '是否纳税信用等级A级: true/false',
|
||||
`is_agri_leading` varchar(10) DEFAULT NULL COMMENT '是否县级及以上农业龙头企业: true/false',
|
||||
`loan_purpose` varchar(20) DEFAULT NULL COMMENT '贷款用途: consumer-消费/business-经营',
|
||||
`biz_proof` varchar(10) DEFAULT NULL COMMENT '是否有经营佐证: true/false',
|
||||
`coll_type` varchar(20) DEFAULT NULL COMMENT '抵质押类型: 一线/一类/二类',
|
||||
`loan_loop` varchar(10) DEFAULT NULL COMMENT '循环功能: true/false(贷款合同是否开通循环功能)',
|
||||
`coll_type` varchar(20) DEFAULT NULL COMMENT '抵质押类型: 一类/二类/三类/四类',
|
||||
`coll_third_party` varchar(10) DEFAULT NULL COMMENT '抵质押物是否三方所有: true/false',
|
||||
`loan_rate` varchar(20) NOT NULL COMMENT '贷款利率',
|
||||
`loan_rate` varchar(20) DEFAULT NULL COMMENT '贷款利率',
|
||||
`execute_rate` varchar(20) DEFAULT NULL COMMENT '执行利率(%)',
|
||||
`cust_name` varchar(100) DEFAULT NULL COMMENT '客户名称',
|
||||
`id_type` varchar(50) DEFAULT NULL COMMENT '证件类型',
|
||||
`id_num` varchar(100) DEFAULT NULL COMMENT '证件号码',
|
||||
`is_inclusive_finance` varchar(10) DEFAULT NULL COMMENT '是否普惠小微借款人: true/false',
|
||||
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
|
||||
@@ -3,57 +3,64 @@
|
||||
*/
|
||||
DROP TABLE IF EXISTS model_corp_output_fields;
|
||||
CREATE TABLE `model_corp_output_fields` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
`cust_isn` VARCHAR(100) COMMENT '客户内码',
|
||||
`cust_type` VARCHAR(100) COMMENT '客户类型',
|
||||
`guar_type` VARCHAR(100) COMMENT '担保方式',
|
||||
`cust_name` VARCHAR(100) COMMENT '客户名称',
|
||||
`id_type` VARCHAR(100) COMMENT '证件类型',
|
||||
`id_num` VARCHAR(100) COMMENT '证件号码',
|
||||
`base_loan_rate` VARCHAR(100) COMMENT '基准利率',
|
||||
`is_first_loan` VARCHAR(100) COMMENT '我行首贷客户',
|
||||
`faith_day` VARCHAR(100) COMMENT '用信天数',
|
||||
`bp_first_loan` VARCHAR(100) COMMENT 'BP_首贷',
|
||||
`bp_age_loan` VARCHAR(100) COMMENT 'BP_贷龄',
|
||||
`total_bp_loyalty` VARCHAR(100) COMMENT 'TOTAL_BP_忠诚度',
|
||||
`balance_avg` VARCHAR(100) COMMENT '存款年日均',
|
||||
`loan_avg` VARCHAR(100) COMMENT '贷款年日均',
|
||||
`derivation_rate` VARCHAR(100) COMMENT '派生率',
|
||||
`total_bp_contribution` VARCHAR(100) COMMENT 'TOTAL_BP_贡献度',
|
||||
`mid_ent_connect` VARCHAR(100) COMMENT '中间业务_企业_企业互联',
|
||||
`mid_ent_effect` VARCHAR(100) COMMENT '中间业务_企业_有效价值客户',
|
||||
`mid_ent_inter` VARCHAR(100) COMMENT '中间业务_企业_国际业务',
|
||||
`mid_ent_accept` VARCHAR(100) COMMENT '中间业务_企业_承兑',
|
||||
`mid_ent_discount` VARCHAR(100) COMMENT '中间业务_企业_贴现',
|
||||
`mid_ent_ele_ddc` VARCHAR(100) COMMENT '中间业务_企业_电费代扣',
|
||||
`mid_ent_water_ddc` VARCHAR(100) COMMENT '中间业务_企业_水费代扣',
|
||||
`mid_ent_tax` VARCHAR(100) COMMENT '中间业务_企业_税务代扣',
|
||||
`bp_mid` VARCHAR(100) COMMENT 'BP_中间业务',
|
||||
`payroll` VARCHAR(100) COMMENT '代发工资户数',
|
||||
`inv_loan_amount` VARCHAR(100) COMMENT '存量贷款余额',
|
||||
`bp_payroll` VARCHAR(100) COMMENT 'BP_代发工资',
|
||||
`is_clean_ent` VARCHAR(100) COMMENT '净身企业',
|
||||
`has_settle_acct` VARCHAR(100) COMMENT '开立基本结算账户',
|
||||
`is_agri_guar` VARCHAR(100) COMMENT '省农担担保贷款',
|
||||
`is_green_loan` VARCHAR(100) COMMENT '绿色贷款',
|
||||
`is_tech_ent` VARCHAR(100) COMMENT '科技型企业',
|
||||
`bp_ent_type` VARCHAR(100) COMMENT 'BP_企业客户类别',
|
||||
`totoal_bp_relevance` VARCHAR(100) COMMENT 'TOTAL_BP_关联度',
|
||||
`loan_term` VARCHAR(100) COMMENT '贷款期限',
|
||||
`bp_loan_term` VARCHAR(100) COMMENT 'BP_贷款期限',
|
||||
`apply_amt` VARCHAR(100) COMMENT '申请金额',
|
||||
`bp_loan_amount` VARCHAR(100) COMMENT 'BP_贷款额度',
|
||||
`coll_type` VARCHAR(100) COMMENT '抵质押类型',
|
||||
`coll_third_party` VARCHAR(100) COMMENT '抵质押物是否三方所有',
|
||||
`bp_collateral` VARCHAR(100) COMMENT 'BP_抵押物',
|
||||
`grey_cust` VARCHAR(100) COMMENT '灰名单客户',
|
||||
`prin_overdue` VARCHAR(100) COMMENT '本金逾期',
|
||||
`interest_overdue` VARCHAR(100) COMMENT '利息逾期',
|
||||
`card_overdue` VARCHAR(100) COMMENT '信用卡逾期',
|
||||
`bp_grey_overdue` VARCHAR(100) COMMENT 'BP_灰名单与逾期',
|
||||
`totoal_bp_risk` VARCHAR(100) COMMENT 'TOTAL_BP_风险度',
|
||||
`total_bp` VARCHAR(100) COMMENT '浮动BP',
|
||||
`calculate_rate` VARCHAR(100) COMMENT '测算利率',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户贷款利率测算表';
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
`cust_isn` VARCHAR(100) COMMENT '客户内码',
|
||||
`cust_type` VARCHAR(100) COMMENT '客户类型',
|
||||
`guar_type` VARCHAR(100) COMMENT '担保方式',
|
||||
`cust_name` VARCHAR(100) COMMENT '客户名称',
|
||||
`id_type` VARCHAR(100) COMMENT '证件类型',
|
||||
`id_num` VARCHAR(100) COMMENT '证件号码',
|
||||
`repay_method` VARCHAR(100) COMMENT '还款方式',
|
||||
`base_loan_rate` VARCHAR(100) COMMENT '基准利率',
|
||||
`is_first_loan` VARCHAR(100) COMMENT '我行首贷客户',
|
||||
`faith_day` VARCHAR(100) COMMENT '用信天数',
|
||||
`bp_first_loan` VARCHAR(100) COMMENT 'BP_首贷',
|
||||
`bp_age_loan` VARCHAR(100) COMMENT 'BP_贷龄',
|
||||
`total_bp_loyalty` VARCHAR(100) COMMENT 'TOTAL_BP_忠诚度',
|
||||
`balance_avg` VARCHAR(100) COMMENT '存款年日均',
|
||||
`loan_avg` VARCHAR(100) COMMENT '贷款年日均',
|
||||
`derivation_rate` VARCHAR(100) COMMENT '派生率',
|
||||
`total_bp_contribution` VARCHAR(100) COMMENT 'TOTAL_BP_贡献度',
|
||||
`mid_ent_connect` VARCHAR(100) COMMENT '中间业务_企业_企业互联',
|
||||
`mid_ent_effect` VARCHAR(100) COMMENT '中间业务_企业_有效价值客户',
|
||||
`mid_ent_inter` VARCHAR(100) COMMENT '中间业务_企业_国际业务',
|
||||
`mid_ent_accept` VARCHAR(100) COMMENT '中间业务_企业_承兑',
|
||||
`mid_ent_discount` VARCHAR(100) COMMENT '中间业务_企业_贴现',
|
||||
`mid_ent_ele_ddc` VARCHAR(100) COMMENT '中间业务_企业_电费代扣',
|
||||
`mid_ent_water_ddc` VARCHAR(100) COMMENT '中间业务_企业_水费代扣',
|
||||
`mid_ent_tax` VARCHAR(100) COMMENT '中间业务_企业_税务代扣',
|
||||
`bp_mid` VARCHAR(100) COMMENT 'BP_中间业务',
|
||||
`payroll` VARCHAR(100) COMMENT '代发工资户数',
|
||||
`inv_loan_amount` VARCHAR(100) COMMENT '存量贷款余额',
|
||||
`bp_payroll` VARCHAR(100) COMMENT 'BP_代发工资',
|
||||
`is_clean_ent` VARCHAR(100) COMMENT '净身企业',
|
||||
`has_settle_acct` VARCHAR(100) COMMENT '开立基本结算账户',
|
||||
`is_agri_guar` VARCHAR(100) COMMENT '省农担担保贷款',
|
||||
`is_green_loan` VARCHAR(100) COMMENT '绿色贷款',
|
||||
`is_tech_ent` VARCHAR(100) COMMENT '科技型企业',
|
||||
`is_trade_build_ent` VARCHAR(100) COMMENT '贸易和建筑业企业',
|
||||
`bp_ent_type` VARCHAR(100) COMMENT 'BP_企业客户类别',
|
||||
`totoal_bp_relevance` VARCHAR(100) COMMENT 'TOTAL_BP_关联度',
|
||||
`loan_term` VARCHAR(100) COMMENT '借款期限',
|
||||
`bp_loan_term` VARCHAR(100) COMMENT 'BP_贷款期限',
|
||||
`apply_amt` VARCHAR(100) COMMENT '申请金额',
|
||||
`bp_loan_amount` VARCHAR(100) COMMENT 'BP_贷款额度',
|
||||
`coll_type` VARCHAR(100) COMMENT '抵质押类型',
|
||||
`coll_third_party` VARCHAR(100) COMMENT '抵质押物是否三方所有',
|
||||
`bp_collateral` VARCHAR(100) COMMENT 'BP_抵押物',
|
||||
`grey_cust` VARCHAR(100) COMMENT '灰名单客户',
|
||||
`prin_overdue` VARCHAR(100) COMMENT '本金逾期',
|
||||
`interest_overdue` VARCHAR(100) COMMENT '利息逾期',
|
||||
`card_overdue` VARCHAR(100) COMMENT '信用卡逾期',
|
||||
`bp_grey_overdue` VARCHAR(100) COMMENT 'BP_灰名单与逾期',
|
||||
`totoal_bp_risk` VARCHAR(100) COMMENT 'TOTAL_BP_风险度',
|
||||
`total_bp` VARCHAR(100) COMMENT '浮动BP',
|
||||
`calculate_rate` VARCHAR(100) COMMENT '测算利率',
|
||||
`loan_rate_history` VARCHAR(100) COMMENT '历史利率',
|
||||
`min_rate_product` VARCHAR(100) COMMENT '产品最低利率下限',
|
||||
`smooth_range` VARCHAR(100) COMMENT '平滑幅度',
|
||||
`final_calculate_rate` VARCHAR(100) COMMENT '最终测算利率',
|
||||
`reference_rate` VARCHAR(100) COMMENT '参考利率',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户贷款利率测算表';
|
||||
|
||||
10
sql/update_org_code_default_20260409.sql
Normal file
10
sql/update_org_code_default_20260409.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
-- 统一 loan_pricing_workflow 表 org_code 默认值与存量数据
|
||||
|
||||
ALTER TABLE `loan_pricing_workflow`
|
||||
MODIFY COLUMN `org_code` varchar(20) NOT NULL DEFAULT '892000' COMMENT '机构编码(统一值892000)';
|
||||
|
||||
UPDATE `loan_pricing_workflow`
|
||||
SET `org_code` = '892000'
|
||||
WHERE `org_code` IS NULL
|
||||
OR `org_code` = ''
|
||||
OR `org_code` <> '892000';
|
||||
410
tongweb/2026-04-16-TongWeb接入全流程通用指南.md
Normal file
410
tongweb/2026-04-16-TongWeb接入全流程通用指南.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# TongWeb接入全流程通用指南
|
||||
|
||||
## 1. 适用场景
|
||||
|
||||
本文档用于指导 Spring Boot 2.x 项目接入东方通 TongWeb 内嵌容器,适用于以下场景:
|
||||
|
||||
- 现有项目默认使用 Spring Boot 内嵌 Tomcat。
|
||||
- 需要切换为 TongWeb 自启动运行。
|
||||
- 需要将 TongWeb license 文件随应用一起打包。
|
||||
- 需要沉淀一套可以迁移到其他项目的标准接入步骤。
|
||||
|
||||
本文以本仓库的接入经验为基础,输出的是一套可复用流程,而不是只面向当前项目的零散记录。
|
||||
|
||||
## 2. 前置准备
|
||||
|
||||
接入前需要准备以下信息:
|
||||
|
||||
### 2.1 TongWeb Starter 依赖
|
||||
|
||||
当前使用的依赖坐标:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.tongweb.springboot</groupId>
|
||||
<artifactId>tongweb-spring-boot-starter-2.x</artifactId>
|
||||
<version>7.0.E.7</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 2.2 Maven 仓库
|
||||
|
||||
如果项目默认只配了 Maven Central 或阿里云公共仓库,TongWeb 依赖通常无法直接解析,需要补充 TongWeb 仓库:
|
||||
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>tongweb-releases</id>
|
||||
<name>TongWeb Maven Releases</name>
|
||||
<url>https://mvn.elitescloud.com/nexus/repository/maven-releases/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
|
||||
### 2.3 License 文件
|
||||
|
||||
需要一份可用的 TongWeb license 文件,例如:
|
||||
|
||||
- `Tongweb_license.dat`
|
||||
|
||||
建议确认以下信息:
|
||||
|
||||
- 许可证版本是否与目标 TongWeb 版本一致。
|
||||
- 许可证是否仍在有效期内。
|
||||
- 许可证是否允许当前部署规模使用。
|
||||
|
||||
## 3. 接入总流程
|
||||
|
||||
TongWeb 接入建议按下面顺序执行:
|
||||
|
||||
1. 确认项目里是谁引入了默认 Tomcat。
|
||||
2. 排除默认 Tomcat 依赖。
|
||||
3. 引入 TongWeb Starter。
|
||||
4. 把 license 文件放入 `resources`。
|
||||
5. 在 `application.yml` 中增加 TongWeb 配置。
|
||||
6. 执行构建、依赖树、产物检查和启动验证。
|
||||
7. 根据日志处理依赖解析、license 不匹配、配置冲突等问题。
|
||||
|
||||
## 4. 依赖改造
|
||||
|
||||
### 4.1 找出默认 Tomcat 来源
|
||||
|
||||
很多项目不是在启动模块直接声明 `spring-boot-starter-web`,而是通过公共框架模块间接引入。因此第一步必须先查清默认 Tomcat 的入口来源。
|
||||
|
||||
例如:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
如果它出现在公共框架模块里,实际排除 Tomcat 时通常要在应用启动模块对该依赖做 `exclusion`。
|
||||
|
||||
### 4.2 排除默认 Tomcat
|
||||
|
||||
推荐在最终启动模块中对上游框架模块做排除,避免默认内嵌 Tomcat 和 TongWeb 同时进入运行时:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.xxx</groupId>
|
||||
<artifactId>project-framework</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 4.3 引入 TongWeb Starter
|
||||
|
||||
在最终启动模块增加 TongWeb Starter:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.tongweb.springboot</groupId>
|
||||
<artifactId>tongweb-spring-boot-starter-2.x</artifactId>
|
||||
<version>7.0.E.7</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 4.4 资源打包
|
||||
|
||||
如果项目资源打包规则比较严格,建议显式保留 `resources` 配置,避免 `.dat` 文件没有进入产物:
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
```
|
||||
|
||||
## 5. License 文件接入
|
||||
|
||||
### 5.1 放置路径
|
||||
|
||||
将 TongWeb license 文件放到:
|
||||
|
||||
```text
|
||||
src/main/resources/Tongweb_license.dat
|
||||
```
|
||||
|
||||
建议直接随项目源码管理,便于构建产物统一携带。
|
||||
|
||||
### 5.2 命名原则
|
||||
|
||||
配置中的文件名必须与实际资源名完全一致。例如资源名是:
|
||||
|
||||
```text
|
||||
Tongweb_license.dat
|
||||
```
|
||||
|
||||
那么配置里也必须写:
|
||||
|
||||
```text
|
||||
classpath:Tongweb_license.dat
|
||||
```
|
||||
|
||||
不要一个地方写 `license.dat`,另一个地方写 `Tongweb_license.dat`,否则运行时会直接出现 license 读取失败。
|
||||
|
||||
## 6. 配置接入
|
||||
|
||||
建议把 TongWeb 配置统一放到 `application.yml` 中,便于其他项目直接复用,而不是散落到多个环境文件。
|
||||
|
||||
推荐配置如下:
|
||||
|
||||
```yml
|
||||
server:
|
||||
tongweb:
|
||||
license:
|
||||
path: classpath:Tongweb_license.dat
|
||||
```
|
||||
|
||||
如果项目本身已经有 `server` 节点,直接挂到其下即可,不需要额外拆配置文件。
|
||||
|
||||
## 7. 构建与验证
|
||||
|
||||
接入完成后,至少执行下面几类验证。
|
||||
|
||||
### 7.1 依赖解析验证
|
||||
|
||||
```bash
|
||||
mvn -pl ruoyi-admin -am package -DskipTests
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- TongWeb 依赖能够正常下载。
|
||||
- 项目可以正常构建。
|
||||
|
||||
### 7.2 产物检查
|
||||
|
||||
```bash
|
||||
jar tf ruoyi-admin/target/ruoyi-admin.jar | rg 'Tongweb_license.dat|tongweb'
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- `Tongweb_license.dat` 已进入 `BOOT-INF/classes/`
|
||||
- TongWeb 相关 jar 已进入 `BOOT-INF/lib/`
|
||||
|
||||
### 7.3 依赖树检查
|
||||
|
||||
```bash
|
||||
mvn -pl ruoyi-admin dependency:tree '-Dincludes=com.tongweb.springboot:*,com.tongweb:*,org.apache.tomcat.embed:*'
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- 能看到 TongWeb Starter 及相关依赖。
|
||||
- 默认 `spring-boot-starter-tomcat` 不应再作为主依赖链出现。
|
||||
|
||||
注意:
|
||||
|
||||
- 某些项目中仍可能看到 `tomcat-embed-el`,它可能来自 `spring-boot-starter-validation` 等其他依赖。
|
||||
- 是否需要继续清理,最终以实际启动结果为准。
|
||||
|
||||
### 7.4 启动验证
|
||||
|
||||
建议从应用模块目录直接执行:
|
||||
|
||||
```bash
|
||||
mvn -f ruoyi-admin/pom.xml spring-boot:run -Dspring-boot.run.profiles=dev
|
||||
```
|
||||
|
||||
这样可以避免从聚合工程根目录调用时,Maven 把插件错误落到父 `pom` 上。
|
||||
|
||||
验证重点:
|
||||
|
||||
- 应用是否成功启动。
|
||||
- TongWeb License SDK 日志是否出现。
|
||||
- 端口是否成功监听。
|
||||
- 是否出现 TongWeb 与 Tomcat 的容器冲突报错。
|
||||
|
||||
### 7.5 启动后进程清理
|
||||
|
||||
如果是本地验证,结束测试后需要手动关闭 Java 进程,避免残留服务继续占用端口。
|
||||
|
||||
例如:
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:63310 -sTCP:LISTEN
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
## 8. 推荐自动化测试
|
||||
|
||||
如果项目有测试体系,建议至少补两类测试。
|
||||
|
||||
### 8.1 资源存在性测试
|
||||
|
||||
验证 license 文件能否从 classpath 读取:
|
||||
|
||||
```java
|
||||
assertNotNull(
|
||||
TongWebLicenseResourceTest.class.getClassLoader().getResource("Tongweb_license.dat"));
|
||||
```
|
||||
|
||||
### 8.2 配置存在性测试
|
||||
|
||||
验证 `application.yml` 中是否存在:
|
||||
|
||||
```text
|
||||
server.tongweb.license.path
|
||||
```
|
||||
|
||||
这样可以避免后续重构时把 TongWeb 配置误删。
|
||||
|
||||
## 9. 常见问题
|
||||
|
||||
### 9.1 TongWeb 依赖下载失败
|
||||
|
||||
典型现象:
|
||||
|
||||
- Maven 提示找不到 `tongweb-spring-boot-starter-2.x`
|
||||
|
||||
原因:
|
||||
|
||||
- 项目只配置了公共仓库,没有配置 TongWeb 专用仓库。
|
||||
|
||||
处理方式:
|
||||
|
||||
- 补充 TongWeb Maven 仓库。
|
||||
|
||||
### 9.2 `spring-boot:run` 找不到插件
|
||||
|
||||
典型现象:
|
||||
|
||||
- `No plugin found for prefix 'spring-boot'`
|
||||
|
||||
处理方式:
|
||||
|
||||
- 改为从子模块目录执行,或者用 `-f ruoyi-admin/pom.xml` 指向具体模块。
|
||||
|
||||
### 9.3 `spring-boot:run` 落到父工程
|
||||
|
||||
典型现象:
|
||||
|
||||
- `Unable to find a suitable main class`
|
||||
|
||||
原因:
|
||||
|
||||
- Spring Boot 插件被执行在聚合父 `pom` 上。
|
||||
|
||||
处理方式:
|
||||
|
||||
- 使用:
|
||||
|
||||
```bash
|
||||
mvn -f ruoyi-admin/pom.xml spring-boot:run
|
||||
```
|
||||
|
||||
### 9.4 License 不匹配或已过期
|
||||
|
||||
典型现象:
|
||||
|
||||
- 版本号不匹配告警
|
||||
- 有效期已过期
|
||||
|
||||
这类问题说明 TongWeb 配置链路通常已经生效,真正的问题是授权文件本身不适配当前环境。
|
||||
|
||||
处理方式:
|
||||
|
||||
- 更换与目标 TongWeb 版本一致、且仍在有效期内的 license 文件。
|
||||
|
||||
### 9.5 项目里仍保留 `server.tomcat.*`
|
||||
|
||||
如果项目原先已有:
|
||||
|
||||
```yml
|
||||
server:
|
||||
tomcat:
|
||||
...
|
||||
```
|
||||
|
||||
建议先不要大规模重构。
|
||||
|
||||
处理策略:
|
||||
|
||||
- 若 TongWeb 启动时仅忽略这些配置,则先保留。
|
||||
- 只有当日志明确指出这些配置导致启动失败时,再做最小必要调整。
|
||||
|
||||
## 10. 可复制模板
|
||||
|
||||
### 10.1 `pom.xml` 最小改造模板
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.xxx</groupId>
|
||||
<artifactId>project-framework</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tongweb.springboot</groupId>
|
||||
<artifactId>tongweb-spring-boot-starter-2.x</artifactId>
|
||||
<version>7.0.E.7</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 10.2 `application.yml` 模板
|
||||
|
||||
```yml
|
||||
server:
|
||||
tongweb:
|
||||
license:
|
||||
path: classpath:Tongweb_license.dat
|
||||
```
|
||||
|
||||
### 10.3 目录模板
|
||||
|
||||
```text
|
||||
src/main/resources/Tongweb_license.dat
|
||||
```
|
||||
|
||||
## 11. 最终检查清单
|
||||
|
||||
在其他项目复用时,可以按下面清单逐项确认:
|
||||
|
||||
- 是否已确认默认 Tomcat 的引入来源
|
||||
- 是否已排除 `spring-boot-starter-tomcat`
|
||||
- 是否已引入 TongWeb Starter
|
||||
- 是否已配置 TongWeb Maven 仓库
|
||||
- 是否已将 `Tongweb_license.dat` 放入 `src/main/resources`
|
||||
- 是否已在 `application.yml` 中加入 `server.tongweb.license.path`
|
||||
- 是否已通过构建验证
|
||||
- 是否已确认产物中包含 TongWeb 相关 jar 与 license 文件
|
||||
- 是否已完成启动验证
|
||||
- 是否已确认 license 版本、有效期和授权范围可用
|
||||
|
||||
## 12. 结论
|
||||
|
||||
TongWeb 接入本质上只包含四件事:
|
||||
|
||||
- 替换默认内嵌容器依赖
|
||||
- 接入 TongWeb 仓库与 Starter
|
||||
- 让 license 文件进入 classpath
|
||||
- 用启动日志验证 TongWeb 是否真正接管运行
|
||||
|
||||
只要按这个顺序执行,TongWeb 接入通常可以用最短路径完成,且这套流程可以直接迁移到其他 Spring Boot 项目中。
|
||||
1
tongweb/license.dat
Executable file
1
tongweb/license.dat
Executable file
@@ -0,0 +1 @@
|
||||
uc3Y29XJfVtZtZTbmF72t3V405cxamrXBnM0P0vqrrLnJjQ0T0Mt93avL/euwcmvgpWN09qZhbWX25eO9U91ptOrcWNK1XJz6z9waqNC5L40d09ybfrmrDP352Ny76fqyPauv06+ru7f+bTwG99zvHOS8bQvJub/rL3JkoKbfbnZXJmVyVtYwMjPTIjEyQtMsaWMQpnNlNlbkPTX2lTE5EwNsaWOApnNlNlb5cGX3RmVsU9czZQZWFmVhpjcfZGdGVT0yF0Z0LTMDITEwEyLuZFCmVXRl9kYxClPS01ByRXX1Y3b2RmFtRfTUb2ZT12Vi5nVXX1ClRnNpZlcfTnb25mVyVtYuMCPTclRX5FCQVVX0N1VO9DTKYmVD0GlwluZUV1PQpXJk9IYyZVd2FD0K9JZfTWVFd051F4XlcjbWJQpU0tMFZGV19W9ul0atYmPUVk5FVkCWRVV19U9OJTSJQ0X0x0U9VOQLWUWFlTU1lLbSMmSmhkNHlBRrcVdG8kNtUxYCT2RHFTc5lperM2WUFkkvU3M3MzTDBldOlqeTb3YUVGx3VWT1WkaERHhilxOTM0T0l1FrdBS1aUWG4GE5FtaLMyUXZUlz8zM0UnSCs0lDM5RDVFRzJDBzZmOoRkNFdEt6YwNKTkTXA1ZFVXXJT0UlNElD5fTDRTRU5TdU1YdROEL2xUhvV3OLY3bTVmhMpZUJU2QXF2I1VYdxTjWVVm9jZKUPWUMXFHNrFJeJZDU29mM2hKQpUmUVJGIxwwOVSUaXFXQy9JU4cFdlkGJQY4SCYWYjFkJndiaCVFMlNk1QZQTwQWRDJ0th1YMwaHYmEzQrB2aWTTRmpgpOA5dfVkRVd0lPVSUMSUTl9kNFNFTyaWPU0G5mNwaieUdUJ0NiZjNVbnOC90tNYyb2S3djNmh4BidmNlRnBk1PdCShdDYUdS9mNMSiMnYzVml2pSameEY2NStCZtRvU3dloGgvQyU0TmcTlTlMJVYhc3VEp01EpYRwUGNUUWZ5daVxU3blZzg3dnR2UncnR2U4RkU2CkSUc19W5FVTSURVJ0xJ9OXOQ0Q0VG1iU9WFcDYWNktah2M3blbjNlZsZndjOTdXFTFHNQS0SzUE4FVpdqR1dEL2RWgvpMSabVZkJlhRJFRMZUTUJ0NGdwUGbGZitjFE01bZSlRzZHZ4RMUFd2cHhEZqtZbwR2clQUgxFsb0Z1ZFVW5tJvUwWEd2gjVog1eKYUaUdHYK5JUXX1TkVlNJZFUfTET05U5DlDRTVURV9jJi49SmbHR2pU5UFwVUK0ZTBFE5pmd3ZGOEdXppU1VTNEb09UtntCRycUaHUmR1ovVYNGUU1HFy1vVsZXYlBmt1lsNVZDVUNUNyJQeHUXZlF2pzVMVlVjVmNWx2VxWZaUSEh1FlJ0bmRmbENkRV9VWiRVT3R01apMUIMkWjdW8K5JTXX1TkVlNJZFUfTET05U5DlDRTVURV9Ulw49bhMWcW5HdRdZNaK0UEp3AyU0TwY0Wm1md6tDMpN0cDRVBK11aKR0VTZkI3RocKUXRm5Vl6llcpMWaXJXRLZOOaZDOGZ1R0gychWURVJk5JR5VNYXQTl250dGYheFOG1FZog4RFZWMkRmtK1QRCaUclJlNYNFRHSWTFpFUKxYRXX1TkVlNJZFUfTET05U5DlDRTVURV9Hho49T5aTempVMwFhU1b1Vi9HR4YzO1dDNks05HhEQxY2VUZXlMNybyVmVEl3dNlLbYSGbGlWxMVNWUcEZXpVN0w5NVZDVGFUw4NMSUOWbkRjV21QaMbESVhGx1w3MiY1WmJXB6o0NjS1T2tWxjNSeRY0UzV0g2VhR5Z0RWlzkKRMdXX1TkVlNJZFUfTET05U5DlDRTVURV9Hdt49apUHZVNnhlpxQ5MENGNnh1VYN3aDQ2QW5qRqd4K1cXYk9ZdHW4VzeE9XVHB6YmM3Wk1DYwVLdqS1aTNUtjhINicVeUV1JBZRZxTGYWdTVytuepR1QVVXZlNoSVOFdVlVkzRqdPcjOW9HBll6Ota2dHFGV6dtN6c1ekN2UKdwc
|
||||
Reference in New Issue
Block a user