feat(c0): 服务机构端基础数据模型(9 表 + 多租户/软删清单)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-14 12:36:43 +08:00
parent 8fca4a1b1e
commit 6f32875295
3 changed files with 299 additions and 2 deletions

View File

@ -0,0 +1,150 @@
-- CreateTable
CREATE TABLE `sys_agency` (
`code` VARCHAR(64) NOT NULL,
`org_name` VARCHAR(100) NOT NULL,
`org_type` VARCHAR(20) NOT NULL,
`credit_code` VARCHAR(18) NOT NULL,
`loc_code` VARCHAR(12) NOT NULL,
`loc_name` VARCHAR(50) NOT NULL,
`svc_source` VARCHAR(10) NOT NULL,
`license_no` VARCHAR(50) NULL,
`address` VARCHAR(200) NOT NULL,
`contact_name` VARCHAR(30) NOT NULL,
`contact_phone` VARCHAR(15) NOT NULL,
`bed_count` INTEGER NULL,
`sign_date` DATE NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` VARCHAR(36) NULL,
`updated_at` DATETIME(3) NOT NULL,
`deleted_at` DATETIME(3) NULL,
PRIMARY KEY (`code`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_staff` (
`id` VARCHAR(36) NOT NULL,
`tenant_code` VARCHAR(64) NOT NULL,
`user_id` VARCHAR(36) NULL,
`name` VARCHAR(30) NOT NULL,
`id_card_no` VARCHAR(255) NOT NULL,
`person_code` VARCHAR(20) NULL,
`phone` VARCHAR(15) NOT NULL,
`job_type` VARCHAR(20) NOT NULL,
`work_type` VARCHAR(10) NOT NULL,
`entry_date` DATE NULL,
`leave_date` DATE NULL,
`employ_status` VARCHAR(10) NOT NULL,
`cert_type` VARCHAR(20) NULL,
`cert_no` VARCHAR(50) NULL,
`cert_level` VARCHAR(20) NULL,
`cert_expire` DATE NULL,
`cert_status` VARCHAR(20) NULL,
`cert_verify_url` VARCHAR(200) NULL,
`group_leader_id` VARCHAR(36) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` VARCHAR(36) NULL,
`updated_at` DATETIME(3) NOT NULL,
`deleted_at` DATETIME(3) NULL,
UNIQUE INDEX `sys_staff_user_id_key`(`user_id`),
INDEX `sys_staff_tenant_code_name_idx`(`tenant_code`, `name`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_svc_national` (
`id` VARCHAR(36) NOT NULL,
`nat_code` VARCHAR(20) NOT NULL,
`nat_name` VARCHAR(100) NOT NULL,
`nat_category` VARCHAR(20) NOT NULL,
`nat_sub_category` VARCHAR(50) NULL,
`recommend_staff` VARCHAR(50) NULL,
`recommend_duration` VARCHAR(50) NULL,
`is_medical` BOOLEAN NOT NULL DEFAULT false,
`version` INTEGER NOT NULL DEFAULT 1,
UNIQUE INDEX `sys_svc_national_nat_code_key`(`nat_code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_svc_local` (
`id` VARCHAR(36) NOT NULL,
`loc_code` VARCHAR(12) NOT NULL,
`local_item_name` VARCHAR(100) NOT NULL,
`local_category` VARCHAR(20) NULL,
`svc_time` VARCHAR(50) NULL,
`min_freq` VARCHAR(50) NULL,
`staff_qual` VARCHAR(50) NULL,
`is_medical` BOOLEAN NOT NULL DEFAULT false,
`source_doc` VARCHAR(200) NULL,
`map_status` VARCHAR(20) NOT NULL DEFAULT 'mapped',
INDEX `sys_svc_local_loc_code_idx`(`loc_code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_svc_mapping` (
`id` VARCHAR(36) NOT NULL,
`loc_id` VARCHAR(36) NOT NULL,
`nat_id` VARCHAR(36) NULL,
`map_type` VARCHAR(20) NOT NULL,
`map_note` VARCHAR(200) NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'confirmed',
INDEX `sys_svc_mapping_loc_id_idx`(`loc_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_svc_org` (
`id` VARCHAR(36) NOT NULL,
`tenant_code` VARCHAR(64) NOT NULL,
`src_type` VARCHAR(10) NOT NULL,
`src_id` VARCHAR(36) NOT NULL,
`is_enabled` BOOLEAN NOT NULL DEFAULT true,
`price` VARCHAR(50) NULL,
`price_effective` DATETIME(3) NULL,
`is_new_national` BOOLEAN NOT NULL DEFAULT false,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` VARCHAR(36) NULL,
`updated_at` DATETIME(3) NOT NULL,
`deleted_at` DATETIME(3) NULL,
INDEX `sys_svc_org_tenant_code_src_type_idx`(`tenant_code`, `src_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_dict_abnormal_type` (
`id` VARCHAR(36) NOT NULL,
`code` VARCHAR(50) NOT NULL,
`name` VARCHAR(50) NOT NULL,
`sort` INTEGER NOT NULL DEFAULT 0,
UNIQUE INDEX `sys_dict_abnormal_type_code_key`(`code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_dict_photo_tier` (
`id` VARCHAR(36) NOT NULL,
`code` VARCHAR(20) NOT NULL,
`name` VARCHAR(50) NOT NULL,
`tier` VARCHAR(10) NOT NULL,
UNIQUE INDEX `sys_dict_photo_tier_code_key`(`code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- CreateTable
CREATE TABLE `sys_loc_config` (
`loc_code` VARCHAR(12) NOT NULL,
`loc_name` VARCHAR(50) NOT NULL,
`svc_source` VARCHAR(10) NOT NULL,
`item_count_rule` VARCHAR(100) NULL,
PRIMARY KEY (`loc_code`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

View File

@ -298,3 +298,150 @@ model RoleOrg {
@@unique([tenantCode, roleCode, orgCode]) @@unique([tenantCode, roleCode, orgCode])
@@map("sys_role_org") @@map("sys_role_org")
} }
/// 机构(=租户的业务信息,1:1 于 Tenant,code 与 tenant.code 同值)
model Agency {
code String @id @db.VarChar(64)
orgName String @map("org_name") @db.VarChar(100)
orgType String @map("org_type") @db.VarChar(20)
creditCode String @map("credit_code") @db.VarChar(18)
locCode String @map("loc_code") @db.VarChar(12)
locName String @map("loc_name") @db.VarChar(50)
svcSource String @map("svc_source") @db.VarChar(10)
licenseNo String? @map("license_no") @db.VarChar(50)
address String @map("address") @db.VarChar(200)
contactName String @map("contact_name") @db.VarChar(30)
contactPhone String @map("contact_phone") @db.VarChar(15)
bedCount Int? @map("bed_count")
signDate DateTime? @map("sign_date") @db.Date
createdAt DateTime @default(now()) @map("created_at")
createdBy String? @map("created_by") @db.VarChar(36)
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
@@map("sys_agency")
}
/// 人员(护理员/医护/组长,账号在 User,staff.user_id 关联 user.id)
model Staff {
id String @id @default(uuid(7)) @db.VarChar(36)
tenantCode String @map("tenant_code") @db.VarChar(64)
userId String? @unique @map("user_id") @db.VarChar(36)
name String @db.VarChar(30)
idCardNo String @map("id_card_no") @db.VarChar(255)
personCode String? @map("person_code") @db.VarChar(20)
phone String @db.VarChar(15)
jobType String @map("job_type") @db.VarChar(20)
workType String @map("work_type") @db.VarChar(10)
entryDate DateTime? @map("entry_date") @db.Date
leaveDate DateTime? @map("leave_date") @db.Date
employStatus String @map("employ_status") @db.VarChar(10)
certType String? @map("cert_type") @db.VarChar(20)
certNo String? @map("cert_no") @db.VarChar(50)
certLevel String? @map("cert_level") @db.VarChar(20)
certExpire DateTime? @map("cert_expire") @db.Date
certStatus String? @map("cert_status") @db.VarChar(20)
certVerifyUrl String? @map("cert_verify_url") @db.VarChar(200)
groupLeaderId String? @map("group_leader_id") @db.VarChar(36)
createdAt DateTime @default(now()) @map("created_at")
createdBy String? @map("created_by") @db.VarChar(36)
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
@@index([tenantCode, name])
@@map("sys_staff")
}
/// 国家基准库 L1(全局,36 项)
model SvcNational {
id String @id @default(uuid(7)) @db.VarChar(36)
natCode String @unique @map("nat_code") @db.VarChar(20)
natName String @map("nat_name") @db.VarChar(100)
natCategory String @map("nat_category") @db.VarChar(20)
natSubCategory String? @map("nat_sub_category") @db.VarChar(50)
recommendStaff String? @map("recommend_staff") @db.VarChar(50)
recommendDuration String? @map("recommend_duration") @db.VarChar(50)
isMedical Boolean @default(false) @map("is_medical")
version Int @default(1)
@@map("sys_svc_national")
}
/// 属地目录 L2(全局,昆明 45 项 / 州市引用 L1)
model SvcLocal {
id String @id @default(uuid(7)) @db.VarChar(36)
locCode String @map("loc_code") @db.VarChar(12)
localItemName String @map("local_item_name") @db.VarChar(100)
localCategory String? @map("local_category") @db.VarChar(20)
svcTime String? @map("svc_time") @db.VarChar(50)
minFreq String? @map("min_freq") @db.VarChar(50)
staffQual String? @map("staff_qual") @db.VarChar(50)
isMedical Boolean @default(false) @map("is_medical")
sourceDoc String? @map("source_doc") @db.VarChar(200)
mapStatus String @default("mapped") @map("map_status") @db.VarChar(20)
@@index([locCode])
@@map("sys_svc_local")
}
/// 属地↔国家映射 L3(全局,机构不可见)
model SvcMapping {
id String @id @default(uuid(7)) @db.VarChar(36)
locId String @map("loc_id") @db.VarChar(36)
natId String? @map("nat_id") @db.VarChar(36)
mapType String @map("map_type") @db.VarChar(20)
mapNote String? @map("map_note") @db.VarChar(200)
status String @default("confirmed") @db.VarChar(20)
@@index([locId])
@@map("sys_svc_mapping")
}
/// 机构项目库 L4(租户级:机构勾选+配价)
model SvcOrg {
id String @id @default(uuid(7)) @db.VarChar(36)
tenantCode String @map("tenant_code") @db.VarChar(64)
srcType String @map("src_type") @db.VarChar(10)
srcId String @map("src_id") @db.VarChar(36)
isEnabled Boolean @default(true) @map("is_enabled")
price String? @map("price") @db.VarChar(50)
priceEffective DateTime? @map("price_effective")
isNewNational Boolean @default(false) @map("is_new_national")
createdAt DateTime @default(now()) @map("created_at")
createdBy String? @map("created_by") @db.VarChar(36)
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
@@index([tenantCode, srcType])
@@map("sys_svc_org")
}
/// 异常分类字典(全局)
model DictAbnormalType {
id String @id @default(uuid(7)) @db.VarChar(36)
code String @unique @db.VarChar(50)
name String @db.VarChar(50)
sort Int @default(0)
@@map("sys_dict_abnormal_type")
}
/// 拍照分档字典(全局)
model DictPhotoTier {
id String @id @default(uuid(7)) @db.VarChar(36)
code String @unique @db.VarChar(20)
name String @db.VarChar(50)
tier String @db.VarChar(10)
@@map("sys_dict_photo_tier")
}
/// 统筹区配置(全局)
model LocConfig {
locCode String @id @map("loc_code") @db.VarChar(12)
locName String @map("loc_name") @db.VarChar(50)
svcSource String @map("svc_source") @db.VarChar(10)
itemCountRule String? @map("item_count_rule") @db.VarChar(100)
@@map("sys_loc_config")
}

View File

@ -3,9 +3,9 @@ import { PrismaClient } from '@prisma/client';
import { getRequestContext } from '../common/context/request-context.js'; import { getRequestContext } from '../common/context/request-context.js';
/** 不带 tenantCode 的模型(租户表自身)。 */ /** 不带 tenantCode 的模型(租户表自身)。 */
const TENANT_FREE = new Set(['Tenant']); const TENANT_FREE = new Set(['Tenant', 'Agency', 'SvcNational', 'SvcLocal', 'SvcMapping', 'DictAbnormalType', 'DictPhotoTier', 'LocConfig']);
/** 软删模型:读操作统一注入 deletedAt IS NULL。 */ /** 软删模型:读操作统一注入 deletedAt IS NULL。 */
const SOFT_DELETE = new Set(['User', 'Role', 'Resource', 'Org', 'DictType', 'DictItem', 'Config', 'Notice']); const SOFT_DELETE = new Set(['User', 'Role', 'Resource', 'Org', 'DictType', 'DictItem', 'Config', 'Notice', 'Staff', 'SvcOrg']);
const READ_OPS = new Set(['findMany', 'findFirst', 'findUnique', 'count', 'aggregate', 'groupBy']); const READ_OPS = new Set(['findMany', 'findFirst', 'findUnique', 'count', 'aggregate', 'groupBy']);
/** 没有 where 子句的操作(create 的租户由 service 在 data 里显式写入)。 */ /** 没有 where 子句的操作(create 的租户由 service 在 data 里显式写入)。 */
const NO_WHERE_OPS = new Set(['create', 'createMany']); const NO_WHERE_OPS = new Set(['create', 'createMany']);