# 设计方案:穴位定位功能 ## 架构设计 ### 数据流 ``` 用户信息录入 → 体型数据处理 → 穴位坐标计算 → 经络图渲染 → 艾灸执行 │ │ │ │ │ │ │ ▼ │ │ │ 穴位选择/定制 │ │ │ │ └──────────────┴──────────────┴──────────────┘ ↕ 穴位数据库 ``` ### 技术选型 | 层级 | 技术 | 理由 | |------|------|------| | 前端可视化 | Canvas + SVG | 经络图绘制,支持缩放、点击交互 | | 状态管理 | Zustand | 穴位选择状态,轻量高效 | | 数据格式 | JSON + 静态资源 | 穴位数据存储,易于维护 | | 坐标计算 | TypeScript | 类型安全,精度控制 | | API | RESTful | 与现有架构一致 | ## 数据模型 ### 穴位数据结构 ```typescript // 穴位定义 interface Acupoint { id: string; // 穴位编号 name: string; // 穴位名称 pinyin: string; // 拼音 meridian: string; // 所属经络 location: { // 定位描述 anatomical: string; // 解剖位置描述 relative: string; // 相对位置描述(如:大椎穴下3寸) }; offset: { // 相对于大椎穴的偏移(基于标准体型) x: number; // 横向偏移(cm) y: number; // 纵向偏移(cm) }; effects: string[]; // 功效(驱寒、祛风等) contraindications: string[]; // 禁忌 } // 用户体型数据 interface BodyData { userId: string; gender: 'male' | 'female'; age: number; height: number; // cm weight: number; // kg shoulderHeight?: number; // 肩高(传感器检测) shoulderWidth?: number; // 肩宽(传感器检测) } // 计算后的穴位位置 interface CalculatedAcupoint { acupointId: string; position: { x: number; // 实际坐标(cm) y: number; }; confidence: number; // 置信度 0-1 } ``` ### 穴位数据库(初始数据) ```typescript const ACUPOINT_DATABASE: Acupoint[] = [ { id: 'dazhui', name: '大椎穴', pinyin: 'dazhui', meridian: '督脉', location: { anatomical: '第七颈椎棘突下凹陷中', relative: '基准穴位,位于背部最高点' }, offset: { x: 0, y: 0 }, effects: ['驱寒', '祛湿', '解表', '清热'], contraindications: ['孕妇慎用'] }, { id: 'shenzhu', name: '身柱穴', pinyin: 'shenzhu', meridian: '督脉', location: { anatomical: '第三胸椎棘突下凹陷中', relative: '大椎穴下3寸' }, offset: { x: 0, y: -7.5 }, effects: ['止咳平喘', '安神'], contraindications: [] }, // ... 其他穴位 ]; ``` ## 穴位坐标计算算法 ### 基于体型的缩放因子 ```typescript function calculateScaleFactor(bodyData: BodyData): { x: number; y: number } { // 标准体型参考(中国成年人平均) const STANDARD = { male: { height: 170, weight: 65, shoulderWidth: 42 }, female: { height: 158, weight: 55, shoulderWidth: 36 } }; const ref = STANDARD[bodyData.gender]; // 高度缩放因子 const heightScale = bodyData.height / ref.height; // 宽度缩放因子(肩宽与身高正相关) const estimatedShoulderWidth = bodyData.shoulderWidth || (bodyData.gender === 'male' ? bodyData.height * 0.247 : bodyData.height * 0.228); const widthScale = estimatedShoulderWidth / ref.shoulderWidth; return { x: widthScale, y: heightScale }; } function calculateAcupointPosition( acupoint: Acupoint, bodyData: BodyData ): CalculatedAcupoint { const scale = calculateScaleFactor(bodyData); // 应用缩放因子 const position = { x: acupoint.offset.x * scale.x, y: acupoint.offset.y * scale.y }; // 置信度计算(基于数据完整性) let confidence = 0.8; // 基础置信度 if (bodyData.shoulderHeight) confidence += 0.1; if (bodyData.shoulderWidth) confidence += 0.1; return { acupointId: acupoint.id, position, confidence }; } ``` ## API 设计 ### 端点 | 方法 | 路径 | 描述 | |------|------|------| | GET | `/api/acupoints` | 获取所有穴位列表 | | GET | `/api/acupoints/:id` | 获取单个穴位详情 | | POST | `/api/acupoints/calculate` | 计算用户穴位位置 | | POST | `/api/acupoints/recommend` | 基于症状推荐穴位 | | GET | `/api/acupoints/meridian/:name` | 获取某经络的所有穴位 | ### 请求/响应示例 ```typescript // POST /api/acupoints/calculate // Request { "userId": "user_001", "bodyData": { "gender": "male", "height": 175, "weight": 70 } } // Response { "userId": "user_001", "basePoint": { "id": "dazhui", "name": "大椎穴", "position": { "x": 0, "y": 0 } }, "acupoints": [ { "acupointId": "shenzhu", "name": "身柱穴", "position": { "x": 0, "y": -7.72 }, "confidence": 0.8 }, // ... ] } ``` ## 前端组件设计 ### 组件结构 ``` src/components/ ├── MeridianGraph/ │ ├── MeridianGraph.tsx # 经络图主组件 │ ├── AcupointMarker.tsx # 穴位标记点 │ ├── AcupointTooltip.tsx # 穴位信息浮层 │ └── MeridianGraph.types.ts ├── AcupointSelector/ │ ├── AcupointSelector.tsx # 穴位选择器 │ ├── AcupointCard.tsx # 穴位卡片 │ └── AcupointFilter.tsx # 穴位筛选 └── AcupointRecommendation/ ├── SymptomInput.tsx # 症状输入 └── RecommendationList.tsx # 推荐列表 ``` ### 经络图组件 ```tsx // MeridianGraph.tsx interface MeridianGraphProps { acupoints: CalculatedAcupoint[]; selectedIds: string[]; onSelect: (id: string) => void; mode: 'view' | 'select' | 'edit'; } export function MeridianGraph({ acupoints, selectedIds, onSelect, mode }: MeridianGraphProps) { // 人体背部轮廓 SVG // 穴位点击交互 // 缩放和平移 // 穴位高亮显示 } ``` ## 实施步骤 ### Wave 1: 数据层 1. 创建穴位数据库 JSON 2. 实现坐标计算算法 3. 编写单元测试 ### Wave 2: API 层 1. 创建 `/api/acupoints` 路由 2. 实现计算和推荐接口 3. API 文档 ### Wave 3: 前端可视化 1. 经络图 SVG 组件 2. 穴位交互逻辑 3. 穴位选择器组件 ### Wave 4: 集成 1. 与用户信息模块集成 2. 与艾灸方案模块集成 3. 端到端测试 ## 回滚策略 每个 Wave 完成后提交 Git,可随时回退到上一稳定状态。