From cd30af5bd6982648cb2dc6d8827278aec8678bc5 Mon Sep 17 00:00:00 2001 From: attacker Date: Mon, 6 Jul 2026 07:16:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(quality):=20Q5=20=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E6=B2=BB=E7=90=86=E6=89=A9?= =?UTF-8?q?=E5=B1=95=20=E2=80=94=20RDP/API=20Token/AIOps=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=208=20=E5=A4=84=20=5F=20=3D=20DB=20=E5=86=99=E5=85=A5?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20log.Printf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Q4 修复了 controller/ 层 8 处 DB 写入的 _ = 静默忽略,但后续合并的 RDP 远程桌面、API Token、AIOps 告警分析等模块引入了新的 _ = DB 写入忽略。 本次将 controller/middleware 层所有 DB 写入操作(Create/Update/Updates) 的 _ = 静默忽略改为带 log.Printf 错误日志(不阻断业务流程)。 修改文件 (6): - middleware/auth.go: API 密钥最后使用时间更新 - controller/terminal_session.go: 终端回放审计日志 + 修复 log 变量遮蔽 - controller/user_api_token.go: API 密钥默认值补全 - controller/rdp.go: RDP 会话结束状态 + 命令记录 - controller/rdp_session.go: 会话终止 + 回放审计 + 修复 log 变量遮蔽 - controller/aiops_alert_analysis.go: AI 告警分析结果保存 修复 2 处 Go 变量名遮蔽陷阱(log := model.AuditLog{} 遮蔽 "log" 包 → auditLog) 保留 4 处可接受忽略(1 DB 读取 + 3 WebSocket 帧写入) 验证: go build=0, 17/17 集成测试通过, Docker healthy --- PROJECT_EVALUATION.md | 2 +- ROADMAP.md | 31 ++++++++++++++++++++++ backend/controller/aiops_alert_analysis.go | 5 +++- backend/controller/rdp.go | 8 ++++-- backend/controller/rdp_session.go | 11 +++++--- backend/controller/terminal_session.go | 7 +++-- backend/controller/user_api_token.go | 5 +++- backend/middleware/auth.go | 5 +++- 8 files changed, 63 insertions(+), 11 deletions(-) diff --git a/PROJECT_EVALUATION.md b/PROJECT_EVALUATION.md index 3fb46c6..db900cb 100644 --- a/PROJECT_EVALUATION.md +++ b/PROJECT_EVALUATION.md @@ -137,7 +137,7 @@ | 前端未使用变量/导入(ESLint no-unused-vars) | ~~218 处~~ → **0 处** | ~~P1~~ ✅ done | | 前端 console.log/console.error | ~~93 处~~ → **3 处** | ~~P1~~ ✅ done | | 前端 React Hooks 关键规则(exhaustive-deps/set-state-in-effect/purity/immutability 等 6 大规则) | ~~82 处回归~~ → **0 处**(U12 修复 i18n 全量改造引入的回归) | ~~P1~~ ✅ done (U12) | -| 后端 `_ =` 忽略错误 | 13 处(3 处中风险) | P2 | +| 后端 `_ =` 忽略错误 | ~~13 处(3 处中风险)~~ → **controller/middleware 层 DB 写入零忽略**(Q4+Q5 全量治理) | ~~P2~~ ✅ done | | 前端硬编码内网 IP(10.10.10.75 等) | 6+ 处 | P2 | | 后端告警引擎分布式安全 | ✅ Redis 分布式锁(SetNX + 60s TTL) | 后端 | ~~P2~~ done | diff --git a/ROADMAP.md b/ROADMAP.md index f091082..8f0ae77 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -474,6 +474,7 @@ services/api.ts (1324L) | M15 | 中间件监控 (Redis/MySQL) | P1 | **done** | | U5 | Kubernetes 模块国际化 | P3 | **done** | | U12 | 工程质量回归修复(ESLint errors + React Hooks 关键规则) | P3 | **done** | +| Q4+ | 后端错误处理治理扩展(Q5: RDP/API Token/AIOps 模块 `_ =` DB 写入) | P0 | **done** | --- @@ -514,6 +515,36 @@ services/api.ts (1324L) --- +## Q4+: 后端错误处理治理扩展(Q5) [done] + +**问题**: Q4 修复了 controller/ 层 8 处 DB 写入的 `_ =` 静默忽略,但后续合并的 RDP 远程桌面(`e0d652d`)、API Token(`76ec938`)、AIOps 告警分析(`a9513c2`)等模块引入了新的 `_ =` DB 写入忽略,共计 8 处。 + +**已完成 (2026-07-06)**: + +| 文件 | 位置 | 操作 | 修复 | +|------|------|------|------| +| `middleware/auth.go:125` | API 密钥认证 | `Update("api_access_key_last_used_at")` | + `log.Printf("[Auth]")` + 新增 `"log"` import | +| `controller/terminal_session.go:279` | 终端回放审计 | `Create(&log)` | 重命名 `log`→`auditLog`(变量遮蔽修复)+ `log.Printf("[TerminalAudit]")` + 新增 `"log"` import | +| `controller/user_api_token.go:139` | API 密钥默认值补全 | `Updates(patches)` | + `log.Printf("[UserApiKey]")` + 新增 `"log"` import | +| `controller/rdp.go:269` | RDP 会话结束 | `Updates(updates)` | + `log.Printf("[RDP]")` | +| `controller/rdp.go:531` | RDP 命令记录 | `Create(&cmd)` | + `log.Printf("[RDPAudit]")` | +| `controller/rdp_session.go:130` | RDP 会话终止 | `Updates(map)` | + `log.Printf("[RDPSession]")` + 新增 `"log"` import | +| `controller/rdp_session.go:175` | RDP 回放审计 | `Create(&log)` | 重命名 `log`→`auditLog`(变量遮蔽修复)+ `log.Printf("[RDPAudit]")` | +| `controller/aiops_alert_analysis.go:401` | AI 告警分析结果 | `Create(&row)` | + `log.Printf("[AIOpsAnalysis]")` + 新增 `"log"` import | + +**保留的可接受忽略模式**(4 处,不影响 DB 写入): +- `controller/cron_job_run.go:44` — `.First()` DB **读取**(非写入) +- `pkg/rdp/session_engine.go:97,190,210` — `sink.SendFrame()` **WebSocket 帧写入**(客户端断开时正常) + +**验收标准**: +- [x] controller/ + middleware/ 层所有 DB 写入操作(Create/Update/Updates)零 `_ =` 静默忽略 +- [x] 修复 2 处 Go 变量名遮蔽陷阱(`log := model.AuditLog{}` → `auditLog`) +- [x] `go build` 零错误 +- [x] 17/17 集成回归测试通过(零回归) +- [x] Docker 部署 healthy + +--- + ## 状态标记规则 ``` diff --git a/backend/controller/aiops_alert_analysis.go b/backend/controller/aiops_alert_analysis.go index c1c65fd..cc853aa 100644 --- a/backend/controller/aiops_alert_analysis.go +++ b/backend/controller/aiops_alert_analysis.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log" "net/http" "strconv" "strings" @@ -398,7 +399,9 @@ func (h *AIOpsAlertAnalysisHandler) saveAnalysisTrace(ctx context.Context, in an Duration: in.Duration, Error: in.Error, } - _ = h.db.WithContext(ctx).Create(&row).Error + if err := h.db.WithContext(ctx).Create(&row).Error; err != nil { + log.Printf("[AIOpsAnalysis] 保存告警分析结果失败 (alert_id=%s, provider=%s): %v", in.AlertID, in.Provider, err) + } } func marshalTraceJSON(v interface{}) string { diff --git a/backend/controller/rdp.go b/backend/controller/rdp.go index b493793..b74a7f3 100644 --- a/backend/controller/rdp.go +++ b/backend/controller/rdp.go @@ -266,7 +266,9 @@ func (h *RDPHandler) Serve(c *gin.Context) { if errMsg != "" { updates["error"] = errMsg } - _ = h.db.Model(&model.RDPSession{}).Where("id = ?", session.ID).Updates(updates).Error + if err := h.db.Model(&model.RDPSession{}).Where("id = ?", session.ID).Updates(updates).Error; err != nil { + log.Printf("[RDP] 更新会话结束状态失败 (session_id=%d, status=%s): %v", session.ID, status, err) + } }() audit := newRDPOperationRecorder(h.db, session, userIDPtr, username) @@ -528,7 +530,9 @@ func (r *rdpOperationRecorder) create(command string) { Command: command, Timestamp: time.Now(), } - _ = r.db.Create(&cmd).Error + if err := r.db.Create(&cmd).Error; err != nil { + log.Printf("[RDPAudit] 记录 RDP 操作命令失败 (session_id=%d, user=%s, cmd=%q): %v", r.session.ID, r.username, command, err) + } } func (h *RDPHandler) loadRDPCredential(server model.Server) (model.RDPSecretContent, error) { diff --git a/backend/controller/rdp_session.go b/backend/controller/rdp_session.go index dac402e..8c1ff0d 100644 --- a/backend/controller/rdp_session.go +++ b/backend/controller/rdp_session.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "log" "net/http" "strconv" "strings" @@ -127,7 +128,9 @@ func (h *RDPSessionHandler) KillRDPSession(c *gin.Context) { return } duration := int(now.Sub(session.StartTime).Seconds()) - _ = h.db.Model(&session).Updates(map[string]interface{}{"status": "closed", "end_time": now, "duration": duration}).Error + if err := h.db.Model(&session).Updates(map[string]interface{}{"status": "closed", "end_time": now, "duration": duration}).Error; err != nil { + log.Printf("[RDPSession] 终止会话更新状态失败 (session_id=%d): %v", id, err) + } c.JSON(http.StatusOK, model.Response{Code: 200, Message: "已终止"}) } @@ -160,7 +163,7 @@ func (h *RDPSessionHandler) LogRDPReplayView(c *gin.Context) { return } uid := middleware.GetUserID(c) - log := model.AuditLog{ + auditLog := model.AuditLog{ UserID: &uid, Username: middleware.GetUsername(c), IP: c.ClientIP(), @@ -172,6 +175,8 @@ func (h *RDPSessionHandler) LogRDPReplayView(c *gin.Context) { Request: fmt.Sprintf("回放 Windows RDP 会话 #%d(%s → %s/%s)", id, session.Username, session.ServerName, session.ServerIP), Status: 200, } - _ = h.db.Create(&log).Error + if err := h.db.Create(&auditLog).Error; err != nil { + log.Printf("[RDPAudit] 记录回放审计日志失败 (session_id=%d, user=%s): %v", id, session.Username, err) + } c.JSON(http.StatusOK, model.Response{Code: 200, Message: "ok"}) } diff --git a/backend/controller/terminal_session.go b/backend/controller/terminal_session.go index af4cbb4..977db7f 100644 --- a/backend/controller/terminal_session.go +++ b/backend/controller/terminal_session.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "log" "net/http" "strconv" "strings" @@ -264,7 +265,7 @@ func (h *TerminalSessionHandler) LogReplayView(c *gin.Context) { return } uid := middleware.GetUserID(c) - log := model.AuditLog{ + auditLog := model.AuditLog{ UserID: &uid, Username: middleware.GetUsername(c), IP: c.ClientIP(), @@ -276,7 +277,9 @@ func (h *TerminalSessionHandler) LogReplayView(c *gin.Context) { Request: fmt.Sprintf("回放终端会话 #%d(%s → %s/%s)", id, session.Username, session.ServerName, session.ServerIP), Status: 200, } - _ = h.db.Create(&log).Error // 落库失败不阻断回放 + if err := h.db.Create(&auditLog).Error; err != nil { + log.Printf("[TerminalAudit] 记录回放审计日志失败 (session_id=%d, user=%s): %v", id, session.Username, err) + } c.JSON(http.StatusOK, model.Response{Code: 200, Message: "ok"}) } diff --git a/backend/controller/user_api_token.go b/backend/controller/user_api_token.go index cc2d341..602a66f 100644 --- a/backend/controller/user_api_token.go +++ b/backend/controller/user_api_token.go @@ -3,6 +3,7 @@ package controller import ( "crypto/rand" "fmt" + "log" "math/big" "net/http" "time" @@ -136,7 +137,9 @@ func (h *UserHandler) ensureAPIKeyMeta(user *model.User) { user.ApiAccessKeyDurationMonths = defaultAPIKeyMonths } if len(patches) > 0 { - _ = h.db.Model(&model.User{}).Where("id = ?", user.ID).Updates(patches).Error + if err := h.db.Model(&model.User{}).Where("id = ?", user.ID).Updates(patches).Error; err != nil { + log.Printf("[UserApiKey] 补全 API 密钥默认值失败 (user_id=%d): %v", user.ID, err) + } } } diff --git a/backend/middleware/auth.go b/backend/middleware/auth.go index 3ac4586..8bfb1ee 100644 --- a/backend/middleware/auth.go +++ b/backend/middleware/auth.go @@ -1,6 +1,7 @@ package middleware import ( + "log" "net/http" "strings" "time" @@ -122,7 +123,9 @@ func tryAPIKeyAuth(c *gin.Context, db *gorm.DB, tokenString string) bool { return false } now := time.Now() - _ = db.Model(&model.User{}).Where("id = ?", user.ID).Update("api_access_key_last_used_at", now).Error + if err := db.Model(&model.User{}).Where("id = ?", user.ID).Update("api_access_key_last_used_at", now).Error; err != nil { + log.Printf("[Auth] 更新 API 密钥最后使用时间失败 (user_id=%d): %v", user.ID, err) + } c.Set("userID", user.ID) c.Set("username", user.Username) c.Set("isAdmin", user.IsAdmin)