Commit bb87363d6be8f60b1e23d23a7ee92683cd8de315

Authored by “wangming”
1 parent 040963e1

添加设备查看会话记录功能,包括开始和结束记录的接口实现,更新相关实体和文档,确保接口测试流程的规范化。

.cursor/agents/backend-developer.md
@@ -58,7 +58,7 @@ model: fast @@ -58,7 +58,7 @@ model: fast
58 - 分页参数与逻辑在统计与列表间保持一致 58 - 分页参数与逻辑在统计与列表间保持一致
59 59
60 ### 数据库与文档 60 ### 数据库与文档
61 -- **表命名**:业务前缀 + 功能名(如 `lq_`);字段驼峰;时间用 DateTime 61 +- **表命名**:业务前缀 + 功能名;字段驼峰;时间用 DateTime
62 - **统计类 SQL**:提交前用 MCP MySQL 工具执行验证,确认语法、字段、JOIN、统计逻辑正确后再写入代码 62 - **统计类 SQL**:提交前用 MCP MySQL 工具执行验证,确认语法、字段、JOIN、统计逻辑正确后再写入代码
63 63
64 ### API 与接口 64 ### API 与接口
.cursor/rules/project_rules.mdc
@@ -87,7 +87,7 @@ Id = Guid.NewGuid().ToString() @@ -87,7 +87,7 @@ Id = Guid.NewGuid().ToString()
87 ## 🗄️ 数据库规范 87 ## 🗄️ 数据库规范
88 88
89 ### 命名规范 89 ### 命名规范
90 -- **表命名**: 业务前缀 + 功能名称 (如: lq_) 90 +- **表命名**: 业务前缀 + 功能名称
91 - **字段命名**: 驼峰化 91 - **字段命名**: 驼峰化
92 - **时间字段**: 统一使用 DateTime 类型 92 - **时间字段**: 统一使用 DateTime 类型
93 93
.cursor/skills/api-interface-testing/SKILL.md
@@ -11,11 +11,21 @@ description: 按项目规范执行接口测试,包含获取 Token、使用 cur @@ -11,11 +11,21 @@ description: 按项目规范执行接口测试,包含获取 Token、使用 cur
11 - 用户明确要求进行接口测试或提供测试示例 11 - 用户明确要求进行接口测试或提供测试示例
12 - 提交代码前确认接口符合「必须测试」规范 12 - 提交代码前确认接口符合「必须测试」规范
13 13
  14 +## 执行方式(必须按此执行)
  15 +
  16 +当用户本地 API 已启动(如 `http://localhost:2011`)时,**必须直接使用 Shell 执行 curl 命令**进行接口测试,而不是只给出 curl 示例让用户自己执行。
  17 +
  18 +1. **登录获取 Token**:用 Shell 执行登录 curl,从返回 JSON 中解析 `data.token`
  19 +2. **调用目标接口**:用 Shell 依次执行 curl,请求头带上 `Authorization: {data.token}`
  20 +3. **数据库验证**:涉及增删改的接口,**必须**用 MCP MySQL 工具执行 SELECT 查库验证(遵循 `mcp-mysql-and-sql-validation` skill)
  21 +4. **输出测试报告**:汇总 HTTP 状态、返回值、数据库校验结果,给出通过/不通过结论
  22 +
14 ## 测试流程 23 ## 测试流程
15 24
16 1. **获取 Token**:先调用登录接口拿到 `data.token` 25 1. **获取 Token**:先调用登录接口拿到 `data.token`
17 2. **调用目标接口**:请求头带上 `Authorization: {data.token}` 26 2. **调用目标接口**:请求头带上 `Authorization: {data.token}`
18 3. **验证结果**:按下方清单检查返回值与行为 27 3. **验证结果**:按下方清单检查返回值与行为
  28 +4. **查库验证**:对新增/修改/删除类接口,用 MCP 执行 SQL 验证数据是否正确落库
19 29
20 ## 获取 Token 30 ## 获取 Token
21 31
@@ -69,3 +79,36 @@ curl -X POST "http://localhost:2011/api/xxx/YourAction" \ @@ -69,3 +79,36 @@ curl -X POST "http://localhost:2011/api/xxx/YourAction" \
69 ## 工具 79 ## 工具
70 80
71 可使用 curl、Postman、Swagger 等;给出示例时优先提供 **curl**,便于在终端直接执行。 81 可使用 curl、Postman、Swagger 等;给出示例时优先提供 **curl**,便于在终端直接执行。
  82 +
  83 +## 完整执行示例(Shell + MCP 查库)
  84 +
  85 +以下为 SbRecords StartViewRecord / EndViewRecord 接口的实测流程,**以后接口测试均按此方式执行**:
  86 +
  87 +1. **Shell 执行登录**,获取 token:
  88 + ```bash
  89 + curl -s -X POST "http://localhost:2011/api/oauth/Login" \
  90 + -H "Content-Type: application/x-www-form-urlencoded" \
  91 + -d "account=admin&password=66762a3ccde2a2cff3060d7a4a0a576b"
  92 + ```
  93 + 从返回 JSON 中提取 `data.token`,后续请求头使用 `Authorization: $TOKEN`
  94 +
  95 +2. **Shell 执行目标接口**(如 StartViewRecord):
  96 + ```bash
  97 + curl -s -X POST "http://localhost:2011/api/Extend/SbRecords/Actions/StartViewRecord" \
  98 + -H "Authorization: $TOKEN" \
  99 + -H "Content-Type: application/x-www-form-urlencoded" \
  100 + -d "reId=xxx&type=查看设备"
  101 + ```
  102 + 记录返回的 `data`(如 recordId)
  103 +
  104 +3. **Shell 执行后续接口**(如 EndViewRecord):
  105 + ```bash
  106 + curl -s -X POST "http://localhost:2011/api/Extend/SbRecords/Actions/EndViewRecord" \
  107 + -H "Authorization: $TOKEN" \
  108 + -H "Content-Type: application/x-www-form-urlencoded" \
  109 + -d "recordId=xxx"
  110 + ```
  111 +
  112 +4. **MCP 查库验证**:调用 `mcp_HongHua-JJ-my-sql-db_query` 执行 SELECT,校验关键字段(如 F_LeaveTime、F_DurationSeconds)是否正确写入
  113 +
  114 +5. **输出测试报告**:表格汇总各用例的预期 vs 实际、数据库校验结果,给出「通过/不通过」结论
机具(服务端)/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/SbRecordsEntity.cs
1 -using NCC.Common.Const; 1 +using NCC.Common.Const;
2 using SqlSugar; 2 using SqlSugar;
3 using System; 3 using System;
4 4
@@ -28,6 +28,18 @@ namespace NCC.Extend.Entitys @@ -28,6 +28,18 @@ namespace NCC.Extend.Entitys
28 /// </summary> 28 /// </summary>
29 [SugarColumn(ColumnName = "F_AddTime")] 29 [SugarColumn(ColumnName = "F_AddTime")]
30 public DateTime? AddTime { get; set; } 30 public DateTime? AddTime { get; set; }
  31 +
  32 + /// <summary>
  33 + /// 离开时间
  34 + /// </summary>
  35 + [SugarColumn(ColumnName = "F_LeaveTime")]
  36 + public DateTime? LeaveTime { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 停留时长(秒)
  40 + /// </summary>
  41 + [SugarColumn(ColumnName = "F_DurationSeconds")]
  42 + public long DurationSeconds { get; set; }
31 43
32 /// <summary> 44 /// <summary>
33 /// 查看用户 45 /// 查看用户
机具(服务端)/netcore/src/Modularity/Extend/NCC.Extend.Entitys/NCC.Extend.Entitys.xml
@@ -8728,6 +8728,16 @@ @@ -8728,6 +8728,16 @@
8728 记录时间 8728 记录时间
8729 </summary> 8729 </summary>
8730 </member> 8730 </member>
  8731 + <member name="P:NCC.Extend.Entitys.SbRecordsEntity.LeaveTime">
  8732 + <summary>
  8733 + 离开时间
  8734 + </summary>
  8735 + </member>
  8736 + <member name="P:NCC.Extend.Entitys.SbRecordsEntity.DurationSeconds">
  8737 + <summary>
  8738 + 停留时长(秒)
  8739 + </summary>
  8740 + </member>
8731 <member name="P:NCC.Extend.Entitys.SbRecordsEntity.AddUser"> 8741 <member name="P:NCC.Extend.Entitys.SbRecordsEntity.AddUser">
8732 <summary> 8742 <summary>
8733 查看用户 8743 查看用户
机具(服务端)/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/ISbRecordsService.cs
1 -namespace NCC.Extend.Interfaces.SbRecords 1 +using System.Threading.Tasks;
  2 +
  3 +namespace NCC.Extend.Interfaces.SbRecords
2 { 4 {
3 public interface ISbRecordsService 5 public interface ISbRecordsService
4 { 6 {
  7 + /// <summary>
  8 + /// 开始一条设备查看会话记录(进入页面时调用)。
  9 + /// </summary>
  10 + /// <param name="reId">关联记录 Id,一般为设备 Id。</param>
  11 + /// <param name="type">记录类型,例如“查看设备”。</param>
  12 + /// <param name="item1">备用字段1,可选。</param>
  13 + /// <param name="item2">备用字段2,可选。</param>
  14 + /// <returns>返回新建记录的 Id(后续结束会话时使用)。</returns>
  15 + Task<string> StartViewRecordAsync(string reId, string type, string item1 = null, string item2 = null);
  16 +
  17 + /// <summary>
  18 + /// 结束一条设备查看会话记录(离开页面时调用),并计算停留时长。
  19 + /// </summary>
  20 + /// <param name="recordId">开始会话时返回的记录 Id。</param>
  21 + /// <returns>返回是否更新成功。</returns>
  22 + Task<bool> EndViewRecordAsync(string recordId);
5 } 23 }
6 } 24 }
7 \ No newline at end of file 25 \ No newline at end of file
机具(服务端)/netcore/src/Modularity/Extend/NCC.Extend/SbRecordsService.cs
@@ -50,6 +50,80 @@ namespace NCC.Extend.SbRecords @@ -50,6 +50,80 @@ namespace NCC.Extend.SbRecords
50 } 50 }
51 51
52 /// <summary> 52 /// <summary>
  53 + /// 开始一条设备查看会话记录(进入页面时调用)。
  54 + /// </summary>
  55 + /// <param name="reId">关联记录 Id,一般为设备 Id。</param>
  56 + /// <param name="type">记录类型,例如“查看设备”。</param>
  57 + /// <param name="item1">备用字段1,可选。</param>
  58 + /// <param name="item2">备用字段2,可选。</param>
  59 + /// <returns>返回新建记录的 Id(后续结束会话时使用)。</returns>
  60 + [HttpPost("Actions/StartViewRecord")]
  61 + public async Task<string> StartViewRecordAsync(string reId, string type, string item1 = null, string item2 = null)
  62 + {
  63 + var now = DateTime.Now;
  64 + var entity = new SbRecordsEntity
  65 + {
  66 + Id = YitIdHelper.NextId().ToString(),
  67 + ReId = reId,
  68 + AddTime = now,
  69 + LeaveTime = null,
  70 + DurationSeconds = 0,
  71 + AddUser = _userManager.UserId,
  72 + Enable = true,
  73 + Item1 = item1,
  74 + Item2 = item2,
  75 + Type = type
  76 + };
  77 +
  78 + var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
  79 + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
  80 +
  81 + return entity.Id;
  82 + }
  83 +
  84 + /// <summary>
  85 + /// 结束一条设备查看会话记录(离开页面时调用),并计算停留时长。
  86 + /// </summary>
  87 + /// <param name="recordId">开始会话时返回的记录 Id。</param>
  88 + /// <returns>返回是否更新成功。</returns>
  89 + [HttpPost("Actions/EndViewRecord")]
  90 + public async Task<bool> EndViewRecordAsync(string recordId)
  91 + {
  92 + var now = DateTime.Now;
  93 +
  94 + var entity = await _db.Queryable<SbRecordsEntity>().FirstAsync(p => p.Id == recordId);
  95 + if (entity == null)
  96 + {
  97 + return false;
  98 + }
  99 +
  100 + if (entity.AddTime == null)
  101 + {
  102 + entity.AddTime = now;
  103 + }
  104 +
  105 + if (entity.LeaveTime != null)
  106 + {
  107 + return true;
  108 + }
  109 +
  110 + entity.LeaveTime = now;
  111 + var seconds = (now - entity.AddTime.Value).TotalSeconds;
  112 + if (seconds < 0)
  113 + {
  114 + seconds = 0;
  115 + }
  116 +
  117 + entity.DurationSeconds = (long)Math.Round(seconds, MidpointRounding.AwayFromZero);
  118 +
  119 + var result = await _db.Updateable(entity)
  120 + .UpdateColumns(x => new { x.LeaveTime, x.DurationSeconds })
  121 + .ExecuteCommandAsync();
  122 +
  123 + return result > 0;
  124 + }
  125 +
  126 + /// <summary>
53 /// 获取查看历史记录 127 /// 获取查看历史记录
54 /// </summary> 128 /// </summary>
55 /// <param name="id">参数</param> 129 /// <param name="id">参数</param>