Merge branch 'main' of http://113.45.67.59:3003/qiuchenfan/casualroom
This commit is contained in:
commit
a5f4247589
|
|
@ -0,0 +1,337 @@
|
||||||
|
import { Prisma } from "@fenghuo/db";
|
||||||
|
import { BaseService } from "../base/base.service";
|
||||||
|
import { prisma } from '@fenghuo/db';
|
||||||
|
import { ObjectType, FamilyRelation, ApplicationStatus } from "@fenghuo/common";
|
||||||
|
import { z } from "zod"; // 添加 z 导入
|
||||||
|
import { CreateApplicationSchema, ApprovalSchema } from './accommodation.trpc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 住房申请服务类
|
||||||
|
*/
|
||||||
|
export class AccommodationService extends BaseService<Prisma.ApplicationDelegate> {
|
||||||
|
constructor() {
|
||||||
|
super(prisma, ObjectType.APPLICATION as any)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建申请(包含家属信息)
|
||||||
|
*/
|
||||||
|
async createApplication(params: z.infer<typeof CreateApplicationSchema>) {
|
||||||
|
try {
|
||||||
|
return await this.prisma.$transaction(async (tx) => {
|
||||||
|
// 1. 根据申请人信息查找匹配的 Profile
|
||||||
|
const matchedProfile = await this.findMatchingProfile(params);
|
||||||
|
|
||||||
|
// 2. 创建申请记录
|
||||||
|
const application = await tx.application.create({
|
||||||
|
data: {
|
||||||
|
// 申请人信息
|
||||||
|
applicantName: params.applicantName,
|
||||||
|
applicantDuty: params.applicantDuty,
|
||||||
|
applicantBirthday: params.applicantBirthday,
|
||||||
|
applicantHireDate: params.applicantHireDate,
|
||||||
|
applicantMarriageDate: params.applicantMarriageDate,
|
||||||
|
applicantPhone: params.applicantPhone,
|
||||||
|
|
||||||
|
// 关联到 Profile(如果找到匹配的)
|
||||||
|
profileId: matchedProfile?.id || null,
|
||||||
|
|
||||||
|
// 申请信息
|
||||||
|
applyDays: params.applyDays,
|
||||||
|
plannedCheckIn: params.plannedCheckIn,
|
||||||
|
plannedCheckOut: params.plannedCheckOut,
|
||||||
|
reason: params.reason,
|
||||||
|
status: ApplicationStatus.PENDING,
|
||||||
|
|
||||||
|
// 嵌套创建家属信息
|
||||||
|
familyMembers: {
|
||||||
|
create: params.familyMembers.map(member => ({
|
||||||
|
name: member.name,
|
||||||
|
birthDate: member.birthDate,
|
||||||
|
relation: member.relation,
|
||||||
|
nativePlace: member.nativePlace,
|
||||||
|
employer: member.employer,
|
||||||
|
address: member.address
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
familyMembers: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
organization: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return application;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`创建申请失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批申请
|
||||||
|
*/
|
||||||
|
async approveApplication(params: z.infer<typeof ApprovalSchema>) {
|
||||||
|
try {
|
||||||
|
const { applicationId, role, opinion, status } = params;
|
||||||
|
|
||||||
|
// 构建更新数据
|
||||||
|
const updateData: Prisma.ApplicationUpdateInput = {};
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case 'group':
|
||||||
|
updateData.groupOpinion = opinion;
|
||||||
|
break;
|
||||||
|
case 'hr':
|
||||||
|
updateData.hrOpinion = opinion;
|
||||||
|
break;
|
||||||
|
case 'barracks':
|
||||||
|
updateData.barracksOpinion = opinion;
|
||||||
|
break;
|
||||||
|
case 'guarantee':
|
||||||
|
updateData.guaranteeOpinion = opinion;
|
||||||
|
break;
|
||||||
|
case 'leader':
|
||||||
|
updateData.leaderOpinion = opinion;
|
||||||
|
if (status) {
|
||||||
|
updateData.status = status;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('无效的审批角色');
|
||||||
|
}
|
||||||
|
|
||||||
|
const application = await this.prisma.application.update({
|
||||||
|
where: { id: applicationId },
|
||||||
|
data: updateData,
|
||||||
|
include: {
|
||||||
|
familyMembers: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
organization: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return application;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`审批申请失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取申请统计信息
|
||||||
|
*/
|
||||||
|
async getApplicationStats(profileId: string, year: number) {
|
||||||
|
try {
|
||||||
|
const startDate = new Date(year, 0, 1);
|
||||||
|
const endDate = new Date(year + 1, 0, 1);
|
||||||
|
|
||||||
|
const applications = await this.prisma.application.findMany({
|
||||||
|
where: {
|
||||||
|
profileId,
|
||||||
|
status: ApplicationStatus.APPROVED,
|
||||||
|
createdAt: {
|
||||||
|
gte: startDate,
|
||||||
|
lt: endDate
|
||||||
|
},
|
||||||
|
deletedAt: null
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
applyDays: true,
|
||||||
|
plannedCheckIn: true,
|
||||||
|
plannedCheckOut: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalDays = applications.reduce((sum, app) => sum + app.applyDays, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalApplications: applications.length,
|
||||||
|
totalDays,
|
||||||
|
applications: applications.map(app => ({
|
||||||
|
days: app.applyDays,
|
||||||
|
checkIn: app.plannedCheckIn,
|
||||||
|
checkOut: app.plannedCheckOut
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`获取统计信息失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取申请详情
|
||||||
|
*/
|
||||||
|
async getApplicationById(id: string) {
|
||||||
|
try {
|
||||||
|
const application = await this.prisma.application.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
familyMembers: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
organization: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!application) {
|
||||||
|
throw new Error('申请不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
return application;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`获取申请详情失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取申请列表(带分页)
|
||||||
|
*/
|
||||||
|
async getApplicationsWithPagination(params: {
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
where?: Prisma.ApplicationWhereInput;
|
||||||
|
orderBy?: Prisma.ApplicationOrderByWithRelationInput;
|
||||||
|
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const result = await this.findManyWithPagination(params);
|
||||||
|
|
||||||
|
const itemsWithRelations = await Promise.all(
|
||||||
|
result.items.map(async (item) => {
|
||||||
|
return await this.prisma.application.findUnique({
|
||||||
|
where: { id: item.id },
|
||||||
|
include: {
|
||||||
|
familyMembers: true,
|
||||||
|
profile: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
organization: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
items: itemsWithRelations
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`获取申请列表失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除申请(软删除)
|
||||||
|
*/
|
||||||
|
async deleteApplication(id: string) {
|
||||||
|
try {
|
||||||
|
return await this.softDeleteByIds([id]);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`删除申请失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除申请
|
||||||
|
*/
|
||||||
|
async deleteApplications(ids: string[]) {
|
||||||
|
try {
|
||||||
|
return await this.softDeleteByIds(ids);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`批量删除申请失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复已删除的申请
|
||||||
|
*/
|
||||||
|
async restoreApplication(id: string) {
|
||||||
|
try {
|
||||||
|
return await this.restoreByIds([id]);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`恢复申请失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新申请信息
|
||||||
|
*/
|
||||||
|
async updateApplication(id: string, data: Prisma.ApplicationUpdateInput) {
|
||||||
|
try {
|
||||||
|
return await this.updateById(id, data);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`更新申请失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据申请人信息查找匹配的 Profile
|
||||||
|
* 匹配逻辑:姓名+职别 -> 如果多个结果 -> 用出生年月进一步筛选
|
||||||
|
*/
|
||||||
|
private async findMatchingProfile(params: z.infer<typeof CreateApplicationSchema>) {
|
||||||
|
if (!params.applicantName || !params.applicantDuty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 第一步:用姓名和职别进行匹配
|
||||||
|
const whereConditions: Prisma.ProfileWhereInput = {
|
||||||
|
name: params.applicantName,
|
||||||
|
dutyName: params.applicantDuty, // 使用职别名称匹配
|
||||||
|
deletedAt: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchingProfiles = await this.prisma.profile.findMany({
|
||||||
|
where: whereConditions,
|
||||||
|
include: {
|
||||||
|
organization: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果只有一个匹配结果,直接返回
|
||||||
|
if (matchingProfiles.length === 1) {
|
||||||
|
return matchingProfiles[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有多个匹配结果,用出生年月进一步筛选
|
||||||
|
if (matchingProfiles.length > 1 && params.applicantBirthday) {
|
||||||
|
const exactMatch = matchingProfiles.find(profile =>
|
||||||
|
profile.birthday &&
|
||||||
|
profile.birthday.getTime() === params.applicantBirthday!.getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
return exactMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果仍然有多个结果或没有匹配,返回第一个(或者返回 null)
|
||||||
|
return matchingProfiles.length > 0 ? matchingProfiles[0] : null;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// 匹配失败不影响申请创建,返回 null
|
||||||
|
console.warn('Profile 匹配失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出服务实例
|
||||||
|
export const accommodationService = new AccommodationService();
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
import { accommodationService } from './accommodation.service';
|
||||||
|
import { protectedProcedure, publicProcedure, router } from "../../trpc/base";
|
||||||
|
import { z, ZodType } from "zod";
|
||||||
|
import { Prisma } from "@fenghuo/db";
|
||||||
|
import { FamilyRelation, ApplicationStatus } from "@fenghuo/common";
|
||||||
|
|
||||||
|
// Prisma 类型的 Zod Schema
|
||||||
|
const ApplicationCreateArgsSchema: ZodType<Prisma.ApplicationCreateArgs> = z.any();
|
||||||
|
const ApplicationUpdateArgsSchema: ZodType<Prisma.ApplicationUpdateArgs> = z.any();
|
||||||
|
const ApplicationUpdateInputSchema: ZodType<Prisma.ApplicationUpdateInput> = z.any();
|
||||||
|
const ApplicationWhereInputSchema: ZodType<Prisma.ApplicationWhereInput> = z.any();
|
||||||
|
const ApplicationSelectSchema: ZodType<Prisma.ApplicationSelect> = z.any();
|
||||||
|
const ApplicationOrderByWithRelationInputSchema: ZodType<Prisma.ApplicationOrderByWithRelationInput> = z.any();
|
||||||
|
const ApplicationIncludeSchema: ZodType<Prisma.ApplicationInclude> = z.any();
|
||||||
|
|
||||||
|
// 自定义输入验证 Schema
|
||||||
|
export const CreateApplicationSchema = z.object({
|
||||||
|
// 申请人信息
|
||||||
|
applicantName: z.string().min(1, '申请人姓名不能为空'),
|
||||||
|
applicantDuty: z.string().min(1, '申请人职别不能为空'),
|
||||||
|
applicantBirthday: z.date().optional(),
|
||||||
|
applicantHireDate: z.date().optional(),
|
||||||
|
applicantMarriageDate: z.date().optional(),
|
||||||
|
applicantPhone: z.string().optional(),
|
||||||
|
applicantIdNum: z.string().optional(),
|
||||||
|
|
||||||
|
// 家属信息
|
||||||
|
familyMembers: z.array(z.object({
|
||||||
|
name: z.string().min(1, '家属姓名不能为空'),
|
||||||
|
birthDate: z.date().optional(),
|
||||||
|
relation: z.enum([
|
||||||
|
FamilyRelation.SPOUSE,
|
||||||
|
FamilyRelation.CHILD,
|
||||||
|
FamilyRelation.PARENT,
|
||||||
|
FamilyRelation.OTHER
|
||||||
|
]),
|
||||||
|
nativePlace: z.string().optional(),
|
||||||
|
employer: z.string().optional(),
|
||||||
|
address: z.string().optional()
|
||||||
|
})).min(1, '至少需要一个家属信息'),
|
||||||
|
|
||||||
|
// 申请信息
|
||||||
|
applyDays: z.number().min(1, '申请天数必须大于0'),
|
||||||
|
plannedCheckIn: z.date(),
|
||||||
|
plannedCheckOut: z.date(),
|
||||||
|
reason: z.string().optional()
|
||||||
|
}).refine(data => data.plannedCheckOut > data.plannedCheckIn, {
|
||||||
|
message: '退房时间必须晚于入住时间',
|
||||||
|
path: ['plannedCheckOut']
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ApprovalSchema = z.object({
|
||||||
|
applicationId: z.string().min(1, '申请ID不能为空'),
|
||||||
|
role: z.enum(['group', 'hr', 'barracks', 'guarantee', 'leader']),
|
||||||
|
opinion: z.string().min(1, '审批意见不能为空'),
|
||||||
|
status: z.enum([
|
||||||
|
ApplicationStatus.PENDING,
|
||||||
|
ApplicationStatus.APPROVED,
|
||||||
|
ApplicationStatus.REJECTED
|
||||||
|
]).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
const StatsQuerySchema = z.object({
|
||||||
|
profileId: z.string().min(1, 'Profile ID不能为空'),
|
||||||
|
year: z.number().min(2000).max(2100)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const accommodationRouter = router({
|
||||||
|
// 创建申请
|
||||||
|
createApplication: protectedProcedure
|
||||||
|
.input(CreateApplicationSchema)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.createApplication(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 获取申请详情
|
||||||
|
getApplicationById: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
id: z.string().min(1, '申请ID不能为空')
|
||||||
|
}))
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return accommodationService.getApplicationById(input.id);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 获取申请列表(带分页)
|
||||||
|
getApplications: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
page: z.number().optional(),
|
||||||
|
pageSize: z.number().optional(),
|
||||||
|
where: ApplicationWhereInputSchema.optional(),
|
||||||
|
orderBy: ApplicationOrderByWithRelationInputSchema.optional(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return accommodationService.getApplicationsWithPagination(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 审批申请
|
||||||
|
approveApplication: protectedProcedure
|
||||||
|
.input(ApprovalSchema)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.approveApplication(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 获取申请统计
|
||||||
|
getApplicationStats: protectedProcedure
|
||||||
|
.input(StatsQuerySchema)
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return accommodationService.getApplicationStats(input.profileId, input.year);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 更新申请
|
||||||
|
updateApplication: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
id: z.string().min(1, '申请ID不能为空'),
|
||||||
|
data: ApplicationUpdateInputSchema
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.updateApplication(input.id, input.data);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 删除申请(软删除)
|
||||||
|
deleteApplication: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
id: z.string().min(1, '申请ID不能为空')
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.deleteApplication(input.id);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 批量删除申请
|
||||||
|
deleteApplications: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
ids: z.array(z.string()).min(1, '至少选择一个申请')
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.deleteApplications(input.ids);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 恢复申请
|
||||||
|
restoreApplication: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
id: z.string().min(1, '申请ID不能为空')
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.restoreApplication(input.id);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 基础的 CRUD 操作(使用 Prisma 原生参数)
|
||||||
|
create: protectedProcedure
|
||||||
|
.input(ApplicationCreateArgsSchema)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.create(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
update: protectedProcedure
|
||||||
|
.input(ApplicationUpdateArgsSchema)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.update(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
findFirst: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
where: ApplicationWhereInputSchema.optional(),
|
||||||
|
select: ApplicationSelectSchema.optional(),
|
||||||
|
include: ApplicationIncludeSchema.optional(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return accommodationService.findFirst(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
findManyWithPagination: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
page: z.number().optional(),
|
||||||
|
pageSize: z.number().optional(),
|
||||||
|
where: ApplicationWhereInputSchema.optional(),
|
||||||
|
select: ApplicationSelectSchema.optional(),
|
||||||
|
orderBy: ApplicationOrderByWithRelationInputSchema.optional(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return accommodationService.findManyWithPagination(input);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 软删除相关操作
|
||||||
|
softDeleteByIds: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
ids: z.array(z.string()),
|
||||||
|
data: ApplicationUpdateInputSchema.nullish(),
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.softDeleteByIds(input.ids, input.data);
|
||||||
|
}),
|
||||||
|
|
||||||
|
restoreByIds: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
ids: z.array(z.string()),
|
||||||
|
data: ApplicationUpdateInputSchema.nullish(),
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return accommodationService.restoreByIds(input.ids, input.data);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
@ -13,6 +13,8 @@ export enum ObjectType {
|
||||||
FILE_VERSION = 'file_version',
|
FILE_VERSION = 'file_version',
|
||||||
PROFILE = 'profile',
|
PROFILE = 'profile',
|
||||||
RESOURCE = 'resource',
|
RESOURCE = 'resource',
|
||||||
|
APPLICATION = 'application',
|
||||||
|
ACCOMMODATION = 'accommodation',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UserActionType {
|
export enum UserActionType {
|
||||||
|
|
@ -147,3 +149,27 @@ export enum FileVersionChangeType {
|
||||||
AUTO_SAVE = 'auto_save',
|
AUTO_SAVE = 'auto_save',
|
||||||
COLLABORATION = 'collaboration'
|
COLLABORATION = 'collaboration'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 家属关系枚举
|
||||||
|
export enum FamilyRelation {
|
||||||
|
SPOUSE = '配偶', // 配偶
|
||||||
|
CHILD = '子女', // 子女
|
||||||
|
PARENT = '父母', // 父母
|
||||||
|
OTHER = '其他' // 其他关系
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请状态枚举
|
||||||
|
export enum ApplicationStatus {
|
||||||
|
PENDING = 'pending', // 待审批
|
||||||
|
APPROVED = 'approved', // 已同意
|
||||||
|
REJECTED = 'rejected' // 已拒绝
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审批角色枚举
|
||||||
|
export enum ApprovalRole {
|
||||||
|
GROUP = 'group', // 小组
|
||||||
|
HR = 'hr', // 人力科
|
||||||
|
BARRACKS = 'barracks', // 营房
|
||||||
|
GUARANTEE = 'guarantee', // 保障部
|
||||||
|
LEADER = 'leader' // 领导
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -405,6 +405,46 @@ exports.Prisma.SsoProviderScalarFieldEnum = {
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.Prisma.FamilyMemberScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
birthDate: 'birthDate',
|
||||||
|
relation: 'relation',
|
||||||
|
nativePlace: 'nativePlace',
|
||||||
|
employer: 'employer',
|
||||||
|
address: 'address',
|
||||||
|
applicationId: 'applicationId',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
deletedAt: 'deletedAt'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.ApplicationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
applicantName: 'applicantName',
|
||||||
|
applicantDuty: 'applicantDuty',
|
||||||
|
applicantBirthday: 'applicantBirthday',
|
||||||
|
applicantHireDate: 'applicantHireDate',
|
||||||
|
applicantMarriageDate: 'applicantMarriageDate',
|
||||||
|
applicantPhone: 'applicantPhone',
|
||||||
|
profileId: 'profileId',
|
||||||
|
applyDays: 'applyDays',
|
||||||
|
plannedCheckIn: 'plannedCheckIn',
|
||||||
|
plannedCheckOut: 'plannedCheckOut',
|
||||||
|
reason: 'reason',
|
||||||
|
usedDaysThisYear: 'usedDaysThisYear',
|
||||||
|
remainingDays: 'remainingDays',
|
||||||
|
groupOpinion: 'groupOpinion',
|
||||||
|
hrOpinion: 'hrOpinion',
|
||||||
|
barracksOpinion: 'barracksOpinion',
|
||||||
|
guaranteeOpinion: 'guaranteeOpinion',
|
||||||
|
leaderOpinion: 'leaderOpinion',
|
||||||
|
status: 'status',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
deletedAt: 'deletedAt'
|
||||||
|
};
|
||||||
|
|
||||||
exports.Prisma.SortOrder = {
|
exports.Prisma.SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
|
|
@ -453,7 +493,9 @@ exports.Prisma.ModelName = {
|
||||||
oauthApplication: 'oauthApplication',
|
oauthApplication: 'oauthApplication',
|
||||||
oauthAccessToken: 'oauthAccessToken',
|
oauthAccessToken: 'oauthAccessToken',
|
||||||
oauthConsent: 'oauthConsent',
|
oauthConsent: 'oauthConsent',
|
||||||
ssoProvider: 'ssoProvider'
|
ssoProvider: 'ssoProvider',
|
||||||
|
FamilyMember: 'FamilyMember',
|
||||||
|
Application: 'Application'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "prisma-client-e6300ea92a36b9e59626dd91d6c26072231c307b248909be09ae2c1e4aaf4b03",
|
"name": "prisma-client-d4221045c8bd0b0530fb2223c54e752adff39e629694496fc8f8784c88fb8239",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"browser": "index-browser.js",
|
"browser": "index-browser.js",
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,9 @@ model Profile {
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime?
|
deletedAt DateTime?
|
||||||
|
|
||||||
|
// 关联关系 - 临时住房申请相关
|
||||||
|
applications Application[]
|
||||||
|
|
||||||
@@index([organizationId, deletedAt]) // 组织人员查询优化
|
@@index([organizationId, deletedAt]) // 组织人员查询优化
|
||||||
@@index([hireDate])
|
@@index([hireDate])
|
||||||
@@index([level])
|
@@index([level])
|
||||||
|
|
@ -563,3 +566,74 @@ model ssoProvider {
|
||||||
@@index([issuer])
|
@@index([issuer])
|
||||||
@@map("sso_providers")
|
@@map("sso_providers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===临时住房申请系统===
|
||||||
|
|
||||||
|
// 家属信息表
|
||||||
|
model FamilyMember {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String // 家属姓名
|
||||||
|
birthDate DateTime? @map("birth_date") // 出生年月
|
||||||
|
relation String // 与申请人关系(配偶、子女、父母、其他)
|
||||||
|
nativePlace String? @map("native_place") // 籍贯
|
||||||
|
employer String? // 工作单位
|
||||||
|
address String? // 实际居住地
|
||||||
|
|
||||||
|
// 关联关系 - 直接关联申请表
|
||||||
|
applicationId String @map("application_id") // 关联的申请ID
|
||||||
|
application Application @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
|
|
||||||
|
@@index([applicationId, deletedAt]) // 申请家属查询优化
|
||||||
|
@@index([relation]) // 关系类型查询优化
|
||||||
|
@@map("family_members")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 临时住房申请表
|
||||||
|
model Application {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
|
||||||
|
// 申请人信息(冗余存储,用于审计)
|
||||||
|
applicantName String @map("applicant_name") // 申请人姓名
|
||||||
|
applicantDuty String @map("applicant_duty") // 申请人职别
|
||||||
|
applicantBirthday DateTime? @map("applicant_birthday") // 申请人出生年月
|
||||||
|
applicantHireDate DateTime? @map("applicant_hire_date") // 申请人入职时间
|
||||||
|
applicantMarriageDate DateTime? @map("applicant_marriage_date") // 申请人结婚时间
|
||||||
|
applicantPhone String? @map("applicant_phone") // 申请人联系方式
|
||||||
|
|
||||||
|
// 关联到 Profile(用于数据管理和统计)
|
||||||
|
profileId String? @map("profile_id") // 关联的员工ID(可选)
|
||||||
|
profile Profile? @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
// 申请信息
|
||||||
|
applyDays Int @map("apply_days") // 申请住用天数
|
||||||
|
plannedCheckIn DateTime @map("planned_check_in") // 计划入住时间
|
||||||
|
plannedCheckOut DateTime @map("planned_check_out") // 计划退房时间
|
||||||
|
reason String? // 申请理由
|
||||||
|
usedDaysThisYear Int @default(0) @map("used_days_this_year") // 本年度已住用天数
|
||||||
|
remainingDays Int @default(0) @map("remaining_days") // 剩余住用天数
|
||||||
|
|
||||||
|
// 审批意见
|
||||||
|
groupOpinion String? @map("group_opinion") // yinlian意见
|
||||||
|
hrOpinion String? @map("hr_opinion") // 人力科意见
|
||||||
|
barracksOpinion String? @map("barracks_opinion") // 营房意见
|
||||||
|
guaranteeOpinion String? @map("guarantee_opinion") // 保障部意见
|
||||||
|
leaderOpinion String? @map("leader_opinion") // 领导意见
|
||||||
|
status String @default("pending") // 申请状态:待审批、已同意、已拒绝
|
||||||
|
|
||||||
|
// 关联关系
|
||||||
|
familyMembers FamilyMember[] // 该申请的所有家属
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
|
|
||||||
|
@@index([profileId, deletedAt]) // 员工申请查询优化
|
||||||
|
@@index([status, deletedAt]) // 状态查询优化
|
||||||
|
@@index([plannedCheckIn, plannedCheckOut]) // 时间范围查询优化
|
||||||
|
@@index([applicantName]) // 申请人姓名查询优化
|
||||||
|
@@map("applications")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,46 @@ exports.Prisma.SsoProviderScalarFieldEnum = {
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.Prisma.FamilyMemberScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
birthDate: 'birthDate',
|
||||||
|
relation: 'relation',
|
||||||
|
nativePlace: 'nativePlace',
|
||||||
|
employer: 'employer',
|
||||||
|
address: 'address',
|
||||||
|
applicationId: 'applicationId',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
deletedAt: 'deletedAt'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.ApplicationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
applicantName: 'applicantName',
|
||||||
|
applicantDuty: 'applicantDuty',
|
||||||
|
applicantBirthday: 'applicantBirthday',
|
||||||
|
applicantHireDate: 'applicantHireDate',
|
||||||
|
applicantMarriageDate: 'applicantMarriageDate',
|
||||||
|
applicantPhone: 'applicantPhone',
|
||||||
|
profileId: 'profileId',
|
||||||
|
applyDays: 'applyDays',
|
||||||
|
plannedCheckIn: 'plannedCheckIn',
|
||||||
|
plannedCheckOut: 'plannedCheckOut',
|
||||||
|
reason: 'reason',
|
||||||
|
usedDaysThisYear: 'usedDaysThisYear',
|
||||||
|
remainingDays: 'remainingDays',
|
||||||
|
groupOpinion: 'groupOpinion',
|
||||||
|
hrOpinion: 'hrOpinion',
|
||||||
|
barracksOpinion: 'barracksOpinion',
|
||||||
|
guaranteeOpinion: 'guaranteeOpinion',
|
||||||
|
leaderOpinion: 'leaderOpinion',
|
||||||
|
status: 'status',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
deletedAt: 'deletedAt'
|
||||||
|
};
|
||||||
|
|
||||||
exports.Prisma.SortOrder = {
|
exports.Prisma.SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
|
|
@ -453,7 +493,9 @@ exports.Prisma.ModelName = {
|
||||||
oauthApplication: 'oauthApplication',
|
oauthApplication: 'oauthApplication',
|
||||||
oauthAccessToken: 'oauthAccessToken',
|
oauthAccessToken: 'oauthAccessToken',
|
||||||
oauthConsent: 'oauthConsent',
|
oauthConsent: 'oauthConsent',
|
||||||
ssoProvider: 'ssoProvider'
|
ssoProvider: 'ssoProvider',
|
||||||
|
FamilyMember: 'FamilyMember',
|
||||||
|
Application: 'Application'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "profiles" ADD COLUMN "marriageDate" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "phone" TEXT;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "family_members" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"profile_id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"birthDate" TIMESTAMP(3),
|
||||||
|
"relation" TEXT NOT NULL,
|
||||||
|
"nativePlace" TEXT,
|
||||||
|
"employer" TEXT,
|
||||||
|
"address" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3),
|
||||||
|
|
||||||
|
CONSTRAINT "family_members_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "applications" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"profile_id" TEXT NOT NULL,
|
||||||
|
"family_member_id" TEXT NOT NULL,
|
||||||
|
"applyDays" INTEGER NOT NULL,
|
||||||
|
"plannedCheckIn" TIMESTAMP(3) NOT NULL,
|
||||||
|
"plannedCheckOut" TIMESTAMP(3) NOT NULL,
|
||||||
|
"reason" TEXT,
|
||||||
|
"usedDaysThisYear" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"remainingDays" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"groupOpinion" TEXT,
|
||||||
|
"hrOpinion" TEXT,
|
||||||
|
"barracksOpinion" TEXT,
|
||||||
|
"guaranteeOpinion" TEXT,
|
||||||
|
"leaderOpinion" TEXT,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3),
|
||||||
|
|
||||||
|
CONSTRAINT "applications_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "family_members_profile_id_deletedAt_idx" ON "family_members"("profile_id", "deletedAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "family_members_relation_idx" ON "family_members"("relation");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "applications_profile_id_deletedAt_idx" ON "applications"("profile_id", "deletedAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "applications_family_member_id_deletedAt_idx" ON "applications"("family_member_id", "deletedAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "applications_status_deletedAt_idx" ON "applications"("status", "deletedAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "applications_plannedCheckIn_plannedCheckOut_idx" ON "applications"("plannedCheckIn", "plannedCheckOut");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "profiles_phone_idx" ON "profiles"("phone");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "family_members" ADD CONSTRAINT "family_members_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "profiles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "applications" ADD CONSTRAINT "applications_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "profiles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "applications" ADD CONSTRAINT "applications_family_member_id_fkey" FOREIGN KEY ("family_member_id") REFERENCES "family_members"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `applyDays` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `barracksOpinion` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `groupOpinion` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `guaranteeOpinion` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `hrOpinion` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `plannedCheckIn` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `plannedCheckOut` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `birthDate` on the `family_members` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `nativePlace` on the `family_members` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `marriageDate` on the `profiles` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `apply_days` to the `applications` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `planned_check_in` to the `applications` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `planned_check_out` to the `applications` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "applications_plannedCheckIn_plannedCheckOut_idx";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "applications" DROP COLUMN "applyDays",
|
||||||
|
DROP COLUMN "barracksOpinion",
|
||||||
|
DROP COLUMN "groupOpinion",
|
||||||
|
DROP COLUMN "guaranteeOpinion",
|
||||||
|
DROP COLUMN "hrOpinion",
|
||||||
|
DROP COLUMN "plannedCheckIn",
|
||||||
|
DROP COLUMN "plannedCheckOut",
|
||||||
|
ADD COLUMN "apply_days" INTEGER NOT NULL,
|
||||||
|
ADD COLUMN "barracks_opinion" TEXT,
|
||||||
|
ADD COLUMN "group_opinion" TEXT,
|
||||||
|
ADD COLUMN "guarantee_opinion" TEXT,
|
||||||
|
ADD COLUMN "hr_opinion" TEXT,
|
||||||
|
ADD COLUMN "planned_check_in" TIMESTAMP(3) NOT NULL,
|
||||||
|
ADD COLUMN "planned_check_out" TIMESTAMP(3) NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "family_members" DROP COLUMN "birthDate",
|
||||||
|
DROP COLUMN "nativePlace",
|
||||||
|
ADD COLUMN "birth_date" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "native_place" TEXT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "profiles" DROP COLUMN "marriageDate",
|
||||||
|
ADD COLUMN "marriage_date" TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "applications_planned_check_in_planned_check_out_idx" ON "applications"("planned_check_in", "planned_check_out");
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `family_member_id` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `leaderOpinion` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `remainingDays` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `usedDaysThisYear` on the `applications` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `profile_id` on the `family_members` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `marriage_date` on the `profiles` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `phone` on the `profiles` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `applicant_duty` to the `applications` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `applicant_name` to the `applications` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `application_id` to the `family_members` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "applications" DROP CONSTRAINT "applications_family_member_id_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "family_members" DROP CONSTRAINT "family_members_profile_id_fkey";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "applications_family_member_id_deletedAt_idx";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "family_members_profile_id_deletedAt_idx";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "profiles_phone_idx";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "applications" DROP COLUMN "family_member_id",
|
||||||
|
DROP COLUMN "leaderOpinion",
|
||||||
|
DROP COLUMN "remainingDays",
|
||||||
|
DROP COLUMN "usedDaysThisYear",
|
||||||
|
ADD COLUMN "applicant_birthday" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "applicant_duty" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "applicant_hire_date" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "applicant_marriage_date" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "applicant_name" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "applicant_phone" TEXT,
|
||||||
|
ADD COLUMN "leader_opinion" TEXT,
|
||||||
|
ADD COLUMN "remaining_days" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN "used_days_this_year" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ALTER COLUMN "profile_id" DROP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "family_members" DROP COLUMN "profile_id",
|
||||||
|
ADD COLUMN "application_id" TEXT NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "profiles" DROP COLUMN "marriage_date",
|
||||||
|
DROP COLUMN "phone";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "applications_applicant_name_idx" ON "applications"("applicant_name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "family_members_application_id_deletedAt_idx" ON "family_members"("application_id", "deletedAt");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "family_members" ADD CONSTRAINT "family_members_application_id_fkey" FOREIGN KEY ("application_id") REFERENCES "applications"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
@ -304,6 +304,9 @@ model Profile {
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime?
|
deletedAt DateTime?
|
||||||
|
|
||||||
|
// 关联关系 - 临时住房申请相关
|
||||||
|
applications Application[]
|
||||||
|
|
||||||
@@index([organizationId, deletedAt]) // 组织人员查询优化
|
@@index([organizationId, deletedAt]) // 组织人员查询优化
|
||||||
@@index([hireDate])
|
@@index([hireDate])
|
||||||
@@index([level])
|
@@index([level])
|
||||||
|
|
@ -563,3 +566,74 @@ model ssoProvider {
|
||||||
@@index([issuer])
|
@@index([issuer])
|
||||||
@@map("sso_providers")
|
@@map("sso_providers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===临时住房申请系统===
|
||||||
|
|
||||||
|
// 家属信息表
|
||||||
|
model FamilyMember {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String // 家属姓名
|
||||||
|
birthDate DateTime? @map("birth_date") // 出生年月
|
||||||
|
relation String // 与申请人关系(配偶、子女、父母、其他)
|
||||||
|
nativePlace String? @map("native_place") // 籍贯
|
||||||
|
employer String? // 工作单位
|
||||||
|
address String? // 实际居住地
|
||||||
|
|
||||||
|
// 关联关系 - 直接关联申请表
|
||||||
|
applicationId String @map("application_id") // 关联的申请ID
|
||||||
|
application Application @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
|
|
||||||
|
@@index([applicationId, deletedAt]) // 申请家属查询优化
|
||||||
|
@@index([relation]) // 关系类型查询优化
|
||||||
|
@@map("family_members")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 临时住房申请表
|
||||||
|
model Application {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
|
||||||
|
// 申请人信息(冗余存储,用于审计)
|
||||||
|
applicantName String @map("applicant_name") // 申请人姓名
|
||||||
|
applicantDuty String @map("applicant_duty") // 申请人职别
|
||||||
|
applicantBirthday DateTime? @map("applicant_birthday") // 申请人出生年月
|
||||||
|
applicantHireDate DateTime? @map("applicant_hire_date") // 申请人入职时间
|
||||||
|
applicantMarriageDate DateTime? @map("applicant_marriage_date") // 申请人结婚时间
|
||||||
|
applicantPhone String? @map("applicant_phone") // 申请人联系方式
|
||||||
|
|
||||||
|
// 关联到 Profile(用于数据管理和统计)
|
||||||
|
profileId String? @map("profile_id") // 关联的员工ID(可选)
|
||||||
|
profile Profile? @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
// 申请信息
|
||||||
|
applyDays Int @map("apply_days") // 申请住用天数
|
||||||
|
plannedCheckIn DateTime @map("planned_check_in") // 计划入住时间
|
||||||
|
plannedCheckOut DateTime @map("planned_check_out") // 计划退房时间
|
||||||
|
reason String? // 申请理由
|
||||||
|
usedDaysThisYear Int @default(0) @map("used_days_this_year") // 本年度已住用天数
|
||||||
|
remainingDays Int @default(0) @map("remaining_days") // 剩余住用天数
|
||||||
|
|
||||||
|
// 审批意见
|
||||||
|
groupOpinion String? @map("group_opinion") // yinlian意见
|
||||||
|
hrOpinion String? @map("hr_opinion") // 人力科意见
|
||||||
|
barracksOpinion String? @map("barracks_opinion") // 营房意见
|
||||||
|
guaranteeOpinion String? @map("guarantee_opinion") // 保障部意见
|
||||||
|
leaderOpinion String? @map("leader_opinion") // 领导意见
|
||||||
|
status String @default("pending") // 申请状态:待审批、已同意、已拒绝
|
||||||
|
|
||||||
|
// 关联关系
|
||||||
|
familyMembers FamilyMember[] // 该申请的所有家属
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
|
|
||||||
|
@@index([profileId, deletedAt]) // 员工申请查询优化
|
||||||
|
@@index([status, deletedAt]) // 状态查询优化
|
||||||
|
@@index([plannedCheckIn, plannedCheckOut]) // 时间范围查询优化
|
||||||
|
@@index([applicantName]) // 申请人姓名查询优化
|
||||||
|
@@map("applications")
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue