Pārlūkot izejas kodu

📝 docs(rules): add development process documentation

- add 11-admin-frontend.md: management backend frontend page development process specification
- add 11-custom-crud.md: custom complex CRUD development process specification
- add 11-home-frontend.md: home frontend page development process specification
- add 11-standard-crud.md: standard general CRUD development process specification
yourname 4 mēneši atpakaļ
vecāks
revīzija
926c263bc9

+ 444 - 0
.roo/rules/11-admin-frontend.md

@@ -0,0 +1,444 @@
+# 管理后台前端页面开发流程规范
+
+## 适用场景
+
+管理后台页面开发,包括列表页、详情页、表单页等后台功能页面的实现流程。
+
+## 开发流程
+
+### 1. **创建页面组件**
+   - 位置: `src/client/admin/pages/[EntityName]List.tsx` 和 `src/client/admin/pages/[EntityName]Detail.tsx`
+   - 列表页组件示例:
+     ```tsx
+     import React, { useEffect, useState } from 'react';
+     import { Table, Button, Space, Tag, message } from 'antd';
+     import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
+     import { useNavigate } from 'react-router-dom';
+     import type { InferResponseType } from 'hono/client';
+     import { yourEntityClient } from '@/client/api';
+
+     // 类型定义
+     type EntityItem = InferResponseType<typeof yourEntityClient[':id']['$get'], 200>;
+     type EntityListResponse = InferResponseType<typeof yourEntityClient.$get, 200>;
+
+     const YourEntityList: React.FC = () => {
+       const navigate = useNavigate();
+       const [data, setData] = useState<EntityItem[]>([]);
+       const [loading, setLoading] = useState<boolean>(true);
+       const [pagination, setPagination] = useState({
+         current: 1,
+         pageSize: 10,
+         total: 0
+       });
+
+       // 获取数据列表
+       const fetchData = async () => {
+         try {
+           setLoading(true);
+           const res = await yourEntityClient.$get({
+             query: {
+               page: pagination.current,
+               pageSize: pagination.pageSize
+             }
+           });
+           
+           if (!res.ok) throw new Error('获取数据失败');
+           
+           const result: EntityListResponse = await res.json();
+           setData(result.data);
+           setPagination({
+             ...pagination,
+             total: result.pagination.total
+           });
+         } catch (error) {
+           message.error(error instanceof Error ? error.message : '获取数据失败');
+         } finally {
+           setLoading(false);
+         }
+       };
+
+       useEffect(() => {
+         fetchData();
+       }, [pagination.current, pagination.pageSize]);
+
+       // 分页变化处理
+       const handleTableChange = (pagination: any) => {
+         setPagination({
+           ...pagination,
+           current: pagination.current,
+           pageSize: pagination.pageSize
+         });
+       };
+
+       // 操作按钮处理
+       const handleAdd = () => {
+         navigate('/your-entities/new');
+       };
+
+       const handleEdit = (id: number) => {
+         navigate(`/your-entities/${id}`);
+       };
+
+       const handleDelete = async (id: number) => {
+         try {
+           const res = await yourEntityClient[':id'].$delete({
+             param: { id }
+           });
+           
+           if (!res.ok) throw new Error('删除失败');
+           
+           message.success('删除成功');
+           fetchData();
+         } catch (error) {
+           message.error(error instanceof Error ? error.message : '删除失败');
+         }
+       };
+
+       // 表格列定义
+       const columns = [
+         {
+           title: 'ID',
+           dataIndex: 'id',
+           key: 'id',
+           width: 80
+         },
+         {
+           title: '名称',
+           dataIndex: 'name',
+           key: 'name'
+         },
+         {
+           title: '状态',
+           dataIndex: 'status',
+           key: 'status',
+           render: (status: number) => (
+             <Tag color={status === 1 ? 'green' : 'red'}>
+               {status === 1 ? '启用' : '禁用'}
+             </Tag>
+           )
+         },
+         {
+           title: '操作',
+           key: 'action',
+           render: (_: any, record: EntityItem) => (
+             <Space size="middle">
+               <Button 
+                 type="text" 
+                 icon={<EditOutlined />} 
+                 onClick={() => handleEdit(record.id)}
+               >
+                 编辑
+               </Button>
+               <Button 
+                 type="text" 
+                 danger 
+                 icon={<DeleteOutlined />} 
+                 onClick={() => handleDelete(record.id)}
+               >
+                 删除
+               </Button>
+             </Space>
+           )
+         }
+       ];
+
+       return (
+         <div className="page-container">
+           <div className="page-header">
+             <h2>实体管理</h2>
+             <Button 
+               type="primary" 
+               icon={<PlusOutlined />} 
+               onClick={handleAdd}
+             >
+               添加实体
+             </Button>
+           </div>
+           
+           <Table
+             columns={columns}
+             dataSource={data.map(item => ({ ...item, key: item.id }))}
+             loading={loading}
+             pagination={{
+               current: pagination.current,
+               pageSize: pagination.pageSize,
+               total: pagination.total,
+               showSizeChanger: true,
+               showQuickJumper: true,
+               showTotal: (total) => `共 ${total} 条记录`
+             }}
+             onChange={handleTableChange}
+             bordered
+           />
+         </div>
+       );
+     };
+
+     export default YourEntityList;
+     ```
+
+### 2. **注册路由配置**
+   - 位置: `src/client/admin/routes.tsx`
+   - 添加路由配置示例:
+     ```typescript
+     import YourEntityList from './pages/YourEntityList';
+     import YourEntityDetail from './pages/YourEntityDetail';
+     
+     export const routes = [
+       // ...其他路由
+       {
+         path: '/your-entities',
+         element: <YourEntityList />
+       },
+       {
+         path: '/your-entities/:id',
+         element: <YourEntityDetail />
+       },
+       {
+         path: '/your-entities/new',
+         element: <YourEntityDetail isNew={true} />
+       }
+     ];
+     ```
+
+### 3. **添加菜单配置**
+   - 位置: `src/client/admin/menu.tsx`
+   - 添加菜单配置示例:
+     ```typescript
+     import { TableOutlined } from '@ant-design/icons';
+     
+     export const menuItems = [
+       // ...其他菜单项
+       {
+         key: 'your-entities',
+         icon: <TableOutlined />,
+         label: '实体管理',
+         path: '/your-entities'
+       }
+     ];
+     ```
+
+### 4. **创建表单组件**
+   - 位置: `src/client/admin/components/[EntityName]Form.tsx`
+   - 表单组件示例:
+     ```tsx
+     import React from 'react';
+     import { Form, Input, Select, message } from 'antd';
+     import type { CreateRequest, UpdateRequest } from '@/client/api/your-entity';
+
+     interface YourEntityFormProps {
+       initialValues?: Partial<CreateRequest>;
+       onFinish: (values: CreateRequest | UpdateRequest) => Promise<void>;
+       loading: boolean;
+     }
+
+     const { Option } = Select;
+
+     const YourEntityForm: React.FC<YourEntityFormProps> = ({
+       initialValues,
+       onFinish,
+       loading
+     }) => {
+       const [form] = Form.useForm();
+
+       // 初始化表单值
+       React.useEffect(() => {
+         if (initialValues) {
+           form.setFieldsValue(initialValues);
+         } else {
+           form.resetFields();
+         }
+       }, [form, initialValues]);
+
+       // 表单提交处理
+       const handleSubmit = async () => {
+         try {
+           const values = await form.validateFields();
+           await onFinish(values);
+         } catch (error) {
+           message.error('表单验证失败,请检查输入内容');
+         }
+       };
+
+       return (
+         <Form
+           form={form}
+           layout="vertical"
+           onFinish={handleSubmit}
+           initialValues={initialValues}
+         >
+           <Form.Item
+             name="name"
+             label="名称"
+             rules={[
+               { required: true, message: '请输入名称' },
+               { max: 20, message: '名称不能超过20个字符' }
+             ]}
+           >
+             <Input placeholder="请输入名称" />
+           </Form.Item>
+
+           <Form.Item
+             name="status"
+             label="状态"
+             rules={[{ required: true, message: '请选择状态' }]}
+           >
+             <Select placeholder="请选择状态">
+               <Option value={1}>启用</Option>
+               <Option value={0}>禁用</Option>
+             </Select>
+           </Form.Item>
+
+           <Form.Item>
+             <button 
+               type="submit" 
+               className="ant-btn ant-btn-primary"
+               disabled={loading}
+             >
+               {loading ? '提交中...' : '提交'}
+             </button>
+           </Form.Item>
+         </Form>
+       );
+     };
+
+     export default YourEntityForm;
+     ```
+
+### 5. **实现详情页**
+   - 位置: `src/client/admin/pages/[EntityName]Detail.tsx`
+   - 详情页组件示例:
+     ```tsx
+     import React, { useEffect, useState } from 'react';
+     import { useParams, useNavigate } from 'react-router-dom';
+     import { Card, Spin, message } from 'antd';
+     import type { InferResponseType, InferRequestType } from 'hono/client';
+     import { yourEntityClient } from '@/client/api';
+     import YourEntityForm from '../components/YourEntityForm';
+
+     // 类型定义
+     type EntityDetail = InferResponseType<typeof yourEntityClient[':id']['$get'], 200>;
+     type CreateRequest = InferRequestType<typeof yourEntityClient.$post>['json'];
+     type UpdateRequest = InferRequestType<typeof yourEntityClient[':id']['$put']>['json'];
+
+     interface YourEntityDetailProps {
+       isNew?: boolean;
+     }
+
+     const YourEntityDetail: React.FC<YourEntityDetailProps> = ({ isNew = false }) => {
+       const { id } = useParams<{ id: string }>();
+       const navigate = useNavigate();
+       const [loading, setLoading] = useState<boolean>(!isNew);
+       const [initialValues, setInitialValues] = useState<Partial<CreateRequest>>({});
+
+       // 获取详情数据
+       const fetchDetail = async () => {
+         if (!id || isNew) return;
+         
+         try {
+           setLoading(true);
+           const res = await yourEntityClient[':id'].$get({
+             param: { id: Number(id) }
+           });
+           
+           if (!res.ok) throw new Error('获取详情失败');
+           
+           const data: EntityDetail = await res.json();
+           setInitialValues(data);
+         } catch (error) {
+           message.error(error instanceof Error ? error.message : '获取详情失败');
+         } finally {
+           setLoading(false);
+         }
+       };
+
+       useEffect(() => {
+         fetchDetail();
+       }, [id, isNew]);
+
+       // 表单提交处理
+       const handleFinish = async (values: CreateRequest | UpdateRequest) => {
+         try {
+           setLoading(true);
+           
+           if (isNew) {
+             // 创建新实体
+             const res = await yourEntityClient.$post({
+               json: values as CreateRequest
+             });
+             
+             if (!res.ok) throw new Error('创建失败');
+             message.success('创建成功');
+           } else if (id) {
+             // 更新实体
+             const res = await yourEntityClient[':id'].$put({
+               param: { id: Number(id) },
+               json: values as UpdateRequest
+             });
+             
+             if (!res.ok) throw new Error('更新失败');
+             message.success('更新成功');
+           }
+           
+           navigate('/your-entities');
+         } catch (error) {
+           message.error(error instanceof Error ? error.message : '操作失败');
+         } finally {
+           setLoading(false);
+         }
+       };
+
+       return (
+         <div className="page-container">
+           <div className="page-header">
+             <h2>{isNew ? '添加实体' : '编辑实体'}</h2>
+           </div>
+           
+           <Card>
+             <Spin spinning={loading}>
+               <YourEntityForm
+                 initialValues={initialValues}
+                 onFinish={handleFinish}
+                 loading={loading}
+               />
+             </Spin>
+           </Card>
+         </div>
+       );
+     };
+
+     export default YourEntityDetail;
+     ```
+
+### 6. **样式规范**
+   - 使用CSS Modules或Styled Components进行样式隔离
+   - 页面容器样式:
+     ```css
+     .page-container {
+       padding: 24px;
+     }
+     
+     .page-header {
+       display: flex;
+       justify-content: space-between;
+       align-items: center;
+       margin-bottom: 24px;
+     }
+     ```
+
+### 7. **权限控制**
+   - 使用ProtectedRoute组件包装需要权限控制的页面:
+     ```tsx
+     import { ProtectedRoute } from '@/client/admin/components/ProtectedRoute';
+     
+     export const routes = [
+       // ...其他路由
+       {
+         path: '/your-entities',
+         element: (
+           <ProtectedRoute requiredPermissions={['your_entity:read']}>
+             <YourEntityList />
+           </ProtectedRoute>
+         )
+       }
+     ];

+ 366 - 0
.roo/rules/11-custom-crud.md

@@ -0,0 +1,366 @@
+# 自定义复杂CRUD开发流程规范
+
+## 适用场景
+
+适用于包含复杂业务逻辑的实体,需要手动实现服务方法和路由处理的场景,如:
+- 复杂业务规则
+- 多表关联操作
+- 特殊权限控制
+- 非标准数据处理
+
+## 开发流程
+
+### 1. **创建实体**
+   - 位置: `src/server/modules/[模块名]/[实体名].entity.ts`
+   - 定义实体类和Zod Schema
+   - 示例:
+     ```typescript
+     import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
+     import { z } from '@hono/zod-openapi';
+     
+     @Entity('your_entity')
+     export class YourEntity {
+       @PrimaryGeneratedColumn({ unsigned: true })
+       id!: number;
+       
+       @Column({ name: 'name', type: 'varchar', length: 255 })
+       name!: string;
+       
+       // 其他业务字段...
+     }
+     
+     // Zod Schema定义
+     export const YourEntitySchema = z.object({
+       id: z.number().int().positive().openapi({ description: '实体ID' }),
+       name: z.string().max(255).openapi({ description: '名称', example: '示例名称' })
+       // 其他字段Schema...
+     });
+     
+     export const CreateYourEntityDto = z.object({
+       name: z.string().max(255).openapi({ description: '名称', example: '示例名称' })
+       // 其他创建字段...
+     });
+     
+     export const UpdateYourEntityDto = z.object({
+       name: z.string().max(255).optional().openapi({ description: '名称', example: '示例名称' })
+       // 其他更新字段...
+     });
+     ```
+
+### 2. **注册实体到数据源**
+   - 在`src/server/data-source.ts`中添加实体导入和注册:
+     ```typescript
+     // 实体类导入
+     import { YourEntity } from "./modules/[模块名]/[实体名].entity"
+     
+     export const AppDataSource = new DataSource({
+       // ...其他配置
+       entities: [
+         User, Role, YourEntity // 添加新实体到数组
+       ],
+       // ...其他配置
+     });
+     ```
+
+### 3. **创建自定义Service**
+   - 位置: `src/server/modules/[模块名]/[实体名].service.ts`
+   - 实现自定义业务逻辑和数据访问
+   - 示例:
+     ```typescript
+     import { DataSource, Repository } from 'typeorm';
+     import { YourEntity } from './your-entity.entity';
+     import { CreateYourEntityDto, UpdateYourEntityDto } from './your-entity.entity';
+     import { AppError } from '@/server/utils/errorHandler';
+     
+     export class YourEntityService {
+       private repository: Repository<YourEntity>;
+       
+       constructor(dataSource: DataSource) {
+         this.repository = dataSource.getRepository(YourEntity);
+       }
+       
+       /**
+        * 获取实体列表(带复杂过滤条件)
+        */
+       async findAll(filters: any): Promise<[YourEntity[], number]> {
+         const query = this.repository.createQueryBuilder('entity');
+         
+         // 添加复杂业务过滤逻辑
+         if (filters.status) {
+           query.andWhere('entity.status = :status', { status: filters.status });
+         }
+         
+         // 多表关联查询示例
+         if (filters.includeRelated) {
+           query.leftJoinAndSelect('entity.relatedEntity', 'related');
+         }
+         
+         const [items, total] = await query.getManyAndCount();
+         return [items, total];
+       }
+       
+       /**
+        * 根据ID获取单个实体
+        */
+       async findById(id: number): Promise<YourEntity> {
+         const entity = await this.repository.findOneBy({ id });
+         if (!entity) {
+           throw new AppError('实体不存在', 404);
+         }
+         return entity;
+       }
+       
+       /**
+        * 创建实体(带业务规则验证)
+        */
+       async create(data: CreateYourEntityDto): Promise<YourEntity> {
+         // 业务规则验证示例
+         const existing = await this.repository.findOneBy({ name: data.name });
+         if (existing) {
+           throw new AppError('名称已存在', 400);
+         }
+         
+         const entity = this.repository.create(data);
+         return this.repository.save(entity);
+       }
+       
+       /**
+        * 更新实体(带业务逻辑处理)
+        */
+       async update(id: number, data: UpdateYourEntityDto): Promise<YourEntity> {
+         const entity = await this.findById(id);
+         
+         // 业务逻辑处理示例
+         if (data.name && data.name !== entity.name) {
+           // 记录名称变更日志等业务操作
+         }
+         
+         Object.assign(entity, data);
+         return this.repository.save(entity);
+       }
+       
+       /**
+        * 删除实体(带权限检查)
+        */
+       async delete(id: number, userId: number): Promise<boolean> {
+         const entity = await this.findById(id);
+         
+         // 权限检查示例
+         if (entity.createdBy !== userId) {
+           throw new AppError('没有删除权限', 403);
+         }
+         
+         await this.repository.remove(entity);
+         return true;
+       }
+       
+       /**
+        * 自定义业务方法示例
+        */
+       async customBusinessOperation(params: any): Promise<any> {
+         // 实现复杂业务逻辑
+         // 可能包含事务、多表操作等
+       }
+     }
+     ```
+
+### 4. **创建自定义API路由**
+   - 目录结构:
+     ```
+     src/server/api/[实体名]/
+     ├── get.ts       # 列表查询
+     ├── post.ts      # 创建实体
+     ├── [id]/
+     │   ├── get.ts   # 获取单个实体
+     │   ├── put.ts   # 更新实体
+     │   └── delete.ts # 删除实体
+     └── index.ts     # 路由聚合
+     ```
+   
+   - **列表查询路由示例** (get.ts):
+     ```typescript
+     import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+     import { z } from 'zod';
+     import { YourEntitySchema } from '@/server/modules/your-module/your-entity.entity';
+     import { ErrorSchema } from '@/server/utils/errorHandler';
+     import { AppDataSource } from '@/server/data-source';
+     import { YourEntityService } from '@/server/modules/your-module/your-entity.service';
+     import { AuthContext } from '@/server/types/context';
+     import { authMiddleware } from '@/server/middleware/auth.middleware';
+     
+     // 查询参数Schema
+     const ListQuery = z.object({
+       page: z.coerce.number().int().positive().default(1).openapi({
+         description: '页码',
+         example: 1
+       }),
+       pageSize: z.coerce.number().int().positive().default(10).openapi({
+         description: '每页条数',
+         example: 10
+       }),
+       status: z.coerce.number().optional().openapi({
+         description: '状态过滤',
+         example: 1
+       })
+     });
+     
+     // 响应Schema
+     const ListResponse = z.object({
+       data: z.array(YourEntitySchema),
+       pagination: z.object({
+         total: z.number().openapi({ example: 100, description: '总记录数' }),
+         current: z.number().openapi({ example: 1, description: '当前页码' }),
+         pageSize: z.number().openapi({ example: 10, description: '每页数量' })
+       })
+     });
+     
+     // 路由定义
+     const routeDef = createRoute({
+       method: 'get',
+       path: '/',
+       middleware: [authMiddleware],
+       request: {
+         query: ListQuery
+       },
+       responses: {
+         200: {
+           description: '成功获取实体列表',
+           content: { 'application/json': { schema: ListResponse } }
+         },
+         400: {
+           description: '请求参数错误',
+           content: { 'application/json': { schema: ErrorSchema } }
+         },
+         500: {
+           description: '服务器错误',
+           content: { 'application/json': { schema: ErrorSchema } }
+         }
+       }
+     });
+     
+     // 路由实现
+     const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+       try {
+         const query = c.req.valid('query');
+         const service = new YourEntityService(AppDataSource);
+         
+         const [data, total] = await service.findAll({
+           status: query.status,
+           // 其他过滤条件
+         });
+         
+         return c.json({
+           data,
+           pagination: {
+             total,
+             current: query.page,
+             pageSize: query.pageSize
+           }
+         }, 200);
+       } catch (error) {
+         const { code = 500, message = '获取列表失败' } = error as Error & { code?: number };
+         return c.json({ code, message }, code);
+       }
+     });
+     
+     export default app;
+     ```
+     
+   - **创建实体路由示例** (post.ts):
+     ```typescript
+     import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+     import { CreateYourEntityDto, YourEntitySchema } from '@/server/modules/your-module/your-entity.entity';
+     import { ErrorSchema } from '@/server/utils/errorHandler';
+     import { AppDataSource } from '@/server/data-source';
+     import { YourEntityService } from '@/server/modules/your-module/your-entity.service';
+     import { AuthContext } from '@/server/types/context';
+     import { authMiddleware } from '@/server/middleware/auth.middleware';
+     
+     // 路由定义
+     const routeDef = createRoute({
+       method: 'post',
+       path: '/',
+       middleware: [authMiddleware],
+       request: {
+         body: {
+           content: {
+             'application/json': { schema: CreateYourEntityDto }
+           }
+         }
+       },
+       responses: {
+         200: {
+           description: '成功创建实体',
+           content: { 'application/json': { schema: YourEntitySchema } }
+         },
+         400: {
+           description: '请求参数错误',
+           content: { 'application/json': { schema: ErrorSchema } }
+         },
+         500: {
+           description: '服务器错误',
+           content: { 'application/json': { schema: ErrorSchema } }
+         }
+       }
+     });
+     
+     // 路由实现
+     const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+       try {
+         const data = await c.req.json();
+         const service = new YourEntityService(AppDataSource);
+         const result = await service.create(data);
+         return c.json(result, 200);
+       } catch (error) {
+         const { code = 500, message = '创建实体失败' } = error as Error & { code?: number };
+         return c.json({ code, message }, code);
+       }
+     });
+     
+     export default app;
+     ```
+     
+   - **路由聚合示例** (index.ts):
+     ```typescript
+     import { OpenAPIHono } from '@hono/zod-openapi';
+     import listRoute from './get';
+     import createRoute from './post';
+     import getByIdRoute from './[id]/get';
+     import updateRoute from './[id]/put';
+     import deleteRoute from './[id]/delete';
+     
+     const app = new OpenAPIHono()
+       .route('/', listRoute)
+       .route('/', createRoute)
+       .route('/', getByIdRoute)
+       .route('/', updateRoute)
+       .route('/', deleteRoute);
+     
+     export default app;
+     ```
+
+### 5. **注册路由**
+   - 在`src/server/api.ts`中添加路由注册:
+     ```typescript
+     import yourEntityRoutes from '@/server/api/your-entity/index';
+     
+     // 注册路由
+     api.route('/api/v1/your-entities', yourEntityRoutes);
+     ```
+
+### 6. **创建客户端API**
+   - 在`src/client/api.ts`中添加客户端定义:
+     ```typescript
+     import { hc } from 'hono/client';
+     import { YourEntityRoutes } from '@/server/api';
+     
+     export const yourEntityClient = hc<YourEntityRoutes>('/api/v1', {
+       fetch: axiosFetch,
+     }).api.v1['your-entities'];
+     ```
+
+### 7. **前端调用**
+   - 在页面组件中使用类型推断:
+     ```typescript
+     type EntityResponse = InferResponseType<typeof entityClient.$get, 200>;
+     type CreateRequest = InferRequestType<typeof entityClient.$post>['json'];

+ 381 - 0
.roo/rules/11-home-frontend.md

@@ -0,0 +1,381 @@
+# 前台Home页面开发流程规范
+
+## 适用场景
+
+前台用户界面开发,包括首页、登录页、注册页、会员中心等面向普通用户的页面实现流程。
+
+## 目录结构
+
+```
+src/client/home/
+├── index.tsx           # 入口文件
+├── routes.tsx          # 路由配置
+├── components/         # 共享组件
+│   ├── ui/             # UI组件
+│   ├── ErrorPage.tsx   # 错误页面
+│   ├── NotFoundPage.tsx # 404页面
+│   └── ProtectedRoute.tsx # 路由保护组件
+├── hooks/              # 自定义hooks
+│   └── AuthProvider.tsx # 认证上下文
+├── layouts/            # 布局组件
+│   └── MainLayout.tsx  # 主布局
+└── pages/              # 页面组件
+    ├── HomePage.tsx    # 首页
+    ├── LoginPage.tsx   # 登录页
+    ├── RegisterPage.tsx # 注册页
+    └── MemberPage.tsx  # 会员中心
+```
+
+## 开发流程
+
+### 1. **创建页面组件**
+   - 位置: `src/client/home/pages/[PageName]Page.tsx`
+   - 首页组件示例 (HomePage.tsx):
+     ```tsx
+     import React from 'react';
+     import { Link } from 'react-router-dom';
+     import { Button, Card, Space, Typography } from 'antd';
+     import { UserOutlined, ShoppingCartOutlined } from '@ant-design/icons';
+     import MainLayout from '../layouts/MainLayout';
+
+     const { Title, Paragraph } = Typography;
+
+     const HomePage: React.FC = () => {
+       return (
+         <MainLayout>
+           <div className="hero-section">
+             <Title level={1}>欢迎使用我们的平台</Title>
+             <Paragraph>
+               探索更多功能,提升您的体验
+             </Paragraph>
+             <Space size="middle">
+               <Button type="primary" size="large" icon={<ShoppingCartOutlined />}>
+                 立即开始
+               </Button>
+               <Button size="large" icon={<UserOutlined />}>
+                 <Link to="/login">登录</Link>
+               </Button>
+             </Space>
+           </div>
+
+           <div className="features-section">
+             <Card title="功能一" hoverable>
+               <p>这是平台的核心功能之一</p>
+             </Card>
+             <Card title="功能二" hoverable>
+               <p>这是平台的核心功能之二</p>
+             </Card>
+             <Card title="功能三" hoverable>
+               <p>这是平台的核心功能之三</p>
+             </Card>
+           </div>
+         </MainLayout>
+       );
+     };
+
+     export default HomePage;
+     ```
+
+### 2. **创建布局组件**
+   - 位置: `src/client/home/layouts/[LayoutName].tsx`
+   - 主布局示例 (MainLayout.tsx):
+     ```tsx
+     import React from 'react';
+     import { Layout, Menu, Avatar, Dropdown, Space } from 'antd';
+     import { Link, useNavigate } from 'react-router-dom';
+     import { UserOutlined, LogoutOutlined, HomeOutlined } from '@ant-design/icons';
+     import { useAuth } from '../hooks/AuthProvider';
+
+     const { Header, Content, Footer } = Layout;
+
+     const MainLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+       const { user, logout } = useAuth();
+       const navigate = useNavigate();
+
+       const handleLogout = async () => {
+         await logout();
+         navigate('/login');
+       };
+
+       const userMenuItems = [
+         {
+           key: 'profile',
+           icon: <UserOutlined />,
+           label: <Link to="/member">个人中心</Link>,
+         },
+         {
+           key: 'logout',
+           icon: <LogoutOutlined />,
+           label: <span onClick={handleLogout}>退出登录</span>,
+         },
+       ];
+
+       return (
+         <Layout className="layout">
+           <Header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+             <div className="logo">
+               <Link to="/">平台名称</Link>
+             </div>
+             <Menu theme="dark" mode="horizontal" items={[
+               { key: 'home', icon: <HomeOutlined />, label: <Link to="/">首页</Link> }
+             ]} />
+             <Space>
+               {user ? (
+                 <Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
+                   <Avatar icon={<UserOutlined />} />
+                 </Dropdown>
+               ) : (
+                 <Space>
+                   <Link to="/login">登录</Link>
+                   <Link to="/register">注册</Link>
+                 </Space>
+               )}
+             </Space>
+           </Header>
+           <Content style={{ padding: '0 50px' }}>
+             <div style={{ background: '#fff', padding: 24, minHeight: 280 }}>
+               {children}
+             </div>
+           </Content>
+           <Footer style={{ textAlign: 'center' }}>
+             平台名称 ©{new Date().getFullYear()} Created with React
+           </Footer>
+         </Layout>
+       );
+     };
+
+     export default MainLayout;
+     ```
+
+### 3. **配置路由**
+   - 位置: `src/client/home/routes.tsx`
+   - 路由配置示例:
+     ```typescript
+     import React from 'react';
+     import { createBrowserRouter } from 'react-router-dom';
+     import HomePage from './pages/HomePage';
+     import LoginPage from './pages/LoginPage';
+     import RegisterPage from './pages/RegisterPage';
+     import MemberPage from './pages/MemberPage';
+     import NotFoundPage from './components/NotFoundPage';
+     import ErrorPage from './components/ErrorPage';
+     import ProtectedRoute from './components/ProtectedRoute';
+
+     export const router = createBrowserRouter([
+       {
+         path: '/',
+         element: <HomePage />,
+         errorElement: <ErrorPage />
+       },
+       {
+         path: '/login',
+         element: <LoginPage />
+       },
+       {
+         path: '/register',
+         element: <RegisterPage />
+       },
+       {
+         path: '/member',
+         element: (
+           <ProtectedRoute>
+             <MemberPage />
+           </ProtectedRoute>
+         )
+       },
+       {
+         path: '*',
+         element: <NotFoundPage />
+       }
+     ]);
+     ```
+
+### 4. **实现认证功能**
+   - 位置: `src/client/home/hooks/AuthProvider.tsx`
+   - 认证上下文示例:
+     ```tsx
+     import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
+     import { authClient } from '@/client/api';
+
+     interface User {
+       id: number;
+       username: string;
+       email: string;
+     }
+
+     interface AuthContextType {
+       user: User | null;
+       loading: boolean;
+       login: (username: string, password: string) => Promise<void>;
+       register: (username: string, email: string, password: string) => Promise<void>;
+       logout: () => Promise<void>;
+     }
+
+     const AuthContext = createContext<AuthContextType | undefined>(undefined);
+
+     export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
+       const [user, setUser] = useState<User | null>(null);
+       const [loading, setLoading] = useState(true);
+
+       useEffect(() => {
+         // 初始化时检查用户是否已登录
+         const checkAuth = async () => {
+           try {
+             const token = localStorage.getItem('token');
+             if (token) {
+               const res = await authClient.me.$get();
+               if (res.ok) {
+                 const data = await res.json();
+                 setUser(data);
+               } else {
+                 localStorage.removeItem('token');
+               }
+             }
+           } catch (error) {
+             console.error('Auth initialization error:', error);
+           } finally {
+             setLoading(false);
+           }
+         };
+
+         checkAuth();
+       }, []);
+
+       const login = async (username: string, password: string) => {
+         const res = await authClient.login.password.$post({
+           json: { username, password }
+         });
+         
+         if (!res.ok) {
+           throw new Error('登录失败');
+         }
+         
+         const data = await res.json();
+         localStorage.setItem('token', data.token);
+         setUser(data.user);
+       };
+
+       const register = async (username: string, email: string, password: string) => {
+         const res = await authClient.register.create.$post({
+           json: { username, email, password }
+         });
+         
+         if (!res.ok) {
+           throw new Error('注册失败');
+         }
+       };
+
+       const logout = async () => {
+         try {
+           await authClient.logout.$post();
+         } finally {
+           localStorage.removeItem('token');
+           setUser(null);
+         }
+       };
+
+       return (
+         <AuthContext.Provider value={{ user, loading, login, register, logout }}>
+           {children}
+         </AuthContext.Provider>
+       );
+     };
+
+     export const useAuth = () => {
+       const context = useContext(AuthContext);
+       if (context === undefined) {
+         throw new Error('useAuth must be used within an AuthProvider');
+       }
+       return context;
+     };
+     ```
+
+### 5. **创建UI组件**
+   - 位置: `src/client/home/components/ui/[ComponentName].tsx`
+   - 按钮组件示例 (PrimaryButton.tsx):
+     ```tsx
+     import React from 'react';
+     import { Button, ButtonProps } from 'antd';
+
+     interface PrimaryButtonProps extends ButtonProps {
+       children: React.ReactNode;
+     }
+
+     const PrimaryButton: React.FC<PrimaryButtonProps> = ({ children, ...props }) => {
+       return (
+         <Button type="primary" size="middle" {...props}>
+           {children}
+         </Button>
+       );
+     };
+
+     export default PrimaryButton;
+     ```
+
+### 6. **入口文件配置**
+   - 位置: `src/client/home/index.tsx`
+   - 配置示例:
+     ```tsx
+     import React from 'react';
+     import ReactDOM from 'react-dom/client';
+     import { RouterProvider } from 'react-router-dom';
+     import { AuthProvider } from './hooks/AuthProvider';
+     import { router } from './routes';
+     import '../style.css';
+
+     ReactDOM.createRoot(document.getElementById('root')!).render(
+       <React.StrictMode>
+         <AuthProvider>
+           <RouterProvider router={router} />
+         </AuthProvider>
+       </React.StrictMode>
+     );
+     ```
+
+### 7. **样式规范**
+   - 使用CSS Modules或Styled Components进行样式隔离
+   - 全局样式: `src/client/style.css`
+   - 页面特定样式: 与页面组件同名的`.module.css`文件
+   - 示例 (HomePage.module.css):
+     ```css
+     .heroSection {
+       text-align: center;
+       padding: 60px 0;
+       background-color: #f5f5f5;
+       margin-bottom: 40px;
+     }
+
+     .featuresSection {
+       display: grid;
+       grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+       gap: 24px;
+     }
+     ```
+
+### 8. **路由保护**
+   - 使用ProtectedRoute组件保护需要登录的页面:
+     ```tsx
+     import React from 'react';
+     import { Navigate } from 'react-router-dom';
+     import { useAuth } from '../hooks/AuthProvider';
+
+     interface ProtectedRouteProps {
+       children: React.ReactNode;
+     }
+
+     const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
+       const { user, loading } = useAuth();
+       
+       if (loading) {
+         return <div>Loading...</div>;
+       }
+       
+       if (!user) {
+         // 未登录,重定向到登录页
+         return <Navigate to="/login" replace />;
+       }
+       
+       return <>{children}</>;
+     };
+
+     export default ProtectedRoute;

+ 90 - 0
.roo/rules/11-standard-crud.md

@@ -0,0 +1,90 @@
+# 标准通用CRUD开发流程规范
+
+## 适用场景
+
+适用于简单数据模型,无复杂业务逻辑,仅需基础CRUD操作的场景。采用`GenericCrudService`和`createCrudRoutes`快速生成接口。
+
+## 开发流程
+
+### 1. **创建实体**
+   - 位置: `src/server/modules/[模块名]/[实体名].entity.ts`
+   - 必须包含Zod Schema定义
+   - 参考已有实体文件如`user.entity.ts`
+
+### 2. **注册实体到数据源**
+   - 在`src/server/data-source.ts`中添加实体导入和注册:
+     ```typescript
+     // 实体类导入
+     import { YourEntity } from "./modules/[模块名]/[实体名].entity"
+     
+     export const AppDataSource = new DataSource({
+       // ...其他配置
+       entities: [
+         User, Role, YourEntity // 添加新实体到数组
+       ],
+       // ...其他配置
+     });
+     ```
+
+### 3. **创建Service**
+   - 位置: `src/server/modules/[模块名]/[实体名].service.ts`
+   - 继承`GenericCrudService`
+   - 通过构造函数注入DataSource
+   ```typescript
+   import { GenericCrudService } from '@/server/utils/generic-crud.service';
+   import { DataSource } from 'typeorm';
+   import { YourEntity } from './your-entity.entity';
+   
+   export class YourEntityService extends GenericCrudService<YourEntity> {
+     constructor(dataSource: DataSource) {
+       super(dataSource, YourEntity);
+     }
+   }
+   ```
+
+### 4. **创建API路由**
+   - 使用`createCrudRoutes`快速生成CRUD路由:
+     ```typescript
+     import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
+     import { YourEntity } from '@/server/modules/your-module/your-entity.entity';
+     import { YourEntitySchema, CreateYourEntityDto, UpdateYourEntityDto } from '@/server/modules/your-module/your-entity.entity';
+     import { authMiddleware } from '@/server/middleware/auth.middleware';
+     
+     const yourEntityRoutes = createCrudRoutes({
+       entity: YourEntity,
+       createSchema: CreateYourEntityDto,
+       updateSchema: UpdateYourEntityDto,
+       getSchema: YourEntitySchema,
+       listSchema: YourEntitySchema,
+       searchFields: ['name', 'description'], // 可选,指定搜索字段
+       middleware: [authMiddleware] // 可选,添加中间件
+     });
+     
+     export default yourEntityRoutes;
+     ```
+
+### 5. **注册路由**
+   - 在`src/server/api.ts`中添加路由注册:
+     ```typescript
+     import yourEntityRoutes from '@/server/api/your-entity/index';
+     
+     // 注册路由
+     api.route('/api/v1/your-entities', yourEntityRoutes);
+     ```
+
+### 6. **创建客户端API**
+   - 在`src/client/api.ts`中添加客户端定义:
+     ```typescript
+     import { hc } from 'hono/client';
+     import { YourEntityRoutes } from '@/server/api';
+     
+     export const yourEntityClient = hc<YourEntityRoutes>('/api/v1', {
+       fetch: axiosFetch,
+     }).api.v1['your-entities'];
+     ```
+
+### 7. **前端调用**
+   - 在页面组件中使用类型推断:
+     ```typescript
+     type EntityResponse = InferResponseType<typeof entityClient.$get, 200>;
+     type CreateRequest = InferRequestType<typeof entityClient.$post>['json'];