kra-new/docs/code-review-issues.md

262 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 代码审查问题清单internal + pkg
- 审查日期2026-08-27三轮。第三轮2026-08-27核查前两轮修复落地情况 + 以"高内聚低耦合 / 简单实现复杂化 / 过分拆分 / 包归属(可抽 pkg/utils"为重点重审全库
- 审查方式:第一轮 codegraph + 5 路并行深读;第二轮 6 路逐文件深读;第三轮 5 路并行(修复核查 + 重审)。所有死代码结论均经全仓库 Grep 反查调用方验证;第三轮附带 `go build ./...` 编译验证通过
- 第三轮修复核查结论:**S0×17、P0×6、P1-1/2/3/4/5/6/7/10、P4-4、S4 两条全部真实落地**(已从本文档删除,留痕见文末"已修复确认清单"**未修复**S1 死代码大部分、P2/P3/P4 结构问题大部分、1-8/1-8b/1-9/2-10/3-8/3-9
- 当前待办集中在T0 修复回归缺陷 → S1/T3 死代码 → T1 过分拆分合并 → T2 归属移动 → S2 重复消除 → P2/P4 结构
优先级定义P0 = 零风险可直接删P1 = 低风险去重P2 = 中风险删转发层P3 = 涉及 import 路径批量修改的移位P4 = 大动作结构合并/文档修正。
---
## P1 重复实现 / 双份维护(遗留)
| # | 问题 | 位置 | 建议 |
|---|------|------|------|
| 1-8 | DSN 一致性双写:`persistDatabaseConfig`、`refreshDatabaseSource`、`InitializeDatabase` 三处维护"结构化字段→Source"不变式refresh 用"先清空再恢复"绕开 databaseDSN 短路 | internal/data/config_store.go:78-94internal/data/initialization_backend.go:42-59,135-140 | 固化进 databaseDSN 唯一入口 |
| 1-8b | "storage/email 缺省则沿用现值"策略散布 4 处(文件/DB 配置合并规则;原 5 处已收敛为 4 处,但无统一 MergeRuntimeConfig | internal/config/runtime.go:275-286internal/data/config_store.go:173-183internal/data/initialization_backend.go:77-85,171-186 | config 包提供唯一 `MergeRuntimeConfig` |
| 1-9 | config.Store 与 runtimeconfig.Store 各写一套同构的 listener 注册/通知/克隆机制(语义确有差异:文件全量 vs DB 集成窄通道,可辩护为有意分离,需明确决策) | internal/config/runtime.go:29,150-196internal/integration/runtimeconfig/store.go:62-167桥接靠 data 层三处 Replacedata.go:417、config_store.go:258、initialization_backend.go:214 | 抽泛型 notifier或写决策注释固化现状 |
| 1-11 | mq.Registry 接口面积翻倍且恶化legacy APISubscribe/Unsubscribe/Publish内部转译为声明式 Register双面并存且整个 mq 体系业务消费者为零 | pkg/mq/mq.gointernal/integration/mq/emqx.go:539-589 | 见 T3整体裁撤或收缩 |
## P2 转发 shim / 门面层(过度分层)
| # | 问题 | 位置 |
|---|------|------|
| 2-1 | payment 回调 ack 穿透仍四层(第三轮部分收敛:定义已单源化到 paymentkit、biz 用别名零拷贝;但 biz/service/handler 三层转发仍在,且 dto/payment.go:58-62 重复声明同构 3 字段结构体,加字段需同步三处) | pkg/paymentkit/callback.go:11-83 → biz/payment/payment.go:206-215 → service/payment/payment.go:131-148 → handler/payment.go:173-184 |
| 2-2 | integration/payment/result.go 自称 "Compatibility shims"11 个纯转发函数全部仍在normalizePaymentStatus/parseIntegerAmount/jsonObject/nestedString 等) | internal/integration/payment/result.go:68-110 |
| 2-3 | service 根包 25 个 `type X = systemservice.X` 别名门面 + 函数转发(迁移脚手架,注释自辩 "one import instead of four";使 handler 无法感知真实包结构) | internal/service/service.go:18-51 |
| 2-4 | handler/http.go 纯转发别名层3 常量+2 类型+6 函数一对一转发到 pkg/httpx 与 middleware.Claims仅为省一个 import每个 handler 文件仍要同时认识两个包) | internal/server/handler/http.go:14-29 |
| 2-5 | service/task 双面 APIDO 签名版与 DTO 版并存DO 版 Create/Update/Tasks 仅被同文件 DTO 版内部调用handler 只用 `*Request` 版 | internal/service/task/task.go:35-46 |
| 2-6 | TaskUsecase 嵌入透传且加重:仍嵌入 TaskRepo 透传 9 方法给 worker新增 TaskApplicationUsecase 再包一层,其 6 方法纯转发——双 usecase 并存worker 持前者、service 持后者wire_gen.go:115-119 | internal/biz/task/task.go:76-79,159-237 |
| 2-7 | `openWithDriver` 单调用点便捷转发(第三轮部分修复:已拆出 openWithDriverConfigwrapper 仍有 1 个生产调用 + 约 20 处测试调用) | internal/data/database.go:134-136生产调用 :248 |
| 2-8 | Backend 接口三层缝合biz `InitializationRepo` → initialize.Repo内嵌 6 方法 Backend + seed 回调单传入点)→ data.Datainitialize 包只为"转手 6 个同名方法 + 注入 catalog"存在 | internal/initialize/initialize.go:17-46internal/data/initialization_backend.go:17-217cmd/wire.go:45 |
| 2-9 | data-scope 审计回调 `dataScopeAuditEnqueue` 穿透 6 层签名,生产实现唯一 | internal/data/data.go:238,245,392 → runtime_clients.go:168 → data_scope.go:19-136 |
| 2-10 | `*Data` 方法约 10 处模板式 `if d == nil` 防御(构造归 Wire 管理,不可能为 nil`NewIntegrationRuntime` 的 nil→空 Store 回退掩盖错误状态 | internal/data/data.go:41,72,82,88,97,119,126,373-375initialization_backend.go:18 |
## P3 包归属问题pkg 应为可对外复用、无业务语义)
| # | 包 | 问题 | 建议 |
|---|----|------|------|
| 3-1 | pkg/paymentkit | 目录名≠包名(实际 `package paymentutil`17 个支付渠道常量是本项目商户目录(与 biz/integration 中文渠道定义一一对应);回调 ack 硬编码微信/支付宝协议、中文业务文案;调用方 100% 在 internal | 整体并入 internal/integration/payment顺带消灭 17 常量双份导出biz/payment/payment.go:20-43 逐个重命名再导出一遍)、三层金额/回调转发、text/firstAny 双份 |
| 3-2 | pkg/logging | source.go:54-61 硬编码 internal 目录布局效果等同反向依赖zap.go:346-383 中文上报文案、:342 特判遗留 gva 项目文件名AGENTS.md 声称的 `internal/logging/` 目录实际不存在 | 内移为 internal/logging同步修 AGENTS.md |
| 3-3 | pkg/httpx | response.go:17 业务状态码 10001PasswordChangeRequired:60-69 硬编码 `x-token` cookie 契约;调用方全部在 internal/server | 移入 internal/server |
| 3-4 | internal/initialize/configuration.go | `management*` 家族(:221-318手工构造 camelCase JSON属 service 层 DTO 塑形职责;根因是 biz `InitializationRepo``json.RawMessage` 为出入参,表现形状泄漏进 repo 层 | JSON 形状定义移至 internal/service/dtosnakeCase/normalizeDuration 等纯函数与掩码逻辑分文件(当前 464 行混杂三种职责) |
| 3-5 | pkg/database/pagination、gormkit | 调用方 100% 在 internal/data分页纯数学无业务语义轻度 | 可下沉 internal/data低优先级 |
| 3-6 | pkg/mq | rabbitmq.go:127 硬编码项目名前缀 `kra-`subscription.go 的 Contributor 面向本项目 module 概念 | 去项目化(低优先级) |
| 3-7 | pkg/module、pkg/task、pkg/database/migration | 只被本仓库消费但互为依赖构成同层契约组module 依赖 migration/task单独搬会破坏依赖方向一致性 | 维持现状 |
| 3-8 | internal/data/runtime_clients.go | `newReloadableDB` 在通用热切换构造器里调用领域函数 `registerDataScopeCallbacks`,越界 | 该调用移到 data.go 与 replacePrimaryDB 同址 |
| 3-9 | internal/data/data_scope_record.go | 整文件只有一行类型别名 `dataAccessLogPO = datasystem.DataAccessLogPO` | 删除文件并入 data_scope.go |
## P4 结构性设计(双份数据 / 文档漂移,大动作需决策)
| # | 问题 | 位置 | 建议 |
|---|------|------|------|
| 4-1 | router 与 routecatalog 双声明21 个 router 文件纯声明式注册 method+pathroutecatalog 又用一张 map 声明同一批路由的元数据public/audit/group靠契约测试强制对齐——改一条路径要同时改两处 | internal/server/router/*routes.go:22-43 手工 21 连调internal/routecatalog/catalog.go:41- | 表驱动合并为单一声明源method+path+handlerFunc+元数据),可同时消掉对齐测试 |
| 4-2 | 新增一个资源实际要触碰 7 处dto、service/system、handler、router/资源文件、router/routes.go、routecatalog、Set/provider | — | 同 4-1 |
| 4-3 | 同一份集成配置三种形状两条通道:`config.Storage` 强类型 ↔ `sys_integration_configs` JSON 行 ↔ 运行时客户端storage/email 走 config.Storeemail 即时读快照、storage 手动 Replacemq/websocket 走 runtimeconfig 订阅——同一"集成"概念两套配置源两种重载模式 | internal/data/integration_config.go:43-104,157-179internal/data/integration/runtime.go:13-29storage/reloadable.go:18-32email/email.go:25-34 | 评估统一为一条解析-分发通道 |
| 4-5 | swagger 运行时文档:约 160 行 map 手拼 Swagger 2.0 JSON + 正则加工,全局单例仍在 server 根包 | internal/server/swagger.go:21-159 | 可辩护README 有意为之),但按 server/README 自己的规则更宜独立子包 |
| 4-6 | 错误日志热路径做磁盘 IO + go/parser AST 解析(为错误上报附上出错方法源码) | pkg/logging/source.go:71-104pkg/logging/zap.go:373-379 | 展示性需求不该在日志关键路径,缓存或降级 |
| 4-7 | dto 包文件组织混乱dto/system.go 混装 Login/User/ServerInfo 三域dto/settings.go 横跨 Dictionary/SystemParameter/APIToken/SecurityConfig 四域(名不副实) | internal/service/dto/system.go:5-105settings.go:5-172 | 按域拆分重命名 |
| 4-8 | 校验双轨制handler 手工 if 校验与 dto binding 标签并存menu dto 无 binding 标签全靠 handler 手补 | internal/server/handler/user.go:27-64internal/service/dto/settings.go:46-48 | 统一为 binding 标签 |
| 4-9 | 单方法 handler 各占结构体+构造器+Set 字段Session/Navigation 可并入相邻资源 handlerSet 已膨胀到 23 个字段、provider 23 个构造器) | internal/server/handler/session.go:10-12navigation.go:9-11set.go:3-26 | 合并 |
| 4-10 | local 存储两套入口逻辑staticfiles 直读 config 本地盘语义 vs integration/storage 的 Reloadable 体系 | internal/server/staticfiles/staticfiles.go:49-92internal/integration/storage/local.go | 边界收敛(低优先级) |
## 审查后认为合理、不建议改动的部分
- **modules 与 routecatalog 分离**前者是启动期模块装配wire/种子/迁移),后者是请求期热路径策略查询(有 byMethod 桶等性能优化),消费方零重叠,仅存在 modules→routecatalog 的合理单向依赖。
- **Provider 接口缝模式**(子包不反向 import 根 data 避免成环 + 各子包测试假 Data模式本身正当仅 1-1 所述两处重复需合并。
- **config.StoreYAML 文件)与 runtimeconfig.StoreDB 集成表)职责分离**:不重叠,不建议合并包;仅需评估统一 pub-sub 实现1-9
- **utils/routepath 与 routecatalog**:互补非重复(前者剥离配置前缀供落库/Casbin 用,后者纯策略匹配)。
- **TaskScheduler 多锁**:每处均有并发正确性注释论证,是真实并发需求的代价。
- **worker→biz 正向依赖 + biz 经 TaskRuntime/TaskReloader 接口反向倒置**:整个任务链路最规范的一段。
- **三个"registry"实为三种角色**pkg/task=实现、biz/task=接口缝、worker=贡献者):不是重复机制,但命名误导(建议重命名 worker/task_registry.go
- **data 根 11 个文件同包共享 Data 状态与重载锁**:符合 data/README 既定约定,不建议拆包。
---
## 第二轮深审发现S 系列编号)
### S1 死代码补充(第一轮 P0 之外;第三轮核查:约 15 组仍存在,仅 PersistConfig 属误报已更正)
**biz 层死接口方法/死注入面**
- PermissionRepo 3/4 方法死Buttons/SetAuthorityButtons/AuthorityButtonIDs— internal/biz/system/permission.go:6-8
- APITokenRepo.DisableAPIToken — api_token.go:26UserRepo.CreateUser/UpdateUserWithAuthorities — user.go:45,49
- MediaMetadataRepo.FindMediaByHash — media_metadata.go:44MenuRepo.AuthorityMenuIDs — menu.go:61
- SecurityUsecase.ActiveTokenMatches — security.go:243-249EmailUsecase.Alert — email.go:31-48
- PaymentUsecase 注册面全死SetHooks/SetOrderSourceRegistry/SetFulfillmentRegistry/RegisterBusinessModule含两阶段注册+回滚补偿)— biz/payment/payment.go:296-326PayInternal/RefundInternal/AuthorizeRefund 无生产实现service 却作为正式 API 暴露Fulfill
**data 层死实现**与上述接口配套data/system/export.go:430-440、user.go:317-319,474-491、api_token.go:108-114、media.go:67-73、permission.go:113-117、menu.go:283-287
**service 层死方法**MenuService.Listmenu.go:14-20handler 实际用 Tree连带 biz MenuUsecase.List 也死、RecordLogin/RecordDataAccessaudit.go:56-58,91-93后者整链含 biz 接口+data 实现全死、IsTokenDisabledapi_token.go:60-62、security_session.go 14 方法中 8 个死(:17-67、AuditRecorder.CreateErrorRequest+recordedErrorDomainaudit_error.go:17-31
**integration 层零消费者基础设施**
- mq legacy 发布/订阅 API 全链零生产调用Publish/Subscribe/Unsubscribe 及 To 变体、Register/Unregister、订阅簿记/dispatcher/reconcile 约 350 行空转)— integration/mq/emqx.go:358-630连带 cmd/main.go:69 `_ mq.Client` 幻影参数与 integration/provider.go:28,29 两条死 wire 绑定(外加 :31 的 platformws.Hub 死绑定)
- websocketHub 接口零消费者pkg/websocket/melody.go:28-40integration 层 On* 四注册方法零调用server.go:326-373双层 handler 登记机制两层都永远为空server.go:28-31,169-180 vs melody.go:65-118
- s3 多 provider endpoint 五个死分支(唯一调用点只传 "minio")— storage/s3_storage.go:29-41
- namedClient/Client() 整型死代码 — emqx.go:632-650
- 零散wechat_v2.go:584 `stringOr`、paypal.go:651 `normalizePayPalOrderState`、paypal.go:388-391 `paypalOrderTradeNo`、result.go:98,148-155 `nestedString`/`amountFromDecimalField`shim 连调用者都没有、middleware/capture.go:72-74 `isBootstrapPath`
**仅测试调用(生产死代码)**decryptWechatV3wechat_v3.go:600-605、alipaySignContentalipay.go:572-585、validatePaymentConfigdata/payment/payment.go:551-553
**dto 死类型/死字段**LoginLogRequestdto/system.go:68-75、DataAccessRecordRequestdto/audit.go:21-30、GetAuthorityButtonsRequest.Selected、MenuResponse.Authorities 恒 nullbiz 无此字段、DynamicMenuResponse.MenuButtons 恒 nil、SysBaseMenuID 输入被静默丢弃dto/menu.go:21,28、version 导出结构体大量零值噪声字段ID:0/CreatedAt 零时间/authoritys:nullversion.go:23-94
### S2 重复模式补充(大块可消除,估算合计 1000+ 行)
- **data/system List 样板 13 处同构**(构建 db→逐字段翻译 filter→Count→分页→Find→循环 toBiz可抽泛型 helper 消 200+ 行 — user.go:281-315、api.go:119-181、data_access_log.go:32-55、operation_log.go:39-68、error_record.go:83-109 等 13 处
- **payment 渠道适配器家族重复**(合计约 500 行):下单方式归一化骨架 9 份Replacer 归一化行逐字出现 9 次);退款身份校验 4 份同构;状态归一化 switch 10 份(可收敛为 `normalizeState(state, successWords, failedWords)` + 词表);金额拆分守恒 5 份 + biz 层再校验第 6 份SDK 客户端构造 10 份同构firstNonEmpty 三胞胎alipay.go:475/qq.go:146/paypal.go:707mustMarshalAlipayPayload ≡ mustJSON
- **storage provider 家族重复**(约 200 行key/unkey/file 三件套 5 份逐行相同aliyun:35-48/aws:63-76/huawei:31-44/s3:61-74/tencent:45-58DeletePrefix 分页循环 5 份(可提 `deletePrefixViaList`Compose 一行委托 7 份limit 守卫 7 份;构造尾部样板 5 份
- **handler 四段式样板约 70 处**ShouldBindJSON→Fail→service→Write`bindJSON`+`respond` 两个 helper 可消一半
- **biz 接口嵌入透传 12 处 usecase**API/APIRepo/Token/LogViewer/Audit/AuditRecorder/Authority/Dictionary/Export/Media/Parameter/Permission/Position/Version与同包 9 个私有字段风格并存
- **middleware**请求体读取三处access_log.go:56-74 / audit.go:49-67 fallback / handler/media.go:17-21同一请求体三层 MaxBytesReader脱敏/截断三处且分散capture.go:41-68 / audit.go:118-136 / redact 词表在 redact.go 但函数散在 access_log.go双重脱敏双重截断audit.go:47-48,93 对已 mask 已截断的正文再处理一遍)
- mq/websocket 两包各写一套 map 解码 helper 且逐字符相同emqx.go:226-260 vs server.go:189-240TestConfig 探测骨架三处同构emqx.go:92-133/server.go:65-126/connectivity.go
- authority 树构建算法两份biz authority.go:48-78 vs menu.go:80-99CreateAuthority/CopyAuthority 前 8 行校验逐字重复data authority.go:133-151,211-245
- serviceauthorityResponse 与 convertAuthority 逐字重复authority.go:30-36 vs user_conversion.go:8-14"单条 DTO helper + for-append"样板 9 处;"Request 包装 + Filter 包装 + 裸方法"三重入口家族audit/parameter/export/dictionary/mediauser 空对象兜底三连
- data/paymentcallbackFields/first 整函数复制payment.go:507-540 vs result.go:14-56values() 与 testRow() 近重复;渠道"免 notify_url 方法"同义词表在 data 层重抄一份payment.go:390-426与各适配器 createMethod 表双份维护)
- gorm.DeletedAt→*time.Time 转换 4 处逐字重复user.go:99-104、menu.go:17-21,241-245,250-255
- version 阶段→消息映射两份handler version.go:107-121 vs 158-167engine.Routes()→dto 转换两份api.go:161-167 vs public.go:135-139
- defaults 合并逻辑三层三份service/integration:86-98 / biz/integration:215-224 / data/integration/migrations.go:69-75
- media 分片根目录逻辑两份biz media.go:126-132 vs media_upload.go:61-69
- media_upload 三处复制会话校验样板biz media_upload.go:120-129,176-184,244-250顺带吞掉 ErrUploadSessionNotFound 可判定性)
### S3 分层/归属违规补充
- **data 直依赖 integration**data/payment/payment.go:17 import `kra/internal/integration/payment`TestProvider 是 145 行业务编排本地建单、500ms×3 重试轮询、退款闭环)长在 data 层;还自建第二 paymentOrderRepo 实例绕过 wire 单一构造点
- **service 层混入业务/存储细节**system_init.go:13-26 DSN/驱动连接串与回退规则system_info.go:14-41 直连 gopsutil 采集(每请求阻塞 200msexport_excel.go:63-71 SQL 别名/前缀归一api_token.go:28-42 发币编排与到期规则user.go:36-37,85-96 密码策略编排api.go:60-82 Groups 分组推导version.go:184-221 导入导出全编排
- **biz DO 带 json 标签**PaymentResult/PaymentTestResult死标签service 逐字段转 dtoPaymentRequest把指纹编码格式锚死在 DObiz/integration 的 Definition/Field/Option 家族直接充当前端契约dto/integration_config.go:21 内嵌 biz 类型biz 事实上兼任 DTO 提供方)
- **DO 兼过滤器**API.OrderKey/Desc/StrictAll、SystemParameter/ExportTemplate.StartCreatedAt/EndCreatedAt 混入实体LoginLog 已改为独立 Filter
- **middleware 硬编码业务语义**error_audit.go:70-77 靠中文消息黑名单判断是否审计改文案即改审计行为error_audit.go:25,55-67 硬编码业务路径access_log.go:82-203 支付回调专用逻辑内嵌通用中间件rate_limit.go:33-51 限流策略参数内联A8 限流挂在全局链却只匹配两个 public 路由
- **biz 契约泄漏存储/表现原语**QueryExport 返回 `[]map[string]any`LogViewer 的文件读取器细节NextCursor/LimitedByBytesexport DO 字面携带 SQL/Join/Table 片段Export 域整体是查询引擎不是领域逻辑,应下沉 dataUserOptions `{Label,Value}` UI 形状进 bizAuthenticationResult 携带含密码哈希的完整 User
- **data 层纪律**:转换函数命名四种风格违反 new<X>/toBiz 契约FromPO×16/ToPO×4/ToBiz×2/new×2PO 分布无规则models.go 集中 6 个 + 散落 30+models.go:98-100 还混 repo 声明Table("字符串") 绕过已有 PO 7 处user.go:58,70,88,231,259、announcement.go:100、security.go:92saveRelations 回写入参 DO 约 10 处audit.go 名不副实(只有构造器,实现在 5 个文件runtime.go 拼盘settings + tokenIssuer 无关联)
- **编排类文件过重**seedSystem 单函数 126 行 10 类职责seed.go:30-156authority.go 834 行四类职责CRUD/严格权限引擎被 4 个文件 15+ 处借用/DataScope 域解析/用户-角色关联,权限引擎应独立 accessGuardBuildVersionBundle 105 行五职责version.go:76-181migrations.go 两个通信 surface 迁移互为重复子集(:36-130 vs :132-173
- **dto 契约问题**ID 类型三处分叉int/uint/string 混用,迫使 handler 做转换AuthorityResponse.DeletedAt 泄漏且破坏全库 `json:"-"` 约定dto/authority.go:34ErrorRecordMutationRequest 一半指针一半值不自洽DTO 反向依赖 biz 类型dto/integration_config.go:21
- **错误体系**错误定义双体系errors.go 仅 3 个 kratos 类型错误,其余 stdlib errors.New 散落 13+ 文件,无 reason 码);错误包装 Error+Unwrap 与 Error+Is 两机制混用PasswordPolicyError 类型定义在 servicesecurity.go:11-19
- **List 契约三种风格并存**(过滤结构体内含分页 / 位置参数+指针 DO / 裸标量 5-6 参)
- **data 分页三风格**pagination.ApplyRequired/Apply/手写 Limit-Offset手写版 page=0 产生负 offsetposition.go:89、export.go:208-210 无防护media.go:112-115 有)
### S4 简单实现复杂化补充(遗留)
- payment Create 过度防御:先拷 9+1 字段再逐一回比 + Extra 双次 JSON 序列化同一不可变保证指纹层RequestFingerprint + 落库后二次指纹比对)已做两道 — biz/payment/payment.go:385-402,898-907,588-590
- loadUser/loadUsers 双实现(单实体 6 次串行查询 vs 批量实现,可复用)— data/system/user.go:53-97,115-218
- JWT 签名双重检查validateSigningOptions 后 signToken 再查一遍)— data/system/token.go:34-51
- 中间件 nil 防御四处gin.go:28-30 已兜底access.go:33/access_log.go:36/audit.go:27/cors.go:22-25 仍各自检查)
- public.go captchaConfig 恒真分支与无效首调用public.go:29-43
- 媒体上传三重大小防御limitMultipartBody + rejectMediaTooLarge + header.Size 检查)— handler/media.go:38-50,226-240
- emqx.go 恒真 ctx 判断(:561-569,592-600
- PaymentLogger 单实现接口(仅为包装 *slog.Logger— biz/payment/payment_log.go:10-32
- dictionary.go Tree 解析结果被丢弃handler dictionary.go:211-213byType 分支不用 id/parseErr
- DictionaryRepo/MediaRepo 组合式子接口无独立消费方biz dictionary.go:56-59、media.go:19-22
### 第二轮结构性发现(遗留)
- **mq 与 websocket 是"零消费者基础设施"**:大量生命周期/重放/簿记机制空转,要么接入首个真实业务消费者,要么裁掉 legacy 半区保留最小面TestConfig 探测 + Enabled/Path/HandleRequest
- **payment 域 data/integration 边界倒置**paymentRepo 实为"配置读取+适配器编排+ack 组装"的编排层,仅 4 个纯转发方法符合 repo 形态;渠道知识(同义词表)在 data 与 integration 双份维护必然漂移
- **gopay.go/gopay_helpers.go 名实相反**gopay.go 是窄基座4 个函数gopay_helpers.go 是杂物间7 类职责混装,渠道专属谓词/状态机应下沉各渠道文件,公共函数 mergeMap 反而散在 alipay.go
- **vendor.go**vendorSupperPay 是死枚举26,38 定义注册switch 无 case金额拆分 14 键配置 DSL 疑似投机通用性(无内置默认使用);通用渠道定义 18 字段全 Required=true
- **gva/ 目录是遗留参考库**(独立 module 不参与 kra 编译pkg/logging 为其保留文件名特判zap.go:342
---
## 第三轮审查发现T 系列编号2026-08-27
### T0 修复回归缺陷与残留S0 修复核查时发现,优先处理)
| # | 问题 | 位置 |
|---|------|------|
| T0-1 | **vendor 退款静默受理残留(资金安全,高危)**S0-1 修复覆盖了"FAIL 状态字段"场景,但 create/refund 响应 HTTP 200 且 body 非 JSON、或 JSON 无可识别状态字段时仍默认 `created`biz `validatePaymentRefundResult` 接受 created → 真实被渠道拒绝但响应无状态字段的退款会被永久记为已受理。建议refund 端点响应无状态字段时报错或至少 pending | internal/integration/payment/vendor.go:135-163biz/payment/payment.go:1020-1021,837-855 |
| T0-2 | Excel `[]byte` 全转文本副作用MySQL 数值列也会变文本单元格,大面积"数字以文本存储"警告(原值保真达成,展示体验回退);可结合模板列类型区分处理 | internal/service/system/export_excel.go:85-86 |
| T0-3 | ErrAuthoritiesRequired 错误透传缺失biz 返回具体错误后 handler 只回"修改失败",用户看不到"至少一个角色"data 层 setUserAuthorities 还残留一处重复中文防御错误(双轨) | internal/server/handler/user.go:208-212internal/data/system/user.go:563-565 |
| T0-4 | 版本导入非原子残留:落库成功但留痕 CreateVersion 失败时返回错误,用户误判"导入失败"(幂等查重使重试可控) | internal/service/system/version.go:256-270 |
| T0-5 | 任务元数据双源漂移已发生:种子描述("**定时**清理数据库过期日志…")与注册方法描述("清理数据库过期日志…")不一致——两处维护必然继续漂移;建议 worker 注册时复用 catalog TimedTask 元数据 | internal/modules/task/definition.go:14-15 vs internal/worker/task_registry.go:31,45 |
| T0-6 | export SQL/ImportSQL 摆设字段dto 保留但 ValidateExportTemplate 拒绝非空,只能提交空值且响应回显空值 | internal/service/dto/export.go:26-27internal/biz/system/export.go:72-74 |
| T0-7 | payment 回调读体失败分支未入 Gin 错误链S0-12 修复只覆盖 service 调用错误) | internal/server/handler/payment.go:149-153 |
| T0-8 | LoginLogFilter 收敛为仅 Username/Status 两字段:若前端需按 IP/时间筛选登录日志则能力缺失(设计取舍需确认) | internal/biz/system/audit.go:42-45 |
| T0-9 | 任务种子与注册方法的"必经链路"提示PaymentUsecase 注册面SetHooks 等 4 个方法)零调用时,生产装配下支付主链路 Create/Refund/Fulfill 必然在 preparePaymentRequest 报"支付业务订单来源未注册"——模板未完成态,建议 Wire/cmd 层提供默认注册或 fail-fast 提示 | internal/biz/payment/payment.go:296-326,407-410 |
### T1 过分拆分清单(用户重点维度,量化)
**根因模式三条**:①零逻辑 usecase 壳wire 强制每域一个构造器放大);②"每资源 N 文件"机械切分dto+biz+service+handler+router 各一个);③为 import 美观引入的中间缝合包/门面。
**A. biz/system38 文件 3430 行,其中 17 个非测试文件 <60 行(合计约 575 行,保守可归并 8-10 个文件)**
| 文件 | 行数 | 内容 | 合并目标 |
|---|---|---|---|
| errors.go | 14 | 4 个错误变量token.go:35-42 另有 6 个,同类分散) | 并入 user.go 或统一 errors |
| cache.go | 15 | Cache 接口 4 方法 | 并入 security.go主消费者 |
| maintenance.go | 19 | Repo 接口+纯透传壳 | 并入 user.go 或保留worker 消费) |
| actor.go | 19 | ctx 携带 helper | 并入 authority.go唯一消费者 |
| access_control.go | 20 | 透传壳 2 方法 | 并入 authority.go/api.go |
| storage.go | 26 | FileStorage 接口 | 并入 media.go主消费者 |
| data_scope.go | 27 | DataScope 类型+ctx helper | 并入 authority.go |
| upload_session.go | 38 | DO+Repo 13 方法无 usecase | 并入 media_upload.go同域 |
| parameter.go | 33 | DO+Repo+零方法壳 | 壳删后 28 行 |
| settings.go | 43 | 3 设置类型+接口 | 可保留wire 5 处消费) |
| department.go | 46 | DO+Repo+改名壳 | 壳删后 32 行 |
| position.go | 48 | DO+Repo | 与 department.go 合并为 organization.go |
| token.go | 48 | AuthClaims+TokenIssuer | 并入 authentication.go唯一 biz 消费) |
| media_metadata.go | 53 | DO+Repo | 并入 media.gomedia 域 4 文件最典型同域碎片) |
| email.go / api_token.go / version.go | 48/55/57 | — | 保留(有逻辑)或去壳 |
**B. 纯透传壳 usecase 12 个**(整个 struct 无自有逻辑/纯改名转发service 可直依赖 biz repo 接口——repo 接口仍在 biz分层契约不破wire 链物证 wire_gen.go:88-91 permission 全程零逻辑):
ParameterUsecase(parameter.go:29)、PermissionUsecase(permission.go:14)、VersionUsecase(version.go:55)、AnnouncementUsecase(announcement.go:397 方法全一行透传)、MaintenanceUsecase(maintenance.go:11)、AccessControlUsecase(access_control.go:5)、DepartmentUsecase(department.go:342 改名)、AuthorityUsecase(authority.go:42仅 Tree 有逻辑)、TaskUsecase(task.go:76)、MenuUsecase(menu.go:7210 处透传)、UserUsecase(user.go:5913 处透传)、SystemConfigUsecase(system_init.go:507 处透传)。
对照组有真实逻辑应保留Authentication/Security/Media/Email/IntegrationConfig/Payment/TaskApplication。
**C. service/system 碎片**
- SystemConfigService 一型拆四文件system.go(19)+system_config.go(26)+system_init.go(38)+system_info.go(42)=125 行 4 文件
- security_session.go:17-68SecurityService 14 方法全部一行透传(零 DTO 工作)
- audit.go 与 audit_error.go 同属 AuditService/AuditRecorder 可合并audit_log_file.go 是独立 LogViewerService保留
- email.go(16)/permission.go(24)/access_control.go(28仅 5 行逻辑) 近纯透传小文件
**D. router 碎片**22 文件 464 行,平均 21 行/文件(最小 email.go 12 行routes.go:18-43 手工 21 连调——纯注册碎片无内聚(与 4-1 表驱动合并一并解决)。
**E. 单符号包/微文件**
- internal/modules/surface整包只有一个 10 行函数surface.go:10-192 个调用方
- internal/data/provider整包只有一个 7 行 2 方法接口provider.go:5-8——中性缝可辩护建议与 provider.go 别名使用方注释互指
- provider.go+providers.go 双小文件模式 ×4 子包integration 12+5 行、payment 8+5、task 7+5、system 17+29→ 8 文件并 4
- data/system/bootstrap.go 27 行仅 seed.go 使用可并入worker/worker.go 7 行、initialize/provider.go 5 行、pkg/task/provider.go 3 行、biz/payment/provider.go 8 行均为纯 wire-set 微文件
- data_scope_record.go 整文件 1 行别名(同 3-9
**F. 巨微并存两极**
- biz/payment/payment.go 单文件 1150 行usecase+常量再导出+5 validator+指纹工具vs 同域 payment_log.go 32 行/provider.go 8 行微文件
- dtoauthentication.go 8 行/email.go 7 行超小文件 vs settings.go 170 行横跨四域(同 4-7
### T2 包归属移动建议(从 → 到)
| # | 从 | 到 | 动作 | 影响 |
|---|---|---|---|---|
| 1 | pkg/httpx | internal/server/httpx | 整包内移(消费者 100% 在 internal/server 7 文件;中文文案+x-token 是本项目契约logging/source.go:59 已预留该路径 marker | 7 文件 |
| 2 | pkg/logging | internal/logging | 整包内移source.go:47-69 硬编码本仓库 internal 路径zap.go:366-382 中文文案;模块路由硬编码服务日志查看器语义) | 约 5 文件 |
| 3 | pkg/module | internal/modules | 契约内移Menu/API/Surface/TimedTask 是本项目模块系统契约import gin15 消费者全在本仓库) | 15 文件 |
| 4 | pkg/paymentkit 渠道常量provider.go | internal/biz/payment 本体 | 常量归位并删 biz/payment/payment.go:20-43 的 17+2 个别名转发层;目录更名 paymentutil 对齐包名通用工具signing/json/amount/xml可留 pkg 或并入 internal/utils | 约 8 文件 |
| 5 | pkg/mq + pkg/websocket(Hub) | 收缩或合并进 internal/integration/mq | 全链零业务消费者(详见 T3保留 TestConfig 探测+基础驱动,删 Registry/Client()/namedClient/legacy API/Hub/ApplySubscriptions | 约 7 文件 |
| 6 | integration/mq emqx.go:226-260 与 integration/websocket server.go:189-240 的重复 JSON map helper | internal/utils/jsonvalue | 上收去重configText≡text、configBool≡boolValue 逐字符相同) | 2-3 文件 |
| 7 | storageaws-sdk-v2 栈与 minio-go 栈双 S3 实现并存 | 统一 S3 兼容单栈(可选) | qiniu/aliyun/huawei/tencent 原生 SDK 均有 S3 兼容端点,可收敛删 4 实现+s3 死分支 | 5-6 文件 |
| 8 | handler/query.go:13-61Gin 工具、middleware/request.go:67-102纯函数、handler/announcement.go parseTime被 export.go 跨域借用) | pkg 或 internal/utils 候选 | 通用无状态逻辑上收 | 3 文件 |
依赖方向合规确认第三轮验证pkg 无 import internal仅 zap_test.go:131 字符串字面量integration 无 import data/serviceservice→biz→data 无反向。
### T3 死代码回归与新增S1 现状核查)
- **S1 清单约 15 组死方法仍存在**biz 接口+data 实现+service 包装三层残留已更正误报service 层 PersistConfig 实为活代码
- mq 零消费者机制未清理且**规模扩大**emqx.go 约 710 行 + mq.go 106 行 + subscription.go 55 行声明式订阅全套无注册者legacy API 转译层又加了一层1-11 恶化)
- websocket Hub 死绑定、s3 五个不可达 endpoint 分支local.go:39-48 只让 minio 进 newS3Storage、namedClient/Client()、`_ mq.Client` 幻影参数main.go:65、isBootstrapPath 死函数——全部未清理
- 新增死代码wechat_v2.go:47 私有 wechatV2Sign 变为仅测试调用可移测试文件pkg/task RegisterAll 仅测试调用;**NewGinEngine 生产死代码**gin.go:23-25wire 用 NewGinEngineWithRuntimeTaskScheduler.Trigger(task) 可未导出(仅 TriggerID 内部用)
- P1-3 修复残留result.go:58-64 与 payment_helpers.go:5-11 两包仍保留同名本地包装转发 paymentutilmicro-shim 未拆)
### 已修复确认清单(第三轮验证后从本文档删除,留痕)
- P0 全部 6 条protoutil 整包删除WechatV2Sign 死实现删除Data 三死方法删除Bootstrap 别名删除;静态任务注册链删除(重建为活的 TimedTasks 单源链路Schedule(task) 删除
- P1-1/2/4/5/6/7/10provider.Database 中性接口task/security 两处仅剩一行别名种子单源化含防回归测试validateMenuRequestsurface.APIsForPrefixpersistConfig Locked 变体删除NewData/reloadConfig 均用 rollback 收集器websocket snapshotHandlers 泛型收敛
- P1-3paymentkit.Text/FirstText 落地(残留 micro-shim 见 T3
- P4-4AGENTS.md 已更新为真实栈
- S0 全部 17 条含回归验证登录日志三态过滤、claims 注入、时间戳服务端生成、x-user-id 删除+CORS 收紧含防回归测试、export 安全查询构建器双保险、ListAuthorities 委托统一入口、版本错误返回、失败关闭、SecurityConfig nil+error、限流白名单闭环、回调错误入链、Excel 文本保真、Apple JWS 固定 Root CA G3 真实指纹(经官方根证书清单核实)+完整链校验+签名时刻验证、ErrAuthoritiesRequired、OriginSetting 错误传播、cache Lua 原子补 TTL语义正确
- S4 两条QueryExport 单次严格解析saveRelations 单 replace 参数
## 处置建议总览(三轮合并,按优先级)
1. **先修 T0 修复回归缺陷**T0-1 资金安全高危优先)
2. **删 S1+T3 死代码**纯减法零风险biz/data/service 三层死方法约 15 组 + mq/websocket 零消费者机制 + 微死码,估算 800+ 行)
3. **T1 过分拆分合并**(透传壳 usecase 12 个、biz/system 小文件归并、SystemConfigService 四合一、provider/providers 双文件 ×4——零行为变更的文件级减法
4. **T2 归属移动**httpx/logging/module/paymentkit 内移 + jsonvalue 上收,按表逐项决策)
5. **S2 重复消除三大块**data/system List 泛型、payment 渠道骨架、storage provider 基座,估算 900+ 行)
6. **P2/P4 结构收敛**service 根门面、Backend 三层缝合、router+routecatalog 表驱动合并)