5.9 KiB
5.9 KiB
T5 · 诊单待办事项 + 企微定时推送
来源:~/.claude/plans/6-7-fluffy-sunset.md(T5 节) 范围:单个 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/liststcm.diagnosisTodo/addtcm.diagnosisTodo/cancel
后端文件
server/app/common/model/tcm/DiagnosisTodo.phpserver/app/adminapi/controller/tcm/DiagnosisTodoController.phpserver/app/adminapi/logic/tcm/DiagnosisTodoLogic.phpserver/app/adminapi/lists/tcm/DiagnosisTodoLists.phpserver/app/adminapi/validate/tcm/DiagnosisTodoValidate.phpserver/app/command/DiagnosisTodoNotify.php
Command 行为(tcm:diagnosis-todo-notify)
每次执行:
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- 对每条:
- 乐观锁更新:
update set status=1, notified_at=now where id=? and status=0—— 如果影响行 0,说明被其它进程抢走,跳过。 - 取
creator_id→Admin::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>。
- 乐观锁更新:
- 每条 try-catch,整批不中断;输出
处理 N / 成功 X / 失败 Y / 跳过未绑定 Z。 - 注册到
server/config/console.php:'tcm:diagnosis-todo-notify' => 'app\\command\\DiagnosisTodoNotify' - 用户在服务器 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 / 创建人 / 推送时间 / 失败原因 / 操作。
- 状态 chip:
info(待执行) /success(已发送) /warning(已取消) /danger(失败)。 - 操作列:
status===0显「取消」二次确认;其它隐藏。 - 排序:
remind_time desc;分页。
验收清单(对应计划文件 T5 验收)
- 在某诊单编辑页 → 待办事项 tab → 新建一条「2 分钟后,内容=测试」→ 表格出现「待执行」。
- 等 2 分钟,手动跑
php think tcm:diagnosis-todo-notify,企微收到文本;状态变「已发送」、推送时间已写。 - 把 admin
work_wechat_userid清空,再创建一条,跑命令 → 状态「失败」+error列显示原因;不会再次重试。 - 创建一条 1 小时后的 → 立即点「取消」→ 状态变「已取消」、操作列消失;再跑命令也不会推。
- 切换到该诊单的另一名 admin(非创建人)→ 看见这条但没有取消按钮。
- 历史 / 当前同表,按时间倒序,分页正常。
tcm:diagnosis-todo-notify二次执行不重复推送同一条(乐观锁验证)。
不做(边界)
- 不实现「提前 N 分钟提醒」逻辑("时间到了"做精准触发即可)。
- 不实现重试退避 —— 失败即终态。
- 不引入 SSE / WebSocket(待办列表不实时刷新,分钟级 cron 已够用)。
- 不修改已有的
BloodRecordList.vue / DietRecordList.vue / ExerciseRecordList.vue。 - 不与 T1–T4 共用任何文件之外的逻辑(这些子任务是独立 PR)。