数据库表和后端初步搭建
This commit is contained in:
parent
ee3b1228dd
commit
9c64dc8dd4
|
|
@ -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',
|
||||
PROFILE = 'profile',
|
||||
RESOURCE = 'resource',
|
||||
APPLICATION = 'application',
|
||||
ACCOMMODATION = 'accommodation',
|
||||
}
|
||||
|
||||
export enum UserActionType {
|
||||
|
|
@ -146,4 +148,28 @@ export enum FileVersionChangeType {
|
|||
MANUAL = 'manual',
|
||||
AUTO_SAVE = 'auto_save',
|
||||
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'
|
||||
};
|
||||
|
||||
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 = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
|
|
@ -453,7 +493,9 @@ exports.Prisma.ModelName = {
|
|||
oauthApplication: 'oauthApplication',
|
||||
oauthAccessToken: 'oauthAccessToken',
|
||||
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",
|
||||
"types": "index.d.ts",
|
||||
"browser": "index-browser.js",
|
||||
|
|
|
|||
|
|
@ -304,6 +304,9 @@ model Profile {
|
|||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// 关联关系 - 临时住房申请相关
|
||||
applications Application[]
|
||||
|
||||
@@index([organizationId, deletedAt]) // 组织人员查询优化
|
||||
@@index([hireDate])
|
||||
@@index([level])
|
||||
|
|
@ -563,3 +566,74 @@ model ssoProvider {
|
|||
@@index([issuer])
|
||||
@@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'
|
||||
};
|
||||
|
||||
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 = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
|
|
@ -453,7 +493,9 @@ exports.Prisma.ModelName = {
|
|||
oauthApplication: 'oauthApplication',
|
||||
oauthAccessToken: 'oauthAccessToken',
|
||||
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
|
||||
deletedAt DateTime?
|
||||
|
||||
// 关联关系 - 临时住房申请相关
|
||||
applications Application[]
|
||||
|
||||
@@index([organizationId, deletedAt]) // 组织人员查询优化
|
||||
@@index([hireDate])
|
||||
@@index([level])
|
||||
|
|
@ -563,3 +566,74 @@ model ssoProvider {
|
|||
@@index([issuer])
|
||||
@@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