prisma.config.ts 新增 seed 命令:node --env-file=.env --loader ts-node/esm prisma/seed.ts
各 DTO 增加 Swagger ApiProperty/ApiPropertyOptional 描述与示例(尤其 phone/password) seed.ts 为新增完整 seed 脚本(幂等 upsert + 角色样例 + 患者 + 工程师分配)
This commit is contained in:
parent
0024562863
commit
569d827b78
@ -7,6 +7,7 @@ export default defineConfig({
|
|||||||
schema: "prisma/schema.prisma",
|
schema: "prisma/schema.prisma",
|
||||||
migrations: {
|
migrations: {
|
||||||
path: "prisma/migrations",
|
path: "prisma/migrations",
|
||||||
|
seed: "node --env-file=.env --loader ts-node/esm prisma/seed.ts",
|
||||||
},
|
},
|
||||||
datasource: {
|
datasource: {
|
||||||
url: process.env["DATABASE_URL"],
|
url: process.env["DATABASE_URL"],
|
||||||
|
|||||||
301
prisma/seed.ts
Normal file
301
prisma/seed.ts
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
import { randomBytes, scrypt } from 'node:crypto';
|
||||||
|
import { promisify } from 'node:util';
|
||||||
|
import { PrismaClient } from '../src/generated/prisma/client.js';
|
||||||
|
import { UserRole } from '../src/generated/prisma/enums.js';
|
||||||
|
|
||||||
|
const scryptAsync = promisify(scrypt);
|
||||||
|
|
||||||
|
const TEST_PASSWORD = 'Test123456';
|
||||||
|
const HOSPITAL_CODE = 'DEMO_HOSP_001';
|
||||||
|
const HOSPITAL_NAME = 'Demo Hospital';
|
||||||
|
const DEPARTMENT_NAME = 'Cardiology';
|
||||||
|
const MEDICAL_GROUP_NAME = 'Group A';
|
||||||
|
|
||||||
|
interface SeedUserInput {
|
||||||
|
phone: string;
|
||||||
|
name: string;
|
||||||
|
role: UserRole;
|
||||||
|
hospitalId: number | null;
|
||||||
|
departmentId: number | null;
|
||||||
|
medicalGroupId: number | null;
|
||||||
|
managerId: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PatientInput {
|
||||||
|
name: string;
|
||||||
|
hospitalId: number;
|
||||||
|
departmentId: number | null;
|
||||||
|
medicalGroupId: number | null;
|
||||||
|
doctorId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireDatabaseUrl(): string {
|
||||||
|
const url = process.env.DATABASE_URL;
|
||||||
|
if (!url) {
|
||||||
|
throw new Error('DATABASE_URL is required. Run with `node --env-file=.env ...`.');
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hashPassword(password: string): Promise<string> {
|
||||||
|
const salt = randomBytes(16).toString('hex');
|
||||||
|
const derivedKey = (await scryptAsync(password, salt, 64)) as Buffer;
|
||||||
|
return `${salt}:${derivedKey.toString('hex')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upsertUser(
|
||||||
|
prisma: PrismaClient,
|
||||||
|
user: SeedUserInput,
|
||||||
|
passwordHash: string,
|
||||||
|
) {
|
||||||
|
return prisma.user.upsert({
|
||||||
|
where: { phone: user.phone },
|
||||||
|
create: {
|
||||||
|
phone: user.phone,
|
||||||
|
passwordHash,
|
||||||
|
name: user.name,
|
||||||
|
role: user.role,
|
||||||
|
hospitalId: user.hospitalId,
|
||||||
|
departmentId: user.departmentId,
|
||||||
|
medicalGroupId: user.medicalGroupId,
|
||||||
|
managerId: user.managerId,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
passwordHash,
|
||||||
|
name: user.name,
|
||||||
|
role: user.role,
|
||||||
|
hospitalId: user.hospitalId,
|
||||||
|
departmentId: user.departmentId,
|
||||||
|
medicalGroupId: user.medicalGroupId,
|
||||||
|
managerId: user.managerId,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
role: true,
|
||||||
|
phone: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upsertPatientByNaturalKey(
|
||||||
|
prisma: PrismaClient,
|
||||||
|
patient: PatientInput,
|
||||||
|
) {
|
||||||
|
const existing = await prisma.patient.findFirst({
|
||||||
|
where: {
|
||||||
|
name: patient.name,
|
||||||
|
hospitalId: patient.hospitalId,
|
||||||
|
doctorId: patient.doctorId,
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
return prisma.patient.update({
|
||||||
|
where: { id: existing.id },
|
||||||
|
data: {
|
||||||
|
departmentId: patient.departmentId,
|
||||||
|
medicalGroupId: patient.medicalGroupId,
|
||||||
|
},
|
||||||
|
select: { id: true, name: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.patient.create({
|
||||||
|
data: patient,
|
||||||
|
select: { id: true, name: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const adapter = new PrismaPg({ connectionString: requireDatabaseUrl() });
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const passwordHash = await hashPassword(TEST_PASSWORD);
|
||||||
|
|
||||||
|
const hospital = await prisma.hospital.upsert({
|
||||||
|
where: { code: HOSPITAL_CODE },
|
||||||
|
create: {
|
||||||
|
code: HOSPITAL_CODE,
|
||||||
|
name: HOSPITAL_NAME,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: HOSPITAL_NAME,
|
||||||
|
},
|
||||||
|
select: { id: true, name: true, code: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const department = await prisma.department.upsert({
|
||||||
|
where: {
|
||||||
|
hospitalId_name: {
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
name: DEPARTMENT_NAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: DEPARTMENT_NAME,
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
select: { id: true, name: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const medicalGroup = await prisma.medicalGroup.upsert({
|
||||||
|
where: {
|
||||||
|
departmentId_name: {
|
||||||
|
departmentId: department.id,
|
||||||
|
name: MEDICAL_GROUP_NAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: MEDICAL_GROUP_NAME,
|
||||||
|
departmentId: department.id,
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
select: { id: true, name: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const systemAdmin = await upsertUser(
|
||||||
|
prisma,
|
||||||
|
{
|
||||||
|
phone: '+8613800000001',
|
||||||
|
name: 'System Admin',
|
||||||
|
role: UserRole.SYSTEM_ADMIN,
|
||||||
|
hospitalId: null,
|
||||||
|
departmentId: null,
|
||||||
|
medicalGroupId: null,
|
||||||
|
managerId: null,
|
||||||
|
},
|
||||||
|
passwordHash,
|
||||||
|
);
|
||||||
|
|
||||||
|
const hospitalAdmin = await upsertUser(
|
||||||
|
prisma,
|
||||||
|
{
|
||||||
|
phone: '+8613800000002',
|
||||||
|
name: 'Hospital Admin',
|
||||||
|
role: UserRole.HOSPITAL_ADMIN,
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: null,
|
||||||
|
medicalGroupId: null,
|
||||||
|
managerId: null,
|
||||||
|
},
|
||||||
|
passwordHash,
|
||||||
|
);
|
||||||
|
|
||||||
|
const director = await upsertUser(
|
||||||
|
prisma,
|
||||||
|
{
|
||||||
|
phone: '+8613800000003',
|
||||||
|
name: 'Director',
|
||||||
|
role: UserRole.DIRECTOR,
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: department.id,
|
||||||
|
medicalGroupId: null,
|
||||||
|
managerId: hospitalAdmin.id,
|
||||||
|
},
|
||||||
|
passwordHash,
|
||||||
|
);
|
||||||
|
|
||||||
|
const teamLead = await upsertUser(
|
||||||
|
prisma,
|
||||||
|
{
|
||||||
|
phone: '+8613800000004',
|
||||||
|
name: 'Team Lead',
|
||||||
|
role: UserRole.TEAM_LEAD,
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: department.id,
|
||||||
|
medicalGroupId: medicalGroup.id,
|
||||||
|
managerId: director.id,
|
||||||
|
},
|
||||||
|
passwordHash,
|
||||||
|
);
|
||||||
|
|
||||||
|
const doctor = await upsertUser(
|
||||||
|
prisma,
|
||||||
|
{
|
||||||
|
phone: '+8613800000005',
|
||||||
|
name: 'Doctor',
|
||||||
|
role: UserRole.DOCTOR,
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: department.id,
|
||||||
|
medicalGroupId: medicalGroup.id,
|
||||||
|
managerId: teamLead.id,
|
||||||
|
},
|
||||||
|
passwordHash,
|
||||||
|
);
|
||||||
|
|
||||||
|
const engineer = await upsertUser(
|
||||||
|
prisma,
|
||||||
|
{
|
||||||
|
phone: '+8613800000006',
|
||||||
|
name: 'Engineer',
|
||||||
|
role: UserRole.ENGINEER,
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: null,
|
||||||
|
medicalGroupId: null,
|
||||||
|
managerId: hospitalAdmin.id,
|
||||||
|
},
|
||||||
|
passwordHash,
|
||||||
|
);
|
||||||
|
|
||||||
|
await upsertPatientByNaturalKey(prisma, {
|
||||||
|
name: 'Patient Alpha',
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: department.id,
|
||||||
|
medicalGroupId: medicalGroup.id,
|
||||||
|
doctorId: doctor.id,
|
||||||
|
});
|
||||||
|
await upsertPatientByNaturalKey(prisma, {
|
||||||
|
name: 'Patient Beta',
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
departmentId: department.id,
|
||||||
|
medicalGroupId: medicalGroup.id,
|
||||||
|
doctorId: doctor.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.engineerHospitalAssignment.upsert({
|
||||||
|
where: {
|
||||||
|
hospitalId_engineerId: {
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
engineerId: engineer.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
hospitalId: hospital.id,
|
||||||
|
engineerId: engineer.id,
|
||||||
|
assignedById: systemAdmin.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
assignedById: systemAdmin.id,
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Seed completed for hospital ${hospital.code} (${hospital.name}).`);
|
||||||
|
console.log('Test password for all seeded users:', TEST_PASSWORD);
|
||||||
|
console.table(
|
||||||
|
[systemAdmin, hospitalAdmin, director, teamLead, doctor, engineer].map(
|
||||||
|
(user) => ({
|
||||||
|
role: user.role,
|
||||||
|
phone: user.phone,
|
||||||
|
name: user.name ?? '',
|
||||||
|
password: TEST_PASSWORD,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log('No mini-program or official-account openId was pre-seeded.');
|
||||||
|
} finally {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error('Seed failed:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Matches } from 'class-validator';
|
import { Matches } from 'class-validator';
|
||||||
|
|
||||||
export class ChangePasswordDto {
|
export class ChangePasswordDto {
|
||||||
@ -5,11 +6,19 @@ export class ChangePasswordDto {
|
|||||||
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
||||||
message: '密码至少8位,且包含字母和数字',
|
message: '密码至少8位,且包含字母和数字',
|
||||||
})
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Current password of the account.',
|
||||||
|
example: 'Test123456',
|
||||||
|
})
|
||||||
oldPassword: string;
|
oldPassword: string;
|
||||||
|
|
||||||
// 新密码使用与注册一致的安全策略。
|
// 新密码使用与注册一致的安全策略。
|
||||||
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
||||||
message: '密码至少8位,且包含字母和数字',
|
message: '密码至少8位,且包含字母和数字',
|
||||||
})
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'New password, 8-64 chars with letters and numbers.',
|
||||||
|
example: 'NewTest123456',
|
||||||
|
})
|
||||||
newPassword: string;
|
newPassword: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,22 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Matches } from 'class-validator';
|
import { Matches } from 'class-validator';
|
||||||
|
|
||||||
export class LoginPhoneDto {
|
export class LoginPhoneDto {
|
||||||
// 手机号登录入口字段。
|
// 手机号登录入口字段。
|
||||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Login phone number in E.164 format.',
|
||||||
|
example: '+8613800138000',
|
||||||
|
})
|
||||||
phone: string;
|
phone: string;
|
||||||
|
|
||||||
// 登录密码,规则与注册保持一致。
|
// 登录密码,规则与注册保持一致。
|
||||||
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
||||||
message: '密码至少8位,且包含字母和数字',
|
message: '密码至少8位,且包含字母和数字',
|
||||||
})
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Password must be 8-64 chars and include letters and numbers.',
|
||||||
|
example: 'Test123456',
|
||||||
|
})
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import {
|
import {
|
||||||
IsInt,
|
IsInt,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
@ -10,53 +11,89 @@ import {
|
|||||||
export class RegisterDto {
|
export class RegisterDto {
|
||||||
// 手机号是主登录标识。
|
// 手机号是主登录标识。
|
||||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Phone number used as login account (E.164 format).',
|
||||||
|
example: '+8613800138000',
|
||||||
|
})
|
||||||
phone: string;
|
phone: string;
|
||||||
|
|
||||||
// 注册密码强度策略。
|
// 注册密码强度策略。
|
||||||
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
||||||
message: '密码至少8位,且包含字母和数字',
|
message: '密码至少8位,且包含字母和数字',
|
||||||
})
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Password must be 8-64 chars and include letters and numbers.',
|
||||||
|
example: 'Test123456',
|
||||||
|
})
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
// 个人展示名称。
|
// 个人展示名称。
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@MaxLength(64)
|
@MaxLength(64)
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Display name.',
|
||||||
|
example: 'Demo Doctor',
|
||||||
|
})
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
// 组织归属:医院。
|
// 组织归属:医院。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Hospital id to bind during registration.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
hospitalId?: number;
|
hospitalId?: number;
|
||||||
|
|
||||||
// 组织归属:科室。
|
// 组织归属:科室。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Department id to bind during registration.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
departmentId?: number;
|
departmentId?: number;
|
||||||
|
|
||||||
// 组织归属:小组。
|
// 组织归属:小组。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Medical group id to bind during registration.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
medicalGroupId?: number;
|
medicalGroupId?: number;
|
||||||
|
|
||||||
// 直属上级用户 ID。
|
// 直属上级用户 ID。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Direct manager user id.',
|
||||||
|
example: 2,
|
||||||
|
})
|
||||||
managerId?: number;
|
managerId?: number;
|
||||||
|
|
||||||
// 可选:注册时直接绑定小程序账号。
|
// 可选:注册时直接绑定小程序账号。
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@MaxLength(128)
|
@MaxLength(128)
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Optional mini-program openId.',
|
||||||
|
example: 'mini_open_id_xxx',
|
||||||
|
})
|
||||||
wechatMiniOpenId?: string;
|
wechatMiniOpenId?: string;
|
||||||
|
|
||||||
// 可选:注册时直接绑定服务号账号。
|
// 可选:注册时直接绑定服务号账号。
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@MaxLength(128)
|
@MaxLength(128)
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Optional official-account openId.',
|
||||||
|
example: 'official_open_id_xxx',
|
||||||
|
})
|
||||||
wechatOfficialOpenId?: string;
|
wechatOfficialOpenId?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { UserRole } from '../../generated/prisma/enums.js';
|
import { UserRole } from '../../generated/prisma/enums.js';
|
||||||
import {
|
import {
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@ -13,51 +14,88 @@ import {
|
|||||||
export class CreateUserDto {
|
export class CreateUserDto {
|
||||||
// 手机号作为唯一登录名。
|
// 手机号作为唯一登录名。
|
||||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'User login phone number (E.164 format).',
|
||||||
|
example: '+8613800138000',
|
||||||
|
})
|
||||||
phone: string;
|
phone: string;
|
||||||
|
|
||||||
// 管理端创建用户时直接设置初始密码。
|
// 管理端创建用户时直接设置初始密码。
|
||||||
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
@Matches(/^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, {
|
||||||
message: '密码至少8位,且包含字母和数字',
|
message: '密码至少8位,且包含字母和数字',
|
||||||
})
|
})
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Initial password, 8-64 chars with letters and numbers.',
|
||||||
|
example: 'Test123456',
|
||||||
|
})
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
// 真实姓名用于业务展示。
|
// 真实姓名用于业务展示。
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@MaxLength(64)
|
@MaxLength(64)
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Display name.',
|
||||||
|
example: 'Demo User',
|
||||||
|
})
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
// 未传角色时由服务端默认成 DOCTOR。
|
// 未传角色时由服务端默认成 DOCTOR。
|
||||||
@IsEnum(UserRole)
|
@IsEnum(UserRole)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Role of the user. Defaults to DOCTOR when omitted.',
|
||||||
|
enum: UserRole,
|
||||||
|
example: UserRole.DOCTOR,
|
||||||
|
})
|
||||||
role?: UserRole;
|
role?: UserRole;
|
||||||
|
|
||||||
// 组织归属:医院。
|
// 组织归属:医院。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Hospital id.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
hospitalId?: number;
|
hospitalId?: number;
|
||||||
|
|
||||||
// 组织归属:科室。
|
// 组织归属:科室。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Department id.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
departmentId?: number;
|
departmentId?: number;
|
||||||
|
|
||||||
// 组织归属:小组。
|
// 组织归属:小组。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Medical group id.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
medicalGroupId?: number;
|
medicalGroupId?: number;
|
||||||
|
|
||||||
// 上下级关系:直属上级用户 ID。
|
// 上下级关系:直属上级用户 ID。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Direct manager user id.',
|
||||||
|
example: 2,
|
||||||
|
})
|
||||||
managerId?: number;
|
managerId?: number;
|
||||||
|
|
||||||
// 是否启用账号,默认 true。
|
// 是否启用账号,默认 true。
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Whether the account is active. Defaults to true.',
|
||||||
|
example: true,
|
||||||
|
})
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { UserRole } from '../../generated/prisma/enums.js';
|
import { UserRole } from '../../generated/prisma/enums.js';
|
||||||
import {
|
import {
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@ -14,45 +15,78 @@ export class UpdateUserDto {
|
|||||||
// 修改手机号。
|
// 修改手机号。
|
||||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
@Matches(/^\+?[1-9]\d{7,14}$/, { message: '手机号格式不正确' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'User login phone number (E.164 format).',
|
||||||
|
example: '+8613800138000',
|
||||||
|
})
|
||||||
phone?: string;
|
phone?: string;
|
||||||
|
|
||||||
// 修改姓名。
|
// 修改姓名。
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@MaxLength(64)
|
@MaxLength(64)
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Display name.',
|
||||||
|
example: 'Updated User',
|
||||||
|
})
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
// 修改角色(仅管理员可用)。
|
// 修改角色(仅管理员可用)。
|
||||||
@IsEnum(UserRole)
|
@IsEnum(UserRole)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Role of the user.',
|
||||||
|
enum: UserRole,
|
||||||
|
example: UserRole.DOCTOR,
|
||||||
|
})
|
||||||
role?: UserRole;
|
role?: UserRole;
|
||||||
|
|
||||||
// 修改医院归属(仅管理员可用)。
|
// 修改医院归属(仅管理员可用)。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Hospital id.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
hospitalId?: number;
|
hospitalId?: number;
|
||||||
|
|
||||||
// 修改科室归属(仅管理员可用)。
|
// 修改科室归属(仅管理员可用)。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Department id.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
departmentId?: number;
|
departmentId?: number;
|
||||||
|
|
||||||
// 修改小组归属(仅管理员可用)。
|
// 修改小组归属(仅管理员可用)。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Medical group id.',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
medicalGroupId?: number;
|
medicalGroupId?: number;
|
||||||
|
|
||||||
// 修改直属上级(仅管理员可用)。
|
// 修改直属上级(仅管理员可用)。
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Direct manager user id.',
|
||||||
|
example: 2,
|
||||||
|
})
|
||||||
managerId?: number;
|
managerId?: number;
|
||||||
|
|
||||||
// 启停账号(仅管理员可用)。
|
// 启停账号(仅管理员可用)。
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Whether account is active.',
|
||||||
|
example: true,
|
||||||
|
})
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user