- 移除未使用的TabPane组件 - 修复类型定义和导入方式 - 优化mock数据源的环境变量判断逻辑 - 更新文档结构并归档旧文件 - 添加新的UI组件和Memo组件 - 调整API路径和响应处理
287 lines
7.1 KiB
Markdown
287 lines
7.1 KiB
Markdown
# 后端架构
|
|
|
|
> **入口**: [_index.md](_index.md)
|
|
|
|
---
|
|
|
|
## 1. 技术栈
|
|
|
|
| 技术 | 版本 | 用途 |
|
|
|------|------|------|
|
|
| Node.js | 18.x | 运行时 |
|
|
| Express | 4.x | Web框架 |
|
|
| TypeScript | 5.x | 类型系统 |
|
|
| Knex.js | 3.x | 数据库ORM |
|
|
| Redis | 6.x | 缓存/队列 |
|
|
| BullMQ | 4.x | 任务队列 |
|
|
|
|
---
|
|
|
|
## 2. 目录结构
|
|
|
|
```
|
|
server/src/
|
|
├── api/ # API层
|
|
│ ├── routes/ # 路由定义
|
|
│ ├── controllers/ # 控制器
|
|
│ └── middlewares/ # 中间件
|
|
├── services/ # 服务层
|
|
│ ├── ProductService.ts
|
|
│ ├── OrderService.ts
|
|
│ └── ...
|
|
├── models/ # 数据模型
|
|
├── repositories/ # 数据访问层
|
|
├── core/ # 核心模块
|
|
│ ├── connectors/ # 平台连接器
|
|
│ └── orchestrator/ # 编排器
|
|
├── utils/ # 工具函数
|
|
├── types/ # 类型定义
|
|
└── config/ # 配置文件
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 分层架构
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ API层 (Controller) │
|
|
│ - 请求处理 │
|
|
│ - 参数校验 │
|
|
│ - 权限检查 │
|
|
└─────────────────────────┬───────────────────────────────────┘
|
|
│
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ 服务层 (Service) │
|
|
│ - 业务逻辑 │
|
|
│ - 事务管理 │
|
|
│ - 状态流转 │
|
|
└─────────────────────────┬───────────────────────────────────┘
|
|
│
|
|
↓
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ 数据访问层 (Repository) │
|
|
│ - 数据库操作 │
|
|
│ - 缓存操作 │
|
|
│ - 外部API调用 │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 4. 服务层设计
|
|
|
|
### 4.1 服务模板
|
|
|
|
```typescript
|
|
/**
|
|
* [BE-P001] 商品服务
|
|
* @description 商品主数据管理
|
|
*/
|
|
export class ProductService {
|
|
private static TABLE_NAME = 'cf_product';
|
|
|
|
static async list(params: ListParams): Promise<Product[]> {
|
|
const query = db(this.TABLE_NAME)
|
|
.where('tenant_id', params.tenantId);
|
|
|
|
if (params.status) {
|
|
query.where('status', params.status);
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
static async get(id: string): Promise<Product | null> {
|
|
return db(this.TABLE_NAME).where({ id }).first();
|
|
}
|
|
|
|
static async create(data: CreateProductRequest): Promise<Product> {
|
|
const [product] = await db(this.TABLE_NAME)
|
|
.insert({
|
|
...data,
|
|
id: generateId(),
|
|
created_at: new Date(),
|
|
updated_at: new Date(),
|
|
})
|
|
.returning('*');
|
|
|
|
return product;
|
|
}
|
|
|
|
static async update(id: string, data: UpdateProductRequest): Promise<Product> {
|
|
const [product] = await db(this.TABLE_NAME)
|
|
.where({ id })
|
|
.update({
|
|
...data,
|
|
updated_at: new Date(),
|
|
})
|
|
.returning('*');
|
|
|
|
return product;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4.2 服务命名规范
|
|
|
|
| 后缀 | 说明 | 示例 |
|
|
|------|------|------|
|
|
| Service | 业务服务 | ProductService |
|
|
| Repository | 数据访问 | ProductRepository |
|
|
| Connector | 平台连接 | ShopifyConnector |
|
|
| Orchestrator | 编排器 | PublishOrchestrator |
|
|
|
|
---
|
|
|
|
## 5. 中间件
|
|
|
|
### 5.1 权限中间件
|
|
|
|
```typescript
|
|
export const authorize = (permission: string) => {
|
|
return async (ctx: Context, next: Next) => {
|
|
const { user } = ctx.state;
|
|
|
|
if (!user) {
|
|
throw new UnauthorizedError('未登录');
|
|
}
|
|
|
|
const hasPermission = await checkPermission(user, permission);
|
|
if (!hasPermission) {
|
|
throw new ForbiddenError('无权限');
|
|
}
|
|
|
|
await next();
|
|
};
|
|
};
|
|
```
|
|
|
|
### 5.2 使用方式
|
|
|
|
```typescript
|
|
router.get(
|
|
'/api/v1/products',
|
|
authorize('product:read'),
|
|
ProductController.list
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 任务队列
|
|
|
|
### 6.1 队列定义
|
|
|
|
```typescript
|
|
import { Queue, Worker } from 'bullmq';
|
|
|
|
export const collectionQueue = new Queue('collection', {
|
|
connection: redis,
|
|
});
|
|
|
|
export const collectionWorker = new Worker(
|
|
'collection',
|
|
async (job) => {
|
|
const { platform, shopId, type } = job.data;
|
|
|
|
// 执行采集任务
|
|
const result = await CollectionAdapterService.executeCollectionTask(
|
|
platform,
|
|
shopId,
|
|
type
|
|
);
|
|
|
|
return result;
|
|
},
|
|
{ connection: redis, concurrency: 10 }
|
|
);
|
|
```
|
|
|
|
### 6.2 任务添加
|
|
|
|
```typescript
|
|
await collectionQueue.add('collect-products', {
|
|
platform: 'SHOPIFY',
|
|
shopId: 'shop-001',
|
|
type: 'product',
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 错误处理
|
|
|
|
### 7.1 错误类型
|
|
|
|
```typescript
|
|
export class AppError extends Error {
|
|
constructor(
|
|
public code: string,
|
|
public message: string,
|
|
public statusCode: number = 500
|
|
) {
|
|
super(message);
|
|
}
|
|
}
|
|
|
|
export class ValidationError extends AppError {
|
|
constructor(message: string) {
|
|
super('VAL_INVALID_PARAM', message, 400);
|
|
}
|
|
}
|
|
|
|
export class UnauthorizedError extends AppError {
|
|
constructor(message: string) {
|
|
super('AUTH_UNAUTHORIZED', message, 401);
|
|
}
|
|
}
|
|
|
|
export class ForbiddenError extends AppError {
|
|
constructor(message: string) {
|
|
super('AUTH_FORBIDDEN', message, 403);
|
|
}
|
|
}
|
|
|
|
export class NotFoundError extends AppError {
|
|
constructor(message: string) {
|
|
super('BIZ_NOT_FOUND', message, 404);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7.2 错误处理中间件
|
|
|
|
```typescript
|
|
export const errorHandler = async (ctx: Context, next: Next) => {
|
|
try {
|
|
await next();
|
|
} catch (error) {
|
|
if (error instanceof AppError) {
|
|
ctx.status = error.statusCode;
|
|
ctx.body = {
|
|
success: false,
|
|
error: {
|
|
code: error.code,
|
|
message: error.message,
|
|
},
|
|
};
|
|
} else {
|
|
ctx.status = 500;
|
|
ctx.body = {
|
|
success: false,
|
|
error: {
|
|
code: 'SYS_INTERNAL_ERROR',
|
|
message: '内部服务器错误',
|
|
},
|
|
};
|
|
}
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
*最后更新: 2026-03-22*
|