Compare commits
3 Commits
3100b36906
...
9cb77b096e
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cb77b096e | |||
| b3ba6fd59d | |||
| 2aef6701c2 |
@@ -0,0 +1,412 @@
|
||||
# 项目流水标签后端详细日志设计
|
||||
|
||||
## 概述
|
||||
|
||||
本次设计面向“项目流水标签”后端链路补充详细日志提醒能力,覆盖手动重算、自动触发、项目级互斥、规则级执行、参数解析、结果落库和自动补跑全过程。
|
||||
|
||||
目标同时满足两类需求:
|
||||
|
||||
- 排障:出现“没有触发”“任务卡住”“规则没执行”“结果为 0”“自动补跑未生效”等问题时,能够通过日志快速定位断点
|
||||
- 审计:能够追踪是谁在什么时间,对哪个项目、哪个模型发起了手动重算,以及本次重算的结果摘要
|
||||
|
||||
本次设计只补应用日志,不调整数据库表结构,不新增前端展示,不引入 AOP、链路追踪框架或独立审计表。
|
||||
|
||||
## 已确认范围
|
||||
|
||||
- 日志面向“排障 + 审计”双目标
|
||||
- 日志保存位置沿用现有后端应用日志
|
||||
- 记录手动重算与自动触发两类入口
|
||||
- 记录项目级互斥、补跑标记、补跑消费过程
|
||||
- 记录任务级摘要、规则级执行、结果清理和结果写入
|
||||
- 记录规则参数解析来源和结果
|
||||
- 阈值参数值允许打印
|
||||
- 身份证号、账号、`objectKey` 等敏感字段不打印明文
|
||||
- 不打印 SQL 明细
|
||||
- 命中明细不按条展开到 `info`
|
||||
|
||||
## 现状问题
|
||||
|
||||
当前与流水标签相关的主要代码位于:
|
||||
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java`
|
||||
|
||||
现状上,“文件上传 / 拉取本行信息”链路已有较多日志,但“流水标签重算”核心链路日志不足,主要存在以下问题:
|
||||
|
||||
1. 手动重算和自动触发进入标签链路后,缺少统一入口日志
|
||||
2. 项目级互斥和 `needRerun` 标记逻辑几乎不可观测
|
||||
3. 标签任务开始、结束、失败都没有清晰摘要
|
||||
4. 单条规则执行的耗时、命中数、阈值来源无法定位
|
||||
5. 结果清理和批量写库缺少过程确认
|
||||
|
||||
这会导致问题发生时难以区分:
|
||||
|
||||
- 是入口未触发
|
||||
- 是被项目级互斥拦截
|
||||
- 是规则没有命中
|
||||
- 是规则执行失败
|
||||
- 是结果写库失败
|
||||
- 是补跑标记了但未真正再次执行
|
||||
|
||||
## 方案对比
|
||||
|
||||
### 方案一:在现有方法中直接补日志
|
||||
|
||||
- 在现有类中按节点直接补 `info / debug / warn / error`
|
||||
- 不做统一模板
|
||||
|
||||
优点:
|
||||
|
||||
- 改动最小
|
||||
- 落地最快
|
||||
- 与当前项目写法最接近
|
||||
|
||||
缺点:
|
||||
|
||||
- 日志字段格式容易分散
|
||||
- 后续继续扩展时容易重复和漂移
|
||||
|
||||
### 方案二:统一字段格式的轻量日志设计
|
||||
|
||||
- 在现有类中补日志
|
||||
- 统一核心上下文字段和文案结构
|
||||
- 允许通过少量私有辅助方法减少重复
|
||||
|
||||
优点:
|
||||
|
||||
- 兼顾快速落地和长期可检索性
|
||||
- 同时适合排障和审计
|
||||
- 不引入额外框架,风险较低
|
||||
|
||||
缺点:
|
||||
|
||||
- 比方案一多一点整理成本
|
||||
|
||||
### 方案三:日志之外再做持久化审计摘要
|
||||
|
||||
- 除应用日志外,再把关键摘要落到任务表或独立审计表
|
||||
|
||||
优点:
|
||||
|
||||
- 审计能力最强
|
||||
- 不依赖日志平台
|
||||
|
||||
缺点:
|
||||
|
||||
- 超出本次“补详细日志提醒”的范围
|
||||
- 会引入表结构或数据模型变更
|
||||
|
||||
## 最终方案
|
||||
|
||||
采用方案二:统一字段格式的轻量日志设计。
|
||||
|
||||
具体原则如下:
|
||||
|
||||
1. 只在现有后端类补日志,不改数据库结构
|
||||
2. `info` 负责任务摘要和关键里程碑
|
||||
3. `debug` 负责规则参数、命中数量、批处理细节
|
||||
4. `warn` 负责互斥、降级、参数缺失、无命中等非致命异常
|
||||
5. `error` 负责任务失败、规则异常、写库失败、触发失败
|
||||
6. 日志字段尽量统一,保证同一任务可以通过 `projectId/taskId` 串起来
|
||||
|
||||
## 日志等级设计
|
||||
|
||||
### info
|
||||
|
||||
用于记录:
|
||||
|
||||
- 手动重算入口
|
||||
- 自动触发入口
|
||||
- 获取项目锁成功
|
||||
- 任务创建成功
|
||||
- 规则加载完成
|
||||
- 历史结果清理开始
|
||||
- 单规则开始和结束摘要
|
||||
- 结果批量写入摘要
|
||||
- 任务成功摘要
|
||||
- 自动补跑开始和结束
|
||||
|
||||
### debug
|
||||
|
||||
用于记录:
|
||||
|
||||
- 规则阈值参数
|
||||
- 参数来源项目
|
||||
- 规则命中明细数量
|
||||
- 无需写库时的空结果分支
|
||||
- 补跑标记消费细节
|
||||
|
||||
### warn
|
||||
|
||||
用于记录:
|
||||
|
||||
- 手动重算被运行中任务拒绝
|
||||
- 自动触发命中运行中任务,仅标记 `needRerun`
|
||||
- 自动触发被跳过
|
||||
- 规则参数缺失
|
||||
- 规则执行结果为空或无命中
|
||||
|
||||
### error
|
||||
|
||||
用于记录:
|
||||
|
||||
- 任务整体失败
|
||||
- 单规则执行异常
|
||||
- 结果写库异常
|
||||
- 参数解析过程中出现不可恢复异常
|
||||
- 自动触发或补跑异常
|
||||
|
||||
## 统一上下文字段
|
||||
|
||||
建议所有流水标签日志尽量带上以下字段:
|
||||
|
||||
- `projectId`
|
||||
- `taskId`
|
||||
- `modelCode`
|
||||
- `triggerType`
|
||||
- `operator`
|
||||
- `ruleCode`
|
||||
- `costMs`
|
||||
- `hitCount`
|
||||
|
||||
其中:
|
||||
|
||||
- 任务创建前无法取得 `taskId` 的场景允许缺省
|
||||
- 与单规则无关的日志可以不打印 `ruleCode`
|
||||
- 与耗时无关的日志可以不打印 `costMs`
|
||||
|
||||
## 脱敏规则
|
||||
|
||||
本次日志遵循以下脱敏边界:
|
||||
|
||||
- 允许打印阈值参数编码和值
|
||||
- 不打印身份证号明文
|
||||
- 不打印账号明文
|
||||
- 不打印完整 `objectKey`
|
||||
- 不打印规则 SQL
|
||||
- 不在 `info` 级别展开逐条命中结果
|
||||
|
||||
若后续需要定位对象级命中,可在 `debug` 级别打印脱敏后的对象标识摘要,例如前 3 位加后 2 位,但本次设计不要求默认展开。
|
||||
|
||||
## 打点设计
|
||||
|
||||
### 1. 入口层
|
||||
|
||||
涉及类:
|
||||
|
||||
- `CcdiBankTagController`
|
||||
- `CcdiFileUploadServiceImpl`
|
||||
|
||||
目标:
|
||||
|
||||
- 明确日志是从手动入口还是自动入口进入
|
||||
- 对自动触发链路补“已触发 / 已跳过”判断
|
||||
|
||||
建议日志:
|
||||
|
||||
```text
|
||||
【流水标签】收到手动重算请求: projectId={}, modelCode={}, operator={}
|
||||
【流水标签】批处理完成,准备触发自动重算: projectId={}, triggerType={}, anySuccess={}
|
||||
【流水标签】跳过自动重算: projectId={}, triggerType={}, reason=all_records_failed
|
||||
```
|
||||
|
||||
### 2. 协调层
|
||||
|
||||
涉及类:
|
||||
|
||||
- `ProjectBankTagRebuildCoordinator`
|
||||
|
||||
目标:
|
||||
|
||||
- 观测项目级互斥
|
||||
- 区分“任务丢失”和“任务被合并补跑”
|
||||
- 记录锁获取、锁释放、补跑消费全过程
|
||||
|
||||
建议日志:
|
||||
|
||||
```text
|
||||
【流水标签】手动重算开始排队: projectId={}, modelCode={}, operator={}
|
||||
【流水标签】项目已有运行中任务,拒绝手动重算: projectId={}, modelCode={}, operator={}
|
||||
【流水标签】项目正在重算,已标记完成后补跑: projectId={}, runningTaskId={}, triggerType={}
|
||||
【流水标签】获取项目重算锁成功: projectId={}
|
||||
【流水标签】检测到需要补跑,准备再次执行: projectId={}, previousTaskId={}
|
||||
【流水标签】未检测到补跑标记,结束自动重算: projectId={}, taskId={}
|
||||
【流水标签】释放项目重算锁: projectId={}
|
||||
```
|
||||
|
||||
### 3. 执行层
|
||||
|
||||
涉及类:
|
||||
|
||||
- `CcdiBankTagServiceImpl`
|
||||
|
||||
目标:
|
||||
|
||||
- 形成任务级生命周期日志
|
||||
- 形成规则级执行和写库摘要
|
||||
|
||||
建议日志:
|
||||
|
||||
```text
|
||||
【流水标签】任务创建成功: taskId={}, projectId={}, modelCode={}, triggerType={}, operator={}
|
||||
【流水标签】加载启用规则完成: taskId={}, projectId={}, modelCode={}, ruleCount={}
|
||||
【流水标签】开始清理历史结果: taskId={}, projectId={}, modelCode={}
|
||||
【流水标签】规则开始执行: taskId={}, projectId={}, ruleCode={}, resultType={}
|
||||
【流水标签】规则执行参数: taskId={}, ruleCode={}, thresholds={}
|
||||
【流水标签】规则执行完成: taskId={}, projectId={}, ruleCode={}, hitCount={}, costMs={}
|
||||
【流水标签】规则无命中: taskId={}, projectId={}, ruleCode={}, costMs={}
|
||||
【流水标签】批量写入标签结果: taskId={}, projectId={}, resultCount={}
|
||||
【流水标签】任务执行成功: taskId={}, projectId={}, modelCode={}, triggerType={}, ruleCount={}, hitCount={}, costMs={}
|
||||
【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}
|
||||
```
|
||||
|
||||
### 4. 参数解析层
|
||||
|
||||
涉及类:
|
||||
|
||||
- `BankTagRuleConfigResolver`
|
||||
|
||||
目标:
|
||||
|
||||
- 说明阈值从项目默认配置还是项目专属配置解析而来
|
||||
- 在参数缺失时明确记录缺了哪些编码
|
||||
|
||||
建议日志:
|
||||
|
||||
```text
|
||||
【流水标签】解析规则参数: projectId={}, effectiveProjectId={}, ruleCode={}, requiredParams={}
|
||||
【流水标签】规则参数解析结果: projectId={}, ruleCode={}, thresholdValues={}
|
||||
【流水标签】规则参数缺失: projectId={}, ruleCode={}, missingParams={}
|
||||
```
|
||||
|
||||
## 关键异常场景设计
|
||||
|
||||
### 手动重算被拒绝
|
||||
|
||||
当项目已经存在运行中任务时:
|
||||
|
||||
- 抛出原有业务异常
|
||||
- 额外补 `warn`,明确项目、模型、操作人和拒绝原因
|
||||
|
||||
目的:
|
||||
|
||||
- 便于区分“接口未进来”和“接口进来了但被互斥挡住”
|
||||
|
||||
### 自动重算被合并
|
||||
|
||||
当批量上传或拉取本行触发自动重算时,如果项目已在运行中:
|
||||
|
||||
- 不再直接丢弃
|
||||
- 通过 `markNeedRerun` 标记补跑
|
||||
- 日志明确说明当前触发未丢失,而是等待本轮完成后自动重跑
|
||||
|
||||
### 参数缺失但继续执行
|
||||
|
||||
当前规则参数解析器未对所有缺失参数直接抛错,部分规则可能按空值或 `0` 继续执行。
|
||||
|
||||
此场景需要:
|
||||
|
||||
- `warn` 记录缺失参数编码
|
||||
- `debug` 记录实际解析到的阈值集合
|
||||
- 文案中说明本次按当前默认值继续执行
|
||||
|
||||
### 单规则执行失败
|
||||
|
||||
当某条规则在查询或结果构造过程中抛异常时:
|
||||
|
||||
- `error` 记录 `taskId/projectId/ruleCode`
|
||||
- 保留原有失败传播语义
|
||||
- 由任务级失败日志补充整任务摘要
|
||||
|
||||
### 结果写库失败
|
||||
|
||||
当历史结果已清理但新结果写入失败时:
|
||||
|
||||
- `error` 记录失败发生在“结果写入”阶段
|
||||
- 日志中带上准备写入的结果条数
|
||||
|
||||
这样可以避免误判为“规则无命中”。
|
||||
|
||||
### 自动补跑消费
|
||||
|
||||
自动重算完成后如果检测到 `needRerun=1`:
|
||||
|
||||
- `info` 记录上一轮任务 ID 和将进入补跑
|
||||
- `debug` 记录补跑标记消费结果
|
||||
|
||||
如果没有检测到补跑标记,也应记录收尾日志,避免看到“开始”却看不到“结束”。
|
||||
|
||||
## 建议实现方式
|
||||
|
||||
为控制改动范围,本次不新增独立日志组件,优先采用以下实现策略:
|
||||
|
||||
1. 在相关类中直接补充结构统一的日志
|
||||
2. 对重复字段较多的场景,可增加少量私有辅助方法拼接摘要
|
||||
3. 不为了日志而引入 ThreadLocal、MDC、AOP 或统一切面
|
||||
|
||||
这样可以把改动集中在当前标签链路相关类中,降低回归风险。
|
||||
|
||||
## 验证策略
|
||||
|
||||
本次重点验证“行为分支可被覆盖”,不建议编写对日志文本本身高度耦合的脆弱断言。
|
||||
|
||||
建议关注以下测试:
|
||||
|
||||
### 协调器测试
|
||||
|
||||
- 已有运行中任务时,手动重算被拒绝
|
||||
- 自动触发时命中运行中任务并标记 `needRerun`
|
||||
- 自动触发完成后成功消费补跑标记
|
||||
|
||||
### 标签服务测试
|
||||
|
||||
- 启用规则数为 0
|
||||
- 规则执行有命中且成功写库
|
||||
- 规则执行无命中时不写库
|
||||
- 单规则抛异常导致任务失败
|
||||
|
||||
### 参数解析测试
|
||||
|
||||
- 使用默认项目参数
|
||||
- 使用项目专属参数
|
||||
- 存在参数缺失时返回缺失状态
|
||||
|
||||
## 非目标
|
||||
|
||||
本次设计不包含以下内容:
|
||||
|
||||
- 新增数据库表或审计表
|
||||
- 前端页面展示任务执行日志
|
||||
- 接入链路追踪系统
|
||||
- 打印规则 SQL
|
||||
- 打印对象或账号明细
|
||||
- 调整标签任务执行策略或线程池模型
|
||||
|
||||
## 预期效果
|
||||
|
||||
落地后,开发和运维应能够通过日志快速回答以下问题:
|
||||
|
||||
- 手动重算是否真正进入后端
|
||||
- 自动触发是否提交成功,还是因为整批失败而跳过
|
||||
- 项目当前是否被互斥锁住
|
||||
- 自动触发是否被合并为补跑
|
||||
- 本次任务创建了哪个 `taskId`
|
||||
- 加载了多少条规则
|
||||
- 哪条规则执行最慢、命中多少
|
||||
- 参数是否来自默认配置,是否存在缺失
|
||||
- 结果是否已删除旧数据并完成新写入
|
||||
- 任务最终成功还是失败,失败在哪个阶段
|
||||
|
||||
## 落地范围
|
||||
|
||||
建议本次代码改动控制在以下文件附近:
|
||||
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java`
|
||||
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java`
|
||||
|
||||
如需进一步实现,可在此设计基础上继续拆分为具体实现计划。
|
||||
@@ -1,253 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 模型参数配置 - 快速功能验证脚本
|
||||
# 用于手动验证端到端功能
|
||||
|
||||
echo "======================================"
|
||||
echo "模型参数配置 - 功能验证"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# 颜色定义
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 测试计数
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
# 测试函数
|
||||
test_case() {
|
||||
local name=$1
|
||||
local expected=$2
|
||||
local actual=$3
|
||||
|
||||
if [ "$expected" == "$actual" ]; then
|
||||
echo -e "${GREEN}✓${NC} $name"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} $name"
|
||||
echo " 预期: $expected"
|
||||
echo " 实际: $actual"
|
||||
((FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "1️⃣ 检查前端代码"
|
||||
echo "-------------------------------------------"
|
||||
|
||||
# 检查文件是否存在
|
||||
if [ -f "ruoyi-ui/src/views/ccdi/modelParam/index.vue" ]; then
|
||||
echo -e "${GREEN}✓${NC} 全局配置页面文件存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 全局配置页面文件不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
if [ -f "ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue" ]; then
|
||||
echo -e "${GREEN}✓${NC} 项目配置页面文件存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 项目配置页面文件不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查API方法
|
||||
if grep -q "listAllParams" ruoyi-ui/src/api/ccdi/modelParam.js; then
|
||||
echo -e "${GREEN}✓${NC} listAllParams API方法存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} listAllParams API方法不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
if grep -q "saveAllParams" ruoyi-ui/src/api/ccdi/modelParam.js; then
|
||||
echo -e "${GREEN}✓${NC} saveAllParams API方法存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} saveAllParams API方法不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "2️⃣ 检查loading状态"
|
||||
echo "-------------------------------------------"
|
||||
|
||||
# 检查loading变量
|
||||
if grep -q "loading: false" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} loading状态变量已定义"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} loading状态变量未定义"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查v-loading指令
|
||||
if grep -q "v-loading=\"loading\"" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} v-loading指令已使用"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} v-loading指令未使用"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查空状态
|
||||
if grep -q "暂无参数配置数据" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} 空状态提示已添加"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 空状态提示未添加"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "3️⃣ 检查修改追踪"
|
||||
echo "-------------------------------------------"
|
||||
|
||||
# 检查markAsModified方法
|
||||
if grep -q "markAsModified" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} markAsModified方法存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} markAsModified方法不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查$set使用
|
||||
if grep -q "\$set" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} 使用$set确保响应式"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 未使用$set"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查修改计数
|
||||
if grep -q "modifiedCount" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} modifiedCount计算属性存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} modifiedCount计算属性不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "4️⃣ 检查保存功能"
|
||||
echo "-------------------------------------------"
|
||||
|
||||
# 检查保存方法
|
||||
if grep -q "handleSaveAll" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} handleSaveAll方法存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} handleSaveAll方法不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查saving状态
|
||||
if grep -q "saving: false" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} saving状态变量已定义"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} saving状态变量未定义"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查按钮loading
|
||||
if grep -q ":loading=\"saving\"" ruoyi-ui/src/views/ccdi/modelParam/index.vue; then
|
||||
echo -e "${GREEN}✓${NC} 保存按钮绑定loading状态"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 保存按钮未绑定loading状态"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "5️⃣ 检查后端接口"
|
||||
echo "-------------------------------------------"
|
||||
|
||||
# 检查Mapper方法
|
||||
if grep -q "selectByProjectId" ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiModelParamMapper.java 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} selectByProjectId Mapper方法存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} selectByProjectId Mapper方法不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查Service方法
|
||||
if grep -q "selectAllParams" ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiModelParamService.java 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} selectAllParams Service方法存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} selectAllParams Service方法不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查Controller接口
|
||||
if grep -q "listAll" ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiModelParamController.java 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} listAll Controller接口存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} listAll Controller接口不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
if grep -q "saveAll" ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiModelParamController.java 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} saveAll Controller接口存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} saveAll Controller接口不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "6️⃣ 检查测试文件"
|
||||
echo "-------------------------------------------"
|
||||
|
||||
# 检查测试文件
|
||||
if [ -f "ruoyi-ui/tests/e2e/model-param-config.test.js" ]; then
|
||||
echo -e "${GREEN}✓${NC} 端到端测试文件存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 端到端测试文件不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
# 检查测试计划
|
||||
if [ -f "docs/test-plans/2026-03-09-e2e-test-plan.md" ]; then
|
||||
echo -e "${GREEN}✓${NC} 测试计划文档存在"
|
||||
((PASS++))
|
||||
else
|
||||
echo -e "${RED}✗${NC} 测试计划文档不存在"
|
||||
((FAIL++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo -e "测试结果: ${GREEN}通过 $PASS${NC} / ${RED}失败 $FAIL${NC}"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ 所有检查项通过!${NC}"
|
||||
echo ""
|
||||
echo "📋 下一步:"
|
||||
echo "1. 启动后端服务: mvn spring-boot:run"
|
||||
echo "2. 启动前端服务: cd ruoyi-ui && npm run dev"
|
||||
echo "3. 访问页面: http://localhost:80/ccdi/modelParam"
|
||||
echo "4. 手动验证功能:"
|
||||
echo " - 查看loading效果"
|
||||
echo " - 修改参数,查看修改数量"
|
||||
echo " - 保存参数,查看保存状态"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}❌ 有 $FAIL 项检查未通过${NC}"
|
||||
echo ""
|
||||
echo "请检查上述失败项并修复"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,97 +0,0 @@
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
DEPLOY_PS1 = REPO_ROOT / "deploy" / "deploy.ps1"
|
||||
DEPLOY_BAT = REPO_ROOT / "deploy" / "deploy-to-nas.bat"
|
||||
DOCKER_COMPOSE = REPO_ROOT / "docker-compose.yml"
|
||||
ENV_EXAMPLE = REPO_ROOT / ".env.example"
|
||||
LSFX_MOCK_COMPOSE = REPO_ROOT / "lsfx-mock-server" / "docker-compose.yml"
|
||||
|
||||
|
||||
def run_powershell(*args):
|
||||
return subprocess.run(
|
||||
[
|
||||
"powershell",
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-File",
|
||||
str(DEPLOY_PS1),
|
||||
*args,
|
||||
],
|
||||
cwd=REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def run_bat(*args):
|
||||
return subprocess.run(
|
||||
[
|
||||
"cmd.exe",
|
||||
"/c",
|
||||
str(DEPLOY_BAT),
|
||||
*args,
|
||||
],
|
||||
cwd=REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def test_deploy_ps1_dry_run_prints_target():
|
||||
result = run_powershell("-DryRun")
|
||||
output = f"{result.stdout}\n{result.stderr}"
|
||||
|
||||
assert result.returncode == 0
|
||||
assert "DryRun" in output
|
||||
assert "116.62.17.81" in output
|
||||
assert "9444" in output
|
||||
assert "/volume1/webapp/ccdi" in output
|
||||
|
||||
|
||||
def test_bat_dry_run_uses_default_nas_target():
|
||||
result = run_bat("--dry-run")
|
||||
output = f"{result.stdout}\n{result.stderr}"
|
||||
|
||||
assert result.returncode == 0
|
||||
assert "DryRun" in output
|
||||
assert "116.62.17.81" in output
|
||||
assert "9444" in output
|
||||
assert "/volume1/webapp/ccdi" in output
|
||||
|
||||
|
||||
def test_bat_dry_run_accepts_override_arguments():
|
||||
result = run_bat("1.2.3.4", "9000", "demo", "demoPwd", "/demo/path", "--dry-run")
|
||||
output = f"{result.stdout}\n{result.stderr}"
|
||||
|
||||
assert result.returncode == 0
|
||||
assert "1.2.3.4" in output
|
||||
assert "9000" in output
|
||||
assert "demo" in output
|
||||
assert "/demo/path" in output
|
||||
|
||||
|
||||
def test_compose_exposes_lsfx_mock_port_via_backend_namespace():
|
||||
compose_text = DOCKER_COMPOSE.read_text(encoding="utf-8")
|
||||
|
||||
assert '${LSFX_MOCK_PORT:-62320}:8000' in compose_text
|
||||
assert 'network_mode: "service:backend"' in compose_text
|
||||
|
||||
|
||||
def test_compose_defaults_backend_profile_to_nas():
|
||||
compose_text = DOCKER_COMPOSE.read_text(encoding="utf-8")
|
||||
env_text = ENV_EXAMPLE.read_text(encoding="utf-8")
|
||||
|
||||
assert '${SPRING_PROFILES_ACTIVE:-nas}' in compose_text
|
||||
assert 'SPRING_PROFILES_ACTIVE=nas' in env_text
|
||||
|
||||
|
||||
def test_lsfx_mock_standalone_compose_exposes_62320():
|
||||
compose_text = LSFX_MOCK_COMPOSE.read_text(encoding="utf-8")
|
||||
|
||||
assert '"62320:8000"' in compose_text
|
||||
@@ -1,22 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
getUploadFileAction,
|
||||
getUploadFileStatusText,
|
||||
getUploadFileStatusType
|
||||
} from "../../ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.js";
|
||||
|
||||
assert.deepEqual(getUploadFileAction("parsed_failed"), {
|
||||
key: "viewError",
|
||||
text: "查看错误原因"
|
||||
});
|
||||
assert.deepEqual(getUploadFileAction("parsed_success"), {
|
||||
key: "delete",
|
||||
text: "删除"
|
||||
});
|
||||
assert.equal(getUploadFileAction("uploading"), null);
|
||||
assert.equal(getUploadFileAction("parsing"), null);
|
||||
assert.equal(getUploadFileAction("deleted"), null);
|
||||
assert.equal(getUploadFileStatusText("deleted"), "已删除");
|
||||
assert.equal(getUploadFileStatusType("deleted"), "info");
|
||||
|
||||
console.log("PASS");
|
||||
Reference in New Issue
Block a user