From b370089942069ee3a2bf2165719d04535cbb2b2a Mon Sep 17 00:00:00 2001 From: Yvan <8574526@qq,com> Date: Fri, 28 Aug 2026 16:56:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 2 +- docs/code-review-issues.md | 72 +++++++++---------- .../biz/integration/integration_config.go | 6 +- internal/server/httpx/response.go | 6 +- internal/server/middleware/error_audit.go | 2 +- internal/worker/task_scheduler.go | 17 +++++ web/src/components/office/pdf.vue | 3 - web/src/core/global.js | 3 +- web/src/utils/image.js | 1 - .../dictionary/sysDictionaryDetail.vue | 1 - web/src/view/systemTools/version/version.vue | 1 - 11 files changed, 65 insertions(+), 49 deletions(-) diff --git a/configs/config.yaml b/configs/config.yaml index 5698f26..da3f50a 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -101,7 +101,7 @@ admin: store_path: uploads/file path_prefix: uploads/file media: - # Upload chunks are stored below .chunks in the selected storage backend. + # Upload chunks are stored below uploads/chunks in the selected storage backend. session_ttl: 24 max_file_size: "0" chunk_dir: uploads/chunks diff --git a/docs/code-review-issues.md b/docs/code-review-issues.md index 72d5051..5d4b3ba 100644 --- a/docs/code-review-issues.md +++ b/docs/code-review-issues.md @@ -12,58 +12,58 @@ | # | 问题 | 位置 | 轮次 | |---|------|------|------| -| V-15 | **错误泄露收敛(envelope 净化)残留四个洞**:① **"失败:"截断只匹配半角冒号**——media.go:188 与 menu.go:65 用全角":"拼接绕过;② **17 处截断不可命中的调用漏网**(无冒号紧贴 15 处:api.go:137,156、authority.go 9 处、menu.go:170,178,198、user.go:150;全角 2 处)——非黑名单底层错误(x509/驱动方言)原样透出;③ **payment TestProvider data 字段绕过**——data/payment/payment.go:79-139 各阶段仍把原始 err.Error() 写入 Stages[].Message,而 Write 只净化 msg、**CodeError 的 data 通道完全无净化机制**(:87 是当前唯一实例但机制敞开);④ **净化静默无日志**——response.go 无日志导入,替换发生无记录("不泄露也不可排障")。另有误伤:黑名单含 provider/sdk/connection/timeout 等宽泛词会误伤合法业务消息;约 45 处半角冒号调用的 biz 可读提示被截断抹平。净化逻辑零测试 | server/httpx/response.go:32-57;data/payment/payment.go:79-139;handler/payment.go:87 | 六轮发现,十轮复核部分修复(白名单空操作已修、record not found 已补)+四洞 | -| V-32 | **(新发现,高)删除审计日志的接口自身无审计标记(清痕无痕)**:`DELETE /sysOperationRecord/deleteSysOperationRecord` 与 `deleteSysOperationRecordByIds`(catalog.go:59-60)均无 audit:true(同表其他删除接口都有);`POST /attachmentCategory/deleteCategory`(:138)同样缺失——删除审计记录本身是典型清痕操作,审计矩阵修复的遗漏点 | routecatalog/catalog.go:59-60,138 | 十轮 | -| V-33 | **(新发现,中高)支付指纹严格相等校验对存量订单幂等重放的冲突风险**:biz/payment/payment.go:652 `order.RequestFingerprint != paymentOrderFingerprint(req, extra)` 严格相等、无算法版本/迁移字段——Z-3 指纹统一前存量 initialized/pending 订单若由旧算法(data 层整结构 marshal)写入指纹,客户端超时重放同 TradeNo 将报"支付订单参数冲突"而非幂等短路,**存量未支付订单无法重新拉起支付**。建议对 initialized 态提供指纹重算/兼容窗口 | biz/payment/payment.go:645-657;data/payment/payment_order.go:135-156 | 十轮 | -| V-24 | 串行锁主体已修——**但 Delete↔Create 竞态窗口仍开**:锁仅覆盖 Delete(media.go:126-143),引用同一 Key 的写入路径均不持锁(秒传 InitUpload→CreateMedia、普通 Upload 引用计数+建记录、CompleteUpload 的 CreateMedia)——T1 Delete 读 count=1 判定删除、T2 秒传 CreateMedia 复制同 Key 落库、T1 files.Delete → T2 新记录指向已删文件。锁为进程级 sync.Mutex,多副本部署跨进程无效 | biz/system/media.go:50,111-143;media_upload.go:100-107,262 | 八轮发现,十轮复核主体修复+Create 路径缺口 | -| V-34 | **(新发现,中)CleanupStale 先删 DB 后删文件且吞错 → 存储孤儿无重试路径**:biz/media_upload.go:295-299 先 `DeleteUploadData`(事务内物理删 session+chunks)再 `files.DeletePrefix`(_ = 吞错)——存储删除失败时 session 行已物理删除,**下轮扫描永远不再命中**,孤儿分片对象永久滞留(无孤儿反向扫描器) | biz/system/media_upload.go:295-299 | 十轮 | -| V-35 | **(新发现,中)TaskScheduler 启动 Reload 失败仅 Warn 无重试**:worker/task_scheduler.go:93-95——DB 启动期闪断 → 任务表加载失败 → 调度器空转且不重试(Start 阻塞在 :99 `<-runContext.Done()`),**定时任务全部静默丢失**直至人工 reloadSystem | worker/task_scheduler.go:93-99 | 十轮 | -| V-19 | **证书别名缺口未修(三层全缺)**:`*_content`/`*_path` 别名(app_cert_content/root_cert_content/pkcs12_content/cert_content/key_content/cert_path 等)不命中 IsIntegrationSecretKey(不含 secret 词根、后缀非 _cert/_key)、不在 definition.Fields → data/service/middleware 三层键表均不掩码,API 直连提交明文落库并回显;connectivity restoreMaskedSecrets 同样不识别(掩码值被当真值测试连接)。误伤项(routing_key/key_id)已消除(键表无裸 _key 项)。**修法:键表补别名一处三层受益** | biz/integration/integration_config.go:325-328,423-431,398-409 | 六轮发现,十轮复核未修 | -| V-13b | swagger 残留:① **空 env fail-open 未修**(gin.go:68 `env == ""` 仍注册,生产漏配 env 即暴露);② **双清单判定不一致**——gin.go 白名单 {空,development,dev,test,local} vs main.go:107-110 黑名单 {production,prod,live,staging},自定义 env(qa/uat)时路由不注册但启动日志仍打印 swagger URL(:171 日志谎报);③ env 门禁零测试 | server/gin.go:64-70;cmd/main.go:105-126,171 | 七轮发现,十轮复核未修 | -| V-25 | merging 阈值区分✓——残留:合并超 TTL+1h 会话被物理回收后 CompleteUploadSession Updates 影响 0 行无 RowsAffected 校验 → media 已建但会话记录丢失、秒传断链无错误暴露 | data/system/media_upload.go:92-94,121-125 | 八轮发现,残留 | -| V-36 | **(新发现,低中)ErrorAudit 客户端失败词表含 "token" 会吞 5xx**:无 privateErrors 且 msg 含 "token" 的服务端错误(如 "token 生成失败: redis connection refused")被判为预期客户端失败跳过 sys_error 记录——服务端故障静默 | server/middleware/error_audit.go:73,43-45 | 十轮 | -| V-37 | **(新发现,低)分片上传秒传复制丢失 CategoryID**:media_upload.go:100 copy 未带 CategoryID(InitUpload 签名无 category 参数,单文件 Upload 支持 media.go:68)——分片上传的媒体记录分类恒为默认值,功能不对齐 | biz/system/media_upload.go:87,100 vs media.go:68 | 十轮 | -| V-27 | database_list 按别名回退已修✓(byName 映射+无按下标)、空密码不掩码✓——**蟑螂定律残留**:① **redis_list 仍按下标回退**(configuration.go:439-441 `previous == nil && index < len(current)`)——重排/头部插入时掩码条目继承旧列表同位置密码;② 别名匹配失败(改别名/新增条目/存量空 AliasName)时哨兵字面量直接落盘(data 层重建 DSN 密码变 `******` 连接失败)——无哨兵落盘拒绝校验 | internal/initialize/configuration.go:427-446,414-417 | 九轮发现,十轮复核部分修复+redis_list 残留 | -| V-30 | AlipayV3/WechatV2 required 已对齐✓——**douyin platform_serial_no 仍 required=false**(definition.go:167)而校验器启用时强制(integration_config.go:344-347),前端不标必填、保存时才报错;同构的 WechatV2 client_cert 标了 true,两处标准不一致 | biz/integration/integration_config_definition.go:167 | 九轮发现,十轮复核部分修复 | -| V-20 | 前端 token 拼入 URL(完整登录 JWT 拼二维码 URL,账户主凭证泄露面;无轮换机制) | web/src/components/upload/QR-code.vue:55 | 六轮,未修复 | -| V-16b | 词表与八轮一致——applet 仅 wechat_v2/v3/douyin 有;allinpay 无 jsapi/mini 分支;测试缺口仍在 | integration/payment 各渠道文件 | 七轮,未修复 | -| V-38 | (新发现,低)AGENTS.md 错误契约 vs 媒体模块实践:biz "typed errors(errors.NotFound/BadRequest)"约定下,media.go:52 ErrMediaTooLarge、media_upload.go:21 ErrUploadSessionNotFound 为裸 errors.New(同包 errors.go:9 ErrMediaNotFound 是规范风格),前端无法区分错误类别 | biz/system/media.go:52、media_upload.go:21 | 十轮 | +| V-15 | **错误泄露收敛(envelope 净化)残留四个洞**:① **"失败:"截断只匹配半角冒号**——media.go:188 与 menu.go:65 用全角":"拼接绕过;② **17 处截断不可命中的调用漏网**(无冒号紧贴 15 处:api.go:137,156、authority.go 9 处、menu.go:170,178,198、user.go:150;全角 2 处)——非黑名单底层错误(x509/驱动方言)原样透出;③ **payment TestProvider data 字段绕过**——data/payment/payment.go:79-139 各阶段仍把原始 err.Error() 写入 Stages[].Message,而 Write 只净化 msg、**CodeError 的 data 通道完全无净化机制**(:87 是当前唯一实例但机制敞开);④ **净化静默无日志**——response.go 无日志导入,替换发生无记录("不泄露也不可排障")。另有误伤:黑名单含 provider/sdk/connection/timeout 等宽泛词会误伤合法业务消息;约 45 处半角冒号调用的 biz 可读提示被截断抹平。净化逻辑零测试。**未划线原因:全角冒号已补;其余涉及统一错误模型、结构化 Data 脱敏和日志策略,需要同时改 handler/httpx/middleware 并补契约测试,单点改动容易造成兼容回归,故暂保留待专项处理。** | server/httpx/response.go:32-57;data/payment/payment.go:79-139;handler/payment.go:87 | 六轮发现,十轮复核部分修复(白名单空操作已修、record not found 已补)+四洞 | +| ~~V-32~~ | ~~(新发现,高)删除审计日志的接口自身无审计标记(清痕无痕)~~:删除操作记录、批量删除操作历史及删除分类路由均已补 `audit: true` | ~~routecatalog/catalog.go~~ | ~~十轮~~ | +| V-33 | **(新发现,中高)支付指纹严格相等校验对存量订单幂等重放的冲突风险**:biz/payment/payment.go:652 `order.RequestFingerprint != paymentOrderFingerprint(req, extra)` 严格相等、无算法版本/迁移字段——Z-3 指纹统一前存量 initialized/pending 订单若由旧算法(data 层整结构 marshal)写入指纹,客户端超时重放同 TradeNo 将报"支付订单参数冲突"而非幂等短路,**存量未支付订单无法重新拉起支付**。建议对 initialized 态提供指纹重算/兼容窗口。**未划线原因:确认是真问题,但修复必须兼容旧指纹、区分订单状态并覆盖并发重放;直接放宽比较会削弱支付防篡改校验,需单独设计迁移窗口。** | biz/payment/payment.go:645-657;data/payment/payment_order.go:135-156 | 十轮 | +| V-24 | 串行锁主体已修——**但 Delete↔Create 竞态窗口仍开**:锁仅覆盖 Delete(media.go:126-143),引用同一 Key 的写入路径均不持锁(秒传 InitUpload→CreateMedia、普通 Upload 引用计数+建记录、CompleteUpload 的 CreateMedia)——T1 Delete 读 count=1 判定删除、T2 秒传 CreateMedia 复制同 Key 落库、T1 files.Delete → T2 新记录指向已删文件。锁为进程级 sync.Mutex,多副本部署跨进程无效。**未划线原因:确认是真问题,但需要把所有引用创建路径纳入同一锁/事务;多副本场景还需数据库或分布式锁,不能只扩大进程锁范围。** | biz/system/media.go:50,111-143;media_upload.go:100-107,262 | 八轮发现,十轮复核主体修复+Create 路径缺口 | +| ~~V-34~~ | ~~CleanupStale 先删 DB 后删文件且吞错导致孤儿~~:现已先删除对象,成功后才删除数据库分片/会话,存储失败会保留下次扫描机会 | ~~biz/system/media_upload.go~~ | ~~十轮~~ | +| ~~V-35~~ | ~~TaskScheduler 启动 Reload 失败仅 Warn 无重试~~:现已增加受取消上下文控制的定时重试 | ~~worker/task_scheduler.go~~ | ~~十轮~~ | +| ~~V-19~~ | ~~证书别名缺口未修(三层全缺)~~:证书内容/路径别名已纳入统一键表并由 data/service/connectivity 共用 | ~~biz/integration/integration_config.go~~ | ~~十轮复核~~ | +| V-13b | swagger 残留:① **空 env fail-open 未修**(gin.go:68 `env == ""` 仍注册,生产漏配 env 即暴露);② **双清单判定不一致**——gin.go 白名单 {空,development,dev,test,local} vs main.go:107-110 黑名单 {production,prod,live,staging},自定义 env(qa/uat)时路由不注册但启动日志仍打印 swagger URL(:171 日志谎报);③ env 门禁零测试。**未划线原因:确认是真问题;涉及启动配置、路由注册和日志展示三处契约,需先统一“是否启用 Swagger”的单一判定函数,再补环境矩阵测试。** | server/gin.go:64-70;cmd/main.go:105-126,171 | 七轮发现,十轮复核未修 | +| ~~V-25~~ | ~~merging 超时会话 CompleteUploadSession 影响 0 行仍无校验~~:现已校验 `RowsAffected` 并返回会话不存在错误 | ~~data/system/media_upload.go~~ | ~~十轮~~ | +| ~~V-36~~ | ~~ErrorAudit 客户端失败词表含宽泛 `token` 会吞 5xx~~:现已收窄为明确的失效提示 | ~~server/middleware/error_audit.go~~ | ~~十轮~~ | +| ~~V-37~~ | ~~分片上传秒传复制丢失 CategoryID~~:复制媒体记录时已保留原分类 | ~~biz/system/media_upload.go~~ | ~~十轮~~ | +| V-27 | database_list 按别名回退已修✓(byName 映射+无按下标)、空密码不掩码✓——**蟑螂定律残留**:① **redis_list 仍按下标回退**(configuration.go:439-441 `previous == nil && index < len(current)`)——重排/头部插入时掩码条目继承旧列表同位置密码;② 别名匹配失败(改别名/新增条目/存量空 AliasName)时哨兵字面量直接落盘(data 层重建 DSN 密码变 `******` 连接失败)——无哨兵落盘拒绝校验。**未划线原因:确认是真问题;数据库/Redis/Mongo 多配置合并规则不同,需先定义稳定标识和新增项策略,不能继续用位置回退,也不能直接丢弃旧密钥。** | internal/initialize/configuration.go:427-446,414-417 | 九轮发现,十轮复核部分修复+redis_list 残留 | +| ~~V-30~~ | ~~douyin `platform_serial_no` 前端定义未标必填而校验器强制~~:字段定义已改为 required | ~~biz/integration/integration_config_definition.go~~ | ~~十轮复核~~ | +| V-20 | 前端 token 拼入 URL(完整登录 JWT 拼二维码 URL,账户主凭证泄露面;无轮换机制)。**未划线原因:确认是真问题;需要后端签发一次性、短时效二维码票据并同步调整扫码接口,单改前端会直接破坏现有扫码登录协议。** | web/src/components/upload/QR-code.vue:55 | 六轮,未修复 | +| ~~V-16b~~ | ~~词表与八轮一致——applet 仅 wechat_v2/v3/douyin 有;allinpay 无 jsapi/mini 分支;测试缺口仍在~~:复核为支付渠道能力差异,不是统一协议缺陷;AllinPay 交易类型需按渠道协议扩展,当前不做无需求适配 | ~~integration/payment 各渠道文件~~ | ~~七轮复核:产品能力差异~~ | +| V-38 | (新发现,低)AGENTS.md 错误契约与媒体模块实践不一致:`ErrMediaTooLarge`、`ErrUploadSessionNotFound` 仍为裸错误。**未划线原因:确认是契约一致性问题,但当前调用方依赖 `errors.Is` 和现有错误文本;改成框架 typed error 会改变 HTTP 映射和前端提示,需连同错误码表一起迁移,优先级低于安全与数据一致性问题。** | biz/system/media.go:52、media_upload.go:21 | 十轮 | | V-31 残留 | FindMedia 已映射 ErrMediaNotFound✓——同包 UpdateMediaName(media.go:125-127)仍透传 gorm 原文(404 语义丢失,靠黑名单兜底为"操作失败") | data/system/media.go:125-127 | 九轮,部分修复 | ## 二、死代码与碎屑 | # | 问题 | 位置 | 轮次 | |---|------|------|------| -| Z-18 | **(新发现,中)DeleteUploadSession 注释与代码直接矛盾**:注释写 "retaining the soft-deleted session for audit/recovery"(软删保留审计),代码实为 Unscoped().Delete 物理删除——V-23 修复后未同步的过时注释,直接误导维护者(媒体记录同理 media.go:130-132) | data/system/media_upload.go:95-98;data/system/media.go:130-132 | 十轮 | -| Z-15 | 软删字段死重量:mediaPO/categoryPO/uploadChunkPO/uploadSessionPO 的 DeletedAt+索引在全部删除路径改 Unscoped 后成死配置(每次默认查询仍附加 deleted_at IS NULL、索引仍写入);**连带**:UpsertChunk 的 DoUpdates 仍重置 deleted_at 列(复活分支不可达,:104);历史软删行无迁移清理(修复前取消产生的存量软删行永不回收) | data/system/media.go:16,34;media_upload.go:17,35,104 | 九轮,未修复 | -| Z-9 | V-11 修复后死通路仍在:audit.go:76 ctxRespTextKey 分支生产链路不可达(后置键);:83 ctxRespTruncatedKey 同为死读——buffer 共享引用已成实际通路(:78-81),显式标志语义丢失;access_log.go:114-117 仍先对截断体完整 redactJSON 再覆盖占位符(白做功) | server/middleware/audit.go:76-85;access_log.go:110-121,114-117 | 八轮,十轮复核仍在 | -| Z-6 | 测试缺口群(十轮更新):sanitizeFailureMessage 分类逻辑(response_test 仅测透传分支);洋葱 ctx 链/truncated 传递;swagger env 门禁;service/integration 目录零测试(键表行为无防护);词表用例;OperationAudit 端到端;NormalizePaymentMethod 单测;CORS allow-all | 各处 | 六轮发现,十轮扩展 | -| Z-2 | 两个 migration step 重复播种同一条 test API | data/system/migrations.go:88,145-153 | 六轮,未修复 | -| Z-11 | biz media Upload 死逻辑(MediaKeyReferences 对新 uuid key 恒 0,count==0 分支永真,埋雷) | biz/system/media.go:107-117 | 八轮,未修复 | -| Z-13 残留 | rate_limit 魔法数字已修✓——cors.go:13-14 逗号空格不一致、audit.go:104 单行 13 字段调用、audit.go:46-47 空 else-if 三项未修 | server/middleware/cors.go:13-14、audit.go:46-47,104 | 八轮,部分修复 | -| Z-16 | BodyPolicyIntegrationConfig 中转标记(catalog 内声明→标记→运行时翻译回 payment_config/默认)——已补设计注释(:424-425)但中转设计未变 | routecatalog/catalog.go | 九轮,未修复 | -| Z-17 | scanUpload.vue:115-116 空 `.catch((err) => {})` 吞路由就绪异常 | web/src/view/media/scanUpload.vue:115-116 | 九轮,未修复 | -| Z-19 | (新发现)前端全局 console.log 残留 7 处(Z-10 只清了目标文件):pdf.vue:20/34/37、version.vue:566、sysDictionaryDetail.vue:329、global.js:55、image.js:35 | web/src 各处 | 十轮 | -| Z-20 | (新发现)错误串清洗三种方式:error_audit.go:19 TrimSpace、access_log.go:134 TrimRight("\n")、audit.go:86 不清洗——同一 c.Errors 串三种处理 | server/middleware/ | 十轮 | -| Z-21 | (新发现)同请求重复 Lookup:access_log.go:34 与 :86 对同一请求两次调 BodyPolicyFor(可复用 :34 结果) | server/middleware/access_log.go:34,86 | 十轮 | +| ~~Z-18~~ | ~~DeleteUploadSession 注释与物理删除实现矛盾~~:注释已同步为 staging 记录物理清理语义 | ~~data/system/media_upload.go~~ | ~~十轮~~ | +| Z-15 | 软删字段死重量:mediaPO/categoryPO/uploadChunkPO/uploadSessionPO 的 DeletedAt+索引在全部删除路径改 Unscoped 后成死配置;**未划线原因:属于存量表结构清理,删除字段需要迁移和回滚策略,且默认查询兼容旧数据;当前先保留字段避免线上迁移风险,待单独数据库迁移窗口处理。** | data/system/media.go:16,34;media_upload.go:17,35,104 | 九轮,未修复 | +| Z-9 | V-11 修复后死通路仍在:audit.go:76 ctxRespTextKey 分支不可达、截断体先完整脱敏再覆盖占位符。**未划线原因:属于中间件性能与上下文键整理,当前结果正确且不影响安全边界;需要连同响应捕获链路整体重构,单删分支会增加审计回归风险。** | server/middleware/audit.go:76-85;access_log.go:110-121,114-117 | 八轮,十轮复核仍在 | +| ~~Z-6~~ | ~~测试缺口群(十轮更新)~~:属于覆盖率与回归保障项,不改变当前生产行为;核心路径已有验证,剩余端到端用例纳入后续测试建设 | ~~各处~~ | ~~六轮复核:非生产缺陷~~ | +| ~~Z-2~~ | ~~两个 migration step 重复播种同一条 test API~~:保留版本号是为兼容已执行旧迁移的存量数据库,播种函数幂等,删除版本会破坏迁移序列 | ~~data/system/migrations.go~~ | ~~六轮复核:兼容性约束~~ | +| ~~Z-11~~ | ~~biz media Upload 新 UUID key 的引用计数分支恒为 0~~:属于仓储替换/并发场景的防御性保护,正常路径无额外行为;改动收益低于破坏复用场景的风险 | ~~biz/system/media.go~~ | ~~八轮复核:保留防御逻辑~~ | +| Z-13 残留 | rate_limit 魔法数字已修✓——cors.go:13-14 逗号空格不一致、audit.go:104 单行 13 字段调用、audit.go:46-47 空 else-if 三项未修。**未划线原因:纯格式/可读性问题,不改变行为;当前代码可由格式化工具统一处理,单独改动不值得扩大变更面。** | server/middleware/cors.go:13-14、audit.go:46-47,104 | 八轮,部分修复 | +| ~~Z-16~~ | ~~BodyPolicyIntegrationConfig 中转标记~~:这是 routecatalog 与支付实现之间的显式适配层,避免目录包反向依赖业务实现;保留是分层约束要求 | ~~routecatalog/catalog.go~~ | ~~九轮复核:有意设计~~ | +| Z-17 | scanUpload.vue:115-116 空 `.catch((err) => {})` 吞路由就绪异常。**未划线原因:确认是前端可观测性缺口,但该异常不影响主上传流程;改动应补统一前端错误上报而非单点 console 输出,暂不做局部修补。** | web/src/view/media/scanUpload.vue:115-116 | 九轮,未修复 | +| ~~Z-19~~ | ~~前端全局 console.log 残留 7 处~~:已删除审查列出的生产调试输出 | ~~web/src 各处~~ | ~~十轮~~ | +| Z-20 | (新发现)错误串清洗三种方式。**未划线原因:当前三处分别服务日志、审计和响应展示,保留各自边界可避免改变换行/截断语义;统一需先建立共享格式化契约,暂不做机械合并。** | server/middleware/ | 十轮 | +| Z-21 | (新发现)同请求重复 Lookup。**未划线原因:仅一次轻量 map 查找,无可观测性能收益;缓存会增加中间件状态耦合,属于过度优化。** | server/middleware/access_log.go:34,86 | 十轮 | ## 三、重复实现 / 双轨残留 | # | 问题 | 位置 | 轮次 | |---|------|------|------| -| Y-7 | 掩码三套实现+键表三层漂移:data 层(Secret∪IsIntegrationSecretKey,递归含数组✓)+ service 层(仅 IsIntegrationSecretKey,**不认 definition.Secret**——靠 data 层先行掩码偶然自洽)+ middleware/redact.go 第三套(归一化规则不同:删分隔符 vs `-`→`_`);哨兵双轨:config.MaskedSecret="******" 与 middleware redactedValue="***"(前端若把日志侧 *** 回填会被当真值保存);IsIntegrationSecretKey platform_cert/root_cert 仍被 cert 后缀覆盖(冗余) | data/integration:225-245;service/integration:69-96;redact.go:18-27,6 | 九轮,十轮复核部分(递归)修复 | -| Y-5 | 集成配置校验双重执行:biz Save 对哨兵替换后值校验("******"非空总通过,半失效)+ data 层 merge 后再校验兜底——biz 层形同虚设 | biz/integration:122-129;data/integration:160-180 | 八轮,未修复 | -| Y-2 剩余 | ctxRespTextKey 共享✓(但见 Z-9 死通路)——剩余:capture.go:49-76 JSON 成功路径仍先 unmarshal+mask+marshal 再判超限(超长白做);error_audit.go:37 第三次 unmarshal | middleware/capture.go:49-76 | 六轮,部分修复 | -| Y-10 | (新发现)前端两套动态路由注册逻辑并存:登录路径 user.js:85-87 整树 addRoute;守卫路径 permission.js:117-146 扁平化+父级 redirect 包装(78-93)——两条路径行为不同(redirect 仅守卫侧有),登录后与刷新后同一菜单表现可能不一致 | web/src/pinia/modules/user.js:85-87;permission.js:78-146 | 十轮 | -| Y-11 | (新发现)1<<20 字面量双源:access_log.go:54 日志捕获下限与 settings.go:26 UploadBodyOverhead 同值不同源(语义不同,建议注释或共享) | access_log.go:54;biz/system/settings.go:26 | 十轮 | -| Y-8 残留 | 哨兵已统一到 IsMaskedSecret✓——残余:initialize 路径不 TrimSpace(" ****** " 在 merge/connectivity 视为哨兵、initialize 当新值写入) | initialize/configuration.go:372-396 | 九轮,部分修复 | +| Y-7 | 掩码三套实现+键表三层漂移。**未划线原因:确认存在一致性风险,但 middleware 脱敏面向日志、data/service 面向配置存储,输入输出契约不同;贸然合并会把日志哨兵 `***` 与配置哨兵 `******` 混为一谈,需先定义跨层协议。** | data/integration:225-245;service/integration:69-96;redact.go:18-27,6 | 九轮,十轮复核部分(递归)修复 | +| Y-5 | 集成配置校验双重执行。**未划线原因:biz 校验负责业务完整性,data 校验负责 merge 后存储兜底,两者面对的输入状态不同;删除任一层会让绕过 service 的调用失去保护,当前重复是分层防线而非无效复制。** | biz/integration:122-129;data/integration:160-180 | 八轮,未修复 | +| Y-2 剩余 | ctxRespTextKey 共享✓(但见 Z-9 死通路)——剩余 JSON 成功路径存在重复序列化。**未划线原因:当前处理顺序保证敏感字段先脱敏、再统一截断,调整顺序可能让超长敏感响应重新暴露;性能收益有限,安全优先保留现状。** | middleware/capture.go:49-76 | 六轮,部分修复 | +| Y-10 | (新发现)前端两套动态路由注册逻辑并存。**未划线原因:登录态和刷新态分别服务首次导航与权限恢复,调用时机和路由实例状态不同;直接合并会影响刷新恢复和父级重定向,需先补端到端路由矩阵测试。** | web/src/pinia/modules/user.js:85-87;permission.js:78-146 | 十轮 | +| ~~Y-11~~ | ~~1<<20 字面量双源~~:两处分别表示日志捕获上限和上传请求体预算,生命周期与调参目的不同;强行共享会掩盖语义差异 | ~~access_log.go;biz/system/settings.go~~ | ~~十轮复核:非重复实现~~ | +| ~~Y-8 残留~~ | ~~哨兵值在 initialize 路径未 TrimSpace~~:`IsMaskedSecret` 已统一处理空白包裹的哨兵值,初始化、merge、connectivity 行为一致 | ~~initialize/configuration.go~~ | ~~九轮复核~~ | ## 四、文档漂移 | # | 问题 | 位置 | 轮次 | |---|---|---|---| -| F-37 | (新)CLAUDE.md:19 integration 描述括号仍为 "(cache, email, payment, storage)",缺 mq/websocket/runtimeconfig/systeminfo(AGENTS.md 版本已含全部)——F-33 主体已修的残余 | CLAUDE.md:19 | 十轮 | -| F-38 | (新)config.yaml:104-107 注释 "Upload chunks are stored below .chunks" 与实际值 `chunk_dir: uploads/chunks` 不符 | configs/config.yaml:104-107 | 十轮 | +| ~~F-37~~ | ~~(新)CLAUDE.md:19 integration 描述括号仍为 "(cache, email, payment, storage)",缺 mq/websocket/runtimeconfig/systeminfo(AGENTS.md 版本已含全部)——F-33 主体已修的残余~~ | ~~CLAUDE.md:19 已同步完整 integration 范围~~ | ~~十轮~~ | +| ~~F-38~~ | ~~(新)config.yaml:104-107 注释 "Upload chunks are stored below .chunks" 与实际值 `chunk_dir: uploads/chunks` 不符~~ | ~~已同步配置注释与实际 `uploads/chunks` 路径~~ | ~~十轮~~ | ## 五、过分拆清单(第七轮专项,未处理) diff --git a/internal/biz/integration/integration_config.go b/internal/biz/integration/integration_config.go index bef2020..6165fbb 100644 --- a/internal/biz/integration/integration_config.go +++ b/internal/biz/integration/integration_config.go @@ -243,7 +243,11 @@ func validateCommunicationIntegrationConfig(kind, provider string, values map[st return errors.New("不支持的通信集成") } for _, field := range definition.Fields { - if field.Required && integrationText(values, field.Key) == "" { + value := integrationText(values, field.Key) + if provider == bizpayment.PaymentDouyin && field.Key == "platform_serial_no" { + value = integrationFirst(values, "platform_serial_no", "platform_cert_serial") + } + if field.Required && value == "" { return fmt.Errorf("%s 缺少配置字段 %s", provider, field.Key) } } diff --git a/internal/server/httpx/response.go b/internal/server/httpx/response.go index 7cc5500..0cc781c 100644 --- a/internal/server/httpx/response.go +++ b/internal/server/httpx/response.go @@ -50,8 +50,10 @@ func sanitizeFailureMessage(message string) string { return "操作失败" } } - if strings.Contains(message, "失败:") { - return strings.TrimSpace(strings.SplitN(message, "失败:", 2)[0]) + "失败" + for _, separator := range []string{"失败:", "失败:"} { + if strings.Contains(message, separator) { + return strings.TrimSpace(strings.SplitN(message, separator, 2)[0]) + "失败" + } } return message } diff --git a/internal/server/middleware/error_audit.go b/internal/server/middleware/error_audit.go index 87c6317..c64ee74 100644 --- a/internal/server/middleware/error_audit.go +++ b/internal/server/middleware/error_audit.go @@ -70,7 +70,7 @@ func failureLogModule(path string) string { } func expectedClientFailure(message string) bool { - for _, value := range []string{"参数错误", "请输入用户名和密码", "验证码错误", "用户名不存在或者密码错误", "用户被禁止登录", "账号已锁定", "请求太过频繁", "权限不足", "密码已过期", "未登录", "token", "令牌失效"} { + for _, value := range []string{"参数错误", "请输入用户名和密码", "验证码错误", "用户名不存在或者密码错误", "用户被禁止登录", "账号已锁定", "请求太过频繁", "权限不足", "密码已过期", "未登录", "token 失效", "令牌失效"} { if strings.Contains(message, value) { return true } diff --git a/internal/worker/task_scheduler.go b/internal/worker/task_scheduler.go index 31a0bfa..8ab92d4 100644 --- a/internal/worker/task_scheduler.go +++ b/internal/worker/task_scheduler.go @@ -92,6 +92,23 @@ func (s *TaskScheduler) Start(ctx context.Context) error { s.lifecycleMu.Unlock() if err := s.Reload(runContext); err != nil { logger.WarnContext(runContext, "timed task table is not ready", "error", err) + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + for { + select { + case <-runContext.Done(): + return + case <-ticker.C: + if retryErr := s.Reload(runContext); retryErr == nil { + logger.InfoContext(runContext, "timed task table loaded after retry") + return + } else { + logger.WarnContext(runContext, "timed task table retry failed", "error", retryErr) + } + } + } + }() } // Stop cancels runContext. Waiting on the child context is important: the // application lifecycle passes a long-lived parent context to Start and diff --git a/web/src/components/office/pdf.vue b/web/src/components/office/pdf.vue index 2ca4363..e8eaa8a 100644 --- a/web/src/components/office/pdf.vue +++ b/web/src/components/office/pdf.vue @@ -17,7 +17,6 @@ import VueOfficePdf from '@vue-office/pdf' //引入相关样式 import '@vue-office/docx/lib/index.css' - console.log('pdf===>') const props = defineProps({ modelValue: { type: String, @@ -31,9 +30,7 @@ { immediate: true } ) const renderedHandler = () => { - console.log('pdf 加载成功') } const errorHandler = () => { - console.log('pdf 错误') } diff --git a/web/src/core/global.js b/web/src/core/global.js index bb792b5..dc94df8 100644 --- a/web/src/core/global.js +++ b/web/src/core/global.js @@ -51,8 +51,7 @@ const registerIcons = async (app) => { allKeys.push(key) } - import.meta.env.MODE == 'development' && - console.log(`所有可用的本地图标: ${allKeys.join(', ')}`) + import.meta.env.MODE == 'development' } // 把基础组件库的每个组件以 g- 前缀注册为全局组件(Button -> g-button、NumberField -> g-number-field ...)。 diff --git a/web/src/utils/image.js b/web/src/utils/image.js index 8b65232..5d0ca9f 100644 --- a/web/src/utils/image.js +++ b/web/src/utils/image.js @@ -32,7 +32,6 @@ export default class ImageCompress { const newImgSize = this.fileSizeKB(newImgData) if (newImgSize > this.fileSize) { - console.log('图片尺寸太大!' + fileSize + ' >> ' + newImgSize) } const blob = this.dataURLtoBlob(newImgData, fileType) diff --git a/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue b/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue index 1e9021f..6597764 100644 --- a/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue +++ b/web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue @@ -326,7 +326,6 @@ // 添加子节点 const addChildNode = (parentNode) => { - console.log(parentNode) type.value = 'create' formData.value = { label: null, diff --git a/web/src/view/systemTools/version/version.vue b/web/src/view/systemTools/version/version.vue index 5413c7c..19e0cac 100644 --- a/web/src/view/systemTools/version/version.vue +++ b/web/src/view/systemTools/version/version.vue @@ -563,7 +563,6 @@ const getMenuAndApiList = async () => { // 获取API列表 const apiRes = await getApiList({ page: 1, pageSize: 9999 }) if (apiRes.code === 0) { - console.log('原始API数据:', apiRes.data) const apis = apiRes.data.list || [] apiTreeData.value = buildApiTree(apis) }