Files
freedakgmail 5c8c70097c 0.8.0.0
2025-12-21 17:32:33 +08:00

168 lines
4.7 KiB
TypeScript

import { prisma } from "./prisma"
/**
* 重新计算家族树所有成员的代数
* @param treeId 家族树ID
* @param offset 代数偏移量(正数表示增加,负数表示减少)
* @param excludeMemberId 排除的成员ID(通常是新添加的成员)
*/
export async function recalculateGenerations(
treeId: string,
offset: number,
excludeMemberId?: string
): Promise<void> {
if (offset === 0) return
await prisma.$transaction(async (tx) => {
const members = await tx.familyMember.findMany({
where: {
treeId,
...(excludeMemberId ? { id: { not: excludeMemberId } } : {}),
},
select: {
id: true,
generation: true,
},
})
for (const member of members) {
const newGeneration = member.generation + offset
if (newGeneration < 1) {
throw new Error(`代数调整后出现无效值: ${newGeneration},成员ID: ${member.id}`)
}
await tx.familyMember.update({
where: { id: member.id },
data: { generation: newGeneration },
})
}
})
}
/**
* 查找家族树的新根成员(第1代且无父母的成员)
* 优先选择年龄最大的(出生日期最早的)直系成员
* @param treeId 家族树ID
* @returns 新根成员的ID,如果没有则返回null
*/
export async function findNewRootMember(treeId: string): Promise<string | null> {
const firstGenMembers = await prisma.familyMember.findMany({
where: {
treeId,
generation: 1,
fatherId: null,
motherId: null,
},
select: {
id: true,
birthDate: true,
createdAt: true,
},
})
if (firstGenMembers.length === 0) return null
// 按出生日期排序(年龄最大的优先),如果没有出生日期则按创建时间排序
const sorted = firstGenMembers.sort((a, b) => {
// 如果都有出生日期,按出生日期排序(早的在前)
if (a.birthDate && b.birthDate) {
return a.birthDate.localeCompare(b.birthDate)
}
// 如果只有一个有出生日期,有出生日期的优先
if (a.birthDate) return -1
if (b.birthDate) return 1
// 如果都没有出生日期,按创建时间排序
return a.createdAt.getTime() - b.createdAt.getTime()
})
return sorted[0].id
}
/**
* 获取家族树中的最小代数
* @param treeId 家族树ID
* @returns 最小代数,如果没有成员则返回null
*/
export async function getMinGeneration(treeId: string): Promise<number | null> {
const result = await prisma.familyMember.aggregate({
where: { treeId },
_min: {
generation: true,
},
})
return result._min.generation
}
/**
* 检查成员是否是根成员(第1代且无父母)
* @param memberId 成员ID
* @returns 是否是根成员
*/
export async function isRootMember(memberId: string): Promise<boolean> {
const member = await prisma.familyMember.findUnique({
where: { id: memberId },
select: {
generation: true,
fatherId: true,
motherId: true,
},
})
if (!member) return false
return member.generation === 1 && !member.fatherId && !member.motherId
}
/**
* 检查成员是否有子女
* @param memberId 成员ID
* @returns 是否有子女
*/
export async function hasChildren(memberId: string): Promise<boolean> {
const member = await prisma.familyMember.findUnique({
where: { id: memberId },
select: {
childrenIds: true,
},
})
return member ? member.childrenIds.length > 0 : false
}
// ============================================
// 前端工具函数(不依赖 Prisma,可在客户端使用)
// ============================================
/**
* 检查成员是否为第1代成员(需要显示代数重新计算警告)
* 这是一个纯函数,可以在前端使用
* @param member 家族成员对象,需要包含 generation 字段
* @returns 是否为第1代成员
*/
export function isFirstGenerationMember(member: { generation?: number | null }): boolean {
// 处理边界情况:generation 为 undefined、null 或无效值时返回 false
if (member.generation === undefined || member.generation === null) {
return false
}
return member.generation === 1
}
/**
* 检查添加父母操作是否需要显示代数重新计算警告
* @param member 目标成员对象
* @param relationType 关系类型
* @returns 是否需要显示警告
*/
export function shouldShowGenerationWarning(
member: { generation?: number | null },
relationType: 'father' | 'mother' | 'spouse' | 'child'
): boolean {
// 只有添加父母(father/mother)时才需要检查
if (relationType !== 'father' && relationType !== 'mother') {
return false
}
return isFirstGenerationMember(member)
}