Files
makemd/docs/05_AI/06_Wrong_vs_Right_Examples.md
wurenzhi 15ee1758f5 refactor: 重构项目结构并优化类型定义
- 移除extension模块,将功能迁移至node-agent
- 修复类型导出问题,使用export type明确类型导出
- 统一数据库连接方式,从直接导入改为使用config/database
- 更新文档中的项目结构描述
- 添加多个服务的实用方法,如getForecast、getBalances等
- 修复类型错误和TS1205警告
- 优化RedisService调用方式
- 添加新的实体类型定义
- 更新审计日志格式,统一字段命名
2026-03-21 15:04:06 +08:00

21 KiB
Raw Blame History

错误示例与正确示例对比Wrong vs Right Examples

模块: 05_AI - 错误示例与正确示例对比
更新日期: 2026-03-20
用途: 帮助 AI 快速识别错误代码模式并避免重复犯错


📋 使用说明

本文档通过对比错误示例和正确示例,帮助 AI

  1. 快速识别错误模式
  2. 理解正确实现方式
  3. 避免重复犯错
  4. 提高代码质量

1 TypeScript 类型安全

1.1 禁止 any 类型

错误示例

// 错误:使用 any 类型
function handleData(data: any) {
  return data.name
}

function processUser(user: any) {
  console.log(user.email)
  return user.id
}

const userData: any = fetchUser()

正确示例

// 正确:使用 unknown + 类型守卫
function handleData(data: unknown) {
  if (typeof data === 'object' && data !== null && 'name' in data) {
    return (data as { name: string }).name
  }
  throw new Error('Invalid data')
}

// 正确:使用类型守卫函数
function isUser(data: unknown): data is User {
  return (
    typeof data === 'object' &&
    data !== null &&
    'id' in data &&
    'email' in data &&
    typeof (data as User).id === 'string' &&
    typeof (data as User).email === 'string'
  )
}

function processUser(data: unknown) {
  if (isUser(data)) {
    console.log(data.email)
    return data.id
  }
  throw new Error('Invalid user data')
}

// 正确:使用 Schema 验证
const userData = UserSchema.parse(fetchUser())

1.2 函数必须声明返回类型

错误示例

// 错误:未声明返回类型
function getUser(userId: string) {
  return userRepository.findById(userId)
}

function calculateTotal(items: Item[]) {
  return items.reduce((sum, item) => sum + item.price, 0)
}

async function fetchData(url: string) {
  const response = await fetch(url)
  return response.json()
}

正确示例

// 正确:声明返回类型
function getUser(userId: string): Promise<User | null> {
  return userRepository.findById(userId)
}

function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0)
}

async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url)
  return response.json() as Promise<T>
}

1.3 API 必须定义类型

错误示例

// 错误:未定义类型
const response = await axios.get('/api/users')
const users = response.data

const createResponse = await axios.post('/api/users', userData)

const product = await fetch('/api/products/123').then(res => res.json())

正确示例

// 正确:定义请求和响应类型
interface ApiResponse<T> {
  success: boolean
  data: T
  message?: string
}

interface User {
  id: string
  name: string
  email: string
}

const response = await axios.get<ApiResponse<User[]>>('/api/users')
const users = response.data.data

const createResponse = await axios.post<ApiResponse<User>>('/api/users', userData)

const product = await fetch('/api/products/123')
  .then(res => res.json() as Promise<ApiResponse<Product>>)
  .then(data => data.data)

1.4 类型从 Schema 推导

错误示例

// 错误:手动定义类型
interface User {
  id: string
  name: string
  email: string
  age: number
}

interface Product {
  id: string
  name: string
  price: number
}

// 错误:类型和 Schema 不一致
const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email()
  // 缺少 age 字段
})

type User = z.infer<typeof UserSchema>

正确示例

// 正确:从 Schema 推导类型
import { z } from 'zod'

const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().positive()
})

type User = z.infer<typeof UserSchema>

const ProductSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1).max(200),
  price: z.number().positive()
})

type Product = z.infer<typeof ProductSchema>

1.5 统一类型中心

错误示例

// 错误:各模块重复定义类型
// dashboard/src/pages/User/User.tsx
interface User {
  id: string
  name: string
}

// server/src/models/User.ts
interface User {
  id: string
  name: string
  email: string
}

// extension/src/types/User.ts
interface User {
  id: string
  name: string
  email: string
  role: string
}

正确示例

// 正确:从类型中心导入
// types/domain/User.ts
export const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string(),
  email: z.string().email(),
  role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR'])
})

export type User = z.infer<typeof UserSchema>

// dashboard/src/pages/User/User.tsx
import { User } from '@/types/domain/User'

// server/src/models/User.ts
import { User } from '@/types/domain/User'

// node-agent/src/index.ts
import { User } from '@/types/domain/User'

2 逻辑集中化

2.1 Controller 层职责

错误示例

// 错误Controller 中写业务逻辑
export class UserController {
  async createUser(req: Request, res: Response) {
    const { username, email, password } = req.body
    
    // 业务逻辑在 Controller 中
    if (!username || !email || !password) {
      return res.status(400).json({ error: 'Missing fields' })
    }
    
    if (password.length < 8) {
      return res.status(400).json({ error: 'Password too short' })
    }
    
    const hashedPassword = await bcrypt.hash(password, 10)
    
    const user = await this.userRepository.create({
      username,
      email,
      passwordHash: hashedPassword,
      role: 'OPERATOR',
      status: 'ACTIVE'
    })
    
    res.json(user)
  }
}

正确示例

// 正确Controller 只负责请求/响应
export class UserController {
  constructor(private readonly userService: UserService) {}

  async createUser(req: Request, res: Response) {
    try {
      const userDTO = await this.userService.createUser(req.body)
      res.json(createSuccessResponse(userDTO))
    } catch (error) {
      if (error instanceof ValidationError) {
        res.status(400).json(createErrorResponse('VALIDATION_ERROR', error.message))
      } else {
        res.status(500).json(createErrorResponse('INTERNAL_ERROR', error.message))
      }
    }
  }
}

// 业务逻辑在 Service 层
export class UserService {
  async createUser(request: CreateUserRequest): Promise<UserDTO> {
    // 1. 验证请求数据
    const validatedRequest = CreateUserRequestSchema.parse(request)
    
    // 2. 业务逻辑处理
    const hashedPassword = await this.hashPassword(validatedRequest.password)
    
    // 3. 创建领域模型
    const user = UserSchema.parse({
      id: crypto.randomUUID(),
      username: validatedRequest.username,
      email: validatedRequest.email,
      passwordHash: hashedPassword,
      role: validatedRequest.role || 'OPERATOR',
      status: 'ACTIVE',
      createdAt: new Date(),
      updatedAt: new Date()
    })
    
    // 4. 保存到数据库
    const savedUser = await this.userRepository.create(user)
    
    // 5. 转换为 DTO
    return toUserDTO(savedUser)
  }
}

2.2 前端业务规则

错误示例

// 错误:前端直接写业务规则
function ProductCard({ product }: { product: Product }) {
  // 业务规则在前端
  const profitMargin = (product.price - product.cost) / product.price
  
  // 业务判断在前端
  const canSell = profitMargin >= 0.15
  
  // 状态判断在前端
  const statusText = product.status === 'ACTIVE' ? '可售' : '不可售'
  
  return (
    <div>
      <h3>{product.name}</h3>
      <p>利润率: {(profitMargin * 100).toFixed(2)}%</p>
      <p>状态: {statusText}</p>
      {canSell && <button>购买</button>}
    </div>
  )
}

正确示例

// 正确:前端只负责展示
function ProductCard({ product }: { product: ProductDTO }) {
  return (
    <div>
      <h3>{product.name}</h3>
      <p>利润率: {product.profitMargin}%</p>
      <p>状态: {product.statusText}</p>
      {product.canSell && <button onClick={() => handleBuy(product)}>购买</button>}
    </div>
  )
}

// 业务规则在 Service 层
export class ProductService {
  async getProduct(id: string): Promise<ProductDTO> {
    const product = await this.productRepository.findById(id)
    
    if (!product) {
      throw new NotFoundError('Product', id)
    }
    
    // 业务规则计算
    const profitMargin = this.calculateProfitMargin(product)
    const canSell = this.checkProfitMargin(profitMargin, product.businessType)
    const statusText = this.getStatusText(product.status)
    
    return {
      ...product,
      profitMargin: (profitMargin * 100).toFixed(2),
      canSell,
      statusText
    }
  }
  
  private calculateProfitMargin(product: Product): number {
    return (product.price - product.cost) / product.price
  }
  
  private checkProfitMargin(margin: number, businessType: 'B2B' | 'B2C'): boolean {
    const threshold = businessType === 'B2B' ? 0.15 : 0.20
    return margin >= threshold
  }
}

2.3 数据库操作

错误示例

// 错误:直接操作数据库
export class UserController {
  async getUser(req: Request, res: Response) {
    // 直接使用数据库连接
    const user = await db('users').where({ id: req.params.id }).first()
    res.json(user)
  }
}

// 错误:跨模块直接操作数据库
export class OrderService {
  async createOrder(orderData: OrderData) {
    // 直接操作商品表
    const product = await db('products').where({ id: orderData.productId }).first()
    
    // 直接更新库存
    await db('inventory').where({ product_id: orderData.productId }).decrement('quantity', orderData.quantity)
    
    // 直接创建订单
    const order = await db('orders').insert(orderData)
    
    return order
  }
}

正确示例

// 正确:通过 Repository 层操作数据库
export class UserController {
  constructor(
    private readonly userService: UserService
  ) {}

  async getUser(req: Request, res: Response) {
    const user = await this.userService.getUser(req.params.id)
    res.json(createSuccessResponse(user))
  }
}

// 正确:通过 Service 层协调多模块
export class OrderService {
  constructor(
    private readonly orderRepository: OrderRepository,
    private readonly productService: ProductService,
    private readonly inventoryService: InventoryService
  ) {}

  async createOrder(orderData: OrderData): Promise<Order> {
    // 通过 Service 获取商品信息
    const product = await this.productService.getProduct(orderData.productId)
    
    // 通过 Service 更新库存
    await this.inventoryService.decreaseStock(orderData.productId, orderData.quantity)
    
    // 通过 Repository 创建订单
    const order = await this.orderRepository.create(orderData)
    
    return order
  }
}

3 数据安全

3.1 表命名规范

错误示例

// 错误:表名不以 cf_ 开头
await db.schema.createTable('users', (table) => {
  table.string('id').primary()
  table.string('name')
  table.string('email')
})

await db.schema.createTable('products', (table) => {
  table.string('id').primary()
  table.string('name')
  table.decimal('price')
})

正确示例

// 正确:表名以 cf_ 开头
await db.schema.createTable('cf_users', (table) => {
  table.string('id').primary()
  table.string('name')
  table.string('email')
})

await db.schema.createTable('cf_products', (table) => {
  table.string('id').primary()
  table.string('name')
  table.decimal('price', 10, 2) // 金额字段使用 decimal(10,2)
})

3.2 金额字段

错误示例

// 错误:金额字段使用 float/double
await db.schema.createTable('cf_orders', (table) => {
  table.string('id').primary()
  table.float('total_amount') // 错误
  table.double('subtotal') // 错误
})

// 错误:金额计算使用浮点数
const total = 10.5 + 20.3 // 30.800000000000004
const price = product.price * 1.1 // 浮点数精度问题

正确示例

// 正确:金额字段使用 decimal(10,2)
await db.schema.createTable('cf_orders', (table) => {
  table.string('id').primary()
  table.decimal('total_amount', 10, 2) // 正确
  table.decimal('subtotal', 10, 2) // 正确
})

// 正确:金额计算使用整数(分)或 decimal
const total = Math.round((10.5 + 20.3) * 100) / 100 // 30.80
const price = Math.round(product.price * 1.1 * 100) / 100

// 或使用 decimal.js
import Decimal from 'decimal.js'
const total = new Decimal(10.5).plus(20.3).toNumber() // 30.8

3.3 五元组

错误示例

// 错误:缺少五元组
async function processOrder(orderId: string) {
  const order = await orderRepository.findById(orderId)
  await orderService.process(order)
}

// 错误:五元组不完整
const log = {
  orderId: '123',
  action: 'process',
  timestamp: new Date()
}

正确示例

// 正确:包含完整五元组
interface TaskContext {
  tenantId: string
  shopId: string
  taskId: string
  traceId: string
  businessType: 'TOC' | 'TOB'
}

async function processOrder(orderId: string, context: TaskContext) {
  const order = await orderRepository.findById(orderId)
  
  // 记录日志时包含五元组
  logger.info('Processing order', {
    ...context,
    orderId,
    action: 'process'
  })
  
  await orderService.process(order, context)
}

// 正确:五元组完整
const log = {
  tenantId: 'tenant-123',
  shopId: 'shop-456',
  taskId: 'task-789',
  traceId: 'trace-abc',
  businessType: 'TOC',
  orderId: '123',
  action: 'process',
  timestamp: new Date()
}

4 业务规则

4.1 状态机流转

错误示例

// 错误:非法状态流转
async function updateOrderStatus(orderId: string, newStatus: string) {
  const order = await orderRepository.findById(orderId)
  
  // 直接修改状态,未检查流转规则
  order.status = newStatus
  await orderRepository.update(orderId, order)
}

// 错误:跳过状态
async function shipOrder(orderId: string) {
  const order = await orderRepository.findById(orderId)
  
  // 从 PENDING 直接跳到 SHIPPED跳过 PAID 和 PROCESSING
  order.status = 'SHIPPED'
  await orderRepository.update(orderId, order)
}

正确示例

// 正确:通过 Service 层遵循状态机
export class OrderService {
  private readonly STATE_TRANSITIONS = {
    PENDING: ['PAID', 'CANCELLED'],
    PAID: ['PROCESSING', 'REFUNDED'],
    PROCESSING: ['SHIPPED', 'REFUNDED'],
    SHIPPED: ['COMPLETED', 'REFUNDED'],
    COMPLETED: [],
    CANCELLED: [],
    REFUNDED: []
  }

  async updateOrderStatus(
    orderId: string,
    newStatus: OrderStatus,
    context: TaskContext
  ): Promise<Order> {
    const order = await this.orderRepository.findById(orderId)
    
    if (!order) {
      throw new NotFoundError('Order', orderId)
    }
    
    // 检查状态流转是否合法
    if (!this.isValidTransition(order.status, newStatus)) {
      throw new Error(`Invalid state transition: ${order.status} -> ${newStatus}`)
    }
    
    // 更新状态
    order.status = newStatus
    order.updatedAt = new Date()
    
    const updatedOrder = await this.orderRepository.update(orderId, order)
    
    // 记录状态变更日志
    logger.info('Order status updated', {
      ...context,
      orderId,
      fromStatus: order.status,
      toStatus: newStatus
    })
    
    return updatedOrder
  }
  
  private isValidTransition(from: OrderStatus, to: OrderStatus): boolean {
    return this.STATE_TRANSITIONS[from]?.includes(to) || false
  }
}

4.2 权限校验

错误示例

// 错误:硬编码权限判断
async function deleteUser(userId: string, req: Request) {
  const user = req.user
  
  // 硬编码权限判断
  if (user.role !== 'ADMIN') {
    throw new Error('Permission denied')
  }
  
  await userRepository.delete(userId)
}

// 错误:跳过权限校验
async function getAllUsers() {
  return await userRepository.findAll()
}

正确示例

// 正确:使用 authorize 中间件
import { authorize } from '@/middleware/auth'

router.delete('/users/:id',
  authorize('user:delete'),
  userController.deleteUser
)

// 正确Controller 中调用 Service
export class UserController {
  async deleteUser(req: Request, res: Response) {
    await this.userService.deleteUser(req.params.id)
    res.json(createSuccessResponse({ message: 'User deleted' }))
  }
}

// 正确Service 层实现权限逻辑
export class UserService {
  async deleteUser(userId: string): Promise<void> {
    // 检查用户是否存在
    const user = await this.userRepository.findById(userId)
    if (!user) {
      throw new NotFoundError('User', userId)
    }
    
    // 执行删除
    await this.userRepository.delete(userId)
  }
}

4.3 利润红线

错误示例

// 错误:未检查利润红线
async function createQuote(productId: string, price: number) {
  const product = await productRepository.findById(productId)
  
  // 未检查利润率
  const quote = await quoteRepository.create({
    productId,
    price,
    status: 'ACTIVE'
  })
  
  return quote
}

// 错误:仅用售价 - 采购价判断
const profit = price - product.cost
if (profit > 0) {
  // 允许报价
}

正确示例

// 正确:使用 PricingService 检查利润红线
export class QuoteService {
  constructor(
    private readonly pricingService: PricingService,
    private readonly quoteRepository: QuoteRepository
  ) {}

  async createQuote(
    productId: string,
    price: number,
    businessType: 'B2B' | 'B2C'
  ): Promise<Quote> {
    const product = await this.productRepository.findById(productId)
    
    if (!product) {
      throw new NotFoundError('Product', productId)
    }
    
    // 计算净利率(含平台费/物流/税费/汇率/售后/广告摊销)
    const profitMargin = await this.pricingService.calculateProfitMargin({
      price,
      cost: product.cost,
      platform: product.platform,
      businessType
    })
    
    // 检查利润红线
    if (businessType === 'B2B' && profitMargin < 0.15) {
      throw new Error('B2B profit margin must be at least 15%')
    }
    
    if (businessType === 'B2C' && profitMargin < 0.20) {
      // 触发风控预警
      await this.riskAlertService.createAlert({
        type: 'LOW_PROFIT_MARGIN',
        productId,
        profitMargin,
        threshold: 0.20
      })
    }
    
    // 创建报价
    const quote = await this.quoteRepository.create({
      productId,
      price,
      profitMargin,
      status: 'ACTIVE'
    })
    
    return quote
  }
}

5 Mock 数据

5.1 硬编码 Mock 数据

错误示例

// 错误:在业务组件中硬编码 Mock 数据
function ProductList() {
  const products = [
    { id: 1, name: 'Mock商品1', price: 100 },
    { id: 2, name: 'Mock商品2', price: 200 }
  ]
  
  return (
    <div>
      {products.map(p => (
        <div key={p.id}>{p.name}</div>
      ))}
    </div>
  )
}

// 错误:通过 if 判断切换 Mock/真实数据
async function getProducts() {
  if (process.env.NODE_ENV === 'development') {
    return mockProducts
  }
  return await api.getProducts()
}

正确示例

// 正确:通过 DataSource 抽象层
// services/productDataSource.ts
import { ProductDTOSchema, type ProductDTO } from '@/types'

export const productDataSource = {
  async list(): Promise<ProductDTO[]> {
    if (process.env.REACT_APP_USE_MOCK === 'true') {
      const { mockProducts } = await import('@/mock/data/product.mock')
      return mockProducts.map(p => ProductDTOSchema.parse(p))
    }
    
    const response = await api.getProducts()
    return response.data.map(p => ProductDTOSchema.parse(p))
  }
}

// components/ProductList.tsx
function ProductList() {
  const { data: products } = useQuery({
    queryKey: ['products'],
    queryFn: () => productDataSource.list()
  })
  
  return (
    <div>
      {products?.map(p => (
        <div key={p.id}>{p.name}</div>
      ))}
    </div>
  )
}

// mock/data/product.mock.ts
/**
 * [MOCK] 商品 Mock 数据
 * AI注意: 这是Mock实现不是真实业务逻辑
 * 仅在USE_MOCK=true时启用
 */
export const mockProducts = [
  { id: '1', name: '商品1', price: 100 },
  { id: '2', name: '商品2', price: 200 }
]

📊 总结

类别 常见错误 正确做法
TypeScript 使用 any、不声明返回类型 使用 unknown + 类型守卫、声明返回类型
逻辑集中化 Controller 写业务逻辑 业务逻辑在 Service 层
数据安全 表名无前缀、金额用 float 表名 cf_ 前缀、金额用 decimal(10,2)
业务规则 非法状态流转、跳过权限校验 遵循状态机、使用 authorize 中间件
Mock 数据 硬编码 Mock、if 判断切换 DataSource 抽象层、环境变量控制

本文档持续更新,帮助 AI 避免常见错误。