bookworm-smart-assistant/skills/project-audit-expert/references/code-review-patterns.md
Bookworm Admin b7a8e29d21 release: v6.7.0 - OTA E2E test release
- VERSION file as authoritative version source
- export.mjs reads VERSION with package.json fallback
- bw-ota.ps1 DryRun mode for safe testing
- auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
2026-04-27 17:59:44 +08:00

5.5 KiB
Raw Blame History

深度代码审查模式库

基于多个生产项目Next.js/FastAPI/Go 全栈)实战总结的常见问题与修复方案。

TypeScript/JavaScript 专项

类型安全 (来自实际项目教训)

// ============================================
// 问题 1: useState<any> 泛滥
// 来源: ai-stats/page.tsx 中发现 6 处 useState<any>
// ============================================

// ❌ 实际项目中的坏代码
const [data, setData] = useState<any>(null);
const [report, setReport] = useState<any>({});
// eslint-disable @typescript-eslint/no-explicit-any  // 更糟: 直接禁用规则

// ✅ 修复: 定义完整接口类型
interface AiStatsData {
  totalReplies: number;
  avgQualityScore: number;
  channels: ChannelStatusData[];
}
const [data, setData] = useState<AiStatsData | null>(null);

// ============================================
// 问题 2: JSON.parse 无类型验证
// 来源: auth-store.ts 中 atob(token) 后直接 parse
// ============================================

// ❌ 危险: 解析后未验证结构
const payload = JSON.parse(atob(parts[1]));
return payload.role; // 可能 undefined

// ✅ 使用 Zod 进行运行时验证
import { z } from 'zod';
const TokenPayload = z.object({
  sub: z.string(),
  role: z.enum(['admin', 'user']),
  exp: z.number(),
});
const result = TokenPayload.safeParse(JSON.parse(atob(parts[1])));
if (!result.success) throw new Error('Invalid token payload');

重复代码消除 (DRY)

// ============================================
// 问题 3: 工具函数在多处重复定义
// 来源: golden/page.tsx 和 customer/[id]/page.tsx 各自定义 formatRelativeTime
// ============================================

// ❌ 多个文件各自实现相同逻辑
// golden/page.tsx:  function formatRelativeTime(date) { ... }
// customer/page.tsx: function formatRelativeTime(date) { ... }

// ✅ 提取到共享模块
// lib/format.ts
export function formatRelativeTime(date: string | Date | null): string {
  if (!date) return '-';
  const d = new Date(date);
  if (isNaN(d.getTime())) return '-';
  // ... 统一实现
}

错误处理

// ============================================
// 问题 4: fetch 后未检查 response.ok
// 来源: mybioweb 项目 lib/api.ts
// ============================================

// ❌ 缺少响应状态检查
const data = await fetch(url);

// ✅ 完整错误处理链
async function request<T>(url: string, options?: RequestInit): Promise<T> {
  try {
    const response = await fetch(url, options);
    if (!response.ok) {
      const error = await response.json().catch(() => ({}));
      throw new ApiError(response.status, error.code, error.message);
    }
    return response.json();
  } catch (error) {
    if (error instanceof ApiError) throw error;
    throw new ApiError(0, 'NETWORK_ERROR', '网络错误');
  }
}

Python/FastAPI 专项

异常处理收窄

# ============================================
# 问题 5: except Exception 吞掉所有错误
# 来源: auto_delivery_service.py:184
# ============================================

# ❌ 过宽的异常捕获
try:
    result = process_delivery(data)
except Exception:
    return None  # 数据库错误也被静默吞掉

# ✅ 分类处理异常
from sqlalchemy.exc import SQLAlchemyError

try:
    result = process_delivery(data)
except (ValueError, KeyError) as e:
    logger.warning(f"数据格式异常: {e}")
    return None
except SQLAlchemyError as e:
    logger.error(f"数据库错误: {e}", exc_info=True)
    raise  # DB 错误必须向上传播

数据库字段持久化

# ============================================
# 问题 6: 内存中修改字段但从未持久化到 DB
# 来源: example_selector.py:337 设置 last_used_at 但 batch update 只更新 times_used
# ============================================

# ❌ 字段赋值但未包含在 batch update 中
golden.last_used_at = now_cst()  # 内存中修改
# batch update 只更新 times_used, last_used_at 被遗漏

# ✅ batch update 必须包含所有修改字段
stmt = (
    sa_update(GoldenExample)
    .where(GoldenExample.id == golden.id)
    .values(
        times_used=golden.times_used + 1,
        last_used_at=now_cst(),  # 同步持久化
    )
)
await session.execute(stmt)

数据库约束完整性

# ============================================
# 问题 7: 缺少唯一约束,依赖应用层去重
# 来源: models/example_usage.py
# ============================================

# ❌ 无数据库层面约束
class ExampleUsage(Base):
    conversation_id = Column(Integer)
    golden_example_id = Column(Integer)

# ✅ 添加唯一约束 + 复合索引
class ExampleUsage(Base):
    conversation_id = Column(Integer, nullable=False)
    golden_example_id = Column(Integer, nullable=False)

    __table_args__ = (
        UniqueConstraint('conversation_id', 'golden_example_id',
                         name='uq_conv_golden'),
        Index('ix_conv_golden', 'conversation_id', 'golden_example_id'),
    )

Go/Gin 专项

// 检查清单:
// - [ ] error 是否全部处理(不允许 _ 忽略 error
// - [ ] goroutine 是否有 recover 保护
// - [ ] 数据库连接是否使用连接池
// - [ ] GORM 查询是否避免了 N+1
// - [ ] context 是否正确传递和取消