# Tolan AI 英语语音助手 - 开发规则 ## 项目概述 - **后端**: FastAPI + Python 3.11+ + WebSocket - **前端**: React 18 + TypeScript + Vite + Ant Design - **AI服务**: 阿里云 DashScope (STT/LLM/TTS) - **架构**: 实时语音对话流水线 (STT → LLM → TTS) --- ## 代码生成原则 ### 文档规则 - ❌ 避免生成冗长的 Markdown 文档 - ✅ 代码优先,注释简洁 - ✅ 必要文档:配置说明、API定义(列表形式) - ✅ 文档格式:表格 > 列表 > 段落 - ✅ **文档生成路径规则**: - 项目文档 → `项目文档/` 目录 - 后端文档 → `backend/文档/` 目录 - 前端文档 → `frontend/docs/` 目录(如需要) - 配置文档 → 对应模块根目录(如 `backend/ENV_CONFIG.md`) ### 代码风格 - **Python**: 类型提示、async/await、异常处理必需 - **TypeScript**: 严格类型、接口定义、错误边界 - **命名**: 英文、语义化、统一风格(snake_case/camelCase) - **注释**: 仅对复杂逻辑、性能优化、临时方案添加 --- ## 后端开发规则 ### FastAPI 结构 ```python # 路由定义 @app.websocket("/ws") async def endpoint(websocket: WebSocket): ... # 类型定义(必需) from pydantic import BaseModel class Message(BaseModel): type: str data: dict ``` ### 异步编程 - 所有 I/O 操作使用 `async/await` - 使用 `asyncio.to_thread()` 包装同步库 - WebSocket 消息处理异步化 ### 配置管理 - 所有配置从 `.env` 读取 - 使用 `pydantic-settings` 管理 - 必需项无默认值,可选项提供默认值 ### AI 服务集成 ```python # DashScope API 调用模式 import dashscope dashscope.api_key = settings.DASHSCOPE_API_KEY # 流式处理 async for chunk in ai_service.generate_stream(text): await websocket.send_json(chunk) ``` ### 错误处理 ```python try: result = await api_call() except Exception as e: logger.error(f"Error: {e}") await send_error(request_id, str(e)) ``` --- ## 前端开发规则 ### React + TypeScript ```typescript // 类型定义(必需) interface Message { type: string; requestId: string; data: unknown; } // 组件定义 export const Component: React.FC = ({ ... }) => { // Hooks const [state, setState] = useState(initial); // 处理函数 const handleEvent = useCallback(() => { ... }, [deps]); return ; }; ``` ### WebSocket 通信 ```typescript // 发送消息 ws.send(JSON.stringify({ type, requestId, data })); // 接收消息 ws.onmessage = (event) => { const message: Message = JSON.parse(event.data); handleMessage(message); }; ``` ### 状态管理 (Zustand) ```typescript export const useStore = create((set) => ({ value: initial, update: (newValue) => set({ value: newValue }), })); ``` ### 音频处理 ```typescript // 录音 const recorder = new Recorder({ sampleRate: 16000 }); recorder.start(); // Base64 编码 const base64 = btoa(String.fromCharCode(...audioData)); ``` ### 移动端适配 (postcss-px-to-viewport) #### 配置信息 - **设计稿基准**: 375px × 812px (iPhone X/11/12/13/14) - **转换单位**: vw (viewport width) - **自动转换**: CSS 文件中的 px 自动转换为 vw - **排除目录**: node_modules(第三方组件不转换) #### 核心规则 ##### ✅ CSS 文件样式(自动转换) ```css /* 直接写 px,插件自动转换为 vw */ .box { width: 100px; /* → 26.66667vw */ padding: 16px; /* → 4.26667vw */ font-size: 14px; /* → 3.73333vw */ } ``` ##### ✅ 内联样式(手动转换) ```typescript import { px2vw } from '@/utils/viewport'; // 必须使用 px2vw() 函数
// ❌ 错误:内联样式不会自动转换
// 保持 100px ``` ##### ✅ 固定尺寸元素(不转换) ```css /* 使用大写 PX 避免转换 */ .icon { width: 20PX; /* 保持 20px */ height: 20PX; /* 保持 20px */ } /* 或添加 .ignore 类 */ .ignore { width: 20px; /* 不会转换 */ } ``` ##### ❌ 1px 边框问题 ```css /* 当前配置:1px 会转换为 vw */ /* 如需真实 1px(如分隔线),使用大写 PX */ .divider { border: 1PX solid #ddd; /* 保持 1px */ } /* 或使用 transform 方案 */ .hairline::after { height: 1px; transform: scaleY(0.5); } ``` #### 使用场景 ##### 场景 1: 动态计算的内联样式 ```typescript // ❌ 错误 const width = 100;
// ✅ 正确 import { px2vw } from '@/utils/viewport'; const width = 100;
``` ##### 场景 2: 响应式最大/最小值 ```css /* 结合使用 vw 和固定值 */ .container { width: 100vw; max-width: 430PX; /* 使用 PX 限制最大宽度 */ } ``` ##### 场景 3: 媒体查询 ```css /* 媒体查询中的 px 不会转换(已配置 mediaQuery: false) */ @media (min-width: 768px) { .content { max-width: 430px; /* 保持 px */ } } ``` ##### 场景 4: TouchTarget 最小尺寸 ```typescript // Apple HIG 标准:最小 44px // 这些 px 会被转换为 vw