# Agents 开发规范 - 涉及到任何代码修改,记得更新此文档和设计文档 - 需求记录文档: [`docs/REQUIREMENTS.md`](docs/REQUIREMENTS.md) ## 前端自动化测试规范 **重要**: 前端进行功能更新和修改后,必须使用 Playwright 进行自动化测试验证。 **详细文档**: 参见 [`tests/README.md`](tests/README.md) ### 测试目录结构 ``` tests/ ├── README.md # 测试使用文档 ├── constitution.test.js # 体质分析功能测试 ├── profile.test.js # "我的"页面功能测试 ├── health-profile-complete.test.js # 健康档案完整功能测试(推荐) └── screenshots/ # 测试截图目录 ``` ### 测试流程 1. **修改代码后**:编写或运行 Playwright 测试脚本验证功能 2. **测试通过后**:才能确认修复完成 3. **不要让用户手动测试**:自动化测试能发现的问题应自行解决 ### 测试脚本示例 ```javascript // test-功能名.js const { chromium } = require("playwright"); (async () => { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); // 1. 打开应用 await page.goto("http://localhost:8081"); await page.waitForTimeout(2000); // 2. 执行测试步骤... // 3. 截图验证 await page.screenshot({ path: "test-screenshot.png" }); // 4. 断言检查 const element = await page.locator("text=期望文本").first(); if (await element.isVisible()) { console.log("✓ 测试通过"); } else { console.log("✗ 测试失败"); } await browser.close(); })(); ``` ### 运行测试 ```bash # 安装 (首次) npm install playwright npx playwright install chromium # 运行测试 node test-功能名.js ``` ### 测试检查清单 1. [ ] 功能正常工作 2. [ ] 截图验证 UI 显示正确 3. [ ] 错误场景处理正确 4. [ ] 清理测试文件 (`rm test-*.js test-*.png`) --- ## 前后端 API 响应格式规范 **重要**: 所有 API 必须遵循此格式约定,避免响应格式不匹配问题。 ### 统一响应结构 ```json { "code": 0, // 0=成功,其他=错误码 "message": "success", "data": // 业务数据 } ``` ### data 字段约定 | 接口类型 | data 格式 | 示例 | | -------- | ------------------ | -------------------------- | | 列表接口 | 直接返回数组 `T[]` | `data: [{id:1}, {id:2}]` | | 详情接口 | 返回对象 `T` | `data: {id:1, name:"xxx"}` | | 创建接口 | 返回创建的对象 | `data: {id:1, ...}` | | 删除接口 | `null` 或空 | `data: null` | ### 字段命名约定 - **后端 API**: 使用 snake_case(`created_at`, `user_id`) - **前端应用**: 使用 camelCase(`createdAt`, `userId`) - **前端需做转换**: 在 Store 层统一转换 ### 类型定义文件 - **后端**: `server/docs/API.md` - API 文档 - **前端**: `app/src/api/types.ts` - 统一类型定义 ### 新增 API 检查清单 1. [ ] 查看 `server/docs/API.md` 确认后端响应格式 2. [ ] 在 `app/src/api/types.ts` 添加类型定义 3. [ ] 在 API 模块中使用正确的泛型类型 4. [ ] 在 Store 中添加 snake_case → camelCase 转换 --- ## 开发记录 ### 2026-02-02: Expo Web 兼容性修复 **问题**: React Native 的 `Alert.alert()` 在 Web 上无法正常显示弹窗 **解决方案**: 创建跨平台的 `AlertProvider` 组件,使用 react-native-paper 的 Snackbar 和 Dialog 替代原生 Alert **新增文件**: - `app/src/components/AlertProvider.tsx` - 全局 Alert Context 和组件 - `app/src/components/index.ts` - 组件导出 **修改文件**: - `app/App.tsx` - 集成 AlertProvider - `app/src/screens/auth/LoginScreen.tsx` - 替换 Alert 调用 - `app/src/screens/chat/ChatDetailScreen.tsx` - 替换 Alert 调用 - `app/src/screens/chat/ChatListScreen.tsx` - 替换 Alert 调用 - `app/src/screens/constitution/ConstitutionTestScreen.tsx` - 替换 Alert 调用 - `app/src/screens/profile/ProfileScreen.tsx` - 替换 Alert 调用 **使用方式**: ```tsx import { useAlert } from "../../components"; const { showAlert, showToast } = useAlert(); // Toast 简单提示 showToast("请输入正确的手机号"); // Dialog 确认弹窗 showAlert("确认删除", "确定要删除吗?", [ { text: "取消", style: "cancel" }, { text: "删除", style: "destructive", onPress: () => handleDelete() }, ]); ``` --- ### 2026-02-02: 健康档案功能对接 **内容**: 对接后端真实 API,实现健康档案功能 **新增文件**: - `app/src/stores/healthStore.ts` - 健康档案状态管理 - `app/src/screens/profile/HealthProfileScreen.tsx` - 健康档案页面 **修改文件**: - `app/src/api/user.ts` - 添加健康档案相关 API(病史、家族病史、过敏记录) - `app/src/navigation/index.tsx` - 添加 ProfileStack 和健康档案路由 - `app/src/screens/profile/ProfileScreen.tsx` - 对接健康档案导航和真实病史数据 **功能说明**: 1. **健康档案页面**: 展示基础信息、生活习惯、病史记录、家族病史、过敏记录 2. **用药记录**: 改用后端病史数据,显示"治疗中"状态的记录 3. **支持下拉刷新和长按删除** **API 对接**: - `GET /api/user/health-profile` - 获取完整健康档案 - `GET /api/user/lifestyle` - 获取生活习惯 - `GET /api/user/medical-history` - 获取病史列表 - `GET /api/user/family-history` - 获取家族病史 - `GET /api/user/allergy-records` - 获取过敏记录 - `DELETE /api/user/medical-history/:id` - 删除病史 - `DELETE /api/user/family-history/:id` - 删除家族病史 - `DELETE /api/user/allergy-records/:id` - 删除过敏记录 --- ### 2026-02-02: API 响应格式统一规范 **问题**: 前后端响应格式不匹配导致数据解析失败(已出现两次:Token 获取、对话列表) **根本原因**: - 前端期望: `data: { conversations: [...], total: number }` - 后端实际: `data: [...]` (直接返回数组) **解决方案**: 1. 创建统一类型定义文件 `app/src/api/types.ts` 2. 明确约定:列表接口 data 直接返回数组,不包装 3. Store 层统一做 snake_case → camelCase 转换 **新增文件**: - `app/src/api/types.ts` - 统一 API 类型定义 **修改文件**: - `app/src/api/conversation.ts` - 使用新类型定义 - `app/src/stores/chatStore.ts` - 添加转换函数 **关键代码**: ```typescript // app/src/api/types.ts - API 响应类型与后端保持一致 export interface ConversationItem { id: number; title: string; created_at: string; // snake_case updated_at: string; } // app/src/stores/chatStore.ts - 转换函数 const convertConversation = (item: ConversationItem): Conversation => ({ id: String(item.id), title: item.title, createdAt: item.created_at, // 转为 camelCase updatedAt: item.updated_at, }); ``` --- ### 2026-02-02: 体质分析功能修复 **问题**: 体质分析功能多处 API 格式不匹配导致运行时错误 **错误信息**: 1. `Cannot read properties of undefined (reading 'length')` - questions 字段 2. `currentQuestion.options.map is not a function` - options 字段格式 3. `Cannot read properties of undefined (reading 'suggestions')` - 结果格式 **根本原因**: - 问卷题目:后端直接返回数组 `data: [...]`,前端期望 `data: { questions: [...] }` - 选项格式:后端存储 `["没有","很少","有时","经常","总是"]`,前端期望 `[{value, label}]` - 结果格式:后端返回 `{ primary_constitution: {...}, all_scores: [...] }`,前端期望 `{ primaryType, scores }` **修改文件**: - `app/src/stores/constitutionStore.ts` - 添加数据格式转换 - `app/src/api/constitution.ts` - 更新类型定义 - `app/src/screens/constitution/ConstitutionResultScreen.tsx` - 添加防御性代码 **关键修复代码**: ```typescript // 问卷数据转换 const questions = rawQuestions.map((q: any) => { let parsedOptions = typeof q.options === "string" ? JSON.parse(q.options) : q.options; // 字符串数组转为 {value, label} 格式 if (Array.isArray(parsedOptions) && typeof parsedOptions[0] === "string") { parsedOptions = parsedOptions.map((label: string, index: number) => ({ value: index + 1, label, })); } return { id: q.id || q.ID, constitution_type: q.constitution_type, question: q.question_text || q.question, options: parsedOptions, order_num: q.order_num, }; }); // 结果数据转换 const scores: Record = {}; apiResult.all_scores?.forEach((item: any) => { scores[item.type] = item.score; }); const result = { primaryType: apiResult.primary_constitution?.type, scores, // ... }; ``` **测试验证**: - 测试脚本: `tests/constitution.test.js` - 测试结果: 12/13 通过,67 道题完整测试 - 测试截图: `tests/screenshots/constitution-result.png` --- ### 2026-02-02: "我的"页面功能测试 **测试内容**: 1. 用户信息显示(昵称、手机号、体质标签) 2. 编辑昵称功能 3. 适老模式开关 4. 健康管理菜单(健康档案、用药记录、体质报告、对话历史) 5. 用药/治疗记录弹窗 6. 关于我们弹窗 7. 退出登录功能 8. 健康档案导航 **测试结果**: 16/18 通过 **通过项目**: - ✓ 用户信息显示完整 - ✓ 编辑昵称功能正常 - ✓ 适老模式开关正常 - ✓ 所有菜单项显示正常 - ✓ 用药记录弹窗正常 - ✓ 退出登录按钮显示 - ✓ 健康档案导航正常 **测试文件**: - 测试脚本: `tests/profile.test.js` - 测试截图: `tests/screenshots/profile-*.png`, `tests/screenshots/health-profile-*.png` - 测试文档: `tests/README.md` --- ### 2026-02-02: 健康档案编辑功能 **新增功能**: 1. 基础信息编辑弹窗 - 编辑姓名、性别、出生日期、身高、体重、血型、职业、婚姻状况、地区 2. 生活习惯编辑弹窗 - 编辑睡眠时间、睡眠质量、饮食、运动、吸烟、饮酒等 3. 病史记录新增功能 - 添加疾病名称、类型、诊断日期、状态、备注 4. 家族病史新增功能 - 添加亲属关系、疾病名称、备注 5. 过敏记录新增功能 - 添加过敏类型、过敏原、严重程度、反应描述 **修改文件**: - `app/src/api/user.ts` - 添加 addMedicalHistory, addFamilyHistory, addAllergyRecord API - `app/src/stores/healthStore.ts` - 添加创建记录的方法 - `app/src/screens/profile/HealthProfileScreen.tsx` - 添加编辑弹窗和新增功能 **测试结果**: 23/25 通过 **通过项目**: - ✓ 健康档案页面打开 - ✓ 基础信息卡片显示 - ✓ 基础信息编辑弹窗打开 - ✓ 生活习惯卡片显示 - ✓ 病史记录卡片显示 - ✓ 病史记录新增弹窗打开 - ✓ 家族病史卡片显示 - ✓ 过敏记录卡片显示 --- ### 2026-02-02: 健康档案保存功能修复 **问题描述**: 1. 基础信息保存后页面不显示更新的数据 2. 病史记录、家族病史、过敏记录添加失败 3. 后端返回的数据格式与前端期望不匹配 **根本原因**: 1. `fetchHealthProfile` 错误地将 `response.data` 直接赋给 `profile`,但后端返回的是嵌套结构 `{ profile, lifestyle, medical_history, ... }` 2. 后端字段名 `medical_history`(单数)与前端使用的 `medical_histories`(复数)不匹配 3. 添加记录时后端返回 `data: null`,但前端检查 `response.code === 0 && response.data`,导致误判为失败 **修复内容**: - `app/src/stores/healthStore.ts`: - `fetchHealthProfile`: 修复数据解析,支持嵌套结构 `data.profile || data` - `fetchHealthProfile`: 兼容两种字段名 `data.medical_history || data.medical_histories` - `updateHealthProfile`: 修改判断条件,只检查 `response.code === 0` - `updateLifestyle`: 同上 - `addMedicalHistory`: 修改判断条件,只检查 `response.code === 0`,并在成功后调用 `fetchHealthProfile()` 刷新数据 - `addFamilyHistory`: 同上 - `addAllergyRecord`: 同上 **测试验证**: - 测试脚本: `tests/health-profile-complete.test.js` - 测试范围: - 基础信息: 9 个字段(姓名、性别、出生日期、身高、体重、血型、职业、婚姻状况、地区) - 生活习惯: 10 个字段(入睡时间、起床时间、睡眠质量、三餐规律、饮食偏好、日饮水量、运动频率、运动类型、吸烟、饮酒) - 病史记录: 5 个字段(疾病名称、疾病类型、诊断日期、治疗状态、备注) - 家族病史: 3 个字段(亲属关系、疾病名称、备注) - 过敏记录: 4 个字段(过敏类型、过敏原、严重程度、过敏反应描述) - 测试结果: **58/58 通过** ✓ - 测试截图: `tests/screenshots/hp-*.png`