Files
zyt/.trellis/tasks/archive/2026-05/05-05-tcm-diagnosis-todo/prd.md
T

5.9 KiB
Raw Blame History

T5 · 诊单待办事项 + 企微定时推送

来源:~/.claude/plans/6-7-fluffy-sunset.mdT5 节) 范围:单个 PR、全栈,独立于 T1–T4,可单独并行上线。

用户故事

二中心医助希望在诊单上挂一些「将来要做的事」(例:3 天后回访、2 小时后提醒患者复测血糖),到点由企业微信推送给创建人本人(不是患者、不是当前指派医助)。失败一次即终态,由人工取消重建,不做自动重试。

范围

  • 「待办事项」tab 出现在诊单编辑页 (tcm/diagnosis/edit.vue) — 在「运动打卡记录」之后。
  • 待办列表(历史 + 当前一锅端,按 remind_time desc,分页)。
  • 新建:选时间 + 输入内容;只允许 remind_time > now
  • 取消:仅 status=0 可取消;只允许创建人本人或超管,按已有 role 边界判定。
  • 不实现编辑(要改 = 取消重建)。
  • 不实现提前 N 分钟提醒、不实现自动重试。
  • 列表本身不做轮询,进 tab 拉一次。

数据模型

新建表 zyt_tcm_diagnosis_todo

字段 类型 备注
id int unsigned PK auto_increment
diagnosis_id int unsigned 所属诊单
patient_id int unsigned 冗余便于反查
content varchar(500) 通知内容
remind_time int(10) unsigned 提醒时间戳(秒)
status tinyint 0=待执行 / 1=已发送 / 2=已取消 / 3=发送失败
creator_id int unsigned admin.id
creator_name varchar(64) 冗余,免 join
notified_at int(10) unsigned NULL 实际推送时间(成功时写)
error varchar(500) NULL 失败原因
cancelled_at int(10) unsigned NULL
cancelled_by int unsigned NULL
create_time / update_time / delete_time int(10) unsigned 软删 + 整型时间戳

索引:

  • idx_diagnosis_id (diagnosis_id)
  • idx_creator_id (creator_id)
  • idx_status_remind_time (status, remind_time) —— cron 扫表核心索引

迁移文件:server/sql/1.9.20260420/add_tcm_diagnosis_todo.sql

API 路径

GET  /tcm.diagnosisTodo/lists?diagnosis_id=:id   分页列表
POST /tcm.diagnosisTodo/add        { diagnosis_id, content, remind_time }
POST /tcm.diagnosisTodo/cancel     { id }
GET  /tcm.diagnosisTodo/detail?id=:id

权限节点(add_diagnosis_todo_menu.sql,按钮型菜单挂在「诊单管理」下):

  • tcm.diagnosisTodo/lists
  • tcm.diagnosisTodo/add
  • tcm.diagnosisTodo/cancel

后端文件

  • server/app/common/model/tcm/DiagnosisTodo.php
  • server/app/adminapi/controller/tcm/DiagnosisTodoController.php
  • server/app/adminapi/logic/tcm/DiagnosisTodoLogic.php
  • server/app/adminapi/lists/tcm/DiagnosisTodoLists.php
  • server/app/adminapi/validate/tcm/DiagnosisTodoValidate.php
  • server/app/command/DiagnosisTodoNotify.php

Command 行为(tcm:diagnosis-todo-notify

每次执行:

  1. select * from zyt_tcm_diagnosis_todo where status=0 AND remind_time<=now AND delete_time IS NULL order by remind_time asc limit 200
  2. 对每条:
    • 乐观锁更新:update set status=1, notified_at=now where id=? and status=0 —— 如果影响行 0,说明被其它进程抢走,跳过。
    • creator_idAdmin::find($creator_id)->work_wechat_userid,没绑定时记录 error='创建人未绑定企业微信' 并置 status=3
    • 推文本:【患者跟踪提醒】\n患者:{patient_name}\n时间:{remind_time_text}\n内容:{content}\n— 二中心跟踪系统
    • WechatWorkAppMessageService::sendTextToUser($wxId, $text);失败 → status=3, error=<msg>
  3. 每条 try-catch,整批不中断;输出 处理 N / 成功 X / 失败 Y / 跳过未绑定 Z
  4. 注册到 server/config/console.php'tcm:diagnosis-todo-notify' => 'app\\command\\DiagnosisTodoNotify'
  5. 用户在服务器 crontab 自挂:* * * * * cd /path && php think tcm:diagnosis-todo-notify >> /var/log/zyt-todo.log 2>&1

前端文件

  • 新建:admin/src/views/tcm/diagnosis/components/DiagnosisTodoList.vue
  • 修改:admin/src/views/tcm/diagnosis/edit.vue(在 name="exercise" 之后追加 tab
  • 修改:admin/src/api/tcm.ts(加 4 个函数)

UI

  • 顶部「+ 新增待办」按钮 → el-dialog (el-date-picker type=datetime + el-input type=textarea)。
  • 表格列:提醒时间 / 内容 / 状态 chip / 创建人 / 推送时间 / 失败原因 / 操作。
  • 状态 chipinfo (待执行) / success (已发送) / warning (已取消) / danger (失败)。
  • 操作列:status===0 显「取消」二次确认;其它隐藏。
  • 排序:remind_time desc;分页。

验收清单(对应计划文件 T5 验收)

  1. 在某诊单编辑页 → 待办事项 tab → 新建一条「2 分钟后,内容=测试」→ 表格出现「待执行」。
  2. 等 2 分钟,手动跑 php think tcm:diagnosis-todo-notify,企微收到文本;状态变「已发送」、推送时间已写。
  3. 把 admin work_wechat_userid 清空,再创建一条,跑命令 → 状态「失败」+ error 列显示原因;不会再次重试。
  4. 创建一条 1 小时后的 → 立即点「取消」→ 状态变「已取消」、操作列消失;再跑命令也不会推。
  5. 切换到该诊单的另一名 admin(非创建人)→ 看见这条但没有取消按钮
  6. 历史 / 当前同表,按时间倒序,分页正常。
  7. tcm:diagnosis-todo-notify 二次执行不重复推送同一条(乐观锁验证)。

不做(边界)

  • 不实现「提前 N 分钟提醒」逻辑("时间到了"做精准触发即可)。
  • 不实现重试退避 —— 失败即终态。
  • 不引入 SSE / WebSocket(待办列表不实时刷新,分钟级 cron 已够用)。
  • 不修改已有的 BloodRecordList.vue / DietRecordList.vue / ExerciseRecordList.vue
  • 不与 T1–T4 共用任何文件之外的逻辑(这些子任务是独立 PR)。