963 lines
21 KiB
Markdown
963 lines
21 KiB
Markdown
|
|
# 错误示例与正确示例对比(Wrong vs Right Examples)
|
|||
|
|
|
|||
|
|
> **模块**: 05_AI - 错误示例与正确示例对比
|
|||
|
|
> **更新日期**: 2026-03-20
|
|||
|
|
> **用途**: 帮助 AI 快速识别错误代码模式并避免重复犯错
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 使用说明
|
|||
|
|
|
|||
|
|
本文档通过对比错误示例和正确示例,帮助 AI:
|
|||
|
|
|
|||
|
|
1. **快速识别错误模式**
|
|||
|
|
2. **理解正确实现方式**
|
|||
|
|
3. **避免重复犯错**
|
|||
|
|
4. **提高代码质量**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1️⃣ TypeScript 类型安全
|
|||
|
|
|
|||
|
|
### 1.1 禁止 any 类型
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:使用 any 类型
|
|||
|
|
function handleData(data: any) {
|
|||
|
|
return data.name
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function processUser(user: any) {
|
|||
|
|
console.log(user.email)
|
|||
|
|
return user.id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const userData: any = fetchUser()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:使用 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 函数必须声明返回类型
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:未声明返回类型
|
|||
|
|
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()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:声明返回类型
|
|||
|
|
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 必须定义类型
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:未定义类型
|
|||
|
|
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())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:定义请求和响应类型
|
|||
|
|
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 推导
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:手动定义类型
|
|||
|
|
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>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:从 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 统一类型中心
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:各模块重复定义类型
|
|||
|
|
// 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
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:从类型中心导入
|
|||
|
|
// 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'
|
|||
|
|
|
|||
|
|
// extension/src/types/User.ts
|
|||
|
|
import { User } from '@/types/domain/User'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2️⃣ 逻辑集中化
|
|||
|
|
|
|||
|
|
### 2.1 Controller 层职责
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误: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)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确: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 前端业务规则
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:前端直接写业务规则
|
|||
|
|
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>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:前端只负责展示
|
|||
|
|
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 数据库操作
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:直接操作数据库
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:通过 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 表命名规范
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:表名不以 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')
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:表名以 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 金额字段
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:金额字段使用 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 // 浮点数精度问题
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:金额字段使用 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 五元组
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:缺少五元组
|
|||
|
|
async function processOrder(orderId: string) {
|
|||
|
|
const order = await orderRepository.findById(orderId)
|
|||
|
|
await orderService.process(order)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 错误:五元组不完整
|
|||
|
|
const log = {
|
|||
|
|
orderId: '123',
|
|||
|
|
action: 'process',
|
|||
|
|
timestamp: new Date()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:包含完整五元组
|
|||
|
|
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 状态机流转
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:非法状态流转
|
|||
|
|
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)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:通过 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 权限校验
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:硬编码权限判断
|
|||
|
|
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()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:使用 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 利润红线
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:未检查利润红线
|
|||
|
|
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) {
|
|||
|
|
// 允许报价
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:使用 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 数据
|
|||
|
|
|
|||
|
|
#### ❌ 错误示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 错误:在业务组件中硬编码 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()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ✅ 正确示例
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 正确:通过 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 避免常见错误。*
|