فهرست منبع

📝 docs(architecture): 新增和更新架构文档

- 新增后端模块包开发规范文档,基于实际实现经验总结
- 新增后端模块包测试规范文档,详细说明集成测试和单元测试最佳实践
- 更新编码标准文档,拆分测试策略内容到独立文档
- 新增Mini UI包开发规范文档,基于Taro小程序开发经验
- 新增Mini UI包测试规范文档,规定Jest测试框架使用规范
- 更新源码树文档,反映完整项目结构
- 更新测试策略文档,重构为概述文档并拆分详细规范
- 新增Web UI包开发规范文档,基于史诗008经验总结
- 新增Web Server包测试规范文档
- 新增Web UI包测试规范文档

【文档更新】
- 所有新增文档基于实际项目经验总结,包含具体实现示例
- 统一文档格式和版本信息标准
- 提供详细的参考实现和最佳实践指导
yourname 3 هفته پیش
والد
کامیت
e61b4a855d

+ 872 - 0
docs/architecture/backend-module-package-standards.md

@@ -0,0 +1,872 @@
+# 后端模块包规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 2.0 | 2025-12-26 | 基于实际实现重写,修正不准确的描述 | James (Claude Code) |
+| 1.0 | 2025-12-02 | 基于史诗007系列移植经验创建 | Claude Code |
+
+## 概述
+
+本文档定义了后端模块包的设计、开发和集成规范,基于项目实际的模块包实现经验总结。
+
+### 包组织方式
+
+项目采用两种包组织方式:
+
+1. **allin-packages模式**: 每个业务模块独立成包
+   - 目录: `allin-packages/{module-name}-module/`
+   - 示例: `@d8d/allin-channel-module`, `@d8d/allin-platform-module`
+
+2. **core-module聚合模式**: 将多个相关模块打包在一起
+   - 目录: `packages/core-module/{module-name}/`
+   - 示例: `@d8d/core-module/auth-module`, `@d8d/core-module/user-module`
+
+## 1. 包结构规范
+
+### 1.1 目录结构
+
+#### allin-packages模式
+```
+allin-packages/{module-name}-module/
+├── package.json
+├── tsconfig.json
+├── vitest.config.ts
+├── src/
+│   ├── entities/
+│   │   └── {entity-name}.entity.ts
+│   ├── services/
+│   │   └── {service-name}.service.ts
+│   ├── routes/
+│   │   ├── {module}-custom.routes.ts
+│   │   ├── {module}-crud.routes.ts
+│   │   ├── {module}.routes.ts
+│   │   └── index.ts
+│   ├── schemas/
+│   │   └── {schema-name}.schema.ts
+│   ├── types/
+│   │   └── index.ts
+│   └── index.ts
+└── tests/
+    ├── integration/
+    │   └── {module}.integration.test.ts
+    └── utils/
+        └── test-data-factory.ts
+```
+
+#### core-module聚合模式
+```
+packages/core-module/
+├── auth-module/
+│   ├── src/
+│   │   ├── entities/
+│   │   ├── services/
+│   │   ├── routes/
+│   │   └── schemas/
+│   └── tests/
+├── user-module/
+│   └── ...
+└── file-module/
+    └── ...
+```
+
+### 1.2 包命名规范
+
+| 包类型 | 命名模式 | 示例 |
+|--------|----------|------|
+| allin业务包 | `@d8d/allin-{name}-module` | `@d8d/allin-channel-module` |
+| core子模块 | `@d8d/core-module` | 内部按路径区分 |
+
+### 1.3 workspace配置
+```yaml
+# pnpm-workspace.yaml
+packages:
+  - 'allin-packages/*'
+  - 'packages/*'
+```
+
+## 2. 实体设计规范
+
+### 2.1 Entity定义
+
+**实际实现模式**:
+```typescript
+import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
+
+@Entity('channel_info')
+export class Channel {
+  @PrimaryGeneratedColumn({
+    name: 'channel_id',
+    type: 'int',
+    unsigned: true,
+    comment: '渠道ID'
+  })
+  id!: number;
+
+  @Column({
+    name: 'channel_name',
+    type: 'varchar',
+    length: 100,
+    nullable: false,
+    comment: '渠道名称'
+  })
+  @Index('idx_channel_name', { unique: true })
+  channelName!: string;
+
+  @Column({
+    name: 'status',
+    type: 'int',
+    default: 1,
+    comment: '状态:1-正常,0-禁用'
+  })
+  status!: number;
+
+  @Column({
+    name: 'create_time',
+    type: 'timestamp',
+    default: () => 'CURRENT_TIMESTAMP',
+    comment: '创建时间'
+  })
+  createTime!: Date;
+
+  @Column({
+    name: 'update_time',
+    type: 'timestamp',
+    default: () => 'CURRENT_TIMESTAMP',
+    onUpdate: 'CURRENT_TIMESTAMP',
+    comment: '更新时间'
+  })
+  updateTime!: Date;
+}
+```
+
+### 2.2 关键要点
+
+- **完整的列定义**: 包含 `type`, `length`, `nullable`, `comment` 等属性
+- **索引装饰器**: 使用 `@Index` 定义唯一索引
+- **时间戳字段**: 使用 `timestamp` 类型而非 `datetime`
+- **默认值**: 使用 `default: () => 'CURRENT_TIMESTAMP'`
+
+### 2.3 关联关系
+
+**多对一关系**:
+```typescript
+@ManyToOne(() => Platform, { eager: false })
+@JoinColumn({ name: 'platform_id', referencedColumnName: 'id' })
+platform!: Platform;
+```
+
+**避免循环依赖**:
+```typescript
+// 使用字符串引用避免循环依赖
+@ManyToOne('DisabledPerson', { nullable: true })
+@JoinColumn({ name: 'person_id', referencedColumnName: 'id' })
+person!: import('@d8d/allin-disability-module/entities').DisabledPerson | null;
+```
+
+### 2.4 文件关联模式
+
+```typescript
+@Column({ name: 'avatar_file_id', type: 'int', unsigned: true, nullable: true })
+avatarFileId!: number | null;
+
+@ManyToOne(() => File)
+@JoinColumn({ name: 'avatar_file_id', referencedColumnName: 'id' })
+avatarFile!: File | null;
+```
+
+## 3. 服务层规范
+
+### 3.1 GenericCrudService继承
+
+```typescript
+import { GenericCrudService } from '@d8d/shared-crud';
+import { DataSource } from 'typeorm';
+import { Channel } from '../entities/channel.entity';
+
+export class ChannelService extends GenericCrudService<Channel> {
+  constructor(dataSource: DataSource) {
+    super(dataSource, Channel);
+  }
+}
+```
+
+### 3.2 方法覆盖模式(使用override)
+
+**实际实现**:
+```typescript
+export class ChannelService extends GenericCrudService<Channel> {
+  constructor(dataSource: DataSource) {
+    super(dataSource, Channel);
+  }
+
+  /**
+   * 创建渠道 - 覆盖父类方法,添加名称唯一性检查
+   */
+  override async create(data: Partial<Channel>, userId?: string | number): Promise<Channel> {
+    // 检查渠道名称是否已存在(只检查正常状态的渠道)
+    if (data.channelName) {
+      const existingChannel = await this.repository.findOne({
+        where: { channelName: data.channelName, status: 1 }
+      });
+      if (existingChannel) {
+        throw new Error('渠道名称已存在');
+      }
+    }
+
+    // 设置默认值
+    const channelData = {
+      contactPerson: '',
+      contactPhone: '',
+      channelType: '',
+      description: '',
+      ...data,
+      status: 1,
+      createTime: new Date(),
+      updateTime: new Date()
+    };
+
+    return super.create(channelData, userId);
+  }
+
+  /**
+   * 更新渠道 - 覆盖父类方法,添加存在性和名称重复检查
+   */
+  override async update(id: number, data: Partial<Channel>, userId?: string | number): Promise<Channel | null> {
+    // 检查渠道是否存在
+    const channel = await this.repository.findOne({ where: { id, status: 1 } });
+    if (!channel) {
+      throw new Error('渠道不存在');
+    }
+
+    // 检查名称是否与其他渠道重复
+    if (data.channelName && data.channelName !== channel.channelName) {
+      const existingChannel = await this.repository.findOne({
+        where: { channelName: data.channelName, id: Not(id), status: 1 }
+      });
+      if (existingChannel) {
+        throw new Error('渠道名称已存在');
+      }
+    }
+
+    const updateData = {
+      ...data,
+      updateTime: new Date()
+    };
+
+    return super.update(id, updateData, userId);
+  }
+
+  /**
+   * 删除渠道 - 覆盖父类方法,改为软删除
+   */
+  override async delete(id: number, userId?: string | number): Promise<boolean> {
+    // 软删除:设置status为0
+    const result = await this.repository.update({ id }, { status: 0 });
+    return result.affected === 1;
+  }
+}
+```
+
+### 3.3 关键要点
+
+- **使用 `override` 关键字**: 明确标识覆盖父类方法
+- **软删除逻辑**: 使用 `status` 字段而非物理删除
+- **业务逻辑检查**: 在调用父类方法前进行验证
+- **设置默认值**: 为可选字段设置合理的默认值
+- **时间戳管理**: 自动设置 `createTime` 和 `updateTime`
+
+## 4. 路由层规范
+
+### 4.1 使用OpenAPIHono
+
+**实际实现**:
+```typescript
+import { OpenAPIHono } from '@hono/zod-openapi';
+import { AuthContext } from '@d8d/shared-types';
+import channelCustomRoutes from './channel-custom.routes';
+import { channelCrudRoutes } from './channel-crud.routes';
+
+// 创建路由实例 - 聚合自定义路由和CRUD路由
+const channelRoutes = new OpenAPIHono<AuthContext>()
+  .route('/', channelCustomRoutes)
+  .route('/', channelCrudRoutes);
+
+export { channelRoutes };
+export default channelRoutes;
+```
+
+### 4.2 导出模式
+
+**routes/index.ts**:
+```typescript
+export * from './channel.routes';
+export * from './channel-custom.routes';
+export * from './channel-crud.routes';
+```
+
+### 4.3 自定义路由响应规范
+
+**重要**: 自定义路由(非CRUD路由)必须使用 `createRoute` 定义,并在返回响应前使用 `parseWithAwait` 验证和转换数据。
+
+**完整示例**:
+```typescript
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { parseWithAwait, createZodErrorResponse } from '@d8d/shared-utils';
+import { ErrorSchema, ZodErrorSchema } from '@d8d/shared-utils/schema/error';
+import { ChannelSchema, CreateChannelSchema } from '../schemas/channel.schema';
+
+// 定义路由(包含请求和响应Schema)
+const createChannelRoute = createRoute({
+  method: 'post',
+  path: '/create',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: CreateChannelSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '创建成功',
+      content: {
+        'application/json': { schema: ChannelSchema }
+      }
+    },
+    // ✅ 400使用 ZodErrorSchema(因为使用 createZodErrorResponse)
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ZodErrorSchema } }
+    },
+    // ✅ 其他错误使用 ErrorSchema
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 实现路由处理函数
+channelCustomRoutes.openapi(createChannelRoute, async (c) => {
+  try {
+    const data = c.req.valid('json');
+    const channelService = new ChannelService(AppDataSource);
+    const result = await channelService.create(data);
+
+    // ✅ 必须:使用 parseWithAwait 验证和转换响应数据
+    const validatedResult = await parseWithAwait(ChannelSchema, result);
+    return c.json(validatedResult, 200);
+  } catch (error) {
+    if (error instanceof z.ZodError) {
+      // ✅ 推荐:使用 createZodErrorResponse 处理Zod验证错误
+      return c.json(createZodErrorResponse(error), 400);
+    }
+    return c.json({ code: 500, message: error.message }, 500);
+  }
+});
+```
+
+#### 数组响应处理
+
+**推荐做法**: 定义数组响应 Schema
+
+```typescript
+// Schema定义
+export const ChannelListResponseSchema = z.object({
+  data: z.array(ChannelSchema).openapi({
+    description: '渠道列表'
+  }),
+  total: z.number().int().min(0).openapi({
+    description: '总数',
+    example: 10
+  })
+});
+
+// 路由定义
+const listChannelRoute = createRoute({
+  method: 'get',
+  path: '/list',
+  responses: {
+    200: {
+      description: '获取列表成功',
+      content: {
+        'application/json': { schema: ChannelListResponseSchema }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ZodErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+channelCustomRoutes.openapi(listChannelRoute, async (c) => {
+  try {
+    const result = await channelService.getList(query);
+
+    // ✅ 推荐:直接使用 parseWithAwait 验证整个响应对象
+    const validatedResult = await parseWithAwait(ChannelListResponseSchema, result);
+    return c.json(validatedResult, 200);
+  } catch (error) {
+    if (error instanceof z.ZodError) {
+      return c.json(createZodErrorResponse(error), 400);
+    }
+    return c.json({ code: 500, message: error.message }, 500);
+  }
+});
+```
+
+**❌ 不推荐**: 使用 `Promise.all` 循环验证
+
+```typescript
+// ❌ 不推荐:这种方式效率较低
+const validatedData = await Promise.all(
+  result.data.map(item => parseWithAwait(ChannelSchema, item))
+);
+return c.json({ data: validatedData, total: result.total }, 200);
+```
+
+#### 关键要点
+
+**响应Schema定义**:
+- **400响应使用 `ZodErrorSchema`**: 因为使用 `createZodErrorResponse` 返回详细的验证错误
+- **其他错误使用 `ErrorSchema`**: 401, 404, 500 等使用简单的错误格式
+- **数组响应定义专用Schema**: 使用 `z.array(ItemSchema)` 定义列表响应Schema
+
+**代码实现**:
+- **必须使用 `createRoute`**: 所有自定义路由必须使用 `createRoute` 定义
+- **必须使用 `parseWithAwait`**: 所有自定义路由返回前必须验证数据
+- **捕获 ZodError**: 在catch块中处理 `z.ZodError` 异常
+- **使用 `createZodErrorResponse`**: 提供统一的Zod错误响应格式
+- **避免使用 Promise.all 循环验证**: 应定义数组响应Schema,直接验证整个响应对象
+
+### 4.4 关键要点
+
+- **使用 `OpenAPIHono`**: 而非普通的 `Hono`
+- **使用 `AuthContext` 泛型**: 提供类型安全的认证上下文
+- **路由聚合**: 分别定义自定义路由和CRUD路由,然后聚合
+- **不设置 `basePath`**: 在聚合路由时处理路径
+- **自定义路由必须使用 `createRoute`**: 所有自定义路由必须使用 `createRoute` 定义
+- **自定义路由必须使用 `parseWithAwait`**: 验证响应数据符合Schema定义
+- **400响应使用 `ZodErrorSchema`**: 因为使用 `createZodErrorResponse` 返回详细验证错误
+- **其他错误使用 `ErrorSchema`**: 401, 404, 500 等使用简单错误格式
+
+## 5. Schema规范
+
+### 5.1 使用Zod + OpenAPI装饰器
+
+**实际实现**:
+```typescript
+import { z } from '@hono/zod-openapi';
+
+// 渠道实体Schema
+export const ChannelSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '渠道ID',
+    example: 1
+  }),
+  channelName: z.string().max(100).openapi({
+    description: '渠道名称',
+    example: '微信小程序'
+  }),
+  channelType: z.string().max(50).openapi({
+    description: '渠道类型',
+    example: '小程序'
+  }),
+  contactPerson: z.string().max(50).openapi({
+    description: '联系人',
+    example: '张三'
+  }),
+  contactPhone: z.string().max(20).openapi({
+    description: '联系电话',
+    example: '13800138000'
+  }),
+  description: z.string().nullable().optional().openapi({
+    description: '描述',
+    example: '微信小程序渠道'
+  }),
+  status: z.number().int().min(0).max(1).default(1).openapi({
+    description: '状态:1-正常,0-禁用',
+    example: 1
+  }),
+  createTime: z.coerce.date<Date>().openapi({
+    description: '创建时间',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  updateTime: z.coerce.date<Date>().openapi({
+    description: '更新时间',
+    example: '2024-01-01T00:00:00Z'
+  })
+});
+
+// 创建渠道DTO
+export const CreateChannelSchema = z.object({
+  channelName: z.string().min(1).max(100).openapi({
+    description: '渠道名称',
+    example: '微信小程序'
+  }),
+  channelType: z.string().max(50).optional().openapi({
+    description: '渠道类型',
+    example: '小程序'
+  }),
+  contactPerson: z.string().max(50).optional().openapi({
+    description: '联系人',
+    example: '张三'
+  }),
+  contactPhone: z.string().max(20).optional().openapi({
+    description: '联系电话',
+    example: '13800138000'
+  }),
+  description: z.string().optional().openapi({
+    description: '描述',
+    example: '微信小程序渠道'
+  })
+});
+
+// 更新渠道DTO(所有字段可选)
+export const UpdateChannelSchema = CreateChannelSchema.partial();
+
+// 列表响应Schema
+export const ChannelListResponseSchema = z.object({
+  data: z.array(ChannelSchema).openapi({
+    description: '渠道列表'
+  }),
+  total: z.number().int().min(0).openapi({
+    description: '总数',
+    example: 10
+  })
+});
+```
+
+### 5.2 Zod 4.0 coerce使用说明
+
+**重要**: Zod 4.0 中,`z.coerce.date()` 和 `z.coerce.number()` 需要添加泛型参数来指定类型。
+
+```typescript
+// ✅ 正确:Zod 4.0 - 使用泛型指定类型
+z.coerce.date<Date>()       // 转换为Date类型
+z.coerce.number<number>()    // 转换为number类型
+
+// ❌ 错误:不指定泛型(Zod 4.0中类型推断可能不准确)
+z.coerce.date()
+z.coerce.number()
+```
+
+### 5.3 类型使用说明
+
+**重要**: Schema只用于请求参数验证和响应定义,**不需要导出推断的TypeScript类型**。
+
+```typescript
+// ❌ 不需要:导出推断类型(实际项目中不会被使用)
+export type Channel = z.infer<typeof ChannelSchema>;
+export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
+export type UpdateChannelDto = z.infer<typeof UpdateChannelSchema>;
+```
+
+**原因**:
+- UI包通过RPC直接从API路由推断类型
+- `@hono/zod-openapi` 自动生成OpenAPI文档
+- 前端使用 `hc.rpc()` 自动获得类型安全的客户端
+
+**正确做法**:只导出Schema常量,不导出推断类型。
+
+### 5.4 关键要点
+
+- **使用 `.openapi()` 装饰器**: 添加描述和示例
+- **使用 `z.coerce.date<Date>()` 和 `z.coerce.number<number>()`**: Zod 4.0需要添加泛型参数
+- **使用 `.nullable().optional()`**: 处理可空字段
+- **定义列表响应Schema**: 使用 `z.array(ItemSchema)` 定义数组响应,而不是循环验证
+- **不导出推断类型**: 类型由RPC自动推断,不需要手动导出
+
+## 6. 软删除规范
+
+### 6.1 字段定义
+
+```typescript
+@Column({
+  name: 'status',
+  type: 'int',
+  default: 1,
+  comment: '状态:1-正常,0-禁用'
+})
+status!: number;
+```
+
+### 6.2 Service层实现
+
+```typescript
+// 覆盖delete方法实现软删除
+override async delete(id: number, userId?: string | number): Promise<boolean> {
+  const result = await this.repository.update({ id }, { status: 0 });
+  return result.affected === 1;
+}
+```
+
+### 6.3 查询时过滤
+
+```typescript
+// 查询时只查询正常状态的记录
+const channel = await this.repository.findOne({
+  where: { id, status: 1 }
+});
+```
+
+## 7. 数据库类型规范
+
+### 7.1 PostgreSQL类型映射
+
+| 数据库类型 | TypeORM类型 | 备注 |
+|------------|-------------|------|
+| `int unsigned` | `int` + `unsigned: true` | 主键常用 |
+| `varchar(n)` | `varchar` + `length: n` | 字符串 |
+| `text` | `text` | 长文本 |
+| `timestamp` | `timestamp` | 时间戳 |
+| `decimal(p,s)` | `decimal` + `precision/scale` | 金额 |
+| `int` (状态) | `int` | 状态枚举 |
+
+### 7.2 Decimal字段处理
+
+```typescript
+// 实体定义
+@Column({
+  name: 'total_amount',
+  type: 'decimal',
+  precision: 10,
+  scale: 2
+})
+totalAmount!: number;
+
+// Schema验证(使用z.coerce.number<number>()处理字符串)
+const CreateSchema = z.object({
+  totalAmount: z.coerce.number<number>().min(0),
+});
+```
+
+## 8. 错误处理规范
+
+### 8.1 标准错误响应格式
+
+```typescript
+// 正确的错误响应格式
+return c.json({
+  code: 400,
+  message: '渠道名称已存在'
+}, 400);
+```
+
+### 8.2 HTTP状态码使用
+
+| 状态码 | 使用场景 |
+|--------|----------|
+| 200 | 操作成功(包括删除) |
+| 201 | 创建成功 |
+| 400 | 请求参数错误 |
+| 401 | 未授权 |
+| 404 | 资源不存在 |
+| 500 | 服务器内部错误 |
+
+### 8.3 DELETE操作响应
+
+```typescript
+// DELETE成功应返回200,而不是404
+app.delete('/:id', async (c) => {
+  const result = await service.delete(id);
+  return c.json({ success: true }, 200); // ✅ 正确
+  // return c.json({ success: true }, 404); // ❌ 错误
+});
+```
+
+## 9. 包配置规范
+
+### 9.1 package.json
+
+```json
+{
+  "name": "@d8d/allin-channel-module",
+  "version": "1.0.0",
+  "type": "module",
+  "main": "src/index.ts",
+  "types": "src/index.ts",
+  "scripts": {
+    "test": "vitest run",
+    "test:coverage": "vitest run --coverage",
+    "typecheck": "tsc --noEmit"
+  },
+  "dependencies": {
+    "@d8d/shared-crud": "workspace:*",
+    "@d8d/shared-types": "workspace:*",
+    "@d8d/shared-utils": "workspace:*",
+    "typeorm": "^0.3.20"
+  },
+  "devDependencies": {
+    "@hono/zod-openapi": "latest",
+    "vitest": "latest"
+  }
+}
+```
+
+### 9.2 依赖配置
+
+```json
+{
+  "dependencies": {
+    "@d8d/allin-platform-module": "workspace:*",
+    "@d8d/file-module": "workspace:*",
+    "@d8d/allin-enums": "workspace:*"
+  }
+}
+```
+
+## 10. 测试规范
+
+详细的测试规范请参考 [后端模块包测试规范](./backend-module-testing-standards.md)。
+
+### 10.1 测试数据工厂
+
+```typescript
+export class TestDataFactory {
+  static createChannelData(overrides: Partial<Channel> = {}): Partial<Channel> {
+    const timestamp = Date.now();
+    return {
+      channelName: `测试渠道_${timestamp}`,
+      channelType: '测试类型',
+      contactPerson: '测试联系人',
+      contactPhone: '13800138000',
+      status: 1,
+      ...overrides
+    };
+  }
+
+  static async createTestChannel(
+    dataSource: DataSource,
+    overrides: Partial<Channel> = {}
+  ): Promise<Channel> {
+    const channelData = this.createChannelData(overrides);
+    const channelRepo = dataSource.getRepository(Channel);
+    const channel = channelRepo.create(channelData);
+    return await channelRepo.save(channel);
+  }
+}
+```
+
+### 10.2 测试配置
+
+```typescript
+// vitest.config.ts
+export default defineConfig({
+  test: {
+    globals: true,
+    environment: 'node',
+    include: ['tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+    fileParallelism: false // 避免数据库连接冲突
+  }
+});
+```
+
+## 11. 开发流程
+
+### 11.1 类型检查
+
+```bash
+# 开发过程中运行类型检查
+pnpm typecheck
+
+# 针对特定包
+cd allin-packages/channel-module && pnpm typecheck
+```
+
+### 11.2 运行测试
+
+```bash
+# 进入模块目录
+cd allin-packages/channel-module
+
+# 运行测试
+pnpm test
+
+# 运行集成测试
+pnpm test:integration
+
+# 生成覆盖率报告
+pnpm test:coverage
+```
+
+### 11.3 注释规范
+
+```typescript
+/**
+ * 创建渠道 - 覆盖父类方法,添加名称唯一性检查
+ * @param data 渠道数据
+ * @param userId 操作用户ID
+ * @returns 创建的渠道
+ * @throws Error 当渠道名称已存在时
+ */
+override async create(data: Partial<Channel>, userId?: string | number): Promise<Channel> {
+  // ...
+}
+```
+
+## 12. 参考实现
+
+### 12.1 已完成模块
+
+**allin-packages**:
+- `channel-module`: 基础CRUD模块
+- `platform-module`: 基础依赖包
+- `company-module`: 模块间依赖
+- `disability-module`: 文件模块集成
+
+**core-module**:
+- `auth-module`: 认证模块
+- `user-module`: 用户模块
+- `file-module`: 文件模块
+
+### 12.2 最佳实践
+
+1. **使用 `override` 关键字**: 明确标识覆盖父类方法
+2. **完整的列定义**: 包含 `type`, `comment`, `nullable` 等属性
+3. **OpenAPI文档**: 使用 `.openapi()` 装饰器添加文档
+4. **类型推断导出**: 导出 `z.infer` 推断的类型
+5. **软删除实现**: 使用 `status` 字段而非物理删除
+6. **时间戳管理**: 自动设置 `createTime` 和 `updateTime`
+7. **错误处理**: 提供明确的错误消息
+
+## 附录:检查清单
+
+### 新模块包创建检查清单
+
+- [ ] 包名符合规范:`@d8d/allin-{name}-module` 或 `@d8d/core-module`
+- [ ] 目录结构完整:entities, services, routes, schemas, tests
+- [ ] Entity包含完整列定义:type, comment, nullable等
+- [ ] Service使用 `override` 关键字
+- [ ] 软删除实现:使用 `status` 字段
+- [ ] Schema使用 `.openapi()` 装饰器
+- [ ] Schema不导出推断类型(类型由RPC自动推断)
+- [ ] Schema使用 `z.coerce.date<Date>()` 和 `z.coerce.number<number>()` 泛型语法
+- [ ] 路由使用 `OpenAPIHono` 和 `AuthContext`
+- [ ] 自定义路由使用 `parseWithAwait` 验证响应数据
+- [ ] 自定义路由使用 `createZodErrorResponse` 处理Zod错误
+- [ ] 路由400响应使用 `ZodErrorSchema`,其他错误使用 `ErrorSchema`
+- [ ] 测试数据工厂使用时间戳保证唯一性
+- [ ] vitest.config.ts 设置 `fileParallelism: false`
+- [ ] 类型检查通过
+- [ ] 所有测试通过
+
+## 相关文档
+
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [测试策略概述](./testing-strategy.md)
+- [编码标准](./coding-standards.md)
+
+---
+
+**文档状态**: 正式版
+**基于实际实现**: 2025-12-26

+ 953 - 0
docs/architecture/backend-module-testing-standards.md

@@ -0,0 +1,953 @@
+# 后端模块包测试规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 2.0 | 2025-12-26 | 基于实际测试实现重写,修正不准确的描述 | James (Claude Code) |
+| 1.0 | 2025-12-26 | 从测试策略文档拆分,专注后端模块包测试 | James (Claude Code) |
+
+## 概述
+
+本文档定义了后端模块包的测试标准和最佳实践,基于项目实际的测试实现经验总结。
+
+### 目标包
+- **packages/core-module**: 核心模块(用户、认证、文件等)
+- **allin-packages/**: AllIn业务模块(订单、企业、渠道等)
+
+### 实际测试架构
+
+**重要**: 项目的测试采用**集中式管理**模式:
+- 测试文件位于各模块包内的 `tests/` 目录
+- 使用共享测试工具 `@d8d/shared-test-util`
+- 集成测试使用真实PostgreSQL数据库
+- 单元测试使用mock隔离依赖
+
+## 测试框架栈
+
+### 测试运行器
+- **Vitest**: 统一的测试运行器
+- **配置**: 关闭文件并行测试 (`fileParallelism: false`) 避免数据库连接冲突
+
+### 测试工具
+- **hono/testing**: `testClient` - API路由测试
+- **@d8d/shared-test-util**: 共享测试基础设施
+  - `IntegrationTestDatabase` - 集成测试数据库管理
+  - `setupIntegrationDatabaseHooksWithEntities` - 测试生命周期钩子
+  - `TestDataFactory` - 测试数据工厂
+- **TypeORM**: 数据库操作和实体管理
+
+### Mock工具
+- **vitest.mock**: 依赖项模拟
+- **vi.mocked**: 类型安全的mock操作
+
+## 测试文件结构
+
+### 实际项目结构
+```
+packages/core-module/
+├── auth-module/
+│   └── tests/
+│       ├── integration/
+│       │   ├── auth.integration.test.ts
+│       │   ├── phone-decrypt.integration.test.ts
+│       │   └── system-config-integration.test.ts
+│       └── unit/
+│           └── mini-auth.service.test.ts
+├── file-module/
+│   └── tests/
+│       ├── integration/
+│       │   └── file.routes.integration.test.ts
+│       └── unit/
+│           └── file.service.test.ts
+└── user-module/
+    └── tests/
+        ├── integration/
+        │   ├── role.integration.test.ts
+        │   └── user.routes.integration.test.ts
+        └── utils/
+            ├── integration-test-db.ts       # 模块专用测试工具
+            └── integration-test-utils.ts
+
+allin-packages/order-module/
+├── src/
+│   ├── routes/
+│   ├── services/
+│   ├── entities/
+│   └── schemas/
+├── tests/
+│   ├── integration/
+│   │   ├── order.integration.test.ts
+│   │   └── talent-employment.integration.test.ts
+│   └── utils/
+│       └── test-data-factory.ts
+└── vitest.config.ts
+```
+
+### 配置文件模板
+
+```typescript
+// vitest.config.ts
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+  test: {
+    globals: true,
+    environment: 'node',
+    include: ['tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+    coverage: {
+      provider: 'v8',
+      reporter: ['text', 'json', 'html'],
+      exclude: [
+        'node_modules/',
+        'dist/',
+        'tests/',
+        '**/*.d.ts',
+        '**/*.config.*'
+      ]
+    },
+    // 关键: 关闭并行测试避免数据库连接冲突
+    fileParallelism: false
+  }
+});
+```
+
+## 集成测试最佳实践
+
+### 1. 使用hono/testing测试API
+
+```typescript
+import { describe, it, expect, beforeEach } from 'vitest';
+import { testClient } from 'hono/testing';
+import {
+  IntegrationTestDatabase,
+  setupIntegrationDatabaseHooksWithEntities
+} from '@d8d/shared-test-util';
+import { userRoutes } from '../src/routes';
+import { UserEntity } from '../src/entities/user.entity';
+import { Role } from '../src/entities/role.entity';
+import { File } from '@d8d/core-module/file-module';
+import { TestDataFactory } from './utils/integration-test-db';
+
+// 设置集成测试钩子
+setupIntegrationDatabaseHooksWithEntities([UserEntity, Role, File])
+
+describe('用户路由API集成测试', () => {
+  let client: ReturnType<typeof testClient<typeof userRoutes>>;
+  let authService: AuthService;
+  let testToken: string;
+  let testUser: any;
+
+  beforeEach(async () => {
+    // 创建测试客户端
+    client = testClient(userRoutes);
+
+    // 获取数据源
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+    if (!dataSource) throw new Error('Database not initialized');
+
+    // 初始化服务
+    const userService = new UserService(dataSource);
+    authService = new AuthService(userService);
+
+    // 创建测试用户并生成token
+    testUser = await TestDataFactory.createTestUser(dataSource, {
+      username: 'testuser_auth',
+      email: 'testuser_auth@example.com'
+    });
+
+    testToken = authService.generateToken(testUser);
+  });
+
+  describe('用户创建路由测试', () => {
+    it('应该拒绝无认证令牌的用户创建请求', async () => {
+      const userData = {
+        username: 'testuser_create',
+        email: 'testcreate@example.com',
+        password: 'TestPassword123!',
+        nickname: 'Test User',
+        phone: '13800138001'
+      };
+
+      const response = await client.index.$post({
+        json: userData
+      });
+
+      // 应该返回401状态码,因为缺少认证
+      expect(response.status).toBe(401);
+      if (response.status === 401) {
+        const responseData = await response.json();
+        expect(responseData.message).toContain('Authorization header missing');
+      }
+    });
+
+    it('应该成功创建用户(使用有效认证令牌)', async () => {
+      const userData = {
+        username: 'testuser_create_success',
+        email: 'testcreate_success@example.com',
+        password: 'TestPassword123!',
+        nickname: 'Test User Success'
+      };
+
+      const response = await client.index.$post({
+        json: userData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+      const responseData = await response.json();
+      expect(responseData.data).toHaveProperty('id');
+      expect(responseData.data.username).toBe('testuser_create_success');
+    });
+  });
+
+  describe('用户查询路由测试', () => {
+    it('应该支持分页查询用户列表', async () => {
+      // 创建多个测试用户
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      for (let i = 0; i < 15; i++) {
+        await TestDataFactory.createTestUser(dataSource, {
+          username: `pageuser_${i}`
+        });
+      }
+
+      const response = await client.index.$get({
+        query: {
+          page: '1',
+          pageSize: '10'
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+      const responseData = await response.json();
+      expect(responseData.data).toHaveLength(10);
+      expect(responseData.total).toBeGreaterThanOrEqual(15);
+    });
+  });
+});
+```
+
+### 2. 测试数据工厂模式
+
+```typescript
+// tests/utils/integration-test-db.ts
+import { DataSource } from 'typeorm';
+import { UserEntity } from '../../src/entities/user.entity';
+import { Role } from '../../src/entities/role.entity';
+
+/**
+ * 测试数据工厂类
+ */
+export class TestDataFactory {
+  /**
+   * 创建测试用户数据
+   */
+  static createUserData(overrides: Partial<UserEntity> = {}): Partial<UserEntity> {
+    const timestamp = Date.now();
+    return {
+      username: `testuser_${timestamp}`,
+      password: 'TestPassword123!',
+      email: `test_${timestamp}@example.com`,
+      phone: `138${timestamp.toString().slice(-8)}`,
+      nickname: `Test User ${timestamp}`,
+      name: `Test Name ${timestamp}`,
+      isDisabled: 0,
+      isDeleted: 0,
+      ...overrides
+    };
+  }
+
+  /**
+   * 创建测试角色数据
+   */
+  static createRoleData(overrides: Partial<Role> = {}): Partial<Role> {
+    const timestamp = Date.now();
+    return {
+      name: `test_role_${timestamp}`,
+      description: `Test role description ${timestamp}`,
+      ...overrides
+    };
+  }
+
+  /**
+   * 在数据库中创建测试用户
+   */
+  static async createTestUser(
+    dataSource: DataSource,
+    overrides: Partial<UserEntity> = {}
+  ): Promise<UserEntity> {
+    const userData = this.createUserData(overrides);
+    const userRepository = dataSource.getRepository(UserEntity);
+
+    const user = userRepository.create(userData);
+    return await userRepository.save(user);
+  }
+
+  /**
+   * 在数据库中创建测试角色
+   */
+  static async createTestRole(
+    dataSource: DataSource,
+    overrides: Partial<Role> = {}
+  ): Promise<Role> {
+    const roleData = this.createRoleData(overrides);
+    const roleRepository = dataSource.getRepository(Role);
+
+    const role = roleRepository.create(roleData);
+    return await roleRepository.save(role);
+  }
+}
+```
+
+### 3. 集成测试断言工具
+
+```typescript
+// tests/utils/integration-test-utils.ts
+import { IntegrationTestDatabase } from '@d8d/shared-test-util';
+import { UserEntity } from '../../src/entities/user.entity';
+
+/**
+ * 集成测试断言工具
+ */
+export class IntegrationTestAssertions {
+  /**
+   * 断言响应状态码
+   */
+  static expectStatus(response: { status: number }, expectedStatus: number): void {
+    if (response.status !== expectedStatus) {
+      throw new Error(`Expected status ${expectedStatus}, but got ${response.status}`);
+    }
+  }
+
+  /**
+   * 断言用户存在于数据库中
+   */
+  static async expectUserToExist(username: string): Promise<void> {
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+    if (!dataSource) {
+      throw new Error('Database not initialized');
+    }
+
+    const userRepository = dataSource.getRepository(UserEntity);
+    const user = await userRepository.findOne({ where: { username } });
+
+    if (!user) {
+      throw new Error(`Expected user ${username} to exist in database`);
+    }
+  }
+
+  /**
+   * 断言用户不存在于数据库中
+   */
+  static async expectUserNotToExist(username: string): Promise<void> {
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+    if (!dataSource) {
+      throw new Error('Database not initialized');
+    }
+
+    const userRepository = dataSource.getRepository(UserEntity);
+    const user = await userRepository.findOne({ where: { username } });
+
+    if (user) {
+      throw new Error(`Expected user ${username} not to exist in database`);
+    }
+  }
+}
+```
+
+### 4. 测试完整业务流程
+
+```typescript
+describe('订单管理完整流程测试', () => {
+  let client: ReturnType<typeof testClient<typeof orderRoutes>>;
+  let testToken: string;
+  let testPlatform: any;
+  let testCompany: any;
+  let testChannel: any;
+
+  beforeEach(async () => {
+    client = testClient(orderRoutes);
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+
+    // 创建测试基础数据
+    testPlatform = await createTestPlatform(dataSource);
+    testCompany = await createTestCompany(dataSource);
+    testChannel = await createTestChannel(dataSource);
+
+    // 创建测试用户并生成token
+    const testUser = await TestDataFactory.createTestUser(dataSource);
+    testToken = JWTUtil.generateToken({
+      userId: testUser.id,
+      username: testUser.username
+    });
+  });
+
+  it('应该完成订单创建到分配人员的完整流程', async () => {
+    // 1. 创建订单
+    const createResponse = await client.create.$post({
+      json: {
+        orderName: '测试订单',
+        platformId: testPlatform.id,
+        companyId: testCompany.id,
+        channelId: testChannel.id,
+        expectedStartDate: new Date().toISOString(),
+        orderStatus: 'DRAFT'
+      }
+    }, {
+      headers: { 'Authorization': `Bearer ${testToken}` }
+    });
+
+    expect(createResponse.status).toBe(200);
+    const { data: order } = await createResponse.json();
+
+    // 2. 更新订单状态
+    const updateResponse = await client[':id'].$patch({
+      param: { id: order.id },
+      json: { orderStatus: 'ACTIVE' }
+    }, {
+      headers: { 'Authorization': `Bearer ${testToken}` }
+    });
+
+    expect(updateResponse.status).toBe(200);
+
+    // 3. 分配人员到订单
+    const assignResponse = await client.assign.$post({
+      json: {
+        orderId: order.id,
+        personIds: [1, 2, 3]
+      }
+    }, {
+      headers: { 'Authorization': `Bearer ${testToken}` }
+    });
+
+    expect(assignResponse.status).toBe(200);
+
+    // 4. 验证订单详情
+    const detailResponse = await client[':id'].$get({
+      param: { id: order.id }
+    }, {
+      headers: { 'Authorization': `Bearer ${testToken}` }
+    });
+
+    expect(detailResponse.status).toBe(200);
+    const { data: orderDetail } = await detailResponse.json();
+    expect(orderDetail.orderStatus).toBe('ACTIVE');
+  });
+});
+```
+
+## 单元测试最佳实践
+
+### 1. Service层单元测试
+
+```typescript
+import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
+import { DataSource } from 'typeorm';
+import { FileService } from '../../src/services/file.service';
+import { File } from '../../src/entities/file.entity';
+import { MinioService } from '../../src/services/minio.service';
+
+// Mock依赖项
+vi.mock('../../src/services/minio.service');
+vi.mock('@d8d/shared-utils', () => ({
+  logger: {
+    error: vi.fn(),
+    db: vi.fn()
+  },
+  ErrorSchema: {}
+}));
+vi.mock('uuid', () => ({
+  v4: () => 'test-uuid-123'
+}));
+
+describe('FileService', () => {
+  let mockDataSource: DataSource;
+  let fileService: FileService;
+
+  beforeEach(() => {
+    mockDataSource = {
+      getRepository: vi.fn(() => ({
+        findOne: vi.fn(),
+        findOneBy: vi.fn(),
+        save: vi.fn(),
+        create: vi.fn()
+      }))
+    } as unknown as DataSource;
+
+    fileService = new FileService(mockDataSource);
+  });
+
+  afterEach(() => {
+    vi.clearAllMocks();
+  });
+
+  describe('createFile', () => {
+    it('应该成功创建文件并生成上传策略', async () => {
+      const mockFileData = {
+        name: 'test.txt',
+        type: 'text/plain',
+        size: 1024,
+        uploadUserId: 1
+      };
+
+      const mockUploadPolicy = {
+        'x-amz-algorithm': 'test-algorithm',
+        'x-amz-credential': 'test-credential',
+        host: 'https://minio.example.com'
+      };
+
+      const mockSavedFile = {
+        id: 1,
+        ...mockFileData,
+        path: '1/test-uuid-123-test.txt',
+        uploadTime: new Date(),
+        createdAt: new Date(),
+        updatedAt: new Date()
+      };
+
+      const mockGenerateUploadPolicy = vi.fn().mockResolvedValue(mockUploadPolicy);
+      vi.mocked(MinioService).mockImplementation(() => ({
+        generateUploadPolicy: mockGenerateUploadPolicy
+      } as unknown as MinioService));
+
+      // Mock GenericCrudService的create方法
+      vi.spyOn(fileService, 'create').mockResolvedValue(mockSavedFile as File);
+
+      const result = await fileService.createFile(mockFileData);
+
+      expect(mockGenerateUploadPolicy).toHaveBeenCalledWith('1/test-uuid-123-test.txt');
+      expect(fileService.create).toHaveBeenCalledWith(expect.objectContaining({
+        name: 'test.txt',
+        path: '1/test-uuid-123-test.txt',
+        uploadUserId: 1
+      }));
+      expect(result).toEqual({
+        file: mockSavedFile,
+        uploadPolicy: mockUploadPolicy
+      });
+    });
+
+    it('应该处理文件创建错误', async () => {
+      const mockFileData = {
+        name: 'test.txt',
+        uploadUserId: 1
+      };
+
+      const mockGenerateUploadPolicy = vi.fn()
+        .mockRejectedValue(new Error('MinIO error'));
+
+      vi.mocked(MinioService).mockImplementation(() => ({
+        generateUploadPolicy: mockGenerateUploadPolicy
+      } as unknown as MinioService));
+
+      await expect(fileService.createFile(mockFileData))
+        .rejects.toThrow('MinIO error');
+    });
+  });
+});
+```
+
+### 2. Schema验证测试
+
+```typescript
+import { describe, it, expect } from 'vitest';
+import { CreatePlatformSchema, UpdatePlatformSchema } from '../../src/schemas/platform.schema';
+
+describe('平台Schema验证测试', () => {
+  describe('CreatePlatformSchema', () => {
+    it('应该验证有效的平台数据', () => {
+      const data = {
+        platformName: '测试平台',
+        contactEmail: 'test@example.com',
+        contactPhone: '13800138000'
+      };
+
+      const result = CreatePlatformSchema.safeParse(data);
+
+      expect(result.success).toBe(true);
+      if (result.success) {
+        expect(result.data.platformName).toBe('测试平台');
+        expect(result.data.contactEmail).toBe('test@example.com');
+      }
+    });
+
+    it('应该验证空字符串转换为undefined', () => {
+      const data = {
+        platformName: '测试平台',
+        contactEmail: '' // 空字符串应该转换为undefined
+      };
+
+      const result = CreatePlatformSchema.safeParse(data);
+
+      expect(result.success).toBe(true);
+      if (result.success) {
+        expect(result.data.contactEmail).toBeUndefined();
+      }
+    });
+
+    it('应该拒绝无效的邮箱格式', () => {
+      const data = {
+        platformName: '测试平台',
+        contactEmail: 'invalid-email'
+      };
+
+      const result = CreatePlatformSchema.safeParse(data);
+
+      expect(result.success).toBe(false);
+    });
+
+    it('应该拒绝缺失必填字段', () => {
+      const data = {
+        contactEmail: 'test@example.com'
+        // 缺少 platformName
+      };
+
+      const result = CreatePlatformSchema.safeParse(data);
+
+      expect(result.success).toBe(false);
+    });
+  });
+});
+```
+
+### 3. 工具函数测试
+
+```typescript
+import { describe, it, expect } from 'vitest';
+import { generateOrderNumber, calculateOrderAmount } from '../../src/utils/order.util';
+
+describe('订单工具函数测试', () => {
+  describe('generateOrderNumber', () => {
+    it('应该生成唯一订单号', () => {
+      const orderNumber1 = generateOrderNumber();
+      const orderNumber2 = generateOrderNumber();
+
+      expect(orderNumber1).not.toBe(orderNumber2);
+      expect(orderNumber1).toMatch(/^ORD\d{13}$/); // ORD + 13位时间戳
+    });
+
+    it('应该生成带前缀的订单号', () => {
+      const orderNumber = generateOrderNumber('TEST');
+      expect(orderNumber).toMatch(/^TEST\d{13}$/);
+    });
+  });
+
+  describe('calculateOrderAmount', () => {
+    it('应该正确计算订单金额', () => {
+      const items = [
+        { quantity: 2, unitPrice: 100 },
+        { quantity: 3, unitPrice: 50 }
+      ];
+
+      const total = calculateOrderAmount(items);
+
+      expect(total).toBe(350); // 2*100 + 3*50
+    });
+
+    it('应该处理空数组', () => {
+      const total = calculateOrderAmount([]);
+      expect(total).toBe(0);
+    });
+
+    it('应该处理小数精度', () => {
+      const items = [
+        { quantity: 1, unitPrice: 99.99 },
+        { quantity: 2, unitPrice: 50.005 }
+      ];
+
+      const total = calculateOrderAmount(items);
+
+      expect(total).toBeCloseTo(200, 2);
+    });
+  });
+});
+```
+
+## 共享测试工具使用
+
+### IntegrationTestDatabase
+
+来自 `@d8d/shared-test-util`,用于管理集成测试数据库:
+
+```typescript
+import {
+  IntegrationTestDatabase,
+  setupIntegrationDatabaseHooksWithEntities
+} from '@d8d/shared-test-util';
+import { UserEntity } from '../src/entities/user.entity';
+import { Role } from '../src/entities/role.entity';
+
+// 设置测试生命周期钩子
+setupIntegrationDatabaseHooksWithEntities([UserEntity, Role])
+
+describe('集成测试', () => {
+  it('使用集成测试数据库', async () => {
+    // 获取数据源
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+
+    // 使用Repository
+    const userRepo = dataSource.getRepository(UserEntity);
+    const user = await userRepo.findOne({ where: { id: 1 } });
+
+    expect(user).toBeDefined();
+  });
+});
+```
+
+### 自定义模块测试工具
+
+每个模块可以定义自己的测试工具:
+
+```typescript
+// tests/utils/integration-test-db.ts
+import { DataSource } from 'typeorm';
+import { IntegrationTestDatabase } from '@d8d/shared-test-util';
+import { Order } from '../../src/entities/order.entity';
+import { Talent } from '../../src/entities/talent.entity';
+
+export class OrderTestDataFactory {
+  /**
+   * 创建测试订单数据
+   */
+  static async createTestOrder(
+    dataSource: DataSource,
+    overrides: Partial<Order> = {}
+  ): Promise<Order> {
+    const timestamp = Date.now();
+    const orderData = {
+      orderName: `测试订单_${timestamp}`,
+      orderStatus: 'DRAFT',
+      workStatus: 'NOT_WORKING',
+      ...overrides
+    };
+
+    const orderRepo = dataSource.getRepository(Order);
+    const order = orderRepo.create(orderData);
+    return await orderRepo.save(order);
+  }
+
+  /**
+   * 创建测试残疾人数据
+   */
+  static async createTestTalent(
+    dataSource: DataSource,
+    overrides: Partial<Talent> = {}
+  ): Promise<Talent> {
+    const timestamp = Date.now();
+    const talentData = {
+      name: `测试残疾人_${timestamp}`,
+      idCard: `110101199001011${timestamp.toString().slice(-3)}`,
+      phone: `138${timestamp.toString().slice(-8)}`,
+      ...overrides
+    };
+
+    const talentRepo = dataSource.getRepository(Talent);
+    const talent = talentRepo.create(talentData);
+    return await talentRepo.save(talent);
+  }
+}
+```
+
+## 测试命名约定
+
+### 文件命名
+- 集成测试:`[feature].integration.test.ts`
+- 单元测试:`[component].test.ts`
+- 测试工具:`integration-test-db.ts`、`integration-test-utils.ts`
+
+### 测试描述
+```typescript
+describe('[模块名]', () => {
+  describe('[功能名]', () => {
+    it('应该[预期行为]', async () => { });
+    it('应该拒绝[错误情况]', async () => { });
+  });
+});
+```
+
+## 运行测试
+
+### 在模块包中运行
+```bash
+# 进入模块目录
+cd packages/core-module/user-module
+# 或
+cd allin-packages/order-module
+
+# 运行所有测试
+pnpm test
+
+# 运行集成测试
+pnpm test:integration
+
+# 运行单元测试
+pnpm test:unit
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 运行特定测试
+pnpm test --testNamePattern="应该成功创建用户"
+
+# 监听模式
+pnpm test --watch
+```
+
+### 根目录运行
+```bash
+# 运行所有模块测试
+pnpm test
+
+# 运行特定目录的测试
+pnpm test "packages/core-module/**/*.test.ts"
+pnpm test "allin-packages/**/*.test.ts"
+```
+
+## 覆盖率标准
+
+| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
+|----------|----------|----------|--------------|
+| 集成测试 | 60% | 70% | 80% |
+| 单元测试 | 50% | 60% | 70% |
+
+**关键模块定义**:
+- 认证授权Service:70%单元测试覆盖率
+- 数据库操作Service:70%单元测试覆盖率
+- API路由:80%集成测试覆盖率
+- Schema验证:90%单元测试覆盖率
+
+## 常见错误避免
+
+### ❌ 不要在集成测试中mock数据库
+```typescript
+// 错误:集成测试中mock数据库Repository
+vi.mock('typeorm', () => ({
+  getRepository: vi.fn(() => mockRepo)
+}));
+
+// 正确:使用真实的数据库和Repository
+const dataSource = await IntegrationTestDatabase.getDataSource();
+const userRepo = dataSource.getRepository(UserEntity);
+```
+
+### ❌ 不要硬编码测试数据
+```typescript
+// 错误:硬编码用户名
+it('应该创建用户', async () => {
+  await createTestUser(dataSource, { username: 'testuser' });
+});
+
+// 正确:使用时间戳生成唯一数据
+it('应该创建用户', async () => {
+  await TestDataFactory.createTestUser(dataSource); // 自动生成唯一用户名
+});
+```
+
+### ❌ 不要忽略认证测试
+```typescript
+// 错误:不测试认证
+it('应该创建用户', async () => {
+  const response = await client.create.$post({ json: userData });
+  expect(response.status).toBe(200);
+});
+
+// 正确:测试有认证和无认证两种情况
+it('应该拒绝无认证的请求', async () => {
+  const response = await client.create.$post({ json: userData });
+  expect(response.status).toBe(401);
+});
+
+it('应该接受有效认证的请求', async () => {
+  const response = await client.create.$post({
+    json: userData
+  }, {
+    headers: { 'Authorization': `Bearer ${testToken}` }
+  });
+  expect(response.status).toBe(200);
+});
+```
+
+### ❌ 不要忘记清理测试数据
+```typescript
+// 错误:不使用setupIntegrationDatabaseHooksWithEntities
+describe('测试', () => {
+  beforeEach(async () => {
+    await IntegrationTestDatabase.initializeWithEntities([UserEntity]);
+  });
+  // 没有afterEach清理
+});
+
+// 正确:使用setupIntegrationDatabaseHooksWithEntities自动管理
+setupIntegrationDatabaseHooksWithEntities([UserEntity])
+
+describe('测试', () => {
+  // beforeEach和afterEach自动设置
+});
+```
+
+## 调试技巧
+
+### 1. 运行特定测试
+```bash
+# 运行特定测试文件
+pnpm test user.routes.integration.test.ts
+
+# 运行匹配名称的测试
+pnpm test --testNamePattern="应该成功创建用户"
+
+# 显示详细输出
+pnpm test --reporter=verbose
+```
+
+### 2. 调试SQL查询
+```typescript
+// 在测试中启用SQL日志
+const dataSource = await IntegrationTestDatabase.getDataSource();
+// 查看实际执行的SQL
+dataSource.driver.createQueryRunner('debug').query('SELECT * FROM users');
+```
+
+### 3. 使用only专注某个测试
+```typescript
+describe.only('专注这个测试套件', () => {
+  it.only('只运行这个测试', async () => {
+    // ...
+  });
+});
+```
+
+## 参考实现
+
+### 核心模块
+- 用户模块:`packages/core-module/user-module/tests/`
+- 认证模块:`packages/core-module/auth-module/tests/`
+- 文件模块:`packages/core-module/file-module/tests/`
+
+### AllIn业务模块
+- 订单模块:`allin-packages/order-module/tests/`
+- 企业模块:`allin-packages/company-module/tests/`
+- 渠道模块:`allin-packages/channel-module/tests/`
+- 残疾人模块:`allin-packages/disability-module/tests/`
+
+### 共享测试工具
+- 测试基础设施:`packages/shared-test-util/src/`
+
+## 相关文档
+
+- [测试策略概述](./testing-strategy.md)
+- [Web UI包测试规范](./web-ui-testing-standards.md)
+- [Web Server包测试规范](./web-server-testing-standards.md)
+- [Mini UI包测试规范](./mini-ui-testing-standards.md)
+- [后端模块包开发规范](./backend-module-package-standards.md)
+
+---
+
+**文档状态**: 正式版
+**适用范围**: packages/core-module、allin-packages/
+**基于实际测试实现**: 2025-12-26

+ 460 - 46
docs/architecture/coding-standards.md

@@ -3,52 +3,466 @@
 ## 版本信息
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
-| 2.6 | 2025-12-15 | 移除测试策略内容,更新RPC客户端最佳实践,修正$path()方法描述与实际代码不一致问题 | James |
-| 2.5 | 2025-12-12 | 修正测试目录描述,从 `__tests__` 更新为 `tests` | Bob (Scrum Master) |
+| 3.0 | 2025-12-26 | 拆分测试策略到独立文档,保留编码标准 | James (Claude Code) |
+| 2.5 | 2025-12-26 | 添加Mini UI包开发规范章节 | Bob (Scrum Master) |
 | 2.4 | 2025-09-20 | 与主架构文档版本一致 | Winston |
 
 ## 现有标准合规性
-- **代码风格**: TypeScript严格模式,一致的缩进和命名
-- **linting规则**: 已配置ESLint,支持TypeScript和React
-- **文档风格**: 代码注释良好,架构文档完整
-- **测试策略**: 独立文档 `testing-strategy.md` 包含完整的测试规范和API模拟策略
-
-## 架构原则
-- **模块化设计**: 基于monorepo的模块化架构,支持按需安装
-- **类型安全**: 全面使用TypeScript,确保编译时类型检查
-- **依赖注入**: 通过客户端管理器模式实现依赖注入,便于测试和替换
-- **关注点分离**: 业务逻辑、数据访问、UI呈现分层清晰
-- **可扩展性**: 支持单租户和多租户部署模式
-
-## 关键编码规范
-- **命名约定**: 使用camelCase(变量、函数)、PascalCase(类、接口)、kebab-case(文件、目录)
-- **代码组织**: 遵循功能分组原则,相关代码放在同一目录
-- **错误处理**: 统一使用异常处理,避免静默失败
-- **日志记录**: 结构化日志,包含上下文信息和级别
-- **配置管理**: 环境变量和配置文件分离,支持不同环境配置
-- **安全实践**: 输入验证、输出编码、最小权限原则
-
-## RPC客户端架构最佳实践
-
-### 客户端管理器模式
-- **单例模式**: 每个UI包使用单例模式的客户端管理器(如`AdvertisementClientManager`、`UserClientManager`)确保全局唯一的客户端实例
-- **延迟初始化**: 客户端应在首次使用时初始化,避免过早创建,通过`get()`方法实现懒加载
-- **统一基础**: 所有客户端管理器使用`@d8d/shared-ui-components`包中的`rpcClient`函数创建Hono RPC客户端
-- **类型安全**: 使用Hono的InferRequestType和InferResponseType确保API调用的类型一致性
-- **组件调用规范**: 在组件中应使用`clientManager.get().api.$method`而非直接使用导出的客户端实例,保持架构一致性
-
-### API调用结构
-- **Hono风格**: 生成的客户端使用Hono风格的方法调用(`index.$get`、`index.$post`、`:id.$put`、`:id.$delete`等)
-- **属性访问**: 通过属性访问嵌套API端点(如`client.provinces.$get()`、`client[':id'].$get()`)
-- **参数传递**: 使用`param`对象传递路径参数,`json`对象传递请求体,`query`对象传递查询参数
-
-### 跨UI包集成支持
-- **统一模拟**: 为简化测试复杂度,特别是跨UI包集成测试场景,测试时应统一模拟`@d8d/shared-ui-components/utils/hc`中的`rpcClient`函数
-- **跨包优势**: 统一模拟点支持多个UI包组件的API模拟,无需分别模拟各个客户端管理器
-- **测试规范**: 详细的API模拟策略见[测试策略文档](./testing-strategy.md#api模拟规范)
-
-### 架构一致性要求
-- **统一入口**: 所有API调用必须通过客户端管理器获取实例
-- **错误处理**: 客户端应提供统一的错误处理机制
-- **配置管理**: 支持不同环境的API基础URL配置
-- **生命周期**: 提供`reset()`方法用于测试或重新初始化
+
+### 代码风格
+- **TypeScript严格模式**: 所有项目必须启用严格类型检查
+- **一致的缩进**: 使用2个空格缩进
+- **命名约定**:
+  - 文件名: kebab-case (如: `user.service.ts`)
+  - 类名: PascalCase (如: `UserService`)
+  - 函数/变量: camelCase (如: `getUserById`)
+  - 常量: UPPER_SNAKE_CASE (如: `API_BASE_URL`)
+  - 接口: PascalCase,无I前缀 (如: `User`)
+
+### Linting规则
+- **ESLint**: 已配置ESLint,支持TypeScript和React
+- **Prettier**: 统一代码格式化
+- **提交前检查**: 使用husky进行pre-commit钩子检查
+
+### 文档风格
+- **代码注释**: 关键逻辑必须添加注释说明
+- **JSDoc**: 公共API必须包含JSDoc注释
+- **README**: 每个包必须有独立的README说明用途和使用方法
+
+## 开发规范引用
+
+### UI包开发
+开发Web UI包时,**必须**参考并遵循[UI包开发规范](./ui-package-standards.md),该规范基于史诗008(AllIn UI模块移植)的经验总结。
+
+**关键检查点**:
+1. **API路径映射验证**: 开发前必须验证故事中的API路径映射与实际后端路由定义的一致性
+2. **类型推断最佳实践**: 必须使用RPC推断类型,而不是直接导入schema类型
+3. **测试选择器优化**: 必须为关键交互元素添加`data-testid`属性
+4. **表单组件模式**: 必须使用条件渲染两个独立的Form组件
+5. **API调用一致性**: 必须根据实际路由名称修正API调用
+
+**常见错误避免**:
+- ❌ 不要直接导入schema类型(可能导致Date/string类型不匹配)
+- ❌ 不要使用`getByText()`查找可能重复的文本元素
+- ❌ 不要在单个Form组件上动态切换props
+- ❌ 不要使用故事中描述但实际不存在的路由名称
+
+**参考实现**:
+- 广告管理UI包:`packages/advertisement-management-ui`
+- 平台管理UI包:`allin-packages/platform-management-ui`
+- 渠道管理UI包:`allin-packages/channel-management-ui`(史诗008.002)
+
+### Mini UI包开发
+开发Mini UI包(Taro小程序UI包)时,**必须**参考并遵循[Mini UI包开发规范](./mini-ui-package-standards.md),该规范基于史诗011(用人方小程序)和史诗017(人才小程序)的实施经验总结。
+
+**关键检查点**:
+
+#### 1. Taro小程序布局规范
+- **View组件默认横向布局**: View容器内的子元素默认是横向布局(`flex-row`),必须显式添加 `flex flex-col` 类才能实现垂直布局
+- **Text组件默认内联显示**: Text组件默认是内联显示(类似span),需要使用`flex flex-col`强制垂直排列
+
+**正确示例**:
+```typescript
+// ✅ 正确: 使用 flex flex-col 实现垂直布局
+<View className="flex flex-col">
+  <Text>姓名: 张三</Text>
+  <Text>性别: 男</Text>
+  <Text>年龄: 35</Text>
+</View>
+
+// ❌ 错误: 缺少 flex flex-col,子元素会横向排列
+<View>
+  <Text>姓名: 张三</Text>
+  <Text>性别: 男</Text>
+  <Text>年龄: 35</Text>
+</View>
+```
+
+#### 2. 图标使用规范
+- **必须使用Heroicons图标类**: 不要使用emoji或文本符号
+- **图标类命名格式**: `i-heroicons-{icon-name}-{size}-{style}`
+- **必须添加尺寸类**: 如 `w-5 h-5`、`text-lg` 等
+
+**正确示例**:
+```typescript
+// ✅ 正确: 使用Heroicons图标类
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-600" />
+<View className="i-heroicons-user-20-solid w-6 h-6 text-blue-500" />
+<View className="i-heroicons-bell-20-solid w-4 h-4 text-gray-400" />
+
+// ❌ 错误: 使用emoji
+<Text>🔔</Text>
+<Text>👤</Text>
+<View>←</View>
+```
+
+**常用图标**:
+- `chevron-left-20-solid` - 左箭头(返回按钮)
+- `user-20-solid` - 用户
+- `bell-20-solid` - 通知铃
+- `document-text-20-solid` - 文档
+- `chart-bar-20-solid` - 图表
+- `calendar-20-solid` - 日历
+- `phone-20-solid` - 电话
+- `lock-closed-20-solid` - 锁
+- `qr-code-20-solid` - 二维码
+
+#### 3. Navbar导航栏集成规范
+- **TabBar页面(一级)**: 使用Navbar无返回按钮(`leftIcon=""`、`leftText=""`)
+- **非TabBar页面(二级)**: 使用Navbar带返回按钮(`leftIcon="i-heroicons-chevron-left-20-solid"`)
+- **Navbar组件来源**: `@d8d/mini-shared-ui-components/components/navbar`
+
+#### 4. API客户端模式
+- **每个UI包独立管理**: 每个Mini UI包包含自己的API客户端和RPC类型
+- **使用相对路径导入**: UI包内部必须使用相对路径,不要使用别名
+- **RPC推断类型**: 必须使用RPC推断类型,而不是直接导入schema类型
+
+**常见错误避免**:
+- ❌ 不要忘记添加 `flex flex-col` 实现垂直布局
+- ❌ 不要使用emoji代替Heroicons图标
+- ❌ 不要忘记为图标添加尺寸类(`w-5 h-5`、`text-lg`等)
+- ❌ 不要在Mini UI包内部导入中使用别名(`@/`、`~/`等)
+- ❌ 不要使用Vitest作为Mini项目的测试框架(应使用Jest)
+
+**参考实现**:
+- 用人方小程序UI包:`mini-ui-packages/yongren-dashboard-ui`
+- 人才小程序UI包:`mini-ui-packages/rencai-dashboard-ui`
+- 共享组件包:`mini-ui-packages/mini-shared-ui-components`
+
+### 后端模块包开发
+开发后端模块包时,**必须**参考并遵循[后端模块包开发规范](./backend-module-package-standards.md),该规范基于史诗007系列(渠道、平台、公司、残疾管理等模块)的实际实施经验总结。
+
+**关键检查点**:
+
+#### 1. Entity定义规范
+- **完整的列定义**: 必须包含 `type`, `length`, `nullable`, `comment` 等属性
+- **使用索引装饰器**: 使用 `@Index` 定义唯一索引和普通索引
+- **时间戳字段**: 使用 `timestamp` 类型,设置 `default: () => 'CURRENT_TIMESTAMP'`
+- **主键定义**: 使用 `@PrimaryGeneratedColumn`,包含 `unsigned: true` 和 `comment`
+
+**正确示例**:
+```typescript
+@Entity('channel_info')
+export class Channel {
+  @PrimaryGeneratedColumn({
+    name: 'channel_id',
+    type: 'int',
+    unsigned: true,
+    comment: '渠道ID'
+  })
+  id!: number;
+
+  @Column({
+    name: 'channel_name',
+    type: 'varchar',
+    length: 100,
+    nullable: false,
+    comment: '渠道名称'
+  })
+  @Index('idx_channel_name', { unique: true })
+  channelName!: string;
+
+  @Column({
+    name: 'create_time',
+    type: 'timestamp',
+    default: () => 'CURRENT_TIMESTAMP',
+    comment: '创建时间'
+  })
+  createTime!: Date;
+}
+```
+
+#### 2. Service层规范
+- **继承 `GenericCrudService`**: 使用基类提供的CRUD能力
+- **使用 `override` 关键字**: 明确标识覆盖父类方法
+- **软删除实现**: 使用 `status` 字段而非物理删除
+- **业务逻辑检查**: 在调用父类方法前进行验证
+
+**正确示例**:
+```typescript
+export class ChannelService extends GenericCrudService<Channel> {
+  constructor(dataSource: DataSource) {
+    super(dataSource, Channel);
+  }
+
+  override async create(data: Partial<Channel>, userId?: string | number): Promise<Channel> {
+    // 业务逻辑检查
+    const existingChannel = await this.repository.findOne({
+      where: { channelName: data.channelName, status: 1 }
+    });
+    if (existingChannel) {
+      throw new Error('渠道名称已存在');
+    }
+
+    const channelData = {
+      ...data,
+      status: 1,
+      createTime: new Date(),
+      updateTime: new Date()
+    };
+
+    return super.create(channelData, userId);
+  }
+}
+```
+
+#### 3. 路由层规范
+- **使用 `OpenAPIHono`**: 而非普通的 `Hono`
+- **使用 `AuthContext` 泛型**: 提供类型安全的认证上下文
+- **自定义路由必须使用 `parseWithAwait`**: 验证响应数据符合Schema定义
+- **使用 `createZodErrorResponse`**: 处理Zod验证错误
+
+**正确示例**:
+```typescript
+import { parseWithAwait, createZodErrorResponse } from '@d8d/shared-utils';
+
+channelCustomRoutes.get('/statistics/:id', async (c) => {
+  try {
+    const result = await channelService.getStatistics(id);
+
+    // ✅ 必须:使用 parseWithAwait 验证和转换响应数据
+    const validatedResult = await parseWithAwait(ChannelSchema, result);
+    return c.json(validatedResult, 200);
+  } catch (error) {
+    if (error instanceof z.ZodError) {
+      return c.json(createZodErrorResponse(error), 400);
+    }
+    return c.json({ code: 500, message: error.message }, 500);
+  }
+});
+```
+
+#### 4. Schema规范
+- **使用 `.openapi()` 装饰器**: 添加描述和示例
+- **使用 `z.coerce.date<Date>()` 和 `z.coerce.number<number>()`**: Zod 4.0需要添加泛型参数
+- **不导出推断类型**: 类型由RPC自动推断,不需要手动导出 `z.infer<typeof Schema>`
+
+**正确示例**:
+```typescript
+export const ChannelSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '渠道ID',
+    example: 1
+  }),
+  channelName: z.string().max(100).openapi({
+    description: '渠道名称',
+    example: '微信小程序'
+  }),
+  createTime: z.coerce.date<Date>().openapi({
+    description: '创建时间',
+    example: '2024-01-01T00:00:00Z'
+  })
+});
+```
+
+**常见错误避免**:
+- ❌ Entity列定义不要省略 `type`, `comment`, `nullable` 等属性
+- ❌ Service覆盖方法不要忘记使用 `override` 关键字
+- ❌ 自定义路由不要省略 `parseWithAwait` 验证
+- ❌ Schema中不要使用 `z.coerce.date()` 或 `z.coerce.number()`(必须添加泛型)
+- ❌ Schema不要导出 `z.infer` 推断的类型(类型由RPC自动推断)
+- ❌ 不要使用物理删除(应使用 `status` 字段实现软删除)
+
+**参考实现**:
+- 渠道模块:`allin-packages/channel-module`
+- 平台模块:`allin-packages/platform-module`
+- 公司模块:`allin-packages/company-module`
+- 残疾管理模块:`allin-packages/disability-module`
+- 认证模块:`packages/core-module/auth-module`
+- 用户模块:`packages/core-module/user-module`
+
+## 类型安全
+
+### TypeScript配置
+```json
+{
+  "compilerOptions": {
+    "strict": true,
+    "noImplicitAny": true,
+    "strictNullChecks": true,
+    "strictFunctionTypes": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  }
+}
+```
+
+### 类型定义
+```typescript
+// ✅ 推荐: 使用interface定义对象形状
+interface User {
+  id: number;
+  username: string;
+  email: string;
+  createdAt: Date;
+}
+
+// ✅ 推荐: 使用type定义联合类型
+type Status = 'pending' | 'active' | 'inactive';
+
+// ✅ 推荐: 使用泛型增强类型复用
+interface ApiResponse<T> {
+  status: number;
+  data: T;
+  message?: string;
+}
+```
+
+## 错误处理
+
+### 统一错误处理
+```typescript
+// 定义自定义错误类
+class ValidationError extends Error {
+  constructor(public errors: Record<string, string[]>) {
+    super('验证失败');
+    this.name = 'ValidationError';
+  }
+}
+
+// 使用自定义错误
+function validateUser(data: unknown): User {
+  const result = userSchema.safeParse(data);
+  if (!result.success) {
+    throw new ValidationError(result.error.flatten().fieldErrors);
+  }
+  return result.data;
+}
+```
+
+### 错误日志
+```typescript
+import { logger } from '@d8d/shared-utils/logger';
+
+try {
+  await userService.createUser(userData);
+} catch (error) {
+  logger.error('创建用户失败', {
+    error: error.message,
+    stack: error.stack,
+    userData: JSON.stringify(userData)
+  });
+  throw error;
+}
+```
+
+## 安全最佳实践
+
+### 输入验证
+```typescript
+// ✅ 使用Schema验证
+import { userSchema } from './schemas/user.schema';
+
+const validatedData = await userSchema.parseAsync(inputData);
+
+// ❌ 不要直接信任用户输入
+const user = { username: req.body.username }; // 不安全
+```
+
+### 敏感数据处理
+```typescript
+// ✅ 从响应中排除敏感字段
+function sanitizeUser(user: User): Partial<User> {
+  const { password, ...sanitized } = user;
+  return sanitized;
+}
+
+// ✅ 日志中不记录敏感信息
+logger.info('用户登录', { userId: user.id }); // 正确
+logger.info('用户登录', { user }); // 错误 - 会记录密码
+```
+
+### SQL注入防护
+```typescript
+// ✅ 使用TypeORM参数化查询
+const users = await userRepo.find({
+  where: { username: username }
+});
+
+// ❌ 不要拼接SQL字符串
+const query = `SELECT * FROM users WHERE username = '${username}'`; // 危险
+```
+
+## 性能优化
+
+### 数据库查询优化
+```typescript
+// ✅ 只查询需要的字段
+const users = await userRepo.find({
+  select: ['id', 'username', 'email']
+});
+
+// ✅ 使用索引字段查询
+const user = await userRepo.findOne({
+  where: { email: userEmail } // email字段应有索引
+});
+
+// ❌ 避免N+1查询
+const orders = await orderRepo.find({
+  relations: ['user', 'products'] // 使用join而不是循环查询
+});
+```
+
+### 缓存策略
+```typescript
+// ✅ 使用Redis缓存
+import { cacheGet, cacheSet } from '@d8d/shared-utils/redis.util';
+
+async function getUserById(id: number) {
+  const cached = await cacheGet(`user:${id}`);
+  if (cached) return JSON.parse(cached);
+
+  const user = await userRepo.findOne({ where: { id } });
+  await cacheSet(`user:${id}`, JSON.stringify(user), 3600);
+  return user;
+}
+```
+
+## 代码组织
+
+### 文件结构
+```
+packages/user-module/
+├── src/
+│   ├── entities/        # 数据实体
+│   ├── services/        # 业务逻辑
+│   ├── schemas/         # 验证Schema
+│   ├── routes/          # API路由
+│   ├── middleware/      # 中间件
+│   ├── utils/           # 工具函数
+│   └── index.ts         # 包入口
+├── tests/               # 测试文件
+├── README.md            # 包说明
+└── package.json
+```
+
+### 导出规范
+```typescript
+// index.ts - 统一导出
+export * from './entities';
+export * from './services';
+export { userRoutes } from './routes';
+export { userSchema } from './schemas';
+```
+
+## 相关文档
+
+### 测试规范
+- [测试策略概述](./testing-strategy.md)
+- [Web UI包测试规范](./web-ui-testing-standards.md)
+- [Web Server包测试规范](./web-server-testing-standards.md)
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [Mini UI包测试规范](./mini-ui-testing-standards.md)
+
+### 开发规范
+- [UI包开发规范](./ui-package-standards.md)
+- [Mini UI包开发规范](./mini-ui-package-standards.md)
+- [后端模块包开发规范](./backend-module-package-standards.md)
+- [API设计规范](./api-design-integration.md)
+
+---
+
+**文档状态**: 正式版
+**下次评审**: 2026-01-26

+ 5 - 5
docs/architecture/component-architecture.md

@@ -62,7 +62,7 @@ src/server/
 │   ├── users/             # 用户管理路由
 │   │   ├── index.ts       # 用户列表路由
 │   │   ├── [id].ts        # 用户详情路由
-│   │   └── __tests__/     # 路由测试
+│   │   └── tests/         # 路由测试
 │   ├── roles/             # 角色管理路由
 │   ├── files/              # 文件管理路由
 │   │   ├── multipart-policy/    # 多部分上传策略
@@ -73,17 +73,17 @@ src/server/
 ├── modules/               # 业务模块层
 │   ├── auth/              # 认证业务模块
 │   │   ├── auth.service.ts # 认证服务
-│   │   └── __tests__/     # 认证测试
+│   │   └── tests/         # 认证测试
 │   ├── users/             # 用户业务模块
 │   │   ├── user.entity.ts  # 用户实体
 │   │   ├── user.service.ts # 用户服务
-│   │   └── __tests__/     # 用户测试
+│   │   └── tests/         # 用户测试
 │   ├── files/              # 文件业务模块
 │   │   ├── file.entity.ts  # 文件实体
 │   │   ├── file.service.ts # 文件服务
 │   │   ├── minio.service.ts # MinIO服务
 │   │   ├── file.schema.ts  # 文件验证Schema
-│   │   └── __tests__/     # 文件测试
+│   │   └── tests/         # 文件测试
 ├── utils/                 # 工具层
 │   ├── generic-crud.service.ts  # 通用CRUD服务
 │   ├── generic-crud.routes.ts   # 通用CRUD路由
@@ -91,7 +91,7 @@ src/server/
 │   ├── backup.ts          # 数据库备份工具
 │   ├── restore.ts         # 数据库恢复工具
 │   ├── logger.ts          # 日志工具
-│   └── __tests__/         # 工具测试
+│   └── tests/             # 工具测试
 ├── middleware/            # 中间件层
 │   ├── auth.ts           # 认证中间件
 │   └── validation.ts     # 验证中间件

+ 39 - 0
docs/architecture/index.md

@@ -43,6 +43,45 @@
     - [现有标准合规性](./coding-standards.md#现有标准合规性)
     - [增强特定标准](./coding-standards.md#增强特定标准)
     - [关键集成规则](./coding-standards.md#关键集成规则)
+    - [UI包开发提示](./coding-standards.md#ui包开发提示)
+    - [Mini UI包开发提示](./coding-standards.md#mini-ui包开发提示)
+  - [后端模块包开发规范](./backend-module-package-standards.md)
+    - [包结构规范](./backend-module-package-standards.md#包结构规范)
+    - [实体设计规范](./backend-module-package-standards.md#实体设计规范)
+    - [数据库类型规范](./backend-module-package-standards.md#数据库类型规范)
+    - [服务层规范](./backend-module-package-standards.md#服务层规范)
+    - [路由层规范](./backend-module-package-standards.md#路由层规范)
+    - [验证系统规范](./backend-module-package-standards.md#验证系统规范)
+    - [模块集成规范](./backend-module-package-standards.md#模块集成规范)
+    - [测试规范](./backend-module-package-standards.md#测试规范)
+    - [开发流程规范](./backend-module-package-standards.md#开发流程规范)
+    - [错误处理规范](./backend-module-package-standards.md#错误处理规范)
+    - [参考实现](./backend-module-package-standards.md#参考实现)
+  - [UI包开发规范](./ui-package-standards.md)
+    - [包结构规范](./ui-package-standards.md#包结构规范)
+    - [RPC客户端实现规范](./ui-package-standards.md#rpc客户端实现规范)
+    - [组件开发规范](./ui-package-standards.md#组件开发规范)
+    - [类型定义规范](./ui-package-standards.md#类型定义规范)
+    - [状态管理规范](./ui-package-standards.md#状态管理规范)
+    - [测试规范](./ui-package-standards.md#测试规范)
+    - [构建和发布规范](./ui-package-standards.md#构建和发布规范)
+    - [集成规范](./ui-package-standards.md#集成规范)
+    - [错误处理规范](./ui-package-standards.md#错误处理规范)
+    - [开发流程规范](./ui-package-standards.md#开发流程规范)
+    - [参考实现](./ui-package-standards.md#参考实现)
+  - [Mini UI包开发规范](./mini-ui-package-standards.md)
+    - [概述](./mini-ui-package-standards.md#概述)
+    - [Taro小程序核心布局规范](./mini-ui-package-standards.md#taro小程序核心布局规范)
+    - [图标使用规范](./mini-ui-package-standards.md#图标使用规范)
+    - [Taro组件使用规范](./mini-ui-package-standards.md#taro组件使用规范)
+    - [Navbar导航栏集成规范](./mini-ui-package-standards.md#navbar导航栏集成规范)
+    - [照片预览功能实现](./mini-ui-package-standards.md#照片预览功能实现)
+    - [数据脱敏规范](./mini-ui-package-standards.md#数据脱敏规范)
+    - [Mini UI包结构规范](./mini-ui-package-standards.md#mini-ui包结构规范)
+    - [测试规范](./mini-ui-package-standards.md#测试规范)
+    - [常见问题和解决方案](./mini-ui-package-standards.md#常见问题和解决方案)
+    - [最佳实践](./mini-ui-package-standards.md#最佳实践)
+    - [参考实现](./mini-ui-package-standards.md#参考实现)
   - [安全集成](./security-integration.md)
     - [现有安全措施](./security-integration.md#现有安全措施)
     - [增强安全要求](./security-integration.md#增强安全要求)

+ 1236 - 0
docs/architecture/mini-ui-package-standards.md

@@ -0,0 +1,1236 @@
+# Mini UI包开发规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.1 | 2025-12-26 | 添加图标使用规范(Heroicons) | Bob (Scrum Master) |
+| 1.0 | 2025-12-26 | 基于史诗011和017经验创建Mini UI包开发规范 | Bob (Scrum Master) |
+
+## 概述
+
+本文档专门针对Taro小程序UI包(mini-ui-packages)的开发规范,基于史诗011(用人方小程序)和史诗017(人才小程序)的实施经验总结。Mini UI包与Web UI包有显著的差异,特别是在布局、组件行为和平台特性方面。
+
+**适用范围:**
+- `mini-ui-packages/` 目录下的所有UI包
+- 使用Taro框架的小程序项目
+- 所有基于`@tarojs/components`的组件开发
+
+## Taro小程序核心布局规范
+
+### 1. View组件的默认布局行为
+
+**重要**: 在Taro小程序中,`<View>` 组件内的子元素默认是**横向布局**(`flex-row`),这与Web开发的div默认垂直布局行为完全不同。
+
+#### 1.1 垂直布局规范
+
+**问题**: View容器默认横向布局,导致子元素横向排列
+
+**解决方案**: 必须显式添加 `flex flex-col` 类才能实现垂直布局
+
+**正确示例**:
+```typescript
+import { View, Text } from '@tarojs/components'
+
+// ✅ 正确: 使用 flex flex-col 实现垂直布局
+<View className="flex flex-col">
+  <Text>姓名: 张三</Text>
+  <Text>性别: 男</Text>
+  <Text>年龄: 35</Text>
+</View>
+
+// ❌ 错误: 缺少 flex flex-col,子元素会横向排列
+<View>
+  <Text>姓名: 张三</Text>
+  <Text>性别: 男</Text>
+  <Text>年龄: 35</Text>
+</View>
+```
+
+#### 1.2 信息卡片布局模式
+
+**来源**: 史诗011.003经验总结
+
+**标准模式**:
+```typescript
+import { View, Text } from '@tarojs/components'
+
+export function PersonalBasicInfo({ personalInfo }: { personalInfo: PersonalInfoResponse }) {
+  return (
+    <View className="bg-white rounded-lg p-4">
+      <Text className="text-lg font-semibold mb-4">个人基本信息</Text>
+
+      {/* 垂直布局的信息列表 - 必须使用 flex flex-col */}
+      <View className="flex flex-col space-y-2">
+        <View className="flex justify-between">
+          <Text className="text-gray-600">姓名</Text>
+          <Text>{personalInfo.name}</Text>
+        </View>
+
+        <View className="flex justify-between">
+          <Text className="text-gray-600">性别</Text>
+          <Text>{personalInfo.gender}</Text>
+        </View>
+
+        <View className="flex justify-between">
+          <Text className="text-gray-600">年龄</Text>
+          <Text>{personalInfo.age}</Text>
+        </View>
+
+        {/* 更多字段... */}
+      </View>
+    </View>
+  )
+}
+```
+
+**关键点**:
+1. **信息列表容器**必须使用 `flex flex-col` 实现垂直布局
+2. **每个信息项**使用 `flex justify-between` 实现标签和值的左右分布
+3. 使用 `space-y-2` 或 `space-y-3` 添加垂直间距
+4. **重要**: 记住在所有需要垂直排列的 View 上添加 `flex flex-col`
+
+### 2. Text组件的默认内联行为
+
+**问题**: Text组件默认是内联显示(类似Web中的`<span>`),不会自动换行
+
+**影响**: 导致多个Text组件在同一行显示,即使它们在不同的代码行上
+
+**解决方案**: 使用`flex flex-col`强制Text组件垂直排列
+
+**实际案例** (来源: 史诗011.003):
+```typescript
+// ❌ 错误: Text组件会内联显示在同一行
+<View>
+  <Text>统计1</Text>
+  <Text>统计2</Text>
+  <Text>统计3</Text>
+</View>
+
+// ✅ 正确: 使用 flex flex-col 强制垂直排列
+<View className="flex flex-col">
+  <Text>统计1</Text>
+  <Text>统计2</Text>
+  <Text>统计3</Text>
+</View>
+```
+
+### 3. 常见布局模式
+
+#### 3.1 卡片容器布局
+
+```typescript
+<View className="bg-white rounded-lg p-4 shadow-sm">
+  <Text className="text-lg font-semibold mb-4">卡片标题</Text>
+
+  {/* 内容区域 - 垂直布局 */}
+  <View className="flex flex-col space-y-3">
+    {items.map(item => (
+      <View key={item.id} className="flex justify-between border-b pb-2">
+        <Text className="text-gray-600">{item.label}</Text>
+        <Text>{item.value}</Text>
+      </View>
+    ))}
+  </View>
+</View>
+```
+
+#### 3.2 列表项布局
+
+```typescript
+<View className="flex flex-col space-y-2">
+  {list.map(item => (
+    <View key={item.id} className="bg-white rounded-lg p-3">
+      {/* 列表项标题 */}
+      <Text className="font-semibold mb-2">{item.title}</Text>
+
+      {/* 列表项内容 - 垂直布局 */}
+      <View className="flex flex-col space-y-1">
+        <Text className="text-sm text-gray-600">{item.description}</Text>
+        <Text className="text-xs text-gray-400">{item.date}</Text>
+      </View>
+    </View>
+  ))}
+</View>
+```
+
+#### 3.3 网格布局
+
+```typescript
+{/* 2列网格 */}
+<View className="grid grid-cols-2 gap-3">
+  {items.map(item => (
+    <View key={item.id} className="bg-white rounded-lg p-3">
+      <View className="flex flex-col">
+        <Text className="font-semibold">{item.title}</Text>
+        <Text className="text-sm text-gray-600">{item.value}</Text>
+      </View>
+    </View>
+  ))}
+</View>
+```
+
+## 图标使用规范
+
+### 3.1 图标系统概述
+
+**项目使用的图标库**: Heroicons (UnoCSS图标集)
+
+**图标类命名规范**: `i-heroicons-{icon-name}-{size}-{style}`
+
+**重要**: **不要使用emoji**,必须使用Heroicons图标类。
+
+### 3.2 图标类使用规范
+
+#### 3.2.1 基础图标使用
+
+**正确示例**:
+```typescript
+import { View } from '@tarojs/components'
+
+// ✅ 正确: 使用Heroicons图标类
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-600" />
+<View className="i-heroicons-user-20-solid w-6 h-6 text-blue-500" />
+<View className="i-heroicons-bell-20-solid w-4 h-4 text-gray-400" />
+
+// ❌ 错误: 使用emoji
+<Text>🔔</Text>
+<Text>👤</Text>
+<View>←</View>
+```
+
+#### 3.2.2 图标类命名格式
+
+**格式**: `i-heroicons-{图标名称}-{尺寸}-{样式}`
+
+**常用图标名称**:
+- `chevron-left` - 左箭头
+- `chevron-right` - 右箭头
+- `user` - 用户
+- `bell` - 通知铃
+- `document-text` - 文档
+- `chart-bar` - 图表
+- `building-office` - 建筑/企业
+- `calendar` - 日历
+- `phone` - 电话
+- `lock-closed` - 锁
+- `camera` - 相机
+- `qr-code` - 二维码
+- `device-phone-mobile` - 手机
+- `arrow-right-on-rectangle` - 登出/外跳
+- `arrow-left-on-rectangle` - 登入/内跳
+- `exclamation-triangle` - 警告
+- `exclamation-circle` - 提示
+- `photo` - 图片
+- `arrow-path` - 加载中
+
+**尺寸选项**:
+- `20` - 20x20 (推荐用于小程序)
+- `24` - 24x24
+
+**样式选项**:
+- `solid` - 实心图标(推荐)
+- `outline` - 轮廓图标
+
+#### 3.2.3 图标尺寸和颜色
+
+**尺寸类**:
+```typescript
+<View className="i-heroicons-user-20-solid w-4 h-4" />   // 16px
+<View className="i-heroicons-user-20-solid w-5 h-5" />   // 20px
+<View className="i-heroicons-user-20-solid w-6 h-6" />   // 24px
+<View className="i-heroicons-user-20-solid w-8 h-8" />   // 32px
+<View className="i-heroicons-user-20-solid text-xl" />  // 使用Tailwind文本尺寸
+<View className="i-heroicons-user-20-solid text-2xl" /> // 使用Tailwind文本尺寸
+```
+
+**颜色类**:
+```typescript
+<View className="i-heroicons-user-20-solid text-gray-400" />  // 灰色
+<View className="i-heroicons-user-20-solid text-blue-500" />  // 蓝色
+<View className="i-heroicons-user-20-solid text-green-500" /> // 绿色
+<View className="i-heroicons-user-20-solid text-red-500" />   // 红色
+<View className="i-heroicons-user-20-solid text-white" />    // 白色
+<View className="i-heroicons-user-20-solid text-yellow-600" /> // 黄色
+<View className="i-heroicons-user-20-solid text-purple-500" /> // 紫色
+```
+
+#### 3.2.4 常见使用场景
+
+**导航栏返回按钮**:
+```typescript
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5" />
+```
+
+**功能入口图标**:
+```typescript
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-user-20-solid text-blue-500 text-lg mb-1" />
+  <Text className="text-sm">个人信息</Text>
+</View>
+
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-document-text-20-solid text-green-500 text-lg mb-1" />
+  <Text className="text-sm">考勤记录</Text>
+</View>
+
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-chart-bar-20-solid text-purple-500 text-lg mb-1" />
+  <Text className="text-sm">薪资查询</Text>
+</View>
+
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-building-office-2-20-solid text-yellow-600 text-lg mb-1" />
+  <Text className="text-sm">企业信息</Text>
+</View>
+```
+
+**状态指示器**:
+```typescript
+// 加载中
+<View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5" />
+
+// 成功/提示
+<View className="i-heroicons-check-circle-20-solid text-green-500 w-6 h-6" />
+
+// 警告
+<View className="i-heroicons-exclamation-triangle-20-solid text-orange-500 w-6 h-6" />
+
+// 错误
+<View className="i-heroicons-x-circle-20-solid text-red-500 w-6 h-6" />
+
+// 信息
+<View className="i-heroicons-information-circle-20-solid text-blue-500 w-6 h-6" />
+```
+
+**输入框图标**:
+```typescript
+<View className="flex items-center">
+  <View className="i-heroicons-phone-20-solid text-gray-400 mr-3 w-5 h-5" />
+  <Input placeholder="请输入手机号" />
+</View>
+
+<View className="flex items-center">
+  <View className="i-heroicons-lock-closed-20-solid text-gray-400 mr-3 w-5 h-5" />
+  <Input placeholder="请输入密码" type="password" />
+</View>
+```
+
+**二维码按钮**:
+```typescript
+<View className="flex items-center">
+  <Text>张三</Text>
+  <View className="i-heroicons-qr-code-20-solid text-white text-lg ml-2" />
+</View>
+```
+
+#### 3.2.5 动画效果
+
+**旋转动画**:
+```typescript
+<View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5" />
+```
+
+**脉冲动画**:
+```typescript
+<View className="i-heroicons-bell-20-solid animate-pulse w-6 h-6" />
+```
+
+### 3.3 Navbar图标规范
+
+**返回按钮图标**:
+```typescript
+leftIcon = 'i-heroicons-chevron-left-20-solid'
+```
+
+**示例**:
+```typescript
+<Navbar
+  title="页面标题"
+  leftIcon="i-heroicons-chevron-left-20-solid"
+  leftText="返回"
+  onClickLeft={() => Taro.navigateBack()}
+/>
+```
+
+### 3.4 TabBar图标规范
+
+**使用iconClass属性**(推荐):
+```typescript
+const tabList = [
+  {
+    title: '首页',
+    iconClass: 'i-heroicons-home-20-solid',
+    selectedIconClass: 'i-heroicons-home-20-solid',
+    pagePath: '/pages/index/index'
+  },
+  {
+    title: '考勤',
+    iconClass: 'i-heroicons-calendar-20-solid',
+    selectedIconClass: 'i-heroicons-calendar-20-solid',
+    pagePath: '/pages/attendance/index'
+  }
+]
+```
+
+### 3.5 图标查找参考
+
+**Heroicons官方图标库**: https://heroicons.com/
+
+**使用UnoCSS图标集**: 项目使用UnoCSS的Heroicons图标集,所有图标名称遵循Heroicons命名规范。
+
+**查找图标的方法**:
+1. 访问 Heroicons 官网查找所需图标
+2. 记录图标名称(如 `user`, `chevron-left`)
+3. 使用格式: `i-heroicons-{图标名称}-20-solid`
+4. 添加尺寸和颜色类
+
+### 3.6 常见错误避免
+
+**错误示例**:
+```typescript
+// ❌ 错误1: 使用emoji
+<Text>🔔 通知</Text>
+<View>👤</View>
+
+// ❌ 错误2: 使用文本符号
+<Text>← 返回</Text>
+<View>→</View>
+
+// ❌ 错误3: 使用其他图标库
+<View className="fa fa-user" />
+<View className="material-icons">person</View>
+
+// ❌ 错误4: 忘记添加尺寸类
+<View className="i-heroicons-user-20-solid" />  {/* 没有尺寸,可能不显示 */}
+
+// ❌ 错误5: 图标类名拼写错误
+<View className="i-heroicon-user-20-solid" />   {/* 缺少s */}
+<View className="i-heroicons-user-20-solid" />   {/* ✅ 正确 */}
+```
+
+**正确示例**:
+```typescript
+// ✅ 正确1: 使用Heroicons图标类
+<View className="i-heroicons-bell-20-solid w-5 h-5" />
+<View className="i-heroicons-user-20-solid w-6 h-6" />
+
+// ✅ 正确2: 添加尺寸和颜色
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-600" />
+
+// ✅ 正确3: 图标+文本组合
+<View className="flex items-center">
+  <View className="i-heroicons-phone-20-solid text-blue-500 w-5 h-5 mr-2" />
+  <Text>联系电话</Text>
+</View>
+```
+
+## Taro组件使用规范
+
+### 4.1 基础组件导入
+
+```typescript
+import { View, Text, Image, Button, ScrollView } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+```
+
+### 4.2 Image组件规范
+
+```typescript
+<Image
+  src={imageUrl}
+  mode="aspectFill"  // 或 aspectFit, widthFix
+  className="w-full h-32 rounded-lg"
+  lazyLoad          // 懒加载
+  onClick={handleClick}
+/>
+```
+
+**mode模式说明**:
+- `aspectFill`: 保持纵横比缩放图片,确保图片填充整个容器(可能裁剪)
+- `aspectFit`: 保持纵横比缩放图片,确保图片完全显示(可能有空白)
+- `widthFix`: 宽度不变,高度自动变化,保持原图宽高比
+
+### 4.3 ScrollView组件规范
+
+```typescript
+<ScrollView
+  scrollY           // 垂直滚动
+  className="h-full"
+  onScrollToLower={handleLoadMore}
+  lowerThreshold={100}
+>
+  <View className="flex flex-col">
+    {items.map(item => (
+      <View key={item.id}>{item.content}</View>
+    ))}
+  </View>
+</ScrollView>
+```
+
+### 4.4 Button组件规范
+
+**注意**: Taro的Button组件有默认样式,如需自定义样式建议使用View
+
+```typescript
+// ✅ 推荐: 使用View实现自定义按钮
+<View
+  className="bg-blue-500 text-white py-2 px-4 rounded text-center"
+  onClick={handleClick}
+>
+  <Text>确定</Text>
+</View>
+
+// ⚠️ 谨慎使用: Taro Button组件有平台默认样式
+<Button onClick={handleClick}>确定</Button>
+```
+
+## Navbar导航栏集成规范
+
+### 5.1 Navbar组件来源
+
+```typescript
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+```
+
+### 5.2 页面层级划分
+
+**TabBar页面(一级,无返回按钮)**:
+- 首页/个人主页
+- 列表页
+- 个人信息页
+- 设置页
+
+**非TabBar页面(二级,带返回按钮)**:
+- 详情页
+- 编辑页
+- 从其他页面跳转来的页面
+
+### 5.3 Navbar配置规范
+
+**TabBar页面(无返回按钮)**:
+```typescript
+<Navbar
+  title="页面标题"
+  leftIcon=""
+  leftText=""
+  onClickLeft={() => {}}
+  placeholder
+  fixed
+/>
+```
+
+**非TabBar页面(带返回按钮)**:
+```typescript
+import Taro from '@tarojs/taro'
+
+<Navbar
+  title="页面标题"
+  leftIcon="i-heroicons-chevron-left-20-solid"
+  leftText="返回"
+  onClickLeft={() => Taro.navigateBack()}
+  placeholder
+  fixed
+/>
+```
+
+### 5.4 完整页面结构示例
+
+```typescript
+import { View, ScrollView } from '@tarojs/components'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+
+export function MyPage() {
+  return (
+    <View className="h-screen bg-gray-100">
+      {/* Navbar导航栏 */}
+      <Navbar
+        title="页面标题"
+        leftIcon=""
+        leftText=""
+        onClickLeft={() => {}}
+        placeholder
+        fixed
+      />
+
+      {/* 页面内容 */}
+      <ScrollView scrollY className="h-full">
+        <View className="flex flex-col space-y-3 p-4">
+          {/* 页面内容 */}
+        </View>
+      </ScrollView>
+    </View>
+  )
+}
+```
+
+## 照片预览功能实现
+
+### 6.1 使用Taro.previewImage
+
+```typescript
+import Taro from '@tarojs/taro'
+import { View, Image, Text } from '@tarojs/components'
+
+interface DocumentPhotoItemProps {
+  type: string
+  url: string
+}
+
+export function DocumentPhotoItem({ type, url }: DocumentPhotoItemProps) {
+  const handlePreview = () => {
+    Taro.previewImage({
+      current: url,    // 当前显示图片的http链接
+      urls: [url]      // 需要预览的图片http链接列表
+    })
+  }
+
+  return (
+    <View onClick={handlePreview} className="flex flex-col">
+      <Image
+        src={url}
+        mode="aspectFill"
+        className="w-full h-32 rounded-lg"
+      />
+      <Text>{type}</Text>
+    </View>
+  )
+}
+```
+
+**多图片预览**:
+```typescript
+const handlePreview = (currentIndex: number) => {
+  Taro.previewImage({
+    current: images[currentIndex].url,
+    urls: images.map(img => img.url)
+  })
+}
+```
+
+## 数据脱敏规范
+
+### 7.1 银行卡号脱敏
+
+```typescript
+/**
+ * 脱敏银行卡号
+ * @param cardNumber 完整银行卡号
+ * @returns 脱敏后的银行卡号(如:**** **** **** 1234)
+ */
+export function maskCardNumber(cardNumber: string): string {
+  if (!cardNumber || cardNumber.length < 4) {
+    return '****'
+  }
+  const last4 = cardNumber.slice(-4)
+  return `**** **** **** ${last4}`
+}
+
+// 使用示例
+<View className="flex justify-between">
+  <Text className="text-gray-600">银行卡号</Text>
+  <Text>{maskCardNumber(bankCard.cardNumber)}</Text>
+</View>
+```
+
+### 7.2 身份证号脱敏
+
+```typescript
+/**
+ * 脱敏身份证号
+ * @param idCard 完整身份证号
+ * @returns 脱敏后的身份证号(如:3301**********1234)
+ */
+export function maskIdCard(idCard: string): string {
+  if (!idCard || idCard.length < 8) {
+    return '********'
+  }
+  const prefix = idCard.slice(0, 4)
+  const suffix = idCard.slice(-4)
+  return `${prefix}**********${suffix}`
+}
+
+// 使用示例
+<View className="flex justify-between">
+  <Text className="text-gray-600">身份证号</Text>
+  <Text>{maskIdCard(personalInfo.idCard)}</Text>
+</View>
+```
+
+### 7.3 手机号脱敏
+
+```typescript
+/**
+ * 脱敏手机号
+ * @param phone 完整手机号
+ * @returns 脱敏后的手机号(如:138****5678)
+ */
+export function maskPhone(phone: string): string {
+  if (!phone || phone.length < 7) {
+    return '****'
+  }
+  return `${phone.slice(0, 3)}****${phone.slice(-4)}`
+}
+```
+
+## Mini UI包结构规范
+
+### 8.1 标准目录结构
+
+```text
+mini-ui-packages/<package-name>/
+├── src/
+│   ├── pages/                    # 页面组件
+│   │   └── PageName/
+│   │       ├── PageName.tsx
+│   │       └── index.ts
+│   ├── components/               # UI组件
+│   │   ├── ComponentName.tsx
+│   │   └── index.ts
+│   ├── api/                      # API客户端
+│   │   ├── client.ts
+│   │   └── index.ts
+│   ├── utils/                    # 工具函数
+│   │   ├── helpers.ts
+│   │   └── index.ts
+│   └── index.ts                  # 主入口
+├── tests/                        # 测试文件
+│   ├── pages/
+│   │   └── PageName/
+│   │       └── PageName.test.tsx
+│   └── components/
+│       └── ComponentName.test.tsx
+├── package.json
+├── jest.config.cjs               # Jest配置
+└── tsconfig.json
+```
+
+### 8.2 package.json配置
+
+```json
+{
+  "name": "@d8d/<package-name>",
+  "version": "1.0.0",
+  "type": "module",
+  "main": "src/index.ts",
+  "types": "src/index.ts",
+  "exports": {
+    ".": {
+      "types": "./src/index.ts",
+      "import": "./src/index.ts"
+    },
+    "./api": {
+      "types": "./src/api/index.ts",
+      "import": "./src/api/index.ts"
+    },
+    "./pages/<PageName>/<PageName>": {
+      "types": "./src/pages/<PageName>/<PageName>.tsx",
+      "import": "./src/pages/<PageName>/<PageName>.tsx"
+    }
+  },
+  "dependencies": {
+    "@d8d/mini-shared-ui-components": "workspace:*",
+    "@tarojs/components": "^4.1.4",
+    "@tarojs/taro": "^4.1.4",
+    "react": "^19.1.0"
+  },
+  "devDependencies": {
+    "@testing-library/react": "^16.3.0",
+    "jest": "^30.2.0",
+    "ts-jest": "^29.4.5"
+  }
+}
+```
+
+## 数据获取规范
+
+### 9.1 使用React Query管理服务端状态
+
+**重要**: Mini UI包必须使用React Query (`@tanstack/react-query`) 管理服务端状态,而不是手动使用`useState` + `useEffect`。
+
+**原因**:
+- 符合项目技术栈要求(见`component-architecture.md`)
+- 自动处理加载状态、错误状态、缓存
+- 更好的类型推断和RPC集成
+- 统一的数据获取模式
+
+#### 9.1.1 基本用法
+
+```typescript
+import { useQuery } from '@tanstack/react-query'
+import { apiClient } from '../api'
+
+const MyPage: React.FC = () => {
+  // ✅ 正确: 使用React Query
+  const { data, isLoading, error } = useQuery({
+    queryKey: ['resource-name'],
+    queryFn: async () => {
+      const res = await apiClient.resource.$get()
+      if (!res.ok) {
+        throw new Error('获取数据失败')
+      }
+      return await res.json()
+    }
+  })
+
+  // ❌ 错误: 不要使用useState + useEffect手动管理状态
+  // const [data, setData] = useState(null)
+  // const [loading, setLoading] = useState(true)
+  // useEffect(() => {
+  //   const fetchData = async () => {
+  //     setLoading(true)
+  //     const res = await apiClient.resource.$get()
+  //     const data = await res.json()
+  //     setData(data)
+  //     setLoading(false)
+  //   }
+  //   fetchData()
+  // }, [])
+
+  if (isLoading) return <div>加载中...</div>
+  if (error) return <div>加载失败</div>
+
+  return <div>{/* 渲染数据 */}</div>
+}
+```
+
+#### 9.1.2 多个独立查询
+
+```typescript
+const MyPage: React.FC = () => {
+  // 多个独立的查询可以并行执行
+  const { data: statusData, isLoading: statusLoading } = useQuery({
+    queryKey: ['employment-status'],
+    queryFn: async () => {
+      const res = await apiClient.employment.status.$get()
+      if (!res.ok) throw new Error('获取就业状态失败')
+      return await res.json()
+    }
+  })
+
+  const { data: recordsData, isLoading: recordsLoading } = useQuery({
+    queryKey: ['salary-records'],
+    queryFn: async () => {
+      const res = await apiClient.employment['salary-records'].$get({
+        query: { take: 3 }
+      })
+      if (!res.ok) throw new Error('获取薪资记录失败')
+      const data = await res.json()
+      return data.data || []
+    }
+  })
+
+  const { data: historyData, isLoading: historyLoading } = useQuery({
+    queryKey: ['employment-history'],
+    queryFn: async () => {
+      const res = await apiClient.employment.history.$get({
+        query: { take: 20 }
+      })
+      if (!res.ok) throw new Error('获取就业历史失败')
+      const data = await res.json()
+      return data.data || []
+    }
+  })
+
+  // 每个查询有独立的loading状态
+  return (
+    <View>
+      <StatusCard data={statusData} loading={statusLoading} />
+      <RecordsCard data={recordsData} loading={recordsLoading} />
+      <HistoryCard data={historyData} loading={historyLoading} />
+    </View>
+  )
+}
+```
+
+#### 9.1.3 错误处理
+
+```typescript
+const MyPage: React.FC = () => {
+  const { data, isLoading, error } = useQuery({
+    queryKey: ['resource'],
+    queryFn: async () => {
+      const res = await apiClient.resource.$get()
+      if (!res.ok) {
+        throw new Error('获取数据失败')
+      }
+      return await res.json()
+    }
+  })
+
+  // 使用useEffect处理错误
+  React.useEffect(() => {
+    if (error) {
+      Taro.showToast({
+        title: error.message,
+        icon: 'none'
+      })
+    }
+  }, [error])
+
+  if (isLoading) return <div>加载中...</div>
+
+  return <div>{/* 渲染数据 */}</div>
+}
+```
+
+#### 9.1.4 数据修改 (useMutation)
+
+对于需要修改服务端数据的操作(POST、PUT、DELETE),使用`useMutation`:
+
+```typescript
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { apiClient } from '../api'
+
+const MyPage: React.FC = () => {
+  const queryClient = useQueryClient()
+
+  // 数据修改mutation
+  const mutation = useMutation({
+    mutationFn: async (formData: MyFormData) => {
+      const res = await apiClient.resource.$post({
+        json: formData
+      })
+      if (!res.ok) {
+        throw new Error('操作失败')
+      }
+      return await res.json()
+    },
+    onSuccess: (data) => {
+      // 成功后刷新相关查询
+      queryClient.invalidateQueries({ queryKey: ['resource-list'] })
+      Taro.showToast({
+        title: '操作成功',
+        icon: 'success'
+      })
+    },
+    onError: (error) => {
+      Taro.showToast({
+        title: error.message,
+        icon: 'none'
+      })
+    }
+  })
+
+  const handleSubmit = (formData: MyFormData) => {
+    mutation.mutate(formData)
+  }
+
+  return (
+    <View>
+      <button onClick={() => handleSubmit(formData)}>
+        {mutation.isPending ? '提交中...' : '提交'}
+      </button>
+    </View>
+  )
+}
+```
+
+**关键点**:
+- 使用`mutationFn`定义异步操作
+- 使用`onSuccess`处理成功逻辑,通常需要`invalidateQueries`刷新数据
+- 使用`onError`处理错误
+- 使用`isPending`判断加载状态
+
+#### 9.1.5 无限滚动查询 (useInfiniteQuery)
+
+对于分页列表数据,使用`useInfiniteQuery`实现无限滚动:
+
+```typescript
+import { useInfiniteQuery } from '@tanstack/react-query'
+import { apiClient } from '../api'
+
+const MyPage: React.FC = () => {
+  // 无限滚动查询
+  const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
+    queryKey: ['infinite-list'],
+    queryFn: async ({ pageParam = 0 }) => {
+      const res = await apiClient.items.$get({
+        query: { skip: pageParam * 20, take: 20 }
+      })
+      if (!res.ok) {
+        throw new Error('获取数据失败')
+      }
+      const data = await res.json()
+      return {
+        items: data.data || [],
+        nextCursor: pageParam + 1
+      }
+    },
+    initialPageParam: 0,
+    getNextPageParam: (lastPage) => lastPage.nextCursor
+  })
+
+  // 扁平化所有页的数据
+  const allItems = data?.pages.flatMap(page => page.items) || []
+
+  const handleLoadMore = () => {
+    if (hasNextPage && !isFetchingNextPage) {
+      fetchNextPage()
+    }
+  }
+
+  return (
+    <ScrollView
+      scrollY
+      onScrollToLower={handleLoadMore}
+    >
+      {allItems.map(item => (
+        <View key={item.id}>{item.name}</View>
+      ))}
+
+      {isFetchingNextPage && <Text>加载更多...</Text>}
+      {!hasNextPage && allItems.length > 0 && <Text>没有更多数据了</Text>}
+    </ScrollView>
+  )
+}
+```
+
+**关键点**:
+- `pageParam`用于传递分页参数
+- `getNextPageParam`决定是否有下一页
+- 使用`pages.flatMap`合并所有页数据
+- 使用`fetchNextPage`加载下一页
+- 使用`hasNextPage`和`isFetchingNextPage`控制加载状态
+
+## 测试规范
+
+### 10.1 Jest配置
+
+```javascript
+module.exports = {
+  preset: 'ts-jest',
+  testEnvironment: 'jsdom',
+  setupFilesAfterEnv: ['@d8d/mini-testing-utils/testing/setup'],
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1',
+    '^~/(.*)$': '<rootDir>/tests/$1',
+    '^@tarojs/taro$': '@d8d/mini-testing-utils/testing/taro-api-mock.ts',
+    '\\.(css|less|scss)$': '@d8d/mini-testing-utils/testing/style-mock.js'
+  },
+  testMatch: [
+    '<rootDir>/tests/**/*.test.{ts,tsx}'
+  ],
+  transform: {
+    '^.+\\.(ts|tsx)$': 'ts-jest'
+  }
+}
+```
+
+### 10.2 组件测试示例
+
+```typescript
+import { render, screen } from '@testing-library/react'
+import { View, Text } from '@tarojs/components'
+import { MyComponent } from '../MyComponent'
+
+describe('MyComponent', () => {
+  it('渲染组件并验证垂直布局', () => {
+    render(<MyComponent />)
+
+    // 验证组件包含 flex flex-col 类
+    const container = screen.getByTestId('my-container')
+    expect(container.className).toContain('flex flex-col')
+  })
+})
+```
+
+### 10.3 页面集成测试(使用React Query)
+
+**重要**: 页面集成测试必须使用真实的React Query,不要mock React Query。
+
+```typescript
+import React from 'react'
+import { render, screen, waitFor } from '@testing-library/react'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import MyPage from '../pages/MyPage'
+
+// Mock API client
+jest.mock('../api', () => ({
+  apiClient: {
+    resource: {
+      $get: jest.fn()
+    }
+  }
+}))
+
+const { apiClient } = require('../api')
+
+const createTestQueryClient = () => new QueryClient({
+  defaultOptions: {
+    queries: { retry: false, staleTime: Infinity },
+    mutations: { retry: false }
+  }
+})
+
+const renderWithQueryClient = (component: React.ReactElement) => {
+  const queryClient = createTestQueryClient()
+  return render(
+    <QueryClientProvider client={queryClient}>
+      {component}
+    </QueryClientProvider>
+  )
+}
+
+describe('MyPage', () => {
+  beforeEach(() => {
+    jest.clearAllMocks()
+  })
+
+  it('应该显示加载状态', async () => {
+    // Mock API为pending状态
+    apiClient.resource.$get.mockImplementation(() => new Promise(() => {}))
+
+    renderWithQueryClient(<MyPage />)
+
+    expect(screen.getByText('加载中...')).toBeInTheDocument()
+  })
+
+  it('应该成功加载并显示数据', async () => {
+    const mockData = { name: '测试数据' }
+    apiClient.resource.$get.mockResolvedValue({
+      ok: true,
+      json: async () => mockData
+    })
+
+    renderWithQueryClient(<MyPage />)
+
+    await waitFor(() => {
+      expect(screen.getByText('测试数据')).toBeInTheDocument()
+    })
+  })
+})
+```
+
+## 常见问题和解决方案
+
+### 11.1 布局问题
+
+**问题**: 元素横向排列而不是垂直排列
+- **原因**: View容器默认是flex-row
+- **解决**: 添加`flex flex-col`类
+
+**问题**: Text组件在同一行显示
+- **原因**: Text组件默认是内联显示
+- **解决**: 父容器添加`flex flex-col`类
+
+### 11.2 样式问题
+
+**问题**: Tailwind样式不生效
+- **原因**: 类名冲突或拼写错误
+- **解决**: 检查类名拼写,确保使用正确的Tailwind类名
+
+**问题**: 样式在不同平台表现不一致
+- **原因**: 不同小程序平台的样式引擎差异
+- **解决**: 使用Taro提供的跨平台样式方案,避免使用平台特有样式
+
+### 11.3 API问题
+
+**问题**: RPC客户端类型错误
+- **原因**: API路径映射错误或类型推断不正确
+- **解决**: 验证后端路由定义,使用RPC推断类型
+
+### 11.4 React Query问题
+
+**问题**: 测试中React Query不工作
+- **原因**: 忘记使用QueryClientProvider包裹
+- **解决**: 使用renderWithQueryClient包装组件
+
+**问题**: queryKey冲突导致数据混乱
+- **原因**: 不同查询使用了相同的queryKey
+- **解决**: 为每个查询使用唯一的queryKey
+
+**问题**: mutation后数据没有更新
+- **原因**: 忘记调用invalidateQueries
+- **解决**: 在onSuccess回调中刷新相关查询
+
+**问题**: 无限滚动一直触发加载
+- **原因**: getNextPageParam返回逻辑错误
+- **解决**: 正确判断是否还有下一页,返回undefined或nextCursor
+
+## 最佳实践
+
+### 12.1 组件开发
+
+1. **始终使用flex flex-col实现垂直布局**
+2. **为每个View添加语义化的className**
+3. **使用data-testid属性便于测试**
+4. **组件props使用TypeScript接口定义**
+5. **使用相对路径导入包内模块**
+6. **使用React Query管理服务端状态**
+7. **为每个query使用唯一的queryKey**
+
+### 12.2 数据获取
+
+1. **使用useQuery获取数据,不要使用useState + useEffect**
+2. **在queryFn中检查response.ok,失败时throw Error**
+3. **使用useEffect处理错误,显示Toast提示**
+4. **多个独立查询使用不同的queryKey**
+5. **测试中使用真实的QueryClientProvider**
+
+### 12.3 数据修改
+
+1. **使用useMutation处理POST/PUT/DELETE操作**
+2. **在mutationFn中检查response.ok,失败时throw Error**
+3. **使用onSuccess刷新相关查询(invalidateQueries)**
+4. **使用onError显示错误提示**
+5. **使用isPending显示加载状态,避免重复提交**
+
+### 12.4 无限滚动
+
+1. **使用useInfiniteQuery处理分页列表**
+2. **使用pages.flatMap合并所有页数据**
+3. **正确实现getNextPageParam判断是否有下一页**
+4. **使用hasNextPage和isFetchingNextPage控制加载状态**
+5. **在ScrollView的onScrollToLower中触发fetchNextPage**
+
+### 12.5 性能优化
+
+1. **使用Image组件的lazyLoad属性**
+2. **列表数据使用虚拟滚动**
+3. **避免不必要的重渲染**
+4. **使用React.memo优化组件性能**
+5. **利用React Query的缓存机制减少重复请求**
+
+### 12.6 代码质量
+
+1. **遵循项目编码标准**
+2. **编写单元测试和集成测试**
+3. **使用TypeScript严格模式**
+4. **运行pnpm typecheck确保类型正确**
+5. **使用ESLint进行代码检查**
+
+## 参考实现
+
+### 13.1 用人方小程序UI包
+
+- `mini-ui-packages/yongren-dashboard-ui`
+- `mini-ui-packages/yongren-order-management-ui`
+- `mini-ui-packages/yongren-talent-management-ui`
+
+### 13.2 人才小程序UI包
+
+- `mini-ui-packages/rencai-dashboard-ui`
+- `mini-ui-packages/rencai-personal-info-ui`
+- `mini-ui-packages/rencai-employment-ui` - 使用React Query的参考实现
+- `mini-ui-packages/rencai-auth-ui`
+
+### 13.3 共享组件
+
+- `mini-ui-packages/mini-shared-ui-components`
+
+## 版本历史
+
+| 版本 | 日期 | 变更说明 | 作者 |
+|------|------|----------|------|
+| 1.3 | 2025-12-28 | 添加useMutation和useInfiniteQuery规范,完善React Query最佳实践 | James (Claude Code) |
+| 1.2 | 2025-12-28 | 添加React Query数据获取规范,更新测试规范章节 | James (Claude Code) |
+| 1.1 | 2025-12-26 | 添加图标使用规范(Heroicons) | Bob (Scrum Master) |
+| 1.0 | 2025-12-26 | 基于史诗011和017经验创建Mini UI包开发规范 | Bob (Scrum Master) |
+
+---
+
+**重要提醒**:
+1. 本规范专门针对Taro小程序UI包开发,与Web UI包开发规范(`ui-package-standards.md`)不同
+2. `flex flex-col`是Taro小程序中最常用的布局类,请务必牢记
+3. **使用React Query管理服务端状态**,不要使用useState + useEffect手动管理
+4. 所有Mini UI包的开发都应遵循本规范

+ 473 - 0
docs/architecture/mini-ui-testing-standards.md

@@ -0,0 +1,473 @@
+# Mini UI 包测试规范
+
+**版本**: 1.0
+**创建日期**: 2025-12-26
+**适用范围**: 所有 Taro 小程序 UI 包测试
+
+## 概述
+
+本文档规定了 Mini UI 包的测试编写规范,基于故事 017.003 的实施经验总结。遵循这些规范可以确保测试的一致性、可维护性和正确性。
+
+## 核心原则
+
+### 1. 使用 Jest,不是 Vitest
+
+**重要**: Mini UI 包使用 **Jest** 测试框架,不是 Vitest。
+
+```javascript
+// jest.config.cjs
+module.exports = {
+  preset: 'ts-jest',
+  testEnvironment: 'jsdom',
+  // ...
+}
+```
+
+### 2. 使用共享的 mini-testing-utils
+
+**关键原则**: 不要在每个 UI 包中重写 Taro mock,直接使用 `@d8d/mini-testing-utils` 提供的共享 mock。
+
+```javascript
+// jest.config.cjs
+module.exports = {
+  setupFilesAfterEnv: ['@d8d/mini-testing-utils/testing/setup'],
+  moduleNameMapper: {
+    '^@tarojs/taro$': '@d8d/mini-testing-utils/testing/taro-api-mock.ts',
+  }
+}
+```
+
+### 3. 使用真实的 React Query
+
+**关键原则**: 使用真实的 React Query(不要 mock),以便验证 RPC 类型推断。
+
+```typescript
+// ✅ 正确: 使用真实的 React Query
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+
+const createTestWrapper = () => {
+  const queryClient = new QueryClient({
+    defaultOptions: {
+      queries: { retry: false, staleTime: Infinity }
+    }
+  })
+  return ({ children }: { children: React.ReactNode }) => (
+    <QueryClientProvider client={queryClient}>
+      {children}
+    </QueryClientProvider>
+  )
+}
+```
+
+```typescript
+// ❌ 错误: 不要 mock React Query
+jest.mock('@tanstack/react-query', () => ({
+  useQuery: jest.fn()
+}))
+```
+
+## Jest 配置规范
+
+### 标准 jest.config.cjs 模板
+
+```javascript
+// jest.config.cjs
+module.exports = {
+  preset: 'ts-jest',
+  testEnvironment: 'jsdom',
+
+  // 使用 mini-testing-utils 提供的共享 setup
+  setupFilesAfterEnv: ['@d8d/mini-testing-utils/testing/setup'],
+
+  moduleNameMapper: {
+    // 测试文件中的别名映射(仅用于测试文件)
+    '^@/(.*)$': '<rootDir>/src/$1',
+    '^~/(.*)$': '<rootDir>/tests/$1',
+
+    // Taro API 重定向到共享 mock
+    '^@tarojs/taro$': '@d8d/mini-testing-utils/testing/taro-api-mock.ts',
+
+    // 样式和文件映射
+    '\\.(css|less|scss|sass)$': '@d8d/mini-testing-utils/testing/style-mock.js',
+    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
+      '@d8d/mini-testing-utils/testing/file-mock.js'
+  },
+
+  testMatch: [
+    '<rootDir>/tests/**/*.spec.{ts,tsx}',
+    '<rootDir>/tests/**/*.test.{ts,tsx}'
+  ],
+
+  collectCoverageFrom: [
+    'src/**/*.{ts,tsx}',
+    '!src/**/*.d.ts',
+    '!src/**/index.{ts,tsx}',
+    '!src/**/*.stories.{ts,tsx}'
+  ],
+
+  coverageDirectory: 'coverage',
+  coverageReporters: ['text', 'lcov', 'html'],
+
+  testPathIgnorePatterns: [
+    '/node_modules/',
+    '/dist/',
+    '/coverage/'
+  ],
+
+  transform: {
+    '^.+\\.(ts|tsx)$': 'ts-jest',
+    '^.+\\.(js|jsx)$': 'babel-jest'
+  },
+
+  transformIgnorePatterns: [
+    '/node_modules/(?!(swiper|@tarojs)/)'
+  ],
+
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json']
+}
+```
+
+## 测试文件结构规范
+
+```
+mini-ui-packages/<package-name>/
+└── tests/
+    ├── unit/                      # 单元测试
+    │   └── components/
+    │       ├── ComponentName.test.tsx
+    │       └── ...
+    └── pages/                     # 页面组件测试
+        └── PageName/
+            └── PageName.test.tsx
+```
+
+## 测试编写规范
+
+### 1. 组件测试模板
+
+```typescript
+/**
+ * ComponentName 组件测试
+ */
+import React from 'react'
+import { render, screen } from '@testing-library/react'
+import '@testing-library/jest-dom'
+import ComponentName, { PropsInterface } from '../../../src/components/ComponentName'
+import Taro from '@tarojs/taro'
+
+describe('ComponentName', () => {
+  const mockProps: PropsInterface = {
+    // mock props
+  }
+
+  beforeEach(() => {
+    // 清理 Taro API mock
+    ;(Taro.someApi as jest.Mock).mockClear()
+  })
+
+  it('应该正确渲染', () => {
+    render(<ComponentName {...mockProps} />)
+    expect(screen.getByText('expected text')).toBeInTheDocument()
+  })
+})
+```
+
+### 2. 页面组件测试模板
+
+```typescript
+/**
+ * PageName 页面测试
+ * 使用真实的React Query和RPC类型验证
+ */
+import React from 'react'
+import { render, screen, waitFor } from '@testing-library/react'
+import '@testing-library/jest-dom'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import PageName from '../../../src/pages/PageName/PageName'
+import { apiClient } from '../../../src/api'
+import { useRequireAuth } from '@d8d/xxx-auth-ui/hooks'
+import Taro from '@tarojs/taro'
+
+// Mock auth hooks
+jest.mock('@d8d/xxx-auth-ui/hooks', () => ({
+  useRequireAuth: jest.fn()
+}))
+
+// Mock API client - 使用真实的RPC类型
+jest.mock('../../../src/api', () => ({
+  apiClient: {
+    route: {
+      $get: jest.fn()
+    }
+  }
+}))
+
+const createMockResponse = <T,>(status: number, data: T) => ({
+  status,
+  ok: status >= 200 && status < 300,
+  json: async () => data
+})
+
+const createTestWrapper = () => {
+  const queryClient = new QueryClient({
+    defaultOptions: {
+      queries: {
+        retry: false,
+        staleTime: Infinity,
+        refetchOnWindowFocus: false
+      }
+    }
+  })
+
+  return ({ children }: { children: React.ReactNode }) => (
+    <QueryClientProvider client={queryClient}>
+      {children}
+    </QueryClientProvider>
+  )
+}
+
+describe('PageName', () => {
+  beforeEach(() => {
+    jest.clearAllMocks()
+    // Mock useRequireAuth to do nothing (user is authenticated)
+    ;(useRequireAuth as jest.Mock).mockImplementation(() => {})
+    // Reset Taro API mocks
+    ;(Taro.setNavigationBarTitle as jest.Mock).mockClear()
+    ;(Taro.showToast as jest.Mock).mockClear()
+  })
+
+  it('应该渲染带有正确数据的页面', async () => {
+    // Mock API calls - 使用符合RPC类型的响应
+    ;(apiClient.route.$get as jest.Mock).mockResolvedValue(
+      createMockResponse(200, mockData)
+    )
+
+    const wrapper = createTestWrapper()
+    render(<PageName />, { wrapper })
+
+    await waitFor(() => {
+      expect(screen.getByText('expected text')).toBeInTheDocument()
+    })
+  })
+})
+```
+
+### 3. Mock RPC 响应规范
+
+```typescript
+// ✅ 正确: 使用 Mock 响应工具函数
+const createMockResponse = <T,>(status: number, data: T) => ({
+  status,
+  ok: status >= 200 && status < 300,
+  json: async () => data
+})
+
+// 使用示例
+; (apiClient.route.$get as jest.Mock).mockResolvedValue(
+  createMockResponse(200, { name: '张三', age: 35 })
+)
+```
+
+### 4. Taro API 使用规范
+
+```typescript
+// ✅ 正确: 直接使用 Taro API,不需要自定义 mock
+import Taro from '@tarojs/taro'
+
+describe('Component', () => {
+  beforeEach(() => {
+    // 清理 mock
+    ;(Taro.showToast as jest.Mock).mockClear()
+  })
+
+  it('应该调用Taro API', () => {
+    render(<Component />)
+    expect(Taro.showToast).toHaveBeenCalledWith({ title: '成功' })
+  })
+})
+
+// ❌ 错误: 不要在测试文件中自定义 Taro mock
+jest.mock('@tarojs/taro', () => ({
+  default: {
+    showToast: jest.fn()  // 错误!应该使用 mini-testing-utils 的共享 mock
+  }
+}))
+```
+
+## 测试最佳实践
+
+### 1. RPC 类型推断验证
+
+使用真实的 React Query 来验证 RPC 类型推断:
+
+```typescript
+// ✅ 正确: 测试中使用真实的 RPC 类型
+const mockData = {
+  name: '张三',
+  age: 35,
+  // 类型错误会在编译时被捕获
+  wrongField: 'type error'  // TypeScript 会报错
+}
+
+; (apiClient.route.$get as jest.Mock).mockResolvedValue(
+  createMockResponse(200, mockData)
+)
+```
+
+### 2. 异步测试
+
+使用 `waitFor` 处理异步状态更新:
+
+```typescript
+it('应该在加载完成后渲染数据', async () => {
+  ; (apiClient.route.$get as jest.Mock).mockResolvedValue(
+    createMockResponse(200, mockData)
+  )
+
+  render(<Page />, { wrapper })
+
+  await waitFor(() => {
+    expect(screen.getByText('data')).toBeInTheDocument()
+  })
+})
+```
+
+### 3. 用户交互测试
+
+```typescript
+it('应该处理点击事件', () => {
+  const { fireEvent } = require('@testing-library/react')
+
+  render(<Component />)
+
+  const button = screen.getByText('Click me')
+  fireEvent.click(button)
+
+  expect(Taro.someApi).toHaveBeenCalled()
+})
+```
+
+## 常见错误
+
+### 错误 1: 重写 Taro mock
+
+```typescript
+// ❌ 错误: 在测试文件中重写 Taro mock
+jest.mock('@tarojs/taro', () => ({
+  default: {
+    showToast: jest.fn()
+  }
+}))
+
+// ✅ 正确: 直接导入使用
+import Taro from '@tarojs/taro'
+// mock 在 mini-testing-utils/testing/setup.ts 中已配置
+```
+
+### 错误 2: Mock React Query
+
+```typescript
+// ❌ 错误: Mock React Query
+jest.mock('@tanstack/react-query', () => ({
+  useQuery: jest.fn(() => ({ data: mockData }))
+}))
+
+// ✅ 正确: 使用真实的 React Query
+const wrapper = createTestWrapper()
+render(<Page />, { wrapper })
+```
+
+### 错误 3: 在 UI 包源代码中使用别名
+
+```typescript
+// ❌ 错误: 源代码中使用别名
+import { apiClient } from '@/api'
+import { Component } from '@/components'
+
+// ✅ 正确: 源代码中使用相对路径
+import { apiClient } from '../api'
+import { Component } from '../components'
+```
+
+### 错误 4: 忘记清理 mock
+
+```typescript
+// ❌ 错误: 忘记清理 mock
+describe('Component', () => {
+  it('test 1', () => {
+    expect(Taro.showToast).toHaveBeenCalledTimes(1)
+  })
+
+  it('test 2', () => {
+    // test 1 的 mock 调用会影响 test 2
+    expect(Taro.showToast).toHaveBeenCalledTimes(1)  // 可能失败
+  })
+})
+
+// ✅ 正确: 在 beforeEach 中清理 mock
+describe('Component', () => {
+  beforeEach(() => {
+    ; (Taro.showToast as jest.Mock).mockClear()
+  })
+
+  it('test 1', () => {
+    expect(Taro.showToast).toHaveBeenCalledTimes(1)
+  })
+
+  it('test 2', () => {
+    // mock 已清理,计数重新开始
+    expect(Taro.showToast).toHaveBeenCalledTimes(1)
+  })
+})
+```
+
+## 测试执行
+
+```bash
+# 运行所有测试
+cd mini-ui-packages/<package-name> && pnpm test
+
+# 运行特定测试
+pnpm test --testNamePattern="ComponentName"
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 监听模式
+pnpm test:watch
+```
+
+## mini-testing-utils 扩展
+
+如果需要添加新的 Taro API mock,在 `mini-testing-utils/testing/taro-api-mock.ts` 中添加:
+
+```typescript
+// 1. 添加 mock 函数声明
+export const mockNewApi = jest.fn()
+
+// 2. 在默认导出中添加
+export default {
+  // ...
+  newApi: mockNewApi
+}
+
+// 3. 在命名导出中添加
+export {
+  // ...
+  mockNewApi as newApi
+}
+```
+
+## 参考资料
+
+- [Jest 官方文档](https://jestjs.io/)
+- [Testing Library](https://testing-library.com/)
+- [Taro 官方文档](https://taro-docs.jd.com/)
+- [mini-ui-package-standards.md](./mini-ui-package-standards.md) - Mini UI 包开发规范
+- [testing-strategy.md](./testing-strategy.md) - 通用测试策略
+
+## 版本历史
+
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-12-26 | 初始版本,基于故事 017.003 实施经验 | Dev Agent |

+ 326 - 384
docs/architecture/source-tree.md

@@ -3,479 +3,421 @@
 ## 版本信息
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
-| 4.0 | 2025-11-17 | 添加多租户包架构,包含10个多租户模块包和10个多租户管理界面包 | Claude |
-| 3.5 | 2025-11-12 | 添加订单管理模块包 (@d8d/orders-module) | James |
-| 3.4 | 2025-11-12 | 添加商品管理模块包 (@d8d/goods-module) | James |
-| 3.3 | 2025-11-12 | 补充新添加的业务模块包(广告、商户、供应商) | Winston |
+| 3.3 | 2025-12-26 | 更新完整项目结构,包含allin-packages和mini-ui-packages | James |
 | 3.2 | 2025-11-11 | 更新包结构,添加基础设施和业务模块包 | Winston |
 | 3.1 | 2025-11-09 | 更新测试结构,清理重复测试文件 | James |
 | 3.0 | 2025-10-22 | 更新为 monorepo 结构,添加 packages/server | Winston |
 
 ## 实际项目结构
 ```text
-d8d-mini-starter/
-├── mini/                       # 小程序项目 (Taro + React)
+188-179-template-6/
+├── .bmad-core/                      # BMad框架配置目录
+│   ├── agents/                      # 代理配置
+│   ├── agent-teams/                 # 团队配置
+│   ├── checklists/                  # 检查清单
+│   ├── data/                        # 知识库数据
+│   ├── tasks/                       # 任务定义
+│   ├── templates/                   # 模板文件
+│   ├── workflows/                   # 工作流定义
+│   └── core-config.yaml             # 核心配置文件
+├── .claude/                         # Claude CLI配置
+│   └── commands/                    # Claude命令定义
+├── .gitea/                          # Gitea配置
+├── .github/                         # GitHub Actions配置
+│   └── workflows/                   # CI/CD工作流
+├── .git/                            # Git版本控制
+├── allin-packages/                  # AllIn业务包目录
+│   ├── channel-module/              # 渠道管理模块
+│   ├── channel-management-ui/       # 渠道管理UI
+│   ├── company-module/              # 企业管理模块
+│   ├── company-management-ui/       # 企业管理UI
+│   ├── disability-module/           # 残疾人管理模块
+│   ├── disability-person-management-ui/  # 残疾人个人管理UI
+│   ├── enums/                      # 枚举类型包
+│   ├── order-module/               # 订单管理模块
+│   ├── order-management-ui/        # 订单管理UI
+│   ├── platform-module/            # 平台管理模块
+│   ├── platform-management-ui/     # 平台管理UI
+│   ├── salary-module/              # 薪资管理模块
+│   ├── salary-management-ui/       # 薪资管理UI
+│   └── statistics-module/          # 统计模块
+├── docs/                           # 项目文档目录
+│   ├── architecture/               # 架构文档(分片)
+│   │   ├── coding-standards.md     # 编码标准和测试策略
+│   │   ├── source-tree.md          # 源码树和文件组织(本文件)
+│   │   ├── tech-stack.md           # 技术栈
+│   │   └── ui-package-standards.md # UI包开发规范
+│   ├── prd/                        # 产品需求文档(分片)
+│   ├── qa/                         # QA文档
+│   ├── stories/                    # 用户故事文档
+│   └── *.md                        # 其他项目文档
+├── mini/                           # 商户小程序项目 (Taro + React)
 │   ├── src/
-│   │   ├── app.tsx            # 小程序入口
-│   │   ├── app.config.ts      # 小程序配置
-│   │   ├── api.ts             # API客户端
+│   │   ├── app.tsx                 # 小程序入口
+│   │   ├── app.config.ts           # 小程序配置
+│   │   ├── api.ts                  # API客户端
 │   │   ├── components/
-│   │   │   └── ui/            # UI组件库
-│   │   │       ├── avatar-upload.tsx    # 头像上传组件
-│   │   │       ├── button.tsx           # 按钮组件
-│   │   │       ├── card.tsx             # 卡片组件
-│   │   │       ├── form.tsx             # 表单组件
-│   │   │       ├── image.tsx            # 图片组件
-│   │   │       ├── input.tsx            # 输入框组件
-│   │   │       ├── label.tsx            # 标签组件
-│   │   │       ├── navbar.tsx           # 导航栏组件
-│   │   │       └── tab-bar.tsx          # 标签栏组件
+│   │   │   └── ui/                 # UI组件库
+│   │   │       ├── avatar-upload.tsx     # 头像上传组件
+│   │   │       ├── button.tsx            # 按钮组件
+│   │   │       ├── card.tsx              # 卡片组件
+│   │   │       ├── form.tsx              # 表单组件
+│   │   │       ├── image.tsx             # 图片组件
+│   │   │       ├── input.tsx             # 输入框组件
+│   │   │       ├── label.tsx             # 标签组件
+│   │   │       ├── navbar.tsx            # 导航栏组件
+│   │   │       └── tab-bar.tsx           # 标签栏组件
 │   │   ├── layouts/
-│   │   │   └── tab-bar-layout.tsx       # 标签栏布局
+│   │   │   └── tab-bar-layout.tsx        # 标签栏布局
 │   │   ├── pages/
-│   │   │   ├── explore/                 # 探索页面
+│   │   │   ├── explore/                  # 探索页面
 │   │   │   │   ├── index.tsx
 │   │   │   │   └── index.config.ts
-│   │   │   ├── index/                   # 首页
+│   │   │   ├── index/                    # 首页
 │   │   │   │   ├── index.tsx
 │   │   │   │   └── index.config.ts
-│   │   │   ├── login/                   # 登录页面
+│   │   │   ├── login/                    # 登录页面
 │   │   │   │   ├── index.tsx
 │   │   │   │   ├── index.config.ts
-│   │   │   │   └── wechat-login.tsx     # 微信登录
-│   │   │   ├── profile/                 # 个人资料
+│   │   │   │   └── wechat-login.tsx      # 微信登录
+│   │   │   ├── profile/                  # 个人资料
 │   │   │   │   ├── index.tsx
 │   │   │   │   └── index.config.ts
-│   │   │   └── register/                # 注册页面
+│   │   │   └── register/                 # 注册页面
 │   │   │       ├── index.tsx
 │   │   │       └── index.config.ts
-│   │   ├── schemas/            # 验证模式
-│   │   └── utils/              # 工具函数
+│   │   ├── schemas/               # 验证模式
+│   │   └── utils/                 # 工具函数
 │   ├── config/
-│   │   ├── dev.ts              # 开发环境配置
-│   │   ├── index.ts            # 配置入口
-│   │   └── prod.ts             # 生产环境配置
+│   │   ├── dev.ts                  # 开发环境配置
+│   │   ├── index.ts                # 配置入口
+│   │   └── prod.ts                 # 生产环境配置
+│   ├── tests/                      # 测试文件
+│   ├── dist/                       # 构建输出
 │   └── package.json
-├── packages/                   # 共享包
-│   ├── server/                 # API服务器包 (@d8d/server) - 重构后
-│   │   ├── src/
-│   │   │   ├── api.ts                          # API路由导出
-│   │   │   └── index.ts                        # 服务器入口
-│   │   ├── tests/
-│   │   └── package.json
-│   ├── shared-types/           # 共享类型定义 (@d8d/shared-types)
-│   │   ├── src/
-│   │   │   └── index.ts                        # 类型定义导出
-│   │   └── package.json
-│   ├── shared-utils/           # 共享工具函数 (@d8d/shared-utils)
-│   │   ├── src/
-│   │   │   ├── utils/
-│   │   │   │   ├── jwt.util.ts                 # JWT工具
-│   │   │   │   ├── errorHandler.ts             # 错误处理
-│   │   │   │   ├── parseWithAwait.ts           # 异步解析
-│   │   │   │   ├── logger.ts                   # 日志工具
-│   │   │   │   └── redis.util.ts               # Redis会话管理
-│   │   │   └── data-source.ts                  # 数据库连接
-│   │   ├── tests/
-│   │   └── package.json
-│   ├── shared-crud/            # 通用CRUD基础设施 (@d8d/shared-crud)
-│   │   ├── src/
-│   │   │   ├── services/
-│   │   │   │   ├── generic-crud.service.ts     # 通用CRUD服务
-│   │   │   │   └── concrete-crud.service.ts    # 具体CRUD服务
-│   │   │   └── routes/
-│   │   │       └── generic-crud.routes.ts      # 通用CRUD路由
-│   │   ├── tests/
-│   │   └── package.json
-│   ├── shared-test-util/       # 测试基础设施 (@d8d/shared-test-util)
-│   │   ├── src/
-│   │   │   ├── integration-test-db.ts          # 集成测试数据库工具
-│   │   │   ├── integration-test-utils.ts       # 集成测试断言工具
-│   │   ├── tests/
-│   │   └── package.json
-│   ├── user-module/            # 用户管理模块 (@d8d/user-module)
-│   │   ├── src/
-│   │   │   ├── entities/
-│   │   │   │   ├── user.entity.ts              # 用户实体
-│   │   │   │   └── role.entity.ts              # 角色实体
-│   │   │   ├── services/
-│   │   │   │   ├── user.service.ts             # 用户服务
-│   │   │   │   └── role.service.ts             # 角色服务
-│   │   │   ├── schemas/
-│   │   │   │   ├── user.schema.ts              # 用户Schema
-│   │   │   │   └── role.schema.ts              # 角色Schema
-│   │   │   └── routes/
-│   │   │       ├── user.routes.ts              # 用户路由
-│   │   │       ├── role.routes.ts              # 角色路由
-│   │   │       └── custom.routes.ts            # 自定义路由
-│   │   ├── tests/
-│   │   └── package.json
-│   ├── auth-module/            # 认证管理模块 (@d8d/auth-module)
+├── mini-talent/                    # 人才小程序项目 (Taro + React)
+│   ├── src/                        # 源代码
+│   ├── tests/                      # 测试文件
+│   └── package.json
+├── mini-ui-packages/               # 小程序UI组件包目录
+│   ├── mini-charts/                # 小程序图表组件
+│   ├── mini-enterprise-auth-ui/    # 企业认证UI
+│   ├── mini-shared-ui-components/  # 共享UI组件
+│   ├── mini-testing-utils/         # 测试工具
+│   ├── rencai-attendance-ui/       # 人才考勤UI
+│   ├── rencai-auth-ui/            # 人才认证UI
+│   ├── rencai-dashboard-ui/       # 人才仪表盘UI
+│   ├── rencai-employment-ui/       # 人才就业UI
+│   ├── rencai-personal-info-ui/   # 人才个人信息UI
+│   ├── rencai-settings-ui/        # 人才设置UI
+│   ├── rencai-shared-ui/          # 人才共享UI
+│   ├── yongren-dashboard-ui/      # 用人仪表盘UI
+│   ├── yongren-order-management-ui/ # 用人订单管理UI
+│   ├── yongren-settings-ui/       # 用人设置UI
+│   ├── yongren-shared-ui/         # 用人共享UI
+│   ├── yongren-statistics-ui/     # 用人统计UI
+│   └── yongren-talent-management-ui/ # 用人人才管理UI
+├── packages/                       # 核心共享包目录
+│   # ==================== UI管理包 ====================
+│   ├── advertisement-management-ui/        # 广告管理UI
+│   ├── advertisement-type-management-ui/   # 广告类型管理UI
+│   ├── area-management-ui/                 # 区域管理UI
+│   ├── auth-management-ui/                 # 认证管理UI
+│   ├── bank-name-management-ui/            # 银行名称管理UI
+│   ├── delivery-address-management-ui/     # 收货地址管理UI
+│   ├── file-management-ui/                 # 文件管理UI
+│   ├── goods-category-management-ui/       # 商品类别管理UI
+│   ├── goods-management-ui/                # 商品管理UI
+│   ├── merchant-management-ui/             # 商户管理UI
+│   ├── order-management-ui/                # 订单管理UI
+│   ├── supplier-management-ui/             # 供应商管理UI
+│   ├── user-management-ui/                 # 用户管理UI
+│   # 多租户版本
+│   ├── advertisement-management-ui-mt/     # 广告管理UI(多租户)
+│   ├── advertisement-type-management-ui-mt/ # 广告类型管理UI(多租户)
+│   ├── area-management-ui-mt/              # 区域管理UI(多租户)
+│   ├── auth-management-ui-mt/              # 认证管理UI(多租户)
+│   ├── bank-name-management-ui-mt/         # 银行名称管理UI(多租户)
+│   ├── delivery-address-management-ui-mt/  # 收货地址管理UI(多租户)
+│   ├── file-management-ui-mt/              # 文件管理UI(多租户)
+│   ├── goods-category-management-ui-mt/    # 商品类别管理UI(多租户)
+│   ├── goods-management-ui-mt/             # 商品管理UI(多租户)
+│   ├── merchant-management-ui-mt/          # 商户管理UI(多租户)
+│   ├── order-management-ui-mt/             # 订单管理UI(多租户)
+│   ├── supplier-management-ui-mt/          # 供应商管理UI(多租户)
+│   └── user-management-ui-mt/              # 用户管理UI(多租户)
+│   # ==================== 业务模块包 ====================
+│   ├── advertisements-module/              # 广告模块
+│   ├── auth-module/                        # 认证模块
 │   │   ├── src/
 │   │   │   ├── services/
-│   │   │   │   ├── auth.service.ts             # 认证服务
-│   │   │   │   └── mini-auth.service.ts        # 小程序认证服务
+│   │   │   │   ├── auth.service.ts         # 认证服务
+│   │   │   │   └── mini-auth.service.ts    # 小程序认证服务
 │   │   │   ├── schemas/
-│   │   │   │   └── auth.schema.ts              # 认证Schema
+│   │   │   │   └── auth.schema.ts          # 认证Schema
 │   │   │   ├── routes/
-│   │   │   │   ├── login.route.ts              # 登录路由
-│   │   │   │   ├── register.route.ts           # 注册路由
-│   │   │   │   ├── mini-login.route.ts         # 小程序登录路由
-│   │   │   │   ├── phone-decrypt.route.ts      # 手机号解密路由
-│   │   │   │   ├── me.route.ts                 # 获取用户信息路由
-│   │   │   │   ├── update-me.route.ts          # 更新用户信息路由
-│   │   │   │   ├── logout.route.ts             # 登出路由
-│   │   │   │   └── sso-verify.route.ts         # SSO验证路由
+│   │   │   │   ├── login.route.ts          # 登录路由
+│   │   │   │   ├── register.route.ts       # 注册路由
+│   │   │   │   ├── mini-login.route.ts     # 小程序登录路由
+│   │   │   │   ├── phone-decrypt.route.ts  # 手机号解密路由
+│   │   │   │   ├── me.route.ts             # 获取用户信息路由
+│   │   │   │   ├── update-me.route.ts      # 更新用户信息路由
+│   │   │   │   ├── logout.route.ts         # 登出路由
+│   │   │   │   └── sso-verify.route.ts     # SSO验证路由
 │   │   │   └── middleware/
-│   │   │       ├── auth.middleware.ts          # 认证中间件
-│   │   │       └── index.ts                    # 中间件导出
+│   │   │       ├── auth.middleware.ts      # 认证中间件
+│   │   │       └── index.ts                # 中间件导出
 │   │   ├── tests/
 │   │   └── package.json
-│   ├── file-module/            # 文件管理模块 (@d8d/file-module)
+│   ├── bank-names-module/                  # 银行名称模块
+│   ├── delivery-address-module/            # 收货地址模块
+│   ├── file-module/                        # 文件管理模块
 │   │   ├── src/
 │   │   │   ├── entities/
-│   │   │   │   └── file.entity.ts              # 文件实体
+│   │   │   │   └── file.entity.ts          # 文件实体
 │   │   │   ├── services/
-│   │   │   │   ├── file.service.ts             # 文件服务
-│   │   │   │   └── minio.service.ts            # MinIO服务
+│   │   │   │   ├── file.service.ts         # 文件服务
+│   │   │   │   └── minio.service.ts        # MinIO服务
 │   │   │   ├── schemas/
-│   │   │   │   └── file.schema.ts              # 文件Schema
+│   │   │   │   └── file.schema.ts          # 文件Schema
 │   │   │   └── routes/
-│   │   │       ├── upload-policy/post.ts       # 上传策略路由
-│   │   │       ├── multipart-policy/post.ts    # 多部分上传策略路由
-│   │   │       ├── multipart-complete/post.ts  # 完成多部分上传路由
+│   │   │       ├── upload-policy/post.ts   # 上传策略路由
+│   │   │       ├── multipart-policy/post.ts # 多部分上传策略路由
+│   │   │       ├── multipart-complete/post.ts # 完成多部分上传路由
 │   │   │       └── [id]/
-│   │   │           ├── get.ts                  # 获取文件详情路由
-│   │   │           ├── get-url.ts              # 获取文件URL路由
-│   │   │           ├── download.ts             # 文件下载路由
-│   │   │           └── delete.ts               # 删除文件路由
+│   │   │           ├── get.ts              # 获取文件详情路由
+│   │   │           ├── get-url.ts          # 获取文件URL路由
+│   │   │           ├── download.ts         # 文件下载路由
+│   │   │           └── delete.ts           # 删除文件路由
 │   │   ├── tests/
 │   │   └── package.json
-│   ├── mini-payment/           # 微信小程序支付模块 (@d8d/mini-payment)
+│   ├── geo-areas/                          # 地理区域模块
 │   │   ├── src/
-│   │   │   ├── entities/
-│   │   │   │   ├── payment.entity.ts           # 支付实体
-│   │   │   │   └── payment.types.ts            # 支付类型定义
-│   │   │   ├── services/
-│   │   │   │   └── payment.service.ts          # 支付服务
-│   │   │   ├── schemas/
-│   │   │   │   └── payment.schema.ts           # 支付Schema
-│   │   │   └── routes/
-│   │   │       └── payment/
-│   │   │           ├── create.ts               # 支付创建路由
-│   │   │           ├── callback.ts             # 支付回调路由
-│   │   │           └── status.ts               # 支付状态查询路由
+│   │   │   ├── modules/areas/
+│   │   │   │   ├── area.entity.ts          # 地区实体
+│   │   │   │   ├── area.service.ts         # 地区服务
+│   │   │   │   └── area.schema.ts          # 地区Schema
+│   │   │   ├── api/
+│   │   │   │   ├── areas/index.ts          # 公共地区API
+│   │   │   │   └── admin/areas/index.ts    # 管理地区API
+│   │   │   └── index.ts                    # 包入口
 │   │   ├── tests/
-│   │   │   └── integration/
-│   │   │       ├── payment.integration.test.ts         # 支付集成测试
-│   │   │       └── payment-callback.integration.test.ts # 支付回调集成测试
 │   │   └── package.json
-│   ├── delivery-address-module/ # 配送地址管理模块 (@d8d/delivery-address-module)
+│   ├── goods-category-module/              # 商品类别模块
+│   ├── goods-module/                       # 商品模块
+│   ├── merchant-module/                    # 商户模块
+│   ├── orders-module/                      # 订单模块
+│   ├── supplier-module/                    # 供应商模块
+│   ├── tenant-module/                      # 租户模块
+│   ├── user-module/                        # 用户管理模块
 │   │   ├── src/
 │   │   │   ├── entities/
-│   │   │   │   └── delivery-address.entity.ts  # 配送地址实体
+│   │   │   │   ├── user.entity.ts          # 用户实体
+│   │   │   │   └── role.entity.ts          # 角色实体
 │   │   │   ├── services/
-│   │   │   │   └── delivery-address.service.ts # 配送地址服务
+│   │   │   │   ├── user.service.ts         # 用户服务
+│   │   │   │   └── role.service.ts         # 角色服务
 │   │   │   ├── schemas/
-│   │   │   │   ├── user-delivery-address.schema.ts    # 用户专用Schema
-│   │   │   │   └── admin-delivery-address.schema.ts   # 管理员专用Schema
+│   │   │   │   ├── user.schema.ts          # 用户Schema
+│   │   │   │   └── role.schema.ts          # 角色Schema
 │   │   │   └── routes/
-│   │   │       ├── index.ts                    # 路由导出
-│   │   │       ├── user-routes.ts              # 用户路由(数据权限控制)
-│   │   │       ├── admin-routes.ts             # 管理员路由(完整权限)
-│   │   │       └── admin-custom.routes.ts      # 管理员自定义路由(地区验证)
+│   │   │       ├── user.routes.ts          # 用户路由
+│   │   │       ├── role.routes.ts          # 角色路由
+│   │   │       └── custom.routes.ts        # 自定义路由
 │   │   ├── tests/
-│   │   │   └── integration/
-│   │   │       ├── user-routes.integration.test.ts    # 用户路由集成测试
-│   │   │       └── admin-routes.integration.test.ts   # 管理员路由集成测试
 │   │   └── package.json
-│   ├── advertisements-module/  # 广告管理模块 (@d8d/advertisements-module)
+│   # 多租户版本
+│   ├── advertisements-module-mt/           # 广告模块(多租户)
+│   ├── auth-module-mt/                     # 认证模块(多租户)
+│   ├── bank-names-module-mt/               # 银行名称模块(多租户)
+│   ├── delivery-address-module-mt/         # 收货地址模块(多租户)
+│   ├── file-module-mt/                     # 文件管理模块(多租户)
+│   ├── goods-category-module-mt/           # 商品类别模块(多租户)
+│   ├── goods-module-mt/                    # 商品模块(多租户)
+│   ├── merchant-module-mt/                 # 商户模块(多租户)
+│   ├── orders-module-mt/                   # 订单模块(多租户)
+│   ├── supplier-module-mt/                 # 供应商模块(多租户)
+│   ├── tenant-module-mt/                   # 租户模块(多租户)
+│   └── user-module-mt/                     # 用户管理模块(多租户)
+│   # ==================== 共享基础设施包 ====================
+│   ├── core-module/                        # 核心模块
+│   ├── shared-crud/                        # 通用CRUD基础设施
 │   │   ├── src/
-│   │   │   ├── entities/
-│   │   │   │   ├── advertisement.entity.ts            # 广告实体
-│   │   │   │   └── advertisement-type.entity.ts       # 广告类型实体
 │   │   │   ├── services/
-│   │   │   │   ├── advertisement.service.ts           # 广告服务
-│   │   │   │   └── advertisement-type.service.ts      # 广告类型服务
-│   │   │   ├── schemas/
-│   │   │   │   ├── advertisement.schema.ts            # 广告Schema
-│   │   │   │   └── advertisement-type.schema.ts       # 广告类型Schema
+│   │   │   │   ├── generic-crud.service.ts # 通用CRUD服务
+│   │   │   │   └── concrete-crud.service.ts # 具体CRUD服务
 │   │   │   └── routes/
-│   │   │       ├── advertisements.ts                  # 广告路由
-│   │   │       └── advertisement-types.ts             # 广告类型路由
+│   │   │       └── generic-crud.routes.ts  # 通用CRUD路由
 │   │   ├── tests/
-│   │   │   └── integration/
-│   │   │       ├── advertisements.integration.test.ts # 广告集成测试
-│   │   │       └── advertisement-types.integration.test.ts # 广告类型集成测试
 │   │   └── package.json
-│   ├── merchant-module/        # 商户管理模块 (@d8d/merchant-module)
+│   ├── shared-test-util/                   # 测试基础设施
 │   │   ├── src/
-│   │   │   ├── entities/
-│   │   │   │   └── merchant.entity.ts                 # 商户实体
-│   │   │   ├── services/
-│   │   │   │   └── merchant.service.ts                # 商户服务
-│   │   │   ├── schemas/
-│   │   │   │   ├── merchant.schema.ts                 # 商户Schema
-│   │   │   │   ├── user-merchant.schema.ts            # 用户专用Schema
-│   │   │   │   └── admin-merchant.schema.ts           # 管理员专用Schema
-│   │   │   ├── types/
-│   │   │   │   └── merchant.types.ts                  # 商户类型定义
-│   │   │   └── routes/
-│   │   │       ├── index.ts                           # 路由导出
-│   │   │       ├── user-routes.ts                     # 用户路由
-│   │   │       └── admin-routes.ts                    # 管理员路由
+│   │   │   ├── integration-test-db.ts      # 集成测试数据库工具
+│   │   │   └── integration-test-utils.ts   # 集成测试断言工具
 │   │   ├── tests/
-│   │   │   └── integration/
-│   │   │       ├── user-routes.integration.test.ts    # 用户路由集成测试
-│   │   │       └── admin-routes.integration.test.ts   # 管理员路由集成测试
 │   │   └── package.json
-│   ├── supplier-module/        # 供应商管理模块 (@d8d/supplier-module)
+│   ├── shared-types/                       # 共享类型定义
 │   │   ├── src/
-│   │   │   ├── entities/
-│   │   │   │   └── supplier.entity.ts                 # 供应商实体
-│   │   │   ├── services/
-│   │   │   │   └── supplier.service.ts                # 供应商服务
-│   │   │   ├── schemas/
-│   │   │   │   ├── supplier.schema.ts                 # 供应商Schema
-│   │   │   │   ├── user-supplier.schema.ts            # 用户专用Schema
-│   │   │   │   └── admin-supplier.schema.ts           # 管理员专用Schema
-│   │   │   ├── types/
-│   │   │   │   └── supplier.types.ts                  # 供应商类型定义
-│   │   │   └── routes/
-│   │   │       ├── index.ts                           # 路由导出
-│   │   │       ├── user-routes.ts                     # 用户路由
-│   │   │       └── admin-routes.ts                    # 管理员路由
+│   │   │   └── index.ts                    # 类型定义导出
+│   │   └── package.json
+│   ├── shared-ui-components/               # 共享UI组件
+│   ├── shared-utils/                       # 共享工具函数
+│   │   ├── src/
+│   │   │   ├── utils/
+│   │   │   │   ├── jwt.util.ts             # JWT工具
+│   │   │   │   ├── errorHandler.ts         # 错误处理
+│   │   │   │   ├── parseWithAwait.ts       # 异步解析
+│   │   │   │   ├── logger.ts               # 日志工具
+│   │   │   │   └── redis.util.ts           # Redis会话管理
+│   │   │   └── data-source.ts              # 数据库连接
 │   │   ├── tests/
-│   │   │   └── integration/
-│   │   │       ├── user-routes.integration.test.ts    # 用户路由集成测试
-│   │   │       └── admin-routes.integration.test.ts   # 管理员路由集成测试
 │   │   └── package.json
-│   ├── goods-module/           # 商品管理模块 (@d8d/goods-module)
+│   ├── mini-payment/                       # 小程序支付模块
 │   │   ├── src/
 │   │   │   ├── entities/
-│   │   │   │   ├── goods.entity.ts                  # 商品实体
-│   │   │   │   └── goods-category.entity.ts          # 商品分类实体
+│   │   │   │   ├── payment.entity.ts       # 支付实体
+│   │   │   │   └── payment.types.ts        # 支付类型定义
 │   │   │   ├── services/
-│   │   │   │   ├── goods.service.ts                  # 商品服务
-│   │   │   │   └── goods-category.service.ts         # 商品分类服务
+│   │   │   │   └── payment.service.ts      # 支付服务
 │   │   │   ├── schemas/
-│   │   │   │   ├── goods.schema.ts                   # 商品Schema
-│   │   │   │   ├── goods-category.schema.ts          # 商品分类Schema
-│   │   │   │   ├── random.schema.ts                  # 随机商品Schema
-│   │   │   │   ├── user-goods.schema.ts              # 用户专用Schema
-│   │   │   │   ├── admin-goods.schema.ts             # 管理员专用Schema
-│   │   │   │   └── public-goods.schema.ts            # 公开商品Schema
-│   │   │   ├── types/
-│   │   │   │   └── goods.types.ts                    # 商品类型定义
+│   │   │   │   └── payment.schema.ts       # 支付Schema
 │   │   │   └── routes/
-│   │   │       ├── admin-goods-categories.ts         # 商品分类管理路由
-│   │   │       ├── public-goods-random.ts            # 公开随机商品路由
-│   │   │       ├── user-goods-routes.ts              # 用户路由
-│   │   │       ├── admin-goods-routes.ts             # 管理员路由
-│   │   │       ├── public-goods-routes.ts            # 公开商品路由
-│   │   │       └── index.ts                          # 路由导出
+│   │   │       └── payment/
+│   │   │           ├── create.ts           # 支付创建路由
+│   │   │           ├── callback.ts         # 支付回调路由
+│   │   │           └── status.ts           # 支付状态查询路由
 │   │   ├── tests/
 │   │   │   └── integration/
-│   │   │       ├── admin-goods-categories.integration.test.ts    # 商品分类集成测试
-│   │   │       ├── public-goods-random.integration.test.ts       # 随机商品集成测试
-│   │   │       ├── user-goods-routes.integration.test.ts         # 用户路由集成测试
-│   │   │       ├── admin-goods-routes.integration.test.ts        # 管理员路由集成测试
-│   │   │       └── public-goods-routes.integration.test.ts       # 公开商品路由集成测试
+│   │   │       ├── payment.integration.test.ts       # 支付集成测试
+│   │   │       └── payment-callback.integration.test.ts # 支付回调集成测试
 │   │   └── package.json
-│   └── geo-areas/              # 地区模块 (@d8d/geo-areas)
+│   # ==================== 服务器包 ====================
+│   └── server/                             # API服务器包
 │       ├── src/
-│       │   ├── modules/areas/
-│       │   │   ├── area.entity.ts              # 地区实体
-│       │   │   ├── area.service.ts             # 地区服务
-│       │   │   └── area.schema.ts              # 地区Schema
-│       │   ├── api/
-│       │   │   ├── areas/index.ts              # 公共地区API
-│       │   │   └── admin/areas/index.ts        # 管理地区API
-│       │   └── index.ts                        # 包入口
+│       │   ├── api.ts                      # API路由导出
+│       │   └── index.ts                    # 服务器入口
 │       ├── tests/
 │       └── package.json
-│   └── orders-module/          # 订单管理模块 (@d8d/orders-module)
-│       ├── src/
-│       │   ├── entities/
-│       │   │   ├── order.entity.ts             # 订单实体
-│       │   │   ├── order-goods.entity.ts       # 订单商品实体
-│       │   │   ├── order-refund.entity.ts      # 订单退款实体
-│       │   │   └── index.ts                    # 实体导出
-│       │   ├── services/
-│       │   │   ├── order.service.ts            # 订单服务
-│       │   │   ├── order-goods.service.ts      # 订单商品服务
-│       │   │   ├── order-refund.service.ts     # 订单退款服务
-│       │   │   ├── user-order-goods.service.ts # 用户订单商品服务
-│       │   │   ├── user-refunds.service.ts     # 用户退款服务
-│       │   │   └── index.ts                    # 服务导出
-│       │   ├── schemas/
-│       │   │   ├── create-order.schema.ts      # 创建订单Schema
-│       │   │   ├── order.schema.ts             # 订单Schema
-│       │   │   ├── user-order.schema.ts        # 用户订单Schema
-│       │   │   ├── order-goods.schema.ts       # 订单商品Schema
-│       │   │   ├── order-refund.schema.ts      # 订单退款Schema
-│       │   │   └── index.ts                    # Schema导出
-│       │   ├── types/
-│       │   │   ├── order.types.ts              # 订单类型定义
-│       │   │   └── index.ts                    # 类型导出
-│       │   ├── routes/
-│       │   │   ├── create-order.ts             # 创建订单路由
-│       │   │   ├── user/
-│       │   │   │   ├── orders.ts               # 用户订单路由
-│       │   │   │   ├── order-items.ts          # 用户订单商品路由
-│       │   │   │   └── refunds.ts              # 用户退款路由
-│       │   │   ├── admin/
-│       │   │   │   ├── orders.ts               # 管理员订单路由
-│       │   │   │   ├── order-items.ts          # 管理员订单商品路由
-│       │   │   │   └── refunds.ts              # 管理员退款路由
-│       │   │   └── index.ts                    # 路由导出
-│       │   └── index.ts                        # 包入口
-│       ├── tests/
-│       │   └── integration/
-│       │       ├── entity-configuration.integration.test.ts    # 实体配置集成测试
-│       │       ├── create-order.integration.test.ts            # 创建订单集成测试
-│       │       ├── user-orders.integration.test.ts             # 用户订单集成测试
-│       │       ├── user-order-items.integration.test.ts        # 用户订单商品集成测试
-│       │       ├── user-refunds.integration.test.ts            # 用户退款集成测试
-│       │       ├── admin-orders.integration.test.ts            # 管理员订单集成测试
-│       │       ├── admin-order-items.integration.test.ts       # 管理员订单商品集成测试
-│       │       ├── admin-refunds.integration.test.ts           # 管理员退款集成测试
-│       │       └── utils/
-│       │           └── test-data-factory.ts                    # 测试数据工厂
-│       └── package.json
-│   └── shared-ui-components/    # 共享UI组件包 (@d8d/shared-ui-components)
-│       ├── src/
-│       │   ├── components/      # UI组件库
-│       │   │   ├── ui/          # shadcn/ui组件
-│       │   │   │   ├── accordion.tsx            # 手风琴组件
-│       │   │   │   ├── alert-dialog.tsx         # 警告对话框
-│       │   │   │   ├── alert.tsx                # 警告组件
-│       │   │   │   └── ...                      # 其他46+组件
-│       │   │   └── index.ts     # 组件导出
-│       │   └── index.ts         # 包入口
-│       ├── tests/
-│       └── package.json
-│   └── 多租户包架构 (Epic-007 多租户包复制方案)
-│       ├── 多租户模块包 (10个)
-│       │   ├── tenant-module-mt/                # 租户基础包 (@d8d/tenant-module-mt)
-│       │   ├── user-module-mt/                  # 多租户用户管理模块 (@d8d/user-module-mt)
-│       │   ├── auth-module-mt/                  # 多租户认证管理模块 (@d8d/auth-module-mt)
-│       │   ├── file-module-mt/                  # 多租户文件管理模块 (@d8d/file-module-mt)
-│       │   ├── geo-areas-mt/                    # 多租户地区模块 (@d8d/geo-areas-mt)
-│       │   ├── delivery-address-module-mt/      # 多租户地址管理模块 (@d8d/delivery-address-module-mt)
-│       │   ├── merchant-module-mt/              # 多租户商户管理模块 (@d8d/merchant-module-mt)
-│       │   ├── supplier-module-mt/              # 多租户供应商管理模块 (@d8d/supplier-module-mt)
-│       │   ├── goods-module-mt/                 # 多租户商品管理模块 (@d8d/goods-module-mt)
-│       │   ├── orders-module-mt/                # 多租户订单管理模块 (@d8d/orders-module-mt)
-│       │   └── advertisements-module-mt/        # 多租户广告管理模块 (@d8d/advertisements-module-mt)
-│       └── 多租户管理界面包 (10个)
-│           ├── auth-management-ui-mt/           # 多租户认证管理界面 (@d8d/auth-management-ui-mt)
-│           ├── user-management-ui-mt/           # 多租户用户管理界面 (@d8d/user-management-ui-mt)
-│           ├── advertisement-management-ui-mt/  # 多租户广告管理界面 (@d8d/advertisement-management-ui-mt)
-│           ├── advertisement-type-management-ui-mt/ # 多租户广告分类管理界面 (@d8d/advertisement-type-management-ui-mt)
-│           ├── order-management-ui-mt/          # 多租户订单管理界面 (@d8d/order-management-ui-mt)
-│           ├── goods-management-ui-mt/          # 多租户商品管理界面 (@d8d/goods-management-ui-mt)
-│           ├── goods-category-management-ui-mt/ # 多租户商品分类管理界面 (@d8d/goods-category-management-ui-mt)
-│           ├── supplier-management-ui-mt/       # 多租户供应商管理界面 (@d8d/supplier-management-ui-mt)
-│           ├── merchant-management-ui-mt/       # 多租户商户管理界面 (@d8d/merchant-management-ui-mt)
-│           ├── file-management-ui-mt/           # 多租户文件管理界面 (@d8d/file-management-ui-mt)
-│           ├── delivery-address-management-ui-mt/ # 多租户地址管理界面 (@d8d/delivery-address-management-ui-mt)
-│           └── area-management-ui-mt/           # 多租户区域管理界面 (@d8d/area-management-ui-mt)
-├── web/                        # Web应用 (Hono + React SSR)
+├── scripts/                                # 脚本文件目录
+├── web/                                    # Web管理后台 (Hono + React SSR)
 │   ├── src/
-│   │   ├── client/             # 客户端代码
-│   │   │   ├── admin/          # 管理后台
-│   │   │   │   ├── components/ # 管理后台组件
-│   │   │   │   │   ├── AvatarSelector.tsx       # 头像选择器
-│   │   │   │   │   ├── DataTablePagination.tsx  # 表格分页
-│   │   │   │   │   ├── ErrorPage.tsx            # 错误页面
-│   │   │   │   │   ├── FileSelector.tsx         # 文件选择器
-│   │   │   │   │   ├── MinioUploader.tsx        # MinIO上传器
-│   │   │   │   │   ├── NotFoundPage.tsx         # 404页面
-│   │   │   │   │   └── ProtectedRoute.tsx       # 路由保护
+│   │   ├── client/                         # 客户端代码
+│   │   │   ├── admin/                      # 管理后台
+│   │   │   │   ├── components/             # 管理后台组件
+│   │   │   │   │   ├── AvatarSelector.tsx  # 头像选择器
+│   │   │   │   │   ├── DataTablePagination.tsx # 表格分页
+│   │   │   │   │   ├── ErrorPage.tsx       # 错误页面
+│   │   │   │   │   ├── FileSelector.tsx    # 文件选择器
+│   │   │   │   │   ├── MinioUploader.tsx   # MinIO上传器
+│   │   │   │   │   ├── NotFoundPage.tsx    # 404页面
+│   │   │   │   │   └── ProtectedRoute.tsx  # 路由保护
 │   │   │   │   ├── hooks/
-│   │   │   │   │   └── AuthProvider.tsx         # 认证状态管理
+│   │   │   │   │   └── AuthProvider.tsx    # 认证状态管理
 │   │   │   │   ├── layouts/
-│   │   │   │   │   └── MainLayout.tsx           # 主布局
+│   │   │   │   │   └── MainLayout.tsx      # 主布局
 │   │   │   │   ├── pages/
-│   │   │   │   │   ├── Dashboard.tsx            # 仪表板
-│   │   │   │   │   ├── Files.tsx                # 文件管理
-│   │   │   │   │   ├── Login.tsx                # 登录页面
-│   │   │   │   │   └── Users.tsx                # 用户管理
-│   │   │   │   ├── menu.tsx                     # 菜单配置
-│   │   │   │   ├── routes.tsx                   # 路由配置
-│   │   │   │   └── index.tsx                    # 管理后台入口
+│   │   │   │   │   ├── Dashboard.tsx       # 仪表板
+│   │   │   │   │   ├── Files.tsx           # 文件管理
+│   │   │   │   │   ├── Login.tsx           # 登录页面
+│   │   │   │   │   └── Users.tsx           # 用户管理
+│   │   │   │   ├── menu.tsx                # 菜单配置
+│   │   │   │   ├── routes.tsx              # 路由配置
+│   │   │   │   └── index.tsx               # 管理后台入口
 │   │   │   ├── components/
-│   │   │   │   └── ui/                          # shadcn/ui组件库
-│   │   │   │       ├── accordion.tsx            # 手风琴组件
-│   │   │   │       ├── alert-dialog.tsx         # 警告对话框
-│   │   │   │       ├── alert.tsx                # 警告组件
-│   │   │   │       └── ...                      # 其他50+组件
-│   │   │   ├── api.ts                           # API客户端
-│   │   │   └── index.tsx                        # 前端入口
-│   │   ├── server/             # 服务器端代码 (SSR)
-│   │   │   ├── index.tsx                        # 服务器入口
-│   │   │   └── renderer.tsx                     # React渲染器
-│   │   └── share/              # 共享代码
+│   │   │   │   └── ui/                     # shadcn/ui组件库
+│   │   │   │       ├── accordion.tsx       # 手风琴组件
+│   │   │   │       ├── alert-dialog.tsx    # 警告对话框
+│   │   │   │       ├── alert.tsx           # 警告组件
+│   │   │   │       └── ...                 # 其他50+组件
+│   │   │   ├── api.ts                      # API客户端
+│   │   │   └── index.tsx                   # 前端入口
+│   │   ├── server/                         # 服务器端代码 (SSR)
+│   │   │   ├── index.tsx                   # 服务器入口
+│   │   │   └── renderer.tsx                # React渲染器
+│   │   └── share/                          # 共享代码
 │   ├── tests/
-│   │   ├── e2e/                # E2E测试 (Playwright)
-│   │   ├── integration/        # Web集成测试
-│   │   │   └── client/         # 客户端集成测试
-│   │   └── unit/               # 单元测试
-│   │       └── client/         # 客户端单元测试
-│   │           ├── pages/      # 页面组件测试
-│   │           │   └── Users.test.tsx           # 用户页面测试
-│   │           └── debug.test.tsx               # 调试测试
+│   │   ├── e2e/                            # E2E测试 (Playwright)
+│   │   ├── integration/                    # Web集成测试
+│   │   │   └── client/                     # 客户端集成测试
+│   │   └── unit/                           # 单元测试
+│   │       └── client/                     # 客户端单元测试
+│   │           ├── pages/                  # 页面组件测试
+│   │           │   └── Users.test.tsx      # 用户页面测试
+│   │           └── debug.test.tsx          # 调试测试
 │   └── package.json
-├── docs/                       # 项目文档
-│   └── architecture/           # 架构文档
-├── scripts/                    # 脚本文件
-├── .bmad-core/                 # BMAD核心配置
-├── .claude/                    # Claude配置
-├── .git/                       # Git仓库
-├── package.json                # 根项目配置
-├── pnpm-workspace.yaml         # pnpm workspace 配置
-└── pnpm-lock.yaml              # 依赖锁定文件
+├── .gitignore                              # Git忽略文件
+├── .npmrc                                  # NPM配置
+├── Dockerfile                              # Docker配置
+├── LICENSE                                 # 许可证
+├── README.md                               # 项目说明
+├── package.json                            # 根项目配置
+├── pnpm-lock.yaml                          # 依赖锁定文件
+├── pnpm-workspace.yaml                     # pnpm workspace配置
+└── tsconfig.json                           # TypeScript配置
 ```
 
-## 集成指南
+## 项目结构概览
+
+### 主要目录分类
+
+| 目录 | 用途 | 技术栈 |
+|------|------|--------|
+| `mini/` | 商户小程序 | Taro + React |
+| `mini-talent/` | 人才小程序 | Taro + React |
+| `mini-ui-packages/` | 小程序UI组件包 | React + Taro |
+| `web/` | Web管理后台 | Hono + React SSR |
+| `packages/` | 核心共享包 | Node.js + TypeScript |
+| `allin-packages/` | AllIn业务包 | Node.js + TypeScript |
+| `docs/` | 项目文档 | Markdown |
+
+### 包架构层次
+
+```
+基础设施层:
+  └── shared-types → shared-utils → shared-crud → shared-test-util
+
+业务模块层:
+  └── user-module → auth-module → file-module → geo-areas
+  └── goods-module → orders-module → merchant-module
+  └── (多租户版本: *-mt)
+
+UI展示层:
+  └── user-management-ui → auth-management-ui → file-management-ui
+  └── (多租户版本: *-ui-mt)
+
+应用层:
+  └── server (API服务器)
+  └── web (Web管理后台)
+  └── mini/mini-talent (小程序)
+```
+
+### 集成指南
+
 - **文件命名**: 保持现有kebab-case命名约定
-- **项目结构**: 采用monorepo模式,包含小程序(mini)、Web应用(web)和模块化包架构
-- **包管理**: 使用pnpm workspace管理多包依赖关系
+- **项目结构**: 采用monorepo模式,使用pnpm workspace管理多包依赖关系
+- **包管理**: 核心包在`packages/`,业务扩展包在`allin-packages/`,小程序UI组件在`mini-ui-packages/`
 - **小程序架构**: 基于Taro框架,支持多平台(微信小程序、H5等)
 - **Web应用架构**: 基于Hono + React SSR,使用shadcn/ui组件库
 - **模块化架构**: 采用分层包结构,支持按需安装和独立开发
-- **API服务器**: 重构后的`@d8d/server`包,基于模块化架构,提供RESTful API
+- **多租户支持**: 核心模块和UI包都有对应的多租户版本(`-mt`后缀)
 - **API设计**: 使用Hono框架,RESTful API设计,支持文件分片上传
 - **数据库**: 使用PostgreSQL + TypeORM
 - **存储**: 使用MinIO进行文件存储
-- **包架构层次**:
-  - **基础设施层**: shared-types → shared-utils → shared-crud
-  - **测试基础设施**: shared-test-util
-  - **业务模块层**: user-module → auth-module → file-module → delivery-address-module → advertisements-module → merchant-module → supplier-module → goods-module → geo-areas → orders-module
-  - **多租户模块层**: 10个多租户模块包(-mt后缀),支持租户数据隔离
-  - **前端界面层**: 共享UI组件包 + 单租户管理界面包 + 多租户管理界面包
-  - **应用层**: server (重构后)
-- **多租户架构**:
-  - **包复制策略**: 基于Epic-007方案,通过复制单租户包创建多租户版本
-  - **租户隔离**: 通过租户ID实现数据隔离,支持多租户部署
-  - **前端包**: 10个多租户管理界面包,支持租户上下文管理
-  - **后端包**: 10个多租户模块包,支持租户数据隔离
-  - **共享组件**: `@d8d/shared-ui-components` 提供46+基础UI组件
 - **测试结构**:
   - **基础设施包**: 每个包独立的单元测试和集成测试
   - **业务模块包**: 每个模块包含完整的测试套件
-  - **多租户包**: 独立的测试套件,验证租户数据隔离
   - **server包**: 集成测试验证模块间协作
   - **web应用**: 组件测试、集成测试和E2E测试
 - **开发环境**: 多八多云端开发容器,包含Node.js 20.19.2、PostgreSQL 17、Redis 7、MinIO
 - **构建工具**: 使用Vite + pnpm,支持SSR构建
-- **架构优势**:
-  - 清晰的模块边界和职责分离
-  - 支持按需安装,减少包体积
-  - 基础设施和业务逻辑分离
-  - 多租户支持,支持租户数据隔离
-  - 统一的测试模式和工具
-  - 更好的代码复用和维护性
-  - 独立的包版本管理
-  - 支持单租户和多租户部署模式
+
+### 架构优势
+
+- 清晰的模块边界和职责分离
+- 支持按需安装,减少包体积
+- 基础设施和业务逻辑分离
+- 统一的测试模式和工具
+- 更好的代码复用和维护性
+- 独立的包版本管理
+- 支持多租户架构
+- 小程序UI组件复用(rencai/yongren系列)

+ 161 - 592
docs/architecture/testing-strategy.md

@@ -3,193 +3,95 @@
 ## 版本信息
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
-| 2.9 | 2025-12-15 | 添加API模拟规范和前端组件测试策略,修正$path()方法描述与实际代码不一致问题 | James |
+| 3.0 | 2025-12-26 | 重构为概述文档,拆分详细规范到独立文档 | James (Claude Code) |
+| 2.10 | 2025-12-12 | 添加使用共享测试工具处理复杂组件的规范 | James (Claude Code) |
+| 2.9 | 2025-12-12 | 添加测试用例编写规范,基于订单管理集成测试经验 | James (Claude Code) |
 | 2.8 | 2025-11-11 | 更新包测试结构,添加模块化包测试策略 | Winston |
 | 2.7 | 2025-11-09 | 更新为monorepo测试架构,清理重复测试文件 | James |
-| 2.6 | 2025-10-15 | 完成遗留测试文件迁移到统一的tests目录结构 | Winston |
-| 2.5 | 2025-10-14 | 更新测试文件位置到统一的tests目录结构 | Claude |
-| 2.4 | 2025-09-20 | 更新测试策略与主架构文档版本一致 | Winston |
 
 ## 概述
 
-本文档定义了D8D Starter项目的完整测试策略,基于monorepo架构和现有的测试基础设施。测试策略遵循测试金字塔模型,确保代码质量、功能稳定性和系统可靠性
+本文档是D8D Starter项目的测试策略概述,定义了整体的测试架构和原则。详细的测试规范请参考各包的专用测试规范文档
 
-### 测试架构更新 (v2.8)
+### 测试架构概览
 
-项目已重构为模块化包架构,测试架构相应调整为:
-- **基础设施包**: shared-types、shared-utils、shared-crud、shared-test-util
-- **业务模块包**: user-module、auth-module、file-module、geo-areas
-- **应用层**: server (重构后),包含模块集成测试
-- **web**: Web应用,包含组件测试、集成测试和E2E测试
-- **CI/CD**: 独立的工作流分别处理各包的测试
+项目采用分层测试架构,遵循测试金字塔模型:
 
-### 包测试架构 (v2.8)
+```
+           /\
+          /  \        E2E测试 (Playwright)
+         /____\       关键业务流程 100%
+        /      \
+       /        \     集成测试 (Vitest)
+      /          \    模块间协作 ≥60%
+     /____________\
+    /              \   单元测试 (Vitest/Jest)
+   /________________\  独立单元 ≥80%
+```
 
-项目采用分层测试架构,每个包独立测试:
-- **基础设施包**: 纯单元测试,不依赖外部服务
-- **业务模块包**: 单元测试 + 集成测试,验证模块功能
-- **应用层**: 集成测试,验证模块间协作
-- **共享测试工具**: shared-test-util 提供统一的测试基础设施
+### 包分类测试策略
 
-## 测试金字塔策略
+| 包类型 | 测试类型 | 主要框架 | 详细规范 |
+|--------|----------|----------|----------|
+| **Web UI包** | 组件测试 + 集成测试 + E2E测试 | Vitest + Testing Library + Playwright | [Web UI包测试规范](./web-ui-testing-standards.md) |
+| **Web Server包** | 集成测试 | Vitest + hono/testing | [Web Server包测试规范](./web-server-testing-standards.md) |
+| **后端模块包** | 单元测试 + 集成测试 | Vitest + TypeORM | [后端模块包测试规范](./backend-module-testing-standards.md) |
+| **Mini UI包** | 组件测试 + 集成测试 | Jest + @testing-library/react | [Mini UI包测试规范](./mini-ui-testing-standards.md) |
+
+## 测试分层策略
 
 ### 单元测试 (Unit Tests)
 - **范围**: 单个函数、类或组件
-- **目标**: 验证独立单元的correctness
-- **位置**:
-  - **基础设施包**: `packages/shared-*/tests/unit/**/*.test.ts`
-  - **业务模块包**: `packages/*-module/tests/unit/**/*.test.ts`
-  - **server包**: `packages/server/tests/unit/**/*.test.ts`
-  - **web应用**: `web/tests/unit/**/*.test.{ts,tsx}`
-- **框架**: Vitest
-- **覆盖率目标**: ≥ 80%
+- **目标**: 验证独立单元的正确性
 - **执行频率**: 每次代码变更
+- **执行速度**: 快(毫秒级)
+- **覆盖率目标**: ≥ 80%
+
+**适用包**:
+- 后端模块包的Service层、Schema验证
+- Web UI包的独立组件
+- 共享工具包的纯函数
 
 ### 集成测试 (Integration Tests)
 - **范围**: 多个组件/服务协作
 - **目标**: 验证模块间集成和交互
-- **位置**:
-  - **业务模块包**: `packages/*-module/tests/integration/**/*.test.ts`
-  - **server包**: `packages/server/tests/integration/**/*.test.ts` (模块集成测试)
-  - **web应用**: `web/tests/integration/**/*.test.{ts,tsx}`
-- **框架**: Vitest + Testing Library + hono/testing + shared-test-util
-- **覆盖率目标**: ≥ 60%
 - **执行频率**: 每次API变更
+- **执行速度**: 中等(秒级)
+- **覆盖率目标**: ≥ 60%
+
+**适用包**:
+- Web Server包的API端点集成
+- 后端模块包的路由与数据库集成
+- Web UI包的组件与API集成
 
 ### E2E测试 (End-to-End Tests)
 - **范围**: 完整用户流程
 - **目标**: 验证端到端业务流程
-- **位置**: `web/tests/e2e/**/*.test.{ts,tsx}`
-- **框架**: Playwright
-- **覆盖率目标**: 关键用户流程100%
 - **执行频率**: 每日或每次重大变更
+- **执行速度**: 慢(分钟级)
+- **覆盖率目标**: 关键用户流程100%
 
-### 小程序测试策略
-- **项目**: mini小程序 (Taro小程序)
-- **测试框架**: Jest (不是Vitest)
-- **测试位置**: `mini/tests/unit/**/*.test.{ts,tsx}`
-- **测试特点**:
-  - 使用Jest测试框架,配置在`mini/jest.config.js`中
-  - 包含Taro小程序API的模拟 (`__mocks__/taroMock.ts`)
-  - 组件测试使用React Testing Library
-  - 支持TypeScript和ES6+语法
-- **测试命令**:
-  - `pnpm test` - 运行所有测试
-  - `pnpm test --testNamePattern "测试名称"` - 运行特定测试
-  - `pnpm test:coverage` - 生成覆盖率报告
-
-## 测试环境配置
-
-### 开发环境
-```typescript
-// vitest.config.ts - 开发环境配置
-export default defineConfig({
-  test: {
-    projects: [
-      // Node.js 环境项目 - 后端测试
-      {
-        test: {
-          include: [
-            'tests/unit/server/**/*.test.{ts,js}',
-            'tests/integration/server/**/*.test.{ts,js}'
-          ],
-          // ... 其他配置
-        }
-      },
-      // Happy DOM 环境项目 - 前端组件测试
-      {
-        test: {
-          include: [
-            'tests/unit/client/**/*.test.{ts,js,tsx,jsx}',
-            'tests/integration/client/**/*.test.{ts,js,tsx,jsx}'
-          ],
-          // ... 其他配置
-        }
-      }
-    ]
-  }
-});
-```
+**适用包**:
+- Web应用的完整用户场景
+- Mini小程序的关键业务流程
 
-### CI/CD环境
-```yaml
-# GitHub Actions 测试配置 (模块化包架构)
-name: Test Pipeline
-
-jobs:
-  # 基础设施包测试
-  shared-packages-tests:
-    runs-on: ubuntu-latest
-    steps:
-      - run: cd packages/shared-types && pnpm test
-      - run: cd packages/shared-utils && pnpm test
-      - run: cd packages/shared-crud && pnpm test
-      - run: cd packages/shared-test-util && pnpm test
-
-  # 业务模块包测试
-  business-modules-tests:
-    runs-on: ubuntu-latest
-    services:
-      postgres:
-        image: postgres:17
-        env:
-          POSTGRES_PASSWORD: test_password
-          POSTGRES_DB: test_d8dai
-    steps:
-      - run: cd packages/user-module && pnpm test
-      - run: cd packages/auth-module && pnpm test
-      - run: cd packages/file-module && pnpm test
-      - run: cd packages/geo-areas && pnpm test
-
-  # 服务器集成测试
-  server-integration-tests:
-    runs-on: ubuntu-latest
-    services:
-      postgres:
-        image: postgres:17
-        env:
-          POSTGRES_PASSWORD: test_password
-          POSTGRES_DB: test_d8dai
-    steps:
-      - run: cd packages/server && pnpm test
-
-  # Web应用测试
-  web-integration-tests:
-    runs-on: ubuntu-latest
-    services:
-      postgres:
-        image: postgres:17
-        env:
-          POSTGRES_PASSWORD: test_password
-          POSTGRES_DB: test_d8dai
-    steps:
-      - run: cd web && pnpm test:integration
-
-  web-component-tests:
-    runs-on: ubuntu-latest
-    steps:
-      - run: cd web && pnpm test:components
-
-  web-e2e-tests:
-    runs-on: ubuntu-latest
-    steps:
-      - run: cd web && pnpm test:e2e:chromium
-```
+## 测试框架栈
 
-## 测试覆盖率标准
+### Web应用测试
+- **Vitest**: 测试运行器
+- **Testing Library**: React组件测试
+- **Playwright**: E2E测试
+- **Happy DOM**: 轻量级DOM环境
 
-### 各层覆盖率要求
-| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
-|----------|----------|----------|--------------|
-| 单元测试 | 70% | 80% | 90% |
-| 集成测试 | 50% | 60% | 70% |
-| E2E测试 | 关键流程100% | 主要流程80% | - |
+### 后端测试
+- **Vitest**: 测试运行器
+- **hono/testing**: Hono路由测试
+- **TypeORM**: 数据库测试
+- **PostgreSQL**: 测试数据库
 
-### 关键模块定义
-- **认证授权模块**: 必须达到90%单元测试覆盖率
-- **数据库操作模块**: 必须达到85%单元测试覆盖率
-- **核心业务逻辑**: 必须达到80%集成测试覆盖率
-- **用户管理功能**: 必须100% E2E测试覆盖
+### Mini小程序测试
+- **Jest**: 测试运行器(与Web应用不同)
+- **@testing-library/react**: 组件测试
 
 ## 测试数据管理
 
@@ -217,421 +119,63 @@ const inactiveUser = createTestUser({ active: false });
 - **E2E测试**: 使用接近生产环境的数据库
 
 ### 数据清理策略
-1. **事务回滚** (推荐)
-2. **数据库清理** (每个测试后)
-3. **测试数据隔离** (使用唯一标识符)
-
-## API模拟规范
-
-### 概述
-API模拟规范为管理后台UI包提供测试中的API模拟策略。虽然当前项目实践中每个UI包都有自己的客户端管理器,但为了简化测试复杂度、特别是跨UI包集成测试场景,规范要求统一模拟共享UI组件包中的`rpcClient`函数。
-
-### 问题背景
-当前实现中,每个UI包测试文件模拟自己的客户端管理器(如`AdvertisementClientManager`、`UserClientManager`)。这种模式在单一UI包测试时可行,但在**跨UI包集成测试**时存在严重问题:
-
-**示例场景**:收货地址UI包中使用区域管理UI包的区域选择组件,两个组件分别使用各自的客户端管理器。
-- 收货地址组件 → 使用`DeliveryAddressClientManager`
-- 区域选择组件 → 使用`AreaClientManager`
-- 测试时需要同时模拟两个客户端管理器,配置复杂且容易冲突
-
-**统一模拟优势**:通过模拟`@d8d/shared-ui-components`包中的`rpcClient`函数,可以:
-1. **统一控制**:所有API调用都经过同一个模拟点
-2. **简化配置**:无需关心具体客户端管理器,只需配置API响应
-3. **跨包支持**:天然支持多个UI包组件的集成测试
-4. **维护性**:API响应配置集中管理,易于更新
-
-### 现有模式分析(仅供参考)
-项目中的管理后台UI包当前遵循以下架构模式:
-1. **客户端管理器模式**:每个UI包都有一个客户端管理器类(如`AdvertisementClientManager`、`UserClientManager`)
-2. **rpcClient使用**:客户端管理器使用`@d8d/shared-ui-components`包中的`rpcClient`函数创建Hono RPC客户端
-3. **API结构**:生成的客户端使用Hono风格的方法调用(如`index.$get`、`index.$post`、`:id.$put`等)
-
-**注意**:新的测试规范要求直接模拟`rpcClient`函数,而不是模拟各个客户端管理器。
-
-### rpcClient函数分析
-`rpcClient`函数位于`@d8d/shared-ui-components`包的`src/utils/hc.ts`文件中,其核心功能是创建Hono RPC客户端:
-
-```typescript
-// packages/shared-ui-components/src/utils/hc.ts
-export const rpcClient = <T extends Hono<any, any, any>>(aptBaseUrl: string): ReturnType<typeof hc<T>> => {
-  return hc<T>(aptBaseUrl, {
-    fetch: axiosFetch
-  })
-}
-```
-
-该函数接收API基础URL参数,返回一个配置了axios适配器的Hono客户端实例。
-
-### 模拟策略
-
-#### 1. 统一模拟rpcClient函数
-在测试中,使用Vitest的`vi.mock`直接模拟`@d8d/shared-ui-components`包中的`rpcClient`函数,统一拦截所有API调用:
-
-```typescript
-// 测试文件顶部 - 统一模拟rpcClient函数
-import { vi } from 'vitest'
-import type { Hono } from 'hono'
-
-// 创建模拟的rpcClient函数
-const mockRpcClient = vi.fn((aptBaseUrl: string) => {
-  // 根据页面组件实际调用的RPC路径定义模拟端点
-  return {
-    // 收货地址UI包使用的端点
-    index: {
-      $get: vi.fn(),
-      $post: vi.fn(),
-    },
-    ':id': {
-      $put: vi.fn(),
-      $delete: vi.fn(),
-    },
-
-    // 区域管理UI包使用的端点(跨包集成)
-    provinces: {
-      $get: vi.fn(),
-    },
-
-    // 地区列表API端点
-    $get: vi.fn(),
-  }
-})
-
-// 模拟共享UI组件包中的rpcClient函数
-vi.mock('@d8d/shared-ui-components/utils/hc', () => ({
-  rpcClient: mockRpcClient
-}))
-```
-
-#### 2. 创建模拟响应辅助函数
-创建通用的模拟响应辅助函数,用于生成一致的API响应格式:
-
-```typescript
-// 在测试文件中定义或从共享工具导入
-const createMockResponse = (status: number, data?: any) => ({
-  status,
-  ok: status >= 200 && status < 300,
-  body: null,
-  bodyUsed: false,
-  statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
-  headers: new Headers(),
-  url: '',
-  redirected: false,
-  type: 'basic' as ResponseType,
-  json: async () => data || {},
-  text: async () => '',
-  blob: async () => new Blob(),
-  arrayBuffer: async () => new ArrayBuffer(0),
-  formData: async () => new FormData(),
-  clone: function() { return this; }
-});
-
-// 创建简化版响应工厂(针对常见业务数据结构)
-const createMockApiResponse = <T>(data: T, success = true) => ({
-  success,
-  data,
-  timestamp: new Date().toISOString()
-})
-
-const createMockErrorResponse = (message: string, code = 'ERROR') => ({
-  success: false,
-  error: { code, message },
-  timestamp: new Date().toISOString()
-})
-```
-
-#### 3. 在测试用例中配置模拟响应
-在测试用例的`beforeEach`或具体测试中配置模拟响应,支持跨UI包集成:
-
-```typescript
-// 跨UI包集成测试示例:收货地址UI包(包含区域选择组件)
-describe('收货地址管理(跨UI包集成)', () => {
-  let mockClient: any;
-
-  beforeEach(() => {
-    vi.clearAllMocks();
-
-    // 获取模拟的rpcClient实例
-    mockClient = mockRpcClient('/');
-
-    // 配置收货地址API响应(收货地址UI包)
-    mockClient.index.$get.mockResolvedValue(createMockResponse(200, {
-      data: [
-        {
-          id: 1,
-          name: '测试地址',
-          phone: '13800138000',
-          provinceId: 1,
-          cityId: 2,
-          districtId: 3,
-          detail: '测试街道'
-        }
-      ],
-      pagination: { total: 1, page: 1, pageSize: 10 }
-    }));
-
-    mockClient.index.$post.mockResolvedValue(createMockResponse(201, {
-      id: 2,
-      name: '新地址'
-    }));
-
-    mockClient[':id']['$put'].mockResolvedValue(createMockResponse(200));
-    mockClient[':id']['$delete'].mockResolvedValue(createMockResponse(204));
-
-    // 配置区域API响应(区域管理UI包 - 跨包支持)
-    mockClient.$get.mockResolvedValue(createMockResponse(200, {
-      data: [
-        { id: 1, name: '北京市', code: '110000', level: 1 },
-        { id: 2, name: '朝阳区', code: '110105', level: 2, parentId: 1 },
-        { id: 3, name: '海淀区', code: '110108', level: 2, parentId: 1 }
-      ]
-    }));
-
-    mockClient.provinces.$get.mockResolvedValue(createMockResponse(200, {
-      data: [
-        { id: 1, name: '北京市', code: '110000' },
-        { id: 2, name: '上海市', code: '310000' }
-      ]
-    }));
-
-    // 获取某个地区的子地区(如城市)通常通过查询参数实现
-    mockClient.$get.mockImplementation((options?: any) => {
-      if (options?.query?.parentId === 1) {
-        return Promise.resolve(createMockResponse(200, {
-          data: [
-            { id: 2, name: '朝阳区', code: '110105', parentId: 1 },
-            { id: 3, name: '海淀区', code: '110108', parentId: 1 }
-          ]
-        }));
-      }
-      // 默认返回空列表
-      return Promise.resolve(createMockResponse(200, { data: [] }));
-    });
-  });
-
-  it('应该显示收货地址列表并支持区域选择', async () => {
-    // 测试代码:验证收货地址UI和区域选择组件都能正常工作
-    // 所有API调用都通过统一的mockRpcClient模拟
-  });
-
-  it('应该处理API错误场景', async () => {
-    // 模拟API错误
-    mockClient.index.$get.mockRejectedValue(new Error('网络错误'));
-
-    // 测试错误处理
-  });
-});
-```
-
-### 管理后台UI包测试策略
-
-#### 1. 模拟范围
-- **统一模拟点**: 集中模拟`@d8d/shared-ui-components/utils/hc`中的`rpcClient`函数
-- **HTTP方法**: 支持Hono风格的`$get`、`$post`、`$put`、`$delete`方法
-- **API端点**: 支持标准端点(`index`)、参数化端点(`:id`)和属性访问端点(如`client.provinces.$get()`)
-- **响应格式**: 模拟完整的Response对象,包含`status`、`ok`、`json()`等方法
-- **跨包支持**: 天然支持多个UI包组件的API模拟,无需分别模拟客户端管理器
-
-#### 2. 测试设置
-1. **统一模拟**: 在每个测试文件顶部使用`vi.mock`统一模拟`rpcClient`函数
-2. **测试隔离**: 每个测试用例使用独立的模拟实例,在`beforeEach`中重置
-3. **响应配置**: 根据测试场景配置不同的模拟响应(成功、失败、错误等)
-4. **错误测试**: 模拟各种错误场景(网络错误、验证错误、权限错误、服务器错误等)
-5. **跨包集成**: 支持配置多个UI包的API响应,适用于组件集成测试
-
-#### 3. 最佳实践
-- **统一模拟**: 所有API调用都通过模拟`rpcClient`函数统一拦截
-- **按需定义**: 根据页面组件实际调用的RPC路径定义模拟端点,无需动态创建所有可能端点
-- **类型安全**: 使用TypeScript确保模拟响应与API类型兼容
-- **可维护性**: 保持模拟响应与实际API响应结构一致,便于后续更新
-- **文档化**: 在测试注释中说明模拟的API行为和预期结果
-- **响应工厂**: 创建可重用的模拟响应工厂函数,确保响应格式一致性
-- **跨包考虑**: 为集成的UI包组件配置相应的API响应
-
-### 验证和调试
-
-#### 1. 模拟验证
-```typescript
-// 验证API调用次数和参数 - 使用统一模拟的rpcClient
-describe('API调用验证(统一模拟)', () => {
-  beforeEach(() => {
-    vi.clearAllMocks();
-  });
-
-  it('应该验证API调用次数和参数', async () => {
-    // 获取模拟的客户端实例
-    const mockClient = mockRpcClient('/');
-
-    // 配置模拟响应
-    mockClient.index.$get.mockResolvedValue(createMockResponse(200, { data: [] }));
-    mockClient.index.$post.mockResolvedValue(createMockResponse(201, { id: 1 }));
-    mockClient[':id']['$put'].mockResolvedValue(createMockResponse(200));
-    mockClient.$get.mockResolvedValue(createMockResponse(200, { data: [] }));
-
-    // 执行测试代码(触发API调用)...
-
-    // 验证API调用次数
-    expect(mockClient.index.$get).toHaveBeenCalledTimes(1);
-    expect(mockClient.$get).toHaveBeenCalledTimes(1);
-
-    // 验证API调用参数
-    expect(mockClient.index.$post).toHaveBeenCalledWith({
-      json: {
-        title: '新广告',
-        code: 'new-ad',
-        typeId: 1
-      }
-    });
-
-    // 验证带参数的API调用
-    expect(mockClient[':id']['$put']).toHaveBeenCalledWith({
-      param: { id: 1 },
-      json: {
-        title: '更新后的广告'
-      }
-    });
-
-    // 验证地区API调用
-    expect(mockClient.$get).toHaveBeenCalledWith({
-      query: { level: 1 }
-    });
-  });
-
-  it('应该验证错误场景', async () => {
-    const mockClient = mockRpcClient('/');
-
-    // 配置错误响应
-    mockClient.index.$get.mockRejectedValue(new Error('网络错误'));
-
-    // 执行测试代码...
-
-    // 验证错误调用
-    expect(mockClient.index.$get).toHaveBeenCalledTimes(1);
-  });
-});
-```
-
-#### 2. 调试技巧
-- **console.debug**: 在测试中使用`console.debug`输出模拟调用信息,便于调试
-  ```typescript
-  // 在测试中输出调试信息
-  console.debug('Mock client calls:', {
-    getCalls: mockClient.index.$get.mock.calls,
-    postCalls: mockClient.index.$post.mock.calls
-  });
-  ```
-
-- **调用检查**: 使用`vi.mocked()`检查模拟函数的调用参数和次数
-  ```typescript
-  // 检查mockRpcClient的调用
-  const mockCalls = vi.mocked(mockRpcClient).mock.calls;
-  console.debug('rpcClient调用参数:', mockCalls);
-
-  // 检查具体端点调用
-  const getCalls = vi.mocked(mockClient.index.$get).mock.calls;
-  ```
-
-- **响应验证**: 确保模拟响应的格式与实际API响应一致
-  ```typescript
-  // 验证响应格式
-  const response = await mockClient.index.$get();
-  expect(response.status).toBe(200);
-  expect(response.ok).toBe(true);
-  const data = await response.json();
-  expect(data).toHaveProperty('data');
-  expect(data).toHaveProperty('pagination');
-  ```
-
-- **错误模拟**: 测试各种错误场景,确保UI能正确处理
-  ```typescript
-  // 模拟不同类型的错误
-  mockClient.index.$get.mockRejectedValue(new Error('网络错误')); // 网络错误
-  mockClient.index.$get.mockResolvedValue(createMockResponse(500)); // 服务器错误
-  mockClient.index.$get.mockResolvedValue(createMockResponse(401)); // 认证错误
-  ```
-
-- **快照测试**: 使用Vitest的快照测试验证UI在不同API响应下的渲染结果
-- **跨包调试**: 在跨UI包集成测试中,验证所有相关API都正确配置了模拟响应
+1. **事务回滚** (推荐) - 测试后自动回滚
+2. **数据库清理** (每个测试后) - 清理测试数据
+3. **测试数据隔离** (使用唯一标识符) - 避免数据冲突
 
 ## 测试执行流程
 
 ### 本地开发测试
 
-#### 基础设施
+#### 后端模块包
 ```bash
-# 运行所有基础设施包测试
-cd packages/shared-types && pnpm test
-cd packages/shared-utils && pnpm test
-cd packages/shared-crud && pnpm test
-cd packages/shared-test-util && pnpm test
-
-# 生成覆盖率报告
-cd packages/shared-utils && pnpm test:coverage
-```
-
-#### 业务模块包
-```bash
-# 运行所有业务模块包测试
+# 运行所有测试
 cd packages/user-module && pnpm test
-cd packages/auth-module && pnpm test
-cd packages/file-module && pnpm test
-cd packages/geo-areas && pnpm test
 
 # 运行单元测试
-cd packages/user-module && pnpm test:unit
+pnpm test:unit
 
 # 运行集成测试
-cd packages/auth-module && pnpm test:integration
+pnpm test:integration
 
 # 生成覆盖率报告
-cd packages/user-module && pnpm test:coverage
+pnpm test:coverage
 ```
 
-#### server包
+#### Web Server包
 ```bash
-# 运行所有测试
-cd packages/server && pnpm test
-
 # 运行集成测试
-cd packages/server && pnpm test:integration
+cd packages/server && pnpm test
 
 # 生成覆盖率报告
-cd packages/server && pnpm test:coverage
+pnpm test:coverage
 ```
 
-#### web应用
+#### Web应用
 ```bash
 # 运行所有测试
 cd web && pnpm test
 
-# 运行单元测试
-cd web && pnpm test:unit
+# 运行组件测试
+pnpm test:components
 
 # 运行集成测试
-cd web && pnpm test:integration
-
-# 运行组件测试
-cd web && pnpm test:components
+pnpm test:integration
 
 # 运行E2E测试
-cd web && pnpm test:e2e:chromium
+pnpm test:e2e:chromium
 
 # 生成覆盖率报告
-cd web && pnpm test:coverage
+pnpm test:coverage
 ```
 
-#### 小程序 (mini)
+#### Mini UI包
 ```bash
-# 运行所有测试
-cd mini && pnpm test
+# 进入UI包目录
+cd mini-ui-packages/rencai-dashboard-ui
 
-# 运行特定测试
-cd mini && pnpm test --testNamePattern "商品卡片"
-
-# 生成覆盖率报告
-cd mini && pnpm test:coverage
-
-# 调试测试
-cd mini && pnpm test --testNamePattern "测试名称" --verbose
+# 运行测试(使用Jest)
+pnpm test
 ```
 
 ### CI/CD流水线测试
@@ -642,6 +186,21 @@ cd mini && pnpm test --testNamePattern "测试名称" --verbose
 5. **覆盖率检查** → 满足最低要求
 6. **测试报告** → 生成详细报告
 
+## 覆盖率标准
+
+### 各层覆盖率要求
+| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
+|----------|----------|----------|--------------|
+| 单元测试 | 70% | 80% | 90% |
+| 集成测试 | 50% | 60% | 70% |
+| E2E测试 | 关键流程100% | 主要流程80% | - |
+
+### 关键模块定义
+- **认证授权模块**: 必须达到90%单元测试覆盖率
+- **数据库操作模块**: 必须达到85%单元测试覆盖率
+- **核心业务逻辑**: 必须达到80%集成测试覆盖率
+- **用户管理功能**: 必须100% E2E测试覆盖
+
 ## 质量门禁
 
 ### 测试通过标准
@@ -685,44 +244,6 @@ cd mini && pnpm test --testNamePattern "测试名称" --verbose
 - **autocannon**: API性能测试
 - **Playwright**: E2E性能监控
 
-## 测试文档标准
-
-### 测试代码规范
-```typescript
-// 良好的测试示例
-describe('UserService', () => {
-  describe('createUser()', () => {
-    it('应该创建新用户并返回用户对象', async () => {
-      // Arrange
-      const userData = { username: 'testuser', email: 'test@example.com' };
-
-      // Act
-      const result = await userService.createUser(userData);
-
-      // Assert
-      expect(result).toHaveProperty('id');
-      expect(result.username).toBe('testuser');
-      expect(result.email).toBe('test@example.com');
-    });
-
-    it('应该拒绝重复的用户名', async () => {
-      // Arrange
-      const existingUser = await createTestUser({ username: 'existing' });
-
-      // Act & Assert
-      await expect(
-        userService.createUser({ username: 'existing', email: 'new@example.com' })
-      ).rejects.toThrow('用户名已存在');
-    });
-  });
-});
-```
-
-### 测试命名约定
-- **文件名**: `[module].test.ts` 或 `[module].integration.test.ts`
-- **描述**: 使用「应该...」格式描述测试行为
-- **用例**: 明确描述测试场景和预期结果
-
 ## 监控和报告
 
 ### 测试监控指标
@@ -737,26 +258,74 @@ describe('UserService', () => {
 - **自定义报告**: 业务指标测试报告
 - **历史趋势**: 测试质量趋势分析
 
-## 附录
+## 调试技巧
+
+### 查看测试详情
+```bash
+# 运行特定测试查看详细信息
+pnpm test --testNamePattern "测试名称"
+
+# 显示完整输出
+pnpm test --reporter=verbose
+
+# 监听模式(开发时)
+pnpm test --watch
+```
+
+### 调试E2E测试
+```bash
+# E2E测试失败时先查看页面结构
+cat test-results/**/error-context.md
+
+# 使用调试模式
+pnpm test:e2e:chromium --debug
+```
+
+### 表单调试
+```typescript
+// 表单提交失败时,在form onsubmit的第二个参数中加console.debug
+form.handleSubmit(handleSubmit, (errors) => console.debug('表单验证错误:', errors))
+```
+
+## 相关文档
+
+### 专用测试规范
+- **[Web UI包测试规范](./web-ui-testing-standards.md)** - Web UI组件和Web应用测试
+- **[Web Server包测试规范](./web-server-testing-standards.md)** - API服务器集成测试
+- **[后端模块包测试规范](./backend-module-testing-standards.md)** - 业务模块单元和集成测试
+- **[Mini UI包测试规范](./mini-ui-testing-standards.md)** - Taro小程序UI包测试
+
+### 开发规范
+- **[编码标准](./coding-standards.md)** - 代码风格和最佳实践
+- **[UI包开发规范](./ui-package-standards.md)** - Web UI包开发
+- **[Mini UI包开发规范](./mini-ui-package-standards.md)** - Mini UI包开发
+- **[后端模块包开发规范](./backend-module-package-standards.md)** - 后端模块开发
 
-### 相关文档
-- [集成测试最佳实践](../integration-testing-best-practices.md)
-- [编码标准](./coding-standards.md)
-- [API设计规范](./api-design-integration.md)
+### 架构文档
+- **[技术栈](./tech-stack.md)** - 项目技术栈和版本
+- **[源码树](./source-tree.md)** - 项目文件结构
+- **[API设计规范](./api-design-integration.md)** - API设计标准
+
+## 工具版本
+
+| 工具 | 版本 | 用途 |
+|------|------|------|
+| Vitest | 3.2.4 | Web/后端测试运行器 |
+| Jest | 最新 | Mini UI包测试运行器 |
+| Testing Library | 16.3.0 | React组件测试 |
+| Playwright | 1.55.0 | E2E测试 |
+| Happy DOM | 最新 | 轻量级DOM环境 |
+| hono/testing | 内置 | Hono路由测试 |
+| TypeORM | 0.3.20 | 数据库测试 |
+| shared-test-util | 1.0.0 | 共享测试基础设施 |
 
-### 工具版本
-- **Vitest**: 3.2.4
-- **Testing Library**: 16.3.0
-- **Playwright**: 1.55.0
-- **hono/testing**: 内置(Hono 4.8.5)
-- **shared-test-util**: 1.0.0 (测试基础设施包)
-- **TypeORM**: 0.3.20 (数据库测试)
-- **Redis**: 7.0.0 (会话管理测试)
-- **Jest**: 29.x (mini小程序专用)
+## 更新日志
 
-### 更新日志
 | 日期 | 版本 | 描述 |
 |------|------|------|
+| 2025-12-26 | 3.0 | 重构为概述文档,拆分详细规范到独立文档 |
+| 2025-12-12 | 2.10 | 添加使用共享测试工具处理复杂组件的规范 |
+| 2025-12-12 | 2.9 | 添加测试用例编写规范,基于订单管理集成测试经验 |
 | 2025-11-11 | 2.8 | 更新包测试结构,添加模块化包测试策略 |
 | 2025-11-09 | 2.7 | 更新为monorepo测试架构,清理重复测试文件 |
 | 2025-10-15 | 2.6 | 完成遗留测试文件迁移到统一的tests目录结构 |
@@ -767,4 +336,4 @@ describe('UserService', () => {
 ---
 
 **文档状态**: 正式版
-**下次评审**: 2025-12-19
+**下次评审**: 2026-01-26

+ 1001 - 0
docs/architecture/ui-package-standards.md

@@ -0,0 +1,1001 @@
+# UI包开发规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.1 | 2025-12-04 | 添加Radix UI组件测试环境修复规范(基于故事008.007经验) | James |
+| 1.0 | 2025-12-03 | 基于史诗008经验创建UI包规范 | Claude Code |
+
+## 概述
+
+UI包是独立的前端模块包,用于封装特定业务功能的React组件、API客户端和状态管理逻辑。每个UI包作为一个独立的npm包发布,可以被主应用或其他UI包引用。
+
+## 包结构规范
+
+### 标准目录结构
+```text
+packages/<module-name>-ui/
+├── package.json                    # 包配置
+├── tsconfig.json                   # TypeScript配置
+├── vite.config.ts                  # Vite构建配置
+├── src/
+│   ├── index.ts                    # 主入口文件
+│   ├── components/                 # React组件
+│   │   ├── <ComponentName>.tsx     # 组件实现
+│   │   ├── <ComponentName>.test.tsx # 组件测试
+│   │   └── index.ts                # 组件导出
+│   ├── api/                        # API客户端
+│   │   ├── <module>Client.ts       # RPC客户端管理器
+│   │   └── index.ts                # API导出
+│   ├── hooks/                      # 自定义Hooks
+│   │   ├── use<HookName>.ts        # Hook实现
+│   │   └── index.ts                # Hook导出
+│   ├── types/                      # TypeScript类型定义
+│   │   ├── index.ts                # 类型导出
+│   │   └── <type>.ts               # 具体类型定义
+│   └── utils/                      # 工具函数
+│       └── index.ts                # 工具导出
+├── tests/                         # 测试文件
+│   └── integration/                # 集成测试
+└── README.md                       # 包文档
+```
+
+### package.json配置
+```json
+{
+  "name": "@d8d/<module-name>-ui",
+  "version": "1.0.0",
+  "description": "UI包描述",
+  "type": "module",
+  "main": "src/index.ts",
+  "types": "src/index.ts",
+  "exports": {
+    ".": {
+      "types": "./src/index.ts",
+      "import": "./src/index.ts",
+      "require": "./src/index.ts"
+    },
+    "./components": {
+      "types": "./src/components/index.ts",
+      "import": "./src/components/index.ts",
+      "require": "./src/components/index.ts"
+    },
+    "./hooks": {
+      "types": "./src/hooks/index.ts",
+      "import": "./src/hooks/index.ts",
+      "require": "./src/hooks/index.ts"
+    },
+    "./api": {
+      "types": "./src/api/index.ts",
+      "import": "./src/api/index.ts",
+      "require": "./src/api/index.ts"
+    }
+  },
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "build": "unbuild",
+    "dev": "tsc --watch",
+    "test": "vitest run",
+    "test:watch": "vitest",
+    "test:coverage": "vitest run --coverage",
+    "lint": "eslint src --ext .ts,.tsx",
+    "typecheck": "tsc --noEmit"
+  },
+  "dependencies": {
+    "@d8d/shared-types": "workspace:*",
+    "@d8d/shared-ui-components": "workspace:*",
+    "@d8d/<module-name>-module": "workspace:*",
+    "@hookform/resolvers": "^5.2.1",
+    "@tanstack/react-query": "^5.90.9",
+    "class-variance-authority": "^0.7.1",
+    "clsx": "^2.1.1",
+    "date-fns": "^4.1.0",
+    "hono": "^4.8.5",
+    "lucide-react": "^0.536.0",
+    "react": "^19.1.0",
+    "react-dom": "^19.1.0",
+    "react-hook-form": "^7.61.1",
+    "sonner": "^2.0.7",
+    "tailwind-merge": "^3.3.1",
+    "zod": "^4.0.15"
+  },
+  "devDependencies": {
+    "@testing-library/jest-dom": "^6.8.0",
+    "@testing-library/react": "^16.3.0",
+    "@testing-library/user-event": "^14.6.1",
+    "@types/node": "^22.10.2",
+    "@types/react": "^19.2.2",
+    "@types/react-dom": "^19.2.3",
+    "@typescript-eslint/eslint-plugin": "^8.18.1",
+    "@typescript-eslint/parser": "^8.18.1",
+    "eslint": "^9.17.0",
+    "jsdom": "^26.0.0",
+    "typescript": "^5.8.3",
+    "unbuild": "^3.4.0",
+    "vitest": "^4.0.9"
+  },
+  "peerDependencies": {
+    "react": "^19.1.0",
+    "react-dom": "^19.1.0"
+  }
+}
+```
+
+## RPC客户端实现规范
+
+### 客户端管理器模式
+每个UI包必须实现一个`ClientManager`类来管理RPC客户端生命周期:
+
+```typescript
+// src/api/<module>Client.ts
+import { <module>Routes } from '@d8d/<module-name>-module';
+import { rpcClient } from '@d8d/shared-ui-components/utils/hc'
+
+export class <Module>ClientManager {
+  private static instance: <Module>ClientManager;
+  private client: ReturnType<typeof rpcClient<typeof <module>Routes>> | null = null;
+
+  private constructor() {}
+
+  public static getInstance(): <Module>ClientManager {
+    if (!<Module>ClientManager.instance) {
+      <Module>ClientManager.instance = new <Module>ClientManager();
+    }
+    return <Module>ClientManager.instance;
+  }
+
+  // 初始化客户端
+  public init(baseUrl: string = '/'): ReturnType<typeof rpcClient<typeof <module>Routes>> {
+    return this.client = rpcClient<typeof <module>Routes>(baseUrl);
+  }
+
+  // 获取客户端实例
+  public get(): ReturnType<typeof rpcClient<typeof <module>Routes>> {
+    if (!this.client) {
+      return this.init()
+    }
+    return this.client;
+  }
+
+  // 重置客户端(用于测试或重新初始化)
+  public reset(): void {
+    this.client = null;
+  }
+}
+
+// 导出单例实例
+const <module>ClientManager = <Module>ClientManager.getInstance();
+
+// 导出默认客户端实例(延迟初始化)
+export const <module>Client = <module>ClientManager.get()
+
+export {
+  <module>ClientManager
+}
+```
+
+### API导出文件
+```typescript
+// src/api/index.ts
+export {
+  <Module>ClientManager,
+  <module>ClientManager,
+  <module>Client
+} from './<module>Client';
+```
+
+## 组件开发规范
+
+### 组件结构
+```typescript
+// src/components/<ComponentName>.tsx
+import React from 'react';
+import { useQuery, useMutation } from '@tanstack/react-query';
+import { <module>ClientManager } from '../api/<module>Client';
+import type { <Module>Response, <Module>SearchParams } from '../types';
+
+interface <ComponentName>Props {
+  // 组件属性定义
+}
+
+export const <ComponentName>: React.FC<<ComponentName>Props> = (props) => {
+  // 使用客户端管理器获取客户端实例
+  const client = <module>ClientManager.get();
+
+  // 数据查询示例
+  const { data, isLoading } = useQuery({
+    queryKey: ['<module>', searchParams],
+    queryFn: async () => {
+      const res = await client.index.$get({
+        query: {
+          page: searchParams.page,
+          pageSize: searchParams.limit,
+          keyword: searchParams.search
+        }
+      });
+      if (res.status !== 200) throw new Error('获取数据失败');
+      return await res.json();
+    }
+  });
+
+  // 数据变更示例
+  const mutation = useMutation({
+    mutationFn: async (data: CreateRequest) => {
+      const res = await client.index.$post({ json: data });
+      if (res.status !== 201) throw new Error('创建失败');
+      return await res.json();
+    },
+    onSuccess: () => {
+      // 成功处理
+    },
+    onError: (error) => {
+      // 错误处理
+    }
+  });
+
+  return (
+    // 组件JSX
+  );
+};
+
+export default <ComponentName>;
+```
+
+### 表单组件模式规范(基于史诗008经验)
+
+#### 1. 条件渲染独立Form组件
+**规范**:当组件需要支持创建和编辑两种表单模式时,必须使用条件渲染两个独立的Form组件,避免在单个Form组件上动态切换props。
+
+```typescript
+// ✅ 正确:条件渲染两个独立的Form组件
+{isCreateForm ? (
+  <Form {...createForm}>
+    {/* 创建表单内容 */}
+  </Form>
+) : (
+  <Form {...updateForm}>
+    {/* 编辑表单内容 */}
+  </Form>
+)}
+
+// ❌ 错误:在单个Form组件上动态切换props(可能导致类型不兼容)
+<Form {...(isCreateForm ? createForm : updateForm)}>
+  {/* 表单内容 */}
+</Form>
+```
+
+#### 2. 参考现有模式
+**规范**:参考PlatformManagement.tsx的表单处理模式,确保一致性。
+
+#### 3. 表单状态管理
+**规范**:创建表单和编辑表单分别使用独立的useForm实例,避免状态混淆。
+
+```typescript
+const createForm = useForm({
+  resolver: zodResolver(CreateSchema),
+  defaultValues: createDefaultValues
+});
+
+const updateForm = useForm({
+  resolver: zodResolver(UpdateSchema),
+  defaultValues: updateDefaultValues
+});
+```
+
+### 组件导出
+```typescript
+// src/components/index.ts
+export { <ComponentName> } from './<ComponentName>';
+export type { <ComponentName>Props } from './<ComponentName>';
+```
+
+## 类型定义规范
+
+### 类型文件结构
+```typescript
+// src/types/index.ts
+import type { InferResponseType, InferRequestType } from 'hono';
+import type { <module>Routes } from '@d8d/<module-name>-module';
+import { <module>Client } from '../api/<module>Client';
+
+// 使用导出的client进行类型推导
+export type <Module>Response = InferResponseType<typeof <module>Client.index.$get>;
+export type Create<Module>Request = InferRequestType<typeof <module>Client.index.$post>;
+export type Update<Module>Request = InferRequestType<typeof <module>Client[':id']['$put']>;
+
+// 搜索参数类型
+export interface <Module>SearchParams {
+  page: number;
+  limit: number;
+  search?: string;
+  // 其他搜索参数
+}
+
+// 组件属性类型
+export interface <ComponentName>Props {
+  // 属性定义
+}
+```
+
+### 类型推断最佳实践(基于史诗008经验)
+
+#### 1. 使用RPC推断类型
+**规范**:必须使用RPC推断类型,而不是直接导入schema类型,避免Date/string类型不匹配问题。
+
+```typescript
+// ✅ 正确:使用RPC推断类型(推荐)
+export type <Module>ListItem = <Module>ListResponse['data'][0];
+
+// ❌ 错误:直接导入schema类型(可能导致Date/string不匹配)
+import type { <Module> } from '@d8d/<module-name>-module/schemas';
+```
+
+#### 2. 参考现有UI包模式
+**规范**:参考现有UI包(如广告管理UI)的类型定义模式,确保一致性。
+
+```typescript
+// 广告管理UI模式参考
+export type AdvertisementResponse = InferResponseType<typeof advertisementClient.index.$get, 200>['data'][0];
+```
+
+#### 3. 处理混合路由模式
+**规范**:当模块使用自定义路由与CRUD路由混合时,必须通过查看后端模块集成测试确认正确的路由结构。
+
+```typescript
+// 示例:渠道模块的getChannel路由结构
+// 根据后台集成测试,路由结构是 getChannel[':id'].$get
+export type ChannelDetailResponse = InferResponseType<typeof channelClient.getChannel[':id']['$get'], 200>;
+```
+
+#### 4. 避免复杂的条件类型
+**规范**:使用简单的类型索引而不是复杂的条件类型,提高代码可读性。
+
+```typescript
+// ✅ 正确:简单类型索引
+export type <Module>ListItem = <Module>ListResponse['data'][0];
+
+// ❌ 避免:复杂的条件类型
+export type <Module>Item = <Module>ListResponse extends { data: infer T } ? T extends Array<infer U> ? U : never : never;
+```
+
+## 状态管理规范
+
+### React Query配置
+```typescript
+// 在组件中使用React Query进行状态管理
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+
+// 查询示例
+const { data, isLoading, error, refetch } = useQuery({
+  queryKey: ['<module>', id],
+  queryFn: async () => {
+    const res = await client[':id'].$get({ param: { id } });
+    if (res.status !== 200) throw new Error('获取详情失败');
+    return await res.json();
+  },
+  enabled: !!id, // 条件查询
+});
+
+// 变更示例
+const queryClient = useQueryClient();
+const mutation = useMutation({
+  mutationFn: async (data: UpdateRequest) => {
+    const res = await client[':id']['$put']({
+      param: { id },
+      json: data
+    });
+    if (res.status !== 200) throw new Error('更新失败');
+    return await res.json();
+  },
+  onSuccess: () => {
+    // 使相关查询失效
+    queryClient.invalidateQueries({ queryKey: ['<module>'] });
+    queryClient.invalidateQueries({ queryKey: ['<module>', id] });
+  }
+});
+```
+
+## 测试规范
+
+### 测试文件结构
+```text
+packages/<module-name>-ui/
+├── tests/
+│   ├── integration/                    # 集成测试
+│   │   └── <component-name>.integration.test.tsx
+│   ├── unit/                          # 单元测试
+│   │   └── <hook-name>.test.tsx
+│   └── components/                    # 组件测试
+│       └── <ComponentName>.test.tsx
+```
+
+### Mock响应工具函数
+```typescript
+// 在测试文件中使用的标准mock响应函数
+const createMockResponse = (status: number, data?: any) => ({
+  status,
+  ok: status >= 200 && status < 300,
+  body: null,
+  bodyUsed: false,
+  statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
+  headers: new Headers(),
+  url: '',
+  redirected: false,
+  type: 'basic' as ResponseType,
+  json: async () => data || {},
+  text: async () => '',
+  blob: async () => new Blob(),
+  arrayBuffer: async () => new ArrayBuffer(0),
+  formData: async () => new FormData(),
+  clone: function() { return this; }
+});
+```
+
+### 测试选择器优化规范(基于史诗008经验)
+
+#### 1. 优先使用test ID
+**规范**:必须为关键交互元素添加`data-testid`属性,避免使用文本查找导致的测试冲突。
+
+```typescript
+// 在组件中添加test ID
+<DialogTitle data-testid="create-<module>-modal-title">创建<Module></DialogTitle>
+<Button data-testid="search-button">搜索</Button>
+
+// 在测试中使用test ID
+const modalTitle = screen.getByTestId('create-<module>-modal-title');
+const searchButton = screen.getByTestId('search-button');
+```
+
+#### 2. 避免文本选择器冲突
+**规范**:当页面中有多个相同文本元素时,必须使用test ID代替`getByText()`。
+
+```typescript
+// ❌ 错误:可能找到错误的元素
+const createButton = screen.getByText('创建');
+
+// ✅ 正确:使用唯一的test ID
+const createButton = screen.getByTestId('create-<module>-button');
+```
+
+#### 3. 命名约定
+**规范**:test ID命名使用kebab-case格式:`{action}-{element}-{purpose}`。
+
+```typescript
+// 示例命名
+data-testid="create-channel-modal-title"
+data-testid="edit-channel-button-1"
+data-testid="delete-confirm-dialog-title"
+```
+
+#### 4. Radix UI组件测试环境修复(基于故事008.007经验)
+**规范**:在测试环境中使用Radix UI组件(特别是Select、DropdownMenu等)时,必须添加必要的DOM API mock。
+
+**问题**:Radix UI组件在测试环境中可能缺少某些DOM API(如`scrollIntoView`),导致测试失败。
+
+**解决方案**:在测试setup文件中添加必要的mock。
+
+```typescript
+// tests/setup.ts
+import '@testing-library/jest-dom';
+import { vi } from 'vitest';
+
+// Mock sonner
+vi.mock('sonner', () => ({
+  toast: {
+    success: vi.fn(),
+    error: vi.fn(),
+    warning: vi.fn(),
+    info: vi.fn()
+  }
+}));
+
+// Mock scrollIntoView for Radix UI components
+Element.prototype.scrollIntoView = vi.fn();
+```
+
+**Select组件test ID规范**:为Radix UI Select组件的选项添加test ID,避免文本查找冲突。
+
+```typescript
+// 在组件中为SelectItem添加test ID
+<SelectContent>
+  <SelectItem value="all" data-testid="order-status-option-all">全部状态</SelectItem>
+  <SelectItem value={OrderStatus.DRAFT} data-testid="order-status-option-draft">草稿</SelectItem>
+  <SelectItem value={OrderStatus.CONFIRMED} data-testid="order-status-option-confirmed">已确认</SelectItem>
+</SelectContent>
+
+// 在测试中使用test ID查找Select选项
+expect(screen.getByTestId('order-status-option-all')).toBeInTheDocument();
+expect(screen.getByTestId('order-status-option-draft')).toBeInTheDocument();
+expect(screen.getByTestId('order-status-option-confirmed')).toBeInTheDocument();
+```
+
+### 组件集成测试
+```typescript
+// tests/integration/<component-name>.integration.test.tsx
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { <ComponentName> } from '../../src/components/<ComponentName>';
+import { <module>ClientManager, <module>Client } from '../../src/api/<module>Client';
+
+// Mock RPC客户端
+vi.mock('../../src/api/<module>Client', () => {
+  const mock<Module>Client = {
+    index: {
+      $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            // 其他字段
+          }
+        ],
+        pagination: {
+          page: 1,
+          pageSize: 10,
+          total: 1,
+          totalPages: 1
+        }
+      }))),
+      $post: vi.fn(() => Promise.resolve(createMockResponse(201, {
+        id: 2,
+        name: '新建数据'
+      }))),
+    },
+    ':id': {
+      $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
+        id: 1,
+        name: '测试数据详情'
+      }))),
+      $put: vi.fn(() => Promise.resolve(createMockResponse(200, {
+        id: 1,
+        name: '更新后的数据'
+      }))),
+      $delete: vi.fn(() => Promise.resolve(createMockResponse(204))),
+    },
+  };
+
+  const mock<Module>ClientManager = {
+    get: vi.fn(() => mock<Module>Client),
+  };
+
+  return {
+    <module>ClientManager: mock<Module>ClientManager,
+    <module>Client: mock<Module>Client,
+  };
+});
+
+// Mock其他依赖
+vi.mock('sonner', () => ({
+  toast: {
+    success: vi.fn(),
+    error: vi.fn(),
+    info: vi.fn(),
+    warning: vi.fn(),
+  }
+}));
+
+describe('<ComponentName>集成测试', () => {
+  const queryClient = new QueryClient({
+    defaultOptions: {
+      queries: {
+        retry: false,
+      },
+    },
+  });
+
+  beforeEach(() => {
+    vi.clearAllMocks();
+    queryClient.clear();
+  });
+
+  it('渲染组件并加载数据', async () => {
+    render(
+      <QueryClientProvider client={queryClient}>
+        <<ComponentName> />
+      </QueryClientProvider>
+    );
+
+    await waitFor(() => {
+      expect(screen.getByText('测试数据')).toBeInTheDocument();
+    });
+  });
+
+  it('创建新数据', async () => {
+    render(
+      <QueryClientProvider client={queryClient}>
+        <<ComponentName> />
+      </QueryClientProvider>
+    );
+
+    const createButton = screen.getByText('新建');
+    fireEvent.click(createButton);
+
+    const nameInput = screen.getByLabelText('名称');
+    fireEvent.change(nameInput, { target: { value: '新数据' } });
+
+    const submitButton = screen.getByText('提交');
+    fireEvent.click(submitButton);
+
+    await waitFor(() => {
+      expect(<module>Client.index.$post).toHaveBeenCalledWith({
+        json: expect.objectContaining({ name: '新数据' })
+      });
+    });
+  });
+});
+```
+
+### Hook单元测试
+```typescript
+// tests/unit/use<HookName>.test.tsx
+import React from 'react';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { renderHook, waitFor } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { use<HookName> } from '../../src/hooks/use<HookName>';
+import { <module>Client } from '../../src/api/<module>Client';
+
+// Mock RPC客户端
+vi.mock('../../src/api/<module>Client', () => {
+  const mock<Module>Client = {
+    index: {
+      $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
+        data: [{ id: 1, name: '测试数据' }],
+        pagination: { total: 1 }
+      }))),
+    },
+  };
+
+  return {
+    <module>Client: mock<Module>Client,
+  };
+});
+
+describe('use<HookName>', () => {
+  const queryClient = new QueryClient({
+    defaultOptions: {
+      queries: {
+        retry: false,
+      },
+    },
+  });
+
+  const wrapper = ({ children }: { children: React.ReactNode }) => (
+    <QueryClientProvider client={queryClient}>
+      {children}
+    </QueryClientProvider>
+  );
+
+  beforeEach(() => {
+    vi.clearAllMocks();
+    queryClient.clear();
+  });
+
+  it('加载数据', async () => {
+    const { result } = renderHook(() => use<HookName>({ page: 1, limit: 10 }), { wrapper });
+
+    await waitFor(() => {
+      expect(result.current.isLoading).toBe(false);
+    });
+
+    expect(result.current.data).toEqual([
+      { id: 1, name: '测试数据' }
+    ]);
+    expect(<module>Client.index.$get).toHaveBeenCalledWith({
+      query: { page: 1, pageSize: 10 }
+    });
+  });
+});
+```
+
+### 组件单元测试
+```typescript
+// tests/components/<ComponentName>.test.tsx
+import { describe, it, expect, vi } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { <ComponentName> } from '../../src/components/<ComponentName>';
+
+// Mock子组件
+vi.mock('../ChildComponent', () => ({
+  ChildComponent: () => <div>Mock子组件</div>
+}));
+
+describe('<ComponentName>', () => {
+  it('渲染组件', () => {
+    render(<<ComponentName> />);
+    expect(screen.getByText('组件标题')).toBeInTheDocument();
+  });
+
+  it('显示传入的属性', () => {
+    render(<<ComponentName> name="测试名称" />);
+    expect(screen.getByText('测试名称')).toBeInTheDocument();
+  });
+});
+```
+
+## 构建和发布规范
+
+### 构建配置
+UI包在PNPM工作空间中直接使用TypeScript源码,不需要构建步骤。主入口直接指向`src/index.ts`,TypeScript会自动处理类型检查和编译。
+
+### TypeScript配置
+```json
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "lib": ["DOM", "DOM.Iterable", "ES2020"],
+    "module": "ESNext",
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react-jsx",
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "include": ["src"]
+}
+```
+
+### 构建脚本说明
+- `"build": "unbuild"`: 使用unbuild进行构建(可选,用于生产环境发布)
+- `"dev": "tsc --watch"`: 开发模式下监听TypeScript文件变化
+- `"typecheck": "tsc --noEmit"`: 类型检查,不生成输出文件
+
+## 集成规范
+
+### 在主应用中使用
+```typescript
+// 主应用中的使用示例
+import { <ComponentName> } from '@d8d/<module-name>-ui';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+const queryClient = new QueryClient();
+
+function App() {
+  return (
+    <QueryClientProvider client={queryClient}>
+      <<ComponentName> />
+    </QueryClientProvider>
+  );
+}
+```
+
+### 环境配置
+UI包应该支持以下环境变量配置:
+- `VITE_API_BASE_URL`: API基础URL(默认为`/`)
+- `VITE_APP_ENV`: 应用环境(development/production)
+
+## 错误处理规范
+
+### API错误处理
+```typescript
+// 统一的错误处理模式
+try {
+  const res = await client.index.$get({ query: params });
+  if (res.status !== 200) {
+    const errorData = await res.json();
+    throw new Error(errorData.message || '请求失败');
+  }
+  return await res.json();
+} catch (error) {
+  if (error instanceof Error) {
+    toast.error(error.message);
+  } else {
+    toast.error('未知错误');
+  }
+  throw error;
+}
+```
+
+### 组件错误边界
+```typescript
+import React, { Component, ErrorInfo, ReactNode } from 'react';
+
+interface Props {
+  children: ReactNode;
+  fallback?: ReactNode;
+}
+
+interface State {
+  hasError: boolean;
+  error?: Error;
+}
+
+export class ErrorBoundary extends Component<Props, State> {
+  public state: State = {
+    hasError: false
+  };
+
+  public static getDerivedStateFromError(error: Error): State {
+    return { hasError: true, error };
+  }
+
+  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+    console.error('UI组件错误:', error, errorInfo);
+  }
+
+  public render() {
+    if (this.state.hasError) {
+      return this.props.fallback || <div>组件加载失败</div>;
+    }
+
+    return this.props.children;
+  }
+}
+```
+
+## 开发流程规范
+
+### 1. 开发前检查清单(基于史诗008经验)
+在开始UI包开发前,必须完成以下检查:
+
+#### 1.1 API路径映射验证
+**规范**:必须验证故事中的API路径映射与实际后端路由定义的一致性。
+
+```bash
+# 检查后端模块的路由定义
+cat allin-packages/<module-name>-module/src/routes/*.routes.ts
+
+# 查看后端集成测试确认路由结构
+cat allin-packages/<module-name>-module/tests/integration/*.test.ts
+```
+
+#### 1.2 路由结构确认
+**规范**:必须通过查看后台模块集成测试确认正确的路由结构,特别是混合路由模式。
+
+#### 1.3 参考现有UI包
+**规范**:必须参考现有UI包(如广告管理UI、平台管理UI)的实现模式。
+
+### 2. API调用一致性规范
+**规范**:必须根据实际路由名称修正API调用,确保前端API调用与后端路由定义完全一致。
+
+```typescript
+// ❌ 错误:使用故事中描述但实际不存在的路由
+const res = await client.index.$get(...);
+const res = await client.channels.$get(...);
+
+// ✅ 正确:使用实际路由名称
+const res = await client.getAll<Module>s.$get(...);
+const res = await client.search<Module>s.$get(...);
+```
+
+### 3. 创建新UI包
+```bash
+# 复制模板
+cp -r packages/template-ui packages/<module-name>-ui
+
+# 更新包名和依赖
+cd packages/<module-name>-ui
+# 修改package.json中的name和dependencies
+```
+
+### 2. 开发组件
+```bash
+# 启动开发模式
+pnpm dev
+
+# 运行测试
+pnpm test
+
+# 类型检查
+pnpm typecheck
+
+# 代码检查
+pnpm lint
+```
+
+### 3. 构建和发布
+```bash
+# 构建包
+pnpm build
+
+# 本地测试
+# 在主应用中引用本地构建的包进行测试
+
+# 发布到npm(由CI/CD流程处理)
+```
+
+## 参考实现
+
+### 现有UI包参考
+- **广告管理UI包**: `packages/advertisement-management-ui`
+  - 组件: `src/components/AdvertisementManagement.tsx`
+  - API客户端: `src/api/advertisementClient.ts`
+  - 类型定义: `src/types/index.ts`
+
+- **区域管理UI包**: `packages/area-management-ui`
+  - 组件: `src/components/AreaManagement.tsx`
+  - API客户端: `src/api/areaClient.ts`
+
+### 关键代码片段
+```typescript
+// RPC客户端管理器实现参考
+import { advertisementRoutes } from '@d8d/advertisements-module';
+import { rpcClient } from '@d8d/shared-ui-components/utils/hc'
+
+export class AdvertisementClientManager {
+  private static instance: AdvertisementClientManager;
+  private client: ReturnType<typeof rpcClient<typeof advertisementRoutes>> | null = null;
+
+  public static getInstance(): AdvertisementClientManager {
+    if (!AdvertisementClientManager.instance) {
+      AdvertisementClientManager.instance = new AdvertisementClientManager();
+    }
+    return AdvertisementClientManager.instance;
+  }
+
+  public get(): ReturnType<typeof rpcClient<typeof advertisementRoutes>> {
+    if (!this.client) {
+      return this.init()
+    }
+    return this.client;
+  }
+}
+```
+
+## 版本管理
+
+### 版本号规则
+- **主版本号**: 不兼容的API变更
+- **次版本号**: 向后兼容的功能性新增
+- **修订号**: 向后兼容的问题修正
+
+### 变更日志
+每个版本更新必须包含变更日志,记录:
+1. 新增功能
+2. 问题修复
+3. 破坏性变更
+4. 依赖更新
+
+## 性能优化
+
+### 代码分割
+```typescript
+// 使用React.lazy进行代码分割
+const LazyComponent = React.lazy(() => import('./HeavyComponent'));
+
+function App() {
+  return (
+    <React.Suspense fallback={<LoadingSpinner />}>
+      <LazyComponent />
+    </React.Suspense>
+  );
+}
+```
+
+### 组件优化
+- 使用`React.memo`避免不必要的重渲染
+- 使用`useMemo`和`useCallback`缓存计算和函数
+- 实现虚拟列表处理大量数据
+
+## 安全规范
+
+### 输入验证
+- 所有用户输入必须在前端进行验证
+- 使用Zod schema进行表单验证
+- 敏感数据不存储在客户端状态中
+
+### XSS防护
+- 使用React的自动转义机制
+- 避免使用`dangerouslySetInnerHTML`
+- 对动态内容进行清理
+
+## 文档要求
+
+每个UI包必须包含:
+1. **README.md**: 包概述、安装、使用示例
+2. **API文档**: 组件Props和API接口说明
+3. **示例代码**: 完整的使用示例
+4. **变更日志**: 版本更新记录
+
+---
+
+*本规范基于史诗008(AllIn UI模块移植)的经验总结,确保UI包开发的一致性和可维护性。*

+ 530 - 0
docs/architecture/web-server-testing-standards.md

@@ -0,0 +1,530 @@
+# Web Server 包测试规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-12-26 | 从测试策略文档拆分,专注Web Server包测试 | James (Claude Code) |
+
+## 概述
+
+本文档定义了Web Server包的测试标准和最佳实践。
+- **目标**: `packages/server` - API服务器包
+- **测试类型**: 集成测试(模块间协作测试)
+
+## 测试框架栈
+
+- **Vitest**: 测试运行器
+- **hono/testing**: Hono官方测试工具
+- **TypeORM**: 数据库测试
+- **PostgreSQL**: 测试数据库
+- **shared-test-util**: 共享测试基础设施
+
+## 测试策略
+
+### 集成测试(Integration Tests)
+- **范围**: 模块间集成、API端点集成
+- **目标**: 验证各业务模块在server环境中的正确集成
+- **位置**: `packages/server/tests/integration/**/*.test.ts`
+- **框架**: Vitest + hono/testing + TypeORM
+- **覆盖率目标**: ≥ 60%
+
+## 测试文件结构
+
+```
+packages/server/
+├── src/
+│   ├── api.ts              # API路由导出
+│   └── index.ts            # 服务器入口
+└── tests/
+    ├── integration/
+    │   ├── auth.integration.test.ts      # 认证集成测试
+    │   ├── users.integration.test.ts     # 用户管理集成测试
+    │   ├── files.integration.test.ts     # 文件管理集成测试
+    │   └── api-integration.test.ts       # API端点集成测试
+    └── fixtures/
+        ├── test-db.ts                    # 测试数据库配置
+        └── test-data.ts                  # 测试数据工厂
+```
+
+## 集成测试最佳实践
+
+### 1. 使用hono/testing测试API端点
+```typescript
+import { test } from 'vitest';
+import { integrateRoutes } from 'hono/testing';
+import app from '../src/api';
+
+describe('POST /api/auth/login', () => {
+  it('应该成功登录并返回token', async () => {
+    // Arrange
+    const testData = {
+      username: 'testuser',
+      password: 'password123'
+    };
+
+    // Act
+    const res = await integrateRoutes(app).POST('/api/auth/login', {
+      json: testData
+    });
+
+    // Assert
+    expect(res.status).toBe(200);
+    const json = await res.json();
+    expect(json).toHaveProperty('token');
+    expect(json.user.username).toBe('testuser');
+  });
+});
+```
+
+### 2. 测试数据库集成
+```typescript
+import { describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { App } from 'hono';
+import { DataSource } from 'typeorm';
+import { getTestDataSource } from './fixtures/test-db';
+import { User } from '@d8d/user-module/entities';
+
+describe('用户管理集成测试', () => {
+  let dataSource: DataSource;
+
+  beforeEach(async () => {
+    // 设置测试数据库
+    dataSource = await getTestDataSource();
+    await dataSource.initialize();
+  });
+
+  afterEach(async () => {
+    // 清理测试数据库
+    await dataSource.destroy();
+  });
+
+  it('应该创建用户并返回用户数据', async () => {
+    const app = new App();
+    app.route('/api/users', userRoutes);
+
+    const res = await integrateRoutes(app).POST('/api/users', {
+      json: {
+        username: 'testuser',
+        email: 'test@example.com',
+        password: 'password123'
+      }
+    });
+
+    expect(res.status).toBe(201);
+
+    // 验证数据库中的数据
+    const userRepo = dataSource.getRepository(User);
+    const user = await userRepo.findOne({ where: { username: 'testuser' } });
+    expect(user).toBeDefined();
+    expect(user?.email).toBe('test@example.com');
+  });
+});
+```
+
+### 3. 测试认证中间件
+```typescript
+import { generateToken } from '@d8d/shared-utils/jwt.util';
+
+describe('认证保护的API端点', () => {
+  it('应该拒绝未认证的请求', async () => {
+    const res = await integrateRoutes(app).GET('/api/users/me');
+
+    expect(res.status).toBe(401);
+    expect(await res.json()).toEqual({
+      error: '未授权访问'
+    });
+  });
+
+  it('应该接受有效token的请求', async () => {
+    const token = generateToken({ userId: 1, username: 'testuser' });
+
+    const res = await integrateRoutes(app).GET('/api/users/me', {
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    });
+
+    expect(res.status).toBe(200);
+    const json = await res.json();
+    expect(json.username).toBe('testuser');
+  });
+});
+```
+
+### 4. 测试模块间集成
+```typescript
+describe('认证与用户模块集成', () => {
+  it('应该成功注册用户并自动登录', async () => {
+    const app = new App();
+    app.route('/api/auth', authRoutes);
+    app.route('/api/users', userRoutes);
+
+    // 1. 注册用户
+    const registerRes = await integrateRoutes(app).POST('/api/auth/register', {
+      json: {
+        username: 'newuser',
+        email: 'new@example.com',
+        password: 'password123'
+      }
+    });
+
+    expect(registerRes.status).toBe(201);
+
+    // 2. 登录
+    const loginRes = await integrateRoutes(app).POST('/api/auth/login', {
+      json: {
+        username: 'newuser',
+        password: 'password123'
+      }
+    });
+
+    expect(loginRes.status).toBe(200);
+    const { token } = await loginRes.json();
+    expect(token).toBeDefined();
+
+    // 3. 使用token访问受保护的端点
+    const meRes = await integrateRoutes(app).GET('/api/users/me', {
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    });
+
+    expect(meRes.status).toBe(200);
+    const user = await meRes.json();
+    expect(user.username).toBe('newuser');
+  });
+});
+```
+
+### 5. 测试文件上传集成
+```typescript
+import { createReadStream } from 'fs';
+import { FormData } from 'hono/client';
+
+describe('文件上传集成测试', () => {
+  it('应该成功上传文件并返回文件URL', async () => {
+    const formData = new FormData();
+    formData.append('file', createReadStream('test/fixtures/test-file.png'));
+
+    const res = await integrateRoutes(app).POST('/api/files/upload', {
+      body: formData
+    });
+
+    expect(res.status).toBe(201);
+    const json = await res.json();
+    expect(json).toHaveProperty('url');
+    expect(json).toHaveProperty('id');
+  });
+});
+```
+
+### 6. 测试错误处理
+```typescript
+describe('API错误处理', () => {
+  it('应该返回404当资源不存在', async () => {
+    const res = await integrateRoutes(app).GET('/api/users/99999');
+
+    expect(res.status).toBe(404);
+    expect(await res.json()).toEqual({
+      error: '用户不存在'
+    });
+  });
+
+  it('应该返回400当请求数据无效', async () => {
+    const res = await integrateRoutes(app).POST('/api/users', {
+      json: {
+        username: '', // 无效的用户名
+        email: 'invalid-email' // 无效的邮箱
+      }
+    });
+
+    expect(res.status).toBe(400);
+    const json = await res.json();
+    expect(json).toHaveProperty('errors');
+  });
+});
+```
+
+### 7. 使用共享测试工具
+```typescript
+import { createIntegrationTestApp, setupTestDatabase, teardownTestDatabase } from '@d8d/shared-test-util';
+
+describe('使用共享测试工具', () => {
+  let dataSource: DataSource;
+  let app: Hono;
+
+  beforeAll(async () => {
+    dataSource = await setupTestDatabase();
+    app = await createIntegrationTestApp(dataSource);
+  });
+
+  afterAll(async () => {
+    await teardownTestDatabase(dataSource);
+  });
+
+  it('应该使用共享工具运行集成测试', async () => {
+    const res = await integrateRoutes(app).GET('/api/users');
+
+    expect(res.status).toBe(200);
+  });
+});
+```
+
+## 测试数据管理
+
+### 测试数据工厂
+```typescript
+// tests/fixtures/test-data.ts
+import { User } from '@d8d/user-module/entities';
+
+export function createTestUser(overrides = {}): Partial<User> {
+  return {
+    id: 1,
+    username: 'testuser',
+    email: 'test@example.com',
+    password: 'hashedpassword',
+    role: 'user',
+    active: true,
+    createdAt: new Date(),
+    ...overrides
+  };
+}
+
+export async function seedTestUser(dataSource: DataSource, userData = {}) {
+  const userRepo = dataSource.getRepository(User);
+  const user = userRepo.create(createTestUser(userData));
+  return await userRepo.save(user);
+}
+```
+
+### 数据库清理策略
+```typescript
+// 选项1: 事务回滚(推荐)
+describe('使用事务回滚', () => {
+  let queryRunner: QueryRunner;
+
+  beforeEach(async () => {
+    queryRunner = dataSource.createQueryRunner();
+    await queryRunner.startTransaction();
+  });
+
+  afterEach(async () => {
+    await queryRunner.rollbackTransaction();
+    await queryRunner.release();
+  });
+});
+
+// 选项2: 每个测试后清理
+describe('使用数据库清理', () => {
+  afterEach(async () => {
+    const entities = dataSource.entityMetadatas;
+    for (const entity of entities) {
+      const repository = dataSource.getRepository(entity.name);
+      await repository.clear();
+    }
+  });
+});
+```
+
+## 测试命名约定
+
+### 文件命名
+- 集成测试:`[module].integration.test.ts`
+- API测试:`[endpoint].integration.test.ts`
+
+### 测试描述
+```typescript
+describe('用户管理API', () => {
+  describe('GET /api/users', () => {
+    it('应该返回用户列表', async () => { });
+    it('应该支持分页', async () => { });
+    it('应该支持搜索过滤', async () => { });
+  });
+
+  describe('POST /api/users', () => {
+    it('应该创建新用户', async () => { });
+    it('应该验证重复用户名', async () => { });
+    it('应该验证邮箱格式', async () => { });
+  });
+});
+```
+
+## 环境配置
+
+### 测试环境变量
+```typescript
+// vitest.config.ts
+export default defineConfig({
+  test: {
+    setupFiles: ['./tests/setup.ts'],
+    env: {
+      NODE_ENV: 'test',
+      DATABASE_URL: 'postgresql://postgres:test_password@localhost:5432/test_d8dai',
+      JWT_SECRET: 'test_secret',
+      REDIS_URL: 'redis://localhost:6379/1'
+    }
+  }
+});
+```
+
+### 测试数据库设置
+```typescript
+// tests/fixtures/test-db.ts
+import { DataSource } from 'typeorm';
+import { User } from '@d8d/user-module/entities';
+import { File } from '@d8d/file-module/entities';
+
+export async function getTestDataSource(): Promise<DataSource> {
+  return new DataSource({
+    type: 'postgres',
+    host: 'localhost',
+    port: 5432,
+    username: 'postgres',
+    password: 'test_password',
+    database: 'test_d8dai',
+    entities: [User, File],
+    synchronize: true, // 测试环境自动同步表结构
+    dropSchema: true, // 每次测试前清空数据库
+    logging: false
+  });
+}
+```
+
+## 覆盖率标准
+
+| 测试类型 | 最低要求 | 目标要求 |
+|----------|----------|----------|
+| 集成测试 | 50% | 60% |
+
+**关键端点要求**:
+- 认证端点:100%覆盖
+- 用户管理端点:80%覆盖
+- 文件上传端点:70%覆盖
+
+## 运行测试
+
+### 本地开发
+```bash
+# 运行所有集成测试
+cd packages/server && pnpm test
+
+# 运行特定集成测试
+pnpm test users.integration.test.ts
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 运行特定测试用例
+pnpm test --testNamePattern="应该成功创建用户"
+```
+
+### CI/CD
+```yaml
+server-integration-tests:
+  runs-on: ubuntu-latest
+  services:
+    postgres:
+      image: postgres:17
+      env:
+        POSTGRES_PASSWORD: test_password
+        POSTGRES_DB: test_d8dai
+      options: >-
+        --health-cmd pg_isready
+        --health-interval 10s
+        --health-timeout 5s
+        --health-retries 5
+  steps:
+    - uses: actions/checkout@v3
+    - uses: pnpm/action-setup@v2
+    - run: cd packages/server && pnpm install
+    - run: cd packages/server && pnpm test
+```
+
+## 调试技巧
+
+### 1. 使用调试模式
+```bash
+# 运行特定测试并显示详细信息
+pnpm test --testNamePattern="用户登录" --reporter=verbose
+
+# 监听模式(开发时)
+pnpm test --watch
+```
+
+### 2. 查看响应详情
+```typescript
+const res = await integrateRoutes(app).POST('/api/users', { json: userData });
+
+// 打印完整响应
+console.log('Status:', res.status);
+console.log('Headers:', res.headers);
+console.log('Body:', await res.json());
+```
+
+### 3. 数据库调试
+```typescript
+// 启用SQL查询日志
+const dataSource = new DataSource({
+  // ...其他配置
+  logging: true, // 显示所有SQL查询
+  maxQueryExecutionTime: 1000 // 记录慢查询
+});
+```
+
+## 常见错误避免
+
+### ❌ 不要在集成测试中使用mock
+```typescript
+// 错误:模拟数据库查询
+vi.mock('@d8d/user-module/services/user.service', () => ({
+  UserService: {
+    findAll: vi.fn(() => Promise.resolve(mockUsers))
+  }
+}));
+
+// 正确:使用真实的数据库和服务
+```
+
+### ❌ 不要忽略异步清理
+```typescript
+// 错误:不清理数据库
+afterEach(() => {
+  // 数据库没有被清理
+});
+
+// 正确:确保数据库清理
+afterEach(async () => {
+  await dataSource.dropDatabase();
+});
+```
+
+### ❌ 不要硬编码测试数据
+```typescript
+// 错误:硬编码ID
+it('应该返回用户详情', async () => {
+  const res = await integrateRoutes(app).GET('/api/users/123');
+});
+
+// 正确:使用动态创建的数据
+it('应该返回用户详情', async () => {
+  const user = await seedTestUser(dataSource);
+  const res = await integrateRoutes(app).GET(`/api/users/${user.id}`);
+});
+```
+
+## 参考实现
+
+- Server包集成测试:`packages/server/tests/integration/`
+- 共享测试工具:`packages/shared-test-util/`
+
+## 相关文档
+
+- [测试策略概述](./testing-strategy.md)
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [Web UI包测试规范](./web-ui-testing-standards.md)
+- [后端模块包开发规范](./backend-module-package-standards.md)
+
+---
+
+**文档状态**: 正式版
+**适用范围**: packages/server

+ 439 - 0
docs/architecture/web-ui-testing-standards.md

@@ -0,0 +1,439 @@
+# Web UI 包测试规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-12-26 | 从测试策略文档拆分,专注Web UI包测试 | James (Claude Code) |
+
+## 概述
+
+本文档定义了Web UI包的测试标准和最佳实践,包括:
+- **packages/*-ui**: Web UI组件包(用户管理UI、文件管理UI等)
+- **web/tests/**: Web应用测试(组件测试、集成测试、E2E测试)
+
+## 测试框架栈
+
+### 单元测试和集成测试
+- **Vitest**: 测试运行器
+- **Testing Library**: React组件测试(`@testing-library/react`、`@testing-library/user-event`)
+- **Happy DOM**: 轻量级DOM环境(`@happy-dom/global-registrator`)
+
+### E2E测试
+- **Playwright**: 端到端测试框架
+
+## 测试分层策略
+
+### 1. 组件测试(Component Tests)
+- **范围**: 单个UI组件
+- **目标**: 验证组件渲染、交互和状态
+- **位置**: `packages/*-ui/tests/**/*.test.tsx` 或 `web/tests/unit/client/**/*.test.tsx`
+- **框架**: Vitest + Testing Library
+- **覆盖率目标**: ≥ 80%
+
+### 2. 集成测试(Integration Tests)
+- **范围**: 多个组件协作、与API集成
+- **目标**: 验证组件间交互和数据流
+- **位置**: `web/tests/integration/**/*.test.tsx`
+- **框架**: Vitest + Testing Library + MSW(API模拟)
+- **覆盖率目标**: ≥ 60%
+
+### 3. E2E测试(End-to-End Tests)
+- **范围**: 完整用户流程
+- **目标**: 验证端到端业务流程
+- **位置**: `web/tests/e2e/**/*.test.ts`
+- **框架**: Playwright
+- **覆盖率目标**: 关键用户流程100%
+
+## 测试文件结构
+
+### UI包结构
+```
+packages/user-management-ui/
+├── src/
+│   ├── components/
+│   │   ├── UserTable.tsx
+│   │   └── UserForm.tsx
+│   └── index.ts
+└── tests/
+    ├── unit/
+    │   ├── UserTable.test.tsx
+    │   └── UserForm.test.tsx
+    └── integration/
+        └── UserManagementFlow.test.tsx
+```
+
+### Web应用结构
+```
+web/tests/
+├── unit/
+│   └── client/
+│       ├── pages/
+│       │   └── Users.test.tsx
+│       └── components/
+│           └── DataTablePagination.test.tsx
+├── integration/
+│   └── client/
+│       └── user-management.test.tsx
+└── e2e/
+    ├── login.spec.ts
+    └── user-management.spec.ts
+```
+
+## 组件测试最佳实践
+
+### 1. 使用Testing Library原则
+```typescript
+// ✅ 推荐:从用户角度测试
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+test('应该允许用户创建新用户', async () => {
+  const user = userEvent.setup();
+  render(<UserForm />);
+
+  // 填写表单
+  await user.type(screen.getByLabelText(/用户名/), 'testuser');
+  await user.type(screen.getByLabelText(/邮箱/), 'test@example.com');
+
+  // 提交表单
+  await user.click(screen.getByRole('button', { name: /创建/ }));
+
+  // 验证结果
+  expect(screen.getByText(/创建成功/)).toBeInTheDocument();
+});
+
+// ❌ 避免:测试实现细节
+test('submitButton onClick 应该被调用', () => {
+  const mockOnClick = vi.fn();
+  render(<Button onClick={mockOnClick}>提交</Button>);
+
+  fireEvent.click(screen.getByText('提交'));
+  expect(mockOnClick).toHaveBeenCalled();
+});
+```
+
+### 2. 使用数据测试ID
+```typescript
+// 组件代码
+<Button data-testid="submit-button" type="submit">提交</Button>
+
+// 测试代码
+const submitButton = screen.getByTestId('submit-button');
+await user.click(submitButton);
+```
+
+### 3. 模拟API调用
+```typescript
+import { vi } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { UserForm } from './UserForm';
+
+// 模拟RPC客户端
+vi.mock('@d8d/shared-ui-components/utils/hc', () => ({
+  rpcClient: vi.fn(() => ({
+    users: {
+      create: {
+        $post: vi.fn(() => Promise.resolve({
+          status: 201,
+          json: async () => ({ id: 1, username: 'testuser' })
+        }))
+      }
+    }
+  }))
+}));
+```
+
+### 4. 测试异步状态
+```typescript
+test('应该显示加载状态', async () => {
+  render(<UserList />);
+
+  // 初始加载状态
+  expect(screen.getByText(/加载中/)).toBeInTheDocument();
+
+  // 等待数据加载完成
+  await waitFor(() => {
+    expect(screen.getByText(/用户列表/)).toBeInTheDocument();
+  });
+});
+```
+
+## 集成测试最佳实践
+
+### 1. 使用真实组件而非模拟
+```typescript
+// ✅ 推荐:使用真实组件,模拟API
+vi.mock('@d8d/shared-ui-components/utils/hc', () => ({
+  rpcClient: vi.fn(() => ({
+    users: {
+      $get: vi.fn(() => Promise.resolve({
+        status: 200,
+        json: async () => ({ data: mockUsers })
+      }))
+    }
+  }))
+}));
+
+// ❌ 避免:模拟整个组件
+vi.mock('@d8d/user-management-ui', () => ({
+  UserTable: () => <div data-testid="mock-user-table" />
+}));
+```
+
+### 2. 提供完整的模拟数据
+```typescript
+const mockUsers = [
+  {
+    id: 1,
+    username: 'testuser',
+    email: 'test@example.com',
+    role: 'user',
+    createdAt: '2025-01-01T00:00:00.000Z',
+    // 包含真实组件需要的所有字段
+  }
+];
+```
+
+### 3. 验证完整的用户流程
+```typescript
+test('应该成功创建用户并刷新列表', async () => {
+  const user = userEvent.setup();
+  render(<UserManagementPage />);
+
+  // 打开创建表单
+  await user.click(screen.getByTestId('create-user-button'));
+
+  // 填写表单
+  await user.type(screen.getByLabelText(/用户名/), 'newuser');
+  await user.type(screen.getByLabelText(/邮箱/), 'new@example.com'));
+
+  // 提交表单
+  await user.click(screen.getByRole('button', { name: /创建/ }));
+
+  // 验证成功消息
+  await waitFor(() => {
+    expect(screen.getByText(/创建成功/)).toBeInTheDocument();
+  });
+
+  // 验证列表刷新
+  await waitFor(() => {
+    expect(screen.getByText('newuser')).toBeInTheDocument();
+  });
+});
+```
+
+### 4. 使用共享测试工具处理复杂组件
+```typescript
+import { completeRadixSelectFlow } from '@d8d/shared-ui-components/tests/utils';
+
+// 处理Radix UI Select组件的完整选择流程
+await completeRadixSelectFlow('role-selector', 'admin', { useFireEvent: true });
+```
+
+## E2E测试最佳实践
+
+### 1. 使用Page Object模式
+```typescript
+// tests/e2e/pages/login.page.ts
+export class LoginPage {
+  constructor(private page: Page) {}
+
+  async goto() {
+    await this.page.goto('/login');
+  }
+
+  async login(username: string, password: string) {
+    await this.page.fill('input[name="username"]', username);
+    await this.page.fill('input[name="password"]', password);
+    await this.page.click('button[type="submit"]');
+  }
+
+  async expectWelcomeMessage() {
+    await expect(this.page.getByText(/欢迎/)).toBeVisible();
+  }
+}
+
+// 测试文件
+test('用户登录流程', async ({ page }) => {
+  const loginPage = new LoginPage(page);
+  await loginPage.goto();
+  await loginPage.login('testuser', 'password');
+  await loginPage.expectWelcomeMessage();
+});
+```
+
+### 2. 使用稳定的定位器
+```typescript
+// ✅ 推荐:使用语义化定位器
+await page.click('button:has-text("提交")');
+await page.fill('input[placeholder="请输入用户名"]', 'testuser');
+await page.click('[data-testid="submit-button"]');
+
+// ❌ 避免:使用不稳定的CSS选择器
+await page.click('.btn-primary');
+```
+
+### 3. 等待策略
+```typescript
+// 等待元素可见
+await page.waitForSelector('[data-testid="user-list"]');
+
+// 等待网络请求完成
+await page.waitForLoadState('networkidle');
+
+// 等待特定条件
+await page.waitForURL('/dashboard');
+```
+
+## 测试命名约定
+
+### 文件命名
+- 组件测试:`[ComponentName].test.tsx`
+- 集成测试:`[feature].integration.test.tsx`
+- E2E测试:`[feature].spec.ts`
+
+### 测试描述
+```typescript
+describe('UserForm', () => {
+  describe('表单验证', () => {
+    it('应该验证必填字段', async () => { });
+    it('应该验证邮箱格式', async () => { });
+  });
+
+  describe('表单提交', () => {
+    it('应该成功创建用户', async () => { });
+    it('应该处理网络错误', async () => { });
+  });
+});
+```
+
+## 常见错误避免
+
+### ❌ 不要测试实现细节
+```typescript
+// 错误:测试useState调用
+expect(useState).toHaveBeenCalledWith([]);
+
+// 正确:测试渲染结果
+expect(screen.getByText('用户列表')).toBeInTheDocument();
+```
+
+### ❌ 不要过度模拟
+```typescript
+// 错误:模拟整个组件库
+vi.mock('@d8d/shared-ui-components', () => ({
+  Button: () => <button data-testid="mock-button" />
+}));
+
+// 正确:使用真实组件,模拟其依赖
+```
+
+### ❌ 不要忽略异步操作
+```typescript
+// 错误:不等待异步操作
+fireEvent.click(submitButton);
+expect(successMessage).toBeInTheDocument();
+
+// 正确:等待异步操作完成
+await user.click(submitButton);
+await waitFor(() => {
+  expect(screen.getByText(/成功/)).toBeInTheDocument();
+});
+```
+
+## 覆盖率标准
+
+| 测试类型 | 最低要求 | 目标要求 |
+|----------|----------|----------|
+| 组件测试 | 70% | 80% |
+| 集成测试 | 50% | 60% |
+| E2E测试 | 关键流程100% | 主要流程80% |
+
+## 运行测试
+
+### 本地开发
+```bash
+# 运行所有测试
+pnpm test
+
+# 运行组件测试
+pnpm test:components
+
+# 运行集成测试
+pnpm test:integration
+
+# 运行E2E测试
+pnpm test:e2e:chromium
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 运行特定测试
+pnpm test --testNamePattern="UserForm"
+```
+
+### CI/CD
+```yaml
+web-component-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd web && pnpm test:components
+
+web-integration-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd web && pnpm test:integration
+
+web-e2e-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd web && pnpm test:e2e:chromium
+```
+
+## 调试技巧
+
+### 1. 使用调试模式
+```bash
+# 运行特定测试并显示详细信息
+pnpm test --testNamePattern="UserForm" --reporter=verbose
+
+# 在浏览器中打开调试器
+pnpm test:components --debug
+```
+
+### 2. 查看DOM结构
+```typescript
+// 在测试中打印DOM结构
+screen.debug();
+
+// 打印特定元素
+screen.debug(screen.getByTestId('user-table'));
+```
+
+### 3. E2E测试调试
+```typescript
+// 使用调试模式
+test('调试模式', async ({ page }) => {
+  await page.goto('/users');
+  await page.pause(); // 暂停执行,打开Playwright Inspector
+});
+```
+
+## 参考实现
+
+- 用户管理UI包:`packages/user-management-ui`
+- 文件管理UI包:`packages/file-management-ui`
+- Web应用测试:`web/tests/`
+
+## 相关文档
+
+- [测试策略概述](./testing-strategy.md)
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [Web Server包测试规范](./web-server-testing-standards.md)
+- [Mini UI包测试规范](./mini-ui-testing-standards.md)
+- [UI包开发规范](./ui-package-standards.md)
+
+---
+
+**文档状态**: 正式版
+**适用范围**: Web UI包和Web应用