feat: 后台管理页全量重构 + backoffice 共享模块 + 启动脚本优化

## 后台管理页重构
- admin 端: add_doctor / doctors / home / patients 四页重构
- doctor 端: consultations / dashboard / followup_edit / followups / home / patient_detail / patients / profile / report_detail / reports / settings 十一页重构
- 新增 backoffice 共享模块: backoffice_refresh_providers + backoffice_formatters + backoffice_ui

## 后端
- AdminService 增强(分页/搜索/统计)
- doctor_endpoints 微调
- appsettings.Development 调整
- 新增 admin_service_tests

## 前端其他
- 今日健康卡片 taskRow 行高 5->7(每行 44->48px)
- 健康仪表盘配色定 #4FACFE 纯色蓝
- admin_drawer / doctor_drawer 微调
- api_client IP 适配

## 启动脚本
- start-dev.bat: 加后端就绪检测(轮询 openapi 端点, 最多等 30s)

## 清理
- 删除旧品牌图(agent_welcome_abstract / login_background_v1)
- 删除 app_status_badge 组件
- 删除旧 HANDOFF 文档, 新增 07-17 版

## 测试
- 新增 backoffice_formatters / backoffice_refresh / backoffice_ui 三个测试
- app_router_test 扩展
This commit is contained in:
MingNian
2026-07-18 17:48:44 +08:00
parent e1f4a4b91f
commit ae94ced2d5
41 changed files with 1610 additions and 820 deletions

219
docs/HANDOFF-2026-07-17.md Normal file
View File

@@ -0,0 +1,219 @@
# 小脉健康项目交接报告2026-07-17
## 1. 本轮工作范围
本轮工作集中在以下三项:
1. 对医生端和管理员端进行患者端风格的轻量统一。
2. 检查并修复医生、登录账号、医生资料之间的同步逻辑。
3. 进行源码级逻辑检查和小范围自动化验证。
本轮没有生成 APK、没有进行真机或截图测试、没有启动 Web API、没有部署、没有推送远程代码也没有修改真实数据库数据。
## 2. 医生端与管理员端 UI 改造
### 2.1 共用视觉层
新增后台共用展示组件:
- `health_app/lib/widgets/backoffice_ui.dart`
- `health_app/lib/utils/backoffice_formatters.dart`
- `health_app/lib/providers/backoffice_refresh_providers.dart`
统一内容包括:
- 使用患者端现有的紫蓝渐变和浅色页面背景。
- 使用白色圆角卡片、轻边框和统一阴影。
- 统一加载、空数据、加载失败和重试状态。
- 统一管理员端、医生端侧边栏的选中态。
- 对空姓名头像和不完整时间字符串进行安全展示。
### 2.2 医生端
已覆盖的主要页面:
- 工作台
- 患者列表与患者详情
- 问诊列表
- 报告列表与报告详情
- 随访列表与新增/编辑随访
- 医生资料
- 医生设置
- 医生侧边栏
已修复的页面问题:
- “新建随访”以前会被路由器当成缺少 ID 的详情页,现在可以正常进入新建模式。
- 新增或编辑随访后,返回列表会自动刷新。
- 随访和报告日期不再直接强制截取 16 个字符,避免空值或短字符串导致 `RangeError`
- 空患者姓名不再因为直接读取第一个字符而导致崩溃。
- 患者列表加载失败后不再反复自动请求。
- 医生资料页面重建时不再反复覆盖用户正在编辑的输入内容。
- 停用医生登录后,工作台显示“账号已停用,新患者暂时无法选择您”的提示。
### 2.3 管理员端
已覆盖的主要页面:
- 医生管理
- 患者管理
- 新增医生
- 编辑医生
- 管理员侧边栏
已修复的页面问题:
- 管理员接口失败时不再无限显示加载状态。
- 医生列表增加“编辑”入口,复用新增医生表单。
- 新增或编辑医生后,返回列表会自动刷新。
- 停用和删除失败时显示后端返回的具体原因,不再静默失败。
- 删除确认文案改为安全规则:有绑定患者或问诊记录时只能停用,不能删除。
- 停用医生在管理员列表中继续显示“已停用”状态。
## 3. 医生账号三层数据同步
医生相关数据由以下三部分组成:
- `Doctor`:患者选择、医生展示和业务归属。
- `User(Role=Doctor)`:医生登录账号。
- `DoctorProfile`:医生端资料和医生实体关联。
本轮修改了 `backend/src/Health.Infrastructure/Admin/AdminService.cs`,规则如下。
### 3.1 新增医生
- 一次性创建 `Doctor`、医生登录 `User``DoctorProfile`
- 三者使用相同的手机号和姓名,并建立正确 ID 关联。
- 手机号已被患者、医生或其他账号使用时拒绝新增。
### 3.2 编辑医生
- 手机号同步更新 `Doctor` 和登录 `User`
- 姓名同步更新 `Doctor`、登录 `User``DoctorProfile`
- 职称、科室同步更新 `Doctor``DoctorProfile`
- 专业方向更新到 `Doctor`
- 修改手机号前检查其他账号和医生是否已经占用。
- 医生登录资料缺失时拒绝局部修改,避免继续扩大不一致数据。
### 3.3 停用医生
- 同步更新 `Doctor.IsActive``DoctorProfile.IsActive`
- 医生登录账号保持 `Role=Doctor`,仍可正常登录并服务已绑定患者。
- 患者与医生的原有绑定关系保持不变。
- 公开医生列表原本已经只查询 `Doctor.IsActive == true`,因此新患者注册时不会再看到已停用医生。
- 注册接口原本已经再次校验医生必须有效且启用。
### 3.4 删除医生
- 医生仍有绑定患者时拒绝删除。
- 医生已有问诊记录时拒绝删除。
- 无绑定患者和问诊记录时,删除 `Doctor`、对应登录 `User``DoctorProfile` 和刷新令牌。
- 有历史数据的医生应使用“停用”,不应通过物理删除清理。
## 4. 自动化验证
本轮最终验证结果:
- `flutter analyze`:通过,零问题。
- 后端 `Health.Tests`56 项通过0 项失败0 项跳过。
- 医生/管理员 UI、路由和刷新相关测试通过。
新增或更新的测试包括:
- `backend/tests/Health.Tests/admin_service_tests.cs`
- `health_app/test/backoffice_ui_test.dart`
- `health_app/test/backoffice_formatters_test.dart`
- `health_app/test/backoffice_refresh_test.dart`
- `health_app/test/app_router_test.dart`
说明:`dotnet test` 会自动编译测试程序集,但本轮没有执行发布构建,没有生成 Android APK/AAB也没有执行 iOS 构建。
## 5. 数据库核对结果
本轮只进行了只读连接检查,没有修改数据库。
- 项目本地配置指向 `localhost:5432 / health_manager`
- 本机 PostgreSQL 没有运行,因此无法读取本地实际数据。
- 开发 API `http://10.4.237.12:5000` 当前无法连接。
- 正式 API `https://erpapi.datalumina.cn/xiaomai/api/doctors` 可以访问,但公开接口返回的启用医生数量为 0。
- 交接资料显示正式数据库和备份方案尚未最终配置。
因此目前无法确认数据库中是否已经存在以下旧数据问题:
- `Doctor` 存在但没有医生登录 `User`
- `DoctorProfile` 缺失或关联错误。
- 三层手机号、姓名或启用状态不一致。
- 重复医生手机号。
- 患者 `DoctorId` 指向不存在的医生。
新代码可以防止以后继续产生这些不一致,但不会自动修改已有数据。获得生产 PostgreSQL 只读连接或实际数据库备份后,应单独执行数据核对。
## 6. 仍未解决的高优先级问题
### P0固定管理员验证码
`backend/src/Health.Infrastructure/Auth/AuthService.cs` 仍保留固定管理员手机号和固定验证码 `000000`。上线前必须移除,管理员需要使用受控的创建和认证方式。
### P0短信服务仍是开发实现
`backend/src/Health.Infrastructure/Services/sms_service.cs` 只向服务端控制台输出验证码,没有接入真实短信服务。生产环境不会把验证码返回 App因此普通用户无法正常接收验证码。
### P0问诊 SignalR Hub 未鉴权
`backend/src/Health.WebApi/Hubs/ConsultationHub.cs` 没有强制 JWT 身份验证,也没有校验用户或医生是否属于指定问诊。调用者可以传入 `senderType``senderName`,存在加入其他问诊、伪造医生消息和写入数据库的风险。
### P1医生聊天错误复用患者聊天流程
医生问诊列表进入的 `DoctorChatPage` 复用了患者端 `consultationChatProvider`
- 把问诊 ID 当成医生 ID。
- 调用患者配额接口。
- 尝试创建新问诊。
- 发送消息时使用患者身份。
- 医生需要回复的 `WaitingDoctor` 状态反而不能发送。
医生实时问诊目前不能视为可用功能。需要拆分独立医生聊天 Provider 和页面,并与 Hub 鉴权一起处理。
### P1AI SSE 鉴权和上传文件隐私
此前检查发现:
- AI SSE 接口允许从 query token 中直接读取 JWT claims没有完整验证签名、签发者、受众和有效期。
- 上传的医疗图片和报告通过 `/uploads` 静态公开访问。
- 附件本地路径解析缺少完整的目录边界和文件归属校验。
- CORS 当前允许任意来源并携带凭据。
这些问题应在正式上线前统一修复。
### P1iOS 蓝牙流程仍需修复和真机验证
此前检查发现 iOS 页面仍请求 Android 风格蓝牙权限,并调用仅 Android 支持的 `FlutterBluePlus.turnOn()`。iOS Pods 也需要在 macOS 上重新安装并验证。当前 Windows 环境不能证明 iOS 构建和真机蓝牙可用。
## 7. Apple 医疗硬件审核准备
如果首版明确只支持欧姆龙 J735建议准备
- J735 当前有效的医疗器械注册或批准材料。
- 小脉健康 App 与 J735 的联调测试报告。
- 真实 iPhone、J735 和当前版本 App 的完整配对及测量演示视频。
- App、审核说明、兼容设备清单和宣传文案全部明确首版支持范围。
注册证只能证明血压计本身合规,不能替代 App 与硬件联调测试报告和演示视频。
## 8. 建议后续顺序
1. 修复固定管理员验证码并接入正式短信服务。
2. 拆分医生聊天流程并为 SignalR Hub 增加 JWT、角色和问诊归属校验。
3. 修复 AI SSE 鉴权、上传文件访问控制、路径边界和 CORS。
4. 获得真实数据库只读连接,核对并修复旧医生账号数据。
5. 在 macOS 和真实 iPhone 上修复并验证 iOS 蓝牙流程。
6. 准备 J735 监管材料、联调报告和真实设备演示视频。
7. 完成正式服务器、数据库、短信、对象存储、域名和 HTTPS 配置后,再进入发布构建和应用商店提交。
## 9. 当前操作边界
- 本轮代码修改和本交接报告均未进行新的 Git 提交或远程推送。
- 没有执行数据库迁移。
- 没有写入、更新或删除真实数据库数据。
- 没有生成或上传发布安装包。
- 没有启动需要额外关闭的前端或后端常驻服务。