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

274 lines
37 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共三轮全量审查
- 审查方式codegraph 符号分析 + 多路并行逐文件深读 + 调用链 Grep 反查验证;第三轮附带 `go build ./...` 编译验证通过
- 文档结构:问题按**类型**归类(不按轮次);每条标注发现轮次【一轮/二轮/三轮】;已修复并经复查确认的统一列在文末
- gva/ 目录是遗留参考库(独立 module 不参与 kra 编译),不在审查范围
- 依赖方向合规确认pkg 无 import internalintegration 无 import data/serviceservice→biz→data 无反向;无循环依赖——架构骨架健康,债务集中在粒度与零消费抽象
---
## 一、修复回归与残留缺陷S0 修复后遗留9 条)
| # | 问题 | 位置 | 轮次 |
|---|------|------|------|
| R-1 | **vendor 退款静默受理残留(资金安全,高危)**S0-1 修复覆盖"FAIL 状态字段"场景,但 create/refund 响应 HTTP 200 且 body 非 JSON、或无可识别状态字段时仍默认 `created`biz `validatePaymentRefundResult` 接受 created → 真实被拒但响应无状态字段的退款会被永久记为已受理。建议 refund 端点无状态字段时报错或至少 pending | internal/integration/payment/vendor.go:135-163biz/payment/payment.go:1020-1021,837-855 | 三轮 |
| R-2 | Excel `[]byte` 全转文本副作用MySQL 数值列也变文本单元格,大面积"数字以文本存储"警告(原值保真达成,展示体验回退);可结合模板列类型区分处理 | internal/service/system/export_excel.go:85-86 | 三轮 |
| R-3 | ErrAuthoritiesRequired 错误透传缺失biz 返回具体错误后 handler 只回"修改失败"data 层 setUserAuthorities 还残留一处重复中文防御错误(双轨) | internal/server/handler/user.go:208-212internal/data/system/user.go:563-565 | 三轮 |
| R-4 | 版本导入非原子残留:落库成功但留痕 CreateVersion 失败时返回错误,用户误判"导入失败"(幂等查重使重试可控) | internal/service/system/version.go:256-270 | 三轮 |
| R-5 | 任务元数据双源漂移已发生:种子描述("**定时**清理…")与注册方法描述("清理…")不一致;建议 worker 注册时复用 catalog TimedTask 元数据 | internal/modules/task/definition.go:14-15 vs internal/worker/task_registry.go:31,45 | 三轮 |
| R-6 | export SQL/ImportSQL 摆设字段dto 保留但 ValidateExportTemplate 拒绝非空,只能提交空值且响应回显空值 | internal/service/dto/export.go:26-27internal/biz/system/export.go:72-74 | 三轮 |
| R-7 | payment 回调读体失败分支未入 Gin 错误链S0-12 修复只覆盖 service 调用错误) | internal/server/handler/payment.go:149-153 | 三轮 |
| R-8 | LoginLogFilter 收敛为仅 Username/Status 两字段:若前端需按 IP/时间筛选登录日志则能力缺失(设计取舍需确认) | internal/biz/system/audit.go:42-45 | 三轮 |
| R-9 | PaymentUsecase 注册面零调用时,生产装配下支付主链路 Create/Refund/Fulfill 必然在 preparePaymentRequest 报"支付业务订单来源未注册"——模板未完成态,建议 Wire/cmd 层提供默认注册或 fail-fast 提示 | internal/biz/payment/payment.go:296-326,407-410 | 三轮 |
## 二、死代码与零消费者机制
### 2.1 biz/data/service 三层死方法(约 15 组,均经全仓 Grep 反查确认零调用)【二轮发现,三轮复核仍在】
| 层 | 死代码 | 位置 |
|----|--------|------|
| biz 接口 | PermissionRepo.Buttons/SetAuthorityButtons/AuthorityButtonIDs3/4 方法死) | biz/system/permission.go:6-8 |
| biz 接口 | APITokenRepo.DisableAPITokenUserRepo.CreateUser/UpdateUserWithAuthorities | biz/system/api_token.go:26user.go:45,49 |
| biz 接口 | MediaMetadataRepo.FindMediaByHashMenuRepo.AuthorityMenuIDs | biz/system/media_metadata.go:44menu.go:61 |
| biz 接口 | EmailUsecase.AlertSecurityUsecase.ActiveTokenMatches | biz/system/email.go:31-48security.go:243-249 |
| biz 注入面 | PaymentUsecase 注册面SetHooks/SetOrderSourceRegistry/SetFulfillmentRegistry/RegisterBusinessModule含两阶段注册+回滚补偿PayInternal/RefundInternal/AuthorizeRefund 无生产实现service 却作为正式 API 暴露Fulfill | biz/payment/payment.go:296-326 |
| data 实现 | 与上述接口配套parseTemplateColumns、userRepo.CreateUser/UpdateUserWithAuthorities、DisableAPIToken、FindMediaByHash、AuthorityButtonIDs、AuthorityMenuIDs | 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.List连带 biz MenuUsecase.Listhandler 实际用 TreeRecordLogin/RecordDataAccess后者整链含 biz 接口+data 实现全死IsTokenDisabledsecurity_session.go 14 方法中 8 个死AuditRecorder.CreateErrorRequest+recordedErrorDomain | service/system/menu.go:14-20、audit.go:56-58,91-93、api_token.go:60-62、security_session.go:17-67、audit_error.go:17-31 |
| dto 死类型 | LoginLogRequestDataAccessRecordRequestGetAuthorityButtonsRequest.SelectedMenuResponse.Authorities 恒 nullbiz 无此字段DynamicMenuResponse.MenuButtons 恒 nilSysBaseMenuID 输入被静默丢弃version 导出结构体大量零值噪声字段ID:0/CreatedAt 零时间/authoritys:null | dto/system.go:68-75、audit.go:21-30、menu.go:21,28,132、version.go:23-94 |
误报更正service 层 PersistConfig 实为活代码initialize.go:20 消费)【三轮更正】。
### 2.2 mq / websocket 零消费者基础设施【二轮发现,三轮复核反而扩大】
- mq 全链(声明式订阅+legacy API+簿记/dispatcher/reconcile业务消费者为零emqx.go 约 710 行 + mq.go 106 行 + subscription.go 55 行空转legacy APISubscribe/Unsubscribe/Publishemqx.go:539-589内部又转译为声明式 Register双面并存且加码`internal/modules`、`biz`、`service` 无一处注册订阅或调用 Publish
- websocketHub 接口零消费者单实现pkg/websocket/melody.go:28-40wire 死绑定 provider.go:31integration 层 On* 四注册方法零调用server.go:326-373双层 handler 登记机制两层都永远为空server.go:28-31,169-180 vs melody.go:65-118
- 连带死装配cmd/main.go:65 `_ mq.Client` 幻影参数(为保活 wire 图integration/provider.go:28,29 两条死 wire 绑定
- 建议要么接入首个真实业务消费者要么裁掉保留最小面TestConfig 探测 + Enabled/Path/HandleRequest
### 2.3 零散死代码【二/三轮】
| 死代码 | 位置 |
|--------|------|
| s3 多 provider endpoint 五个不可达分支local.go:39-48 只让 minio 进 newS3Storage | storage/s3_storage.go:29-41 |
| namedClient/Client() 整型死代码 | integration/mq/emqx.go:47-50,632-650 |
| isBootstrapPath 零调用 | middleware/capture.go:72-74 |
| NewGinEngine 生产死代码wire 用 NewGinEngineWithRuntime测试专用 | server/gin.go:23-25 |
| vendorSupperPay 死枚举(定义注册但 switch 无 case | integration/payment/vendor.go:26,38 |
| stringOrnormalizePayPalOrderStatepaypalOrderTradeNonestedString/amountFromDecimalFieldshim 连调用者都没有) | wechat_v2.go:584、paypal.go:651,388-391、result.go:98,148-155 |
| RegisterAll 仅测试调用TaskScheduler.Trigger(task) 可未导出(仅 TriggerID 内部用) | pkg/task/registry.go:39worker/task_scheduler.go:425 |
| 仅测试调用decryptWechatV3、alipaySignContent、validatePaymentConfig、wechatV2Sign可移测试文件 | wechat_v3.go:600-605、alipay.go:572-585、data/payment/payment.go:551-553、wechat_v2.go:47 |
| P1-3 修复残留 micro-shimresult.go:58-64 与 payment_helpers.go:5-11 仍保留同名本地包装转发 paymentutil | integration/payment/result.godata/payment/payment_helpers.go |
## 三、重复实现 / 双份维护
### 3.1 大块可消除(估算合计 1900+ 行)【二轮】
| # | 问题 | 位置 |
|---|------|------|
| D-1 | data/system List 样板 13 处同构(构建 db→逐字段翻译 filter→Count→分页→Find→循环 toBiz可抽泛型 helper 消 200+ 行 | data/system/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 处 |
| D-2 | payment 渠道适配器家族重复约 500 行:下单方式归一化骨架 9 份Replacer 归一化行逐字出现 9 次);退款身份校验 4 份同构;状态归一化 switch 10 份(可收敛为 normalizeState+词表);金额拆分守恒 5 份+biz 层第 6 份SDK 客户端构造 10 份同构firstNonEmpty 三胞胎mustMarshalAlipayPayload≡mustJSON | integration/payment/alipay.go:434-456、douyin.go:108-126、qq.go:124-144、lakala.go:69-75、saobei.go:87-101、wechat_v2.go:184-204、allinpay.go:205-221、gopay_helpers.go:145-225、alipay.go:475/qq.go:146/paypal.go:707、alipay.go:470-473 等 |
| D-3 | storage provider 家族重复约 200 行key/unkey/file 三件套 5 份逐行相同DeletePrefix 分页循环 5 份(可提 deletePrefixViaListCompose 一行委托 7 份limit 守卫 7 份;构造尾部样板 5 份 | storage/aliyun_storage.go:35-48、aws_storage.go:63-76、huawei_storage.go:31-44、s3_storage.go:61-74、tencent_storage.go:45-58 等 |
| D-4 | handler 四段式样板约 70 处ShouldBindJSON→Fail→service→Write抽 bindJSON+respond 两个 helper 可消一半 | server/handler/*announcement.go:17-28、api.go:43-240、audit.go:27-316 等) |
| D-5 | 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.goaudit.go:47-48,93 对已 mask 已截断的正文再处理一遍 | server/middleware/* |
| D-6 | mq/websocket 两包各写一套 map 解码 helper 且逐字符相同configText≡text、configBool≡boolValueTestConfig 探测骨架三处同构 | emqx.go:226-260 vs websocket/server.go:189-240emqx.go:92-133/server.go:65-126/connectivity.go |
### 3.2 配置/数据不变式双份维护【一轮,三轮复核仍在】
| # | 问题 | 位置 |
|---|------|------|
| D-7 | DSN 一致性三处维护"结构化字段→Source"不变式refreshDatabaseSource 用"先清空再恢复"绕开 databaseDSN 短路 | data/config_store.go:78-94initialization_backend.go:42-59,135-140 |
| D-8 | "storage/email 缺省则沿用现值"策略散布 4 处(原 5 处收敛为 4无统一 MergeRuntimeConfig | config/runtime.go:275-286config_store.go:173-183initialization_backend.go:77-85,171-186 |
| D-9 | config.Store 与 runtimeconfig.Store 各写一套同构 listener/通知/克隆机制(语义有差异:文件全量 vs DB 集成窄通道,可辩护为有意分离,需明确决策;桥接靠 data 层三处 Replace | config/runtime.go:29,150-196runtimeconfig/store.go:62-167 |
| D-10 | "storage/email 不落盘"不变式三重执行persistConfigValues 置 nil+Delete、persistDatabaseConfig 再 Delete、removeIntegrationConfigFromFile 启动时又删一遍(一次性迁移 shim 常驻持久化路径) | config_store.go:46-52,103-104,108-125data.go:279-283 |
### 3.3 中小重复【一/二轮】
| # | 问题 | 位置 |
|---|------|------|
| D-11 | authority 树构建算法两份byID map+Children 重置+父子装配,仅节点类型不同) | biz authority.go:48-78 vs menu.go:80-99 |
| D-12 | CreateAuthority/CopyAuthority 前 8 行校验逐字重复 | data/system/authority.go:133-151,211-245 |
| D-13 | gorm.DeletedAt→*time.Time 转换 4 处逐字重复 | data/system/user.go:99-104、menu.go:17-21,241-245,250-255 |
| D-14 | version 阶段→消息映射两份engine.Routes()→dto 转换两份 | handler version.go:107-121 vs 158-167api.go:161-167 vs public.go:135-139 |
| D-15 | defaults 合并逻辑三层三份 | service/integration:86-98 / biz/integration:215-224 / data/integration/migrations.go:69-75 |
| D-16 | media 分片根目录逻辑两份media_upload 三处复制会话校验样板(顺带吞掉 ErrUploadNotFound 可判定性) | biz media.go:126-132 vs media_upload.go:61-69media_upload.go:120-129,176-184,244-250 |
| D-17 | data/paymentcallbackFields/first 整函数复制且已分叉integration 版 Content-Type 大小写不敏感遍历 vs data 版直接下标行为漂移隐患values() 与 testRow() 近重复;渠道"免 notify_url"同义词表在 data 层重抄一份与各适配器 createMethod 表双份维护 | data/payment/payment.go:507-540,31-50,273-292,394-425 vs integration/payment/result.go:14-56 |
| D-18 | serviceauthorityResponse 与 convertAuthority 逐字重复;"单条 DTO helper + for-append"样板 9 处;"Request 包装+Filter 包装+裸方法"三重入口家族audit/parameter/export/dictionary/mediauser 空对象兜底三连 | service/system/authority.go:30-36 vs user_conversion.go:8-14 等 |
| D-19 | payment 金额守恒校验四处重复biz validatePaymentBreakdown + vendor + wechat_v3 + douyin 各一份) | biz/payment/payment.go:1112-1135vendor.go:235-253wechat_v3.go:555douyin.go |
| D-20 | 状态词汇归一三处维护paymentutil.NormalizeStatus + 各 adapter + data/payment normalizeOrderPaymentStatus | paymentkit/status.go:8-19data/payment/payment_order.go:478 |
## 四、过度分层:转发门面 / 透传壳 / 回调穿透
| # | 问题 | 位置 | 轮次 |
|---|------|------|------|
| F-1 | payment 回调 ack 穿透四层(定义已单源化到 paymentkit、biz 用别名——算部分收敛;但 biz/service/handler 三层转发仍在dto/payment.go:58-62 重复声明同构 3 字段结构体,加字段需同步三处) | paymentkit/callback.go:11-83 → biz/payment/payment.go:206-215 → service/payment/payment.go:131-148 → handler/payment.go:173-184 | 一轮(三轮复核部分收敛) |
| F-2 | integration/payment/result.go 自称 "Compatibility shims"11 个纯转发函数全部仍在 | integration/payment/result.go:68-110 | 一轮 |
| F-3 | service 根包 25 个 `type X = systemservice.X` 别名门面 + 函数转发(迁移脚手架;使 handler 无法感知真实包结构) | service/service.go:18-51 | 一轮 |
| F-4 | handler/http.go 纯转发别名层3 常量+2 类型+6 函数一对一转发,仅为省一个 import每个 handler 文件仍要同时认识两个包) | server/handler/http.go:14-29 | 一轮 |
| F-5 | service/task 双面 APIDO 签名版与 DTO 版并存DO 版仅被同文件 DTO 版内部调用handler 只用 `*Request` 版 | service/task/task.go:35-46 | 一轮 |
| F-6 | task 双 usecase 并存且加重TaskUsecase 嵌入 TaskRepo 透传 9 方法给 workerTaskApplicationUsecase 再包一层、其 6 方法纯转发worker 持前者、service 持后者wire_gen.go:115-119 | biz/task/task.go:76-79,159-237 | 一/三轮 |
| F-7 | Backend 三层缝合biz InitializationRepo → initialize.Repo内嵌 6 方法 Backend + seed 回调单传入点)→ data.Datainitialize 包只为"转手 6 个同名方法+注入 catalog"存在 | initialize/initialize.go:17-46data/initialization_backend.go:17-217cmd/wire.go:45 | 一轮 |
| F-8 | data-scope 审计回调 dataScopeAuditEnqueue 穿透 6 层签名,生产实现唯一 | data/data.go:238,245,392 → runtime_clients.go:168 → data_scope.go:19-136 | 一轮 |
| F-9 | **纯透传壳 usecase 12 个**(整个 struct 无自有逻辑/纯改名转发service 可直依赖 biz repo 接口——repo 接口仍在 biz分层契约不破wire 链物证 wire_gen.go:88-91 permission 全程零逻辑Parameter/Permission/Version/Announcement(7 方法全透传)/Maintenance/AccessControl/Department(2 改名)/Authority(仅 Tree 有逻辑)/Task/Menu(10 处透传)/User(13 处透传)/SystemConfig(7 处透传)。对照组有真实逻辑应保留Authentication/Security/Media/Email/IntegrationConfig/Payment/TaskApplication | biz/system/*、biz/task/task.go | 三轮(二轮发现同型) |
| F-10 | security_session.goSecurityService 14 方法全部一行透传(零 DTO 工作service 层另有近纯透传小文件 email.go(16)/permission.go(24)/access_control.go(28仅 5 行逻辑) | service/system/security_session.go:17-68 等 | 三轮 |
| F-11 | biz 接口嵌入透传 12 处 usecaseAPI/Token/LogViewer/Audit/AuditRecorder/Authority/Dictionary/Export/Media/Parameter/Permission/Position/Version与同包 9 个私有字段风格并存 | biz/system/* | 二轮 |
| F-12 | `openWithDriver` 单调用点便捷转发(已拆出 openWithDriverConfigwrapper 仍有 1 个生产调用 + 约 20 处测试调用) | data/database.go:134-136生产调用 :248 | 一轮(三轮复核部分修复) |
## 五、过分拆分 / 文件组织
**根因模式三条**【三轮】:①零逻辑 usecase 壳wire 强制每域一个构造器放大);②"每资源 N 文件"机械切分dto+biz+service+handler+router 各一个);③为 import 美观引入的中间缝合包/门面。
| # | 问题 | 位置 | 轮次 |
|---|------|------|------|
| S-1 | biz/system 38 文件 3430 行,其中 17 个非测试文件 <60 合计约 575 保守可归并 8-10 个文件errors.go(14)、cache.go(15)、maintenance.go(19)、actor.go(19)、access_control.go(20)、storage.go(26)、data_scope.go(27)、upload_session.go(38)、parameter.go(33)、settings.go(43)、department.go(46)、position.go(48)、token.go(48)、media_metadata.go(53)、email.go(48)/api_token.go(55)/version.go(57)。合并建议actor/data_scopeauthoritycachesecuritystorage/media_metadata/upload_sessionmedia department+positionorganizationtokenauthenticationerrors 集中 | biz/system/* | 三轮 |
| S-2 | SystemConfigService 一型拆四文件system.go(19)+system_config.go(26)+system_init.go(38)+system_info.go(42)=125 audit.go audit_error.go 同属 AuditService/AuditRecorder 可合并audit_log_file.go 是独立 LogViewerService保留 | service/system/* | 三轮 |
| S-3 | router 22 文件 464 平均 21 /文件最小 email.go 12 routes.go:18-43 手工 21 连调——纯注册碎片无内聚 X-1 表驱动合并一并解决 | server/router/* | 三轮 |
| S-4 | 单符号包/微文件modules/surface 整包只有 1 10 行函数data/provider 整包只有 1 7 2 方法接口中性缝可辩护provider.go+providers.go 双小文件模式 ×4 子包8 文件可并 4bootstrap.go(27 行仅 seed )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 行别名 | 各处 | 三轮 |
| S-5 | 巨微两极biz/payment/payment.go 单文件 1150 usecase+常量再导出+5 validator+指纹工具vs 同域 payment_log.go(32)/provider.go(8)dto authentication.go(8 )/email.go(7 ) vs settings.go(170 行横跨四域) | biz/paymentservice/dto | 三轮 |
| S-6 | dto 包文件组织混乱system.go 混装 Login/User/ServerInfo 三域settings.go 横跨 Dictionary/SystemParameter/APIToken/SecurityConfig 四域名不副实 | service/dto/system.go:5-133settings.go:5-170 | 一轮 |
| S-7 | data 层组织纪律转换函数命名四种风格违反 new<X>/toBiz 契约FromPO×16/ToPO×4/ToBiz×2/new×2PO 分布无规则models.go 集中 6 个+散落 30+models.go:98-100 还混 repo 声明audit.go 名不副实(只有构造器,实现在 5 个文件runtime.go 拼盘settings+tokenIssuer 无关联转换函数跨文件错位dictionary.go 的 parameterFromPO 服务 parameter.go | data/system/* | 二轮 |
| S-8 | media 域同域碎片biz/system 的 media.go(158)/media_upload.go(268)/media_metadata.go(53)/upload_session.go(38) 拆四文件 | biz/system/media* | 三轮 |
| S-9 | 单方法 handler 各占结构体+构造器+Set 字段Session/Navigation 可并入相邻资源 handlerSet 已膨胀到 23 字段、provider 23 个构造器) | server/handler/session.go、navigation.go、set.go:3-26 | 一轮 |
## 六、包归属问题(应移 pkg / internal / utils
### 6.1 移动建议清单【三轮汇总】
| # | 从 | 到 | 理由 | 影响 |
|---|---|---|---|---|
| P-1 | pkg/httpx | internal/server/httpx | 消费者 100% 在 internal/server 7 文件;中文文案+x-token cookie 是本项目契约logging/source.go:59 已预留该路径 marker | 7 文件 |
| P-2 | pkg/logging | internal/logging | source.go:47-69 硬编码本仓库 internal 路径zap.go:366-382 中文文案模块路由硬编码服务日志查看器语义AGENTS.md 声称的 internal/logging 不存在 | 约 5 文件 |
| P-3 | pkg/module | internal/modules | Menu/API/Surface/TimedTask 是本项目模块系统契约import gin15 消费者全在本仓库 | 15 文件 |
| P-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 文件 |
| P-5 | pkg/mq + pkg/websocket(Hub) | 收缩或合并进 internal/integration/mq | 全链零业务消费者(见 2.2);保留 TestConfig 探测+基础驱动,删 Registry/Client()/namedClient/legacy API/Hub/ApplySubscriptions | 约 7 文件 |
| P-6 | integration/mq 与 integration/websocket 的重复 JSON map helper | internal/utils/jsonvalue | 上收去重(逐字符相同) | 2-3 文件 |
| P-7 | storageaws-sdk-v2 栈与 minio-go 栈双 S3 实现并存 | 统一 S3 兼容单栈(可选) | qiniu/aliyun/huawei/tencent 原生 SDK 均有 S3 兼容端点,可收敛删 4 实现+s3 死分支 | 5-6 文件 |
| P-8 | handler/query.go:13-61Gin 工具、middleware/request.go:67-102纯函数、handler/announcement.go parseTime被 export.go 跨域借用) | pkg 或 internal/utils 候选 | 通用无状态逻辑上收 | 3 文件 |
### 6.2 其他归属/职责越界【一/二轮】
| # | 问题 | 位置 |
|---|------|------|
| P-9 | internal/initialize/configuration.gomanagement* 家族(:221-318手工构造 camelCase JSON属 service 层 DTO 塑形职责;根因是 biz InitializationRepo 以 json.RawMessage 为出入参,表现形状泄漏进 repo 层464 行混杂 DTO 塑形/JSON 规范化/秘密掩码三种职责 | initialize/configuration.go |
| P-10 | pkg/database/pagination、gormkit调用方 100% 在 internal/data无业务语义轻度 | pkg/database/* |
| P-11 | pkg/mqrabbitmq.go:127 硬编码项目名前缀 `kra-`subscription.go 的 Contributor 面向本项目 module 概念 | pkg/mq |
| P-12 | newReloadableDB 在通用热切换构造器里调用领域函数 registerDataScopeCallbacks越界应移到 data.go 与 replacePrimaryDB 同址) | data/runtime_clients.go:168-171 |
| P-13 | pkg/module、pkg/task、pkg/database/migration互为依赖构成同层契约组单独搬会破坏依赖方向一致性——维持现状 | pkg/* |
## 七、分层 / 职责违规
| # | 问题 | 位置 | 轮次 |
|---|------|------|------|
| L-1 | data 直依赖 integrationdata/payment/payment.go:17 import kra/internal/integration/paymentpaymentRepo 实为"配置读取+适配器编排+ack 组装"的编排层Create/Query/Refund/HandleCallback 四方法无一行 DO↔PO 转换,真正仓储职责全在 paymentOrderRepoTestProvider 是 145 行业务编排本地建单、500ms×3 重试轮询、退款闭环)长在 data 层;还自建第二 paymentOrderRepo 实例(:91绕过 wire 单一构造点 | data/payment/payment.go | 二轮 |
| L-2 | 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 导入导出全编排 | service/system/* | 二轮 |
| L-3 | biz DO 带 json 标签PaymentResult/PaymentTestResult死标签service 逐字段转 dtoPaymentRequest把指纹编码格式锚死在 DObiz/integration 的 Definition/Field/Option 家族直接充当前端契约dto/integration_config.go:21 内嵌 biz 类型biz 事实上兼任 DTO 提供方) | biz/payment/payment.go:59-204biz/integration | 二轮 |
| L-4 | DO 兼过滤器API.OrderKey/Desc/StrictAll、SystemParameter/ExportTemplate.StartCreatedAt/EndCreatedAt 混入实体 | biz/system/api.go:16-18、parameter.go:16-17、export.go:30-31 | 二轮 |
| L-5 | middleware 硬编码业务语义error_audit.go:70-77 靠中文消息黑名单判断是否审计改文案即改审计行为error_audit.go:25,55-67 硬编码业务路径access_log.go:82-203 支付回调专用逻辑内嵌通用中间件rate_limit.go:33-51 限流策略参数内联且挂在全局链却只匹配两个 public 路由 | server/middleware/* | 二轮 |
| L-6 | biz 契约泄漏存储/表现原语QueryExport 返回 []map[string]anyLogViewer 的文件读取器细节export DO 字面携带 SQL/Join/Table 片段Export 域整体是查询引擎不是领域逻辑,应下沉 dataUserOptions {Label,Value} UI 形状进 bizAuthenticationResult 携带含密码哈希的完整 User | biz/system/* | 二轮 |
| L-7 | data 层纪律Table("字符串") 绕过已有 PO 7 处saveRelations 回写入参 DO 约 10 处OriginSetting 裸 string↔map 转换与 gormkit.JSON 封装并存 | data/system/user.go:58,70,88,231,259 等 | 二轮 |
| L-8 | 编排类文件过重seedSystem 单函数 126 行 10 类职责authority.go 834 行四类职责CRUD/严格权限引擎被 4 文件 15+ 处借用/DataScope 域解析/用户-角色关联,权限引擎应独立 accessGuardBuildVersionBundle 105 行五职责migrations.go 两个通信 surface 迁移互为重复子集 | data/system/seed.go:30-156、authority.go、version.go:76-181、migrations.go:36-173 | 二轮 |
| L-9 | dto 契约问题ID 类型三处分叉int/uint/string 混用迫使 handler 转换AuthorityResponse.DeletedAt 泄漏且破坏全库 json:"-" 约定ErrorRecordMutationRequest 一半指针一半值不自洽DTO 反向依赖 biz 类型 | service/dto/* | 二轮 |
| L-10 | 错误体系双体系errors.go 仅 3 个 kratos 类型错误,其余 stdlib errors.New 散落 13+ 文件,无 reason 码token.go:35-42 另有 6 个分散);错误包装 Error+Unwrap 与 Error+Is 两机制混用PasswordPolicyError 类型定义在 service | biz/system/errors.go 等 | 二轮 |
| L-11 | List 契约三种风格并存(过滤结构体内含分页/位置参数+指针 DO/裸标量 5-6 参data 分页三风格pagination.ApplyRequired/Apply/手写 Limit-Offset手写版 page=0 产生负 offsetposition.go:89、export.go:208-210 无防护) | biz+data 多处 | 二轮 |
| L-12 | 校验双轨制handler 手工 if 校验与 dto binding 标签并存user.go:26-64 手工 6 字段 vs dto.UserRequest 无标签);同类资源一半 binding、一半手工、一半裸奔 | server/handler/* + service/dto/* | 一/三轮 |
## 八、简单实现复杂化
| # | 问题 | 位置 | 轮次 |
|---|------|------|------|
| C-1 | payment Create 过度防御:先拷 9+1 字段逐一回比+Extra 双次 JSON 序列化同一不可变保证指纹层RequestFingerprint+落库后二次比对)已做两道 | biz/payment/payment.go:385-402,898-907,588-590 | 二轮 |
| C-2 | loadUser/loadUsers 双实现(单实体 6 次串行查询 vs 批量实现,可复用省约 45 行) | data/system/user.go:53-97,115-218 | 二轮 |
| C-3 | JWT 签名双重检查validateSigningOptions 后 signToken 再查一遍) | data/system/token.go:34-51 | 二轮 |
| C-4 | `*Data` 方法约 10 处模板式 `if d == nil` 防御(构造归 Wire 管理NewIntegrationRuntime 的 nil→空 Store 回退掩盖错误状态;中间件 nil 防御四处gin.go:28-30 已兜底access/access_log/audit/cors 仍各自检查) | data/data.go:41,72,82,88,97,119,126,373-375middleware/* | 一/二轮 |
| C-5 | public.go captchaConfig 恒真分支与无效首调用 | server/handler/public.go:29-43 | 二轮 |
| C-6 | 媒体上传三重大小防御limitMultipartBody+rejectMediaTooLarge+header.Size 检查,三重中两重冗余) | server/handler/media.go:38-50,226-240 | 二轮 |
| C-7 | emqx.go 恒真 ctx 判断(:561-569,592-600websocket 双层 handler 登记(两层各持四份列表互相重放,且都为空) | integration/mq/emqx.gointegration/websocket | 二轮 |
| C-8 | 单实现接口PaymentLogger仅为包装 *slog.LoggerDictionaryRepo/MediaRepo 组合式子接口无独立消费方 | biz/payment/payment_log.go:10-32biz/system | 二轮 |
| C-9 | dictionary.go Tree 解析结果被丢弃byType 分支不用 id/parseErr | server/handler/dictionary.go:211-213 | 二轮 |
| C-10 | mq 体系接口面积翻倍且恶化(见 2.2mq.Registry 双 API 并存 | pkg/mq/mq.go:66-83emqx.go:539-589 | 一轮(三轮复核恶化) |
## 九、结构性设计(大动作需决策)
| # | 问题 | 位置 | 轮次 |
|---|------|------|------|
| X-1 | router 与 routecatalog 双声明21 个 router 文件纯声明式注册 method+pathroutecatalog 又用一张 195 条 map 声明同一批路由的元数据,靠契约测试强制对齐——改一条路径要同时改两处。表驱动合并为单一声明源可同时消掉对齐测试 | server/router/*routecatalog/catalog.go:41-236 | 一轮 |
| X-2 | 新增一个资源实际要触碰 7 处dto、service、handler、router 资源文件、routes.go、routecatalog、Set/provider | — | 一轮 |
| X-3 | 同一份集成配置三种形状两条通道config.Storage 强类型 ↔ sys_integration_configs JSON 行 ↔ 运行时客户端storage/email 走 config.Storeemail 即时读快照、storage 手动 Replacemq/websocket 走 runtimeconfig 订阅——同一"集成"概念两套配置源两种重载模式 | data/integration_config.go:43-104,157-179data/integration/runtime.go:13-29 | 一轮(三轮细化) |
| X-4 | swagger 运行时文档:约 160 行 map 手拼 Swagger 2.0 JSON+正则加工,全局单例仍在 server 根包 | server/swagger.go:21-159 | 一轮 |
| X-5 | 错误日志热路径做磁盘 IO+go/parser AST 解析(每条 Error 日志触发 os.ReadFile+parser.ParseFile无缓存 | pkg/logging/source.go:71-103zap.go:373-379 | 一轮(三轮复核仍在) |
| X-6 | local 存储两套入口staticfiles 直读 config 本地盘语义 vs integration/storage 的 Reloadable 体系 | server/staticfiles/staticfiles.go:20-47integration/storage/local.go | 一轮 |
| X-7 | gopay.go/gopay_helpers.go 名实相反gopay.go 是窄基座4 个函数,:12-13 注释过时gopay_helpers.go 是杂物间7 类职责混装,渠道专属谓词/状态机应下沉各渠道文件,公共函数 mergeMap 反而散在 alipay.go | integration/payment/* | 二轮 |
| X-8 | vendor.go金额拆分 14 键配置 DSL 疑似投机通用性(无内置默认使用);通用渠道定义 18 字段全 Required=true不用退款的商户也被迫配置退款地址 | integration/payment/vendor.go:170-238biz/integration/integration_config_definition.go:58-77 | 二轮 |
| X-9 | 集成配置双轨制(同 X-3config 热重载双通道fsnotify watchLoop 只 Replace 快照不重建 DB/Redis/Mongo 客户端,全量重载只能手动 POST 触发——行为不透明 | config/runtime.go:263-287data/config_store.go:127 | 三轮 |
| X-10 | payment 域新增一个渠道需改 4 处paymentkit 常量+biz 再导出+adapter 工厂+配置定义)散弹式修改 | paymentkit/provider.go:4-30 等 | 三轮 |
| X-11 | TaskScheduler 的 6 把锁与 dispatch async 参数:每处均有并发正确性注释论证,是真实并发需求的代价——不建议改,仅记录 | worker/task_scheduler.go:24-36,260-283 | 二轮 |
## 审查后认为合理、不建议改动的部分
- **modules 与 routecatalog 分离**:启动期模块装配 vs 请求期热路径策略查询,消费方零重叠,仅合理单向依赖
- **Provider 接口缝模式**(子包不反向 import 根 data 避免成环+测试假 Data模式正当provider.Database 中性接口P1-1 修复)设计合理,唯 system 包内 Provider5 方法)与 DatabaseProvider别名两个近义缝命名易混淆建议注释互指
- **config.StoreYAML 文件)与 runtimeconfig.StoreDB 集成表)职责分离**:正确,仅 pub-sub 同构待决策D-9
- **utils/routepath 与 routecatalog**互补非重复utils 整体纪律良好routepath/uploadpolicy 均无状态多域共用)
- **worker→biz 正向依赖+biz 经 TaskRuntime/TaskReloader 接口反向倒置**:任务链路最规范的一段
- **data 根多文件同包共享 Data 状态与重载锁**:符合 data/README 既定约定
- **apple_jws.go 的 CheckSignatureFrom+OID 检查**:经 gopay 源码核对是补真实漏洞gopay 不验证叶证书由中间证书签发),必要;仅单一根指纹需运维轮换预案注释
- **capture/auth 等中间件质量**capture 作为唯一请求/响应捕获点、auth singleflight 防取消传染,有据可依
## 处置建议(按优先级)
1. **先修一节修复回归缺陷**R-1 资金安全高危优先)
2. **删二节死代码**(纯减法零风险:三层死方法约 15 组+mq/websocket 零消费者机制+零散死码,估算 800+ 行)
3. **做五节过分拆分合并**(透传壳 usecase 12 个、biz/system 小文件归并、SystemConfigService 四合一、provider/providers 双文件 ×4——零行为变更的文件级减法
4. **执行六节归属移动**(按 P-1~P-8 逐项决策)
5. **消除三节大块重复**D-1/2/3 三大块估算 900+ 行)
6. **收敛四/七/八/九节结构与分层**service 根门面、Backend 三层缝合、L 系列归位、X 系列大动作)
---
## 已修复并经复查确认第三轮逐条验证2026-08-27
> 以下条目经第三轮 5 路代理逐条核查属实(含编译验证通过),从上方问题清单移除,此处留痕。复查中发现的部分修复残留已回升为一节 R 系列条目。
### 第一轮问题P 系列)
- P0-1pkg/protoutil 整包删除
- P0-2paymentkit WechatV2Sign 死实现删除
- P0-3Data 的 MongoClient/NamedDatabases/NamedRedisClients 三死方法删除
- P0-4config Bootstrap 别名删除
- P0-5静态任务方法注册链删除重建为活的 TimedTasks 单源链路,含防回归测试)
- P0-6TaskScheduler.Schedule(task) 删除(生产与测试均改用 ScheduleID
- P1-1中性 internal/data/provider.Database 接口抽取task/security 两处仅剩一行别名)
- P1-2任务种子单源化migrations fallback 删除,只来自 module catalog
- P1-3text/firstAny 收敛到 paymentkit.Text/FirstText残留 micro-shim 见 2.3
- P1-4menu handler 校验提取 validateMenuRequest
- P1-5surface.APIsForPrefix 提取(含契约测试)
- P1-6persistConfig Locked 变体删除,锁逻辑单点化
- P1-7NewData/reloadConfig 均改用 rollback 收集器commit/run 语义核对无误)
- P1-10websocket snapshotHandlers 泛型收敛
- P4-4AGENTS.md 更新为真实栈Gin+手写 DTO+Wire
### 第二轮 S0 正确性/安全缺陷17 条全部修复,含回归验证)
- S0-1vendor 通用渠道退款业务状态解析+失败回归测试(残留缺口见 R-1
- S0-2登录日志独立 LoginLogFilter+*bool 三态筛选status=false 正确过滤)
- S0-3公告/媒体导入 UserID 从 claims 注入DTO 移除可伪造字段
- S0-4客户端时间戳改为服务端生成ErrorRecord/Menu/Parameter/Export 均覆盖)
- S0-5审计中间件 x-user-id 头回退删除+CORS 白名单收紧(含防回归测试)
- S0-6export 统一安全查询构建器(标识符/Join/条件/排序校验+禁任意 SQLbiz/data 双保险BETWEEN 值参数化)
- S0-7ListAuthorities 委托统一权限入口
- S0-8版本导入 JSON/Save 阶段错误返回
- S0-9用户服务失败关闭安全配置读取错误即返回
- S0-10SecurityConfig 未就绪只返回 nil+error
- S0-11限流文案加入预期客户端失败白名单闭环匹配
- S0-12支付回调 service 错误加入 Gin error chain读体分支残留见 R-7
- S0-13Excel 文本保真("007" 保持 string含测试副作用见 R-2
- S0-14Apple JWS 固定 Apple Root CA G3 真实指纹(经官方根证书清单核实)+完整链校验(叶←中←根+CA 约束+OID+ES256+签名时刻验证
- S0-15SetUserAuthorities 空列表改 ErrAuthoritiesRequiredBadRequest
- S0-16OriginSetting JSON 解析错误传播(单条/批量/关联加载)
- S0-17cache.Incr Redis 原子 Lua 补 TTL仅无 TTL 的 key 补,语义正确;内存实现同步修复)
### 第二轮 S4 两条
- export QueryExport 双重 JSON 解析 → 单次严格列解析
- saveRelations 三布尔参数 → 单 replace 参数