feat: enrich default workspace mock data

This commit is contained in:
2026-07-03 02:27:48 -07:00
parent 0bc296a8ee
commit 36eae0be53
5 changed files with 508 additions and 26 deletions

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,31 @@
# Local mock data
## Goal
Provide richer local mock data so the养老机构工作台 can be opened locally with realistic content across the existing dashboard, beds, elders, care, health, and emergency surfaces.
## Confirmed Facts
- The project already has Drizzle/Postgres schema and a `seedDefaultWorkspaceData(organizationId)` helper in `modules/core/server/default-workspace-data.ts`.
- Existing default data covers rooms, beds, elders, admissions, health profiles, vital records, chronic conditions, health anomaly reviews, and care tasks.
- Local Postgres is configured through `compose.yaml`; Drizzle uses `DATABASE_URL`.
- This is a lightweight task: no schema change, no product workflow redesign, and no new external dependency is required.
## Requirements
- Add a larger, more varied default dataset using existing schema tables and enum values.
- Include enough records to make local pages visibly populated: facilities/beds, elder roster, active admissions, care tasks, health vitals/reviews, chronic conditions, and emergency incidents.
- Keep seeding idempotent for an organization that already has workspace data; do not overwrite user-created local data.
- Preserve existing TypeScript type safety and existing data relationships by resolving records through names/codes inside the seed transaction.
- Avoid production-only assumptions; this data is for local/demo initialization only.
## Acceptance Criteria
- [x] Default workspace seed data has more realistic volume and state variety across the existing modules.
- [x] Seed data inserts without foreign-key errors into a migrated local database.
- [x] `pnpm type-check` passes.
- [x] No schema migration is introduced for this task.
## Notes
- Out of scope: fake auth users, generated production data, cross-organization demo scenarios, and UI redesign.

View File

@@ -0,0 +1,26 @@
{
"id": "local-mock-data",
"name": "local-mock-data",
"title": "Local mock data",
"description": "",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "TalexDreamSoul",
"assignee": "TalexDreamSoul",
"createdAt": "2026-07-03",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}

View File

@@ -105,15 +105,33 @@ type SeedCareTask = {
title: string;
};
type SeedSystemIncident = {
acknowledgedHoursAgo?: number;
createdHoursAgo: number;
description: string;
resolvedHoursAgo?: number;
severity: "info" | "warning" | "critical";
source: string;
status: "open" | "acknowledged" | "resolved" | "closed";
title: string;
};
const DEFAULT_ROOMS: SeedRoom[] = [
{ campusName: "主院区", buildingName: "护理楼", floorName: "一层", floorLevel: 1, code: "A101", name: "阳光房", capacity: 2 },
{ campusName: "主院区", buildingName: "护理楼", floorName: "一层", floorLevel: 1, code: "A102", name: "康养房", capacity: 2 },
{ campusName: "主院区", buildingName: "护理楼", floorName: "二层", floorLevel: 2, code: "A201", name: "记忆照护房", capacity: 2 },
{ campusName: "主院区", buildingName: "护理楼", floorName: "二层", floorLevel: 2, code: "A202", name: "重点照护房", capacity: 2 },
{ campusName: "主院区", buildingName: "护理楼", floorName: "三层", floorLevel: 3, code: "A301", name: "失能照护房", capacity: 2 },
{ campusName: "主院区", buildingName: "护理楼", floorName: "三层", floorLevel: 3, code: "A302", name: "安宁照护房", capacity: 2 },
{ campusName: "主院区", buildingName: "康复楼", floorName: "一层", floorLevel: 1, code: "R101", name: "康复观察房", capacity: 2 },
{ campusName: "主院区", buildingName: "康复楼", floorName: "一层", floorLevel: 1, code: "R102", name: "短住评估房", capacity: 2 },
{ campusName: "主院区", buildingName: "康复楼", floorName: "二层", floorLevel: 2, code: "R201", name: "术后康复房", capacity: 2 },
{ campusName: "主院区", buildingName: "康复楼", floorName: "二层", floorLevel: 2, code: "R202", name: "认知训练房", capacity: 2 },
{ campusName: "北苑院区", buildingName: "颐养楼", floorName: "一层", floorLevel: 1, code: "B101", name: "自理房", capacity: 2 },
{ campusName: "北苑院区", buildingName: "颐养楼", floorName: "二层", floorLevel: 2, code: "B201", name: "亲情套房", capacity: 2 },
{ campusName: "北苑院区", buildingName: "颐养楼", floorName: "二层", floorLevel: 2, code: "B202", name: "陪护套房", capacity: 2 },
{ campusName: "南苑院区", buildingName: "医养楼", floorName: "一层", floorLevel: 1, code: "S101", name: "医养观察房", capacity: 2 },
{ campusName: "南苑院区", buildingName: "医养楼", floorName: "一层", floorLevel: 1, code: "S102", name: "短期托养房", capacity: 2 },
];
const DEFAULT_BEDS: SeedBed[] = [
@@ -125,14 +143,28 @@ const DEFAULT_BEDS: SeedBed[] = [
{ code: "A201-2", roomCode: "A201", status: "available", notes: "靠近护理站" },
{ code: "A202-1", roomCode: "A202", status: "occupied", notes: "压疮风险重点巡查" },
{ code: "A202-2", roomCode: "A202", status: "disabled", notes: "待更换护栏" },
{ code: "A301-1", roomCode: "A301", status: "occupied", notes: "失能照护,配置防跌倒垫" },
{ code: "A301-2", roomCode: "A301", status: "available", notes: "鼻饲护理设备已消毒待用" },
{ code: "A302-1", roomCode: "A302", status: "available", notes: "家属陪伴预约优先" },
{ code: "A302-2", roomCode: "A302", status: "maintenance", notes: "氧气接口保养" },
{ code: "R101-1", roomCode: "R101", status: "occupied", notes: "康复训练后休息床位" },
{ code: "R101-2", roomCode: "R101", status: "available", notes: "临时观察床位" },
{ code: "R102-1", roomCode: "R102", status: "available", notes: "短住评估床位" },
{ code: "R102-2", roomCode: "R102", status: "available", notes: "短住评估床位" },
{ code: "R201-1", roomCode: "R201", status: "occupied", notes: "术后康复第 2 周" },
{ code: "R201-2", roomCode: "R201", status: "available", notes: "康复床位候补" },
{ code: "R202-1", roomCode: "R202", status: "occupied", notes: "认知训练老人长期床位" },
{ code: "R202-2", roomCode: "R202", status: "available", notes: "靠近活动室" },
{ code: "B101-1", roomCode: "B101", status: "occupied", notes: "自理老人长期床位" },
{ code: "B101-2", roomCode: "B101", status: "available", notes: "低照护需求床位" },
{ code: "B201-1", roomCode: "B201", status: "occupied", notes: "亲属探访便利" },
{ code: "B201-1", roomCode: "B201", status: "available", notes: "亲属探访便利" },
{ code: "B201-2", roomCode: "B201", status: "available", notes: "亲情套房备用床" },
{ code: "B202-1", roomCode: "B202", status: "occupied", notes: "陪护套房,家属每周探访" },
{ code: "B202-2", roomCode: "B202", status: "available", notes: "可接收短住老人" },
{ code: "S101-1", roomCode: "S101", status: "occupied", notes: "医养观察,夜间吸氧" },
{ code: "S101-2", roomCode: "S101", status: "disabled", notes: "待更换床垫" },
{ code: "S102-1", roomCode: "S102", status: "available", notes: "短期托养床位" },
{ code: "S102-2", roomCode: "S102", status: "available", notes: "短期托养床位" },
];
const DEFAULT_ELDERS: SeedElder[] = [
@@ -236,6 +268,86 @@ const DEFAULT_ELDERS: SeedElder[] = [
phone: "13800000010",
medicalNotes: "短住康复结束后退住。",
},
{
name: "马淑芬",
gender: "female",
age: 86,
careLevel: "intensive",
status: "active",
primaryContact: "马晨",
phone: "13800000011",
medicalNotes: "吞咽困难,晚餐需软食并观察呛咳。",
},
{
name: "胡志强",
gender: "male",
age: 80,
careLevel: "assisted",
status: "active",
primaryContact: "胡悦",
phone: "13800000012",
medicalNotes: "术后康复,需记录每日步行距离。",
},
{
name: "高兰英",
gender: "female",
age: 77,
careLevel: "semi-assisted",
status: "active",
primaryContact: "高磊",
phone: "13800000013",
medicalNotes: "轻度认知障碍,活动后需陪同返回房间。",
},
{
name: "钱松柏",
gender: "male",
age: 89,
careLevel: "intensive",
status: "active",
primaryContact: "钱宁",
phone: "13800000014",
medicalNotes: "慢阻肺,夜间吸氧,重点关注血氧。",
},
{
name: "林玉琴",
gender: "female",
age: 71,
careLevel: "self-care",
status: "active",
primaryContact: "林涛",
phone: "13800000015",
medicalNotes: "短住托养,需协助完成初次健康档案。",
},
{
name: "曹文海",
gender: "male",
age: 83,
careLevel: "assisted",
status: "pending",
primaryContact: "曹颖",
phone: "13800000016",
medicalNotes: "已预约下周入住,等待家属确认床位。",
},
{
name: "罗海燕",
gender: "female",
age: 75,
careLevel: "semi-assisted",
status: "pending",
primaryContact: "罗杰",
phone: "13800000017",
medicalNotes: "需补充近期体检报告。",
},
{
name: "邓启明",
gender: "male",
age: 70,
careLevel: "self-care",
status: "discharged",
primaryContact: "邓可",
phone: "13800000018",
medicalNotes: "试住结束后转家庭照护。",
},
];
const DEFAULT_ADMISSIONS: SeedAdmission[] = [
@@ -245,7 +357,13 @@ const DEFAULT_ADMISSIONS: SeedAdmission[] = [
{ elderName: "赵国华", bedCode: "A202-1", status: "active", notes: "重点照护入住" },
{ elderName: "孙秀梅", bedCode: "R101-1", status: "active", notes: "康复观察入住" },
{ elderName: "刘长青", bedCode: "B101-1", status: "active", notes: "自理区入住" },
{ elderName: "马淑芬", bedCode: "A301-1", status: "active", notes: "失能照护入住" },
{ elderName: "胡志强", bedCode: "R201-1", status: "active", notes: "术后康复入住" },
{ elderName: "高兰英", bedCode: "R202-1", status: "active", notes: "认知训练入住" },
{ elderName: "钱松柏", bedCode: "S101-1", status: "active", notes: "医养观察入住" },
{ elderName: "林玉琴", bedCode: "B202-1", status: "active", notes: "短住托养入住" },
{ elderName: "吴佩兰", bedCode: "B201-1", status: "discharged", notes: "历史退住记录" },
{ elderName: "邓启明", bedCode: "A301-2", status: "discharged", notes: "试住退住记录" },
];
const DEFAULT_HEALTH_PROFILES: SeedHealthProfile[] = [
@@ -281,6 +399,46 @@ const DEFAULT_HEALTH_PROFILES: SeedHealthProfile[] = [
careRestrictions: "避免爬楼和长时间站立。",
emergencyNotes: "胸痛需立即报告。",
},
{
elderName: "马淑芬",
allergyNotes: "头孢类药物过敏",
medicalHistory: "脑梗后吞咽功能下降,近期体重下降。",
medicationNotes: "晚间营养补充剂,服药后观察吞咽。",
careRestrictions: "软食,进食时保持坐位,餐后半小时内不平卧。",
emergencyNotes: "呛咳、紫绀或呼吸急促时立即通知医生。",
},
{
elderName: "胡志强",
allergyNotes: "",
medicalHistory: "髋关节术后康复中。",
medicationNotes: "按医嘱服用止痛药和抗凝药。",
careRestrictions: "转移位需双人协助,禁止独自上下楼。",
emergencyNotes: "伤口渗血或疼痛加重时联系康复医生。",
},
{
elderName: "高兰英",
allergyNotes: "无明确药物过敏史",
medicalHistory: "轻度认知障碍,夜间偶发迷路。",
medicationNotes: "睡前药物由护理员核对。",
careRestrictions: "外出活动需佩戴定位牌。",
emergencyNotes: "离开活动区超过 10 分钟未返回时启动寻人流程。",
},
{
elderName: "钱松柏",
allergyNotes: "",
medicalHistory: "慢阻肺,长期吸入治疗。",
medicationNotes: "吸入剂每日两次,夜间低流量吸氧。",
careRestrictions: "避免受凉和烟味刺激。",
emergencyNotes: "血氧低于 90 或呼吸困难时立即启动应急流程。",
},
{
elderName: "林玉琴",
allergyNotes: "无",
medicalHistory: "短住托养,既往骨质疏松。",
medicationNotes: "钙剂和维生素 D 每日提醒。",
careRestrictions: "浴室需防滑协助。",
emergencyNotes: "跌倒后即刻评估并通知家属。",
},
];
const DEFAULT_VITAL_RECORDS: SeedVitalRecord[] = [
@@ -331,6 +489,90 @@ const DEFAULT_VITAL_RECORDS: SeedVitalRecord[] = [
spo2: 91,
notes: "体温和血氧异常,需复核。",
},
{
elderName: "马淑芬",
recordedHoursAgo: 3,
source: "manual",
systolicBp: 142,
diastolicBp: 86,
heartRate: 96,
temperatureTenths: 369,
spo2: 94,
bloodGlucoseTenths: 73,
weightTenths: 486,
notes: "晚餐前复测,吞咽情况稳定。",
},
{
elderName: "胡志强",
recordedHoursAgo: 9,
source: "device",
systolicBp: 124,
diastolicBp: 76,
heartRate: 82,
temperatureTenths: 364,
spo2: 98,
weightTenths: 705,
notes: "康复训练后体征平稳。",
},
{
elderName: "高兰英",
recordedHoursAgo: 15,
source: "manual",
systolicBp: 136,
diastolicBp: 84,
heartRate: 78,
temperatureTenths: 366,
spo2: 97,
bloodGlucoseTenths: 65,
notes: "夜间巡护记录,情绪平稳。",
},
{
elderName: "钱松柏",
recordedHoursAgo: 1,
source: "device",
systolicBp: 150,
diastolicBp: 88,
heartRate: 104,
temperatureTenths: 372,
spo2: 89,
notes: "夜间血氧低,已安排吸氧复核。",
},
{
elderName: "林玉琴",
recordedHoursAgo: 22,
source: "import",
systolicBp: 118,
diastolicBp: 72,
heartRate: 70,
temperatureTenths: 363,
spo2: 99,
bloodGlucoseTenths: 58,
weightTenths: 552,
notes: "入院体检导入记录。",
},
{
elderName: "王桂兰",
recordedHoursAgo: 26,
source: "device",
systolicBp: 145,
diastolicBp: 90,
heartRate: 84,
temperatureTenths: 366,
spo2: 97,
notes: "前一日夜间复测,血压较高。",
},
{
elderName: "周玉珍",
recordedHoursAgo: 30,
source: "manual",
systolicBp: 130,
diastolicBp: 80,
heartRate: 74,
temperatureTenths: 365,
spo2: 98,
bloodGlucoseTenths: 92,
notes: "晨间血糖记录。",
},
];
const DEFAULT_CHRONIC_CONDITIONS: SeedChronicCondition[] = [
@@ -355,6 +597,48 @@ const DEFAULT_CHRONIC_CONDITIONS: SeedChronicCondition[] = [
treatmentNotes: "按医嘱服药,避免劳累。",
followUpNotes: "月度复诊提醒。",
},
{
elderName: "马淑芬",
name: "吞咽功能障碍",
status: "active",
treatmentNotes: "软食和吞咽训练,餐中观察呛咳。",
followUpNotes: "每三天评估进食量和体重。",
},
{
elderName: "胡志强",
name: "髋关节术后康复",
status: "active",
treatmentNotes: "每日步态训练,逐步增加步行距离。",
followUpNotes: "康复师每周复评。",
},
{
elderName: "高兰英",
name: "轻度认知障碍",
status: "controlled",
treatmentNotes: "认知训练和日常定向提醒。",
followUpNotes: "观察夜间离床次数。",
},
{
elderName: "钱松柏",
name: "慢阻肺",
status: "active",
treatmentNotes: "吸入治疗和低流量吸氧。",
followUpNotes: "每日汇总夜间血氧趋势。",
},
{
elderName: "林玉琴",
name: "骨质疏松",
status: "controlled",
treatmentNotes: "补钙、维生素 D 和防跌倒宣教。",
followUpNotes: "短住结束前出具照护建议。",
},
{
elderName: "吴佩兰",
name: "膝关节退行性病变",
status: "resolved",
treatmentNotes: "历史康复记录归档。",
followUpNotes: "退住后无需跟进。",
},
];
const DEFAULT_HEALTH_REVIEWS: SeedHealthReview[] = [
@@ -385,6 +669,33 @@ const DEFAULT_HEALTH_REVIEWS: SeedHealthReview[] = [
description: "餐后血糖高于日常水平。",
resolutionNotes: "已调整晚餐主食并安排次日复测。",
},
{
elderName: "钱松柏",
vitalIndex: 7,
severity: "critical",
status: "pending",
title: "夜间血氧低",
description: "设备记录血氧低于 90需要确认吸氧和呼吸状态。",
resolutionNotes: "",
},
{
elderName: "马淑芬",
vitalIndex: 4,
severity: "warning",
status: "reviewed",
title: "体重下降观察",
description: "近期体重偏低,需要结合进食量继续观察。",
resolutionNotes: "已通知营养师调整晚间加餐。",
},
{
elderName: "王桂兰",
vitalIndex: 9,
severity: "info",
status: "resolved",
title: "血压复核完成",
description: "前一日夜间血压偏高,今日已复测。",
resolutionNotes: "晨间复测仍偏高,转入本日待处理复核。",
},
];
const DEFAULT_CARE_TASKS: SeedCareTask[] = [
@@ -439,6 +750,125 @@ const DEFAULT_CARE_TASKS: SeedCareTask[] = [
assigneeLabel: "三号护理组",
executionNotes: "",
},
{
elderName: "马淑芬",
title: "晚餐软食进食观察",
careType: "meal",
priority: "urgent",
status: "pending",
scheduledHoursOffset: 2,
assigneeLabel: "重点护理组",
executionNotes: "",
},
{
elderName: "胡志强",
title: "步行训练记录",
careType: "rehab",
priority: "normal",
status: "completed",
scheduledHoursOffset: -6,
completedHoursAgo: 5,
assigneeLabel: "康复护理组",
executionNotes: "扶行 80 米,未诉明显疼痛。",
},
{
elderName: "高兰英",
title: "活动室返回陪同",
careType: "daily_care",
priority: "normal",
status: "in_progress",
scheduledHoursOffset: 0,
assigneeLabel: "二号护理组",
executionNotes: "已到活动室,等待小组活动结束。",
},
{
elderName: "钱松柏",
title: "夜间吸氧设备检查",
careType: "inspection",
priority: "urgent",
status: "pending",
scheduledHoursOffset: -1,
assigneeLabel: "夜班护理组",
executionNotes: "",
},
{
elderName: "林玉琴",
title: "短住入院宣教",
careType: "other",
priority: "low",
status: "completed",
scheduledHoursOffset: -24,
completedHoursAgo: 23,
assigneeLabel: "接待护理组",
executionNotes: "已完成房间设施、防跌倒和呼叫器使用说明。",
},
{
elderName: "赵国华",
title: "床单位清洁消毒",
careType: "cleaning",
priority: "normal",
status: "cancelled",
scheduledHoursOffset: -8,
assigneeLabel: "保洁协同组",
executionNotes: "因临时复查延期,已改派夜班完成。",
},
];
const DEFAULT_SYSTEM_INCIDENTS: SeedSystemIncident[] = [
{
severity: "warning",
status: "open",
title: "床头呼叫器待检修",
description: "A102-2 床位呼叫器处于检修状态,完成后可恢复使用。",
source: "默认工作区初始化",
createdHoursAgo: 2,
},
{
severity: "info",
status: "acknowledged",
title: "本周护理评估待完成",
description: "陈秀英、何德明等待入住评估,可在老人档案和床位工作区继续处理。",
source: "默认工作区初始化",
createdHoursAgo: 6,
acknowledgedHoursAgo: 5,
},
{
severity: "critical",
status: "resolved",
title: "夜间巡房异常已处理",
description: "重点照护房夜间巡房提醒已恢复,保留事件用于状态页验证。",
source: "默认工作区初始化",
createdHoursAgo: 14,
acknowledgedHoursAgo: 13,
resolvedHoursAgo: 11,
},
{
severity: "critical",
status: "open",
title: "S101 医养观察房血氧告警",
description: "钱松柏夜间血氧持续低于阈值,需要护理组和医生复核。",
source: "生命体征设备",
createdHoursAgo: 1,
},
{
severity: "warning",
status: "acknowledged",
title: "A302 氧气接口保养延期",
description: "A302-2 氧气接口保养未按计划完成,床位暂不安排入住。",
source: "设备巡检",
createdHoursAgo: 20,
acknowledgedHoursAgo: 18,
},
{
severity: "info",
status: "closed",
title: "短住托养资料归档完成",
description: "林玉琴短住托养资料已录入并完成家属确认。",
source: "接待护理组",
createdHoursAgo: 28,
acknowledgedHoursAgo: 27,
resolvedHoursAgo: 24,
},
];
function hasRows(rows: unknown[]): boolean {
@@ -738,31 +1168,24 @@ export async function seedDefaultWorkspaceData(organizationId: string): Promise<
}),
);
await transaction.insert(systemIncidents).values([
{
await transaction.insert(systemIncidents).values(
DEFAULT_SYSTEM_INCIDENTS.map((incident) => ({
organizationId,
severity: "warning",
status: "open",
title: "床头呼叫器待检修",
description: "A102-2 床位呼叫器处于检修状态,完成后可恢复使用。",
source: "默认工作区初始化",
},
{
organizationId,
severity: "info",
status: "acknowledged",
title: "本周护理评估待完成",
description: "陈秀英、何德明等待入住评估,可在老人档案和床位工作区继续处理。",
source: "默认工作区初始化",
},
{
organizationId,
severity: "critical",
status: "resolved",
title: "夜间巡房异常已处理",
description: "重点照护房夜间巡房提醒已恢复,保留事件用于状态页验证。",
source: "默认工作区初始化",
},
]);
severity: incident.severity,
status: incident.status,
title: incident.title,
description: incident.description,
source: incident.source,
acknowledgedAt:
incident.acknowledgedHoursAgo === undefined ? undefined : new Date(now.getTime() - incident.acknowledgedHoursAgo * 60 * 60 * 1000),
resolvedAt:
incident.resolvedHoursAgo === undefined ? undefined : new Date(now.getTime() - incident.resolvedHoursAgo * 60 * 60 * 1000),
createdAt: new Date(now.getTime() - incident.createdHoursAgo * 60 * 60 * 1000),
updatedAt: new Date(
now.getTime() -
(incident.resolvedHoursAgo ?? incident.acknowledgedHoursAgo ?? incident.createdHoursAgo) * 60 * 60 * 1000,
),
})),
);
});
}