187 lines
6.1 KiB
JavaScript
187 lines
6.1 KiB
JavaScript
import { PrismaClient } from '@prisma/client'
|
|
|
|
const prisma = new PrismaClient()
|
|
const applyChanges = process.argv.includes('--apply')
|
|
const actorUserId = 'system-template-consolidation'
|
|
const actorName = '系统模板归并'
|
|
|
|
function chooseRetainedVersion(versions) {
|
|
const byNewest = [...versions].sort((left, right) => (
|
|
Number(right.version || 0) - Number(left.version || 0)
|
|
|| new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime()
|
|
))
|
|
return byNewest.find((version) => version.status === 'published')
|
|
|| byNewest.find((version) => version.status === 'draft')
|
|
|| byNewest[0]
|
|
|| null
|
|
}
|
|
|
|
function readDefinition(value) {
|
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : {}
|
|
}
|
|
|
|
async function buildPlan() {
|
|
const templates = await prisma.performanceSurveyTemplate.findMany({
|
|
include: {
|
|
versions: {
|
|
orderBy: [{ version: 'desc' }, { updatedAt: 'desc' }],
|
|
},
|
|
},
|
|
orderBy: { createdAt: 'asc' },
|
|
})
|
|
|
|
return templates.map((template) => {
|
|
const retained = chooseRetainedVersion(template.versions)
|
|
const removed = retained
|
|
? template.versions.filter((version) => version.id !== retained.id)
|
|
: []
|
|
return {
|
|
template,
|
|
retained,
|
|
removed,
|
|
}
|
|
})
|
|
}
|
|
|
|
async function applyPlan(plan) {
|
|
return prisma.$transaction(async (tx) => {
|
|
const summary = {
|
|
templates: 0,
|
|
retainedVersions: 0,
|
|
removedVersions: 0,
|
|
reassignedPlanItems: 0,
|
|
deduplicatedPlanItems: 0,
|
|
}
|
|
|
|
for (const item of plan) {
|
|
const { template, retained, removed } = item
|
|
if (!retained) continue
|
|
summary.templates += 1
|
|
summary.retainedVersions += 1
|
|
|
|
const allVersionIds = template.versions.map((version) => version.id)
|
|
const planItems = await tx.performanceSurveyPositionPlanItem.findMany({
|
|
where: { templateVersionId: { in: allVersionIds } },
|
|
orderBy: [{ positionPlanId: 'asc' }, { sortOrder: 'asc' }, { createdAt: 'asc' }],
|
|
})
|
|
const itemsByPlan = new Map()
|
|
for (const planItem of planItems) {
|
|
const current = itemsByPlan.get(planItem.positionPlanId) || []
|
|
current.push(planItem)
|
|
itemsByPlan.set(planItem.positionPlanId, current)
|
|
}
|
|
|
|
for (const groupedItems of itemsByPlan.values()) {
|
|
const retainedItem = groupedItems.find((planItem) => planItem.templateVersionId === retained.id)
|
|
const primary = retainedItem || groupedItems[0]
|
|
const redundantIds = groupedItems
|
|
.filter((planItem) => planItem.id !== primary.id)
|
|
.map((planItem) => planItem.id)
|
|
if (redundantIds.length > 0) {
|
|
await tx.performanceSurveyPositionPlanItem.deleteMany({
|
|
where: { id: { in: redundantIds } },
|
|
})
|
|
summary.deduplicatedPlanItems += redundantIds.length
|
|
}
|
|
|
|
const nextSortOrder = Math.min(...groupedItems.map((planItem) => planItem.sortOrder))
|
|
const nextEnabled = groupedItems.some((planItem) => planItem.isEnabled)
|
|
if (
|
|
primary.templateVersionId !== retained.id
|
|
|| primary.sortOrder !== nextSortOrder
|
|
|| primary.isEnabled !== nextEnabled
|
|
) {
|
|
await tx.performanceSurveyPositionPlanItem.update({
|
|
where: { id: primary.id },
|
|
data: {
|
|
templateVersionId: retained.id,
|
|
sortOrder: nextSortOrder,
|
|
isEnabled: nextEnabled,
|
|
},
|
|
})
|
|
summary.reassignedPlanItems += 1
|
|
}
|
|
}
|
|
|
|
if (removed.length > 0) {
|
|
await tx.performanceSurveyTemplateVersion.deleteMany({
|
|
where: { id: { in: removed.map((version) => version.id) } },
|
|
})
|
|
summary.removedVersions += removed.length
|
|
}
|
|
if (retained.version !== 1) {
|
|
await tx.performanceSurveyTemplateVersion.update({
|
|
where: { id: retained.id },
|
|
data: { version: 1 },
|
|
})
|
|
}
|
|
|
|
const definition = readDefinition(retained.definition)
|
|
await tx.performanceSurveyTemplate.update({
|
|
where: { id: template.id },
|
|
data: {
|
|
name: String(definition.name || template.name),
|
|
description: String(definition.description || ''),
|
|
surveyType: String(definition.surveyType || template.surveyType),
|
|
weight: Number(definition.weight || template.weight || 1),
|
|
questions: Array.isArray(definition.questions) ? definition.questions : [],
|
|
scoreBands: Array.isArray(definition.scoreBands) ? definition.scoreBands : [],
|
|
},
|
|
})
|
|
await tx.performanceSurveyConfigAuditLog.create({
|
|
data: {
|
|
action: 'template.versions_collapsed',
|
|
targetType: 'template',
|
|
targetId: template.id,
|
|
actorUserId,
|
|
actorName,
|
|
reason: '模板改为单配置模式',
|
|
beforeSnapshot: {
|
|
versionIds: template.versions.map((version) => version.id),
|
|
versions: template.versions.map((version) => ({
|
|
id: version.id,
|
|
version: version.version,
|
|
status: version.status,
|
|
})),
|
|
},
|
|
afterSnapshot: {
|
|
retainedVersionId: retained.id,
|
|
status: retained.status,
|
|
},
|
|
metadata: {
|
|
removedVersionIds: removed.map((version) => version.id),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
return summary
|
|
}, { maxWait: 10_000, timeout: 60_000 })
|
|
}
|
|
|
|
try {
|
|
const plan = await buildPlan()
|
|
const preview = plan.map(({ template, retained, removed }) => ({
|
|
templateId: template.id,
|
|
templateCode: template.code,
|
|
templateName: template.name,
|
|
retained: retained
|
|
? { id: retained.id, version: retained.version, status: retained.status }
|
|
: null,
|
|
removed: removed.map((version) => ({
|
|
id: version.id,
|
|
version: version.version,
|
|
status: version.status,
|
|
})),
|
|
}))
|
|
|
|
if (!applyChanges) {
|
|
console.log(JSON.stringify({ mode: 'preview', templates: preview }, null, 2))
|
|
} else {
|
|
const summary = await applyPlan(plan)
|
|
console.log(JSON.stringify({ mode: 'applied', summary }, null, 2))
|
|
}
|
|
} finally {
|
|
await prisma.$disconnect()
|
|
}
|