Kaynağa Gözat

feat: 移植平台管理模块到 @d8d/allin-platform-module

- 创建平台管理模块包结构
- 转换 Platform 实体:下划线命名 → 驼峰命名
- 实现 PlatformService 继承 GenericCrudService
- 添加平台名称唯一性检查业务逻辑
- 实现 6 个自定义 Hono 路由,保持原始 API 端点
- 转换验证系统:class-validator → Zod Schema
- 编写完整的 API 集成测试套件
- 通过 TypeScript 类型检查

作为基础依赖模块,为 company-module 等提供平台数据支持

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 ay önce
ebeveyn
işleme
c7298834d0

+ 80 - 0
allin-packages/platform-module/package.json

@@ -0,0 +1,80 @@
+{
+  "name": "@d8d/allin-platform-module",
+  "version": "1.0.0",
+  "description": "平台管理模块 - 提供平台信息的完整CRUD功能,作为基础依赖模块",
+  "type": "module",
+  "main": "src/index.ts",
+  "types": "src/index.ts",
+  "exports": {
+    ".": {
+      "types": "./src/index.ts",
+      "import": "./src/index.ts",
+      "require": "./src/index.ts"
+    },
+    "./services": {
+      "types": "./src/services/index.ts",
+      "import": "./src/services/index.ts",
+      "require": "./src/services/index.ts"
+    },
+    "./schemas": {
+      "types": "./src/schemas/index.ts",
+      "import": "./src/schemas/index.ts",
+      "require": "./src/schemas/index.ts"
+    },
+    "./routes": {
+      "types": "./src/routes/index.ts",
+      "import": "./src/routes/index.ts",
+      "require": "./src/routes/index.ts"
+    },
+    "./entities": {
+      "types": "./src/entities/index.ts",
+      "import": "./src/entities/index.ts",
+      "require": "./src/entities/index.ts"
+    }
+  },
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "build": "tsc",
+    "dev": "tsc --watch",
+    "test": "vitest run",
+    "test:watch": "vitest",
+    "test:integration": "vitest run tests/integration",
+    "test:coverage": "vitest run --coverage",
+    "lint": "eslint src --ext .ts,.tsx",
+    "typecheck": "tsc --noEmit"
+  },
+  "dependencies": {
+    "@d8d/shared-types": "workspace:*",
+    "@d8d/shared-utils": "workspace:*",
+    "@d8d/shared-crud": "workspace:*",
+    "@d8d/auth-module": "workspace:*",
+    "@d8d/user-module": "workspace:*",
+    "@d8d/file-module": "workspace:*",
+    "@hono/zod-openapi": "^1.0.2",
+    "typeorm": "^0.3.20",
+    "zod": "^4.1.12"
+  },
+  "devDependencies": {
+    "@types/node": "^22.10.2",
+    "typescript": "^5.8.3",
+    "vitest": "^3.2.4",
+    "@d8d/shared-test-util": "workspace:*",
+    "@typescript-eslint/eslint-plugin": "^8.18.1",
+    "@typescript-eslint/parser": "^8.18.1",
+    "eslint": "^9.17.0"
+  },
+  "peerDependencies": {
+    "hono": "^4.8.5"
+  },
+  "keywords": [
+    "platform",
+    "management",
+    "crud",
+    "api",
+    "base-module"
+  ],
+  "author": "D8D Team",
+  "license": "MIT"
+}

+ 1 - 0
allin-packages/platform-module/src/entities/index.ts

@@ -0,0 +1 @@
+export { Platform } from './platform.entity.js';

+ 76 - 0
allin-packages/platform-module/src/entities/platform.entity.ts

@@ -0,0 +1,76 @@
+import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
+
+@Entity('employer_platform')
+export class Platform {
+  @PrimaryGeneratedColumn({
+    name: 'platform_id',
+    type: 'int',
+    unsigned: true,
+    comment: '平台ID'
+  })
+  id!: number;
+
+  @Column({
+    name: 'platform_name',
+    type: 'varchar',
+    length: 100,
+    nullable: false,
+    comment: '平台名称'
+  })
+  @Index('idx_platform_name', { unique: true })
+  platformName!: string;
+
+  @Column({
+    name: 'contact_person',
+    type: 'varchar',
+    length: 50,
+    nullable: false,
+    default: '',
+    comment: '联系人'
+  })
+  contactPerson!: string;
+
+  @Column({
+    name: 'contact_phone',
+    type: 'varchar',
+    length: 20,
+    nullable: false,
+    default: '',
+    comment: '联系电话'
+  })
+  contactPhone!: string;
+
+  @Column({
+    name: 'contact_email',
+    type: 'varchar',
+    length: 100,
+    nullable: true,
+    comment: '联系邮箱'
+  })
+  contactEmail?: 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;
+}

+ 24 - 0
allin-packages/platform-module/src/index.ts

@@ -0,0 +1,24 @@
+// 导出实体
+export { Platform } from './entities/index.js';
+
+// 导出服务
+export { PlatformService } from './services/index.js';
+
+// 导出Schema和类型
+export {
+  PlatformSchema,
+  CreatePlatformSchema,
+  UpdatePlatformSchema,
+  DeletePlatformSchema,
+  PaginationQuerySchema,
+  SearchPlatformQuerySchema,
+  type Platform as PlatformType,
+  type CreatePlatformDto,
+  type UpdatePlatformDto,
+  type DeletePlatformDto,
+  type PaginationQuery,
+  type SearchPlatformQuery
+} from './schemas/index.js';
+
+// 导出路由
+export { platformRoutes, platformRoutesDefault, platformCustomRoutes, platformCrudRoutes } from './routes/index.js';

+ 3 - 0
allin-packages/platform-module/src/routes/index.ts

@@ -0,0 +1,3 @@
+export { platformRoutes, default as platformRoutesDefault } from './platform.routes.js';
+export { default as platformCustomRoutes } from './platform-custom.routes.js';
+export { platformCrudRoutes } from './platform-crud.routes.js';

+ 19 - 0
allin-packages/platform-module/src/routes/platform-crud.routes.ts

@@ -0,0 +1,19 @@
+import { createCrudRoutes } from '@d8d/shared-crud';
+import { authMiddleware } from '@d8d/auth-module';
+import { Platform } from '../entities/platform.entity';
+import { PlatformSchema, CreatePlatformSchema, UpdatePlatformSchema } from '../schemas/platform.schema';
+
+export const platformCrudRoutes = createCrudRoutes({
+  entity: Platform,
+  createSchema: CreatePlatformSchema,
+  updateSchema: UpdatePlatformSchema,
+  getSchema: PlatformSchema,
+  listSchema: PlatformSchema,
+  searchFields: ['platformName', 'contactPerson', 'contactPhone'],
+  middleware: [authMiddleware],
+  readOnly: true, // 设置为只读,因为创建、更新、删除操作通过自定义路由处理
+  userTracking: {
+    createdByField: 'createdBy',
+    updatedByField: 'updatedBy'
+  }
+});

+ 379 - 0
allin-packages/platform-module/src/routes/platform-custom.routes.ts

@@ -0,0 +1,379 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource, ErrorSchema, parseWithAwait } from '@d8d/shared-utils';
+import { authMiddleware } from '@d8d/auth-module';
+import { AuthContext } from '@d8d/shared-types';
+import { PlatformService } from '../services/platform.service';
+import {
+  PlatformSchema,
+  CreatePlatformSchema,
+  UpdatePlatformSchema,
+  DeletePlatformSchema,
+  PaginationQuerySchema,
+  SearchPlatformQuerySchema
+} from '../schemas/platform.schema';
+
+// 创建平台路由 - 返回布尔值
+const createPlatformRoute = createRoute({
+  method: 'post',
+  path: '/createPlatform',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: CreatePlatformSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '平台创建成功',
+      content: {
+        'application/json': { schema: PlatformSchema }
+      }
+    },
+    400: {
+      description: '参数错误或平台名称已存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '创建平台失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 删除平台路由 - 返回布尔值
+const deletePlatformRoute = createRoute({
+  method: 'post',
+  path: '/deletePlatform',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: DeletePlatformSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '平台删除成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({ description: '是否成功' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '删除平台失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 更新平台路由 - 返回布尔值
+const updatePlatformRoute = createRoute({
+  method: 'post',
+  path: '/updatePlatform',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: UpdatePlatformSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '平台更新成功',
+      content: {
+        'application/json': { schema: PlatformSchema }
+      }
+    },
+    400: {
+      description: '参数错误或平台名称已存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '平台不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '更新平台失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 获取所有平台路由 - 分页查询
+const getAllPlatformsRoute = createRoute({
+  method: 'get',
+  path: '/getAllPlatforms',
+  middleware: [authMiddleware],
+  request: {
+    query: PaginationQuerySchema
+  },
+  responses: {
+    200: {
+      description: '获取平台列表成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            data: z.array(PlatformSchema).openapi({ description: '平台列表' }),
+            total: z.number().int().openapi({ description: '总记录数' })
+          })
+        }
+      }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '获取平台列表失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 搜索平台路由 - 按名称模糊搜索
+const searchPlatformsRoute = createRoute({
+  method: 'get',
+  path: '/searchPlatforms',
+  middleware: [authMiddleware],
+  request: {
+    query: SearchPlatformQuerySchema
+  },
+  responses: {
+    200: {
+      description: '搜索平台成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            data: z.array(PlatformSchema).openapi({ description: '平台列表' }),
+            total: z.number().int().openapi({ description: '总记录数' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '搜索关键词不能为空',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '搜索平台失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 获取单个平台路由
+const getPlatformRoute = createRoute({
+  method: 'get',
+  path: '/getPlatform/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      id: z.coerce.number().int().positive().openapi({
+        param: { name: 'id', in: 'path' },
+        example: 1,
+        description: '平台ID'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '获取平台详情成功',
+      content: {
+        'application/json': { schema: PlatformSchema.nullable() }
+      }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '获取平台详情失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>()
+  // 创建平台
+  .openapi(createPlatformRoute, async (c) => {
+    try {
+      const data = c.req.valid('json');
+      const platformService = new PlatformService(AppDataSource);
+
+      const result = await platformService.create(data);
+
+      return c.json(await parseWithAwait(PlatformSchema, result), 200);
+    } catch (error) {
+      if (error instanceof z.ZodError) {
+        return c.json({
+          code: 400,
+          message: '参数错误',
+          errors: error.issues
+        }, 400);
+      }
+
+      // 处理平台名称重复错误
+      if (error instanceof Error && error.message.includes('平台名称已存在')) {
+        return c.json({
+          code: 400,
+          message: '平台名称已存在'
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '创建平台失败'
+      }, 500);
+    }
+  })
+  // 删除平台
+  .openapi(deletePlatformRoute, async (c) => {
+    try {
+      const { id } = c.req.valid('json');
+      const platformService = new PlatformService(AppDataSource);
+
+      const success = await platformService.delete(id);
+
+      return c.json({ success }, 200);
+    } catch (error) {
+      if (error instanceof z.ZodError) {
+        return c.json({
+          code: 400,
+          message: '参数错误',
+          errors: error.issues
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '删除平台失败'
+      }, 500);
+    }
+  })
+  // 更新平台
+  .openapi(updatePlatformRoute, async (c) => {
+    try {
+      const data = c.req.valid('json');
+      const platformService = new PlatformService(AppDataSource);
+
+      const result = await platformService.update(data.id, data);
+
+      if (!result) {
+        return c.json({ code: 404, message: '平台不存在' }, 404);
+      }
+
+      return c.json(await parseWithAwait(PlatformSchema, result), 200);
+    } catch (error) {
+      if (error instanceof z.ZodError) {
+        return c.json({
+          code: 400,
+          message: '参数错误',
+          errors: error.issues
+        }, 400);
+      }
+
+      // 处理平台名称重复错误
+      if (error instanceof Error && error.message.includes('平台名称已存在')) {
+        return c.json({
+          code: 400,
+          message: '平台名称已存在'
+        }, 400);
+      }
+
+      // 处理平台不存在错误
+      if (error instanceof Error && error.message.includes('平台不存在')) {
+        return c.json({
+          code: 404,
+          message: '平台不存在'
+        }, 404);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '更新平台失败'
+      }, 500);
+    }
+  })
+  // 获取所有平台
+  .openapi(getAllPlatformsRoute, async (c) => {
+    try {
+      const { skip, take } = c.req.valid('query');
+      const platformService = new PlatformService(AppDataSource);
+
+      const result = await platformService.findAll(skip, take);
+
+      return c.json(result, 200);
+    } catch (error) {
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '获取平台列表失败'
+      }, 500);
+    }
+  })
+  // 搜索平台
+  .openapi(searchPlatformsRoute, async (c) => {
+    try {
+      const { name, skip, take } = c.req.valid('query');
+      const platformService = new PlatformService(AppDataSource);
+
+      const result = await platformService.searchByName(name, skip, take);
+
+      return c.json(result, 200);
+    } catch (error) {
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '搜索平台失败'
+      }, 500);
+    }
+  })
+  // 获取单个平台
+  .openapi(getPlatformRoute, async (c) => {
+    try {
+      const { id } = c.req.valid('param');
+      const platformService = new PlatformService(AppDataSource);
+
+      const result = await platformService.findOne(id);
+
+      if (!result) {
+        return c.json(null, 200);
+      }
+
+      const validatedResult = await parseWithAwait(PlatformSchema, result);
+      return c.json(validatedResult, 200);
+    } catch (error) {
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '获取平台详情失败'
+      }, 500);
+    }
+  });
+
+export default app;

+ 12 - 0
allin-packages/platform-module/src/routes/platform.routes.ts

@@ -0,0 +1,12 @@
+import { OpenAPIHono } from '@hono/zod-openapi';
+import { AuthContext } from '@d8d/shared-types';
+import platformCustomRoutes from './platform-custom.routes';
+import { platformCrudRoutes } from './platform-crud.routes';
+
+// 创建路由实例 - 聚合自定义路由和CRUD路由
+const platformRoutes = new OpenAPIHono<AuthContext>()
+  .route('/', platformCustomRoutes)
+  .route('/', platformCrudRoutes);
+
+export { platformRoutes };
+export default platformRoutes;

+ 14 - 0
allin-packages/platform-module/src/schemas/index.ts

@@ -0,0 +1,14 @@
+export {
+  PlatformSchema,
+  CreatePlatformSchema,
+  UpdatePlatformSchema,
+  DeletePlatformSchema,
+  PaginationQuerySchema,
+  SearchPlatformQuerySchema,
+  type Platform,
+  type CreatePlatformDto,
+  type UpdatePlatformDto,
+  type DeletePlatformDto,
+  type PaginationQuery,
+  type SearchPlatformQuery
+} from './platform.schema.js';

+ 117 - 0
allin-packages/platform-module/src/schemas/platform.schema.ts

@@ -0,0 +1,117 @@
+import { z } from '@hono/zod-openapi';
+
+// 平台实体Schema
+export const PlatformSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '平台ID',
+    example: 1
+  }),
+  platformName: z.string().max(100).openapi({
+    description: '平台名称',
+    example: '微信平台'
+  }),
+  contactPerson: z.string().max(50).openapi({
+    description: '联系人',
+    example: '张三'
+  }),
+  contactPhone: z.string().max(20).openapi({
+    description: '联系电话',
+    example: '13800138000'
+  }),
+  contactEmail: z.string().email().max(100).nullable().optional().openapi({
+    description: '联系邮箱',
+    example: 'zhangsan@example.com'
+  }),
+  status: z.number().int().min(0).max(1).default(1).openapi({
+    description: '状态:1-正常,0-禁用',
+    example: 1
+  }),
+  createTime: z.coerce.date().openapi({
+    description: '创建时间',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  updateTime: z.coerce.date().openapi({
+    description: '更新时间',
+    example: '2024-01-01T00:00:00Z'
+  })
+});
+
+// 创建平台DTO
+export const CreatePlatformSchema = z.object({
+  platformName: z.string().min(1).max(100).openapi({
+    description: '平台名称',
+    example: '微信平台'
+  }),
+  contactPerson: z.string().max(50).optional().openapi({
+    description: '联系人',
+    example: '张三'
+  }),
+  contactPhone: z.string().max(20).optional().openapi({
+    description: '联系电话',
+    example: '13800138000'
+  }),
+  contactEmail: z.string().email().max(100).optional().openapi({
+    description: '联系邮箱',
+    example: 'zhangsan@example.com'
+  })
+});
+
+// 更新平台DTO
+export const UpdatePlatformSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '平台ID',
+    example: 1
+  }),
+  platformName: z.string().min(1).max(100).optional().openapi({
+    description: '平台名称',
+    example: '微信平台'
+  }),
+  contactPerson: z.string().max(50).optional().openapi({
+    description: '联系人',
+    example: '张三'
+  }),
+  contactPhone: z.string().max(20).optional().openapi({
+    description: '联系电话',
+    example: '13800138000'
+  }),
+  contactEmail: z.string().email().max(100).optional().openapi({
+    description: '联系邮箱',
+    example: 'zhangsan@example.com'
+  })
+});
+
+// 删除平台DTO
+export const DeletePlatformSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '平台ID',
+    example: 1
+  })
+});
+
+// 分页查询参数Schema
+export const PaginationQuerySchema = z.object({
+  skip: z.coerce.number().int().min(0).default(0).optional().openapi({
+    description: '跳过记录数',
+    example: 0
+  }),
+  take: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
+    description: '获取记录数',
+    example: 10
+  })
+});
+
+// 搜索平台参数Schema
+export const SearchPlatformQuerySchema = PaginationQuerySchema.extend({
+  name: z.string().min(1).openapi({
+    description: '搜索关键词',
+    example: '微信'
+  })
+});
+
+// 类型定义
+export type Platform = z.infer<typeof PlatformSchema>;
+export type CreatePlatformDto = z.infer<typeof CreatePlatformSchema>;
+export type UpdatePlatformDto = z.infer<typeof UpdatePlatformSchema>;
+export type DeletePlatformDto = z.infer<typeof DeletePlatformSchema>;
+export type PaginationQuery = z.infer<typeof PaginationQuerySchema>;
+export type SearchPlatformQuery = z.infer<typeof SearchPlatformQuerySchema>;

+ 1 - 0
allin-packages/platform-module/src/services/index.ts

@@ -0,0 +1 @@
+export { PlatformService } from './platform.service.js';

+ 110 - 0
allin-packages/platform-module/src/services/platform.service.ts

@@ -0,0 +1,110 @@
+import { GenericCrudService } from '@d8d/shared-crud';
+import { DataSource, Repository, Like, Not } from 'typeorm';
+import { Platform } from '../entities/platform.entity';
+
+export class PlatformService extends GenericCrudService<Platform> {
+  constructor(dataSource: DataSource) {
+    super(dataSource, Platform);
+  }
+
+  /**
+   * 创建平台 - 覆盖父类方法,添加名称唯一性检查
+   */
+  async create(data: Partial<Platform>, userId?: string | number): Promise<Platform> {
+    // 检查平台名称是否已存在
+    if (data.platformName) {
+      const existingPlatform = await this.repository.findOne({
+        where: { platformName: data.platformName }
+      });
+      if (existingPlatform) {
+        throw new Error('平台名称已存在');
+      }
+    }
+
+    // 设置默认值
+    const platformData = {
+      contactPerson: '',
+      contactPhone: '',
+      contactEmail: '',
+      ...data,
+      status: 1,
+      createTime: new Date(),
+      updateTime: new Date()
+    };
+
+    return super.create(platformData, userId);
+  }
+
+  /**
+   * 更新平台 - 覆盖父类方法,添加存在性和名称重复检查
+   */
+  async update(id: number, data: Partial<Platform>, userId?: string | number): Promise<Platform | null> {
+    // 检查平台是否存在
+    const platform = await this.repository.findOne({ where: { id } });
+    if (!platform) {
+      throw new Error('平台不存在');
+    }
+
+    // 检查平台名称是否与其他平台重复
+    if (data.platformName && data.platformName !== platform.platformName) {
+      const existingPlatform = await this.repository.findOne({
+        where: { platformName: data.platformName, id: Not(id) }
+      });
+      if (existingPlatform) {
+        throw new Error('平台名称已存在');
+      }
+    }
+
+    // 设置更新时间
+    const updateData = {
+      ...data,
+      updateTime: new Date()
+    };
+
+    // 现在可以使用父类的update方法,因为主键字段名已改为'id'
+    return super.update(id, updateData, userId);
+  }
+
+  /**
+   * 删除平台 - 覆盖父类方法,改为软删除(设置status为0)
+   */
+  async delete(id: number, userId?: string | number): Promise<boolean> {
+    // 改为软删除:设置status为0
+    const result = await this.repository.update({ id }, { status: 0 });
+    return result.affected === 1;
+  }
+
+  /**
+   * 获取所有平台(分页) - 自定义方法,返回源服务的格式
+   */
+  async findAll(skip?: number, take?: number): Promise<{ data: Platform[], total: number }> {
+    const [data, total] = await this.repository.findAndCount({
+      skip: skip ?? 0,
+      take: take ?? 10,
+      order: { id: 'DESC' }
+    });
+    return { data, total };
+  }
+
+  /**
+   * 按名称搜索平台 - 自定义方法
+   */
+  async searchByName(name: string, skip?: number, take?: number): Promise<{ data: Platform[], total: number }> {
+    const [data, total] = await this.repository.findAndCount({
+      where: {
+        platformName: Like(`%${name}%`)
+      },
+      skip: skip ?? 0,
+      take: take ?? 10,
+      order: { id: 'DESC' }
+    });
+    return { data, total };
+  }
+
+  /**
+   * 获取单个平台 - 自定义方法
+   */
+  async findOne(id: number): Promise<Platform | null> {
+    return this.repository.findOne({ where: { id } });
+  }
+}

+ 479 - 0
allin-packages/platform-module/tests/integration/platform.integration.test.ts

@@ -0,0 +1,479 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { testClient } from 'hono/testing';
+import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
+import { JWTUtil } from '@d8d/shared-utils';
+import { UserEntity, Role } from '@d8d/user-module';
+import { File } from '@d8d/file-module';
+import platformRoutes from '../../src/routes/platform.routes';
+import { Platform } from '../../src/entities/platform.entity';
+
+// 设置集成测试钩子
+setupIntegrationDatabaseHooksWithEntities([UserEntity, File, Role, Platform])
+
+describe('平台管理API集成测试', () => {
+  let client: ReturnType<typeof testClient<typeof platformRoutes>>;
+  let testToken: string;
+  let testUser: UserEntity;
+
+  beforeEach(async () => {
+    // 创建测试客户端
+    client = testClient(platformRoutes);
+
+    // 获取数据源
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+
+    // 创建测试用户
+    const userRepository = dataSource.getRepository(UserEntity);
+    testUser = userRepository.create({
+      username: `test_user_${Date.now()}`,
+      password: 'test_password',
+      nickname: '测试用户',
+      registrationSource: 'web'
+    });
+    await userRepository.save(testUser);
+
+    // 生成测试用户的token
+    testToken = JWTUtil.generateToken({
+      id: testUser.id,
+      username: testUser.username,
+      roles: [{name:'user'}]
+    });
+  });
+
+  describe('POST /platform/createPlatform', () => {
+    it('应该成功创建平台', async () => {
+      const createData = {
+        platformName: '微信平台',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        contactEmail: 'zhangsan@example.com'
+      };
+
+      const response = await client.createPlatform.$post({
+        json: createData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      console.debug('创建平台响应状态:', response.status);
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.platformName).toBe(createData.platformName);
+        expect(data.contactPerson).toBe(createData.contactPerson);
+        expect(data.contactPhone).toBe(createData.contactPhone);
+      }
+    });
+
+    it('应该验证平台名称重复', async () => {
+      // 先创建一个平台
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+      const existingPlatform = platformRepository.create({
+        platformName: '微信平台',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        status: 1
+      });
+      await platformRepository.save(existingPlatform);
+
+      // 尝试创建同名平台
+      const createData = {
+        platformName: '微信平台', // 重复的名称
+        contactPerson: '李四',
+        contactPhone: '13900139000'
+      };
+
+      const response = await client.createPlatform.$post({
+        json: createData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该验证必填字段', async () => {
+      const createData = {
+        platformName: '', // 空字符串,应该验证失败
+        contactPerson: '张三',
+        contactPhone: '13800138000'
+      };
+
+      const response = await client.createPlatform.$post({
+        json: createData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+  });
+
+  describe('POST /platform/deletePlatform', () => {
+    it('应该成功删除平台(软删除)', async () => {
+      // 先创建一个平台
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+      const platform = platformRepository.create({
+        platformName: '测试删除平台',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        status: 1
+      });
+      await platformRepository.save(platform);
+
+      const deleteData = {
+        id: platform.id
+      };
+
+      const response = await client.deletePlatform.$post({
+        json: deleteData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.success).toBe(true);
+
+        // 验证平台状态已改为0(软删除)
+        const deletedPlatform = await platformRepository.findOne({ where: { id: platform.id } });
+        expect(deletedPlatform?.status).toBe(0);
+      }
+    });
+
+    it('应该处理不存在的平台ID', async () => {
+      const deleteData = {
+        id: 99999 // 不存在的ID
+      };
+
+      const response = await client.deletePlatform.$post({
+        json: deleteData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200); // 删除不存在的平台也返回成功(软删除逻辑)
+    });
+  });
+
+  describe('POST /platform/updatePlatform', () => {
+    it('应该成功更新平台', async () => {
+      // 先创建一个平台
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+      const platform = platformRepository.create({
+        platformName: '原始平台名称',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        status: 1
+      });
+      await platformRepository.save(platform);
+
+      const updateData = {
+        id: platform.id,
+        platformName: '更新后的平台名称',
+        contactPerson: '李四',
+        contactPhone: '13900139000'
+      };
+
+      const response = await client.updatePlatform.$post({
+        json: updateData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.platformName).toBe(updateData.platformName);
+        expect(data.contactPerson).toBe(updateData.contactPerson);
+      }
+    });
+
+    it('应该验证平台名称重复(更新时)', async () => {
+      // 创建两个平台
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+
+      const platform1 = platformRepository.create({
+        platformName: '平台A',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        status: 1
+      });
+      await platformRepository.save(platform1);
+
+      const platform2 = platformRepository.create({
+        platformName: '平台B',
+        contactPerson: '李四',
+        contactPhone: '13900139000',
+        status: 1
+      });
+      await platformRepository.save(platform2);
+
+      // 尝试将平台2的名称改为平台1的名称
+      const updateData = {
+        id: platform2.id,
+        platformName: '平台A' // 重复的名称
+      };
+
+      const response = await client.updatePlatform.$post({
+        json: updateData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该处理不存在的平台', async () => {
+      const updateData = {
+        id: 99999, // 不存在的ID
+        platformName: '新名称'
+      };
+
+      const response = await client.updatePlatform.$post({
+        json: updateData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('GET /platform/getAllPlatforms', () => {
+    it('应该成功获取平台列表(分页)', async () => {
+      // 创建一些测试数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+
+      for (let i = 1; i <= 5; i++) {
+        const platform = platformRepository.create({
+          platformName: `平台${i}`,
+          contactPerson: `联系人${i}`,
+          contactPhone: `1380013800${i}`,
+          status: 1
+        });
+        await platformRepository.save(platform);
+      }
+
+      const response = await client.getAllPlatforms.$get({
+        query: {
+          skip: 0,
+          take: 10
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(5);
+        expect(data.total).toBe(5);
+        expect(data.data[0].platformName).toBe('平台5'); // 按ID降序排列
+      }
+    });
+
+    it('应该处理分页参数', async () => {
+      // 创建更多测试数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+
+      for (let i = 1; i <= 15; i++) {
+        const platform = platformRepository.create({
+          platformName: `分页平台${i}`,
+          contactPerson: `联系人${i}`,
+          contactPhone: `1380013800${i}`,
+          status: 1
+        });
+        await platformRepository.save(platform);
+      }
+
+      const response = await client.getAllPlatforms.$get({
+        query: {
+          skip: 5,
+          take: 5
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(5);
+        expect(data.total).toBe(15);
+      }
+    });
+  });
+
+  describe('GET /platform/searchPlatforms', () => {
+    it('应该成功按名称搜索平台', async () => {
+      // 创建测试数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+
+      const platform1 = platformRepository.create({
+        platformName: '微信平台',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        status: 1
+      });
+      await platformRepository.save(platform1);
+
+      const platform2 = platformRepository.create({
+        platformName: '支付宝平台',
+        contactPerson: '李四',
+        contactPhone: '13900139000',
+        status: 1
+      });
+      await platformRepository.save(platform2);
+
+      const response = await client.searchPlatforms.$get({
+        query: {
+          name: '微信',
+          skip: 0,
+          take: 10
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(1);
+        expect(data.data[0].platformName).toBe('微信平台');
+      }
+    });
+
+    it('应该验证搜索关键词不能为空', async () => {
+      const response = await client.searchPlatforms.$get({
+        query: {
+          name: '', // 空关键词
+          skip: 0,
+          take: 10
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+  });
+
+  describe('GET /platform/getPlatform/{id}', () => {
+    it('应该成功获取单个平台详情', async () => {
+      // 先创建一个平台
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const platformRepository = dataSource.getRepository(Platform);
+      const platform = platformRepository.create({
+        platformName: '测试平台详情',
+        contactPerson: '张三',
+        contactPhone: '13800138000',
+        contactEmail: 'zhangsan@example.com',
+        status: 1
+      });
+      await platformRepository.save(platform);
+
+      const response = await client.getPlatform[':id'].$get({
+        param: {
+          id: platform.id
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data?.platformName).toBe('测试平台详情');
+        expect(data?.contactEmail).toBe('zhangsan@example.com');
+      }
+    });
+
+    it('应该处理不存在的平台ID', async () => {
+      const response = await client.getPlatform[':id'].$get({
+        param: {
+          id: 99999 // 不存在的ID
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200); // 返回200,但数据为null
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data).toBeNull();
+      }
+    });
+  });
+
+  describe('认证测试', () => {
+    it('应该验证所有端点需要认证', async () => {
+      // 测试没有token的情况
+      const response = await client.createPlatform.$post({
+        json: {
+          platformName: '测试平台'
+        }
+      });
+
+      expect(response.status).toBe(401);
+    });
+
+    it('应该验证无效token', async () => {
+      const response = await client.createPlatform.$post({
+        json: {
+          platformName: '测试平台'
+        }
+      }, {
+        headers: {
+          'Authorization': 'Bearer invalid_token'
+        }
+      });
+
+      expect(response.status).toBe(401);
+    });
+  });
+});

+ 16 - 0
allin-packages/platform-module/tsconfig.json

@@ -0,0 +1,16 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "composite": true,
+    "rootDir": ".",
+    "outDir": "dist"
+  },
+  "include": [
+    "src/**/*",
+    "tests/**/*"
+  ],
+  "exclude": [
+    "node_modules",
+    "dist"
+  ]
+}

+ 21 - 0
allin-packages/platform-module/vitest.config.ts

@@ -0,0 +1,21 @@
+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: [
+        'tests/**',
+        '**/*.d.ts',
+        '**/*.config.*',
+        '**/dist/**'
+      ]
+    },
+    // 关闭并行测试以避免数据库连接冲突
+    fileParallelism: false
+  }
+});

+ 39 - 1
docs/stories/007.006.transplant-platform-management-module.story.md

@@ -1,7 +1,7 @@
 # Story 007.006: 移植平台管理模块(platform → @d8d/allin-platform-module)
 
 ## Status
-Draft
+Ready for Development
 
 ## Story
 **As a** 开发者,
@@ -322,12 +322,50 @@ Draft
 *此部分由开发代理在实现过程中填写*
 
 ### Agent Model Used
+James (Developer Agent)
 
 ### Debug Log References
+- 类型检查错误修复:Platform 类型重复定义、relationFields 配置错误、测试数据验证、路由路径引用
+- 参考 channel-module 实现模式,确保架构一致性
 
 ### Completion Notes List
+1. ✅ 成功创建 `allin-packages/platform-module` 目录结构
+2. ✅ 完成实体转换:`Platform` 实体从下划线命名转换为驼峰命名,添加详细 TypeORM 配置
+3. ✅ 完成服务层转换:继承 `GenericCrudService<Platform>`,覆盖 create、update、delete 方法,添加平台名称唯一性检查
+4. ✅ 完成路由层转换:实现 6 个自定义 Hono 路由,保持原始 API 端点路径和功能
+5. ✅ 完成验证系统转换:从 class-validator DTO 转换为 Zod Schema,添加详细验证规则
+6. ✅ 配置 package.json:作为基础依赖包,添加必要的工作区依赖
+7. ✅ 编写 API 集成测试:覆盖所有 6 个端点,包含认证、验证、业务逻辑和错误处理测试
+8. ✅ 通过类型检查:修复所有 TypeScript 类型错误
+9. ⏳ 验证作为依赖包被其他模块引用的能力(待验证)
 
 ### File List
+**创建的文件:**
+- `allin-packages/platform-module/package.json` - 包配置
+- `allin-packages/platform-module/tsconfig.json` - TypeScript 配置
+- `allin-packages/platform-module/vitest.config.ts` - 测试配置
+- `allin-packages/platform-module/src/entities/platform.entity.ts` - 平台实体
+- `allin-packages/platform-module/src/entities/index.ts` - 实体导出
+- `allin-packages/platform-module/src/services/platform.service.ts` - 平台服务
+- `allin-packages/platform-module/src/services/index.ts` - 服务导出
+- `allin-packages/platform-module/src/schemas/platform.schema.ts` - Zod Schema 定义
+- `allin-packages/platform-module/src/schemas/index.ts` - Schema 导出
+- `allin-packages/platform-module/src/routes/platform-custom.routes.ts` - 自定义路由
+- `allin-packages/platform-module/src/routes/platform-crud.routes.ts` - CRUD 路由
+- `allin-packages/platform-module/src/routes/platform.routes.ts` - 主路由
+- `allin-packages/platform-module/src/routes/index.ts` - 路由导出
+- `allin-packages/platform-module/src/index.ts` - 包主入口
+- `allin-packages/platform-module/tests/integration/platform.integration.test.ts` - 集成测试
+
+**修改的文件:**
+- `docs/stories/007.006.transplant-platform-management-module.story.md` - 更新状态和开发记录
+- `pnpm-lock.yaml` - 依赖更新(通过 pnpm install)
+
+### Change Log
+| Date | Version | Description | Author |
+|------|---------|-------------|--------|
+| 2025-12-02 | 1.0 | 初始故事创建 | Bob (Scrum Master) |
+| 2025-12-02 | 1.1 | 完成平台管理模块移植实现 | James (Developer) |
 
 ## QA Results
 Results from QA Agent QA review of the completed story implementation

+ 55 - 0
pnpm-lock.yaml

@@ -67,6 +67,61 @@ importers:
         specifier: ^3.2.4
         version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.1)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.64.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
 
+  allin-packages/platform-module:
+    dependencies:
+      '@d8d/auth-module':
+        specifier: workspace:*
+        version: link:../../packages/auth-module
+      '@d8d/file-module':
+        specifier: workspace:*
+        version: link:../../packages/file-module
+      '@d8d/shared-crud':
+        specifier: workspace:*
+        version: link:../../packages/shared-crud
+      '@d8d/shared-types':
+        specifier: workspace:*
+        version: link:../../packages/shared-types
+      '@d8d/shared-utils':
+        specifier: workspace:*
+        version: link:../../packages/shared-utils
+      '@d8d/user-module':
+        specifier: workspace:*
+        version: link:../../packages/user-module
+      '@hono/zod-openapi':
+        specifier: ^1.0.2
+        version: 1.0.2(hono@4.8.5)(zod@4.1.12)
+      hono:
+        specifier: ^4.8.5
+        version: 4.8.5
+      typeorm:
+        specifier: ^0.3.20
+        version: 0.3.27(ioredis@5.8.2)(pg@8.16.3)(redis@4.7.1)(reflect-metadata@0.2.2)
+      zod:
+        specifier: ^4.1.12
+        version: 4.1.12
+    devDependencies:
+      '@d8d/shared-test-util':
+        specifier: workspace:*
+        version: link:../../packages/shared-test-util
+      '@types/node':
+        specifier: ^22.10.2
+        version: 22.19.1
+      '@typescript-eslint/eslint-plugin':
+        specifier: ^8.18.1
+        version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.8.3)
+      '@typescript-eslint/parser':
+        specifier: ^8.18.1
+        version: 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.8.3)
+      eslint:
+        specifier: ^9.17.0
+        version: 9.38.0(jiti@2.6.1)
+      typescript:
+        specifier: ^5.8.3
+        version: 5.8.3
+      vitest:
+        specifier: ^3.2.4
+        version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.1)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.64.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+
   mini:
     dependencies:
       '@babel/runtime':