healthapp
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

13 KiB

Agents 开发规范

  • 涉及到任何代码修改,记得更新此文档和设计文档
  • 需求记录文档: docs/REQUIREMENTS.md

前端自动化测试规范

重要: 前端进行功能更新和修改后,必须使用 Playwright 进行自动化测试验证。

详细文档: 参见 tests/README.md

测试目录结构

tests/
├── README.md                         # 测试使用文档
├── constitution.test.js              # 体质分析功能测试
├── profile.test.js                   # "我的"页面功能测试
├── health-profile-complete.test.js   # 健康档案完整功能测试(推荐)
└── screenshots/                      # 测试截图目录

测试流程

  1. 修改代码后:编写或运行 Playwright 测试脚本验证功能
  2. 测试通过后:才能确认修复完成
  3. 不要让用户手动测试:自动化测试能发现的问题应自行解决

测试脚本示例

// 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();
})();

运行测试

# 安装 (首次)
npm install playwright
npx playwright install chromium

# 运行测试
node test-功能名.js

测试检查清单

  1. 功能正常工作
  2. 截图验证 UI 显示正确
  3. 错误场景处理正确
  4. 清理测试文件 (rm test-*.js test-*.png)

前后端 API 响应格式规范

重要: 所有 API 必须遵循此格式约定,避免响应格式不匹配问题。

统一响应结构

{
  "code": 0,          // 0=成功,其他=错误码
  "message": "success",
  "data": <T>         // 业务数据
}

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 调用

使用方式:

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 - 添加转换函数

关键代码:

// 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 - 添加防御性代码

关键修复代码:

// 问卷数据转换
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<string, number> = {};
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