refactor(types): 重构类型系统,统一共享类型定义
feat(types): 新增共享类型中心,包含用户、产品、订单等核心领域类型 fix(types): 修复类型定义错误,统一各模块类型引用 style(types): 优化类型文件格式和注释 docs(types): 更新类型文档和变更日志 test(types): 添加类型测试用例 build(types): 配置类型共享路径 chore(types): 清理重复类型定义文件
This commit is contained in:
962
docs/05_AI/06_Wrong_vs_Right_Examples.md
Normal file
962
docs/05_AI/06_Wrong_vs_Right_Examples.md
Normal file
@@ -0,0 +1,962 @@
|
||||
# 错误示例与正确示例对比(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 避免常见错误。*
|
||||
Reference in New Issue
Block a user