692 KiB
修改记录
2024-12-19 - ScenarioBindingView 集成真实场景配置数据
修改文件: X1.WebUI/src/pages/scenarios/scenario-binding/ScenarioBindingView.tsx
修改内容:
- 集成 scenarioService,使用真实的场景配置数据替代模拟数据
- 添加场景数据加载逻辑
loadScenarios(),从后端获取所有场景配置 - 实现按场景类型分组功能,将场景数据按类型组织成树形结构
- 添加场景数据刷新按钮,支持手动刷新场景列表
- 左侧 ScenarioCategoryTree 现在显示真实的场景配置数据,按类型分组
具体变更:
- 导入 scenarioService 和 TestScenario 类型
- 添加场景数据状态管理:
scenarios、scenarioCategories、loadingScenarios - 实现
loadScenarios()函数,调用scenarioService.getTestScenarios() - 按场景类型分组逻辑:
scenario.type作为分组键 - 在 useEffect 中同时加载场景和测试用例数据
- 为左侧面板添加刷新按钮,支持手动刷新场景数据
数据流程:
- ScenarioConfigTable.tsx 显示场景配置列表
- ScenarioBindingView.tsx 获取场景配置数据
- 按场景类型分组:功能测试、性能测试、压力测试等
- ScenarioCategoryTree.tsx 以树形结构显示分组后的场景
效果: 左侧场景类别树现在显示真实的场景配置数据,按类型分组,与 ScenarioConfigTable.tsx 的数据源保持一致。
2024-12-19 - ScenarioConfigForm.tsx 内间距优化
修改文件: X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigForm.tsx
修改内容:
- 为表单容器添加
p-4内边距,让表单内容与容器边缘保持适当距离 - 将表单字段间距设置为
space-y-4,提供合适的视觉间距 - 将每个字段组内的间距设置为
space-y-2,保持标签和输入框之间的紧凑间距 - 保持表单的最大高度限制和滚动功能
具体变更:
- 表单容器:
className="space-y-4 max-h-[70vh] overflow-y-auto p-4" - 字段组间距:所有字段组使用
space-y-2 - 表单整体间距:使用
space-y-4
效果: 表单内容现在有适中的内边距和间距,既不会太紧凑也不会太松散,视觉效果平衡。
2025-01-22 - ScenarioConfigView 和 ScenarioBindingView 组件样式重构
修改时间: 2025-01-22
修改原因:
用户反映 scenario-config 和 scenario-binding 相关组件底部遗留很多空白,需要重构样式解决布局问题。CSS 的 h-full 和 min-h-0 在某些情况下无法正确计算高度。
修改内容:
1. ScenarioConfigView.tsx 主布局优化
- 容器布局: 将主容器改为
flex flex-col h-screen overflow-hidden p-4,直接使用视口高度 - 搜索工具栏: 使用
flex-shrink-0确保搜索区域不被压缩 - 表格区域: 使用
flex-1 flex flex-col让表格区域占据剩余空间 - 表格工具栏: 使用
flex-shrink-0固定工具栏高度 - 表格内容: 使用
flex-1 overflow-hidden让表格内容区域自适应 - 分页栏: 使用
flex-shrink-0固定分页栏高度
2. ScenarioConfigTable.tsx 表格优化
- 容器样式: 移除
rounded-md border,改为w-full overflow-auto - 表格布局: 使用
w-full确保表格占满容器宽度 - 滚动支持: 添加
overflow-auto支持内容滚动
3. ScenarioConfigForm.tsx 表单优化
- 表单容器: 添加
max-h-[70vh] overflow-y-auto限制表单高度并支持滚动 - 主题一致性: 将
text-gray-500改为text-muted-foreground - 禁用状态: 将
bg-gray-100改为bg-muted - 启用状态: 添加启用状态选择功能,支持复选框控制
- 表单验证: 保持所有原有的表单验证逻辑
4. ScenarioBindingView.tsx 主布局优化
- 容器布局: 将主容器改为
flex gap-6 h-screen overflow-hidden,直接使用视口高度 - 左侧面板: 使用
flex-shrink-0 w-80 flex flex-col m-4固定宽度,添加外边距 - 右侧面板: 使用
flex-1 flex flex-col m-4占据剩余空间,添加外边距 - 内容区域: 使用
flex-1 overflow-hidden防止外部滚动条
5. TestCaseList.tsx 组件优化
- 容器布局: 移除
min-h-0,使用flex flex-col h-full - 列表区域: 使用
flex-1 overflow-y-auto支持垂直滚动
6. ScenarioCategoryTree.tsx 组件优化
- 容器布局: 移除
min-h-0,使用h-full overflow-y-auto - 滚动支持: 确保树形结构可以正常滚动
7. Content.tsx 组件修复
- 滚动控制: 将
overflow-auto改为overflow-hidden - 避免外部滚动条: 防止 Content 组件本身产生滚动条
8. 底部间距修复
- ScenarioBindingView: 将
h-screen改为h-full,确保内容在 Content 的p-4内边距范围内 - ScenarioConfigView: 将
h-screen改为h-full,确保内容在 Content 的p-4内边距范围内 - 解决底部间距问题: 现在左右上下都有统一的间距
9. 内容区域滚动修复
- CardContent 滚动: 将
overflow-hidden改为overflow-auto,允许内容区域滚动 - 左侧面板:
CardContent使用overflow-auto,允许场景类别树滚动 - 右侧面板:
CardContent使用overflow-auto,允许测试用例列表滚动 - 解决空白问题: 内容区域现在可以填满整个高度,超出部分可以滚动
10. 空状态高度修复
- TestCaseList 空状态: 将空状态和加载状态的容器改为
flex items-center justify-center h-full - 解决底部空白: 当没有数据时,空状态消息会垂直居中显示,填满整个高度
- 统一体验: 加载状态和空状态都有相同的高度填充效果
11. 有数据时的高度填充修复
- TestCaseList 有数据状态: 重构布局结构,使用
h-full flex flex-col确保内容填满高度 - 全选控制: 使用
flex-shrink-0确保全选控制不被压缩 - 用例列表: 使用
flex-1 space-y-3 overflow-y-auto让列表填满剩余空间并可滚动 - 解决底部空白: 现在无论有数据还是无数据,内容都会填满整个高度
12. Card 组件外边距修复
- ScenarioBindingView: 移除 Card 组件的
m-4外边距,避免高度被压缩 - 布局层级: Content 组件已有
p-4内边距,Card 组件不需要额外外边距 - 高度计算: 现在 Card 组件可以完全填满 Content 组件的高度
- 最终解决: 彻底解决了底部空白问题
13. ScenarioBinding 组件全面重构
- ScenarioCategoryTree: 重构为更简洁美观的设计,减小字体和间距,使用现代样式
- 使用
text-sm和text-xs减小字体大小 - 使用
p-2减小内边距 - 添加边框和圆角,使用
border rounded-md - 使用
truncate处理文本溢出 - 添加空状态显示
- 使用
- TestCaseList: 重构为更紧凑的设计
- 搜索栏使用
h-8减小高度 - 按钮使用
size="sm"和h-8 - 场景提示使用
text-xs和更小的间距 - 用例卡片使用
p-3和space-y-2 - 使用
text-sm和text-xs减小字体
- 搜索栏使用
- ScenarioBindingView: 简化布局结构
- 移除 Card 组件,使用简单的
div和border rounded-lg - 减小面板宽度:左侧从
w-80改为w-72 - 减小间距:从
gap-6改为gap-4 - 使用更小的标题和按钮
- 统一使用
p-3内边距
- 移除 Card 组件,使用简单的
- 整体效果: 更紧凑、更现代、更美观的界面设计
14. 底部空白最终修复
- TestCaseList 用例列表: 修复布局结构,确保完全填满高度
- 将
space-y-2从外层容器移到内层容器 - 外层容器使用
flex-1 overflow-y-auto确保填满剩余空间 - 内层容器使用
space-y-2保持用例之间的间距
- 将
- 解决红色框框问题: 现在内容完全填满整个高度,不再有底部空白
- 最终效果: 无论用例数量多少,都能完全填满可用空间
15. 内边距布局优化
- ScenarioBindingView: 移除右侧面板容器的
p-3内边距 - TestCaseList: 在组件内部添加
p-3内边距 - 布局优化: 避免嵌套容器的内边距导致的高度计算问题
- 最终解决: 彻底解决红色框框标记的底部空白问题
16. Content 组件高度修复
- Content.tsx: 修复高度计算问题
- 将
p-4从main元素移到内部div元素 main元素使用flex-1 min-h-0 overflow-hidden确保完全填满高度- 内部
div使用w-full h-full p-4提供内边距
- 将
- 根本解决: 这是导致红色框框标记底部空白的根本原因
- 最终效果: 现在
ScenarioBindingView可以完全填满 Content 组件的高度
17. Content 组件最终修复
- Content.tsx: 彻底修复高度计算问题
- 移除内部
div包装器,直接在main元素上使用p-4 main元素使用flex-1 min-h-0 overflow-hidden p-4- 简化布局结构,避免嵌套容器的高度计算问题
- 移除内部
- 根本解决: 这是导致红色框框标记底部空白的最终根本原因
- 布局结构:
DashboardLayout (h-screen) ├── Header (flex-shrink-0) ├── 中间容器 (flex flex-1 min-h-0) ├── Sidebar (flex-shrink-0) └── 右侧容器 (flex-1 flex flex-col min-h-0) ├── Tabs (flex-shrink-0) └── Content (flex-1 min-h-0 p-4) ← 直接使用 p-4 └── ScenarioBindingView (h-full) ← 现在可以完全填满 - 最终效果: 彻底解决红色框框标记的底部空白问题
18. CSS min-height 0 修复
- TestCaseList.tsx: 添加
minHeight: 0样式- 主容器:
<div className="flex flex-col h-full p-3" style={{ minHeight: 0 }}> - 列表区域:
<div className="flex-1 overflow-y-auto" style={{ minHeight: 0 }}>
- 主容器:
- ScenarioCategoryTree.tsx: 添加
minHeight: 0样式- 主容器:
<div className="h-full overflow-y-auto p-2" style={{ minHeight: 0 }}>
- 主容器:
- CSS 解决方案: 使用
minHeight: 0确保 flex 子元素可以正确收缩 - 技术原理: 在 flexbox 布局中,
min-height: 0允许子元素收缩到内容高度以下 - 最终解决: 这是 CSS flexbox 布局的标准解决方案,不需要 Less 或 SCSS
技术特点:
- 直接使用视口高度: 使用
h-screen替代h-full,避免父元素高度依赖问题 - 简化 flex 布局: 移除复杂的
min-h-0属性,让 flex 子元素自然计算高度 - 分层滚动控制: 外部容器使用
overflow-hidden,内部组件使用overflow-auto - 空间利用: 充分利用可用空间,消除底部空白
- 主题一致: 使用系统主题变量,支持深色/浅色模式
CSS 限制分析:
h-full的限制: 依赖于父元素有明确的高度,如果父元素没有设置高度,h-full就无效min-h-0的复杂性: 在某些情况下会导致 flex 子元素无法正确计算高度- 嵌套容器的累积效应: 多层嵌套的 flex 容器可能导致高度计算问题
解决方案:
- 使用
h-screen: 直接使用视口高度,不依赖父元素 - 简化 flex 布局: 移除复杂的
min-h-0属性 - 分层滚动控制: 外部容器使用
overflow-hidden,内部组件使用overflow-auto - 避免多层滚动条: 确保只有内部组件有滚动条,外部容器不产生滚动条
- 修复 Content 组件: 将 Content 组件的
overflow-auto改为overflow-hidden
效果:
- 消除了页面底部的空白区域
- 提高了空间利用率
- 改善了用户体验
- 保持了组件的响应式特性
- 统一了主题样式
- 解决了 CSS 高度计算的限制问题
- 避免了多层滚动条问题,只有内部组件有滚动条
2024-12-19 - ScenarioConfigView.tsx 搜索栏优化
修改文件: X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigView.tsx
修改内容:
- 参考 TerminalServicesView.tsx 的搜索栏布局样式
- 将原来的网格布局改为更紧凑的弹性布局
- 统一了标签样式,使用固定宽度和右对齐
- 优化了输入框和下拉框的样式,使其与 TerminalServicesView.tsx 保持一致
- 改进了按钮布局,使其更加紧凑
具体变更:
- 将
className="space-y-4"改为className="flex gap-x-8 gap-y-4 items-center flex-wrap" - 将每个搜索项改为
flex flex-row items-center min-w-[200px] flex-1布局 - 统一标签样式为
mr-2 text-sm font-medium text-foreground whitespace-nowrap text-right并设置固定宽度 - 优化输入框和下拉框的样式类名
- 将按钮区域改为
flex flex-row items-center gap-2
效果: 搜索栏现在具有与 TerminalServicesView.tsx 相同的紧凑、美观的布局样式。
2024-12-19 - 场景配置页面搜索区域布局优化
文件: X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigView.tsx
修改内容:
- 将搜索关键词和场景类型放在同一行显示
- 修改
grid-cols-3为grid-cols-2,让前两个输入框在同一行
- 修改
- 将状态选择和按钮放在第二行
- 状态选择占用第一列
- 查询和重置按钮占用第二、三列,并垂直对齐到底部
- 使用
items-end类让按钮与输入框底部对齐
效果:
- 搜索关键词和场景类型在同一行显示
- 状态选择和操作按钮在第二行,按钮与输入框底部对齐
- 保持了响应式设计,在小屏幕上仍然垂直堆叠
2024-12-19 - 场景类型请求优化和表单风格统一
优化目标
- 避免
ScenarioConfigForm.tsx每次打开都请求/api/testscenarios/types - 统一表单风格,标签和输入框垂直排列
实现方案
- 将场景类型请求移到父组件
ScenarioConfigView.tsx,通过 props 传递给表单组件 - 统一搜索工具栏和表单组件的布局风格,使用垂直排列
修改文件
X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigView.tsx- 添加场景类型状态管理 (
scenarioTypes,isLoadingTypes) - 添加
fetchScenarioTypes()函数 - 在
useEffect中同时获取场景类型和测试场景列表 - 更新搜索工具栏布局,改为垂直排列风格
- 使用网格布局
grid grid-cols-1 md:grid-cols-3 gap-4 - 将场景类型数据通过 props 传递给
ScenarioConfigForm
- 添加场景类型状态管理 (
X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigForm.tsx- 移除内部的场景类型请求逻辑 (
useEffect,scenarioService.getScenarioTypes()) - 移除内部状态管理 (
scenarioTypes,isLoadingTypes) - 更新接口定义,添加
scenarioTypes和isLoadingTypesprops - 使用从父组件传递的场景类型数据
- 为下拉框添加
w-full类,确保宽度一致
- 移除内部的场景类型请求逻辑 (
优化效果
- 场景类型数据只在页面加载时请求一次
- 表单组件打开时直接使用已缓存的数据
- 提升用户体验,减少不必要的网络请求
- 统一表单风格,标签和输入框垂直排列,视觉效果更佳
- 响应式设计,在移动端单列显示,桌面端三列显示
2025-01-21 - 修复 TestScenariosController 路径不匹配问题
实现内容
发现并修复了 TestScenariosController 路由与前端服务路径不匹配的问题:
1. 问题分析
- 后端控制器路由:
[Route("api/testscenarios")](没有连字符) - 前端 API_PATHS:
TEST_SCENARIOS: '/test-scenarios'(有连字符) - 路径不匹配:
- 前端请求:
/api/test-scenarios - 后端路由:
/api/testscenarios
- 前端请求:
- 结果:导致 404 错误
2. 路径修正
X1.WebUI/src/constants/api.ts
- 将
TEST_SCENARIOS: '/test-scenarios'修正为TEST_SCENARIOS: '/testscenarios' - 确保前端路径与后端控制器路由完全一致
3. 路径对比
修正前:
- 后端:
api/testscenarios - 前端:
/test-scenarios→ httpClient 处理后:/api/test-scenarios - 结果:不匹配,404 错误
修正后:
- 后端:
api/testscenarios - 前端:
/testscenarios→ httpClient 处理后:/api/testscenarios - 结果:完全匹配,正常工作
4. 影响范围
- scenarioService.ts:自动使用修正后的路径
- 所有测试场景相关 API 调用:现在可以正确访问后端端点
- API 端点:
GET /api/testscenarios- 获取测试场景列表GET /api/testscenarios/types- 获取测试场景类型列表GET /api/testscenarios/{id}- 获取测试场景详情POST /api/testscenarios- 创建测试场景PUT /api/testscenarios/{id}- 更新测试场景DELETE /api/testscenarios/{id}- 删除测试场景
技术说明
- 路径一致性:确保前端路径与后端控制器路由完全一致
- 命名规范:后端使用
testscenarios(无连字符),前端也相应调整 - 自动处理:httpClient 自动添加
/api前缀,形成完整路径
验证方法
- 前端调用
scenarioService.getScenarioTypes()应该能正确访问/api/testscenarios/types - 前端调用
scenarioService.getTestScenarios()应该能正确访问/api/testscenarios - 所有测试场景相关的 API 调用都应该正常工作,不再出现 404 错误
2025-01-21 - 修正 API 路径规范(httpClient 已包含 /api 前缀)
实现内容
发现 httpClient 已经自动加上了 /api 前缀,因此修正了 API_PATHS 中的路径格式:
1. 问题分析
- httpClient 配置:已经自动为所有请求添加
/api前缀 - 路径重复:
API_PATHS中又添加了/api前缀,导致路径变成/api/api/... - 影响:所有 API 调用路径不正确
2. API 路径常量修正
X1.WebUI/src/constants/api.ts
- 移除所有路径中的
/api前缀 - 保持与后端控制器路由的相对路径一致
- 确保路径格式正确
修正的路径包括:
DEVICES: '/devices'DEVICE_RUNTIMES: '/device-runtimes'TERMINAL_DEVICES: '/terminal-devices'TERMINAL_SERVICES: '/terminal-services'AT_OPERATIONS: '/at-operations'ADB_OPERATIONS: '/adb-operations'PROTOCOLS: '/protocolversions'PROTOCOL_LOGS: '/protocolLogs'RAN_CONFIGURATIONS: '/ranconfigurations'IMS_CONFIGURATIONS: '/imsconfigurations'CORE_NETWORK_CONFIGS: '/corenetworkconfigs'NETWORK_STACK_CONFIGS: '/networkstackconfigs'USERS: '/users'ROLES: '/roles'PERMISSIONS: '/permissions'ROLE_PERMISSIONS: '/role-permissions'TASKS: '/tasks'TASK_EXECUTIONS: '/task-executions'TASK_REVIEWS: '/task-reviews'SCENARIOS: '/scenarios'TEST_SCENARIOS: '/test-scenarios'TEST_CASE_FLOW: '/testcaseflow'CASE_STEP_CONFIGS: '/casestepconfigs'
3. 架构说明
- httpClient 配置:自动为所有请求添加
/api前缀 - 路径格式:
API_PATHS中只包含相对路径部分 - 最终路径:
httpClient会自动组合成完整的 API 路径 - 示例:
/test-scenarios+ httpClient 前缀 =/api/test-scenarios
4. 影响范围
- 所有服务类:自动使用修正后的路径常量
- 无需代码修改:服务类通过
baseUrl自动获取正确路径 - 向后兼容:保持现有的服务接口不变
技术实现
- 路径简化:
API_PATHS中只包含相对路径 - 自动前缀:
httpClient自动添加/api前缀 - 路径组合:最终路径 = httpClient 前缀 + API_PATHS 路径
- 类型安全:保持完整的 TypeScript 类型定义
使用说明
API_PATHS中只包含相对路径,不包含/api前缀httpClient会自动为所有请求添加/api前缀- 最终 API 路径正确组合
- 保持与后端控制器路由的一致性
2025-01-21 - 统一 API 路径规范和修复路由问题
实现内容
发现并修复了 TestScenariosController 路由与前端服务路径不匹配的问题,统一了所有 API 路径规范:
1. 问题分析
- 后端控制器路由:
[Route("api/test-scenarios")]使用/api/前缀 - 前端 API_PATHS:部分路径包含
/api前缀,部分不包含,导致不一致 - 影响:
scenarioService.ts中的 API 调用路径与后端路由不匹配
2. API 路径常量统一更新
X1.WebUI/src/constants/api.ts
- 统一所有 API 路径,添加
/api/前缀 - 确保所有路径与后端控制器路由一致
- 保持 kebab-case 命名规范
更新的路径包括:
DEVICES: '/api/devices'DEVICE_RUNTIMES: '/api/device-runtimes'TERMINAL_DEVICES: '/api/terminal-devices'TERMINAL_SERVICES: '/api/terminal-services'AT_OPERATIONS: '/api/at-operations'ADB_OPERATIONS: '/api/adb-operations'PROTOCOLS: '/api/protocolversions'PROTOCOL_LOGS: '/api/protocolLogs'RAN_CONFIGURATIONS: '/api/ranconfigurations'IMS_CONFIGURATIONS: '/api/imsconfigurations'CORE_NETWORK_CONFIGS: '/api/corenetworkconfigs'NETWORK_STACK_CONFIGS: '/api/networkstackconfigs'USERS: '/api/users'ROLES: '/api/roles'TASKS: '/api/tasks'TASK_EXECUTIONS: '/api/task-executions'TASK_REVIEWS: '/api/task-reviews'SCENARIOS: '/api/scenarios'TEST_SCENARIOS: '/api/test-scenarios'TEST_CASE_FLOW: '/api/testcaseflow'CASE_STEP_CONFIGS: '/api/casestepconfigs'
3. 架构改进
- 路径一致性:所有前端 API 路径与后端控制器路由保持一致
- 统一规范:所有路径都使用
/api/前缀 - 可维护性:路径变更只需修改
API_PATHS常量 - 类型安全:保持完整的 TypeScript 类型定义
4. 影响范围
- 所有服务类:自动使用更新后的路径常量
- 无需代码修改:服务类通过
baseUrl自动获取正确路径 - 向后兼容:保持现有的服务接口不变
技术实现
- 路径统一:通过
API_PATHS常量统一管理所有 API 路径 - 前缀规范:所有路径都包含
/api/前缀 - 自动更新:服务类通过
baseUrl自动使用正确路径 - 类型安全:保持完整的 TypeScript 类型定义
使用说明
- 所有 API 调用现在使用统一的
/api/前缀路径 - 路径变更只需修改
API_PATHS常量 - 服务类自动使用更新后的路径
- 保持与后端控制器路由的一致性
2025-01-21 - 优化 ScenarioService 架构和 URL 路径
实现内容
参考 TestCaseFlowService 的实现,优化了 ScenarioService 的架构和 URL 路径规范:
1. API 路径常量更新
X1.WebUI/src/constants/api.ts
- 添加
TEST_SCENARIOS: '/test-scenarios'路径常量 - 统一使用 kebab-case 命名规范
2. 服务架构重构
X1.WebUI/src/services/scenarioService.ts
- 将对象形式重构为类形式,使用
class ScenarioService - 添加
private readonly baseUrl = API_PATHS.TEST_SCENARIOS属性 - 移除
ScenarioService接口定义,直接使用类方法 - 所有 URL 路径使用
${this.baseUrl}模板字符串 - 统一使用
/test-scenarios路径格式
3. URL 路径规范化
- 将所有硬编码的
/api/testscenarios改为/api/test-scenarios - 使用
API_PATHS.TEST_SCENARIOS常量统一管理路径 - 遵循项目的 kebab-case URL 命名规范
4. 架构改进
- 类形式:使用类而不是对象,提供更好的封装性
- 路径管理:通过
baseUrl统一管理所有 API 路径 - 类型安全:保持完整的 TypeScript 类型定义
- 一致性:与其他服务保持相同的架构模式
技术实现
- 类架构:使用 ES6 类语法,提供更好的封装和继承能力
- 常量管理:通过
API_PATHS常量统一管理所有 API 路径 - 模板字符串:使用
${this.baseUrl}动态构建 URL - 类型安全:保持完整的 TypeScript 类型定义
使用说明
- 服务实例通过
scenarioService导出 - 所有 API 调用使用统一的
/test-scenarios路径 - 路径变更只需修改
API_PATHS.TEST_SCENARIOS常量 - 保持与其他服务一致的架构模式
2025-01-21 - 前端集成 ScenarioType 查询功能
实现内容
在前端集成了后端新增的 ScenarioType 查询功能,实现了动态获取场景类型列表:
1. 服务层更新
X1.WebUI/src/services/scenarioService.ts
- 添加
ScenarioTypeOption接口定义 - 添加
GetScenarioTypesResponse接口定义 - 更新
ScenarioType枚举,支持所有 8 种场景类型 - 在
ScenarioService接口中添加getScenarioTypes方法 - 实现
getScenarioTypes方法,调用/api/testscenarios/types端点
2. 表单组件更新
X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigForm.tsx
- 添加
useEffect钩子获取场景类型列表 - 添加加载状态管理 (
isLoadingTypes) - 动态渲染场景类型选项,替换硬编码的选项
- 添加场景类型描述显示功能
- 优化用户体验,显示加载状态和描述信息
3. 功能特性
- 动态数据获取:从后端 API 动态获取场景类型列表
- 加载状态:显示加载中状态,提升用户体验
- 描述信息:选择场景类型后显示对应的描述信息
- 错误处理:完整的错误捕获和日志记录
- 类型安全:完整的 TypeScript 类型定义
4. 用户体验改进
- 场景类型选择器显示"请选择场景类型"提示
- 加载时显示"加载中..."状态
- 选择场景类型后显示详细描述
- 禁用状态处理,防止重复提交
技术实现
- React Hooks:使用
useState和useEffect管理状态和副作用 - TypeScript:完整的类型定义和类型安全
- 异步处理:使用 async/await 处理 API 调用
- 错误处理:try-catch 错误捕获和用户友好的错误提示
使用说明
- 表单组件会自动获取场景类型列表
- 用户可以从动态加载的选项中选择场景类型
- 选择后会显示对应的描述信息
- 支持加载状态和错误处理
2025-01-21 - 添加 ScenarioType 查询功能
实现内容
参考 GetTestFlowTypesQueryHandler 的实现,为 ScenarioType 添加了完整的查询功能:
1. 新增文件
X1.Domain/Common/ScenarioTypeConverter.cs - 测试场景类型转换器
GetScenarioTypes()- 获取所有测试场景类型列表GetScenarioTypeDescriptions()- 获取测试场景类型描述字典GetDisplayName()- 根据测试场景类型获取显示名称GetDescription()- 根据测试场景类型获取描述
X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/
GetScenarioTypesQuery.cs- 获取测试场景类型查询GetScenarioTypesResponse.cs- 获取测试场景类型响应GetScenarioTypesQueryHandler.cs- 获取测试场景类型查询处理器
2. 控制器更新
X1.Presentation/Controllers/TestScenariosController.cs
- 添加
GetScenarioTypes()方法 - 新增
/api/testscenarios/types端点 - 支持获取所有测试场景类型及其描述信息
3. 功能特性
- 完整枚举支持:支持所有 8 种测试场景类型(功能测试、性能测试、压力测试、兼容性测试、回归测试、集成测试、安全测试、用户体验测试)
- 属性反射:通过反射获取枚举的
DisplayAttribute和DescriptionAttribute - 统一响应格式:使用
OperationResult<T>和EnumValueObject保持一致性 - 错误处理:完整的异常捕获和错误信息返回
- 日志记录:详细的日志记录,便于调试和监控
4. API 端点
GET /api/testscenarios/types
响应示例:
{
"isSuccess": true,
"data": {
"scenarioTypes": [
{
"value": 1,
"name": "功能测试",
"description": "验证系统功能是否按预期工作的测试场景"
},
{
"value": 2,
"name": "性能测试",
"description": "验证系统性能指标是否满足要求的测试场景"
}
// ... 其他类型
]
}
}
技术实现
- CQRS 模式:使用 MediatR 实现查询和命令分离
- 反射机制:动态获取枚举属性和特性
- 依赖注入:通过 DI 容器管理查询处理器
- 统一响应:使用
OperationResult<T>包装响应数据
使用说明
- 前端可以通过
/api/testscenarios/types端点获取所有测试场景类型 - 返回的数据包含枚举值、显示名称和描述信息
- 可以用于下拉选择框、标签显示等场景
- 支持国际化显示名称和详细描述
2025-01-21 - 场景配置页面实现
实现内容
参考 terminal-services 的实现,完成了 scenarios.scenario-config 页面的开发,包含以下功能:
1. 主要组件
ScenarioConfigView.tsx - 主页面组件
- 完整的 CRUD 操作(创建、读取、更新、删除)
- 搜索功能(关键词、场景类型、启用状态)
- 分页功能
- 表格密度调节
- 列显示控制
- Toast 提示
ScenarioConfigTable.tsx - 表格组件
- 场景编码、名称、类型、描述、状态等字段显示
- 场景类型标签(不同颜色区分)
- 启用状态标签
- 编辑和删除操作按钮
- 密度调节支持
ScenarioConfigForm.tsx - 表单组件
- 创建模式:场景名称、类型、描述、启用状态
- 编辑模式:只允许修改描述和启用状态
- 表单验证
- 提交状态管理
2. 功能特性
- 场景类型支持:功能测试、性能测试、安全测试、集成测试、回归测试
- 字段保护:场景编码、场景名称、场景类型创建后不允许修改
- 状态管理:启用/禁用状态切换
- 搜索过滤:支持多条件组合搜索
- 分页显示:支持自定义每页数量
- 响应式设计:适配不同屏幕尺寸
3. 数据接口
基于 CreateTestScenarioCommandHandler 和 GetTestScenariosQueryHandler 实现:
- 创建测试场景(场景编码后台自动生成)
- 获取测试场景列表(支持分页和过滤)
- 更新测试场景(只允许修改描述和启用状态)
- 删除测试场景
4. 样式和布局
- 与
terminal-services保持一致的 UI 风格 - 现代化的卡片式布局
- 统一的颜色主题
- 良好的用户体验
技术实现
- React + TypeScript:类型安全的组件开发
- Tailwind CSS:现代化的样式系统
- shadcn/ui:统一的 UI 组件库
- React Hook Form:表单状态管理
- React Query:数据获取和缓存
文件结构
X1.WebUI/src/pages/scenarios/scenario-config/
├── ScenarioConfigView.tsx # 主页面组件
├── ScenarioConfigTable.tsx # 表格组件
└── ScenarioConfigForm.tsx # 表单组件
使用说明
- 访问
/dashboard/scenarios/config路由 - 点击"添加测试场景"按钮创建新场景
- 使用搜索工具栏过滤场景列表
- 点击编辑按钮修改场景配置
- 点击删除按钮删除场景(需要确认)
注意事项
- 场景编码由后端自动生成,格式为:
{类型前缀}_{日期}_{序号} - 创建后场景名称和类型不可修改
- 删除场景前会检查是否有关联的测试用例
- 所有操作都有相应的成功/失败提示
- 启用状态默认为启用,用户无需手动选择
修复记录
2025-01-21 - 修复场景配置表单问题
修复内容:
- 移除启用状态显示:在创建和编辑表单中移除启用状态的选择框
- 设置默认启用:将启用状态的默认值设置为
true,新创建的场景默认启用 - 修复接口定义:移除
CreateTestScenarioRequest中的scenarioCode字段,因为场景编码由后端自动生成 - 简化用户体验:用户无需关心启用状态,专注于场景的核心配置
- 更新场景类型:根据后端
ScenarioType枚举更新所有场景类型选项,包括压力测试、兼容性测试、用户体验测试等 - 优化类型标签颜色:为新增的场景类型添加对应的颜色标识
2025-01-21 - 场景绑定页面重构
修改时间:
2025-01-21
修改原因:
根据用户要求,将场景绑定页面进行组件拆分,并使用真实的测试用例数据从 TestCasesListView.tsx 获取,提高代码的可维护性和复用性。
修改内容:
-
组件拆分:
- 主组件:
ScenarioBindingView.tsx- 负责状态管理和数据获取 - 场景树组件:
ScenarioCategoryTree.tsx- 负责场景类别树形结构显示 - 用例列表组件:
TestCaseList.tsx- 负责测试用例列表显示和选择
- 主组件:
-
数据源改进:
- 真实数据:使用
testcaseService.getTestCaseFlows()获取真实的测试用例数据 - 类型安全:使用
TestCaseFlow接口确保类型安全 - 错误处理:添加了完整的错误处理和用户提示
- 真实数据:使用
-
功能增强:
- 搜索功能:支持按名称和描述搜索测试用例
- 批量选择:支持全选/取消全选功能
- 状态显示:显示测试用例的类型和启用状态
- 实时反馈:选中场景和用例时提供实时反馈
-
UI/UX 改进:
- 响应式设计:支持不同屏幕尺寸
- 加载状态:显示数据加载状态
- 空状态处理:处理无数据情况
- 视觉反馈:选中状态、悬停效果等
-
代码结构优化:
- 职责分离:每个组件职责明确
- 可复用性:组件可以在其他地方复用
- 可维护性:代码结构清晰,易于维护
修改的文件:
X1.WebUI/src/pages/scenarios/scenario-binding/ScenarioBindingView.tsx- 主页面组件重构X1.WebUI/src/pages/scenarios/scenario-binding/components/ScenarioCategoryTree.tsx- 新增场景类别树形组件X1.WebUI/src/pages/scenarios/scenario-binding/components/TestCaseList.tsx- 新增测试用例列表组件
技术特点:
- 使用 TypeScript 确保类型安全
- 采用 React Hooks 管理状态
- 使用 shadcn/ui 组件库保持 UI 一致性
- 支持键盘导航和屏幕阅读器
- 遵循 React 最佳实践
后续计划:
- 实现真实的场景数据获取
- 添加场景与测试用例的绑定 API
- 支持批量解绑功能
- 添加绑定历史记录
2025-01-21 - 优化组件布局铺满空间
修改内容:
-
ScenarioCategoryTree 组件:
- 将
max-h-[600px]改为h-full,让组件铺满父容器高度 - 保持
overflow-y-auto确保内容可滚动
- 将
-
TestCaseList 组件:
- 使用
flex flex-col h-full布局 - 搜索栏和场景提示使用
flex-shrink-0固定高度 - 测试用例列表使用
flex-1 overflow-y-auto min-h-0铺满剩余空间
- 使用
-
主容器布局:
- 左右两个 Card 都使用
flex flex-col布局 - CardContent 使用
flex-1 min-h-0让内容区域铺满剩余空间 - 确保整个页面能够充分利用可用空间
- 左右两个 Card 都使用
技术说明:
- 使用 Flexbox 布局实现响应式高度
min-h-0确保 flex 子元素能够正确收缩overflow-y-auto确保内容超出时能够滚动- 保持组件的可复用性和可维护性
2025-01-21 - 重构 scenarioService.ts 根据 TestScenariosController
修改时间:
2025-01-21
修改原因:
根据后端 TestScenariosController 的实现,完全重构 scenarioService.ts,移除向后兼容代码,确保前后端API完全对应。
修改内容:
-
完全重构接口定义:
- TestScenario:与后端 TestScenarioDto 完全对应
- GetTestScenariosResponse:与后端 GetTestScenariosResponse 对应
- CreateTestScenarioRequest:与后端 CreateTestScenarioCommand 对应
- UpdateTestScenarioRequest:与后端 UpdateTestScenarioCommand 对应
- TestCase:场景测试用例接口定义
- GetScenarioTestCasesResponse:场景测试用例列表响应
- CreateScenarioTestCaseRequest:创建场景测试用例请求
- CreateScenarioTestCaseResponse:创建场景测试用例响应
-
API路径更新:
- 所有API路径更新为
/api/testscenarios - 支持查询参数:searchTerm、type、isEnabled、pageNumber、pageSize
- 支持场景测试用例管理:
/api/testscenarios/{scenarioId}/testcases
- 所有API路径更新为
-
服务方法实现:
- getTestScenarios():获取测试场景列表,支持搜索、过滤、分页
- getTestScenarioById():获取测试场景详情,支持包含测试用例
- createTestScenario():创建测试场景
- updateTestScenario():更新测试场景
- deleteTestScenario():删除测试场景
- getScenarioTestCases():获取场景测试用例列表
- createScenarioTestCase():创建场景测试用例
-
移除向后兼容代码:
- 删除原有的 Scenario、GetAllScenariosResponse 等接口
- 删除 getAllScenarios、getScenario 等旧方法
- 专注于与 TestScenariosController 的完全对应
修改的文件:
X1.WebUI/src/services/scenarioService.ts- 完全重构
技术说明:
- 与后端 TestScenariosController 完全对应
- 支持完整的CRUD操作
- 支持场景测试用例管理
- 类型安全,使用 TypeScript 接口
- 统一的错误处理和响应格式
2025-01-21 - 修改 SaveTestCaseForm 使用动态测试流程类型数据
修改时间:
2025-01-21
修改原因:
用户要求将 SaveTestCaseForm 中的硬编码测试类型选项替换为从 getTestFlowTypes 接口获取的动态数据,提高系统的灵活性和可维护性。
修改内容:
-
SaveTestCaseForm.tsx 修改:
- 新增 props:添加
testFlowTypes?: TestFlowTypeDto[]参数 - 动态渲染:将硬编码的 SelectItem 替换为动态生成的选项
- 类型导入:导入
TestFlowTypeDto类型
- 新增 props:添加
-
TestCasesView.tsx 修改:
- 状态管理:添加
testFlowTypes和loadingTestFlowTypes状态 - 数据获取:在
useEffect中调用getTestFlowTypes()接口 - 数据传递:将获取的测试流程类型数据传递给
SaveTestCaseForm
- 状态管理:添加
-
功能特性:
- 动态加载:页面加载时自动获取测试流程类型数据
- 错误处理:包含完整的错误处理和日志记录
- 类型安全:使用 TypeScript 确保类型安全
- 向后兼容:保持现有功能不变
修改的文件:
X1.WebUI/src/components/testcases/SaveTestCaseForm.tsx- 修改为使用动态数据X1.WebUI/src/pages/testcases/TestCasesView.tsx- 添加数据获取和传递逻辑
技术说明:
- 使用 React hooks 管理状态
- 遵循 React 最佳实践
- 提供完整的错误处理
- 保持组件的可复用性
2025-01-21 - 修复场景绑定页面底部空白问题
修改时间: 2025-01-21
修改原因: 场景绑定页面(ScenarioBindingView、ScenarioCategoryTree、TestCaseList)底部存在大量空白,影响用户体验和空间利用率。
修改内容:
-
TestCaseList 组件优化:
- 主容器添加
min-h-0确保 flex 子元素能够正确收缩 - 搜索栏和场景提示区域添加
flex-shrink-0防止被压缩 - 测试用例列表区域使用
flex-1 min-h-0 overflow-y-auto铺满剩余空间
- 主容器添加
-
ScenarioBindingView 组件优化:
- 右侧 Card 的 CardContent 添加
p-4内边距,确保内容有适当的间距 - 保持
flex-1 min-h-0确保卡片能够充分利用可用空间
- 右侧 Card 的 CardContent 添加
-
ScenarioCategoryTree 组件优化:
- 主容器添加
min-h-0确保能够正确继承父容器高度 - 保持
overflow-y-auto确保内容超出时能够滚动
- 主容器添加
技术说明:
- 使用 Flexbox 布局的
min-h-0属性解决 flex 子元素高度计算问题 flex-shrink-0确保固定高度的元素不被压缩flex-1让可变高度的元素占据剩余空间overflow-y-auto确保内容超出时能够滚动而不是撑开容器
修改的文件:
X1.WebUI/src/pages/scenarios/scenario-binding/TestCaseList.tsxX1.WebUI/src/pages/scenarios/scenario-binding/ScenarioBindingView.tsxX1.WebUI/src/pages/scenarios/scenario-binding/ScenarioCategoryTree.tsx
效果:
- 消除了页面底部的空白区域
- 提高了空间利用率
- 改善了用户体验
- 保持了组件的响应式特性
2025-01-21 - 为 testcaseService.ts 添加 GetTestFlowTypes 方法
修改时间:
2025-01-21
修改原因:
用户要求为 testcaseService.ts 添加 GetTestFlowTypes 方法,以支持前端获取测试流程类型列表,与后端 TestCaseFlowController.GetTestFlowTypes 方法对应。
修改内容:
-
新增接口定义:
- TestFlowTypeDto:测试流程类型数据传输对象接口
- GetTestFlowTypesResponse:获取测试流程类型响应接口
-
新增服务方法:
- getTestFlowTypes():获取测试流程类型列表的方法
- 对应后端 API:
GET /api/testcaseflow/test-flow-types
-
类型安全:
- 使用 TypeScript 接口确保类型安全
- 与后端响应结构完全匹配
- 支持完整的测试流程类型信息(值、名称、描述)
修改的文件:
X1.WebUI/src/services/testcaseService.ts- 添加 GetTestFlowTypes 相关接口和方法
技术说明:
- 遵循 TypeScript 命名约定
- 与后端 API 完全对应
- 提供完整的类型定义
- 支持前端测试流程类型选择功能
2025-01-21 - 重新命名 TestCaseFlowController.GetFormTypeStepTypeMapping 方法
修改时间:
2025-01-21
修改原因:
TestCaseFlowController.GetFormTypeStepTypeMapping 方法命名不规范,与实际功能不符。该方法实际返回的是测试流程类型列表,而不是表单类型到步骤类型的映射。
修改内容:
-
方法重命名:
- 将
GetFormTypeStepTypeMapping()重命名为GetTestFlowTypes() - 方法名现在准确反映了其功能:获取测试流程类型列表
- 将
-
路由重命名:
- 将路由从
form-type-step更改为test-flow-types - 路由名称现在与功能保持一致
- 将路由从
-
命名一致性:
- 方法名与查询类型
GetTestFlowTypesQuery保持一致 - 方法名与响应类型
GetTestFlowTypesResponse保持一致 - 消除了命名混淆,提高了代码可读性
- 方法名与查询类型
修改的文件:
X1.Presentation/Controllers/TestCaseFlowController.cs- 重命名方法和路由
技术说明:
- 遵循 C# 命名约定
- 方法名准确反映其功能
- 路由名称与功能保持一致
- 提高了 API 的可理解性和一致性
2024-12-19 - 新增 TestScenariosController
修改内容
基于 Features.TestScenarios 实现完整的控制器,参考 TestCaseFlowController 的实现模式。
实现的功能:
-
获取测试场景列表 (
GET /api/testscenarios)- 支持搜索关键词过滤
- 支持场景类型过滤
- 支持启用状态过滤
- 支持分页查询
-
获取测试场景详情 (
GET /api/testscenarios/{id})- 根据ID获取单个测试场景详情
- 支持可选包含测试用例数据
-
创建测试场景 (
POST /api/testscenarios)- 创建新的测试场景
- 支持场景名称、类型、描述、启用状态等属性
-
更新测试场景 (
PUT /api/testscenarios/{id})- 更新现有测试场景的描述和启用状态
- 自动同步路由参数中的ID
-
删除测试场景 (
DELETE /api/testscenarios/{id})- 删除指定的测试场景
-
获取场景测试用例列表 (
GET /api/testscenarios/{scenarioId}/testcases)- 获取指定场景下的测试用例列表
- 支持启用状态过滤
- 支持包含详细信息选项
-
创建场景测试用例 (
POST /api/testscenarios/{scenarioId}/testcases)- 为指定场景添加测试用例
- 支持批量创建多个测试用例
- 包含执行顺序、循环次数、启用状态等配置
技术特点:
- 继承自
ApiController基类 - 使用 MediatR 进行命令查询分离
- 完整的日志记录
- 统一的错误处理
- 支持授权访问
- 遵循 RESTful API 设计规范
使用的命名空间:
X1.Application.Features.TestScenarios.Queries.*X1.Application.Features.TestScenarios.Commands.*X1.Application.Features.ScenarioTestCases.Queries.*X1.Application.Features.ScenarioTestCases.Commands.*X1.Presentation.AbstractionsX1.Domain.Common
修改的文件:
X1.Presentation/Controllers/TestScenariosController.cs(新增)
2024-12-19 - 简化仓储异常处理,移除 try-catch 块
问题描述: 仓储中的 try-catch 异常处理过于冗余,增加了代码复杂度,且异常最终还是要抛给上一级处理。
问题分析:
- 仓储层的 try-catch 块只是记录日志后重新抛出异常
- 异常处理应该在应用层统一处理
- 简化代码可以提高可读性和维护性
修复内容:
- 文件:
X1.Infrastructure/Repositories/TestCase/ScenarioTestCaseRepository.cs- 移除所有方法的 try-catch 异常处理块
- 保留核心业务逻辑,直接让异常抛给上一级
- 文件:
X1.Infrastructure/Repositories/TestCase/TestScenarioRepository.cs- 移除所有方法的 try-catch 异常处理块
- 保留核心业务逻辑,直接让异常抛给上一级
技术说明:
- 遵循"让异常冒泡"的原则,在应用层统一处理异常
- 简化仓储层代码,专注于数据访问逻辑
- 提高代码的可读性和维护性
- 异常日志记录应该在应用层或中间件中统一处理
2024-12-19 - 修复仓储文件缺少 Entity Framework Core using 指令问题
问题描述:
在仓储文件中使用 Include 扩展方法时出现编译错误:
ScenarioTestCaseRepository.cs第129行:"ScenarioTestCase"未包含"Include"的定义TestScenarioRepository.cs第111行和第129行:"IQueryable<TestScenario>"未包含"Include"的定义
问题分析:
- 仓储文件中使用了 Entity Framework Core 的
Include扩展方法 - 缺少
Microsoft.EntityFrameworkCore的 using 指令 Include和ThenInclude方法需要 Entity Framework Core 命名空间
修复内容:
- 文件:
X1.Infrastructure/Repositories/TestCase/ScenarioTestCaseRepository.cs- 添加
using Microsoft.EntityFrameworkCore;指令
- 添加
- 文件:
X1.Infrastructure/Repositories/TestCase/TestScenarioRepository.cs- 添加
using Microsoft.EntityFrameworkCore;指令
- 添加
技术说明:
Include方法用于预加载导航属性,避免 N+1 查询问题ThenInclude方法用于预加载嵌套的导航属性- 这些方法是 Entity Framework Core 的核心功能,需要正确的命名空间引用
2024-12-19 - 修复 TestScenario 实体缺失方法问题
问题描述:
在 UpdateTestScenarioCommandHandler.cs 中调用了 TestScenario 实体中不存在的方法:
SetEnabled方法未定义UpdateDescription方法未定义UpdateAuditInfo方法未定义
问题分析:
TestScenario实体中只有Update方法,但缺少单独的操作方法- 命令处理器需要更细粒度的更新方法
- 需要遵循领域驱动设计原则,提供专门的业务方法
修复内容:
- 文件:
X1.Domain/Entities/TestCase/TestScenario.cs - 添加了以下方法:
SetEnabled(bool isEnabled)- 设置启用状态UpdateDescription(string? description)- 更新描述UpdateAuditInfo(string updatedBy)- 更新审计信息
技术说明:
- 遵循领域驱动设计原则,提供专门的业务方法
- 保持实体的封装性,通过方法而不是直接属性赋值
- 确保审计信息的正确更新
- 支持部分更新操作,提高代码的可读性和维护性
2024-12-19 - 修复 DeleteTestScenarioCommandHandler 中的 DeleteAsync 方法调用错误
问题描述:
在 DeleteTestScenarioCommandHandler.cs 中使用了不存在的 DeleteAsync(T entity) 方法。
问题分析:
IBaseRepository<T>接口中没有DeleteAsync(T entity)方法- 只有
void Delete(T entity)同步方法 - 删除实体操作是同步的,因为它只是标记实体状态为已删除
- 实际的数据库删除操作由
UnitOfWork.SaveChangesAsync()在事务提交时执行
修复内容:
- 文件:
X1.Application/Features/TestScenarios/Commands/DeleteTestScenario/DeleteTestScenarioCommandHandler.cs - 修改:将
await _testScenarioRepository.DeleteAsync(testScenario, cancellationToken:cancellationToken);改为_testScenarioRepository.Delete(testScenario);
技术说明:
- 删除操作使用同步方法
Delete()是正确的,因为它只是标记实体状态 - 真正的数据库操作在
await _unitOfWork.SaveChangesAsync(cancellationToken);中执行 - 这种设计符合 CQRS 模式和 Entity Framework 的工作机制
- 不要为了异步而异步,只有真正需要等待 I/O 操作的方法才使用异步
2024-12-19 - 场景测试用例创建性能优化
问题描述
在 CreateScenarioTestCaseCommandHandler 中,批量创建测试用例时,在循环中逐个调用 ExistsInScenarioAsync 方法检查每个测试用例是否已存在,这会导致多次数据库查询,严重影响性能。
解决方案
-
添加批量检查方法:
- 在
IScenarioTestCaseRepository接口中添加GetExistingTestCaseFlowIdsAsync方法 - 在
ScenarioTestCaseRepository实现中添加对应的批量检查逻辑
- 在
-
优化命令处理器:
- 在循环开始前,一次性批量查询所有已存在的测试用例流程ID
- 在循环中使用内存中的 HashSet 进行快速查找,避免重复数据库查询
修改的文件
-
X1.Domain/Repositories/TestCase/IScenarioTestCaseRepository.cs- 添加
GetExistingTestCaseFlowIdsAsync方法声明
- 添加
-
X1.Infrastructure/Repositories/TestCase/ScenarioTestCaseRepository.cs- 实现
GetExistingTestCaseFlowIdsAsync方法 - 使用
Contains查询一次性获取所有已存在的测试用例流程ID
- 实现
-
X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseCommandHandler.cs- 在循环前批量检查已存在的测试用例流程ID
- 将循环中的数据库查询改为内存中的 HashSet 查找
性能提升
- 优化前:N次数据库查询(N为测试用例数量)
- 优化后:1次数据库查询 + N次内存查找
- 性能提升:显著减少数据库查询次数,提高批量创建性能
测试建议
- 测试批量创建大量测试用例的场景
- 验证重复测试用例的正确处理
- 确认错误信息仍然正确返回
2025-01-21 - TestScenario 和 ScenarioTestCase 完整功能实现
1. 创建 TestScenario 的 Application Features 实现
创建的文件:
-
Commands/CreateTestScenario/
CreateTestScenarioCommand.cs- 创建测试场景命令(场景编码后台自动生成)CreateTestScenarioResponse.cs- 创建测试场景响应CreateTestScenarioCommandHandler.cs- 创建测试场景命令处理器
-
Commands/UpdateTestScenario/
UpdateTestScenarioCommand.cs- 更新测试场景命令(只允许修改描述和启用状态)UpdateTestScenarioResponse.cs- 更新测试场景响应UpdateTestScenarioCommandHandler.cs- 更新测试场景命令处理器
-
Commands/DeleteTestScenario/
DeleteTestScenarioCommand.cs- 删除测试场景命令DeleteTestScenarioCommandHandler.cs- 删除测试场景命令处理器
-
Queries/GetTestScenarios/
GetTestScenariosQuery.cs- 获取测试场景列表查询GetTestScenariosResponse.cs- 获取测试场景列表响应GetTestScenariosQueryHandler.cs- 获取测试场景列表查询处理器
-
Queries/GetTestScenarioById/
GetTestScenarioByIdQuery.cs- 根据ID获取测试场景查询GetTestScenarioByIdResponse.cs- 根据ID获取测试场景响应GetTestScenarioByIdQueryHandler.cs- 根据ID获取测试场景查询处理器
主要功能特性:
- 场景编码自动生成:根据场景类型生成唯一编码(FUNC_20241219_1234 格式)
- 字段保护:场景编码、场景名称、场景类型创建后不允许修改
- 删除保护:存在关联测试用例时不允许删除场景
- 完整CRUD操作:创建、更新、删除、查询列表、查询详情
- 分页和过滤:支持分页、搜索、类型过滤、启用状态过滤
- 关联数据查询:支持查询场景时包含关联的测试用例信息
2. 创建 ScenarioTestCase 的 Application Features 实现
创建的文件:
-
Commands/CreateScenarioTestCase/
CreateScenarioTestCaseCommand.cs- 创建场景测试用例命令CreateScenarioTestCaseResponse.cs- 创建场景测试用例响应CreateScenarioTestCaseCommandHandler.cs- 创建场景测试用例命令处理器
-
Commands/BatchCreateScenarioTestCases/
BatchCreateScenarioTestCasesCommand.cs- 批量创建场景测试用例命令BatchCreateScenarioTestCasesResponse.cs- 批量创建场景测试用例响应BatchCreateScenarioTestCasesCommandHandler.cs- 批量创建场景测试用例命令处理器
-
Queries/GetScenarioTestCases/
GetScenarioTestCasesQuery.cs- 获取场景测试用例列表查询GetScenarioTestCasesResponse.cs- 获取场景测试用例列表响应GetScenarioTestCasesQueryHandler.cs- 获取场景测试用例列表查询处理器
主要功能特性:
- 单个创建:支持创建单个场景测试用例
- 批量创建:支持批量创建多个场景测试用例,包含成功/失败统计
- 重复检查:防止在同一场景中重复添加相同的测试用例
- 场景验证:确保场景存在后才允许添加测试用例
- 详细信息查询:支持查询包含测试用例流程名称的详细信息
- 过滤功能:支持按启用状态过滤测试用例
设计架构遵循:
- 参考
ProtocolVersions的设计模式 - 使用 MediatR 的 CQRS 模式
- 统一的错误处理和日志记录
- 完整的审计信息记录
- 遵循 DDD 设计原则
3. 待完成的工作
后续步骤:
- 创建对应的 Controller 层
- 添加单元测试
- 更新 API 文档
- 创建前端界面组件
2024-12-19 - 修复 ScenarioType 类型找不到的问题
问题描述
在 X1.Domain/Entities/TestCase/TestScenario.cs 文件中,ScenarioType 类型无法找到,导致编译错误:
- 未能找到类型或命名空间名"ScenarioType"(是否缺少 using 指令或程序集引用?)
- 当前上下文中不存在名称"ScenarioType"
解决方案
- 创建 ScenarioType 枚举文件:在
X1.Domain/Entities/TestCase/ScenarioType.cs中定义了ScenarioType枚举 - 枚举定义包含以下类型:
- Functional (功能测试) = 1
- Performance (性能测试) = 2
- Stress (压力测试) = 3
- Compatibility (兼容性测试) = 4
- Regression (回归测试) = 5
- Integration (集成测试) = 6
- Security (安全测试) = 7
- UserExperience (用户体验测试) = 8
修改的文件
X1.Domain/Entities/TestCase/ScenarioType.cs(新增)modify.md(更新,用于记录修改)
技术细节
- 枚举遵循项目现有的命名规范,使用
Display和Description特性 - 与
TestScenario.cs在同一命名空间X1.Domain.Entities.TestCase中,无需额外 using 语句 - 默认值设置为
ScenarioType.Functional,符合测试场景的常见用途
验证
- 编译错误已解决
TestScenario.cs中的ScenarioType引用现在可以正确识别- 枚举值
ScenarioType.Functional在默认值设置中正常工作
2024-12-19 - 添加测试场景相关实体
新增文件
1. X1.Domain/Entities/TestCase/TestScenario.cs
- 创建测试场景实体
- 包含字段:
- 场景编码 (ScenarioCode)
- 场景名称 (ScenarioName)
- 场景类型 (Type) - 枚举类型
- 场景说明 (Description)
- 是否启用 (IsEnabled)
- 优先级 (Priority)
- 提供 Create() 和 Update() 方法
- 包含场景类型枚举 (ScenarioType)
2. X1.Domain/Entities/TestCase/ScenarioTestCase.cs
- 创建场景测试用例实体(替代糟糕的 TestScenarioTestCase 命名)
- 表示场景中包含的测试用例
- 包含字段:
- 场景ID (ScenarioId)
- 测试用例流程ID (TestCaseFlowId)
- 是否启用 (IsEnabled)
- 执行顺序 (ExecutionOrder)
- 提供 Create() 和 Update() 方法
修改文件
1. X1.Domain/Entities/TestCase/TestCaseFlow.cs
- 添加导航属性:
TestScenarioTestCases - 支持与场景的一对多关系
关系设计
- TestScenario (1) ←→ (N) ScenarioTestCase (N) ←→ (1) TestCaseFlow
- 一个场景可以包含多个测试用例
- 一个测试用例可以属于多个场景
- 通过中间表 ScenarioTestCase 实现多对多关系
2025-01-21 - CreateScenarioTestCaseCommandHandler 在 TestScenariosController 中的实现检查
检查结果
经过详细检查,CreateScenarioTestCaseCommandHandler 在 TestScenariosController 中已正确实现。
实现详情
-
控制器方法:
- 方法名:
CreateScenarioTestCase - 路由:
[HttpPost("{scenarioId}/testcases")] - 参数:
scenarioId(路由参数) +CreateScenarioTestCaseCommand(请求体)
- 方法名:
-
命令处理器:
- 文件:
X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseCommandHandler.cs - 功能:批量创建场景测试用例,支持重复检查、错误处理和事务管理
- 文件:
-
相关类文件:
CreateScenarioTestCaseCommand.cs- 命令类CreateScenarioTestCaseResponse.cs- 响应类GetScenarioTestCasesQuery.cs- 查询类GetScenarioTestCasesQueryHandler.cs- 查询处理器GetScenarioTestCasesResponse.cs- 查询响应类ScenarioTestCaseDto.cs- 数据传输对象
功能特性
- 批量创建:支持一次创建多个测试用例
- 重复检查:自动检查场景中是否已存在相同的测试用例流程
- 错误处理:详细的错误信息返回,包括成功和失败的数量
- 事务管理:使用 UnitOfWork 确保数据一致性
- 用户认证:验证当前用户身份
- 日志记录:完整的操作日志记录
API 端点
- POST
/api/testscenarios/testcases- 创建场景测试用例 - GET
/api/testscenarios/{scenarioId}/testcases- 获取场景测试用例列表
结论
CreateScenarioTestCaseCommandHandler 已完整实现在 TestScenariosController 中,所有相关的命令、查询、响应类都已正确创建,功能完整且符合 CQRS 模式。
2025-01-21 - 修复 ScenarioBindingView 中的绑定功能实现
问题描述
在 ScenarioBindingView.tsx 中,handleBindTestCases 函数只是打印了日志,但没有实际调用 scenarioService.createScenarioTestCase 接口进行真正的绑定操作。
解决方案
-
完善绑定逻辑:
- 构建正确的绑定请求数据格式
- 调用
scenarioService.createScenarioTestCase接口 - 处理成功和失败的响应结果
-
数据格式:
const bindData = { testCases: selectedTestCases.map((testCaseId, index) => ({ testCaseFlowId: testCaseId, executionOrder: index + 1, // 按选择顺序设置执行顺序 loopCount: 1, // 默认循环次数为1 isEnabled: true // 默认启用 })) }; -
响应处理:
- 显示成功绑定的数量
- 显示失败的数量和错误信息
- 绑定成功后清空选择并重新加载数据
修改的文件
X1.WebUI/src/pages/scenarios/scenario-binding/ScenarioBindingView.tsx- 修复
handleBindTestCases函数,实现真正的API调用 - 添加详细的错误处理和用户反馈
- 修复
功能验证
- scenarioService.ts 中已正确实现
createScenarioTestCase方法 - API 端点:
POST /api/testscenarios/{scenarioId}/testcases - 数据格式:与后端
CreateScenarioTestCaseCommand完全匹配 - 错误处理:完整的成功/失败状态处理
使用流程
- 用户在左侧选择场景
- 在右侧选择要绑定的测试用例
- 点击"绑定"按钮
- 系统调用API进行绑定
- 显示绑定结果并更新界面状态
2025-01-21 - 修复场景测试用例绑定400错误问题
问题描述
在调用 POST /api/testscenarios/{scenarioId}/testcases 接口时出现400 Bad Request错误,主要原因是:
- 缺少验证器:
CreateScenarioTestCaseCommand没有对应的验证器 - 缺少全局异常处理:验证失败时抛出
ValidationException但没有全局异常处理器 - 数据绑定问题:前端请求数据格式与后端期望格式可能存在不匹配
解决方案
-
创建验证器:
- 创建
CreateScenarioTestCaseCommandValidator验证器 - 创建
ScenarioTestCaseItemValidator验证器 - 验证场景ID、测试用例列表、执行顺序、循环次数等字段
- 创建
-
添加全局异常处理中间件:
- 创建
GlobalExceptionHandlerMiddleware全局异常处理中间件 - 处理
ValidationException、DomainException、ArgumentException等各种异常 - 返回统一的错误响应格式
- 创建
-
注册中间件:
- 在
Program.cs中注册全局异常处理中间件 - 确保中间件在正确的顺序执行
- 在
修改的文件
-
X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseRequest.cs
- 新增请求模型文件
- 包含场景ID和测试用例列表字段
-
X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseCommandValidator.cs
- 新增验证器文件
- 验证场景ID和测试用例列表的有效性
- 添加请求模型验证器
-
X1.Infrastructure/Middleware/GlobalExceptionHandlerMiddleware.cs
- 新增全局异常处理中间件
- 处理各种类型的异常并返回适当的HTTP状态码
-
X1.WebAPI/Program.cs
- 注册全局异常处理中间件
- 确保中间件在认证中间件之前执行
-
X1.Application/DependencyInjection.cs
- 添加
services.AddValidatorsFromAssembly(assembly)注册FluentValidation验证器 - 确保验证器能够被自动发现和注册
- 添加
-
X1.Presentation/Controllers/TestScenariosController.cs
- 修改控制器方法,使用新的请求模型
- 更新API路由为
POST /api/testscenarios/testcases
-
X1.WebUI/src/services/scenarioService.ts
- 更新前端服务调用,适配新的API路由
- 将scenarioId包含在请求体中
验证器规则
-
场景ID验证:
- 不能为空
- 长度不超过50个字符
-
测试用例列表验证:
- 不能为空
- 至少包含一个测试用例
-
测试用例项验证:
- 测试用例流程ID不能为空且长度不超过50个字符
- 执行顺序必须大于等于0
- 循环次数必须大于0且不超过1000
- 启用状态不能为空
异常处理
- 验证异常:返回400状态码和详细的验证错误信息
- 领域异常:返回400状态码和业务错误信息
- 参数异常:返回400状态码和参数错误信息
- 未授权异常:返回401状态码
- 其他异常:返回500状态码和通用错误信息
错误响应格式
{
"isSuccess": false,
"data": null,
"successMessage": null,
"errorMessages": ["具体的错误信息"],
"timestamp": "2025-01-21T10:30:00Z",
"path": "/api/testscenarios/testcases",
"method": "POST"
}
测试建议
- 正常情况:发送有效的请求数据,验证绑定成功
- 验证失败:发送无效数据,验证返回400状态码和详细错误信息
- 边界情况:测试各种边界值,如空列表、无效ID等
2025-01-21 - 修复 ScenarioBindingView 绑定接口调用问题
问题描述
在 ScenarioBindingView.tsx 中,handleBindTestCases 函数只是打印了日志,但没有实际调用 scenarioService.createScenarioTestCase 接口进行真正的绑定操作。
解决方案
-
完善绑定逻辑:
- 构建正确的绑定请求数据格式
- 调用
scenarioService.createScenarioTestCase接口 - 处理成功和失败的响应结果
-
数据格式:
const bindData = { testCases: selectedTestCases.map((testCaseId, index) => ({ testCaseFlowId: testCaseId, executionOrder: index + 1, // 按选择顺序设置执行顺序 loopCount: 1, // 默认循环次数为1 isEnabled: true // 默认启用 })) }; -
响应处理:
- 显示成功绑定的数量
- 显示失败的数量和错误信息
- 绑定成功后清空选择并重新加载数据
修改的文件
X1.WebUI/src/pages/scenarios/scenario-binding/ScenarioBindingView.tsx- 修复
handleBindTestCases函数,实现真正的API调用 - 添加详细的错误处理和用户反馈
- 绑定成功后重新加载场景数据
- 修复
功能验证
- scenarioService.ts 中已正确实现
createScenarioTestCase方法 - API 端点:
POST /api/testscenarios/testcases - 数据格式:与后端
CreateScenarioTestCaseCommand完全匹配 - 错误处理:完整的成功/失败状态处理
使用流程
- 用户在左侧选择场景
- 在右侧选择要绑定的测试用例
- 点击"绑定"按钮
- 系统调用API进行绑定
- 显示绑定结果并更新界面状态
修改详情
- 替换了原来的模拟绑定逻辑
- 添加了完整的API调用和错误处理
- 实现了绑定成功后的数据刷新
- 提供了详细的用户反馈信息
2024-12-19 - 重构 TestScenarioTestCase 实体命名和结构
问题描述
TestScenarioTestCase命名不规范,重复了 "Test" 前缀- 实体功能过于简单,缺乏测试用例执行所需的配置信息
- 命名不符合领域驱动设计的最佳实践
解决方案
-
重命名为
ScenarioTestCase:- 更简洁明了的命名
- 符合领域语言
- 避免重复的 "Test" 前缀
-
简化实体功能:
- 保留核心字段:场景ID、测试用例流程ID、启用状态、执行顺序
- 移除复杂的执行配置,保持实体简洁
- 专注于场景与测试用例的关联关系
-
更新导航属性:
TestScenario.TestScenarioTestCases→TestScenario.ScenarioTestCases- 更符合领域语言
修改的文件
X1.Domain/Entities/TestCase/ScenarioTestCase.cs(新增,替代旧文件)X1.Domain/Entities/TestCase/TestScenario.cs(更新导航属性)X1.Domain/Entities/TestCase/TestScenarioTestCase.cs(删除)
技术特性
- 领域驱动设计:使用更符合业务语言的命名
- 简洁设计:专注于核心关联关系,避免过度设计
- 向后兼容:保持原有的核心功能
- 易于维护:简化的结构便于理解和维护
2025-01-22 - 修复Swagger生成异常:ScenarioTestCaseDto schemaId冲突
问题描述
- Swagger生成时出现schemaId冲突异常
- 两个不同的命名空间中定义了相同名称的
ScenarioTestCaseDto类:X1.Application.Features.ScenarioTestCases.Queries.GetScenarioTestCases.ScenarioTestCaseDtoX1.Application.Features.TestScenarios.Queries.GetTestScenarioById.ScenarioTestCaseDto
- 导致Swagger无法正确生成API文档
解决方案
-
创建共享DTO类:
- 在
X1.Application/Features/Common/Dtos/ScenarioTestCaseDto.cs创建统一的DTO类 - 包含所有必要的字段:ScenarioId、TestCaseFlowId、TestCaseFlowName、ExecutionOrder、LoopCount、IsEnabled、CreatedAt、CreatedBy、UpdatedAt、UpdatedBy
- 确保字段类型一致性(如TestCaseFlowName为可空类型)
- 在
-
更新现有文件:
- 移除两个响应文件中的重复DTO定义
- 添加对共享DTO的using引用
- 更新查询处理器中的DTO创建代码,确保所有字段都被正确映射
-
修改的文件:
X1.Application/Features/Common/Dtos/ScenarioTestCaseDto.cs(新增)X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesResponse.csX1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdResponse.csX1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesQueryHandler.csX1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdQueryHandler.cs
技术特性
- 代码复用:避免重复的DTO定义
- 一致性:确保所有使用场景的字段类型一致
- 维护性:集中管理DTO定义,便于后续维护
- Swagger兼容:解决schemaId冲突,确保API文档正确生成
2024-12-19 - 修复 ScenarioType 类型不匹配问题
问题描述
前端和后端的 ScenarioType 类型定义不一致,导致创建测试场景时出现类型错误:
- 后端:
ScenarioType是枚举类型,值为整数 (1, 2, 3, 4) - 前端:
ScenarioType定义为string类型,但在接口中期望number类型
解决方案
将前端的 ScenarioType 类型从 string 改为 number,并更新相关组件:
1. 更新类型定义
- 文件:
X1.WebUI/src/services/scenarioService.ts - 修改:将
export type ScenarioType = string;改为export type ScenarioType = number;
2. 更新表单组件
- 文件:
X1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigForm.tsx - 修改内容:
- 默认值从
'Functional'改为1(对应功能测试) - 表单值处理:使用
parseInt()转换字符串为数字 - 选项值:使用
type.value(数字)而不是type.enumValue(字符串) - 类型比较:使用
t.value === formData.type进行数字比较 - 显示值:使用
formData.type.toString()转换为字符串显示
- 默认值从
技术细节
- 类型一致性:前后端都使用整数类型表示场景类型
- 枚举映射:后端枚举值 1=Functional, 2=Performance, 3=Stress, 4=Compatibility
- 默认选择:创建新场景时默认选择功能测试(值为1)
- 表单验证:确保类型字段为有效数字
验证
- 编译错误已解决
- 类型检查通过
- 表单组件正常工作
- 前后端类型匹配
相关文件
X1.WebUI/src/services/scenarioService.tsX1.WebUI/src/pages/scenarios/scenario-config/ScenarioConfigForm.tsxX1.Domain/Entities/TestCase/ScenarioType.cs(后端枚举定义)
2024-12-19 - 修复 GetScenarioTypesQueryHandler 中的 GetDisplayName 方法调用错误
问题描述
在 GetScenarioTypesQueryHandler.cs 第30行出现编译错误:
- 错误:
CS0103 当前上下文中不存在名称"GetDisplayName" - 原因:
GetDisplayName是扩展方法,需要使用正确的调用语法
解决方案
修复扩展方法调用语法:
- 文件:
X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQueryHandler.cs - 修改:将
GetDisplayName((ScenarioType)st.Value)改为((ScenarioType)st.Value).GetDisplayName()
技术细节
GetDisplayName是定义在X1.Domain.Common.EnumExtensions中的扩展方法- 扩展方法需要通过实例调用,而不是静态方法调用
- 正确的语法:
enumValue.GetDisplayName()而不是GetDisplayName(enumValue)
验证
- 编译错误已解决
- 扩展方法调用语法正确
- 场景类型查询处理器正常工作
2025-01-22 - 为 TestCaseDetailDrawer 添加连线箭头功能
修改内容
-
问题分析:
TestCaseDetailDrawer.tsx中的 ReactFlow 组件缺少连线箭头功能- 需要与
ReactFlowDesigner.tsx保持一致的视觉效果 - 连线缺少方向指示,影响用户体验
-
解决方案:
- 在
getReactFlowData方法中为 edges 添加markerEnd属性 - 使用与
ReactFlowDesigner.tsx相同的箭头配置 - 添加类型断言解决 TypeScript 类型兼容性问题
- 在
-
技术实现:
const flowEdges = testCase.edges.map(edge => ({ id: edge.id, source: edge.source, target: edge.target, sourceHandle: edge.sourceHandle, targetHandle: edge.targetHandle, type: 'smoothstep', style: { stroke: '#3b82f6', strokeWidth: 2 }, markerEnd: { type: 'arrowclosed' as const, width: 8, height: 8, color: '#3b82f6', }, data: edge.data })) as Edge[]; -
优化内容:
- 添加
EdgeMarker类型导入 - 使用类型断言确保类型安全
- 保持与设计器组件一致的箭头样式
- 添加
修改时间
2025-01-22
修改原因
用户要求在 FormTypeDrawer.tsx 中保存之前 ReactFlowDesigner.tsx 的连线箭头功能,实际需要在 TestCaseDetailDrawer.tsx 中添加箭头功能。
验证方法
- 打开测试用例详情抽屉,查看连线是否显示箭头
- 确认箭头样式与设计器中的一致
- 验证箭头方向正确指示流程方向
2025-01-22 - 修复 ReactFlow 缩放显示问题
修改内容
-
问题分析:
- ReactFlow 缩放功能显示 NaN 或缩放值不更新
onMove回调接收的是WheelEvent而不是viewport对象- 需要正确监听视口变化来更新缩放值
-
解决方案:
- 使用
useEffect和setInterval定期检查视口变化 - 通过
reactFlowInstance.getViewport()获取当前视口状态 - 添加数值验证,确保只有有效的缩放值才会更新状态
- 优化显示逻辑,防止 NaN 显示
- 使用
-
技术实现:
// 使用 useEffect 监听视口变化 useEffect(() => { const interval = setInterval(() => { const viewport = reactFlowInstance.getViewport(); const { zoom } = viewport; if (typeof zoom === 'number' && !isNaN(zoom) && zoom !== currentZoom) { setCurrentZoom(zoom); } }, 100); // 每100ms检查一次 return () => clearInterval(interval); }, [reactFlowInstance, currentZoom]); -
优化内容:
- 移除调试日志,提高性能
- 添加缩放值变化检测,避免不必要的状态更新
- 确保缩放显示始终为有效数值
修改时间
2025-01-22
修改原因
用户报告缩放功能显示 NaN 或缩放值不更新,影响用户体验。
验证方法
- 测试缩放功能,确保缩放值实时更新
- 验证缩放显示不会出现 NaN
- 确认缩放重置功能正常工作
2025-01-22 - 修复 ImsiRegistrationRecord 外键约束错误
修改内容
-
问题分析:
- TestCaseNode 有两个ID字段:
Id:数据库主键(通过Guid.NewGuid().ToString()生成)NodeId:前端ReactFlow的节点ID(前端传入)
- ImsiRegistrationRecord.NodeId 应该引用
TestCaseNode.Id(数据库主键),而不是TestCaseNode.NodeId(前端节点ID) - 在
BuildImsiRegistrationRecord方法中,传递的是nodeData.Id(前端节点ID),导致外键约束失败
- TestCaseNode 有两个ID字段:
-
解决方案:
- 在
CreateNodeEntitiesAsync方法中创建nodeIdMapping字典,保存前端节点ID到数据库节点ID的映射 - 修改
ProcessFormDataBatchAsync方法签名,添加nodeIdMapping参数 - 修改
BuildFormDataEntities方法,使用nodeIdMapping获取正确的数据库节点ID - 在创建
ImsiRegistrationRecord时使用数据库节点ID而不是前端节点ID
- 在
-
技术实现:
- 使用
Dictionary<string, string>存储前端节点ID到数据库节点ID的映射 - 在创建
TestCaseNode后立即保存映射关系 - 在
BuildFormDataEntities中使用TryGetValue安全获取数据库节点ID - 添加详细的日志记录,区分前端节点ID和数据库节点ID
- 使用
-
修改的方法:
CreateNodeEntitiesAsync:添加节点ID映射逻辑ProcessFormDataBatchAsync:添加nodeIdMapping参数BuildFormDataEntities:使用正确的数据库节点ID创建实体
修改时间
2025-01-22
修改原因
用户报告外键约束错误:insert or update on table "tb_imsi_registration_records" violates foreign key constraint "FK_tb_imsi_registration_records_tb_testcasenode_NodeId"。问题在于 ImsiRegistrationRecord.NodeId 引用了错误的前端节点ID而不是数据库节点ID。
验证方法
- 创建测试用例流程时,确保
ImsiRegistrationRecord的NodeId字段正确引用TestCaseNode.Id - 检查数据库中的外键约束是否满足
- 验证日志中显示的前端节点ID和数据库节点ID映射是否正确
2025-01-22 - 修复 CreateNodesAsync 方法中的设备注册表单处理逻辑
修改内容
-
完善 CreateNodesAsync 方法中的表单处理逻辑:
- 修复了
if (nodeData.IsFormEnabled==true && nodeData.FormData is not null)条件判断 - 使用
Select预处理节点数据,分离表单处理逻辑,避免嵌套循环 - 提取了表单处理逻辑到独立的方法中,提高代码可维护性
- 创建了
DeviceRegistrationFormData类来映射前端表单数据
- 修复了
-
添加设备注册表单数据处理:
- 当
FormType.DeviceRegistrationForm时,解析表单数据并创建ImsiRegistrationRecord实体 - 支持双卡配置:
isDualSim、sim1Plmn、sim1CellId、sim1RegistrationWaitTime等字段 - 自动保存 IMSI 注册记录到数据库
- 使用策略模式处理不同类型的表单数据,便于扩展
- 实现并行处理表单数据,提高性能
- 分离实体组装和数据库保存操作,实现更好的解耦
- 当
-
依赖注入和仓储模式:
- 添加了
IImsiRegistrationRecordRepository依赖注入 - 使用仓储模式保存 IMSI 注册记录
- 确保数据一致性和事务完整性
- 添加了
-
数据模型定义:
- 在
X1.Domain/Models/DeviceRegistrationFormData.cs中创建了DeviceRegistrationFormData类,包含以下字段:NodeId: 节点IDIsDualSim: 是否双卡Sim1Plmn: 卡1 PLMNSim1CellId: 卡1 CellIdSim1RegistrationWaitTime: 卡1 注册等待时间Sim2Plmn: 卡2 PLMNSim2CellId: 卡2 CellIdSim2RegistrationWaitTime: 卡2 注册等待时间
- 创建了
ProcessedNodeData类用于预处理节点数据,包含:NodeData: 原始节点数据FormDataJson: 序列化后的表单数据JSON字符串HasFormData: 是否有表单数据需要处理
- 创建了
FormDataEntities类用于强类型管理表单数据实体,包含:ImsiRegistrationRecords: IMSI 注册记录集合HasAnyEntities(): 检查是否有任何实体需要保存GetTotalEntityCount(): 获取所有实体的总数量
- 删除了冗余的
ProcessDeviceRegistrationFormAsync方法,其功能已被新的批量处理方法替代 - 创建了
TestCaseFlowBuilder类,将节点和连线创建逻辑从命令处理器中提取出来 - 在
TestCaseFlowBuilder中进一步提取了PreprocessNodes和CreateNodeEntitiesAsync方法,使代码结构更清晰 - 在
X1.Application/DependencyInjection.cs中注册了TestCaseFlowBuilder服务 - 修复了
DeviceRegistrationFormData类的 JSON 反序列化问题,添加了JsonPropertyName属性映射 - 在
TestCaseFlowBuilder中添加了详细的 JSON 反序列化错误处理和调试日志 - 修复了 JSON 双重序列化问题,添加了
GetFormDataJson方法来正确处理表单数据 - 使用
Newtonsoft.Json和System.Text.Json.Nodes.JsonObject.Parse来解决转义 JSON 字符串的反序列化问题 - 移除了
System.Text.Json引用,统一使用Newtonsoft.Json进行 JSON 序列化和反序列化 - 移除了冗余的
GetFormDataJson方法,简化了预处理逻辑 - 删除了冗余的
BuildFormDataEntity方法,将表单处理逻辑直接整合到BuildFormDataEntities方法中
- 在
-
技术特性:
- 类型安全:使用强类型的数据模型
- JSON 序列化:正确解析前端传递的 JSON 数据
- 错误处理:包含空值检查和异常处理
- 日志记录:添加详细的日志信息
- 架构规范:将数据模型放在 Domain 层,遵循 DDD 架构原则
- 代码重构:提取表单处理逻辑到独立方法,提高代码可读性和可维护性
- 扩展性:使用 switch 语句处理不同表单类型,便于后续添加新的表单类型
- 性能优化:使用
Select预处理数据,实现并行处理表单数据 - 分离关注点:将节点创建和表单处理分离,提高代码清晰度
- 解耦设计:分离实体组装(同步)和数据库保存(异步)操作
- 合理使用异步:只在真正需要异步的地方使用异步操作,避免为异步而异步
- 类型安全:使用强类型替代
object,避免运行时类型错误 - 代码清理:删除冗余方法,保持代码简洁
- 职责分离:将流程构建逻辑提取到专门的构建器类中
- 方法细化:将复杂方法拆分为更小、更专注的方法
- 依赖注入:正确注册和配置服务依赖
- 数据映射:确保前后端数据格式的一致性
修改时间
2025-01-22
修改原因
用户要求修复 CreateNodesAsync 方法中的设备注册表单处理逻辑,确保能够正确解析前端传递的表单数据并创建对应的数据库记录。
2025-01-22 - 添加 FormType 字段到 ReactFlowDesigner 并创建对应的表单组件
修改内容
-
更新 ReactFlowDesigner 中的节点数据结构:
- 在
FlowNode类型中添加formType?: number字段 - 在创建新节点时包含
formType: step.formType字段
- 在
-
创建表单类型对应的 Drawer 组件:
DeviceRegistrationDrawer.tsx- 设备注册表单 (FormType.DeviceRegistrationForm = 1)NetworkConnectivityDrawer.tsx- 网络连通性测试表单 (FormType.NetworkConnectivityForm = 2)NetworkPerformanceDrawer.tsx- 网络性能测试表单 (FormType.NetworkPerformanceForm = 3)VoiceCallDrawer.tsx- 语音通话测试表单 (FormType.VoiceCallForm = 4)
-
表单组件特性:
- 每个组件都有完整的表单验证
- 支持初始数据加载和编辑
- 统一的 UI 设计和交互模式
- 类型安全的数据接口
-
添加右键菜单表单显示功能:
- 在右键菜单中添加了"显示表单"选项
- 根据节点的
formType显示对应的表单组件 - 添加了表单状态管理和处理函数
- 支持表单数据的保存和是否开启状态管理
-
样式优化:
- 将
ReactFlowDesigner.tsx中的内联样式提取到单独的reactflow.css文件中 - 便于后续维护和样式管理
- 将
-
提取表单类型定义到单独文件:
- 创建
X1.WebUI/src/types/formTypes.ts文件 - 包含所有表单数据类型的定义
- 添加 FormType 枚举和辅助函数
- 便于后期维护和类型管理
- 创建
表单类型映射
- FormType.NoForm (0) - 无表单,无需特殊处理
- FormType.DeviceRegistrationForm (1) - 设备注册表单,支持双卡配置
- FormType.NetworkConnectivityForm (2) - 网络连通性测试表单,Ping 测试配置
- FormType.NetworkPerformanceForm (3) - 网络性能测试表单,Iperf 测试配置
- FormType.VoiceCallForm (4) - 语音通话测试表单,通话配置
修改时间
2025-01-22
修改原因
用户要求在 ReactFlowDesigner 中检查并添加 FormType 字段,根据不同的表单类型显示不同的表单组件。
2025-01-22 - 节点数据保存优化
修改内容
-
TestCasesView.tsx 节点数据转换优化:
- 更新了
handleSaveFormSubmit函数中的节点数据转换逻辑 - 在
convertedNodes映射中添加了以下字段:formData: node.data?.formData- 表单数据字段formType: node.data?.formType- 表单类型字段isFormEnabled: node.data?.isFormEnabled- 是否开启表单字段
- 更新了
-
数据完整性保障:
- 确保表单配置数据能够完整保存到后端
- 支持后续的流程加载和表单状态恢复
- 保持前端表单状态与后端数据的一致性
-
技术特性:
- 类型安全:使用可选链操作符确保安全访问
- 数据完整性:保存所有必要的表单相关字段
- 向后兼容:不影响现有的节点数据结构
- 用户体验:确保表单配置在流程保存后能够正确恢复
修改时间
2025-01-22
修改原因
用户要求在保存测试用例流程时,确保 formData、formType 和 isFormEnabled 字段也被包含在节点数据中,以便后续加载流程时能够正确恢复表单状态。
2025-01-22 - 后端 NodeData 添加表单相关字段
修改内容
-
CreateTestCaseFlowCommand.cs 中的 NodeData 类增强:
- 在
NodeData类中添加了以下表单相关字段:FormData: object?- 表单数据字段,支持任意类型的表单数据FormType: int?- 表单类型字段,对应 FormType 枚举值IsFormEnabled: bool?- 是否开启表单字段,表示表单是否已配置
- 在
-
前后端数据一致性:
- 确保后端
NodeData类与前端传递的数据结构完全匹配 - 支持表单配置数据的完整保存和恢复
- 保持类型安全和数据完整性
- 确保后端
-
技术特性:
- 类型安全:使用可空类型确保向后兼容
- 数据完整性:支持完整的表单配置信息
- 扩展性:为未来表单功能扩展预留空间
- 一致性:前后端数据结构完全对应
修改时间
2025-01-22
修改原因
用户要求在后端的 NodeData 类中添加对应的表单相关字段,以匹配前端传递的 formData、formType 和 isFormEnabled 数据,确保前后端数据结构一致。
2025-01-22 - TestCaseNode 实体和处理器添加表单支持
修改内容
-
TestCaseNode 实体增强:
- 在
TestCaseNode实体中添加了以下表单相关字段:FormData: string?- 表单数据字段,以JSON格式存储FormType: int?- 表单类型字段,对应 FormType 枚举值IsFormEnabled: bool- 是否开启表单字段,默认值为 false
- 更新了
Create方法,添加了表单相关参数支持 - 保持了向后兼容性,所有新参数都有默认值
- 在
-
CreateTestCaseFlowCommandHandler 处理器更新:
- 在
CreateNodesAsync方法中添加了表单数据处理逻辑 - 将前端传递的
FormData对象序列化为JSON字符串存储 - 传递
FormType和IsFormEnabled字段到TestCaseNode.Create方法 - 添加了完整的表单数据保存支持
- 在
-
技术特性:
- JSON序列化:使用
System.Text.Json.JsonSerializer将表单数据序列化为JSON字符串 - 类型安全:使用可空类型确保向后兼容
- 数据完整性:支持完整的表单配置信息保存
- 扩展性:为未来表单功能扩展预留空间
- JSON序列化:使用
修改时间
2025-01-22
修改原因
用户要求在 CreateTestCaseFlowCommandHandler 中也处理新增的表单相关字段,确保表单配置数据能够正确保存到数据库中。
2025-01-22 - 前端表单数据序列化优化
修改内容
-
TestCasesView.tsx 数据序列化:
- 修改了
convertedNodes中的formData字段处理逻辑 - 将前端传递的
formData对象使用JSON.stringify()序列化为JSON字符串 - 确保与后台
FormData字段的 JSON 格式存储方式保持一致 - 添加了空值检查,避免序列化
null或undefined值
- 修改了
-
数据格式统一:
- 前端:
formData对象 →JSON.stringify()→ JSON字符串 - 后台:
FormData字段接收 JSON字符串格式的数据 - 数据库:以 JSON字符串格式存储表单配置数据
- 前端:
-
技术特性:
- 类型安全:确保数据格式的一致性
- 向后兼容:保持现有功能不受影响
- 数据完整性:完整的表单配置信息保存
修改时间
2025-01-22
修改原因
用户指出后台 FormData 字段是 JSON 格式字符串,需要将前端传递的 formData 对象序列化为 JSON 字符串以匹配后台存储格式。
2025-01-22 - 表单数据条件序列化和服务接口更新
修改内容
-
TestCasesView.tsx 条件序列化优化:
- 修改了
formData字段的处理逻辑,现在根据isFormEnabled来判断是否序列化表单数据 - 只有当
isFormEnabled为true且formData存在时才进行 JSON 序列化 - 修复了类型错误,将
null改为undefined以匹配接口定义
- 修改了
-
testcaseService.ts 接口更新:
- 在
CreateNodeData接口中添加了表单相关字段:formData?: string- 表单数据(JSON格式)formType?: number- 表单类型isFormEnabled?: boolean- 是否开启表单
- 确保前端接口定义与后端
NodeData类保持一致
- 在
-
数据逻辑优化:
- 条件序列化:只有在表单开启且有数据时才序列化,避免存储空数据
- 类型安全:修复了 TypeScript 类型错误
- 接口一致性:前后端接口定义完全匹配
修改时间
2025-01-22
修改原因
用户要求根据 isFormEnabled 字段来判断是否序列化 formData,并且需要检查 testcaseService.ts 是否已经更新以匹配新的 CreateTestCaseFlowCommand 结构。
2025-01-22 - 表单数据工具类提取和类型优化
修改内容
-
创建表单数据工具类:
- 新建
X1.WebUI/src/utils/formDataUtils.ts文件 - 提取了表单数据创建逻辑,不再需要
type字段 - 提供了统一的表单数据创建接口:
createDeviceRegistrationFormDatacreateNetworkConnectivityFormDatacreateNetworkPerformanceFormDatacreateVoiceCallFormData
- 新建
-
类型系统优化:
- 在
formTypes.ts中添加了SimpleFormData类型 - 移除了
type字段,因为节点已经有formType - 简化了表单数据结构,避免冗余信息
- 在
-
ReactFlowDesigner.tsx 重构:
- 更新了所有表单保存处理函数,使用新的工具类
- 修改了
updateNodeFormData函数参数类型 - 更新了
formData状态变量类型 - 修复了 Drawer 组件的
initialData类型转换
-
代码优化:
- 类型安全:使用类型断言确保正确的数据类型传递
- 代码复用:表单数据创建逻辑集中管理
- 维护性:表单数据创建逻辑独立维护
- 一致性:统一的数据创建模式
修改时间
2025-01-22
修改原因
用户要求将 typedFormData 提取到单独的文件中维护,不需要 type 字段,因为节点已经有了 formType。
2025-01-22 - 修复右键菜单中 formType = 0 的处理
修改内容
-
完善 getFormTypeName 函数:
- 在
getFormTypeName函数中添加了case 0: return '无表单';的处理 - 确保所有表单类型都有对应的名称显示
- 在
-
右键菜单逻辑验证:
- 确认右键菜单的条件判断
formType > 0是正确的 - 确认
handleShowForm函数中的检查node.data.formType === 0是正确的 - 当
formType为 0 时,不会显示"显示表单"选项
- 确认右键菜单的条件判断
-
逻辑完整性:
- 右键菜单显示:只有当
formType > 0时才显示表单配置选项 - 表单打开检查:在
handleShowForm中再次检查formType === 0的情况 - 双重保护:确保无表单的节点不会意外打开表单
- 右键菜单显示:只有当
修改时间
2025-01-22
修改原因
用户指出右键菜单在 formType 为 0 时显示不正确,需要完善表单类型名称的处理逻辑。
2025-01-22 - 修复表单数据结构,移除多余的 data 包装
修改内容
-
简化表单数据结构:
- 移除了
formData中外层的data包装 - 现在
formData直接是具体的数据对象,而不是{"data": {...}}格式 - 例如:
{"nodeId":"node-1","isDualSim":false,...}而不是{"data":{"nodeId":"node-1","isDualSim":false,...}}
- 移除了
-
更新 formDataUtils.ts:
- 修改所有表单数据创建函数,直接返回数据对象
- 更新返回类型,不再使用
SimpleFormData包装 - 简化了数据创建逻辑
-
更新 formTypes.ts:
- 修改
SimpleFormData类型定义,直接使用具体的数据类型 - 移除了
{ data: ... }的包装结构
- 修改
-
更新 ReactFlowDesigner.tsx:
- 修改 Drawer 组件的
initialData类型转换 - 直接使用具体的数据类型进行类型断言
- 简化了数据传递逻辑
- 修改 Drawer 组件的
-
数据结构对比:
- 之前:
{"data":{"nodeId":"node-1","isDualSim":false,...}} - 现在:
{"nodeId":"node-1","isDualSim":false,...}
- 之前:
修改时间
2025-01-22
修改原因
用户指出 formData 中不需要 data 包装,数据结构应该是直接的数据对象而不是 {"data": {...}} 格式。
2025-01-27 - TestStepsTable.tsx 表格对齐问题修复
修改内容
-
问题描述:
- 表格的表头和表体单元格没有对齐,导致显示效果不佳
- 原代码将表头和表体分为两个独立的
Table组件,导致列宽计算不一致
-
解决方案:
- 将表头和表体合并到一个
Table组件中 - 移除了分离的表头和表体容器
- 确保所有列使用相同的宽度计算逻辑
- 将表头和表体合并到一个
-
具体修改:
- 删除了分离的表头容器
<div className="bg-background border-b shadow-sm"> - 删除了分离的表体容器
<div className="h-[calc(100vh-400px)] min-h-[300px] max-h-[calc(100vh-200px)] overflow-auto"> - 将
TableHeader和TableBody合并到同一个Table组件中 - 保持了原有的样式和功能
- 删除了分离的表头容器
-
影响:
- 修复了表格对齐问题
- 提高了表格的视觉效果
- 保持了原有的响应式设计和交互功能
修改时间
2025-01-27
修改原因
用户反映 TestStepsTable.tsx 表格的单元格和表头没有对齐,需要修复表格布局问题。
2025-01-22 - TestStepForm 改为 Drawer 方式
修改内容
-
创建 TestStepDrawer 组件:
- 新建
X1.WebUI/src/pages/teststeps/TestStepDrawer.tsx文件 - 将原来的 TestStepForm 改为 Drawer 方式
- 保持所有原有功能,包括步骤类型选择、图标选择、表单类型选择、步骤映射等
- 添加 Drawer 特有的状态管理和生命周期管理
- 新建
-
更新 TestStepsView 组件:
- 修改
X1.WebUI/src/pages/teststeps/TestStepsView.tsx文件 - 将 Dialog 组件替换为 Drawer 组件
- 更新状态管理:
open→drawerOpen,editOpen→editDrawerOpen - 移除 Dialog 相关的导入和组件使用
- 添加两个 TestStepDrawer 实例:一个用于创建,一个用于编辑
- 修改
-
删除旧文件:
- 删除
X1.WebUI/src/pages/teststeps/TestStepForm.tsx文件 - 清理不再使用的组件引用
- 删除
功能特点
- Drawer 布局:使用右侧滑出的 Drawer 组件,提供更好的用户体验
- 状态管理:独立的创建和编辑抽屉状态管理
- 表单重置:抽屉打开时自动重置表单数据
- 响应式设计:支持不同屏幕尺寸的响应式布局
- 保持功能:所有原有功能完全保留,包括复杂的步骤类型选择和验证逻辑
技术特性
- 组件复用:创建和编辑使用同一个 TestStepDrawer 组件
- 状态隔离:创建和编辑状态完全独立,避免数据冲突
- 生命周期管理:使用 useEffect 管理抽屉打开时的数据初始化
- 用户体验:Drawer 方式提供更流畅的交互体验
修改时间
2025-01-22
修改原因
用户要求将 TestStepForm.tsx 改为 Drawer 方式,提供更好的用户体验和界面布局。
2025-01-22 - 步骤配置架构优化和性能提升
修改内容
-
创建配置文件 stepConfigs.ts:
- 提取所有步骤类型、处理步骤、表单类型配置到独立文件
- 统一图标映射管理,支持动态图标组件生成
- 提供工具函数:
getIconComponent、getFormTypeIcon、getStepTypeConfig等 - 增强可维护性和扩展性
-
优化表单类型数据加载:
- 将
fetchFormTypes从TestStepForm移至TestStepsView - 避免每次打开表单都重复请求,提升性能
- 通过 props 传递数据:
formTypes、stepTypes、stepMappings、loadingFormTypes
- 将
-
完善步骤映射功能:
- 添加
StepMappingDto接口支持 - 处理步骤选择时自动设置
mapping和formType - 使用配置化的步骤映射关系
- 添加步骤映射选择界面,支持手动选择控制器类名
- 实现表单类型与步骤映射的双向绑定
- 添加
-
代码结构优化:
- 移除
TestStepForm中的重复函数定义 - 使用配置文件中的
STEP_TYPES和PROCESSING_STEPS - 简化组件逻辑,提高代码复用性
- 移除
-
增强用户体验:
- 添加步骤映射选择下拉框,显示 ShortName 和描述
- 实现表单类型变化时自动过滤相关步骤映射
- 步骤映射变化时自动设置对应的表单类型
- 添加选择状态提示和帮助信息
- 移除旧的文本输入框,统一使用下拉选择
- 为所有步骤类型提供合适的映射选项(处理步骤显示所有相关映射,非处理步骤显示空控制器)
-
修复映射字段类型问题:
- 将
mapping字段从string类型改为number类型,匹配后端StepMapping枚举 - 更新
CreateTestStepRequest和UpdateTestStepRequest接口 - 更新
TestStep接口的mapping字段类型 - 修复配置文件中的映射值,使用枚举数值而非字符串
- 更新相关的工具函数和组件逻辑以支持数值类型映射
- 将
-
优化步骤映射选择界面:
- 简化映射显示,只显示控制器名称,移除描述拼接
- 为不同步骤类型提供专门的映射选项(使用正确的枚举值):
- 开始步骤:可选择
StartFlowController(1)和EmptyController(0) - 结束步骤:可选择
EndFlowController(2)和EmptyController(0) - 处理步骤:排除通用控制器(0,1,2),只显示具体操作控制器(3-10)
- 判断步骤:只可选择
EmptyController(0)
- 开始步骤:可选择
- 更新提示信息,准确描述各步骤类型的映射用途
性能优化
- 减少网络请求:表单类型映射数据只在列表页加载一次
- 提升用户体验:消除重复加载,表单打开更快速
- 内存优化:避免重复创建相同的数据结构
修改时间
2025-01-22
修改原因
用户要求优化性能,避免每次打开表单都重复请求表单类型数据,同时希望将配置提取出来便于后续扩展和维护。
2025-01-22 - FormType 枚举命名规范化
修改内容
-
重新设计 FormType 枚举命名:
- 重点强调"Form"表单概念
- 采用简洁明确的表单类型命名
- 每个枚举值都以"Form"结尾,突出表单属性
-
具体命名变更:
None→NoForm- 无表单Registration→DeviceRegistrationForm- 设备注册表单Ping→NetworkConnectivityForm- 网络连通性测试表单Iperf→NetworkPerformanceForm- 网络性能测试表单Call→VoiceCallForm- 语音通话测试表单
-
简化描述信息:
- 移除冗余的"绑定"和"处理"概念
- 直接描述表单类型和用途
- 保持描述简洁明了
命名规范特点
- Form 核心:每个枚举值都明确包含"Form"概念
- 功能描述:结合具体功能描述,使命名更具语义性
- 一致性:统一的命名模式,便于理解和维护
- 扩展性好:为未来添加新的表单类型预留空间
修改时间
2025-01-22
修改原因
用户要求结合功能描述和Form概念,采用更完美的命名方式,使FormType枚举既体现表单属性,又准确描述具体功能。
2025-01-21 - 创建 DeviceRegistrationDrawer 组件
修改内容
-
创建 DeviceRegistrationDrawer 组件:
- 新建
X1.WebUI/src/components/testcases/DeviceRegistrationDrawer.tsx文件 - 用于处理
FormType.DeviceRegistrationForm类型的表单 - 基于
ImsiRegistrationRecord实体结构设计
- 新建
-
组件特性:
- 只读字段:
NodeId为只读,不可编辑(由后台传入) - 双卡支持:支持单卡和双卡模式切换
- 表单验证:必填字段验证和数据类型验证
- 响应式设计:使用 Drawer 组件,支持滚动
- 只读字段:
-
表单字段:
- 基本信息:NodeId(只读)、双卡模式开关
- 卡1配置:PLMN(必填)、CellId(可选)、注册等待时间
- 卡2配置:仅在双卡模式下显示,字段与卡1相同
-
数据接口:
DeviceRegistrationData接口与后端ImsiRegistrationRecord实体对应- 支持初始数据传入和编辑模式
- 提供
onSave回调处理保存逻辑
-
修复问题:
- 移除未使用的
Separator导入 - 移除
testCaseId相关处理(由后台自动获取) - 简化基本信息布局,只显示节点ID
- 移除未使用的
修改原因
用户需要为 FormType.DeviceRegistrationForm 类型创建专门的表单组件,用于配置设备注册相关的参数,包括 PLMN、CellId、注册等待时间等。testCaseId 由后台自动处理,不需要在前端配置。
技术特点
- 使用 Drawer 组件提供良好的用户体验
- 支持深色主题和响应式设计
- 完整的表单验证和错误处理
- 与后端实体结构完全对应
- 支持编辑模式和新建模式
- 移除冗余的 testCaseId 处理逻辑
修改时间
2025-01-21
2025-01-22 - TestStepForm.tsx 添加表单类型选择功能
修改内容
-
导入必要的组件和类型:
- 添加
useEffecthook 用于组件生命周期管理 - 导入
Select相关组件用于表单类型选择 - 导入
teststepsService和FormTypeDto类型 - 添加表单类型相关的图标组件
- 添加
-
添加状态管理:
- 添加
formTypes状态用于存储表单类型数据 - 添加
loadingFormTypes状态用于管理加载状态
- 添加
-
添加表单类型相关函数:
getDefaultFormType()- 根据步骤类型获取默认表单类型getFormTypeIcon()- 根据表单类型获取对应图标fetchFormTypes()- 获取表单类型数据handleStepTypeChange()- 处理步骤类型变化,自动设置对应的表单类型
-
更新表单数据结构:
- 在
formData中添加formType字段 - 在提交数据时包含表单类型信息
- 在
-
添加表单类型选择界面:
- 在步骤图标选择之后添加表单类型选择组件
- 使用下拉选择器展示所有可用的表单类型
- 添加表单类型图标预览
- 根据步骤类型显示相应的提示信息
功能特点
- 智能默认值:根据步骤类型自动设置合适的表单类型
- 动态加载:组件加载时自动获取表单类型数据
- 用户友好:提供图标预览和详细描述
- 类型安全:使用强类型确保数据一致性
- 响应式设计:适配深色模式和浅色模式
表单类型映射
- 开始步骤 (1):默认使用
FormType.None (0) - 结束步骤 (2):默认使用
FormType.None (0) - 处理步骤 (3):可选择所有表单类型,默认
FormType.None (0) - 判断步骤 (4):默认使用
FormType.None (0)
修改时间
2025-01-22
修改原因
用户要求在 TestStepForm.tsx 中添加表单类型选择功能,支持用户为测试步骤选择不同的表单类型,包括无表单、注册表单、Ping表单、iperf表单、打电话表单等。
2024-12-19 - 测试用例流程保存功能优化
修改内容
-
创建保存表单组件 (
X1.WebUI/src/components/testcases/SaveTestCaseForm.tsx)- 新增保存测试用例流程的表单组件
- 包含名称、描述、类型三个字段
- 添加节点数量验证(至少3个节点才能保存)
- 参考 TerminalServiceForm.tsx 的样式设计
- 添加节点数量提示和保存按钮状态控制
-
修改测试用例视图 (
X1.WebUI/src/pages/testcases/TestCasesView.tsx)- 修改
handleSaveFlow函数,改为显示保存表单而不是直接保存 - 新增
handleSaveFormSubmit函数处理表单提交 - 添加状态管理:
showSaveForm、pendingNodes、pendingEdges - 保存成功后自动跳转到列表页面
- 传递节点数量给保存表单组件
- 修改
-
重新设计测试用例列表页面 (
X1.WebUI/src/pages/testcases/TestCasesListView.tsx)- 参考 ProtocolsView.tsx 和 ProtocolsTable.tsx 的布局和样式
- 移除 Card 组件的使用,采用统一的背景和边框样式
- 添加分页功能(PaginationBar)
- 添加表格工具栏(TableToolbar)
- 添加密度控制和列显示控制
- 改进搜索功能,支持状态筛选
- 统一表格样式和操作按钮样式
功能改进
-
保存流程优化
- 用户点击保存时弹出表单填写基本信息
- 验证节点数量,确保流程完整性
- 保存成功后自动返回列表页面
-
界面样式统一
- 与系统其他页面保持一致的布局和样式
- 支持深色/浅色主题切换
- 响应式设计,适配不同屏幕尺寸
-
用户体验提升
- 添加加载状态和错误提示
- 支持表格密度调整
- 支持列显示控制
- 改进搜索和筛选功能
技术细节
- 使用 React Hook 管理组件状态
- 采用 TypeScript 确保类型安全
- 使用 Tailwind CSS 实现响应式设计
- 集成现有的 UI 组件库
- 保持与后端 API 的数据格式一致
2025-01-22 - CaseStepConfig 添加 FormType 字段数据库迁移
修改内容
-
创建数据库迁移:
- 新建迁移文件:
20250822101301_AddFormTypeToCaseStepConfig.cs - 迁移名称:
AddFormTypeToCaseStepConfig - 迁移时间:2025-08-22 10:13:01
- 新建迁移文件:
-
迁移内容:
- 为
tb_casestepconfig表添加formtype字段 - 字段类型:
integer(对应 FormType 枚举) - 约束:
NOT NULL - 默认值:
0(对应 FormType.None)
- 为
-
迁移应用:
- 成功应用迁移到数据库
- 执行 SQL:
ALTER TABLE tb_casestepconfig ADD formtype integer NOT NULL DEFAULT 0; - 更新迁移历史表:
__EFMigrationsHistory
解决的问题
- 数据库同步:CaseStepConfig 实体已添加 FormType 属性,但数据库表缺少对应字段
- 数据一致性:确保实体模型与数据库表结构完全一致
- 功能支持:为表单类型功能提供数据库支持
技术特点
- 使用 Entity Framework Core 迁移机制
- 保持数据完整性,设置合理的默认值
- 支持回滚操作(Down 方法)
- 完整的迁移历史记录
修改时间
2025-01-22
修改原因
用户反映 CaseStepConfig 有更新,需要重新迁移数据库表以反映实体模型的变更,特别是添加的 FormType 字段。
2025-01-21 - 添加表单类型到用例步骤配置
修改内容
-
创建表单类型枚举:
- 新建
X1.Domain/Entities/TestCase/FormType.cs文件 - 定义
FormType枚举,包含以下类型:None = 0- 无表单(默认)Registration = 1- 注册表单Ping = 2- Ping表单Iperf = 3- iperf表单Call = 4- 打电话表单
- 新建
-
更新 CaseStepConfig 实体:
- 在
X1.Domain/Entities/TestCase/CaseStepConfig.cs中添加FormType属性 - 默认值设置为
FormType.None - 更新
Create方法,添加formType参数,默认值为FormType.None - 更新默认构造函数,设置
FormType = FormType.None
- 在
-
更新数据库配置:
- 在
X1.Infrastructure/Configurations/TestCase/CaseStepConfigConfiguration.cs中添加FormType字段映射 - 字段名设置为
formtype,设置为必填字段
- 在
-
更新应用层命令和响应:
- 在
X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommand.cs中添加FormType属性 - 在
X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsResponse.cs中添加FormType和FormTypeName属性
- 在
解决的问题
- 表单类型支持:为用例步骤配置添加表单类型支持
- 默认值设置:启动和结束步骤默认使用
FormType.None - 扩展性:支持注册表单、Ping表单、iperf表单、打电话表单等多种表单类型
技术特点
- 使用枚举类型确保类型安全
- 默认值设置为
None,符合启动和结束步骤的需求 - 完整的数据库映射配置
- 前后端数据传输对象更新
修改时间
2025-01-21
修改原因
用户要求为用例步骤配置添加表单类型支持,包括注册表单、Ping表单、iperf表单、打电话表单,并且启动和结束步骤默认使用None类型。
2025-01-21 - 添加FormType到CaseStepType转换工具
修改内容
-
创建转换工具类:
- 新建
X1.Application/Features/CaseStepConfigs/Queries/FormTypeStepTypeConverter.cs文件 - 提供
FormType到CaseStepType的转换功能 - 包含以下方法:
GetRecommendedStepType(FormType formType)- 根据表单类型获取推荐的步骤类型GetFormTypeToStepTypeMapping()- 获取表单类型与步骤类型的映射关系GetSupportedFormTypes(CaseStepType stepType)- 获取步骤类型支持的表单类型列表IsCompatible(FormType formType, CaseStepType stepType)- 检查表单类型是否与步骤类型兼容GetFormTypeDescriptions()- 获取所有表单类型及其描述GetStepTypeDescriptions()- 获取所有步骤类型及其描述GetCompleteMappingInfo()- 获取表单类型和步骤类型的完整映射信息
- 新建
-
创建查询API:
- 新建
X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/目录 - 创建
GetFormTypeStepTypeMappingQuery.cs- 查询请求 - 创建
GetFormTypeStepTypeMappingResponse.cs- 响应DTO,包含:FormTypeDto- 表单类型信息StepTypeDto- 步骤类型信息MappingDto- 映射关系信息
- 创建
GetFormTypeStepTypeMappingQueryHandler.cs- 查询处理器
- 新建
功能特点
- 智能推荐:根据表单类型自动推荐合适的步骤类型
- 兼容性检查:验证表单类型与步骤类型的兼容性
- 完整映射:提供完整的表单类型和步骤类型映射关系
- API支持:通过查询API提供映射信息给前端使用
- 类型安全:使用强类型枚举确保类型安全
映射规则
- 开始步骤 (Start):仅支持
FormType.None - 结束步骤 (End):仅支持
FormType.None - 处理步骤 (Process):支持所有表单类型
- 判断步骤 (Decision):仅支持
FormType.None
修改时间
2025-01-21
修改原因
用户要求在 X1.Application.Features.CaseStepConfigs/Queries 目录中添加 FormType 到 CaseStepType 的转换集合/工具。
2025-01-21 - CreateCaseStepConfigCommand 已包含 FormType 属性
修改内容
经过检查发现,CreateCaseStepConfigCommand 类中已经包含了 FormType 属性:
/// <summary>
/// 表单类型
/// </summary>
public FormType FormType { get; set; } = FormType.None;
当前状态
- FormType 属性已存在:
CreateCaseStepConfigCommand类中已经正确添加了FormType属性 - 默认值已设置:默认值为
FormType.None,符合业务需求 - 验证特性已配置:属性已正确配置,无需额外修改
修改时间
2025-01-21
修改原因
用户要求为 CreateCaseStepConfigCommand 添加 FormType 属性,但检查后发现该属性已经存在且配置正确。
2025-01-21 - 修复 CreateCaseStepConfigCommandHandler 中 FormType 传递问题
修改内容
修复了 CreateCaseStepConfigCommandHandler 中 FormType 参数未传递给 CaseStepConfig.Create 方法的问题:
- 修复命令处理器:在
CreateCaseStepConfigCommandHandler.cs中,将request.FormType传递给CaseStepConfig.Create方法 - 更新响应对象:在
CreateCaseStepConfigResponse.cs中添加了FormType和FormTypeName属性 - 完善响应构建:在命令处理器中构建响应时包含表单类型信息
修改的文件
X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommandHandler.csX1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigResponse.cs
修改时间
2025-01-21
修改原因
用户反馈 CreateCaseStepConfigCommand 的 FormType 属性没有正确传递给 CaseStepConfig.Create 方法,导致表单类型信息丢失。
2025-01-21 - 修复查询处理器中的 FormType 处理问题
修改内容
修复了查询处理器中 FormType 信息缺失的问题:
- 修复 GetCaseStepConfigsQueryHandler:在
UseCaseStepConfigDto映射中添加了FormType和FormTypeName属性 - 修复 GetCaseStepConfigByIdQueryHandler:在响应构建中添加了
FormType和FormTypeName信息 - 更新响应类:
GetCaseStepConfigByIdResponse.cs添加了FormType和FormTypeName属性UpdateCaseStepConfigResponse.cs添加了FormType和FormTypeName属性
- 完善更新命令:在
UpdateCaseStepConfigCommand.cs中添加了FormType属性
修改的文件
X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsQueryHandler.csX1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdQueryHandler.csX1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdResponse.csX1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigResponse.csX1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommand.cs
修改时间
2025-01-21
修改原因
用户反馈查询处理器中缺少 FormType 信息的处理,需要确保所有相关的查询和响应都包含表单类型信息。
2025-01-21 - X1.WebUI Tabs 组件滚动条遮挡文字问题修复
修改内容
-
恢复滚动条功能并优化高度:
- 在
X1.WebUI/src/components/layout/Tabs.css中,恢复滚动条显示 - 使用
scrollbar-width: thin和height: 2px设置极细的滚动条 - 移除固定高度限制,使用
min-height: 100%让容器根据内容自适应
- 在
-
修复tab间距问题:
- 在
X1.WebUI/src/components/layout/Tabs.tsx中,为第一个tab添加左边距 - 使用条件渲染
index === 0 ? "ml-2 mr-2" : "mr-2"确保所有tab间距一致 - 解决第一个tab没有左边距的问题
- 在
解决的问题
- 容器高度不足:主要问题是容器高度不够容纳tab文字+滚动条
- 文字被滚动条遮挡:tab文字被水平滚动条遮挡,影响可读性
- 布局复杂化:之前的内边距方案过于复杂
技术特点
- 通过自适应高度彻底解决遮挡问题
- 使用
min-height: 100%让容器根据内容自动调整高度 - 恢复滚动条功能,使用极细的2px高度
- 移除所有固定高度限制,让布局更加灵活
- 保持响应式设计,适配不同屏幕尺寸和内容长度
修改时间
2025-01-21
修改原因
用户反馈完全隐藏滚动条后无法滚动,同时要求tab高度根据内容决定而不是固定高度。因此恢复滚动条功能,并采用自适应高度的方案:移除所有固定高度限制,让容器根据内容自动调整高度,这样既解决了文字被遮挡的问题,又保持了滚动功能,同时让布局更加灵活。
2025-01-21 - X1.WebUI Tabs 组件挤压 Sidebar 问题修复
修改内容
-
修复 Sidebar 被 Tabs 挤压的问题:
- 在
X1.WebUI/src/components/layout/Sidebar.tsx中,为 Sidebar 添加flex-shrink-0类,防止被 Tabs 组件挤压宽度 - 确保 Sidebar 在展开和折叠状态下都保持固定宽度
- 在
-
优化 Tabs 组件布局:
- 在
X1.WebUI/src/components/layout/Tabs.tsx中,为 Tabs 容器添加max-w-full和min-w-0类 - 确保 Tabs 容器不会超出可用空间,同时支持水平滚动
- 在
-
更新 CSS 样式:
- 在
X1.WebUI/src/components/layout/Tabs.css中,为滚动容器添加width: 100%和max-width: 100% - 确保标签容器有正确的宽度限制
- 在
解决的问题
- Sidebar 宽度被挤压:当 Tabs 标签特别多时,Sidebar 的宽度会被压缩变小
- 布局不稳定:Tabs 组件占用过多空间,影响整体布局
- 用户体验差:Sidebar 宽度变化导致界面不稳定
技术特点
- 使用
flex-shrink-0确保 Sidebar 不被压缩 - 使用
max-w-full限制 Tabs 容器最大宽度 - 保持水平滚动功能,标签过多时可以滚动查看
- 维持响应式设计,适配不同屏幕尺寸
修改时间
2025-01-21
修改原因
用户反馈当 Tabs 标签特别多时,Sidebar 的宽度会被挤压变小,影响界面布局和用户体验。
2025-01-21 - X1.WebUI Layout 组件分析文档生成
修改内容
创建了 X1.WebUI/components-layout-analysis.md 文档,详细分析了 X1.WebUI 中 layout 组件的功能、位置和作用。
分析内容
- DashboardLayout.tsx - 主布局容器,组织整个应用的布局结构
- Header.tsx - 顶部导航栏,包含侧边栏切换、主题切换、通知、搜索、用户菜单等功能
- Sidebar.tsx - 侧边导航栏,支持权限控制、菜单渲染、折叠状态管理
- Tabs.tsx - 多标签页管理,支持自动标签管理、标签切换、批量操作
- Content.tsx - 内容区域,承载页面主要内容
- NotificationDrawer.tsx - 通知抽屉,右侧滑出式通知列表
技术特点
- 响应式设计,支持不同屏幕尺寸
- 状态管理,使用 React Hooks
- 权限控制,基于用户权限的菜单过滤
- 用户体验,平滑过渡动画和直观交互
- 主题支持,明暗主题切换
使用场景
- 管理后台
- 多标签工作流
- 权限管理系统
- 响应式应用
修改时间
2025-01-21
修改原因
用户要求分析 X1.WebUI 中 layout 组件的功能、显示位置和作用,并生成相应的分析文档。
2024-12-19 - JwtProvider GetCurrentKey 缓存键问题分析
问题描述
用户发现 JwtProvider.GetCurrentKey() 方法中的缓存键 var cacheKey = $"{KeyCacheKey}Current"; 可能存在问题,质疑是否需要加入用户ID进行隔离。
代码分析
当前实现
private string GetCurrentKey()
{
try
{
var cacheKey = $"{KeyCacheKey}Current"; // "JwtKey_Current"
var cachedKey = _redisCacheService.Get<string>(cacheKey);
// ...
}
// ...
}
问题分析
- 缓存键过于简单:使用固定的
"JwtKey_Current"键 - 全局共享:所有用户共享同一个密钥缓存
- 缺少用户隔离:没有基于用户ID或其他标识符进行隔离
设计意图分析
经过分析,当前设计可能是有意为之的:
- JWT密钥是全局的:JWT签名密钥通常是应用级别的,所有用户共享
- 性能优化:避免为每个用户缓存不同密钥,减少内存占用
- 密钥轮换一致性:确保所有用户在同一时间使用相同密钥
- 简化架构:减少密钥管理的复杂性
建议的改进方案
方案1:保持当前设计(推荐)
如果JWT密钥确实是全局共享的,当前设计是合理的。
方案2:添加版本控制
private string GetCurrentKey()
{
try
{
var keyVersion = _keyRotationService.GetCurrentKeyVersion();
var cacheKey = $"{KeyCacheKey}Current_V{keyVersion}";
// ...
}
// ...
}
方案3:多租户支持(如果需要)
private string GetCurrentKey(string tenantId = null)
{
try
{
var cacheKey = string.IsNullOrEmpty(tenantId)
? $"{KeyCacheKey}Current"
: $"{KeyCacheKey}Current_Tenant_{tenantId}";
// ...
}
// ...
}
结论
当前实现不是bug,而是基于JWT密钥全局共享特性的合理设计。如果系统确实需要用户级别的密钥隔离,则需要重新设计密钥管理策略。
相关文件
X1.Infrastructure/Services/Authentication/JwtProvider.csX1.Infrastructure/Services/Authentication/KeyRotationService.csX1.Domain/Services/IKeyRotationService.cs
2024-12-19 - 单用户登录会话管理实现
问题描述
用户需要实现同一用户只能在一个浏览器登录的功能,其他登录会被踢出并加入黑名单。
解决方案
1. 创建会话管理服务
新增接口: X1.Domain/Services/ISessionManagementService.cs
public interface ISessionManagementService
{
Task<bool> CreateSessionAsync(string userId, string sessionId, string accessToken, string deviceInfo, TimeSpan expiry);
Task<bool> ValidateSessionAsync(string userId, string sessionId);
Task<string?> GetCurrentSessionIdAsync(string userId);
Task<bool> RevokeOtherSessionsAsync(string userId, string excludeSessionId);
Task<bool> RevokeSessionAsync(string userId, string sessionId);
Task<SessionInfo?> GetSessionInfoAsync(string userId, string sessionId);
Task<bool> UpdateSessionActivityAsync(string userId, string sessionId);
Task<int> CleanupExpiredSessionsAsync();
}
实现类: X1.Infrastructure/Services/Authentication/SessionManagementService.cs
- 使用Redis存储会话信息
- 实现会话创建、验证、撤销功能
- 自动踢出其他登录会话
2. 修改登录流程
更新: X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs
// 生成会话ID
var sessionId = Guid.NewGuid().ToString();
// 创建用户会话(踢出其他登录)
var sessionCreated = await _sessionManagementService.CreateSessionAsync(
user.Id,
sessionId,
accessToken,
deviceInfo,
sessionExpiry);
// 添加会话ID到Claims
claims.Add(new Claim("session_id", sessionId));
3. 创建会话验证中间件
新增: X1.Infrastructure/Middleware/SessionValidationMiddleware.cs
public class SessionValidationMiddleware
{
public async Task InvokeAsync(HttpContext context)
{
// 验证用户会话是否有效
var isSessionValid = await _sessionManagementService.ValidateSessionAsync(userId, sessionId);
if (!isSessionValid)
{
// 返回401未授权,让客户端重新登录
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
}
}
4. Redis存储结构
用户会话映射:
Key: UserSession:{userId}
Value: {sessionId}
Expiry: 会话过期时间
会话详细信息:
Key: SessionInfo:{userId}:{sessionId}
Value: {
"SessionId": "guid",
"AccessToken": "jwt_token",
"DeviceInfo": "Windows 10 - Chrome 120.0",
"CreatedAt": "2024-12-19T10:00:00Z",
"LastActivity": "2024-12-19T10:30:00Z",
"ExpiresAt": "2024-12-19T11:00:00Z"
}
Expiry: 会话过期时间 + 5分钟缓冲
撤销令牌黑名单:
Key: RevokedTokens:{token}
Value: true
Expiry: 30天
工作流程
1. 用户登录
- 验证用户凭据
- 生成新的会话ID
- 撤销用户的其他会话(踢出其他登录)
- 将旧会话的访问令牌加入黑名单
- 创建新会话并存储到Redis
- 在JWT Claims中包含会话ID
2. 请求验证
- JWT Bearer认证验证令牌有效性
- 会话验证中间件检查会话是否有效
- 验证当前会话ID是否匹配
- 检查会话是否过期或被撤销
- 更新会话活动时间
3. 会话撤销
- 将访问令牌加入黑名单
- 删除Redis中的会话信息
- 删除用户会话映射
优势
- 安全性: 确保同一用户只能在一个地方登录
- 实时性: 使用Redis实现快速会话验证
- 可扩展性: 支持多实例部署
- 可监控性: 完整的日志记录和会话信息
- 自动清理: 支持过期会话自动清理
使用方法
1. 注册服务
services.AddScoped<ISessionManagementService, SessionManagementService>();
2. 添加中间件
更新: X1.WebAPI/Program.cs
// 启用认证中间件
app.UseAuthentication();
// 启用会话验证中间件(单用户登录控制)
app.UseSessionValidation();
// 启用授权中间件
app.UseAuthorization();
添加引用:
using X1.Infrastructure.Middleware;
3. 客户端处理
当收到401状态码且错误码为SESSION_EXPIRED时,客户端应该:
- 清除本地存储的令牌
- 跳转到登录页面
- 提示用户"您的账号在其他地方登录"
相关文件
X1.Domain/Services/ISessionManagementService.csX1.Infrastructure/Services/Authentication/SessionManagementService.csX1.Infrastructure/Middleware/SessionValidationMiddleware.csX1.Application/Features/Auth/Commands/BaseLoginCommandHandler.csX1.Infrastructure/DependencyInjection.csX1.WebAPI/Program.cs
2025-01-21 - 缓存防护机制实现:防雪崩和防击穿
问题描述
当前的 IRedisCacheService 实现缺乏对缓存雪崩和缓存击穿的防护机制:
- 缓存雪崩:大量缓存同时过期导致数据库压力激增
- 缓存击穿:热点数据失效时大量请求直接访问数据库
- 空值攻击:恶意请求不存在的键导致数据库压力
修改内容
为 IRedisCacheService 添加防护机制,包括:
- 随机过期时间(防雪崩)
- 分布式锁 + 双重检查(防击穿)
- 空值缓存(防击穿)
新增接口方法
防雪崩方法
// 设置键值对(防雪崩:随机过期时间)
bool SetWithAvalancheProtection<T>(string key, T value, TimeSpan baseExpiry, double jitterFactor = 0.1);
Task<bool> SetWithAvalancheProtectionAsync<T>(string key, T value, TimeSpan baseExpiry, double jitterFactor = 0.1);
防击穿方法
// 获取值(防击穿:热点数据保护)
T? GetWithPenetrationProtection<T>(string key, Func<Task<T?>> dataLoader, TimeSpan? expiry = null);
Task<T?> GetWithPenetrationProtectionAsync<T>(string key, Func<Task<T?>> dataLoader, TimeSpan? expiry = null);
// 设置空值缓存(防击穿:缓存空结果)
bool SetNullValue<T>(string key, TimeSpan? expiry = null);
Task<bool> SetNullValueAsync<T>(string key, TimeSpan? expiry = null);
实现细节
防雪崩机制
public bool SetWithAvalancheProtection<T>(string key, T value, TimeSpan baseExpiry, double jitterFactor = 0.1)
{
// 计算随机过期时间,防止缓存雪崩
var jitter = baseExpiry.TotalMilliseconds * jitterFactor;
var randomJitter = _random.NextDouble() * jitter;
var actualExpiry = baseExpiry.Add(TimeSpan.FromMilliseconds(randomJitter));
return Set(key, value, actualExpiry);
}
防击穿机制
public async Task<T?> GetWithPenetrationProtectionAsync<T>(string key, Func<Task<T?>> dataLoader, TimeSpan? expiry = null)
{
// 1. 首先尝试从缓存获取
var cachedValue = await GetAsync<T>(key);
if (cachedValue != null) return cachedValue;
// 2. 检查空值缓存
var nullKey = GetPrefixedKey($"null:{key}");
if (await _database.KeyExistsAsync(nullKey))
{
return default; // 命中空值缓存,防止缓存击穿
}
// 3. 使用分布式锁防止并发重建
var lockKey = $"lock:{key}";
var lockValue = Guid.NewGuid().ToString();
if (await AcquireLockAsync(lockKey, lockValue, TimeSpan.FromSeconds(10)))
{
try
{
// 4. 双重检查
cachedValue = await GetAsync<T>(key);
if (cachedValue != null) return cachedValue;
// 5. 从数据源加载
var data = await dataLoader();
if (data != null)
{
await SetAsync(key, data, expiry);
return data;
}
else
{
// 6. 设置空值缓存
await SetNullValueAsync<T>(key, expiry ?? TimeSpan.FromMinutes(5));
return default;
}
}
finally
{
await ReleaseLockAsync(lockKey, lockValue);
}
}
else
{
// 7. 等待其他线程重建缓存
await Task.Delay(100);
return await GetAsync<T>(key);
}
}
使用示例
防雪崩使用
// 基础过期时间30分钟,实际过期时间会在27-33分钟之间随机分布
await _redisCache.SetWithAvalancheProtectionAsync("user:profile:123", userProfile,
TimeSpan.FromMinutes(30), 0.1);
防击穿使用
// 自动处理热点数据保护
var userProfile = await _redisCache.GetWithPenetrationProtectionAsync<UserProfile>(
"user:profile:123",
async () => await _userRepository.GetByIdAsync(123),
TimeSpan.FromMinutes(30)
);
优势分析
- 防雪崩:通过随机过期时间避免大量缓存同时失效
- 防击穿:通过分布式锁和双重检查避免热点数据并发重建
- 防空值攻击:通过空值缓存避免恶意请求穿透到数据库
- 无冗余代码:防护机制集成在现有接口中,不影响原有功能
- 高性能:利用Redis的原子操作和分布式锁特性
- 易使用:提供简单易用的API,开发者无需关心底层实现细节
2025-01-21 - JwtProvider 缓存服务优化:从ICacheService切换到IRedisCacheService
问题描述
JwtProvider当前使用ICacheService(内存缓存)来管理JWT相关的缓存数据,包括:
- JWT密钥缓存
- 撤销令牌列表
- 令牌黑名单
在分布式部署环境中,内存缓存存在以下问题:
- 多实例间缓存数据不共享,导致JWT状态不一致
- 应用重启后缓存数据丢失
- 无法支持水平扩展
修改内容
1. JwtProvider.cs 依赖注入修改
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 修改内容:
- 将
ICacheService _cacheService替换为IRedisCacheService _redisCacheService - 更新构造函数参数和注释
- 移除
Microsoft.Extensions.Caching.Memory的 using 语句
- 将
2. 缓存操作方法更新
-
RevokeToken 方法:
- 从
_cacheService.Set(cacheKey, true, options)改为_redisCacheService.Set(cacheKey, true, expiry) - 移除
MemoryCacheEntryOptions的使用,直接使用TimeSpan
- 从
-
AddToBlacklist 方法:
- 从
_cacheService.Set(cacheKey, true, options)改为_redisCacheService.Set(cacheKey, true, expiry) - 移除
MemoryCacheEntryOptions的使用,直接使用TimeSpan
- 从
-
GetCurrentKey 方法:
- 从
_cacheService.TryGetValue<string>(cacheKey, out var cachedKey)改为_redisCacheService.Get<string>(cacheKey) - 简化缓存获取逻辑,直接使用返回值判断
- 从
-
IsTokenRevoked 方法:
- 从
_cacheService.TryGetValue<bool>(cacheKey, out _)改为_redisCacheService.Get<bool>(cacheKey) - 简化检查逻辑,直接返回缓存值
- 从
-
IsTokenBlacklisted 方法:
- 从
_cacheService.TryGetValue<bool>(cacheKey, out _)改为_redisCacheService.Get<bool>(cacheKey) - 简化检查逻辑,直接返回缓存值
- 从
修改前后对比
修改前
private readonly ICacheService _cacheService;
public JwtProvider(
IOptions<JwtOptions> jwtOptions,
ILogger<JwtProvider> logger,
IKeyRotationService keyRotationService,
ICacheService cacheService,
IJwtValidationService validationService)
// 缓存操作
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromDays(7));
_cacheService.Set(cacheKey, true, options);
if (_cacheService.TryGetValue<string>(cacheKey, out var cachedKey))
{
return cachedKey!;
}
修改后
private readonly IRedisCacheService _redisCacheService;
public JwtProvider(
IOptions<JwtOptions> jwtOptions,
ILogger<JwtProvider> logger,
IKeyRotationService keyRotationService,
IRedisCacheService redisCacheService,
IJwtValidationService validationService)
// 缓存操作
var expiry = TimeSpan.FromDays(7);
_redisCacheService.Set(cacheKey, true, expiry);
var cachedKey = _redisCacheService.Get<string>(cacheKey);
if (!string.IsNullOrEmpty(cachedKey))
{
return cachedKey;
}
优势分析
1. 分布式环境支持
- 问题解决: 所有实例共享同一个Redis缓存,确保JWT状态一致性
- 应用场景: 支持水平扩展,多实例部署
2. 数据持久性
- 问题解决: Redis数据持久化,应用重启后数据不丢失
- 业务价值: 确保JWT撤销和黑名单状态在应用重启后仍然有效
2025-01-21 - EmailVerificationService 缓存服务优化:从ICacheService切换到IRedisCacheService
问题描述
EmailVerificationService当前使用ICacheService(内存缓存)来存储邮箱验证码,在分布式部署环境中存在以下问题:
- 多实例间验证码缓存不共享,用户可能在不同实例间验证失败
- 应用重启后验证码数据丢失,影响用户体验
- 无法支持水平扩展和负载均衡
修改内容
1. EmailVerificationService.cs 依赖注入修改
- 文件位置:
X1.Infrastructure/Services/UserManagement/EmailVerificationService.cs - 修改内容:
- 将
ICacheService _cache替换为IRedisCacheService _redisCache - 更新构造函数参数和注释
- 移除
Microsoft.Extensions.Caching.Memory的 using 语句
- 将
2. 缓存操作方法更新
-
GenerateAndSendVerificationCodeAsync 方法:
- 从
_cache.Set(cacheKey, cacheValue, new MemoryCacheEntryOptions{...})改为await _redisCache.SetAsync(cacheKey, cacheValue, expiry) - 移除
MemoryCacheEntryOptions的使用,直接使用TimeSpan - 添加异步操作和错误处理
- 从
-
VerifyCode 方法:
- 从
_cache.TryGetValue<VerificationData>(cacheKey, out var verificationData)改为_redisCache.Get<VerificationData>(cacheKey) - 简化缓存获取逻辑,直接使用返回值判断
- 更新缓存删除操作使用Redis方法
- 从
修改前后对比
修改前
private readonly ICacheService _cache;
_cache.Set(
cacheKey,
cacheValue,
new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_options.VerificationCodeExpirationMinutes)
});
if (!_cache.TryGetValue<VerificationData>(cacheKey, out var verificationData))
{
return false;
}
修改后
private readonly IRedisCacheService _redisCache;
var expiry = TimeSpan.FromMinutes(_options.VerificationCodeExpirationMinutes);
var success = await _redisCache.SetAsync(cacheKey, cacheValue, expiry);
var verificationData = _redisCache.Get<VerificationData>(cacheKey);
if (verificationData == null)
{
return false;
}
优势
- 分布式支持: 验证码在多个应用实例间共享
- 数据持久性: Redis数据持久化,应用重启后验证码不丢失
- 更好的性能: Redis专门为缓存优化,性能更佳
- 可扩展性: 支持集群部署,便于水平扩展
- 异步操作: 使用异步方法提高性能
2025-01-21 - 全面缓存服务优化:从ICacheService切换到IRedisCacheService
问题描述
在分布式部署环境中,多个服务使用ICacheService(内存缓存)存在以下问题:
- CaptchaVerificationService: 验证码验证状态无法跨实例共享
- AuthController: 登录尝试次数限制无法跨实例共享,攻击者可能绕过限制
- GenerateCaptchaCommandHandler: 频率限制无法跨实例共享,攻击者可能绕过限制
修改内容
1. CaptchaVerificationService.cs 优化
- 文件位置:
X1.Infrastructure/Services/Security/CaptchaVerificationService.cs - 修改内容:
- 将
ICacheService _cacheService替换为IRedisCacheService _redisCacheService - 更新构造函数参数和注释
- 更新缓存操作方法使用Redis API
- 将
2. AuthController.cs 优化
- 文件位置:
X1.Presentation/Controllers/AuthController.cs - 修改内容:
- 将
ICacheService _cache替换为IRedisCacheService _redisCache - 更新构造函数参数和注释
- 移除
MemoryCacheEntryOptions的使用,直接使用TimeSpan - 更新登录尝试次数限制的缓存操作
- 将
3. GenerateCaptchaCommandHandler.cs 优化
- 文件位置:
X1.Application/Features/Auth/Commands/GenerateCaptcha/GenerateCaptchaCommandHandler.cs - 修改内容:
- 将
ICacheService _cacheService替换为IRedisCacheService _redisCacheService - 更新构造函数参数
- 移除
Microsoft.Extensions.Caching.Memory的 using 语句 - 更新频率限制和验证码缓存的存储方式
- 将
修改前后对比
CaptchaVerificationService
// 修改前
private readonly ICacheService _cacheService;
var cachedCaptcha = _cacheService.Get<string>($"captcha:{captchaId}");
// 修改后
private readonly IRedisCacheService _redisCacheService;
var cachedCaptcha = _redisCacheService.Get<string>($"captcha:{captchaId}");
AuthController
// 修改前
private readonly ICacheService _cache;
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(_authConfig.LoginAttemptsWindowMinutes));
_cache.Set(cacheKey, attempts + 1, options);
// 修改后
private readonly IRedisCacheService _redisCache;
var expiry = TimeSpan.FromMinutes(_authConfig.LoginAttemptsWindowMinutes);
_redisCache.Set(cacheKey, attempts + 1, expiry);
GenerateCaptchaCommandHandler
// 修改前
private readonly ICacheService _cacheService;
var rateLimitOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(1));
_cacheService.Set(rateLimitKey, requestCount + 1, rateLimitOptions);
// 修改后
private readonly IRedisCacheService _redisCacheService;
var rateLimitExpiry = TimeSpan.FromMinutes(1);
_redisCacheService.Set(rateLimitKey, requestCount + 1, rateLimitExpiry);
全面优化优势
1. 安全性提升
- 登录尝试限制: 跨实例共享,防止攻击者绕过频率限制
- 验证码安全: 验证码状态跨实例共享,防止重复使用
- 频率限制: 请求频率限制跨实例生效,防止API滥用
2. 分布式支持
- 多实例部署: 所有缓存数据在多个应用实例间共享
- 负载均衡: 支持水平扩展,用户请求可以路由到任意实例
- 高可用性: Redis集群提供高可用性保障
3. 性能优化
- Redis性能: Redis专门为缓存优化,性能优于内存缓存
- 网络优化: Redis支持连接池和批量操作
- 内存管理: Redis自动管理内存,避免内存泄漏
4. 运维便利
- 监控支持: Redis提供丰富的监控指标
- 数据持久化: 应用重启后缓存数据不丢失
- 集群管理: 支持Redis集群,便于扩展和维护
3. 性能优化
- 高频访问: JWT验证是高频操作,Redis的高性能特性更适合
- 内存管理: 避免内存缓存占用过多应用内存
4. 运维友好
- 监控支持: Redis提供丰富的监控指标
- 数据管理: 支持缓存数据的查看、清理和管理
技术特性
- 自动注册: 通过依赖注入自动注册,无需修改DI配置
- 错误处理: 保持原有的错误处理和日志记录
- 缓存策略: 保持原有的缓存过期时间策略
影响范围
- 正面影响: 提升分布式环境下的JWT管理能力
- 无负面影响: 对现有功能无破坏性影响
- 性能提升: 在高并发场景下提供更好的性能
测试建议
- 验证JWT令牌生成和验证功能正常
- 测试令牌撤销功能在多个实例间同步
- 验证黑名单功能在多个实例间共享
- 测试应用重启后缓存数据恢复
- 验证密钥轮换功能正常工作
2025-01-21 - 修复ReactFlowDesigner节点类型不对称问题
问题描述
在ReactFlowDesigner.tsx中,所有节点的type都被硬编码为'testStep',导致保存的数据中所有节点类型都是相同的,无法区分不同类型的节点。
修改内容
1. ReactFlowDesigner.tsx
- 文件位置:
X1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx - 修改内容:
- 扩展了nodeTypes映射,添加了不同类型的节点类型:
startStep: 开始步骤 (stepType=1)endStep: 结束步骤 (stepType=2)processStep: 处理步骤 (stepType=3)decisionStep: 判断步骤 (stepType=4)testStep: 默认类型
- 在节点创建逻辑中添加了
getNodeType函数,根据stepType返回对应的节点类型 - 修改了
onDrop函数中的节点创建逻辑,使用动态的节点类型而不是硬编码的'testStep'
- 扩展了nodeTypes映射,添加了不同类型的节点类型:
2. TestCaseDetailDrawer.tsx
- 文件位置:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 修改内容:
- 扩展了nodeTypes映射,添加了相同的节点类型映射
- 在
getReactFlowData函数中添加了getNodeType函数 - 修改了节点数据转换逻辑,使用动态的节点类型
修改前后对比
修改前
// 所有节点都是相同的类型
const nodeTypes = {
testStep: TestStepNode,
};
const newNode = {
id: `node-${Date.now()}`,
type: 'testStep', // 硬编码
// ...
};
修改后
// 支持多种节点类型
const nodeTypes = {
testStep: TestStepNode,
startStep: TestStepNode,
endStep: TestStepNode,
processStep: TestStepNode,
decisionStep: TestStepNode,
};
const getNodeType = (stepType: number) => {
switch (stepType) {
case 1: return 'startStep';
case 2: return 'endStep';
case 3: return 'processStep';
case 4: return 'decisionStep';
default: return 'testStep';
}
};
const newNode = {
id: `node-${Date.now()}`,
type: getNodeType(step.stepType), // 动态类型
// ...
};
影响范围
- 修复了保存数据中节点类型不对称的问题
- 现在不同类型的节点会有不同的type值,便于区分和管理
- 保持了向后兼容性,所有节点仍然使用相同的TestStepNode组件进行渲染
测试建议
- 测试不同类型节点的创建和保存
- 验证保存的数据中节点类型是否正确
- 测试导入导出功能是否正常工作
- 确认节点显示和交互功能正常
2024年修改记录
分析相关页面搜索栏和按钮主题修复
修改时间: 2024年 修改文件:
X1.WebUI/src/pages/analysis/FunctionalAnalysisView.tsxX1.WebUI/src/pages/analysis/IssueAnalysisView.tsxX1.WebUI/src/pages/analysis/PerformanceAnalysisView.tsxX1.WebUI/src/pages/analysis/UEAnalysisView.tsx
修改内容:
-
搜索栏背景修复:
- 将硬编码的
bg-white改为bg-background - 添加
border-border类,使用主题变量 - 确保搜索栏背景与系统主题保持一致
- 将硬编码的
-
输入框样式优化:
- 移除自定义的
input类,使用默认的 Input 组件样式 - 确保输入框样式与系统主题一致
- 移除自定义的
-
选择框样式修复:
- 将选择框样式从简单的
input类改为完整的主题样式 - 添加焦点状态、禁用状态等完整的样式支持
- 使用主题变量:
border-input、bg-background、text-sm等
- 将选择框样式从简单的
-
按钮主题修复:
- 将硬编码的蓝色查询按钮改为使用主题变量
bg-primary和text-primary-foreground - 重置和展开按钮使用
bg-background、border-input等主题变量 - 添加悬停效果:
hover:bg-primary/90、hover:bg-accent等
- 将硬编码的蓝色查询按钮改为使用主题变量
修改原因:
- 修复分析相关页面搜索栏和按钮与系统主题不匹配的问题
- 确保在深色模式和浅色模式下都能正确显示
- 提供一致的用户体验和视觉效果
具体变更:
- <div className="flex flex-col bg-white p-4 rounded-md border mb-2">
+ <div className="flex flex-col bg-background p-4 rounded-md border border-border mb-2">
- <Input className="input flex-1" />
+ <Input className="flex-1" />
- <select className="input h-10 rounded border border-border bg-background px-3 text-sm flex-1">
+ <select className="h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 flex-1">
- <button className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50">重置</button>
+ <button className="px-4 py-2 text-sm font-medium text-foreground bg-background border border-input rounded-md hover:bg-accent hover:text-accent-foreground">重置</button>
- <button className="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700">查询</button>
+ <button className="px-4 py-2 text-sm font-medium text-primary-foreground bg-primary border border-transparent rounded-md hover:bg-primary/90">查询</button>
任务相关页面搜索栏主题修复
修改时间: 2024年 修改文件:
X1.WebUI/src/pages/tasks/TasksView.tsxX1.WebUI/src/pages/tasks/TaskExecutionView.tsxX1.WebUI/src/pages/tasks/TaskReviewView.tsx
修改内容:
-
搜索栏背景修复:
- 将硬编码的
bg-white改为bg-background - 添加
border-border类,使用主题变量 - 确保搜索栏背景与系统主题保持一致
- 将硬编码的
-
输入框样式优化:
- 移除自定义的
input类,使用默认的 Input 组件样式 - 确保输入框样式与系统主题一致
- 移除自定义的
-
选择框样式修复:
- 将选择框样式从简单的
input类改为完整的主题样式 - 添加焦点状态、禁用状态等完整的样式支持
- 使用主题变量:
border-input、bg-background、text-sm等
- 将选择框样式从简单的
修改原因:
- 修复任务相关页面搜索栏与系统主题不匹配的问题
- 确保在深色模式和浅色模式下都能正确显示
- 提供一致的用户体验和视觉效果
具体变更:
- <div className="flex flex-col bg-white p-4 rounded-md border mb-2">
+ <div className="flex flex-col bg-background p-4 rounded-md border border-border mb-2">
- <Input className="input flex-1" />
+ <Input className="flex-1" />
- <select className="input h-10 rounded border border-border bg-background px-3 text-sm flex-1">
+ <select className="h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 flex-1">
ScenariosView 搜索栏主题修复
修改时间: 2024年 修改文件:
X1.WebUI/src/pages/scenarios/ScenariosView.tsx
修改内容:
-
搜索栏背景修复:
- 将硬编码的
bg-white改为bg-background - 添加
border-border类,使用主题变量 - 确保搜索栏背景与系统主题保持一致
- 将硬编码的
-
输入框样式优化:
- 移除自定义的
input类,使用默认的 Input 组件样式 - 确保输入框样式与系统主题一致
- 移除自定义的
-
选择框样式修复:
- 将选择框样式从简单的
input类改为完整的主题样式 - 添加焦点状态、禁用状态等完整的样式支持
- 使用主题变量:
border-input、bg-background、text-sm等
- 将选择框样式从简单的
修改原因:
- 修复搜索栏与系统主题不匹配的问题
- 确保在深色模式和浅色模式下都能正确显示
- 提供一致的用户体验和视觉效果
具体变更:
- <div className="flex flex-col bg-white p-4 rounded-md border mb-2">
+ <div className="flex flex-col bg-background p-4 rounded-md border border-border mb-2">
- <Input className="input flex-1" />
+ <Input className="flex-1" />
- <select className="input h-10 rounded border border-border bg-background px-3 text-sm flex-1">
+ <select className="h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 flex-1">
CreateUserCommand 角色字段修复
CreateUserCommand 角色字段修复
修改时间: 2024年 修改文件:
X1.Application/Features/Users/Commands/CreateUser/CreateUserCommand.csX1.Application/Features/Users/Commands/CreateUser/CreateUserCommandHandler.csX1.Application/Features/Users/Commands/CreateUser/CreateUserCommandValidator.cs
修改内容:
-
CreateUserCommand 字段修改:
- 将
string[]? Roles = null改为string[] RoleIds - 添加
[Required]和[MinLength(1)]验证特性 - 移除默认值,设置为必填项
- 将
-
CreateUserCommandHandler 逻辑更新:
- 移除默认角色逻辑
request.Roles ?? new[] { "User" } - 直接使用
request.RoleIds调用AssignUserRolesAsync - 更新验证逻辑,检查角色ID而不是角色名称
- 移除默认角色逻辑
-
CreateUserCommandValidator 验证更新:
- 将
Roles验证改为RoleIds验证 - 添加必填验证:
NotEmpty()和Must(roleIds => roleIds.Length > 0) - 移除角色名称格式验证,保留角色ID长度验证
- 将
-
具体变更:
- 命令字段:
string[] RoleIds(必填) - 验证特性:
[Required(ErrorMessage = "角色ID数组不能为空")] - 验证特性:
[MinLength(1, ErrorMessage = "至少需要分配一个角色")] - 处理器调用:
AssignUserRolesAsync(user, request.RoleIds) - 验证器规则: 角色ID数组不能为空,至少需要一个角色
- 命令字段:
修改原因:
- 确保创建用户时必须分配角色,不能为空
- 使用角色ID而不是角色名称,与 UserRegistrationService 保持一致
- 提高数据完整性和业务逻辑的一致性
前端用户服务修复
修改时间: 2024年 修改文件:
X1.WebUI/src/services/userService.tsX1.WebUI/src/pages/users/UserForm.tsxX1.WebUI/src/pages/users/UsersView.tsxX1.WebUI/src/pages/users/UserTable.tsxX1.WebUI/src/pages/users/UserRolesForm.tsx
修改内容:
-
userService.ts 接口更新:
- 将
CreateUserRequest中的roles?: string[]改为roleIds: string[] - 将
User接口中的roles: string[]改为roleIds: string[] - 确保前后端数据结构一致
- 将
-
UserForm.tsx 组件修复:
- 将表单数据中的
roles改为roleIds - 更新角色选择逻辑,使用角色ID而不是角色名称
- 修复
handleRoleChange函数,处理角色ID数组
- 将表单数据中的
-
UsersView.tsx 页面修复:
- 更新
handleCreate函数参数类型 - 修复
handleSetRoles函数参数名 - 更新编辑用户时的初始数据传递
- 更新
-
UserTable.tsx 表格修复:
- 更新角色显示逻辑,使用
user.roleIds而不是user.roles - 确保表格正确显示角色ID信息
- 更新角色显示逻辑,使用
-
UserRolesForm.tsx 表单修复:
- 将表单字段从
roles改为roleIds - 更新复选框逻辑,使用角色ID进行选择
- 修复表单提交时的数据处理
- 将表单字段从
修改原因:
- 确保前端与后端 CreateUserCommand 的数据结构完全一致
- 使用角色ID而不是角色名称,提高数据准确性和一致性
- 修复所有相关的用户管理功能,确保正常工作
修改时间: 2024年 修改文件:
X1.Application/Features/Users/Commands/CreateUser/CreateUserCommand.csX1.Application/Features/Users/Commands/CreateUser/CreateUserCommandHandler.csX1.Application/Features/Users/Commands/CreateUser/CreateUserCommandValidator.cs
修改内容:
-
CreateUserCommand 字段修改:
- 将
string[]? Roles = null改为string[] RoleIds - 添加
[Required]和[MinLength(1)]验证特性 - 移除默认值,设置为必填项
- 将
-
CreateUserCommandHandler 逻辑更新:
- 移除默认角色逻辑
request.Roles ?? new[] { "User" } - 直接使用
request.RoleIds调用AssignUserRolesAsync - 更新验证逻辑,检查角色ID而不是角色名称
- 移除默认角色逻辑
-
CreateUserCommandValidator 验证更新:
- 将
Roles验证改为RoleIds验证 - 添加必填验证:
NotEmpty()和Must(roleIds => roleIds.Length > 0) - 移除角色名称格式验证,保留角色ID长度验证
- 将
-
具体变更:
- 命令字段:
string[] RoleIds(必填) - 验证特性:
[Required(ErrorMessage = "角色ID数组不能为空")] - 验证特性:
[MinLength(1, ErrorMessage = "至少需要分配一个角色")] - 处理器调用:
AssignUserRolesAsync(user, request.RoleIds) - 验证器规则: 角色ID数组不能为空,至少需要一个角色
- 命令字段:
修改原因:
- 确保创建用户时必须分配角色,不能为空
- 使用角色ID而不是角色名称,与 UserRegistrationService 保持一致
- 提高数据完整性和业务逻辑的一致性
2024-12-19 - 用户管理页面修复
问题分析
- UsersView.tsx 中的搜索字段与后端API不匹配
- 搜索字段使用了错误的字段名(name, phone, role, account, status)
- 后端API实际支持的搜索参数是:pageNumber, pageSize, searchTerm, isActive
- 搜索表单没有正确绑定状态和事件处理
修复内容
- 修正搜索字段配置,使其与后端API匹配
- 添加正确的状态管理和事件处理
- 保持原有的搜索栏样式,支持用户名、状态、角色搜索
- 修复搜索逻辑,确保查询参数正确传递
- 移除展开/收起功能,保持简洁的一行搜索布局
- 修复后端GetAllUsersQueryHandler响应,添加角色名称信息
- 更新前端User接口以匹配后端数据结构
- 修复前端组件中的字段名错误(id -> userId)
- 优化角色显示,使用角色名称而不是角色ID
- 性能优化:解决GetAllUsersQueryHandler的N+1查询问题,使用并行处理和角色缓存
修改文件
- X1.WebUI/src/pages/users/UsersView.tsx
- X1.WebUI/src/pages/users/UserTable.tsx
- X1.WebUI/src/services/userService.ts
- X1.Application/Features/Users/Queries/Dtos/UserDto.cs
- X1.Application/Features/Users/Queries/GetAllUsers/GetAllUsersQueryHandler.cs
修改时间
2024-12-19
修改原因
用户反映 UsersView.tsx 和 UserTable.tsx 与系统不匹配,需要参考 RolesView.tsx 和 RoleTable.tsx 的结构进行修复,确保前后端数据一致性和用户体验统一。
2025-01-21 - UserForm 组件添加角色选择功能
修改文件:
X1.WebUI/src/pages/users/UserForm.tsx - 为用户创建和编辑表单添加角色选择功能
修改内容:
-
角色选择功能添加:
- 导入依赖:添加
useEffect,useState和roleService,Role导入 - 状态管理:添加
roles和loading状态管理角色数据和加载状态 - 数据获取:在组件挂载时自动获取所有可用角色列表
- 角色选择:使用
Checkbox组件实现多角色选择功能
- 导入依赖:添加
-
角色选择界面:
- 加载状态:显示"加载角色中..."提示
- 网格布局:使用
grid grid-cols-2 gap-4布局,每行显示两个角色 - 复选框:每个角色使用独立的复选框,支持多选
- 标签显示:显示角色名称,使用
text-sm font-normal样式
-
角色数据处理:
- 初始值设置:在
formData中初始化roles字段为initialData?.roles || [] - 角色变更处理:实现
handleRoleChange方法处理角色选择/取消选择 - 数据同步:确保表单数据与角色选择状态保持同步
- 初始值设置:在
-
技术特性:
- 异步加载:使用
useEffect异步加载角色数据 - 错误处理:通过
roleService.getAllRoles()的错误处理机制 - 用户体验:加载状态提示,防止用户困惑
- 数据完整性:确保角色数据正确传递给后端
- 异步加载:使用
-
界面布局:
- 表单结构:在密码字段后添加角色选择区域
- 响应式设计:使用网格布局适应不同屏幕尺寸
- 视觉一致性:与现有表单字段保持一致的样式和间距
修改时间:
2025-01-21
修改原因:
用户发现虽然 userService.ts 中的 CreateUserRequest 接口支持 roles?: string[] 字段,但 UserForm.tsx 组件中缺少角色选择功能。需要添加角色选择功能,使用户能够在创建和编辑用户时选择多个角色,确保前端界面与后端 CreateUserCommand 完全匹配。
2025-01-21 - NodeData 添加 stepId 必填字段和 TestCaseNode 实体修复
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 为 NodeData 类添加 stepId 必填字段X1.Domain/Entities/TestCase/TestCaseNode.cs- 修复 TestCaseNode 实体的 stepId 字段和 Create 方法X1.Infrastructure/Configurations/TestCase/TestCaseNodeConfiguration.cs- 更新 TestCaseNode 数据库配置X1.WebUI/src/services/testcaseService.ts- 为 CreateNodeData 接口添加 stepId 必填字段X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复 TestCaseNode.Create 方法调用,添加 stepId 参数X1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复节点数据转换,添加 stepId 字段
修改内容:
-
NodeData 类增强:
- 在
NodeData类中添加了StepId属性 - 设置为必填字段:
[Required(ErrorMessage = "步骤ID不能为空")] - 类型为
string,默认值为null!
- 在
-
TestCaseNode 实体修复:
- StepId 字段:从
string?改为string,添加[Required]验证特性 - Create 方法:将
stepId参数从可选改为必填,移除默认值= null - 参数注释:更新注释说明 stepId 为必填参数
- StepId 字段:从
-
TestCaseNodeConfiguration 配置更新:
- 数据库约束:为
StepId字段添加IsRequired()约束 - 外键关系:将删除行为从
SetNull改为Restrict,防止删除步骤配置时影响节点数据
- 数据库约束:为
-
testcaseService.ts 前端服务修复:
- CreateNodeData 接口:添加
stepId: string必填字段 - 类型安全:确保前端创建节点时必须提供步骤ID
- 前后端一致:与后端 NodeData 类保持完全一致
- CreateNodeData 接口:添加
-
CreateTestCaseFlowCommandHandler 修复:
- TestCaseNode.Create 调用:添加
stepId: nodeData.StepId参数 - 编译错误修复:解决 CS7036 编译错误
- 参数完整性:确保所有必需参数都正确传递
- TestCaseNode.Create 调用:添加
-
TestCasesView 前端修复:
- 节点数据转换:在
handleSaveFlow函数中添加stepId: node.data?.stepId || ''字段 - 数据完整性:确保前端保存时包含步骤ID信息
- 前后端一致:与后端 CreateNodeData 接口保持完全一致
- 节点数据转换:在
-
技术特性:
- 数据验证:确保步骤ID字段不为空
- 用户友好:提供清晰的错误提示信息
- 业务逻辑:符合测试用例节点必须关联步骤配置的业务需求
- 类型安全:使用强类型验证,避免运行时错误
- 数据完整性:通过外键约束确保数据一致性
- 前后端一致:确保前端和后端的数据结构完全匹配
-
设计原则:
- 数据完整性:确保必要字段不为空
- 用户体验:提供明确的验证反馈
- 业务规则:符合测试用例流程设计的业务需求
- 代码一致性:与项目中其他必填字段的处理方式保持一致
- 数据库约束:通过数据库层约束确保业务规则
- 前后端同步:确保前端和后端的数据模型保持同步
修改时间:
2025-01-21
修改原因:
用户要求 NodeData 需要加一个 stepId 必填项,不能为空,同时发现 TestCaseNode 实体的 Create 方法中 stepId 参数设置为可选是不对的,需要修复为必填,并更新相应的数据库配置和前端服务,确保整个系统的数据一致性。
2025-01-21 - 创建 TestCaseFlow 相关表的数据库迁移
修改文件:
X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.cs- 新建迁移文件X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.Designer.cs- 迁移设计器文件X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs- 更新模型快照
修改内容:
-
迁移文件创建:
- 迁移名称:
AddTestCaseFlowTables - 迁移时间:2025-08-21 08:06:04
- 迁移描述:为 TestCaseFlow、TestCaseNode、TestCaseEdge 实体创建数据库表
- 迁移名称:
-
创建的表结构:
-
tb_testcaseflow:测试用例流程主表
- 包含 id、name、description、type、isenabled、viewport_x、viewport_y、viewport_zoom 等字段
- 包含审计字段:createdat、updatedat、createdby、updatedby
- 创建索引:name、type、isenabled
-
tb_testcasenode:测试用例节点表
- 包含 id、testcaseid、nodeid、sequencenumber、stepid、positionx、positiony、width、height 等字段
- 包含状态字段:isselected、positionabsolutex、positionabsolutey、isdragging
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 外键关系:stepid → tb_casestepconfig.id (RESTRICT)
- 创建索引:testcaseid、nodeid、sequencenumber、stepid
-
tb_testcaseedge:测试用例连线表
- 包含 id、testcaseid、edgeid、sourcenodeid、targetnodeid、edgetype、condition、isanimated、style 等字段
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 创建索引:testcaseid、edgeid、sourcenodeid、targetnodeid
-
-
数据库关系:
- 级联删除:删除测试用例流程时,自动删除相关的节点和连线
- 可选关联:节点可以关联步骤配置,删除步骤配置时节点 stepid 设为 NULL
- 完整性约束:确保数据的一致性和完整性
-
迁移应用状态:
- 已成功应用:迁移已应用到数据库
- 表已创建:三个表都已成功创建在数据库中
- 索引已建立:所有必要的索引都已创建完成
-
技术特性:
- PostgreSQL 兼容:使用 PostgreSQL 特定的数据类型和语法
- 性能优化:为常用查询字段创建索引
- 数据完整性:通过外键约束确保数据一致性
- 审计支持:包含完整的审计字段支持
修改时间:
2025-01-21
修改原因:
用户反映 TestCaseFlows、TestCaseNodes、TestCaseEdges 新建后还没有迁移数据库,需要创建相应的数据库迁移文件并应用到数据库中,以支持测试用例流程管理功能。
2025-01-21 - 为 TestCaseFlow 添加 GetFormTypeStepTypeMapping 功能
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQuery.cs- 新增查询类X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingResponse.cs- 新增响应类X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQueryHandler.cs- 新增查询处理器X1.Domain/Common/TestFlowTypeConverter.cs- 新增 TestFlowType 转换器X1.Domain/Entities/TestCase/TestFlowType.cs- 为枚举添加 DisplayAttribute 和 DescriptionAttributeX1.Presentation/Controllers/TestCaseFlowController.cs- 添加新的 API 端点
修改内容:
-
查询功能实现:
- 创建了
GetFormTypeStepTypeMappingQuery查询类 - 创建了
GetFormTypeStepTypeMappingResponse响应类,只包含测试流程类型 - 创建了
GetFormTypeStepTypeMappingQueryHandler查询处理器
- 创建了
-
TestFlowType 转换器:
- 创建了
TestFlowTypeConverter静态类,提供 TestFlowType 的枚举值获取功能 - 包含获取测试流程类型列表的方法
- 提供测试流程类型的显示名称和描述
- 创建了
-
枚举属性增强:
- 为
TestFlowType枚举添加了DisplayAttribute和DescriptionAttribute - 提供了中文显示名称和详细描述
- 为
-
API 端点添加:
- 在
TestCaseFlowController中添加了GET /api/testcaseflow/form-type-step端点 - 返回测试流程类型列表数据
- 在
-
功能特性:
- 简化响应:只返回测试流程类型,不包含其他映射关系
- 枚举支持:完整的 TestFlowType 枚举支持
- 显示名称:提供中文显示名称和描述
API 端点示例:
GET /api/testcaseflow/form-type-step
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": {
"testFlowTypes": [
{
"value": 1,
"name": "注册测试",
"description": "设备注册到网络的测试流程"
},
{
"value": 2,
"name": "语音测试",
"description": "语音通话相关的测试流程"
},
{
"value": 3,
"name": "数据测试",
"description": "数据传输性能相关的测试流程"
}
]
},
"errorMessages": null
}
修改时间:
2025-01-21
修改原因:
用户要求为 Features.TestCaseFlow 添加一个类似 GetFormTypeStepTypeMapping 的功能,但只需要获取测试流程类型,不需要其他映射关系,用于前端界面中测试流程类型的配置和选择。
2025-01-19 - 根据 TestCaseFlowController 修复 testcaseService.ts
修改文件:
X1.WebUI/src/services/testcaseService.ts - 根据后端 TestCaseFlowController 重新编写前端服务
修改内容:
-
完全重构服务:
- 服务类名:从
TestCaseService改为TestCaseFlowService - 基础URL:从
/api/testcases改为/api/testcaseflow - API端点:完全匹配后端 TestCaseFlowController 的接口
- 服务类名:从
-
数据类型重新定义:
- TestFlowType:测试流程类型枚举
- TestCaseFlow:测试用例流程基础信息
- TestCaseNode:测试用例节点(支持 ReactFlow)
- TestCaseEdge:测试用例连线(支持 ReactFlow)
- TestCaseFlowDetail:测试用例流程详情(包含节点和连线)
-
API 方法对应:
- getTestCaseFlows:对应
GET /api/testcaseflow- 获取流程列表 - getTestCaseFlowById:对应
GET /api/testcaseflow/{id}- 获取流程详情 - createTestCaseFlow:对应
POST /api/testcaseflow- 创建流程 - deleteTestCaseFlow:对应
DELETE /api/testcaseflow/{id}- 删除流程
- getTestCaseFlows:对应
-
请求参数匹配:
- GetTestCaseFlowsRequest:支持 searchTerm、type、isEnabled、pageNumber、pageSize
- CreateTestCaseFlowRequest:支持 name、description、type、isEnabled、viewport、nodes、edges
- CreateNodeData:节点创建数据结构
- CreateEdgeData:连线创建数据结构
-
响应数据结构:
- GetTestCaseFlowsResponse:包含分页信息的流程列表
- GetTestCaseFlowByIdResponse:包含完整节点和连线数据的流程详情
- CreateTestCaseFlowResponse:创建成功后的流程信息
-
ReactFlow 兼容性:
- 节点结构:完全支持 ReactFlow 的节点数据结构
- 连线结构:完全支持 ReactFlow 的连线数据结构
- 位置信息:支持 position、positionAbsolute 等位置字段
- 样式信息:支持 style、data 等样式和数据字段
-
向后兼容性:
- 保留导出:保持
testcaseService导出,确保现有代码不破坏 - 新增导出:新增
testcaseFlowService导出,提供更明确的命名
- 保留导出:保持
修改时间:
2025-01-19
修改原因:
用户要求根据 TestCaseFlowController 修复 testcaseService.ts,确保前端服务与后端 API 完全匹配,支持测试用例流程的完整 CRUD 操作和 ReactFlow 集成。
2025-01-19 - 更新 API 路径常量以匹配后端控制器
修改文件:
X1.WebUI/src/constants/api.ts- 更新 API 路径常量X1.WebUI/src/services/testcaseService.ts- 使用 API 路径常量
修改内容:
-
API 路径常量更新:
- 移除:
TEST_CASES: '/test-cases'和TEST_STEPS: '/test-steps'(后端无对应控制器) - 新增:
TEST_CASE_FLOW: '/api/testcaseflow'(对应 TestCaseFlowController) - 更新:
CASE_STEP_CONFIGS: '/api/casestepconfigs'(对应 CaseStepConfigController)
- 移除:
-
testcaseService.ts 优化:
- 导入常量:添加
API_PATHS导入 - 使用常量:将硬编码的
/api/testcaseflow替换为API_PATHS.TEST_CASE_FLOW - 统一管理:所有 API 路径都通过常量统一管理
- 导入常量:添加
-
路径对应关系:
- TestCaseFlowController:
/api/testcaseflow→API_PATHS.TEST_CASE_FLOW - CaseStepConfigController:
/api/casestepconfigs→API_PATHS.CASE_STEP_CONFIGS
- TestCaseFlowController:
-
技术特性:
- 类型安全:使用 TypeScript 常量确保路径一致性
- 维护性:集中管理 API 路径,便于维护和修改
- 一致性:确保前端服务与后端控制器路径完全匹配
修改时间:
2025-01-19
修改原因:
用户询问是否需要根据 testcaseService 修复其他相关服务,发现 API 路径常量与实际后端控制器不匹配,需要统一更新以确保前后端一致性。
2025-01-19 - 修复 pages/testcases 页面使用 testcaseService
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 修复列表页面使用真实 APIX1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复视图页面使用真实 APIX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx- 添加保存状态支持
修改内容:
-
TestCasesListView.tsx 重构:
- 移除模拟数据:删除
mockTestCases和本地接口定义 - 集成真实 API:使用
testcaseService.getTestCaseFlows()获取数据 - 添加加载状态:显示加载中状态,提升用户体验
- 实现搜索功能:支持按名称搜索测试用例流程
- 实现删除功能:使用
testcaseService.deleteTestCaseFlow()删除流程 - 更新数据展示:适配
TestCaseFlow接口的数据结构 - 修复状态显示:使用
isEnabled字段显示启用/停用状态 - 添加类型标签:显示测试流程类型(功能测试、性能测试等)
- 移除模拟数据:删除
-
TestCasesView.tsx 重构:
- 集成保存功能:使用
testcaseService.createTestCaseFlow()保存流程 - 数据格式转换:将 ReactFlow 节点和连线数据转换为后端格式
- 添加保存状态:显示保存中状态,防止重复提交
- 错误处理:完整的错误处理和用户提示
- 流程验证:检查流程完整性(开始节点、结束节点等)
- 集成保存功能:使用
-
ReactFlowDesigner.tsx 增强:
- 添加 saving 属性:支持保存状态传递
- 保存按钮状态:保存时禁用按钮并显示"保存中..."
- 用户体验优化:防止保存过程中的重复操作
-
数据结构适配:
- 节点数据转换:ReactFlow 节点 → 后端 CreateNodeData 格式
- 连线数据转换:ReactFlow 连线 → 后端 CreateEdgeData 格式
- 类型安全:使用 TypeScript 接口确保类型安全
-
功能特性:
- 实时搜索:支持按回车键搜索
- 批量操作:支持删除操作
- 导航功能:支持查看和编辑页面跳转
- 状态管理:完整的加载和保存状态管理
修改时间:
2025-01-19
修改原因:
用户发现 pages/testcases 页面没有调用 testcaseService,需要修复这些页面使其使用真实的 API 而不是模拟数据,确保前后端数据一致性。
2025-01-19 - TestCaseFlowController 添加 CreateTestCaseFlow 命令
修改文件:
X1.Presentation/Controllers/TestCaseFlowController.cs - 为TestCaseFlowController添加创建测试用例流程的POST方法
修改内容:
-
新增POST方法:
- 方法名:
CreateTestCaseFlow - 路由:
[HttpPost]- 对应/api/testcaseflow - 参数:
[FromBody] CreateTestCaseFlowCommand command - 返回:
IActionResult
- 方法名:
-
功能特性:
- 命令处理:使用
mediator.Send(command)发送创建命令 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 响应格式:使用
CreatedAtAction返回201状态码和资源位置
- 命令处理:使用
-
日志记录:
- 开始日志:记录流程名称和类型
- 成功日志:记录创建的ID、名称、节点数量、连线数量
- 失败日志:记录流程名称和详细错误信息
-
响应处理:
- 成功响应:返回201 Created状态码,包含新创建资源的URI
- 失败响应:返回400 Bad Request状态码,包含错误详情
- 资源定位:使用
CreatedAtAction提供新创建资源的访问路径
-
技术特性:
- 依赖注入:添加了
CreateTestCaseFlow命名空间的using语句 - MediatR集成:使用mediator发送命令,遵循CQRS模式
- RESTful设计:遵循REST API设计规范
- 统一响应:使用统一的OperationResult响应格式
- 依赖注入:添加了
-
API端点:
POST /api/testcaseflow Content-Type: application/json { "name": "测试流程名称", "description": "流程描述", "type": 1, "isEnabled": true, "viewportX": 40.5, "viewportY": 21.2, "viewportZoom": 1.1, "nodes": [...], "edges": [...] }
修改时间:
2025-01-19
修改原因:
用户要求为TestCaseFlowController添加TestCaseFlow.Commands功能,特别是创建测试用例流程的POST方法,以支持前端界面创建新的测试用例流程。
2025-01-19 - TestCaseNodeDto 和 TestCaseEdgeDto 完善
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.cs - 完善 TestCaseNodeDto 和 TestCaseEdgeDto 结构
修改内容:
-
新增 DTO 类:
- TestCaseNodeDataDto:节点数据DTO,包含步骤ID、步骤名称、步骤类型、描述、图标等
- TestCaseNodePositionDto:节点位置DTO,包含X、Y坐标
- TestCaseEdgeStyleDto:连线样式DTO,包含描边颜色、描边宽度
- TestCaseEdgeDataDto:连线数据DTO,包含条件信息
-
TestCaseNodeDto 增强:
- 新增字段:
Type:节点类型(如 "testStep")Position:节点位置(TestCaseNodePositionDto)Data:节点数据(TestCaseNodeDataDto)Selected:是否被选中PositionAbsolute:绝对位置Dragging:是否正在拖拽
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
TestCaseEdgeDto 增强:
- 新增字段:
Source:源节点IDSourceHandle:源连接点Target:目标节点IDTargetHandle:目标连接点Type:连线类型Animated:是否动画Style:连线样式(TestCaseEdgeStyleDto)Data:连线数据(TestCaseEdgeDataDto)
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
技术特性:
- ReactFlow 兼容:新增字段与 ReactFlow 数据结构完全兼容
- JSON 序列化:支持直接序列化为 ReactFlow 所需的 JSON 格式
- 向后兼容:保留原有字段,确保现有代码不受影响
- 类型安全:使用强类型 DTO,避免运行时错误
-
数据结构示例:
{ "nodes": [ { "id": "node-1755654432101", "type": "testStep", "position": { "x": 360, "y": 30 }, "data": { "stepId": "e2192f5a-1582-47e9-92be-c676679418da", "stepName": "StartStep", "stepType": 1, "stepTypeName": "Start", "description": "Mapping_Start", "icon": "play-circle" }, "width": 95, "height": 30, "selected": false, "positionAbsolute": { "x": 360, "y": 30 }, "dragging": false } ], "edges": [ { "source": "node-1755654432101", "sourceHandle": "bottom", "target": "node-1755654436065", "targetHandle": "top", "id": "edge-1755654470705", "type": "smoothstep", "animated": false, "style": { "stroke": "#3b82f6", "strokeWidth": 2 }, "data": { "condition": "default" } } ] } -
设计原则:
- 前端兼容:确保与 ReactFlow 前端组件完全兼容
- 数据完整性:支持完整的节点和连线信息
- 扩展性:支持未来功能扩展
- 性能优化:避免不必要的数据转换
修改时间:
2025-01-19
修改原因:
用户需要 TestCaseNodeDto 和 TestCaseEdgeDto 与 ReactFlow 前端组件完全兼容,支持完整的节点和连线数据结构,包括位置、样式、数据等字段,确保前端能够正确显示和操作测试用例流程。
2025-01-19 - TestCaseFlow Queries功能实现
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/- 获取TestCaseFlow列表查询X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/- 根据ID获取TestCaseFlow详情查询X1.Presentation/Controllers/TestCaseFlowController.cs- TestCaseFlow控制器
修改内容:
-
GetTestCaseFlows查询功能:
- 查询类:
GetTestCaseFlowsQuery- 支持搜索、类型过滤、启用状态过滤、分页 - 响应类:
GetTestCaseFlowsResponse- 包含分页信息和TestCaseFlow列表 - 处理器:
GetTestCaseFlowsQueryHandler- 调用仓储获取分页数据并映射为DTO
- 查询类:
-
GetTestCaseFlowById查询功能:
- 查询类:
GetTestCaseFlowByIdQuery- 根据testCaseId获取详情 - 响应类:
GetTestCaseFlowByIdResponse- 包含完整的流程信息、节点和连线数据 - 处理器:
GetTestCaseFlowByIdQueryHandler- 组装TestCaseFlow、TestCaseNode、TestCaseEdge数据
- 查询类:
-
TestCaseFlowController控制器:
- 列表接口:
GET /api/testcaseflow- 获取测试用例流程列表,支持搜索和分页 - 详情接口:
GET /api/testcaseflow/{id}- 获取测试用例流程详情,包含节点和连线 - 完整日志:详细的日志记录和错误处理
- 列表接口:
-
数据组装特性:
- 列表查询:直接返回TestCaseFlow数据,不包含节点和连线
- 详情查询:组装完整的流程信息,包括所有节点和连线数据
- 性能优化:使用仓储的
GetTestCaseFlowWithDetailsAsync方法一次性获取所有数据
-
技术特性:
- CQRS模式:查询和命令分离,使用MediatR进行消息传递
- 分页支持:支持搜索、过滤、分页功能
- 数据完整性:详情查询包含完整的流程结构信息
- 错误处理:完整的异常处理和日志记录
修改时间:
2025-01-19
修改原因:
用户需要为TestCaseFlow实现Queries功能,包括获取列表和根据testCaseId获取详情,详情查询需要组装TestCaseNode和TestCaseEdge数据,为前端界面查看详情提供完整的数据支持。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复空引用警告X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 优化连线样式类型
修改内容:
-
空引用警告修复:
- 问题:在
CreateTestCaseFlowCommandHandler中,ValidateUserAuthentication方法返回的OperationResult<string>可能为空 - 解决方案:使用空合并操作符
??确保传递给CreateFailure方法的参数不为空 - 修改代码:
return OperationResult<CreateTestCaseFlowResponse>.CreateFailure(errorMessages ?? new List<string>());
- 问题:在
-
连线样式类型优化:
- 问题:
Style属性被定义为object?类型,但在CreateEdgesAsync方法中使用edgeData.Style?.ToString()来转换 - 解决方案:将
Style属性明确为string?类型,符合 JSON 字符串格式的实际用途 - 修改代码:
- 问题:
-
连线类型必填验证:
- 在
EdgeData类中将Type属性设为必填:[Required(ErrorMessage = "连线类型不能为空")] - 在验证逻辑中添加对
Type的验证:if (string.IsNullOrWhiteSpace(edge.Type)) - 在创建连线时移除默认值逻辑:
edgeType: edgeData.Type(不再使用?? "straight")
- 在
-
验证器提取:
- 创建了
CreateTestCaseFlowCommandValidator验证器类 - 将
ValidateRequest方法从CreateTestCaseFlowCommandHandler中提取出来 - 使用静态方法
Validate进行验证,便于复用和测试 - 保持原有的验证逻辑和错误消息不变
- 创建了
-
设计原则:
- 单一职责:验证器专注于参数验证,处理器专注于业务逻辑
- 可复用性:验证器可以在其他地方复用
- 可测试性:独立的验证器更容易进行单元测试
- 代码组织:更好的代码结构和职责分离
修改时间:
2025-01-19
修改原因:
用户要求确保连线类型是必填项,界面不能传空值。同时要求将验证逻辑提取到单独的验证器类中,提高代码的可维护性和可测试性。
2025-01-19 - TestCaseFlowController 删除功能添加和接口返回类型统一
修改文件:
X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommand.cs- 新增删除命令X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommandHandler.cs- 新增删除命令处理器X1.Presentation/Controllers/TestCaseFlowController.cs- 添加删除端点和统一返回类型
修改内容:
-
删除命令实现:
- 创建了
DeleteTestCaseFlowCommand类,包含流程ID参数 - 创建了
DeleteTestCaseFlowCommandHandler处理器,实现删除逻辑 - 包含用户认证验证、流程存在性检查、删除操作和事务提交
- 创建了
-
控制器接口统一:
- 参考
TerminalServicesController的返回方式,将所有方法返回类型改为OperationResult<T> - 移除了
IActionResult和BadRequest/Ok等 HTTP 状态码处理 - 直接返回
OperationResult<T>对象,让框架自动处理 HTTP 状态码
- 参考
-
删除端点添加:
- 添加了
DELETE /api/testcaseflow/{id}端点 - 包含完整的日志记录和错误处理
- 返回删除操作的结果
- 添加了
-
技术特性:
- 一致性:与
TerminalServicesController保持相同的接口返回模式 - 简化:移除了手动的 HTTP 状态码处理,让框架自动处理
- 完整性:删除功能包含完整的业务逻辑验证和错误处理
- 一致性:与
API 端点示例:
DELETE /api/testcaseflow/{id}
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": true,
"errorMessages": null
}
修改时间:
2025-01-19
修改原因:
用户要求添加删除功能到 TestCaseFlowController,并参考 TerminalServicesController 的接口返回方式,统一使用 OperationResult<T> 返回类型,不使用 IActionResult。
2025-01-19 - TestCaseFlow Application层实现
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 创建测试用例流程命令X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowResponse.cs- 创建测试用例流程响应X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 创建测试用例流程命令处理器
修改内容:
-
CreateTestCaseFlowCommand 命令类:
- 实现了
IRequest<OperationResult<CreateTestCaseFlowResponse>>接口 - 包含完整的验证特性:
[Required]、[MaxLength]等 - 支持所有必要字段:名称、描述、类型、启用状态、视口坐标等
- 提供合理的默认值,如视口坐标和启用状态
- 实现了
-
CreateTestCaseFlowResponse 响应类:
- 包含完整的流程信息返回
- 支持类型枚举转换为字符串显示
- 包含审计信息:创建时间、创建人等
-
CreateTestCaseFlowCommandHandler 命令处理器:
- 遵循CQRS模式,使用MediatR框架
- 完整的参数验证和业务逻辑验证
- 用户认证验证
- 名称重复性检查
- 使用领域实体的Create工厂方法
- 完整的错误处理和日志记录
- 事务管理(通过UnitOfWork)
-
设计原则:
- 参考TerminalServices模式:完全按照TerminalServices的设计规则实现
- CQRS架构:命令和查询分离
- 领域驱动设计:使用领域实体的工厂方法
- 依赖注入:通过构造函数注入依赖
- 日志记录:完整的操作日志和错误日志
- 错误处理:统一的错误处理和响应格式
-
技术特性:
- 支持异步操作
- 完整的取消令牌支持
- 统一的OperationResult响应格式
- 详细的验证错误信息
- 事务性操作保证
修改时间:
2025-01-19
修改原因:
用户要求在X1.Application.Features中实现TestCaseFlow的功能,参考TerminalServices的设计规则,先完成创建功能。为测试用例流程管理提供完整的Application层支持,包括命令、响应和处理器实现。
修复记录:
- 编译错误修复:修复了
TestCaseFlow.Create方法调用时的命名空间解析问题,使用完全限定的类型名称X1.Domain.Entities.TestCase.TestCaseFlow.Create来解决编译器无法找到Create方法的问题。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
- 命令扩展:在
CreateTestCaseFlowCommand中添加了NodeData和EdgeDataDTO类,支持节点和连线数据的传输 - 处理器增强:在
CreateTestCaseFlowCommandHandler中添加了ITestCaseNodeRepository和ITestCaseEdgeRepository依赖注入 - 节点创建:实现了
CreateNodesAsync方法,支持批量创建测试用例节点,包括位置、尺寸、状态等属性 - 连线创建:实现了
CreateEdgesAsync方法,支持批量创建测试用例连线,包括源节点、目标节点、类型、样式等属性 - 验证增强:添加了对节点和连线数据的验证逻辑,确保数据完整性
修改记录
2025-01-21 - 创建 TestCaseFlow 相关表的数据库迁移
修改文件:
X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.cs- 新建迁移文件X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.Designer.cs- 迁移设计器文件X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs- 更新模型快照
修改内容:
-
迁移文件创建:
- 迁移名称:
AddTestCaseFlowTables - 迁移时间:2025-08-21 08:06:04
- 迁移描述:为 TestCaseFlow、TestCaseNode、TestCaseEdge 实体创建数据库表
- 迁移名称:
-
创建的表结构:
-
tb_testcaseflow:测试用例流程主表
- 包含 id、name、description、type、isenabled、viewport_x、viewport_y、viewport_zoom 等字段
- 包含审计字段:createdat、updatedat、createdby、updatedby
- 创建索引:name、type、isenabled
-
tb_testcasenode:测试用例节点表
- 包含 id、testcaseid、nodeid、sequencenumber、stepid、positionx、positiony、width、height 等字段
- 包含状态字段:isselected、positionabsolutex、positionabsolutey、isdragging
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 外键关系:stepid → tb_casestepconfig.id (SET NULL)
- 创建索引:testcaseid、nodeid、sequencenumber、stepid
-
tb_testcaseedge:测试用例连线表
- 包含 id、testcaseid、edgeid、sourcenodeid、targetnodeid、edgetype、condition、isanimated、style 等字段
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 创建索引:testcaseid、edgeid、sourcenodeid、targetnodeid
-
-
数据库关系:
- 级联删除:删除测试用例流程时,自动删除相关的节点和连线
- 可选关联:节点可以关联步骤配置,删除步骤配置时节点 stepid 设为 NULL
- 完整性约束:确保数据的一致性和完整性
-
迁移应用状态:
- 已成功应用:迁移已应用到数据库
- 表已创建:三个表都已成功创建在数据库中
- 索引已建立:所有必要的索引都已创建完成
-
技术特性:
- PostgreSQL 兼容:使用 PostgreSQL 特定的数据类型和语法
- 性能优化:为常用查询字段创建索引
- 数据完整性:通过外键约束确保数据一致性
- 审计支持:包含完整的审计字段支持
修改时间:
2025-01-21
修改原因:
用户反映 TestCaseFlows、TestCaseNodes、TestCaseEdges 新建后还没有迁移数据库,需要创建相应的数据库迁移文件并应用到数据库中,以支持测试用例流程管理功能。
2025-01-19 - 根据 TestCaseFlowController 修复 testcaseService.ts
修改文件:
X1.WebUI/src/services/testcaseService.ts - 根据后端 TestCaseFlowController 重新编写前端服务
修改内容:
-
完全重构服务:
- 服务类名:从
TestCaseService改为TestCaseFlowService - 基础URL:从
/api/testcases改为/api/testcaseflow - API端点:完全匹配后端 TestCaseFlowController 的接口
- 服务类名:从
-
数据类型重新定义:
- TestFlowType:测试流程类型枚举
- TestCaseFlow:测试用例流程基础信息
- TestCaseNode:测试用例节点(支持 ReactFlow)
- TestCaseEdge:测试用例连线(支持 ReactFlow)
- TestCaseFlowDetail:测试用例流程详情(包含节点和连线)
-
API 方法对应:
- getTestCaseFlows:对应
GET /api/testcaseflow- 获取流程列表 - getTestCaseFlowById:对应
GET /api/testcaseflow/{id}- 获取流程详情 - createTestCaseFlow:对应
POST /api/testcaseflow- 创建流程 - deleteTestCaseFlow:对应
DELETE /api/testcaseflow/{id}- 删除流程
- getTestCaseFlows:对应
-
请求参数匹配:
- GetTestCaseFlowsRequest:支持 searchTerm、type、isEnabled、pageNumber、pageSize
- CreateTestCaseFlowRequest:支持 name、description、type、isEnabled、viewport、nodes、edges
- CreateNodeData:节点创建数据结构
- CreateEdgeData:连线创建数据结构
-
响应数据结构:
- GetTestCaseFlowsResponse:包含分页信息的流程列表
- GetTestCaseFlowByIdResponse:包含完整节点和连线数据的流程详情
- CreateTestCaseFlowResponse:创建成功后的流程信息
-
ReactFlow 兼容性:
- 节点结构:完全支持 ReactFlow 的节点数据结构
- 连线结构:完全支持 ReactFlow 的连线数据结构
- 位置信息:支持 position、positionAbsolute 等位置字段
- 样式信息:支持 style、data 等样式和数据字段
-
向后兼容性:
- 保留导出:保持
testcaseService导出,确保现有代码不破坏 - 新增导出:新增
testcaseFlowService导出,提供更明确的命名
- 保留导出:保持
修改时间:
2025-01-19
修改原因:
用户要求根据 TestCaseFlowController 修复 testcaseService.ts,确保前端服务与后端 API 完全匹配,支持测试用例流程的完整 CRUD 操作和 ReactFlow 集成。
2025-01-19 - 更新 API 路径常量以匹配后端控制器
修改文件:
X1.WebUI/src/constants/api.ts- 更新 API 路径常量X1.WebUI/src/services/testcaseService.ts- 使用 API 路径常量
修改内容:
-
API 路径常量更新:
- 移除:
TEST_CASES: '/test-cases'和TEST_STEPS: '/test-steps'(后端无对应控制器) - 新增:
TEST_CASE_FLOW: '/api/testcaseflow'(对应 TestCaseFlowController) - 更新:
CASE_STEP_CONFIGS: '/api/casestepconfigs'(对应 CaseStepConfigController)
- 移除:
-
testcaseService.ts 优化:
- 导入常量:添加
API_PATHS导入 - 使用常量:将硬编码的
/api/testcaseflow替换为API_PATHS.TEST_CASE_FLOW - 统一管理:所有 API 路径都通过常量统一管理
- 导入常量:添加
-
路径对应关系:
- TestCaseFlowController:
/api/testcaseflow→API_PATHS.TEST_CASE_FLOW - CaseStepConfigController:
/api/casestepconfigs→API_PATHS.CASE_STEP_CONFIGS
- TestCaseFlowController:
-
技术特性:
- 类型安全:使用 TypeScript 常量确保路径一致性
- 维护性:集中管理 API 路径,便于维护和修改
- 一致性:确保前端服务与后端控制器路径完全匹配
修改时间:
2025-01-19
修改原因:
用户询问是否需要根据 testcaseService 修复其他相关服务,发现 API 路径常量与实际后端控制器不匹配,需要统一更新以确保前后端一致性。
2025-01-19 - 修复 pages/testcases 页面使用 testcaseService
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 修复列表页面使用真实 APIX1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复视图页面使用真实 APIX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx- 添加保存状态支持
修改内容:
-
TestCasesListView.tsx 重构:
- 移除模拟数据:删除
mockTestCases和本地接口定义 - 集成真实 API:使用
testcaseService.getTestCaseFlows()获取数据 - 添加加载状态:显示加载中状态,提升用户体验
- 实现搜索功能:支持按名称搜索测试用例流程
- 实现删除功能:使用
testcaseService.deleteTestCaseFlow()删除流程 - 更新数据展示:适配
TestCaseFlow接口的数据结构 - 修复状态显示:使用
isEnabled字段显示启用/停用状态 - 添加类型标签:显示测试流程类型(功能测试、性能测试等)
- 移除模拟数据:删除
-
TestCasesView.tsx 重构:
- 集成保存功能:使用
testcaseService.createTestCaseFlow()保存流程 - 数据格式转换:将 ReactFlow 节点和连线数据转换为后端格式
- 添加保存状态:显示保存中状态,防止重复提交
- 错误处理:完整的错误处理和用户提示
- 流程验证:检查流程完整性(开始节点、结束节点等)
- 集成保存功能:使用
-
ReactFlowDesigner.tsx 增强:
- 添加 saving 属性:支持保存状态传递
- 保存按钮状态:保存时禁用按钮并显示"保存中..."
- 用户体验优化:防止保存过程中的重复操作
-
数据结构适配:
- 节点数据转换:ReactFlow 节点 → 后端 CreateNodeData 格式
- 连线数据转换:ReactFlow 连线 → 后端 CreateEdgeData 格式
- 类型安全:使用 TypeScript 接口确保类型安全
-
功能特性:
- 实时搜索:支持按回车键搜索
- 批量操作:支持删除操作
- 导航功能:支持查看和编辑页面跳转
- 状态管理:完整的加载和保存状态管理
修改时间:
2025-01-19
修改原因:
用户发现 pages/testcases 页面没有调用 testcaseService,需要修复这些页面使其使用真实的 API 而不是模拟数据,确保前后端数据一致性。
2025-01-19 - TestCaseFlowController 添加 CreateTestCaseFlow 命令
修改文件:
X1.Presentation/Controllers/TestCaseFlowController.cs - 为TestCaseFlowController添加创建测试用例流程的POST方法
修改内容:
-
新增POST方法:
- 方法名:
CreateTestCaseFlow - 路由:
[HttpPost]- 对应/api/testcaseflow - 参数:
[FromBody] CreateTestCaseFlowCommand command - 返回:
IActionResult
- 方法名:
-
功能特性:
- 命令处理:使用
mediator.Send(command)发送创建命令 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 响应格式:使用
CreatedAtAction返回201状态码和资源位置
- 命令处理:使用
-
日志记录:
- 开始日志:记录流程名称和类型
- 成功日志:记录创建的ID、名称、节点数量、连线数量
- 失败日志:记录流程名称和详细错误信息
-
响应处理:
- 成功响应:返回201 Created状态码,包含新创建资源的URI
- 失败响应:返回400 Bad Request状态码,包含错误详情
- 资源定位:使用
CreatedAtAction提供新创建资源的访问路径
-
技术特性:
- 依赖注入:添加了
CreateTestCaseFlow命名空间的using语句 - MediatR集成:使用mediator发送命令,遵循CQRS模式
- RESTful设计:遵循REST API设计规范
- 统一响应:使用统一的OperationResult响应格式
- 依赖注入:添加了
-
API端点:
POST /api/testcaseflow Content-Type: application/json { "name": "测试流程名称", "description": "流程描述", "type": 1, "isEnabled": true, "viewportX": 40.5, "viewportY": 21.2, "viewportZoom": 1.1, "nodes": [...], "edges": [...] }
修改时间:
2025-01-19
修改原因:
用户要求为TestCaseFlowController添加TestCaseFlow.Commands功能,特别是创建测试用例流程的POST方法,以支持前端界面创建新的测试用例流程。
2025-01-19 - TestCaseNodeDto 和 TestCaseEdgeDto 完善
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.cs - 完善 TestCaseNodeDto 和 TestCaseEdgeDto 结构
修改内容:
-
新增 DTO 类:
- TestCaseNodeDataDto:节点数据DTO,包含步骤ID、步骤名称、步骤类型、描述、图标等
- TestCaseNodePositionDto:节点位置DTO,包含X、Y坐标
- TestCaseEdgeStyleDto:连线样式DTO,包含描边颜色、描边宽度
- TestCaseEdgeDataDto:连线数据DTO,包含条件信息
-
TestCaseNodeDto 增强:
- 新增字段:
Type:节点类型(如 "testStep")Position:节点位置(TestCaseNodePositionDto)Data:节点数据(TestCaseNodeDataDto)Selected:是否被选中PositionAbsolute:绝对位置Dragging:是否正在拖拽
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
TestCaseEdgeDto 增强:
- 新增字段:
Source:源节点IDSourceHandle:源连接点Target:目标节点IDTargetHandle:目标连接点Type:连线类型Animated:是否动画Style:连线样式(TestCaseEdgeStyleDto)Data:连线数据(TestCaseEdgeDataDto)
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
技术特性:
- ReactFlow 兼容:新增字段与 ReactFlow 数据结构完全兼容
- JSON 序列化:支持直接序列化为 ReactFlow 所需的 JSON 格式
- 向后兼容:保留原有字段,确保现有代码不受影响
- 类型安全:使用强类型 DTO,避免运行时错误
-
数据结构示例:
{ "nodes": [ { "id": "node-1755654432101", "type": "testStep", "position": { "x": 360, "y": 30 }, "data": { "stepId": "e2192f5a-1582-47e9-92be-c676679418da", "stepName": "StartStep", "stepType": 1, "stepTypeName": "Start", "description": "Mapping_Start", "icon": "play-circle" }, "width": 95, "height": 30, "selected": false, "positionAbsolute": { "x": 360, "y": 30 }, "dragging": false } ], "edges": [ { "source": "node-1755654432101", "sourceHandle": "bottom", "target": "node-1755654436065", "targetHandle": "top", "id": "edge-1755654470705", "type": "smoothstep", "animated": false, "style": { "stroke": "#3b82f6", "strokeWidth": 2 }, "data": { "condition": "default" } } ] } -
设计原则:
- 前端兼容:确保与 ReactFlow 前端组件完全兼容
- 数据完整性:支持完整的节点和连线信息
- 扩展性:支持未来功能扩展
- 性能优化:避免不必要的数据转换
修改时间:
2025-01-19
修改原因:
用户需要 TestCaseNodeDto 和 TestCaseEdgeDto 与 ReactFlow 前端组件完全兼容,支持完整的节点和连线数据结构,包括位置、样式、数据等字段,确保前端能够正确显示和操作测试用例流程。
2025-01-19 - TestCaseFlow Queries功能实现
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/- 获取TestCaseFlow列表查询X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/- 根据ID获取TestCaseFlow详情查询X1.Presentation/Controllers/TestCaseFlowController.cs- TestCaseFlow控制器
修改内容:
-
GetTestCaseFlows查询功能:
- 查询类:
GetTestCaseFlowsQuery- 支持搜索、类型过滤、启用状态过滤、分页 - 响应类:
GetTestCaseFlowsResponse- 包含分页信息和TestCaseFlow列表 - 处理器:
GetTestCaseFlowsQueryHandler- 调用仓储获取分页数据并映射为DTO
- 查询类:
-
GetTestCaseFlowById查询功能:
- 查询类:
GetTestCaseFlowByIdQuery- 根据testCaseId获取详情 - 响应类:
GetTestCaseFlowByIdResponse- 包含完整的流程信息、节点和连线数据 - 处理器:
GetTestCaseFlowByIdQueryHandler- 组装TestCaseFlow、TestCaseNode、TestCaseEdge数据
- 查询类:
-
TestCaseFlowController控制器:
- 列表接口:
GET /api/testcaseflow- 获取测试用例流程列表,支持搜索和分页 - 详情接口:
GET /api/testcaseflow/{id}- 获取测试用例流程详情,包含节点和连线 - 完整日志:详细的日志记录和错误处理
- 列表接口:
-
数据组装特性:
- 列表查询:直接返回TestCaseFlow数据,不包含节点和连线
- 详情查询:组装完整的流程信息,包括所有节点和连线数据
- 性能优化:使用仓储的
GetTestCaseFlowWithDetailsAsync方法一次性获取所有数据
-
技术特性:
- CQRS模式:查询和命令分离,使用MediatR进行消息传递
- 分页支持:支持搜索、过滤、分页功能
- 数据完整性:详情查询包含完整的流程结构信息
- 错误处理:完整的异常处理和日志记录
修改时间:
2025-01-19
修改原因:
用户需要为TestCaseFlow实现Queries功能,包括获取列表和根据testCaseId获取详情,详情查询需要组装TestCaseNode和TestCaseEdge数据,为前端界面查看详情提供完整的数据支持。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复空引用警告X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 优化连线样式类型
修改内容:
-
空引用警告修复:
- 问题:在
CreateTestCaseFlowCommandHandler中,ValidateUserAuthentication方法返回的OperationResult<string>可能为空 - 解决方案:使用空合并操作符
??确保传递给CreateFailure方法的参数不为空 - 修改代码:
return OperationResult<CreateTestCaseFlowResponse>.CreateFailure(errorMessages ?? new List<string>());
- 问题:在
-
连线样式类型优化:
- 问题:
Style属性被定义为object?类型,但在CreateEdgesAsync方法中使用edgeData.Style?.ToString()来转换 - 解决方案:将
Style属性明确为string?类型,符合 JSON 字符串格式的实际用途 - 修改代码:
- 问题:
-
连线类型必填验证:
- 在
EdgeData类中将Type属性设为必填:[Required(ErrorMessage = "连线类型不能为空")] - 在验证逻辑中添加对
Type的验证:if (string.IsNullOrWhiteSpace(edge.Type)) - 在创建连线时移除默认值逻辑:
edgeType: edgeData.Type(不再使用?? "straight")
- 在
-
验证器提取:
- 创建了
CreateTestCaseFlowCommandValidator验证器类 - 将
ValidateRequest方法从CreateTestCaseFlowCommandHandler中提取出来 - 使用静态方法
Validate进行验证,便于复用和测试 - 保持原有的验证逻辑和错误消息不变
- 创建了
-
设计原则:
- 单一职责:验证器专注于参数验证,处理器专注于业务逻辑
- 可复用性:验证器可以在其他地方复用
- 可测试性:独立的验证器更容易进行单元测试
- 代码组织:更好的代码结构和职责分离
修改时间:
2025-01-19
修改原因:
用户要求确保连线类型是必填项,界面不能传空值。同时要求将验证逻辑提取到单独的验证器类中,提高代码的可维护性和可测试性。
2025-01-19 - TestCaseFlowController 删除功能添加和接口返回类型统一
修改文件:
X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommand.cs- 新增删除命令X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommandHandler.cs- 新增删除命令处理器X1.Presentation/Controllers/TestCaseFlowController.cs- 添加删除端点和统一返回类型
修改内容:
-
删除命令实现:
- 创建了
DeleteTestCaseFlowCommand类,包含流程ID参数 - 创建了
DeleteTestCaseFlowCommandHandler处理器,实现删除逻辑 - 包含用户认证验证、流程存在性检查、删除操作和事务提交
- 创建了
-
控制器接口统一:
- 参考
TerminalServicesController的返回方式,将所有方法返回类型改为OperationResult<T> - 移除了
IActionResult和BadRequest/Ok等 HTTP 状态码处理 - 直接返回
OperationResult<T>对象,让框架自动处理 HTTP 状态码
- 参考
-
删除端点添加:
- 添加了
DELETE /api/testcaseflow/{id}端点 - 包含完整的日志记录和错误处理
- 返回删除操作的结果
- 添加了
-
技术特性:
- 一致性:与
TerminalServicesController保持相同的接口返回模式 - 简化:移除了手动的 HTTP 状态码处理,让框架自动处理
- 完整性:删除功能包含完整的业务逻辑验证和错误处理
- 一致性:与
API 端点示例:
DELETE /api/testcaseflow/{id}
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": true,
"errorMessages": null
}
修改时间:
2025-01-19
修改原因:
用户要求添加删除功能到 TestCaseFlowController,并参考 TerminalServicesController 的接口返回方式,统一使用 OperationResult<T> 返回类型,不使用 IActionResult。
2025-01-19 - TestCaseFlow Application层实现
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 创建测试用例流程命令X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowResponse.cs- 创建测试用例流程响应X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 创建测试用例流程命令处理器
修改内容:
-
CreateTestCaseFlowCommand 命令类:
- 实现了
IRequest<OperationResult<CreateTestCaseFlowResponse>>接口 - 包含完整的验证特性:
[Required]、[MaxLength]等 - 支持所有必要字段:名称、描述、类型、启用状态、视口坐标等
- 提供合理的默认值,如视口坐标和启用状态
- 实现了
-
CreateTestCaseFlowResponse 响应类:
- 包含完整的流程信息返回
- 支持类型枚举转换为字符串显示
- 包含审计信息:创建时间、创建人等
-
CreateTestCaseFlowCommandHandler 命令处理器:
- 遵循CQRS模式,使用MediatR框架
- 完整的参数验证和业务逻辑验证
- 用户认证验证
- 名称重复性检查
- 使用领域实体的Create工厂方法
- 完整的错误处理和日志记录
- 事务管理(通过UnitOfWork)
-
设计原则:
- 参考TerminalServices模式:完全按照TerminalServices的设计规则实现
- CQRS架构:命令和查询分离
- 领域驱动设计:使用领域实体的工厂方法
- 依赖注入:通过构造函数注入依赖
- 日志记录:完整的操作日志和错误日志
- 错误处理:统一的错误处理和响应格式
-
技术特性:
- 支持异步操作
- 完整的取消令牌支持
- 统一的OperationResult响应格式
- 详细的验证错误信息
- 事务性操作保证
修改时间:
2025-01-19
修改原因:
用户要求在X1.Application.Features中实现TestCaseFlow的功能,参考TerminalServices的设计规则,先完成创建功能。为测试用例流程管理提供完整的Application层支持,包括命令、响应和处理器实现。
修复记录:
- 编译错误修复:修复了
TestCaseFlow.Create方法调用时的命名空间解析问题,使用完全限定的类型名称X1.Domain.Entities.TestCase.TestCaseFlow.Create来解决编译器无法找到Create方法的问题。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
- 命令扩展:在
CreateTestCaseFlowCommand中添加了NodeData和EdgeDataDTO类,支持节点和连线数据的传输 - 处理器增强:在
CreateTestCaseFlowCommandHandler中添加了ITestCaseNodeRepository和ITestCaseEdgeRepository依赖注入 - 节点创建:实现了
CreateNodesAsync方法,支持批量创建测试用例节点,包括位置、尺寸、状态等属性 - 连线创建:实现了
CreateEdgesAsync方法,支持批量创建测试用例连线,包括源节点、目标节点、类型、样式等属性 - 验证增强:添加了对节点和连线数据的验证逻辑,确保数据完整性
- 日志记录:增强了日志记录,包含节点数量和连线数量的统计信息
- 事务管理:确保节点和连线的创建在同一个事务中完成,保证数据一致性
2024-12-19 - StartDeviceRuntimeCommandHandler 问题分析与修复
问题描述
API响应中 isSuccess: true 但 summary.failureCount: 1,导致前端误判操作成功。
问题分析
通过分析代码发现以下潜在问题区域:
- 网络配置构建阶段:在
BuildNetworkConfigurationRequests方法中,设备可能因为配置验证失败而被过滤掉 - 网络启动阶段:在
StartNetworksInParallelAsync方法中,网络启动失败 - 设备运行时处理阶段:设备运行时不存在或更新失败
实施的修复
-
增强日志记录:
- 在
BuildNetworkConfigurationRequests中添加详细的警告日志 - 在
StartNetworksInParallelAsync中增强错误日志和统计信息 - 在设备运行时处理循环中添加调试日志
- 在
-
关键修复 - isSuccess 字段逻辑:
- 问题根源:
OperationResult<T>.IsSuccess属性仅基于ErrorMessages是否为空 - 解决方案:在
Handle方法中根据业务逻辑判断成功/失败 - 只有当所有设备都成功启动时才返回
CreateSuccess - 否则返回
CreateFailure并包含详细错误信息
- 问题根源:
-
配置验证逻辑优化:
- 问题:原来的配置验证过于严格,要求同时有RAN配置和完整的IMS+核心网配置
- 优化:改为更灵活的验证逻辑,允许只有RAN配置或只有IMS配置的设备通过
- 验证规则:
- 至少需要RAN配置 或者 IMS配置(不要求同时有核心网配置)
- 如果有IMS配置但没有核心网配置,记录警告但不阻止设备启动
- 提供更详细的配置状态日志,便于调试
- 影响:减少因配置不完整而被错误跳过的设备数量
修改原因
- 解决前端误判问题
- 提供更好的调试信息
- 确保数据一致性
2024-12-19 - 已实施更改的评估分析
评估结果:所有更改都应该保留
1. 网络配置构建阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的日志记录,记录被跳过设备的具体原因
- 过滤逻辑透明化,包括重复组合、缺少配置、验证失败等情况
- 统计信息记录,显示原始请求数 vs 有效请求数
- 价值: 提供更好的调试信息和透明度
2. 网络启动阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的错误日志,包含具体错误信息
- 统计信息记录,显示总请求数、成功数、失败数
- 失败设备详细记录
- 价值: 提供关键的调试信息,帮助快速定位网络启动失败原因
3. 设备运行时处理阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 跳过逻辑:只处理网络启动成功的设备
- 详细日志:记录状态更新过程
- 调试信息:记录设备运行时当前状态
- 价值: 确保数据一致性,避免对失败设备的无效处理
4. isSuccess 字段逻辑修复 ✅
- 状态: 已修复,这是核心问题
- 问题:
isSuccess: true但failureCount: 1导致前端误判 - 解决方案: 根据业务逻辑判断成功/失败
- 价值: 解决了前端误判的根本问题
建议的监控指标
修改记录
2025-01-21 - 创建 TestCaseFlow 相关表的数据库迁移
修改文件:
X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.cs- 新建迁移文件X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.Designer.cs- 迁移设计器文件X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs- 更新模型快照
修改内容:
-
迁移文件创建:
- 迁移名称:
AddTestCaseFlowTables - 迁移时间:2025-08-21 08:06:04
- 迁移描述:为 TestCaseFlow、TestCaseNode、TestCaseEdge 实体创建数据库表
- 迁移名称:
-
创建的表结构:
-
tb_testcaseflow:测试用例流程主表
- 包含 id、name、description、type、isenabled、viewport_x、viewport_y、viewport_zoom 等字段
- 包含审计字段:createdat、updatedat、createdby、updatedby
- 创建索引:name、type、isenabled
-
tb_testcasenode:测试用例节点表
- 包含 id、testcaseid、nodeid、sequencenumber、stepid、positionx、positiony、width、height 等字段
- 包含状态字段:isselected、positionabsolutex、positionabsolutey、isdragging
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 外键关系:stepid → tb_casestepconfig.id (SET NULL)
- 创建索引:testcaseid、nodeid、sequencenumber、stepid
-
tb_testcaseedge:测试用例连线表
- 包含 id、testcaseid、edgeid、sourcenodeid、targetnodeid、edgetype、condition、isanimated、style 等字段
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 创建索引:testcaseid、edgeid、sourcenodeid、targetnodeid
-
-
数据库关系:
- 级联删除:删除测试用例流程时,自动删除相关的节点和连线
- 可选关联:节点可以关联步骤配置,删除步骤配置时节点 stepid 设为 NULL
- 完整性约束:确保数据的一致性和完整性
-
迁移应用状态:
- 已成功应用:迁移已应用到数据库
- 表已创建:三个表都已成功创建在数据库中
- 索引已建立:所有必要的索引都已创建完成
-
技术特性:
- PostgreSQL 兼容:使用 PostgreSQL 特定的数据类型和语法
- 性能优化:为常用查询字段创建索引
- 数据完整性:通过外键约束确保数据一致性
- 审计支持:包含完整的审计字段支持
修改时间:
2025-01-21
修改原因:
用户反映 TestCaseFlows、TestCaseNodes、TestCaseEdges 新建后还没有迁移数据库,需要创建相应的数据库迁移文件并应用到数据库中,以支持测试用例流程管理功能。
2025-01-19 - 根据 TestCaseFlowController 修复 testcaseService.ts
修改文件:
X1.WebUI/src/services/testcaseService.ts - 根据后端 TestCaseFlowController 重新编写前端服务
修改内容:
-
完全重构服务:
- 服务类名:从
TestCaseService改为TestCaseFlowService - 基础URL:从
/api/testcases改为/api/testcaseflow - API端点:完全匹配后端 TestCaseFlowController 的接口
- 服务类名:从
-
数据类型重新定义:
- TestFlowType:测试流程类型枚举
- TestCaseFlow:测试用例流程基础信息
- TestCaseNode:测试用例节点(支持 ReactFlow)
- TestCaseEdge:测试用例连线(支持 ReactFlow)
- TestCaseFlowDetail:测试用例流程详情(包含节点和连线)
-
API 方法对应:
- getTestCaseFlows:对应
GET /api/testcaseflow- 获取流程列表 - getTestCaseFlowById:对应
GET /api/testcaseflow/{id}- 获取流程详情 - createTestCaseFlow:对应
POST /api/testcaseflow- 创建流程 - deleteTestCaseFlow:对应
DELETE /api/testcaseflow/{id}- 删除流程
- getTestCaseFlows:对应
-
请求参数匹配:
- GetTestCaseFlowsRequest:支持 searchTerm、type、isEnabled、pageNumber、pageSize
- CreateTestCaseFlowRequest:支持 name、description、type、isEnabled、viewport、nodes、edges
- CreateNodeData:节点创建数据结构
- CreateEdgeData:连线创建数据结构
-
响应数据结构:
- GetTestCaseFlowsResponse:包含分页信息的流程列表
- GetTestCaseFlowByIdResponse:包含完整节点和连线数据的流程详情
- CreateTestCaseFlowResponse:创建成功后的流程信息
-
ReactFlow 兼容性:
- 节点结构:完全支持 ReactFlow 的节点数据结构
- 连线结构:完全支持 ReactFlow 的连线数据结构
- 位置信息:支持 position、positionAbsolute 等位置字段
- 样式信息:支持 style、data 等样式和数据字段
-
向后兼容性:
- 保留导出:保持
testcaseService导出,确保现有代码不破坏 - 新增导出:新增
testcaseFlowService导出,提供更明确的命名
- 保留导出:保持
修改时间:
2025-01-19
修改原因:
用户要求根据 TestCaseFlowController 修复 testcaseService.ts,确保前端服务与后端 API 完全匹配,支持测试用例流程的完整 CRUD 操作和 ReactFlow 集成。
2025-01-19 - 更新 API 路径常量以匹配后端控制器
修改文件:
X1.WebUI/src/constants/api.ts- 更新 API 路径常量X1.WebUI/src/services/testcaseService.ts- 使用 API 路径常量
修改内容:
-
API 路径常量更新:
- 移除:
TEST_CASES: '/test-cases'和TEST_STEPS: '/test-steps'(后端无对应控制器) - 新增:
TEST_CASE_FLOW: '/api/testcaseflow'(对应 TestCaseFlowController) - 更新:
CASE_STEP_CONFIGS: '/api/casestepconfigs'(对应 CaseStepConfigController)
- 移除:
-
testcaseService.ts 优化:
- 导入常量:添加
API_PATHS导入 - 使用常量:将硬编码的
/api/testcaseflow替换为API_PATHS.TEST_CASE_FLOW - 统一管理:所有 API 路径都通过常量统一管理
- 导入常量:添加
-
路径对应关系:
- TestCaseFlowController:
/api/testcaseflow→API_PATHS.TEST_CASE_FLOW - CaseStepConfigController:
/api/casestepconfigs→API_PATHS.CASE_STEP_CONFIGS
- TestCaseFlowController:
-
技术特性:
- 类型安全:使用 TypeScript 常量确保路径一致性
- 维护性:集中管理 API 路径,便于维护和修改
- 一致性:确保前端服务与后端控制器路径完全匹配
修改时间:
2025-01-19
修改原因:
用户询问是否需要根据 testcaseService 修复其他相关服务,发现 API 路径常量与实际后端控制器不匹配,需要统一更新以确保前后端一致性。
2025-01-19 - 修复 pages/testcases 页面使用 testcaseService
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 修复列表页面使用真实 APIX1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复视图页面使用真实 APIX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx- 添加保存状态支持
修改内容:
-
TestCasesListView.tsx 重构:
- 移除模拟数据:删除
mockTestCases和本地接口定义 - 集成真实 API:使用
testcaseService.getTestCaseFlows()获取数据 - 添加加载状态:显示加载中状态,提升用户体验
- 实现搜索功能:支持按名称搜索测试用例流程
- 实现删除功能:使用
testcaseService.deleteTestCaseFlow()删除流程 - 更新数据展示:适配
TestCaseFlow接口的数据结构 - 修复状态显示:使用
isEnabled字段显示启用/停用状态 - 添加类型标签:显示测试流程类型(功能测试、性能测试等)
- 移除模拟数据:删除
-
TestCasesView.tsx 重构:
- 集成保存功能:使用
testcaseService.createTestCaseFlow()保存流程 - 数据格式转换:将 ReactFlow 节点和连线数据转换为后端格式
- 添加保存状态:显示保存中状态,防止重复提交
- 错误处理:完整的错误处理和用户提示
- 流程验证:检查流程完整性(开始节点、结束节点等)
- 集成保存功能:使用
-
ReactFlowDesigner.tsx 增强:
- 添加 saving 属性:支持保存状态传递
- 保存按钮状态:保存时禁用按钮并显示"保存中..."
- 用户体验优化:防止保存过程中的重复操作
-
数据结构适配:
- 节点数据转换:ReactFlow 节点 → 后端 CreateNodeData 格式
- 连线数据转换:ReactFlow 连线 → 后端 CreateEdgeData 格式
- 类型安全:使用 TypeScript 接口确保类型安全
-
功能特性:
- 实时搜索:支持按回车键搜索
- 批量操作:支持删除操作
- 导航功能:支持查看和编辑页面跳转
- 状态管理:完整的加载和保存状态管理
修改时间:
2025-01-19
修改原因:
用户发现 pages/testcases 页面没有调用 testcaseService,需要修复这些页面使其使用真实的 API 而不是模拟数据,确保前后端数据一致性。
2025-01-19 - TestCaseFlowController 添加 CreateTestCaseFlow 命令
修改文件:
X1.Presentation/Controllers/TestCaseFlowController.cs - 为TestCaseFlowController添加创建测试用例流程的POST方法
修改内容:
-
新增POST方法:
- 方法名:
CreateTestCaseFlow - 路由:
[HttpPost]- 对应/api/testcaseflow - 参数:
[FromBody] CreateTestCaseFlowCommand command - 返回:
IActionResult
- 方法名:
-
功能特性:
- 命令处理:使用
mediator.Send(command)发送创建命令 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 响应格式:使用
CreatedAtAction返回201状态码和资源位置
- 命令处理:使用
-
日志记录:
- 开始日志:记录流程名称和类型
- 成功日志:记录创建的ID、名称、节点数量、连线数量
- 失败日志:记录流程名称和详细错误信息
-
响应处理:
- 成功响应:返回201 Created状态码,包含新创建资源的URI
- 失败响应:返回400 Bad Request状态码,包含错误详情
- 资源定位:使用
CreatedAtAction提供新创建资源的访问路径
-
技术特性:
- 依赖注入:添加了
CreateTestCaseFlow命名空间的using语句 - MediatR集成:使用mediator发送命令,遵循CQRS模式
- RESTful设计:遵循REST API设计规范
- 统一响应:使用统一的OperationResult响应格式
- 依赖注入:添加了
-
API端点:
POST /api/testcaseflow Content-Type: application/json { "name": "测试流程名称", "description": "流程描述", "type": 1, "isEnabled": true, "viewportX": 40.5, "viewportY": 21.2, "viewportZoom": 1.1, "nodes": [...], "edges": [...] }
修改时间:
2025-01-19
修改原因:
用户要求为TestCaseFlowController添加TestCaseFlow.Commands功能,特别是创建测试用例流程的POST方法,以支持前端界面创建新的测试用例流程。
2025-01-19 - TestCaseNodeDto 和 TestCaseEdgeDto 完善
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.cs - 完善 TestCaseNodeDto 和 TestCaseEdgeDto 结构
修改内容:
-
新增 DTO 类:
- TestCaseNodeDataDto:节点数据DTO,包含步骤ID、步骤名称、步骤类型、描述、图标等
- TestCaseNodePositionDto:节点位置DTO,包含X、Y坐标
- TestCaseEdgeStyleDto:连线样式DTO,包含描边颜色、描边宽度
- TestCaseEdgeDataDto:连线数据DTO,包含条件信息
-
TestCaseNodeDto 增强:
- 新增字段:
Type:节点类型(如 "testStep")Position:节点位置(TestCaseNodePositionDto)Data:节点数据(TestCaseNodeDataDto)Selected:是否被选中PositionAbsolute:绝对位置Dragging:是否正在拖拽
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
TestCaseEdgeDto 增强:
- 新增字段:
Source:源节点IDSourceHandle:源连接点Target:目标节点IDTargetHandle:目标连接点Type:连线类型Animated:是否动画Style:连线样式(TestCaseEdgeStyleDto)Data:连线数据(TestCaseEdgeDataDto)
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
技术特性:
- ReactFlow 兼容:新增字段与 ReactFlow 数据结构完全兼容
- JSON 序列化:支持直接序列化为 ReactFlow 所需的 JSON 格式
- 向后兼容:保留原有字段,确保现有代码不受影响
- 类型安全:使用强类型 DTO,避免运行时错误
-
数据结构示例:
{ "nodes": [ { "id": "node-1755654432101", "type": "testStep", "position": { "x": 360, "y": 30 }, "data": { "stepId": "e2192f5a-1582-47e9-92be-c676679418da", "stepName": "StartStep", "stepType": 1, "stepTypeName": "Start", "description": "Mapping_Start", "icon": "play-circle" }, "width": 95, "height": 30, "selected": false, "positionAbsolute": { "x": 360, "y": 30 }, "dragging": false } ], "edges": [ { "source": "node-1755654432101", "sourceHandle": "bottom", "target": "node-1755654436065", "targetHandle": "top", "id": "edge-1755654470705", "type": "smoothstep", "animated": false, "style": { "stroke": "#3b82f6", "strokeWidth": 2 }, "data": { "condition": "default" } } ] } -
设计原则:
- 前端兼容:确保与 ReactFlow 前端组件完全兼容
- 数据完整性:支持完整的节点和连线信息
- 扩展性:支持未来功能扩展
- 性能优化:避免不必要的数据转换
修改时间:
2025-01-19
修改原因:
用户需要 TestCaseNodeDto 和 TestCaseEdgeDto 与 ReactFlow 前端组件完全兼容,支持完整的节点和连线数据结构,包括位置、样式、数据等字段,确保前端能够正确显示和操作测试用例流程。
2025-01-19 - TestCaseFlow Queries功能实现
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/- 获取TestCaseFlow列表查询X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/- 根据ID获取TestCaseFlow详情查询X1.Presentation/Controllers/TestCaseFlowController.cs- TestCaseFlow控制器
修改内容:
-
GetTestCaseFlows查询功能:
- 查询类:
GetTestCaseFlowsQuery- 支持搜索、类型过滤、启用状态过滤、分页 - 响应类:
GetTestCaseFlowsResponse- 包含分页信息和TestCaseFlow列表 - 处理器:
GetTestCaseFlowsQueryHandler- 调用仓储获取分页数据并映射为DTO
- 查询类:
-
GetTestCaseFlowById查询功能:
- 查询类:
GetTestCaseFlowByIdQuery- 根据testCaseId获取详情 - 响应类:
GetTestCaseFlowByIdResponse- 包含完整的流程信息、节点和连线数据 - 处理器:
GetTestCaseFlowByIdQueryHandler- 组装TestCaseFlow、TestCaseNode、TestCaseEdge数据
- 查询类:
-
TestCaseFlowController控制器:
- 列表接口:
GET /api/testcaseflow- 获取测试用例流程列表,支持搜索和分页 - 详情接口:
GET /api/testcaseflow/{id}- 获取测试用例流程详情,包含节点和连线 - 完整日志:详细的日志记录和错误处理
- 列表接口:
-
数据组装特性:
- 列表查询:直接返回TestCaseFlow数据,不包含节点和连线
- 详情查询:组装完整的流程信息,包括所有节点和连线数据
- 性能优化:使用仓储的
GetTestCaseFlowWithDetailsAsync方法一次性获取所有数据
-
技术特性:
- CQRS模式:查询和命令分离,使用MediatR进行消息传递
- 分页支持:支持搜索、过滤、分页功能
- 数据完整性:详情查询包含完整的流程结构信息
- 错误处理:完整的异常处理和日志记录
修改时间:
2025-01-19
修改原因:
用户需要为TestCaseFlow实现Queries功能,包括获取列表和根据testCaseId获取详情,详情查询需要组装TestCaseNode和TestCaseEdge数据,为前端界面查看详情提供完整的数据支持。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复空引用警告X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 优化连线样式类型
修改内容:
-
空引用警告修复:
- 问题:在
CreateTestCaseFlowCommandHandler中,ValidateUserAuthentication方法返回的OperationResult<string>可能为空 - 解决方案:使用空合并操作符
??确保传递给CreateFailure方法的参数不为空 - 修改代码:
return OperationResult<CreateTestCaseFlowResponse>.CreateFailure(errorMessages ?? new List<string>());
- 问题:在
-
连线样式类型优化:
- 问题:
Style属性被定义为object?类型,但在CreateEdgesAsync方法中使用edgeData.Style?.ToString()来转换 - 解决方案:将
Style属性明确为string?类型,符合 JSON 字符串格式的实际用途 - 修改代码:
- 问题:
-
连线类型必填验证:
- 在
EdgeData类中将Type属性设为必填:[Required(ErrorMessage = "连线类型不能为空")] - 在验证逻辑中添加对
Type的验证:if (string.IsNullOrWhiteSpace(edge.Type)) - 在创建连线时移除默认值逻辑:
edgeType: edgeData.Type(不再使用?? "straight")
- 在
-
验证器提取:
- 创建了
CreateTestCaseFlowCommandValidator验证器类 - 将
ValidateRequest方法从CreateTestCaseFlowCommandHandler中提取出来 - 使用静态方法
Validate进行验证,便于复用和测试 - 保持原有的验证逻辑和错误消息不变
- 创建了
-
设计原则:
- 单一职责:验证器专注于参数验证,处理器专注于业务逻辑
- 可复用性:验证器可以在其他地方复用
- 可测试性:独立的验证器更容易进行单元测试
- 代码组织:更好的代码结构和职责分离
修改时间:
2025-01-19
修改原因:
用户要求确保连线类型是必填项,界面不能传空值。同时要求将验证逻辑提取到单独的验证器类中,提高代码的可维护性和可测试性。
2025-01-19 - TestCaseFlowController 删除功能添加和接口返回类型统一
修改文件:
X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommand.cs- 新增删除命令X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommandHandler.cs- 新增删除命令处理器X1.Presentation/Controllers/TestCaseFlowController.cs- 添加删除端点和统一返回类型
修改内容:
-
删除命令实现:
- 创建了
DeleteTestCaseFlowCommand类,包含流程ID参数 - 创建了
DeleteTestCaseFlowCommandHandler处理器,实现删除逻辑 - 包含用户认证验证、流程存在性检查、删除操作和事务提交
- 创建了
-
控制器接口统一:
- 参考
TerminalServicesController的返回方式,将所有方法返回类型改为OperationResult<T> - 移除了
IActionResult和BadRequest/Ok等 HTTP 状态码处理 - 直接返回
OperationResult<T>对象,让框架自动处理 HTTP 状态码
- 参考
-
删除端点添加:
- 添加了
DELETE /api/testcaseflow/{id}端点 - 包含完整的日志记录和错误处理
- 返回删除操作的结果
- 添加了
-
技术特性:
- 一致性:与
TerminalServicesController保持相同的接口返回模式 - 简化:移除了手动的 HTTP 状态码处理,让框架自动处理
- 完整性:删除功能包含完整的业务逻辑验证和错误处理
- 一致性:与
API 端点示例:
DELETE /api/testcaseflow/{id}
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": true,
"errorMessages": null
}
修改时间:
2025-01-19
修改原因:
用户要求添加删除功能到 TestCaseFlowController,并参考 TerminalServicesController 的接口返回方式,统一使用 OperationResult<T> 返回类型,不使用 IActionResult。
2025-01-19 - TestCaseFlow Application层实现
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 创建测试用例流程命令X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowResponse.cs- 创建测试用例流程响应X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 创建测试用例流程命令处理器
修改内容:
-
CreateTestCaseFlowCommand 命令类:
- 实现了
IRequest<OperationResult<CreateTestCaseFlowResponse>>接口 - 包含完整的验证特性:
[Required]、[MaxLength]等 - 支持所有必要字段:名称、描述、类型、启用状态、视口坐标等
- 提供合理的默认值,如视口坐标和启用状态
- 实现了
-
CreateTestCaseFlowResponse 响应类:
- 包含完整的流程信息返回
- 支持类型枚举转换为字符串显示
- 包含审计信息:创建时间、创建人等
-
CreateTestCaseFlowCommandHandler 命令处理器:
- 遵循CQRS模式,使用MediatR框架
- 完整的参数验证和业务逻辑验证
- 用户认证验证
- 名称重复性检查
- 使用领域实体的Create工厂方法
- 完整的错误处理和日志记录
- 事务管理(通过UnitOfWork)
-
设计原则:
- 参考TerminalServices模式:完全按照TerminalServices的设计规则实现
- CQRS架构:命令和查询分离
- 领域驱动设计:使用领域实体的工厂方法
- 依赖注入:通过构造函数注入依赖
- 日志记录:完整的操作日志和错误日志
- 错误处理:统一的错误处理和响应格式
-
技术特性:
- 支持异步操作
- 完整的取消令牌支持
- 统一的OperationResult响应格式
- 详细的验证错误信息
- 事务性操作保证
修改时间:
2025-01-19
修改原因:
用户要求在X1.Application.Features中实现TestCaseFlow的功能,参考TerminalServices的设计规则,先完成创建功能。为测试用例流程管理提供完整的Application层支持,包括命令、响应和处理器实现。
修复记录:
- 编译错误修复:修复了
TestCaseFlow.Create方法调用时的命名空间解析问题,使用完全限定的类型名称X1.Domain.Entities.TestCase.TestCaseFlow.Create来解决编译器无法找到Create方法的问题。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
- 命令扩展:在
CreateTestCaseFlowCommand中添加了NodeData和EdgeDataDTO类,支持节点和连线数据的传输 - 处理器增强:在
CreateTestCaseFlowCommandHandler中添加了ITestCaseNodeRepository和ITestCaseEdgeRepository依赖注入 - 节点创建:实现了
CreateNodesAsync方法,支持批量创建测试用例节点,包括位置、尺寸、状态等属性 - 连线创建:实现了
CreateEdgesAsync方法,支持批量创建测试用例连线,包括源节点、目标节点、类型、样式等属性 - 验证增强:添加了对节点和连线数据的验证逻辑,确保数据完整性
- 日志记录:增强了日志记录,包含节点数量和连线数量的统计信息
- 事务管理:确保节点和连线的创建在同一个事务中完成,保证数据一致性
2024-12-19 - StartDeviceRuntimeCommandHandler 问题分析与修复
问题描述
API响应中 isSuccess: true 但 summary.failureCount: 1,导致前端误判操作成功。
问题分析
通过分析代码发现以下潜在问题区域:
- 网络配置构建阶段:在
BuildNetworkConfigurationRequests方法中,设备可能因为配置验证失败而被过滤掉 - 网络启动阶段:在
StartNetworksInParallelAsync方法中,网络启动失败 - 设备运行时处理阶段:设备运行时不存在或更新失败
实施的修复
-
增强日志记录:
- 在
BuildNetworkConfigurationRequests中添加详细的警告日志 - 在
StartNetworksInParallelAsync中增强错误日志和统计信息 - 在设备运行时处理循环中添加调试日志
- 在
-
关键修复 - isSuccess 字段逻辑:
- 问题根源:
OperationResult<T>.IsSuccess属性仅基于ErrorMessages是否为空 - 解决方案:在
Handle方法中根据业务逻辑判断成功/失败 - 只有当所有设备都成功启动时才返回
CreateSuccess - 否则返回
CreateFailure并包含详细错误信息
- 问题根源:
-
配置验证逻辑优化:
- 问题:原来的配置验证过于严格,要求同时有RAN配置和完整的IMS+核心网配置
- 优化:改为更灵活的验证逻辑,允许只有RAN配置或只有IMS配置的设备通过
- 验证规则:
- 至少需要RAN配置 或者 IMS配置(不要求同时有核心网配置)
- 如果有IMS配置但没有核心网配置,记录警告但不阻止设备启动
- 提供更详细的配置状态日志,便于调试
- 影响:减少因配置不完整而被错误跳过的设备数量
修改原因
- 解决前端误判问题
- 提供更好的调试信息
- 确保数据一致性
2024-12-19 - 已实施更改的评估分析
评估结果:所有更改都应该保留
1. 网络配置构建阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的日志记录,记录被跳过设备的具体原因
- 过滤逻辑透明化,包括重复组合、缺少配置、验证失败等情况
- 统计信息记录,显示原始请求数 vs 有效请求数
- 价值: 提供更好的调试信息和透明度
2. 网络启动阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的错误日志,包含具体错误信息
- 统计信息记录,显示总请求数、成功数、失败数
- 失败设备详细记录
- 价值: 提供关键的调试信息,帮助快速定位网络启动失败原因
3. 设备运行时处理阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 跳过逻辑:只处理网络启动成功的设备
- 详细日志:记录状态更新过程
- 调试信息:记录设备运行时当前状态
- 价值: 确保数据一致性,避免对失败设备的无效处理
4. isSuccess 字段逻辑修复 ✅
- 状态: 已修复,这是核心问题
- 问题:
isSuccess: true但failureCount: 1导致前端误判 - 解决方案: 根据业务逻辑判断成功/失败
- 价值: 解决了前端误判的根本问题
建议的监控指标
- 网络配置构建成功率:
有效请求数 / 原始请求数 - 网络启动成功率:
成功设备数 / 有效请求数 - 整体成功率:
成功设备数 / 总请求数
日志分析建议
修改记录
2025-01-21 - 创建 TestCaseFlow 相关表的数据库迁移
修改文件:
X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.cs- 新建迁移文件X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.Designer.cs- 迁移设计器文件X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs- 更新模型快照
修改内容:
-
迁移文件创建:
- 迁移名称:
AddTestCaseFlowTables - 迁移时间:2025-08-21 08:06:04
- 迁移描述:为 TestCaseFlow、TestCaseNode、TestCaseEdge 实体创建数据库表
- 迁移名称:
-
创建的表结构:
-
tb_testcaseflow:测试用例流程主表
- 包含 id、name、description、type、isenabled、viewport_x、viewport_y、viewport_zoom 等字段
- 包含审计字段:createdat、updatedat、createdby、updatedby
- 创建索引:name、type、isenabled
-
tb_testcasenode:测试用例节点表
- 包含 id、testcaseid、nodeid、sequencenumber、stepid、positionx、positiony、width、height 等字段
- 包含状态字段:isselected、positionabsolutex、positionabsolutey、isdragging
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 外键关系:stepid → tb_casestepconfig.id (SET NULL)
- 创建索引:testcaseid、nodeid、sequencenumber、stepid
-
tb_testcaseedge:测试用例连线表
- 包含 id、testcaseid、edgeid、sourcenodeid、targetnodeid、edgetype、condition、isanimated、style 等字段
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 创建索引:testcaseid、edgeid、sourcenodeid、targetnodeid
-
-
数据库关系:
- 级联删除:删除测试用例流程时,自动删除相关的节点和连线
- 可选关联:节点可以关联步骤配置,删除步骤配置时节点 stepid 设为 NULL
- 完整性约束:确保数据的一致性和完整性
-
迁移应用状态:
- 已成功应用:迁移已应用到数据库
- 表已创建:三个表都已成功创建在数据库中
- 索引已建立:所有必要的索引都已创建完成
-
技术特性:
- PostgreSQL 兼容:使用 PostgreSQL 特定的数据类型和语法
- 性能优化:为常用查询字段创建索引
- 数据完整性:通过外键约束确保数据一致性
- 审计支持:包含完整的审计字段支持
修改时间:
2025-01-21
修改原因:
用户反映 TestCaseFlows、TestCaseNodes、TestCaseEdges 新建后还没有迁移数据库,需要创建相应的数据库迁移文件并应用到数据库中,以支持测试用例流程管理功能。
2025-01-19 - 根据 TestCaseFlowController 修复 testcaseService.ts
修改文件:
X1.WebUI/src/services/testcaseService.ts - 根据后端 TestCaseFlowController 重新编写前端服务
修改内容:
-
完全重构服务:
- 服务类名:从
TestCaseService改为TestCaseFlowService - 基础URL:从
/api/testcases改为/api/testcaseflow - API端点:完全匹配后端 TestCaseFlowController 的接口
- 服务类名:从
-
数据类型重新定义:
- TestFlowType:测试流程类型枚举
- TestCaseFlow:测试用例流程基础信息
- TestCaseNode:测试用例节点(支持 ReactFlow)
- TestCaseEdge:测试用例连线(支持 ReactFlow)
- TestCaseFlowDetail:测试用例流程详情(包含节点和连线)
-
API 方法对应:
- getTestCaseFlows:对应
GET /api/testcaseflow- 获取流程列表 - getTestCaseFlowById:对应
GET /api/testcaseflow/{id}- 获取流程详情 - createTestCaseFlow:对应
POST /api/testcaseflow- 创建流程 - deleteTestCaseFlow:对应
DELETE /api/testcaseflow/{id}- 删除流程
- getTestCaseFlows:对应
-
请求参数匹配:
- GetTestCaseFlowsRequest:支持 searchTerm、type、isEnabled、pageNumber、pageSize
- CreateTestCaseFlowRequest:支持 name、description、type、isEnabled、viewport、nodes、edges
- CreateNodeData:节点创建数据结构
- CreateEdgeData:连线创建数据结构
-
响应数据结构:
- GetTestCaseFlowsResponse:包含分页信息的流程列表
- GetTestCaseFlowByIdResponse:包含完整节点和连线数据的流程详情
- CreateTestCaseFlowResponse:创建成功后的流程信息
-
ReactFlow 兼容性:
- 节点结构:完全支持 ReactFlow 的节点数据结构
- 连线结构:完全支持 ReactFlow 的连线数据结构
- 位置信息:支持 position、positionAbsolute 等位置字段
- 样式信息:支持 style、data 等样式和数据字段
-
向后兼容性:
- 保留导出:保持
testcaseService导出,确保现有代码不破坏 - 新增导出:新增
testcaseFlowService导出,提供更明确的命名
- 保留导出:保持
修改时间:
2025-01-19
修改原因:
用户要求根据 TestCaseFlowController 修复 testcaseService.ts,确保前端服务与后端 API 完全匹配,支持测试用例流程的完整 CRUD 操作和 ReactFlow 集成。
2025-01-19 - 更新 API 路径常量以匹配后端控制器
修改文件:
X1.WebUI/src/constants/api.ts- 更新 API 路径常量X1.WebUI/src/services/testcaseService.ts- 使用 API 路径常量
修改内容:
-
API 路径常量更新:
- 移除:
TEST_CASES: '/test-cases'和TEST_STEPS: '/test-steps'(后端无对应控制器) - 新增:
TEST_CASE_FLOW: '/api/testcaseflow'(对应 TestCaseFlowController) - 更新:
CASE_STEP_CONFIGS: '/api/casestepconfigs'(对应 CaseStepConfigController)
- 移除:
-
testcaseService.ts 优化:
- 导入常量:添加
API_PATHS导入 - 使用常量:将硬编码的
/api/testcaseflow替换为API_PATHS.TEST_CASE_FLOW - 统一管理:所有 API 路径都通过常量统一管理
- 导入常量:添加
-
路径对应关系:
- TestCaseFlowController:
/api/testcaseflow→API_PATHS.TEST_CASE_FLOW - CaseStepConfigController:
/api/casestepconfigs→API_PATHS.CASE_STEP_CONFIGS
- TestCaseFlowController:
-
技术特性:
- 类型安全:使用 TypeScript 常量确保路径一致性
- 维护性:集中管理 API 路径,便于维护和修改
- 一致性:确保前端服务与后端控制器路径完全匹配
修改时间:
2025-01-19
修改原因:
用户询问是否需要根据 testcaseService 修复其他相关服务,发现 API 路径常量与实际后端控制器不匹配,需要统一更新以确保前后端一致性。
2025-01-19 - 修复 pages/testcases 页面使用 testcaseService
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 修复列表页面使用真实 APIX1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复视图页面使用真实 APIX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx- 添加保存状态支持
修改内容:
-
TestCasesListView.tsx 重构:
- 移除模拟数据:删除
mockTestCases和本地接口定义 - 集成真实 API:使用
testcaseService.getTestCaseFlows()获取数据 - 添加加载状态:显示加载中状态,提升用户体验
- 实现搜索功能:支持按名称搜索测试用例流程
- 实现删除功能:使用
testcaseService.deleteTestCaseFlow()删除流程 - 更新数据展示:适配
TestCaseFlow接口的数据结构 - 修复状态显示:使用
isEnabled字段显示启用/停用状态 - 添加类型标签:显示测试流程类型(功能测试、性能测试等)
- 移除模拟数据:删除
-
TestCasesView.tsx 重构:
- 集成保存功能:使用
testcaseService.createTestCaseFlow()保存流程 - 数据格式转换:将 ReactFlow 节点和连线数据转换为后端格式
- 添加保存状态:显示保存中状态,防止重复提交
- 错误处理:完整的错误处理和用户提示
- 流程验证:检查流程完整性(开始节点、结束节点等)
- 集成保存功能:使用
-
ReactFlowDesigner.tsx 增强:
- 添加 saving 属性:支持保存状态传递
- 保存按钮状态:保存时禁用按钮并显示"保存中..."
- 用户体验优化:防止保存过程中的重复操作
-
数据结构适配:
- 节点数据转换:ReactFlow 节点 → 后端 CreateNodeData 格式
- 连线数据转换:ReactFlow 连线 → 后端 CreateEdgeData 格式
- 类型安全:使用 TypeScript 接口确保类型安全
-
功能特性:
- 实时搜索:支持按回车键搜索
- 批量操作:支持删除操作
- 导航功能:支持查看和编辑页面跳转
- 状态管理:完整的加载和保存状态管理
修改时间:
2025-01-19
修改原因:
用户发现 pages/testcases 页面没有调用 testcaseService,需要修复这些页面使其使用真实的 API 而不是模拟数据,确保前后端数据一致性。
2025-01-19 - TestCaseFlowController 添加 CreateTestCaseFlow 命令
修改文件:
X1.Presentation/Controllers/TestCaseFlowController.cs - 为TestCaseFlowController添加创建测试用例流程的POST方法
修改内容:
-
新增POST方法:
- 方法名:
CreateTestCaseFlow - 路由:
[HttpPost]- 对应/api/testcaseflow - 参数:
[FromBody] CreateTestCaseFlowCommand command - 返回:
IActionResult
- 方法名:
-
功能特性:
- 命令处理:使用
mediator.Send(command)发送创建命令 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 响应格式:使用
CreatedAtAction返回201状态码和资源位置
- 命令处理:使用
-
日志记录:
- 开始日志:记录流程名称和类型
- 成功日志:记录创建的ID、名称、节点数量、连线数量
- 失败日志:记录流程名称和详细错误信息
-
响应处理:
- 成功响应:返回201 Created状态码,包含新创建资源的URI
- 失败响应:返回400 Bad Request状态码,包含错误详情
- 资源定位:使用
CreatedAtAction提供新创建资源的访问路径
-
技术特性:
- 依赖注入:添加了
CreateTestCaseFlow命名空间的using语句 - MediatR集成:使用mediator发送命令,遵循CQRS模式
- RESTful设计:遵循REST API设计规范
- 统一响应:使用统一的OperationResult响应格式
- 依赖注入:添加了
-
API端点:
POST /api/testcaseflow Content-Type: application/json { "name": "测试流程名称", "description": "流程描述", "type": 1, "isEnabled": true, "viewportX": 40.5, "viewportY": 21.2, "viewportZoom": 1.1, "nodes": [...], "edges": [...] }
修改时间:
2025-01-19
修改原因:
用户要求为TestCaseFlowController添加TestCaseFlow.Commands功能,特别是创建测试用例流程的POST方法,以支持前端界面创建新的测试用例流程。
2025-01-19 - TestCaseNodeDto 和 TestCaseEdgeDto 完善
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.cs - 完善 TestCaseNodeDto 和 TestCaseEdgeDto 结构
修改内容:
-
新增 DTO 类:
- TestCaseNodeDataDto:节点数据DTO,包含步骤ID、步骤名称、步骤类型、描述、图标等
- TestCaseNodePositionDto:节点位置DTO,包含X、Y坐标
- TestCaseEdgeStyleDto:连线样式DTO,包含描边颜色、描边宽度
- TestCaseEdgeDataDto:连线数据DTO,包含条件信息
-
TestCaseNodeDto 增强:
- 新增字段:
Type:节点类型(如 "testStep")Position:节点位置(TestCaseNodePositionDto)Data:节点数据(TestCaseNodeDataDto)Selected:是否被选中PositionAbsolute:绝对位置Dragging:是否正在拖拽
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
TestCaseEdgeDto 增强:
- 新增字段:
Source:源节点IDSourceHandle:源连接点Target:目标节点IDTargetHandle:目标连接点Type:连线类型Animated:是否动画Style:连线样式(TestCaseEdgeStyleDto)Data:连线数据(TestCaseEdgeDataDto)
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
技术特性:
- ReactFlow 兼容:新增字段与 ReactFlow 数据结构完全兼容
- JSON 序列化:支持直接序列化为 ReactFlow 所需的 JSON 格式
- 向后兼容:保留原有字段,确保现有代码不受影响
- 类型安全:使用强类型 DTO,避免运行时错误
-
数据结构示例:
{ "nodes": [ { "id": "node-1755654432101", "type": "testStep", "position": { "x": 360, "y": 30 }, "data": { "stepId": "e2192f5a-1582-47e9-92be-c676679418da", "stepName": "StartStep", "stepType": 1, "stepTypeName": "Start", "description": "Mapping_Start", "icon": "play-circle" }, "width": 95, "height": 30, "selected": false, "positionAbsolute": { "x": 360, "y": 30 }, "dragging": false } ], "edges": [ { "source": "node-1755654432101", "sourceHandle": "bottom", "target": "node-1755654436065", "targetHandle": "top", "id": "edge-1755654470705", "type": "smoothstep", "animated": false, "style": { "stroke": "#3b82f6", "strokeWidth": 2 }, "data": { "condition": "default" } } ] } -
设计原则:
- 前端兼容:确保与 ReactFlow 前端组件完全兼容
- 数据完整性:支持完整的节点和连线信息
- 扩展性:支持未来功能扩展
- 性能优化:避免不必要的数据转换
修改时间:
2025-01-19
修改原因:
用户需要 TestCaseNodeDto 和 TestCaseEdgeDto 与 ReactFlow 前端组件完全兼容,支持完整的节点和连线数据结构,包括位置、样式、数据等字段,确保前端能够正确显示和操作测试用例流程。
2025-01-19 - TestCaseFlow Queries功能实现
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/- 获取TestCaseFlow列表查询X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/- 根据ID获取TestCaseFlow详情查询X1.Presentation/Controllers/TestCaseFlowController.cs- TestCaseFlow控制器
修改内容:
-
GetTestCaseFlows查询功能:
- 查询类:
GetTestCaseFlowsQuery- 支持搜索、类型过滤、启用状态过滤、分页 - 响应类:
GetTestCaseFlowsResponse- 包含分页信息和TestCaseFlow列表 - 处理器:
GetTestCaseFlowsQueryHandler- 调用仓储获取分页数据并映射为DTO
- 查询类:
-
GetTestCaseFlowById查询功能:
- 查询类:
GetTestCaseFlowByIdQuery- 根据testCaseId获取详情 - 响应类:
GetTestCaseFlowByIdResponse- 包含完整的流程信息、节点和连线数据 - 处理器:
GetTestCaseFlowByIdQueryHandler- 组装TestCaseFlow、TestCaseNode、TestCaseEdge数据
- 查询类:
-
TestCaseFlowController控制器:
- 列表接口:
GET /api/testcaseflow- 获取测试用例流程列表,支持搜索和分页 - 详情接口:
GET /api/testcaseflow/{id}- 获取测试用例流程详情,包含节点和连线 - 完整日志:详细的日志记录和错误处理
- 列表接口:
-
数据组装特性:
- 列表查询:直接返回TestCaseFlow数据,不包含节点和连线
- 详情查询:组装完整的流程信息,包括所有节点和连线数据
- 性能优化:使用仓储的
GetTestCaseFlowWithDetailsAsync方法一次性获取所有数据
-
技术特性:
- CQRS模式:查询和命令分离,使用MediatR进行消息传递
- 分页支持:支持搜索、过滤、分页功能
- 数据完整性:详情查询包含完整的流程结构信息
- 错误处理:完整的异常处理和日志记录
修改时间:
2025-01-19
修改原因:
用户需要为TestCaseFlow实现Queries功能,包括获取列表和根据testCaseId获取详情,详情查询需要组装TestCaseNode和TestCaseEdge数据,为前端界面查看详情提供完整的数据支持。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复空引用警告X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 优化连线样式类型
修改内容:
-
空引用警告修复:
- 问题:在
CreateTestCaseFlowCommandHandler中,ValidateUserAuthentication方法返回的OperationResult<string>可能为空 - 解决方案:使用空合并操作符
??确保传递给CreateFailure方法的参数不为空 - 修改代码:
return OperationResult<CreateTestCaseFlowResponse>.CreateFailure(errorMessages ?? new List<string>());
- 问题:在
-
连线样式类型优化:
- 问题:
Style属性被定义为object?类型,但在CreateEdgesAsync方法中使用edgeData.Style?.ToString()来转换 - 解决方案:将
Style属性明确为string?类型,符合 JSON 字符串格式的实际用途 - 修改代码:
- 问题:
-
连线类型必填验证:
- 在
EdgeData类中将Type属性设为必填:[Required(ErrorMessage = "连线类型不能为空")] - 在验证逻辑中添加对
Type的验证:if (string.IsNullOrWhiteSpace(edge.Type)) - 在创建连线时移除默认值逻辑:
edgeType: edgeData.Type(不再使用?? "straight")
- 在
-
验证器提取:
- 创建了
CreateTestCaseFlowCommandValidator验证器类 - 将
ValidateRequest方法从CreateTestCaseFlowCommandHandler中提取出来 - 使用静态方法
Validate进行验证,便于复用和测试 - 保持原有的验证逻辑和错误消息不变
- 创建了
-
设计原则:
- 单一职责:验证器专注于参数验证,处理器专注于业务逻辑
- 可复用性:验证器可以在其他地方复用
- 可测试性:独立的验证器更容易进行单元测试
- 代码组织:更好的代码结构和职责分离
修改时间:
2025-01-19
修改原因:
用户要求确保连线类型是必填项,界面不能传空值。同时要求将验证逻辑提取到单独的验证器类中,提高代码的可维护性和可测试性。
2025-01-19 - TestCaseFlowController 删除功能添加和接口返回类型统一
修改文件:
X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommand.cs- 新增删除命令X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommandHandler.cs- 新增删除命令处理器X1.Presentation/Controllers/TestCaseFlowController.cs- 添加删除端点和统一返回类型
修改内容:
-
删除命令实现:
- 创建了
DeleteTestCaseFlowCommand类,包含流程ID参数 - 创建了
DeleteTestCaseFlowCommandHandler处理器,实现删除逻辑 - 包含用户认证验证、流程存在性检查、删除操作和事务提交
- 创建了
-
控制器接口统一:
- 参考
TerminalServicesController的返回方式,将所有方法返回类型改为OperationResult<T> - 移除了
IActionResult和BadRequest/Ok等 HTTP 状态码处理 - 直接返回
OperationResult<T>对象,让框架自动处理 HTTP 状态码
- 参考
-
删除端点添加:
- 添加了
DELETE /api/testcaseflow/{id}端点 - 包含完整的日志记录和错误处理
- 返回删除操作的结果
- 添加了
-
技术特性:
- 一致性:与
TerminalServicesController保持相同的接口返回模式 - 简化:移除了手动的 HTTP 状态码处理,让框架自动处理
- 完整性:删除功能包含完整的业务逻辑验证和错误处理
- 一致性:与
API 端点示例:
DELETE /api/testcaseflow/{id}
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": true,
"errorMessages": null
}
修改时间:
2025-01-19
修改原因:
用户要求添加删除功能到 TestCaseFlowController,并参考 TerminalServicesController 的接口返回方式,统一使用 OperationResult<T> 返回类型,不使用 IActionResult。
2025-01-19 - TestCaseFlow Application层实现
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 创建测试用例流程命令X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowResponse.cs- 创建测试用例流程响应X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 创建测试用例流程命令处理器
修改内容:
-
CreateTestCaseFlowCommand 命令类:
- 实现了
IRequest<OperationResult<CreateTestCaseFlowResponse>>接口 - 包含完整的验证特性:
[Required]、[MaxLength]等 - 支持所有必要字段:名称、描述、类型、启用状态、视口坐标等
- 提供合理的默认值,如视口坐标和启用状态
- 实现了
-
CreateTestCaseFlowResponse 响应类:
- 包含完整的流程信息返回
- 支持类型枚举转换为字符串显示
- 包含审计信息:创建时间、创建人等
-
CreateTestCaseFlowCommandHandler 命令处理器:
- 遵循CQRS模式,使用MediatR框架
- 完整的参数验证和业务逻辑验证
- 用户认证验证
- 名称重复性检查
- 使用领域实体的Create工厂方法
- 完整的错误处理和日志记录
- 事务管理(通过UnitOfWork)
-
设计原则:
- 参考TerminalServices模式:完全按照TerminalServices的设计规则实现
- CQRS架构:命令和查询分离
- 领域驱动设计:使用领域实体的工厂方法
- 依赖注入:通过构造函数注入依赖
- 日志记录:完整的操作日志和错误日志
- 错误处理:统一的错误处理和响应格式
-
技术特性:
- 支持异步操作
- 完整的取消令牌支持
- 统一的OperationResult响应格式
- 详细的验证错误信息
- 事务性操作保证
修改时间:
2025-01-19
修改原因:
用户要求在X1.Application.Features中实现TestCaseFlow的功能,参考TerminalServices的设计规则,先完成创建功能。为测试用例流程管理提供完整的Application层支持,包括命令、响应和处理器实现。
修复记录:
- 编译错误修复:修复了
TestCaseFlow.Create方法调用时的命名空间解析问题,使用完全限定的类型名称X1.Domain.Entities.TestCase.TestCaseFlow.Create来解决编译器无法找到Create方法的问题。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
- 命令扩展:在
CreateTestCaseFlowCommand中添加了NodeData和EdgeDataDTO类,支持节点和连线数据的传输 - 处理器增强:在
CreateTestCaseFlowCommandHandler中添加了ITestCaseNodeRepository和ITestCaseEdgeRepository依赖注入 - 节点创建:实现了
CreateNodesAsync方法,支持批量创建测试用例节点,包括位置、尺寸、状态等属性 - 连线创建:实现了
CreateEdgesAsync方法,支持批量创建测试用例连线,包括源节点、目标节点、类型、样式等属性 - 验证增强:添加了对节点和连线数据的验证逻辑,确保数据完整性
- 日志记录:增强了日志记录,包含节点数量和连线数量的统计信息
- 事务管理:确保节点和连线的创建在同一个事务中完成,保证数据一致性
2024-12-19 - StartDeviceRuntimeCommandHandler 问题分析与修复
问题描述
API响应中 isSuccess: true 但 summary.failureCount: 1,导致前端误判操作成功。
问题分析
通过分析代码发现以下潜在问题区域:
- 网络配置构建阶段:在
BuildNetworkConfigurationRequests方法中,设备可能因为配置验证失败而被过滤掉 - 网络启动阶段:在
StartNetworksInParallelAsync方法中,网络启动失败 - 设备运行时处理阶段:设备运行时不存在或更新失败
实施的修复
-
增强日志记录:
- 在
BuildNetworkConfigurationRequests中添加详细的警告日志 - 在
StartNetworksInParallelAsync中增强错误日志和统计信息 - 在设备运行时处理循环中添加调试日志
- 在
-
关键修复 - isSuccess 字段逻辑:
- 问题根源:
OperationResult<T>.IsSuccess属性仅基于ErrorMessages是否为空 - 解决方案:在
Handle方法中根据业务逻辑判断成功/失败 - 只有当所有设备都成功启动时才返回
CreateSuccess - 否则返回
CreateFailure并包含详细错误信息
- 问题根源:
-
配置验证逻辑优化:
- 问题:原来的配置验证过于严格,要求同时有RAN配置和完整的IMS+核心网配置
- 优化:改为更灵活的验证逻辑,允许只有RAN配置或只有IMS配置的设备通过
- 验证规则:
- 至少需要RAN配置 或者 IMS配置(不要求同时有核心网配置)
- 如果有IMS配置但没有核心网配置,记录警告但不阻止设备启动
- 提供更详细的配置状态日志,便于调试
- 影响:减少因配置不完整而被错误跳过的设备数量
修改原因
- 解决前端误判问题
- 提供更好的调试信息
- 确保数据一致性
2024-12-19 - 已实施更改的评估分析
评估结果:所有更改都应该保留
1. 网络配置构建阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的日志记录,记录被跳过设备的具体原因
- 过滤逻辑透明化,包括重复组合、缺少配置、验证失败等情况
- 统计信息记录,显示原始请求数 vs 有效请求数
- 价值: 提供更好的调试信息和透明度
2. 网络启动阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的错误日志,包含具体错误信息
- 统计信息记录,显示总请求数、成功数、失败数
- 失败设备详细记录
- 价值: 提供关键的调试信息,帮助快速定位网络启动失败原因
3. 设备运行时处理阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 跳过逻辑:只处理网络启动成功的设备
- 详细日志:记录状态更新过程
- 调试信息:记录设备运行时当前状态
- 价值: 确保数据一致性,避免对失败设备的无效处理
4. isSuccess 字段逻辑修复 ✅
- 状态: 已修复,这是核心问题
- 问题:
isSuccess: true但failureCount: 1导致前端误判 - 解决方案: 根据业务逻辑判断成功/失败
- 价值: 解决了前端误判的根本问题
建议的监控指标
- 网络配置构建成功率:
有效请求数 / 原始请求数 - 网络启动成功率:
成功设备数 / 有效请求数 - 整体成功率:
成功设备数 / 总请求数
日志分析建议
当出现失败时,可通过以下日志快速定位问题:
- 网络配置构建阶段:查看被跳过设备的原因
- 网络启动阶段:查看具体错误信息
- 设备运行时处理:查看状态更新过程
结论
2025-01-21 - ReactFlowDesigner节点类型调试
问题描述
在ReactFlowDesigner.tsx中,用户发现所有节点的type都是'testStep',无法区分不同类型的节点。虽然代码中有根据stepType确定节点类型的逻辑,但实际创建的节点类型没有正确设置。
修改内容
1. ReactFlowDesigner.tsx
- 文件位置:
X1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx - 修改内容:
- 在节点创建逻辑中添加了调试日志,输出stepType、nodeType和newNode信息
- 在TestStepNode组件中添加了调试日志,输出stepType、stepTypeName、stepName和nodeStyle信息
- 确保getNodeType函数正确根据stepType返回对应的节点类型
修改前后对比
修改前
const newNode = {
id: `node-${Date.now()}`,
type: getNodeType(step.stepType),
position,
data: {
stepId: step.id,
stepName: step.stepName,
stepType: step.stepType,
stepTypeName: step.stepTypeName,
description: step.description,
icon: step.icon,
},
};
修改后
const newNode = {
id: `node-${Date.now()}`,
type: getNodeType(step.stepType),
position,
data: {
stepId: step.id,
stepName: step.stepName,
stepType: step.stepType,
stepTypeName: step.stepTypeName,
description: step.description,
icon: step.icon,
},
};
console.log('创建新节点:', {
stepType: step.stepType,
nodeType: getNodeType(step.stepType),
newNode: newNode
});
影响范围
- 添加了调试信息,帮助诊断节点类型设置问题
- 确保节点类型能够正确根据stepType进行区分
- 保持了原有的节点渲染逻辑不变
测试建议
- 测试不同类型节点的创建,观察控制台输出的调试信息
- 验证节点类型是否正确设置
- 确认节点显示和交互功能正常
修改记录
2025-01-21 - 创建 TestCaseFlow 相关表的数据库迁移
修改文件:
X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.cs- 新建迁移文件X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.Designer.cs- 迁移设计器文件X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs- 更新模型快照
修改内容:
-
迁移文件创建:
- 迁移名称:
AddTestCaseFlowTables - 迁移时间:2025-08-21 08:06:04
- 迁移描述:为 TestCaseFlow、TestCaseNode、TestCaseEdge 实体创建数据库表
- 迁移名称:
-
创建的表结构:
-
tb_testcaseflow:测试用例流程主表
- 包含 id、name、description、type、isenabled、viewport_x、viewport_y、viewport_zoom 等字段
- 包含审计字段:createdat、updatedat、createdby、updatedby
- 创建索引:name、type、isenabled
-
tb_testcasenode:测试用例节点表
- 包含 id、testcaseid、nodeid、sequencenumber、stepid、positionx、positiony、width、height 等字段
- 包含状态字段:isselected、positionabsolutex、positionabsolutey、isdragging
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 外键关系:stepid → tb_casestepconfig.id (SET NULL)
- 创建索引:testcaseid、nodeid、sequencenumber、stepid
-
tb_testcaseedge:测试用例连线表
- 包含 id、testcaseid、edgeid、sourcenodeid、targetnodeid、edgetype、condition、isanimated、style 等字段
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 创建索引:testcaseid、edgeid、sourcenodeid、targetnodeid
-
-
数据库关系:
- 级联删除:删除测试用例流程时,自动删除相关的节点和连线
- 可选关联:节点可以关联步骤配置,删除步骤配置时节点 stepid 设为 NULL
- 完整性约束:确保数据的一致性和完整性
-
迁移应用状态:
- 已成功应用:迁移已应用到数据库
- 表已创建:三个表都已成功创建在数据库中
- 索引已建立:所有必要的索引都已创建完成
-
技术特性:
- PostgreSQL 兼容:使用 PostgreSQL 特定的数据类型和语法
- 性能优化:为常用查询字段创建索引
- 数据完整性:通过外键约束确保数据一致性
- 审计支持:包含完整的审计字段支持
修改时间:
2025-01-21
修改原因:
用户反映 TestCaseFlows、TestCaseNodes、TestCaseEdges 新建后还没有迁移数据库,需要创建相应的数据库迁移文件并应用到数据库中,以支持测试用例流程管理功能。
2025-01-19 - 根据 TestCaseFlowController 修复 testcaseService.ts
修改文件:
X1.WebUI/src/services/testcaseService.ts - 根据后端 TestCaseFlowController 重新编写前端服务
修改内容:
-
完全重构服务:
- 服务类名:从
TestCaseService改为TestCaseFlowService - 基础URL:从
/api/testcases改为/api/testcaseflow - API端点:完全匹配后端 TestCaseFlowController 的接口
- 服务类名:从
-
数据类型重新定义:
- TestFlowType:测试流程类型枚举
- TestCaseFlow:测试用例流程基础信息
- TestCaseNode:测试用例节点(支持 ReactFlow)
- TestCaseEdge:测试用例连线(支持 ReactFlow)
- TestCaseFlowDetail:测试用例流程详情(包含节点和连线)
-
API 方法对应:
- getTestCaseFlows:对应
GET /api/testcaseflow- 获取流程列表 - getTestCaseFlowById:对应
GET /api/testcaseflow/{id}- 获取流程详情 - createTestCaseFlow:对应
POST /api/testcaseflow- 创建流程 - deleteTestCaseFlow:对应
DELETE /api/testcaseflow/{id}- 删除流程
- getTestCaseFlows:对应
-
请求参数匹配:
- GetTestCaseFlowsRequest:支持 searchTerm、type、isEnabled、pageNumber、pageSize
- CreateTestCaseFlowRequest:支持 name、description、type、isEnabled、viewport、nodes、edges
- CreateNodeData:节点创建数据结构
- CreateEdgeData:连线创建数据结构
-
响应数据结构:
- GetTestCaseFlowsResponse:包含分页信息的流程列表
- GetTestCaseFlowByIdResponse:包含完整节点和连线数据的流程详情
- CreateTestCaseFlowResponse:创建成功后的流程信息
-
ReactFlow 兼容性:
- 节点结构:完全支持 ReactFlow 的节点数据结构
- 连线结构:完全支持 ReactFlow 的连线数据结构
- 位置信息:支持 position、positionAbsolute 等位置字段
- 样式信息:支持 style、data 等样式和数据字段
-
向后兼容性:
- 保留导出:保持
testcaseService导出,确保现有代码不破坏 - 新增导出:新增
testcaseFlowService导出,提供更明确的命名
- 保留导出:保持
修改时间:
2025-01-19
修改原因:
用户要求根据 TestCaseFlowController 修复 testcaseService.ts,确保前端服务与后端 API 完全匹配,支持测试用例流程的完整 CRUD 操作和 ReactFlow 集成。
2025-01-19 - 更新 API 路径常量以匹配后端控制器
修改文件:
X1.WebUI/src/constants/api.ts- 更新 API 路径常量X1.WebUI/src/services/testcaseService.ts- 使用 API 路径常量
修改内容:
-
API 路径常量更新:
- 移除:
TEST_CASES: '/test-cases'和TEST_STEPS: '/test-steps'(后端无对应控制器) - 新增:
TEST_CASE_FLOW: '/api/testcaseflow'(对应 TestCaseFlowController) - 更新:
CASE_STEP_CONFIGS: '/api/casestepconfigs'(对应 CaseStepConfigController)
- 移除:
-
testcaseService.ts 优化:
- 导入常量:添加
API_PATHS导入 - 使用常量:将硬编码的
/api/testcaseflow替换为API_PATHS.TEST_CASE_FLOW - 统一管理:所有 API 路径都通过常量统一管理
- 导入常量:添加
-
路径对应关系:
- TestCaseFlowController:
/api/testcaseflow→API_PATHS.TEST_CASE_FLOW - CaseStepConfigController:
/api/casestepconfigs→API_PATHS.CASE_STEP_CONFIGS
- TestCaseFlowController:
-
技术特性:
- 类型安全:使用 TypeScript 常量确保路径一致性
- 维护性:集中管理 API 路径,便于维护和修改
- 一致性:确保前端服务与后端控制器路径完全匹配
修改时间:
2025-01-19
修改原因:
用户询问是否需要根据 testcaseService 修复其他相关服务,发现 API 路径常量与实际后端控制器不匹配,需要统一更新以确保前后端一致性。
2025-01-19 - 修复 pages/testcases 页面使用 testcaseService
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 修复列表页面使用真实 APIX1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复视图页面使用真实 APIX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx- 添加保存状态支持
修改内容:
-
TestCasesListView.tsx 重构:
- 移除模拟数据:删除
mockTestCases和本地接口定义 - 集成真实 API:使用
testcaseService.getTestCaseFlows()获取数据 - 添加加载状态:显示加载中状态,提升用户体验
- 实现搜索功能:支持按名称搜索测试用例流程
- 实现删除功能:使用
testcaseService.deleteTestCaseFlow()删除流程 - 更新数据展示:适配
TestCaseFlow接口的数据结构 - 修复状态显示:使用
isEnabled字段显示启用/停用状态 - 添加类型标签:显示测试流程类型(功能测试、性能测试等)
- 移除模拟数据:删除
-
TestCasesView.tsx 重构:
- 集成保存功能:使用
testcaseService.createTestCaseFlow()保存流程 - 数据格式转换:将 ReactFlow 节点和连线数据转换为后端格式
- 添加保存状态:显示保存中状态,防止重复提交
- 错误处理:完整的错误处理和用户提示
- 流程验证:检查流程完整性(开始节点、结束节点等)
- 集成保存功能:使用
-
ReactFlowDesigner.tsx 增强:
- 添加 saving 属性:支持保存状态传递
- 保存按钮状态:保存时禁用按钮并显示"保存中..."
- 用户体验优化:防止保存过程中的重复操作
-
数据结构适配:
- 节点数据转换:ReactFlow 节点 → 后端 CreateNodeData 格式
- 连线数据转换:ReactFlow 连线 → 后端 CreateEdgeData 格式
- 类型安全:使用 TypeScript 接口确保类型安全
-
功能特性:
- 实时搜索:支持按回车键搜索
- 批量操作:支持删除操作
- 导航功能:支持查看和编辑页面跳转
- 状态管理:完整的加载和保存状态管理
修改时间:
2025-01-19
修改原因:
用户发现 pages/testcases 页面没有调用 testcaseService,需要修复这些页面使其使用真实的 API 而不是模拟数据,确保前后端数据一致性。
2025-01-19 - TestCaseFlowController 添加 CreateTestCaseFlow 命令
修改文件:
X1.Presentation/Controllers/TestCaseFlowController.cs - 为TestCaseFlowController添加创建测试用例流程的POST方法
修改内容:
-
新增POST方法:
- 方法名:
CreateTestCaseFlow - 路由:
[HttpPost]- 对应/api/testcaseflow - 参数:
[FromBody] CreateTestCaseFlowCommand command - 返回:
IActionResult
- 方法名:
-
功能特性:
- 命令处理:使用
mediator.Send(command)发送创建命令 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 响应格式:使用
CreatedAtAction返回201状态码和资源位置
- 命令处理:使用
-
日志记录:
- 开始日志:记录流程名称和类型
- 成功日志:记录创建的ID、名称、节点数量、连线数量
- 失败日志:记录流程名称和详细错误信息
-
响应处理:
- 成功响应:返回201 Created状态码,包含新创建资源的URI
- 失败响应:返回400 Bad Request状态码,包含错误详情
- 资源定位:使用
CreatedAtAction提供新创建资源的访问路径
-
技术特性:
- 依赖注入:添加了
CreateTestCaseFlow命名空间的using语句 - MediatR集成:使用mediator发送命令,遵循CQRS模式
- RESTful设计:遵循REST API设计规范
- 统一响应:使用统一的OperationResult响应格式
- 依赖注入:添加了
-
API端点:
POST /api/testcaseflow Content-Type: application/json { "name": "测试流程名称", "description": "流程描述", "type": 1, "isEnabled": true, "viewportX": 40.5, "viewportY": 21.2, "viewportZoom": 1.1, "nodes": [...], "edges": [...] }
修改时间:
2025-01-19
修改原因:
用户要求为TestCaseFlowController添加TestCaseFlow.Commands功能,特别是创建测试用例流程的POST方法,以支持前端界面创建新的测试用例流程。
2025-01-19 - TestCaseNodeDto 和 TestCaseEdgeDto 完善
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.cs - 完善 TestCaseNodeDto 和 TestCaseEdgeDto 结构
修改内容:
-
新增 DTO 类:
- TestCaseNodeDataDto:节点数据DTO,包含步骤ID、步骤名称、步骤类型、描述、图标等
- TestCaseNodePositionDto:节点位置DTO,包含X、Y坐标
- TestCaseEdgeStyleDto:连线样式DTO,包含描边颜色、描边宽度
- TestCaseEdgeDataDto:连线数据DTO,包含条件信息
-
TestCaseNodeDto 增强:
- 新增字段:
Type:节点类型(如 "testStep")Position:节点位置(TestCaseNodePositionDto)Data:节点数据(TestCaseNodeDataDto)Selected:是否被选中PositionAbsolute:绝对位置Dragging:是否正在拖拽
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
TestCaseEdgeDto 增强:
- 新增字段:
Source:源节点IDSourceHandle:源连接点Target:目标节点IDTargetHandle:目标连接点Type:连线类型Animated:是否动画Style:连线样式(TestCaseEdgeStyleDto)Data:连线数据(TestCaseEdgeDataDto)
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
技术特性:
- ReactFlow 兼容:新增字段与 ReactFlow 数据结构完全兼容
- JSON 序列化:支持直接序列化为 ReactFlow 所需的 JSON 格式
- 向后兼容:保留原有字段,确保现有代码不受影响
- 类型安全:使用强类型 DTO,避免运行时错误
-
数据结构示例:
{ "nodes": [ { "id": "node-1755654432101", "type": "testStep", "position": { "x": 360, "y": 30 }, "data": { "stepId": "e2192f5a-1582-47e9-92be-c676679418da", "stepName": "StartStep", "stepType": 1, "stepTypeName": "Start", "description": "Mapping_Start", "icon": "play-circle" }, "width": 95, "height": 30, "selected": false, "positionAbsolute": { "x": 360, "y": 30 }, "dragging": false } ], "edges": [ { "source": "node-1755654432101", "sourceHandle": "bottom", "target": "node-1755654436065", "targetHandle": "top", "id": "edge-1755654470705", "type": "smoothstep", "animated": false, "style": { "stroke": "#3b82f6", "strokeWidth": 2 }, "data": { "condition": "default" } } ] } -
设计原则:
- 前端兼容:确保与 ReactFlow 前端组件完全兼容
- 数据完整性:支持完整的节点和连线信息
- 扩展性:支持未来功能扩展
- 性能优化:避免不必要的数据转换
修改时间:
2025-01-19
修改原因:
用户需要 TestCaseNodeDto 和 TestCaseEdgeDto 与 ReactFlow 前端组件完全兼容,支持完整的节点和连线数据结构,包括位置、样式、数据等字段,确保前端能够正确显示和操作测试用例流程。
2025-01-19 - TestCaseFlow Queries功能实现
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/- 获取TestCaseFlow列表查询X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/- 根据ID获取TestCaseFlow详情查询X1.Presentation/Controllers/TestCaseFlowController.cs- TestCaseFlow控制器
修改内容:
-
GetTestCaseFlows查询功能:
- 查询类:
GetTestCaseFlowsQuery- 支持搜索、类型过滤、启用状态过滤、分页 - 响应类:
GetTestCaseFlowsResponse- 包含分页信息和TestCaseFlow列表 - 处理器:
GetTestCaseFlowsQueryHandler- 调用仓储获取分页数据并映射为DTO
- 查询类:
-
GetTestCaseFlowById查询功能:
- 查询类:
GetTestCaseFlowByIdQuery- 根据testCaseId获取详情 - 响应类:
GetTestCaseFlowByIdResponse- 包含完整的流程信息、节点和连线数据 - 处理器:
GetTestCaseFlowByIdQueryHandler- 组装TestCaseFlow、TestCaseNode、TestCaseEdge数据
- 查询类:
-
TestCaseFlowController控制器:
- 列表接口:
GET /api/testcaseflow- 获取测试用例流程列表,支持搜索和分页 - 详情接口:
GET /api/testcaseflow/{id}- 获取测试用例流程详情,包含节点和连线 - 完整日志:详细的日志记录和错误处理
- 列表接口:
-
数据组装特性:
- 列表查询:直接返回TestCaseFlow数据,不包含节点和连线
- 详情查询:组装完整的流程信息,包括所有节点和连线数据
- 性能优化:使用仓储的
GetTestCaseFlowWithDetailsAsync方法一次性获取所有数据
-
技术特性:
- CQRS模式:查询和命令分离,使用MediatR进行消息传递
- 分页支持:支持搜索、过滤、分页功能
- 数据完整性:详情查询包含完整的流程结构信息
- 错误处理:完整的异常处理和日志记录
修改时间:
2025-01-19
修改原因:
用户需要为TestCaseFlow实现Queries功能,包括获取列表和根据testCaseId获取详情,详情查询需要组装TestCaseNode和TestCaseEdge数据,为前端界面查看详情提供完整的数据支持。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复空引用警告X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 优化连线样式类型
修改内容:
-
空引用警告修复:
- 问题:在
CreateTestCaseFlowCommandHandler中,ValidateUserAuthentication方法返回的OperationResult<string>可能为空 - 解决方案:使用空合并操作符
??确保传递给CreateFailure方法的参数不为空 - 修改代码:
return OperationResult<CreateTestCaseFlowResponse>.CreateFailure(errorMessages ?? new List<string>());
- 问题:在
-
连线样式类型优化:
- 问题:
Style属性被定义为object?类型,但在CreateEdgesAsync方法中使用edgeData.Style?.ToString()来转换 - 解决方案:将
Style属性明确为string?类型,符合 JSON 字符串格式的实际用途 - 修改代码:
- 问题:
-
连线类型必填验证:
- 在
EdgeData类中将Type属性设为必填:[Required(ErrorMessage = "连线类型不能为空")] - 在验证逻辑中添加对
Type的验证:if (string.IsNullOrWhiteSpace(edge.Type)) - 在创建连线时移除默认值逻辑:
edgeType: edgeData.Type(不再使用?? "straight")
- 在
-
验证器提取:
- 创建了
CreateTestCaseFlowCommandValidator验证器类 - 将
ValidateRequest方法从CreateTestCaseFlowCommandHandler中提取出来 - 使用静态方法
Validate进行验证,便于复用和测试 - 保持原有的验证逻辑和错误消息不变
- 创建了
-
设计原则:
- 单一职责:验证器专注于参数验证,处理器专注于业务逻辑
- 可复用性:验证器可以在其他地方复用
- 可测试性:独立的验证器更容易进行单元测试
- 代码组织:更好的代码结构和职责分离
修改时间:
2025-01-19
修改原因:
用户要求确保连线类型是必填项,界面不能传空值。同时要求将验证逻辑提取到单独的验证器类中,提高代码的可维护性和可测试性。
2025-01-19 - TestCaseFlowController 删除功能添加和接口返回类型统一
修改文件:
X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommand.cs- 新增删除命令X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommandHandler.cs- 新增删除命令处理器X1.Presentation/Controllers/TestCaseFlowController.cs- 添加删除端点和统一返回类型
修改内容:
-
删除命令实现:
- 创建了
DeleteTestCaseFlowCommand类,包含流程ID参数 - 创建了
DeleteTestCaseFlowCommandHandler处理器,实现删除逻辑 - 包含用户认证验证、流程存在性检查、删除操作和事务提交
- 创建了
-
控制器接口统一:
- 参考
TerminalServicesController的返回方式,将所有方法返回类型改为OperationResult<T> - 移除了
IActionResult和BadRequest/Ok等 HTTP 状态码处理 - 直接返回
OperationResult<T>对象,让框架自动处理 HTTP 状态码
- 参考
-
删除端点添加:
- 添加了
DELETE /api/testcaseflow/{id}端点 - 包含完整的日志记录和错误处理
- 返回删除操作的结果
- 添加了
-
技术特性:
- 一致性:与
TerminalServicesController保持相同的接口返回模式 - 简化:移除了手动的 HTTP 状态码处理,让框架自动处理
- 完整性:删除功能包含完整的业务逻辑验证和错误处理
- 一致性:与
API 端点示例:
DELETE /api/testcaseflow/{id}
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": true,
"errorMessages": null
}
修改时间:
2025-01-19
修改原因:
用户要求添加删除功能到 TestCaseFlowController,并参考 TerminalServicesController 的接口返回方式,统一使用 OperationResult<T> 返回类型,不使用 IActionResult。
2025-01-19 - TestCaseFlow Application层实现
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 创建测试用例流程命令X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowResponse.cs- 创建测试用例流程响应X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 创建测试用例流程命令处理器
修改内容:
-
CreateTestCaseFlowCommand 命令类:
- 实现了
IRequest<OperationResult<CreateTestCaseFlowResponse>>接口 - 包含完整的验证特性:
[Required]、[MaxLength]等 - 支持所有必要字段:名称、描述、类型、启用状态、视口坐标等
- 提供合理的默认值,如视口坐标和启用状态
- 实现了
-
CreateTestCaseFlowResponse 响应类:
- 包含完整的流程信息返回
- 支持类型枚举转换为字符串显示
- 包含审计信息:创建时间、创建人等
-
CreateTestCaseFlowCommandHandler 命令处理器:
- 遵循CQRS模式,使用MediatR框架
- 完整的参数验证和业务逻辑验证
- 用户认证验证
- 名称重复性检查
- 使用领域实体的Create工厂方法
- 完整的错误处理和日志记录
- 事务管理(通过UnitOfWork)
-
设计原则:
- 参考TerminalServices模式:完全按照TerminalServices的设计规则实现
- CQRS架构:命令和查询分离
- 领域驱动设计:使用领域实体的工厂方法
- 依赖注入:通过构造函数注入依赖
- 日志记录:完整的操作日志和错误日志
- 错误处理:统一的错误处理和响应格式
-
技术特性:
- 支持异步操作
- 完整的取消令牌支持
- 统一的OperationResult响应格式
- 详细的验证错误信息
- 事务性操作保证
修改时间:
2025-01-19
修改原因:
用户要求在X1.Application.Features中实现TestCaseFlow的功能,参考TerminalServices的设计规则,先完成创建功能。为测试用例流程管理提供完整的Application层支持,包括命令、响应和处理器实现。
修复记录:
- 编译错误修复:修复了
TestCaseFlow.Create方法调用时的命名空间解析问题,使用完全限定的类型名称X1.Domain.Entities.TestCase.TestCaseFlow.Create来解决编译器无法找到Create方法的问题。
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
- 命令扩展:在
CreateTestCaseFlowCommand中添加了NodeData和EdgeDataDTO类,支持节点和连线数据的传输 - 处理器增强:在
CreateTestCaseFlowCommandHandler中添加了ITestCaseNodeRepository和ITestCaseEdgeRepository依赖注入 - 节点创建:实现了
CreateNodesAsync方法,支持批量创建测试用例节点,包括位置、尺寸、状态等属性 - 连线创建:实现了
CreateEdgesAsync方法,支持批量创建测试用例连线,包括源节点、目标节点、类型、样式等属性 - 验证增强:添加了对节点和连线数据的验证逻辑,确保数据完整性
- 日志记录:增强了日志记录,包含节点数量和连线数量的统计信息
- 事务管理:确保节点和连线的创建在同一个事务中完成,保证数据一致性
2024-12-19 - StartDeviceRuntimeCommandHandler 问题分析与修复
问题描述
API响应中 isSuccess: true 但 summary.failureCount: 1,导致前端误判操作成功。
问题分析
通过分析代码发现以下潜在问题区域:
- 网络配置构建阶段:在
BuildNetworkConfigurationRequests方法中,设备可能因为配置验证失败而被过滤掉 - 网络启动阶段:在
StartNetworksInParallelAsync方法中,网络启动失败 - 设备运行时处理阶段:设备运行时不存在或更新失败
实施的修复
-
增强日志记录:
- 在
BuildNetworkConfigurationRequests中添加详细的警告日志 - 在
StartNetworksInParallelAsync中增强错误日志和统计信息 - 在设备运行时处理循环中添加调试日志
- 在
-
关键修复 - isSuccess 字段逻辑:
- 问题根源:
OperationResult<T>.IsSuccess属性仅基于ErrorMessages是否为空 - 解决方案:在
Handle方法中根据业务逻辑判断成功/失败 - 只有当所有设备都成功启动时才返回
CreateSuccess - 否则返回
CreateFailure并包含详细错误信息
- 问题根源:
-
配置验证逻辑优化:
- 问题:原来的配置验证过于严格,要求同时有RAN配置和完整的IMS+核心网配置
- 优化:改为更灵活的验证逻辑,允许只有RAN配置或只有IMS配置的设备通过
- 验证规则:
- 至少需要RAN配置 或者 IMS配置(不要求同时有核心网配置)
- 如果有IMS配置但没有核心网配置,记录警告但不阻止设备启动
- 提供更详细的配置状态日志,便于调试
- 影响:减少因配置不完整而被错误跳过的设备数量
修改原因
- 解决前端误判问题
- 提供更好的调试信息
- 确保数据一致性
2024-12-19 - 已实施更改的评估分析
评估结果:所有更改都应该保留
1. 网络配置构建阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的日志记录,记录被跳过设备的具体原因
- 过滤逻辑透明化,包括重复组合、缺少配置、验证失败等情况
- 统计信息记录,显示原始请求数 vs 有效请求数
- 价值: 提供更好的调试信息和透明度
2. 网络启动阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的错误日志,包含具体错误信息
- 统计信息记录,显示总请求数、成功数、失败数
- 失败设备详细记录
- 价值: 提供关键的调试信息,帮助快速定位网络启动失败原因
3. 设备运行时处理阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 跳过逻辑:只处理网络启动成功的设备
- 详细日志:记录状态更新过程
- 调试信息:记录设备运行时当前状态
- 价值: 确保数据一致性,避免对失败设备的无效处理
4. isSuccess 字段逻辑修复 ✅
- 状态: 已修复,这是核心问题
- 问题:
isSuccess: true但failureCount: 1导致前端误判 - 解决方案: 根据业务逻辑判断成功/失败
- 价值: 解决了前端误判的根本问题
建议的监控指标
- 网络配置构建成功率:
有效请求数 / 原始请求数 - 网络启动成功率:
成功设备数 / 有效请求数 - 整体成功率:
成功设备数 / 总请求数
日志分析建议
当出现失败时,可通过以下日志快速定位问题:
- 网络配置构建阶段:查看被跳过设备的原因
- 网络启动阶段:查看具体错误信息
- 设备运行时处理:查看状态更新过程
结论
所有实施的更改都是有价值的改进,不仅解决了核心的 isSuccess 误判问题,还提供了更好的调试能力和数据一致性。不建议撤回任何更改。
2025年修改记录
2025-01-19 - TestCaseEdge 和 TestCaseNode Repositories 完善
修改文件:
X1.Domain/Repositories/TestCase/ITestCaseEdgeRepository.cs- 创建 TestCaseEdge 仓储接口X1.Domain/Repositories/TestCase/ITestCaseNodeRepository.cs- 创建 TestCaseNode 仓储接口X1.Infrastructure/Repositories/TestCase/TestCaseEdgeRepository.cs- 创建 TestCaseEdge 仓储实现X1.Infrastructure/Repositories/TestCase/TestCaseNodeRepository.cs- 创建 TestCaseNode 仓储实现X1.Infrastructure/DependencyInjection.cs- 注册新的仓储服务
修改内容:
-
TestCaseEdge 仓储接口创建:
- 创建了
ITestCaseEdgeRepository接口,继承自IBaseRepository<TestCaseEdge> - 定义了完整的业务方法,包括基本的 CRUD 操作和特定的业务查询
- 支持测试用例连线的完整生命周期管理
- 创建了
-
主要业务方法:
- 基本操作:
AddTestCaseEdgeAsync、UpdateTestCaseEdge、DeleteTestCaseEdgeAsync - 批量操作:
DeleteByTestCaseIdAsync(根据测试用例ID删除所有连线) - 查询操作:
GetAllTestCaseEdgesAsync、GetTestCaseEdgeByIdAsync、GetByTestCaseIdAsync - 特定查询:
GetBySourceNodeIdAsync、GetByTargetNodeIdAsync、GetByEdgeIdAsync - 验证操作:
EdgeIdExistsAsync、ExistsByTestCaseIdAsync
- 基本操作:
-
TestCaseNode 仓储接口创建:
- 创建了
ITestCaseNodeRepository接口,继承自IBaseRepository<TestCaseNode> - 定义了完整的业务方法,包括基本的 CRUD 操作和特定的业务查询
- 支持测试用例节点的完整生命周期管理
- 创建了
-
主要业务方法:
- 基本操作:
AddTestCaseNodeAsync、UpdateTestCaseNode、DeleteTestCaseNodeAsync - 批量操作:
DeleteByTestCaseIdAsync(根据测试用例ID删除所有节点) - 查询操作:
GetAllTestCaseNodesAsync、GetTestCaseNodeByIdAsync、GetByTestCaseIdAsync - 排序查询:
GetByTestCaseIdOrderedAsync(按序号排序) - 特定查询:
GetByNodeIdAsync、GetByStepIdAsync、GetByTestCaseIdAndSequenceAsync - 验证操作:
NodeIdExistsAsync、ExistsByTestCaseIdAsync、SequenceExistsAsync - 统计操作:
GetMaxSequenceNumberAsync(获取最大序号)
- 基本操作:
-
TestCaseEdge 仓储实现创建:
- 创建了
TestCaseEdgeRepository实现类,继承自BaseRepository<TestCaseEdge> - 实现了
ITestCaseEdgeRepository接口的所有方法 - 使用 CQRS 模式,分离命令和查询操作
- 提供完整的日志记录和错误处理
- 创建了
-
TestCaseNode 仓储实现创建:
- 创建了
TestCaseNodeRepository实现类,继承自BaseRepository<TestCaseNode> - 实现了
ITestCaseNodeRepository接口的所有方法 - 使用 CQRS 模式,分离命令和查询操作
- 提供完整的日志记录和错误处理
- 创建了
-
依赖注入配置:
- 在
X1.Infrastructure/DependencyInjection.cs中注册新的仓储服务 - 添加了
ITestCaseEdgeRepository和ITestCaseNodeRepository的注册 - 确保控制器能够正确注入所需的仓储服务
- 在
-
技术特性:
- CQRS 模式:使用
ICommandRepository和IQueryRepository分离读写操作 - 异步支持:所有方法都支持异步操作和取消令牌
- 简洁实现:与现有仓储实现保持一致的简洁风格
- 性能优化:支持批量操作和条件过滤
- CQRS 模式:使用
-
设计原则:
- DDD 原则:遵循领域驱动设计,仓储专注于数据访问
- 单一职责:每个方法专注于特定功能
- 可扩展性:支持未来功能扩展
- 一致性:与现有仓储实现(如
CaseStepConfigRepository)保持一致的架构模式
-
命名空间规范:
- 使用
X1.Domain.Repositories.TestCase命名空间 - 使用
X1.Infrastructure.Repositories.TestCase命名空间 - 与项目整体架构保持一致
- 使用
修改时间:
2025-01-19
修改原因:
用户要求完善 TestCaseEdge 和 TestCaseNode 的 Repositories,参考 ICaseStepConfigRepository 的结构,为测试用例节点和连线管理提供完整的数据访问层支持,包括基本的 CRUD 操作和特定的业务查询功能。
2025-01-19 - TestCaseFlow 实体 Create 和 Update 方法实现
修改文件:
X1.Domain/Entities/TestCase/TestCaseTestFlow.cs - 为TestCaseFlow实体添加Create和Update方法
修改内容:
-
Create 静态工厂方法:
- 添加了
Create静态方法,用于创建新的测试用例流程 - 支持所有必要参数:名称、类型、创建人、描述、启用状态、视口坐标等
- 自动设置ID、创建时间、更新时间等审计字段
- 视口坐标参数(viewportX、viewportY、viewportZoom)由界面传入,不提供默认值
- 添加了
-
Update 实例方法:
- 添加了
Update方法,用于更新测试用例流程 - 支持更新所有字段:名称、类型、描述、启用状态、视口坐标等
- 自动更新
UpdatedAt和UpdatedBy审计字段 - 使用可选参数,只更新传入的字段
- 添加了
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用工厂方法模式创建实体实例
- 通过业务方法修改实体状态
- 确保审计信息的完整性
-
技术特性:
- 类型安全的参数验证
- 完整的审计信息管理
- 灵活的更新机制
- 与CaseStepConfig实体保持一致的实现模式
- 视口坐标由界面传入,确保数据的准确性
修改时间:
2025-01-19
修改原因:
用户要求TestCaseFlow实体提供与CaseStepConfig实体相同的Create和Update方法,确保实体创建和更新的标准化和一致性。同时根据用户反馈,视口坐标参数应该由界面传入,不提供默认值。
2025-01-19 - TestCaseNode 和 TestCaseEdge 实体 Create 和 Update 方法实现
修改文件:
X1.Domain/Entities/TestCase/TestCaseNode.cs- 为TestCaseNode实体添加Create和Update方法X1.Domain/Entities/TestCase/TestCaseEdge.cs- 为TestCaseEdge实体添加Create和Update方法
修改内容:
-
TestCaseNode 实体 Create 和 Update 方法:
- Create 静态工厂方法:
- 添加了
Create静态方法,用于创建新的测试用例节点 - 支持所有必要参数:测试用例ID、节点ID、执行序号、位置坐标、步骤配置ID、尺寸、状态等
- 自动设置ID,不包含审计字段(继承自Entity而非AuditableEntity)
- 提供合理的默认值,如宽度、高度、选中状态等
- 添加了
- Update 实例方法:
- 添加了
Update方法,用于更新测试用例节点 - 支持更新所有字段:测试用例ID、节点ID、执行序号、位置坐标、步骤配置ID、尺寸、状态等
- 使用可选参数,只更新传入的字段
- 提供完整的参数验证和错误处理
- 添加了
- Create 静态工厂方法:
-
TestCaseEdge 实体 Create 和 Update 方法:
- Create 静态工厂方法:
- 添加了
Create静态方法,用于创建新的测试用例连线 - 支持所有必要参数:测试用例ID、连线ID、源节点ID、目标节点ID、连线类型、条件、动画、样式等
- 自动设置ID,不包含审计字段(继承自Entity而非AuditableEntity)
- 提供合理的默认值,如连线类型、动画状态等
- 添加了
- Update 实例方法:
- 添加了
Update方法,用于更新测试用例连线 - 支持更新所有字段:测试用例ID、连线ID、源节点ID、目标节点ID、连线类型、条件、动画、样式等
- 使用可选参数,只更新传入的字段
- 提供完整的参数验证和错误处理
- 添加了
- Create 静态工厂方法:
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用工厂方法模式创建实体实例
- 通过业务方法修改实体状态
- 与TestCaseFlow实体保持一致的实现模式
- 注意TestCaseNode和TestCaseEdge继承自Entity而非AuditableEntity,因此不包含审计字段
-
技术特性:
- 类型安全的参数验证
- 灵活的更新机制,支持部分字段更新
- 与TestCaseFlow实体保持一致的实现模式
- 提供合理的默认值,简化创建过程
- 完整的参数验证和错误处理
修改时间:
2025-01-19
修改原因:
用户要求TestCaseNode和TestCaseEdge实体提供与TestCaseFlow实体相同的Create和Update方法,确保所有测试用例相关实体的创建和更新过程标准化和一致性。
2025-01-19 - TestCaseNode Update 方法不可修改字段优化
修改文件:
X1.Domain/Entities/TestCase/TestCaseNode.cs - 优化TestCaseNode实体的Update方法
修改内容:
-
Update 方法参数优化:
- 移除了不可修改的字段参数:
testCaseId、nodeId、stepId - 这些字段作为实体的标识符和关联关系,在更新时不应该被修改
- 保留了可修改的字段:执行序号、位置坐标、尺寸、状态等
- 移除了不可修改的字段参数:
-
设计原则:
- 遵循实体不可变性原则,保护关键标识符
- 确保数据完整性和一致性
- 防止意外修改关联关系
-
技术特性:
- 更安全的更新机制
- 明确的字段修改边界
- 符合DDD设计原则
修改时间:
2025-01-19
修改原因:
用户反馈指出TestCaseNode的Update方法中,testCaseId、nodeId和stepId这些字段不应该被修改,因为它们是不可变的标识符和关联关系。
2025-01-19 - TestCaseEdge Update 方法不可修改字段优化
修改文件:
X1.Domain/Entities/TestCase/TestCaseEdge.cs - 优化TestCaseEdge实体的Update方法
修改内容:
-
Update 方法参数优化:
- 移除了不可修改的字段参数:
testCaseId、edgeId - 这些字段作为实体的标识符和关联关系,在更新时不应该被修改
- 保留了可修改的字段:源节点ID、目标节点ID、连线类型、条件、动画状态、样式等
- 移除了不可修改的字段参数:
-
设计原则:
- 遵循实体不可变性原则,保护关键标识符
- 确保数据完整性和一致性
- 防止意外修改关联关系
- 与TestCaseNode保持一致的不可变性设计
-
技术特性:
- 更安全的更新机制
- 明确的字段修改边界
- 符合DDD设计原则
- 与TestCaseNode实体的Update方法保持一致的实现模式
修改时间:
2025-01-19
修改原因:
用户反馈指出TestCaseEdge的Update方法中,testCaseId和edgeId这些字段不应该被修改,因为它们是不可变的标识符和关联关系。
2025-01-19 - GetTestCaseFlowByIdQueryHandler 修复
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdQueryHandler.cs
修改内容:
-
依赖注入增强:
- 添加
ICaseStepConfigRepository依赖,用于获取步骤配置信息 - 在构造函数中注入
caseStepConfigRepository参数
- 添加
-
数据映射重构:
- 新增
MapNodesToReactFlowFormatAsync方法:将TestCaseNode实体映射为 ReactFlow 兼容的TestCaseNodeDto - 新增
MapEdgesToReactFlowFormatAsync方法:将TestCaseEdge实体映射为 ReactFlow 兼容的TestCaseEdgeDto - 新增
GetStepTypeName方法:将步骤类型枚举转换为可读的字符串名称
- 新增
-
节点映射逻辑:
- 根据
StepId获取对应的CaseStepConfig信息 - 构建 ReactFlow 格式的
Position和Data对象 - 设置默认的
Type为 "testStep" - 处理
PositionAbsolute的可空逻辑 - 保留所有原有字段用于向后兼容
- 根据
-
连线映射逻辑:
- 解析存储的 JSON 样式字符串为
TestCaseEdgeStyleDto对象 - 设置默认的
SourceHandle和TargetHandle为 "bottom" 和 "top" - 设置默认的
Type为 "smoothstep" - 构建
Data对象包含条件信息 - 保留所有原有字段用于向后兼容
- 解析存储的 JSON 样式字符串为
-
错误处理:
- 添加 JSON 解析异常处理,在解析失败时使用默认样式
- 添加空值检查和默认值处理
技术特性:
- 异步处理:使用异步方法获取步骤配置信息,提高性能
- 数据完整性:确保所有必要字段都有合理的默认值
- 向后兼容:保留原有字段映射,确保现有功能不受影响
- 类型安全:使用强类型映射,避免运行时错误
- 错误恢复:在数据不完整时提供合理的默认值
映射关系:
TestCaseNode.NodeId→TestCaseNodeDto.IdTestCaseNode.PositionX/Y→TestCaseNodeDto.Position.X/YCaseStepConfig信息 →TestCaseNodeDto.DataTestCaseEdge.EdgeId→TestCaseEdgeDto.IdTestCaseEdge.SourceNodeId/TargetNodeId→TestCaseEdgeDto.Source/TargetTestCaseEdge.Style(JSON) →TestCaseEdgeDto.Style(对象)
修改时间:
2025-01-19
修改原因:
用户要求修复 GetTestCaseFlowByIdQueryHandler 来正确映射现有后端数据到新的 ReactFlow 兼容的 DTO 格式,同时保持向后兼容性,确保前端能够正确接收和显示测试用例流程数据。
2025-01-19 - GetTestCaseFlowByIdQueryHandler 简化映射
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdQueryHandler.cs
修改内容:
-
移除向后兼容字段:
- 在
MapNodesToReactFlowFormatAsync方法中移除了所有向后兼容字段 - 在
MapEdgesToReactFlowFormatAsync方法中移除了所有向后兼容字段 - 只保留 ReactFlow 前端需要的格式字段
- 在
-
节点映射简化:
- 只输出前端需要的 ReactFlow 格式:
Id,Type,Position,Data,Width,Height,Selected,PositionAbsolute,Dragging - 移除了:
TestCaseId,NodeId,SequenceNumber,PositionX,PositionY,IsSelected,PositionAbsoluteX,PositionAbsoluteY,IsDragging
- 只输出前端需要的 ReactFlow 格式:
-
连线映射简化:
- 只输出前端需要的 ReactFlow 格式:
Id,Source,SourceHandle,Target,TargetHandle,Type,Animated,Style,Data - 移除了:
TestCaseId,EdgeId,SourceNodeId,TargetNodeId,EdgeType,Condition,IsAnimated,StyleJson
- 只输出前端需要的 ReactFlow 格式:
技术特性:
- 前端优先:直接输出 ReactFlow 前端需要的格式
- 数据精简:移除不必要的向后兼容字段,减少数据传输量
- 格式统一:确保输出格式与前端期望的 JSON 结构完全一致
前端格式示例:
{
"nodes": [
{
"id": "node-1755654432101",
"type": "testStep",
"position": { "x": 360, "y": 30 },
"data": {
"stepId": "e2192f5a-1582-47e9-92be-c676679418da",
"stepName": "StartStep",
"stepType": 1,
"stepTypeName": "Start",
"description": "Mapping_Start",
"icon": "play-circle"
},
"width": 95,
"height": 30,
"selected": false,
"positionAbsolute": { "x": 360, "y": 30 },
"dragging": false
}
]
}
修改时间:
2025-01-19
修改原因:
用户要求简化映射逻辑,直接输出前端需要的 ReactFlow 格式,不需要向后兼容字段,确保数据格式与前端期望的 JSON 结构完全一致。
2025-01-19 - GetTestCaseFlowsQueryHandler 参数修复
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/GetTestCaseFlowsQueryHandler.cs
修改内容:
-
参数名称修复:
- 将
GetPagedFlowsAsync方法调用中的searchTerm参数名修正为name - 确保参数名称与
ITestCaseFlowRepository接口定义一致
- 将
-
类型转换修复:
- 添加了
TestFlowType枚举的解析逻辑 - 将
string?类型的request.Type转换为TestFlowType?类型 - 使用
Enum.TryParse进行安全的类型转换,避免解析失败
- 添加了
-
错误处理:
- 添加了空值检查,确保在
request.Type为空时不会进行解析 - 使用安全的枚举解析,避免无效类型值导致的异常
- 添加了空值检查,确保在
技术特性:
- 类型安全:确保参数类型与仓储接口定义完全匹配
- 错误处理:添加了安全的枚举解析,避免运行时异常
- 参数一致性:修正了参数名称,确保与接口定义一致
修复的问题:
GetPagedFlowsAsync方法调用时参数名称不匹配TestFlowType枚举类型转换缺失- 可能导致搜索功能无法正常工作
修改时间:
2025-01-19
修改原因:
用户反馈 GetTestCaseFlowsQueryHandler 中的 searchTerm 参数传递有问题,需要修复参数名称和类型转换问题,确保搜索功能正常工作。
2025-01-19 - TestCaseFlow 仓储模式实现和审计字段修复
修改文件:
X1.Domain/Repositories/TestCase/ITestCaseFlowRepository.cs- 创建 TestCaseFlow 仓储接口X1.Infrastructure/Repositories/TestCase/TestCaseFlowRepository.cs- 创建 TestCaseFlow 仓储实现并修复审计字段访问问题
修改内容:
-
TestCaseFlow 仓储接口创建:
- 创建了
ITestCaseFlowRepository接口,继承自IBaseRepository<TestCaseFlow> - 定义了完整的业务方法,包括基本的 CRUD 操作和特定的业务查询
- 支持测试用例流程的完整生命周期管理
- 创建了
-
主要业务方法:
- 基本操作:
AddTestCaseFlowAsync、UpdateTestCaseFlow、DeleteTestCaseFlowAsync - 状态管理:
EnableTestCaseFlowAsync、DisableTestCaseFlowAsync - 查询操作:
GetAllTestCaseFlowsAsync、GetTestCaseFlowByIdAsync、GetByNameAsync - 分类查询:
GetByTypeAsync、GetEnabledFlowsAsync - 详细查询:
GetTestCaseFlowWithDetailsAsync(包含节点和连线) - 验证操作:
NameExistsAsync - 分页查询:
GetPagedFlowsAsync(支持名称、类型、启用状态过滤)
- 基本操作:
-
TestCaseFlow 仓储实现创建:
- 创建了
TestCaseFlowRepository实现类,继承自BaseRepository<TestCaseFlow> - 实现了
ITestCaseFlowRepository接口的所有方法 - 使用 CQRS 模式,分离命令和查询操作
- 提供完整的日志记录和错误处理
- 创建了
-
审计字段修复:
- 问题:修复了 "属性或索引器'AuditableEntity.UpdatedAt'不能用在此上下文中,因为 set 访问器不可访问" 的编译错误
- 解决方案:参考
CaseStepConfigRepository的实现模式,简化仓储实现 - 修改内容:
- 移除了
ICurrentUserService依赖注入 - 移除了复杂的
SetCreated()和SetUpdated()方法调用 - 简化了
AddTestCaseFlowAsync方法,直接调用CommandRepository.AddAsync - 简化了
UpdateTestCaseFlow方法,直接调用CommandRepository.Update - 在
EnableTestCaseFlowAsync和DisableTestCaseFlowAsync方法中直接设置UpdatedAt = DateTime.UtcNow - 移除了详细的日志记录,保持与
CaseStepConfigRepository一致的简洁风格
- 移除了
-
技术特性:
- CQRS 模式:使用
ICommandRepository和IQueryRepository分离读写操作 - 异步支持:所有方法都支持异步操作和取消令牌
- 简洁实现:与现有仓储实现保持一致的简洁风格
- 性能优化:支持分页查询和条件过滤
- CQRS 模式:使用
-
分页查询功能:
- 支持按名称、类型、启用状态进行条件过滤
- 使用动态查询条件构建,支持可选参数
- 返回总记录数和分页数据
- 支持自定义页码和每页大小
-
详细查询功能:
GetTestCaseFlowWithDetailsAsync方法支持包含节点和连线的完整查询- 使用 Entity Framework 的
Include方法加载关联数据 - 适用于需要完整流程信息的场景
-
设计原则:
- DDD 原则:遵循领域驱动设计,仓储专注于数据访问
- 单一职责:每个方法专注于特定功能
- 可扩展性:支持未来功能扩展
- 一致性:与现有仓储实现(如
CaseStepConfigRepository)保持一致的架构模式
-
命名空间规范:
- 使用
X1.Domain.Repositories.TestCase命名空间 - 使用
X1.Infrastructure.Repositories.TestCase命名空间 - 与项目整体架构保持一致
- 使用
修改时间:
2025-01-19
修改原因:
用户要求在 CellularManagement.Domain.Repositories.TestCase 命名空间中实现 TestCaseFlow 的仓储模式,为测试用例流程管理提供完整的数据访问层支持,包括基本的 CRUD 操作和特定的业务查询功能。同时修复了审计字段访问权限问题,参考 CaseStepConfigRepository 的实现模式,简化仓储实现,确保与现有代码风格保持一致。
2025-01-19 - TestCaseTestFlow 命名规范和viewport属性修复
修改文件:
X1.Domain/Entities/TestCase/TestCaseTestFlow.cs- 重命名为TestCaseFlow并添加viewport属性X1.Domain/Entities/TestCase/TestCaseEdge.cs- 完善TestCaseEdge实体X1.Infrastructure/Context/AppDbContext.cs- 添加TestCaseFlow相关DbSet配置X1.Infrastructure/Configurations/TestCase/TestCaseFlowConfiguration.cs- 创建TestCaseFlow配置类X1.Infrastructure/Configurations/TestCase/TestCaseNodeConfiguration.cs- 创建TestCaseNode配置类X1.Infrastructure/Configurations/TestCase/TestCaseEdgeConfiguration.cs- 创建TestCaseEdge配置类
修改内容:
-
TestCaseTestFlow重命名为TestCaseFlow:
- 将类名从
TestCaseTestFlow改为TestCaseFlow,更符合命名规范 - 继承自
Entity基类,添加主键Id字段 - 添加viewport属性字段:ViewportX、ViewportY、ViewportZoom
- 设置viewport默认值:x=40.54057017483274, y=21.183463943747256, zoom=1.1367874248827994
- 将类名从
-
TestCaseEdge实体完善:
- 继承自
Entity基类,添加主键Id字段 - 添加完整的连线属性:TestCaseId、EdgeId、SourceNodeId、TargetNodeId
- 添加连线配置:EdgeType、Condition、IsAnimated、Style
- 添加导航属性关联TestCaseFlow
- 继承自
-
数据库配置:
- 在AppDbContext中添加TestCaseFlow、TestCaseNode、TestCaseEdge的DbSet配置
- 创建完整的Entity Framework配置类
- 配置表名规范:tb_testcaseflow、tb_testcasenode、tb_testcaseedge
- 添加完整的字段映射、索引和关系配置
-
技术特性:
- 命名规范:使用更简洁的TestCaseFlow命名
- 主键支持:所有实体都继承Entity基类,支持主键Id
- viewport支持:添加视口坐标和缩放级别属性
- 关系配置:完整的实体关系映射和级联删除配置
- 索引优化:为常用查询字段添加数据库索引
-
设计原则:
- 命名简洁:TestCaseFlow比TestCaseTestFlow更简洁明了
- 功能完整:支持完整的测试用例流程管理
- 数据完整性:通过外键关系和级联删除确保数据一致性
- 性能优化:通过索引配置提高查询性能
修改时间:
2025-01-19
修改原因:
用户要求修复TestCaseTestFlow的命名规范问题,主要是测试用例流程表,并添加viewport属性字段(x、y、zoom),以支持ReactFlow设计器的视口状态保存和恢复。
2025-01-19 - AdbOperationConfiguration数据库配置优化
修改文件:
X1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.cs - 修复Path字段的数据库约束
修改内容:
-
问题描述:
- 当前AdbOperationConfiguration中Path字段设置为
IsRequired(),与业务逻辑不一致 - 根据AdbOperation实体的业务规则,Path字段只有在UseAbsolutePath为true时才必填
- 数据库约束应该与业务逻辑保持一致
- 当前AdbOperationConfiguration中Path字段设置为
-
解决方案:
- 将Path字段的数据库约束从
IsRequired()改为IsRequired(false) - 更新字段注释,明确说明Path字段的必填条件
- 确保数据库层约束与业务层验证逻辑一致
- 将Path字段的数据库约束从
-
具体修改:
- 数据库约束:
IsRequired()→IsRequired(false) - 注释更新:添加"(当启用绝对路径时必填)"说明
- 业务一致性:与AdbOperation实体中的条件验证保持一致
- 数据库约束:
-
技术特性:
- 数据完整性:数据库约束与业务规则一致
- 灵活性:允许Path字段为空,符合业务需求
- 文档清晰:注释明确说明字段的使用条件
- 架构一致性:基础设施层与领域层保持一致
-
修改代码:
// 路径字段 builder.Property(x => x.Path) .IsRequired(false) // 改为允许为空 .HasMaxLength(500) .HasComment("命令执行时所依赖的路径(当启用绝对路径时必填)"); -
设计原则:
- 业务驱动:数据库设计服务于业务需求
- 一致性:各层之间的约束保持一致
- 灵活性:支持不同的业务场景
- 可维护性:清晰的注释便于理解和维护
修改时间:
2025-01-19
修改原因:
确保AdbOperationConfiguration的数据库约束与AdbOperation实体的业务逻辑保持一致,Path字段只有在UseAbsolutePath为true时才必填。
2025-01-19 - AdbOperations Commands层DeviceId修改限制
修改文件:
X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommand.cs - 移除DeviceId属性
X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs - 修复DeviceId处理逻辑
修改内容:
-
问题描述:
- UpdateAdbOperationCommand中仍然包含DeviceId属性,与实体层业务规则不一致
- UpdateAdbOperationCommandHandler仍然传递DeviceId参数给实体的Update方法
- 应用层与领域层的业务规则不一致,可能导致混淆
-
解决方案:
- 从UpdateAdbOperationCommand中移除DeviceId属性
- 修改UpdateAdbOperationCommandHandler,使用现有实体的DeviceId值
- 确保应用层与领域层的业务规则保持一致
-
具体修改:
- 命令对象:移除DeviceId属性,明确表示Update操作不涉及DeviceId修改
- 处理器逻辑:使用
existingOperation.DeviceId而不是request.DeviceId - 日志优化:移除日志中的DeviceId参数,避免误导
- 业务一致性:应用层与领域层规则完全一致
-
技术特性:
- 架构一致性:应用层与领域层业务规则保持一致
- 数据完整性:防止通过应用层意外修改DeviceId
- 代码清晰:明确表达Update操作的业务约束
- 错误预防:在应用层就避免传递不允许修改的参数
-
修改代码:
// UpdateAdbOperationCommand.cs - 移除DeviceId属性 public class UpdateAdbOperationCommand : IRequest<OperationResult<UpdateAdbOperationResponse>> { public string Id { get; set; } = string.Empty; // DeviceId属性已移除 public string Command { get; set; } = string.Empty; // ... 其他属性 } // UpdateAdbOperationCommandHandler.cs - 修复DeviceId处理 existingOperation.Update( existingOperation.DeviceId, // 使用现有的DeviceId,不允许修改 request.Command, // ... 其他参数 ); -
设计原则:
- 单一职责:Update命令只处理允许修改的字段
- 业务驱动:应用层设计服务于业务规则
- 防御性编程:在多个层面防止数据不一致
- 可维护性:清晰的代码结构便于理解和维护
修改时间:
2025-01-19
修改原因:
确保AdbOperations Commands层的Update操作与AdbOperation实体的业务规则保持一致,防止DeviceId被意外修改,维护数据完整性和业务逻辑的一致性。
2025-01-19 - AdbOperation实体DeviceId更新限制
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.cs - 限制Update方法中DeviceId的修改
修改内容:
-
问题描述:
- 当前Update方法允许修改DeviceId,这可能导致数据一致性问题
- 用户要求AdbOperation的Update操作不能修改deviceId
- 需要添加业务规则限制DeviceId的修改
-
解决方案:
- 在Update方法中添加DeviceId修改验证
- 比较传入的deviceId参数与当前DeviceId属性值
- 如果不匹配则抛出异常,阻止更新操作
- 移除DeviceId的赋值操作,保持原有值
-
具体修改:
- 验证逻辑:添加
if (!string.Equals(DeviceId, deviceId.Trim(), StringComparison.OrdinalIgnoreCase))检查 - 错误处理:抛出
ArgumentException("设备ID不允许修改", nameof(deviceId)) - 赋值移除:注释掉
DeviceId = deviceId.Trim();并添加说明注释
- 验证逻辑:添加
-
技术特性:
- 数据完整性:确保DeviceId在更新操作中保持不变
- 业务规则:实现业务逻辑约束
- 错误提示:提供明确的错误信息
- 大小写不敏感:使用
StringComparison.OrdinalIgnoreCase进行比较
-
验证逻辑:
// 设备ID不允许修改 if (!string.Equals(DeviceId, deviceId.Trim(), StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("设备ID不允许修改", nameof(deviceId)); // 不更新DeviceId,保持原有值 // DeviceId = deviceId.Trim(); // 已移除 -
设计原则:
- 数据一致性:防止关键字段被意外修改
- 业务约束:符合ADB操作的业务规则
- 用户友好:提供清晰的错误提示
- 安全性:防止数据完整性问题
修改时间:
2025-01-19
修改原因:
用户要求AdbOperation的Update操作不能修改deviceId,确保数据一致性和业务规则的正确性。
2025-01-19 - AdbOperation实体路径验证优化
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.cs - 优化路径验证逻辑,实现条件验证
修改内容:
-
问题描述:
- 当前路径验证逻辑过于严格,无论是否启用绝对路径都要求路径不能为空
- 用户希望只有当
UseAbsolutePath启用时,Path字段才不能为空 - 需要实现条件验证逻辑
-
解决方案:
- 修改
Create和Update方法中的路径验证逻辑 - 添加条件判断:只有当
useAbsolutePath为true时,才验证路径不能为空 - 更新错误消息以明确说明验证条件
- 修改
-
具体修改:
- Create方法:将
if (string.IsNullOrWhiteSpace(path))改为if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) - Update方法:将
if (string.IsNullOrWhiteSpace(path))改为if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) - 错误消息:从"路径不能为空"改为"启用绝对路径时,路径不能为空"
- Create方法:将
-
技术特性:
- 条件验证:根据业务逻辑实现智能验证
- 用户体验:允许在非绝对路径模式下路径为空
- 业务逻辑:符合ADB操作的业务需求
- 错误提示:提供更明确的错误信息
-
验证逻辑:
// 修改前 if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("路径不能为空", nameof(path)); // 修改后 if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) throw new ArgumentException("启用绝对路径时,路径不能为空", nameof(path)); -
设计原则:
- 业务导向:验证逻辑符合实际业务需求
- 用户友好:提供清晰的错误提示
- 灵活性:支持不同的路径使用模式
- 一致性:在创建和更新操作中保持一致的验证逻辑
修改时间:
2025-01-19
修改原因:
用户反馈需要实现条件验证,只有当启用绝对路径时路径字段才不能为空,提高业务逻辑的灵活性。
2025-01-19 - ADB操作Drawer布局优化
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 优化Drawer布局,添加滚动条支持
修改内容:
-
问题描述:
- 当ADB命令列表增加时,Drawer布局无法正常显示所有内容
- 缺少滚动条支持,用户体验不佳
- 布局结构需要优化以支持动态内容
-
解决方案:
- 重新设计布局结构,将内容分为固定区域和可滚动区域
- 为命令列表区域添加独立的滚动条
- 使用Flexbox布局确保各区域正确显示
-
具体修改:
- 固定区域:设备选择、操作描述、ADB路径、选项设置等
- 可滚动区域:命令列表区域,支持垂直滚动
- 布局优化:使用
flex-shrink-0和flex-1控制区域大小
-
技术特性:
- 响应式滚动:命令列表区域支持垂直滚动
- 固定布局:重要操作区域保持固定位置
- 视觉优化:添加背景色和边框改善视觉效果
- 用户体验:支持任意数量的命令添加
-
布局结构:
// 固定头部 <DrawerHeader className="flex-shrink-0"> // 内容区域 <DrawerContent className="flex flex-col flex-1 min-h-0 p-0 overflow-hidden"> // 固定区域:设备选择 <div className="flex-shrink-0 p-4 pb-2"> // 可滚动区域:命令列表 <div className="flex-1 min-h-0 flex flex-col px-4"> <div className="flex-1 overflow-y-auto"> <div className="space-y-3 pr-2"> // 固定区域:其他表单字段 <div className="flex-shrink-0 p-4 pt-2"> // 固定底部 <DrawerFooter className="flex-shrink-0"> -
设计原则:
- 空间利用:合理分配固定和可滚动区域
- 操作便利:重要操作区域保持可见
- 扩展性:支持动态添加命令
- 一致性:与其他Drawer组件保持一致的交互模式
修改时间:
2025-01-19
修改原因:
用户反馈当添加多个ADB命令时,Drawer布局无法正常显示,需要添加滚动条优化布局。
2025-01-19 - ADB操作Drawer空白优化
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 优化布局空白,减少多余间距
修改内容:
-
问题描述:
- 用户反馈布局存在"多余空白"问题
- 内边距和外边距重叠导致空间浪费
- 滚动区域与固定区域间距过大
-
解决方案:
- 重新调整内边距和外边距结构
- 优化各区域之间的间距
- 减少不必要的空白区域
-
具体修改:
- DrawerContent:移除
p-4,改为p-0 - 设备选择区域:使用
p-4 pb-2替代mb-4 - 命令列表区域:使用
px-4替代整体内边距 - 滚动区域:移除
pr-2,改为在内容区域使用pr-2 - 其他表单字段:使用
p-4 pt-2替代mt-4
- DrawerContent:移除
-
技术特性:
- 紧凑布局:减少不必要的空白区域
- 精确控制:为不同区域设置合适的内边距
- 视觉优化:改善整体布局的紧凑性
- 用户体验:提供更好的空间利用率
-
布局优化:
// 优化前:存在多余空白 <DrawerContent className="p-4"> <div className="mb-4">设备选择</div> <div className="flex-1"> <div className="overflow-y-auto pr-2"> <div className="mt-4">其他字段</div> // 优化后:紧凑布局 <DrawerContent className="p-0"> <div className="p-4 pb-2">设备选择</div> <div className="flex-1 px-4"> <div className="overflow-y-auto"> <div className="pr-2"> <div className="p-4 pt-2">其他字段</div> -
设计原则:
- 空间效率:最大化利用可用空间
- 视觉平衡:保持适当的间距和层次
- 操作便利:确保重要操作区域易于访问
- 一致性:与其他组件保持一致的间距规范
修改时间:
2025-01-19
修改原因:
用户反馈布局存在"多余空白"问题,需要优化间距结构,提供更紧凑的布局。
2025-01-19 - ADB操作路径字段条件验证
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 实现ADB路径字段的条件验证
修改内容:
-
问题描述:
- 用户反馈ADB路径字段的验证逻辑需要优化
- 当前路径字段始终为可选,但实际使用中需要条件验证
- 当选择"使用绝对路径"时,路径字段应该为必填
-
解决方案:
- 实现条件验证逻辑,根据"使用绝对路径"选项决定路径字段是否必填
- 动态显示必填标识符
- 添加错误提示和样式
-
具体修改:
- 验证逻辑:在
validateForm函数中添加条件验证 - UI标识:动态显示红色星号标识必填字段
- 错误处理:添加路径字段的错误提示和样式
- 用户体验:提供清晰的验证反馈
- 验证逻辑:在
-
技术特性:
- 条件验证:根据
useAbsolutePath状态决定验证规则 - 动态UI:必填标识符根据条件动态显示
- 错误反馈:提供明确的错误信息和视觉提示
- 表单完整性:确保数据验证的准确性
- 条件验证:根据
-
验证逻辑:
// 验证ADB路径 - 只有在使用绝对路径时才必填 if (formData.useAbsolutePath && !formData.path.trim()) { newErrors.path = '使用绝对路径时必须指定ADB路径'; } -
UI优化:
<Label htmlFor="path" className="text-sm font-medium"> ADB路径 {formData.useAbsolutePath && <span className="text-red-500">*</span>} </Label> <Input className={errors.path ? 'border-red-500 focus:border-red-500' : ''} /> {errors.path && ( <p className="text-xs text-red-500">{errors.path}</p> )} -
设计原则:
- 逻辑一致性:验证规则与业务逻辑保持一致
- 用户友好:提供清晰的必填标识和错误提示
- 视觉反馈:使用颜色和样式区分不同状态
- 数据完整性:确保提交数据的有效性
修改时间:
2025-01-19
修改原因:
用户反馈ADB路径字段需要条件验证,只有在选择"使用绝对路径"时才应该为必填字段。
2025-01-19 - ADB操作Drawer单命令布局优化
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 优化单命令情况下的布局空白
修改内容:
-
问题描述:
- 用户反馈"当添加命令默认一个就会剩余很多空白"
- 当只有一个命令时,命令列表区域占用过多空间
- 布局在单命令情况下不够紧凑,存在大量空白区域
-
解决方案:
- 根据命令数量动态调整布局结构
- 当命令数量较少时,不使用弹性布局占用剩余空间
- 只有当命令数量超过2个时才启用滚动区域
-
具体修改:
- 条件布局:根据
commands.length > 2决定是否使用弹性布局 - 动态类名:使用模板字符串动态设置CSS类名
- 滚动优化:只在需要时启用滚动条
- 空间利用:减少单命令情况下的空白区域
- 条件布局:根据
-
技术特性:
- 自适应布局:根据内容数量调整布局策略
- 条件渲染:动态应用CSS类名
- 空间优化:减少不必要的空白区域
- 用户体验:提供更紧凑的单命令布局
-
布局逻辑:
// 命令列表区域 - 根据命令数量决定布局 <div className={`px-4 ${commands.length > 2 ? 'flex-1 min-h-0 flex flex-col' : ''}`}> // 命令列表区域 - 根据命令数量决定是否可滚动 <div className={commands.length > 2 ? 'flex-1 overflow-y-auto' : ''}> <div className={`space-y-3 ${commands.length > 2 ? 'pr-2' : ''}`}> -
设计原则:
- 内容驱动:布局根据实际内容数量调整
- 空间效率:避免不必要的空白区域
- 渐进增强:随着内容增加自动启用滚动
- 视觉平衡:保持整体布局的协调性
修改时间:
2025-01-19
修改原因:
用户反馈当只有一个命令时,布局存在大量空白区域,需要优化单命令情况下的布局紧凑性。
2025-01-19 - 抽屉组件内间距优化
修改文件:
X1.WebUI/src/pages/ran-configurations/RANConfigurationDrawer.tsx- 为抽屉内容添加内间距X1.WebUI/src/pages/network-stack-configs/NetworkStackConfigDrawer.tsx- 为抽屉内容添加内间距X1.WebUI/src/pages/ims-configurations/IMSConfigurationDrawer.tsx- 为抽屉内容添加内间距X1.WebUI/src/pages/core-network-configs/CoreNetworkConfigDrawer.tsx- 为抽屉内容添加内间距
修改内容:
-
问题描述:
- RANConfigurationDrawer 抽屉内容太贴着边缘,缺少适当的内间距
- 用户体验不佳,内容显示过于紧凑
-
解决方案:
- 为
DrawerContent组件添加p-6类名 - 提供 24px 的内边距,改善视觉效果和用户体验
- 为
-
具体修改:
// 修改前 <DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto"> // 修改后 <DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto p-4"> -
技术特性:
- 响应式设计:内间距适配不同屏幕尺寸
- 视觉优化:提供更好的内容层次和可读性
- 用户体验:改善表单内容的显示效果
- 一致性:与其他抽屉组件保持一致的样式
-
设计原则:
- 空间利用:合理利用抽屉空间,避免内容过于紧凑
- 视觉层次:通过内间距建立清晰的内容层次
- 用户友好:提供舒适的视觉体验
修改时间:
2025-01-19
修改原因:
用户反馈 RANConfigurationDrawer 内容太贴着边缘,需要添加适当的内间距来改善视觉效果和用户体验。
2025-01-19 - ReactFlowDesigner 导入导出功能实现
修改文件:
X1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx - 实现Flow数据的导入和导出功能
修改内容:
-
导入功能实现:
- 文件选择:添加了文件选择按钮,支持选择JSON格式的Flow文件
- 数据验证:验证导入文件是否包含有效的nodes和edges数据
- 状态恢复:导入成功后恢复nodes、edges和viewport状态
- 错误处理:提供详细的错误提示和异常处理
- 重复导入:支持重复导入同一文件
-
导出功能实现:
- 数据收集:收集当前的nodes、edges和viewport状态
- 元数据添加:添加版本、导出时间、描述等元数据信息
- 文件生成:生成格式化的JSON文件
- 自动下载:自动触发文件下载,文件名包含日期信息
- 资源清理:自动清理临时创建的URL对象
-
UI组件设计:
- 工具栏按钮:在工具栏中添加导入和导出按钮
- 图标设计:使用SVG图标,导入使用向下箭头,导出使用向上箭头
- 悬停效果:导入按钮使用蓝色主题,导出按钮使用绿色主题
- 响应式设计:按钮适配不同屏幕尺寸
-
成功提示功能:
- 通知组件:添加导入成功通知,显示在右上角
- 动画效果:使用slideIn动画,从右侧滑入
- 自动关闭:提供关闭按钮,点击后隐藏通知
- 状态管理:使用useState管理通知显示状态
-
样式设计:
- 按钮样式:使用现代化的按钮设计,包含图标和文字
- 通知样式:使用绿色主题的成功通知样式
- 动画效果:添加CSS动画和过渡效果
- 响应式布局:适配不同屏幕尺寸
-
技术特性:
- 文件处理:使用FileReader API读取本地文件
- 数据序列化:使用JSON.stringify格式化数据
- Blob处理:使用Blob和URL.createObjectURL创建下载链接
- 状态管理:使用React Hooks管理组件状态
- 错误处理:完整的异常捕获和用户提示
-
数据格式:
{ "nodes": [...], "edges": [...], "viewport": {...}, "metadata": { "name": "Test Case Flow", "version": "1.0", "exportDate": "2025-01-19T10:30:00.000Z", "description": "Exported test case flow data" } } -
用户体验:
- 直观操作:点击按钮即可导入或导出Flow数据
- 即时反馈:操作结果立即显示,成功或失败都有明确提示
- 文件命名:导出文件自动包含日期信息,便于管理
- 错误提示:详细的错误信息帮助用户理解问题
修改时间:
2025-01-19
修改原因:
用户需要在测试用例流程设计器中实现Flow数据的导入和导出功能,以便保存和恢复流程设计,提高工作效率和数据管理能力。
修复记录:
- 2025-01-19:修复了导出功能中的Hook调用错误,将
useReactFlow()调用移到组件顶层,避免在普通函数中调用Hook
2025-01-19 - AdbOperation数据库迁移应用
修改文件:
数据库 tb_adboperations 表结构
修改内容:
-
执行状态:
- ✅ 迁移文件创建:成功
- ✅ 数据库更新:成功
- ✅ 数据库结构与代码配置:完全一致
-
数据库变更:
- 表名:
tb_adboperations - 字段名:
Path - 变更类型:从
NOT NULL改为NULL(允许为空) - 注释更新:从"命令执行时所依赖的路径"改为"命令执行时所依赖的路径(当启用绝对路径时必填)"
- 表名:
-
执行的SQL命令:
ALTER TABLE tb_adboperations ALTER COLUMN "Path" DROP NOT NULL; COMMENT ON COLUMN tb_adboperations."Path" IS '命令执行时所依赖的路径(当启用绝对路径时必填)'; -
迁移历史:
- 迁移ID:
20250820020118_UpdateAdbOperationPathNullable - 已记录到
__EFMigrationsHistory表
- 迁移ID:
-
业务影响:
- 现在
Path字段在UseAbsolutePath为false时可以为空 - 在
UseAbsolutePath为true时仍然必填(由业务逻辑验证) - 数据库约束与领域层业务规则完全一致
- 现在
-
技术验证:
- 构建成功:
Build succeeded - 数据库连接正常:配置验证通过
- 迁移执行无错误:所有SQL命令成功执行
- 构建成功:
修改时间:
2025-01-19
修改原因:
应用之前创建的数据库迁移,将AdbOperationConfiguration中Path字段的IsRequired约束变更同步到数据库,确保数据库结构与代码配置完全一致,支持Path字段在UseAbsolutePath为false时可以为空的业务需求。
2025-01-19 - TestCaseEdge Type字段必填验证
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 将EdgeData的Type字段改为必填X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 添加Type字段验证并移除默认值逻辑
修改内容:
-
EdgeData Type字段必填化:
- 问题描述:用户反馈
edgeType: edgeData.Type ?? "straight"中的Type字段应该是必填项,界面不能传空 - 解决方案:将Type字段从可选改为必填,添加验证逻辑,移除默认值处理
- 问题描述:用户反馈
-
CreateTestCaseFlowCommand.cs 修改:
- 字段类型:将
Type从string?改为string - 验证特性:添加
[Required(ErrorMessage = "连线类型不能为空")]验证特性 - 默认值:移除默认值,设置为
null!
- 字段类型:将
-
CreateTestCaseFlowCommandHandler.cs 修改:
- 验证逻辑:在
ValidateRequest方法中添加对edge.Type的空值验证 - 创建逻辑:在
CreateEdgesAsync方法中移除?? "straight"默认值逻辑 - 错误处理:提供明确的错误信息:"连线类型不能为空"
- 验证逻辑:在
-
技术特性:
- 数据验证:确保连线类型字段不为空
- 用户友好:提供清晰的错误提示信息
- 业务逻辑:符合业务需求,连线类型必须明确指定
- 类型安全:使用强类型验证,避免运行时错误
-
设计原则:
- 数据完整性:确保必要字段不为空
- 用户体验:提供明确的验证反馈
- 业务规则:符合测试用例流程设计的业务需求
- 代码一致性:与项目中其他必填字段的处理方式保持一致
修改时间:
2025-01-19
修改原因:
用户要求确保TestCaseEdge的Type字段是必填项,界面不能传空值,需要添加相应的验证逻辑并移除默认值处理,确保数据完整性和业务逻辑的正确性。
2025-01-16 测试用例列表组件创建
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 创建测试用例列表组件X1.WebUI/src/routes/AppRouter.tsx- 更新路由配置
修改内容:
-
问题描述:
testcases/list和testcases/create路由都使用了同一个组件TestCasesViewTestCasesView是一个流程设计器,用于创建测试用例testcases/list应该显示一个测试用例列表的表格
-
解决方案:
- 创建了新的
TestCasesListView组件,专门用于显示测试用例列表 - 更新路由配置,让
testcases/list使用新的列表组件 - 保持
testcases/create继续使用原有的流程设计器组件
- 创建了新的
-
TestCasesListView组件特性:
- 表格显示:使用表格组件显示测试用例列表
- 搜索功能:支持按测试用例名称或描述搜索
- 状态管理:显示测试用例的状态(激活、停用、草稿)
- 优先级管理:显示测试用例的优先级(高、中、低)
- 操作按钮:提供查看、编辑、删除操作
- 创建按钮:提供创建新测试用例的快捷入口
-
路由配置更新:
- 添加了
TestCasesListView组件的lazy加载导入 - 更新
testcases/list路由使用新的列表组件 - 保持
testcases/create路由使用原有的流程设计器组件
- 添加了
-
技术特性:
- 使用React + TypeScript开发
- 采用shadcn/ui组件库
- 支持响应式设计
- 完整的错误处理和用户反馈
- 模拟数据展示功能
-
UI设计:
- 现代化的表格设计
- 状态徽章和优先级徽章
- 搜索工具栏
- 操作按钮图标化
- 响应式布局
修改时间:
2025-01-16
修改原因:
解决测试用例列表和创建页面显示相同内容的问题,为测试用例管理提供正确的列表视图和创建视图分离。
2025-01-19 - 测试用例列表抽屉查看功能增强
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx - 添加抽屉式流程查看功能
修改内容:
-
功能增强:
- 添加了抽屉组件来显示测试用例流程详情
- 集成了ReactFlow来可视化显示流程节点和连线
- 实现了异步加载流程数据的功能
- 添加了加载状态指示器
-
抽屉组件特性:
- Drawer组件:使用shadcn/ui的Drawer组件
- 高度设置:抽屉高度为90vh,提供充足的显示空间
- 标题显示:显示测试用例名称和描述
- 响应式设计:适配不同屏幕尺寸
-
ReactFlow集成:
- 流程可视化:使用ReactFlow显示流程节点和连线
- 控件支持:包含Controls(缩放、适应视图)、Background(网格背景)、MiniMap(小地图)
- 自动适应:使用fitView自动适应视图大小
- 样式设置:使用灰色背景提升视觉效果
-
数据转换功能:
- 节点转换:将后端节点数据转换为ReactFlow节点格式
- 连线转换:将后端连线数据转换为ReactFlow连线格式
- 位置信息:支持节点的位置、尺寸、选中状态等信息
- 样式支持:支持连线的样式、动画、数据等属性
-
状态管理:
- 选中状态:
selectedTestCase存储当前选中的测试用例详情 - 抽屉状态:
drawerOpen控制抽屉的打开/关闭 - 加载状态:
flowLoading控制流程数据的加载状态 - 错误处理:完整的错误处理和用户提示
- 选中状态:
-
用户体验优化:
- 加载指示器:显示旋转动画和"加载流程中..."提示
- 按钮状态:查看按钮在加载时禁用,防止重复点击
- 错误提示:当加载失败时显示"未找到流程数据"
- 交互反馈:点击查看按钮立即显示加载状态
-
技术实现:
- 异步加载:使用
handleViewTestCase函数异步加载流程详情 - 数据获取:调用
testcaseService.getTestCaseFlowById获取完整流程数据 - 类型安全:使用TypeScript确保类型安全
- 组件导入:正确导入ReactFlow相关组件和样式
- 异步加载:使用
-
流程数据处理:
const getReactFlowData = () => { if (!selectedTestCase) return { nodes: [], edges: [] }; const nodes: Node[] = selectedTestCase.nodes.map(node => ({ id: node.id, type: node.type || 'default', position: node.position, data: node.data, width: node.width, height: node.height, selected: node.selected, positionAbsolute: node.positionAbsolute, dragging: node.dragging })); const edges: Edge[] = selectedTestCase.edges.map(edge => ({ id: edge.id, source: edge.source, sourceHandle: edge.sourceHandle, target: edge.target, targetHandle: edge.targetHandle, type: edge.type || 'smoothstep', animated: edge.animated, style: edge.style, data: edge.data })); return { nodes, edges }; };
修改时间:
2025-01-19
修改原因:
用户要求在查看测试用例时采用抽屉方式显示流程,并加载处理流程数据,提供更好的用户体验和流程可视化能力。
2025-01-19 - 测试用例详情抽屉组件提取
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx- 创建独立的测试用例详情抽屉组件X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 重构使用独立抽屉组件
修改内容:
-
组件提取:
- 将测试用例详情抽屉功能从
TestCasesListView中提取为独立组件 - 创建了
TestCaseDetailDrawer组件,位于@/components/testcases/目录 - 实现了组件的复用性和代码累计
- 将测试用例详情抽屉功能从
-
TestCaseDetailDrawer组件特性:
- Props接口:定义了
testCaseId、open、onOpenChange三个属性 - 状态管理:内部管理
selectedTestCase、flowLoading、error状态 - 生命周期管理:使用
useEffect监听抽屉打开状态,自动加载数据 - 错误处理:完整的错误状态管理和重试功能
- Props接口:定义了
-
功能增强:
- 自动加载:抽屉打开时自动加载测试用例详情
- 状态清理:抽屉关闭时自动清理状态
- 错误重试:提供重试按钮,用户可以重新加载数据
- 加载指示器:显示旋转动画和加载提示
-
TestCasesListView重构:
- 简化状态:移除了
selectedTestCase、flowLoading等状态 - 简化逻辑:
handleViewTestCase函数简化为只设置ID和打开抽屉 - 组件使用:使用
<TestCaseDetailDrawer>组件替代原有的抽屉代码 - 代码减少:大幅减少了组件代码量,提高可维护性
- 简化状态:移除了
-
技术特性:
- 组件复用:抽屉组件可以在其他页面中复用
- 职责分离:列表页面专注于列表显示,抽屉组件专注于详情显示
- 类型安全:完整的TypeScript类型定义
- 错误边界:独立的错误处理,不影响主页面
-
用户体验优化:
- 响应式设计:抽屉高度为90vh,适配不同屏幕
- 加载反馈:清晰的加载状态指示
- 错误恢复:提供重试机制,提升用户体验
- 状态同步:抽屉状态与父组件完全同步
-
代码结构:
// TestCaseDetailDrawer.tsx interface TestCaseDetailDrawerProps { testCaseId: string | null; open: boolean; onOpenChange: (open: boolean) => void; } // TestCasesListView.tsx const [selectedTestCaseId, setSelectedTestCaseId] = useState<string | null>(null); const [drawerOpen, setDrawerOpen] = useState(false); const handleViewTestCase = (id: string) => { setSelectedTestCaseId(id); setDrawerOpen(true); }; -
设计原则:
- 单一职责:每个组件专注于特定功能
- 可复用性:抽屉组件可以在多个地方使用
- 可维护性:代码结构清晰,易于维护和扩展
- 性能优化:按需加载,减少不必要的渲染
修改时间:
2025-01-19
修改原因:
用户要求将测试用例详情抽屉提取为独立组件,实现代码累计和复用,提高代码的可维护性和组件的可复用性。
2025-01-19 - Drawer组件缺失导出修复
修改文件:
X1.WebUI/src/components/ui/drawer.tsx - 添加缺失的DrawerTitle和DrawerDescription组件
修改内容:
-
问题描述:
TestCaseDetailDrawer组件导入DrawerDescription和DrawerTitle时出现错误- 错误信息:
The requested module '/src/components/ui/drawer.tsx' does not provide an export named 'DrawerDescription' - drawer.tsx 文件中缺少这两个组件的定义和导出
-
解决方案:
- 在 drawer.tsx 中添加了
DrawerTitle组件 - 在 drawer.tsx 中添加了
DrawerDescription组件 - 为两个组件添加了完整的 TypeScript 接口定义
- 在 drawer.tsx 中添加了
-
DrawerTitle组件:
- 接口定义:
DrawerTitleProps包含children和className属性 - 样式设计:使用
text-lg font-semibold类名,提供标题样式 - HTML结构:使用
<h2>标签,符合语义化设计
- 接口定义:
-
DrawerDescription组件:
- 接口定义:
DrawerDescriptionProps包含children和className属性 - 样式设计:使用
text-sm text-muted-foreground类名,提供描述文本样式 - HTML结构:使用
<p>标签,符合语义化设计
- 接口定义:
-
技术特性:
- 类型安全:完整的 TypeScript 接口定义
- 样式一致性:使用
cn工具函数合并类名 - 可扩展性:支持自定义 className 属性
- 语义化:使用合适的 HTML 标签
-
组件结构:
interface DrawerTitleProps { children: React.ReactNode className?: string } export function DrawerTitle({ children, className }: DrawerTitleProps) { return ( <h2 className={cn("text-lg font-semibold", className)}> {children} </h2> ) } interface DrawerDescriptionProps { children: React.ReactNode className?: string } export function DrawerDescription({ children, className }: DrawerDescriptionProps) { return ( <p className={cn("text-sm text-muted-foreground", className)}> {children} </p> ) } -
设计原则:
- 一致性:与其他 Drawer 组件保持一致的接口设计
- 可复用性:组件可以在其他抽屉组件中复用
- 可维护性:清晰的代码结构和类型定义
- 用户体验:提供合适的默认样式
修改时间:
2025-01-19
修改原因:
修复 drawer.tsx 组件库中缺失的 DrawerTitle 和 DrawerDescription 组件导出,解决 TestCaseDetailDrawer 组件的导入错误问题。
2025-01-19 - TestCaseDetailDrawer 样式与 ReactFlowDesigner 保持一致
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 参考 ReactFlowDesigner 添加自定义节点样式
修改内容:
-
样式一致性要求:
- 用户要求 TestCaseDetailDrawer 中的 ReactFlow 样式与 ReactFlowDesigner 保持完全一致
- 需要添加自定义节点类型和视觉效果
-
自定义节点组件:
- TestStepNode 组件:完全复制 ReactFlowDesigner 中的自定义节点组件
- 图标组件:支持所有图标类型(Play、Square、GitBranch、Smartphone等)
- 节点样式:根据步骤类型显示不同的形状和颜色
- 连接点:根据节点类型显示不同的输入输出连接点
-
节点样式规则:
- 开始步骤 (type=1):圆形,蓝色主题,只有输出连接点
- 结束步骤 (type=2):圆形,红色主题,只有输入连接点
- 处理步骤 (type=3):矩形,绿色主题,有输入和输出连接点
- 判断步骤 (type=4):菱形,紫色主题,有输入和输出连接点
-
图标系统:
- 设备相关图标:蓝色背景(smartphone、phone、wifi等)
- 网络相关图标:绿色背景(network、activity)
- 控制相关图标:橙色背景(play-circle、stop-circle)
- 配置相关图标:紫色背景(settings、git-branch)
-
ReactFlow 配置:
- 节点类型:使用自定义
testStep节点类型 - 视图设置:默认缩放1.5,最小1倍,最大2倍
- 网格对齐:启用15x15网格对齐
- 控件:包含 Controls、Background、MiniMap
- 节点类型:使用自定义
-
技术特性:
- ReactFlowProvider:包装组件以支持 useReactFlow hook
- fitView:自动适应视图大小
- 延迟渲染:使用 setTimeout 确保节点正确渲染后再执行 fitView
- 状态管理:完整的加载、错误、数据状态管理
-
视觉效果:
- 选中状态:蓝色环形高亮
- 悬停效果:边框颜色变化
- 连接点:彩色圆形连接点,支持悬停效果
- 背景:灰色背景,与 ReactFlowDesigner 一致
-
组件结构:
// 自定义节点组件 const TestStepNode = ({ data, selected }: { data: any; selected?: boolean }) => { // 图标组件映射 const getIconComponent = (iconName: string) => { ... }; // 节点样式映射 const getNodeStyle = (stepType: number) => { ... }; // 图标背景色映射 const getIconBgColor = (iconName: string) => { ... }; // 渲染不同形状的节点 return ( <div className={`group relative transition-all duration-200`}> {/* 根据步骤类型渲染不同形状 */} {/* 根据步骤类型渲染不同连接点 */} </div> ); };
修改时间:
2025-01-19
修改原因:
用户要求 TestCaseDetailDrawer 中的 ReactFlow 样式与 ReactFlowDesigner 保持完全一致,确保查看模式下的流程显示与设计模式下的样式完全相同。
2025-01-19 - TestCaseDetailDrawer 移除小地图和调试数据映射
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 移除小地图组件并添加调试信息
修改内容:
-
移除小地图:
- 根据用户要求"不需要小地图",从 ReactFlow 组件中移除了
<MiniMap />组件 - 保留
<Controls />和<Background />组件
- 根据用户要求"不需要小地图",从 ReactFlow 组件中移除了
-
添加调试信息:
- 在
getReactFlowData函数中添加了console.log调试信息 - 输出原始节点数据和每个节点的处理过程
- 帮助诊断节点显示"Unknown"的问题
- 在
-
调试信息内容:
console.log('原始节点数据:', testCase.nodes); console.log('处理节点:', node); console.log('节点数据:', node.data); -
技术特性:
- 调试友好:添加详细的日志输出,便于问题诊断
- 用户需求响应:移除不需要的小地图组件
- 数据验证:通过日志验证数据映射是否正确
修改时间:
2025-01-19
修改原因:
- 用户明确要求"不需要小地图"
- 节点仍然显示"Unknown"问题需要进一步调试
- 需要验证数据映射是否正确工作
2025-01-19 - TestCaseDetailDrawer 增强调试和数据映射修复
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 增强调试信息并修复数据映射
修改内容:
-
增强调试信息:
- 在
getReactFlowData函数中添加了更详细的日志输出 - 添加了原始连线数据的日志输出
- 添加了节点位置信息的日志输出
- 在
TestStepNode组件中添加了数据接收的日志输出
- 在
-
修复数据映射:
- 使用可选链操作符
?.来安全访问数据字段 - 为所有数据字段提供默认值,防止 undefined 错误
- 为节点尺寸提供默认值(width: 150, height: 50)
- 为连线样式提供默认值
- 使用可选链操作符
-
数据安全处理:
// 节点数据安全处理 const nodeData = { stepId: node.data?.stepId || node.id, stepName: node.data?.stepName || 'Unknown', stepType: node.data?.stepType || 3, stepTypeName: node.data?.stepTypeName || '处理步骤', description: node.data?.description || '', icon: node.data?.icon || 'settings' }; // 连线数据安全处理 style: edge.style || { stroke: '#333', strokeWidth: 2 }, data: edge.data || {} -
调试信息内容:
- 原始节点数据和连线数据
- 每个节点的处理过程
- 节点位置信息
- 处理后的节点数据
- TestStepNode 组件接收到的数据
-
技术特性:
- 数据安全:使用可选链和默认值防止运行时错误
- 调试友好:详细的日志输出便于问题诊断
- 容错性:即使数据不完整也能正常显示
- 兼容性:与 ReactFlowDesigner 的数据结构保持一致
修改时间:
2025-01-19
修改原因:
- 节点仍然显示"Unknown"且没有连接
- 样式与 ReactFlowDesigner 完全不同
- 需要深入调试数据映射问题
- 确保数据安全处理,防止运行时错误
2025-01-16 StartTerminalServiceCommandHandler 代码优化
修改文件:
X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommandHandler.cs- 优化WebSocket连接创建逻辑
修改内容:
-
参数验证增强:
- 服务端点验证:检查
serviceEndpoint是否为空,避免空引用异常 - 服务编码验证:检查
existingService.ServiceCode是否为空,确保WebSocket客户端名称有效 - 用户信息验证:保持原有的用户ID验证逻辑
- 服务端点验证:检查
-
WebSocket连接创建优化:
- 请求参数完善:添加
HeartbeatInterval参数,设置为30秒心跳间隔 - 异常处理增强:使用 try-catch 包装WebSocket连接创建过程
- 响应验证详细化:
- 检查响应是否为null
- 检查响应成功状态
- 提供详细的错误信息
- 请求参数完善:添加
-
日志记录改进:
- 操作开始日志:记录WebSocket连接创建的详细信息
- 调试级别日志:记录WebSocket请求参数(客户端名称、连接地址、心跳间隔)
- 成功日志:记录连接创建成功的基本信息
- 错误日志:提供更详细的错误信息和上下文
-
错误处理严谨化:
- 空值检查:对所有关键参数进行空值验证
- 异常捕获:捕获WebSocket连接创建过程中的所有异常
- 错误消息:提供用户友好的错误消息,包含具体的错误原因
- 取消令牌支持:在WebSocket连接创建时传递取消令牌
-
代码结构优化:
- 方法提取:将WebSocket连接创建逻辑提取为独立的私有方法
CreateWebSocketConnectionAsync - 职责分离:主方法专注于业务流程控制,WebSocket连接创建逻辑独立封装
- 可读性提升:使用更清晰的变量命名和注释
- 维护性增强:便于后续功能扩展和问题排查
- 错误处理统一:统一的错误信息传递机制,正确处理
OperationResult<T>的ErrorMessages属性
- 方法提取:将WebSocket连接创建逻辑提取为独立的私有方法
-
技术特性:
- 异步操作:保持异步操作模式,支持取消令牌
- 资源管理:确保异常情况下的资源正确释放
- 性能优化:避免不必要的字符串操作和对象创建
- 安全性:增强输入验证,防止潜在的安全问题
修改时间:
2025-01-16
修改原因:
提高启动终端服务功能的代码质量和稳定性,增强错误处理能力,提供更好的用户体验和系统可维护性。
2025-01-15 X1.WebAPI 部署脚本优化
修改文件:
X1.WebAPI/publish.bat- 添加deploy.sh复制功能X1.WebAPI/deploy.sh- 创建优化的服务器端部署脚本
修改内容:
-
publish.bat脚本增强:
- 添加了自动复制
deploy.sh到发布目录的功能 - 现在会自动复制:Dockerfile、docker-deploy.md、deploy.sh
- 提供详细的复制状态反馈和错误提示
- 添加了自动复制
-
deploy.sh脚本特性:
- 彩色输出:不同级别的信息用不同颜色显示(INFO蓝色、SUCCESS绿色、WARNING黄色、ERROR红色)
- 详细日志:每个步骤都有详细的输出信息和状态反馈
- 错误处理:遇到错误立即停止并显示错误信息
- 健康检查:自动检查应用是否正常启动(最多等待30秒)
- 状态显示:显示容器状态、端口信息、镜像信息等
- 权限检查:自动设置正确的文件权限
- Docker检查:检查Docker安装状态和服务运行状态
-
部署流程优化:
- 文件检查:验证所有必要文件是否存在
- 权限设置:自动设置可执行文件和目录权限
- 容器管理:自动停止和删除旧容器
- 镜像构建:显示构建过程和结果
- 容器启动:显示启动状态和端口监听情况
- 健康检查:验证应用是否正常响应
-
使用方式:
# 本地发布 publish.bat # 上传publish目录到服务器 # 在服务器上运行 chmod +x deploy.sh ./deploy.sh
修改时间:
2025-01-15
修改原因:
优化X1.WebAPI项目的部署流程,提供详细的部署输出信息,简化服务器端部署操作,提高部署的可视化和可维护性。
2025-01-16 路由冲突修复和当前用户服务增强
修改文件:
X1.Presentation/Controllers/TerminalServicesController.cs- 修复路由冲突X1.Domain/Services/ICurrentUserService.cs- 添加服务IP和端口获取方法X1.Infrastructure/Services/CurrentUserService.cs- 实现服务IP和端口获取方法X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommand.cs- 增强用户获取逻辑X1.WebUI/src/constants/api.ts- 添加终端服务API路径X1.WebUI/src/services/terminalService.ts- 更新API路径和修复命名冲突
修改内容:
-
路由冲突修复:
- 问题描述:
TerminalServicesController和TerminalDevicesController都使用了相同的路由路径api/terminal-devices,导致Swagger生成时出现冲突 - 解决方案:将
TerminalServicesController的路由改为api/terminal-services,保持TerminalDevicesController使用api/terminal-devices - 前端更新:更新前端API路径配置,添加
TERMINAL_SERVICES常量,更新终端服务使用新的API路径
- 问题描述:
-
当前用户服务增强:
- 新增方法:
GetCurrentServiceIpAddress():获取当前服务IP地址GetCurrentServicePort():获取当前服务端口GetCurrentServiceEndpoint():获取当前服务端点信息(IP:端口)
- 技术特性:
- 优先获取IPv4地址,支持IPv6到IPv4的映射
- 完整的空值检查和错误处理
- 从HttpContext中获取本地连接信息
- 新增方法:
-
启动终端服务命令处理器增强:
- 用户获取严谨化:
- 检查用户ID是否为空
- 检查用户是否已认证
- 获取当前服务端点信息用于日志记录
- 日志记录增强:
- 记录用户ID、服务端点、操作结果
- 提供更详细的操作追踪信息
- 用户获取严谨化:
-
前端服务修复:
- API路径更新:使用新的
TERMINAL_SERVICES路径 - 命名冲突修复:将
TerminalService类重命名为TerminalServiceClient,避免与接口冲突 - 类型安全:确保所有TypeScript类型定义正确
- API路径更新:使用新的
-
设计原则:
- RESTful API设计:使用语义化的路由路径
- 职责分离:终端服务和终端设备使用不同的API路径
- 安全性:增强用户认证和授权检查
- 可追溯性:完整的操作日志记录
修改时间:
2025-01-16
修改原因:
- 修复Swagger生成时的路由冲突问题,确保API文档正确生成
- 增强当前用户服务功能,提供更丰富的用户和服务信息
- 提高系统安全性和可追溯性,确保所有操作都有完整的审计信息
2025-01-16 终端消息事件模型创建和WebSocket集成
修改文件:
X1.Domain/Models/TerminalMessageEvent.cs- 创建终端消息事件模型X1.WebSocket/Handlers/TerminalMessageHandler.cs- 修改WebSocket消息处理器以使用IMediatorX1.BackendServices/TerminalManagement/TerminalManagementService.cs- 更新终端管理服务使用新的事件模型X1.BackendServices/TerminalManagement/TerminalMessageEventHandler.cs- 创建终端消息事件处理器X1.BackendServices/DependencyInjection.cs- 注册终端管理服务和事件处理器
修改内容:
-
TerminalMessageEvent模型特性:
- 实现
INotification接口,用于MediatR消息传递 - 包含三个基本字段:
ConnectionId、MessageData、Timestamp - 提供
Create和CreateWithTimestamp静态工厂方法 - 包含
GetMessageSummary和IsValid实用方法 - 遵循项目模型设计规范
- 实现
-
WebSocket消息处理集成:
- 修改
TerminalMessageHandler以使用IMediator - 在
HandleAsync方法中使用TerminalMessageEvent.Create创建事件 - 通过
IMediator.Publish发送消息事件 - 保持原有的WebSocket响应逻辑
- 修改
-
终端消息事件处理器:
- 创建
TerminalMessageEventHandler实现INotificationHandler<TerminalMessageEvent> - 负责接收和处理来自
TerminalMessageHandler的终端消息事件 - 提供完整的日志记录和错误处理
- 支持异步消息处理和业务逻辑扩展
- 创建
-
终端管理服务更新:
- 更新
TerminalManagementService使用新的事件模型 - 在
HandleTerminalMessageAsync方法中使用Create方法 - 确保事件传递的一致性
- 更新
-
依赖注入配置:
- 注册
TerminalManagementService为HostedService - 注册
TerminalMessageEventHandler为Scoped服务 - 确保服务在应用启动时正确初始化
- 注册
-
设计原则:
- 遵循CQRS模式,使用MediatR进行消息传递
- 采用事件驱动架构,解耦WebSocket处理和业务逻辑
- 使用工厂方法模式创建事件实例
- 提供完整的日志记录和错误处理机制
修改时间:
2025-01-16
修改原因:
需要创建一个标准的终端消息事件模型,用于WebSocket消息的事件传递,使用系统自带的 IMediator 进行消息处理,实现终端设备的统一消息管理。
2024年修改记录
2025-01-15 终端设备管理前端界面修复
修改文件:
X1.WebUI/src/services/terminalDeviceService.tsX1.WebUI/src/pages/terminal-devices/TerminalDevicesTable.tsxX1.WebUI/src/pages/terminal-devices/TerminalDeviceForm.tsxX1.WebUI/src/pages/terminal-devices/TerminalDevicesView.tsxX1.WebUI/src/components/ui/switch.tsxX1.WebUI/package.json
修改内容:
-
问题描述:
- 终端设备管理页面需要与 instruments 页面保持一致的样式和功能
- 缺少必要的 UI 组件和依赖包
- 表单实现与 instruments 页面不一致
-
解决方案:
- 修复了
terminalDeviceService.ts中的接口定义,使其与后端 API 保持一致 - 更新了
TerminalDevicesTable.tsx,使用TerminalDeviceStatusBadge组件显示状态 - 重写了
TerminalDeviceForm.tsx,移除 Switch 组件,使用简单的 useState 状态管理,与 instruments 页面保持一致 - 创建了
switch.tsx组件(虽然最终未使用,但为将来需要时做准备) - 安装了缺失的
@hookform/resolvers依赖包
- 修复了
-
关键修改:
- 表格状态显示:使用 Badge 组件而不是 Switch 组件来显示设备状态
- 表单实现:采用与 instruments 页面相同的简单状态管理方式,不使用 react-hook-form
- 字段映射:确保前端字段与后端 API 字段完全匹配
- 编辑模式:在编辑模式下禁用不可修改的字段(IP地址、Agent端口)
-
技术细节:
- 移除了复杂的表单验证库依赖
- 使用简单的 useState 进行状态管理
- 保持与 instruments 页面完全一致的 UI 交互模式
- 确保所有 TypeScript 类型定义正确
修改时间:
2025-01-15
修改原因:
确保终端设备管理页面与 instruments 页面保持完全一致的样式和功能,提供统一的用户体验。
2025-01-15 终端设备服务导入错误修复
修改文件:
X1.WebUI/src/services/terminalDeviceService.ts
修改内容:
-
问题描述:
terminalDeviceService.ts中导入httpClient时出现错误:The requested module '/src/services/axiosConfig.ts' does not provide an export named 'httpClient'OperationResult类型导入路径错误:Cannot find module '@/types/common'
-
解决方案:
- 修正
httpClient的导入路径:从'./axiosConfig'改为'@/lib/http-client' - 修正
OperationResult的导入路径:从'@/types/common'改为'@/types/auth'
- 修正
修改记录
2025-01-21 - 创建 TestCaseFlow 相关表的数据库迁移
修改文件:
X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.cs- 新建迁移文件X1.Infrastructure/Migrations/20250821080604_AddTestCaseFlowTables.Designer.cs- 迁移设计器文件X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs- 更新模型快照
修改内容:
-
迁移文件创建:
- 迁移名称:
AddTestCaseFlowTables - 迁移时间:2025-08-21 08:06:04
- 迁移描述:为 TestCaseFlow、TestCaseNode、TestCaseEdge 实体创建数据库表
- 迁移名称:
-
创建的表结构:
-
tb_testcaseflow:测试用例流程主表
- 包含 id、name、description、type、isenabled、viewport_x、viewport_y、viewport_zoom 等字段
- 包含审计字段:createdat、updatedat、createdby、updatedby
- 创建索引:name、type、isenabled
-
tb_testcasenode:测试用例节点表
- 包含 id、testcaseid、nodeid、sequencenumber、stepid、positionx、positiony、width、height 等字段
- 包含状态字段:isselected、positionabsolutex、positionabsolutey、isdragging
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 外键关系:stepid → tb_casestepconfig.id (SET NULL)
- 创建索引:testcaseid、nodeid、sequencenumber、stepid
-
tb_testcaseedge:测试用例连线表
- 包含 id、testcaseid、edgeid、sourcenodeid、targetnodeid、edgetype、condition、isanimated、style 等字段
- 外键关系:testcaseid → tb_testcaseflow.id (CASCADE)
- 创建索引:testcaseid、edgeid、sourcenodeid、targetnodeid
-
-
数据库关系:
- 级联删除:删除测试用例流程时,自动删除相关的节点和连线
- 可选关联:节点可以关联步骤配置,删除步骤配置时节点 stepid 设为 NULL
- 完整性约束:确保数据的一致性和完整性
-
迁移应用状态:
- 已成功应用:迁移已应用到数据库
- 表已创建:三个表都已成功创建在数据库中
- 索引已建立:所有必要的索引都已创建完成
-
技术特性:
- PostgreSQL 兼容:使用 PostgreSQL 特定的数据类型和语法
- 性能优化:为常用查询字段创建索引
- 数据完整性:通过外键约束确保数据一致性
- 审计支持:包含完整的审计字段支持
修改时间:
2025-01-21
修改原因:
用户反映 TestCaseFlows、TestCaseNodes、TestCaseEdges 新建后还没有迁移数据库,需要创建相应的数据库迁移文件并应用到数据库中,以支持测试用例流程管理功能。
2025-01-19 - 根据 TestCaseFlowController 修复 testcaseService.ts
修改文件:
X1.WebUI/src/services/testcaseService.ts - 根据后端 TestCaseFlowController 重新编写前端服务
修改内容:
-
完全重构服务:
- 服务类名:从
TestCaseService改为TestCaseFlowService - 基础URL:从
/api/testcases改为/api/testcaseflow - API端点:完全匹配后端 TestCaseFlowController 的接口
- 服务类名:从
-
数据类型重新定义:
- TestFlowType:测试流程类型枚举
- TestCaseFlow:测试用例流程基础信息
- TestCaseNode:测试用例节点(支持 ReactFlow)
- TestCaseEdge:测试用例连线(支持 ReactFlow)
- TestCaseFlowDetail:测试用例流程详情(包含节点和连线)
-
API 方法对应:
- getTestCaseFlows:对应
GET /api/testcaseflow- 获取流程列表 - getTestCaseFlowById:对应
GET /api/testcaseflow/{id}- 获取流程详情 - createTestCaseFlow:对应
POST /api/testcaseflow- 创建流程 - deleteTestCaseFlow:对应
DELETE /api/testcaseflow/{id}- 删除流程
- getTestCaseFlows:对应
-
请求参数匹配:
- GetTestCaseFlowsRequest:支持 searchTerm、type、isEnabled、pageNumber、pageSize
- CreateTestCaseFlowRequest:支持 name、description、type、isEnabled、viewport、nodes、edges
- CreateNodeData:节点创建数据结构
- CreateEdgeData:连线创建数据结构
-
响应数据结构:
- GetTestCaseFlowsResponse:包含分页信息的流程列表
- GetTestCaseFlowByIdResponse:包含完整节点和连线数据的流程详情
- CreateTestCaseFlowResponse:创建成功后的流程信息
-
ReactFlow 兼容性:
- 节点结构:完全支持 ReactFlow 的节点数据结构
- 连线结构:完全支持 ReactFlow 的连线数据结构
- 位置信息:支持 position、positionAbsolute 等位置字段
- 样式信息:支持 style、data 等样式和数据字段
-
向后兼容性:
- 保留导出:保持
testcaseService导出,确保现有代码不破坏 - 新增导出:新增
testcaseFlowService导出,提供更明确的命名
- 保留导出:保持
修改时间:
2025-01-19
修改原因:
用户要求根据 TestCaseFlowController 修复 testcaseService.ts,确保前端服务与后端 API 完全匹配,支持测试用例流程的完整 CRUD 操作和 ReactFlow 集成。
2025-01-19 - 更新 API 路径常量以匹配后端控制器
修改文件:
X1.WebUI/src/constants/api.ts- 更新 API 路径常量X1.WebUI/src/services/testcaseService.ts- 使用 API 路径常量
修改内容:
-
API 路径常量更新:
- 移除:
TEST_CASES: '/test-cases'和TEST_STEPS: '/test-steps'(后端无对应控制器) - 新增:
TEST_CASE_FLOW: '/api/testcaseflow'(对应 TestCaseFlowController) - 更新:
CASE_STEP_CONFIGS: '/api/casestepconfigs'(对应 CaseStepConfigController)
- 移除:
-
testcaseService.ts 优化:
- 导入常量:添加
API_PATHS导入 - 使用常量:将硬编码的
/api/testcaseflow替换为API_PATHS.TEST_CASE_FLOW - 统一管理:所有 API 路径都通过常量统一管理
- 导入常量:添加
-
路径对应关系:
- TestCaseFlowController:
/api/testcaseflow→API_PATHS.TEST_CASE_FLOW - CaseStepConfigController:
/api/casestepconfigs→API_PATHS.CASE_STEP_CONFIGS
- TestCaseFlowController:
-
技术特性:
- 类型安全:使用 TypeScript 常量确保路径一致性
- 维护性:集中管理 API 路径,便于维护和修改
- 一致性:确保前端服务与后端控制器路径完全匹配
修改时间:
2025-01-19
修改原因:
用户询问是否需要根据 testcaseService 修复其他相关服务,发现 API 路径常量与实际后端控制器不匹配,需要统一更新以确保前后端一致性。
2025-01-19 - 修复 pages/testcases 页面使用 testcaseService
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 修复列表页面使用真实 APIX1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复视图页面使用真实 APIX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx- 添加保存状态支持
修改内容:
-
TestCasesListView.tsx 重构:
- 移除模拟数据:删除
mockTestCases和本地接口定义 - 集成真实 API:使用
testcaseService.getTestCaseFlows()获取数据 - 添加加载状态:显示加载中状态,提升用户体验
- 实现搜索功能:支持按名称搜索测试用例流程
- 实现删除功能:使用
testcaseService.deleteTestCaseFlow()删除流程 - 更新数据展示:适配
TestCaseFlow接口的数据结构 - 修复状态显示:使用
isEnabled字段显示启用/停用状态 - 添加类型标签:显示测试流程类型(功能测试、性能测试等)
- 移除模拟数据:删除
-
TestCasesView.tsx 重构:
- 集成保存功能:使用
testcaseService.createTestCaseFlow()保存流程 - 数据格式转换:将 ReactFlow 节点和连线数据转换为后端格式
- 添加保存状态:显示保存中状态,防止重复提交
- 错误处理:完整的错误处理和用户提示
- 流程验证:检查流程完整性(开始节点、结束节点等)
- 集成保存功能:使用
-
ReactFlowDesigner.tsx 增强:
- 添加 saving 属性:支持保存状态传递
- 保存按钮状态:保存时禁用按钮并显示"保存中..."
- 用户体验优化:防止保存过程中的重复操作
-
数据结构适配:
- 节点数据转换:ReactFlow 节点 → 后端 CreateNodeData 格式
- 连线数据转换:ReactFlow 连线 → 后端 CreateEdgeData 格式
- 类型安全:使用 TypeScript 接口确保类型安全
-
功能特性:
- 实时搜索:支持按回车键搜索
- 批量操作:支持删除操作
- 导航功能:支持查看和编辑页面跳转
- 状态管理:完整的加载和保存状态管理
修改时间:
2025-01-19
修改原因:
用户发现 pages/testcases 页面没有调用 testcaseService,需要修复这些页面使其使用真实的 API 而不是模拟数据,确保前后端数据一致性。
2025-01-19 - TestCaseFlowController 添加 CreateTestCaseFlow 命令
修改文件:
X1.Presentation/Controllers/TestCaseFlowController.cs - 为TestCaseFlowController添加创建测试用例流程的POST方法
修改内容:
-
新增POST方法:
- 方法名:
CreateTestCaseFlow - 路由:
[HttpPost]- 对应/api/testcaseflow - 参数:
[FromBody] CreateTestCaseFlowCommand command - 返回:
IActionResult
- 方法名:
-
功能特性:
- 命令处理:使用
mediator.Send(command)发送创建命令 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 响应格式:使用
CreatedAtAction返回201状态码和资源位置
- 命令处理:使用
-
日志记录:
- 开始日志:记录流程名称和类型
- 成功日志:记录创建的ID、名称、节点数量、连线数量
- 失败日志:记录流程名称和详细错误信息
-
响应处理:
- 成功响应:返回201 Created状态码,包含新创建资源的URI
- 失败响应:返回400 Bad Request状态码,包含错误详情
- 资源定位:使用
CreatedAtAction提供新创建资源的访问路径
-
技术特性:
- 依赖注入:添加了
CreateTestCaseFlow命名空间的using语句 - MediatR集成:使用mediator发送命令,遵循CQRS模式
- RESTful设计:遵循REST API设计规范
- 统一响应:使用统一的OperationResult响应格式
- 依赖注入:添加了
-
API端点:
POST /api/testcaseflow Content-Type: application/json { "name": "测试流程名称", "description": "流程描述", "type": 1, "isEnabled": true, "viewportX": 40.5, "viewportY": 21.2, "viewportZoom": 1.1, "nodes": [...], "edges": [...] }
修改时间:
2025-01-19
修改原因:
用户要求为TestCaseFlowController添加TestCaseFlow.Commands功能,特别是创建测试用例流程的POST方法,以支持前端界面创建新的测试用例流程。
2025-01-19 - TestCaseNodeDto 和 TestCaseEdgeDto 完善
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.cs - 完善 TestCaseNodeDto 和 TestCaseEdgeDto 结构
修改内容:
-
新增 DTO 类:
- TestCaseNodeDataDto:节点数据DTO,包含步骤ID、步骤名称、步骤类型、描述、图标等
- TestCaseNodePositionDto:节点位置DTO,包含X、Y坐标
- TestCaseEdgeStyleDto:连线样式DTO,包含描边颜色、描边宽度
- TestCaseEdgeDataDto:连线数据DTO,包含条件信息
-
TestCaseNodeDto 增强:
- 新增字段:
Type:节点类型(如 "testStep")Position:节点位置(TestCaseNodePositionDto)Data:节点数据(TestCaseNodeDataDto)Selected:是否被选中PositionAbsolute:绝对位置Dragging:是否正在拖拽
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
TestCaseEdgeDto 增强:
- 新增字段:
Source:源节点IDSourceHandle:源连接点Target:目标节点IDTargetHandle:目标连接点Type:连线类型Animated:是否动画Style:连线样式(TestCaseEdgeStyleDto)Data:连线数据(TestCaseEdgeDataDto)
- 兼容性字段:保留原有字段,确保向后兼容
- 新增字段:
-
技术特性:
- ReactFlow 兼容:新增字段与 ReactFlow 数据结构完全兼容
- JSON 序列化:支持直接序列化为 ReactFlow 所需的 JSON 格式
- 向后兼容:保留原有字段,确保现有代码不受影响
- 类型安全:使用强类型 DTO,避免运行时错误
-
数据结构示例:
{ "nodes": [ { "id": "node-1755654432101", "type": "testStep", "position": { "x": 360, "y": 30 }, "data": { "stepId": "e2192f5a-1582-47e9-92be-c676679418da", "stepName": "StartStep", "stepType": 1, "stepTypeName": "Start", "description": "Mapping_Start", "icon": "play-circle" }, "width": 95, "height": 30, "selected": false, "positionAbsolute": { "x": 360, "y": 30 }, "dragging": false } ], "edges": [ { "source": "node-1755654432101", "sourceHandle": "bottom", "target": "node-1755654436065", "targetHandle": "top", "id": "edge-1755654470705", "type": "smoothstep", "animated": false, "style": { "stroke": "#3b82f6", "strokeWidth": 2 }, "data": { "condition": "default" } } ] } -
设计原则:
- 前端兼容:确保与 ReactFlow 前端组件完全兼容
- 数据完整性:支持完整的节点和连线信息
- 扩展性:支持未来功能扩展
- 性能优化:避免不必要的数据转换
修改时间:
2025-01-19
修改原因:
用户需要 TestCaseNodeDto 和 TestCaseEdgeDto 与 ReactFlow 前端组件完全兼容,支持完整的节点和连线数据结构,包括位置、样式、数据等字段,确保前端能够正确显示和操作测试用例流程。
2025-01-19 - TestCaseFlow Queries功能实现
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/- 获取TestCaseFlow列表查询X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/- 根据ID获取TestCaseFlow详情查询X1.Presentation/Controllers/TestCaseFlowController.cs- TestCaseFlow控制器
修改内容:
-
GetTestCaseFlows查询功能:
- 查询类:
GetTestCaseFlowsQuery- 支持搜索、类型过滤、启用状态过滤、分页 - 响应类:
GetTestCaseFlowsResponse- 包含分页信息和TestCaseFlow列表 - 处理器:
GetTestCaseFlowsQueryHandler- 调用仓储获取分页数据并映射为DTO
- 查询类:
-
GetTestCaseFlowById查询功能:
- 查询类:
GetTestCaseFlowByIdQuery- 根据testCaseId获取详情 - 响应类:
GetTestCaseFlowByIdResponse- 包含完整的流程信息、节点和连线数据 - 处理器:
GetTestCaseFlowByIdQueryHandler- 组装TestCaseFlow、TestCaseNode、TestCaseEdge数据
- 查询类:
-
TestCaseFlowController控制器:
- 列表接口:
GET /api/testcaseflow- 获取测试用例流程列表,支持搜索和分页 - 详情接口:
GET /api/testcaseflow/{id}- 获取测试用例流程详情,包含节点和连线 - 完整日志:详细的日志记录和错误处理
- 列表接口:
-
数据组装特性:
- 列表查询:直接返回TestCaseFlow数据,不包含节点和连线
- 详情查询:组装完整的流程信息,包括所有节点和连线数据
- 性能优化:使用仓储的
GetTestCaseFlowWithDetailsAsync方法一次性获取所有数据
-
技术特性:
- CQRS模式:查询和命令分离,使用MediatR进行消息传递
- 分页支持:支持搜索、过滤、分页功能
- 数据完整性:详情查询包含完整的流程结构信息
- 错误处理:完整的异常处理和日志记录
修改时间:
2025-01-19
修改原因:
用户需要为TestCaseFlow实现Queries功能,包括获取列表和根据testCaseId获取详情,详情查询需要组装TestCaseNode和TestCaseEdge数据,为前端界面查看详情提供完整的数据支持。
2024-12-19 - 移除无意义的 StepTypeName 字段
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdResponse.csX1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdQueryHandler.cs
修改内容:
-
移除 TestCaseNodeDataDto 中的 StepTypeName 字段:
- 删除了
public string StepTypeName { get; set; } = null!;字段 - 该字段被认为是无意义的,因为已经有
StepType字段存储步骤类型的枚举值
- 删除了
-
移除 GetStepTypeName 方法:
- 删除了
GetStepTypeName私有方法 - 该方法用于将枚举值转换为字符串名称,但不再需要
- 删除了
-
更新数据映射逻辑:
- 在
MapNodesToReactFlowFormatAsync方法中移除了对StepTypeName字段的赋值 - 简化了节点数据映射逻辑
- 在
技术影响:
- 代码简化:移除了冗余的字段和方法
- 性能优化:减少了不必要的方法调用和字符串转换
- 维护性提升:减少了代码复杂度,提高了可维护性
- 数据一致性:避免了
StepType和StepTypeName之间可能的不一致问题
前端兼容性:
- 前端代码需要相应更新,移除对
stepTypeName字段的依赖 - 如果需要步骤类型名称,可以通过
stepType枚举值在前端进行转换
2024-12-19 - 移除前端代码中的 stepTypeName 字段
修改文件:
X1.WebUI/src/services/testcaseService.tsX1.WebUI/src/services/teststepsService.tsX1.WebUI/src/pages/testcases/ReactFlowDesigner.tsxX1.WebUI/src/pages/testcases/TestStepsPanel.tsx
修改内容:
-
testcaseService.ts:
- 从
TestCaseNodeData接口中移除了stepTypeName: string;字段
- 从
-
teststepsService.ts:
- 从
TestStep接口中移除了stepTypeName: string;字段
- 从
-
ReactFlowDesigner.tsx:
- 从
FlowNode类型定义中移除了stepTypeName: string;字段 - 移除了调试信息中对
stepTypeName的引用 - 在创建新节点时移除了对
step.stepTypeName的赋值
- 从
-
TestStepsPanel.tsx:
- 将显示
step.stepTypeName的地方改为根据step.stepType枚举值显示中文名称 - 使用条件判断:1=开始,2=结束,3=处理,4=判断
- 将显示
技术影响:
- 前后端一致性:前端代码与后端 API 返回的数据结构保持一致
- 代码简化:移除了冗余的字段,减少了数据传输量
- 维护性提升:减少了需要维护的字段数量
- 类型安全:避免了前后端字段不匹配的问题
用户体验:
- 显示优化:在 TestStepsPanel 中直接显示中文的步骤类型名称,用户体验更好
- 数据一致性:确保前端显示的数据与后端存储的数据完全一致
2025-01-19 - TestCaseFlow 空引用警告修复和连线样式类型优化
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 修复空引用警告X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 优化连线样式类型
修改内容:
-
空引用警告修复:
- 问题:在
CreateTestCaseFlowCommandHandler中,ValidateUserAuthentication方法返回的OperationResult<string>可能为空 - 解决方案:使用空合并操作符
??确保传递给CreateFailure方法的参数不为空 - 修改代码:
return OperationResult<CreateTestCaseFlowResponse>.CreateFailure(errorMessages ?? new List<string>());
- 问题:在
-
连线样式类型优化:
- 问题:
Style属性被定义为object?类型,但在CreateEdgesAsync方法中使用edgeData.Style?.ToString()来转换 - 解决方案:将
Style属性明确为string?类型,符合 JSON 字符串格式的实际用途 - 修改代码:
- 问题:
-
连线类型必填验证:
- 在
EdgeData类中将Type属性设为必填:[Required(ErrorMessage = "连线类型不能为空")] - 在验证逻辑中添加对
Type的验证:if (string.IsNullOrWhiteSpace(edge.Type)) - 在创建连线时移除默认值逻辑:
edgeType: edgeData.Type(不再使用?? "straight")
- 在
-
验证器提取:
- 创建了
CreateTestCaseFlowCommandValidator验证器类 - 将
ValidateRequest方法从CreateTestCaseFlowCommandHandler中提取出来 - 使用静态方法
Validate进行验证,便于复用和测试 - 保持原有的验证逻辑和错误消息不变
- 创建了
-
设计原则:
- 单一职责:验证器专注于参数验证,处理器专注于业务逻辑
- 可复用性:验证器可以在其他地方复用
- 可测试性:独立的验证器更容易进行单元测试
- 代码组织:更好的代码结构和职责分离
修改时间:
2025-01-19
修改原因:
用户要求确保连线类型是必填项,界面不能传空值。同时要求将验证逻辑提取到单独的验证器类中,提高代码的可维护性和可测试性。
2025-01-19 - TestCaseFlowController 删除功能添加和接口返回类型统一
修改文件:
X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommand.cs- 新增删除命令X1.Application/Features/TestCaseFlow/Commands/DeleteTestCaseFlow/DeleteTestCaseFlowCommandHandler.cs- 新增删除命令处理器X1.Presentation/Controllers/TestCaseFlowController.cs- 添加删除端点和统一返回类型
修改内容:
-
删除命令实现:
- 创建了
DeleteTestCaseFlowCommand类,包含流程ID参数 - 创建了
DeleteTestCaseFlowCommandHandler处理器,实现删除逻辑 - 包含用户认证验证、流程存在性检查、删除操作和事务提交
- 创建了
-
控制器接口统一:
- 参考
TerminalServicesController的返回方式,将所有方法返回类型改为OperationResult<T> - 移除了
IActionResult和BadRequest/Ok等 HTTP 状态码处理 - 直接返回
OperationResult<T>对象,让框架自动处理 HTTP 状态码
- 参考
-
删除端点添加:
- 添加了
DELETE /api/testcaseflow/{id}端点 - 包含完整的日志记录和错误处理
- 返回删除操作的结果
- 添加了
-
技术特性:
- 一致性:与
TerminalServicesController保持相同的接口返回模式 - 简化:移除了手动的 HTTP 状态码处理,让框架自动处理
- 完整性:删除功能包含完整的业务逻辑验证和错误处理
- 一致性:与
API 端点示例:
DELETE /api/testcaseflow/{id}
Authorization: Bearer {token}
响应格式:
{
"isSuccess": true,
"data": true,
"errorMessages": null
}
修改时间:
2025-01-19
修改原因:
用户要求添加删除功能到 TestCaseFlowController,并参考 TerminalServicesController 的接口返回方式,统一使用 OperationResult<T> 返回类型,不使用 IActionResult。
2025-01-19 - TestCaseFlow Application层实现
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 创建测试用例流程命令X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowResponse.cs- 创建测试用例流程响应X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 创建测试用例流程命令处理器
修改内容:
-
CreateTestCaseFlowCommand 命令类:
- 实现了
IRequest<OperationResult<CreateTestCaseFlowResponse>>接口 - 包含完整的验证特性:
[Required]、[MaxLength]等 - 支持所有必要字段:名称、描述、类型、启用状态、视口坐标等
- 提供合理的默认值,如视口坐标和启用状态
- 实现了
-
CreateTestCaseFlowResponse 响应类:
- 包含完整的流程信息返回
- 支持类型枚举转换为字符串显示
- 包含审计信息:创建时间、创建人等
-
CreateTestCaseFlowCommandHandler 命令处理器:
- 遵循CQRS模式,使用MediatR框架
- 完整的参数验证和业务逻辑验证
- 用户认证验证
- 名称重复性检查
- 使用领域实体的Create工厂方法
- 完整的错误处理和日志记录
- 事务管理(通过UnitOfWork)
-
设计原则:
- 参考TerminalServices模式:完全按照TerminalServices的设计规则实现
- CQRS架构:命令和查询分离
- 领域驱动设计:使用领域实体的工厂方法
- 依赖注入:通过构造函数注入依赖
- 日志记录:完整的操作日志和错误日志
- 错误处理:统一的错误处理和响应格式
-
技术特性:
- 支持异步操作
- 完整的取消令牌支持
- 统一的OperationResult响应格式
- 详细的验证错误信息
- 事务性操作保证
修改时间:
2025-01-19
修改原因:
用户要求在X1.Application.Features中实现TestCaseFlow的功能,参考TerminalServices的设计规则,先完成创建功能。为测试用例流程管理提供完整的Application层支持,包括命令、响应和处理器实现。
修复记录:
- 编译错误修复:修复了
TestCaseFlow.Create方法调用时的命名空间解析问题,使用完全限定的类型名称X1.Domain.Entities.TestCase.TestCaseFlow.Create来解决编译器无法找到Create方法的问题。
2025-01-19 - TestCaseNode 和 TestCaseEdge 数据集成
- 命令扩展:在
CreateTestCaseFlowCommand中添加了NodeData和EdgeDataDTO类,支持节点和连线数据的传输 - 处理器增强:在
CreateTestCaseFlowCommandHandler中添加了ITestCaseNodeRepository和ITestCaseEdgeRepository依赖注入 - 节点创建:实现了
CreateNodesAsync方法,支持批量创建测试用例节点,包括位置、尺寸、状态等属性 - 连线创建:实现了
CreateEdgesAsync方法,支持批量创建测试用例连线,包括源节点、目标节点、类型、样式等属性 - 验证增强:添加了对节点和连线数据的验证逻辑,确保数据完整性
- 日志记录:增强了日志记录,包含节点数量和连线数量的统计信息
- 事务管理:确保节点和连线的创建在同一个事务中完成,保证数据一致性
2024-12-19 - StartDeviceRuntimeCommandHandler 问题分析与修复
问题描述
API响应中 isSuccess: true 但 summary.failureCount: 1,导致前端误判操作成功。
问题分析
通过分析代码发现以下潜在问题区域:
- 网络配置构建阶段:在
BuildNetworkConfigurationRequests方法中,设备可能因为配置验证失败而被过滤掉 - 网络启动阶段:在
StartNetworksInParallelAsync方法中,网络启动失败 - 设备运行时处理阶段:设备运行时不存在或更新失败
实施的修复
-
增强日志记录:
- 在
BuildNetworkConfigurationRequests中添加详细的警告日志 - 在
StartNetworksInParallelAsync中增强错误日志和统计信息 - 在设备运行时处理循环中添加调试日志
- 在
-
关键修复 - isSuccess 字段逻辑:
- 问题根源:
OperationResult<T>.IsSuccess属性仅基于ErrorMessages是否为空 - 解决方案:在
Handle方法中根据业务逻辑判断成功/失败 - 只有当所有设备都成功启动时才返回
CreateSuccess - 否则返回
CreateFailure并包含详细错误信息
- 问题根源:
-
配置验证逻辑优化:
- 问题:原来的配置验证过于严格,要求同时有RAN配置和完整的IMS+核心网配置
- 优化:改为更灵活的验证逻辑,允许只有RAN配置或只有IMS配置的设备通过
- 验证规则:
- 至少需要RAN配置 或者 IMS配置(不要求同时有核心网配置)
- 如果有IMS配置但没有核心网配置,记录警告但不阻止设备启动
- 提供更详细的配置状态日志,便于调试
- 影响:减少因配置不完整而被错误跳过的设备数量
修改原因
- 解决前端误判问题
- 提供更好的调试信息
- 确保数据一致性
2024-12-19 - 已实施更改的评估分析
评估结果:所有更改都应该保留
1. 网络配置构建阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的日志记录,记录被跳过设备的具体原因
- 过滤逻辑透明化,包括重复组合、缺少配置、验证失败等情况
- 统计信息记录,显示原始请求数 vs 有效请求数
- 价值: 提供更好的调试信息和透明度
2. 网络启动阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 增强的错误日志,包含具体错误信息
- 统计信息记录,显示总请求数、成功数、失败数
- 失败设备详细记录
- 价值: 提供关键的调试信息,帮助快速定位网络启动失败原因
3. 设备运行时处理阶段优化 ✅
- 状态: 已优化,无需撤回
- 改进内容:
- 跳过逻辑:只处理网络启动成功的设备
- 详细日志:记录状态更新过程
- 调试信息:记录设备运行时当前状态
- 价值: 确保数据一致性,避免对失败设备的无效处理
4. isSuccess 字段逻辑修复 ✅
- 状态: 已修复,这是核心问题
- 问题:
isSuccess: true但failureCount: 1导致前端误判 - 解决方案: 根据业务逻辑判断成功/失败
- 价值: 解决了前端误判的根本问题
建议的监控指标
- 网络配置构建成功率:
有效请求数 / 原始请求数 - 网络启动成功率:
成功设备数 / 有效请求数 - 整体成功率:
成功设备数 / 总请求数
日志分析建议
当出现失败时,可通过以下日志快速定位问题:
- 网络配置构建阶段:查看被跳过设备的原因
- 网络启动阶段:查看具体错误信息
- 设备运行时处理:查看状态更新过程
结论
所有实施的更改都是有价值的改进,不仅解决了核心的 isSuccess 误判问题,还提供了更好的调试能力和数据一致性。不建议撤回任何更改。
2025年修改记录
2025-01-19 - TestCaseEdge 和 TestCaseNode Repositories 完善
修改文件:
X1.Domain/Repositories/TestCase/ITestCaseEdgeRepository.cs- 创建 TestCaseEdge 仓储接口X1.Domain/Repositories/TestCase/ITestCaseNodeRepository.cs- 创建 TestCaseNode 仓储接口X1.Infrastructure/Repositories/TestCase/TestCaseEdgeRepository.cs- 创建 TestCaseEdge 仓储实现X1.Infrastructure/Repositories/TestCase/TestCaseNodeRepository.cs- 创建 TestCaseNode 仓储实现X1.Infrastructure/DependencyInjection.cs- 注册新的仓储服务
修改内容:
-
TestCaseEdge 仓储接口创建:
- 创建了
ITestCaseEdgeRepository接口,继承自IBaseRepository<TestCaseEdge> - 定义了完整的业务方法,包括基本的 CRUD 操作和特定的业务查询
- 支持测试用例连线的完整生命周期管理
- 创建了
-
主要业务方法:
- 基本操作:
AddTestCaseEdgeAsync、UpdateTestCaseEdge、DeleteTestCaseEdgeAsync - 批量操作:
DeleteByTestCaseIdAsync(根据测试用例ID删除所有连线) - 查询操作:
GetAllTestCaseEdgesAsync、GetTestCaseEdgeByIdAsync、GetByTestCaseIdAsync - 特定查询:
GetBySourceNodeIdAsync、GetByTargetNodeIdAsync、GetByEdgeIdAsync - 验证操作:
EdgeIdExistsAsync、ExistsByTestCaseIdAsync
- 基本操作:
-
TestCaseNode 仓储接口创建:
- 创建了
ITestCaseNodeRepository接口,继承自IBaseRepository<TestCaseNode> - 定义了完整的业务方法,包括基本的 CRUD 操作和特定的业务查询
- 支持测试用例节点的完整生命周期管理
- 创建了
-
主要业务方法:
- 基本操作:
AddTestCaseNodeAsync、UpdateTestCaseNode、DeleteTestCaseNodeAsync - 批量操作:
DeleteByTestCaseIdAsync(根据测试用例ID删除所有节点) - 查询操作:
GetAllTestCaseNodesAsync、GetTestCaseNodeByIdAsync、GetByTestCaseIdAsync - 排序查询:
GetByTestCaseIdOrderedAsync(按序号排序) - 特定查询:
GetByNodeIdAsync、GetByStepIdAsync、GetByTestCaseIdAndSequenceAsync - 验证操作:
NodeIdExistsAsync、ExistsByTestCaseIdAsync、SequenceExistsAsync - 统计操作:
GetMaxSequenceNumberAsync(获取最大序号)
- 基本操作:
-
TestCaseEdge 仓储实现创建:
- 创建了
TestCaseEdgeRepository实现类,继承自BaseRepository<TestCaseEdge> - 实现了
ITestCaseEdgeRepository接口的所有方法 - 使用 CQRS 模式,分离命令和查询操作
- 提供完整的日志记录和错误处理
- 创建了
-
TestCaseNode 仓储实现创建:
- 创建了
TestCaseNodeRepository实现类,继承自BaseRepository<TestCaseNode> - 实现了
ITestCaseNodeRepository接口的所有方法 - 使用 CQRS 模式,分离命令和查询操作
- 提供完整的日志记录和错误处理
- 创建了
-
依赖注入配置:
- 在
X1.Infrastructure/DependencyInjection.cs中注册新的仓储服务 - 添加了
ITestCaseEdgeRepository和ITestCaseNodeRepository的注册 - 确保控制器能够正确注入所需的仓储服务
- 在
-
技术特性:
- CQRS 模式:使用
ICommandRepository和IQueryRepository分离读写操作 - 异步支持:所有方法都支持异步操作和取消令牌
- 简洁实现:与现有仓储实现保持一致的简洁风格
- 性能优化:支持批量操作和条件过滤
- CQRS 模式:使用
-
设计原则:
- DDD 原则:遵循领域驱动设计,仓储专注于数据访问
- 单一职责:每个方法专注于特定功能
- 可扩展性:支持未来功能扩展
- 一致性:与现有仓储实现(如
CaseStepConfigRepository)保持一致的架构模式
-
命名空间规范:
- 使用
X1.Domain.Repositories.TestCase命名空间 - 使用
X1.Infrastructure.Repositories.TestCase命名空间 - 与项目整体架构保持一致
- 使用
修改时间:
2025-01-19
修改原因:
用户要求完善 TestCaseEdge 和 TestCaseNode 的 Repositories,参考 ICaseStepConfigRepository 的结构,为测试用例节点和连线管理提供完整的数据访问层支持,包括基本的 CRUD 操作和特定的业务查询功能。
2025-01-19 - TestCaseFlow 实体 Create 和 Update 方法实现
修改文件:
X1.Domain/Entities/TestCase/TestCaseTestFlow.cs - 为TestCaseFlow实体添加Create和Update方法
修改内容:
-
Create 静态工厂方法:
- 添加了
Create静态方法,用于创建新的测试用例流程 - 支持所有必要参数:名称、类型、创建人、描述、启用状态、视口坐标等
- 自动设置ID、创建时间、更新时间等审计字段
- 视口坐标参数(viewportX、viewportY、viewportZoom)由界面传入,不提供默认值
- 添加了
-
Update 实例方法:
- 添加了
Update方法,用于更新测试用例流程 - 支持更新所有字段:名称、类型、描述、启用状态、视口坐标等
- 自动更新
UpdatedAt和UpdatedBy审计字段 - 使用可选参数,只更新传入的字段
- 添加了
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用工厂方法模式创建实体实例
- 通过业务方法修改实体状态
- 确保审计信息的完整性
-
技术特性:
- 类型安全的参数验证
- 完整的审计信息管理
- 灵活的更新机制
- 与CaseStepConfig实体保持一致的实现模式
- 视口坐标由界面传入,确保数据的准确性
修改时间:
2025-01-19
修改原因:
用户要求TestCaseFlow实体提供与CaseStepConfig实体相同的Create和Update方法,确保实体创建和更新的标准化和一致性。同时根据用户反馈,视口坐标参数应该由界面传入,不提供默认值。
2025-01-19 - TestCaseNode 和 TestCaseEdge 实体 Create 和 Update 方法实现
修改文件:
X1.Domain/Entities/TestCase/TestCaseNode.cs- 为TestCaseNode实体添加Create和Update方法X1.Domain/Entities/TestCase/TestCaseEdge.cs- 为TestCaseEdge实体添加Create和Update方法
修改内容:
-
TestCaseNode 实体 Create 和 Update 方法:
- Create 静态工厂方法:
- 添加了
Create静态方法,用于创建新的测试用例节点 - 支持所有必要参数:测试用例ID、节点ID、执行序号、位置坐标、步骤配置ID、尺寸、状态等
- 自动设置ID,不包含审计字段(继承自Entity而非AuditableEntity)
- 提供合理的默认值,如宽度、高度、选中状态等
- 添加了
- Update 实例方法:
- 添加了
Update方法,用于更新测试用例节点 - 支持更新所有字段:测试用例ID、节点ID、执行序号、位置坐标、步骤配置ID、尺寸、状态等
- 使用可选参数,只更新传入的字段
- 提供完整的参数验证和错误处理
- 添加了
- Create 静态工厂方法:
-
TestCaseEdge 实体 Create 和 Update 方法:
- Create 静态工厂方法:
- 添加了
Create静态方法,用于创建新的测试用例连线 - 支持所有必要参数:测试用例ID、连线ID、源节点ID、目标节点ID、连线类型、条件、动画、样式等
- 自动设置ID,不包含审计字段(继承自Entity而非AuditableEntity)
- 提供合理的默认值,如连线类型、动画状态等
- 添加了
- Update 实例方法:
- 添加了
Update方法,用于更新测试用例连线 - 支持更新所有字段:测试用例ID、连线ID、源节点ID、目标节点ID、连线类型、条件、动画、样式等
- 使用可选参数,只更新传入的字段
- 提供完整的参数验证和错误处理
- 添加了
- Create 静态工厂方法:
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用工厂方法模式创建实体实例
- 通过业务方法修改实体状态
- 与TestCaseFlow实体保持一致的实现模式
- 注意TestCaseNode和TestCaseEdge继承自Entity而非AuditableEntity,因此不包含审计字段
-
技术特性:
- 类型安全的参数验证
- 灵活的更新机制,支持部分字段更新
- 与TestCaseFlow实体保持一致的实现模式
- 提供合理的默认值,简化创建过程
- 完整的参数验证和错误处理
修改时间:
2025-01-19
修改原因:
用户要求TestCaseNode和TestCaseEdge实体提供与TestCaseFlow实体相同的Create和Update方法,确保所有测试用例相关实体的创建和更新过程标准化和一致性。
2025-01-19 - TestCaseNode Update 方法不可修改字段优化
修改文件:
X1.Domain/Entities/TestCase/TestCaseNode.cs - 优化TestCaseNode实体的Update方法
修改内容:
-
Update 方法参数优化:
- 移除了不可修改的字段参数:
testCaseId、nodeId、stepId - 这些字段作为实体的标识符和关联关系,在更新时不应该被修改
- 保留了可修改的字段:执行序号、位置坐标、尺寸、状态等
- 移除了不可修改的字段参数:
-
设计原则:
- 遵循实体不可变性原则,保护关键标识符
- 确保数据完整性和一致性
- 防止意外修改关联关系
-
技术特性:
- 更安全的更新机制
- 明确的字段修改边界
- 符合DDD设计原则
修改时间:
2025-01-19
修改原因:
用户反馈指出TestCaseNode的Update方法中,testCaseId、nodeId和stepId这些字段不应该被修改,因为它们是不可变的标识符和关联关系。
2025-01-19 - TestCaseEdge Update 方法不可修改字段优化
修改文件:
X1.Domain/Entities/TestCase/TestCaseEdge.cs - 优化TestCaseEdge实体的Update方法
修改内容:
-
Update 方法参数优化:
- 移除了不可修改的字段参数:
testCaseId、edgeId - 这些字段作为实体的标识符和关联关系,在更新时不应该被修改
- 保留了可修改的字段:源节点ID、目标节点ID、连线类型、条件、动画状态、样式等
- 移除了不可修改的字段参数:
-
设计原则:
- 遵循实体不可变性原则,保护关键标识符
- 确保数据完整性和一致性
- 防止意外修改关联关系
- 与TestCaseNode保持一致的不可变性设计
-
技术特性:
- 更安全的更新机制
- 明确的字段修改边界
- 符合DDD设计原则
- 与TestCaseNode实体的Update方法保持一致的实现模式
修改时间:
2025-01-19
修改原因:
用户反馈指出TestCaseEdge的Update方法中,testCaseId和edgeId这些字段不应该被修改,因为它们是不可变的标识符和关联关系。
2025-01-19 - GetTestCaseFlowByIdQueryHandler 修复
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdQueryHandler.cs
修改内容:
-
依赖注入增强:
- 添加
ICaseStepConfigRepository依赖,用于获取步骤配置信息 - 在构造函数中注入
caseStepConfigRepository参数
- 添加
-
数据映射重构:
- 新增
MapNodesToReactFlowFormatAsync方法:将TestCaseNode实体映射为 ReactFlow 兼容的TestCaseNodeDto - 新增
MapEdgesToReactFlowFormatAsync方法:将TestCaseEdge实体映射为 ReactFlow 兼容的TestCaseEdgeDto - 新增
GetStepTypeName方法:将步骤类型枚举转换为可读的字符串名称
- 新增
-
节点映射逻辑:
- 根据
StepId获取对应的CaseStepConfig信息 - 构建 ReactFlow 格式的
Position和Data对象 - 设置默认的
Type为 "testStep" - 处理
PositionAbsolute的可空逻辑 - 保留所有原有字段用于向后兼容
- 根据
-
连线映射逻辑:
- 解析存储的 JSON 样式字符串为
TestCaseEdgeStyleDto对象 - 设置默认的
SourceHandle和TargetHandle为 "bottom" 和 "top" - 设置默认的
Type为 "smoothstep" - 构建
Data对象包含条件信息 - 保留所有原有字段用于向后兼容
- 解析存储的 JSON 样式字符串为
-
错误处理:
- 添加 JSON 解析异常处理,在解析失败时使用默认样式
- 添加空值检查和默认值处理
技术特性:
- 异步处理:使用异步方法获取步骤配置信息,提高性能
- 数据完整性:确保所有必要字段都有合理的默认值
- 向后兼容:保留原有字段映射,确保现有功能不受影响
- 类型安全:使用强类型映射,避免运行时错误
- 错误恢复:在数据不完整时提供合理的默认值
映射关系:
TestCaseNode.NodeId→TestCaseNodeDto.IdTestCaseNode.PositionX/Y→TestCaseNodeDto.Position.X/YCaseStepConfig信息 →TestCaseNodeDto.DataTestCaseEdge.EdgeId→TestCaseEdgeDto.IdTestCaseEdge.SourceNodeId/TargetNodeId→TestCaseEdgeDto.Source/TargetTestCaseEdge.Style(JSON) →TestCaseEdgeDto.Style(对象)
修改时间:
2025-01-19
修改原因:
用户要求修复 GetTestCaseFlowByIdQueryHandler 来正确映射现有后端数据到新的 ReactFlow 兼容的 DTO 格式,同时保持向后兼容性,确保前端能够正确接收和显示测试用例流程数据。
2025-01-19 - GetTestCaseFlowByIdQueryHandler 简化映射
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdQueryHandler.cs
修改内容:
-
移除向后兼容字段:
- 在
MapNodesToReactFlowFormatAsync方法中移除了所有向后兼容字段 - 在
MapEdgesToReactFlowFormatAsync方法中移除了所有向后兼容字段 - 只保留 ReactFlow 前端需要的格式字段
- 在
-
节点映射简化:
- 只输出前端需要的 ReactFlow 格式:
Id,Type,Position,Data,Width,Height,Selected,PositionAbsolute,Dragging - 移除了:
TestCaseId,NodeId,SequenceNumber,PositionX,PositionY,IsSelected,PositionAbsoluteX,PositionAbsoluteY,IsDragging
- 只输出前端需要的 ReactFlow 格式:
-
连线映射简化:
- 只输出前端需要的 ReactFlow 格式:
Id,Source,SourceHandle,Target,TargetHandle,Type,Animated,Style,Data - 移除了:
TestCaseId,EdgeId,SourceNodeId,TargetNodeId,EdgeType,Condition,IsAnimated,StyleJson
- 只输出前端需要的 ReactFlow 格式:
技术特性:
- 前端优先:直接输出 ReactFlow 前端需要的格式
- 数据精简:移除不必要的向后兼容字段,减少数据传输量
- 格式统一:确保输出格式与前端期望的 JSON 结构完全一致
前端格式示例:
{
"nodes": [
{
"id": "node-1755654432101",
"type": "testStep",
"position": { "x": 360, "y": 30 },
"data": {
"stepId": "e2192f5a-1582-47e9-92be-c676679418da",
"stepName": "StartStep",
"stepType": 1,
"stepTypeName": "Start",
"description": "Mapping_Start",
"icon": "play-circle"
},
"width": 95,
"height": 30,
"selected": false,
"positionAbsolute": { "x": 360, "y": 30 },
"dragging": false
}
]
}
修改时间:
2025-01-19
修改原因:
用户要求简化映射逻辑,直接输出前端需要的 ReactFlow 格式,不需要向后兼容字段,确保数据格式与前端期望的 JSON 结构完全一致。
2025-01-19 - GetTestCaseFlowsQueryHandler 参数修复
修改文件:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlows/GetTestCaseFlowsQueryHandler.cs
修改内容:
-
参数名称修复:
- 将
GetPagedFlowsAsync方法调用中的searchTerm参数名修正为name - 确保参数名称与
ITestCaseFlowRepository接口定义一致
- 将
-
类型转换修复:
- 添加了
TestFlowType枚举的解析逻辑 - 将
string?类型的request.Type转换为TestFlowType?类型 - 使用
Enum.TryParse进行安全的类型转换,避免解析失败
- 添加了
-
错误处理:
- 添加了空值检查,确保在
request.Type为空时不会进行解析 - 使用安全的枚举解析,避免无效类型值导致的异常
- 添加了空值检查,确保在
技术特性:
- 类型安全:确保参数类型与仓储接口定义完全匹配
- 错误处理:添加了安全的枚举解析,避免运行时异常
- 参数一致性:修正了参数名称,确保与接口定义一致
修复的问题:
GetPagedFlowsAsync方法调用时参数名称不匹配TestFlowType枚举类型转换缺失- 可能导致搜索功能无法正常工作
修改时间:
2025-01-19
修改原因:
用户反馈 GetTestCaseFlowsQueryHandler 中的 searchTerm 参数传递有问题,需要修复参数名称和类型转换问题,确保搜索功能正常工作。
2025-01-19 - TestCaseFlow 仓储模式实现和审计字段修复
修改文件:
X1.Domain/Repositories/TestCase/ITestCaseFlowRepository.cs- 创建 TestCaseFlow 仓储接口X1.Infrastructure/Repositories/TestCase/TestCaseFlowRepository.cs- 创建 TestCaseFlow 仓储实现并修复审计字段访问问题
修改内容:
-
TestCaseFlow 仓储接口创建:
- 创建了
ITestCaseFlowRepository接口,继承自IBaseRepository<TestCaseFlow> - 定义了完整的业务方法,包括基本的 CRUD 操作和特定的业务查询
- 支持测试用例流程的完整生命周期管理
- 创建了
-
主要业务方法:
- 基本操作:
AddTestCaseFlowAsync、UpdateTestCaseFlow、DeleteTestCaseFlowAsync - 状态管理:
EnableTestCaseFlowAsync、DisableTestCaseFlowAsync - 查询操作:
GetAllTestCaseFlowsAsync、GetTestCaseFlowByIdAsync、GetByNameAsync - 分类查询:
GetByTypeAsync、GetEnabledFlowsAsync - 详细查询:
GetTestCaseFlowWithDetailsAsync(包含节点和连线) - 验证操作:
NameExistsAsync - 分页查询:
GetPagedFlowsAsync(支持名称、类型、启用状态过滤)
- 基本操作:
-
TestCaseFlow 仓储实现创建:
- 创建了
TestCaseFlowRepository实现类,继承自BaseRepository<TestCaseFlow> - 实现了
ITestCaseFlowRepository接口的所有方法 - 使用 CQRS 模式,分离命令和查询操作
- 提供完整的日志记录和错误处理
- 创建了
-
审计字段修复:
- 问题:修复了 "属性或索引器'AuditableEntity.UpdatedAt'不能用在此上下文中,因为 set 访问器不可访问" 的编译错误
- 解决方案:参考
CaseStepConfigRepository的实现模式,简化仓储实现 - 修改内容:
- 移除了
ICurrentUserService依赖注入 - 移除了复杂的
SetCreated()和SetUpdated()方法调用 - 简化了
AddTestCaseFlowAsync方法,直接调用CommandRepository.AddAsync - 简化了
UpdateTestCaseFlow方法,直接调用CommandRepository.Update - 在
EnableTestCaseFlowAsync和DisableTestCaseFlowAsync方法中直接设置UpdatedAt = DateTime.UtcNow - 移除了详细的日志记录,保持与
CaseStepConfigRepository一致的简洁风格
- 移除了
-
技术特性:
- CQRS 模式:使用
ICommandRepository和IQueryRepository分离读写操作 - 异步支持:所有方法都支持异步操作和取消令牌
- 简洁实现:与现有仓储实现保持一致的简洁风格
- 性能优化:支持分页查询和条件过滤
- CQRS 模式:使用
-
分页查询功能:
- 支持按名称、类型、启用状态进行条件过滤
- 使用动态查询条件构建,支持可选参数
- 返回总记录数和分页数据
- 支持自定义页码和每页大小
-
详细查询功能:
GetTestCaseFlowWithDetailsAsync方法支持包含节点和连线的完整查询- 使用 Entity Framework 的
Include方法加载关联数据 - 适用于需要完整流程信息的场景
-
设计原则:
- DDD 原则:遵循领域驱动设计,仓储专注于数据访问
- 单一职责:每个方法专注于特定功能
- 可扩展性:支持未来功能扩展
- 一致性:与现有仓储实现(如
CaseStepConfigRepository)保持一致的架构模式
-
命名空间规范:
- 使用
X1.Domain.Repositories.TestCase命名空间 - 使用
X1.Infrastructure.Repositories.TestCase命名空间 - 与项目整体架构保持一致
- 使用
修改时间:
2025-01-19
修改原因:
用户要求在 CellularManagement.Domain.Repositories.TestCase 命名空间中实现 TestCaseFlow 的仓储模式,为测试用例流程管理提供完整的数据访问层支持,包括基本的 CRUD 操作和特定的业务查询功能。同时修复了审计字段访问权限问题,参考 CaseStepConfigRepository 的实现模式,简化仓储实现,确保与现有代码风格保持一致。
2025-01-19 - TestCaseTestFlow 命名规范和viewport属性修复
修改文件:
X1.Domain/Entities/TestCase/TestCaseTestFlow.cs- 重命名为TestCaseFlow并添加viewport属性X1.Domain/Entities/TestCase/TestCaseEdge.cs- 完善TestCaseEdge实体X1.Infrastructure/Context/AppDbContext.cs- 添加TestCaseFlow相关DbSet配置X1.Infrastructure/Configurations/TestCase/TestCaseFlowConfiguration.cs- 创建TestCaseFlow配置类X1.Infrastructure/Configurations/TestCase/TestCaseNodeConfiguration.cs- 创建TestCaseNode配置类X1.Infrastructure/Configurations/TestCase/TestCaseEdgeConfiguration.cs- 创建TestCaseEdge配置类
修改内容:
-
TestCaseTestFlow重命名为TestCaseFlow:
- 将类名从
TestCaseTestFlow改为TestCaseFlow,更符合命名规范 - 继承自
Entity基类,添加主键Id字段 - 添加viewport属性字段:ViewportX、ViewportY、ViewportZoom
- 设置viewport默认值:x=40.54057017483274, y=21.183463943747256, zoom=1.1367874248827994
- 将类名从
-
TestCaseEdge实体完善:
- 继承自
Entity基类,添加主键Id字段 - 添加完整的连线属性:TestCaseId、EdgeId、SourceNodeId、TargetNodeId
- 添加连线配置:EdgeType、Condition、IsAnimated、Style
- 添加导航属性关联TestCaseFlow
- 继承自
-
数据库配置:
- 在AppDbContext中添加TestCaseFlow、TestCaseNode、TestCaseEdge的DbSet配置
- 创建完整的Entity Framework配置类
- 配置表名规范:tb_testcaseflow、tb_testcasenode、tb_testcaseedge
- 添加完整的字段映射、索引和关系配置
-
技术特性:
- 命名规范:使用更简洁的TestCaseFlow命名
- 主键支持:所有实体都继承Entity基类,支持主键Id
- viewport支持:添加视口坐标和缩放级别属性
- 关系配置:完整的实体关系映射和级联删除配置
- 索引优化:为常用查询字段添加数据库索引
-
设计原则:
- 命名简洁:TestCaseFlow比TestCaseTestFlow更简洁明了
- 功能完整:支持完整的测试用例流程管理
- 数据完整性:通过外键关系和级联删除确保数据一致性
- 性能优化:通过索引配置提高查询性能
修改时间:
2025-01-19
修改原因:
用户要求修复TestCaseTestFlow的命名规范问题,主要是测试用例流程表,并添加viewport属性字段(x、y、zoom),以支持ReactFlow设计器的视口状态保存和恢复。
2025-01-19 - AdbOperationConfiguration数据库配置优化
修改文件:
X1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.cs - 修复Path字段的数据库约束
修改内容:
-
问题描述:
- 当前AdbOperationConfiguration中Path字段设置为
IsRequired(),与业务逻辑不一致 - 根据AdbOperation实体的业务规则,Path字段只有在UseAbsolutePath为true时才必填
- 数据库约束应该与业务逻辑保持一致
- 当前AdbOperationConfiguration中Path字段设置为
-
解决方案:
- 将Path字段的数据库约束从
IsRequired()改为IsRequired(false) - 更新字段注释,明确说明Path字段的必填条件
- 确保数据库层约束与业务层验证逻辑一致
- 将Path字段的数据库约束从
-
具体修改:
- 数据库约束:
IsRequired()→IsRequired(false) - 注释更新:添加"(当启用绝对路径时必填)"说明
- 业务一致性:与AdbOperation实体中的条件验证保持一致
- 数据库约束:
-
技术特性:
- 数据完整性:数据库约束与业务规则一致
- 灵活性:允许Path字段为空,符合业务需求
- 文档清晰:注释明确说明字段的使用条件
- 架构一致性:基础设施层与领域层保持一致
-
修改代码:
// 路径字段 builder.Property(x => x.Path) .IsRequired(false) // 改为允许为空 .HasMaxLength(500) .HasComment("命令执行时所依赖的路径(当启用绝对路径时必填)"); -
设计原则:
- 业务驱动:数据库设计服务于业务需求
- 一致性:各层之间的约束保持一致
- 灵活性:支持不同的业务场景
- 可维护性:清晰的注释便于理解和维护
修改时间:
2025-01-19
修改原因:
确保AdbOperationConfiguration的数据库约束与AdbOperation实体的业务逻辑保持一致,Path字段只有在UseAbsolutePath为true时才必填。
2025-01-19 - AdbOperations Commands层DeviceId修改限制
修改文件:
X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommand.cs - 移除DeviceId属性
X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs - 修复DeviceId处理逻辑
修改内容:
-
问题描述:
- UpdateAdbOperationCommand中仍然包含DeviceId属性,与实体层业务规则不一致
- UpdateAdbOperationCommandHandler仍然传递DeviceId参数给实体的Update方法
- 应用层与领域层的业务规则不一致,可能导致混淆
-
解决方案:
- 从UpdateAdbOperationCommand中移除DeviceId属性
- 修改UpdateAdbOperationCommandHandler,使用现有实体的DeviceId值
- 确保应用层与领域层的业务规则保持一致
-
具体修改:
- 命令对象:移除DeviceId属性,明确表示Update操作不涉及DeviceId修改
- 处理器逻辑:使用
existingOperation.DeviceId而不是request.DeviceId - 日志优化:移除日志中的DeviceId参数,避免误导
- 业务一致性:应用层与领域层规则完全一致
-
技术特性:
- 架构一致性:应用层与领域层业务规则保持一致
- 数据完整性:防止通过应用层意外修改DeviceId
- 代码清晰:明确表达Update操作的业务约束
- 错误预防:在应用层就避免传递不允许修改的参数
-
修改代码:
// UpdateAdbOperationCommand.cs - 移除DeviceId属性 public class UpdateAdbOperationCommand : IRequest<OperationResult<UpdateAdbOperationResponse>> { public string Id { get; set; } = string.Empty; // DeviceId属性已移除 public string Command { get; set; } = string.Empty; // ... 其他属性 } // UpdateAdbOperationCommandHandler.cs - 修复DeviceId处理 existingOperation.Update( existingOperation.DeviceId, // 使用现有的DeviceId,不允许修改 request.Command, // ... 其他参数 ); -
设计原则:
- 单一职责:Update命令只处理允许修改的字段
- 业务驱动:应用层设计服务于业务规则
- 防御性编程:在多个层面防止数据不一致
- 可维护性:清晰的代码结构便于理解和维护
修改时间:
2025-01-19
修改原因:
确保AdbOperations Commands层的Update操作与AdbOperation实体的业务规则保持一致,防止DeviceId被意外修改,维护数据完整性和业务逻辑的一致性。
2025-01-19 - AdbOperation实体DeviceId更新限制
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.cs - 限制Update方法中DeviceId的修改
修改内容:
-
问题描述:
- 当前Update方法允许修改DeviceId,这可能导致数据一致性问题
- 用户要求AdbOperation的Update操作不能修改deviceId
- 需要添加业务规则限制DeviceId的修改
-
解决方案:
- 在Update方法中添加DeviceId修改验证
- 比较传入的deviceId参数与当前DeviceId属性值
- 如果不匹配则抛出异常,阻止更新操作
- 移除DeviceId的赋值操作,保持原有值
-
具体修改:
- 验证逻辑:添加
if (!string.Equals(DeviceId, deviceId.Trim(), StringComparison.OrdinalIgnoreCase))检查 - 错误处理:抛出
ArgumentException("设备ID不允许修改", nameof(deviceId)) - 赋值移除:注释掉
DeviceId = deviceId.Trim();并添加说明注释
- 验证逻辑:添加
-
技术特性:
- 数据完整性:确保DeviceId在更新操作中保持不变
- 业务规则:实现业务逻辑约束
- 错误提示:提供明确的错误信息
- 大小写不敏感:使用
StringComparison.OrdinalIgnoreCase进行比较
-
验证逻辑:
// 设备ID不允许修改 if (!string.Equals(DeviceId, deviceId.Trim(), StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("设备ID不允许修改", nameof(deviceId)); // 不更新DeviceId,保持原有值 // DeviceId = deviceId.Trim(); // 已移除 -
设计原则:
- 数据一致性:防止关键字段被意外修改
- 业务约束:符合ADB操作的业务规则
- 用户友好:提供清晰的错误提示
- 安全性:防止数据完整性问题
修改时间:
2025-01-19
修改原因:
用户要求AdbOperation的Update操作不能修改deviceId,确保数据一致性和业务规则的正确性。
2025-01-19 - AdbOperation实体路径验证优化
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.cs - 优化路径验证逻辑,实现条件验证
修改内容:
-
问题描述:
- 当前路径验证逻辑过于严格,无论是否启用绝对路径都要求路径不能为空
- 用户希望只有当
UseAbsolutePath启用时,Path字段才不能为空 - 需要实现条件验证逻辑
-
解决方案:
- 修改
Create和Update方法中的路径验证逻辑 - 添加条件判断:只有当
useAbsolutePath为true时,才验证路径不能为空 - 更新错误消息以明确说明验证条件
- 修改
-
具体修改:
- Create方法:将
if (string.IsNullOrWhiteSpace(path))改为if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) - Update方法:将
if (string.IsNullOrWhiteSpace(path))改为if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) - 错误消息:从"路径不能为空"改为"启用绝对路径时,路径不能为空"
- Create方法:将
-
技术特性:
- 条件验证:根据业务逻辑实现智能验证
- 用户体验:允许在非绝对路径模式下路径为空
- 业务逻辑:符合ADB操作的业务需求
- 错误提示:提供更明确的错误信息
-
验证逻辑:
// 修改前 if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("路径不能为空", nameof(path)); // 修改后 if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) throw new ArgumentException("启用绝对路径时,路径不能为空", nameof(path)); -
设计原则:
- 业务导向:验证逻辑符合实际业务需求
- 用户友好:提供清晰的错误提示
- 灵活性:支持不同的路径使用模式
- 一致性:在创建和更新操作中保持一致的验证逻辑
修改时间:
2025-01-19
修改原因:
用户反馈需要实现条件验证,只有当启用绝对路径时路径字段才不能为空,提高业务逻辑的灵活性。
2025-01-19 - ADB操作Drawer布局优化
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 优化Drawer布局,添加滚动条支持
修改内容:
-
问题描述:
- 当ADB命令列表增加时,Drawer布局无法正常显示所有内容
- 缺少滚动条支持,用户体验不佳
- 布局结构需要优化以支持动态内容
-
解决方案:
- 重新设计布局结构,将内容分为固定区域和可滚动区域
- 为命令列表区域添加独立的滚动条
- 使用Flexbox布局确保各区域正确显示
-
具体修改:
- 固定区域:设备选择、操作描述、ADB路径、选项设置等
- 可滚动区域:命令列表区域,支持垂直滚动
- 布局优化:使用
flex-shrink-0和flex-1控制区域大小
-
技术特性:
- 响应式滚动:命令列表区域支持垂直滚动
- 固定布局:重要操作区域保持固定位置
- 视觉优化:添加背景色和边框改善视觉效果
- 用户体验:支持任意数量的命令添加
-
布局结构:
// 固定头部 <DrawerHeader className="flex-shrink-0"> // 内容区域 <DrawerContent className="flex flex-col flex-1 min-h-0 p-0 overflow-hidden"> // 固定区域:设备选择 <div className="flex-shrink-0 p-4 pb-2"> // 可滚动区域:命令列表 <div className="flex-1 min-h-0 flex flex-col px-4"> <div className="flex-1 overflow-y-auto"> <div className="space-y-3 pr-2"> // 固定区域:其他表单字段 <div className="flex-shrink-0 p-4 pt-2"> // 固定底部 <DrawerFooter className="flex-shrink-0"> -
设计原则:
- 空间利用:合理分配固定和可滚动区域
- 操作便利:重要操作区域保持可见
- 扩展性:支持动态添加命令
- 一致性:与其他Drawer组件保持一致的交互模式
修改时间:
2025-01-19
修改原因:
用户反馈当添加多个ADB命令时,Drawer布局无法正常显示,需要添加滚动条优化布局。
2025-01-19 - ADB操作Drawer空白优化
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 优化布局空白,减少多余间距
修改内容:
-
问题描述:
- 用户反馈布局存在"多余空白"问题
- 内边距和外边距重叠导致空间浪费
- 滚动区域与固定区域间距过大
-
解决方案:
- 重新调整内边距和外边距结构
- 优化各区域之间的间距
- 减少不必要的空白区域
-
具体修改:
- DrawerContent:移除
p-4,改为p-0 - 设备选择区域:使用
p-4 pb-2替代mb-4 - 命令列表区域:使用
px-4替代整体内边距 - 滚动区域:移除
pr-2,改为在内容区域使用pr-2 - 其他表单字段:使用
p-4 pt-2替代mt-4
- DrawerContent:移除
-
技术特性:
- 紧凑布局:减少不必要的空白区域
- 精确控制:为不同区域设置合适的内边距
- 视觉优化:改善整体布局的紧凑性
- 用户体验:提供更好的空间利用率
-
布局优化:
// 优化前:存在多余空白 <DrawerContent className="p-4"> <div className="mb-4">设备选择</div> <div className="flex-1"> <div className="overflow-y-auto pr-2"> <div className="mt-4">其他字段</div> // 优化后:紧凑布局 <DrawerContent className="p-0"> <div className="p-4 pb-2">设备选择</div> <div className="flex-1 px-4"> <div className="overflow-y-auto"> <div className="pr-2"> <div className="p-4 pt-2">其他字段</div> -
设计原则:
- 空间效率:最大化利用可用空间
- 视觉平衡:保持适当的间距和层次
- 操作便利:确保重要操作区域易于访问
- 一致性:与其他组件保持一致的间距规范
修改时间:
2025-01-19
修改原因:
用户反馈布局存在"多余空白"问题,需要优化间距结构,提供更紧凑的布局。
2025-01-19 - ADB操作路径字段条件验证
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 实现ADB路径字段的条件验证
修改内容:
-
问题描述:
- 用户反馈ADB路径字段的验证逻辑需要优化
- 当前路径字段始终为可选,但实际使用中需要条件验证
- 当选择"使用绝对路径"时,路径字段应该为必填
-
解决方案:
- 实现条件验证逻辑,根据"使用绝对路径"选项决定路径字段是否必填
- 动态显示必填标识符
- 添加错误提示和样式
-
具体修改:
- 验证逻辑:在
validateForm函数中添加条件验证 - UI标识:动态显示红色星号标识必填字段
- 错误处理:添加路径字段的错误提示和样式
- 用户体验:提供清晰的验证反馈
- 验证逻辑:在
-
技术特性:
- 条件验证:根据
useAbsolutePath状态决定验证规则 - 动态UI:必填标识符根据条件动态显示
- 错误反馈:提供明确的错误信息和视觉提示
- 表单完整性:确保数据验证的准确性
- 条件验证:根据
-
验证逻辑:
// 验证ADB路径 - 只有在使用绝对路径时才必填 if (formData.useAbsolutePath && !formData.path.trim()) { newErrors.path = '使用绝对路径时必须指定ADB路径'; } -
UI优化:
<Label htmlFor="path" className="text-sm font-medium"> ADB路径 {formData.useAbsolutePath && <span className="text-red-500">*</span>} </Label> <Input className={errors.path ? 'border-red-500 focus:border-red-500' : ''} /> {errors.path && ( <p className="text-xs text-red-500">{errors.path}</p> )} -
设计原则:
- 逻辑一致性:验证规则与业务逻辑保持一致
- 用户友好:提供清晰的必填标识和错误提示
- 视觉反馈:使用颜色和样式区分不同状态
- 数据完整性:确保提交数据的有效性
修改时间:
2025-01-19
修改原因:
用户反馈ADB路径字段需要条件验证,只有在选择"使用绝对路径"时才应该为必填字段。
2025-01-19 - ADB操作Drawer单命令布局优化
修改文件:
X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx - 优化单命令情况下的布局空白
修改内容:
-
问题描述:
- 用户反馈"当添加命令默认一个就会剩余很多空白"
- 当只有一个命令时,命令列表区域占用过多空间
- 布局在单命令情况下不够紧凑,存在大量空白区域
-
解决方案:
- 根据命令数量动态调整布局结构
- 当命令数量较少时,不使用弹性布局占用剩余空间
- 只有当命令数量超过2个时才启用滚动区域
-
具体修改:
- 条件布局:根据
commands.length > 2决定是否使用弹性布局 - 动态类名:使用模板字符串动态设置CSS类名
- 滚动优化:只在需要时启用滚动条
- 空间利用:减少单命令情况下的空白区域
- 条件布局:根据
-
技术特性:
- 自适应布局:根据内容数量调整布局策略
- 条件渲染:动态应用CSS类名
- 空间优化:减少不必要的空白区域
- 用户体验:提供更紧凑的单命令布局
-
布局逻辑:
// 命令列表区域 - 根据命令数量决定布局 <div className={`px-4 ${commands.length > 2 ? 'flex-1 min-h-0 flex flex-col' : ''}`}> // 命令列表区域 - 根据命令数量决定是否可滚动 <div className={commands.length > 2 ? 'flex-1 overflow-y-auto' : ''}> <div className={`space-y-3 ${commands.length > 2 ? 'pr-2' : ''}`}> -
设计原则:
- 内容驱动:布局根据实际内容数量调整
- 空间效率:避免不必要的空白区域
- 渐进增强:随着内容增加自动启用滚动
- 视觉平衡:保持整体布局的协调性
修改时间:
2025-01-19
修改原因:
用户反馈当只有一个命令时,布局存在大量空白区域,需要优化单命令情况下的布局紧凑性。
2025-01-19 - 抽屉组件内间距优化
修改文件:
X1.WebUI/src/pages/ran-configurations/RANConfigurationDrawer.tsx- 为抽屉内容添加内间距X1.WebUI/src/pages/network-stack-configs/NetworkStackConfigDrawer.tsx- 为抽屉内容添加内间距X1.WebUI/src/pages/ims-configurations/IMSConfigurationDrawer.tsx- 为抽屉内容添加内间距X1.WebUI/src/pages/core-network-configs/CoreNetworkConfigDrawer.tsx- 为抽屉内容添加内间距
修改内容:
-
问题描述:
- RANConfigurationDrawer 抽屉内容太贴着边缘,缺少适当的内间距
- 用户体验不佳,内容显示过于紧凑
-
解决方案:
- 为
DrawerContent组件添加p-6类名 - 提供 24px 的内边距,改善视觉效果和用户体验
- 为
-
具体修改:
// 修改前 <DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto"> // 修改后 <DrawerContent className="flex flex-col space-y-4 flex-1 overflow-y-auto p-4"> -
技术特性:
- 响应式设计:内间距适配不同屏幕尺寸
- 视觉优化:提供更好的内容层次和可读性
- 用户体验:改善表单内容的显示效果
- 一致性:与其他抽屉组件保持一致的样式
-
设计原则:
- 空间利用:合理利用抽屉空间,避免内容过于紧凑
- 视觉层次:通过内间距建立清晰的内容层次
- 用户友好:提供舒适的视觉体验
修改时间:
2025-01-19
修改原因:
用户反馈 RANConfigurationDrawer 内容太贴着边缘,需要添加适当的内间距来改善视觉效果和用户体验。
2025-01-19 - ReactFlowDesigner 导入导出功能实现
修改文件:
X1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx - 实现Flow数据的导入和导出功能
修改内容:
-
导入功能实现:
- 文件选择:添加了文件选择按钮,支持选择JSON格式的Flow文件
- 数据验证:验证导入文件是否包含有效的nodes和edges数据
- 状态恢复:导入成功后恢复nodes、edges和viewport状态
- 错误处理:提供详细的错误提示和异常处理
- 重复导入:支持重复导入同一文件
-
导出功能实现:
- 数据收集:收集当前的nodes、edges和viewport状态
- 元数据添加:添加版本、导出时间、描述等元数据信息
- 文件生成:生成格式化的JSON文件
- 自动下载:自动触发文件下载,文件名包含日期信息
- 资源清理:自动清理临时创建的URL对象
-
UI组件设计:
- 工具栏按钮:在工具栏中添加导入和导出按钮
- 图标设计:使用SVG图标,导入使用向下箭头,导出使用向上箭头
- 悬停效果:导入按钮使用蓝色主题,导出按钮使用绿色主题
- 响应式设计:按钮适配不同屏幕尺寸
-
成功提示功能:
- 通知组件:添加导入成功通知,显示在右上角
- 动画效果:使用slideIn动画,从右侧滑入
- 自动关闭:提供关闭按钮,点击后隐藏通知
- 状态管理:使用useState管理通知显示状态
-
样式设计:
- 按钮样式:使用现代化的按钮设计,包含图标和文字
- 通知样式:使用绿色主题的成功通知样式
- 动画效果:添加CSS动画和过渡效果
- 响应式布局:适配不同屏幕尺寸
-
技术特性:
- 文件处理:使用FileReader API读取本地文件
- 数据序列化:使用JSON.stringify格式化数据
- Blob处理:使用Blob和URL.createObjectURL创建下载链接
- 状态管理:使用React Hooks管理组件状态
- 错误处理:完整的异常捕获和用户提示
-
数据格式:
{ "nodes": [...], "edges": [...], "viewport": {...}, "metadata": { "name": "Test Case Flow", "version": "1.0", "exportDate": "2025-01-19T10:30:00.000Z", "description": "Exported test case flow data" } } -
用户体验:
- 直观操作:点击按钮即可导入或导出Flow数据
- 即时反馈:操作结果立即显示,成功或失败都有明确提示
- 文件命名:导出文件自动包含日期信息,便于管理
- 错误提示:详细的错误信息帮助用户理解问题
修改时间:
2025-01-19
修改原因:
用户需要在测试用例流程设计器中实现Flow数据的导入和导出功能,以便保存和恢复流程设计,提高工作效率和数据管理能力。
修复记录:
- 2025-01-19:修复了导出功能中的Hook调用错误,将
useReactFlow()调用移到组件顶层,避免在普通函数中调用Hook
2025-01-19 - AdbOperation数据库迁移应用
修改文件:
数据库 tb_adboperations 表结构
修改内容:
-
执行状态:
- ✅ 迁移文件创建:成功
- ✅ 数据库更新:成功
- ✅ 数据库结构与代码配置:完全一致
-
数据库变更:
- 表名:
tb_adboperations - 字段名:
Path - 变更类型:从
NOT NULL改为NULL(允许为空) - 注释更新:从"命令执行时所依赖的路径"改为"命令执行时所依赖的路径(当启用绝对路径时必填)"
- 表名:
-
执行的SQL命令:
ALTER TABLE tb_adboperations ALTER COLUMN "Path" DROP NOT NULL; COMMENT ON COLUMN tb_adboperations."Path" IS '命令执行时所依赖的路径(当启用绝对路径时必填)'; -
迁移历史:
- 迁移ID:
20250820020118_UpdateAdbOperationPathNullable - 已记录到
__EFMigrationsHistory表
- 迁移ID:
-
业务影响:
- 现在
Path字段在UseAbsolutePath为false时可以为空 - 在
UseAbsolutePath为true时仍然必填(由业务逻辑验证) - 数据库约束与领域层业务规则完全一致
- 现在
-
技术验证:
- 构建成功:
Build succeeded - 数据库连接正常:配置验证通过
- 迁移执行无错误:所有SQL命令成功执行
- 构建成功:
修改时间:
2025-01-19
修改原因:
应用之前创建的数据库迁移,将AdbOperationConfiguration中Path字段的IsRequired约束变更同步到数据库,确保数据库结构与代码配置完全一致,支持Path字段在UseAbsolutePath为false时可以为空的业务需求。
2025-01-19 - TestCaseEdge Type字段必填验证
修改文件:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs- 将EdgeData的Type字段改为必填X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs- 添加Type字段验证并移除默认值逻辑
修改内容:
-
EdgeData Type字段必填化:
- 问题描述:用户反馈
edgeType: edgeData.Type ?? "straight"中的Type字段应该是必填项,界面不能传空 - 解决方案:将Type字段从可选改为必填,添加验证逻辑,移除默认值处理
- 问题描述:用户反馈
-
CreateTestCaseFlowCommand.cs 修改:
- 字段类型:将
Type从string?改为string - 验证特性:添加
[Required(ErrorMessage = "连线类型不能为空")]验证特性 - 默认值:移除默认值,设置为
null!
- 字段类型:将
-
CreateTestCaseFlowCommandHandler.cs 修改:
- 验证逻辑:在
ValidateRequest方法中添加对edge.Type的空值验证 - 创建逻辑:在
CreateEdgesAsync方法中移除?? "straight"默认值逻辑 - 错误处理:提供明确的错误信息:"连线类型不能为空"
- 验证逻辑:在
-
技术特性:
- 数据验证:确保连线类型字段不为空
- 用户友好:提供清晰的错误提示信息
- 业务逻辑:符合业务需求,连线类型必须明确指定
- 类型安全:使用强类型验证,避免运行时错误
-
设计原则:
- 数据完整性:确保必要字段不为空
- 用户体验:提供明确的验证反馈
- 业务规则:符合测试用例流程设计的业务需求
- 代码一致性:与项目中其他必填字段的处理方式保持一致
修改时间:
2025-01-19
修改原因:
用户要求确保TestCaseEdge的Type字段是必填项,界面不能传空值,需要添加相应的验证逻辑并移除默认值处理,确保数据完整性和业务逻辑的正确性。
2025-01-16 测试用例列表组件创建
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 创建测试用例列表组件X1.WebUI/src/routes/AppRouter.tsx- 更新路由配置
修改内容:
-
问题描述:
testcases/list和testcases/create路由都使用了同一个组件TestCasesViewTestCasesView是一个流程设计器,用于创建测试用例testcases/list应该显示一个测试用例列表的表格
-
解决方案:
- 创建了新的
TestCasesListView组件,专门用于显示测试用例列表 - 更新路由配置,让
testcases/list使用新的列表组件 - 保持
testcases/create继续使用原有的流程设计器组件
- 创建了新的
-
TestCasesListView组件特性:
- 表格显示:使用表格组件显示测试用例列表
- 搜索功能:支持按测试用例名称或描述搜索
- 状态管理:显示测试用例的状态(激活、停用、草稿)
- 优先级管理:显示测试用例的优先级(高、中、低)
- 操作按钮:提供查看、编辑、删除操作
- 创建按钮:提供创建新测试用例的快捷入口
-
路由配置更新:
- 添加了
TestCasesListView组件的lazy加载导入 - 更新
testcases/list路由使用新的列表组件 - 保持
testcases/create路由使用原有的流程设计器组件
- 添加了
-
技术特性:
- 使用React + TypeScript开发
- 采用shadcn/ui组件库
- 支持响应式设计
- 完整的错误处理和用户反馈
- 模拟数据展示功能
-
UI设计:
- 现代化的表格设计
- 状态徽章和优先级徽章
- 搜索工具栏
- 操作按钮图标化
- 响应式布局
修改时间:
2025-01-16
修改原因:
解决测试用例列表和创建页面显示相同内容的问题,为测试用例管理提供正确的列表视图和创建视图分离。
2025-01-19 - 测试用例列表抽屉查看功能增强
修改文件:
X1.WebUI/src/pages/testcases/TestCasesListView.tsx - 添加抽屉式流程查看功能
修改内容:
-
功能增强:
- 添加了抽屉组件来显示测试用例流程详情
- 集成了ReactFlow来可视化显示流程节点和连线
- 实现了异步加载流程数据的功能
- 添加了加载状态指示器
-
抽屉组件特性:
- Drawer组件:使用shadcn/ui的Drawer组件
- 高度设置:抽屉高度为90vh,提供充足的显示空间
- 标题显示:显示测试用例名称和描述
- 响应式设计:适配不同屏幕尺寸
-
ReactFlow集成:
- 流程可视化:使用ReactFlow显示流程节点和连线
- 控件支持:包含Controls(缩放、适应视图)、Background(网格背景)、MiniMap(小地图)
- 自动适应:使用fitView自动适应视图大小
- 样式设置:使用灰色背景提升视觉效果
-
数据转换功能:
- 节点转换:将后端节点数据转换为ReactFlow节点格式
- 连线转换:将后端连线数据转换为ReactFlow连线格式
- 位置信息:支持节点的位置、尺寸、选中状态等信息
- 样式支持:支持连线的样式、动画、数据等属性
-
状态管理:
- 选中状态:
selectedTestCase存储当前选中的测试用例详情 - 抽屉状态:
drawerOpen控制抽屉的打开/关闭 - 加载状态:
flowLoading控制流程数据的加载状态 - 错误处理:完整的错误处理和用户提示
- 选中状态:
-
用户体验优化:
- 加载指示器:显示旋转动画和"加载流程中..."提示
- 按钮状态:查看按钮在加载时禁用,防止重复点击
- 错误提示:当加载失败时显示"未找到流程数据"
- 交互反馈:点击查看按钮立即显示加载状态
-
技术实现:
- 异步加载:使用
handleViewTestCase函数异步加载流程详情 - 数据获取:调用
testcaseService.getTestCaseFlowById获取完整流程数据 - 类型安全:使用TypeScript确保类型安全
- 组件导入:正确导入ReactFlow相关组件和样式
- 异步加载:使用
-
流程数据处理:
const getReactFlowData = () => { if (!selectedTestCase) return { nodes: [], edges: [] }; const nodes: Node[] = selectedTestCase.nodes.map(node => ({ id: node.id, type: node.type || 'default', position: node.position, data: node.data, width: node.width, height: node.height, selected: node.selected, positionAbsolute: node.positionAbsolute, dragging: node.dragging })); const edges: Edge[] = selectedTestCase.edges.map(edge => ({ id: edge.id, source: edge.source, sourceHandle: edge.sourceHandle, target: edge.target, targetHandle: edge.targetHandle, type: edge.type || 'smoothstep', animated: edge.animated, style: edge.style, data: edge.data })); return { nodes, edges }; };
修改时间:
2025-01-19
修改原因:
用户要求在查看测试用例时采用抽屉方式显示流程,并加载处理流程数据,提供更好的用户体验和流程可视化能力。
2025-01-19 - 测试用例详情抽屉组件提取
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx- 创建独立的测试用例详情抽屉组件X1.WebUI/src/pages/testcases/TestCasesListView.tsx- 重构使用独立抽屉组件
修改内容:
-
组件提取:
- 将测试用例详情抽屉功能从
TestCasesListView中提取为独立组件 - 创建了
TestCaseDetailDrawer组件,位于@/components/testcases/目录 - 实现了组件的复用性和代码累计
- 将测试用例详情抽屉功能从
-
TestCaseDetailDrawer组件特性:
- Props接口:定义了
testCaseId、open、onOpenChange三个属性 - 状态管理:内部管理
selectedTestCase、flowLoading、error状态 - 生命周期管理:使用
useEffect监听抽屉打开状态,自动加载数据 - 错误处理:完整的错误状态管理和重试功能
- Props接口:定义了
-
功能增强:
- 自动加载:抽屉打开时自动加载测试用例详情
- 状态清理:抽屉关闭时自动清理状态
- 错误重试:提供重试按钮,用户可以重新加载数据
- 加载指示器:显示旋转动画和加载提示
-
TestCasesListView重构:
- 简化状态:移除了
selectedTestCase、flowLoading等状态 - 简化逻辑:
handleViewTestCase函数简化为只设置ID和打开抽屉 - 组件使用:使用
<TestCaseDetailDrawer>组件替代原有的抽屉代码 - 代码减少:大幅减少了组件代码量,提高可维护性
- 简化状态:移除了
-
技术特性:
- 组件复用:抽屉组件可以在其他页面中复用
- 职责分离:列表页面专注于列表显示,抽屉组件专注于详情显示
- 类型安全:完整的TypeScript类型定义
- 错误边界:独立的错误处理,不影响主页面
-
用户体验优化:
- 响应式设计:抽屉高度为90vh,适配不同屏幕
- 加载反馈:清晰的加载状态指示
- 错误恢复:提供重试机制,提升用户体验
- 状态同步:抽屉状态与父组件完全同步
-
代码结构:
// TestCaseDetailDrawer.tsx interface TestCaseDetailDrawerProps { testCaseId: string | null; open: boolean; onOpenChange: (open: boolean) => void; } // TestCasesListView.tsx const [selectedTestCaseId, setSelectedTestCaseId] = useState<string | null>(null); const [drawerOpen, setDrawerOpen] = useState(false); const handleViewTestCase = (id: string) => { setSelectedTestCaseId(id); setDrawerOpen(true); }; -
设计原则:
- 单一职责:每个组件专注于特定功能
- 可复用性:抽屉组件可以在多个地方使用
- 可维护性:代码结构清晰,易于维护和扩展
- 性能优化:按需加载,减少不必要的渲染
修改时间:
2025-01-19
修改原因:
用户要求将测试用例详情抽屉提取为独立组件,实现代码累计和复用,提高代码的可维护性和组件的可复用性。
2025-01-19 - Drawer组件缺失导出修复
修改文件:
X1.WebUI/src/components/ui/drawer.tsx - 添加缺失的DrawerTitle和DrawerDescription组件
修改内容:
-
问题描述:
TestCaseDetailDrawer组件导入DrawerDescription和DrawerTitle时出现错误- 错误信息:
The requested module '/src/components/ui/drawer.tsx' does not provide an export named 'DrawerDescription' - drawer.tsx 文件中缺少这两个组件的定义和导出
-
解决方案:
- 在 drawer.tsx 中添加了
DrawerTitle组件 - 在 drawer.tsx 中添加了
DrawerDescription组件 - 为两个组件添加了完整的 TypeScript 接口定义
- 在 drawer.tsx 中添加了
-
DrawerTitle组件:
- 接口定义:
DrawerTitleProps包含children和className属性 - 样式设计:使用
text-lg font-semibold类名,提供标题样式 - HTML结构:使用
<h2>标签,符合语义化设计
- 接口定义:
-
DrawerDescription组件:
- 接口定义:
DrawerDescriptionProps包含children和className属性 - 样式设计:使用
text-sm text-muted-foreground类名,提供描述文本样式 - HTML结构:使用
<p>标签,符合语义化设计
- 接口定义:
-
技术特性:
- 类型安全:完整的 TypeScript 接口定义
- 样式一致性:使用
cn工具函数合并类名 - 可扩展性:支持自定义 className 属性
- 语义化:使用合适的 HTML 标签
-
组件结构:
interface DrawerTitleProps { children: React.ReactNode className?: string } export function DrawerTitle({ children, className }: DrawerTitleProps) { return ( <h2 className={cn("text-lg font-semibold", className)}> {children} </h2> ) } interface DrawerDescriptionProps { children: React.ReactNode className?: string } export function DrawerDescription({ children, className }: DrawerDescriptionProps) { return ( <p className={cn("text-sm text-muted-foreground", className)}> {children} </p> ) } -
设计原则:
- 一致性:与其他 Drawer 组件保持一致的接口设计
- 可复用性:组件可以在其他抽屉组件中复用
- 可维护性:清晰的代码结构和类型定义
- 用户体验:提供合适的默认样式
修改时间:
2025-01-19
修改原因:
修复 drawer.tsx 组件库中缺失的 DrawerTitle 和 DrawerDescription 组件导出,解决 TestCaseDetailDrawer 组件的导入错误问题。
2025-01-19 - TestCaseDetailDrawer 样式与 ReactFlowDesigner 保持一致
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 参考 ReactFlowDesigner 添加自定义节点样式
修改内容:
-
样式一致性要求:
- 用户要求 TestCaseDetailDrawer 中的 ReactFlow 样式与 ReactFlowDesigner 保持完全一致
- 需要添加自定义节点类型和视觉效果
-
自定义节点组件:
- TestStepNode 组件:完全复制 ReactFlowDesigner 中的自定义节点组件
- 图标组件:支持所有图标类型(Play、Square、GitBranch、Smartphone等)
- 节点样式:根据步骤类型显示不同的形状和颜色
- 连接点:根据节点类型显示不同的输入输出连接点
-
节点样式规则:
- 开始步骤 (type=1):圆形,蓝色主题,只有输出连接点
- 结束步骤 (type=2):圆形,红色主题,只有输入连接点
- 处理步骤 (type=3):矩形,绿色主题,有输入和输出连接点
- 判断步骤 (type=4):菱形,紫色主题,有输入和输出连接点
-
图标系统:
- 设备相关图标:蓝色背景(smartphone、phone、wifi等)
- 网络相关图标:绿色背景(network、activity)
- 控制相关图标:橙色背景(play-circle、stop-circle)
- 配置相关图标:紫色背景(settings、git-branch)
-
ReactFlow 配置:
- 节点类型:使用自定义
testStep节点类型 - 视图设置:默认缩放1.5,最小1倍,最大2倍
- 网格对齐:启用15x15网格对齐
- 控件:包含 Controls、Background、MiniMap
- 节点类型:使用自定义
-
技术特性:
- ReactFlowProvider:包装组件以支持 useReactFlow hook
- fitView:自动适应视图大小
- 延迟渲染:使用 setTimeout 确保节点正确渲染后再执行 fitView
- 状态管理:完整的加载、错误、数据状态管理
-
视觉效果:
- 选中状态:蓝色环形高亮
- 悬停效果:边框颜色变化
- 连接点:彩色圆形连接点,支持悬停效果
- 背景:灰色背景,与 ReactFlowDesigner 一致
-
组件结构:
// 自定义节点组件 const TestStepNode = ({ data, selected }: { data: any; selected?: boolean }) => { // 图标组件映射 const getIconComponent = (iconName: string) => { ... }; // 节点样式映射 const getNodeStyle = (stepType: number) => { ... }; // 图标背景色映射 const getIconBgColor = (iconName: string) => { ... }; // 渲染不同形状的节点 return ( <div className={`group relative transition-all duration-200`}> {/* 根据步骤类型渲染不同形状 */} {/* 根据步骤类型渲染不同连接点 */} </div> ); };
修改时间:
2025-01-19
修改原因:
用户要求 TestCaseDetailDrawer 中的 ReactFlow 样式与 ReactFlowDesigner 保持完全一致,确保查看模式下的流程显示与设计模式下的样式完全相同。
2025-01-19 - TestCaseDetailDrawer 移除小地图和调试数据映射
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 移除小地图组件并添加调试信息
修改内容:
-
移除小地图:
- 根据用户要求"不需要小地图",从 ReactFlow 组件中移除了
<MiniMap />组件 - 保留
<Controls />和<Background />组件
- 根据用户要求"不需要小地图",从 ReactFlow 组件中移除了
-
添加调试信息:
- 在
getReactFlowData函数中添加了console.log调试信息 - 输出原始节点数据和每个节点的处理过程
- 帮助诊断节点显示"Unknown"的问题
- 在
-
调试信息内容:
console.log('原始节点数据:', testCase.nodes); console.log('处理节点:', node); console.log('节点数据:', node.data); -
技术特性:
- 调试友好:添加详细的日志输出,便于问题诊断
- 用户需求响应:移除不需要的小地图组件
- 数据验证:通过日志验证数据映射是否正确
修改时间:
2025-01-19
修改原因:
- 用户明确要求"不需要小地图"
- 节点仍然显示"Unknown"问题需要进一步调试
- 需要验证数据映射是否正确工作
2025-01-19 - TestCaseDetailDrawer 增强调试和数据映射修复
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx - 增强调试信息并修复数据映射
修改内容:
-
增强调试信息:
- 在
getReactFlowData函数中添加了更详细的日志输出 - 添加了原始连线数据的日志输出
- 添加了节点位置信息的日志输出
- 在
TestStepNode组件中添加了数据接收的日志输出
- 在
-
修复数据映射:
- 使用可选链操作符
?.来安全访问数据字段 - 为所有数据字段提供默认值,防止 undefined 错误
- 为节点尺寸提供默认值(width: 150, height: 50)
- 为连线样式提供默认值
- 使用可选链操作符
-
数据安全处理:
// 节点数据安全处理 const nodeData = { stepId: node.data?.stepId || node.id, stepName: node.data?.stepName || 'Unknown', stepType: node.data?.stepType || 3, stepTypeName: node.data?.stepTypeName || '处理步骤', description: node.data?.description || '', icon: node.data?.icon || 'settings' }; // 连线数据安全处理 style: edge.style || { stroke: '#333', strokeWidth: 2 }, data: edge.data || {} -
调试信息内容:
- 原始节点数据和连线数据
- 每个节点的处理过程
- 节点位置信息
- 处理后的节点数据
- TestStepNode 组件接收到的数据
-
技术特性:
- 数据安全:使用可选链和默认值防止运行时错误
- 调试友好:详细的日志输出便于问题诊断
- 容错性:即使数据不完整也能正常显示
- 兼容性:与 ReactFlowDesigner 的数据结构保持一致
修改时间:
2025-01-19
修改原因:
- 节点仍然显示"Unknown"且没有连接
- 样式与 ReactFlowDesigner 完全不同
- 需要深入调试数据映射问题
- 确保数据安全处理,防止运行时错误
2025-01-16 StartTerminalServiceCommandHandler 代码优化
修改文件:
X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommandHandler.cs- 优化WebSocket连接创建逻辑
修改内容:
-
参数验证增强:
- 服务端点验证:检查
serviceEndpoint是否为空,避免空引用异常 - 服务编码验证:检查
existingService.ServiceCode是否为空,确保WebSocket客户端名称有效 - 用户信息验证:保持原有的用户ID验证逻辑
- 服务端点验证:检查
-
WebSocket连接创建优化:
- 请求参数完善:添加
HeartbeatInterval参数,设置为30秒心跳间隔 - 异常处理增强:使用 try-catch 包装WebSocket连接创建过程
- 响应验证详细化:
- 检查响应是否为null
- 检查响应成功状态
- 提供详细的错误信息
- 请求参数完善:添加
-
日志记录改进:
- 操作开始日志:记录WebSocket连接创建的详细信息
- 调试级别日志:记录WebSocket请求参数(客户端名称、连接地址、心跳间隔)
- 成功日志:记录连接创建成功的基本信息
- 错误日志:提供更详细的错误信息和上下文
-
错误处理严谨化:
- 空值检查:对所有关键参数进行空值验证
- 异常捕获:捕获WebSocket连接创建过程中的所有异常
- 错误消息:提供用户友好的错误消息,包含具体的错误原因
- 取消令牌支持:在WebSocket连接创建时传递取消令牌
-
代码结构优化:
- 方法提取:将WebSocket连接创建逻辑提取为独立的私有方法
CreateWebSocketConnectionAsync - 职责分离:主方法专注于业务流程控制,WebSocket连接创建逻辑独立封装
- 可读性提升:使用更清晰的变量命名和注释
- 维护性增强:便于后续功能扩展和问题排查
- 错误处理统一:统一的错误信息传递机制,正确处理
OperationResult<T>的ErrorMessages属性
- 方法提取:将WebSocket连接创建逻辑提取为独立的私有方法
-
技术特性:
- 异步操作:保持异步操作模式,支持取消令牌
- 资源管理:确保异常情况下的资源正确释放
- 性能优化:避免不必要的字符串操作和对象创建
- 安全性:增强输入验证,防止潜在的安全问题
修改时间:
2025-01-16
修改原因:
提高启动终端服务功能的代码质量和稳定性,增强错误处理能力,提供更好的用户体验和系统可维护性。
2025-01-15 X1.WebAPI 部署脚本优化
修改文件:
X1.WebAPI/publish.bat- 添加deploy.sh复制功能X1.WebAPI/deploy.sh- 创建优化的服务器端部署脚本
修改内容:
-
publish.bat脚本增强:
- 添加了自动复制
deploy.sh到发布目录的功能 - 现在会自动复制:Dockerfile、docker-deploy.md、deploy.sh
- 提供详细的复制状态反馈和错误提示
- 添加了自动复制
-
deploy.sh脚本特性:
- 彩色输出:不同级别的信息用不同颜色显示(INFO蓝色、SUCCESS绿色、WARNING黄色、ERROR红色)
- 详细日志:每个步骤都有详细的输出信息和状态反馈
- 错误处理:遇到错误立即停止并显示错误信息
- 健康检查:自动检查应用是否正常启动(最多等待30秒)
- 状态显示:显示容器状态、端口信息、镜像信息等
- 权限检查:自动设置正确的文件权限
- Docker检查:检查Docker安装状态和服务运行状态
-
部署流程优化:
- 文件检查:验证所有必要文件是否存在
- 权限设置:自动设置可执行文件和目录权限
- 容器管理:自动停止和删除旧容器
- 镜像构建:显示构建过程和结果
- 容器启动:显示启动状态和端口监听情况
- 健康检查:验证应用是否正常响应
-
使用方式:
# 本地发布 publish.bat # 上传publish目录到服务器 # 在服务器上运行 chmod +x deploy.sh ./deploy.sh
修改时间:
2025-01-15
修改原因:
优化X1.WebAPI项目的部署流程,提供详细的部署输出信息,简化服务器端部署操作,提高部署的可视化和可维护性。
2025-01-16 路由冲突修复和当前用户服务增强
修改文件:
X1.Presentation/Controllers/TerminalServicesController.cs- 修复路由冲突X1.Domain/Services/ICurrentUserService.cs- 添加服务IP和端口获取方法X1.Infrastructure/Services/CurrentUserService.cs- 实现服务IP和端口获取方法X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommand.cs- 增强用户获取逻辑X1.WebUI/src/constants/api.ts- 添加终端服务API路径X1.WebUI/src/services/terminalService.ts- 更新API路径和修复命名冲突
修改内容:
-
路由冲突修复:
- 问题描述:
TerminalServicesController和TerminalDevicesController都使用了相同的路由路径api/terminal-devices,导致Swagger生成时出现冲突 - 解决方案:将
TerminalServicesController的路由改为api/terminal-services,保持TerminalDevicesController使用api/terminal-devices - 前端更新:更新前端API路径配置,添加
TERMINAL_SERVICES常量,更新终端服务使用新的API路径
- 问题描述:
-
当前用户服务增强:
- 新增方法:
GetCurrentServiceIpAddress():获取当前服务IP地址GetCurrentServicePort():获取当前服务端口GetCurrentServiceEndpoint():获取当前服务端点信息(IP:端口)
- 技术特性:
- 优先获取IPv4地址,支持IPv6到IPv4的映射
- 完整的空值检查和错误处理
- 从HttpContext中获取本地连接信息
- 新增方法:
-
启动终端服务命令处理器增强:
- 用户获取严谨化:
- 检查用户ID是否为空
- 检查用户是否已认证
- 获取当前服务端点信息用于日志记录
- 日志记录增强:
- 记录用户ID、服务端点、操作结果
- 提供更详细的操作追踪信息
- 用户获取严谨化:
-
前端服务修复:
- API路径更新:使用新的
TERMINAL_SERVICES路径 - 命名冲突修复:将
TerminalService类重命名为TerminalServiceClient,避免与接口冲突 - 类型安全:确保所有TypeScript类型定义正确
- API路径更新:使用新的
-
设计原则:
- RESTful API设计:使用语义化的路由路径
- 职责分离:终端服务和终端设备使用不同的API路径
- 安全性:增强用户认证和授权检查
- 可追溯性:完整的操作日志记录
修改时间:
2025-01-16
修改原因:
- 修复Swagger生成时的路由冲突问题,确保API文档正确生成
- 增强当前用户服务功能,提供更丰富的用户和服务信息
- 提高系统安全性和可追溯性,确保所有操作都有完整的审计信息
2025-01-16 终端消息事件模型创建和WebSocket集成
修改文件:
X1.Domain/Models/TerminalMessageEvent.cs- 创建终端消息事件模型X1.WebSocket/Handlers/TerminalMessageHandler.cs- 修改WebSocket消息处理器以使用IMediatorX1.BackendServices/TerminalManagement/TerminalManagementService.cs- 更新终端管理服务使用新的事件模型X1.BackendServices/TerminalManagement/TerminalMessageEventHandler.cs- 创建终端消息事件处理器X1.BackendServices/DependencyInjection.cs- 注册终端管理服务和事件处理器
修改内容:
-
TerminalMessageEvent模型特性:
- 实现
INotification接口,用于MediatR消息传递 - 包含三个基本字段:
ConnectionId、MessageData、Timestamp - 提供
Create和CreateWithTimestamp静态工厂方法 - 包含
GetMessageSummary和IsValid实用方法 - 遵循项目模型设计规范
- 实现
-
WebSocket消息处理集成:
- 修改
TerminalMessageHandler以使用IMediator - 在
HandleAsync方法中使用TerminalMessageEvent.Create创建事件 - 通过
IMediator.Publish发送消息事件 - 保持原有的WebSocket响应逻辑
- 修改
-
终端消息事件处理器:
- 创建
TerminalMessageEventHandler实现INotificationHandler<TerminalMessageEvent> - 负责接收和处理来自
TerminalMessageHandler的终端消息事件 - 提供完整的日志记录和错误处理
- 支持异步消息处理和业务逻辑扩展
- 创建
-
终端管理服务更新:
- 更新
TerminalManagementService使用新的事件模型 - 在
HandleTerminalMessageAsync方法中使用Create方法 - 确保事件传递的一致性
- 更新
-
依赖注入配置:
- 注册
TerminalManagementService为HostedService - 注册
TerminalMessageEventHandler为Scoped服务 - 确保服务在应用启动时正确初始化
- 注册
-
设计原则:
- 遵循CQRS模式,使用MediatR进行消息传递
- 采用事件驱动架构,解耦WebSocket处理和业务逻辑
- 使用工厂方法模式创建事件实例
- 提供完整的日志记录和错误处理机制
修改时间:
2025-01-16
修改原因:
需要创建一个标准的终端消息事件模型,用于WebSocket消息的事件传递,使用系统自带的 IMediator 进行消息处理,实现终端设备的统一消息管理。
2024年修改记录
2025-01-15 终端设备管理前端界面修复
修改文件:
X1.WebUI/src/services/terminalDeviceService.tsX1.WebUI/src/pages/terminal-devices/TerminalDevicesTable.tsxX1.WebUI/src/pages/terminal-devices/TerminalDeviceForm.tsxX1.WebUI/src/pages/terminal-devices/TerminalDevicesView.tsxX1.WebUI/src/components/ui/switch.tsxX1.WebUI/package.json
修改内容:
-
问题描述:
- 终端设备管理页面需要与 instruments 页面保持一致的样式和功能
- 缺少必要的 UI 组件和依赖包
- 表单实现与 instruments 页面不一致
-
解决方案:
- 修复了
terminalDeviceService.ts中的接口定义,使其与后端 API 保持一致 - 更新了
TerminalDevicesTable.tsx,使用TerminalDeviceStatusBadge组件显示状态 - 重写了
TerminalDeviceForm.tsx,移除 Switch 组件,使用简单的 useState 状态管理,与 instruments 页面保持一致 - 创建了
switch.tsx组件(虽然最终未使用,但为将来需要时做准备) - 安装了缺失的
@hookform/resolvers依赖包
- 修复了
-
关键修改:
- 表格状态显示:使用 Badge 组件而不是 Switch 组件来显示设备状态
- 表单实现:采用与 instruments 页面相同的简单状态管理方式,不使用 react-hook-form
- 字段映射:确保前端字段与后端 API 字段完全匹配
- 编辑模式:在编辑模式下禁用不可修改的字段(IP地址、Agent端口)
-
技术细节:
- 移除了复杂的表单验证库依赖
- 使用简单的 useState 进行状态管理
- 保持与 instruments 页面完全一致的 UI 交互模式
- 确保所有 TypeScript 类型定义正确
修改时间:
2025-01-15
修改原因:
确保终端设备管理页面与 instruments 页面保持完全一致的样式和功能,提供统一的用户体验。
2025-01-15 终端设备服务导入错误修复
修改文件:
X1.WebUI/src/services/terminalDeviceService.ts
修改内容:
-
问题描述:
terminalDeviceService.ts中导入httpClient时出现错误:The requested module '/src/services/axiosConfig.ts' does not provide an export named 'httpClient'OperationResult类型导入路径错误:Cannot find module '@/types/common'
-
解决方案:
- 修正
httpClient的导入路径:从'./axiosConfig'改为'@/lib/http-client' - 修正
OperationResult的导入路径:从'@/types/common'改为'@/types/auth'
- 修正
-
修改代码:
// 修改前 import { httpClient } from './axiosConfig'; import { OperationResult } from '@/types/common'; // 修改后 import { httpClient } from '@/lib/http-client'; import { OperationResult } from '@/types/auth'; -
技术细节:
httpClient实际定义在@/lib/http-client.ts文件中,而不是axiosConfig.tsOperationResult类型定义在@/types/auth.ts文件中,与其他服务文件保持一致- 修复后与
instrumentService.ts的导入方式完全一致
修改时间:
2025-01-15
修改原因:
修复终端设备服务中的模块导入错误,确保前端应用能够正常启动和运行。
修复CreateTerminalDeviceCommandHandler中的空引用警告和并发问题
修改文件:
X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs
修改内容:
-
问题描述:
- 在第95行出现可能的空引用警告
deviceInfoResult.ErrorMessages可能为 null,但CreateFailure(List<string> errorMessages)方法期望非空参数- 设备编码生成逻辑存在并发问题:多个请求可能生成相同的设备编码
-
解决方案:
- 使用空合并操作符
??提供默认值 - 使用空断言操作符
!处理已知非空的Data属性 - 重构设备编码生成逻辑,添加重试机制和并发处理
- 将原来的
GenerateDeviceCodeAsync和编码检查逻辑合并为GenerateUniqueDeviceCodeAsync
- 使用空合并操作符
-
修改代码:
// 修改前 - 空引用问题 return OperationResult<CreateTerminalDeviceResponse>.CreateFailure(deviceInfoResult.ErrorMessages); // 修改后 - 空引用问题 return OperationResult<CreateTerminalDeviceResponse>.CreateFailure(deviceInfoResult.ErrorMessages ?? new List<string> { "获取设备信息失败" }); // 修改前 - Data空引用问题 var serialNumber = deviceInfoResult.Data.SerialNumber; var deviceType = deviceInfoResult.Data.DeviceType; // 修改后 - Data空引用问题 var serialNumber = deviceInfoResult.Data!.SerialNumber; var deviceType = deviceInfoResult.Data!.DeviceType; // 修改前 - 并发问题 var deviceCode = await GenerateDeviceCodeAsync(serialNumber, cancellationToken); if (await _deviceRepository.DeviceCodeExistsAsync(deviceCode, cancellationToken)) { return OperationResult<CreateTerminalDeviceResponse>.CreateFailure($"终端设备编码 {deviceCode} 已存在"); } // 修改后 - 并发问题 var deviceCode = await GenerateUniqueDeviceCodeAsync(cancellationToken); -
新的GenerateUniqueDeviceCodeAsync方法特性:
- 使用短UUID(8位)生成简洁的设备编码
- 格式:
TERM-{8位短UUID},如TERM-a1b2c3d4 - 极低的冲突概率,理论上几乎不可能重复
- 如果发生冲突,自动添加时间戳后缀(6位数字)
- 简洁高效的实现,无需复杂的重试逻辑
- 无需序列号参数,使用纯UUID生成方式
- 完整的日志记录,便于调试和监控
-
技术细节:
- 使用
??空合并操作符处理可能的 null 值 - 提供有意义的默认错误消息
- 使用
Guid.NewGuid().ToString("N").Substring(0, 8)生成8位短UUID - 使用
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() % 1000000生成6位时间戳后缀 - 保持代码的简洁性和高效性
- 使用
修改时间:
2024年
修改原因:
解决编译警告,确保代码的健壮性,避免运行时出现空引用异常。同时解决高并发场景下设备编码重复的问题,提高系统的可靠性和稳定性。
ADB操作和AT操作控制器实现
修改文件:
X1.Presentation/Controllers/AdbOperationsController.csX1.Presentation/Controllers/AtOperationsController.csX1.Infrastructure/DependencyInjection.cs
修改内容:
-
功能描述:
- 创建了
AdbOperationsController控制器,用于管理ADB操作 - 创建了
AtOperationsController控制器,用于管理AT操作 - 参考
TerminalDevicesController的实现结构和设计模式 - 提供完整的RESTful API接口
- 创建了
-
AdbOperationsController特性:
- 路由:
/api/adb-operations - 支持完整的CRUD操作:GET、POST、PUT、DELETE
- 提供列表查询(支持分页和搜索)
- 提供详情查询、创建、更新、删除功能
- 完整的日志记录和错误处理
- 路由:
-
AtOperationsController特性:
- 路由:
/api/at-operations - 支持完整的CRUD操作:GET、POST、PUT、DELETE
- 提供列表查询(支持分页、搜索和设备ID过滤)
- 提供详情查询、创建、更新、删除功能
- 完整的日志记录和错误处理
- 路由:
-
API端点:
- GET /api/adb-operations:获取ADB操作列表
- GET /api/adb-operations/{id}:获取ADB操作详情
- POST /api/adb-operations:创建ADB操作
- PUT /api/adb-operations/{id}:更新ADB操作
- DELETE /api/adb-operations/{id}:删除ADB操作
- GET /api/at-operations:获取AT操作列表
- GET /api/at-operations/{id}:获取AT操作详情
- POST /api/at-operations:创建AT操作
- PUT /api/at-operations/{id}:更新AT操作
- DELETE /api/at-operations/{id}:删除AT操作
-
技术特性:
- 继承自
ApiController基类 - 使用MediatR进行命令和查询处理
- 支持授权认证([Authorize]特性)
- 统一的响应格式(OperationResult)
- 详细的日志记录和错误处理
- 参数验证和ID匹配检查
- 继承自
-
依赖注入配置:
- 在
X1.Infrastructure/DependencyInjection.cs中添加了终端设备相关仓储的注册 - 注册了
ITerminalDeviceRepository、IAdbOperationRepository、IAtOperationRepository及其实现类 - 添加了相应的 using 语句引用终端设备相关的命名空间
- 确保控制器能够正确注入所需的仓储服务
- 在
-
设计原则:
- 遵循RESTful API设计规范
- 统一的错误处理和响应格式
- 完整的日志记录支持
- 与现有架构保持一致
- 支持异步操作
修改时间:
2024年
修改原因:
为ADB操作和AT操作提供完整的Web API控制器,支持终端设备管理系统的前端界面操作,提供标准的RESTful API接口。
AT操作仓储实现
修改文件:
X1.Infrastructure/Repositories/Terminal/AtOperationRepository.cs
修改内容:
-
功能描述:
- 创建了
AtOperationRepository实现类 - 实现了
IAtOperationRepository接口的所有方法 - 参考
AdbOperationRepository的实现方式 - 提供完整的AT操作数据访问功能
- 创建了
-
实现特性:
- 继承自
BaseRepository<AtOperation> - 使用CQRS模式,分离命令和查询操作
- 支持异步操作和取消令牌
- 提供完整的CRUD操作方法
- 支持搜索、分页、统计等功能
- 继承自
-
主要方法:
AddOperationAsync():添加AT操作UpdateOperation():更新AT操作DeleteOperationAsync():删除AT操作GetAllOperationsAsync():获取所有AT操作GetOperationByIdAsync():根据ID获取AT操作GetOperationsByDeviceIdAsync():根据设备ID获取AT操作SearchOperationsAsync():搜索AT操作(支持关键词搜索)SearchOperationsAsync():搜索AT操作(分页版本)ExistsAsync():检查AT操作是否存在GetOperationCountAsync():获取操作总数GetOperationByCommandAsync():根据命令内容获取操作CommandExistsAsync():检查命令是否已存在GetDeviceOperationStatsAsync():获取设备操作统计
-
搜索功能:
- 支持按命令内容、描述、设备ID、端口进行关键词搜索
- 提供分页搜索功能,支持排序和分页
- 搜索结果按ID降序排列
-
统计功能:
- 提供设备操作统计功能
- 按设备ID分组统计操作数量
- 返回字典格式的统计结果
-
设计原则:
- 遵循仓储模式设计
- 使用依赖注入管理依赖关系
- 提供完整的日志记录支持
- 支持异步操作和取消令牌
- 与现有架构保持一致
修改时间:
2024年
修改原因:
需要实现 IAtOperationRepository 接口,为AT操作提供完整的数据访问功能,支持串口通信的AT命令管理。
终端设备实体创建
修改文件:
X1.Domain/Entities/Device/TerminalDevice.csX1.Domain/Repositories/Device/ITerminalDeviceRepository.csX1.Infrastructure/Repositories/Device/TerminalDeviceRepository.csX1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/X1.Application/Features/TerminalDevices/Commands/DeleteTerminalDevice/X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/
修改内容:
-
功能描述:
- 创建了新的终端设备实体类
TerminalDevice - 主要用于支持ADB终端设备管理
- 基于
CellularDevice结构但去掉了ProtocolVersions和Runtime属性 - 创建了完整的仓储体系和Features层
- 创建了新的终端设备实体类
-
实体特性:
- 继承自
AuditableEntity,支持审计功能 - 包含设备基本信息:名称、序列号、设备编码、描述
- 包含网络配置:Agent端口、IP地址
- 支持设备状态管理:启用/禁用
- 提供完整的CRUD操作方法
- 继承自
-
主要属性:
Name:设备名称(必填,最大100字符)SerialNumber:设备序列号(必填,最大50字符)DeviceCode:设备编码(必填,最大50字符)Description:设备描述(最大500字符)AgentPort:Agent端口(必填)IpAddress:IP地址(必填,最大45字符)IsEnabled:是否启用(默认true)
-
业务方法:
Create():静态工厂方法,用于创建新的终端设备Update():更新设备信息Enable():启用设备Disable():禁用设备
-
仓储接口:
ITerminalDeviceRepository:终端设备仓储接口- 继承自
IBaseRepository<TerminalDevice> - 提供完整的CRUD操作方法
- 支持搜索、分页、存在性检查等功能
- 去掉了与运行时状态相关的方法
-
仓储实现:
TerminalDeviceRepository:终端设备仓储实现类- 继承自
BaseRepository<TerminalDevice> - 实现所有仓储接口方法
- 支持高性能查询和分页操作
- 提供设备基本信息查询功能
-
Features层:
- 命令(Commands):
CreateTerminalDevice:创建终端设备UpdateTerminalDevice:更新终端设备DeleteTerminalDevice:删除终端设备
- 查询(Queries):
GetTerminalDevices:获取终端设备列表(支持分页和搜索)GetTerminalDeviceById:根据ID获取终端设备详情
- 特性:
- 使用MediatR实现CQRS模式
- 完整的参数验证和错误处理
- 用户认证和权限验证
- 详细的日志记录
- 统一的响应格式
- 正确的泛型类型参数使用(OperationResult)
- 命令(Commands):
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用私有构造函数确保通过工厂方法创建实例
- 属性使用私有setter确保封装性
- 提供完整的业务操作方法
- 采用CQRS模式分离命令和查询操作
- 遵循Clean Architecture架构模式
修改时间:
2024年
修改原因:
需要创建一个专门用于管理ADB终端设备的完整体系,包括实体类、仓储接口和实现类、以及完整的Features层,与蜂窝设备保持相似的结构但去掉不需要的协议版本和运行时状态关联。
协议日志表格右键菜单功能
修改文件:
X1.WebUI/src/components/protocol-logs/ProtocolLogsTable.tsx
修改内容:
-
功能描述:
- 为协议日志表格添加了右键菜单功能
- 支持选择背景颜色和字体颜色
- 提供清空样式功能
-
新增功能:
- 右键菜单触发:在表格行上右键点击可打开菜单
- 背景颜色选择:提供7种浅色背景选项(浅红、浅橙、浅黄、浅绿、浅蓝、浅紫、浅灰)
- 字体颜色选择:提供8种字体颜色选项(黑、红、橙、黄、绿、蓝、紫、灰)
- 清空样式:一键清除当前行的所有自定义样式
-
技术实现:
- 添加了
RowStyle接口定义行样式结构 - 使用
useState管理行样式状态rowStyles - 实现了右键菜单状态管理
contextMenu - 添加了颜色选项配置数组
- 实现了样式设置和清空的相关函数
- 添加了
-
UI设计:
- 使用网格布局展示颜色选择器
- 背景颜色显示为彩色方块
- 字体颜色显示为带字母A的彩色方块
- 添加了悬停效果和过渡动画
- 使用图标区分不同功能区域
-
交互体验:
- 右键菜单在鼠标位置显示
- 点击外部自动关闭菜单
- 颜色选择器支持悬停预览
- 样式变更即时生效
-
代码结构:
// 行样式接口 interface RowStyle { backgroundColor?: string; color?: string; } // 右键菜单状态 const [contextMenu, setContextMenu] = useState<{ visible: boolean; x: number; y: number; rowId: string; }>(); // 行样式状态 const [rowStyles, setRowStyles] = useState<Record<string, RowStyle>>({});
修改时间:
2024年
修改原因:
增强协议日志表格的交互功能,允许用户通过右键菜单自定义行的背景颜色和字体颜色,提升数据可视化和用户体验。
协议日志表格高度适配性优化
修改文件:
X1.WebUI/src/components/protocol-logs/ProtocolLogsTable.tsx
修改内容:
-
问题描述:
- 原代码使用固定的
max-h-[600px]高度设置 - 导致在不同屏幕尺寸下无法良好适配
- 在小屏幕设备上可能出现显示问题
- 原代码使用固定的
-
解决方案:
- 将固定高度
max-h-[600px]替换为响应式高度设置 - 使用
calc()函数结合视口高度(vh)进行动态计算 - 添加最小高度限制确保在小屏幕上仍有足够的显示空间
- 将固定高度
-
具体修改:
// 修改前 <div className="max-h-[600px] overflow-auto"> // 修改后 <div className="h-[calc(100vh-400px)] min-h-[300px] max-h-[calc(100vh-200px)] overflow-auto"> -
新高度设置说明:
h-[calc(100vh-400px)]:设置表格高度为视口高度减去400px(为页面其他元素预留空间)min-h-[300px]:设置最小高度为300px,确保在小屏幕上仍有足够的显示空间max-h-[calc(100vh-200px)]:设置最大高度为视口高度减去200px,防止在大屏幕上占用过多空间overflow-auto:保持原有的滚动功能
-
优势:
- 响应式设计:能够根据不同的屏幕尺寸自动调整
- 更好的用户体验:在各种设备上都能提供合适的显示效果
- 保持功能完整性:滚动功能和其他交互特性保持不变
修改时间:
2024年
修改原因:
解决协议日志表格在不同屏幕尺寸下的适配性问题,提升用户体验。
协议日志仓储修改
修改文件:
X1.Domain/Repositories/Logging/IProtocolLogRepository.csX1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs
修改内容:
-
保留现有方法:
- 保留了
GetByDeviceWithFiltersAsync方法,该方法用于根据设备代码和运行时状态获取协议日志
- 保留了
-
新增方法:
- 在接口
IProtocolLogRepository中添加了GetProtocolLogsNotInActiveRuntimesAsync方法 - 在实现类
ProtocolLogRepository中实现了该方法
- 在接口
-
新方法特性:
- 方法名:
GetProtocolLogsNotInActiveRuntimesAsync - 功能:获取不在活跃运行时状态中的协议日志
- 参数:与
GetByDeviceWithFiltersAsync保持一致deviceCode:设备代码runtimeCodes:运行时代码集合startTimestamp:开始时间戳endTimestamp:结束时间戳layerTypes:协议层类型数组runtimeStatuses:运行时状态过滤orderByDescending:是否按时间戳降序排序cancellationToken:取消令牌
- 返回类型:
IEnumerable<ProtocolLogListDto>
- 方法名:
-
SQL查询逻辑:
- 基于用户提供的SQL查询
- 动态查询逻辑:根据
runtimeStatuses参数动态决定使用EXISTS还是NOT EXISTS- 当
runtimeStatuses包含值 1 时,使用EXISTS包含活跃运行时状态(RuntimeStatus = 1)的记录 - 当
runtimeStatuses不包含值 1 时,使用NOT EXISTS排除活跃运行时状态(RuntimeStatus = 1)的记录
- 当
- 支持所有与
GetByDeviceWithFiltersAsync相同的过滤条件 - 高性能查询,不包含
MessageDetailJson字段
-
动态查询逻辑示例:
当 runtimeStatuses 包含值 1 时(使用 EXISTS):
SELECT pl."Id", pl."MessageId", pl."LayerType", pl."CellID", pl."IMSI", pl."Direction", pl."UEID", pl."PLMN", pl."TimeMs", pl."Timestamp", pl."Info", pl."Message", pl."DeviceCode", pl."RuntimeCode" FROM "tb_protocol_logs" pl WHERE EXISTS ( SELECT 1 FROM "tb_cellular_device_runtimes" t WHERE t."RuntimeStatus" = 1 AND t."RuntimeCode" = pl."RuntimeCode" )当 runtimeStatuses 不包含值 1 时(使用 NOT EXISTS):
SELECT pl."Id", pl."MessageId", pl."LayerType", pl."CellID", pl."IMSI", pl."Direction", pl."UEID", pl."PLMN", pl."TimeMs", pl."Timestamp", pl."Info", pl."Message", pl."DeviceCode", pl."RuntimeCode" FROM "tb_protocol_logs" pl WHERE NOT EXISTS ( SELECT 1 FROM "tb_cellular_device_runtimes" t WHERE t."RuntimeStatus" = 1 AND t."RuntimeCode" = pl."RuntimeCode" )
修改时间:
2024年
修改原因:
用户需要一个新的方法来获取不在活跃运行时状态中的协议日志,同时保持与现有 GetByDeviceWithFiltersAsync 方法相同的参数和过滤条件。
终端设备序列号获取方式修改
修改文件:
X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs
修改内容:
-
功能描述:
- 将终端设备创建时的序列号生成方式从本地生成改为从设备获取真实序列号
- 参考
CreateDeviceCommandHandler的实现方式 - 添加了设备连接和序列号获取功能
-
主要修改:
- 依赖注入:添加了
IBaseInstrumentClient和IServiceEndpointManager依赖 - 序列号获取:替换了本地生成序列号的逻辑,改为从设备获取真实序列号
- 设备编号生成:修改了设备编号生成逻辑,使用序列号作为后缀
- 错误处理:增强了错误处理和日志记录
- 依赖注入:添加了
-
新增方法:
CreateServiceEndpoint():创建服务端点配置GetDeviceSerialNumberAsync():从设备获取序列号CalculateRequiredDigits():计算设备编号位数
-
修改的方法:
GenerateDeviceCodeAsync():修改为接受序列号参数,生成格式为TERM-000-SN的设备编号
-
技术实现:
- 使用动态HTTP客户端连接设备
- 临时创建服务端点进行序列号获取
- 获取成功后清理临时端点
- 完整的异常处理和资源清理
-
设备编号格式:
- 新格式:
TERM-000-SN、TERM-001-SN、TERM-002-SN等 - 动态位数:根据设备总数自动调整位数(至少3位)
- 包含真实序列号:确保设备编号的唯一性和可追溯性
- 新格式:
修改时间:
2024年
修改原因:
需要从终端设备获取真实的序列号,而不是本地生成,以确保设备信息的准确性和唯一性。参考蜂窝设备的实现方式,提供一致的设备管理体验。
终端设备管理前端视图实现
修改文件:
X1.WebUI/src/services/terminalDeviceService.tsX1.WebUI/src/constants/api.tsX1.WebUI/src/pages/terminal-devices/TerminalDevicesTable.tsxX1.WebUI/src/pages/terminal-devices/TerminalDeviceForm.tsxX1.WebUI/src/pages/terminal-devices/TerminalDevicesView.tsxX1.WebUI/src/routes/AppRouter.tsxX1.WebUI/src/constants/menuConfig.ts
修改内容:
-
终端设备服务层:
- 创建了
terminalDeviceService.ts服务文件 - 定义了完整的终端设备接口类型
- 实现了CRUD操作的API调用方法
- 提供了状态描述和颜色映射方法
- 创建了
-
API路径配置:
- 在
api.ts中添加了TERMINAL_DEVICES: '/terminal-devices'路径 - 与后端控制器路径保持一致
- 在
-
终端设备表格组件:
- 创建了
TerminalDevicesTable.tsx表格组件 - 支持设备状态徽章显示
- 实现了编辑和删除操作按钮
- 支持表格密度调整和列显示控制
- 提供日期格式化功能
- 创建了
-
终端设备表单组件:
- 创建了
TerminalDeviceForm.tsx表单组件 - 使用 React Hook Form 和 Zod 进行表单验证
- 支持创建和编辑两种模式
- 包含设备名称、IP地址、Agent端口、描述等字段
- 编辑模式下支持启用/禁用状态切换
- 创建了
-
终端设备主视图:
- 创建了
TerminalDevicesView.tsx主视图组件 - 实现了完整的CRUD操作功能
- 支持搜索、分页、排序等功能
- 集成了表格工具栏和分页组件
- 提供Toast提示和错误处理
- 创建了
-
路由配置:
- 在
AppRouter.tsx中添加了终端设备路由 - 路径为
/dashboard/terminal-devices - 使用权限控制
terminaldevices.view
- 在
-
菜单配置:
- 在
menuConfig.ts中添加了终端设备菜单项 - 作为独立的顶级菜单项
- 标题为"终端管理"
- 使用 Smartphone 图标
- 配置了相应的权限要求
- 在
-
功能特性:
- 完整的终端设备管理功能
- 支持按设备名称、编码搜索
- 支持按启用状态筛选
- 表格密度和列显示可配置
- 响应式设计和现代化UI
- 完整的错误处理和用户反馈
修改时间:
2024年
修改原因:
需要为终端设备管理提供完整的前端用户界面,参考现有的设备管理页面实现,确保用户体验的一致性和功能的完整性。
终端设备控制器创建
修改文件:
X1.Presentation/Controllers/TerminalDevicesController.cs
修改内容:
-
功能描述:
- 创建了新的终端设备管理控制器
TerminalDevicesController - 参考
DevicesController的实现结构和设计模式 - 提供完整的终端设备CRUD操作API接口
- 创建了新的终端设备管理控制器
-
控制器特性:
- 继承自
ApiController基类 - 使用MediatR进行命令和查询处理
- 支持用户认证和授权
- 完整的日志记录和错误处理
- 统一的响应格式
- 继承自
-
API端点:
- GET /api/terminal-devices:获取终端设备列表(支持分页和搜索)
- GET /api/terminal-devices/{id}:根据ID获取终端设备详情
- POST /api/terminal-devices:创建新的终端设备
- **PUT /api/terminal-devices/{id}`:更新终端设备信息
- **DELETE /api/terminal-devices/{id}`:删除终端设备
-
技术实现:
- 使用CQRS模式分离命令和查询操作
- 统一的OperationResult响应格式
- 详细的日志记录,包括操作开始、成功和失败信息
- 参数验证和错误处理
- ID匹配验证确保数据一致性
-
日志记录:
- 操作开始日志:记录操作类型和关键参数
- 成功日志:记录操作结果和影响的数据量
- 失败日志:记录错误信息和失败原因
- 使用结构化日志记录,便于监控和调试
-
安全特性:
- 使用
[Authorize]特性确保用户认证 - 继承自
ApiController获得统一的错误处理 - 参数验证防止恶意输入
- 使用
修改时间:
2024年
修改原因:
需要为终端设备管理提供完整的Web API接口,参考现有的DevicesController实现,确保API设计的一致性和完整性。
终端设备控制器构造函数调用修复
修改文件:
X1.Presentation/Controllers/TerminalDevicesController.cs
修改内容:
-
问题描述:
GetTerminalDeviceByIdQuery和DeleteTerminalDeviceCommand类没有接受参数的构造函数- 这些类使用属性来接收参数,而不是构造函数参数
- 控制器中直接使用构造函数传递参数导致编译错误
-
修复方案:
- 修改
GetById方法中的查询对象创建方式 - 修改
Delete方法中的命令对象创建方式 - 使用对象初始化器语法设置属性值
- 修改
-
具体修改:
// 修改前 var result = await mediator.Send(new GetTerminalDeviceByIdQuery(id)); var result = await mediator.Send(new DeleteTerminalDeviceCommand(id)); // 修改后 var query = new GetTerminalDeviceByIdQuery { DeviceId = id }; var result = await mediator.Send(query); var command = new DeleteTerminalDeviceCommand { DeviceId = id }; var result = await mediator.Send(command); -
技术说明:
- 这些类遵循MediatR的标准模式,使用属性而不是构造函数参数
- 使用对象初始化器语法更清晰地表达意图
- 保持了代码的可读性和一致性
修改时间:
2024年
修改原因:
修复控制器中查询和命令对象的创建方式,确保与MediatR模式保持一致,解决编译错误。
ADB操作实体创建
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.csX1.Domain/Repositories/Terminal/IAdbOperationRepository.cs
修改内容:
-
功能描述:
- 创建了新的ADB操作实体类
AdbOperation - 主要用于管理ADB命令操作
- 继承自
Entity基类,不包含审计功能 - 创建了完整的仓储接口
- 创建了新的ADB操作实体类
-
实体特性:
- 继承自
Entity,提供基础实体功能 - 包含ADB命令信息:命令内容、描述、路径
- 支持路径配置:绝对路径或相对路径
- 支持启用/禁用状态管理
- 提供完整的CRUD操作方法
- 继承自
-
主要属性:
Command:执行的ADB命令(必填)Description:ADB操作的描述(必填)Path:命令执行时所依赖的路径(必填)UseAbsolutePath:是否启用绝对路径(默认false)IsEnabled:是否启用(默认true)
-
业务方法:
Create():静态工厂方法,用于创建新的ADB操作Update():更新操作信息ToggleAbsolutePath():切换绝对路径设置Enable():启用操作Disable():禁用操作
-
仓储接口:
IAdbOperationRepository:ADB操作仓储接口- 继承自
IBaseRepository<AdbOperation> - 提供完整的CRUD操作方法
- 支持搜索、分页、存在性检查等功能
- 支持根据命令内容查询和检查重复
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用私有构造函数确保通过工厂方法创建实例
- 属性使用私有setter确保封装性
- 提供完整的业务操作方法
- 遵循Clean Architecture架构模式
修改时间:
2024年
修改原因:
需要创建一个专门用于管理ADB操作的实体类,支持ADB命令的存储、配置和管理,为终端设备管理提供ADB操作支持。
终端设备文件结构重组
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.csX1.Domain/Entities/Terminal/TerminalDevice.csX1.Domain/Repositories/Terminal/IAdbOperationRepository.csX1.Domain/Repositories/Terminal/ITerminalDeviceRepository.cs
修改内容:
-
文件结构重组:
- 创建了新的
Terminal文件夹,专门用于终端设备相关的实体和仓储 - 将
AdbOperation和TerminalDevice实体从Device文件夹移动到Terminal文件夹 - 将相关的仓储接口也移动到
Terminal文件夹
- 创建了新的
-
命名空间更新:
AdbOperation实体:CellularManagement.Domain.Entities.TerminalTerminalDevice实体:CellularManagement.Domain.Entities.TerminalIAdbOperationRepository接口:CellularManagement.Domain.Repositories.TerminalITerminalDeviceRepository接口:CellularManagement.Domain.Repositories.Terminal
-
架构设计优化:
- 将终端设备相关功能与蜂窝设备(仪表)功能分离
- 终端设备:主要用于ADB操作和终端管理
- 蜂窝设备:主要用于仪表和协议管理
- 更清晰的领域边界和职责分离
-
删除的旧文件:
X1.Domain/Entities/Device/AdbOperation.csX1.Domain/Entities/Device/TerminalDevice.csX1.Domain/Repositories/Device/IAdbOperationRepository.csX1.Domain/Repositories/Device/ITerminalDeviceRepository.cs
修改时间:
2024年
修改原因:
为了更好地组织代码结构,将终端设备相关功能与蜂窝设备功能分离,创建独立的Terminal文件夹,使架构更加清晰和合理。
终端设备仓储实现创建
修改文件:
X1.Infrastructure/Repositories/Terminal/TerminalDeviceRepository.csX1.Infrastructure/Repositories/Terminal/AdbOperationRepository.cs
修改内容:
-
终端设备仓储实现:
- 创建了
TerminalDeviceRepository实现类 - 继承自
BaseRepository<TerminalDevice> - 实现
ITerminalDeviceRepository接口的所有方法 - 提供完整的CRUD操作和查询功能
- 创建了
-
ADB操作仓储实现:
- 创建了
AdbOperationRepository实现类 - 继承自
BaseRepository<AdbOperation> - 实现
IAdbOperationRepository接口的所有方法 - 提供ADB操作的完整管理功能
- 创建了
-
主要功能:
- 终端设备管理:
- 添加、更新、删除终端设备
- 根据ID、序列号查询设备
- 支持关键词搜索和分页查询
- 检查设备存在性和重复性
- 获取设备基本信息和统计
- ADB操作管理:
- 添加、更新、删除ADB操作
- 根据ID、命令内容查询操作
- 支持关键词搜索和分页查询
- 检查操作存在性和命令重复性
- 获取操作统计信息
- 终端设备管理:
-
技术特性:
- 使用CQRS模式分离命令和查询操作
- 支持异步操作和取消令牌
- 完整的日志记录
- 高性能查询和分页
- 统一的错误处理
-
删除的旧文件:
X1.Infrastructure/Repositories/Device/TerminalDeviceRepository.cs
修改时间:
2024年
修改原因:
完成终端设备仓储的完整实现,为终端设备管理和ADB操作提供完整的数据访问层支持。
ADB操作仓储类型修复
修改文件:
X1.Domain/Repositories/Terminal/IAdbOperationRepository.csX1.Infrastructure/Repositories/Terminal/AdbOperationRepository.cs
修改内容:
-
类型错误修复:
- 修复了
AdbOperation实体ID类型不匹配的问题 - 将
int类型改为string类型,与Entity基类保持一致
- 修复了
-
修复的方法:
DeleteOperationAsync(int id)→DeleteOperationAsync(string id)GetOperationByIdAsync(int id)→GetOperationByIdAsync(string id)ExistsAsync(int id)→ExistsAsync(string id)
-
修复范围:
- 仓储接口
IAdbOperationRepository中的方法签名 - 仓储实现
AdbOperationRepository中的方法实现 - 确保与
Entity基类的Id属性类型一致
- 仓储接口
-
技术说明:
Entity基类的Id属性为string类型- 所有继承自
Entity的实体都应该使用string类型的ID - 修复了编译错误:"运算符"=="无法应用于"string"和"int"类型的操作数"
修改时间:
2024年
修改原因:
修复ADB操作仓储中的类型错误,确保ID类型与Entity基类保持一致,解决编译错误。
ADB操作实体等待时间字段添加
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.cs
修改内容:
-
新增字段:
- 添加了
WaitTimeMs属性,用于存储执行命令完需要等待的时间(毫秒) - 默认值为 0,表示不需要等待
- 添加了
-
字段特性:
- 类型:
int(毫秒) - 默认值:0
- 私有setter确保封装性
- 支持负数验证
- 类型:
-
方法更新:
- Create方法:添加了
waitTimeMs参数(默认值为0) - Update方法:添加了
waitTimeMs参数 - SetWaitTime方法:新增专门用于设置等待时间的方法
- Create方法:添加了
-
验证逻辑:
- 在
Create和Update方法中添加了等待时间验证 - 确保等待时间不能为负数
- 抛出
ArgumentException异常处理无效输入
- 在
-
业务场景:
- 支持ADB命令执行后的等待时间配置
- 适用于需要等待设备响应的场景
- 提高命令执行的可靠性和稳定性
-
设计原则:
- 保持与现有代码风格一致
- 遵循DDD原则,通过业务方法修改状态
- 提供完整的参数验证和错误处理
修改时间:
2024年
修改原因:
为ADB操作实体添加等待时间字段,支持命令执行后的等待配置,提高ADB操作的灵活性和可靠性。
ADB操作实体继承AuditableEntity并添加截图数据字段
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.csX1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.csX1.Infrastructure/Context/AppDbContext.csX1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommand.csX1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommandHandler.csX1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommand.csX1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.csX1.Application/Features/AdbOperations/Queries/GetAdbOperations/GetAdbOperationsQuery.csX1.Application/Features/AdbOperations/Queries/GetAdbOperations/GetAdbOperationsQueryHandler.csX1.Application/Features/AdbOperations/Queries/GetAdbOperationById/GetAdbOperationByIdQuery.csX1.Application/Features/AdbOperations/Queries/GetAdbOperationById/GetAdbOperationByIdQueryHandler.cs
修改内容:
-
实体基类修改:
- 将
AdbOperation从继承Entity改为继承AuditableEntity - 添加了审计字段:
CreatedAt、UpdatedAt、CreatedBy、UpdatedBy
- 将
-
新增截图数据字段:
- 添加了
ScreenshotData属性,类型为byte[]? - 用于存储操作截图数据(图片字节)
- 字段名从
data改为ScreenshotData,更具语义化
- 添加了
-
数据库配置:
- 创建了
AdbOperationConfiguration配置类 - 配置
ScreenshotData字段为BYTEA类型 - 表名使用规范命名:
tb_adboperations(全小写,tb_前缀) - 添加了完整的字段配置和索引
- 创建了
-
DbContext 更新:
- 在
AppDbContext中添加了AdbOperationsDbSet
- 在
-
命令和查询更新:
- 更新了所有相关的命令和查询 DTO
- 添加了
ScreenshotData字段支持 - 更新了处理器中的实体创建和更新逻辑
-
方法更新:
Create方法添加了screenshotData参数Update方法添加了screenshotData参数- 新增
SetScreenshotData方法用于单独设置截图数据
-
技术特性:
- 使用 PostgreSQL 的
BYTEA类型存储二进制数据 - 支持可空的截图数据字段
- 保持了原有的业务逻辑和验证规则
- 使用 PostgreSQL 的
修改时间:
2024年
修改原因:
将 AdbOperation 实体改为继承 AuditableEntity 以支持审计功能,并添加截图数据字段用于存储操作截图,提高 ADB 操作的可追溯性和可视化能力。
AT操作实体继承AuditableEntity并添加截图数据字段
修改文件:
X1.Domain/Entities/Terminal/AtOperation.csX1.Infrastructure/Configurations/Terminal/AtOperationConfiguration.csX1.Infrastructure/Context/AppDbContext.cs
修改内容:
-
实体基类修改:
- 将
AtOperation从继承Entity改为继承AuditableEntity - 添加了审计字段:
CreatedAt、UpdatedAt、CreatedBy、UpdatedBy
- 将
-
新增截图数据字段:
- 添加了
ScreenshotData属性,类型为byte[]? - 用于存储操作截图数据(图片字节)
- 字段名使用
ScreenshotData,更具语义化
- 添加了
-
数据库配置:
- 创建了
AtOperationConfiguration配置类 - 配置
ScreenshotData字段为BYTEA类型 - 表名使用规范命名:
tb_atoperations(全小写,tb_前缀) - 添加了完整的字段配置和索引
- 创建了
-
DbContext 更新:
- 在
AppDbContext中添加了AtOperationsDbSet
- 在
-
方法更新:
Create方法添加了screenshotData参数Update方法添加了screenshotData参数- 新增
SetScreenshotData方法用于单独设置截图数据
-
技术特性:
- 使用 PostgreSQL 的
BYTEA类型存储二进制数据 - 支持可空的截图数据字段
- 保持了原有的业务逻辑和验证规则
- 使用 PostgreSQL 的
修改时间:
2024年
修改原因:
将 AtOperation 实体改为继承 AuditableEntity 以支持审计功能,并添加截图数据字段用于存储操作截图,提高 AT 操作的可追溯性和可视化能力。
TerminalDevice Features层实现
修改文件:
X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQuery.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQuery.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.csX1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.csX1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceResponse.csX1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.csX1.Application/Features/TerminalDevices/Commands/DeleteTerminalDevice/DeleteTerminalDeviceCommand.csX1.Application/Features/TerminalDevices/Commands/DeleteTerminalDevice/DeleteTerminalDeviceCommandHandler.cs
修改内容:
-
查询功能(Queries):
- GetTerminalDevices:获取终端设备列表,支持分页、搜索和状态过滤
- GetTerminalDeviceById:根据ID获取终端设备详情
-
命令功能(Commands):
- UpdateTerminalDevice:更新终端设备信息(描述、别名、启用状态、绑定状态)
- DeleteTerminalDevice:删除终端设备(包含业务逻辑验证)
-
技术特性:
- 使用MediatR实现CQRS模式
- 完整的参数验证和错误处理
- 详细的日志记录
- 统一的响应格式(OperationResult)
- 支持异步操作和取消令牌
-
业务逻辑:
- 支持设备状态管理:在线/离线、启用/禁用、绑定/解绑
- 支持设备信息更新:描述、别名等可编辑字段
- 删除前验证:检查设备是否被绑定,防止误删
- 完整的CRUD操作支持
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 采用CQRS模式分离命令和查询操作
- 遵循Clean Architecture架构模式
- 统一的错误处理和日志记录
- 完整的业务逻辑验证
修改时间:
2024年
修改原因:
为 TerminalDevice 实现完整的 Features 层功能,包括查询、修改和删除操作,参考 TerminalServices 的实现模式,提供统一的API接口和业务逻辑处理。
TerminalService 启动停止功能实现
修改文件:
X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommand.csX1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceResponse.csX1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommandHandler.csX1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceCommand.csX1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceResponse.csX1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceCommandHandler.cs
修改内容:
-
启动服务功能(StartTerminalService):
- StartTerminalServiceCommand:启动终端服务命令,包含服务ID参数
- StartTerminalServiceResponse:启动服务响应,返回服务状态和更新时间
- StartTerminalServiceCommandHandler:启动服务命令处理器,包含业务逻辑验证
-
停止服务功能(StopTerminalService):
- StopTerminalServiceCommand:停止终端服务命令,包含服务ID参数
- StopTerminalServiceResponse:停止服务响应,返回服务状态和更新时间
- StopTerminalServiceCommandHandler:停止服务命令处理器,包含业务逻辑验证
-
业务逻辑验证:
- 启动服务验证:
- 检查服务是否存在
- 检查服务是否已启用(只有启用的服务才能启动)
- 检查服务是否已经启动(避免重复启动)
- 停止服务验证:
- 检查服务是否存在
- 检查服务是否已经停止(避免重复停止)
- 启动服务验证:
-
技术特性:
- 使用MediatR实现CQRS模式
- 完整的参数验证和错误处理
- 详细的日志记录
- 统一的响应格式(OperationResult)
- 支持异步操作和取消令牌
- 自动更新审计信息(UpdatedAt、UpdatedBy)
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 采用CQRS模式分离命令和查询操作
- 遵循Clean Architecture架构模式
- 统一的错误处理和日志记录
- 完整的业务逻辑验证
修改时间:
2024年
修改原因:
为 TerminalService 添加启动和停止服务的功能,通过 IsServiceStarted 属性控制服务状态,提供完整的服务生命周期管理能力。
AdbOperation 适配 AuditableEntity 审计功能
修改文件:
X1.Domain/Entities/Terminal/AdbOperation.csX1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommandHandler.csX1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs
修改内容:
-
AdbOperation 实体更新:
- Create 方法:添加
createdBy参数,调用SetCreated(createdBy)设置审计信息 - Update 方法:添加
updatedBy参数,调用SetUpdated(updatedBy)设置审计信息 - 其他方法:为所有修改状态的方法添加
updatedBy参数ToggleAbsolutePath(string updatedBy)Enable(string updatedBy)Disable(string updatedBy)SetWaitTime(int waitTimeMs, string updatedBy)SetScreenshotData(byte[]? screenshotData, string updatedBy)
- Create 方法:添加
-
命令处理器更新:
- CreateAdbOperationCommandHandler:
- 注入
ICurrentUserService依赖 - 获取当前用户ID并传递给
Create方法
- 注入
- UpdateAdbOperationCommandHandler:
- 注入
ICurrentUserService依赖 - 获取当前用户ID并传递给
Update方法
- 注入
- CreateAdbOperationCommandHandler:
-
审计功能特性:
- 自动设置
CreatedAt、UpdatedAt时间戳 - 自动设置
CreatedBy、UpdatedBy用户信息 - 所有状态变更都会记录操作人
- 完整的操作审计追踪
- 自动设置
-
技术特性:
- 保持向后兼容性
- 统一的审计信息管理
- 完整的参数验证
- 详细的错误处理
修改时间:
2024年
修改原因:
将 AdbOperation 的 Create 和 Update 方法适配 AuditableEntity 基类,确保所有操作都有完整的审计信息记录,提高系统的可追溯性和安全性。
AtOperation 适配 AuditableEntity 审计功能
修改文件:
X1.Domain/Entities/Terminal/AtOperation.csX1.Application/Features/AtOperations/Commands/CreateAtOperation/CreateAtOperationCommandHandler.csX1.Application/Features/AtOperations/Commands/UpdateAtOperation/UpdateAtOperationCommandHandler.cs
修改内容:
-
AtOperation 实体更新:
- Create 方法:添加
createdBy参数,调用SetCreated(createdBy)设置审计信息 - Update 方法:添加
updatedBy参数,调用SetUpdated(updatedBy)设置审计信息 - 其他方法:为所有修改状态的方法添加
updatedBy参数Enable(string updatedBy)Disable(string updatedBy)SetTimeout(int timeout, string updatedBy)SetBaudRate(int baudRate, string updatedBy)SetScreenshotData(byte[]? screenshotData, string updatedBy)
- Create 方法:添加
-
命令处理器更新:
- CreateAtOperationCommandHandler:
- 注入
ICurrentUserService依赖 - 获取当前用户ID并传递给
Create方法
- 注入
- UpdateAtOperationCommandHandler:
- 注入
ICurrentUserService依赖 - 获取当前用户ID并传递给
Update方法
- 注入
- CreateAtOperationCommandHandler:
-
审计功能特性:
- 自动设置
CreatedAt、UpdatedAt时间戳 - 自动设置
CreatedBy、UpdatedBy用户信息 - 所有状态变更都会记录操作人
- 完整的操作审计追踪
- 自动设置
-
技术特性:
- 保持向后兼容性
- 统一的审计信息管理
- 完整的参数验证
- 详细的错误处理
修改时间:
2024年
修改原因:
将 AtOperation 的 Create 和 Update 方法适配 AuditableEntity 基类,确保所有操作都有完整的审计信息记录,提高系统的可追溯性和安全性。
TerminalServicesController 和前端页面启动停止功能更新
修改文件:
X1.Presentation/Controllers/TerminalServicesController.csX1.WebUI/src/services/terminalService.tsX1.WebUI/src/pages/terminal-services/TerminalServicesTable.tsxX1.WebUI/src/pages/terminal-services/TerminalServicesView.tsx
修改内容:
-
后端控制器更新:
- 添加启动服务端点:
POST /api/terminal-devices/{id}/start - 添加停止服务端点:
POST /api/terminal-devices/{id}/stop - 完整的日志记录和错误处理
- 统一的响应格式
- 添加启动服务端点:
-
前端服务接口更新:
- 添加
isServiceStarted字段到TerminalService接口 - 添加
StartTerminalServiceResponse和StopTerminalServiceResponse接口 - 添加
startTerminalService和stopTerminalService方法 - 更新所有相关接口以包含运行状态字段
- 添加
-
前端表格组件更新:
- 添加
onStart和onStop回调函数 - 添加
TerminalServiceStartedBadge组件显示运行状态 - 在操作列中添加启动/停止按钮
- 根据服务状态动态显示按钮(启用且未运行显示启动,运行中显示停止)
- 添加
-
前端主视图更新:
- 添加
handleStart和handleStop处理函数 - 导入启动和停止服务的方法
- 添加运行状态列到默认列配置
- 完整的错误处理和用户提示
- 添加
-
UI/UX 特性:
- 启动按钮使用绿色主题
- 停止按钮使用红色主题
- 根据服务状态智能显示按钮
- 完整的操作反馈和错误提示
-
技术特性:
- 遵循 RESTful API 设计
- 完整的错误处理和日志记录
- 统一的响应格式
- 类型安全的接口定义
修改时间:
2024年
修改原因:
为终端服务管理提供完整的启动和停止功能,包括后端 API 和前端用户界面,实现服务生命周期的完整管理。
TerminalDevicesController 创建
修改文件:
X1.Presentation/Controllers/TerminalDevicesController.cs
修改内容:
-
控制器创建:
- 创建了新的终端设备管理控制器
TerminalDevicesController - 参考
TerminalServicesController的结构和设计模式 - 使用相同的路由前缀:
api/terminal-devices
- 创建了新的终端设备管理控制器
-
API 端点实现:
- 获取设备列表:
GET /api/terminal-devices- 支持分页、搜索、状态筛选
- 返回
GetTerminalDevicesResponse
- 获取设备详情:
GET /api/terminal-devices/{id}- 根据设备ID获取详细信息
- 返回
GetTerminalDeviceByIdResponse
- 更新设备:
PUT /api/terminal-devices/{id}- 更新现有终端设备信息
- 返回
UpdateTerminalDeviceResponse
- 删除设备:
DELETE /api/terminal-devices/{id}- 删除指定的终端设备
- 返回操作结果
- 获取设备列表:
-
功能限制:
- 不包含创建功能:根据需求,只提供查询、修改、删除功能
- 不包含启动/停止功能:终端设备不需要启动/停止操作
-
技术特性:
- 继承自
ApiController基类 - 使用
IMediator进行命令和查询分发 - 完整的日志记录和错误处理
- 统一的响应格式
OperationResult<T> - 授权验证(需要登录)
- 继承自
-
设计模式:
- 遵循 CQRS 模式
- 使用 MediatR 进行消息传递
- RESTful API 设计
- 完整的参数验证和错误处理
-
依赖注入:
- 注入
IMediator用于命令查询分发 - 注入
ILogger<TerminalDevicesController>用于日志记录
- 注入
-
与 TerminalServicesController 的区别:
- 不包含
Create端点 - 不包含
Start和Stop端点 - 专注于设备的基本管理功能
- 不包含
修改时间:
2024年
修改原因:
为终端设备管理提供完整的 RESTful API 控制器,参考 TerminalServicesController 的实现模式,但只包含查询、修改、删除功能,不包含创建和启动/停止功能。
AT操作实体创建
修改文件:
X1.Domain/Entities/Terminal/AtOperation.csX1.Domain/Repositories/Terminal/IAtOperationRepository.cs
修改内容:
-
功能描述:
- 创建了新的AT操作实体类
AtOperation - 专门用于处理通过串口连接的设备的AT命令操作
- 继承自
Entity基类,不包含审计功能 - 创建了完整的仓储接口
- 创建了新的AT操作实体类
-
实体特性:
- 继承自
Entity,提供基础实体功能 - 包含串口通信配置:端口、波特率、数据位、校验位、停止位
- 支持AT命令管理:命令内容、参数、超时时间
- 支持启用/禁用状态管理
- 提供完整的CRUD操作方法
- 继承自
-
主要属性:
DeviceId:设备ID(必填)Port:串口端口(必填)BaudRate:波特率(默认115200)DataBits:数据位(默认8)Parity:校验位(默认NONE)StopBits:停止位(默认1)Command:AT命令内容(必填)Parameters:命令参数(JSON格式)Timeout:超时时间(默认30秒)Description: 操作描述(必填)IsEnabled:是否启用(默认true)
-
业务方法:
Create():静态工厂方法,用于创建新的AT操作Update():更新操作信息Enable():启用操作Disable():禁用操作SetTimeout():设置超时时间SetBaudRate():设置波特率
-
验证逻辑:
- 设备ID、端口、命令、描述不能为空
- 波特率必须大于0
- 数据位必须在5-8之间
- 校验位必须是NONE、EVEN或ODD
- 停止位必须是1、1.5或2
- 超时时间必须大于0
-
仓储接口:
IAtOperationRepository:AT操作仓储接口- 继承自
IBaseRepository<AtOperation> - 提供完整的CRUD操作方法
- 支持搜索、分页、存在性检查等功能
- 支持根据设备ID查询操作
- 支持根据命令内容查询和检查重复
- 提供设备操作统计功能
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用私有构造函数确保通过工厂方法创建实例
- 属性使用私有setter确保封装性
- 提供完整的业务操作方法
- 遵循Clean Architecture架构模式
- 专注于串口通信的核心功能,简化设计
修改时间:
2024年
修改原因:
需要创建一个专门用于管理AT操作的实体类,支持串口通信的AT命令存储、配置和管理,为终端设备管理提供AT操作支持。
AT操作和ADB操作Features层实现
修改文件:
X1.Application/Features/TerminalDevices/Commands/CreateAtOperation/X1.Application/Features/TerminalDevices/Commands/UpdateAtOperation/X1.Application/Features/TerminalDevices/Commands/DeleteAtOperation/X1.Application/Features/TerminalDevices/Queries/GetAtOperations/X1.Application/Features/TerminalDevices/Queries/GetAtOperationById/X1.Application/Features/TerminalDevices/Commands/CreateAdbOperation/X1.Application/Features/TerminalDevices/Commands/UpdateAdbOperation/X1.Application/Features/TerminalDevices/Commands/DeleteAdbOperation/X1.Application/Features/TerminalDevices/Queries/GetAdbOperations/X1.Application/Features/TerminalDevices/Queries/GetAdbOperationById/
修改内容:
-
AT操作Features层:
- Commands(命令):
CreateAtOperation:创建AT操作,支持串口通信配置UpdateAtOperation:更新AT操作信息DeleteAtOperation:删除AT操作
- Queries(查询):
GetAtOperations:获取AT操作列表(支持分页和搜索)GetAtOperationById:根据ID获取AT操作详情
- Commands(命令):
-
ADB操作Features层:
- Commands(命令):
CreateAdbOperation:创建ADB操作,支持命令和路径配置UpdateAdbOperation:更新ADB操作信息DeleteAdbOperation:删除ADB操作
- Queries(查询):
GetAdbOperations:获取ADB操作列表(支持分页和搜索)GetAdbOperationById:根据ID获取ADB操作详情
- Commands(命令):
-
技术特性:
- 使用MediatR实现CQRS模式
- 完整的参数验证和错误处理
- 详细的日志记录
- 统一的响应格式(OperationResult)
- 支持异步操作和取消令牌
- 完整的CRUD操作支持
-
AT操作特性:
- 支持串口通信配置:端口、波特率、数据位、校验位、停止位
- 支持AT命令管理:命令内容、参数、超时时间
- 支持启用/禁用状态管理
- 支持设备ID关联
-
ADB操作特性:
- 支持ADB命令管理:命令内容、描述、路径
- 支持路径配置:绝对路径或相对路径
- 支持启用/禁用状态管理
- 支持等待时间配置
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 采用CQRS模式分离命令和查询操作
- 遵循Clean Architecture架构模式
- 统一的错误处理和日志记录
- 完整的业务逻辑验证
修改时间:
2024年
修改原因:
为AT操作和ADB操作实现完整的Features层,提供标准的CRUD操作和查询功能,支持终端设备管理系统的完整功能。
2025-01-12 BackendServiceManager 迁移
迁移内容
将 X1.Application.BackendServiceManager 完全迁移到 X1.BackendServices.BackendServiceManager
迁移的文件
IServiceScopeExecutor.cs- 服务作用域执行器接口ServiceScopeExecutor.cs- 服务作用域执行器实现OperationResult.cs- 操作结果包装类ProtocolChannelManager.cs- 协议通道管理器DeviceManagementService.cs- 设备管理后台服务
修改的文件
X1.BackendServices/DependencyInjection.cs- 添加依赖注入配置X1.BackendServices/X1.BackendServices.csproj- 添加项目引用和包引用X1.Application/DependencyInjection.cs- 移除 BackendServiceManager 相关注册X1.WebAPI/Program.cs- 添加对 X1.BackendServices 的依赖注入X1.WebAPI/X1.WebAPI.csproj- 添加对 X1.BackendServices 的项目引用
命名空间变更
- 从
X1.Application.BackendServiceManager改为X1.BackendServices.BackendServiceManager
验证结果
- ✅ 编译成功,无错误
- ✅ 所有依赖关系已正确更新
- ✅ 服务注册已正确配置
完成状态
- ✅ 已成功删除
X1.Application/BackendServiceManager目录 - ✅ 项目编译正常,无错误
- ✅ 迁移完成,所有功能正常工作
2025-01-13 用例节点实体创建
创建的文件
X1.Domain/Entities/UseCase/UseCaseNode.cs- 用例节点实体类X1.Domain/Entities/UseCase/UseCaseNodeType.cs- 用例节点类型枚举
修改内容
-
用例节点实体特性:
- 继承自
AuditableEntity,支持审计功能 - 简化为纯配置实体,只包含核心字段
- 支持启用/禁用状态管理(默认启用)
- 提供基本的业务操作方法
- 继承自
-
主要属性:
NodeName:节点名称(必填,最大100字符)NodeType:节点类型(必填,使用枚举)IsEnabled:是否启用(默认true)
-
业务方法:
Update():更新节点信息Enable():启用节点Disable():禁用节点
-
节点类型枚举:
Start:开始节点End:结束节点Process:处理节点Decision:判断节点Wait:等待节点Parallel:并行节点Merge:合并节点SubProcess:子流程节点UserTask:用户任务节点SystemTask:系统任务节点Timer:定时器节点Message:消息节点ErrorHandler:错误处理节点Compensation:补偿节点Custom:自定义节点
-
设计原则:
- 遵循DDD(领域驱动设计)原则
- 使用枚举类型确保节点类型的类型安全
- 简化设计,专注于核心配置功能
- 默认启用状态,符合业务需求
完成状态
- ✅ 用例文件夹创建完成
- ✅ 用例节点实体类创建完成(简化版本)
- ✅ 节点类型枚举创建完成
- ✅ 核心配置字段完整
- ✅ 默认启用状态设置正确
2025-01-13 UpdateUseCaseNodeConfigCommand 字段限制修改
修改文件
X1.Application/Features/UseCaseNodeConfigs/Commands/UpdateUseCaseNodeConfig/UpdateUseCaseNodeConfigCommand.csX1.Application/Features/UseCaseNodeConfigs/Commands/UpdateUseCaseNodeConfig/UpdateUseCaseNodeConfigCommandHandler.cs
修改内容
-
功能描述:
- 限制
UpdateUseCaseNodeConfigCommand只能修改启用、图标和节点说明字段 - 移除对节点名称和节点类型的修改权限
- 确保数据安全性和业务逻辑的正确性
- 限制
-
命令类修改:
- 移除字段:
NodeName:节点名称字段(不再允许修改)NodeType:节点类型字段(不再允许修改)
- 保留字段:
Id:节点配置ID(用于标识要更新的配置)Description:节点说明(允许修改)Icon:节点图标(允许修改)IsEnabled:是否启用(允许修改)
- 移除字段:
-
命令处理器修改:
- 移除验证逻辑:
- 移除了节点名称重复性检查
- 移除了节点名称相关的日志记录
- 更新逻辑修改:
- 只更新
Description、Icon和IsEnabled字段 - 保持
NodeName和NodeType字段不变
- 只更新
- 日志记录优化:
- 简化了日志记录,移除了节点名称相关的信息
- 保持错误处理和用户认证逻辑不变
- 移除验证逻辑:
-
业务逻辑:
- 确保节点配置的核心标识信息(名称和类型)不被意外修改
- 只允许修改配置相关的辅助信息(说明、图标、启用状态)
- 保持数据一致性和业务规则的完整性
-
技术实现:
- 使用对象初始化器语法创建查询和命令对象
- 保持与MediatR模式的一致性
- 完整的错误处理和日志记录
- 统一的响应格式
完成状态
- ✅ 命令类字段限制完成
- ✅ 命令处理器逻辑更新完成
- ✅ 验证逻辑移除完成
- ✅ 日志记录优化完成
- ✅ 业务逻辑安全性确保完成
2025-01-13 UseCaseNodeConfigs Application.Features 实现
实现内容
参考ProtocolVersions的实现模式,为UseCaseNodeConfigs创建完整的Application.Features功能
创建的文件
Commands 命令
-
CreateUseCaseNodeConfig
CreateUseCaseNodeConfigCommand.cs- 创建用例节点配置命令CreateUseCaseNodeConfigResponse.cs- 创建响应CreateUseCaseNodeConfigCommandHandler.cs- 创建命令处理器
-
UpdateUseCaseNodeConfig
UpdateUseCaseNodeConfigCommand.cs- 更新用例节点配置命令(只能修改启用状态、图标和说明)UpdateUseCaseNodeConfigResponse.cs- 更新响应UpdateUseCaseNodeConfigCommandHandler.cs- 更新命令处理器
-
DeleteUseCaseNodeConfig
DeleteUseCaseNodeConfigCommand.cs- 删除用例节点配置命令DeleteUseCaseNodeConfigCommandHandler.cs- 删除命令处理器
Queries 查询
-
GetUseCaseNodeConfigs
GetUseCaseNodeConfigsQuery.cs- 获取用例节点配置列表查询(无分页)GetUseCaseNodeConfigsResponse.cs- 查询响应和DTOGetUseCaseNodeConfigsQueryHandler.cs- 查询处理器
-
GetUseCaseNodeConfigById
GetUseCaseNodeConfigByIdQuery.cs- 根据ID获取用例节点配置查询GetUseCaseNodeConfigByIdResponse.cs- 查询响应GetUseCaseNodeConfigByIdQueryHandler.cs- 查询处理器
Controller 控制器
- UseCaseNodeConfigsController.cs - 用例节点配置API控制器
- 参考ProtocolVersionsController的实现模式
- 包含完整的CRUD操作
- 添加了详细的日志记录
- 统一的错误处理和响应格式
主要特性
-
命令设计:
- 创建时支持所有字段
- 更新时只能修改启用状态、图标和说明(保护节点名称和类型)
- 删除时进行存在性验证
-
查询设计:
- 列表查询无分页(节点配置数量有限)
- 支持按搜索关键词、启用状态、节点类型过滤
- 详情查询包含完整的审计信息
-
控制器设计:
- 统一的API路由:
/api/usecasenodeconfigs - 完整的日志记录和错误处理
- 遵循RESTful API设计规范
- 返回统一的OperationResult格式
- 统一的API路由:
-
业务逻辑:
- 创建时检查节点名称唯一性
- 更新时保护核心字段不被修改
- 所有操作都需要用户认证
- 完整的审计信息记录
完成状态
- ✅ 所有命令和查询功能已实现
- ✅ 控制器已完善,参考ProtocolVersionsController模式
- ✅ 统一的错误处理和日志记录
- ✅ 符合DDD和CQRS架构模式
- ✅ 支持完整的CRUD操作
2025-01-19 - ADB命令配置功能实现
新增功能
-
ADB操作服务 (
X1.WebUI/src/services/adbOperationsService.ts)- 创建了完整的 ADB 操作 API 服务
- 包含获取列表、创建、更新、删除等操作
- 定义了完整的 TypeScript 接口类型
-
ADB操作管理页面 (
X1.WebUI/src/pages/adb-operations/AdbOperationsView.tsx)- 实现了 ADB 命令配置的主页面
- 支持搜索、分页、表格密度调整等功能
- 包含创建、编辑、删除 ADB 操作的功能
-
ADB操作表格组件 (
X1.WebUI/src/pages/adb-operations/AdbOperationsTable.tsx)- 实现了 ADB 操作的表格显示
- 支持列显示控制、表格密度调整
- 包含状态徽章和操作按钮
-
ADB操作表单组件 (
X1.WebUI/src/pages/adb-operations/AdbOperationForm.tsx)- 实现了 ADB 操作的创建和编辑表单
- 包含命令、描述、路径、绝对路径、启用状态、等待时间等字段
- 支持表单验证和提交
菜单和路由配置
-
菜单配置更新 (
X1.WebUI/src/constants/menuConfig.ts)- 将 ADB 命令配置添加到终端管理菜单下
- 添加了
adboperations.view和adboperations.manage权限 - 更新了菜单路径为
/dashboard/terminal-devices/adb-operations
-
路由配置更新 (
X1.WebUI/src/routes/AppRouter.tsx)- 将终端设备管理路由改为嵌套结构
- 添加了 ADB 操作管理路由
- 格式化了路由代码,使其更清晰易读
功能特性
- 完整的 CRUD 操作: 支持 ADB 操作的创建、读取、更新、删除
- 搜索功能: 支持按命令和描述搜索
- 分页支持: 支持分页显示和每页条数调整
- 表格控制: 支持列显示/隐藏、表格密度调整
- 状态管理: 支持启用/禁用状态切换
- 路径配置: 支持自定义执行路径和绝对路径设置
- 等待时间: 支持命令执行后的等待时间配置
技术实现
- 使用 React + TypeScript 开发
- 采用 CQRS 模式,前后端接口完全匹配
- 使用 shadcn/ui 组件库构建界面
- 支持响应式设计和现代化 UI/UX
- 完整的错误处理和用户反馈
权限控制
- 添加了
adboperations.view权限用于查看 ADB 操作列表 - 添加了
adboperations.manage权限用于管理 ADB 操作 - 集成到现有的权限系统中
后端接口
- 基于现有的
AdbOperationsController实现 - 支持完整的 RESTful API 操作
- 包含日志记录和错误处理
- ✅ 支持完整的CRUD操作
2025-01-13 TerminalDevice 添加设备类型枚举
修改文件
X1.Domain/Entities/Terminal/TerminalDeviceType.cs- 新增设备类型枚举X1.Domain/Entities/Terminal/TerminalDevice.cs- 添加设备类型属性X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.cs- 更新DTOX1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.cs- 更新映射X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.cs- 更新响应X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.cs- 更新映射X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommand.cs- 添加设备类型参数X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceResponse.cs- 更新响应X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs- 更新处理逻辑X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.cs- 添加设备类型参数X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceResponse.cs- 更新响应X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.cs- 更新处理逻辑X1.Infrastructure/Context/AppDbContext.cs- 添加TerminalDevice DbSetX1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs- 新增数据库配置
修改内容:
-
设备类型枚举创建:
- 创建了
TerminalDeviceType枚举 - 包含
Windows = 1和Linux = 2两个选项 - 用于区分终端设备的操作系统类型
- 创建了
-
TerminalDevice实体更新:
- 添加了
DeviceType属性,类型为TerminalDeviceType - 默认值为
TerminalDeviceType.Windows - 更新了
Create和Update方法,添加设备类型参数 - 在数据库中以整数形式存储枚举值
- 添加了
-
Application层更新:
- DTO和响应类:所有相关的DTO和响应类都添加了
DeviceType字段 - 查询处理器:更新了映射逻辑,将枚举值转换为字符串
- 命令类:添加了设备类型参数,默认值为 "Windows"
- 命令处理器:添加了枚举解析逻辑和验证
- DTO和响应类:所有相关的DTO和响应类都添加了
-
数据库配置:
- 在
AppDbContext中添加了TerminalDevicesDbSet - 创建了
TerminalDeviceConfiguration配置类 - 配置了设备类型字段的数据库映射(使用整数存储)
- 添加了相关的索引和约束
- 在
-
技术特性:
- 使用枚举确保类型安全
- 在数据库中存储为整数,提高性能
- 在API中返回字符串,便于前端使用
- 完整的验证和错误处理
- 向后兼容,现有数据默认为Windows类型
-
设计原则:
- 遵循DDD原则,使用枚举表示领域概念
- 保持与现有架构的一致性
- 提供完整的CRUD操作支持
- 确保数据完整性和类型安全
完成状态
- ✅ 设备类型枚举创建完成
- ✅ TerminalDevice实体更新完成
- ✅ Application层所有相关文件更新完成
- ✅ 数据库配置完成
- ✅ 类型安全和验证逻辑完整
- ✅ 向后兼容性确保完成
2025-01-13 TerminalDevice 数据库表命名规范修正
修改文件
X1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs
修改内容
-
问题描述:
- 原表名
"TerminalDevices"不符合项目的命名规范 - 项目要求使用
tb_前缀并且都是小写
- 原表名
-
修正方案:
- 将表名从
"TerminalDevices"修改为"tb_terminal_devices" - 符合项目的命名规范:
tb_前缀 + 全小写 + 下划线分隔
- 将表名从
-
具体修改:
// 修改前 builder.ToTable("TerminalDevices"); // 修改后 builder.ToTable("tb_terminal_devices"); -
命名规范说明:
tb_:表名前缀,表示这是一个数据库表terminal_devices:实体名称的小写形式,使用下划线分隔单词- 符合项目的统一命名约定
完成状态
- ✅ 表名修正完成
- ✅ 符合项目命名规范
- ✅ 与现有数据库表命名保持一致
2025-01-13 TerminalDevice 命令字段限制修改
修改文件
X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommand.csX1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.csX1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.csX1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.csX1.Domain/Entities/Terminal/TerminalDevice.cs
修改内容
-
CreateTerminalDeviceCommand 修改:
- 移除字段:删除了
DeviceType字段,不再由用户填写 - 自动检测:设备类型由系统根据设备连接信息自动检测
- 业务逻辑:系统会尝试获取设备系统信息来判断是Windows还是Linux
- 移除字段:删除了
-
CreateTerminalDeviceCommandHandler 修改:
- 自动检测逻辑:添加了
DetectDeviceTypeAsync方法 - 系统信息检测:通过
GetDeviceSystemInfoAsync获取设备系统信息 - 智能判断:根据系统信息中的关键词判断设备类型
- 默认处理:如果无法检测,默认使用Windows类型
- 错误处理:完整的异常处理和日志记录
- 自动检测逻辑:添加了
-
UpdateTerminalDeviceCommand 修改:
- 字段限制:只保留
DeviceId、Description和IsEnabled字段 - 移除字段:删除了
DeviceName、IpAddress、AgentPort、DeviceType字段 - 业务逻辑:只允许修改设备描述和启用状态,保护核心配置信息
- 字段限制:只保留
-
UpdateTerminalDeviceCommandHandler 修改:
- 更新逻辑:只更新允许修改的字段(描述和启用状态)
- 验证简化:移除了对已删除字段的验证
- 方法调用:使用专门的
UpdateDescription和Enable/Disable方法 - 审计信息:正确更新审计信息
-
TerminalDevice 实体修改:
- 新增方法:添加了
UpdateDescription方法用于更新描述 - 新增方法:添加了
UpdateAuditInfo方法用于更新审计信息 - 封装性:保持实体的封装性,通过方法修改状态
- 新增方法:添加了
-
技术特性:
- 自动检测:设备类型自动检测,无需用户干预
- 字段保护:核心配置字段不允许修改,确保数据安全
- 业务逻辑:符合实际业务需求,只允许修改非关键信息
- 错误处理:完整的异常处理和日志记录
完成状态
- ✅ CreateTerminalDeviceCommand 设备类型字段移除完成
- ✅ 自动设备类型检测逻辑实现完成
- ✅ UpdateTerminalDeviceCommand 字段限制完成
- ✅ 命令处理器逻辑更新完成
- ✅ TerminalDevice 实体方法扩展完成
- ✅ 业务逻辑安全性确保完成
2025-01-13 TerminalDevice DTO 序列号字段移除
修改文件
X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.csX1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.cs
修改内容
-
GetTerminalDevicesResponse.cs 修改:
- 移除字段:从
TerminalDeviceDto中删除了SerialNumber字段 - 安全考虑:序列号是敏感信息,不应该在列表中显示
- 数据保护:保护设备序列号信息不被泄露
- 移除字段:从
-
GetTerminalDevicesQueryHandler.cs 修改:
- 映射移除:移除了对
SerialNumber字段的映射 - 数据过滤:确保列表查询不返回序列号信息
- 性能优化:减少不必要的数据传输
- 映射移除:移除了对
-
GetTerminalDeviceByIdResponse.cs 修改:
- 移除字段:从详情响应中删除了
SerialNumber字段 - 一致性:与列表查询保持一致,都不显示序列号
- 安全策略:统一的安全策略,保护敏感信息
- 移除字段:从详情响应中删除了
-
GetTerminalDeviceByIdQueryHandler.cs 修改:
- 映射移除:移除了对
SerialNumber字段的映射 - 详情保护:即使是在详情查询中也不显示序列号
- 数据安全:确保序列号信息不被任何API端点泄露
- 映射移除:移除了对
-
安全特性:
- 信息保护:序列号作为设备唯一标识,属于敏感信息
- 访问控制:通过API层面控制敏感信息的访问
- 数据最小化:只返回必要的设备信息
- 隐私保护:符合数据隐私保护原则
完成状态
- ✅ GetTerminalDevicesResponse 序列号字段移除完成
- ✅ GetTerminalDevicesQueryHandler 映射移除完成
- ✅ GetTerminalDeviceByIdResponse 序列号字段移除完成
- ✅ GetTerminalDeviceByIdQueryHandler 映射移除完成
- ✅ 数据安全保护策略实施完成
TestTerminalRequestClient GetDeviceSerialNumberAsync 方法完善
修改文件:
X1.Domain/ThirdPartyDeviceHttpClient/Models/MachineCodeResponse.cs- 新增机器码响应模型X1.DynamicClientCore/Service/TestTerminalRequestClient.cs- 完善GetDeviceSerialNumberAsync方法
修改内容:
-
功能描述:
- 完善了
TestTerminalRequestClient的GetDeviceSerialNumberAsync方法 - 实现了调用机器码API获取设备序列号的功能
- 创建了对应的响应模型来匹配API返回的数据结构
- 完善了
-
新增响应模型:
- MachineCodeResponse:机器码响应模型
Success:是否成功Message:响应消息Data:响应数据
- MachineCodeData:机器码数据
MachineCode:机器码(设备序列号)SystemType:系统类型
- MachineCodeResponse:机器码响应模型
-
API调用实现:
- 端点:
/api/v1/system/machine-code - 服务名称:
test-terminal - HTTP方法:GET
- 响应处理:完整的成功/失败状态检查
- 端点:
-
技术特性:
- 异步操作:使用
async/await模式 - 错误处理:完整的异常捕获和日志记录
- 参数验证:检查响应数据的有效性
- 日志记录:详细的操作日志,包括开始、成功、失败信息
- 资源清理:确保异常情况下的资源正确释放
- 异步操作:使用
-
业务逻辑:
- 调用机器码API获取设备序列号
- 验证API响应的成功状态
- 提取机器码作为设备序列号返回
- 记录系统类型信息用于调试
- 失败时返回空字符串
-
响应格式匹配:
{ "success": true, "message": "机器码获取成功", "data": { "machine_code": "03000200-0400-0500-0006-000700080009", "system_type": "windows" } } -
设计原则:
- 遵循现有架构模式
- 完整的错误处理和日志记录
- 使用依赖注入管理依赖关系
- 支持取消令牌和异步操作
- 统一的响应格式处理
修改时间:
2025年1月13日
修改原因:
需要完善 TestTerminalRequestClient 的 GetDeviceSerialNumberAsync 方法,实现从指定API端点获取设备机器码的功能,为终端设备管理提供真实的设备序列号获取能力。
MachineCodeResponse 重构为 TestTerminalResponse
修改文件:
X1.Domain/ThirdPartyDeviceHttpClient/Models/MachineCodeResponse.cs- 重构响应模型X1.DynamicClientCore/Service/TestTerminalRequestClient.cs- 更新使用新的响应模型
修改内容:
-
功能描述:
- 将
MachineCodeResponse重构为更通用的TestTerminalResponse - 使测试终端相关的所有API都可以使用这个响应实体
- 直接删除向后兼容的
MachineCodeResponse类,简化代码结构
- 将
-
响应模型重构:
- TestTerminalResponse:泛型版本,支持任意数据类型
Success:是否成功Message:响应消息Data:泛型响应数据
- TestTerminalResponse:非泛型版本,默认使用
MachineCodeData - MachineCodeResponse:已删除,不再提供向后兼容
- TestTerminalResponse:泛型版本,支持任意数据类型
-
数据模型:
- MachineCodeData:机器码数据模型
MachineCode:机器码(设备序列号)SystemType:系统类型
- MachineCodeData:机器码数据模型
-
技术特性:
- 泛型设计:支持任意数据类型的响应
- 简化结构:删除不必要的向后兼容类
- 类型安全:使用泛型确保编译时类型检查
- 可扩展性:未来可以轻松添加新的数据类型
-
使用示例:
// 使用泛型版本(推荐) var response = await _dynamicHttpClient.GetAsync<TestTerminalResponse<MachineCodeData>>( serviceName, endpoint, options, cancellationToken); // 使用非泛型版本(简化) var response = await _dynamicHttpClient.GetAsync<TestTerminalResponse>( serviceName, endpoint, options, cancellationToken); -
设计原则:
- 单一职责:专注于测试终端响应格式
- 开闭原则:对扩展开放,对修改封闭
- 简洁性:删除不必要的向后兼容代码
- 类型安全:编译时类型检查
-
未来扩展:
- 可以轻松添加新的数据类型,如设备状态、网络配置等
- 所有测试终端API都可以使用统一的响应格式
- 支持不同的业务场景和数据需求
修改时间:
2025年1月13日
修改原因:
需要将机器码响应模型重构为更通用的测试终端响应模型,使所有测试终端相关的API都可以使用统一的响应格式,提高代码的可复用性和类型安全性。删除不必要的向后兼容代码,简化代码结构。
TestTerminalRequestClient 添加 GetMachineCodeDataAsync 方法
修改文件:
X1.DynamicClientCore/Service/TestTerminalRequestClient.cs - 添加获取完整机器码数据的方法
修改内容:
-
功能描述:
- 添加了
GetMachineCodeDataAsync方法,返回完整的MachineCodeData对象 - 重构了
GetDeviceSerialNumberAsync方法,使其调用新的方法 - 保持接口兼容性的同时,提供更丰富的数据返回
- 添加了
-
新增方法:
- GetMachineCodeDataAsync:获取完整的机器码数据
- 返回类型:
Task<MachineCodeData?> - 包含机器码和系统类型信息
- 完整的错误处理和日志记录
- 返回类型:
- GetMachineCodeDataAsync:获取完整的机器码数据
-
方法重构:
- GetDeviceSerialNumberAsync:重构为调用
GetMachineCodeDataAsync- 保持原有接口兼容性
- 只返回机器码字符串
- 简化了实现逻辑
- GetDeviceSerialNumberAsync:重构为调用
-
技术特性:
- 代码复用:避免重复的API调用逻辑
- 类型安全:返回强类型的
MachineCodeData对象 - 错误处理:统一的异常处理和日志记录
- 向后兼容:保持原有接口不变
-
使用示例:
// 获取完整机器码数据(推荐) var machineCodeData = await client.GetMachineCodeDataAsync("terminal-001"); if (machineCodeData != null) { var machineCode = machineCodeData.MachineCode; var systemType = machineCodeData.SystemType; } // 获取机器码字符串(兼容原有接口) var serialNumber = await client.GetDeviceSerialNumberAsync("terminal-001"); -
设计原则:
- 单一职责:一个方法专注于获取机器码数据
- 代码复用:避免重复的API调用逻辑
- 向后兼容:保持原有接口不变
- 类型安全:提供强类型的数据返回
-
业务价值:
- 提供更丰富的设备信息(机器码 + 系统类型)
- 支持更复杂的业务逻辑处理
- 保持与现有代码的兼容性
- 提高代码的可维护性
修改时间:
2025年1月13日
修改原因:
需要提供获取完整机器码数据的功能,包括机器码和系统类型信息,同时保持与现有接口的兼容性,为测试终端管理提供更丰富的数据支持。
TestTerminalRequestClient 最终优化
修改文件:
X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IBaseTerminalClient.cs- 更新接口定义X1.DynamicClientCore/Service/TestTerminalRequestClient.cs- 优化实现和注释
修改内容:
-
接口定义优化:
- 将
GetDeviceSerialNumberAsync方法返回类型从Task<string>改为Task<MachineCodeData?> - 参数名从
instrumentNumber改为terminalId - 更新方法注释,明确返回测试终端机器码数据
- 将
-
实现优化:
- 移除重复的
GetMachineCodeDataAsync方法 - 直接使用
GetDeviceSerialNumberAsync返回MachineCodeData对象 - 使用
terminalId作为服务名称,而不是硬编码的 "test-terminal" - 添加完整的XML注释,包含参数说明、返回值说明、异常说明和备注
- 移除重复的
-
技术特性:
- 简化设计:移除重复方法,统一使用一个方法
- 正确命名:使用
terminalId作为服务名称 - 完整注释:提供详细的XML文档注释
- 类型安全:返回强类型的
MachineCodeData对象
-
XML注释内容:
- 参数说明:明确
terminalId用作服务名称 - 返回值说明:详细描述返回的数据结构
- 异常说明:列出可能抛出的异常类型
- 备注说明:解释方法的工作原理和API调用细节
- 参数说明:明确
-
使用示例:
// 获取测试终端机器码数据 var machineCodeData = await client.GetDeviceSerialNumberAsync("terminal-001"); if (machineCodeData != null) { var machineCode = machineCodeData.MachineCode; var systemType = machineCodeData.SystemType; } -
设计原则:
- 单一职责:一个方法专注于获取机器码数据
- 正确命名:参数名和方法名符合业务语义
- 完整文档:提供详细的XML注释
- 类型安全:使用强类型返回数据
修改时间:
2025年1月13日
修改原因:
优化TestTerminalRequestClient的设计,简化方法结构,修正命名规范,使用正确的服务名称,并提供完整的XML文档注释,提高代码的可读性和可维护性。
2025-01-15 终端设备管理前端界面修复
修复内容
根据TerminalDevicesController.cs控制器,修复pages.terminal-devices.view界面,参考pages.instruments样式实现完整的终端设备管理功能
修改的文件
1. 终端设备服务层更新
文件:X1.WebUI/src/services/terminalDeviceService.ts
修改内容:
- 更新终端设备接口定义,使其与后端响应结构匹配
- 移除serialNumber字段,添加deviceType字段
- 更新创建和更新请求接口,匹配后端命令结构
- 添加导出函数,与instruments服务保持一致
- 修复接口类型不匹配问题
2. 终端设备表格组件更新
文件:X1.WebUI/src/pages/terminal-devices/TerminalDevicesTable.tsx
修改内容:
- 移除serialNumber字段的显示
- 添加deviceType字段的显示
- 更新字段映射关系
- 保持与instruments表格组件相同的样式和功能
3. 终端设备表单组件修复
文件:X1.WebUI/src/pages/terminal-devices/TerminalDeviceForm.tsx
修改内容:
- 修复编码问题,重新创建文件确保UTF-8编码
- 更新表单验证逻辑,匹配后端接口
- 移除deviceName字段的更新功能,只保留description和isEnabled
- 创建模式下包含所有必要字段
- 编辑模式下只允许修改描述和启用状态
4. 终端设备主视图创建
文件:X1.WebUI/src/pages/terminal-devices/TerminalDevicesView.tsx
修改内容:
- 创建完整的终端设备管理页面
- 参考instruments页面的样式和功能
- 实现完整的CRUD操作:创建、读取、更新、删除
- 支持搜索、分页、排序等功能
- 集成表格工具栏和分页组件
- 提供Toast提示和错误处理
5. 依赖包安装
文件:X1.WebUI/package.json
修改内容:
- 安装缺失的
@hookform/resolvers依赖包 - 确保表单验证功能正常工作
功能特性
-
完整的终端设备管理:
- 支持终端设备的创建、查看、编辑、删除
- 自动获取设备序列号和系统类型
- 设备类型自动检测(Windows/Linux)
-
搜索和筛选功能:
- 支持按设备名称或设备编码搜索
- 支持按启用状态筛选
- 实时搜索和重置功能
-
表格功能:
- 支持表格密度调整(紧凑、默认、舒适)
- 支持列显示/隐藏控制
- 支持分页和每页条数调整
- 状态徽章显示
-
表单验证:
- 使用React Hook Form和Zod进行表单验证
- 完整的字段验证和错误提示
- 支持创建和编辑两种模式
-
用户体验:
- 响应式设计,适配不同屏幕尺寸
- Toast提示和错误处理
- 加载状态和提交状态管理
- 现代化的UI设计
技术实现
-
前端架构:
- 使用React + TypeScript开发
- 采用shadcn/ui组件库
- 使用React Hook Form进行表单管理
- 使用Zod进行数据验证
-
API集成:
- 基于TerminalDevicesController的RESTful API
- 完整的CRUD操作支持
- 统一的错误处理和响应格式
-
状态管理:
- 使用React Hooks管理组件状态
- 支持搜索、分页、表格配置等状态
- 完整的加载和错误状态管理
完成状态
- ✅ 终端设备服务层更新完成
- ✅ 表格组件更新完成
- ✅ 表单组件修复完成
- ✅ 主视图创建完成
- ✅ 依赖包安装完成
- ✅ 编码问题修复完成
- ✅ 功能测试通过
CreateTerminalDeviceCommandHandler 设备类型检测优化
修改文件:
X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs - 优化设备类型检测逻辑
修改内容:
-
依赖注入修复:
- 将
IBaseInstrumentClient替换为IBaseTerminalClient - 更新字段名从
_instrumentClient到_terminalClient - 添加正确的using语句引用
- 将
-
设备类型检测优化:
- 实现真实的设备类型检测逻辑
- 通过调用
GetDeviceSerialNumberAsync获取系统类型信息 - 根据系统类型字符串智能判断设备类型
- 支持Linux和Windows系统的识别
-
系统类型识别逻辑:
- Linux系统识别:linux、ubuntu、centos、debian、fedora、redhat、suse、unix
- Windows系统识别:windows、win、nt
- 默认处理:未知系统类型默认使用Windows
-
技术特性:
- 真实检测:基于API返回的系统类型信息进行检测
- 智能识别:支持多种Linux发行版和Windows版本
- 错误处理:检测失败时使用默认类型
- 资源管理:自动清理临时服务端点
-
方法调用修复:
- 更新
GetDeviceSerialNumberAsync方法调用 - 处理返回的
MachineCodeData对象 - 提取
MachineCode和SystemType信息 - 完整的null检查和异常处理
- 更新
-
日志记录优化:
- 记录设备类型检测过程
- 记录系统类型和检测结果
- 记录检测失败的原因
- 详细的调试信息
-
使用示例:
// 设备类型检测流程 var deviceType = await DetectDeviceTypeByPortsAsync(request, serviceEndpoint, cancellationToken); // 根据API返回的系统类型自动判断是Windows还是Linux
修改时间:
2025年1月13日
修改原因:
需要实现真实的设备类型检测功能,通过API获取的系统类型信息智能判断设备是Windows还是Linux系统,提高设备管理的准确性和自动化程度。
CreateTerminalDeviceCommandHandler API调用优化
修改文件:
X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs - 优化API调用逻辑
修改内容:
-
API调用优化:
- 合并序列号获取和设备类型检测为一次API调用
- 移除重复的
DetectDeviceTypeByPortsAsync方法 - 创建
DeviceInfo数据传输对象,包含序列号和设备类型
-
新增DeviceInfo类:
SerialNumber:设备序列号DeviceType:设备类型(Windows/Linux)- 用于封装API返回的完整设备信息
-
方法重构:
GetDeviceSerialNumberAsync→GetDeviceInfoAsync- 在一次API调用中同时获取序列号和系统类型
- 根据系统类型自动判断设备类型
- 返回包含完整信息的
DeviceInfo对象
-
技术特性:
- 性能优化:减少API调用次数,从2次减少到1次
- 数据完整性:确保序列号和系统类型信息的一致性
- 错误处理:统一的错误处理和资源清理
- 类型安全:使用强类型的
DeviceInfo对象
-
工作流程优化:
原流程:获取序列号 → 检测设备类型(重复API调用) 新流程:获取设备信息(一次API调用,包含序列号和系统类型) -
代码简化:
- 移除重复的API调用逻辑
- 简化主处理流程
- 统一的资源管理和错误处理
-
使用示例:
// 一次API调用获取完整设备信息 var deviceInfoResult = await GetDeviceInfoAsync(request, serviceEndpoint, cancellationToken); if (deviceInfoResult.IsSuccess) { var serialNumber = deviceInfoResult.Data.SerialNumber; var deviceType = deviceInfoResult.Data.DeviceType; }
修改时间:
2025年1月13日
修改原因:
优化API调用逻辑,避免重复的API请求,提高性能并确保数据一致性。将序列号获取和设备类型检测合并为一次API调用,简化代码结构并提升用户体验。
2025-01-15 - ADB 操作管理页面实现
新增文件
-
X1.WebUI/src/pages/adb-operations/AdbOperationsView.tsx
- 实现了完整的 ADB 操作管理页面
- 参考 AdbOperationsController 和 pages.protocols 的结构
- 包含搜索、分页、表格密度控制、列显示控制等功能
- 支持创建、编辑、删除 ADB 操作
-
X1.WebUI/src/pages/adb-operations/AdbOperationsTable.tsx
- 实现了 ADB 操作数据表格组件
- 支持自定义列显示和表格密度
- 包含状态徽章和绝对路径徽章组件
- 支持编辑和删除操作
-
X1.WebUI/src/pages/adb-operations/AdbOperationForm.tsx
- 实现了 ADB 操作表单组件
- 支持创建和编辑模式
- 包含所有必要的字段:命令、描述、路径、绝对路径、启用状态、等待时间
- 使用 Checkbox 组件替代 Switch 组件
修改文件
- X1.WebUI/src/services/adbOperationsService.ts
- 修复了 OperationResult 的导入路径
- 从 '@/types/auth' 改为 '@/config/types/config.types'
功能特性
- 搜索功能: 支持按关键词搜索命令和描述
- 分页功能: 支持分页显示和每页条数选择
- 表格控制: 支持表格密度调整和列显示控制
- CRUD 操作: 完整的创建、读取、更新、删除功能
- 状态管理: 实时状态反馈和错误处理
- 响应式设计: 适配不同屏幕尺寸
- 用户体验: Toast 提示、加载状态、防重复提交
技术实现
- 使用 React Hooks 进行状态管理
- 采用 TypeScript 进行类型安全
- 使用 shadcn/ui 组件库
- 遵循 Clean Architecture 架构模式
- 参考现有 protocols 页面的设计模式
2025-01-15 终端设备表格组件编码问题修复
修改文件
X1.WebUI/src/pages/terminal-devices/TerminalDevicesTable.tsx
修改内容
-
问题描述:
- 文件出现编码问题:
Unexpected character ''. (1:0) - 这通常是由于文件编码格式不正确导致的
- 文件出现编码问题:
-
解决方案:
- 删除原文件并重新创建,确保正确的UTF-8编码
- 修复字段映射问题:将
device.deviceName改为device.name - 修复日期处理问题:添加空值检查,避免
undefined日期转换错误
-
具体修复:
- 字段映射修复:
device.deviceName→device.name(匹配接口定义) - 日期处理修复:添加
device.createdAt ?和device.updatedAt ?空值检查 - 编码问题修复:重新创建文件确保UTF-8编码正确
- 字段映射修复:
-
技术细节:
- 确保所有字段映射与
TerminalDevice接口定义一致 - 添加完整的空值检查,提高代码健壮性
- 保持与
instruments页面表格组件相同的功能和样式
- 确保所有字段映射与
完成状态
- ✅ 文件编码问题修复完成
- ✅ 字段映射错误修复完成
- ✅ 日期处理错误修复完成
- ✅ 表格组件功能正常
2025-01-15 终端设备改为终端服务
修改内容
-
文件夹重命名:
- 将
X1.WebUI/src/pages/terminal-devices重命名为X1.WebUI/src/pages/terminal-services
- 将
-
服务文件重命名和更新:
- 将
terminalDeviceService.ts重命名为terminalService.ts - 更新所有接口名称,将"终端设备"改为"终端服务"
- 更新所有函数名称和变量名称
- 将
-
前端组件重命名和更新:
TerminalDevicesView.tsx→TerminalServicesView.tsxTerminalDevicesTable.tsx→TerminalServicesTable.tsxTerminalDeviceForm.tsx→TerminalServiceForm.tsx- 更新所有组件内部的文本和变量名称
-
路由配置更新:
- 更新
AppRouter.tsx中的路由路径从terminal-devices改为terminal-services - 更新组件引用名称
- 更新
-
菜单配置更新:
- 更新
menuConfig.ts中的菜单路径和标题 - 将"终端设备"改为"终端服务"
- 更新权限配置从
terminaldevices.view改为terminalservices.view
- 更新
-
权限配置更新:
- 更新权限类型定义从
terminaldevices.view/manage改为terminalservices.view/manage - 更新路由配置中的权限要求
- 更新权限类型定义从
修改原因
为了更好地反映系统的实际功能,将"终端设备"改为"终端服务",因为这里管理的是终端服务而不是物理设备。
影响范围
- 前端用户界面
- 路由配置
- 菜单显示
- 服务层接口
注意事项
- 后端API路径保持不变(仍使用
TERMINAL_DEVICES) - 权限配置已更新为
terminalservices.view和terminalservices.manage - 数据库表结构保持不变
2024-12-19 - 创建终端设备表 (TerminalDevice) 和更新终端服务字段名
新增文件
-
X1.Domain/Entities/Terminal/TerminalDevice.cs - 终端设备实体
- 继承自
Entity基类 - 包含设备基本信息:品牌、型号、设备标识、名称
- 包含Android相关信息:版本、SDK版本、构建信息
- 包含硬件信息:序列号、启动序列号、硬件平台
- 包含状态管理:设备状态、是否启用、是否被绑定
- 包含连接时间记录:最后连接时间、最后断开时间
- 继承自
-
X1.Domain/Repositories/Terminal/ITerminalDeviceRepository.cs - 终端设备仓储接口
- 基本的CRUD操作
- 按品牌、型号、状态查询
- 在线/离线设备查询
- 启用/禁用设备查询
- 已绑定/未绑定设备查询
- 统计功能:设备数量、品牌统计、Android版本统计
-
X1.Infrastructure/Repositories/Terminal/TerminalDeviceRepository.cs - 终端设备仓储实现
- 实现所有仓储接口方法
- 使用CQRS模式分离读写操作
- 支持分页查询和搜索功能
-
X1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs - Entity Framework配置
- 表名:
tb_terminal_devices - 字段长度和约束配置
- 索引配置:序列号、设备编码、品牌、型号、状态等
- 默认值配置
- 表名:
修改文件
-
X1.Infrastructure/Context/AppDbContext.cs
- 添加
TerminalDevicesDbSet
- 添加
-
X1.Infrastructure/DependencyInjection.cs
- 注册
ITerminalDeviceRepository和TerminalDeviceRepository
- 注册
-
X1.Domain/Entities/Terminal/TerminalService.cs
- 将
DeviceCode字段重命名为ServiceCode - 更新
Create和Update方法中的参数名 - 更新字段注释
- 将
-
X1.Domain/Repositories/Terminal/ITerminalServiceRepository.cs
- 将
DeviceCodeExistsAsync方法重命名为ServiceCodeExistsAsync - 更新方法注释
- 将
-
X1.Infrastructure/Repositories/Terminal/TerminalServiceRepository.cs
- 更新搜索方法中的字段引用
- 将
DeviceCodeExistsAsync方法重命名为ServiceCodeExistsAsync - 更新
GetServiceBasicInfoListAsync方法中的字段映射
-
X1.Domain/Models/DeviceBasicInfo.cs
- 将
DeviceCode属性重命名为ServiceCode - 更新类注释和方法注释
- 将
-
X1.Infrastructure/Configurations/Terminal/TerminalServiceConfiguration.cs
- 更新属性配置从
DeviceCode到ServiceCode - 更新索引配置
- 更新属性配置从
字段说明
Brand: 设备品牌 (必填,最大50字符)Model: 设备型号 (必填,最大100字符)Device: 设备标识 (必填,最大50字符)Name: 设备名称 (必填,最大100字符)AndroidVersion: Android版本 (最大20字符)SdkVersion: SDK版本 (最大10字符)BuildId: 构建ID (最大100字符)BuildType: 构建类型 (最大20字符)Serial: 设备序列号 (必填,最大50字符,唯一索引)BootSerial: 启动序列号 (最大50字符)Hardware: 硬件信息 (最大100字符)HardwarePlatform: 硬件平台 (最大100字符)Locale: 区域设置 (最大20字符)Status: 设备状态 (枚举类型,TerminalDeviceStatus.Online表示在线,TerminalDeviceStatus.Offline表示离线)DeviceCode: 设备编码 (必填,最大50字符,唯一索引)ServiceSerial: 关联的终端服务序列号 (必填,最大50字符,索引)IsEnabled: 是否启用 (默认true)IsBound: 是否被绑定 (默认false)LastConnectedAt: 最后连接时间LastDisconnectedAt: 最后断开时间Description: 设备描述 (最大500字符)
业务方法
Create(): 创建终端设备Update(): 更新设备信息(仅允许修改 Description、IsBound、IsEnabled)SetOnline(): 设置设备在线状态SetOffline(): 设置设备离线状态Enable(): 启用设备Disable(): 禁用设备Bind(): 绑定设备Unbind(): 解绑设备UpdateDescription(): 更新设备描述
字段修改限制
- 不可修改字段:Brand、Model、Device、Name、AndroidVersion、SdkVersion、BuildId、BuildType、Serial、BootSerial、Hardware、HardwarePlatform、Locale、Status、ServiceSerial
- 可修改字段:Description、Alias、IsBound、IsEnabled、LastConnectedAt、LastDisconnectedAt
- 自动更新字段:UpdatedAt(在修改时自动更新)
关联关系
TerminalServiceSerialNumber字段用于关联TerminalService.SerialNumber,建立终端设备与终端服务的一对一关系
2024-12-19 - 重新迁移数据库
操作内容
执行了数据库迁移命令,更新数据库结构以匹配最新的实体模型。
执行命令
dotnet ef database update --project X1.Infrastructure --startup-project X1.WebAPI
执行结果
- ✅ 构建成功
- ✅ 数据库配置验证通过
- ✅ JWT配置验证通过
- ✅ 邮件配置验证通过
- ✅ 数据库迁移成功完成
注意事项
- 系统显示了一些警告信息,主要是关于Identity实体的映射和查询过滤器配置
- 这些警告不影响数据库迁移的正常执行
- 数据库结构已成功更新到最新版本
2024-12-19 - 创建终端相关表并应用数据库迁移
问题描述
用户反馈终端相关表(TerminalService、TerminalDevice、AdbOperation、AtOperation)在数据库中查不到。
问题分析
- 检查发现只有初始迁移
20250801075432_InitialCreate,没有包含终端相关表的迁移 - 虽然实体和配置都已存在,但数据库中没有对应的表结构
- 需要创建新的迁移来添加这些表
解决方案
-
创建新迁移:
dotnet ef migrations add AddTerminalTables --project X1.Infrastructure --startup-project X1.WebAPI -
应用迁移到数据库:
dotnet ef database update --project X1.Infrastructure --startup-project X1.WebAPI
创建的迁移文件
- 20250815154347_AddTerminalTables.cs - 包含所有终端相关表的创建语句
创建的表结构
-
tb_terminal_services - 终端服务表
- 包含服务基本信息:名称、序列号、服务编码、描述
- 包含网络配置:代理端口、IP地址
- 包含状态管理:是否启用、服务是否启动、服务类型
- 包含审计字段:创建时间、更新时间、创建人、更新人
-
tb_terminal_devices - 终端设备表
- 包含设备基本信息:品牌、型号、设备标识、名称、别名
- 包含Android信息:版本、SDK版本、构建信息
- 包含硬件信息:序列号、启动序列号、硬件平台
- 包含状态管理:设备状态、是否启用、是否被绑定
- 包含连接时间:最后连接时间、最后断开时间
-
tb_adboperations - ADB操作表
- 包含命令信息:ADB命令、描述、路径
- 包含执行配置:是否使用绝对路径、等待时间
- 包含截图数据:操作截图(字节数组)
- 包含审计字段:创建时间、更新时间、创建人、更新人
-
tb_atoperations - AT操作表
- 包含设备信息:设备ID、串口端口
- 包含串口配置:波特率、数据位、校验位、停止位
- 包含命令信息:AT命令、参数、超时时间、描述
- 包含截图数据:操作截图(字节数组)
- 包含审计字段:创建时间、更新时间、创建人、更新人
创建的索引
- 为所有表创建了相应的索引以提高查询性能
- 为唯一字段创建了唯一索引(如序列号、服务编码等)
- 为常用查询字段创建了普通索引(如状态、创建时间等)
执行结果
- ✅ 构建成功
- ✅ 数据库配置验证通过
- ✅ 所有表创建成功
- ✅ 所有索引创建成功
- ✅ 迁移记录已保存到数据库
验证结果
- 迁移列表显示两个迁移:
InitialCreate和AddTerminalTables - 所有终端相关表现在都可以在数据库中正常查询
- 表结构完整,包含所有必要的字段和约束
2025-01-16 终端服务前端字段名修复
修改文件:
X1.WebUI/src/services/terminalService.ts- 修复接口字段名X1.WebUI/src/pages/terminal-services/TerminalServicesTable.tsx- 修复表格字段映射X1.WebUI/src/pages/terminal-services/TerminalServiceForm.tsx- 修复表单字段名X1.WebUI/src/pages/terminal-services/TerminalServicesView.tsx- 修复列配置字段名
修改内容:
-
服务接口字段名修复:
- TerminalService接口:
deviceCode→serviceCodedeviceType→serviceType
- CreateTerminalServiceRequest接口:
deviceName→serviceName
- CreateTerminalServiceResponse接口:
deviceId→serviceIddeviceName→serviceNamedeviceCode→serviceCodedeviceType→serviceType
- UpdateTerminalServiceRequest接口:
deviceId→serviceId
- UpdateTerminalServiceResponse接口:
deviceId→serviceIddeviceName→serviceNamedeviceCode→serviceCodedeviceType→serviceType
- TerminalService接口:
-
表格组件字段映射修复:
- renderCell方法:
deviceType→serviceType
- columns配置:
deviceType→serviceType
- renderCell方法:
-
表单组件字段名修复:
- formData状态:
deviceName→serviceName
- handleSubmit方法:
deviceId→serviceId
- 表单字段:
deviceName→serviceName
- formData状态:
-
主视图组件列配置修复:
- defaultColumns配置:
deviceCode→serviceCodedeviceType→serviceType
- defaultColumns配置:
-
技术特性:
- 一致性:确保前端字段名与后端API完全一致
- 类型安全:修复TypeScript类型定义
- 功能完整:保持所有CRUD操作功能正常
- 用户体验:确保表格显示和表单操作正常
修改时间:
2025-01-16
修改原因:
修复前端组件中的字段名与后端API不一致的问题,确保终端服务管理功能正常工作。主要问题是前端使用了旧的字段名(如deviceCode、deviceType),而后端API使用的是新的字段名(如serviceCode、serviceType)。
2025-01-16 终端服务表格按钮图标化优化
修改文件:
X1.WebUI/src/pages/terminal-services/TerminalServicesTable.tsx - 将编辑和删除按钮改为纯图标
修改内容:
-
功能描述:
- 将终端服务表格中的编辑和删除按钮改为纯图标,去掉按钮边框和样式
- 使用原生
button元素替代Button组件 - 保持图标颜色和悬停效果,提供更好的视觉体验
-
按钮样式优化:
- 移除按钮组件:将
Button组件改为原生button元素 - 去掉边框:移除
variant="outline"和size="sm"属性 - 简化样式:只保留颜色、悬停效果和基本内边距
- 调整内边距:从
p-2改为p-1,使图标更紧凑
- 移除按钮组件:将
-
颜色方案:
- 启动按钮:绿色 (
text-green-600 hover:text-green-700) - 停止按钮:红色 (
text-red-600 hover:text-red-700) - 编辑按钮:蓝色 (
text-blue-600 hover:text-blue-700) - 删除按钮:红色 (
text-red-600 hover:text-red-700)
- 启动按钮:绿色 (
-
用户体验改进:
- 界面更加简洁,没有多余的边框和背景
- 图标颜色区分不同操作类型
- 悬停效果提供交互反馈
- 保持 tooltip 提示功能
-
技术实现:
- 使用原生 HTML
button元素 - 保持所有点击事件和功能不变
- 使用 Tailwind CSS 类进行样式控制
- 图标尺寸保持
h-4 w-4一致性
- 使用原生 HTML
修改时间:
2025-01-16
修改原因:
用户反馈按钮边框太丑,要求改为纯图标样式,去掉边框和按钮背景,提供更简洁美观的界面。
TerminalMessageHandler 实现 IBaseTerminalClient 接口
修改文件:
X1.WebSocket/Handlers/TerminalMessageHandler.csX1.WebSocket/Extensions/ServiceCollectionExtensions.csX1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IBaseTerminalClient.csX1.DynamicClientCore/Service/TestTerminalRequestClient.cs
修改内容:
-
接口实现:
- TerminalMessageHandler:现在同时实现
IWebSocketMessageHandler和IBaseTerminalClient接口 - 添加了
IDynamicHttpClient依赖注入,用于HTTP请求操作 - 保持了原有的WebSocket消息处理功能
- TerminalMessageHandler:现在同时实现
-
新增方法实现:
-
GetDeviceSerialNumberAsync:获取测试终端机器码数据
- 完整的参数验证和异常处理
- 使用
IDynamicHttpClient调用机器码API - 详细的日志记录和错误处理
- 返回
MachineCodeData对象,包含机器码和系统类型
-
CreateTerminalWebSocketAsync:创建并连接终端 WebSocket 客户端
- 验证请求参数(客户端名称和连接地址)
- 使用
IDynamicHttpClient调用WebSocket客户端创建API - 完整的错误处理和日志记录
- 返回
TerminalWebSocketResponse对象
-
DisconnectTerminalWebSocketAsync:断开终端 WebSocket 客户端连接
- 验证客户端名称参数
- 使用
IDynamicHttpClient调用WebSocket客户端断开连接API - 支持 DELETE 请求方法
- 完整的错误处理和日志记录
- 返回
TerminalWebSocketResponse对象
-
-
接口扩展:
- IBaseTerminalClient:添加了
DisconnectTerminalWebSocketAsync方法- 支持断开指定的WebSocket客户端连接
- 完整的参数验证和异常处理
- 统一的响应格式
- IBaseTerminalClient:添加了
-
依赖注入更新:
- 构造函数添加了
IDynamicHttpClient参数 - 保持了原有的
ILogger和IMediator依赖 - 所有依赖都进行了空值检查和异常处理
- ServiceCollectionExtensions:添加了
TerminalMessageHandler作为IBaseTerminalClient的注册 - 添加了必要的 using 语句:
using X1.Domain.ThirdPartyDeviceHttpClient.ITerminal;
- 构造函数添加了
-
技术特性:
- 双重职责:既处理WebSocket消息,又提供终端客户端功能
- 完整实现:实现了
IBaseTerminalClient接口的所有方法 - 错误处理:统一的异常处理和日志记录
- 参数验证:严格的输入参数验证
- 异步支持:所有方法都支持异步操作和取消令牌
-
API端点配置:
- 机器码获取:使用
{terminalId}/system/machine-code端点 - WebSocket客户端创建:使用
websocket/websocket/clients端点 - WebSocket客户端断开:使用
websocket/websocket/clients/{clientName}/disconnect端点 - 支持自定义请求选项和超时配置
- 机器码获取:使用
-
响应处理:
- 机器码API:使用
TestTerminalResponse<MachineCodeData>响应格式 - WebSocket客户端API:使用
TerminalWebSocketResponse响应格式 - 统一的成功/失败判断逻辑
- 机器码API:使用
-
日志记录:
- 详细的操作开始和完成日志
- 错误情况的警告和错误日志
- 包含关键参数(终端ID、客户端名称等)的结构化日志
-
依赖注入配置:
- 双重注册:
TerminalMessageHandler同时注册为IWebSocketMessageHandler和IBaseTerminalClient - 单例模式:使用单例生命周期,确保状态一致性
- 接口隔离:通过接口注册,支持依赖注入和单元测试
- 双重注册:
-
实现一致性:
- TerminalMessageHandler 和 TestTerminalRequestClient 都完整实现了
IBaseTerminalClient接口 - 两个实现类使用相同的API端点和响应格式
- 统一的错误处理和日志记录模式
- TerminalMessageHandler 和 TestTerminalRequestClient 都完整实现了
使用示例:
// 通过依赖注入获取处理器实例
var handler = serviceProvider.GetService<TerminalMessageHandler>();
// 获取机器码数据
var machineCodeData = await handler.GetDeviceSerialNumberAsync("terminal-001");
if (machineCodeData != null)
{
var machineCode = machineCodeData.MachineCode;
var systemType = machineCodeData.SystemType;
}
// 创建WebSocket客户端
var webSocketRequest = new TerminalWebSocketRequest
{
Name = "test_client",
Url = "ws://localhost:8080/ws",
HeartbeatInterval = 30
};
var webSocketResponse = await handler.CreateTerminalWebSocketAsync(webSocketRequest);
// 断开WebSocket客户端连接
var disconnectResponse = await handler.DisconnectTerminalWebSocketAsync("test_client");
if (disconnectResponse != null && disconnectResponse.Success)
{
Console.WriteLine($"客户端断开成功: {disconnectResponse.Message}");
}
设计原则:
- 单一职责扩展:在保持WebSocket消息处理功能的同时,扩展终端客户端功能
- 接口实现完整:完整实现
IBaseTerminalClient接口的所有方法 - 错误处理统一:使用统一的异常处理和日志记录模式
- 依赖注入规范:遵循依赖注入最佳实践
- 异步操作支持:所有方法都支持异步操作和取消令牌
修改时间:
2025年1月16日
修改原因:
为终端服务启动功能添加 WebSocket 客户端创建接口,支持与 WebSocket 服务器的连接管理,提高系统的实时通信能力。
CreateTerminalWebSocketAsync 方法实现完成
修改文件:
X1.Domain/ThirdPartyDeviceHttpClient/Models/TerminalWebSocketModels.cs(重新创建)X1.WebSocket/Handlers/TerminalMessageHandler.cs(已实现)
修改内容:
-
TerminalMessageHandler 实现状态:
-
✅ GetDeviceSerialNumberAsync:已完整实现
- 参数验证:检查
terminalId是否为空 - API调用:使用
IDynamicHttpClient.GetAsync调用机器码API - 端点配置:使用
{terminalId}/system/machine-code端点 - 响应处理:使用
TestTerminalResponse<MachineCodeData>格式 - 错误处理:完整的异常捕获和日志记录
- 返回数据:包含机器码和系统类型信息
- 参数验证:检查
-
✅ CreateTerminalWebSocketAsync:已完整实现
- 参数验证:检查
request、Name、Url是否为空 - API调用:使用
IDynamicHttpClient.PostAsync调用WebSocket客户端创建API - 端点配置:使用
websocket/websocket/clients端点 - 响应处理:使用
TerminalWebSocketResponse格式 - 错误处理:完整的异常捕获和日志记录
- 返回数据:包含客户端状态和通道信息
- 参数验证:检查
-
-
模型文件重新创建:
- TerminalWebSocketRequest:终端 WebSocket 客户端创建请求模型
Name:客户端名称Url:WebSocket 连接地址HeartbeatInterval:心跳间隔(秒),默认30
- TerminalWebSocketData:终端 WebSocket 客户端数据模型
Name:客户端名称Url:WebSocket 连接地址Status:连接状态HeartbeatInterval:心跳间隔(秒)Channels:支持的通道列表
- TerminalWebSocketResponse:终端 WebSocket 客户端创建响应模型
Message:响应消息Data:响应数据Timestamp:时间戳Success:是否成功,默认true
- TerminalWebSocketRequest:终端 WebSocket 客户端创建请求模型
-
依赖注入配置:
- ✅ ServiceCollectionExtensions:已配置
TerminalMessageHandler作为IBaseTerminalClient的实现 - ✅ 双重注册:同时注册为
IWebSocketMessageHandler和IBaseTerminalClient - ✅ 单例模式:使用单例生命周期,确保状态一致性
- ✅ ServiceCollectionExtensions:已配置
-
技术特性:
- 双重职责:既处理WebSocket消息,又提供终端客户端功能
- 完整实现:实现了
IBaseTerminalClient接口的所有方法 - 错误处理:统一的异常处理和日志记录
- 参数验证:严格的输入参数验证
- 异步支持:所有方法都支持异步操作和取消令牌
- 详细日志:包含关键参数的结构化日志记录
-
API端点配置:
- 机器码获取:
{terminalId}/system/machine-code - WebSocket客户端创建:
websocket/websocket/clients - 支持自定义请求选项和超时配置
- 机器码获取:
-
响应格式:
- 机器码API:
TestTerminalResponse<MachineCodeData> - WebSocket客户端API:
TerminalWebSocketResponse - 统一的成功/失败判断逻辑
- 机器码API:
使用示例:
// 通过依赖注入获取处理器实例
var handler = serviceProvider.GetService<TerminalMessageHandler>();
// 创建WebSocket客户端
var webSocketRequest = new TerminalWebSocketRequest
{
Name = "test_client",
Url = "ws://localhost:8080/ws",
HeartbeatInterval = 30
};
var webSocketResponse = await handler.CreateTerminalWebSocketAsync(webSocketRequest);
if (webSocketResponse?.Success == true)
{
var clientName = webSocketResponse.Data?.Name;
var status = webSocketResponse.Data?.Status;
var channels = webSocketResponse.Data?.Channels;
}
设计原则:
- 单一职责扩展:在保持WebSocket消息处理功能的同时,扩展终端客户端功能
- 接口实现完整:完整实现
IBaseTerminalClient接口的所有方法 - 错误处理统一:使用统一的异常处理和日志记录模式
- 依赖注入规范:遵循依赖注入最佳实践
- 异步操作支持:所有方法都支持异步操作和取消令牌
修改时间:
2025年1月16日
修改原因:
完成 CreateTerminalWebSocketAsync 方法的实现,使 TerminalMessageHandler 能够完整提供终端客户端功能,支持WebSocket客户端的创建和管理。
2025-01-16 TerminalMessageEventHandler 依赖注入问题修复
修改文件:
X1.Application/Features/TerminalMessageEvents/TerminalMessageEventHandler.cs - 修复作用域服务依赖注入问题
修改内容:
-
问题描述:
TerminalMessageEventHandler直接依赖ITerminalDeviceRepository导致依赖注入错误- MediatR 注册
INotificationHandler时默认使用单例生命周期 ITerminalDeviceRepository被注册为作用域服务,无法在单例中直接注入
-
解决方案:
- 使用
IServiceScopeFactory替代直接依赖注入 - 在
Handle方法中创建作用域来获取仓储服务 - 确保作用域在方法结束时正确释放
- 使用
-
具体修改:
-
构造函数修改:
- 移除
ITerminalDeviceRepository直接依赖 - 添加
IServiceScopeFactory依赖注入 - 保持
ILogger依赖不变
- 移除
-
Handle方法修改:
- 使用
_serviceScopeFactory.CreateScope()创建作用域 - 通过
scope.ServiceProvider.GetRequiredService<ITerminalDeviceRepository>()获取仓储服务 - 使用
using语句确保作用域正确释放
- 使用
-
ProcessTerminalMessageAsync方法修改:
- 添加
ITerminalDeviceRepository terminalRepository参数 - 将仓储服务作为参数传递,而不是使用字段
- 更新所有仓储方法调用
- 添加
-
-
技术特性:
- 作用域管理:正确管理服务作用域的生命周期
- 资源释放:使用
using语句确保作用域及时释放 - 依赖注入:遵循依赖注入最佳实践
- 错误处理:保持完整的异常处理和日志记录
- 性能优化:避免长时间持有作用域引用
-
设计原则:
- 单一职责:每个方法专注于特定功能
- 依赖倒置:通过接口依赖而不是具体实现
- 资源管理:正确管理服务作用域的生命周期
- 错误处理:统一的异常处理和日志记录
修改时间:
2025-01-16
修改原因:
修复 TerminalMessageEventHandler 中的依赖注入问题,解决 MediatR 单例处理器无法直接注入作用域服务的问题,确保终端消息事件能够正常处理。
2025-01-15 - AdbOperation 实体添加 DeviceId 字段
修改文件
- X1.Domain/Entities/Terminal/AdbOperation.cs
修改内容
-
新增字段:
- 添加了
DeviceId属性,类型为string,默认值为空字符串 - 该字段与
TerminalDevice的Serial字段关联,用于标识ADB操作所属的设备
- 添加了
-
更新 Create 方法:
- 添加了
deviceId参数 - 添加了设备ID的必填验证
- 在创建实例时设置
DeviceId属性
- 添加了
-
更新 Update 方法:
- 添加了
deviceId参数 - 添加了设备ID的必填验证
- 在更新时设置
DeviceId属性
- 添加了
-
新增 SetDeviceId 方法:
- 提供了专门用于设置设备ID的方法
- 包含参数验证和审计信息更新
技术特性
- 必填验证:设备ID为必填项,不能为空
- 数据关联:与 TerminalDevice 的 Serial 字段建立关联关系
- 审计支持:所有修改都会更新审计信息
- 参数验证:确保数据完整性和一致性
数据关联说明
- ADB 操作的设备ID 取的是
terminal-devices表中的serial字段 - 关联关系:
AdbOperation.DeviceId=TerminalDevice.Serial - 前端显示:设备选择下拉框显示
{设备别名或名称} - {设备品牌} ({序列号})格式 - 数据存储:后端将选中的序列号存储到
AdbOperation.DeviceId字段
影响范围
- 需要更新相关的 Command 和 Query 类以支持 DeviceId 参数
- 需要更新前端表单以包含设备选择功能
- 需要更新数据库迁移脚本
- 需要更新相关的 DTO 和响应类
后续更新
-
CreateAdbOperationCommand 和 Response:
- 添加了
DeviceId字段 - 更新了 CommandHandler 以支持设备ID参数
- 添加了
-
前端服务接口:
- 更新了
AdbOperation接口,添加deviceId字段 - 更新了
CreateAdbOperationRequest和CreateAdbOperationResponse接口
- 更新了
-
前端表单组件:
- 添加了设备选择功能
- 集成了终端设备服务
- 添加了设备ID的必填验证
- 支持多条命令输入,每条命令带时间
- 路径设为可选,描述不做限制
2025-01-16 - Dockerfile优化:提供开发调试支持
修改文件
- X1.WebAPI/Dockerfile - 使用完整版本镜像
- X1.WebAPI/Dockerfile.dev - 创建开发环境专用Dockerfile
问题描述
使用 mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled 精简镜像时,容器内没有bash或sh shell,导致无法使用 docker exec -it 进入容器进行调试。
解决方案
方案1:生产环境Dockerfile(推荐)
使用完整版本的镜像,提供基本的调试支持:
# 使用官方.NET 8.0运行时镜像作为基础镜像(完整版本,支持调试)
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:12789
# 复制发布文件到容器中
COPY publish/ .
# 暴露端口12789
EXPOSE 12789
# 设置应用启动命令
ENTRYPOINT ["./X1.WebAPI"]
方案2:开发环境Dockerfile(完整调试支持)
创建专门的开发环境Dockerfile,提供完整的调试工具:
# 开发环境Dockerfile - 提供完整的调试支持
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy
# 安装开发调试工具
RUN apt-get update && apt-get install -y \
bash \
curl \
wget \
vim \
nano \
htop \
procps \
net-tools \
iputils-ping \
telnet \
&& rm -rf /var/lib/apt/lists/*
# 设置工作目录
WORKDIR /app
# 设置环境变量(开发环境)
ENV ASPNETCORE_ENVIRONMENT=Development
ENV ASPNETCORE_URLS=http://+:12789
ENV DOTNET_USE_POLLING_FILE_WATCHER=1
# 复制发布文件到容器中
COPY publish/ .
# 暴露端口12789
EXPOSE 12789
# 设置应用启动命令
ENTRYPOINT ["./X1.WebAPI"]
修改内容
生产环境Dockerfile
- 镜像选择:从
jammy-chiseled改为jammy - 调试支持:完整镜像包含bash、sh等基本工具
- 性能平衡:在调试便利性和镜像大小之间取得平衡
开发环境Dockerfile
- 完整工具集:安装常用的调试和网络工具
- 开发环境配置:设置Development环境和文件监视
- 调试工具:包含vim、nano、htop、net-tools等
技术特性
生产环境特性
- 基本调试:支持bash shell和基本命令
- 镜像优化:比精简镜像稍大,但包含必要工具
- 安全平衡:保持相对安全的镜像环境
开发环境特性
- 完整调试:提供完整的Linux工具集
- 网络诊断:包含ping、telnet、net-tools等
- 系统监控:包含htop、procps等监控工具
- 文件编辑:包含vim、nano等编辑器
使用方法
生产环境部署
# 构建生产镜像
docker build -t x1-webapi:latest .
# 运行生产容器
docker run -d --name x1-webapi -p 12789:12789 x1-webapi:latest
# 进入容器调试
docker exec -it <container_id> /bin/bash```
#### 开发环境部署
```bash
# 构建开发镜像
docker build -f Dockerfile.dev -t x1-webapi:dev .
# 运行开发容器
docker run -d --name x1-webapi-dev -p 12789:12789 x1-webapi:dev
# 进入容器调试
docker exec -it <container_id> /bin/bash
# 使用开发工具
docker exec -it <container_id> htop
docker exec -it <container_id> ping google.com
docker exec -it <container_id> netstat -tulpn
开发工具说明
网络诊断工具
- ping:网络连通性测试
- telnet:端口连通性测试
- net-tools:网络配置和状态查看
- curl/wget:HTTP请求测试
系统监控工具
- htop:进程监控和系统资源查看
- procps:进程管理工具
- ps:进程状态查看
文件编辑工具
- vim:高级文本编辑器
- nano:简单文本编辑器
部署建议
生产环境
- 使用
Dockerfile(完整版本镜像) - 提供基本调试支持
- 保持相对安全的镜像环境
开发环境
- 使用
Dockerfile.dev(完整工具集) - 提供完整的调试和诊断工具
- 便于问题排查和系统分析
测试环境
- 根据需求选择对应的Dockerfile
- 可以先用开发版本进行测试
- 确认无误后使用生产版本
优势对比
| 特性 | 精简镜像 | 完整镜像 | 开发镜像 |
|---|---|---|---|
| 镜像大小 | 最小 | 中等 | 较大 |
| 调试支持 | 无 | 基本 | 完整 |
| 安全风险 | 最低 | 中等 | 较高 |
| 开发便利性 | 差 | 好 | 最好 |
| 生产适用性 | 最佳 | 好 | 不推荐 |
注意事项
- 生产环境:建议使用完整镜像,平衡调试需求和安全性
- 开发环境:可以使用开发镜像,提供完整的调试工具
- 镜像大小:完整镜像比精简镜像大约增加50-100MB
- 安全考虑:开发镜像包含更多工具,安全风险相对较高
修改时间:
2025-01-16
修改原因:
优化Docker镜像配置,提供更好的开发调试支持。使用完整版本镜像解决无法进入容器的问题,同时创建专门的开发环境Dockerfile,为不同环境提供合适的调试工具支持。
2025-01-16 - deploy.sh脚本更新:支持开发环境部署
修改文件
X1.WebAPI/deploy.sh - 添加开发环境部署支持
功能增强
为deploy.sh脚本添加了命令行参数支持,可以根据不同环境选择对应的Dockerfile进行部署。
新增功能
1. 命令行参数支持
# 生产环境部署(默认)
./deploy.sh
# 开发环境部署
./deploy.sh --dev
# 显示帮助信息
./deploy.sh --help
2. 环境配置
-
生产环境:
- Dockerfile:
Dockerfile - 镜像名称:
x1-webapi - 容器名称:
x1-webapi-container - 环境变量:
ASPNETCORE_ENVIRONMENT=Production
- Dockerfile:
-
开发环境:
- Dockerfile:
Dockerfile.dev - 镜像名称:
x1-webapi-dev - 容器名称:
x1-webapi-dev-container - 环境变量:
ASPNETCORE_ENVIRONMENT=Development
- Dockerfile:
3. 智能构建逻辑
- 生产环境:使用
docker build -t x1-webapi:latest . - 开发环境:使用
docker build -f Dockerfile.dev -t x1-webapi-dev:latest .
4. 环境特定功能
- 文件检查:根据环境检查对应的Dockerfile是否存在
- 权限设置:为对应的Dockerfile设置正确的权限
- 容器管理:使用环境特定的容器名称进行管理
- 调试信息:开发环境显示额外的调试命令
技术特性
参数解析
- 使用
parse_arguments()函数解析命令行参数 - 支持
--dev、--help、-h参数 - 提供详细的帮助信息和使用说明
环境隔离
- 不同环境使用不同的镜像和容器名称
- 避免生产环境和开发环境的冲突
- 支持同时运行多个环境
智能检测
- 自动检测Dockerfile是否存在
- 根据环境提供相应的错误提示
- 动态调整构建和运行参数
使用方法
生产环境部署
# 标准生产环境部署
./deploy.sh
# 输出示例
==========================================
X1.WebAPI Docker 部署脚本
开始时间: 2025-01-16 15:30:00
==========================================
部署环境: production
使用Dockerfile: Dockerfile
镜像名称: x1-webapi
容器名称: x1-webapi-container
==========================================
开发环境部署
# 开发环境部署
./deploy.sh --dev
# 输出示例
==========================================
X1.WebAPI Docker 部署脚本
开始时间: 2025-01-16 15:30:00
==========================================
部署环境: development
使用Dockerfile: Dockerfile.dev
镜像名称: x1-webapi-dev
容器名称: x1-webapi-dev-container
端口: 12790
操作模式: 部署模式
==========================================
帮助信息
# 显示帮助
./deploy.sh --help
# 输出示例
X1.WebAPI Docker 部署脚本
使用方法:
./deploy.sh # 生产环境部署
./deploy.sh --dev # 开发环境部署
./deploy.sh --remove # 删除当前环境容器和镜像
./deploy.sh --dev --remove # 删除开发环境容器和镜像
./deploy.sh --help # 显示帮助信息
环境说明:
生产环境: 使用 Dockerfile,包含基本调试工具
开发环境: 使用 Dockerfile.dev,包含完整调试工具集
开发环境包含的额外工具:
- 网络诊断: ping, telnet, net-tools, curl, wget
- 系统监控: htop, procps, ps
- 文件编辑: vim, nano
- 基本工具: bash, procps
删除功能:
--remove: 删除指定环境的容器和镜像,释放资源
部署信息增强
生产环境信息
容器管理命令:
查看状态: docker ps | grep x1-webapi-container
查看日志: docker logs x1-webapi-container
重启容器: docker restart x1-webapi-container
停止容器: docker stop x1-webapi-container
开发环境信息
容器管理命令:
查看状态: docker ps | grep x1-webapi-dev-container
查看日志: docker logs x1-webapi-dev-container
重启容器: docker restart x1-webapi-dev-container
停止容器: docker stop x1-webapi-dev-container
开发环境调试命令:
进入容器: docker exec -it x1-webapi-dev-container /bin/bash
系统监控: docker exec -it x1-webapi-dev-container htop
网络测试: docker exec -it x1-webapi-dev-container ping google.com
端口检查: docker exec -it x1-webapi-dev-container netstat -tulpn
查看进程: docker exec -it x1-webapi-dev-container ps aux
优势
环境隔离
- 生产环境和开发环境完全隔离
- 避免环境间的相互影响
- 支持并行部署和测试
灵活部署
- 一键切换部署环境
- 统一的部署流程
- 环境特定的配置和工具
调试便利
- 开发环境提供完整的调试工具
- 详细的调试命令提示
- 环境特定的日志和监控
运维友好
- 清晰的帮助信息
- 详细的部署状态反馈
- 环境特定的管理命令
注意事项
- 确保
Dockerfile.dev文件存在(开发环境部署时) - 不同环境的容器名称不同,避免冲突
- 开发环境镜像包含更多工具,体积较大
- 生产环境建议使用标准Dockerfile
修改时间:
2025-01-16
修改原因:
为deploy.sh脚本添加开发环境部署支持,提供灵活的环境选择功能,满足不同场景的部署需求,提高开发和运维效率。
2025-01-19 - 修复 API 路径常量重复 /api 前缀问题
修改文件:
X1.WebUI/src/constants/api.ts - 修复 API 路径常量中的重复 /api 前缀
修改内容:
-
问题分析:
- 环境配置中
VITE_API_BASE_URL已经包含/api后缀:https://localhost:7268/api - API 路径常量中又添加了
/api前缀:/api/testcaseflow - 导致最终 URL 变成:
https://localhost:7268/api/api/testcaseflow(错误)
- 环境配置中
-
修复方案:
- TEST_CASE_FLOW:从
/api/testcaseflow改为/testcaseflow - CASE_STEP_CONFIGS:从
/api/casestepconfigs改为/casestepconfigs
- TEST_CASE_FLOW:从
-
修复后的 URL 组合:
- 基础 URL:
https://localhost:7268/api - API 路径:
/testcaseflow - 最终 URL:
https://localhost:7268/api/testcaseflow(正确)
- 基础 URL:
-
技术说明:
- 环境配置中的
baseURL已经包含了/api路径 - API 路径常量应该只包含相对路径,不包含
/api前缀 - 这样可以避免路径重复,确保正确的 API 调用
- 环境配置中的
修改时间:
2025-01-19
修改原因:
用户发现 API 请求失败,检查发现 API 路径常量中重复了 /api 前缀,导致最终 URL 错误。需要修复路径配置以确保正确的 API 调用。
2025-01-19 - 修复 TestFlowType 枚举类型不匹配问题
修改文件:
X1.WebUI/src/services/testcaseService.ts- 修复 TestFlowType 类型定义X1.WebUI/src/pages/testcases/TestCasesView.tsx- 修复 TestFlowType 使用
修改内容:
-
问题分析:
- 后端枚举:
Registration = 1,Voice = 2,Data = 3(数字枚举) - 前端发送:
"Registration"(字符串) - 问题:后端期望数字枚举值,前端发送字符串,导致类型不匹配
- 后端枚举:
-
修复方案:
- TestFlowType 类型定义:从字符串联合类型改为数字联合类型
- 请求数据:使用数字值而不是字符串值
-
具体修改:
// 修复前 export type TestFlowType = 'Registration' | 'Voice' | 'Data'; type: 'Registration' as TestFlowType, // 修复后 export type TestFlowType = 1 | 2 | 3; type: 1 as TestFlowType, // Registration = 1 -
枚举对应关系:
1=Registration(注册测试)2=Voice(语音测试)3=Data(数据测试)
-
技术说明:
- 后端使用 C# 数字枚举,前端需要发送对应的数字值
- 这样可以确保前后端类型完全匹配
- 避免了字符串到数字的转换问题
修改时间:
2025-01-19
修改原因:
用户发现 API 请求失败,检查发现前端发送的 TestFlowType 是字符串,而后端期望的是数字枚举值,导致类型不匹配。需要修复类型定义以确保前后端数据格式一致。
- 设备运行时处理失败:
- 设备运行时不存在
- 数据库保存失败
已实施的修复
-
增强网络配置构建阶段的日志记录:
- 添加了设备过滤统计信息
- 记录每个设备的配置验证结果
- 显示原始请求数和有效请求数的对比
-
改进网络启动阶段的错误信息:
- 在失败设备日志中包含具体的错误信息
- 添加网络启动结果统计日志
-
增强设备运行时处理阶段的日志:
- 记录成功启动网络的设备数量
- 记录每个设备运行时的当前状态
- 提供更详细的处理过程信息
问题排查建议
根据增强的日志记录,现在可以更容易地定位问题:
- 检查网络配置构建阶段:查看是否有设备被过滤掉及其原因
- 检查网络启动阶段:查看具体的网络启动失败原因
- 检查设备运行时处理阶段:查看设备运行时是否存在及状态更新是否成功
建议在下次运行时查看详细的日志输出,以确定具体在哪个阶段出现了问题。
关键问题修复
问题:isSuccess 字段逻辑错误导致前端误判
- 原因:
OperationResult的IsSuccess属性只检查ErrorMessages是否为空,不考虑业务逻辑 - 影响:即使所有设备启动失败,只要没有异常抛出,
isSuccess仍为true - 修复:根据业务逻辑判断成功失败,只有当所有设备都成功启动时才返回成功
修复后的逻辑:
- 如果所有设备都成功启动:返回
CreateSuccess - 如果有任何设备启动失败:返回
CreateFailure并包含详细的错误信息
这样前端就能正确判断操作是否真正成功,避免误判。
修改时间:
2024-12-19
修改原因:
- 增强StartDeviceRuntimeCommandHandler的日志记录,帮助定位设备启动失败的具体原因
- 修复isSuccess字段逻辑错误,确保前端能正确判断操作成功失败状态
2025-01-21 - TestCaseFlow 删除功能修复
修改文件:
X1.Domain/Entities/TestCase/TestCaseFlow.cs- 移除ISoftDelete接口实现X1.Infrastructure/Configurations/TestCase/TestCaseFlowConfiguration.cs- 移除IsDeleted字段映射X1.Infrastructure/Repositories/TestCase/TestCaseFlowRepository.cs- 移除IsDeleted字段过滤
修改内容:
-
实体修改:
- 移除了
TestCaseFlow实体对ISoftDelete接口的实现 - 移除了
IsDeleted属性字段 - 现在
TestCaseFlow执行真正的物理删除,而不是软删除
- 移除了
-
数据库配置修改:
- 移除了
IsDeleted字段的数据库映射配置 - 不再在数据库中创建
isdeleted列
- 移除了
-
仓储实现修改:
- 移除了所有查询方法中对
IsDeleted字段的过滤条件 - 恢复了原来的查询逻辑,不再过滤已删除的记录
- 包括:
GetAllTestCaseFlowsAsync、GetTestCaseFlowByIdAsync、GetByNameAsync、GetByTypeAsync、GetEnabledFlowsAsync、GetTestCaseFlowWithDetailsAsync、NameExistsAsync、GetPagedFlowsAsync等方法
- 移除了所有查询方法中对
-
删除行为:
- 现在
DeleteTestCaseFlowCommandHandler会执行真正的物理删除 - 删除操作会从数据库中永久移除记录
- 由于
TestCaseFlow不再实现ISoftDelete,UnitOfWork.SaveChangesAsync会执行真正的删除操作
- 现在
-
保留软删除基础设施:
ISoftDelete接口仍然保留,供其他实体使用UnitOfWork.SaveChangesAsync中的软删除逻辑仍然保留- 将来如果需要为
TestCaseFlow重新启用软删除,只需要重新实现ISoftDelete接口即可
技术说明:
- 问题原因:之前
TestCaseFlow实现了ISoftDelete接口,导致删除操作被转换为更新操作(设置IsDeleted = true),但查询时没有正确过滤已删除的记录 - 解决方案:移除软删除实现,改为物理删除,确保数据真正从数据库中移除
- 兼容性:保留了软删除的基础设施,其他实体仍然可以使用软删除功能
修改时间:
2025-01-21
修改原因:
用户反馈 DeleteTestCaseFlowCommandHandler 无法真正删除数据,需要改为物理删除而不是软删除。
2025-01-21 - 数据库迁移更新
修改内容:
-
数据库迁移应用:
- 成功应用了所有待处理的数据库迁移
- 包括最新的
20250821155210_AddSourceHandleAndTargetHandleToTestCaseEdge迁移 - 确保
tb_testcaseflow表结构是最新的,不包含IsDeleted字段
-
迁移状态确认:
- 所有迁移都已成功应用到数据库
- 迁移列表显示所有9个迁移都已应用:
20250801075432_InitialCreate20250815154347_AddTerminalTables20250816135847_AddUseCaseNodeConfigTable20250816141032_RemoveIsBoundFromTerminalDeviceAndAddMappingToUseCaseNodeConfig20250816183258_UpdateTableNames20250819133358_UpdateAdbOperationRequiredFields20250820020118_UpdateAdbOperationPathNullable20250821080604_AddTestCaseFlowTables20250821155210_AddSourceHandleAndTargetHandleToTestCaseEdge
-
删除功能修复:
- 确认
TestCaseFlow实体不再实现ISoftDelete接口 - 确认数据库表
tb_testcaseflow不包含IsDeleted字段 - 删除操作现在应该执行真正的物理删除
- 确认
修改时间:
2025-01-21
修改原因:
用户反馈 DeleteTestCaseFlowCommandHandler 无法真正删除数据,需要确保数据库表结构是最新的,并且删除功能执行物理删除而不是软删除。
2025-01-21 - UnitOfWork软删除逻辑修复
修改文件:
X1.Infrastructure/Context/UnitOfWork.cs- 修复软删除逻辑
修改内容:
-
问题分析:
UnitOfWork.SaveChangesAsync方法中的软删除逻辑会将所有删除操作转换为更新操作- 即使实体没有实现
ISoftDelete接口,也会执行软删除逻辑 - 这导致
TestCaseFlow的删除操作被错误地转换为更新操作
-
修复方案:
- 修改软删除逻辑,只有当实体实现
ISoftDelete接口时才执行软删除 - 对于没有实现
ISoftDelete接口的实体,执行真正的物理删除 - 添加日志记录,便于调试和监控删除操作类型
- 修改软删除逻辑,只有当实体实现
-
修复后的逻辑:
case EntityState.Deleted: // 只有当实体实现 ISoftDelete 接口时才执行软删除 if (entry.Entity is ISoftDelete softDeleteEntity) { // 实现软删除:将删除操作转换为更新操作 entry.State = EntityState.Modified; entry.Property(p => p.UpdatedAt).CurrentValue = now; softDeleteEntity.IsDeleted = true; _logger.LogInformation("执行软删除操作,实体类型: {EntityType}", entry.Entity.GetType().Name); } else { // 执行真正的物理删除 _logger.LogInformation("执行物理删除操作,实体类型: {EntityType}", entry.Entity.GetType().Name); } break; -
影响范围:
TestCaseFlow现在会执行真正的物理删除- 其他实现了
ISoftDelete接口的实体仍然执行软删除 - 保持了系统的向后兼容性
修改时间:
2025-01-21
修改原因:
发现 UnitOfWork.SaveChangesAsync 方法中的软删除逻辑有问题,会将所有删除操作都转换为更新操作,导致 TestCaseFlow 无法真正删除数据。
2025-01-21 - TestCaseDetailDrawer连接点动态生成
修改文件:
X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx- 修改连接点生成逻辑
修改内容:
-
问题分析:
- 原来的连接点位置是写死的,根据节点类型固定生成
- 无法根据实际的边数据动态调整连接点位置
- 不够灵活,无法适应不同的流程设计
-
修复方案:
- 修改连接点生成逻辑,根据边数据动态生成
- 从
sourceHandle和targetHandle中提取连接点信息 - 为每个节点生成实际需要的连接点
-
主要修改:
- TestStepNode组件:移除写死的连接点,改为根据
data.handles动态生成 - getReactFlowData函数:添加连接点收集逻辑,从边数据中提取连接点信息
- getHandlePosition函数:根据连接点ID确定位置(left/right/top/bottom)
- TestStepNode组件:移除写死的连接点,改为根据
-
新的连接点生成逻辑:
// 从边数据中提取连接点信息 testCase.edges.forEach(edge => { // 源节点的输出连接点 if (edge.sourceHandle) { nodeHandles[edge.source].push({ id: edge.sourceHandle, type: 'source', position: getHandlePosition(edge.sourceHandle) }); } // 目标节点的输入连接点 if (edge.targetHandle) { nodeHandles[edge.target].push({ id: edge.targetHandle, type: 'target', position: getHandlePosition(edge.targetHandle) }); } }); -
优势:
- 灵活性:连接点位置完全由数据驱动
- 准确性:只显示实际需要的连接点
- 可维护性:代码更简洁,逻辑更清晰
- 扩展性:支持任意数量和位置的连接点
修改时间:
2025-01-21
修改原因:
用户要求连接点位置根据数据动态生成,而不是写死,提高流程图的灵活性和准确性。
2024-12-19 - GetUserByIdQueryHandler CS7036 错误修复
修改文件:
X1.Application/Features/Users/Queries/GetUserById/GetUserByIdQueryHandler.cs- 修复构造函数参数错误
修改内容:
-
依赖注入更新:
- 添加了
RoleManager<AppRole>依赖注入 - 更新了构造函数参数,包含
roleManager参数 - 添加了必要的 using 语句:
System.Collections.Generic和System.Linq
- 添加了
-
角色信息获取:
- 使用
_userManager.GetRolesAsync(user)获取用户的角色名称 - 使用
_roleManager.FindByNameAsync(roleName)获取角色详细信息 - 正确构建
roleIds和roleNames列表
- 使用
-
UserDto 构造函数修复:
- 更新了
UserDto构造函数调用,提供所有必需的参数 - 包括新添加的
RoleIds和RoleNames参数 - 修复了 CS7036 编译错误
- 更新了
技术说明:
- 问题原因:之前修改
UserDto添加了RoleIds和RoleNames参数,但GetUserByIdQueryHandler没有更新相应的构造函数调用 - 解决方案:注入
RoleManager<AppRole>,获取用户的角色信息,并正确传递给UserDto构造函数 - 性能考虑:对于单个用户查询,角色获取的性能影响较小,但确保了数据完整性
修改时间:
2024-12-19
修改原因:
修复 GetUserByIdQueryHandler.cs 中的 CS7036 编译错误,确保 UserDto 构造函数接收所有必需的参数。
2024-12-19 - AppDbContext IdentityUserRole 配置修复
修改文件:
X1.Infrastructure/Context/AppDbContext.cs- 修复 IdentityUserRole 配置
修改内容:
-
移除 IdentityUserRole 忽略配置:
- 移除了
modelBuilder.Ignore<IdentityUserRole<string>>();配置 - 添加了注释说明为什么不能忽略 IdentityUserRole
- 移除了
-
问题原因:
- 之前错误地忽略了
IdentityUserRole<string>实体 - 导致
UserManager.GetRolesAsync()无法正常工作 - 出现 "Cannot create a DbSet for 'IdentityUserRole'" 错误
- 之前错误地忽略了
-
修复效果:
- 允许 Entity Framework 正确管理用户角色关系表
- 确保
GetAllUsersQueryHandler和GetUserByIdQueryHandler中的角色查询正常工作 - 保持 Identity 系统的完整性
技术说明:
- Identity 依赖:ASP.NET Core Identity 需要
IdentityUserRole<string>表来存储用户和角色的多对多关系 - 性能影响:移除忽略配置不会影响性能,反而确保了功能的正确性
- 兼容性:这个修复与现有的用户角色管理功能完全兼容
修改时间:
2024-12-19
修改原因:
修复 "Cannot create a DbSet for 'IdentityUserRole'" 错误,确保用户角色查询功能正常工作。
2025-01-21 - GetAllUsersQueryHandler 性能优化和批量查询实现
修改文件:
X1.Application/Features/Users/Queries/GetAllUsers/GetAllUsersQueryHandler.cs- 优化批量获取用户角色信息的方法X1.Domain/Repositories/Identity/IUserRoleRepository.cs- 添加批量查询方法接口X1.Infrastructure/Repositories/Identity/UserRoleRepository.cs- 实现批量查询方法
修改内容:
-
GetAllUsersQueryHandler 性能优化:
- 问题分析:原来的实现中,每个用户都需要单独查询角色信息,导致 N+1 查询问题
- 优化方案:
- 一次性获取所有角色信息:
await _roleManager.Roles.ToDictionaryAsync(r => r.Id, r => r, cancellationToken) - 批量获取所有用户的角色信息:
await _userRoleRepository.GetUsersRolesAsync(userIds, cancellationToken) - 避免在循环中进行数据库查询,提高性能
- 一次性获取所有角色信息:
-
IUserRoleRepository 接口扩展:
- 新增方法:
GetUsersRolesAsync(IEnumerable<string> userIds, CancellationToken cancellationToken = default) - 返回类型:
Task<Dictionary<string, List<string>>>- 用户ID到角色ID列表的映射 - 设计目的:支持批量查询多个用户的角色信息,避免循环查询
- 新增方法:
-
UserRoleRepository 实现:
- 实现逻辑:
- 使用
QueryRepository.FindAsync(ur => userIds.Contains(ur.UserId))一次性查询所有相关用户角色 - 使用
GroupBy和ToDictionary将结果按用户ID分组 - 返回用户ID到角色ID列表的字典映射
- 使用
- 性能优势:将 N 次查询优化为 1 次查询
- 实现逻辑:
-
技术特性:
- 查询优化:从 O(N) 查询复杂度优化为 O(1) 查询复杂度
- 内存效率:使用字典查找,避免重复的角色信息查询
- 数据一致性:确保所有用户都能正确获取到角色信息
- 错误处理:对于没有角色的用户,返回空列表而不是 null
-
性能提升:
- 数据库查询:从 N+1 次查询减少到 2 次查询(1次角色查询 + 1次用户角色查询)
- 响应时间:显著减少用户列表查询的响应时间
- 资源消耗:减少数据库连接和查询开销
修改时间:
2025-01-21
修改原因:
用户反馈角色数据不多,不需要每次都查询,需要优化 GetAllUsersQueryHandler 的性能,避免在循环中进行数据库查询,提高用户列表查询的效率。
2025-01-21 - CreateUserCommandHandler 事务保存修复
修改文件:
X1.Application/Features/Users/Commands/CreateUser/CreateUserCommandHandler.cs - 修复事务中缺少 SaveChangesAsync 调用的问题
修改内容:
-
问题分析:
- 原始问题:
CreateUserCommandHandler中的ExecuteTransactionAsync缺少SaveChangesAsync调用 - 对比发现:
RegisterUserCommandHandler中有正确的await _unitOfWork.SaveChangesAsync(cancellationToken);调用 - 影响:可能导致事务中的更改没有被正确保存到数据库
- 原始问题:
-
修复内容:
- 添加保存调用:在事务的最后添加
await _unitOfWork.SaveChangesAsync(cancellationToken); - 位置:在用户创建和角色分配成功后,事务提交前
- 确保原子性:保证用户创建和角色分配要么全部成功,要么全部回滚
- 添加保存调用:在事务的最后添加
-
修复后的流程:
await _unitOfWork.ExecuteTransactionAsync(async () => { // 1. 创建用户 var (success, errorMessage) = await _userRegistrationService.RegisterUserAsync(user, request.Password); // 2. 分配用户角色 (success, errorMessage) = await _userRegistrationService.AssignUserRolesAsync(user, request.RoleIds); // 3. 保存所有更改到数据库(新增) await _unitOfWork.SaveChangesAsync(cancellationToken); }, cancellationToken: cancellationToken); -
技术特性:
- 事务完整性:确保所有数据库操作在同一个事务中完成
- 原子性:用户创建和角色分配要么全部成功,要么全部失败
- 一致性:与
RegisterUserCommandHandler的实现保持一致 - 可靠性:避免数据不一致的问题
修改时间:
2025-01-21
修改原因:
用户发现 CreateUserCommandHandler 中的 ExecuteTransactionAsync 没有进行 SaveChangesAsync 调用,需要修复以确保事务中的更改能够正确保存到数据库。
2025-01-21 - DeleteUserCommandHandler 和 UpdateUserCommandHandler 事务保存修复
修改文件:
X1.Application/Features/Users/Commands/DeleteUser/DeleteUserCommandHandler.cs- 修复事务中缺少 SaveChangesAsync 调用的问题X1.Application/Features/Users/Commands/UpdateUser/UpdateUserCommandHandler.cs- 修复事务中缺少 SaveChangesAsync 调用的问题
修改内容:
-
问题分析:
- 原始问题:
DeleteUserCommandHandler和UpdateUserCommandHandler中的ExecuteTransactionAsync都缺少SaveChangesAsync调用 - 影响:可能导致事务中的更改没有被正确保存到数据库
- 发现方式:通过对比
RegisterUserCommandHandler的正确实现发现
- 原始问题:
-
DeleteUserCommandHandler 修复:
- 添加保存调用:在事务的最后添加
await _unitOfWork.SaveChangesAsync(cancellationToken); - 位置:在用户删除成功后,事务提交前
- 确保原子性:保证用户删除操作能够正确提交到数据库
- 添加保存调用:在事务的最后添加
-
UpdateUserCommandHandler 修复:
- 添加保存调用:在事务的最后添加
await _unitOfWork.SaveChangesAsync(cancellationToken); - 位置:在用户更新成功后,事务提交前
- 确保原子性:保证用户更新操作能够正确提交到数据库
- 添加保存调用:在事务的最后添加
-
修复后的流程:
// DeleteUserCommandHandler await _unitOfWork.ExecuteTransactionAsync(async () => { var result = await _userManager.DeleteAsync(user); if (!result.Succeeded) { /* 错误处理 */ } // 保存所有更改到数据库(新增) await _unitOfWork.SaveChangesAsync(cancellationToken); }, cancellationToken: cancellationToken); // UpdateUserCommandHandler await _unitOfWork.ExecuteTransactionAsync(async () => { var result = await _userManager.UpdateAsync(user); if (!result.Succeeded) { /* 错误处理 */ } // 保存所有更改到数据库(新增) await _unitOfWork.SaveChangesAsync(cancellationToken); }, cancellationToken: cancellationToken); -
技术特性:
- 事务完整性:确保所有数据库操作在同一个事务中完成
- 原子性:用户操作要么全部成功,要么全部失败
- 一致性:与
RegisterUserCommandHandler和CreateUserCommandHandler的实现保持一致 - 可靠性:避免数据不一致的问题
-
修复范围:
- DeleteUserCommandHandler:用户删除操作
- UpdateUserCommandHandler:用户更新操作
- CreateUserCommandHandler:用户创建操作(之前已修复)
- RegisterUserCommandHandler:用户注册操作(已有正确实现)
修改时间:
2025-01-21
修改原因:
用户发现 DeleteUserCommandHandler 和 UpdateUserCommandHandler 中的 ExecuteTransactionAsync 也没有进行 SaveChangesAsync 调用,需要修复以确保事务中的更改能够正确保存到数据库,保持与 RegisterUserCommandHandler 实现的一致性。
2025-01-21 - 优化GetTestCaseFlowByIdQueryHandler中的批量查询性能
问题描述
在 GetTestCaseFlowByIdQueryHandler 的 MapNodesToReactFlowFormatAsync 方法中,对每个节点都进行单独的数据库查询来获取步骤配置信息,导致 N+1 查询问题,影响性能。
修改内容
1. 添加批量查询接口
- 文件位置:
X1.Domain/Repositories/TestCase/ICaseStepConfigRepository.cs - 修改内容:
- 添加了
GetStepConfigsByIdsAsync方法,支持根据ID列表批量获取步骤配置 - 返回类型为
Dictionary<string, CaseStepConfig>,便于快速查找
- 添加了
2. 实现批量查询方法
- 文件位置:
X1.Infrastructure/Repositories/TestCase/CaseStepConfigRepository.cs - 修改内容:
- 实现了
GetStepConfigsByIdsAsync方法 - 使用
QueryRepository.FindAsync和ids.Contains(x.Id)进行批量查询 - 将结果转换为字典格式,键为ID,值为配置对象
- 实现了
3. 优化节点映射方法
- 文件位置:
X1.Application/Features/TestCaseFlow/Queries/GetTestCaseFlowById/GetTestCaseFlowByIdQueryHandler.cs - 修改内容:
- 在
MapNodesToReactFlowFormatAsync方法中,先收集所有需要查询的 StepId - 使用
GetStepConfigsByIdsAsync一次性批量查询所有步骤配置 - 在循环中使用字典查找替代单个查询
- 在
修改前后对比
修改前
foreach (var node in nodes)
{
// 获取步骤配置信息
X1.Domain.Entities.TestCase.CaseStepConfig? stepConfig = null;
if (!string.IsNullOrEmpty(node.StepId))
{
stepConfig = await _caseStepConfigRepository.GetStepConfigByIdAsync(node.StepId, cancellationToken);
}
// ... 其他处理逻辑
}
修改后
// 收集所有需要查询的StepId
var stepIds = nodes.Where(n => !string.IsNullOrEmpty(n.StepId))
.Select(n => n.StepId!)
.Distinct()
.ToList();
// 批量查询步骤配置
var stepConfigsDict = await _caseStepConfigRepository.GetStepConfigsByIdsAsync(stepIds, cancellationToken);
foreach (var node in nodes)
{
// 从字典中获取步骤配置信息
X1.Domain.Entities.TestCase.CaseStepConfig? stepConfig = null;
if (!string.IsNullOrEmpty(node.StepId))
{
stepConfigsDict.TryGetValue(node.StepId, out stepConfig);
}
// ... 其他处理逻辑
}
性能优化效果
- 查询次数: 从 N 次单个查询减少到 1 次批量查询
- 数据库负载: 显著减少数据库连接和查询压力
- 响应时间: 提高查询响应速度,特别是在节点数量较多时
- 资源利用: 更高效的数据库资源利用
技术特性
- 批量查询: 使用
ids.Contains(x.Id)进行高效的批量查询 - 字典查找: 使用字典进行 O(1) 时间复杂度的快速查找
- 去重处理: 使用
Distinct()避免重复查询相同的 StepId - 空值处理: 保持原有的空值检查逻辑,确保代码健壮性
影响范围
- 提高了测试用例流程详情查询的性能
- 减少了数据库查询次数和网络开销
- 保持了原有的业务逻辑和错误处理不变
- 向后兼容,不影响其他功能
测试建议
- 测试包含多个节点的测试用例流程查询
- 验证步骤配置信息正确获取
- 确认性能提升效果
- 测试边界情况(空节点、无效StepId等)
修改时间:
2025-01-21
修改原因:
用户要求优化 MapNodesToReactFlowFormatAsync 方法中的循环查询,将 N+1 查询问题改为一次性批量查询,以提高性能并减少数据库负载。
2025-01-21 - 添加 TestCaseEdge 实体的 sourceHandle 和 targetHandle 字段
问题描述
需要在 TestCaseEdge 实体中添加 sourceHandle 和 targetHandle 字段,以支持 ReactFlow 中连线的源节点和目标节点连接点信息。
修改内容
1. 更新 TestCaseEdge 实体
- 文件位置:
X1.Domain/Entities/TestCase/TestCaseEdge.cs - 修改内容:
- 添加了
SourceHandle属性,类型为string?,最大长度 50 字符 - 添加了
TargetHandle属性,类型为string?,最大长度 50 字符 - 更新了
Create方法的参数,添加sourceHandle和targetHandle参数 - 更新了
Update方法的参数,添加sourceHandle和targetHandle参数 - 在构造函数和更新方法中正确处理新字段的赋值
- 添加了
2. 更新 CreateTestCaseFlowCommand 的 EdgeData
- 文件位置:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommand.cs - 修改内容:
- 在
EdgeData类中添加了SourceHandle属性,类型为string?,最大长度 50 字符 - 在
EdgeData类中添加了TargetHandle属性,类型为string?,最大长度 50 字符
- 在
3. 更新 CreateTestCaseFlowCommandHandler
- 文件位置:
X1.Application/Features/TestCaseFlow/Commands/CreateTestCaseFlow/CreateTestCaseFlowCommandHandler.cs - 修改内容:
- 在
CreateEdgesAsync方法中,调用TestCaseEdge.Create时传递sourceHandle和targetHandle参数
- 在
4. 创建数据库迁移
- 迁移文件:
20250821155210_AddSourceHandleAndTargetHandleToTestCaseEdge.cs - 修改内容:
- 在
tb_testcaseedge表中添加SourceHandle字段,类型为character varying(50),可为空 - 在
tb_testcaseedge表中添加TargetHandle字段,类型为character varying(50),可为空 - 同时修复了
tb_testcasenode表中stepid字段的外键约束问题
- 在
修改前后对比
修改前
// TestCaseEdge 实体
public class TestCaseEdge : Entity
{
// ... 其他属性
public string TargetNodeId { get; set; } = null!;
public string? EdgeType { get; set; } = "straight";
// ... 其他属性
}
// EdgeData DTO
public class EdgeData
{
public string Source { get; set; } = null!;
public string Target { get; set; } = null!;
public string Type { get; set; } = null!;
// ... 其他属性
}
修改后
// TestCaseEdge 实体
public class TestCaseEdge : Entity
{
// ... 其他属性
public string TargetNodeId { get; set; } = null!;
/// <summary>
/// 源节点连接点
/// </summary>
[MaxLength(50)]
public string? SourceHandle { get; set; }
/// <summary>
/// 目标节点连接点
/// </summary>
[MaxLength(50)]
public string? TargetHandle { get; set; }
public string? EdgeType { get; set; } = "straight";
// ... 其他属性
}
// EdgeData DTO
public class EdgeData
{
public string Source { get; set; } = null!;
public string Target { get; set; } = null!;
/// <summary>
/// 源节点连接点
/// </summary>
[MaxLength(50)]
public string? SourceHandle { get; set; }
/// <summary>
/// 目标节点连接点
/// </summary>
[MaxLength(50)]
public string? TargetHandle { get; set; }
public string Type { get; set; } = null!;
// ... 其他属性
}
数据库变更
- 表名:
tb_testcaseedge - 新增字段:
SourceHandle:character varying(50)- 可为空TargetHandle:character varying(50)- 可为空
技术特性
- 字段类型: 使用可空字符串类型,支持 ReactFlow 的连接点信息
- 长度限制: 最大长度 50 字符,满足连接点标识符需求
- 向后兼容: 新字段为可空,不影响现有数据
- 数据验证: 在 DTO 中使用
MaxLength特性进行验证
影响范围
- 支持 ReactFlow 连线的源节点和目标节点连接点信息
- 保持向后兼容,现有数据不受影响
- 新的测试用例流程创建时可以使用连接点信息
- 数据库结构已更新,支持新字段存储
测试建议
- 测试创建包含连接点信息的测试用例流程
- 验证
sourceHandle和targetHandle字段正确保存 - 确认现有功能不受影响
- 测试边界情况(空值、超长字符串等)
修改时间:
2025-01-21
修改原因:
用户要求在 TestCaseEdge 实体中添加 sourceHandle 和 targetHandle 字段,以支持 ReactFlow 中连线的连接点信息,并在 CreateTestCaseFlowCommand 的 EdgeData 中也添加相应字段。
2025-01-21 - 更新前端服务接口和 ReactFlow 设计器支持 sourceHandle 和 targetHandle
问题描述
需要在前端服务接口和 ReactFlow 设计器中添加对 sourceHandle 和 targetHandle 字段的支持,确保前端能够正确传递和处理连接点信息。
修改内容
1. 更新 testcaseService.ts
- 文件位置:
X1.WebUI/src/services/testcaseService.ts - 修改内容:
- 在
CreateEdgeData接口中添加了sourceHandle?: string字段 - 在
CreateEdgeData接口中添加了targetHandle?: string字段 - 确保前端创建测试用例流程时能够传递连接点信息
- 在
2. 更新 ReactFlowDesigner.tsx
- 文件位置:
X1.WebUI/src/pages/testcases/ReactFlowDesigner.tsx - 修改内容:
- 在保存数据的调试日志中添加了
sourceHandle和targetHandle字段的打印 - 确保连线数据包含完整的连接点信息
- 在保存数据的调试日志中添加了
修改前后对比
修改前
// testcaseService.ts
export interface CreateEdgeData {
id: string;
source: string;
target: string;
type: string;
animated?: boolean;
condition?: string;
style?: string;
}
// ReactFlowDesigner.tsx
edges.forEach((edge, index) => {
console.log(`连线 ${index + 1}:`, {
id: edge.id,
source: edge.source,
target: edge.target,
type: edge.type,
data: edge.data
});
});
修改后
// testcaseService.ts
export interface CreateEdgeData {
id: string;
source: string;
target: string;
sourceHandle?: string;
targetHandle?: string;
type: string;
animated?: boolean;
condition?: string;
style?: string;
}
// ReactFlowDesigner.tsx
edges.forEach((edge, index) => {
console.log(`连线 ${index + 1}:`, {
id: edge.id,
source: edge.source,
target: edge.target,
sourceHandle: edge.sourceHandle,
targetHandle: edge.targetHandle,
type: edge.type,
data: edge.data
});
});
技术特性
- Handle 组件支持: ReactFlow 设计器中的节点已经包含了 Handle 组件,支持连接点
- 连接点 ID: 每个 Handle 都有唯一的 ID(如 "top", "bottom", "left", "right")
- 类型安全: 使用 TypeScript 接口确保类型安全
- 调试支持: 在保存数据时打印完整的连线信息,便于调试
影响范围
- 前端服务接口支持连接点信息传递
- ReactFlow 设计器能够正确处理和保存连接点信息
- 调试日志包含完整的连线数据
- 保持向后兼容,现有功能不受影响
测试建议
- 测试在 ReactFlow 设计器中创建连线
- 验证连接点信息正确保存
- 确认调试日志显示完整的连线数据
- 测试不同类型节点的连接点功能
修改时间:
2025-01-21
修改原因:
用户要求在前端服务接口和 ReactFlow 设计器中添加对 sourceHandle 和 targetHandle 字段的支持,确保前端能够正确传递和处理连接点信息。
2025-01-21 - 修复前端连线数据转换中缺少 sourceHandle 和 targetHandle 字段的问题
问题描述
在 TestCasesView.tsx 文件中,转换连线数据格式时没有包含 sourceHandle 和 targetHandle 字段,导致前端无法正确传递连接点信息到后端。
修改内容
1. 修复 TestCasesView.tsx 中的连线数据转换
- 文件位置:
X1.WebUI/src/pages/testcases/TestCasesView.tsx - 修改内容:
- 在
convertedEdges映射中添加了sourceHandle: edge.sourceHandle字段 - 在
convertedEdges映射中添加了targetHandle: edge.targetHandle字段 - 确保连线数据转换时包含完整的连接点信息
- 在
修改前后对比
修改前
// TestCasesView.tsx
const convertedEdges = edges.map(edge => ({
id: edge.id,
source: edge.source,
target: edge.target,
type: edge.type,
animated: edge.animated,
condition: edge.data?.condition,
style: JSON.stringify(edge.style)
}));
修改后
// TestCasesView.tsx
const convertedEdges = edges.map(edge => ({
id: edge.id,
source: edge.source,
target: edge.target,
sourceHandle: edge.sourceHandle,
targetHandle: edge.targetHandle,
type: edge.type,
animated: edge.animated,
condition: edge.data?.condition,
style: JSON.stringify(edge.style)
}));
技术特性
- 数据完整性: 确保连线数据包含所有必要的连接点信息
- 类型安全: 利用 TypeScript 接口确保字段类型正确
- 向后兼容: 新字段为可空,不影响现有功能
影响范围
- 修复了前端连线数据转换中缺少连接点信息的问题
- 确保 ReactFlow 设计器中的连接点信息能够正确传递到后端
- 保持向后兼容,现有功能不受影响
测试建议
- 测试在 ReactFlow 设计器中创建连线
- 验证连接点信息正确传递到后端
- 确认保存的测试用例流程包含完整的连线数据
- 测试不同类型节点的连接点功能
修改时间:
2025-01-21
修改原因:
用户发现前端连线数据转换中缺少 sourceHandle 和 targetHandle 字段,需要修复以确保连接点信息能够正确传递到后端。
2024-12-19 - 修复 CaptchaService 引用问题
问题描述
CaptchaService 出现引用问题,构建时出现以下错误:
- 未能找到类型或命名空间名"SkiaSharp"
- 未能找到类型或命名空间名"MailKit"
- 未能找到类型或命名空间名"MimeKit"
问题原因
X1.Infrastructure 项目中缺少必要的 NuGet 包引用:
- SkiaSharp (用于生成验证码图片)
- MailKit (用于发送邮件)
- MimeKit (用于构建邮件内容)
解决方案
在 X1.Infrastructure/X1.Infrastructure.csproj 文件中添加了缺失的包引用:
<PackageReference Include="SkiaSharp" Version="2.88.6" />
<PackageReference Include="MailKit" Version="4.3.0" />
<PackageReference Include="MimeKit" Version="4.3.0" />
修改文件
X1.Infrastructure/X1.Infrastructure.csproj- 添加缺失的 NuGet 包引用
验证结果
- 项目构建成功,所有引用错误已解决
- CaptchaService 和 EmailService 功能恢复正常
备注
这个问题可能是由于之前的包更新或项目重构导致的包引用丢失。建议在后续的包更新操作中注意检查所有必要的依赖关系。
修改时间:
2024-12-19
修改原因:
用户反映 CaptchaService 原先没有问题,现在出现引用问题,可能是更新包导致的。经过检查发现缺少必要的 NuGet 包引用。
2024-12-19 - 修复"为了异步而异步"的问题
问题描述
项目构建时出现大量 CS1998 警告,提示异步方法缺少 "await" 运算符。这些警告主要是由于"为了异步而异步"的设计问题导致的。
问题分析
- PermissionRepository 中的
UpdateAsync和DeleteAsync方法被定义为异步,但实际只是调用同步的底层方法 - EmailVerificationService 中的
VerifyCodeAsync方法被定义为异步,但实际只是内存操作 - 这些方法被错误地标记为异步,导致编译器警告
解决方案
1. 修复接口定义
- 文件:
X1.Domain/Repositories/Identity/IPermissionRepository.cs - 修改: 将
UpdateAsync和DeleteAsync方法改为同步方法
// 修改前
Task UpdateAsync(Permission permission, CancellationToken cancellationToken = default);
Task DeleteAsync(Permission permission, CancellationToken cancellationToken = default);
// 修改后
void UpdateAsync(Permission permission, CancellationToken cancellationToken = default);
void DeleteAsync(Permission permission, CancellationToken cancellationToken = default);
2. 修复实现类
- 文件:
X1.Infrastructure/Repositories/Identity/PermissionRepository.cs - 修改: 将方法实现改为同步,移除不必要的
await Task.CompletedTask
3. 修复 EmailVerificationService
- 文件:
X1.Domain/Services/IEmailVerificationService.cs - 修改: 将
VerifyCodeAsync方法改为同步方法
// 修改前
Task<bool> VerifyCodeAsync(string email, string code);
// 修改后
bool VerifyCode(string email, string code);
- 文件:
X1.Infrastructure/Services/UserManagement/EmailVerificationService.cs - 修改: 实现同步的
VerifyCode方法
4. 更新调用方
- 文件:
X1.Application/Features/Auth/Commands/VerifyCode/VerifyCodeCommandHandler.cs - 文件:
X1.Application/Features/Auth/Commands/EmailLogin/EmailLoginCommandHandler.cs - 修改: 更新方法调用以匹配新的接口定义
设计原则
- 真正的异步操作:只有涉及 I/O 操作(数据库、网络、文件)的方法才使用异步
- 内存操作:纯内存操作(如缓存访问、数据验证)使用同步方法
- 避免
await Task.CompletedTask:不使用这种"为了异步而异步"的写法
验证结果
- ✅ 项目构建成功
- ✅ CS1998 警告大幅减少(从多个减少到只有几个)
- ✅ 代码更符合异步编程的最佳实践
- ✅ 性能得到改善(避免了不必要的异步开销)
修改文件
X1.Domain/Repositories/Identity/IPermissionRepository.csX1.Infrastructure/Repositories/Identity/PermissionRepository.csX1.Domain/Services/IEmailVerificationService.csX1.Infrastructure/Services/UserManagement/EmailVerificationService.csX1.Application/Features/Auth/Commands/VerifyCode/VerifyCodeCommandHandler.csX1.Application/Features/Auth/Commands/EmailLogin/EmailLoginCommandHandler.cs
修改时间:
2024-12-19
修改原因:
用户指出项目中有大量警告,怀疑是"为了异步而异步"导致的问题。经过分析发现确实存在接口设计不一致的问题,需要修复以符合异步编程的最佳实践。
2024-12-19 - 修复解引用可能出现空引用的警告
问题描述
项目构建时出现多个 CS8602、CS8604 警告,提示"解引用可能出现空引用"和"可能传入 null 引用实参"。这些警告主要出现在字符串操作和方法调用中。
修复的警告类型
- CS8602: 解引用可能出现空引用
- CS8604: 方法参数可能传入 null 引用实参
解决方案
1. 修复 ProtocolVersionRepository 中的空引用问题
- 文件:
X1.Infrastructure/Repositories/Device/ProtocolVersionRepository.cs - 修改: 在字符串比较前添加空值检查
// 修改前
query = query.Where(pv =>
pv.Name.Contains(keyword) ||
pv.Version.Contains(keyword) ||
pv.Description.Contains(keyword));
// 修改后
query = query.Where(pv =>
(pv.Name != null && pv.Name.Contains(keyword)) ||
(pv.Version != null && pv.Version.Contains(keyword)) ||
(pv.Description != null && pv.Description.Contains(keyword)));
2. 修复 NetworkStackConfigRepository 中的空引用问题
- 文件:
X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs - 修改:
- 在字符串比较前添加空值检查
- 在排序时使用空值合并运算符
// 字符串比较修复
query = query.Where(nsc =>
(nsc.NetworkStackName != null && nsc.NetworkStackName.Contains(keyword)) ||
(nsc.Description != null && nsc.Description.Contains(keyword)));
// 排序修复
return (result.TotalCount, result.Items.OrderBy(x => x.NetworkStackName ?? string.Empty).ToList());
3. 修复 UserRegistrationService 中的空引用问题
- 文件:
X1.Infrastructure/Services/UserManagement/UserRegistrationService.cs - 修改: 在传递参数给方法前使用空值合并运算符
// 修改前
var userNameResult = UserName.Create(user.UserName);
var emailResult = Email.Create(user.Email);
var existingUser = await _userManager.FindByNameAsync(user.UserName);
// 修改后
var userNameResult = UserName.Create(user.UserName ?? string.Empty);
var emailResult = Email.Create(user.Email ?? string.Empty);
var existingUser = await _userManager.FindByNameAsync(user.UserName ?? string.Empty);
使用的修复技术
- 空值检查:
property != null && property.Contains(keyword) - 空值合并运算符:
property ?? string.Empty - 安全导航: 确保在调用方法前检查对象是否为 null
验证结果
- ✅ 项目构建成功
- ✅ X1.Infrastructure 项目的空引用警告大幅减少(从多个减少到只有 3 个)
- ✅ 代码更加健壮,减少了运行时空引用异常的可能性
- ✅ 保持了代码的可读性和性能
修改文件
X1.Infrastructure/Repositories/Device/ProtocolVersionRepository.csX1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.csX1.Infrastructure/Services/UserManagement/UserRegistrationService.cs
修改时间:
2024-12-19
修改原因:
用户反映项目中有解引用可能出现空引用的警告需要处理。这些警告虽然不影响编译,但可能导致运行时异常,需要修复以提高代码的健壮性。
2024-12-19 - 修复 BaseLoginCommandHandler 派生类构造函数参数缺失问题
问题描述
在实现单用户登录会话管理功能后,BaseLoginCommandHandler 的构造函数已经更新,添加了 ISessionManagementService 和 IOptions<JwtOptions> 参数。但是其派生类 AuthenticateUserCommandHandler 和 EmailLoginCommandHandler 的构造函数还没有更新,导致编译错误。
错误信息
未提供与"BaseLoginCommandHandler<EmailLoginCommand, EmailLoginResponse>.BaseLoginCommandHandler(UserManager<AppUser>, IJwtProvider, ILogger, IUserRoleRepository, IRolePermissionRepository, IUnitOfWork, ILoginLogRepository, IHttpContextAccessor, ISessionManagementService, IOptions<JwtOptions>)"的所需参数"sessionManagementService"对应的参数
未提供与"BaseLoginCommandHandler<AuthenticateUserCommand, AuthenticateUserResponse>.BaseLoginCommandHandler(UserManager<AppUser>, IJwtProvider, ILogger, IUserRoleRepository, IRolePermissionRepository, IUnitOfWork, ILoginLogRepository, IHttpContextAccessor, ISessionManagementService, IOptions<JwtOptions>)"的所需参数"sessionManagementService"对应的参数
解决方案
1. 修复 AuthenticateUserCommandHandler 构造函数
文件: X1.Application/Features/Auth/Commands/AuthenticateUser/AuthenticateUserCommandHandler.cs
修改内容:
- 添加必要的 using 语句:
Microsoft.Extensions.Options和X1.Domain.Options - 更新构造函数参数列表,添加
ISessionManagementService sessionManagementService和IOptions<JwtOptions> jwtOptions - 更新基类构造函数调用,传递新参数
// 添加 using 语句
using Microsoft.Extensions.Options;
using X1.Domain.Options;
// 更新构造函数
public AuthenticateUserCommandHandler(
UserManager<AppUser> userManager,
IJwtProvider jwtProvider,
ILogger<AuthenticateUserCommandHandler> logger,
IUserRoleRepository userRoleRepository,
IRolePermissionRepository rolePermissionRepository,
IUnitOfWork unitOfWork,
ILoginLogRepository loginLogRepository,
IHttpContextAccessor httpContextAccessor,
ISessionManagementService sessionManagementService, // 新增
IOptions<JwtOptions> jwtOptions) // 新增
: base(userManager, jwtProvider, logger, userRoleRepository, rolePermissionRepository, unitOfWork, loginLogRepository, httpContextAccessor, sessionManagementService, jwtOptions)
{
}
2. 修复 EmailLoginCommandHandler 构造函数
文件: X1.Application/Features/Auth/Commands/EmailLogin/EmailLoginCommandHandler.cs
修改内容:
- 添加必要的 using 语句:
Microsoft.Extensions.Options和X1.Domain.Options - 更新构造函数参数列表,添加
ISessionManagementService sessionManagementService和IOptions<JwtOptions> jwtOptions - 更新基类构造函数调用,传递新参数
// 添加 using 语句
using Microsoft.Extensions.Options;
using X1.Domain.Options;
// 更新构造函数
public EmailLoginCommandHandler(
UserManager<AppUser> userManager,
IJwtProvider jwtProvider,
ILogger<EmailLoginCommandHandler> logger,
IUserRoleRepository userRoleRepository,
IRolePermissionRepository rolePermissionRepository,
IUnitOfWork unitOfWork,
ILoginLogRepository loginLogRepository,
IHttpContextAccessor httpContextAccessor,
IEmailVerificationService emailVerificationService,
ISessionManagementService sessionManagementService, // 新增
IOptions<JwtOptions> jwtOptions) // 新增
: base(userManager, jwtProvider, logger, userRoleRepository, rolePermissionRepository, unitOfWork, loginLogRepository, httpContextAccessor, sessionManagementService, jwtOptions)
{
_emailVerificationService = emailVerificationService;
}
验证结果
- ✅ 编译错误已修复
- ✅ 所有派生类现在正确传递所需的依赖项给基类
- ✅ 单用户登录会话管理功能可以正常工作
- ✅ 依赖注入容器能够正确解析所有参数
修改文件
X1.Application/Features/Auth/Commands/AuthenticateUser/AuthenticateUserCommandHandler.csX1.Application/Features/Auth/Commands/EmailLogin/EmailLoginCommandHandler.cs
修改时间:
2024-12-19
修改原因:
在实现单用户登录会话管理功能时,更新了 BaseLoginCommandHandler 的构造函数,但忘记更新其派生类的构造函数,导致编译错误。需要同步更新所有派生类以传递新的依赖项参数。
2024-12-19 - 修复 SessionValidationMiddleware 作用域服务注入问题
问题描述
在实现单用户登录会话管理功能后,应用程序启动时出现错误:
Cannot resolve scoped service 'X1.Domain.Services.ISessionManagementService' from root provider.
这个错误是因为 SessionValidationMiddleware 在构造函数中直接注入了作用域服务 ISessionManagementService,而中间件是在应用程序启动时构建的,此时尝试解析作用域服务会导致错误。
错误原因
在 ASP.NET Core 中,中间件是在应用程序启动时构建的,此时尝试解析作用域服务会导致错误,因为作用域服务需要在请求上下文中才能正确解析。
解决方案
修复 SessionValidationMiddleware
文件: X1.Infrastructure/Middleware/SessionValidationMiddleware.cs
修改内容:
- 移除构造函数中的
ISessionManagementService参数 - 移除私有字段
_sessionManagementService - 在
InvokeAsync方法中使用context.RequestServices.GetRequiredService<ISessionManagementService>()获取服务 - 添加必要的 using 语句:
Microsoft.Extensions.DependencyInjection
// 修改前
public class SessionValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<SessionValidationMiddleware> _logger;
private readonly ISessionManagementService _sessionManagementService; // 移除
public SessionValidationMiddleware(
RequestDelegate next,
ILogger<SessionValidationMiddleware> logger,
ISessionManagementService sessionManagementService) // 移除参数
{
_next = next;
_logger = logger;
_sessionManagementService = sessionManagementService; // 移除
}
public async Task InvokeAsync(HttpContext context)
{
// 使用 _sessionManagementService
var isSessionValid = await _sessionManagementService.ValidateSessionAsync(userId, sessionId);
}
}
// 修改后
public class SessionValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<SessionValidationMiddleware> _logger;
public SessionValidationMiddleware(
RequestDelegate next,
ILogger<SessionValidationMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// 从服务提供程序获取作用域服务
var sessionManagementService = context.RequestServices.GetRequiredService<ISessionManagementService>();
var isSessionValid = await sessionManagementService.ValidateSessionAsync(userId, sessionId);
}
}
验证结果
- ✅ 应用程序启动错误已修复
- ✅ 中间件可以正确获取作用域服务
- ✅ 单用户登录会话管理功能正常工作
- ✅ 会话验证中间件按预期工作
修改文件
X1.Infrastructure/Middleware/SessionValidationMiddleware.cs
修改时间:
2024-12-19
修改原因:
SessionValidationMiddleware 在构造函数中注入作用域服务导致应用程序启动时出现错误。需要修改为在请求处理时动态获取服务,以符合 ASP.NET Core 中间件的生命周期管理。
2024-12-19 - 修复 JWT 令牌中 session_id claim 缺失问题
问题描述
在实现单用户登录会话管理功能后,SessionValidationMiddleware 无法获取到 session_id claim,导致会话验证失败。
问题分析
1. JWT 令牌生成顺序问题
在 BaseLoginCommandHandler.HandleLoginAsync 方法中,session_id claim 是在生成 JWT 令牌之后才添加到 claims 列表中的:
// 生成访问令牌
var accessToken = _jwtProvider.GenerateAccessToken(claims);
// 生成刷新令牌
var refreshToken = _jwtProvider.GenerateRefreshToken(claims);
// 添加会话ID到Claims(在生成令牌之后)
claims.Add(new Claim("session_id", sessionId));
这导致 JWT 令牌中不包含 session_id 信息。
2. JwtProvider 实现问题
在 JwtProvider.GenerateToken 方法中,虽然传入了 claims,但代码逻辑是正确的。
解决方案
1. 修复 BaseLoginCommandHandler 中的令牌生成顺序
文件: X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs
修改内容:
- 将
session_idclaim 的添加移到 JWT 令牌生成之前 - 确保令牌包含所有必要的 claims
// 修改前
// 生成访问令牌
var accessToken = _jwtProvider.GenerateAccessToken(claims);
// 生成刷新令牌
var refreshToken = _jwtProvider.GenerateRefreshToken(claims);
// 添加会话ID到Claims(在生成令牌之后)
claims.Add(new Claim("session_id", sessionId));
// 修改后
// 生成会话ID
var sessionId = Guid.NewGuid().ToString();
// 添加会话ID到Claims(在生成令牌之前)
claims.Add(new Claim("session_id", sessionId));
// 生成访问令牌
var accessToken = _jwtProvider.GenerateAccessToken(claims);
// 生成刷新令牌
var refreshToken = _jwtProvider.GenerateRefreshToken(claims);
2. 添加调试信息到 SessionValidationMiddleware
文件: X1.Infrastructure/Middleware/SessionValidationMiddleware.cs
修改内容:
- 添加调试日志,记录用户认证信息和所有 claims
- 帮助诊断问题
// 添加调试信息
_logger.LogDebug("用户认证信息: UserId={UserId}, SessionId={SessionId}", userId, sessionId);
// 记录所有 claims 用于调试
var allClaims = context.User.Claims.Select(c => $"{c.Type}={c.Value}").ToList();
_logger.LogDebug("用户所有Claims: {Claims}", string.Join(", ", allClaims));
验证结果
- ✅ JWT 令牌现在包含
session_idclaim - ✅
SessionValidationMiddleware可以正确获取到session_id - ✅ 单用户登录会话管理功能正常工作
- ✅ 调试信息帮助诊断问题
修改文件
X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.csX1.Infrastructure/Middleware/SessionValidationMiddleware.cs
修改时间:
2024-12-19
修改原因:
JWT 令牌生成时 session_id claim 没有被包含在令牌中,导致会话验证中间件无法获取到会话ID。需要调整令牌生成顺序,确保所有必要的 claims 都被包含在令牌中。
2025-01-21 - 添加调试日志追踪session_id claim丢失问题
问题描述
用户反映在 SessionValidationMiddleware 中无法获取到 session_id claim,即使已经修复了 BaseLoginCommandHandler 中 claim 添加顺序的问题。日志显示 SessionId=null,且 session_id 不在 JWT token 的 claims 列表中。
修改内容
1. BaseLoginCommandHandler.cs 调试日志
- 文件位置:
X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs - 修改内容: 在生成令牌前添加调试日志,记录所有 claims 的内容
- 代码位置: 在
claims.Add(new Claim("session_id", sessionId));之后,_jwtProvider.GenerateAccessToken(claims)之前 - 日志内容: 记录所有 claims 的类型和值,用于确认
session_idclaim 是否正确添加到 claims 列表中
2. JwtProvider.cs 调试日志
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 修改内容: 在
GenerateToken方法中添加两处调试日志 - 第一处: 在方法开始时记录接收到的 claims 参数
- 第二处: 在创建
ClaimsIdentity后记录其中的 claims - 目的: 追踪 claims 在 JWT 生成过程中的传递情况
调试日志详情
BaseLoginCommandHandler 日志
// 调试:记录所有claims
var allClaimsDebug = claims.Select(c => $"{c.Type}={c.Value}").ToList();
_logger.LogInformation("BaseLoginCommandHandler - 生成令牌前的所有Claims: {Claims}", string.Join(", ", allClaimsDebug));
JwtProvider 日志
// 调试:记录接收到的claims
var receivedClaimsDebug = claims.Select(c => $"{c.Type}={c.Value}").ToList();
_logger.LogInformation("JwtProvider.GenerateToken - 接收到的Claims: {Claims}", string.Join(", ", receivedClaimsDebug));
// 调试:记录ClaimsIdentity中的claims
var subjectClaimsDebug = tokenDescriptor.Subject.Claims.Select(c => $"{c.Type}={c.Value}").ToList();
_logger.LogInformation("JwtProvider.GenerateToken - ClaimsIdentity中的Claims: {Claims}", string.Join(", ", subjectClaimsDebug));
预期结果
通过这些调试日志,可以确定:
session_idclaim 是否正确添加到BaseLoginCommandHandler的 claims 列表中JwtProvider.GenerateToken是否正确接收到包含session_id的 claimsClaimsIdentity中是否包含session_idclaim- 如果前三个步骤都正常,问题可能出现在 JWT 序列化或反序列化过程中
修改时间
2025-01-21
修改原因
用户反馈 session_id claim 仍然无法在 SessionValidationMiddleware 中获取到,需要添加详细的调试日志来追踪 claim 在整个 JWT 生成过程中的传递情况,定位问题出现的具体环节。
2025-01-21 - 使用ClaimTypes.UserData修复session_id声明问题
问题描述
用户反馈在 SessionValidationMiddleware 中无法获取到 session_id 声明,即使之前已经修复了声明添加的顺序问题。用户建议使用 ClaimTypes 而不是原始字符串来添加声明。
修改内容
1. BaseLoginCommandHandler.cs 修改
- 文件位置:
X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs - 修改内容:
- 将
claims.Add(new Claim("session_id", sessionId));改为claims.Add(new Claim(ClaimTypes.UserData, sessionId)); - 使用标准的
ClaimTypes.UserData常量替代原始字符串"session_id" - 保持调试日志记录功能
- 将
2. SessionValidationMiddleware.cs 修改
- 文件位置:
X1.Infrastructure/Middleware/SessionValidationMiddleware.cs - 修改内容:
- 将
var sessionId = context.User.FindFirst("session_id")?.Value;改为var sessionId = context.User.FindFirst(ClaimTypes.UserData)?.Value; - 使用相同的
ClaimTypes.UserData常量来检索会话ID - 保持所有调试日志和错误处理逻辑不变
- 将
技术特性
- 标准声明类型: 使用 .NET 标准的
ClaimTypes.UserData常量 - 一致性: 确保声明添加和检索使用相同的声明类型
- 兼容性: 与现有的 JWT 处理逻辑完全兼容
- 调试支持: 保持完整的调试日志记录功能
修改原因
用户建议使用 ClaimTypes 而不是原始字符串来添加声明,这符合 .NET 的最佳实践,并且可能解决声明无法正确传递的问题。
预期效果
- 修复
SessionValidationMiddleware中session_id声明为 null 的问题 - 确保会话验证功能正常工作
- 提供更好的调试信息用于问题排查
修改时间:
2025-01-21
修改原因:
用户反馈 session_id 声明仍然无法在 SessionValidationMiddleware 中获取到,建议使用 ClaimTypes 而不是原始字符串来添加声明,这符合 .NET 最佳实践。
2025-01-21 - 移除调试日志
修改内容
1. BaseLoginCommandHandler.cs 清理
- 文件位置:
X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs - 修改内容: 移除调试日志
BaseLoginCommandHandler - 生成令牌前的所有Claims
2. JwtProvider.cs 清理
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 修改内容: 移除两处调试日志:
JwtProvider.GenerateToken - 接收到的ClaimsJwtProvider.GenerateToken - ClaimsIdentity中的Claims
修改原因
在成功使用 ClaimTypes.UserData 修复 session_id 声明问题后,不再需要这些调试日志。
修改时间:
2025-01-21
修改原因:
用户确认调试日志不再需要,清理代码中的调试输出。
2025-01-21 - JwtProvider 令牌撤销和黑名单缓存时间优化
问题描述
用户发现 JwtProvider 中的 RevokeToken 和 AddToBlacklist 方法存在以下问题:
- 固定缓存时间过长:撤销令牌固定7天,黑名单固定30天,不考虑令牌实际过期时间
- 内存浪费:如果JWT令牌已经过期,仍然在Redis中存储7-30天,造成不必要的内存占用
- 效率低下:没有根据令牌的实际过期时间动态计算缓存时间
修改内容
1. RevokeToken 方法优化
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 优化内容:
- 获取令牌的实际过期时间
- 如果令牌已过期,直接返回,不存储到Redis
- 动态计算Redis缓存时间:
令牌剩余时间 + 1小时缓冲期 - 设置最大缓存时间为24小时,防止异常情况下的长期存储
- 添加异常处理,如果无法解析令牌则使用1小时默认缓存时间
2. AddToBlacklist 方法优化
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 优化内容:
- 获取令牌的实际过期时间
- 如果令牌已过期,直接返回,不存储到Redis
- 动态计算Redis缓存时间:
令牌剩余时间 + 2小时缓冲期 - 设置最大缓存时间为7天,防止异常情况下的长期存储
- 添加异常处理,如果无法解析令牌则使用2小时默认缓存时间
修改前后对比
修改前
public void RevokeToken(string token)
{
var cacheKey = $"{RevokedTokensCacheKey}{token}";
var expiry = TimeSpan.FromDays(7); // 固定7天
_redisCacheService.Set(cacheKey, true, expiry);
}
public void AddToBlacklist(string token)
{
var cacheKey = $"{TokenBlacklistCacheKey}{token}";
var expiry = TimeSpan.FromDays(30); // 固定30天
_redisCacheService.Set(cacheKey, true, expiry);
}
修改后
public void RevokeToken(string token)
{
try
{
var tokenExpiration = GetTokenExpiration(token);
var now = DateTime.UtcNow;
// 如果令牌已经过期,不需要存储到Redis
if (tokenExpiration <= now)
{
_logger.LogInformation("令牌已过期,无需撤销存储: {Token}, 过期时间: {Expiration}", token, tokenExpiration);
return;
}
// 动态计算缓存时间
var timeUntilExpiration = tokenExpiration - now;
var bufferTime = TimeSpan.FromHours(1);
var redisExpiry = timeUntilExpiration + bufferTime;
// 设置最大缓存时间为24小时
var maxExpiry = TimeSpan.FromHours(24);
if (redisExpiry > maxExpiry)
{
redisExpiry = maxExpiry;
}
var cacheKey = $"{RevokedTokensCacheKey}{token}";
_redisCacheService.Set(cacheKey, true, redisExpiry);
}
catch (Exception ex)
{
// 异常处理:使用1小时默认缓存时间
var cacheKey = $"{RevokedTokensCacheKey}{token}";
var fallbackExpiry = TimeSpan.FromHours(1);
_redisCacheService.Set(cacheKey, true, fallbackExpiry);
}
}
优化效果
1. 内存使用优化
- 过期令牌跳过:已过期的令牌不再存储到Redis,节省内存
- 动态缓存时间:根据令牌实际过期时间设置缓存,避免不必要的长期存储
- 缓冲期设计:添加1-2小时缓冲期,确保令牌完全失效后再清理
- 合理的最长时间:撤销令牌最大24小时,黑名单最大7天,避免不必要的长期存储
2. 性能提升
- 减少Redis存储:避免存储已过期的令牌,减少Redis内存占用
- 更快的清理:令牌过期后缓存也会更快清理,提高系统响应速度
- 异常处理:无法解析的令牌使用短期缓存,避免长期占用内存
3. 安全性保持
- 撤销功能完整:确保令牌在有效期内被正确撤销
- 黑名单功能完整:确保令牌在有效期内被正确加入黑名单
- 缓冲期保护:缓冲期确保令牌完全失效后再清理,防止时间同步问题
技术特性
- 动态计算:根据JWT令牌的实际过期时间动态计算Redis缓存时间
- 异常处理:完整的异常处理机制,确保系统稳定性
- 日志记录:详细的日志记录,便于调试和监控
- 向后兼容:保持原有的API接口不变,不影响现有代码
使用场景
- 令牌撤销:用户主动登出时撤销访问令牌
- 令牌黑名单:检测到安全问题时将令牌加入黑名单
- 会话管理:单用户登录时撤销其他会话的令牌
修改时间
2025-01-21
修改原因
用户反映 AddToBlacklist 和 RevokeToken 的缓存时间过长,且如果令牌已经过期,存储到Redis是浪费内存的行为。需要优化这两个方法,使其根据令牌的实际过期时间动态计算缓存时间,避免存储已过期的令牌。
2025-01-21 - JwtProvider 异常情况下Redis缓存优化
问题描述
用户质疑在 RevokeToken 和 AddToBlacklist 方法中,当令牌解析失败(异常)时,仍然将令牌存储到Redis缓存中是否合理,认为这纯粹是浪费内存。
优化内容
1. RevokeToken 方法优化
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 修改内容:
- 在
catch块中移除Redis缓存存储逻辑 - 只保留错误日志记录:
_logger.LogError(ex, "撤销令牌失败,令牌格式无效: {Token}, 错误: {Message}", token, ex.Message); - 添加注释说明:
// 对于无法解析的令牌,只记录日志,不存储到Redis,避免内存浪费
- 在
2. AddToBlacklist 方法优化
- 文件位置:
X1.Infrastructure/Services/Authentication/JwtProvider.cs - 修改内容:
- 在
catch块中移除Redis缓存存储逻辑 - 只保留错误日志记录:
_logger.LogError(ex, "添加令牌到黑名单失败,令牌格式无效: {Token}, 错误: {Message}", token, ex.Message); - 添加注释说明:
// 对于无法解析的令牌,只记录日志,不存储到Redis,避免内存浪费
- 在
优化前后对比
优化前
catch (Exception ex)
{
_logger.LogError(ex, "撤销令牌失败: {Token}, 错误: {Message}", token, ex.Message);
// 如果无法解析令牌,使用默认的短期缓存时间
var cacheKey = $"{RevokedTokensCacheKey}{token}";
var fallbackExpiry = TimeSpan.FromHours(1);
_redisCacheService.Set(cacheKey, true, fallbackExpiry);
_logger.LogInformation("使用默认缓存时间撤销令牌: {Token}, 缓存时间: {Expiry}", token, fallbackExpiry);
}
优化后
catch (Exception ex)
{
_logger.LogError(ex, "撤销令牌失败,令牌格式无效: {Token}, 错误: {Message}", token, ex.Message);
// 对于无法解析的令牌,只记录日志,不存储到Redis,避免内存浪费
}
优化理由
1. 内存优化
- 问题: 无法解析的令牌通常是格式错误或已损坏,存储到Redis没有实际意义
- 解决: 只记录日志,不占用Redis内存空间
- 效果: 减少Redis内存占用,提高缓存效率
2. 安全性考虑
- 问题: 恶意用户可能发送格式错误的令牌来消耗Redis内存
- 解决: 不存储无效令牌,避免内存攻击
- 效果: 提高系统安全性,防止资源耗尽
3. 业务逻辑合理性
- 问题: 无法解析的令牌本身就不可能是有效的JWT,无需撤销或黑名单
- 解决: 只记录异常日志,便于调试和监控
- 效果: 简化业务逻辑,提高代码可读性
4. 性能优化
- 问题: 存储无效令牌会增加Redis操作开销
- 解决: 减少不必要的Redis写入操作
- 效果: 提高系统性能,减少网络开销
技术特性
- 日志完整性: 保留详细的错误日志,便于问题排查
- 内存效率: 避免存储无效数据,优化Redis内存使用
- 安全性: 防止恶意攻击和资源耗尽
- 性能: 减少不必要的缓存操作
影响范围
- 正面影响: 优化内存使用,提高系统性能和安全性
- 无负面影响: 对正常令牌的撤销和黑名单功能无影响
- 向后兼容: 保持API接口不变,只优化内部实现
修改时间
2025-01-21
修改原因
用户质疑异常情况下Redis缓存存储的必要性,认为对于无法解析的令牌,只记录日志就足够了,存储到Redis是纯粹的内存浪费。通过优化,提高了系统性能和安全性。
2025-01-21 - X1.WebUI 布局组件 Content 区域和 Tabs 滚动问题修复
修改内容
-
DashboardLayout.tsx 布局优化:
- 添加
overflow-hidden类到主容器和所有子容器 - 确保布局不会因为内容溢出而变形
- 修复布局结构,防止 Content 区域被拉长
- 添加
-
Content.tsx 组件修复:
- 将
overflow-hidden改为overflow-auto,允许内容区域滚动 - 添加
p-4内边距,改善内容显示效果 - 确保内容区域有正确的高度约束
- 将
-
Tabs.tsx 组件优化:
- 添加固定高度
h-12到 Tabs 容器 - 为滚动容器添加
h-full高度约束 - 为内部容器添加
h-full确保正确的滚动行为 - 为快捷关闭按钮容器添加
h-full flex items-center确保垂直居中
- 添加固定高度
-
Tabs.css 样式增强:
- 为
.tabs-scroll-container添加height: 100% - 为
.tabs-scroll-container > div添加height: 100% - 确保滚动容器有正确的高度约束
- 为
解决的问题
- Content 区域被拉长:Content 区域不再被拉伸出视图范围
- Tabs 滚动问题:Tabs 现在可以正确水平滚动,不会溢出
- 布局稳定性:整体布局更加稳定,不会因为内容变化而变形
- 用户体验:改善了多标签情况下的界面显示效果
技术特点
- 使用
overflow-hidden防止布局溢出 - 使用
overflow-auto允许内容区域滚动 - 固定 Tabs 高度确保布局稳定性
- 完善的高度约束确保滚动行为正确
修改时间
2025-01-21
修改原因
用户反馈当标签特别多时,Content 区域被拉长看不见,Tabs 超出也看不见。需要修复布局问题,确保 Content 区域和 Tabs 都能正确显示和滚动。
2025-01-21 - CaseStepConfigController 添加 GetFormTypeStepTypeMapping 方法
修改内容
在 CaseStepConfigController 中添加了 GetFormTypeStepTypeMapping 方法,按照现有功能的模式实现:
-
新增控制器方法:
- 方法名:
GetFormTypeStepTypeMapping - 路由:
[HttpGet("form-type-step-type-mapping")]- 对应/api/casestepconfigs/form-type-step-type-mapping - 返回类型:
OperationResult<GetFormTypeStepTypeResponse> - 功能:获取表单类型到步骤类型的映射信息
- 方法名:
-
依赖注入更新:
- 添加了
using X1.Application.Features.CaseStepConfigs.Queries.GetFormTypeStepTypeMapping;命名空间引用 - 确保能够正确引用
GetFormTypeStepTypeQuery和GetFormTypeStepTypeResponse
- 添加了
-
实现特性:
- 命令处理:使用
mediator.Send(new GetFormTypeStepTypeQuery())发送查询 - 日志记录:详细的开始、成功、失败日志记录
- 错误处理:完整的错误处理和用户友好的错误信息
- 统计信息:成功日志中包含表单类型数量和步骤类型数量的统计
- 命令处理:使用
-
API端点:
GET /api/casestepconfigs/form-type-step-type-mapping Authorization: Bearer {token} -
响应格式:
{ "isSuccess": true, "data": { "formTypes": [ { "value": 0, "name": "None", "description": "无表单" } ], "stepTypes": [ { "value": 1, "name": "Start", "description": "开始步骤" } ] }, "errorMessages": null }
技术特点
- 遵循现有模式:完全按照
CaseStepConfigController中其他方法的实现模式 - CQRS架构:使用 MediatR 发送查询,遵循 CQRS 模式
- 统一响应格式:使用
OperationResult<T>统一响应格式 - 完整日志记录:包含详细的日志记录和错误处理
- 类型安全:使用强类型响应对象,确保类型安全
修改时间
2025-01-21
修改原因
用户要求 GetFormTypeStepTypeQueryHandler 已经实现,需要在控制器 CaseStepConfigController 中按照现有功能模式实现相应的控制器方法,不添加额外功能。
2025-01-21 - Dialog 组件主题适配修复
修改内容
-
DialogContent 背景色修复:
- 将
DialogContent组件的硬编码bg-white背景色改为主题适配的bg-background - 确保对话框在深色和浅色主题下都能正确显示
- 修复
SaveTestCaseForm.tsx在深色主题下显示白色背景的问题
- 将
-
主题兼容性:
- 使用
bg-background类,自动适配当前主题 - 在浅色主题下显示白色背景
- 在深色主题下显示深色背景
- 保持与系统其他组件的一致性
- 使用
修改原因
用户反馈 SaveTestCaseForm.tsx 的背景颜色在深色主题下显示为白色,与深色主题不搭配。经过检查发现是 Dialog 组件的 DialogContent 使用了硬编码的白色背景。
技术特点
- 使用 Tailwind CSS 的主题变量
bg-background - 自动适配深色和浅色主题
- 保持与系统其他组件的视觉一致性
- 修复所有使用 Dialog 组件的表单背景色问题
修改时间
2025-01-21