Prechádzať zdrojové kódy

docs: 创建项目文档架构并更新标准文档

- 新建项目概览和主索引文档 (README.md, index.md)
- 新建架构文档:Monorepo架构、模块组织结构、数据模型、API设计
- 新建开发指南:环境设置、本地开发、部署指南
- 复制并更新8份高质量的标准文档到 docs/standards/

归档中的旧架构文档描述的是"D8D Starter"模板,与实际大型
Monorepo项目不匹配。新文档基于实际项目结构创建。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
yourname 1 týždeň pred
rodič
commit
84ab54abdf

+ 153 - 0
docs/README.md

@@ -0,0 +1,153 @@
+# 188-179 招聘系统项目文档
+
+## 项目概述
+
+本项目是一个大型企业级招聘管理系统,采用 Monorepo 架构,包含管理后台、员工小程序、人才小程序等多个应用。
+
+### 技术栈
+
+- **前端框架**: React 19, Taro 3.x
+- **后端框架**: Hono 4.x, NestJS (部分模块)
+- **数据库**: PostgreSQL 17
+- **缓存**: Redis 7
+- **对象存储**: MinIO
+- **构建工具**: Vite, Turbo
+- **包管理**: pnpm workspaces
+- **测试**: Vitest, Playwright, Testing Library
+
+## 项目结构
+
+```
+188-179-template-6/
+├── packages/              # 核心共享包和模块 (61个)
+├── allin-packages/        # 业务模块包 (15个)
+├── mini-ui-packages/      # 小程序UI组件库 (18个)
+├── mini/                  # 员工小程序 (Taro)
+├── mini-talent/           # 人才小程序 (Taro)
+├── web/                   # 管理后台 (React + Vite)
+├── docs/                  # 项目文档
+└── _bmad/                 # BMAD工作流配置
+```
+
+## 文档导航
+
+### 快速开始
+- [开发环境设置](./guides/setup.md)
+- [本地开发指南](./guides/local-development.md)
+- [部署指南](./guides/deployment.md)
+
+### 架构文档
+- [Monorepo架构](./architecture/monorepo.md)
+- [模块组织结构](./architecture/module-structure.md)
+- [数据模型](./architecture/data-models.md)
+- [API设计](./architecture/api-design.md)
+
+### 开发规范
+- [编码标准](./standards/coding-standards.md)
+- [后端模块开发规范](./standards/backend-module-standards.md)
+- [UI包开发规范](./standards/ui-package-standards.md)
+- [小程序UI包开发规范](./standards/mini-ui-package-standards.md)
+- [测试规范](./standards/testing-standards.md)
+
+## 包分类
+
+### 核心共享包
+- `core-module`: 核心业务逻辑
+- `shared-types`: 共享类型定义
+- `shared-utils`: 共享工具函数
+- `shared-crud`: 通用CRUD服务
+- `shared-ui-components`: 共享UI组件
+
+### 业务模块包
+#### 基础模块
+- `auth-module`: 认证授权
+- `user-module`: 用户管理
+- `file-module`: 文件管理
+- `tenant-module`: 租户管理
+
+#### 业务模块
+- `orders-module`: 订单管理
+- `goods-module`: 商品管理
+- `merchant-module`: 商家管理
+- `supplier-module`: 供应商管理
+- `delivery-address-module`: 收货地址
+- `geo-areas`: 地理区域
+- `advertisements-module`: 广告管理
+- `bank-names-module`: 银行名称
+- `mini-payment`: 小程序支付
+
+### 小程序UI包
+#### 通用组件
+- `mini-charts`: 图表组件
+- `mini-enterprise-auth-ui`: 企业认证UI
+- `mini-shared-ui-components`: 共享UI组件
+- `allin-enums`: 枚举定义
+
+#### 员工端 (yongren)
+- `yongren-dashboard-ui`: 仪表板
+- `yongren-order-management-ui`: 订单管理
+- `yongren-statistics-ui`: 统计分析
+- `yongren-talent-management-ui`: 人才管理
+
+#### 人才端 (rencai)
+- `rencai-auth-ui`: 认证UI
+- `rencai-dashboard-ui`: 仪表板
+- `rencai-attendance-ui`: 考勤管理
+- `rencai-employment-ui`: 入职管理
+- `rencai-personal-info-ui`: 个人信息
+
+## 开发命令
+
+### 安装依赖
+```bash
+pnpm install
+```
+
+### 启动开发环境
+```bash
+# 启动全部应用
+pnpm dev
+
+# 仅启动管理后台
+pnpm run dev:web
+
+# 仅启动员工小程序 H5
+pnpm run dev:mini
+
+# 仅启动人才小程序 H5
+pnpm run dev:mini-talent
+```
+
+### 构建
+```bash
+# 构建所有包
+pnpm build
+
+# 构建小程序UI包
+pnpm run build:mini-ui-packages
+```
+
+### 测试
+```bash
+# 运行所有测试
+pnpm test
+
+# 类型检查
+pnpm typecheck
+
+# 代码检查
+pnpm lint
+```
+
+## 多租户架构
+
+项目支持多租户架构,部分模块具有多租户变体(带 `-mt` 后缀):
+- `auth-module-mt`
+- `goods-module-mt`
+- `orders-module-mt`
+- 等等...
+
+## 联系方式
+
+- 项目名称: 188-179 招聘系统
+- 最后更新: 2026-01-07

+ 368 - 0
docs/architecture/api-design.md

@@ -0,0 +1,368 @@
+# API设计文档
+
+## 概述
+
+本文档描述了 188-179 招聘系统的 API 设计规范。系统采用 RESTful API 设计风格,使用 Hono 框架实现,并通过 `@hono/zod-openapi` 自动生成 OpenAPI 文档。
+
+## API架构
+
+### 技术栈
+
+- **框架**: Hono 4.x
+- **验证**: Zod Schema
+- **文档**: @hono/zod-openapi (自动生成OpenAPI 3.0)
+- **类型**: TypeScript (完全类型安全)
+
+### API组织结构
+
+```
+web/server.js (Hono应用)
+├── /api/v1/
+│   ├── /auth         # 认证相关
+│   ├── /users        # 用户管理
+│   ├── /orders       # 订单管理
+│   ├── /companies    # 企业管理
+│   ├── /platforms    # 平台管理
+│   ├── /channels     # 渠道管理
+│   └── /files        # 文件管理
+└── /ui               # Swagger UI
+```
+
+## 通用规范
+
+### 请求格式
+
+#### Content-Type
+```http
+Content-Type: application/json
+```
+
+#### 认证头
+```http
+Authorization: Bearer <token>
+```
+
+### 响应格式
+
+#### 成功响应
+```json
+{
+  "id": 1,
+  "username": "admin",
+  "email": "admin@example.com"
+}
+```
+
+#### 错误响应
+```json
+{
+  "code": 400,
+  "message": "参数错误"
+}
+```
+
+#### Zod验证错误
+```json
+{
+  "code": "VALIDATION_ERROR",
+  "message": "参数验证失败",
+  "errors": [
+    {
+      "path": ["username"],
+      "message": "用户名不能为空"
+    }
+  ]
+}
+```
+
+### HTTP状态码
+
+| 状态码 | 含义 | 使用场景 |
+|--------|------|----------|
+| 200 | OK | 操作成功 |
+| 201 | Created | 创建成功 |
+| 400 | Bad Request | 请求参数错误 |
+| 401 | Unauthorized | 未认证 |
+| 403 | Forbidden | 权限不足 |
+| 404 | Not Found | 资源不存在 |
+| 500 | Internal Server Error | 服务器错误 |
+
+## RESTful API设计
+
+### CRUD标准接口
+
+#### 获取列表
+```http
+GET /api/v1/users?page=1&pageSize=10&keyword=搜索词
+```
+
+#### 获取详情
+```http
+GET /api/v1/users/:id
+```
+
+#### 创建
+```http
+POST /api/v1/users
+Content-Type: application/json
+
+{
+  "username": "newuser",
+  "email": "user@example.com",
+  "password": "password123"
+}
+```
+
+#### 更新
+```http
+PUT /api/v1/users/:id
+Content-Type: application/json
+
+{
+  "email": "newemail@example.com"
+}
+```
+
+#### 删除
+```http
+DELETE /api/v1/users/:id
+```
+
+### 分页参数
+
+| 参数 | 类型 | 默认值 | 描述 |
+|------|------|--------|------|
+| page | number | 1 | 页码 |
+| pageSize | number | 10 | 每页数量 |
+| keyword | string | - | 搜索关键词 |
+| sortBy | string | createTime | 排序字段 |
+| sortOrder | string | DESC | 排序方向 (ASC/DESC) |
+
+### 分页响应
+```json
+{
+  "data": [...],
+  "pagination": {
+    "total": 100,
+    "current": 1,
+    "pageSize": 10,
+    "totalPages": 10
+  }
+}
+```
+
+## OpenAPI文档
+
+### 访问Swagger UI
+
+```
+http://localhost:8080/ui
+```
+
+### Schema定义示例
+
+```typescript
+import { z } from '@hono/zod-openapi';
+
+// 用户Schema
+export const UserSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '用户ID',
+    example: 1
+  }),
+  username: z.string().max(50).openapi({
+    description: '用户名',
+    example: 'admin'
+  }),
+  email: z.string().email().nullable().openapi({
+    description: '邮箱',
+    example: 'admin@example.com'
+  }),
+  createTime: z.coerce.date<Date>().openapi({
+    description: '创建时间'
+  }),
+  updateTime: z.coerce.date<Date>().openapi({
+    description: '更新时间'
+  })
+});
+
+// 创建用户DTO
+export const CreateUserSchema = z.object({
+  username: z.string().min(1).max(50).openapi({
+    description: '用户名',
+    example: 'newuser'
+  }),
+  email: z.string().email().optional().openapi({
+    description: '邮箱',
+    example: 'user@example.com'
+  }),
+  password: z.string().min(6).openapi({
+    description: '密码',
+    example: 'password123'
+  })
+});
+```
+
+### 路由定义示例
+
+```typescript
+import { OpenAPIHono, createRoute } from '@hono/zod-openapi';
+import { z } from 'zod';
+
+const app = new OpenAPIHono();
+
+// 定义路由
+const getUserRoute = createRoute({
+  method: 'get',
+  path: '/users/{id}',
+  request: {
+    params: z.object({
+      id: z.string().openapi({
+        description: '用户ID',
+        example: '1'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '获取成功',
+      content: {
+        'application/json': { schema: UserSchema }
+      }
+    },
+    404: {
+      description: '用户不存在',
+      content: {
+        'application/json': { schema: ErrorSchema }
+      }
+    }
+  }
+});
+
+// 实现路由
+app.openapi(getUserRoute, async (c) => {
+  const { id } = c.req.valid('param');
+  const user = await userService.findById(Number(id));
+  if (!user) {
+    return c.json({ code: 404, message: '用户不存在' }, 404);
+  }
+  return c.json(user, 200);
+});
+```
+
+## RPC客户端
+
+### 类型安全的API调用
+
+UI包通过 `hc` 客户端获得类型安全的API调用:
+
+```typescript
+import { hc } from 'hono/client';
+import type AppRoutes from '@d8d/server';
+
+const client = hc<AppRoutes>('/api/v1');
+
+// 完全类型安全
+const result = await client.users.$get({
+  query: { page: 1, pageSize: 10 }
+});
+
+const user = await client.users[':id'].$get({
+  param: { id: '1' }
+});
+```
+
+## 认证与授权
+
+### JWT认证
+
+```http
+POST /api/v1/auth/login
+Content-Type: application/json
+
+{
+  "username": "admin",
+  "password": "password123"
+}
+```
+
+响应:
+```json
+{
+  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+  "user": {
+    "id": 1,
+    "username": "admin"
+  }
+}
+```
+
+### 使用Token
+
+```http
+GET /api/v1/users
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+```
+
+## 错误处理
+
+### 标准错误响应
+
+```typescript
+// 简单错误
+return c.json({
+  code: 404,
+  message: '用户不存在'
+}, 404);
+
+// Zod验证错误
+if (error instanceof z.ZodError) {
+  return c.json({
+    code: 'VALIDATION_ERROR',
+    message: '参数验证失败',
+    errors: error.errors
+  }, 400);
+}
+```
+
+## API版本管理
+
+### URL版本控制
+
+```
+/api/v1/users   # 当前版本
+/api/v2/users   # 未来版本
+```
+
+### 向后兼容策略
+
+- 不移除字段,只添加新字段
+- 使用可选字段扩展功能
+- 废弃字段在文档中标注
+
+## 性能优化
+
+### 分页限制
+
+- 默认每页10条
+- 最大每页100条
+
+### 缓存策略
+
+- 使用Redis缓存热点数据
+- 设置合理的缓存过期时间
+
+### 限流策略
+
+- 每用户每分钟100请求限制
+- 超限返回429状态码
+
+## 相关文档
+
+- [数据模型](./data-models.md)
+- [后端模块开发规范](../standards/backend-module-standards.md)
+
+---
+
+**文档版本**: 1.0
+**最后更新**: 2026-01-07

+ 257 - 0
docs/architecture/data-models.md

@@ -0,0 +1,257 @@
+# 数据模型文档
+
+## 概述
+
+本文档描述了 188-179 招聘系统的数据模型设计。系统采用 TypeORM 作为 ORM,PostgreSQL 作为数据库。
+
+## 核心实体
+
+### 用户与认证
+
+#### User (用户表)
+```typescript
+{
+  id: number              // 主键
+  username: string        // 用户名(唯一)
+  email: string | null    // 邮箱
+  password: string        // 加密密码
+  roles: Role[]           // 用户角色(多对多)
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+#### Role (角色表)
+```typescript
+{
+  id: number              // 主键
+  name: string            // 角色名称(唯一)
+  permissions: string[]   // 权限列表
+  users: User[]           // 用户列表(多对多)
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+### 文件管理
+
+#### File (文件表)
+```typescript
+{
+  id: number              // 主键
+  name: string            // 文件名
+  path: string            // MinIO存储路径
+  size: number            // 文件大小(字节)
+  type: string            // 文件类型
+  uploadUserId: number    // 上传用户ID
+  uploadUser: User        // 上传用户(多对一)
+  uploadTime: Date        // 上传时间
+  lastUpdated: Date       // 最后更新时间
+}
+```
+
+## 业务模块实体
+
+### 订单管理
+
+#### Order (订单表)
+```typescript
+{
+  id: number              // 主键
+  orderNo: string         // 订单号(唯一)
+  orderType: string       // 订单类型
+  status: number          // 状态:1-正常,0-禁用
+  amount: number          // 订单金额
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+### 企业管理
+
+#### Company (公司表)
+```typescript
+{
+  id: number              // 主键
+  companyName: string     // 公司名称
+  companyId: string       // 企业ID(唯一)
+  status: number          // 状态:1-正常,0-禁用
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+#### Platform (平台表)
+```typescript
+{
+  id: number              // 主键
+  platformName: string    // 平台名称
+  companyId: number       // 所属公司
+  company: Company        // 公司关联(多对一)
+  status: number          // 状态:1-正常,0-禁用
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+### 渠道管理
+
+#### Channel (渠道表)
+```typescript
+{
+  id: number              // 主键
+  channelName: string     // 渠道名称(唯一)
+  channelType: string     // 渠道类型
+  contactPerson: string   // 联系人
+  contactPhone: string    // 联系电话
+  status: number          // 状态:1-正常,0-禁用
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+### 残疾人员管理
+
+#### DisabledPerson (残疾人员表)
+```typescript
+{
+  id: number              // 主键
+  name: string            // 姓名
+  idCard: string          // 身份证号
+  disabilityType: string  // 残疾类型
+  disabilityLevel: string // 残疾等级
+  disabilityCardNo: string // 残疾证号
+  phone: string           // 联系电话
+  address: string         // 地址
+  avatarFileId: number    // 头像文件ID
+  avatarFile: File        // 头像文件(多对一)
+  status: number          // 状态:1-正常,0-禁用
+  createTime: Date        // 创建时间
+  updateTime: Date        // 更新时间
+}
+```
+
+## 数据库设计规范
+
+### 命名规范
+
+| 类型 | 规范 | 示例 |
+|------|------|------|
+| 表名 | 小写下划线 | `user_info`, `order_detail` |
+| 字段名 | 小写下划线 | `user_name`, `create_time` |
+| 主键 | `id` 或 `{table}_id` | `id`, `user_id` |
+| 外键 | `{referenced_table}_id` | `user_id`, `order_id` |
+| 索引 | `idx_{column}_{purpose}` | `idx_user_name`, `idx_order_status` |
+| 唯一索引 | `unique_{column}` | `unique_username` |
+
+### 字段类型规范
+
+| 数据类型 | PostgreSQL类型 | TypeORM类型 | 用途 |
+|----------|----------------|-------------|------|
+| 主键 | `int unsigned` | `int` | 自增主键 |
+| 字符串(短) | `varchar(n)` | `varchar` | 用户名、名称等 |
+| 字符串(长) | `text` | `text` | 描述、内容等 |
+| 金额 | `decimal(10,2)` | `decimal` | 订单金额等 |
+| 时间戳 | `timestamp` | `timestamp` | 创建时间、更新时间 |
+| 状态 | `int` | `int` | 状态枚举 |
+| 布尔 | `boolean` | `boolean` | 开关状态 |
+
+### 时间戳字段
+
+所有表都应包含时间戳字段:
+
+```typescript
+@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;
+```
+
+### 软删除字段
+
+使用 `status` 字段实现软删除:
+
+```typescript
+@Column({
+  name: 'status',
+  type: 'int',
+  default: 1,
+  comment: '状态:1-正常,0-禁用'
+})
+status!: number;
+```
+
+## 关联关系
+
+### 一对多 (OneToMany)
+
+```typescript
+// Company 拥有多个 Platform
+@OneToMany(() => Platform, platform => platform.company)
+platforms!: Platform[];
+```
+
+### 多对一 (ManyToOne)
+
+```typescript
+// Platform 属于一个 Company
+@ManyToOne(() => Company, { eager: false })
+@JoinColumn({ name: 'company_id', referencedColumnName: 'id' })
+company!: Company;
+```
+
+### 多对多 (ManyToMany)
+
+```typescript
+// User 拥有多个 Role
+@ManyToMany(() => Role, role => role.users)
+@JoinTable({
+  name: 'user_roles',
+  joinColumn: { name: 'user_id', referencedColumnName: 'id' },
+  inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' }
+})
+roles!: Role[];
+```
+
+## 迁移策略
+
+### 创建迁移
+
+```bash
+cd packages/core-module
+pnpm typeorm migration:generate -d src/data-source.ts src/migrations/MigrationName
+```
+
+### 运行迁移
+
+```bash
+pnpm db:migrate
+```
+
+### 回滚迁移
+
+```bash
+pnpm db:migrate:revert
+```
+
+## 相关文档
+
+- [后端模块开发规范](../standards/backend-module-standards.md)
+- [API设计](./api-design.md)
+
+---
+
+**文档版本**: 1.0
+**最后更新**: 2026-01-07

+ 199 - 0
docs/architecture/module-structure.md

@@ -0,0 +1,199 @@
+# 模块组织结构
+
+## 目录概览
+
+```
+188-179-template-6/
+├── packages/              # 核心共享包和模块 (61个)
+├── allin-packages/        # 业务模块包 (15个)
+├── mini-ui-packages/      # 小程序UI组件库 (18个)
+├── mini/                  # 员工小程序
+├── mini-talent/           # 人才小程序
+├── web/                   # 管理后台
+└── docs/                  # 项目文档
+```
+
+## packages/ 目录详解
+
+### 核心共享包
+| 包名 | 描述 |
+|------|------|
+| `core-module` | 核心业务逻辑和工具 |
+| `shared-types` | TypeScript 类型定义 |
+| `shared-utils` | 通用工具函数 |
+| `shared-crud` | 通用 CRUD 服务 |
+| `shared-ui-components` | React 共享 UI 组件 |
+| `shared-test-util` | 测试工具函数 |
+| `server` | 后端服务器 (Hono) |
+
+### 认证与用户模块
+| 包名 | 描述 |
+|------|------|
+| `auth-module` | 认证授权模块 |
+| `auth-module-mt` | 多租户认证模块 |
+| `user-module` | 用户管理模块 |
+| `user-module-mt` | 多租户用户模块 |
+| `user-management-ui` | 用户管理 UI |
+| `user-management-ui-mt` | 多租户用户管理 UI |
+| `auth-management-ui` | 认证管理 UI |
+| `auth-management-ui-mt` | 多租户认证管理 UI |
+
+### 订单与商品模块
+| 包名 | 描述 |
+|------|------|
+| `orders-module` | 订单管理模块 |
+| `orders-module-mt` | 多租户订单模块 |
+| `goods-module` | 商品管理模块 |
+| `goods-module-mt` | 多租户商品模块 |
+| `goods-category-management-ui` | 商品分类 UI |
+| `goods-category-management-ui-mt` | 多租户商品分类 UI |
+| `goods-management-ui` | 商品管理 UI |
+| `goods-management-ui-mt` | 多租户商品管理 UI |
+| `order-management-ui` | 订单管理 UI |
+| `order-management-ui-mt` | 多租户订单管理 UI |
+| `advertisements-module` | 广告模块 |
+| `advertisements-module-mt` | 多租户广告模块 |
+| `advertisement-type-management-ui` | 广告类型 UI |
+| `advertisement-type-management-ui-mt` | 多租户广告类型 UI |
+| `advertisement-management-ui` | 广告管理 UI |
+| `advertisement-management-ui-mt` | 多租户广告管理 UI |
+
+### 商家与供应商模块
+| 包名 | 描述 |
+|------|------|
+| `merchant-module` | 商家管理模块 |
+| `merchant-module-mt` | 多租户商家模块 |
+| `merchant-management-ui` | 商家管理 UI |
+| `merchant-management-ui-mt` | 多租户商家管理 UI |
+| `supplier-module` | 供应商管理模块 |
+| `supplier-module-mt` | 多租户供应商模块 |
+| `supplier-management-ui` | 供应商管理 UI |
+| `supplier-management-ui-mt` | 多租户供应商管理 UI |
+
+### 地址与区域模块
+| 包名 | 描述 |
+|------|------|
+| `delivery-address-module` | 收货地址模块 |
+| `delivery-address-module-mt` | 多租户收货地址模块 |
+| `delivery-address-management-ui` | 收货地址 UI |
+| `delivery-address-management-ui-mt` | 多租户收货地址 UI |
+| `geo-areas` | 地理区域模块 |
+| `geo-areas-mt` | 多租户地理区域模块 |
+| `area-management-ui` | 区域管理 UI |
+| `area-management-ui-mt` | 多租户区域管理 UI |
+
+### 文件与配置模块
+| 包名 | 描述 |
+|------|------|
+| `file-module` | 文件管理模块 |
+| `file-module-mt` | 多租户文件模块 |
+| `file-management-ui` | 文件管理 UI |
+| `file-management-ui-mt` | 多租户文件管理 UI |
+| `tenant-module` | 租户管理模块 |
+| `tenant-module-mt` | 多租户租户模块 |
+| `tenant-management-ui` | 租户管理 UI |
+| `system-config-management-ui-mt` | 系统配置 UI |
+
+### 支付与银行模块
+| 包名 | 描述 |
+|------|------|
+| `mini-payment` | 小程序支付模块 |
+| `mini-payment-mt` | 多租户小程序支付 |
+| `bank-names-module` | 银行名称模块 |
+| `bank-name-management-ui` | 银行名称管理 UI |
+| `bank-name-management-ui-mt` | 多租户银行名称 UI |
+
+## allin-packages/ 目录详解
+
+### 业务模块
+| 包名 | 描述 |
+|------|------|
+| `channel-module` | 频道管理模块 |
+| `channel-management-ui` | 频道管理 UI |
+| `company-module` | 公司管理模块 |
+| `company-management-ui` | 公司管理 UI |
+| `disability-module` | 残疾管理模块 |
+| `disability-person-management-ui` | 残疾人员管理 UI |
+| `order-module` | 订单模块 |
+| `order-management-ui` | 订单管理 UI |
+| `platform-module` | 平台管理模块 |
+| `platform-management-ui` | 平台管理 UI |
+| `salary-module` | 薪资管理模块 |
+| `salary-management-ui` | 薪资管理 UI |
+| `statistics-module` | 统计分析模块 |
+| `enums` | 枚举定义 |
+
+## mini-ui-packages/ 目录详解
+
+### 通用组件
+| 包名 | 描述 |
+|------|------|
+| `mini-charts` | 图表组件库 |
+| `mini-enterprise-auth-ui` | 企业认证 UI 组件 |
+| `mini-shared-ui-components` | 共享 UI 组件 |
+| `mini-testing-utils` | 测试工具 |
+| `allin-enums` | 枚举定义 |
+
+### 员工端组件 (yongren)
+| 包名 | 描述 |
+|------|------|
+| `yongren-shared-ui` | 员工端共享 UI |
+| `yongren-dashboard-ui` | 员工端仪表板 |
+| `yongren-order-management-ui` | 员工端订单管理 |
+| `yongren-settings-ui` | 员工端设置 |
+| `yongren-statistics-ui` | 员工端统计 |
+| `yongren-talent-management-ui` | 员工端人才管理 |
+
+### 人才端组件 (rencai)
+| 包名 | 描述 |
+|------|------|
+| `rencai-shared-ui` | 人才端共享 UI |
+| `rencai-dashboard-ui` | 人才端仪表板 |
+| `rencai-auth-ui` | 人才端认证 UI |
+| `rencai-personal-info-ui` | 人才端个人信息 |
+| `rencai-attendance-ui` | 人才端考勤管理 |
+| `rencai-employment-ui` | 人才端入职管理 |
+| `rencai-settings-ui` | 人才端设置 |
+
+## 应用目录详解
+
+### web/ - 管理后台
+```
+web/
+├── src/              # 源代码
+├── server.js         # 服务器入口
+├── vite.config.ts    # Vite 配置
+└── package.json
+```
+
+### mini/ - 员工小程序 (Taro)
+```
+mini/
+├── src/              # 源代码
+├── config/           # Taro 配置
+└── package.json
+```
+
+### mini-talent/ - 人才小程序 (Taro)
+```
+mini-talent/
+├── src/              # 源代码
+├── config/           # Taro 配置
+└── package.json
+```
+
+## 包依赖关系图
+
+```
+应用层 (web, mini, mini-talent)
+  ↓
+UI包层 (*-management-ui, yongren-*, rencai-*)
+  ↓
+模块层 (*-module)
+  ↓
+核心层 (core-module, shared-*)
+```
+
+## 相关文档
+- [Monorepo架构](./monorepo.md)
+- [数据模型](./data-models.md)

+ 202 - 0
docs/architecture/monorepo.md

@@ -0,0 +1,202 @@
+# Monorepo 架构文档
+
+## 概述
+
+本项目采用 pnpm workspaces 实现 Monorepo 架构,统一管理多个应用、模块和共享包。
+
+## 技术选型
+
+| 技术 | 版本 | 用途 |
+|------|------|------|
+| pnpm | 10.18.3 | 包管理和工作空间 |
+| Turbo | - | 构建编排和缓存 |
+| TypeScript | 5.9.3 | 类型安全 |
+| Vite | 7.x | 构建工具 |
+
+## 工作空间配置
+
+### pnpm-workspace.yaml
+```yaml
+packages:
+  - 'mini'                    # 员工小程序
+  - 'mini-talent'             # 人才小程序
+  - 'web'                     # 管理后台
+  - 'packages/*'              # 核心包 (61个)
+  - 'allin-packages/*'        # 业务模块包 (15个)
+  - 'mini-ui-packages/*'      # 小程序UI包 (18个)
+```
+
+## 包分类架构
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│                    188-179 Monorepo                         │
+├─────────────────────────────────────────────────────────────┤
+│                                                             │
+│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
+│  │   应用层      │  │   应用层      │  │   应用层      │     │
+│  ├──────────────┤  ├──────────────┤  ├──────────────┤     │
+│  │  web/        │  │  mini/       │  │  mini-talent/│     │
+│  │  管理后台     │  │  员工小程序   │  │  人才小程序   │     │
+│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘     │
+│         │                 │                 │              │
+│         └─────────────────┼─────────────────┘              │
+│                           │                                │
+│  ┌────────────────────────▼─────────────────────────────┐  │
+│  │                   模块层                               │  │
+│  ├─────────────────────────────────────────────────────┤  │
+│  │  packages/           allin-packages/                  │  │
+│  │  ├── core-module      ├── channel-module             │  │
+│  │  ├── auth-module      ├── company-module             │  │
+│  │  ├── user-module      ├── disability-module          │  │
+│  │  ├── orders-module    ├── order-module               │  │
+│  │  ├── goods-module     ├── platform-module            │  │
+│  │  ├── merchant-module  ├── salary-module              │  │
+│  │  └── ...              └── statistics-module          │  │
+│  └─────────────────────────────────────────────────────┘  │
+│                           │                                │
+│  ┌────────────────────────▼─────────────────────────────┐  │
+│  │                   共享层                               │  │
+│  ├─────────────────────────────────────────────────────┤  │
+│  │  mini-ui-packages/      packages/ 共享               │  │
+│  │  ├── mini-charts        ├── shared-types             │  │
+│  │  ├── mini-shared-ui     ├── shared-utils             │  │
+│  │  ├── yongren-*          ├── shared-crud              │  │
+│  │  ├── rencai-*           ├── shared-ui-components      │  │
+│  │  └── allin-enums        └── ...                      │  │
+│  └─────────────────────────────────────────────────────┘  │
+│                                                             │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## 依赖关系
+
+### 应用依赖
+```
+web/           → packages/core-module, packages/shared-ui-components
+mini/          → mini-ui-packages/yongren-*, packages/core-module
+mini-talent/   → mini-ui-packages/rencai-*, packages/core-module
+```
+
+### 模块依赖
+```
+*-module       → packages/core-module, packages/shared-types
+*-management-ui → packages/shared-ui-components, packages/*-module
+```
+
+### 共享依赖
+```
+所有包         → packages/shared-types, packages/shared-utils
+```
+
+## 包命名规范
+
+### 后端模块包
+- 格式: `<业务领域>-module`
+- 示例: `auth-module`, `orders-module`, `goods-module`
+- 多租户变体: `<业务领域>-module-mt`
+
+### UI管理包
+- 格式: `<业务领域>-management-ui`
+- 示例: `user-management-ui`, `order-management-ui`
+
+### 小程序UI包
+- 通用组件: `mini-*`
+- 员工端: `yongren-*`
+- 人才端: `rencai-*`
+
+### 共享包
+- 格式: `shared-*`
+- 示例: `shared-types`, `shared-utils`, `shared-crud`
+
+## 构建策略
+
+### 并行构建
+```bash
+# 使用Turbo并行构建所有包
+pnpm build
+
+# 按类别并行构建
+pnpm run build:mini-ui-packages
+pnpm run build:mini-ui-packages:common
+pnpm run build:mini-ui-packages:yongren
+pnpm run build:mini-ui-packages:rencai
+```
+
+### 构建顺序
+1. **共享包优先**: shared-types → shared-utils → shared-crud
+2. **核心模块**: core-module, auth-module
+3. **业务模块**: 依赖核心模块的业务包
+4. **UI包**: 依赖业务模块的UI组件
+5. **应用**: web, mini, mini-talent
+
+## 开发工作流
+
+### 添加新包
+1. 在对应目录创建包文件夹
+2. 初始化 `package.json`
+3. 更新工作空间依赖
+4. 添加构建脚本
+
+### 包间依赖
+```json
+{
+  "name": "order-management-ui",
+  "dependencies": {
+    "@d8d/orders-module": "workspace:*",
+    "@d8d/shared-ui-components": "workspace:*",
+    "@d8d/shared-types": "workspace:*"
+  }
+}
+```
+
+## 多租户支持
+
+部分模块支持多租户架构,具有独立的 `-mt` 变体:
+
+| 单租户版本 | 多租户版本 |
+|-----------|-----------|
+| auth-module | auth-module-mt |
+| goods-module | goods-module-mt |
+| orders-module | orders-module-mt |
+| user-module | user-module-mt |
+
+多租户版本包含额外的租户隔离逻辑和数据过滤。
+
+## 性能优化
+
+### Turbo 缓存
+- 构建缓存位于 `.turbo/` 目录
+- 未变更的包使用缓存,加快构建速度
+
+### 并行执行
+- 使用 `--workspace-concurrency` 控制并行度
+- 默认使用CPU核心数
+
+## 故障排查
+
+### 依赖问题
+```bash
+# 重新安装依赖
+rm -rf node_modules .pnpm-store
+pnpm install
+```
+
+### 构建缓存清理
+```bash
+# 清除Turbo缓存
+rm -rf .turbo
+
+# 重新构建
+pnpm build
+```
+
+### 包链接问题
+```bash
+# 检查包依赖关系
+pnpm list --depth 0
+```
+
+## 相关文档
+- [模块组织结构](./module-structure.md)
+- [开发规范](../standards/coding-standards.md)

+ 364 - 0
docs/guides/deployment.md

@@ -0,0 +1,364 @@
+# 部署指南
+
+## 概述
+
+本文档描述了 188-179 招聘系统的部署流程和配置。
+
+## 部署架构
+
+### 组件列表
+
+| 组件 | 技术栈 | 端口 | 说明 |
+|------|--------|------|------|
+| Web服务器 | Node.js + Hono | 8080 | 管理后台后端 |
+| PostgreSQL | 17 | 5432 | 数据库 |
+| Redis | 7 | 6379 | 缓存和会话 |
+| MinIO | latest | 9000 | 对象存储 |
+
+### 基础设施要求
+
+**最低配置**:
+- CPU: 2核
+- 内存: 4GB
+- 磁盘: 20GB
+
+**推荐配置**:
+- CPU: 4核
+- 内存: 8GB
+- 磁盘: 50GB
+
+## Docker部署
+
+### 使用Docker Compose
+
+```bash
+# 构建镜像
+docker-compose build
+
+# 启动所有服务
+docker-compose up -d
+
+# 查看日志
+docker-compose logs -f
+
+# 停止服务
+docker-compose down
+```
+
+### Dockerfile示例
+
+```dockerfile
+# Web服务Dockerfile
+FROM node:20.19.2-alpine
+
+WORKDIR /app
+
+# 安装pnpm
+RUN npm install -g pnpm@10.18.3
+
+# 复制依赖文件
+COPY package.json pnpm-lock.yaml ./
+COPY pnpm-workspace.yaml ./
+
+# 安装依赖
+RUN pnpm install --frozen-lockfile
+
+# 复制源码
+COPY . .
+
+# 构建共享包
+RUN pnpm run build:mini-ui-packages
+
+# 构建应用
+RUN pnpm build
+
+# 暴露端口
+EXPOSE 8080
+
+# 启动命令
+CMD ["node", "web/server.js"]
+```
+
+### docker-compose.yml示例
+
+```yaml
+version: '3.8'
+
+services:
+  postgres:
+    image: postgres:17
+    environment:
+      POSTGRES_USER: postgres
+      POSTGRES_PASSWORD: postgres
+      POSTGRES_DB: postgres
+    ports:
+      - "5432:5432"
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+
+  redis:
+    image: redis:7-alpine
+    ports:
+      - "6379:6379"
+    volumes:
+      - redis_data:/data
+
+  minio:
+    image: minio/minio:latest
+    command: server /data --console-address ":9001"
+    environment:
+      MINIO_ROOT_USER: minioadmin
+      MINIO_ROOT_PASSWORD: minioadmin
+    ports:
+      - "9000:9000"
+      - "9001:9001"
+    volumes:
+      - minio_data:/data
+
+  web:
+    build: .
+    ports:
+      - "8080:8080"
+    depends_on:
+      - postgres
+      - redis
+      - minio
+    environment:
+      DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
+      REDIS_URL: redis://redis:6379
+      MINIO_ENDPOINT: minio
+      MINIO_PORT: 9000
+      NODE_ENV: production
+
+volumes:
+  postgres_data:
+  redis_data:
+  minio_data:
+```
+
+## 生产环境部署
+
+### 环境变量配置
+
+创建生产环境的 `.env.production`:
+
+```bash
+# 数据库配置
+DATABASE_URL=postgresql://user:password@production-db:5432/dbname
+
+# Redis配置
+REDIS_URL=redis://production-redis:6379
+
+# JWT配置
+JWT_SECRET=your-production-jwt-secret-min-32-chars
+
+# MinIO配置
+MINIO_ENDPOINT=production-minio
+MINIO_PORT=9000
+MINIO_ACCESS_KEY=your-access-key
+MINIO_SECRET_KEY=your-secret-key
+MINIO_BUCKET=d8dai
+MINIO_USE_SSL=true
+
+# 应用配置
+NODE_ENV=production
+PORT=8080
+LOG_LEVEL=info
+
+# CORS配置
+CORS_ORIGIN=https://your-domain.com
+```
+
+### 构建生产版本
+
+```bash
+# 设置生产环境
+export NODE_ENV=production
+
+# 构建所有包
+pnpm build
+
+# 构建小程序UI包
+pnpm run build:mini-ui-packages
+```
+
+### 启动生产服务
+
+```bash
+# 使用PM2管理进程
+pm2 start web/server.js --name "d8d-web"
+
+# 查看状态
+pm2 status
+
+# 查看日志
+pm2 logs d8d-web
+
+# 重启服务
+pm2 restart d8d-web
+```
+
+## 数据库迁移
+
+### 运行迁移
+
+```bash
+cd web
+pnpm db:migrate
+```
+
+### 数据库备份
+
+```bash
+# 备份数据库
+pnpm db:backup
+
+# 恢复数据库
+pnpm db:restore
+```
+
+## 监控和日志
+
+### 应用日志
+
+日志位置:
+- 应用日志: `/var/log/d8d/app.log`
+- 错误日志: `/var/log/d8d/error.log`
+
+### 日志配置
+
+```typescript
+// logger配置
+import { logger } from '@d8d/shared-utils';
+
+logger.setLevel(process.env.LOG_LEVEL || 'info');
+```
+
+### 监控指标
+
+- CPU使用率
+- 内存使用率
+- 响应时间
+- 错误率
+- 请求量
+
+## 安全配置
+
+### HTTPS配置
+
+```nginx
+server {
+    listen 443 ssl http2;
+    server_name your-domain.com;
+
+    ssl_certificate /path/to/cert.pem;
+    ssl_certificate_key /path/to/key.pem;
+
+    location / {
+        proxy_pass http://localhost:8080;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+    }
+}
+```
+
+### 防火墙配置
+
+```bash
+# 只开放必要端口
+ufw allow 80/tcp
+ufw allow 443/tcp
+ufw allow 22/tcp
+ufw enable
+```
+
+## CI/CD配置
+
+### GitHub Actions示例
+
+```yaml
+name: Deploy
+
+on:
+  push:
+    branches: [main]
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v3
+        with:
+          node-version: '20.19.2'
+
+      - name: Install pnpm
+        run: npm install -g pnpm@10.18.3
+
+      - name: Install dependencies
+        run: pnpm install
+
+      - name: Build
+        run: pnpm build
+
+      - name: Deploy to server
+        run: |
+          scp -r . user@server:/var/www/d8d
+          ssh user@server "cd /var/www/d8d && pm2 restart all"
+```
+
+## 故障排查
+
+### 服务无法启动
+
+1. 检查环境变量配置
+2. 检查数据库连接
+3. 检查端口占用
+4. 查看应用日志
+
+### 数据库连接失败
+
+1. 确认数据库服务运行
+2. 检查连接字符串
+3. 验证网络连接
+4. 检查数据库用户权限
+
+### 性能问题
+
+1. 检查系统资源使用
+2. 查看慢查询日志
+3. 检查缓存命中率
+4. 分析应用日志
+
+## 回滚策略
+
+### 数据库回滚
+
+```bash
+# 恢复到之前的备份
+pnpm db:restore
+```
+
+### 应用回滚
+
+```bash
+# 使用PM2回滚到旧版本
+pm2 revert d8d-web
+
+# 或切换到之前的Git提交
+git checkout <previous-commit>
+pnpm build
+pm2 restart d8d-web
+```
+
+## 相关文档
+
+- [开发环境设置](./setup.md)
+- [本地开发指南](./local-development.md)
+
+---
+
+**文档版本**: 1.0
+**最后更新**: 2026-01-07

+ 348 - 0
docs/guides/local-development.md

@@ -0,0 +1,348 @@
+# 本地开发指南
+
+## 开发工作流
+
+### 日常开发流程
+
+1. **拉取最新代码**
+   ```bash
+   git pull origin monorepo
+   pnpm install
+   ```
+
+2. **创建功能分支**
+   ```bash
+   git checkout -b feature/your-feature-name
+   ```
+
+3. **启动开发环境**
+   ```bash
+   # 构建共享包(如果修改了共享代码)
+   pnpm run build:mini-ui-packages
+
+   # 启动应用
+   pnpm dev
+   ```
+
+4. **开发和测试**
+   - 在浏览器中访问 http://localhost:8080
+   - 进行功能开发和调试
+
+5. **代码检查**
+   ```bash
+   # 类型检查
+   pnpm typecheck
+
+   # 代码检查
+   pnpm lint
+
+   # 运行测试
+   pnpm test
+   ```
+
+6. **提交代码**
+   ```bash
+   git add .
+   git commit -m "feat: 描述你的功能"
+   git push origin feature/your-feature-name
+   ```
+
+## 包开发
+
+### 开发共享包
+
+共享包(如 `shared-utils`, `shared-types`)在开发时需要启用监听模式:
+
+```bash
+cd packages/shared-utils
+pnpm dev  # 启动 TypeScript 监听模式
+```
+
+### 开发业务模块
+
+#### 后端模块开发
+
+```bash
+cd allin-packages/channel-module
+
+# 编辑代码
+# src/entities/channel.entity.ts
+# src/services/channel.service.ts
+# src/routes/channel.routes.ts
+
+# 运行测试
+pnpm test
+
+# 类型检查
+pnpm typecheck
+```
+
+#### UI包开发
+
+```bash
+cd packages/user-management-ui
+
+# 编辑组件
+# src/components/UsersTable.tsx
+
+# 开发模式(如果有)
+pnpm dev
+
+# 类型检查
+pnpm typecheck
+```
+
+### 开发小程序UI包
+
+```bash
+cd mini-ui-packages/yongren-dashboard-ui
+
+# 编辑组件
+# src/components/DashboardCard.tsx
+
+# 构建包
+pnpm build
+
+# 运行测试
+pnpm test
+```
+
+## 调试技巧
+
+### 后端调试
+
+#### 使用 VS Code 调试
+
+创建 `.vscode/launch.json`:
+
+```json
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Debug Web Server",
+      "runtimeExecutable": "pnpm",
+      "runtimeArgs": ["run", "dev:web"],
+      "cwd": "${workspaceFolder}",
+      "console": "integratedTerminal"
+    }
+  ]
+}
+```
+
+#### 日志调试
+
+```typescript
+import { logger } from '@d8d/shared-utils';
+
+// 使用logger而不是console.log
+logger.info('User created', { userId: user.id });
+logger.error('Failed to create order', { error });
+```
+
+### 前端调试
+
+#### React DevTools
+
+1. 安装 React DevTools 浏览器扩展
+2. 在开发者工具中查看组件树和状态
+
+#### 网络请求调试
+
+```typescript
+import { hc } from 'hono/client';
+import type AppRoutes from '@d8d/server';
+
+// 在客户端查看RPC调用
+const client = hc<AppRoutes>('/api');
+
+// 浏览器网络标签中查看请求
+```
+
+### 小程序调试
+
+#### 微信开发者工具
+
+1. 打开微信开发者工具
+2. 导入项目:选择 `mini/` 或 `mini-talent/` 目录
+3. 使用调试工具查看组件和状态
+
+#### H5 模式调试
+
+在浏览器中打开:
+- 员工小程序: http://localhost:8080
+- 人才小程序: http://localhost:8081
+
+## 测试
+
+### 运行单元测试
+
+```bash
+# 运行所有测试
+pnpm test
+
+# 运行特定包的测试
+cd packages/core-module/auth-module
+pnpm test
+
+# 监听模式
+pnpm test:watch
+```
+
+### 运行集成测试
+
+```bash
+# 集成测试需要数据库连接
+cd packages/core-module/auth-module
+pnpm test:integration
+```
+
+### 运行E2E测试
+
+```bash
+# 在根目录
+pnpm test:e2e
+
+# 指定浏览器
+pnpm test:e2e:chromium
+```
+
+### 测试覆盖率
+
+```bash
+pnpm test:coverage
+```
+
+## 数据库操作
+
+### 运行迁移
+
+```bash
+# TypeORM迁移
+cd web
+pnpm db:migrate
+```
+
+### 数据库备份
+
+```bash
+# 备份数据库
+pnpm db:backup
+
+# 列出备份
+pnpm db:backup:list
+
+# 恢复数据库
+pnpm db:restore
+```
+
+### 重置数据库
+
+```bash
+# ⚠️ 警告:这会删除所有数据
+pnpm db:reset
+```
+
+## 常见任务
+
+### 添加新的依赖
+
+```bash
+# 添加到根目录
+pnpm add package-name
+
+# 添加到特定包
+cd packages/your-package
+pnpm add package-name
+
+# 添加开发依赖
+pnpm add -D package-name
+```
+
+### 添加新的共享包
+
+```bash
+# 在 packages/ 或 allin-packages/ 创建新包
+mkdir packages/new-package
+cd packages/new-package
+
+# 初始化 package.json
+pnpm init
+
+# 配置包导出
+# ...
+```
+
+### 创建新的UI组件
+
+```bash
+# 在UI包中创建组件
+cd packages/user-management-ui/src/components
+
+# 创建组件文件
+touch UserForm.tsx
+```
+
+## 性能优化
+
+### 构建优化
+
+```bash
+# 使用 Turbo 缓存加速构建
+pnpm build
+
+# 清理缓存
+rm -rf .turbo
+```
+
+### 开发模式优化
+
+- 使用 `--filter` 只构建相关包
+- 并行构建调整工作空间并发数
+
+```bash
+# 只构建特定包
+pnpm --filter "@d8d/user-management-ui" build
+```
+
+## 故障排查
+
+### 端口已被占用
+
+```bash
+# 查找占用端口的进程
+lsof -i :8080
+
+# 杀死进程
+kill -9 <PID>
+```
+
+### 热重载不工作
+
+1. 清理 `.turbo` 缓存
+2. 重启开发服务器
+3. 检查 `vite.config.ts` 配置
+
+### 类型错误
+
+```bash
+# 清理并重新安装
+rm -rf node_modules .pnpm-store
+pnpm install
+
+# 重置 TypeScript 项目服务
+# 在 VS Code 中: Ctrl+Shift+P -> TypeScript: Restart TS Server
+```
+
+## 相关文档
+
+- [开发环境设置](./setup.md)
+- [编码标准](../standards/coding-standards.md)
+- [测试规范](../standards/testing-standards.md)
+
+---
+
+**文档版本**: 1.0
+**最后更新**: 2026-01-07

+ 243 - 0
docs/guides/setup.md

@@ -0,0 +1,243 @@
+# 开发环境设置指南
+
+## 系统要求
+
+### 必需软件
+
+| 软件 | 最低版本 | 推荐版本 |
+|------|----------|----------|
+| Node.js | 20.x | 20.19.2 |
+| pnpm | 8.x | 10.18.3 |
+| PostgreSQL | 15+ | 17 |
+| Redis | 6+ | 7 |
+| Git | 2.x | 最新 |
+
+### 推荐工具
+
+- **IDE**: VS Code
+- **VS Code插件**:
+  - ES Lint
+  - Prettier
+  - TypeScript Vue Plugin (Volar)
+  - Vitest
+
+## 安装步骤
+
+### 1. 克隆项目
+
+```bash
+git clone <repository-url>
+cd 188-179-template-6
+```
+
+### 2. 安装 pnpm (如果未安装)
+
+```bash
+npm install -g pnpm@10.18.3
+```
+
+### 3. 安装项目依赖
+
+```bash
+pnpm install
+```
+
+这将安装所有工作空间的依赖,包括:
+- 核心共享包
+- 业务模块包
+- UI组件包
+- 小程序UI包
+- 应用包
+
+## 环境配置
+
+### PostgreSQL 配置
+
+#### 使用 Docker 运行 PostgreSQL
+
+```bash
+docker run -d \
+  --name postgres-17 \
+  -e POSTGRES_USER=postgres \
+  -e POSTGRES_PASSWORD=postgres \
+  -e POSTGRES_DB=postgres \
+  -p 5432:5432 \
+  postgres:17
+```
+
+#### 本地安装 PostgreSQL
+
+1. 安装 PostgreSQL 17
+2. 创建数据库用户和数据库
+3. 配置连接参数
+
+### Redis 配置
+
+#### 使用 Docker 运行 Redis
+
+```bash
+docker run -d \
+  --name redis-7 \
+  -p 6379:6379 \
+  redis:7
+```
+
+### 环境变量配置
+
+创建 `.env` 文件(参考 `.env.example`):
+
+```bash
+# 数据库配置
+DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
+
+# Redis配置
+REDIS_URL=redis://localhost:6379
+
+# JWT配置
+JWT_SECRET=your-jwt-secret-key-change-in-production
+
+# MinIO配置
+MINIO_ENDPOINT=localhost
+MINIO_PORT=9000
+MINIO_ACCESS_KEY=minioadmin
+MINIO_SECRET_KEY=minioadmin
+MINIO_BUCKET=d8dai
+
+# 应用配置
+NODE_ENV=development
+PORT=3000
+```
+
+## 构建共享包
+
+在启动应用前,需要先构建共享的UI包:
+
+```bash
+# 构建所有小程序UI包
+pnpm run build:mini-ui-packages
+
+# 或者分批构建
+pnpm run build:mini-ui-packages:common    # 通用组件
+pnpm run build:mini-ui-packages:yongren   # 员工端组件
+pnpm run build:mini-ui-packages:rencai    # 人才端组件
+```
+
+## 验证安装
+
+### 运行类型检查
+
+```bash
+# 检查所有包的类型
+pnpm typecheck
+
+# 检查特定包
+cd packages/core-module && pnpm typecheck
+```
+
+### 运行测试
+
+```bash
+# 运行所有测试
+pnpm test
+
+# 运行特定测试
+cd packages/core-module/auth-module && pnpm test
+```
+
+## 启动开发环境
+
+### 启动所有应用
+
+```bash
+pnpm dev
+```
+
+这将启动:
+- 管理后台 (端口 8080)
+- 员工小程序 H5
+- 人才小程序 H5
+
+### 单独启动应用
+
+```bash
+# 管理后台
+pnpm run dev:web
+
+# 员工小程序 H5
+pnpm run dev:mini
+
+# 人才小程序 H5
+pnpm run dev:mini-talent
+```
+
+## 开发工具配置
+
+### VS Code 设置
+
+创建 `.vscode/settings.json`:
+
+```json
+{
+  "typescript.tsdk": "node_modules/typescript/lib",
+  "typescript.enablePromptUseWorkspaceTsdk": true,
+  "editor.formatOnSave": true,
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  }
+}
+```
+
+### Git Hooks (可选)
+
+安装 husky 进行代码检查:
+
+```bash
+pnpm add -D husky lint-staged
+npx husky install
+```
+
+## 故障排查
+
+### 依赖安装失败
+
+```bash
+# 清理缓存重新安装
+rm -rf node_modules .pnpm-store
+pnpm install
+```
+
+### 类型检查错误
+
+```bash
+# 清理构建产物
+pnpm -r run clean
+
+# 重新构建
+pnpm build
+```
+
+### 数据库连接失败
+
+检查:
+1. PostgreSQL 是否运行
+2. 连接参数是否正确
+3. 防火墙设置
+
+### 端口冲突
+
+修改 `.env` 文件中的端口配置:
+
+```bash
+PORT=3001  # 更改为其他端口
+```
+
+## 下一步
+
+环境配置完成后,请参阅:
+- [本地开发指南](./local-development.md)
+- [编码标准](../standards/coding-standards.md)
+
+---
+
+**文档版本**: 1.0
+**最后更新**: 2026-01-07

+ 97 - 0
docs/index.md

@@ -0,0 +1,97 @@
+# 188-179 招聘系统 - 文档索引
+
+欢迎来到 188-179 招聘系统项目文档。本系统是一个大型企业级 Monorepo 招聘管理平台。
+
+## 快速导航
+
+### 🚀 快速开始
+- [项目概览](./README.md) - 项目介绍和技术栈
+- [开发环境设置](./guides/setup.md) - 环境配置和安装
+- [本地开发指南](./guides/local-development.md) - 本地开发工作流
+- [部署指南](./guides/deployment.md) - 生产环境部署
+
+### 🏗️ 架构文档
+- [Monorepo架构](./architecture/monorepo.md) - 工作空间和包管理
+- [模块组织结构](./architecture/module-structure.md) - 包分类和依赖关系
+- [数据模型](./architecture/data-models.md) - 数据库Schema设计
+- [API设计](./architecture/api-design.md) - RESTful API规范
+
+### 📋 开发规范
+- [编码标准](./standards/coding-standards.md) - 通用编码规范
+- [后端模块开发规范](./standards/backend-module-standards.md) - 后端模块包开发
+- [UI包开发规范](./standards/ui-package-standards.md) - React UI组件开发
+- [小程序UI包开发规范](./standards/mini-ui-package-standards.md) - Taro小程序组件开发
+- [测试规范](./standards/testing-standards.md) - 测试策略和最佳实践
+
+### 🧪 测试标准
+- [后端模块测试规范](./standards/backend-module-testing-standards.md) - 后端模块测试
+- [Web服务器测试规范](./standards/web-server-testing-standards.md) - Web服务器E2E测试
+- [Web UI测试规范](./standards/web-ui-testing-standards.md) - React组件测试
+
+## 项目概览
+
+### 技术栈
+
+| 类别 | 技术 |
+|------|------|
+| **前端** | React 19, Taro 3.x, Vite 7.x |
+| **后端** | Hono 4.x, NestJS (部分) |
+| **数据库** | PostgreSQL 17 |
+| **缓存** | Redis 7 |
+| **存储** | MinIO |
+| **包管理** | pnpm workspaces |
+| **测试** | Vitest, Playwright |
+
+### 包统计
+
+| 目录 | 包数量 | 描述 |
+|------|--------|------|
+| `packages/` | 61 | 核心共享包和业务模块 |
+| `allin-packages/` | 15 | AllIn业务模块 |
+| `mini-ui-packages/` | 18 | 小程序UI组件库 |
+| `web/` | 1 | 管理后台应用 |
+| `mini/` | 1 | 员工小程序 |
+| `mini-talent/` | 1 | 人才小程序 |
+
+## 开发快速命令
+
+```bash
+# 安装依赖
+pnpm install
+
+# 启动所有应用
+pnpm dev
+
+# 仅启动管理后台
+pnpm run dev:web
+
+# 构建所有包
+pnpm build
+
+# 运行测试
+pnpm test
+
+# 类型检查
+pnpm typecheck
+```
+
+## 文档更新记录
+
+| 日期 | 更新内容 |
+|------|----------|
+| 2026-01-07 | 创建新文档结构,更新项目架构 |
+| 2025-12-26 | 更新后端模块和测试规范 |
+| 2025-12-04 | 添加UI包测试规范 |
+
+## 获取帮助
+
+如需帮助或有疑问,请:
+1. 查阅相关文档
+2. 检查现有实现作为参考
+3. 联系项目维护者
+
+---
+
+**项目名称**: 188-179 招聘系统
+**最后更新**: 2026-01-07
+**文档版本**: 3.0

+ 872 - 0
docs/standards/backend-module-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/standards/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

+ 468 - 0
docs/standards/coding-standards.md

@@ -0,0 +1,468 @@
+# 编码标准
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 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严格模式**: 所有项目必须启用严格类型检查
+- **一致的缩进**: 使用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

+ 1236 - 0
docs/standards/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包的开发都应遵循本规范

+ 339 - 0
docs/standards/testing-standards.md

@@ -0,0 +1,339 @@
+# 测试策略
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 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 |
+
+## 概述
+
+本文档是D8D Starter项目的测试策略概述,定义了整体的测试架构和原则。详细的测试规范请参考各包的专用测试规范文档。
+
+### 测试架构概览
+
+项目采用分层测试架构,遵循测试金字塔模型:
+
+```
+           /\
+          /  \        E2E测试 (Playwright)
+         /____\       关键业务流程 100%
+        /      \
+       /        \     集成测试 (Vitest)
+      /          \    模块间协作 ≥60%
+     /____________\
+    /              \   单元测试 (Vitest/Jest)
+   /________________\  独立单元 ≥80%
+```
+
+### 包分类测试策略
+
+| 包类型 | 测试类型 | 主要框架 | 详细规范 |
+|--------|----------|----------|----------|
+| **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)
+- **范围**: 单个函数、类或组件
+- **目标**: 验证独立单元的正确性
+- **执行频率**: 每次代码变更
+- **执行速度**: 快(毫秒级)
+- **覆盖率目标**: ≥ 80%
+
+**适用包**:
+- 后端模块包的Service层、Schema验证
+- Web UI包的独立组件
+- 共享工具包的纯函数
+
+### 集成测试 (Integration Tests)
+- **范围**: 多个组件/服务协作
+- **目标**: 验证模块间集成和交互
+- **执行频率**: 每次API变更
+- **执行速度**: 中等(秒级)
+- **覆盖率目标**: ≥ 60%
+
+**适用包**:
+- Web Server包的API端点集成
+- 后端模块包的路由与数据库集成
+- Web UI包的组件与API集成
+
+### E2E测试 (End-to-End Tests)
+- **范围**: 完整用户流程
+- **目标**: 验证端到端业务流程
+- **执行频率**: 每日或每次重大变更
+- **执行速度**: 慢(分钟级)
+- **覆盖率目标**: 关键用户流程100%
+
+**适用包**:
+- Web应用的完整用户场景
+- Mini小程序的关键业务流程
+
+## 测试框架栈
+
+### Web应用测试
+- **Vitest**: 测试运行器
+- **Testing Library**: React组件测试
+- **Playwright**: E2E测试
+- **Happy DOM**: 轻量级DOM环境
+
+### 后端测试
+- **Vitest**: 测试运行器
+- **hono/testing**: Hono路由测试
+- **TypeORM**: 数据库测试
+- **PostgreSQL**: 测试数据库
+
+### Mini小程序测试
+- **Jest**: 测试运行器(与Web应用不同)
+- **@testing-library/react**: 组件测试
+
+## 测试数据管理
+
+### 测试数据策略
+```typescript
+// 测试数据工厂模式
+export function createTestUser(overrides = {}): User {
+  return {
+    id: 1,
+    username: 'testuser',
+    email: 'test@example.com',
+    createdAt: new Date(),
+    ...overrides
+  };
+}
+
+// 使用示例
+const adminUser = createTestUser({ role: 'admin' });
+const inactiveUser = createTestUser({ active: false });
+```
+
+### 数据库测试策略
+- **单元测试**: 使用内存数据库或完全mock
+- **集成测试**: 使用专用测试数据库,事务回滚
+- **E2E测试**: 使用接近生产环境的数据库
+
+### 数据清理策略
+1. **事务回滚** (推荐) - 测试后自动回滚
+2. **数据库清理** (每个测试后) - 清理测试数据
+3. **测试数据隔离** (使用唯一标识符) - 避免数据冲突
+
+## 测试执行流程
+
+### 本地开发测试
+
+#### 后端模块包
+```bash
+# 运行所有测试
+cd packages/user-module && pnpm test
+
+# 运行单元测试
+pnpm test:unit
+
+# 运行集成测试
+pnpm test:integration
+
+# 生成覆盖率报告
+pnpm test:coverage
+```
+
+#### Web Server包
+```bash
+# 运行集成测试
+cd packages/server && pnpm test
+
+# 生成覆盖率报告
+pnpm test:coverage
+```
+
+#### Web应用
+```bash
+# 运行所有测试
+cd web && pnpm test
+
+# 运行组件测试
+pnpm test:components
+
+# 运行集成测试
+pnpm test:integration
+
+# 运行E2E测试
+pnpm test:e2e:chromium
+
+# 生成覆盖率报告
+pnpm test:coverage
+```
+
+#### Mini UI包
+```bash
+# 进入UI包目录
+cd mini-ui-packages/rencai-dashboard-ui
+
+# 运行测试(使用Jest)
+pnpm test
+```
+
+### CI/CD流水线测试
+1. **代码推送** → 触发测试流水线
+2. **单元测试** → 快速反馈,必须通过
+3. **集成测试** → 验证模块集成,必须通过
+4. **E2E测试** → 验证完整流程,建议通过
+5. **覆盖率检查** → 满足最低要求
+6. **测试报告** → 生成详细报告
+
+## 覆盖率标准
+
+### 各层覆盖率要求
+| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
+|----------|----------|----------|--------------|
+| 单元测试 | 70% | 80% | 90% |
+| 集成测试 | 50% | 60% | 70% |
+| E2E测试 | 关键流程100% | 主要流程80% | - |
+
+### 关键模块定义
+- **认证授权模块**: 必须达到90%单元测试覆盖率
+- **数据库操作模块**: 必须达到85%单元测试覆盖率
+- **核心业务逻辑**: 必须达到80%集成测试覆盖率
+- **用户管理功能**: 必须100% E2E测试覆盖
+
+## 质量门禁
+
+### 测试通过标准
+- ✅ 所有单元测试通过
+- ✅ 所有集成测试通过
+- ✅ 关键E2E测试通过
+- ✅ 覆盖率满足最低要求
+- ✅ 无性能回归
+- ✅ 安全测试通过
+
+### 失败处理流程
+1. **测试失败** → 立即通知开发团队
+2. **分析根本原因** → 确定是测试问题还是代码问题
+3. **优先修复** → 阻塞性问题必须立即修复
+4. **重新测试** → 修复后重新运行测试
+5. **文档更新** → 更新测试策略和案例
+
+## 安全测试策略
+
+### 安全测试要求
+- **输入验证测试**: 所有API端点必须测试SQL注入、XSS等攻击
+- **认证测试**: 测试令牌验证、权限控制
+- **数据保护**: 测试敏感数据泄露风险
+- **错误处理**: 测试错误信息是否泄露敏感数据
+
+### 安全测试工具
+- **OWASP ZAP**: 自动化安全扫描
+- **npm audit**: 依赖漏洞检查
+- **自定义安全测试**: 针对业务逻辑的安全测试
+
+## 性能测试策略
+
+### 性能测试要求
+- **API响应时间**: < 100ms (p95)
+- **数据库查询性能**: < 50ms (p95)
+- **并发用户数**: 支持100+并发用户
+- **资源使用**: CPU < 70%, 内存 < 80%
+
+### 性能测试工具
+- **k6**: 负载测试
+- **autocannon**: API性能测试
+- **Playwright**: E2E性能监控
+
+## 监控和报告
+
+### 测试监控指标
+- **测试通过率**: > 95%
+- **测试执行时间**: < 10分钟(单元+集成)
+- **测试稳定性**: 无flaky tests
+- **覆盖率趋势**: 持续改进或保持
+
+### 测试报告要求
+- **HTML报告**: 详细的覆盖率报告
+- **JUnit报告**: CI/CD集成
+- **自定义报告**: 业务指标测试报告
+- **历史趋势**: 测试质量趋势分析
+
+## 调试技巧
+
+### 查看测试详情
+```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)** - 后端模块开发
+
+### 架构文档
+- **[技术栈](./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 | 共享测试基础设施 |
+
+## 更新日志
+
+| 日期 | 版本 | 描述 |
+|------|------|------|
+| 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目录结构 |
+| 2025-10-14 | 2.5 | 重构测试文件结构,统一到tests目录 |
+| 2025-09-20 | 2.4 | 更新版本与主架构文档一致 |
+| 2025-09-19 | 1.0 | 初始版本,基于现有测试基础设施 |
+
+---
+
+**文档状态**: 正式版
+**下次评审**: 2026-01-26

+ 1001 - 0
docs/standards/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/standards/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/standards/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应用