feat: add care execution workspace
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
beds,
|
||||
buildings,
|
||||
campuses,
|
||||
careTasks,
|
||||
chronicConditions,
|
||||
elders,
|
||||
floors,
|
||||
@@ -92,6 +93,18 @@ type SeedHealthReview = {
|
||||
vitalIndex: number;
|
||||
};
|
||||
|
||||
type SeedCareTask = {
|
||||
assigneeLabel: string;
|
||||
careType: "daily_care" | "meal" | "medication" | "rehab" | "inspection" | "cleaning" | "other";
|
||||
completedHoursAgo?: number;
|
||||
elderName: string;
|
||||
executionNotes: string;
|
||||
priority: "low" | "normal" | "high" | "urgent";
|
||||
scheduledHoursOffset: number;
|
||||
status: "pending" | "in_progress" | "completed" | "cancelled";
|
||||
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 },
|
||||
@@ -374,6 +387,60 @@ const DEFAULT_HEALTH_REVIEWS: SeedHealthReview[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_CARE_TASKS: SeedCareTask[] = [
|
||||
{
|
||||
elderName: "王桂兰",
|
||||
title: "晨间协助洗漱与翻身",
|
||||
careType: "daily_care",
|
||||
priority: "normal",
|
||||
status: "pending",
|
||||
scheduledHoursOffset: 1,
|
||||
assigneeLabel: "一号护理组",
|
||||
executionNotes: "",
|
||||
},
|
||||
{
|
||||
elderName: "孙秀梅",
|
||||
title: "膝关节康复训练陪同",
|
||||
careType: "rehab",
|
||||
priority: "high",
|
||||
status: "in_progress",
|
||||
scheduledHoursOffset: -1,
|
||||
assigneeLabel: "康复护理组",
|
||||
executionNotes: "已完成热身,等待康复师复核动作。",
|
||||
},
|
||||
{
|
||||
elderName: "周玉珍",
|
||||
title: "餐前血糖记录与助餐",
|
||||
careType: "meal",
|
||||
priority: "normal",
|
||||
status: "completed",
|
||||
scheduledHoursOffset: -4,
|
||||
completedHoursAgo: 3,
|
||||
assigneeLabel: "二号护理组",
|
||||
executionNotes: "餐前血糖已记录,午餐摄入约八成。",
|
||||
},
|
||||
{
|
||||
elderName: "赵国华",
|
||||
title: "重点照护夜间巡检补记",
|
||||
careType: "inspection",
|
||||
priority: "urgent",
|
||||
status: "pending",
|
||||
scheduledHoursOffset: -2,
|
||||
assigneeLabel: "夜班护理组",
|
||||
executionNotes: "",
|
||||
},
|
||||
{
|
||||
elderName: "刘长青",
|
||||
title: "用药提醒与胸痛询问",
|
||||
careType: "medication",
|
||||
priority: "high",
|
||||
status: "pending",
|
||||
scheduledHoursOffset: 3,
|
||||
assigneeLabel: "三号护理组",
|
||||
executionNotes: "",
|
||||
},
|
||||
];
|
||||
|
||||
function hasRows(rows: unknown[]): boolean {
|
||||
return rows.length > 0;
|
||||
}
|
||||
@@ -558,6 +625,31 @@ export async function seedDefaultWorkspaceData(organizationId: string): Promise<
|
||||
}),
|
||||
);
|
||||
|
||||
const now = new Date();
|
||||
await transaction.insert(careTasks).values(
|
||||
DEFAULT_CARE_TASKS.map((task) => {
|
||||
const elderId = elderIdByName.get(task.elderName);
|
||||
if (!elderId) {
|
||||
throw new Error("默认护理任务初始化失败");
|
||||
}
|
||||
|
||||
const completedAt = task.completedHoursAgo === undefined ? undefined : new Date(now.getTime() - task.completedHoursAgo * 60 * 60 * 1000);
|
||||
|
||||
return {
|
||||
organizationId,
|
||||
elderId,
|
||||
title: task.title,
|
||||
careType: task.careType,
|
||||
priority: task.priority,
|
||||
status: task.status,
|
||||
scheduledAt: new Date(now.getTime() + task.scheduledHoursOffset * 60 * 60 * 1000),
|
||||
assigneeLabel: task.assigneeLabel,
|
||||
executionNotes: task.executionNotes,
|
||||
completedAt,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
await transaction.insert(healthProfiles).values(
|
||||
DEFAULT_HEALTH_PROFILES.map((profile) => {
|
||||
const elderId = elderIdByName.get(profile.elderName);
|
||||
@@ -577,7 +669,6 @@ export async function seedDefaultWorkspaceData(organizationId: string): Promise<
|
||||
}),
|
||||
);
|
||||
|
||||
const now = new Date();
|
||||
const vitalRows = await transaction
|
||||
.insert(vitalRecords)
|
||||
.values(
|
||||
|
||||
@@ -25,6 +25,8 @@ export const PERMISSION_DEFINITIONS: PermissionDefinition[] = [
|
||||
{ id: "audit:read", label: "查看审计", category: "审计", description: "查看关键操作审计日志。" },
|
||||
{ id: "incident:read", label: "查看故障", category: "运维", description: "查看系统健康和故障中心。" },
|
||||
{ id: "incident:manage", label: "处理故障", category: "运维", description: "确认、处理和关闭故障事件。" },
|
||||
{ id: "care:read", label: "查看护理任务", category: "护理", description: "查看护理服务任务、执行状态和记录。" },
|
||||
{ id: "care:manage", label: "处理护理任务", category: "护理", description: "启动、完成和维护护理服务执行记录。" },
|
||||
{ id: "health:read", label: "查看健康数据", category: "健康", description: "查看老人健康档案、生命体征和异常复核。" },
|
||||
{ id: "health:manage", label: "管理健康数据", category: "健康", description: "维护健康档案、生命体征、慢病和异常复核。" },
|
||||
{ id: "facility:read", label: "查看房间床位", category: "床位", description: "查看房间、床位和占用状态。" },
|
||||
@@ -91,6 +93,8 @@ export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [
|
||||
"permission:read",
|
||||
"audit:read",
|
||||
"incident:read",
|
||||
"care:read",
|
||||
"care:manage",
|
||||
"health:read",
|
||||
"health:manage",
|
||||
"facility:read",
|
||||
@@ -111,6 +115,8 @@ export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [
|
||||
"account:read",
|
||||
"role:read",
|
||||
"audit:read",
|
||||
"care:read",
|
||||
"care:manage",
|
||||
"health:read",
|
||||
"health:manage",
|
||||
"facility:read",
|
||||
@@ -127,13 +133,13 @@ export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [
|
||||
key: "caregiver",
|
||||
scope: "organization",
|
||||
description: "照护人员,查看床位和维护老人照护信息。",
|
||||
permissions: ["health:read", "health:manage", "facility:read", "admission:read", "elder:read", "elder:update"],
|
||||
permissions: ["care:read", "care:manage", "health:read", "health:manage", "facility:read", "admission:read", "elder:read", "elder:update"],
|
||||
},
|
||||
{
|
||||
key: "viewer",
|
||||
scope: "organization",
|
||||
description: "普通用户,只读查看老人、床位和入住信息。",
|
||||
permissions: ["health:read", "facility:read", "admission:read", "elder:read"],
|
||||
permissions: ["care:read", "health:read", "facility:read", "admission:read", "elder:read"],
|
||||
},
|
||||
{
|
||||
key: "family",
|
||||
|
||||
@@ -27,6 +27,9 @@ export const joinRequestStatusEnum = pgEnum("join_request_status", ["pending", "
|
||||
export const vitalRecordSourceEnum = pgEnum("vital_record_source", ["manual", "device", "import"]);
|
||||
export const chronicConditionStatusEnum = pgEnum("chronic_condition_status", ["active", "controlled", "resolved"]);
|
||||
export const healthReviewStatusEnum = pgEnum("health_review_status", ["pending", "reviewed", "resolved"]);
|
||||
export const careTaskTypeEnum = pgEnum("care_task_type", ["daily_care", "meal", "medication", "rehab", "inspection", "cleaning", "other"]);
|
||||
export const careTaskPriorityEnum = pgEnum("care_task_priority", ["low", "normal", "high", "urgent"]);
|
||||
export const careTaskStatusEnum = pgEnum("care_task_status", ["pending", "in_progress", "completed", "cancelled"]);
|
||||
|
||||
export const systemSettings = pgTable("system_settings", {
|
||||
id: text("id").primaryKey().default("global"),
|
||||
@@ -299,6 +302,27 @@ export const healthAnomalyReviews = pgTable("health_anomaly_reviews", {
|
||||
reviewQueueLookup: index("health_anomaly_reviews_queue_lookup").on(table.organizationId, table.status, table.severity),
|
||||
}));
|
||||
|
||||
export const careTasks = pgTable("care_tasks", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
|
||||
elderId: uuid("elder_id").references(() => elders.id, { onDelete: "set null" }),
|
||||
title: text("title").notNull(),
|
||||
careType: careTaskTypeEnum("care_type").notNull(),
|
||||
priority: careTaskPriorityEnum("priority").notNull().default("normal"),
|
||||
status: careTaskStatusEnum("status").notNull().default("pending"),
|
||||
scheduledAt: timestamp("scheduled_at", { withTimezone: true }).notNull(),
|
||||
assigneeLabel: text("assignee_label").notNull().default(""),
|
||||
executionNotes: text("execution_notes").notNull().default(""),
|
||||
completedAt: timestamp("completed_at", { withTimezone: true }),
|
||||
createdByAccountId: uuid("created_by_account_id").references(() => accounts.id),
|
||||
completedByAccountId: uuid("completed_by_account_id").references(() => accounts.id),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => ({
|
||||
queueLookup: index("care_tasks_queue_lookup").on(table.organizationId, table.status, table.priority),
|
||||
scheduleLookup: index("care_tasks_schedule_lookup").on(table.organizationId, table.scheduledAt),
|
||||
}));
|
||||
|
||||
export const admissions = pgTable("admissions", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
|
||||
|
||||
Reference in New Issue
Block a user