修改字段 登陆
This commit is contained in:
@@ -5,7 +5,7 @@ set -eu
|
||||
WEBAPP_ROOT="/home/webapp"
|
||||
ENV_ROOT="$WEBAPP_ROOT/env"
|
||||
APP_ROOT="$WEBAPP_ROOT/loan-pricing"
|
||||
JAVA_HOME="$ENV_ROOT/jdk"
|
||||
JAVA_HOME="$ENV_ROOT/java"
|
||||
BACKEND_DIR="$APP_ROOT/backend"
|
||||
LOG_DIR="$APP_ROOT/logs"
|
||||
RUN_DIR="$APP_ROOT/run"
|
||||
@@ -37,13 +37,6 @@ usage() {
|
||||
EOF
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
log_error "请使用 root 用户执行脚本"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_runtime_dirs() {
|
||||
mkdir -p "$BACKEND_DIR" "$LOG_DIR" "$RUN_DIR"
|
||||
}
|
||||
@@ -133,8 +126,10 @@ stop_backend() {
|
||||
}
|
||||
|
||||
start_backend() {
|
||||
ensure_runtime_dirs
|
||||
|
||||
if [ ! -x "$JAVA_HOME/bin/java" ]; then
|
||||
log_error "未检测到 Java,可先执行 /home/webapp/install_env.sh"
|
||||
log_error "未检测到可执行 Java: $JAVA_HOME/bin/java"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -164,19 +159,7 @@ start_backend() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wait_seconds=0
|
||||
while [ "$wait_seconds" -lt 30 ]; do
|
||||
if ss -lnt 2>/dev/null | grep -q ":$BACKEND_PORT "; then
|
||||
log_info "后端已监听端口: $BACKEND_PORT"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
wait_seconds=$((wait_seconds + 1))
|
||||
done
|
||||
|
||||
log_error "后端未在预期时间内监听端口 $BACKEND_PORT"
|
||||
exit 1
|
||||
log_info "后端已启动,PID: $backend_pid"
|
||||
}
|
||||
|
||||
status_backend() {
|
||||
@@ -186,11 +169,6 @@ status_backend() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ss -lnt 2>/dev/null | grep -q ":$BACKEND_PORT "; then
|
||||
log_info "未识别到脚本托管进程,但端口 $BACKEND_PORT 已被占用"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "后端未运行"
|
||||
}
|
||||
|
||||
|
||||
114
bin/prod/restart_java_test.sh
Normal file
114
bin/prod/restart_java_test.sh
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
|
||||
SCRIPT_UNDER_TEST="$ROOT_DIR/bin/prod/restart_java.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
|
||||
}
|
||||
|
||||
create_fake_java() {
|
||||
fake_java="$1"
|
||||
|
||||
cat > "$fake_java" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
while :; do
|
||||
sleep 1
|
||||
done
|
||||
EOF
|
||||
|
||||
chmod +x "$fake_java"
|
||||
}
|
||||
|
||||
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"
|
||||
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"
|
||||
chmod +x "$work_dir/restart_java.sh"
|
||||
}
|
||||
|
||||
cleanup_work_dir() {
|
||||
work_dir="$1"
|
||||
|
||||
if [ -f "$work_dir/loan-pricing/run/backend.pid" ]; then
|
||||
backend_pid=$(cat "$work_dir/loan-pricing/run/backend.pid" 2>/dev/null || true)
|
||||
if [ -n "${backend_pid:-}" ]; then
|
||||
kill "$backend_pid" 2>/dev/null || true
|
||||
wait "$backend_pid" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$work_dir"
|
||||
}
|
||||
|
||||
test_script_contract() {
|
||||
assert_grep 'JAVA_HOME="\$ENV_ROOT/java"' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep '--spring\.profiles\.active=pro' "$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"
|
||||
}
|
||||
|
||||
test_restart_flow() {
|
||||
work_dir=$(mktemp -d)
|
||||
trap 'cleanup_work_dir "$work_dir"' EXIT INT TERM
|
||||
|
||||
prepare_script_env "$work_dir"
|
||||
|
||||
"$work_dir/restart_java.sh" start
|
||||
if [ ! -f "$work_dir/loan-pricing/run/backend.pid" ]; then
|
||||
fail "expected backend pid file after start"
|
||||
fi
|
||||
|
||||
backend_pid=$(cat "$work_dir/loan-pricing/run/backend.pid")
|
||||
kill -0 "$backend_pid" 2>/dev/null || fail "expected backend process to be running after start"
|
||||
|
||||
status_output=$("$work_dir/restart_java.sh" status 2>&1 || true)
|
||||
printf '%s\n' "$status_output" | grep -q '后端正在运行' || fail "expected status output to show running"
|
||||
|
||||
"$work_dir/restart_java.sh" restart
|
||||
restarted_pid=$(cat "$work_dir/loan-pricing/run/backend.pid")
|
||||
kill -0 "$restarted_pid" 2>/dev/null || fail "expected backend process to be running after restart"
|
||||
|
||||
"$work_dir/restart_java.sh" stop
|
||||
if [ -f "$work_dir/loan-pricing/run/backend.pid" ]; then
|
||||
fail "expected backend pid file to be removed after stop"
|
||||
fi
|
||||
|
||||
trap - EXIT INT TERM
|
||||
cleanup_work_dir "$work_dir"
|
||||
}
|
||||
|
||||
main() {
|
||||
[ -f "$SCRIPT_UNDER_TEST" ] || fail "script under test not found: $SCRIPT_UNDER_TEST"
|
||||
test_script_contract
|
||||
test_restart_flow
|
||||
printf 'PASS: restart_java tests\n'
|
||||
}
|
||||
|
||||
main "$@"
|
||||
84
doc/2026-04-03-retail-display-fields-backend-plan.md
Normal file
84
doc/2026-04-03-retail-display-fields-backend-plan.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 个人模型详情缺失展示字段补齐后端实施计划
|
||||
|
||||
> **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:** 后端补齐 `ModelRetailOutputFields` 实体字段,并为 `model_retail_output_fields` 表增加 5 个对应列。保持现有控制器、服务与 Mapper 链路不变,让模型返回字段按既有流程直接反序列化、入库并回查。
|
||||
|
||||
**Tech Stack:** Java 17、Spring Boot、MyBatis Plus、Maven、JUnit 5
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 通过测试锁定缺失字段
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/domain/entity/ModelRetailOutputFieldsTest.java`
|
||||
|
||||
- [ ] **Step 1: 编写实体字段断言测试**
|
||||
|
||||
新增测试,断言 `ModelRetailOutputFields` 包含以下字段:
|
||||
|
||||
```java
|
||||
"loanRateHistory",
|
||||
"minRateProduct",
|
||||
"smoothRange",
|
||||
"finalCalculateRate",
|
||||
"referenceRate"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试并确认先失败**
|
||||
|
||||
Run: `mvn -pl ruoyi-loan-pricing -Dtest=ModelRetailOutputFieldsTest test`
|
||||
Expected: FAIL,提示缺少新增字段。
|
||||
|
||||
### Task 2: 补齐个人模型输出实体字段
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/ModelRetailOutputFields.java`
|
||||
|
||||
- [ ] **Step 1: 在实体中新增 5 个字段**
|
||||
|
||||
新增以下字段:
|
||||
|
||||
- `loanRateHistory`
|
||||
- `minRateProduct`
|
||||
- `smoothRange`
|
||||
- `finalCalculateRate`
|
||||
- `referenceRate`
|
||||
|
||||
- [ ] **Step 2: 保持现有命名与注释风格一致**
|
||||
|
||||
新增字段使用与现有实体一致的 `private String` 定义和中文注释,不引入额外注解。
|
||||
|
||||
- [ ] **Step 3: 重新运行测试确认通过**
|
||||
|
||||
Run: `mvn -pl ruoyi-loan-pricing -Dtest=ModelRetailOutputFieldsTest test`
|
||||
Expected: PASS
|
||||
|
||||
### Task 3: 补齐零售模型输出表结构
|
||||
|
||||
**Files:**
|
||||
- Create: `sql/add_model_retail_output_rate_fields_20260403.sql`
|
||||
- Modify: `sql/model_retail.sql`
|
||||
- Modify: `sql/loan_pricing_schema_20260328.sql`
|
||||
- Modify: `sql/loan_pricing_prod_init_20260331.sql`
|
||||
|
||||
- [ ] **Step 1: 新增数据库迁移脚本**
|
||||
|
||||
在迁移脚本中为 `model_retail_output_fields` 增加以下列:
|
||||
|
||||
- `loan_rate_history`
|
||||
- `min_rate_product`
|
||||
- `smooth_range`
|
||||
- `final_calculate_rate`
|
||||
- `reference_rate`
|
||||
|
||||
- [ ] **Step 2: 同步更新建表基线 SQL**
|
||||
|
||||
将相同列同步到仓库中的零售模型输出表建表脚本,避免新环境继续缺列。
|
||||
|
||||
- [ ] **Step 3: 对开发库执行迁移并验证列存在**
|
||||
|
||||
Run: `mysql ... < sql/add_model_retail_output_rate_fields_20260403.sql`
|
||||
Expected: 迁移执行成功,`SHOW COLUMNS FROM model_retail_output_fields` 可看到新增 5 列
|
||||
113
doc/2026-04-03-retail-display-fields-design.md
Normal file
113
doc/2026-04-03-retail-display-fields-design.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 个人模型详情缺失展示字段补齐设计文档
|
||||
|
||||
## 1. 背景
|
||||
|
||||
个人模型接口返回字段有更新。根据 `doc/上虞对私利率测算_上传字段与展示字段.xlsx` 的 `展示指标` sheet,当前个人详情页仍缺少部分应展示字段,需要补齐页面展示并保证接口链路字段完整。
|
||||
|
||||
## 2. 已确认范围
|
||||
|
||||
- 仅处理个人客户详情页
|
||||
- 仅补齐 `展示指标` sheet 中当前缺失的 6 个字段
|
||||
- 不调整企业客户页面
|
||||
- 不新增兼容逻辑、兜底逻辑或额外展示区域
|
||||
- 保持现有页面结构和分组,按最短路径补齐
|
||||
|
||||
## 3. 缺失字段
|
||||
|
||||
经核对,当前页面缺少以下字段:
|
||||
|
||||
- `loanTerm` 借款期限
|
||||
- `loanRateHistory` 历史利率
|
||||
- `minRateProduct` 产品最低利率下限
|
||||
- `smoothRange` 平滑幅度
|
||||
- `finalCalculateRate` 最终测算利率
|
||||
- `referenceRate` 参考利率
|
||||
|
||||
其中:
|
||||
|
||||
- `loanTerm` 属于流程明细字段,来自 `LoanPricingWorkflow`
|
||||
- 其余 5 个字段属于个人模型输出字段,来自 `ModelRetailOutputFields`
|
||||
- 其余 5 个字段同时要求 `model_retail_output_fields` 表具备对应列,否则新流程详情无法完整落库展示
|
||||
|
||||
## 4. 现状分析
|
||||
|
||||
### 4.1 前端现状
|
||||
|
||||
个人详情页由两个主要区域组成:
|
||||
|
||||
- `PersonalWorkflowDetail.vue` 负责流程详情与左侧关键信息
|
||||
- `ModelOutputDisplay.vue` 负责个人模型输出分组展示
|
||||
|
||||
当前页面已覆盖大部分 `展示指标` 字段,但个人详情页“业务信息”中未展示 `loanTerm`,个人模型输出“测算结果”中也未展示 5 个新增利率相关字段。
|
||||
|
||||
### 4.2 后端现状
|
||||
|
||||
- `LoanPricingWorkflow` 已包含 `loanTerm`
|
||||
- `ModelRetailOutputFields` 当前未包含 `loanRateHistory`、`minRateProduct`、`smoothRange`、`finalCalculateRate`、`referenceRate`
|
||||
|
||||
因此前端目前无法从个人模型输出对象中读取这 5 个字段。
|
||||
|
||||
同时,开发库 `model_retail_output_fields` 表当前也未包含这 5 个字段列。如果只补代码而不补表结构,新的个人流程在模型结果入库时将无法完整保存这些字段。
|
||||
|
||||
## 5. 方案对比
|
||||
|
||||
### 方案一:在现有分组内补齐字段
|
||||
|
||||
做法:
|
||||
|
||||
- 在个人详情页“业务信息”区域补 `loanTerm`
|
||||
- 在个人模型输出“测算结果”区域补 5 个新增利率字段
|
||||
- 后端仅补 `ModelRetailOutputFields` 缺失字段定义
|
||||
|
||||
优点:
|
||||
|
||||
- 改动最小
|
||||
- 不影响现有页面结构
|
||||
- 与现有字段分组最贴合
|
||||
|
||||
缺点:
|
||||
|
||||
- 需要同时修改前后端
|
||||
|
||||
### 方案二:单独新增“利率结果扩展”分组
|
||||
|
||||
做法:
|
||||
|
||||
- 新增一个专门的 Tab 或卡片展示 5 个新增利率字段
|
||||
|
||||
优点:
|
||||
|
||||
- 新增字段集中展示
|
||||
|
||||
缺点:
|
||||
|
||||
- 页面改动更大
|
||||
- 用户认知路径变化
|
||||
- 不符合本次最短路径要求
|
||||
|
||||
## 6. 设计结论
|
||||
|
||||
采用方案一。
|
||||
|
||||
实现方式如下:
|
||||
|
||||
- 后端在 `ModelRetailOutputFields` 中新增 5 个字段定义,保证接口对象具备完整返回结构
|
||||
- 数据库为 `model_retail_output_fields` 新增 5 个对应列,保证模型输出可正常落库
|
||||
- 前端在 `PersonalWorkflowDetail.vue` 的“业务信息”中补齐 `loanTerm`
|
||||
- 前端在 `ModelOutputDisplay.vue` 的个人“测算结果”中补齐 `loanRateHistory`、`minRateProduct`、`smoothRange`、`finalCalculateRate`、`referenceRate`
|
||||
|
||||
## 7. 验证设计
|
||||
|
||||
本次按最小可执行验证:
|
||||
|
||||
- 后端新增一个实体字段断言测试,先验证缺失字段不存在并失败,再补齐后验证通过
|
||||
- 前端新增一个源码断言脚本,先验证缺失展示未实现并失败,再补齐后验证通过
|
||||
- 对开发库执行表结构迁移
|
||||
- 创建新的个人流程并打开详情页,确认新增字段可在真实页面展示
|
||||
- 最后执行前端生产构建,确认页面代码可正常打包
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
- 不调整企业详情页
|
||||
- 不修改模型计算逻辑
|
||||
- 不重构页面布局
|
||||
81
doc/2026-04-03-retail-display-fields-frontend-plan.md
Normal file
81
doc/2026-04-03-retail-display-fields-frontend-plan.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# 个人模型详情缺失展示字段补齐前端实施计划
|
||||
|
||||
> **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:** 在个人详情页补齐 `展示指标` sheet 中缺失的 6 个字段展示。
|
||||
|
||||
**Architecture:** 前端沿用现有个人详情页结构,在流程详情的“业务信息”中补 `loanTerm`,在模型输出“测算结果”中补 5 个新增利率字段,不新增页面分组和交互。
|
||||
|
||||
**Tech Stack:** Vue 2、Element UI、Node.js
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 通过前端断言测试锁定缺失展示
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/tests/retail-display-fields.test.js`
|
||||
- Modify: `ruoyi-ui/package.json`
|
||||
|
||||
- [ ] **Step 1: 编写源码断言脚本**
|
||||
|
||||
断言以下展示已存在:
|
||||
|
||||
- `PersonalWorkflowDetail.vue` 包含 `借款期限` 和 `detailData.loanTerm`
|
||||
- `ModelOutputDisplay.vue` 包含以下字段展示:
|
||||
- `loanRateHistory`
|
||||
- `minRateProduct`
|
||||
- `smoothRange`
|
||||
- `finalCalculateRate`
|
||||
- `referenceRate`
|
||||
|
||||
- [ ] **Step 2: 运行脚本并确认先失败**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:retail-display-fields`
|
||||
Expected: FAIL,提示缺失展示实现。
|
||||
|
||||
### Task 2: 补齐个人详情页字段展示
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue`
|
||||
- Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/ModelOutputDisplay.vue`
|
||||
- Reference: `ruoyi-loan-pricing/src/main/resources/data/retail_output.json`
|
||||
|
||||
- [ ] **Step 1: 在业务信息中补齐借款期限**
|
||||
|
||||
在 `PersonalWorkflowDetail.vue` 的“业务信息”区域新增:
|
||||
|
||||
```vue
|
||||
<el-descriptions-item label="借款期限">{{ detailData.loanTerm || '-' }}</el-descriptions-item>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在个人测算结果中补齐 5 个字段**
|
||||
|
||||
在 `ModelOutputDisplay.vue` 的个人“测算结果”中新增:
|
||||
|
||||
- 历史利率
|
||||
- 产品最低利率下限
|
||||
- 平滑幅度
|
||||
- 最终测算利率
|
||||
- 参考利率
|
||||
|
||||
- [ ] **Step 3: 重新运行前端断言脚本**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run test:retail-display-fields`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 4: 执行前端构建验证**
|
||||
|
||||
Run: `npm --prefix ruoyi-ui run build:prod`
|
||||
Expected: 构建成功,输出包含 `Build complete.`
|
||||
|
||||
- [ ] **Step 5: 启动前后端并打开个人流程详情页验证**
|
||||
|
||||
使用浏览器打开新的个人流程详情页,确认:
|
||||
|
||||
- 流程详情“业务信息”出现 `借款期限`
|
||||
- 模型输出“测算结果”出现并可查看以下字段
|
||||
- 历史利率
|
||||
- 产品最低利率下限
|
||||
- 平滑幅度
|
||||
- 最终测算利率
|
||||
- 参考利率
|
||||
@@ -0,0 +1,24 @@
|
||||
# 生产后端重启脚本实施记录
|
||||
|
||||
## 修改内容
|
||||
- 收敛生产后端重启脚本 `bin/prod/restart_java.sh`
|
||||
- 脚本固定面向已部署的 `backend/ruoyi-admin.jar` 执行启停,不再包含构建逻辑
|
||||
- 后端启动 profile 固定为 `pro`
|
||||
- Java 路径统一为 `/home/webapp/env/java/bin/java`,与现有生产安装脚本保持一致
|
||||
- 移除 `root` 执行校验与端口监听校验,只保留 `start|stop|restart|status` 所需的最小启停逻辑
|
||||
- 新增脚本自测文件 `bin/prod/restart_java_test.sh`
|
||||
|
||||
## 实现说明
|
||||
- `start` 仅检查 Java 可执行文件、目标 jar 是否存在以及当前是否已有同脚本托管进程
|
||||
- `stop` 继续基于 PID 文件和 `-Dloan.pricing.home=/home/webapp/loan-pricing` 进程标记识别并停止当前后端进程
|
||||
- `restart` 按“先停后起”执行,适用于生产环境已部署 jar 的直接重启
|
||||
- `status` 仅返回脚本托管进程状态,不再增加端口占用类附加判断
|
||||
|
||||
## 验证结果
|
||||
- 已执行 `sh bin/prod/restart_java_test.sh`
|
||||
- 已验证以下场景:
|
||||
- 脚本固定使用 `/home/webapp/env/java`
|
||||
- 脚本固定使用 `--spring.profiles.active=pro`
|
||||
- 脚本不包含 `mvn`、`require_root`、`ss/lsof/netstat` 相关依赖
|
||||
- `start -> status -> restart -> stop` 流程执行通过
|
||||
- 自测使用临时目录中的假 `java` 进程完成,测试结束后已自动清理对应进程和临时目录
|
||||
@@ -0,0 +1,72 @@
|
||||
# 个人模型详情缺失展示字段补齐实施记录
|
||||
|
||||
## 实施时间
|
||||
- 2026-04-03
|
||||
|
||||
## 修改内容
|
||||
- 补齐个人详情页“业务信息”中的 `借款期限`
|
||||
- 补齐个人模型输出“测算结果”中的 5 个字段:
|
||||
- `历史利率`
|
||||
- `产品最低利率下限`
|
||||
- `平滑幅度`
|
||||
- `最终测算利率`
|
||||
- `参考利率`
|
||||
- 在后端 `ModelRetailOutputFields` 中新增对应 5 个字段定义
|
||||
- 在零售模型 mock 数据中补齐对应 5 个字段样例值
|
||||
- 新增零售模型输出表结构迁移脚本,并同步更新建表基线 SQL
|
||||
- 新增后端字段断言测试与前端源码断言脚本
|
||||
|
||||
## 修改文件
|
||||
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/ModelRetailOutputFields.java`
|
||||
- `ruoyi-loan-pricing/src/main/resources/data/retail_output.json`
|
||||
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/domain/entity/ModelRetailOutputFieldsTest.java`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue`
|
||||
- `ruoyi-ui/src/views/loanPricing/workflow/components/ModelOutputDisplay.vue`
|
||||
- `ruoyi-ui/tests/retail-display-fields.test.js`
|
||||
- `ruoyi-ui/package.json`
|
||||
- `sql/add_model_retail_output_rate_fields_20260403.sql`
|
||||
- `sql/model_retail.sql`
|
||||
- `sql/loan_pricing_schema_20260328.sql`
|
||||
- `sql/loan_pricing_prod_init_20260331.sql`
|
||||
- `doc/2026-04-03-retail-display-fields-design.md`
|
||||
- `doc/2026-04-03-retail-display-fields-backend-plan.md`
|
||||
- `doc/2026-04-03-retail-display-fields-frontend-plan.md`
|
||||
- `doc/implementation-report-2026-04-03-retail-display-fields.md`
|
||||
|
||||
## 验证方式
|
||||
1. 新增后端测试,断言 `ModelRetailOutputFields` 包含 5 个新增字段,先失败后通过
|
||||
2. 新增前端源码断言脚本,断言个人详情页与模型输出页已补齐字段,先失败后通过
|
||||
3. 执行前端生产构建,确认页面代码可正常打包
|
||||
4. 检查开发库 `model_retail_output_fields` 表结构,确认最初缺少 5 个新列
|
||||
5. 执行 `sql/add_model_retail_output_rate_fields_20260403.sql` 到开发库,并再次确认 5 个新列存在
|
||||
6. 重新编译并重启后端,确保新的实体字段已进入运行中的 SQL 映射
|
||||
7. 创建新的个人流程 `20260403100514909`,调用详情接口确认返回以下真实值:
|
||||
- `loanRateHistory = 6.40`
|
||||
- `minRateProduct = 5.50`
|
||||
- `smoothRange = -0.10`
|
||||
- `finalCalculateRate = 6.05`
|
||||
- `referenceRate = 5.95`
|
||||
8. 启动前端开发服务并使用浏览器自动化打开详情页,确认:
|
||||
- 页面出现 `借款期限`
|
||||
- 切换到“测算结果”页签后,5 个新增字段及对应值均可见
|
||||
9. 验证结束后,停止本次启动的前后端进程
|
||||
|
||||
## 验证结果
|
||||
- `mvn -pl ruoyi-loan-pricing -Dtest=ModelRetailOutputFieldsTest test` 首次失败,补齐后通过
|
||||
- `npm --prefix ruoyi-ui run test:retail-display-fields` 首次失败,补齐后通过
|
||||
- `npm --prefix ruoyi-ui run build:prod` 成功,输出包含 `Build complete.`
|
||||
- 已确认开发库 `model_retail_output_fields` 初始缺少:
|
||||
- `loan_rate_history`
|
||||
- `min_rate_product`
|
||||
- `smooth_range`
|
||||
- `final_calculate_rate`
|
||||
- `reference_rate`
|
||||
- 已执行迁移脚本并确认以上 5 列存在
|
||||
- 已确认旧后端进程因未加载最新依赖导致 SQL 仍缺新列,重编译并重启后问题消失
|
||||
- 已创建个人流程 `20260403100514909` 并通过详情接口拿到 5 个新增字段的真实值
|
||||
- 已通过浏览器自动化确认个人详情页展示位与“测算结果”页签展示均正确
|
||||
- 本次验证期间启动的前后端进程均已停止
|
||||
|
||||
## 说明
|
||||
- `loanTerm` 本次仅补齐详情页展示位;个人创建表单当前无该字段录入入口,不属于本次“模型返回字段更新”范围
|
||||
- 为保证新字段在新环境中也可正常落库,本次同步更新了零售模型输出表的建表基线 SQL
|
||||
@@ -170,6 +170,21 @@ public class ModelRetailOutputFields {
|
||||
// 测算利率
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,12 @@
|
||||
"bpGreyOverdue": "98",
|
||||
"totoalBpRisk": "95",
|
||||
"totalBp": "350",
|
||||
"calculateRate": "6.15"
|
||||
"calculateRate": "6.15",
|
||||
"loanRateHistory": "6.40",
|
||||
"minRateProduct": "5.50",
|
||||
"smoothRange": "-0.10",
|
||||
"finalCalculateRate": "6.05",
|
||||
"referenceRate": "5.95"
|
||||
},
|
||||
"extensionMap": {},
|
||||
"reasonMessage": "Running successfully",
|
||||
@@ -65,4 +70,4 @@
|
||||
"workflowVersion": 14,
|
||||
"callTime": 1736405548630,
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.ruoyi.loanpricing.domain.entity;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ModelRetailOutputFieldsTest
|
||||
{
|
||||
@Test
|
||||
void shouldContainLatestRetailDisplayRateFields()
|
||||
{
|
||||
Set<String> fieldNames = Arrays.stream(ModelRetailOutputFields.class.getDeclaredFields())
|
||||
.map(Field::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertTrue(fieldNames.contains("loanRateHistory"), "缺少字段 loanRateHistory");
|
||||
assertTrue(fieldNames.contains("minRateProduct"), "缺少字段 minRateProduct");
|
||||
assertTrue(fieldNames.contains("smoothRange"), "缺少字段 smoothRange");
|
||||
assertTrue(fieldNames.contains("finalCalculateRate"), "缺少字段 finalCalculateRate");
|
||||
assertTrue(fieldNames.contains("referenceRate"), "缺少字段 referenceRate");
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@
|
||||
"build:prod": "vue-cli-service build",
|
||||
"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:password-transfer": "node tests/password-transfer-api.test.js",
|
||||
"test:retail-display-fields": "node tests/retail-display-fields.test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
|
||||
@@ -93,6 +93,11 @@
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@@ -72,6 +72,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="借款期限">{{ detailData.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否有经营佐证">{{
|
||||
formatBoolean(detailData.bizProof)
|
||||
}}
|
||||
|
||||
29
ruoyi-ui/tests/retail-display-fields.test.js
Normal file
29
ruoyi-ui/tests/retail-display-fields.test.js
Normal file
@@ -0,0 +1,29 @@
|
||||
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')
|
||||
const modelOutput = read('src/views/loanPricing/workflow/components/ModelOutputDisplay.vue')
|
||||
|
||||
assert(
|
||||
personalDetail.includes('label="借款期限"') && personalDetail.includes('detailData.loanTerm'),
|
||||
'个人详情页缺少借款期限展示'
|
||||
)
|
||||
|
||||
const requiredRetailFields = [
|
||||
'retailOutput.loanRateHistory',
|
||||
'retailOutput.minRateProduct',
|
||||
'retailOutput.smoothRange',
|
||||
'retailOutput.finalCalculateRate',
|
||||
'retailOutput.referenceRate'
|
||||
]
|
||||
|
||||
requiredRetailFields.forEach((field) => {
|
||||
assert(modelOutput.includes(field), `模型输出缺少字段展示: ${field}`)
|
||||
})
|
||||
|
||||
console.log('retail display fields assertions passed')
|
||||
6
sql/add_model_retail_output_rate_fields_20260403.sql
Normal file
6
sql/add_model_retail_output_rate_fields_20260403.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE `model_retail_output_fields`
|
||||
ADD COLUMN `loan_rate_history` varchar(100) DEFAULT '' COMMENT '历史利率' AFTER `calculate_rate`,
|
||||
ADD COLUMN `min_rate_product` varchar(100) DEFAULT '' COMMENT '产品最低利率下限' AFTER `loan_rate_history`,
|
||||
ADD COLUMN `smooth_range` varchar(100) DEFAULT '' COMMENT '平滑幅度' AFTER `min_rate_product`,
|
||||
ADD COLUMN `final_calculate_rate` varchar(100) DEFAULT '' COMMENT '最终测算利率' AFTER `smooth_range`,
|
||||
ADD COLUMN `reference_rate` varchar(100) DEFAULT '' COMMENT '参考利率' AFTER `final_calculate_rate`;
|
||||
@@ -908,6 +908,11 @@ CREATE TABLE `model_retail_output_fields` (
|
||||
`totoal_bp_risk` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'TOTAL_BP_风险度',
|
||||
`total_bp` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '浮动BP',
|
||||
`calculate_rate` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '测算利率',
|
||||
`loan_rate_history` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '历史利率',
|
||||
`min_rate_product` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '产品最低利率下限',
|
||||
`smooth_range` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '平滑幅度',
|
||||
`final_calculate_rate` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '最终测算利率',
|
||||
`reference_rate` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '参考利率',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='零售模型输出字段表';
|
||||
|
||||
@@ -490,6 +490,11 @@ CREATE TABLE `model_retail_output_fields` (
|
||||
`totoal_bp_risk` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'TOTAL_BP_风险度',
|
||||
`total_bp` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '浮动BP',
|
||||
`calculate_rate` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '测算利率',
|
||||
`loan_rate_history` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '历史利率',
|
||||
`min_rate_product` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '产品最低利率下限',
|
||||
`smooth_range` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '平滑幅度',
|
||||
`final_calculate_rate` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '最终测算利率',
|
||||
`reference_rate` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '参考利率',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='零售模型输出字段表';
|
||||
|
||||
@@ -106,9 +106,19 @@ CREATE TABLE IF NOT EXISTS model_retail_output_fields (
|
||||
total_bp VARCHAR(100) DEFAULT '' COMMENT '浮动BP',
|
||||
-- 测算利率(百分比,如6.15)
|
||||
calculate_rate VARCHAR(100) DEFAULT '' COMMENT '测算利率',
|
||||
-- 历史利率
|
||||
loan_rate_history VARCHAR(100) DEFAULT '' COMMENT '历史利率',
|
||||
-- 产品最低利率下限
|
||||
min_rate_product VARCHAR(100) DEFAULT '' COMMENT '产品最低利率下限',
|
||||
-- 平滑幅度
|
||||
smooth_range VARCHAR(100) DEFAULT '' COMMENT '平滑幅度',
|
||||
-- 最终测算利率
|
||||
final_calculate_rate VARCHAR(100) DEFAULT '' COMMENT '最终测算利率',
|
||||
-- 参考利率
|
||||
reference_rate VARCHAR(100) DEFAULT '' COMMENT '参考利率',
|
||||
-- 创建时间(审计字段)
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
-- 主键约束
|
||||
PRIMARY KEY (id)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='零售模型输出字段表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='零售模型输出字段表';
|
||||
|
||||
Reference in New Issue
Block a user