# 项目质量规范 (Production Quality Standards) **版本**: 1.0 **更新日期**: 2026-04-03 **适用范围**: D:\project (Go + React/TypeScript) --- ## 一、安全规范 (Security) ### 1.1 加密与随机数 ```go // ✅ 正确:随机数生成失败时返回错误 func generateSecureToken(length int) (string, error) { bytes := make([]byte, length) if _, err := rand.Read(bytes); err != nil { return "", fmt.Errorf("failed to generate secure token: %w", err) } return base64.URLEncoding.EncodeToString(bytes)[:length], nil } // ❌ 禁止:使用不安全 fallback func generateSecureToken(length int) string { // ... if _, err := rand.Read(bytes); err != nil { // 禁止使用时间戳或 math/rand 作为 fallback for i := range bytes { bytes[i] = byte(time.Now().UnixNano() % 256) // 不安全! } } // ... } ``` ### 1.2 敏感数据存储 ```typescript // ✅ 正确:敏感数据使用内存存储 let deviceFingerprintCache: DeviceFingerprint | null = null export function getDeviceFingerprint(): DeviceFingerprint { if (cachedFingerprint) return cachedFingerprint cachedFingerprint = buildFingerprint() return cachedFingerprint } // ❌ 禁止:敏感数据存入 localStorage/sessionStorage localStorage.setItem('device_id', deviceId) // XSS 可读取 localStorage.setItem('token', token) // XSS 可读取 ``` ### 1.3 认证与授权 ```go // ✅ 正确:所有受保护路由使用中间件 adminRoutes.Use(AuthMiddleware.Required()) adminRoutes.Use(AdminOnly()) // ❌ 禁止:硬编码权限检查 if user.Role != "admin" { c.JSON(403, "forbidden") // 分散的权限检查 } ``` ### 1.4 SQL 注入防护 ```go // ✅ 正确:使用参数化查询 db.Where("user_id = ?", userID) db.Where("name LIKE ?", "%"+EscapeLikeWildcard(name)+"%") // ❌ 禁止:字符串拼接 SQL db.Where("user_id = " + userID) // SQL 注入风险 ``` ### 1.5 错误信息泄露 ```go // ✅ 正确:分类错误,不返回原始错误 response.Error(c, http.StatusInternalServerError, "服务器内部错误") // ❌ 禁止:返回原始错误信息给客户端 c.JSON(500, gin.H{"error": err.Error()}) // 可能泄露内部信息 ``` --- ## 二、并发与性能 (Concurrency & Performance) ### 2.1 Goroutine 管理 ```go // ✅ 正确:使用 context 控制生命周期 go func() { select { case <-ctx.Done(): return case <-ticker.C: cleanup() } }() // ❌ 禁止:fire-and-forget goroutine go publishEvent(ctx, event, data) // 无限制的 goroutine ``` ### 2.2 Map 并发访问 ```go // ✅ 正确:使用互斥锁保护共享 map type SSOManager struct { mu sync.RWMutex sessions map[string]*SSOSession } func (m *SSOManager) Get(key string) *SSOSession { m.mu.RLock() defer m.mu.RUnlock() return m.sessions[key] } // ❌ 禁止:map 并发读写 sessions[key] = session // concurrent map write ``` ### 2.3 数据库查询 ```go // ✅ 正确:使用 JOIN 替代 N+1 查询 func GetUserRolesAndPermissions(ctx, userID) ([]*Role, []*Permission, error) { // 单次 JOIN 查询 rows := db.Raw(`SELECT ... FROM user_roles ur JOIN roles r ON ur.role_id = r.id LEFT JOIN role_permissions rp ON r.id = rp.role_id LEFT JOIN permissions p ON rp.permission_id = p.id WHERE ur.user_id = ?`, userID) } // ❌ 禁止:循环内单独查询(N+1) for _, roleID := range roleIDs { ancestors := repo.GetAncestorIDs(ctx, roleID) // 每 role 执行一次查询 } ``` ### 2.4 导出与批处理 ```go // ✅ 正确:分批处理 + 最大限制 const MaxExportRecords = 100000 const BatchSize = 5000 for { batch, hasMore, err := repo.ListBatch(ctx, cursor, BatchSize) if total >= MaxExportRecords { break // 防止 OOM } // 处理 batch... } // ❌ 禁止:无限制加载全表到内存 allRecords := repo.ListAll(ctx) // 百万级记录 OOM ``` --- ## 三、API 设计规范 (API Design) ### 3.1 响应格式 ```go // ✅ 正确:统一包装响应 type APIResponse struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } // 成功响应 response.Success(c, data) // {code: 0, message: "success", data: {...}} response.Paginated(c, items, total, page, pageSize) // ❌ 禁止:裸 JSON 响应 c.JSON(200, gin.H{"users": users}) // 无统一格式 ``` ### 3.2 错误处理 ```go // ✅ 正确:使用标准错误响应 response.BadRequest(c, "无效的请求参数") response.Unauthorized(c, "认证已过期,请重新登录") response.Forbidden(c, "权限不足") response.NotFound(c, "用户不存在") response.InternalError(c, "服务器内部错误") // ❌ 禁止:直接返回错误字符串 c.JSON(400, gin.H{"error": "bad request"}) ``` ### 3.3 分页参数 ``` // ✅ 统一分页格式 GET /users?page=1&page_size=20 // 响应 { "code": 0, "message": "success", "data": { "items": [...], "total": 100, "page": 1, "page_size": 20, "pages": 5 } } ``` --- ## 四、代码风格规范 (Code Style) ### 4.1 错误处理原则 ```go // ✅ 正确:明确处理错误 if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, fmt.Errorf("query failed: %w", err) } // ❌ 禁止:忽略错误 data, _ := json.Marshal(v) // 忽略 marshal 错误 ``` ### 4.2 Context 使用 ```go // ✅ 正确:使用请求 context 或带超时的 context func HandleRequest(c *gin.Context) { ctx := c.Request.Context() // 或 ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second) defer cancel() } // ❌ 禁止:使用 context.Background() go func() { doSomething(context.Background()) // 生命周期不关联 }() ``` ### 4.3 前端 TypeScript ```typescript // ✅ 正确:完整的类型定义 interface User { id: number username: string email: string } // ❌ 禁止:滥用 any function processData(data: any): any { return data // 类型安全丧失 } // ✅ 正确:useMemo 缓存 expensive 计算 const columns = useMemo(() => [ { key: 'name', dataIndex: 'name' }, // ... ], [dependencies]) // ❌ 禁止:每次渲染重新创建 const columns = [ // 每次渲染创建新数组 { key: 'name', dataIndex: 'name' }, ] ``` --- ## 五、测试规范 (Testing) ### 5.1 单元测试 ```go // ✅ 正确:表驱动测试 + 完整断言 func TestLogin(t *testing.T) { tests := []struct { name string req LoginRequest wantErr bool }{ {"valid login", LoginRequest{Username: "test", Password: "pass"}, false}, {"invalid password", LoginRequest{Username: "test", Password: "wrong"}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := Login(tt.req) if (err != nil) != tt.wantErr { t.Errorf("Login() error = %v, wantErr %v", err, tt.wantErr) } }) } } ``` ### 5.2 集成测试 ```go // ✅ 正确:使用测试数据库,测试后清理 func TestUserCRUD(t *testing.T) { db := setupTestDB(t) defer db.Close() repo := NewUserRepository(db) user, err := repo.Create(ctx, &User{Username: "test"}) if err != nil { t.Fatalf("failed to create user: %v", err) } got, err := repo.GetByID(ctx, user.ID) if err != nil { t.Errorf("GetByID() error = %v", err) } if got.Username != user.Username { t.Errorf("GetByID() = %v, want %v", got.Username, user.Username) } } ``` --- ## 六、禁止模式 (Prohibited Patterns) ### 6.1 安全相关 | 禁止模式 | 风险 | 正确做法 | |---------|------|---------| | `localStorage.setItem('token', token)` | XSS 读取 | 内存存储或 HttpOnly Cookie | | `crypto/rand` 失败 fallback 到时间戳 | Token 可预测 | 返回错误 | | `c.JSON(500, gin.H{"error": err})` | 内部信息泄露 | 统一错误响应 | | 用户输入拼接 SQL | SQL 注入 | 参数化查询 | | 重定向未验证 URL | Open Redirect | 白名单验证 | ### 6.2 性能相关 | 禁止模式 | 风险 | 正确做法 | |---------|------|---------| | `for { repo.Query() }` 循环内查询 | N+1 | JOIN 批量查询 | | `ListAll()` 全量加载 | OOM | 分批 + 最大限制 | | `context.Background()` 在 goroutine | 泄漏 | 带超时的 context | | 共享 map 无锁保护 | panic | `sync.RWMutex` | ### 6.3 代码质量 | 禁止模式 | 风险 | 正确做法 | |---------|------|---------| | `data as SomeType` 类型断言 | 运行时 panic | 类型守卫检查 | | 魔法数字 | 可读性差 | 定义常量 | | 重复代码 > 3 处 | 维护性差 | 提取函数/模块 | | 过长函数 > 100 行 | 可读性差 | 拆分为小函数 | --- ## 七、审查清单 (Review Checklist) ### 提交前必须检查 - [ ] `go vet ./...` 无警告 - [ ] `go build ./...` 编译通过 - [ ] `npm run build` 前端编译通过 - [ ] `npm run lint` 无 error(warning 可接受) - [ ] 无 `TODO: fixme` 或 ` FIXME` 未处理 - [ ] 无硬编码密码/密钥/Secret - [ ] 无 `console.log` 生产代码 - [ ] 新增 handler 使用 `response.Success()` 而非裸 `c.JSON` - [ ] 敏感数据不写入 localStorage/sessionStorage - [ ] 异步操作有超时控制 ### 安全专项检查 - [ ] 新增 API 有权限控制 - [ ] 用户输入有验证 - [ ] SQL 使用参数化查询 - [ ] 错误不泄露内部信息 - [ ] Token 使用 crypto/rand 生成 --- ## 八、持续改进 - 每季度进行一次完整的安全审计 - 发现新的反模式及时加入禁止列表 - 定期更新依赖版本(安全补丁) - 代码覆盖率目标:核心业务 > 80% --- ## 附录:已有安全实践 - ✅ Argon2id 密码哈希 - ✅ JWT JTI 黑名单 - ✅ TOTP 两步验证 - ✅ CSRF Token 保护 - ✅ XSS window guard - ✅ SSRF URL 验证 - ✅ 参数化查询防注入