简介:传智健康PRD文档1是一份面向健康管理系统产品设计与开发人员的需求说明书,适合产品经理、后端开发及医疗信息化学习者参考,用于理解健康管理平台从需求定义到功能落地的完整思路。压缩包内共1个docx文件,约890KB,内容围绕数据结构管理、用户角色与功能详情展开。文档重点梳理了健康评估指标的数据结构,包括序号、指标名称、参考值、单位及针对性别等字段的规范化定义,并划分了用户、医护人员、管理员三类角色。产品概述部分覆盖会员管理与预约管理两大模块,配有产品结构图、流程图与特性描述,功能详情延伸至健康评估与健康干预,逐项说明操作流程。已有599人学习下载,读者可借此掌握健康类产品的需求文档结构、字段设计规范与业务流程拆解方式,为同类项目提供可直接借鉴的PRD模板与设计参考。
1. 传智健康PRD文档1:一份需求文档为什么值得当成工程资产来拆
很多团队做健康管理类系统,第一版需求文档写得像功能清单:登录、建档、预约、报告查询,一条条列完就丢给开发。结果做到一半发现字段对不上、状态流转缺分支、权限模型没定义,返工成本比重新写一遍还高。传智健康PRD文档1这个标题,核心不是“传智健康”四个字,而是“PRD文档”作为可执行资产怎么落地。它解决的是从业务语言到工程语言之间的翻译问题,适合产品经理、后端开发和测试三方一起看。我见过太多项目把PRD当一次性交付物,最后文档和代码各说各话,这份东西如果拆得对,能直接变成接口定义、状态机和验收用例的源头。
2. 把PRD拆成可执行资产:从角色建模到状态机落地
2.1 先定角色边界,再谈功能列表
健康管理类系统的第一层复杂度不在功能多少,而在角色交叉。常见角色至少有:普通用户、健康管理师、医生、运营管理员、系统管理员。PRD文档1里如果只写“用户可以预约”,那开发只能猜:预约是用户主动发起还是管理师代约?取消预约谁有权限?这些不写清楚,后面接口鉴权就是一团乱麻。
我一般会先做一张角色-资源-操作矩阵,把每个角色能碰的数据范围钉死。比如普通用户只能看自己的健康档案,健康管理师能看所负责用户的档案但不能改医生诊断,医生能写诊断但不能改预约排班。这张矩阵不是PRD的附属品,它应该直接映射到后端的权限注解和数据库行级过滤。
# 角色-资源-操作矩阵的代码化表达(伪代码,用于生成权限配置) ROLE_PERMISSIONS = { "user": { "health_record": ["read_own"], "appointment": ["create_own", "cancel_own", "read_own"], }, "health_manager": { "health_record": ["read_assigned", "update_assigned"], "appointment": ["create_for_assigned", "cancel_for_assigned"], }, "doctor": { "health_record": ["read_assigned", "write_diagnosis"], "appointment": ["read_assigned", "confirm"], }, "admin": { "health_record": ["read_all"], "appointment": ["read_all", "force_cancel"], "user": ["create", "disable"], }, }这段配置的逻辑是:权限判断不写在业务代码的if-else里,而是抽成独立矩阵,接口层只做一次校验。参数说明上,read_own和read_assigned的区别在于数据范围过滤条件不同,前者拼user_id = current_user,后者拼manager_id = current_user。这样改权限时只动矩阵,不动业务逻辑。
2.2 状态机是PRD里最容易漏掉的部分
预约单的状态流转,PRD文档1如果只写“待确认、已确认、已完成、已取消”,那测试用例都写不全。实际至少要考虑:用户提交后是待确认,管理师确认后是已确认,医生接诊后是进行中,服务结束后是待评价,评价完才是已完成。取消又分用户取消、管理师取消、系统超时取消,每种取消后的退款和通知逻辑不一样。
我习惯在PRD阶段就画状态迁移表,而不是等到开发画流程图。表里至少四列:当前状态、触发事件、目标状态、附加动作。附加动作包括发通知、写日志、改库存、触发退款。这张表直接决定后端是写一个状态机引擎还是硬编码switch。
# 预约状态机的最小实现(基于transitions库风格,此处用字典模拟) APPOINTMENT_TRANSITIONS = { ("pending", "manager_confirm"): ("confirmed", ["notify_user", "lock_schedule"]), ("pending", "user_cancel"): ("cancelled", ["release_schedule"]), ("pending", "timeout"): ("cancelled", ["release_schedule", "notify_user"]), ("confirmed", "doctor_start"): ("in_progress", ["notify_user"]), ("confirmed", "user_cancel"): ("cancelled", ["release_schedule", "refund_if_paid"]), ("in_progress", "doctor_finish"): ("pending_review", ["notify_user"]), ("pending_review", "user_review"): ("completed", ["write_review"]), ("pending_review", "timeout"): ("completed", ["auto_review"]), } def transition(current_state, event): key = (current_state, event) if key not in APPOINTMENT_TRANSITIONS: raise ValueError(f"非法流转: {current_state} -> {event}") next_state, actions = APPOINTMENT_TRANSITIONS[key] for action in actions: execute_action(action) # 执行通知、退款等副作用 return next_state逻辑说明:所有状态变更必须走transition函数,禁止在业务代码里直接改状态字段。参数说明:actions列表里的副作用按顺序执行,refund_if_paid需要幂等,避免重复退款。这样测试只需要覆盖所有合法key和几个非法key,用例数量可控。
2.3 字段定义要细到数据库列和校验规则
PRD里写“用户填写身高体重”,开发建表时可能用varchar(50),测试时输入“一百七十”也能存进去。正确做法是在PRD阶段就给出字段清单:字段名、类型、长度、是否必填、取值范围、单位、校验时机。比如身高用decimal(5,2),单位厘米,范围50到250,提交时校验;体重用decimal(5,2),单位千克,范围10到300。
-- 健康档案核心字段定义(MySQL) CREATE TABLE health_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT '关联用户', height_cm DECIMAL(5,2) DEFAULT NULL COMMENT '身高,厘米,50-250', weight_kg DECIMAL(5,2) DEFAULT NULL COMMENT '体重,千克,10-300', blood_pressure VARCHAR(20) DEFAULT NULL COMMENT '血压,格式如120/80', fasting_glucose DECIMAL(4,1) DEFAULT NULL COMMENT '空腹血糖,mmol/L,2.0-30.0', record_date DATE NOT NULL COMMENT '记录日期', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user_date (user_id, record_date) ) COMMENT='健康档案表';参数说明:DECIMAL(5,2)表示总共5位,小数2位,最大999.99,够用且不浪费。blood_pressure用字符串是因为要存“120/80”这种格式,拆成两个字段也可以,但查询时拼接更麻烦。索引idx_user_date支撑按用户和时间范围查询,这是最高频的查询模式。
3. 从PRD到接口文档:字段映射与错误码设计
3.1 接口入参出参必须和PRD字段一一对应
很多团队PRD和接口文档是两个人写,字段名不一致,前端对着PRD写,后端对着接口文档写,联调时才发现height和height_cm对不上。我的做法是:PRD里的字段清单直接作为接口文档的字段来源,命名规范统一用下划线,单位写在字段名后缀。接口文档只增加三样东西:HTTP方法、路径、错误码。
// POST /api/v1/health-record 请求体示例 { "user_id": 1001, "height_cm": 175.50, "weight_kg": 70.20, "blood_pressure": "120/80", "fasting_glucose": 5.6, "record_date": "2025-01-15" }逻辑说明:请求体字段名和数据库列名保持一致,减少映射层。user_id在真实场景中应从登录态获取,不放在请求体里,这里为了演示保留。参数说明:record_date用YYYY-MM-DD格式,避免时区歧义;数值型字段前端传数字,后端做范围校验。
3.2 错误码要覆盖PRD里的所有异常分支
PRD文档1如果只写正常流程,错误码就会在开发时临时拍脑袋。我一般要求PRD里每个业务规则后面标注可能的异常,比如“身高范围50-250”对应错误码HEIGHT_OUT_OF_RANGE,“预约时间不能早于当前时间”对应APPOINTMENT_TIME_INVALID。错误码表单独维护,接口返回统一结构。
| 错误码 | HTTP状态 | 含义 | 触发场景 |
|---|---|---|---|
| USER_NOT_FOUND | 404 | 用户不存在 | user_id无效 |
| HEIGHT_OUT_OF_RANGE | 400 | 身高超出范围 | 小于50或大于250 |
| WEIGHT_OUT_OF_RANGE | 400 | 体重超出范围 | 小于10或大于300 |
| APPOINTMENT_TIME_INVALID | 400 | 预约时间无效 | 早于当前时间 |
| APPOINTMENT_STATE_CONFLICT | 409 | 预约状态冲突 | 重复确认或取消 |
| PERMISSION_DENIED | 403 | 无权限操作 | 角色矩阵校验失败 |
这张表的价值在于:测试可以直接按错误码写用例,前端可以按错误码做不同提示,后端可以按错误码做统一异常处理。PRD里每加一条业务规则,就加一行错误码,文档和代码同步演进。
3.3 用PRD驱动测试用例,而不是事后补
测试同学最怕PRD写得模糊,只能凭经验猜。如果PRD里已经有状态迁移表和字段校验规则,测试用例可以自动生成大半。比如状态迁移表里每个合法key对应一个正向用例,每个非法组合对应一个异常用例。字段校验规则里每个范围边界对应边界值用例。
# 根据状态迁移表生成测试用例骨架 def generate_state_test_cases(transitions): cases = [] for (state, event), (next_state, actions) in transitions.items(): cases.append({ "name": f"{state} + {event} -> {next_state}", "given": {"state": state}, "when": {"event": event}, "then": {"state": next_state, "actions": actions}, }) # 补充非法流转用例 all_states = set(s for s, _ in transitions.keys()) all_events = set(e for _, e in transitions.keys()) for state in all_states: for event in all_events: if (state, event) not in transitions: cases.append({ "name": f"非法: {state} + {event}", "given": {"state": state}, "when": {"event": event}, "then": {"error": "INVALID_TRANSITION"}, }) return cases逻辑说明:合法流转生成正向用例,非法组合生成异常用例,覆盖度由状态和事件的笛卡尔积保证。参数说明:actions在测试断言里可以只校验是否被调用,不校验具体实现,保持测试稳定。
4. 避坑:PRD落地时最容易翻车的五个地方
4.1 字段单位不统一,前后端各算各的
现象:前端传身高1.75,后端以为是厘米,存成1.75厘米,展示时变成“1.75cm”。原因:PRD只写“身高”,没写单位。解决:字段名强制带单位后缀,如height_cm、weight_kg,接口文档和数据库列名一致,代码里禁止裸用height。
4.2 状态流转绕过状态机,直接改数据库
现象:运营后台为了“方便”,直接UPDATE预约状态为已完成,跳过了评价和通知。原因:没有强制所有状态变更走统一入口。解决:数据库层加触发器或应用层加拦截,非状态机调用的状态变更直接拒绝,并记录操作日志。
4.3 权限矩阵只写角色不写数据范围
现象:健康管理师A能看到健康管理师B负责的用户档案。原因:PRD只写“管理师可查看档案”,没写“仅限所负责用户”。解决:权限矩阵里每个操作必须标注数据范围,代码里用行级过滤或中间件统一注入manager_id条件。
4.4 错误码在PRD里缺失,开发临时编
现象:前端收到500不知道是参数错还是服务挂了,用户看到“系统繁忙”。原因:PRD没定义异常分支和错误码。解决:PRD每个业务规则后标注错误码,接口文档维护错误码表,前端按错误码做差异化提示。
4.5 测试用例只覆盖正常流程,边界全靠猜
现象:上线后用户输入身高0或者体重999,系统照单全收。原因:PRD没写边界值,测试没依据。解决:字段定义里明确范围和边界,测试用边界值分析法生成用例,代码里做双重校验(前端+后端)。
5. 进阶:把PRD变成可执行的契约测试
5.1 用契约测试锁住PRD和代码的一致性
PRD文档1如果只停留在文档层面,代码一改文档就过期。我的习惯是把PRD里的字段定义、状态迁移、错误码抽成一份机器可读的契约文件,比如YAML或JSON,然后写契约测试:每次代码变更后跑一遍,校验接口实际行为是否和契约一致。
# contract/health_record.yaml endpoint: /api/v1/health-record method: POST request: user_id: {type: integer, required: true} height_cm: {type: number, min: 50, max: 250, required: false} weight_kg: {type: number, min: 10, max: 300, required: false} blood_pressure: {type: string, pattern: "^\\d{2,3}/\\d{2,3}$", required: false} fasting_glucose: {type: number, min: 2.0, max: 30.0, required: false} record_date: {type: string, format: date, required: true} responses: 200: {description: "创建成功"} 400: {error_codes: [HEIGHT_OUT_OF_RANGE, WEIGHT_OUT_OF_RANGE, FASTING_GLUCOSE_OUT_OF_RANGE]} 403: {error_codes: [PERMISSION_DENIED]}这份契约文件由PRD负责人维护,开发提交代码前跑契约测试。参数说明:pattern用正则约束血压格式,min/max约束数值范围,required标记必填。测试框架读取YAML后自动生成请求用例,对比实际响应。
5.2 契约测试的落地步骤
第一步,从PRD里抽出所有接口的字段定义和错误码,写成YAML文件,放在代码仓库的contract/目录下。第二步,选一个契约测试框架,比如Pact或Spring Cloud Contract,或者自己写一个轻量级的校验脚本。第三步,在CI流水线里加一个阶段,跑契约测试,失败则阻断合并。第四步,PRD变更时先改YAML,再改代码,保证文档和代码同步。
# 轻量级契约测试脚本示例(Python + requests) python -m pytest tests/contract/ -v --contract-dir=contract/ # 输出示例: # tests/contract/test_health_record.py::test_height_out_of_range PASSED # tests/contract/test_health_record.py::test_permission_denied PASSED逻辑说明:测试脚本读取YAML里的字段约束,构造边界请求,断言响应状态码和错误码。参数说明:--contract-dir指定契约文件目录,方便多模块管理。这样PRD不再是“写完就忘”的文档,而是每次构建都会验证的活契约。
5.3 一个具体技巧:用PRD字段生成数据库迁移脚本
PRD里的字段清单可以直接生成建表语句或迁移脚本,减少手写SQL的笔误。我一般写一个简单的解析器,读取YAML里的字段定义,输出CREATE TABLE或ALTER TABLE语句。这样PRD改字段,迁移脚本自动更新,DBA审核后执行。
# 从YAML字段定义生成MySQL建表语句(简化版) def generate_create_table(table_name, fields): columns = [] for name, spec in fields.items(): col_type = { "integer": "BIGINT", "number": "DECIMAL(10,2)", "string": "VARCHAR(255)", "date": "DATE", }[spec["type"]] nullable = "NOT NULL" if spec.get("required") else "DEFAULT NULL" comment = spec.get("comment", "") columns.append(f" {name} {col_type} {nullable} COMMENT '{comment}'") return f"CREATE TABLE {table_name} (\n" + ",\n".join(columns) + "\n);"逻辑说明:类型映射表把PRD里的抽象类型转成数据库类型,required决定NOT NULL,comment保留业务含义。参数说明:DECIMAL(10,2)是通用数值类型,实际项目按范围调整。这个脚本不追求覆盖所有DDL特性,只保证字段名、类型、必填和注释不遗漏。
我自己的习惯是:PRD评审通过后,第一件事不是催开发排期,而是把字段清单和状态迁移表抽成契约文件,跑通契约测试再开工。这样后面无论谁改代码,PRD都不会变成黑匣子。希望帮到你。
本文还有配套的精品资源,点击获取