Ver Fonte

feat: 静态代码审计,修复逻辑bug和安全漏洞

BaiLuoYan há 3 semanas atrás
pai
commit
ad2729342c
2 ficheiros alterados com 243 adições e 241 exclusões
  1. 14 14
      test-design.md
  2. 229 227
      test-report.md

+ 14 - 14
test-design.md

@@ -177,9 +177,9 @@ MySQL (InnoDB) + Redis Cache
 | TC-0980 | POST /api/perm/sync | REST 反例:未映射 `se.Code=500` | tx 内非业务 err | 继续走 default 分支原样透传 `SyncPermsError` | 契约/反向 | P1 | 防一刀切把 500 误归 404 |
 | TC-0981 | POST /api/perm/sync | gRPC:同 LockByCodeTx ErrNotFound | 同 TC-0979 输入 | `status.Code() == codes.NotFound`,文案 "产品不存在" | 契约 | P0 | gRPC 对外契约 |
 | TC-0982 | POST /api/perm/sync | gRPC 反例:未映射 code | 同 TC-0980 | `codes.Internal`,不得被误分类为 NotFound | 契约/反向 | P1 | 防 SDK 误触发重试 |
-| TC-1021 | POST /api/perm/sync | `syncPerms404_audit_test.go` 原用例 mock `LockByCodeTx` 返回 `SysProduct{Status: 1}` | 原场景保持不变 | 行为不变 | 适配 | P0 | 所有既有 audit 路径都必须显式带 `Status=1`,否则命中 403 分支 |
-| TC-1022 | POST /api/perm/sync | `syncPermsDedup_audit_test.go` / `syncPermsLogic_mock_test.go` / `syncPermsTxLock_audit_test.go` | 同上 | 行为不变 | 适配 | P0 | 对事务内 Status 复核全覆盖 |
-| TC-1023 | POST /api/perm/sync | `syncPermissions404_audit_test.go` gRPC 入口 | `LockByCodeTx` 必带 `Status=1` | UnmappedCode 仍走 Internal | 适配 | P0 | gRPC 层也落入同一契约 |
+| TC-1021 | POST /api/perm/sync | 既有 404 路径用例的 `LockByCodeTx` mock 显式携带 `Status=1` | 原场景保持不变 | 行为不变 | 适配 | P0 | 所有既有 audit 路径都必须显式带 `Status=1`,否则命中 403 分支 |
+| TC-1022 | POST /api/perm/sync | Dedup / mock / TxLock 三路径下事务内 Status 复核全覆盖 | 同上 | 行为不变 | 适配 | P0 | 对事务内 Status 复核全覆盖 |
+| TC-1023 | POST /api/perm/sync | gRPC `SyncPermissions` 入口同样落入 `Status=1` 契约 | `LockByCodeTx` 必带 `Status=1` | UnmappedCode 仍走 Internal | 适配 | P0 | gRPC 层也落入同一契约 |
 | TC-1063 | POST /api/perm/sync | 纯新增(updated=0, disabled=0)→ **不**触发 CleanByProduct | 预先在 Redis 设置 `productIndexKey` canary;执行 `ExecuteSyncPerms(perms=全新 codes)` | canary 仍在 Redis(未被 CleanByProduct 删除);`added>0` | 契约/性能 | P0 | 主反回归 |
 | TC-1064 | POST /api/perm/sync | 至少一条 update(code 存在但 name/Status/Sort 变更)→ **必须**触发 CleanByProduct | 预置 canary + 一条已有 perm;然后 sync 带同 code 但改名 | canary 被删除(CleanByProduct 触达);`updated>0` | 契约 | P0 | update 路径 |
 | TC-1065 | POST /api/perm/sync | 至少一条 disable(列表里不含的 perm 被置 Disabled)→ **必须**触发 CleanByProduct | 同上但 sync 不传原 code;旧 perm 被禁用 | canary 被删除;`disabled>0` | 契约 | P0 | disable 路径 |
@@ -263,12 +263,12 @@ MySQL (InnoDB) + Redis Cache
 | TC-0977 | POST /api/product/create | Redis 失败 + 补偿成功后以同 Code 重建 | 正常 Redis + 相同 productCode | 第二次创建成功,不被 UNIQUE 约束阻塞 | 正向/幂等 | P0 | 补偿把位点清空,同 Code 不卡住 |
 | TC-0978 | POST /api/product/create | 补偿顺序显式校验(child → parent) | 观察三表最终行数 | `sys_product_member`、`sys_user`、`sys_product` 均为 0 | 契约 | P0 | 删除顺序与外键契约一致 |
 | TC-1029 | POST /api/product/create | `seedAdminDept(t, ctx, svcCtx)` 集中化 | 单次调用插一条启用部门 + `t.Cleanup` | 返回 deptId;测试结束自动清理 | 基础 | — | `internal/logic/product/helper_test.go` |
-| TC-1030 | POST /api/product/create | 所有 `createProductLogic_test.go` 的正向用例 | 入参带 `AdminDeptId=seedAdminDept(...)` | 行为不变 | 适配 | P0 | 契约变更全量回归 |
-| TC-1031 | POST /api/product/create | `fetchInitialCredentialsLogic_audit_test.go` | 同上 | 行为不变 | 适配 | P0 | 票据消费路径不破坏 |
-| TC-1032 | POST /api/product/create | `createProductCompensation_audit_test.go` | 同上,补偿路径 | 行为不变 | 适配 | P0 | Redis 降级后的补偿链保留 |
-| TC-1033 | POST /api/product/create | `createProductConflict_audit_test.go` | mock `SysDeptModel.FindOne` 返 `Status=1` | 行为不变;`AdminDeptId` 透传 | 适配 | P0 | mock 侧补齐 |
-| TC-1034 | POST /api/product/create | `createProductLogic_mock_test.go` | 同上,两处 `CreateProductReq` | 行为不变 | 适配 | P0 | mock 侧补齐 |
-| TC-1035 | POST /api/product/create | `TestFetchInitialCredentials_HappyPath` | 读取一次性票据中的 `AdminPassword` | `len(cred.AdminPassword)==16`(不是旧的 24) | 契约 | P1 | 长度断言回归 |
+| TC-1030 | POST /api/product/create | `CreateProduct` 所有正向用例携带 `AdminDeptId` 后行为不变 | 入参带 `AdminDeptId=seedAdminDept(...)` | 行为不变 | 适配 | P0 | 契约变更全量回归 |
+| TC-1031 | POST /api/product/create | `FetchInitialCredentials` 票据消费路径携带 `AdminDeptId` 后行为不变 | 同上 | 行为不变 | 适配 | P0 | 票据消费路径不破坏 |
+| TC-1032 | POST /api/product/create | Redis 降级补偿链路径携带 `AdminDeptId` 后行为不变 | 同上,补偿路径 | 行为不变 | 适配 | P0 | Redis 降级后的补偿链保留 |
+| TC-1033 | POST /api/product/create | 冲突路径下 `SysDeptModel.FindOne` 返 `Status=1` 且 `AdminDeptId` 透传 | mock `SysDeptModel.FindOne` 返 `Status=1` | 行为不变;`AdminDeptId` 透传 | 适配 | P0 | mock 侧补齐 |
+| TC-1034 | POST /api/product/create | mock 侧两处 `CreateProductReq` 补齐 `AdminDeptId` 字段 | 同上,两处 `CreateProductReq` | 行为不变 | 适配 | P0 | mock 侧补齐 |
+| TC-1035 | POST /api/product/create | 一次性票据 `AdminPassword` 长度=16(不再是旧的 24) | 读取一次性票据中的 `AdminPassword` | `len(cred.AdminPassword)==16`(不是旧的 24) | 契约 | P1 | 长度断言回归 |
 
 ### 2.7 产品更新/列表/详情
 
@@ -521,8 +521,8 @@ MySQL (InnoDB) + Redis Cache
 | TC-0992 | POST /api/user/* | SuperAdmin 看任何人 | caller.IsSuperAdmin | 原样返回 Email/Phone/Remark | 正向 | P0 | 防回归(超管被误脱敏) |
 | TC-1011 | POST /api/user/* | 他人先冻结后本轮解冻 | 先跑 Update → UpdateTime 推进,本轮仍持旧 updateTime 直冲 model | model 层 `ErrUpdateConflict`;Logic happy path 解冻成功且 `updateTime` 推进 | 并发/CAS | P0 | CAS 失败路径 + 正向回归 |
 | TC-1012 | POST /api/user/* | Logic 层错误映射 | model 层强制 `ErrUpdateConflict` | 映射为 `response.ErrConflict(409, "数据已被其他操作修改,请刷新后重试")` | 契约 | P1 | 文案与 code 对齐 |
-| TC-1027 | POST /api/user/* | `TestLogin_NonMemberWithProductCode` | 用户在 `productCode` 下非成员 | `CodeError.Code()==403`;文案 "您不是该产品的有效成员" | 安全/Oracle | P0 | 与"禁用成员"同文案 |
-| TC-1028 | POST /api/user/* | `TestLogin_DisabledMemberRejected` | 用户成员资格 `Status=Disabled` | 同上 | 安全/Oracle | P0 | 两条分支合并成一条路径 |
+| TC-1027 | POST /api/user/* | 登录时用户在 `productCode` 下非成员 | 用户在 `productCode` 下非成员 | `CodeError.Code()==403`;文案 "您不是该产品的有效成员" | 安全/Oracle | P0 | 与"禁用成员"同文案 |
+| TC-1028 | POST /api/user/* | 登录时用户成员资格 `Status=Disabled` | 用户成员资格 `Status=Disabled` | 同上 | 安全/Oracle | P0 | 两条分支合并成一条路径 |
 | TC-1078 | POST /api/user/* | BindRoles 与 DeleteRole 并发 6 轮 | - | 每轮新建 user+member+role,两 goroutine 同起 | 终态二选一:(a) 两端都成功(BindRoles 先 → DeleteRole 级联把 UserRole 一并清掉),(b) DeleteRole 先成功 + BindRoles 400 "已被删除或已禁用的角色ID";**任何一轮都不得出现 "sys_role 已删、sys_user_role 仍有 (userId, roleId)" 的 orphan** | P0 | 事务内 S 锁 vs DeleteRole 末尾的 sys_role[X] 锁之间的锁链;兼测错误码映射 |
 
 ### 2.17 成员管理
@@ -631,9 +631,9 @@ MySQL (InnoDB) + Redis Cache
 | TC-0829 | gRPC PermService | 同 IP 两次 gRPC VerifyToken,quota=1 | peer `10.9.8.7:30001` → `10.9.8.7:30002` | 第 1 次 `Valid=false` + nil err;第 2 次 ResourceExhausted | 安全/限流 | P0 | VerifyToken 作为 token oracle 必须受限流保护 |
 | TC-0830 | gRPC PermService | `extractClientIP` 对 "host:port" 剥离 | `192.168.0.1:54321` | 返回 `192.168.0.1`;无 peer 时 error | 契约 | P0 | 剥端口契约不得回退 |
 | TC-0831 | gRPC PermService | gRPC refresh 成功后重放旧 rt(换端口) | quota 宽松 | 第 2 次 Unauthenticated + "登录状态已失效" | 安全/并发 | P0 | H-1 + M-7 纵深交叉 |
-| TC-1036 | gRPC PermService | `TestGetUserPerms_UserNotFound` | userId 全局不存在 | `status.Code()==NotFound` + "用户不是该产品的有效成员" | 安全/Oracle | P0 | 与"非成员"同响应 |
-| TC-1037 | gRPC PermService | `TestGetUserPerms_NonMember_PermissionDenied` | userId 存在但非成员 | 同上(status 码由 `PermissionDenied` 改为 `NotFound`) | 安全/Oracle | P0 | 与"userId 不存在"同响应 |
-| TC-1038 | gRPC PermService | `TestGetUserPerms_DisabledMemberInDevDept_PermissionDenied` | 成员 `Status=Disabled` | 同上 | 安全/Oracle | P0 | 禁用成员走 `loadMembership` 清空 MemberType → 同路径 |
+| TC-1036 | gRPC PermService | `GetUserPerms` 查询 userId 全局不存在 | userId 全局不存在 | `status.Code()==NotFound` + "用户不是该产品的有效成员" | 安全/Oracle | P0 | 与"非成员"同响应 |
+| TC-1037 | gRPC PermService | `GetUserPerms` 查询 userId 存在但非成员 | userId 存在但非成员 | 同上(status 码由 `PermissionDenied` 改为 `NotFound`) | 安全/Oracle | P0 | 与"userId 不存在"同响应 |
+| TC-1038 | gRPC PermService | `GetUserPerms` 查询成员 `Status=Disabled` | 成员 `Status=Disabled` | 同上 | 安全/Oracle | P0 | 禁用成员走 `loadMembership` 清空 MemberType → 同路径 |
 | TC-1051 | gRPC PermService | `SyncPermissions` 同 appKey 连打 `quota+1` 次触发 `ResourceExhausted` | `GrpcSyncLimiter = NewPeriodLimit(60, 1, rds, uniqPrefix)`;同一 appKey 连续 2 次调用 | 第 1 次走业务层(Unauthenticated,因 appKey 不真实);第 2 次 `codes.ResourceExhausted` | 安全/限流 | P0 | appKey 桶命中上限 |
 | TC-1052 | gRPC PermService | `GetUserPerms` 同 appKey 连打 `quota+1` 次触发 `ResourceExhausted` | 同上 limiter 套给 `GrpcGetUserPermsLimiter`;同一 appKey 连续 2 次 | 第 2 次 `codes.ResourceExhausted` | 安全/限流 | P0 | `GetUserPerms` 双桶中的 appKey 桶 |
 | TC-1053 | gRPC PermService | 空 `AppKey` 不消耗 limiter 配额 | `AppKey=""` 连打 3 次;然后真实 `realKey` 首次请求 | 3 次空串请求都 `codes.Unauthenticated`(走 FindOneByAppKey("") → ErrNotFound),`realKey` 首次仍命中业务层 `codes.Unauthenticated`(**不是** `ResourceExhausted`) | 安全/防污染 | P0 | 空串前置分支缺失会把 limiter 计数器打到上限 |

+ 229 - 227
test-report.md

@@ -177,9 +177,9 @@
 | TC-0980 | REST 反例:未映射 `se.Code=500` | ✅ pass |
 | TC-0981 | gRPC:同 LockByCodeTx ErrNotFound | ✅ pass |
 | TC-0982 | gRPC 反例:未映射 code | ✅ pass |
-| TC-1021 | `syncPerms404_audit_test.go` 原用例 mock `LockByCodeTx` 返回 `SysProduct{Status: 1}` | ✅ pass |
-| TC-1022 | `syncPermsDedup_audit_test.go` / `syncPermsLogic_mock_test.go` / `syncPermsTxLock_audit_test.go` | ✅ pass |
-| TC-1023 | `syncPermissions404_audit_test.go` gRPC 入口 | ✅ pass |
+| TC-1021 | 既有 404 路径用例的 `LockByCodeTx` mock 显式携带 `Status=1` | ✅ pass |
+| TC-1022 | Dedup / mock / TxLock 三路径下事务内 Status 复核全覆盖 | ✅ pass |
+| TC-1023 | gRPC `SyncPermissions` 入口同样落入 `Status=1` 契约 | ✅ pass |
 | TC-1063 | 纯新增(updated=0, disabled=0)→ **不**触发 CleanByProduct | ✅ pass |
 | TC-1064 | 至少一条 update(code 存在但 name/Status/Sort 变更)→ **必须**触发 CleanByProduct | ✅ pass |
 | TC-1065 | 至少一条 disable(列表里不含的 perm 被置 Disabled)→ **必须**触发 CleanByProduct | ✅ pass |
@@ -263,12 +263,12 @@
 | TC-0977 | Redis 失败 + 补偿成功后以同 Code 重建 | ✅ pass |
 | TC-0978 | 补偿顺序显式校验(child → parent) | ✅ pass |
 | TC-1029 | `seedAdminDept(t, ctx, svcCtx)` 集中化 | ✅ pass |
-| TC-1030 | 所有 `createProductLogic_test.go` 的正向用例 | ✅ pass |
-| TC-1031 | `fetchInitialCredentialsLogic_audit_test.go` | ✅ pass |
-| TC-1032 | `createProductCompensation_audit_test.go` | ✅ pass |
-| TC-1033 | `createProductConflict_audit_test.go` | ✅ pass |
-| TC-1034 | `createProductLogic_mock_test.go` | ✅ pass |
-| TC-1035 | `TestFetchInitialCredentials_HappyPath` | ✅ pass |
+| TC-1030 | `CreateProduct` 所有正向用例携带 `AdminDeptId` 后行为不变 | ✅ pass |
+| TC-1031 | `FetchInitialCredentials` 票据消费路径携带 `AdminDeptId` 后行为不变 | ✅ pass |
+| TC-1032 | Redis 降级补偿链路径携带 `AdminDeptId` 后行为不变 | ✅ pass |
+| TC-1033 | 冲突路径下 `SysDeptModel.FindOne` 返 `Status=1` 且 `AdminDeptId` 透传 | ✅ pass |
+| TC-1034 | mock 侧两处 `CreateProductReq` 补齐 `AdminDeptId` 字段 | ✅ pass |
+| TC-1035 | 一次性票据 `AdminPassword` 长度=16(不再是旧的 24) | ✅ pass |
 
 ### 2.7 产品更新/列表/详情
 
@@ -516,8 +516,8 @@
 | TC-0992 | SuperAdmin 看任何人 | ⏭️ skip |
 | TC-1011 | 他人先冻结后本轮解冻 | ✅ pass |
 | TC-1012 | Logic 层错误映射 | ✅ pass |
-| TC-1027 | `TestLogin_NonMemberWithProductCode` | ✅ pass |
-| TC-1028 | `TestLogin_DisabledMemberRejected` | ✅ pass |
+| TC-1027 | 登录时用户在 `productCode` 下非成员 | ✅ pass |
+| TC-1028 | 登录时用户成员资格 `Status=Disabled` | ✅ pass |
 | TC-1078 | BindRoles 与 DeleteRole 并发 6 轮 | ✅ pass |
 
 ### 2.17 成员管理
@@ -626,9 +626,9 @@
 | TC-0829 | 同 IP 两次 gRPC VerifyToken,quota=1 | ✅ pass |
 | TC-0830 | `extractClientIP` 对 "host:port" 剥离 | ✅ pass |
 | TC-0831 | gRPC refresh 成功后重放旧 rt(换端口) | ✅ pass |
-| TC-1036 | `TestGetUserPerms_UserNotFound` | ✅ pass |
-| TC-1037 | `TestGetUserPerms_NonMember_PermissionDenied` | ✅ pass |
-| TC-1038 | `TestGetUserPerms_DisabledMemberInDevDept_PermissionDenied` | ✅ pass |
+| TC-1036 | `GetUserPerms` 查询 userId 全局不存在 | ✅ pass |
+| TC-1037 | `GetUserPerms` 查询 userId 存在但非成员 | ✅ pass |
+| TC-1038 | `GetUserPerms` 查询成员 `Status=Disabled` | ✅ pass |
 | TC-1051 | `SyncPermissions` 同 appKey 连打 `quota+1` 次触发 `ResourceExhausted` | ✅ pass |
 | TC-1052 | `GetUserPerms` 同 appKey 连打 `quota+1` 次触发 `ResourceExhausted` | ✅ pass |
 | TC-1053 | 空 `AppKey` 不消耗 limiter 配额 | ✅ pass |
@@ -767,123 +767,123 @@
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0310 | **Insert** | ✅ pass |
-| TC-0311 | **Insert** | ✅ pass |
-| TC-0312 | **Insert** | ✅ pass |
-| TC-0313 | **Insert** | ✅ pass |
-| TC-0314 | **InsertWithTx** | ✅ pass |
-| TC-0315 | **InsertWithTx** | ✅ pass |
-| TC-0316 | **InsertWithTx** | ✅ pass |
-| TC-0317 | **FindOne** | ✅ pass |
-| TC-0318 | **FindOne** | ✅ pass |
-| TC-0319 | **FindOne** | ✅ pass |
-| TC-0320 | **FindOne** | ✅ pass |
-| TC-0321 | **FindOneWithTx** | ✅ pass |
-| TC-0322 | **FindOneWithTx** | ✅ pass |
-| TC-0323 | **FindOneWithTx** | ✅ pass |
-| TC-0324 | **Update** | ✅ pass |
-| TC-0325 | **Update** | ✅ pass |
-| TC-0326 | **Update** | ✅ pass |
-| TC-0327 | **UpdateWithTx** | ✅ pass |
-| TC-0328 | **Delete** | ✅ pass |
-| TC-0329 | **Delete** | ✅ pass |
-| TC-0330 | **DeleteWithTx** | ✅ pass |
-| TC-0331 | **TransactCtx** | ✅ pass |
-| TC-0332 | **TransactCtx** | ✅ pass |
-| TC-0333 | **TableName** | ✅ pass |
+| TC-0310 | Insert — 正常插入 | ✅ pass |
+| TC-0311 | Insert — 正常插入含TokenVersion | ✅ pass |
+| TC-0312 | Insert — 唯一索引冲突 | ✅ pass |
+| TC-0313 | Insert — 缓存key生成正确 | ✅ pass |
+| TC-0314 | InsertWithTx — 事务内插入 | ✅ pass |
+| TC-0315 | InsertWithTx — 事务内插入含TokenVersion | ✅ pass |
+| TC-0316 | InsertWithTx — 事务回滚后无数据 | ✅ pass |
+| TC-0317 | FindOne — 正常查询(缓存未命中) | ✅ pass |
+| TC-0318 | FindOne — 正常查询(缓存命中) | ✅ pass |
+| TC-0319 | FindOne — 记录不存在 | ✅ pass |
+| TC-0320 | FindOne — DB异常(非ErrNotFound) | ✅ pass |
+| TC-0321 | FindOneWithTx — 事务内正常查询 | ✅ pass |
+| TC-0322 | FindOneWithTx — 事务内记录不存在 | ✅ pass |
+| TC-0323 | FindOneWithTx — 事务内可见性 | ✅ pass |
+| TC-0324 | Update — 正常更新 | ✅ pass |
+| TC-0325 | Update — 正常更新含TokenVersion | ✅ pass |
+| TC-0326 | Update — 记录不存在 | ✅ pass |
+| TC-0327 | UpdateWithTx — 事务内更新 | ✅ pass |
+| TC-0328 | Delete — 正常删除 | ✅ pass |
+| TC-0329 | Delete — 记录不存在 | ✅ pass |
+| TC-0330 | DeleteWithTx — 事务内删除 | ✅ pass |
+| TC-0331 | TransactCtx — 正常事务 | ✅ pass |
+| TC-0332 | TransactCtx — fn返回错误 | ✅ pass |
+| TC-0333 | TableName — 获取表名 | ✅ pass |
 
 ### 7.2 批量插入方法
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0334 | **BatchInsert** | ✅ pass |
-| TC-0335 | **BatchInsert** | ✅ pass |
-| TC-0336 | **BatchInsert** | ✅ pass |
-| TC-0337 | **BatchInsert** | ✅ pass |
-| TC-0338 | **BatchInsert** | ✅ pass |
-| TC-0339 | **BatchInsert** | ✅ pass |
-| TC-0340 | **BatchInsertWithTx** | ✅ pass |
-| TC-0341 | **BatchInsertWithTx** | ✅ pass |
-| TC-0342 | **BatchInsertWithTx** | ✅ pass |
+| TC-0334 | BatchInsert — 空列表 | ✅ pass |
+| TC-0335 | BatchInsert — 单条记录 | ✅ pass |
+| TC-0336 | BatchInsert — 多条记录(3条) | ✅ pass |
+| TC-0337 | BatchInsert — 批量插入含TokenVersion | ✅ pass |
+| TC-0338 | BatchInsert — 唯一索引冲突 | ✅ pass |
+| TC-0339 | BatchInsert — 大批量(1000条) | ✅ pass |
+| TC-0340 | BatchInsertWithTx — 空列表 | ✅ pass |
+| TC-0341 | BatchInsertWithTx — 正常多条 | ✅ pass |
+| TC-0342 | BatchInsertWithTx — 事务回滚 | ✅ pass |
 
 ### 7.3 批量更新方法
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0343 | **BatchUpdate** | ✅ pass |
-| TC-0344 | **BatchUpdate** | ✅ pass |
-| TC-0345 | **BatchUpdate** | ✅ pass |
-| TC-0346 | **BatchUpdate** | ✅ pass |
-| TC-0347 | **BatchUpdate** | ✅ pass |
-| TC-0348 | **BatchUpdateWithTx** | ✅ pass |
-| TC-0349 | **BatchUpdateWithTx** | ✅ pass |
-| TC-0350 | **buildBatchUpdateQuery** | ✅ pass |
-| TC-0351 | **buildBatchUpdateQuery** | ✅ pass |
-| TC-0352 | **buildBatchUpdateQuery** | ✅ pass |
+| TC-0343 | BatchUpdate — 空列表 | ✅ pass |
+| TC-0344 | BatchUpdate — 单条记录 | ✅ pass |
+| TC-0345 | BatchUpdate — 多条记录(3条) | ✅ pass |
+| TC-0346 | BatchUpdate — 批量更新不污染数据 | ✅ pass |
+| TC-0347 | BatchUpdate — 部分id不存在 | ✅ pass |
+| TC-0348 | BatchUpdateWithTx — 空列表 | ✅ pass |
+| TC-0349 | BatchUpdateWithTx — 正常多条 | ✅ pass |
+| TC-0350 | buildBatchUpdateQuery — 单条 | ✅ pass |
+| TC-0351 | buildBatchUpdateQuery — 多条 | ✅ pass |
+| TC-0352 | buildBatchUpdateQuery — vals数量正确 | ✅ pass |
 
 ### 7.4 批量删除方法
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0353 | **BatchDelete** | ✅ pass |
-| TC-0354 | **BatchDelete** | ✅ pass |
-| TC-0355 | **BatchDelete** | ✅ pass |
-| TC-0356 | **BatchDelete** | ✅ pass |
-| TC-0357 | **BatchDeleteWithTx** | ✅ pass |
-| TC-0358 | **BatchDeleteWithTx** | ✅ pass |
+| TC-0353 | BatchDelete — 空ids | ✅ pass |
+| TC-0354 | BatchDelete — 单个id | ✅ pass |
+| TC-0355 | BatchDelete — 多个id(3个) | ✅ pass |
+| TC-0356 | BatchDelete — 包含不存在id | ✅ pass |
+| TC-0357 | BatchDeleteWithTx — 空ids | ✅ pass |
+| TC-0358 | BatchDeleteWithTx — 正常多条 | ✅ pass |
 
 ### 7.5 唯一索引查询方法 (按 Model 差异)
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0359 | SysUser | ✅ pass |
-| TC-0360 | SysUser | ✅ pass |
-| TC-0361 | SysUser | ✅ pass |
-| TC-0362 | SysUser | ✅ pass |
-| TC-0363 | SysProduct | ✅ pass |
-| TC-0364 | SysProduct | ✅ pass |
-| TC-0365 | SysProduct | ✅ pass |
-| TC-0366 | SysProduct | ✅ pass |
-| TC-0367 | SysProduct | ✅ pass |
-| TC-0368 | SysProduct | ✅ pass |
-| TC-0369 | SysProduct | ✅ pass |
-| TC-0370 | SysProduct | ✅ pass |
-| TC-0371 | SysPerm | ✅ pass |
-| TC-0372 | SysPerm | ✅ pass |
-| TC-0373 | SysPerm | ✅ pass |
-| TC-0374 | SysPerm | ✅ pass |
-| TC-0375 | SysRole | ✅ pass |
-| TC-0376 | SysRole | ✅ pass |
-| TC-0377 | SysRole | ✅ pass |
-| TC-0378 | SysRole | ✅ pass |
-| TC-0379 | SysRolePerm | ✅ pass |
-| TC-0380 | SysRolePerm | ✅ pass |
-| TC-0381 | SysRolePerm | ✅ pass |
-| TC-0382 | SysRolePerm | ✅ pass |
-| TC-0383 | SysUserPerm | ✅ pass |
-| TC-0384 | SysUserPerm | ✅ pass |
-| TC-0385 | SysUserPerm | ✅ pass |
-| TC-0386 | SysUserPerm | ✅ pass |
-| TC-0387 | SysUserRole | ✅ pass |
-| TC-0388 | SysUserRole | ✅ pass |
-| TC-0389 | SysUserRole | ✅ pass |
-| TC-0390 | SysUserRole | ✅ pass |
-| TC-0391 | SysProductMember | ✅ pass |
-| TC-0392 | SysProductMember | ✅ pass |
-| TC-0393 | SysProductMember | ✅ pass |
-| TC-0394 | SysProductMember | ✅ pass |
+| TC-0359 | FindOneByUsername — 正常查询 | ✅ pass |
+| TC-0360 | FindOneByUsername — 不存在 | ✅ pass |
+| TC-0361 | FindOneByUsernameWithTx — 事务内正常查询 | ✅ pass |
+| TC-0362 | FindOneByUsernameWithTx — 事务内不存在 | ✅ pass |
+| TC-0363 | FindOneByAppKey — 正常查询 | ✅ pass |
+| TC-0364 | FindOneByAppKey — 不存在 | ✅ pass |
+| TC-0365 | FindOneByAppKeyWithTx — 事务内正常查询 | ✅ pass |
+| TC-0366 | FindOneByAppKeyWithTx — 事务内不存在 | ✅ pass |
+| TC-0367 | FindOneByCode — 正常查询 | ✅ pass |
+| TC-0368 | FindOneByCode — 不存在 | ✅ pass |
+| TC-0369 | FindOneByCodeWithTx — 事务内正常查询 | ✅ pass |
+| TC-0370 | FindOneByCodeWithTx — 事务内不存在 | ✅ pass |
+| TC-0371 | FindOneByProductCodeCode — 正常查询 | ✅ pass |
+| TC-0372 | FindOneByProductCodeCode — 不存在 | ✅ pass |
+| TC-0373 | FindOneByProductCodeCodeWithTx — 事务内正常查询 | ✅ pass |
+| TC-0374 | FindOneByProductCodeCodeWithTx — 事务内不存在 | ✅ pass |
+| TC-0375 | FindOneByProductCodeName — 正常查询 | ✅ pass |
+| TC-0376 | FindOneByProductCodeName — 不存在 | ✅ pass |
+| TC-0377 | FindOneByProductCodeNameWithTx — 事务内正常查询 | ✅ pass |
+| TC-0378 | FindOneByProductCodeNameWithTx — 事务内不存在 | ✅ pass |
+| TC-0379 | FindOneByRoleIdPermId — 正常查询 | ✅ pass |
+| TC-0380 | FindOneByRoleIdPermId — 不存在 | ✅ pass |
+| TC-0381 | FindOneByRoleIdPermIdWithTx — 事务内正常查询 | ✅ pass |
+| TC-0382 | FindOneByRoleIdPermIdWithTx — 事务内不存在 | ✅ pass |
+| TC-0383 | FindOneByUserIdPermId — 正常查询 | ✅ pass |
+| TC-0384 | FindOneByUserIdPermId — 不存在 | ✅ pass |
+| TC-0385 | FindOneByUserIdPermIdWithTx — 事务内正常查询 | ✅ pass |
+| TC-0386 | FindOneByUserIdPermIdWithTx — 事务内不存在 | ✅ pass |
+| TC-0387 | FindOneByUserIdRoleId — 正常查询 | ✅ pass |
+| TC-0388 | FindOneByUserIdRoleId — 不存在 | ✅ pass |
+| TC-0389 | FindOneByUserIdRoleIdWithTx — 事务内正常查询 | ✅ pass |
+| TC-0390 | FindOneByUserIdRoleIdWithTx — 事务内不存在 | ✅ pass |
+| TC-0391 | FindOneByProductCodeUserId — 正常查询 | ✅ pass |
+| TC-0392 | FindOneByProductCodeUserId — 不存在 | ✅ pass |
+| TC-0393 | FindOneByProductCodeUserIdWithTx — 事务内正常查询 | ✅ pass |
+| TC-0394 | FindOneByProductCodeUserIdWithTx — 事务内不存在 | ✅ pass |
 
 ### 7.6 内部辅助方法
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0395 | **findListByPrimaryKeys** | ✅ pass |
-| TC-0396 | **findListByPrimaryKeys** | ✅ pass |
-| TC-0397 | **findListByPrimaryKeys** | ✅ pass |
-| TC-0398 | **findListByPrimaryKeys** | ✅ pass |
-| TC-0399 | **getPrimaryKeyValue** | ✅ pass |
-| TC-0400 | **formatPrimary** | ✅ pass |
-| TC-0401 | **queryPrimary** | ✅ pass |
+| TC-0395 | findListByPrimaryKeys — 空ids | ✅ pass |
+| TC-0396 | findListByPrimaryKeys — 正常ids | ✅ pass |
+| TC-0397 | findListByPrimaryKeys — 部分不存在 | ✅ pass |
+| TC-0398 | findListByPrimaryKeys — DB异常 | ✅ pass |
+| TC-0399 | getPrimaryKeyValue — 正常 | ✅ pass |
+| TC-0400 | formatPrimary — 正常 | ✅ pass |
+| TC-0401 | queryPrimary — 正常 | ✅ pass |
 
 ### 7.7 缓存key与前缀初始化
 
@@ -901,167 +901,167 @@
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0405 | 正常分页 | ✅ pass |
-| TC-0406 | 第二页 | ✅ pass |
-| TC-0407 | 空表 | ✅ pass |
-| TC-0408 | count查询失败 | ✅ pass |
-| TC-0409 | list查询失败 | ✅ pass |
-| TC-0410 | 正常查询 | ✅ pass |
-| TC-0411 | productCode不存在 | ✅ pass |
-| TC-0412 | 正常批量查询 | ✅ pass |
-| TC-0413 | 空ids | ✅ pass |
-| TC-0414 | 部分id不存在 | ✅ pass |
-| TC-0415 | DB异常 | ✅ pass |
-| TC-0416 | 有用户的部门 | ✅ pass |
-| TC-0417 | 无用户部门 | ✅ pass |
-| TC-0418 | 状态未变-不递增tokenVersion | ✅ pass |
-| TC-0419 | 状态变更-tokenVersion+1 | ✅ pass |
-| TC-0420 | 乐观锁冲突 | ✅ pass |
-| TC-0421 | 并发场景 | ✅ pass |
-| TC-0422 | userId不存在 | ✅ pass |
-| TC-0736 | 单次 Increment 返回值 == DB 持久值 | ✅ pass |
-| TC-0737 | Increment 后缓存必须被主动清理 | ✅ pass |
-| TC-0738 | 10 goroutine 并发自增同一用户 | ✅ pass |
-| TC-0784 | MySQL 1062 错误正确识别 | ✅ pass |
-| TC-0785 | 非 1062 错误不误判 | ✅ pass |
-| TC-0802 | `expected == DB.tokenVersion` | ✅ pass |
-| TC-0803 | `expected != DB.tokenVersion` | ✅ pass |
-| TC-0805 | 8 goroutine 同时 CAS 同 expected | ✅ pass |
-| TC-0806 | 成功后 id-key / username-key 缓存一致性 | ✅ pass |
-| TC-0812 | logic 层 6 goroutine 并发 RefreshToken 同一旧 rt | ✅ pass |
-| TC-0867 | 透传 username 与 DB 一致 | ✅ pass |
-| TC-0924 | UpdatePassword:FindOne 填缓存后行被并发删除 | ✅ pass |
-| TC-0925 | UpdatePassword:正常写入 | ✅ pass |
-| TC-0926 | UpdatePassword:user 不存在 | ✅ pass |
-| TC-0927 | UpdateStatus:行被并发删除 | ✅ pass |
-| TC-0928 | UpdateStatus:正常禁用 | ✅ pass |
-| TC-0929 | UpdateStatus:user 不存在 | ✅ pass |
-| TC-1044 | `UpdateStatus` 用"错误 username" 调用 → Model 层仍按错误 key 清理 | ✅ pass |
-| TC-1045 | `IncrementTokenVersion` 用"错误 username" 调用 → 同上 | ✅ pass |
-| TC-1046 | `IncrementTokenVersion` 对"已被删除的行" 仍正常走 RowsAffected=0 → `ErrUpdateConflict` 分支 | ✅ pass |
-| TC-1047 | Logic 层 `Logout` 必须把 `ud.Username` 透传到 Model | ✅ pass |
-| TC-1048 | Logic 层 `Logout` 即使 `ud.Username==""` 也必须透传(空串) | ✅ pass |
-| TC-1079 | `UpdateProfileWithTx` 成功后 id/username 两把 sysUser 缓存仍持旧值 | ✅ pass |
-| TC-1080 | `InvalidateProfileCache` 一次性失效 id / username 两把 key | ✅ pass |
-| TC-1081 | 两段式 E2E:UpdateProfileWithTx(不碰缓存) + InvalidateProfileCache(清缓存) | ✅ pass |
-| TC-1082 | UpdateUser tx 分支(改 deptId)post-commit 失效 sysUser 两级缓存 | ✅ pass |
-| TC-1083 | (保留编号) | ✅ pass |
+| TC-0405 | FindListByPage — 正常分页 | ✅ pass |
+| TC-0406 | FindListByPage — 第二页 | ✅ pass |
+| TC-0407 | FindListByPage — 空表 | ✅ pass |
+| TC-0408 | FindListByPage — count查询失败 | ✅ pass |
+| TC-0409 | FindListByPage — list查询失败 | ✅ pass |
+| TC-0410 | FindListByProductMembers — 正常查询 | ✅ pass |
+| TC-0411 | FindListByProductMembers — productCode不存在 | ✅ pass |
+| TC-0412 | FindByIds — 正常批量查询 | ✅ pass |
+| TC-0413 | FindByIds — 空ids | ✅ pass |
+| TC-0414 | FindByIds — 部分id不存在 | ✅ pass |
+| TC-0415 | FindByIds — DB异常 | ✅ pass |
+| TC-0416 | FindIdsByDeptId — 有用户的部门 | ✅ pass |
+| TC-0417 | FindIdsByDeptId — 无用户部门 | ✅ pass |
+| TC-0418 | UpdateProfile — 状态未变-不递增tokenVersion | ✅ pass |
+| TC-0419 | UpdateProfile — 状态变更-tokenVersion+1 | ✅ pass |
+| TC-0420 | UpdateProfile — 乐观锁冲突 | ✅ pass |
+| TC-0421 | UpdateProfile — 并发场景 | ✅ pass |
+| TC-0422 | UpdateProfile — userId不存在 | ✅ pass |
+| TC-0736 | SysUserModel — 单次 Increment 返回值 == DB 持久值 | ✅ pass |
+| TC-0737 | SysUserModel — Increment 后缓存必须被主动清理 | ✅ pass |
+| TC-0738 | SysUserModel — 10 goroutine 并发自增同一用户 | ✅ pass |
+| TC-0784 | SysUserModel — MySQL 1062 错误正确识别 | ✅ pass |
+| TC-0785 | SysUserModel — 非 1062 错误不误判 | ✅ pass |
+| TC-0802 | SysUserModel — `expected == DB.tokenVersion` | ✅ pass |
+| TC-0803 | SysUserModel — `expected != DB.tokenVersion` | ✅ pass |
+| TC-0805 | SysUserModel — 8 goroutine 同时 CAS 同 expected | ✅ pass |
+| TC-0806 | SysUserModel — 成功后 id-key / username-key 缓存一致性 | ✅ pass |
+| TC-0812 | SysUserModel — logic 层 6 goroutine 并发 RefreshToken 同一旧 rt | ✅ pass |
+| TC-0867 | SysUserModel — 透传 username 与 DB 一致 | ✅ pass |
+| TC-0924 | SysUserModel — UpdatePassword:FindOne 填缓存后行被并发删除 | ✅ pass |
+| TC-0925 | SysUserModel — UpdatePassword:正常写入 | ✅ pass |
+| TC-0926 | SysUserModel — UpdatePassword:user 不存在 | ✅ pass |
+| TC-0927 | SysUserModel — UpdateStatus:行被并发删除 | ✅ pass |
+| TC-0928 | SysUserModel — UpdateStatus:正常禁用 | ✅ pass |
+| TC-0929 | SysUserModel — UpdateStatus:user 不存在 | ✅ pass |
+| TC-1044 | SysUserModel — `UpdateStatus` 用"错误 username" 调用 → Model 层仍按错误 key 清理 | ✅ pass |
+| TC-1045 | SysUserModel — `IncrementTokenVersion` 用"错误 username" 调用 → 同上 | ✅ pass |
+| TC-1046 | SysUserModel — `IncrementTokenVersion` 对"已被删除的行" 仍正常走 RowsAffected=0 → `ErrUpdateConflict` 分支 | ✅ pass |
+| TC-1047 | SysUserModel — Logic 层 `Logout` 必须把 `ud.Username` 透传到 Model | ✅ pass |
+| TC-1048 | SysUserModel — Logic 层 `Logout` 即使 `ud.Username==""` 也必须透传(空串) | ✅ pass |
+| TC-1079 | SysUserModel — `UpdateProfileWithTx` 成功后 id/username 两把 sysUser 缓存仍持旧值 | ✅ pass |
+| TC-1080 | SysUserModel — `InvalidateProfileCache` 一次性失效 id / username 两把 key | ✅ pass |
+| TC-1081 | SysUserModel — 两段式 E2E:UpdateProfileWithTx(不碰缓存) + InvalidateProfileCache(清缓存) | ✅ pass |
+| TC-1082 | SysUserModel — UpdateUser tx 分支(改 deptId)post-commit 失效 sysUser 两级缓存 | ✅ pass |
+| TC-1083 | SysUserModel — (保留编号) | ✅ pass |
 
 ### 8.2 SysProductModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0423 | 正常分页 | ✅ pass |
-| TC-0424 | 空表 | ✅ pass |
-| TC-0425 | count失败 | ✅ pass |
+| TC-0423 | FindList — 正常分页 | ✅ pass |
+| TC-0424 | FindList — 空表 | ✅ pass |
+| TC-0425 | FindList — count失败 | ✅ pass |
 
 ### 8.3 SysPermModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0426 | 正常分页 | ✅ pass |
-| TC-0427 | 不存在的productCode | ✅ pass |
-| TC-0428 | 正常查询 | ✅ pass |
-| TC-0429 | 空结果 | ✅ pass |
-| TC-0430 | 正常 | ✅ pass |
-| TC-0431 | 空ids | ✅ pass |
-| TC-0432 | 正常查询 | ✅ pass |
-| TC-0433 | 空结果 | ✅ pass |
-| TC-0434 | key唯一性 | ✅ pass |
-| TC-0435 | codes非空-正常 | ✅ pass |
-| TC-0436 | codes为空-全部禁用 | ✅ pass |
-| TC-0437 | 无需禁用 | ✅ pass |
-| TC-0438 | DB异常 | ✅ pass |
-| TC-0439 | 有权限产品 | ✅ pass |
-| TC-0440 | 无权限产品 | ✅ pass |
-| TC-0441 | 全部已禁用 | ✅ pass |
-| TC-0986 | 产品含 status ∈ {1, 2, 99} 三类行 | ✅ pass |
-| TC-0987 | DisableNotInCodesWithTx 只禁用 status=1 且不在白名单的行 | ✅ pass |
+| TC-0426 | FindListByProductCode — 正常分页 | ✅ pass |
+| TC-0427 | FindListByProductCode — 不存在的productCode | ✅ pass |
+| TC-0428 | FindAllCodesByProductCode — 正常查询 | ✅ pass |
+| TC-0429 | FindAllCodesByProductCode — 空结果 | ✅ pass |
+| TC-0430 | FindByIds — 正常 | ✅ pass |
+| TC-0431 | FindByIds — 空ids | ✅ pass |
+| TC-0432 | FindMapByProductCode — 正常查询 | ✅ pass |
+| TC-0433 | FindMapByProductCode — 空结果 | ✅ pass |
+| TC-0434 | FindMapByProductCode — key唯一性 | ✅ pass |
+| TC-0435 | DisableNotInCodesWithTx — codes非空-正常 | ✅ pass |
+| TC-0436 | DisableNotInCodesWithTx — codes为空-全部禁用 | ✅ pass |
+| TC-0437 | DisableNotInCodesWithTx — 无需禁用 | ✅ pass |
+| TC-0438 | DisableNotInCodesWithTx — DB异常 | ✅ pass |
+| TC-0439 | FindAllCodesByProductCode — 有权限产品 | ✅ pass |
+| TC-0440 | FindAllCodesByProductCode — 无权限产品 | ✅ pass |
+| TC-0441 | FindAllCodesByProductCode — 全部已禁用 | ✅ pass |
+| TC-0986 | SysPermModel — 产品含 status ∈ {1, 2, 99} 三类行 | ✅ pass |
+| TC-0987 | SysPermModel — DisableNotInCodesWithTx 只禁用 status=1 且不在白名单的行 | ✅ pass |
 
 ### 8.4 SysDeptModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0442 | 正常查询 | ✅ pass |
-| TC-0443 | 空表 | ✅ pass |
-| TC-0444 | 正常查询 | ✅ pass |
-| TC-0445 | 无子部门 | ✅ pass |
-| TC-0446 | 正常查询 | ✅ pass |
-| TC-0447 | LIKE注入已阻止 | ✅ pass |
-| TC-0448 | 无匹配 | ✅ pass |
+| TC-0442 | FindAll — 正常查询 | ✅ pass |
+| TC-0443 | FindAll — 空表 | ✅ pass |
+| TC-0444 | FindByParentId — 正常查询 | ✅ pass |
+| TC-0445 | FindByParentId — 无子部门 | ✅ pass |
+| TC-0446 | FindByPathPrefix — 正常查询 | ✅ pass |
+| TC-0447 | FindByPathPrefix — LIKE注入已阻止 | ✅ pass |
+| TC-0448 | FindByPathPrefix — 无匹配 | ✅ pass |
 
 ### 8.5 SysRoleModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0449 | 正常分页 | ✅ pass |
-| TC-0450 | 空结果 | ✅ pass |
-| TC-0451 | 正常 | ✅ pass |
-| TC-0452 | 空ids | ✅ pass |
-| TC-0453 | 有角色用户 | ✅ pass |
-| TC-0454 | 无角色用户 | ✅ pass |
-| TC-1072 | Happy path:所有 roleId 存在且 Enabled | ✅ pass |
-| TC-1073 | 存在一个被删除的 roleId | ✅ pass |
-| TC-1074 | 存在一个 Disabled 的 role | ✅ pass |
-| TC-1075 | 空 / nil 切片 | ✅ pass |
-| TC-1076 | 入参含重复 id | ✅ pass |
-| TC-1077 | 不按 productCode 过滤 | ✅ pass |
+| TC-0449 | FindListByProductCode — 正常分页 | ✅ pass |
+| TC-0450 | FindListByProductCode — 空结果 | ✅ pass |
+| TC-0451 | FindByIds — 正常 | ✅ pass |
+| TC-0452 | FindByIds — 空ids | ✅ pass |
+| TC-0453 | FindMinPermsLevelByUserIdAndProductCode — 有角色用户 | ✅ pass |
+| TC-0454 | FindMinPermsLevelByUserIdAndProductCode — 无角色用户 | ✅ pass |
+| TC-1072 | SysRoleModel — Happy path:所有 roleId 存在且 Enabled | ✅ pass |
+| TC-1073 | SysRoleModel — 存在一个被删除的 roleId | ✅ pass |
+| TC-1074 | SysRoleModel — 存在一个 Disabled 的 role | ✅ pass |
+| TC-1075 | SysRoleModel — 空 / nil 切片 | ✅ pass |
+| TC-1076 | SysRoleModel — 入参含重复 id | ✅ pass |
+| TC-1077 | SysRoleModel — 不按 productCode 过滤 | ✅ pass |
 
 ### 8.6 SysRolePermModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0455 | 正常查询 | ✅ pass |
-| TC-0456 | 无绑定 | ✅ pass |
-| TC-0457 | 正常查询 | ✅ pass |
-| TC-0458 | 空roleIds | ✅ pass |
-| TC-0459 | 去重验证 | ✅ pass |
-| TC-0460 | 正常事务内删除 | ✅ pass |
-| TC-0461 | 无绑定 | ✅ pass |
+| TC-0455 | FindPermIdsByRoleId — 正常查询 | ✅ pass |
+| TC-0456 | FindPermIdsByRoleId — 无绑定 | ✅ pass |
+| TC-0457 | FindPermIdsByRoleIds — 正常查询 | ✅ pass |
+| TC-0458 | FindPermIdsByRoleIds — 空roleIds | ✅ pass |
+| TC-0459 | FindPermIdsByRoleIds — 去重验证 | ✅ pass |
+| TC-0460 | DeleteByRoleIdTx — 正常事务内删除 | ✅ pass |
+| TC-0461 | DeleteByRoleIdTx — 无绑定 | ✅ pass |
 
 ### 8.7 SysUserPermModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0462 | ALLOW-指定产品 | ✅ pass |
-| TC-0463 | DENY-指定产品 | ✅ pass |
-| TC-0464 | 无记录/其他产品 | ✅ pass |
-| TC-0465 | 事务内跨产品删除 | ✅ pass |
-| TC-0466 | 跨产品隔离 | ✅ pass |
+| TC-0462 | FindPermIdsByUserIdAndEffectForProduct — ALLOW-指定产品 | ✅ pass |
+| TC-0463 | FindPermIdsByUserIdAndEffectForProduct — DENY-指定产品 | ✅ pass |
+| TC-0464 | FindPermIdsByUserIdAndEffectForProduct — 无记录/其他产品 | ✅ pass |
+| TC-0465 | DeleteByUserIdForProductTx — 事务内跨产品删除 | ✅ pass |
+| TC-0466 | DeleteByUserIdForProductTx — 跨产品隔离 | ✅ pass |
 
 ### 8.8 SysUserRoleModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0467 | 正常查询 | ✅ pass |
-| TC-0468 | 无绑定 | ✅ pass |
-| TC-0469 | 正常删除 | ✅ pass |
-| TC-0470 | 事务内跨产品删除 | ✅ pass |
-| TC-0471 | 跨产品隔离 | ✅ pass |
-| TC-0472 | 有绑定的角色 | ✅ pass |
-| TC-0473 | 无绑定角色 | ✅ pass |
-| TC-0474 | 跨产品过滤 | ✅ pass |
-| TC-0706 | 同一产品下同时存在启用/禁用角色 | ✅ pass |
-| TC-0707 | DeleteByUserIdAndRoleIdsTx 批量删除 | ✅ pass |
-| TC-0708 | 批量删除空列表为 no-op | ✅ pass |
-| TC-0709 | 批量删除仅作用于指定 userId | ✅ pass |
-| TC-1061 | `DeleteByRoleIdTx` 删除多行后,id 级和组合 key 缓存全部失效 | ✅ pass |
-| TC-1062 | `DeleteByUserIdAndRoleIdsTx` 只删指定 (userId, roleIds) 集合后同上 | ✅ pass |
+| TC-0467 | FindRoleIdsByUserId — 正常查询 | ✅ pass |
+| TC-0468 | FindRoleIdsByUserId — 无绑定 | ✅ pass |
+| TC-0469 | DeleteByRoleIdTx — 正常删除 | ✅ pass |
+| TC-0470 | DeleteByUserIdForProductTx — 事务内跨产品删除 | ✅ pass |
+| TC-0471 | DeleteByUserIdForProductTx — 跨产品隔离 | ✅ pass |
+| TC-0472 | FindUserIdsByRoleId — 有绑定的角色 | ✅ pass |
+| TC-0473 | FindUserIdsByRoleId — 无绑定角色 | ✅ pass |
+| TC-0474 | FindRoleIdsByUserIdForProduct — 跨产品过滤 | ✅ pass |
+| TC-0706 | SysUserRoleModel — 同一产品下同时存在启用/禁用角色 | ✅ pass |
+| TC-0707 | SysUserRoleModel — DeleteByUserIdAndRoleIdsTx 批量删除 | ✅ pass |
+| TC-0708 | SysUserRoleModel — 批量删除空列表为 no-op | ✅ pass |
+| TC-0709 | SysUserRoleModel — 批量删除仅作用于指定 userId | ✅ pass |
+| TC-1061 | SysUserRoleModel — `DeleteByRoleIdTx` 删除多行后,id 级和组合 key 缓存全部失效 | ✅ pass |
+| TC-1062 | SysUserRoleModel — `DeleteByUserIdAndRoleIdsTx` 只删指定 (userId, roleIds) 集合后同上 | ✅ pass |
 
 ### 8.9 SysProductMemberModel
 
 | TC编号 | 测试场景 | 测试结果 |
 | :--- | :--- | :--- |
-| TC-0475 | 正常分页 | ✅ pass |
-| TC-0476 | 空结果 | ✅ pass |
-| TC-0477 | 正常批量 | ✅ pass |
-| TC-0478 | 空userIds | ✅ pass |
-| TC-0479 | 部分不是成员 | ✅ pass |
-| TC-0480 | map key正确 | ✅ pass |
-| TC-0765 | CountActiveAdminsTx 返回正确计数 | ✅ pass |
-| TC-0868 | 产品内 3 个 active admin,排除其中 1 | ✅ pass |
-| TC-0869 | 唯一 active admin,排除他自己 | ✅ pass |
-| TC-0870 | 存在 1 个 active + 1 个 disabled admin | ✅ pass |
+| TC-0475 | FindListByProductCode — 正常分页 | ✅ pass |
+| TC-0476 | FindListByProductCode — 空结果 | ✅ pass |
+| TC-0477 | FindMapByProductCodeUserIds — 正常批量 | ✅ pass |
+| TC-0478 | FindMapByProductCodeUserIds — 空userIds | ✅ pass |
+| TC-0479 | FindMapByProductCodeUserIds — 部分不是成员 | ✅ pass |
+| TC-0480 | FindMapByProductCodeUserIds — map key正确 | ✅ pass |
+| TC-0765 | SysProductMemberModel — CountActiveAdminsTx 返回正确计数 | ✅ pass |
+| TC-0868 | SysProductMemberModel — 产品内 3 个 active admin,排除其中 1 | ✅ pass |
+| TC-0869 | SysProductMemberModel — 唯一 active admin,排除他自己 | ✅ pass |
+| TC-0870 | SysProductMemberModel — 存在 1 个 active + 1 个 disabled admin | ✅ pass |
 
 ## 九、访问控制 (auth/access.go)
 
@@ -1265,6 +1265,8 @@
 
 ---
 
+---
+
 ## 三、测试结论
 
 - **884 个 TC 全部执行**:通过 **986**(含 subtests),跳过 **5**,失败 **0**。