mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
14 KiB
14 KiB
服务层架构文档
前端服务层 - 统一的 API 交互层,基于 TypeScript 和面向对象设计
架构设计
设计理念
服务层采用分层架构和继承模式,遵循以下核心原则:
- 单一职责 - 每个服务模块负责一个业务领域
- 类型安全 - 全量 TypeScript 类型定义,杜绝
any - 统一规范 - 所有服务遵循相同的设计模式
- 错误分类 - 细粒度错误类型,便于精确处理
- 可扩展性 - 基于继承的设计,易于添加新服务
架构分层
graph TB
subgraph UI["业务组件层"]
Components["React Components<br/>Hooks & Contexts"]
end
subgraph Services["服务层 (Services Layer)"]
direction LR
Auth["AuthService<br/><small>认证服务</small>"]
Admin["AdminService<br/><small>管理员服务</small>"]
Merchant["MerchantService<br/><small>商户服务</small>"]
Transaction["TransactionService<br/><small>交易服务</small>"]
Dispute["DisputeService<br/><small>争议服务</small>"]
User["UserService<br/><small>用户服务</small>"]
Config["ConfigService<br/><small>配置服务</small>"]
end
subgraph BaseLayer["服务基类"]
BaseService["BaseService<br/><small>• get() post() put() delete()<br/>• rawGet() rawPost()</small>"]
end
subgraph Core["核心层 (Core Layer)"]
direction LR
ApiClient["api-client.ts<br/><small>• 请求/响应拦截<br/>• 错误处理<br/>• 请求去重<br/>• 401重定向</small>"]
Errors["errors.ts<br/><small>• ApiErrorBase<br/>• NetworkError<br/>• ValidationError<br/>• 等7种错误</small>"]
Types["types.ts<br/><small>• ApiResponse<br/>• PaginationParams<br/>• RequestConfig</small>"]
CoreConfig["config.ts<br/><small>• baseURL<br/>• timeout<br/>• credentials</small>"]
end
Backend["Backend API<br/><small>(Go + Gin)</small>"]
Components -->|调用| Auth
Components -->|调用| Admin
Components -->|调用| Merchant
Components -->|调用| Transaction
Components -->|调用| Dispute
Components -->|调用| User
Components -->|调用| Config
Auth -.->|继承| BaseService
Admin -.->|继承| BaseService
Merchant -.->|继承| BaseService
Transaction -.->|继承| BaseService
Dispute -.->|继承| BaseService
User -.->|继承| BaseService
Config -.->|继承| BaseService
BaseService -->|使用| ApiClient
BaseService -->|使用| Errors
BaseService -->|使用| Types
ApiClient -->|读取| CoreConfig
ApiClient ==>|HTTP 请求| Backend
style UI fill:#e1f5ff
style Services fill:#fff9e6
style BaseLayer fill:#f0f0f0
style Core fill:#e8f5e9
style Backend fill:#ffebee
目录结构
lib/services/
├── core/ # 核心基础设施层
│ ├── api-client.ts # HTTP 客户端(请求拦截、错误处理)
│ ├── base.service.ts # 服务基类(封装 CRUD 方法)
│ ├── config.ts # API 配置(环境变量、超时等)
│ ├── errors.ts # 错误类型定义(7种错误类型)
│ ├── types.ts # 核心类型(ApiResponse、分页等)
│ └── index.ts # 核心模块导出
│
├── auth/ # 认证服务模块
│ ├── auth.service.ts # OAuth 认证、登录、登出
│ ├── types.ts # User、OAuthLoginUrlResponse 等
│ └── index.ts # 模块导出 + 文档
│
├── admin/ # 管理员服务模块
│ ├── admin.service.ts # 系统配置、用户积分配置管理
│ ├── types.ts # SystemConfig、UserPayConfig 等
│ └── index.ts # 模块导出 + 文档
│
├── merchant/ # 商户服务模块
│ ├── merchant.service.ts # API Key、支付链接、订单管理
│ ├── types.ts # MerchantAPIKey、PaymentLink 等
│ └── index.ts # 模块导出 + 文档
│
├── transaction/ # 交易服务模块
│ ├── transaction.service.ts # 交易记录
│ ├── types.ts # Order、TransactionQueryParams 等
│ └── index.ts # 模块导出 + 文档
│
├── dispute/ # 争议服务模块
│ ├── dispute.service.ts # 创建争议、退款审核
│ ├── types.ts # Dispute、DisputeWithOrder 等
│ └── index.ts # 模块导出 + 文档
│
├── user/ # 用户服务模块
│ ├── user.service.ts # 用户设置(支付密钥等)
│ ├── types.ts # UpdatePayKeyRequest 等
│ └── index.ts # 模块导出 + 文档
│
├── config/ # 配置服务模块
│ ├── config.service.ts # 获取公共配置
│ ├── types.ts # PublicConfigResponse
│ └── index.ts # 模块导出 + 文档
│
├── index.ts # 统一导出入口
└── README.md # 本文档
核心模块详解
1. BaseService - 服务基类
职责:为所有业务服务提供统一的 HTTP 方法封装
关键设计:
export class BaseService {
protected static readonly basePath: string = '';
// 标准 RESTful 方法
protected static async get<T>(path: string, params?: Record<string, unknown>): Promise<T>
protected static async post<T>(path: string, data?: unknown): Promise<T>
protected static async put<T>(path: string, data?: unknown): Promise<T>
protected static async patch<T>(path: string, data?: unknown): Promise<T>
protected static async delete<T>(path: string, params?: Record<string, unknown>): Promise<T>
// 特殊端点支持(不遵循标准响应格式)
protected static async rawGet<T>(url: string, params?: unknown): Promise<T>
protected static async rawPost<T>(url: string, data?: unknown): Promise<T>
}
设计优势:
- ✅ 子类只需设置
basePath,无需重复实现 HTTP 逻辑 - ✅ 统一的响应解包(
response.data.data) - ✅ 类型安全的泛型设计
- ✅ 支持特殊端点(如
/api.php)
2. API Client - HTTP 客户端
职责:提供全局唯一的 Axios 实例,处理所有 HTTP 请求
核心功能:
请求拦截器
- 自动添加 Cancel Token(支持请求取消)
- 请求去重(避免重复请求)
响应拦截器
- 401 自动重定向 - 未授权时自动跳转登录页
- 错误分类 - 将 HTTP 状态码映射为具体错误类型
- 统一响应格式 - 解析
ApiResponse<T>结构
错误处理映射
| HTTP 状态码 | 错误类型 | 说明 |
|---|---|---|
| 400 | ValidationError |
参数验证失败 |
| 401 | 自动重定向 | 跳转到登录页 |
| 403 | ForbiddenError |
权限不足 |
| 404 | NotFoundError |
资源不存在 |
| 5xx | ServerError |
服务器错误 |
| 超时 | TimeoutError |
请求超时 |
| 网络 | NetworkError |
网络连接失败 |
3. 错误类型层级
ApiErrorBase (基类)
├── NetworkError (网络连接错误)
├── TimeoutError (请求超时)
├── UnauthorizedError (401 - 未授权)
├── ForbiddenError (403 - 权限不足)
├── NotFoundError (404 - 资源不存在)
├── ValidationError (400 - 参数验证失败)
└── ServerError (5xx - 服务器错误)
设计优势:
- 支持
instanceof类型判断 - 携带详细错误信息(
error_code、details) - 便于前端精确处理不同错误场景
开发规范
1. 服务类规范
必须遵循
export class SomeService extends BaseService {
// 1. 必须继承 BaseService
// 2. basePath 必须是 protected static readonly
protected static readonly basePath = '/api/v1/resource';
// 3. 方法必须是 static async
// 4. 返回类型必须明确(禁止 any)
static async getAll(): Promise<Resource[]> {
return this.get<Resource[]>('/');
}
// 5. 参数类型必须明确定义在 types.ts
static async create(request: CreateResourceRequest): Promise<Resource> {
return this.post<Resource>('/', request);
}
}
方法命名规范
| 操作 | 命名 | 示例 |
|---|---|---|
| 获取列表 | list* 或 getAll |
listAPIKeys() |
| 获取单个 | get* 或 getById |
getAPIKey(id) |
| 创建 | create* |
createAPIKey(request) |
| 更新 | update* |
updateAPIKey(id, request) |
| 删除 | delete* |
deleteAPIKey(id) |
| 特殊操作 | 动词开头 | payMerchantOrder() |
方法排序规范
export class SomeService extends BaseService {
protected static readonly basePath = '/api/v1/resource';
// 1. CRUD 操作(按 Create → Read → Update → Delete)
static async create() { }
static async list() { }
static async get() { }
static async update() { }
static async delete() { }
// 2. 其他业务方法(按业务逻辑分组)
static async someAction() { }
}
2. 类型定义规范
types.ts 文件结构
// 1. 类型别名(Type Aliases)
export type ResourceStatus = 'active' | 'inactive' | 'pending';
// 2. 枚举(Enums) - 使用 const enum 提升性能
export const enum ResourceLevel {
Basic = 1,
Premium = 2,
Enterprise = 3,
}
// 3. 接口(Interfaces) - 按业务逻辑分组
/**
* 资源信息
*/
export interface Resource {
/** 资源 ID */
id: number;
/** 资源名称 */
name: string;
/** 状态 */
status: ResourceStatus;
/** 创建时间 */
created_at: string;
}
/**
* 创建资源请求
*/
export interface CreateResourceRequest {
/** 资源名称(最大 50 字符) */
name: string;
/** 描述(可选,最大 200 字符) */
description?: string;
}
注释规范
- ✅ 所有 interface 必须有 JSDoc 描述
- ✅ 所有字段必须有行内注释
- ✅ 包含约束信息(长度、范围、格式等)
- ✅ 可选字段使用
?标记
3. JSDoc 注释规范
标准模板:
/**
* [一句话描述方法功能]
*
* @param paramName - 参数说明
* @returns 返回值说明
* @throws {ErrorType} 错误条件说明
*
* @example
* ```typescript
* // 使用示例
* const result = await Service.method({ param: 'value' });
* ```
*
* @remarks [可选]
* - 业务规则或注意事项
*/
示例:
/**
* 创建商户 API Key
*
* @param request - API Key 配置
* @returns 创建的 API Key 信息
* @throws {UnauthorizedError} 当未登录时
* @throws {ValidationError} 当参数验证失败时
*
* @example
* ```typescript
* const apiKey = await MerchantService.createAPIKey({
* app_name: '我的应用',
* app_homepage_url: 'https://example.com'
* });
* ```
*
* @remarks
* - app_name 最大 20 字符
* - 需要登录权限
*/
static async createAPIKey(request: CreateAPIKeyRequest): Promise<MerchantAPIKey>
4. 模块 index.ts 规范
标准格式:
/**
* [模块名] 服务模块
*
* @description
* 提供 [业务领域] 相关的功能,包括:
* - 功能点1
* - 功能点2
* - 功能点3
*
* @example
* ```typescript
* import { ServiceName } from '@/lib/services';
*
* // 使用示例(展示最常用的1-2个方法)
* const result = await ServiceName.commonMethod();
* ```
*
* @remarks [可选]
* - 特殊说明(如权限要求等)
*/
export { ServiceName } from './service-name.service';
export type * from './types';
// 或明确导出
export type {
Type1,
Type2,
} from './types';
创建新服务指南
Step 1: 创建目录和文件
mkdir lib/services/resource
touch lib/services/resource/types.ts
touch lib/services/resource/resource.service.ts
touch lib/services/resource/index.ts
Step 2: 定义类型(types.ts)
export interface Resource {
id: number;
name: string;
}
export interface CreateResourceRequest {
name: string;
}
Step 3: 实现服务(resource.service.ts)
import { BaseService } from '../core/base.service';
import type { Resource, CreateResourceRequest } from './types';
export class ResourceService extends BaseService {
protected static readonly basePath = '/api/v1/resources';
static async list(): Promise<Resource[]> {
return this.get<Resource[]>('/');
}
static async create(request: CreateResourceRequest): Promise<Resource> {
return this.post<Resource>('/', request);
}
}
Step 4: 导出模块(index.ts)
/**
* 资源服务模块
*
* @description
* 提供资源管理相关的功能,包括:
* - 资源列表查询
* - 资源创建
*/
export { ResourceService } from './resource.service';
export type * from './types';
Step 5: 注册到统一入口(services/index.ts)
import { ResourceService } from './resource';
const services = {
// ... existing services
resource: ResourceService, // 新增
};
export default services;
export { ResourceService } from './resource';
export type * from './resource';
使用示例
import services from '@/lib/services';
// 调用服务
const user = await services.auth.getUserInfo();
const transactions = await services.transaction.getTransactions({
page: 1,
page_size: 20
});
// 错误处理
import { UnauthorizedError, ValidationError } from '@/lib/services';
try {
await services.merchant.createAPIKey(request);
} catch (error) {
if (error instanceof UnauthorizedError) {
router.push('/login');
} else if (error instanceof ValidationError) {
toast.error(error.message);
}
}
注意事项
禁止事项
- ❌ 使用
any类型 - ❌ 直接使用
apiClient(除非在BaseService内部) - ❌ 绕过 BaseService 实现 HTTP 请求
- ❌ 在业务组件中直接导入 axios
必须遵循
- ✅ 所有服务继承
BaseService - ✅ 使用
protected static readonly basePath - ✅ 方法必须有完整的 JSDoc 注释
- ✅ 类型定义必须在
types.ts中 - ✅ 通过
services对象调用服务