Files
alla-allaos-fullstack/scripts/migrate-membership-business-roles.ts
phaichayon 827fd13fa7 task-l.2
2026-06-22 11:39:41 +07:00

173 lines
5.1 KiB
TypeScript

import { writeFileSync } from 'node:fs';
import { and, eq, isNull } from 'drizzle-orm';
import { crmRoleProfiles, crmUserRoleAssignments, memberships } from '@/db/schema';
import { auditAction } from '@/features/foundation/audit-log/service';
import { db } from '@/lib/db';
type MigrationReport = {
mode: 'dry-run' | 'execute';
startedAt: string;
completedAt?: string;
migratedUsers: number;
skippedUsers: number;
unknownRoles: Array<{ membershipId: string; organizationId: string; userId: string; businessRole: string }>;
duplicateAssignments: Array<{ membershipId: string; assignmentId: string; organizationId: string; userId: string }>;
actions: Array<Record<string, unknown>>;
};
function getArg(name: string) {
const match = process.argv.find((arg) => arg === name || arg.startsWith(`${name}=`));
if (!match) {
return null;
}
if (match === name) {
return 'true';
}
return match.slice(name.length + 1);
}
async function main() {
const execute = getArg('--execute') === 'true';
const reportPath = getArg('--report');
const snapshotPath = getArg('--snapshot');
const report: MigrationReport = {
mode: execute ? 'execute' : 'dry-run',
startedAt: new Date().toISOString(),
migratedUsers: 0,
skippedUsers: 0,
unknownRoles: [],
duplicateAssignments: [],
actions: []
};
const membershipRows = await db.query.memberships.findMany();
if (snapshotPath) {
writeFileSync(snapshotPath, JSON.stringify(membershipRows, null, 2));
}
for (const membership of membershipRows) {
const [roleProfile] = await db
.select({
id: crmRoleProfiles.id
})
.from(crmRoleProfiles)
.where(
and(
eq(crmRoleProfiles.organizationId, membership.organizationId),
eq(crmRoleProfiles.code, membership.businessRole),
isNull(crmRoleProfiles.deletedAt)
)
)
.limit(1);
if (!roleProfile) {
report.skippedUsers += 1;
report.unknownRoles.push({
membershipId: membership.id,
organizationId: membership.organizationId,
userId: membership.userId,
businessRole: membership.businessRole
});
continue;
}
const existing = await db.query.crmUserRoleAssignments.findFirst({
where: and(
eq(crmUserRoleAssignments.organizationId, membership.organizationId),
eq(crmUserRoleAssignments.userId, membership.userId),
eq(crmUserRoleAssignments.roleProfileId, roleProfile.id)
)
});
if (existing && !existing.deletedAt) {
report.skippedUsers += 1;
report.duplicateAssignments.push({
membershipId: membership.id,
assignmentId: existing.id,
organizationId: membership.organizationId,
userId: membership.userId
});
continue;
}
const actionPayload = {
membershipId: membership.id,
organizationId: membership.organizationId,
userId: membership.userId,
roleProfileId: roleProfile.id,
businessRole: membership.businessRole,
branchScopeIds: membership.branchScopeIds ?? [],
productTypeScopeIds: membership.productTypeScopeIds ?? []
};
report.actions.push(actionPayload);
report.migratedUsers += 1;
if (!execute) {
continue;
}
const assignmentId = existing?.id ?? crypto.randomUUID();
if (existing) {
await db
.update(crmUserRoleAssignments)
.set({
branchScopeMode: (membership.branchScopeIds?.length ?? 0) > 0 ? 'selected' : 'all',
branchScopeIds: membership.branchScopeIds ?? [],
productTypeScopeMode:
(membership.productTypeScopeIds?.length ?? 0) > 0 ? 'selected' : 'all',
productTypeScopeIds: membership.productTypeScopeIds ?? [],
isPrimary: true,
isActive: true,
assignedBy: 'system:migration',
assignedAt: new Date(),
updatedAt: new Date(),
deletedAt: null
})
.where(eq(crmUserRoleAssignments.id, existing.id));
} else {
await db.insert(crmUserRoleAssignments).values({
id: assignmentId,
organizationId: membership.organizationId,
userId: membership.userId,
roleProfileId: roleProfile.id,
branchScopeMode: (membership.branchScopeIds?.length ?? 0) > 0 ? 'selected' : 'all',
branchScopeIds: membership.branchScopeIds ?? [],
productTypeScopeMode:
(membership.productTypeScopeIds?.length ?? 0) > 0 ? 'selected' : 'all',
productTypeScopeIds: membership.productTypeScopeIds ?? [],
isPrimary: true,
isActive: true,
assignedBy: 'system:migration'
});
}
await auditAction({
organizationId: membership.organizationId,
userId: 'system:migration',
entityType: 'crm_user_role_assignment',
entityId: assignmentId,
action: 'migrate',
afterData: actionPayload
});
}
report.completedAt = new Date().toISOString();
if (reportPath) {
writeFileSync(reportPath, JSON.stringify(report, null, 2));
}
console.info(JSON.stringify(report, null, 2));
}
main().catch((error) => {
console.error(error);
process.exit(1);
});