136 lines
4.5 KiB
TypeScript
136 lines
4.5 KiB
TypeScript
import { BadRequestException, 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 { CreateGroupDto } from './dto/create-group.dto.js';
|
|
import { UpdateGroupDto } from './dto/update-group.dto.js';
|
|
import { OrganizationQueryDto } from '../organization-common/dto/organization-query.dto.js';
|
|
|
|
/**
|
|
* 小组资源服务:聚焦小组实体 CRUD 与院级作用域约束。
|
|
*/
|
|
@Injectable()
|
|
export class GroupsService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly access: OrganizationAccessService,
|
|
) {}
|
|
|
|
/**
|
|
* 创建小组:系统管理员可跨院;院管仅可在本院科室下创建。
|
|
*/
|
|
async create(actor: ActorContext, dto: CreateGroupDto) {
|
|
this.access.assertRole(actor, [Role.SYSTEM_ADMIN, Role.HOSPITAL_ADMIN]);
|
|
const departmentId = this.access.toInt(dto.departmentId, MESSAGES.ORG.DEPARTMENT_ID_REQUIRED);
|
|
const department = await this.access.ensureDepartmentExists(departmentId);
|
|
this.access.assertHospitalScope(actor, department.hospitalId);
|
|
|
|
return this.prisma.group.create({
|
|
data: {
|
|
name: this.access.normalizeName(dto.name, MESSAGES.ORG.GROUP_NAME_REQUIRED),
|
|
departmentId,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 查询小组列表:院管默认仅返回本院数据。
|
|
*/
|
|
async findAll(actor: ActorContext, query: OrganizationQueryDto) {
|
|
this.access.assertRole(actor, [Role.SYSTEM_ADMIN, Role.HOSPITAL_ADMIN]);
|
|
const paging = this.access.resolvePaging(query);
|
|
const where: Prisma.GroupWhereInput = {};
|
|
|
|
if (query.keyword) {
|
|
where.name = { contains: query.keyword.trim(), mode: 'insensitive' };
|
|
}
|
|
if (query.departmentId != null) {
|
|
where.departmentId = this.access.toInt(query.departmentId, MESSAGES.ORG.DEPARTMENT_ID_REQUIRED);
|
|
}
|
|
|
|
if (actor.role === Role.HOSPITAL_ADMIN) {
|
|
if (!actor.hospitalId) {
|
|
throw new BadRequestException(MESSAGES.ORG.HOSPITAL_ID_REQUIRED);
|
|
}
|
|
where.department = { hospitalId: actor.hospitalId };
|
|
} else if (query.hospitalId != null) {
|
|
where.department = {
|
|
hospitalId: this.access.toInt(query.hospitalId, MESSAGES.ORG.HOSPITAL_ID_REQUIRED),
|
|
};
|
|
}
|
|
|
|
const [total, list] = await this.prisma.$transaction([
|
|
this.prisma.group.count({ where }),
|
|
this.prisma.group.findMany({
|
|
where,
|
|
include: {
|
|
department: { include: { hospital: true } },
|
|
_count: { select: { users: true } },
|
|
},
|
|
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]);
|
|
const groupId = this.access.toInt(id, MESSAGES.ORG.GROUP_ID_REQUIRED);
|
|
const group = await this.prisma.group.findUnique({
|
|
where: { id: groupId },
|
|
include: {
|
|
department: { include: { hospital: true } },
|
|
_count: { select: { users: true } },
|
|
},
|
|
});
|
|
if (!group) {
|
|
throw new NotFoundException(MESSAGES.ORG.GROUP_NOT_FOUND);
|
|
}
|
|
this.access.assertHospitalScope(actor, group.department.hospital.id);
|
|
return group;
|
|
}
|
|
|
|
/**
|
|
* 更新小组:院管仅可修改本院小组。
|
|
*/
|
|
async update(actor: ActorContext, id: number, dto: UpdateGroupDto) {
|
|
const current = await this.findOne(actor, id);
|
|
const data: Prisma.GroupUpdateInput = {};
|
|
|
|
if (dto.departmentId !== undefined) {
|
|
throw new BadRequestException(MESSAGES.ORG.GROUP_REPARENT_FORBIDDEN);
|
|
}
|
|
|
|
if (dto.name !== undefined) {
|
|
data.name = this.access.normalizeName(dto.name, MESSAGES.ORG.GROUP_NAME_REQUIRED);
|
|
}
|
|
|
|
return this.prisma.group.update({
|
|
where: { id: current.id },
|
|
data,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 删除小组:院管仅可删除本院小组。
|
|
*/
|
|
async remove(actor: ActorContext, id: number) {
|
|
const current = await this.findOne(actor, id);
|
|
try {
|
|
return await this.prisma.group.delete({ where: { id: current.id } });
|
|
} catch (error) {
|
|
this.access.handleDeleteConflict(error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|