Files
OpenFlare/frontend/lib/services
2026-06-08 20:37:47 +08:00
..
2026-06-08 20:37:47 +08:00
2026-06-08 20:37:47 +08:00
2026-06-08 20:37:47 +08:00
2026-06-08 20:37:47 +08:00
2026-06-08 20:37:40 +08:00
2026-06-08 20:37:40 +08:00
2026-06-08 20:37:47 +08:00
2026-06-08 20:34:27 +08:00

服务层架构文档

前端服务层 - 统一的 API 交互层,基于 TypeScript 和面向对象设计

架构设计

设计理念

服务层采用分层架构和继承模式,遵循以下核心原则:

  1. 单一职责 - 每个服务模块负责一个业务领域
  2. 类型安全 - 全量 TypeScript 类型定义,杜绝 any
  3. 统一规范 - 所有服务遵循相同的设计模式
  4. 错误分类 - 细粒度错误类型,便于精确处理
  5. 可扩展性 - 基于继承的设计,易于添加新服务

架构分层

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 对象调用服务

相关文档