2. 扩展患者手术与材料模型,更新种子数据 3. 新增字典模块,增强设备植入目录管理能力 4. 重构患者后台服务与表单链路,统一权限与参数校验 5. 管理台新增字典页面并改造患者/设备页面与路由权限 6. 补充字典及相关领域 e2e 测试并更新文档"
141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { Prisma } from '../generated/prisma/client.js';
|
|
import { Role } from '../generated/prisma/enums.js';
|
|
import type { ActorContext } from '../common/actor-context.js';
|
|
import { MESSAGES } from '../common/messages.js';
|
|
import { PrismaService } from '../prisma.service.js';
|
|
import { OrganizationAccessService } from '../organization-common/organization-access.service.js';
|
|
import { CreateHospitalDto } from './dto/create-hospital.dto.js';
|
|
import { UpdateHospitalDto } from './dto/update-hospital.dto.js';
|
|
import { OrganizationQueryDto } from '../organization-common/dto/organization-query.dto.js';
|
|
|
|
/**
|
|
* 医院资源服务:聚焦医院实体的 CRUD 与院级权限约束。
|
|
*/
|
|
@Injectable()
|
|
export class HospitalsService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly access: OrganizationAccessService,
|
|
) {}
|
|
|
|
/**
|
|
* 创建医院:仅系统管理员可调用。
|
|
*/
|
|
async create(actor: ActorContext, dto: CreateHospitalDto) {
|
|
this.access.assertSystemAdmin(
|
|
actor,
|
|
MESSAGES.ORG.SYSTEM_ADMIN_ONLY_CREATE_HOSPITAL,
|
|
);
|
|
return this.prisma.hospital.create({
|
|
data: {
|
|
name: this.access.normalizeName(
|
|
dto.name,
|
|
MESSAGES.ORG.HOSPITAL_NAME_REQUIRED,
|
|
),
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 查询医院列表:系统管理员可查全量;院管/主任/组长仅可查看本院。
|
|
*/
|
|
async findAll(actor: ActorContext, query: OrganizationQueryDto) {
|
|
this.access.assertRole(actor, [
|
|
Role.SYSTEM_ADMIN,
|
|
Role.HOSPITAL_ADMIN,
|
|
Role.DIRECTOR,
|
|
Role.LEADER,
|
|
]);
|
|
const paging = this.access.resolvePaging(query);
|
|
|
|
const where: Prisma.HospitalWhereInput = {};
|
|
if (query.keyword) {
|
|
where.name = { contains: query.keyword.trim(), mode: 'insensitive' };
|
|
}
|
|
if (actor.role !== Role.SYSTEM_ADMIN) {
|
|
where.id = this.access.requireActorHospitalId(actor);
|
|
}
|
|
|
|
const [total, list] = await this.prisma.$transaction([
|
|
this.prisma.hospital.count({ where }),
|
|
this.prisma.hospital.findMany({
|
|
where,
|
|
skip: paging.skip,
|
|
take: paging.take,
|
|
orderBy: { id: 'desc' },
|
|
}),
|
|
]);
|
|
|
|
return { total, ...paging, list };
|
|
}
|
|
|
|
/**
|
|
* 查询医院详情:院管/主任/组长仅能查看本院。
|
|
*/
|
|
async findOne(actor: ActorContext, id: number) {
|
|
this.access.assertRole(actor, [
|
|
Role.SYSTEM_ADMIN,
|
|
Role.HOSPITAL_ADMIN,
|
|
Role.DIRECTOR,
|
|
Role.LEADER,
|
|
]);
|
|
const hospitalId = this.access.toInt(id, MESSAGES.ORG.HOSPITAL_ID_REQUIRED);
|
|
const hospital = await this.prisma.hospital.findUnique({
|
|
where: { id: hospitalId },
|
|
include: {
|
|
_count: {
|
|
select: {
|
|
departments: true,
|
|
users: true,
|
|
patients: true,
|
|
tasks: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
if (!hospital) {
|
|
throw new NotFoundException(MESSAGES.ORG.HOSPITAL_NOT_FOUND);
|
|
}
|
|
this.access.assertHospitalScope(actor, hospital.id);
|
|
return hospital;
|
|
}
|
|
|
|
/**
|
|
* 更新医院:院管仅能更新本院。
|
|
*/
|
|
async update(actor: ActorContext, id: number, dto: UpdateHospitalDto) {
|
|
const current = await this.findOne(actor, id);
|
|
const data: Prisma.HospitalUpdateInput = {};
|
|
if (dto.name !== undefined) {
|
|
data.name = this.access.normalizeName(
|
|
dto.name,
|
|
MESSAGES.ORG.HOSPITAL_NAME_REQUIRED,
|
|
);
|
|
}
|
|
return this.prisma.hospital.update({
|
|
where: { id: current.id },
|
|
data,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 删除医院:仅系统管理员允许。
|
|
*/
|
|
async remove(actor: ActorContext, id: number) {
|
|
this.access.assertSystemAdmin(
|
|
actor,
|
|
MESSAGES.ORG.SYSTEM_ADMIN_ONLY_DELETE_HOSPITAL,
|
|
);
|
|
const hospitalId = this.access.toInt(id, MESSAGES.ORG.HOSPITAL_ID_REQUIRED);
|
|
await this.access.ensureHospitalExists(hospitalId);
|
|
|
|
try {
|
|
return await this.prisma.hospital.delete({ where: { id: hospitalId } });
|
|
} catch (error) {
|
|
this.access.handleDeleteConflict(error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|