Prechádzať zdrojové kódy

为了快速开发,将原来老文档移动并创建了新文档

mcbaiyun 1 mesiac pred
rodič
commit
cf2f571380
41 zmenil súbory, kde vykonal 2143 pridanie a 0 odobranie
  1. 5 0
      docs/DB/t_blood_glucose_data.txt
  2. 2 0
      docs/DB/t_blood_pressure_data.txt
  3. 3 0
      docs/DB/t_heart_rate_data.txt
  4. 4 0
      docs/DB/t_physical_data.txt
  5. 3 0
      docs/DB/t_user_binding.txt
  6. 6 0
      docs/DB/t_user_info.txt
  7. 6 0
      docs/DB/t_user_token.txt
  8. 183 0
      docs/DB/数据库表结构说明.md
  9. 47 0
      docs/Dev/common目录文件说明.md
  10. 112 0
      docs/Dev/config目录配置类说明.md
  11. 106 0
      docs/Dev/controller目录控制器类说明.md
  12. 118 0
      docs/Dev/enums目录枚举类说明.md
  13. 40 0
      docs/Dev/exception目录异常处理类说明.md
  14. 64 0
      docs/Dev/handler目录处理器类说明.md
  15. 219 0
      docs/Dev/model目录模型类说明.md
  16. 188 0
      docs/Dev/service目录服务类说明.md
  17. 58 0
      docs/Dev/util目录工具类说明.md
  18. 128 0
      docs/Dev/项目设计关键点和接口总结.md
  19. 121 0
      docs/FromWXProgram/复诊.md
  20. 730 0
      docs/New/复诊管理功能设计文档.md
  21. 0 0
      docs/OLD/DataBase/t_blood_glucose_data.txt
  22. 0 0
      docs/OLD/DataBase/t_blood_pressure_data.txt
  23. 0 0
      docs/OLD/DataBase/t_heart_rate_data.txt
  24. 0 0
      docs/OLD/DataBase/t_physical_data.txt
  25. 0 0
      docs/OLD/DataBase/t_user_binding.txt
  26. 0 0
      docs/OLD/DataBase/t_user_info.txt
  27. 0 0
      docs/OLD/DataBase/t_user_token.txt
  28. 0 0
      docs/OLD/DevDesign/查询绑定患者健康数据设计.md
  29. 0 0
      docs/OLD/DevDesign/用户头像本地上传与获取接口设计-项目改动概要.md
  30. 0 0
      docs/OLD/DevDesign/用户头像本地上传与获取接口设计.md
  31. 0 0
      docs/OLD/DevRule/01-代码风格指南.md
  32. 0 0
      docs/OLD/DevRule/02-项目结构规范.md
  33. 0 0
      docs/OLD/DevRule/03-API设计规范.md
  34. 0 0
      docs/OLD/DevRule/04-测试规范.md
  35. 0 0
      docs/OLD/DevRule/05-依赖管理规范.md
  36. 0 0
      docs/OLD/DevRule/06-日志和错误处理规范.md
  37. 0 0
      docs/OLD/DevRule/07-数据库规范.md
  38. 0 0
      docs/OLD/DevRule/08-安全规范.md
  39. 0 0
      docs/OLD/ReadME.md
  40. 0 0
      docs/OLD/Swagger泛型返回类型字段信息不显示问题解决方案.md
  41. 0 0
      docs/OLD/前端ID精度丢失问题解决方案.md

+ 5 - 0
docs/DB/t_blood_glucose_data.txt

@@ -0,0 +1,5 @@
+"id"	"user_id"	"measure_time"	"type"	"value"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988502746686595073"	"1988147181088956418"	"12/11/2025 00:00:00"	"空腹"	"6"	"1"	"12/11/2025 15:02:54"	"1"	"12/11/2025 15:02:54"	"0"	
+"1988502766861197314"	"1988147181088956418"	"12/11/2025 00:00:00"	"随机"	"7.6"	"1"	"12/11/2025 15:02:59"	"1"	"12/11/2025 15:02:59"	"0"	
+"1988502817008295937"	"1988147181088956418"	"5/11/2025 00:00:00"	"空腹"	"5.4"	"1"	"12/11/2025 15:03:11"	"1"	"12/11/2025 15:03:11"	"0"	
+"1988502842404806657"	"1988147181088956418"	"5/11/2025 00:00:00"	"随机"	"8.8"	"1"	"12/11/2025 15:03:17"	"1"	"12/11/2025 15:03:17"	"0"	

+ 2 - 0
docs/DB/t_blood_pressure_data.txt

@@ -0,0 +1,2 @@
+"id"	"user_id"	"systolic_pressure"	"diastolic_pressure"	"measure_time"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988500010511409154"	"1988147181088956418"	"120"	"80"	"12/11/2025 00:00:00"	"1"	"12/11/2025 14:52:02"	"1"	"12/11/2025 14:52:02"	"0"	

+ 3 - 0
docs/DB/t_heart_rate_data.txt

@@ -0,0 +1,3 @@
+"id"	"user_id"	"heart_rate"	"measure_time"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988498386246213634"	"1988147181088956418"	"72"	"12/11/2025 00:00:00"	"1"	"12/11/2025 14:45:34"	"1"	"12/11/2025 14:45:34"	"0"	
+"1988498431406284802"	"1988147181088956418"	"54"	"11/11/2025 00:00:00"	"1"	"12/11/2025 14:45:45"	"1"	"12/11/2025 14:45:45"	"0"	

+ 4 - 0
docs/DB/t_physical_data.txt

@@ -0,0 +1,4 @@
+"id"	"user_id"	"height"	"weight"	"measure_time"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988449152629252097"	"1988147181088956418"	"155"	"65"	"9/11/2025 00:00:00"	"1"	"12/11/2025 11:29:56"	"1"	"12/11/2025 11:29:56"	"0"	
+"1988456421005795330"	"1988147181088956418"	"177"	"90"	"12/11/2025 00:00:00"	"1"	"12/11/2025 11:58:49"	"1"	"12/11/2025 11:58:49"	"0"	
+"1989581733017985026"	"1988147181088956418"	"170"	"78"	"15/11/2025 00:00:00"	"1"	"15/11/2025 14:30:24"	"1"	"15/11/2025 14:30:24"	"0"	

+ 3 - 0
docs/DB/t_user_binding.txt

@@ -0,0 +1,3 @@
+"id"	"patient_user_id"	"bound_user_id"	"binding_type"	"status"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1990104700034510849"	"1988147181088956418"	"1988172854356631553"	"DOCTOR"	"1"	"1"	"17/11/2025 01:08:30"	"1"	"17/11/2025 01:08:30"	"0"	
+"1990322533418975234"	"1988147181088956418"	"1988196438584090626"	"FAMILY"	"1"	"1"	"17/11/2025 15:34:05"	"1"	"17/11/2025 15:34:05"	"0"	

+ 6 - 0
docs/DB/t_user_info.txt

@@ -0,0 +1,6 @@
+"id"	"version"	"create_user"	"create_time"	"update_user"	"update_time"	"remark"	"username"	"password"	"role"	"wx_openid"	"avatar"	"nickname"	"sex"	"phone"	"age"	"address"
+"1988147181088956418"	"23"	"1"	"11/11/2025 15:30:01"	"1"	"17/11/2025 15:13:57"				"3"	"oMrLJ4upWlkcM8ngNnj849sF_sZg"	"1988147181088956418/1763179829156.jpeg"	"患者test"	"1"	"19792741433"	"22"	"重庆市 重庆市"
+"1988172854356631553"	"17"	"1"	"11/11/2025 17:12:02"	"1"	"17/11/2025 15:13:51"				"2"	"oMrLJ4upWlkcM8ngNnj849sF_sZg"	"1988172854356631553/1763182966320.jpeg"	"医生test"	"1"		"21"	
+"1988196438584090626"	"4"	"1"	"11/11/2025 18:45:45"	"1"	"17/11/2025 15:14:01"				"4"	"oMrLJ4upWlkcM8ngNnj849sF_sZg"	"1988196438584090626/1763181495049.jpeg"	"家属test"	"1"	"19723123111"	"22"	"重庆市 重庆市"
+"1988218156098805762"	"0"	"1"	"11/11/2025 20:12:02"	"1"	"11/11/2025 20:12:02"				"3"	"oMrLJ4tt491gE-__o36x3SrJ0kVg"						
+"1989589966487068673"	"0"	"1"	"15/11/2025 15:03:07"	"1"	"15/11/2025 15:03:07"				"2"	"oMrLJ4tt491gE-__o36x3SrJ0kVg"						

+ 6 - 0
docs/DB/t_user_token.txt

@@ -0,0 +1,6 @@
+"id"	"version"	"create_user"	"create_time"	"update_user"	"update_time"	"remark"	"user_id"	"token"	"expire_time"
+"1988147181156065282"	"37"	"1"	"11/11/2025 15:30:01"	"1"	"11/11/2025 15:30:01"		"1988147181088956418"	"7deed423fcc94515b74ab603ab237c00"	"20/11/2025 15:33:51"
+"1988172854419546114"	"14"	"1"	"11/11/2025 17:12:02"	"1"	"11/11/2025 17:12:02"		"1988172854356631553"	"eb49edc2a2464871a0f51604bc4e10e2"	"20/11/2025 17:43:26"
+"1988196438584090627"	"9"	"1"	"11/11/2025 18:45:45"	"1"	"11/11/2025 18:45:45"		"1988196438584090626"	"56ba488c953f42549fb1e6cbe1f12450"	"20/11/2025 15:32:19"
+"1988218156098805763"	"0"	"1"	"11/11/2025 20:12:02"	"1"	"11/11/2025 20:12:02"		"1988218156098805762"	"9cde389298664619aaae9c65cf7ab325"	"14/11/2025 20:12:02"
+"1989589966487068674"	"0"	"1"	"15/11/2025 15:03:07"	"1"	"15/11/2025 15:03:07"		"1989589966487068673"	"3b34609dac4548b0a1d66f057a6fee2c"	"18/11/2025 15:03:07"

+ 183 - 0
docs/DB/数据库表结构说明.md

@@ -0,0 +1,183 @@
+# 数据库表结构说明
+
+本文档详细描述了项目中使用的数据库表结构,包括各表的字段说明和示例数据。
+
+## t_blood_glucose_data(血糖数据表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| user_id | BIGINT | 用户ID |
+| measure_time | DATETIME | 测量时间 |
+| type | VARCHAR | 血糖测量类型 |
+| value | DECIMAL | 血糖值 |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| version | INT | 版本号(乐观锁) |
+| remark | VARCHAR | 备注 |
+
+### 示例数据
+```
+"id"	"user_id"	"measure_time"	"type"	"value"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988502746686595073"	"1988147181088956418"	"12/11/2025 00:00:00"	"空腹"	"6"	"1"	"12/11/2025 15:02:54"	"1"	"12/11/2025 15:02:54"	"0"	
+"1988502766861197314"	"1988147181088956418"	"12/11/2025 00:00:00"	"随机"	"7.6"	"1"	"12/11/2025 15:02:59"	"1"	"12/11/2025 15:02:59"	"0"	
+"1988502817008295937"	"1988147181088956418"	"5/11/2025 00:00:00"	"空腹"	"5.4"	"1"	"12/11/2025 15:03:11"	"1"	"12/11/2025 15:03:11"	"0"	
+"1988502842404806657"	"1988147181088956418"	"5/11/2025 00:00:00"	"随机"	"8.8"	"1"	"12/11/2025 15:03:17"	"1"	"12/11/2025 15:03:17"	"0"	
+```
+
+## t_blood_pressure_data(血压数据表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| user_id | BIGINT | 用户ID |
+| systolic_pressure | INT | 收缩压(mmHg) |
+| diastolic_pressure | INT | 舒张压(mmHg) |
+| measure_time | DATETIME | 测量时间 |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| version | INT | 版本号(乐观锁) |
+| remark | VARCHAR | 备注 |
+
+### 示例数据
+```
+"id"	"user_id"	"systolic_pressure"	"diastolic_pressure"	"measure_time"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988500010511409154"	"1988147181088956418"	"120"	"80"	"12/11/2025 00:00:00"	"1"	"12/11/2025 14:52:02"	"1"	"12/11/2025 14:52:02"	"0"	
+```
+
+## t_heart_rate_data(心率数据表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| user_id | BIGINT | 用户ID |
+| heart_rate | INT | 心率(次/分钟) |
+| measure_time | DATETIME | 测量时间 |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| version | INT | 版本号(乐观锁) |
+| remark | VARCHAR | 备注 |
+
+### 示例数据
+```
+"id"	"user_id"	"heart_rate"	"measure_time"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988498386246213634"	"1988147181088956418"	"72"	"12/11/2025 00:00:00"	"1"	"12/11/2025 14:45:34"	"1"	"12/11/2025 14:45:34"	"0"	
+"1988498431406284802"	"1988147181088956418"	"54"	"11/11/2025 00:00:00"	"1"	"12/11/2025 14:45:45"	"1"	"12/11/2025 14:45:45"	"0"	
+```
+
+## t_physical_data(体格数据表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| user_id | BIGINT | 用户ID |
+| height | DECIMAL | 身高(cm) |
+| weight | DECIMAL | 体重(kg) |
+| measure_time | DATETIME | 测量时间 |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| version | INT | 版本号(乐观锁) |
+| remark | VARCHAR | 备注 |
+
+### 示例数据
+```
+"id"	"user_id"	"height"	"weight"	"measure_time"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1988449152629252097"	"1988147181088956418"	"155"	"65"	"9/11/2025 00:00:00"	"1"	"12/11/2025 11:29:56"	"1"	"12/11/2025 11:29:56"	"0"	
+"1988456421005795330"	"1988147181088956418"	"177"	"90"	"12/11/2025 00:00:00"	"1"	"12/11/2025 11:58:49"	"1"	"12/11/2025 11:58:49"	"0"	
+"1989581733017985026"	"1988147181088956418"	"170"	"78"	"15/11/2025 00:00:00"	"1"	"15/11/2025 14:30:24"	"1"	"15/11/2025 14:30:24"	"0"	
+```
+
+## t_user_binding(用户绑定关系表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| patient_user_id | BIGINT | 患者用户ID |
+| bound_user_id | BIGINT | 被绑定用户ID(医生或家属) |
+| binding_type | VARCHAR | 绑定类型(DOCTOR-医生, FAMILY-家属) |
+| status | INT | 绑定状态(1-有效,0-无效) |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| version | INT | 版本号(乐观锁) |
+| remark | VARCHAR | 备注 |
+
+### 示例数据
+```
+"id"	"patient_user_id"	"bound_user_id"	"binding_type"	"status"	"create_user"	"create_time"	"update_user"	"update_time"	"version"	"remark"
+"1990104700034510849"	"1988147181088956418"	"1988172854356631553"	"DOCTOR"	"1"	"1"	"17/11/2025 01:08:30"	"1"	"17/11/2025 01:08:30"	"0"	
+"1990322533418975234"	"1988147181088956418"	"1988196438584090626"	"FAMILY"	"1"	"1"	"17/11/2025 15:34:05"	"1"	"17/11/2025 15:34:05"	"0"	
+```
+
+## t_user_info(用户信息表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| version | INT | 版本号(乐观锁) |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| remark | VARCHAR | 备注 |
+| username | VARCHAR | 用户名(可选) |
+| password | VARCHAR | 密码(可选) |
+| role | INT | 角色(1-管理员, 2-医生, 3-患者, 4-患者家属) |
+| wx_openid | VARCHAR | 微信ID(可选) |
+| avatar | VARCHAR | 头像(可选) |
+| nickname | VARCHAR | 昵称(可选) |
+| sex | INT | 性别(1-男, 2-女) |
+| phone | VARCHAR | 手机号(可选) |
+| age | INT | 年龄(可选) |
+| address | VARCHAR | 地址(可选) |
+
+### 示例数据
+```
+"id"	"version"	"create_user"	"create_time"	"update_user"	"update_time"	"remark"	"username"	"password"	"role"	"wx_openid"	"avatar"	"nickname"	"sex"	"phone"	"age"	"address"
+"1988147181088956418"	"23"	"1"	"11/11/2025 15:30:01"	"1"	"17/11/2025 15:13:57"				"3"	"oMrLJ4upWlkcM8ngNnj849sF_sZg"	"1988147181088956418/1763179829156.jpeg"	"患者test"	"1"	"19792741433"	"22"	"重庆市 重庆市"
+"1988172854356631553"	"17"	"1"	"11/11/2025 17:12:02"	"1"	"17/11/2025 15:13:51"				"2"	"oMrLJ4upWlkcM8ngNnj849sF_sZg"	"1988172854356631553/1763182966320.jpeg"	"医生test"	"1"		"21"	
+"1988196438584090626"	"4"	"1"	"11/11/2025 18:45:45"	"1"	"17/11/2025 15:14:01"				"4"	"oMrLJ4upWlkcM8ngNnj849sF_sZg"	"1988196438584090626/1763181495049.jpeg"	"家属test"	"1"	"19723123111"	"22"	"重庆市 重庆市"
+"1988218156098805762"	"0"	"1"	"11/11/2025 20:12:02"	"1"	"11/11/2025 20:12:02"				"3"	"oMrLJ4tt491gE-__o36x3SrJ0kVg"						
+"1989589966487068673"	"0"	"1"	"15/11/2025 15:03:07"	"1"	"15/11/2025 15:03:07"				"2"	"oMrLJ4tt491gE-__o36x3SrJ0kVg"						
+```
+
+## t_user_token(用户认证表)
+
+### 表结构
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| version | INT | 版本号(乐观锁) |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| remark | VARCHAR | 备注 |
+| user_id | BIGINT | 用户ID |
+| token | VARCHAR | 认证令牌 |
+| expire_time | DATETIME | 过期时间 |
+
+### 示例数据
+```
+"id"	"version"	"create_user"	"create_time"	"update_user"	"update_time"	"remark"	"user_id"	"token"	"expire_time"
+"1988147181156065282"	"37"	"1"	"11/11/2025 15:30:01"	"1"	"11/11/2025 15:30:01"		"1988147181088956418"	"7deed423fcc94515b74ab603ab237c00"	"20/11/2025 15:33:51"
+"1988172854419546114"	"14"	"1"	"11/11/2025 17:12:02"	"1"	"11/11/2025 17:12:02"		"1988172854356631553"	"eb49edc2a2464871a0f51604bc4e10e2"	"20/11/2025 17:43:26"
+"1988196438584090627"	"9"	"1"	"11/11/2025 18:45:45"	"1"	"11/11/2025 18:45:45"		"1988196438584090626"	"56ba488c953f42549fb1e6cbe1f12450"	"20/11/2025 15:32:19"
+"1988218156098805763"	"0"	"1"	"11/11/2025 20:12:02"	"1"	"11/11/2025 20:12:02"		"1988218156098805762"	"9cde389298664619aaae9c65cf7ab325"	"14/11/2025 20:12:02"
+"1989589966487068674"	"0"	"1"	"15/11/2025 15:03:07"	"1"	"15/11/2025 15:03:07"		"1989589966487068673"	"3b34609dac4548b0a1d66f057a6fee2c"	"18/11/2025 15:03:07"
+```

+ 47 - 0
docs/Dev/common目录文件说明.md

@@ -0,0 +1,47 @@
+# common 目录文件说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/common` 目录下包含了项目中的通用类,这些类为整个应用程序提供基础的数据结构和响应格式。
+
+## Page.java
+
+### 用途
+Page 类是一个通用分页数据封装类,用于包装分页查询的结果,便于统一返回分页数据给前端。
+
+### 实现细节
+- 使用了泛型 `<T>`,可以适用于任何类型的数据列表
+- 包含以下属性:
+  - `pageNum`: 当前页码
+  - `pageSize`: 每页大小
+  - `total`: 数据总数
+  - `list`: 当前页数据列表
+- 构造函数接收页码、页面大小和数据列表作为参数
+- 内部使用 `com.github.pagehelper.PageInfo` 来计算总记录数等分页信息
+- 使用了 Lombok 注解 `@Getter` 和 `@Setter` 自动生成 getter 和 setter 方法
+
+### 使用场景
+主要用于所有需要分页功能的业务模块,如用户数据、健康数据等的分页查询结果返回。
+
+## R.java
+
+### 用途
+R 类是统一响应结果封装类,用于标准化 API 接口的返回格式,包含成功和失败两种情况的处理方法。
+
+### 实现细节
+- 使用了泛型 `<T>`,可以封装任意类型的数据
+- 包含以下属性:
+  - `code`: 响应状态码
+  - `message`: 响应消息
+  - `data`: 响应数据
+  - `timestamp`: 时间戳(毫秒)
+  - `requestId`: 请求 ID,用于追踪原始请求
+  - `traceId`: 链路追踪 ID,用于分布式追踪
+- 提供了四个静态方法来创建响应对象:
+  - `success(Integer code, String message)`: 创建无数据的成功响应
+  - `success(Integer code, String message, T data)`: 创建带数据的成功响应
+  - `fail(Integer code, String message)`: 创建无数据的失败响应
+  - `fail(Integer code, String message, T data)`: 创建带数据的失败响应
+- 使用 `TraceUtils` 工具类获取 requestId 和 traceId 信息
+- 使用了 Lombok 注解 `@Data` 自动生成 getter、setter、toString 等方法
+
+### 使用场景
+用于所有对外暴露的 RESTful API 接口,确保接口返回格式的一致性,方便前端处理。

+ 112 - 0
docs/Dev/config目录配置类说明.md

@@ -0,0 +1,112 @@
+# config 目录配置类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/config` 目录下包含了项目中的各种配置类,这些类负责配置应用程序的各种组件和服务。
+
+## AuthInterceptor.java
+
+### 用途
+AuthInterceptor 是一个身份验证拦截器,负责从请求中提取 token 并进行验证,确保只有经过身份验证的用户才能访问受保护的资源。
+
+### 实现细节
+- 实现了 Spring 的 HandlerInterceptor 接口
+- 支持多种 token 传递方式:
+  - 标准的 Authorization header(Bearer token)
+  - X-Token header
+  - token header
+  - 请求体中的 token 参数
+- 优先使用标准的 Authorization header 方式
+- 对于 OPTIONS 预检请求会自动放行
+- 验证通过后会将用户 ID 和权限组信息放入 request attribute 中供后续使用
+- 使用 TokenService 来验证 token 的有效性
+
+## AvatarProperties.java
+
+### 用途
+AvatarProperties 是用于配置用户头像相关属性的配置类,包括存储路径、文件大小限制和允许的文件类型。
+
+### 实现细节
+- 使用 @ConfigurationProperties 注解,前缀为 "avatar"
+- 配置项包括:
+  - rootPath: 头像存储的根路径(绝对路径)
+  - maxSize: 最大文件大小,默认为 2MB
+  - allowedTypes: 允许的文件类型,默认为 jpg,png,jpeg,webp
+- 使用 Spring Boot 的类型安全配置属性机制
+
+## CorsConfig.java
+
+### 用途
+CorsConfig 是跨域资源共享(CORS)配置类,用于配置允许跨域请求的相关设置。
+
+### 实现细节
+- 实现了 WebMvcConfigurer 接口
+- 允许来自 http://localhost:3000 的跨域请求
+- 允许 GET、POST、PUT、DELETE、OPTIONS 方法
+- 允许所有头部信息
+- 暴露 X-Custom-Header、X-Token、Authorization 头部给前端
+- 预检请求有效期为 3600 秒
+- 允许发送 Cookie 凭据
+
+## Knife4jConfig.java
+
+### 用途
+Knife4jConfig 是 API 文档配置类,用于配置 Swagger/OpenAPI 文档的相关信息。
+
+### 实现细节
+- 配置了 API 文档的基本信息:
+  - 标题:"ChronicDiseaseApp API"
+  - 描述:"慢病APP 后端接口文档"
+  - 版本:"v1"
+  - 联系人信息
+- 配置了服务器地址为 http://localhost:8080
+- 使用 GroupedOpenApi 扫描 work.baiyun.chronicdiseaseapp 包下的控制器
+
+## MybatisPlusConfig.java
+
+### 用途
+MybatisPlusConfig 是 MyBatis-Plus 配置类,用于配置 MyBatis-Plus 的各种插件。
+
+### 实现细节
+- 配置了 MybatisPlusInterceptor 拦截器
+- 添加了 OptimisticLockerInnerInterceptor 乐观锁插件
+- 用于处理数据库操作中的并发控制
+
+## RestTemplateConfig.java
+
+### 用途
+RestTemplateConfig 是 RestTemplate 配置类,用于创建和配置 RestTemplate Bean。
+
+### 实现细节
+- 创建了一个 RestTemplate Bean 实例
+- 用于发起 HTTP 请求,例如调用微信接口等
+
+## TraceInterceptor.java
+
+### 用途
+TraceInterceptor 是请求跟踪拦截器,用于为每个请求生成唯一的 requestId 和 traceId,便于日志追踪和问题排查。
+
+### 实现细节
+- 实现了 HandlerInterceptor 接口
+- 从请求头中获取或生成 requestId 和 traceId
+- 将这些 ID 放入 MDC(Mapped Diagnostic Context)中,便于日志输出
+- 在请求完成后清理 MDC 中的信息
+
+## WeChatProperties.java
+
+### 用途
+WeChatProperties 是微信相关配置属性类,用于配置微信小程序的 appid 和 secret。
+
+### 实现细节
+- 使用 @ConfigurationProperties 注解,前缀为 "wechat"
+- 包含 appid 和 secret 两个配置项
+- 用于微信登录等相关功能
+
+## WebMvcConfig.java
+
+### 用途
+WebMvcConfig 是 Spring MVC 配置类,用于注册和配置各种拦截器。
+
+### 实现细节
+- 注册了 AuthInterceptor 和 TraceInterceptor 两个拦截器
+- TraceInterceptor 会拦截所有请求,但排除 Swagger 相关路径
+- AuthInterceptor 会拦截所有请求,但排除登录接口和 Swagger 相关路径
+- 确保 TraceInterceptor 在 AuthInterceptor 之前执行,以便正确设置 MDC 上下文

+ 106 - 0
docs/Dev/controller目录控制器类说明.md

@@ -0,0 +1,106 @@
+# controller 目录控制器类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/controller` 目录下包含了项目中的各种控制器类,这些类负责处理 HTTP 请求,协调业务逻辑,并返回响应结果。
+
+## BloodGlucoseDataController.java
+
+### 用途
+BloodGlucoseDataController 是血糖数据相关的控制器,负责处理血糖数据的增删查操作。
+
+### 实现细节
+- 提供了添加血糖数据的接口
+- 提供了分页查询血糖数据的接口
+- 提供了医生/家属查询患者血糖数据的接口(需要绑定关系)
+- 提供了删除血糖数据的接口
+- 使用了 Swagger 注解进行接口文档说明
+- 所有接口都进行了异常处理和日志记录
+
+## BloodPressureDataController.java
+
+### 用途
+BloodPressureDataController 是血压数据相关的控制器,负责处理血压数据的增删查操作。
+
+### 实现细节
+- 提供了添加血压数据的接口
+- 提供了分页查询血压数据的接口
+- 提供了医生/家属查询患者血压数据的接口(需要绑定关系)
+- 提供了删除血压数据的接口
+- 使用了 Swagger 注解进行接口文档说明
+- 所有接口都进行了异常处理和日志记录
+
+## GeoController.java
+
+### 用途
+GeoController 是地理信息相关的控制器,负责获取地理位置信息。
+
+### 实现细节
+- 提供了根据经纬度获取最近位置的接口
+- 使用 RestTemplate 调用外部地理服务
+- 使用了 Swagger 注解进行接口文档说明
+- 进行了异常处理和日志记录
+
+## HeartRateDataController.java
+
+### 用途
+HeartRateDataController 是心率数据相关的控制器,负责处理心率数据的增删查操作。
+
+### 实现细节
+- 提供了添加心率数据的接口
+- 提供了分页查询心率数据的接口
+- 提供了医生/家属查询患者心率数据的接口(需要绑定关系)
+- 提供了删除心率数据的接口
+- 使用了 Swagger 注解进行接口文档说明
+- 所有接口都进行了异常处理和日志记录
+
+## PhysicalDataController.java
+
+### 用途
+PhysicalDataController 是体格数据相关的控制器,负责处理身高、体重等体格数据的增删查操作。
+
+### 实现细节
+- 提供了添加体格数据的接口
+- 提供了分页查询体格数据的接口
+- 提供了医生/家属查询患者体格数据的接口(需要绑定关系)
+- 提供了删除体格数据的接口
+- 使用了 Swagger 注解进行接口文档说明
+- 所有接口都进行了异常处理和日志记录
+- 对分页结果进行了详细的 VO 转换
+
+## UserAvatarController.java
+
+### 用途
+UserAvatarController 是用户头像相关的控制器,负责处理用户头像的上传和获取。
+
+### 实现细节
+- 提供了上传/更新用户头像的接口
+- 提供了获取用户头像的接口
+- 支持 multipart/form-data 类型的文件上传
+- 使用了 Swagger 注解进行接口文档说明
+- 进行了详细的异常处理和权限验证
+
+## UserBindingController.java
+
+### 用途
+UserBindingController 是用户绑定关系相关的控制器,负责处理用户之间的绑定关系。
+
+### 实现细节
+- 提供了创建用户绑定关系的接口
+- 提供了删除用户绑定关系的接口
+- 提供了分页查询患者绑定关系列表的接口
+- 提供了分页查询用户被绑定关系列表的接口
+- 提供了检查用户绑定关系的接口
+- 使用了 Swagger 注解进行接口文档说明
+- 所有接口都进行了异常处理和日志记录
+
+## WeChatController.java
+
+### 用途
+WeChatController 是微信相关接口的控制器,负责处理微信小程序的登录和用户信息管理。
+
+### 实现细节
+- 提供了根据微信 code 获取 openid 并创建/返回用户 token 的接口
+- 提供了根据 token 获取当前用户信息的接口
+- 提供了更新用户信息的接口
+- 支持多种 token 传递方式(Authorization header、X-Token header、token header 或请求体)
+- 使用了 Swagger 注解进行接口文档说明
+- 进行了详细的参数验证和异常处理

+ 118 - 0
docs/Dev/enums目录枚举类说明.md

@@ -0,0 +1,118 @@
+# enums 目录枚举类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/enums` 目录下包含了项目中的各种枚举类,这些类用于定义系统中使用的固定值集合,提高代码的可读性和类型安全性。
+
+## BloodGlucoseType.java
+
+### 用途
+BloodGlucoseType 是血糖测量类型的枚举类,定义了不同时间点的血糖测量类型。
+
+### 实现细节
+- 包含7种血糖测量类型:
+  - FASTING: 空腹(早餐前测量)
+  - AFTER_BREAKFAST: 早餐后(早餐后2小时测量)
+  - BEFORE_LUNCH: 午餐前(午餐前测量)
+  - AFTER_LUNCH: 午餐后(午餐后2小时测量)
+  - BEFORE_DINNER: 晚餐前(晚餐前测量)
+  - AFTER_DINNER: 晚餐后(晚餐后2小时测量)
+  - BEFORE_BED: 睡前(睡前测量)
+- 每个枚举值包含 code、name 和 description 三个属性
+- 使用 @EnumValue 注解标记 code 字段,用于 MyBatis-Plus 枚举类型处理
+- 提供 fromCode 方法根据 code 值获取对应的枚举实例
+- 重写了 toString 方法返回 name 值
+
+## ErrorCode.java
+
+### 用途
+ErrorCode 是系统错误码枚举类,定义了系统中使用的各种错误码及其描述信息。
+
+### 实现细节
+- 按功能模块划分错误码范围:
+  - 通用错误码(1000-1999):系统错误、参数错误、权限相关等
+  - 用户相关错误码(2000-2999):用户不存在、密码错误等
+  - 微信相关错误码(3000-3999):微信授权、openid 获取等
+  - 数据相关错误码(4000-4999):数据访问、删除、保存等
+  - 地理位置相关错误码(5000-5999):地理位置服务相关
+- 每个错误码包含 code 和 message 两个属性
+- 提供 getCode 和 getMessage 方法获取错误码和描述信息
+
+## ExceptionResultCode.java
+
+### 用途
+ExceptionResultCode 是异常结果码枚举类,定义了系统中常见的异常类型及其编码。
+
+### 实现细节
+- 包含两种异常类型:
+  - VALID_EXCEPTION: 参数校验异常(code=400)
+  - EXCEPTION: 系统异常(code=400)
+- 每个枚举值包含 code 和 message 两个属性
+- 提供 getCode、getMessage 和 getMsg 方法获取编码和消息
+
+## FailResultCode.java
+
+### 用途
+FailResultCode 是失败结果码枚举类,定义了用户相关操作失败的错误码。
+
+### 实现细节
+- 包含三种失败类型:
+  - USER_NOT_EXIST: 用户不存在(code=300)
+  - USER_PASSWORD_ERROR: 用户密码错误(code=301)
+  - USER_NOT_LOGIN: 用户未登录(code=302)
+- 每个枚举值包含 code 和 message 两个属性
+- 提供 getCode 和 getMessage 方法获取编码和消息
+
+## Gender.java
+
+### 用途
+Gender 是性别枚举类,用于表示用户的性别信息。
+
+### 实现细节
+- 包含两种性别:
+  - MALE: 男(code=1)
+  - FEMALE: 女(code=2)
+- 使用 @EnumValue 注解标记 code 字段,用于 MyBatis-Plus 枚举类型处理
+- 提供 fromCode 方法根据 code 值获取对应的枚举实例
+- 重写了 toString 方法返回描述信息
+
+## PermissionGroup.java
+
+### 用途
+PermissionGroup 是权限组枚举类,定义了系统的不同用户角色类型。
+
+### 实现细节
+- 包含四种权限组:
+  - SYS_ADMIN: 管理员(code=1)
+  - DOCTOR: 医生(code=2)
+  - PATIENT: 患者(code=3)
+  - PATIENT_FAMILY: 患者家属(code=4)
+- 使用 @EnumValue 注解标记 code 字段,用于 MyBatis-Plus 枚举类型处理
+- 提供 fromCode 方法根据 code 值获取对应的枚举实例
+- 重写了 toString 方法返回描述信息
+
+## SuccessResultCode.java
+
+### 用途
+SuccessResultCode 是成功结果码枚举类,定义了系统中各种成功操作的状态码。
+
+### 实现细节
+- 包含五种成功类型:
+  - SUCCESS: 操作成功(code=200)
+  - AUTHENTICATION_SUCCESS: 认证成功(code=200)
+  - REFRESH_ACCESS_TOKEN_SUCCESS: Token刷新成功(code=200)
+  - L0GOUT_SUCCESS: 退出登录成功(code=20)
+- 每个枚举值包含 code 和 msg 两个属性
+- 提供 getCode 和 getMsg 方法获取编码和消息
+
+## UserBindingType.java
+
+### 用途
+UserBindingType 是用户绑定类型枚举类,定义了用户之间绑定关系的类型。
+
+### 实现细节
+- 包含两种绑定类型:
+  - DOCTOR: 医生
+  - FAMILY: 家属
+- 每个枚举值包含 code 和 description 两个属性
+- 使用 @EnumValue 注解标记 code 字段,用于 MyBatis-Plus 枚举类型处理
+- 提供 fromCode 方法根据 code 值获取对应的枚举实例
+- 重写了 toString 方法返回描述信息

+ 40 - 0
docs/Dev/exception目录异常处理类说明.md

@@ -0,0 +1,40 @@
+# exception 目录异常处理类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/exception` 目录下包含了项目中的自定义异常类和全局异常处理器,用于统一处理系统中发生的各种异常。
+
+## CustomException.java
+
+### 用途
+CustomException 是自定义的运行时业务异常类,用于封装业务逻辑中出现的异常情况,交由全局异常处理器进行统一处理。
+
+### 实现细节
+- 继承自 RuntimeException,是一个非受检异常
+- 包含一个 int 类型的 code 字段,用于表示异常的错误码
+- 提供两个构造方法:
+  - CustomException(String message):仅传入异常信息,错误码默认为 -1
+  - CustomException(int code, String message):传入错误码和异常信息
+- 提供 getCode() 方法获取错误码
+
+## CustomExceptionHandler.java
+
+### 用途
+CustomExceptionHandler 是全局异常处理器,使用 @RestControllerAdvice 注解,用于统一拦截和处理系统中发生的各种异常,并返回统一格式的错误响应。
+
+### 实现细节
+- 使用 @RestControllerAdvice 注解,对所有控制器中的异常进行统一处理
+- 处理多种类型的异常:
+  - BindException:参数绑定异常,通常由 @Valid 验证失败引起
+  - ConstraintViolationException:约束违反异常,处理参数验证错误
+  - IllegalArgumentException:非法参数异常
+  - IllegalStateException:非法状态异常
+  - CustomException:自定义业务异常
+  - Exception:其他未处理的异常
+- 对不同类型的异常进行不同的处理:
+  - BindException:提取所有错误信息并拼接成字符串返回
+  - ConstraintViolationException:提取所有约束违反信息并拼接成字符串返回
+  - IllegalArgumentException:记录警告日志并返回参数错误信息
+  - IllegalStateException:记录错误日志并返回系统错误信息
+  - CustomException:根据异常中的错误码和信息返回相应错误响应
+  - Exception:记录错误日志并返回系统错误信息
+- 所有异常处理方法都返回统一的 R<Void> 响应格式
+- 使用日志记录各种异常信息,便于问题排查

+ 64 - 0
docs/Dev/handler目录处理器类说明.md

@@ -0,0 +1,64 @@
+# handler 目录处理器类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/handler` 目录下包含了项目中的各种处理器类,主要用于处理 MyBatis 的类型映射和实体对象的元数据处理。
+
+## CustomMetaObjectHandler.java
+
+### 用途
+CustomMetaObjectHandler 是 MyBatis-Plus 的元对象字段处理器,用于自动填充实体对象中的公共字段,如创建时间和更新时间等。
+
+### 实现细节
+- 实现了 MetaObjectHandler 接口
+- 在插入数据时自动填充以下字段:
+  - createTime:创建时间,填充为当前时间
+  - updateTime:更新时间,填充为当前时间
+  - createUser:创建人 ID,如果为空则填充默认值
+  - updateUser:更新人 ID,如果为空则填充默认值
+- 在更新数据时自动填充以下字段:
+  - updateTime:更新时间,填充为当前时间
+  - updateUser:更新人 ID,如果为空则填充默认值
+- 使用了 Hutool 工具库的 ObjectUtil 来判断字段是否为空
+- 使用 @Component 注解注册为 Spring 组件
+
+## GenderTypeHandler.java
+
+### 用途
+GenderTypeHandler 是 MyBatis 的类型处理器,用于处理 Gender 枚举类型与数据库字段之间的转换。
+
+### 实现细节
+- 继承 BaseTypeHandler<Gender> 基类
+- 使用 @MappedTypes(Gender.class) 注解标记处理的类型
+- 实现了四个抽象方法:
+  - setNonNullParameter:将 Gender 枚举转换为整数存储到数据库
+  - getNullableResult:从数据库结果中获取整数值并转换为 Gender 枚举
+- Gender 枚举在数据库中以整数形式存储(1=男,2=女)
+- 支持从 ResultSet 和 CallableStatement 中读取数据
+
+## PermissionGroupTypeHandler.java
+
+### 用途
+PermissionGroupTypeHandler 是 MyBatis 的类型处理器,用于处理 PermissionGroup 枚举类型与数据库字段之间的转换。
+
+### 实现细节
+- 继承 BaseTypeHandler<PermissionGroup> 基类
+- 使用 @MappedTypes(PermissionGroup.class) 注解标记处理的类型
+- 实现了四个抽象方法:
+  - setNonNullParameter:将 PermissionGroup 枚举转换为整数存储到数据库
+  - getNullableResult:从数据库结果中获取整数值并转换为 PermissionGroup 枚举
+- PermissionGroup 枚举在数据库中以整数形式存储(1=管理员,2=医生,3=患者,4=患者家属)
+- 支持从 ResultSet 和 CallableStatement 中读取数据
+
+## UserBindingTypeHandler.java
+
+### 用途
+UserBindingTypeHandler 是 MyBatis 的类型处理器,用于处理 UserBindingType 枚举类型与数据库字段之间的转换。
+
+### 实现细节
+- 继承 BaseTypeHandler<UserBindingType> 基类
+- 使用 @MappedTypes(UserBindingType.class) 注解标记处理的类型
+- 使用 @MappedJdbcTypes(JdbcType.VARCHAR) 注解标记处理的 JDBC 类型
+- 实现了四个抽象方法:
+  - setNonNullParameter:将 UserBindingType 枚举转换为字符串存储到数据库
+  - getNullableResult:从数据库结果中获取字符串值并转换为 UserBindingType 枚举
+- UserBindingType 枚举在数据库中以字符串形式存储("DOCTOR"=医生,"FAMILY"=家属)
+- 支持从 ResultSet 和 CallableStatement 中读取数据

+ 219 - 0
docs/Dev/model目录模型类说明.md

@@ -0,0 +1,219 @@
+# model 目录模型类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/model` 目录下包含了项目中的模型类,分为 po(持久化对象)和 vo(值对象)两个子目录。这些类用于表示系统中的数据结构和传输对象。
+
+## po 包(持久化对象)
+
+### BaseEntity.java
+
+#### 用途
+BaseEntity 是所有持久化对象的基类,包含了实体对象的通用字段。
+
+#### 实现细节
+- 实现了 Serializable 接口
+- 包含以下通用字段:
+  - id: 主键ID,使用雪花算法生成
+  - version: 版本号,用于乐观锁
+  - createUser: 创建者ID
+  - createTime: 创建时间
+  - updateUser: 更新者ID
+  - updateTime: 更新时间
+  - remark: 备注
+- 使用了 MyBatis-Plus 注解进行数据库映射
+- 使用了 Jackson 注解进行 JSON 序列化处理
+- 使用了 Lombok 注解简化 getter 和 setter 方法
+
+### BloodGlucoseData.java
+
+#### 用途
+BloodGlucoseData 是血糖数据实体类,对应数据库中的 t_blood_glucose_data 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - userId: 用户ID
+  - measureTime: 测量时间
+  - type: 血糖测量类型
+  - value: 血糖值
+- 使用了 Swagger 注解进行 API 文档说明
+
+### BloodPressureData.java
+
+#### 用途
+BloodPressureData 是血压数据实体类,对应数据库中的 t_blood_pressure_data 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - userId: 用户ID
+  - systolicPressure: 收缩压(mmHg)
+  - diastolicPressure: 舒张压(mmHg)
+  - measureTime: 测量时间
+- 使用了 Swagger 注解进行 API 文档说明
+
+### HeartRateData.java
+
+#### 用途
+HeartRateData 是心率数据实体类,对应数据库中的 t_heart_rate_data 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - userId: 用户ID
+  - heartRate: 心率(次/分钟)
+  - measureTime: 测量时间
+- 使用了 Swagger 注解进行 API 文档说明
+
+### PhysicalData.java
+
+#### 用途
+PhysicalData 是体格数据实体类,对应数据库中的 t_physical_data 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - userId: 用户ID
+  - height: 身高(cm)
+  - weight: 体重(kg)
+  - measureTime: 测量时间
+- 使用了 Swagger 注解进行 API 文档说明
+
+### UserBinding.java
+
+#### 用途
+UserBinding 是用户绑定关系实体类,对应数据库中的 t_user_binding 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - patientUserId: 患者用户ID
+  - boundUserId: 被绑定用户ID(医生或家属)
+  - bindingType: 绑定类型(DOCTOR-医生, FAMILY-家属)
+  - status: 绑定状态(1-有效,0-无效)
+- 使用了自定义的 UserBindingTypeHandler 进行类型处理
+
+### UserInfo.java
+
+#### 用途
+UserInfo 是用户信息实体类,对应数据库中的 t_user_info 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - username: 用户名
+  - password: 密码
+  - role: 角色(权限组)
+  - wxOpenid: 微信ID
+  - avatar: 头像
+  - nickname: 昵称
+  - sex: 性别
+  - phone: 手机号
+  - age: 年龄
+  - address: 地址
+- 使用了自定义的 PermissionGroupTypeHandler 和 GenderTypeHandler 进行类型处理
+
+### UserToken.java
+
+#### 用途
+UserToken 是用户认证令牌实体类,对应数据库中的 t_user_token 表。
+
+#### 实现细节
+- 继承自 BaseEntity
+- 包含以下字段:
+  - userId: 用户ID
+  - token: 认证令牌
+  - expireTime: 过期时间
+- 设计说明:
+  1. 每次登录成功后,生成新的token,覆盖旧的token
+  2. token有效期为72小时
+  3. 每次请求时,检查token是否过期,若过期则返回未登录状态
+  4. 每次请求时,若token快过期(小于24小时),则延长token有效期
+  5. token的生成格式为uuid,存储在DB中
+
+## vo 包(值对象/视图对象)
+
+### BaseQueryRequest.java
+
+#### 用途
+BaseQueryRequest 是基础查询请求对象,用于分页查询的参数传递。
+
+#### 实现细节
+- 包含以下字段:
+  - pageNum: 页码,默认值为1
+  - pageSize: 每页大小,默认值为10,最大值为100
+  - startTime: 开始时间
+  - endTime: 结束时间
+- 使用了验证注解确保参数的有效性
+
+### AuthPrincipal.java
+
+#### 用途
+AuthPrincipal 是认证主体对象,包含用户ID和权限组信息。
+
+#### 实现细节
+- 包含以下字段:
+  - userId: 用户ID
+  - role: 权限组
+- 提供了默认构造方法和带参构造方法
+- 提供了 getter 和 setter 方法
+
+### UserBindingResponse.java
+
+#### 用途
+UserBindingResponse 是用户绑定关系响应对象,用于返回用户绑定关系信息。
+
+#### 实现细节
+- 包含以下字段:
+  - id: 记录ID
+  - patientUserId: 患者用户ID
+  - boundUserId: 被绑定用户ID(医生或家属)
+  - bindingType: 绑定类型
+  - status: 绑定状态
+  - createTime: 创建时间
+  - boundUserNickname: 被绑定用户昵称
+  - boundUserPhone: 被绑定用户手机号
+
+### BloodGlucoseDataResponse.java
+
+#### 用途
+BloodGlucoseDataResponse 是血糖数据响应对象,用于返回血糖数据信息。
+
+#### 实现细节
+- 包含以下字段:
+  - id: 记录ID
+  - measureTime: 测量时间
+  - type: 血糖测量类型
+  - value: 血糖值
+  - createTime: 创建时间
+
+### AddBloodGlucoseDataRequest.java
+
+#### 用途
+AddBloodGlucoseDataRequest 是添加血糖数据请求对象,用于接收添加血糖数据的参数。
+
+#### 实现细节
+- 包含以下字段:
+  - type: 血糖测量类型(必填)
+  - value: 血糖值(必填,范围1.0-30.0)
+  - measureTime: 测量时间(必填)
+- 使用了验证注解确保参数的有效性
+
+### PhysicalDataPageResponse.java
+
+#### 用途
+PhysicalDataPageResponse 是体格数据分页响应对象,用于返回分页查询的体格数据结果。
+
+#### 实现细节
+- 包含以下字段:
+  - records: 数据列表
+  - total: 总数
+  - size: 每页大小
+  - current: 当前页码
+  - orders: 排序字段
+  - optimizeCountSql: 是否优化 count SQL
+  - searchCount: 是否搜索 count
+  - optimizeJoinOfCountSql: 是否优化 join count SQL
+  - maxLimit: 最大限制
+  - countId: countId
+  - pages: 总页数
+- 内部定义了 OrderItem 静态类用于表示排序信息

+ 188 - 0
docs/Dev/service目录服务类说明.md

@@ -0,0 +1,188 @@
+# service 目录服务类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/service` 目录下包含了项目中的服务接口和实现类,这些类负责处理业务逻辑,是系统的核心部分。
+
+## 接口类
+
+### BloodGlucoseDataService.java
+
+#### 用途
+BloodGlucoseDataService 是血糖数据服务接口,定义了血糖数据相关业务操作的方法。
+
+#### 方法说明
+- addBloodGlucoseData: 添加血糖数据
+- listBloodGlucoseData: 分页查询当前用户的血糖数据
+- listBloodGlucoseDataByPatient: 分页查询指定患者的血糖数据(需要绑定关系)
+- deleteBloodGlucoseData: 删除指定ID的血糖数据
+
+### BloodPressureDataService.java
+
+#### 用途
+BloodPressureDataService 是血压数据服务接口,定义了血压数据相关业务操作的方法。
+
+#### 方法说明
+- addBloodPressureData: 添加血压数据
+- listBloodPressureData: 分页查询当前用户的血压数据
+- listBloodPressureDataByPatient: 分页查询指定患者的血压数据(需要绑定关系)
+- deleteBloodPressureData: 删除指定ID的血压数据
+
+### HeartRateDataService.java
+
+#### 用途
+HeartRateDataService 是心率数据服务接口,定义了心率数据相关业务操作的方法。
+
+#### 方法说明
+- addHeartRateData: 添加心率数据
+- listHeartRateData: 分页查询当前用户的心率数据
+- listHeartRateDataByPatient: 分页查询指定患者的心率数据(需要绑定关系)
+- deleteHeartRateData: 删除指定ID的心率数据
+
+### PhysicalDataService.java
+
+#### 用途
+PhysicalDataService 是体格数据服务接口,定义了体格数据相关业务操作的方法。
+
+#### 方法说明
+- addPhysicalData: 添加体格数据
+- listPhysicalData: 分页查询当前用户的体格数据
+- listPhysicalDataByPatient: 分页查询指定患者的体格数据(需要绑定关系)
+- deletePhysicalData: 删除指定ID的体格数据
+
+### TokenService.java
+
+#### 用途
+TokenService 是令牌服务接口,负责用户认证令牌的生成、验证和撤销。
+
+#### 方法说明
+- createToken: 为用户生成新的令牌
+- validateToken: 校验令牌并返回认证主体信息
+- revokeToken: 撤销指定令牌
+
+### UserAvatarService.java
+
+#### 用途
+UserAvatarService 是用户头像服务接口,负责用户头像的保存和加载。
+
+#### 方法说明
+- saveAvatar: 保存用户头像并返回相对路径
+- loadAvatarAsResource: 根据用户ID加载头像资源
+
+### UserBindingService.java
+
+#### 用途
+UserBindingService 是用户绑定服务接口,负责处理用户之间的绑定关系。
+
+#### 方法说明
+- createUserBinding: 创建用户绑定关系
+- deleteUserBinding: 删除用户绑定关系
+- listBindingsByPatient: 分页查询患者的绑定关系列表
+- listBindingsByBoundUser: 分页查询用户被绑定的关系列表
+- checkUserBinding: 检查绑定关系是否存在
+
+### UserService.java
+
+#### 用途
+UserService 是用户服务接口,继承自 MyBatis-Plus 的 IService 接口。
+
+### WeChatService.java
+
+#### 用途
+WeChatService 是微信服务接口,负责与微信小程序相关的操作。
+
+#### 方法说明
+- getOpenId: 根据微信code获取用户openid
+
+## 实现类
+
+### BloodGlucoseDataServiceImpl.java
+
+#### 用途
+BloodGlucoseDataServiceImpl 是血糖数据服务的实现类,实现了血糖数据相关业务逻辑。
+
+#### 实现细节
+- 实现了添加血糖数据功能,自动关联当前用户
+- 实现了分页查询功能,支持按时间范围查询
+- 实现了根据患者ID查询血糖数据功能,包含权限控制(家属只能查看一年内的数据)
+- 实现了删除血糖数据功能,验证数据归属后才允许删除
+
+### BloodPressureDataServiceImpl.java
+
+#### 用途
+BloodPressureDataServiceImpl 是血压数据服务的实现类,实现了血压数据相关业务逻辑。
+
+#### 实现细节
+- 实现了添加血压数据功能,自动关联当前用户
+- 实现了分页查询功能,支持按时间范围查询
+- 实现了根据患者ID查询血压数据功能,包含权限控制(家属只能查看一年内的数据)
+- 实现了删除血压数据功能,验证数据归属后才允许删除
+
+### HeartRateDataServiceImpl.java
+
+#### 用途
+HeartRateDataServiceImpl 是心率数据服务的实现类,实现了心率数据相关业务逻辑。
+
+#### 实现细节
+- 实现了添加心率数据功能,自动关联当前用户
+- 实现了分页查询功能,支持按时间范围查询
+- 实现了根据患者ID查询心率数据功能,包含权限控制(家属只能查看一年内的数据)
+- 实现了删除心率数据功能,验证数据归属后才允许删除
+
+### PhysicalDataServiceImpl.java
+
+#### 用途
+PhysicalDataServiceImpl 是体格数据服务的实现类,实现了体格数据相关业务逻辑。
+
+#### 实现细节
+- 实现了添加体格数据功能,自动关联当前用户
+- 实现了分页查询功能,支持按时间范围查询
+- 实现了根据患者ID查询体格数据功能,包含权限控制(家属只能查看一年内的数据)
+- 实现了删除体格数据功能,验证数据归属后才允许删除
+
+### TokenServiceImpl.java
+
+#### 用途
+TokenServiceImpl 是令牌服务的实现类,负责用户认证令牌的生成、验证和撤销。
+
+#### 实现细节
+- 使用 TokenUtil 工具类生成令牌
+- 实现了令牌的创建和更新逻辑
+- 实现了令牌验证功能,包含过期检查和自动续期机制
+- 实现了令牌撤销功能
+
+### UserAvatarServiceImpl.java
+
+#### 用途
+UserAvatarServiceImpl 是用户头像服务的实现类,负责用户头像的保存和加载。
+
+#### 实现细节
+- 实现了头像文件的上传和保存,包含文件类型和大小验证
+- 实现了防止路径穿越的安全措施
+- 实现了头像文件的加载功能
+- 通过事务管理确保数据库更新和文件保存的一致性
+
+### UserBindingServiceImpl.java
+
+#### 用途
+UserBindingServiceImpl 是用户绑定服务的实现类,负责处理用户之间的绑定关系。
+
+#### 实现细节
+- 实现了创建用户绑定关系功能,支持更新已存在的绑定关系
+- 实现了删除用户绑定关系功能(软删除)
+- 实现了分页查询绑定关系功能,支持按患者ID或绑定用户ID查询
+- 实现了批量查询用户信息以避免N+1问题
+- 实现了绑定关系检查功能
+
+### UserServiceImpl.java
+
+#### 用途
+UserServiceImpl 是用户服务的实现类,继承自 MyBatis-Plus 的 ServiceImpl 类。
+
+### WeChatServiceImpl.java
+
+#### 用途
+WeChatServiceImpl 是微信服务的实现类,负责与微信小程序相关的操作。
+
+#### 实现细节
+- 实现了调用微信 jscode2session 接口获取 openid 的功能
+- 包含了完整的错误处理和日志记录
+- 使用 RestTemplate 进行 HTTP 请求

+ 58 - 0
docs/Dev/util目录工具类说明.md

@@ -0,0 +1,58 @@
+# util 目录工具类说明
+
+在 `src/main/java/work/baiyun/chronicdiseaseapp/util` 目录下包含了项目中的各种工具类,这些类提供了通用的工具方法,用于处理文件操作、安全验证、令牌生成和链路追踪等。
+
+## FileUtils.java
+
+### 用途
+FileUtils 是文件操作工具类,提供了一系列处理文件相关操作的静态方法。
+
+### 实现细节
+- getExtension 方法:从文件名中提取扩展名,并转换为小写
+- isAllowedType 方法:检查文件扩展名是否在允许的类型列表中
+- ensureDirectory 方法:确保指定路径的目录存在,如果不存在则创建
+- isValidSize 方法:检查文件大小是否在允许的最大值范围内
+- 使用了 Spring 的 DataSize 类来处理文件大小限制
+
+## SecurityUtils.java
+
+### 用途
+SecurityUtils 是安全工具类,用于从请求上下文中获取当前用户信息。
+
+### 实现细节
+- getCurrentUserId 方法:
+  - 从当前请求上下文中获取 currentUserId 属性
+  - 支持 Long、Integer、String 三种类型的用户ID
+  - 如果获取失败则抛出自定义异常
+- getCurrentUserRole 方法:
+  - 从当前请求上下文中获取 currentUserRole 属性(PermissionGroup 类型)
+  - 支持 PermissionGroup、Integer/Long code、或 String(枚举名或 code)三种类型
+  - 如果获取失败则抛出自定义异常
+- 使用了 Spring 的 RequestContextHolder 来获取当前请求上下文
+
+## TokenUtil.java
+
+### 用途
+TokenUtil 是令牌工具类,用于生成和管理用户认证令牌。
+
+### 实现细节
+- 定义了令牌的有效期常量 TOKEN_TTL_HOURS(72小时)
+- 定义了令牌刷新阈值 REFRESH_THRESHOLD_HOURS(24小时)
+- generateToken 方法:使用 UUID 生成无连接符的令牌字符串
+
+## TraceUtils.java
+
+### 用途
+TraceUtils 是链路追踪工具类,用于获取或生成请求ID和追踪ID,便于日志追踪和问题排查。
+
+### 实现细节
+- getRequestId 方法:
+  - 优先从 MDC(Mapped Diagnostic Context)中获取 requestId
+  - 如果 MDC 中没有,则从请求头或请求属性中获取
+  - 如果都获取不到,则生成新的 UUID 作为 requestId
+- getTraceId 方法:
+  - 优先从 MDC 中获取 traceId
+  - 如果 MDC 中没有,则从请求头或请求属性中获取
+  - 如果都获取不到,则生成新的 UUID 作为 traceId
+- generateId 方法:生成 UUID 字符串作为ID
+- 使用了 SLF4J 的 MDC 来管理诊断上下文

+ 128 - 0
docs/Dev/项目设计关键点和接口总结.md

@@ -0,0 +1,128 @@
+# 项目设计关键点和接口总结
+
+## 1. 项目概述
+
+本项目是一个慢性病管理应用程序的后端系统,主要提供健康数据管理、用户认证、用户绑定关系管理等功能。系统采用 Spring Boot 框架构建,使用 MyBatis-Plus 进行数据持久化操作。
+
+## 2. 核心设计关键点
+
+### 2.1 统一响应格式
+系统采用 [R.java](file:///D:/慢病APP/other/EmulatedProject/src/main/java/work/baiyun/chronicdiseaseapp/common/R.java) 类作为统一的 API 响应格式,确保所有接口返回一致的结构:
+- code: 响应状态码
+- message: 响应消息
+- data: 响应数据
+- timestamp: 时间戳
+- requestId: 请求ID
+- traceId: 链路追踪ID
+
+### 2.2 身份认证与授权
+- 使用 Token 机制进行身份认证
+- Token 有效期为 72 小时,小于 24 小时自动刷新
+- 通过 [AuthInterceptor](file:///D:/慢病APP/other/EmulatedProject/src/main/java/work/baiyun/chronicdiseaseapp/config/AuthInterceptor.java) 拦截器实现身份验证
+- 支持多种 Token 传递方式:Authorization header、X-Token header、token header 或请求体参数
+
+### 2.3 数据模型设计
+- 使用 BaseEntity 作为所有实体类的基类,包含通用字段(ID、版本号、创建时间、更新时间等)
+- 采用 MyBatis-Plus 实现数据库操作
+- 使用自定义 TypeHandler 处理枚举类型与数据库字段的映射
+
+### 2.4 异常处理机制
+- 通过 [CustomExceptionHandler](file:///D:/慢病APP/other/EmulatedProject/src/main/java/work/baiyun/chronicdiseaseapp/exception/CustomExceptionHandler.java) 统一处理各种异常
+- 定义了详细的错误码体系,便于前端进行精确的错误处理
+
+### 2.5 用户绑定关系
+- 实现了患者与医生、患者与家属之间的绑定关系管理
+- 不同类型的绑定关系具有不同的数据访问权限(如家属只能查看一年内的数据)
+
+### 2.6 链路追踪
+- 通过 [TraceInterceptor](file:///D:/慢病APP/other/EmulatedProject/src/main/java/work/baiyun/chronicdiseaseapp/config/TraceInterceptor.java) 实现请求跟踪
+- 使用 MDC(Mapped Diagnostic Context)记录 requestId 和 traceId,便于日志追踪
+
+## 3. 核心业务模块
+
+### 3.1 健康数据管理模块
+包括血糖、血压、心率和体格数据的管理,各模块具有相似的结构:
+- 添加数据接口
+- 分页查询当前用户数据接口
+- 分页查询指定患者数据接口(需绑定关系)
+- 删除数据接口
+
+### 3.2 用户管理模块
+- 微信登录认证(通过 code 获取 openid)
+- 用户信息管理(头像、昵称、手机号等)
+- 用户头像上传和获取
+
+### 3.3 用户绑定模块
+- 创建用户绑定关系
+- 删除用户绑定关系
+- 查询绑定关系列表
+- 检查绑定关系是否存在
+
+### 3.4 地理位置模块
+- 根据经纬度获取最近位置信息
+
+## 4. 主要接口列表
+
+### 4.1 血糖数据接口 (`/blood-glucose`)
+- `POST /blood-glucose/add` - 添加血糖数据
+- `POST /blood-glucose/list` - 分页查询血糖数据
+- `POST /blood-glucose/list-by-bound-user` - 医生/家属分页查询患者血糖数据
+- `POST /blood-glucose/delete` - 删除血糖数据
+
+### 4.2 血压数据接口 (`/blood-pressure`)
+- `POST /blood-pressure/add` - 添加血压数据
+- `POST /blood-pressure/list` - 分页查询血压数据
+- `POST /blood-pressure/list-by-bound-user` - 医生/家属分页查询患者血压数据
+- `POST /blood-pressure/delete` - 删除血压数据
+
+### 4.3 心率数据接口 (`/heart-rate`)
+- `POST /heart-rate/add` - 添加心率数据
+- `POST /heart-rate/list` - 分页查询心率数据
+- `POST /heart-rate/list-by-bound-user` - 医生/家属分页查询患者心率数据
+- `POST /heart-rate/delete` - 删除心率数据
+
+### 4.4 体格数据接口 (`/physical`)
+- `POST /physical/add` - 添加体格数据
+- `POST /physical/list` - 分页查询体格数据
+- `POST /physical/list-by-bound-user` - 医生/家属分页查询患者体格数据
+- `POST /physical/delete` - 删除体格数据
+
+### 4.5 用户绑定接口 (`/user-binding`)
+- `POST /user-binding/create` - 创建用户绑定关系
+- `POST /user-binding/delete` - 删除用户绑定关系
+- `POST /user-binding/list-by-patient` - 分页查询患者的绑定关系列表
+- `POST /user-binding/list-by-bound-user` - 分页查询用户被绑定的关系列表
+- `POST /user-binding/check` - 检查用户绑定关系
+
+### 4.6 微信接口
+- `POST /get_openid` - 获取 openid
+- `POST /user_info` - 获取用户信息
+- `POST /update_user_info` - 更新用户信息
+
+### 4.7 用户头像接口
+- `POST /user/avatar/upload` - 上传/更新用户头像
+- `GET /user/avatar/{userId}` - 获取用户头像
+
+### 4.8 地理位置接口 (`/geo`)
+- `GET /geo/nearest` - 获取最近位置
+
+## 5. 技术特点
+
+### 5.1 安全性
+- 多种 Token 传递方式支持
+- 绑定关系权限控制
+- 文件上传安全检查(类型、大小、路径穿越防护)
+
+### 5.2 可维护性
+- 统一的异常处理机制
+- 完整的 API 文档(集成 Swagger/Knife4j)
+- 详细的日志记录和链路追踪
+
+### 5.3 扩展性
+- 模块化设计,各业务模块相对独立
+- 使用 Spring Boot 自动配置特性
+- 枚举类型定义便于扩展新的业务类型
+
+## 6. 部署和运行
+
+项目支持通过 Maven 命令运行:`mvn spring-boot:run`,也可通过项目根目录下的批处理脚本运行应用。

+ 121 - 0
docs/FromWXProgram/复诊.md

@@ -0,0 +1,121 @@
+# 复诊管理(Follow-up)前端设计提取
+
+本文档从当前项目代码中提取并集中说明与“复诊管理”相关的前端页面、导航、交互与现状(哪些页面已实现、哪些页面为占位/待实现),并给出必要的接口建议与 UI 行为说明,方便前后端对齐与产品设计对接。
+
+---
+
+## 概览
+
+当前仓库涉及与“复诊/预约/随访”相关的前端位置:
+- 医生侧
+  - `pages/doctor/index/index.vue`(医生首页)
+    - 功能入口:功能卡片 -> “复诊管理”(点击跳转 `/pages/doctor/manage/followup`)
+    - 今日提醒卡片展示 `todayReminders.followUpCount`(待处理复诊数量)
+    - 活动区展示 `patientActivities`,其中某些项描述会提到“完成了今日复诊”作为活动示例
+  - `pages/doctor/index/my-patients.vue`(我的病人)
+    - 为每个病人提供 “邀请复诊” 按钮(当前为占位,弹窗提示“邀请复诊功能开发中”)
+  - `pages/doctor/manage/index.vue`(后台管理)
+    - 菜单项包含健康资讯 / 快捷问题 / 药品管理,暂无具体复诊管理页面(仅占位)
+
+- 患者侧
+  - `pages/patient/index/my-doctor.vue`(我的医生)
+    - 提供“预约复诊”按钮(当前占位,弹窗提示“预约功能开发中”)
+  - `pages/patient/index/index.vue`(患者首页)
+    - 页面卡片中有“医生预约 / 一键预约复诊”入口(多为占位)
+
+- 家属
+  - `pages/patient-family/index/my-family.vue`(我的家人)
+    - 提供“邀请复诊”按钮(占位)
+
+- 健康数据提醒触发
+  - `pages/patient/health/details/*`(血压 / 血糖 / 心率 / 体格)
+    - 当用户把某些指标值录入超阈值时,会通过 `uni.showModal` 弹窗建议“建议尽快复诊” —— 这是在数据输入端触发复诊建议的逻辑点。
+
+---
+
+## 现有 UI 元素与交互(逐页面细节)
+
+### 1. `pages/doctor/index/index.vue`(医生首页)
+- 位置/导航
+  - 功能卡片(右侧): 点击 `复诊管理` 调用 `onItemClick('复诊管理')` 并导航至 `'/pages/doctor/manage/followup'`。
+  - 卡片内文字:"复诊管理" / 子标题:"安排复诊随访"
+- 今日提醒(左上)
+  - 显示 `todayReminders.followUpCount`(数字)与标签 "待处理复诊"
+  - 点击该提醒也会跳转 `/pages/doctor/manage/followup`
+- 活动卡
+  - patientActivities list,示例中包含描述如 "患者李四完成了今日复诊"。这是显示最近复诊/活动的 UI 区块。
+
+### 2. `pages/doctor/index/my-patients.vue`(我的病人)
+- 每个病人条目(`.patient-card`)中的操作按钮:
+  - "健康数据" -> 跳转 `/pages/public/health/index?patientId=...&bindingType=DOCTOR`
+  - "健康动态" -> 占位
+  - "发送提醒" -> 占位
+  - "邀请复诊" -> 占位(弹窗提示)
+- Invite 按钮逻辑 
+  - 目前 `inviteRevisit(patient)` 仅调用 `uni.showToast({ title: '邀请复诊功能开发中' })`,无后端调用或模态输入。
+
+### 3. `pages/patient/index/my-doctor.vue`(我的医生)
+- 操作按钮:
+  - "预约复诊" -> `makeAppointment()` -> 占位(弹窗)
+  - "联系医生" -> 占位
+  - "绑定医生" -> 占位(但绑定可由 `userBinding.create` 实现)
+- 用户故事期望:点击预约 -> 打开预约表单/选择时间 -> 提交 -> 后端创建预约 -> 跳转到预约详情或显示成功消息
+
+### 4. `pages/patient-family/index/my-family.vue`(我的家人)
+- 同 `my-patients.vue`:提供 "邀请复诊" 按钮 -> 目前为占位
+
+### 5. `pages/patient/health/details/*`(指标页面)
+- 在 `confirmAdd()` 或提交数据时:
+  - 对异常阈值(如 血压 > 140/90、空腹血糖 > 7.0)弹窗 `uni.showModal({title: '异常', content: '建议尽快复诊'})` 提醒病人。
+  - 该弹窗仅提示,不包含直接预约/联系医生按钮。
+
+---
+
+## 推荐复诊流程(前端设计建议)
+为了把现有占位功能逐步实现,建议采用以下最小可用流程:
+
+1. 医生端:`复诊管理` 列表
+   - 页面 URL:`/pages/doctor/manage/followup`(当前缺失,需创建)
+   - 列表展示:待处理/已完成的复诊邀请(字段:id、patientName、patientAvatar、status, scheduledTime, createdAt)
+   - 操作:查看 -> 详情页(可触发: 同意、拒绝、变更时间、发送消息)
+   - 新增:医生可以手动发起 `邀请复诊`(直接从 `我的病人` 发起),或基于预设规则(异常数据触发复诊建议)创建新的复诊任务
+2. 医生向患者发起复诊邀请
+   - 弹窗表单:选择患者、选择/输入时间、可选备注
+   - 后端:POST /followup/invite
+   - 前端:调用后刷新 `followup` 列表并更新 `todayReminders.followUpCount`
+3. 患者端:预约/接收复诊邀请
+   - 患者接收邀请后:可接受/拒绝或重新选择时间
+   - 管理患者预约列表:`/pages/patient/appointments`(未来新增)
+4. 健康数据自动建议复诊
+   - 当 `confirmAdd()` 检测到异常时,除了 `showModal`,增加 "现在预约" 按钮或 "联系医生",直接跳转到 `my-doctor` -> 预约交互,或弹出预约表单
+
+---
+
+## 已知数据/接口(前端已使用或建议)
+- 已在页面调用但未封装到 `src/api`:
+  - GET `https://wx.baiyun.work/doctor/today_reminders` —— 返回 `{ followUpCount, abnormalCount }`
+  - GET `https://wx.baiyun.work/doctor/patient_activities` —— 返回列表活动
+- 建议新增(后端与前端协作)接口:
+  - GET /followup/list?doctorUserId=...&status=... -- 复诊列表
+  - POST /followup/invite -- 邀请复诊(doctor -> patient)
+    - body: { patientUserId, scheduledTime, remark }
+  - POST /followup/accept -- 病人接受邀请
+  - POST /followup/decline -- 病人拒绝邀请
+  - GET /appointment/list?userId=... -- 患者预约列表
+  - POST /appointment/create -- 患者创建预约
+
+---
+
+## UI/UX 注意点与改进建议
+- `inviteRevisit` / `makeAppointment` 的占位按钮应支持模态表单输入(时间、备注),并在提交成功后把患者活动和医生 `todayReminders.followUpCount` 更新。
+- 与 `user-binding` 集成:邀请/预约时须确保 `patientUserId` 的可用性与安全权限(仅当医生与患者绑定时允许邀请)。
+- 在异常值提示中(`showModal`),建议加入可操作按钮(如“现在预约”)以提升转化。
+
+---
+
+## 当前缺失/待实现要点(行动项)
+- [ ] 创建 `pages/doctor/manage/followup.vue` 页面骨架,并实现最小列表展示及跳转功能。
+- [ ] 在 `src/api` 中新增 `followup.ts`,封装 `list/invite/accept/decline` 接口并使用统一请求包装 `src/api/request.ts`。
+- [ ] 在 `my-patients` 和 `my-family` 中把 `inviteRevisit` 从占位修改为调用 `followup/invite`,并添加预约对话框(time picker)
+- [ ] 在患者`my-doctor` 页面实现预约交互:打开选择时间 -> POST `/appointment/create`
+- [ ] 在异常检测逻辑(数据页)添加“立即预约”动作:跳转到预约流程或直接发起 

+ 730 - 0
docs/New/复诊管理功能设计文档.md

@@ -0,0 +1,730 @@
+# 复诊管理功能设计文档
+
+## 1. 功能概述
+
+复诊管理功能旨在为患者和医生提供一个便捷的复诊预约和管理平台。该功能允许患者发起复诊请求,医生审核并安排复诊时间,双方可以查看和管理复诊记录。
+
+## 2. 数据库设计
+
+### 2.1 复诊记录表 (t_follow_up)
+
+根据项目数据库表设计规范,创建复诊记录表:
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| id | BIGINT | 主键ID |
+| patient_user_id | BIGINT | 患者用户ID |
+| doctor_user_id | BIGINT | 医生用户ID |
+| appointment_time | DATETIME | 预约时间 |
+| actual_time | DATETIME | 实际就诊时间 |
+| status | VARCHAR(20) | 复诊状态 (PENDING-待确认, CONFIRMED-已确认, CANCELLED-已取消, COMPLETED-已完成) |
+| reason | TEXT | 复诊原因 |
+| notes | TEXT | 备注 |
+| create_user | BIGINT | 创建者ID |
+| create_time | DATETIME | 创建时间 |
+| update_user | BIGINT | 更新者ID |
+| update_time | DATETIME | 更新时间 |
+| version | INT | 版本号(乐观锁) |
+| remark | VARCHAR(255) | 备注 |
+
+## 3. 实体类设计
+
+### 3.1 PO实体类 (FollowUp.java)
+
+```java
+package work.baiyun.chronicdiseaseapp.model.po;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "复诊记录表")
+@TableName("t_follow_up")
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class FollowUp extends BaseEntity {
+    @Schema(description = "患者用户ID")
+    @TableField("patient_user_id")
+    private Long patientUserId;
+
+    @Schema(description = "医生用户ID")
+    @TableField("doctor_user_id")
+    private Long doctorUserId;
+
+    @Schema(description = "预约时间")
+    @TableField("appointment_time")
+    private LocalDateTime appointmentTime;
+
+    @Schema(description = "实际就诊时间")
+    @TableField("actual_time")
+    private LocalDateTime actualTime;
+
+    @Schema(description = "复诊状态 (PENDING-待确认, CONFIRMED-已确认, CANCELLED-已取消, COMPLETED-已完成)")
+    @TableField("status")
+    private String status;
+
+    @Schema(description = "复诊原因")
+    @TableField("reason")
+    private String reason;
+
+    @Schema(description = "备注")
+    @TableField("notes")
+    private String notes;
+}
+```
+
+### 3.2 VO对象
+
+#### 3.2.1 请求对象
+
+```java
+// CreateFollowUpRequest.java
+package work.baiyun.chronicdiseaseapp.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "创建复诊请求")
+@Data
+public class CreateFollowUpRequest {
+    @Schema(description = "医生用户ID", required = true)
+    @NotNull(message = "医生ID不能为空")
+    private Long doctorUserId;
+
+    @Schema(description = "预约时间", required = true)
+    @NotNull(message = "预约时间不能为空")
+    private LocalDateTime appointmentTime;
+
+    @Schema(description = "复诊原因")
+    private String reason;
+}
+
+// UpdateFollowUpRequest.java
+package work.baiyun.chronicdiseaseapp.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "更新复诊请求")
+@Data
+public class UpdateFollowUpRequest {
+    @Schema(description = "复诊记录ID", required = true)
+    @NotNull(message = "复诊记录ID不能为空")
+    private Long id;
+
+    @Schema(description = "预约时间")
+    private LocalDateTime appointmentTime;
+
+    @Schema(description = "复诊状态")
+    private String status;
+
+    @Schema(description = "备注")
+    private String notes;
+}
+```
+
+#### 3.2.2 响应对象
+
+```java
+// FollowUpResponse.java
+package work.baiyun.chronicdiseaseapp.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "复诊记录响应")
+@Data
+public class FollowUpResponse {
+    @Schema(description = "记录ID")
+    private String id;
+
+    @Schema(description = "患者用户ID")
+    private Long patientUserId;
+
+    @Schema(description = "医生用户ID")
+    private Long doctorUserId;
+
+    @Schema(description = "预约时间")
+    private LocalDateTime appointmentTime;
+
+    @Schema(description = "实际就诊时间")
+    private LocalDateTime actualTime;
+
+    @Schema(description = "复诊状态")
+    private String status;
+
+    @Schema(description = "复诊原因")
+    private String reason;
+
+    @Schema(description = "备注")
+    private String notes;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+    @Schema(description = "患者昵称")
+    private String patientNickname;
+
+    @Schema(description = "医生昵称")
+    private String doctorNickname;
+}
+
+// FollowUpPageResponse.java
+package work.baiyun.chronicdiseaseapp.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import java.util.List;
+
+@Schema(description = "复诊记录分页响应")
+@Data
+public class FollowUpPageResponse {
+    @Schema(description = "数据列表")
+    private List<FollowUpResponse> records;
+
+    @Schema(description = "总数")
+    private long total;
+
+    @Schema(description = "每页大小")
+    private long size;
+
+    @Schema(description = "当前页码")
+    private long current;
+
+    @Schema(description = "总页数")
+    private long pages;
+}
+```
+
+## 4. Mapper接口设计
+
+```java
+// FollowUpMapper.java
+package work.baiyun.chronicdiseaseapp.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import work.baiyun.chronicdiseaseapp.model.po.FollowUp;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface FollowUpMapper extends BaseMapper<FollowUp> {
+}
+```
+
+## 5. Service层设计
+
+### 5.1 接口定义
+
+```java
+// FollowUpService.java
+package work.baiyun.chronicdiseaseapp.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import work.baiyun.chronicdiseaseapp.model.vo.BaseQueryRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.FollowUpResponse;
+import work.baiyun.chronicdiseaseapp.model.vo.CreateFollowUpRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.UpdateFollowUpRequest;
+
+public interface FollowUpService {
+    /**
+     * 患者创建复诊请求
+     */
+    void createFollowUp(CreateFollowUpRequest request);
+
+    /**
+     * 更新复诊记录(医生确认、取消或患者修改)
+     */
+    void updateFollowUp(UpdateFollowUpRequest request);
+
+    /**
+     * 分页查询当前用户的复诊记录
+     */
+    Page<FollowUpResponse> listFollowUps(BaseQueryRequest request);
+
+    /**
+     * 医生分页查询患者的复诊记录
+     */
+    Page<FollowUpResponse> listFollowUpsByPatient(Long patientUserId, BaseQueryRequest request);
+
+    /**
+     * 删除复诊记录
+     */
+    void deleteFollowUp(Long id);
+}
+```
+
+### 5.2 实现类
+
+```java
+// FollowUpServiceImpl.java
+package work.baiyun.chronicdiseaseapp.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import work.baiyun.chronicdiseaseapp.mapper.FollowUpMapper;
+import work.baiyun.chronicdiseaseapp.mapper.UserInfoMapper;
+import work.baiyun.chronicdiseaseapp.model.po.FollowUp;
+import work.baiyun.chronicdiseaseapp.model.po.UserInfo;
+import work.baiyun.chronicdiseaseapp.model.vo.BaseQueryRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.FollowUpResponse;
+import work.baiyun.chronicdiseaseapp.model.vo.CreateFollowUpRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.UpdateFollowUpRequest;
+import work.baiyun.chronicdiseaseapp.service.FollowUpService;
+import work.baiyun.chronicdiseaseapp.util.SecurityUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class FollowUpServiceImpl implements FollowUpService {
+
+    @Autowired
+    private FollowUpMapper followUpMapper;
+
+    @Autowired
+    private UserInfoMapper userInfoMapper;
+
+    @Override
+    public void createFollowUp(CreateFollowUpRequest request) {
+        Long userId = SecurityUtils.getCurrentUserId();
+        FollowUp followUp = new FollowUp();
+        BeanUtils.copyProperties(request, followUp);
+        followUp.setPatientUserId(userId);
+        followUp.setStatus("PENDING"); // 默认状态为待确认
+        followUpMapper.insert(followUp);
+    }
+
+    @Override
+    public void updateFollowUp(UpdateFollowUpRequest request) {
+        Long userId = SecurityUtils.getCurrentUserId();
+        FollowUp followUp = followUpMapper.selectById(request.getId());
+        
+        if (followUp == null) {
+            throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
+                work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_NOT_FOUND.getCode(),
+                "复诊记录不存在");
+        }
+        
+        // 权限检查:患者只能修改自己的记录,医生只能修改分配给自己的记录
+        work.baiyun.chronicdiseaseapp.enums.PermissionGroup role = SecurityUtils.getCurrentUserRole();
+        if (role == work.baiyun.chronicdiseaseapp.enums.PermissionGroup.PATIENT && 
+            !followUp.getPatientUserId().equals(userId)) {
+            throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
+                work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_ACCESS_DENIED.getCode(),
+                "无权操作该复诊记录");
+        }
+        
+        if ((role == work.baiyun.chronicdiseaseapp.enums.PermissionGroup.DOCTOR || 
+             role == work.baiyun.chronicdiseaseapp.enums.PermissionGroup.SYS_ADMIN) && 
+            !followUp.getDoctorUserId().equals(userId)) {
+            throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
+                work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_ACCESS_DENIED.getCode(),
+                "无权操作该复诊记录");
+        }
+        
+        // 更新字段
+        if (request.getAppointmentTime() != null) {
+            followUp.setAppointmentTime(request.getAppointmentTime());
+        }
+        if (request.getStatus() != null) {
+            followUp.setStatus(request.getStatus());
+        }
+        if (request.getNotes() != null) {
+            followUp.setNotes(request.getNotes());
+        }
+        
+        // 如果状态更新为已完成,则设置实际就诊时间
+        if ("COMPLETED".equals(request.getStatus())) {
+            followUp.setActualTime(java.time.LocalDateTime.now());
+        }
+        
+        followUpMapper.updateById(followUp);
+    }
+
+    @Override
+    public Page<FollowUpResponse> listFollowUps(BaseQueryRequest request) {
+        Long userId = SecurityUtils.getCurrentUserId();
+        work.baiyun.chronicdiseaseapp.enums.PermissionGroup role = SecurityUtils.getCurrentUserRole();
+        
+        Page<FollowUp> page = new Page<>(request.getPageNum(), request.getPageSize());
+        LambdaQueryWrapper<FollowUp> wrapper = new LambdaQueryWrapper<>();
+        
+        // 根据用户角色查询不同的数据
+        if (role == work.baiyun.chronicdiseaseapp.enums.PermissionGroup.PATIENT) {
+            wrapper.eq(FollowUp::getPatientUserId, userId);
+        } else if (role == work.baiyun.chronicdiseaseapp.enums.PermissionGroup.DOCTOR || 
+                   role == work.baiyun.chronicdiseaseapp.enums.PermissionGroup.SYS_ADMIN) {
+            wrapper.eq(FollowUp::getDoctorUserId, userId);
+        }
+        
+        wrapper.ge(request.getStartTime() != null, FollowUp::getCreateTime, request.getStartTime())
+               .le(request.getEndTime() != null, FollowUp::getCreateTime, request.getEndTime())
+               .orderByDesc(FollowUp::getCreateTime);
+
+        Page<FollowUp> result = followUpMapper.selectPage(page, wrapper);
+
+        // 批量查询用户信息
+        Set<Long> userIds = result.getRecords().stream()
+            .flatMap(r -> java.util.stream.Stream.of(r.getPatientUserId(), r.getDoctorUserId()))
+            .filter(id -> id != null)
+            .collect(Collectors.toSet());
+
+        Map<Long, UserInfo> userInfoMap;
+        if (userIds.isEmpty()) {
+            userInfoMap = java.util.Collections.emptyMap();
+        } else {
+            List<UserInfo> userInfos = userInfoMapper.selectBatchIds(userIds);
+            userInfoMap = userInfos.stream().collect(Collectors.toMap(UserInfo::getId, u -> u));
+        }
+
+        List<FollowUpResponse> responses = result.getRecords().stream().map(r -> {
+            FollowUpResponse resp = new FollowUpResponse();
+            BeanUtils.copyProperties(r, resp);
+            resp.setId(r.getId().toString());
+            
+            // 设置用户昵称
+            UserInfo patient = userInfoMap.get(r.getPatientUserId());
+            if (patient != null) {
+                resp.setPatientNickname(patient.getNickname());
+            }
+            
+            UserInfo doctor = userInfoMap.get(r.getDoctorUserId());
+            if (doctor != null) {
+                resp.setDoctorNickname(doctor.getNickname());
+            }
+            
+            return resp;
+        }).collect(Collectors.toList());
+
+        Page<FollowUpResponse> responsePage = new Page<>();
+        responsePage.setRecords(responses);
+        responsePage.setCurrent(result.getCurrent());
+        responsePage.setSize(result.getSize());
+        responsePage.setTotal(result.getTotal());
+        responsePage.setPages(result.getPages());
+        return responsePage;
+    }
+
+    @Override
+    public Page<FollowUpResponse> listFollowUpsByPatient(Long patientUserId, BaseQueryRequest request) {
+        // 检查绑定关系
+        Long userId = SecurityUtils.getCurrentUserId();
+        work.baiyun.chronicdiseaseapp.enums.PermissionGroup role = SecurityUtils.getCurrentUserRole();
+        
+        // 只有医生和管理员可以查询其他患者的复诊记录
+        if (role != work.baiyun.chronicdiseaseapp.enums.PermissionGroup.DOCTOR && 
+            role != work.baiyun.chronicdiseaseapp.enums.PermissionGroup.SYS_ADMIN) {
+            throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
+                work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_ACCESS_DENIED.getCode(),
+                "无权查询其他患者的复诊记录");
+        }
+        
+        Page<FollowUp> page = new Page<>(request.getPageNum(), request.getPageSize());
+        LambdaQueryWrapper<FollowUp> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(FollowUp::getPatientUserId, patientUserId)
+               .ge(request.getStartTime() != null, FollowUp::getCreateTime, request.getStartTime())
+               .le(request.getEndTime() != null, FollowUp::getCreateTime, request.getEndTime())
+               .orderByDesc(FollowUp::getCreateTime);
+
+        Page<FollowUp> result = followUpMapper.selectPage(page, wrapper);
+
+        List<FollowUpResponse> responses = result.getRecords().stream().map(r -> {
+            FollowUpResponse resp = new FollowUpResponse();
+            BeanUtils.copyProperties(r, resp);
+            resp.setId(r.getId().toString());
+            return resp;
+        }).collect(Collectors.toList());
+
+        Page<FollowUpResponse> responsePage = new Page<>();
+        responsePage.setRecords(responses);
+        responsePage.setCurrent(result.getCurrent());
+        responsePage.setSize(result.getSize());
+        responsePage.setTotal(result.getTotal());
+        responsePage.setPages(result.getPages());
+        return responsePage;
+    }
+
+    @Override
+    public void deleteFollowUp(Long id) {
+        Long userId = SecurityUtils.getCurrentUserId();
+        FollowUp followUp = followUpMapper.selectById(id);
+        
+        if (followUp == null) {
+            throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
+                work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_NOT_FOUND.getCode(),
+                "复诊记录不存在");
+        }
+        
+        // 只有患者本人才能删除自己的复诊记录
+        if (!followUp.getPatientUserId().equals(userId)) {
+            throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
+                work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_ACCESS_DENIED.getCode(),
+                "无权删除该复诊记录");
+        }
+        
+        followUpMapper.deleteById(id);
+    }
+}
+```
+
+## 6. Controller层设计
+
+```java
+// FollowUpController.java
+package work.baiyun.chronicdiseaseapp.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import work.baiyun.chronicdiseaseapp.common.R;
+import work.baiyun.chronicdiseaseapp.model.vo.BaseQueryRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.FollowUpResponse;
+import work.baiyun.chronicdiseaseapp.model.vo.CreateFollowUpRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.UpdateFollowUpRequest;
+import work.baiyun.chronicdiseaseapp.service.FollowUpService;
+import work.baiyun.chronicdiseaseapp.enums.ErrorCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@RestController
+@RequestMapping("/follow-up")
+@Tag(name = "复诊管理", description = "复诊预约与管理相关接口")
+public class FollowUpController {
+
+    private static final Logger logger = LoggerFactory.getLogger(FollowUpController.class);
+
+    @Autowired
+    private FollowUpService followUpService;
+
+    @Operation(summary = "创建复诊请求", description = "患者创建复诊预约请求")
+    @ApiResponses(value = {
+        @ApiResponse(responseCode = "200", description = "复诊请求创建成功",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class))),
+        @ApiResponse(responseCode = "500", description = "服务器内部错误",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class)))
+    })
+    @PostMapping(path = "/create", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<?> create(@RequestBody CreateFollowUpRequest req) {
+        try {
+            followUpService.createFollowUp(req);
+            return R.success(200, "复诊请求创建成功");
+        } catch (Exception e) {
+            logger.error("create follow up request failed", e);
+            return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
+        }
+    }
+
+    @Operation(summary = "更新复诊记录", description = "更新复诊记录(医生确认、取消或患者修改)")
+    @ApiResponses(value = {
+        @ApiResponse(responseCode = "200", description = "复诊记录更新成功",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class))),
+        @ApiResponse(responseCode = "500", description = "服务器内部错误",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class)))
+    })
+    @PostMapping(path = "/update", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<?> update(@RequestBody UpdateFollowUpRequest req) {
+        try {
+            followUpService.updateFollowUp(req);
+            return R.success(200, "复诊记录更新成功");
+        } catch (Exception e) {
+            logger.error("update follow up request failed", e);
+            return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
+        }
+    }
+
+    @Operation(summary = "分页查询复诊记录", description = "根据时间范围和分页参数查询复诊记录")
+    @ApiResponses(value = {
+        @ApiResponse(responseCode = "200", description = "成功查询复诊记录列表",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.FollowUpPageResponse.class))),
+        @ApiResponse(responseCode = "500", description = "服务器内部错误",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class)))
+    })
+    @PostMapping(path = "/list", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<?> list(@RequestBody BaseQueryRequest req) {
+        try {
+            Page<FollowUpResponse> page = followUpService.listFollowUps(req);
+            work.baiyun.chronicdiseaseapp.model.vo.FollowUpPageResponse vo = new work.baiyun.chronicdiseaseapp.model.vo.FollowUpPageResponse();
+            vo.setRecords(page.getRecords());
+            vo.setTotal(page.getTotal());
+            vo.setSize(page.getSize());
+            vo.setCurrent(page.getCurrent());
+            vo.setPages(page.getPages());
+            return R.success(200, "ok", vo);
+        } catch (Exception e) {
+            logger.error("list follow up records failed", e);
+            return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
+        }
+    }
+
+    @Operation(summary = "医生分页查询患者复诊记录", description = "医生查询患者复诊记录(需有绑定关系或权限)")
+    @ApiResponses(value = {
+        @ApiResponse(responseCode = "200", description = "成功查询复诊记录列表",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.FollowUpPageResponse.class))),
+        @ApiResponse(responseCode = "403", description = "无权限访问",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class)))
+    })
+    @PostMapping(path = "/list-by-patient", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<?> listByPatient(Long patientUserId, @RequestBody BaseQueryRequest req) {
+        try {
+            Page<FollowUpResponse> page = followUpService.listFollowUpsByPatient(patientUserId, req);
+            work.baiyun.chronicdiseaseapp.model.vo.FollowUpPageResponse vo = new work.baiyun.chronicdiseaseapp.model.vo.FollowUpPageResponse();
+            vo.setRecords(page.getRecords());
+            vo.setTotal(page.getTotal());
+            vo.setSize(page.getSize());
+            vo.setCurrent(page.getCurrent());
+            vo.setPages(page.getPages());
+            return R.success(200, "ok", vo);
+        } catch (Exception e) {
+            logger.error("list follow up records by patient failed", e);
+            return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
+        }
+    }
+
+    @Operation(summary = "删除复诊记录", description = "根据ID删除复诊记录")
+    @ApiResponses(value = {
+        @ApiResponse(responseCode = "200", description = "复诊记录删除成功",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class))),
+        @ApiResponse(responseCode = "500", description = "服务器内部错误",
+            content = @Content(mediaType = "application/json",
+                schema = @Schema(implementation = Void.class)))
+    })
+    @PostMapping(path = "/delete", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<?> delete(@RequestBody work.baiyun.chronicdiseaseapp.model.vo.DeleteFollowUpRequest req) {
+        try {
+            followUpService.deleteFollowUp(req.getId());
+            return R.success(200, "复诊记录删除成功");
+        } catch (Exception e) {
+            logger.error("delete follow up record failed", e);
+            return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
+        }
+    }
+}
+```
+
+## 7. 请求/响应对象补充
+
+```java
+// DeleteFollowUpRequest.java
+package work.baiyun.chronicdiseaseapp.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "删除复诊记录请求")
+@Data
+public class DeleteFollowUpRequest {
+    @Schema(description = "复诊记录ID")
+    private Long id;
+}
+```
+
+## 8. 错误码补充
+
+在 ErrorCode 枚举中可能需要添加以下错误码:
+
+```java
+// 在 ErrorCode.java 中添加
+FOLLOW_UP_NOT_FOUND(6000, "复诊记录不存在"),
+FOLLOW_UP_ACCESS_DENIED(6001, "无权访问复诊记录"),
+FOLLOW_UP_STATUS_INVALID(6002, "复诊状态无效");
+```
+
+## 9. 接口调用示例
+
+### 9.1 患者创建复诊请求
+```
+POST /follow-up/create
+{
+  "doctorUserId": 1988172854356631553,
+  "appointmentTime": "2025-12-01T10:00:00",
+  "reason": "血糖控制不佳,需要调整治疗方案"
+}
+```
+
+### 9.2 医生确认复诊
+```
+POST /follow-up/update
+{
+  "id": 1988502746686595073,
+  "status": "CONFIRMED",
+  "notes": "已确认预约"
+}
+```
+
+### 9.3 查询复诊记录
+```
+POST /follow-up/list
+{
+  "pageNum": 1,
+  "pageSize": 10,
+  "startTime": "2025-11-01T00:00:00",
+  "endTime": "2025-12-01T23:59:59"
+}
+```
+
+## 10. 权限设计
+
+1. **患者**:
+   - 可以创建复诊请求
+   - 可以查看自己的复诊记录
+   - 可以删除自己的待确认复诊请求
+   - 可以修改自己的待确认复诊请求
+
+2. **医生**:
+   - 可以查看分配给自己的复诊请求
+   - 可以确认、取消或完成复诊
+   - 可以查看患者的复诊历史
+
+3. **系统管理员**:
+   - 具有医生的所有权限
+   - 可以查看所有复诊记录
+
+## 11. 状态流转
+
+```
+PENDING(待确认) -> CONFIRMED(已确认) -> COMPLETED(已完成)
+     |
+     v
+CANCELLED(已取消)
+```
+
+## 12. 安全考虑
+
+1. 数据访问权限控制
+2. 参数验证和输入检查
+3. 防止SQL注入
+4. 日志记录和审计追踪

+ 0 - 0
docs/DataBase/t_blood_glucose_data.txt → docs/OLD/DataBase/t_blood_glucose_data.txt


+ 0 - 0
docs/DataBase/t_blood_pressure_data.txt → docs/OLD/DataBase/t_blood_pressure_data.txt


+ 0 - 0
docs/DataBase/t_heart_rate_data.txt → docs/OLD/DataBase/t_heart_rate_data.txt


+ 0 - 0
docs/DataBase/t_physical_data.txt → docs/OLD/DataBase/t_physical_data.txt


+ 0 - 0
docs/DataBase/t_user_binding.txt → docs/OLD/DataBase/t_user_binding.txt


+ 0 - 0
docs/DataBase/t_user_info.txt → docs/OLD/DataBase/t_user_info.txt


+ 0 - 0
docs/DataBase/t_user_token.txt → docs/OLD/DataBase/t_user_token.txt


+ 0 - 0
docs/DevDesign/查询绑定患者健康数据设计.md → docs/OLD/DevDesign/查询绑定患者健康数据设计.md


+ 0 - 0
docs/DevDesign/用户头像本地上传与获取接口设计-项目改动概要.md → docs/OLD/DevDesign/用户头像本地上传与获取接口设计-项目改动概要.md


+ 0 - 0
docs/DevDesign/用户头像本地上传与获取接口设计.md → docs/OLD/DevDesign/用户头像本地上传与获取接口设计.md


+ 0 - 0
docs/DevRule/01-代码风格指南.md → docs/OLD/DevRule/01-代码风格指南.md


+ 0 - 0
docs/DevRule/02-项目结构规范.md → docs/OLD/DevRule/02-项目结构规范.md


+ 0 - 0
docs/DevRule/03-API设计规范.md → docs/OLD/DevRule/03-API设计规范.md


+ 0 - 0
docs/DevRule/04-测试规范.md → docs/OLD/DevRule/04-测试规范.md


+ 0 - 0
docs/DevRule/05-依赖管理规范.md → docs/OLD/DevRule/05-依赖管理规范.md


+ 0 - 0
docs/DevRule/06-日志和错误处理规范.md → docs/OLD/DevRule/06-日志和错误处理规范.md


+ 0 - 0
docs/DevRule/07-数据库规范.md → docs/OLD/DevRule/07-数据库规范.md


+ 0 - 0
docs/DevRule/08-安全规范.md → docs/OLD/DevRule/08-安全规范.md


+ 0 - 0
docs/ReadME.md → docs/OLD/ReadME.md


+ 0 - 0
docs/Swagger泛型返回类型字段信息不显示问题解决方案.md → docs/OLD/Swagger泛型返回类型字段信息不显示问题解决方案.md


+ 0 - 0
docs/前端ID精度丢失问题解决方案.md → docs/OLD/前端ID精度丢失问题解决方案.md