76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
4.6 KiB
TypeScript
126 lines
4.6 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function ensureUser(name: string, username: string, deptName: string, employeeNo: string, passwordHash: string) {
|
|
const existing = await prisma.user.findFirst({ where: { OR: [{ username }, { displayName: name }] } });
|
|
if (existing) return existing.id;
|
|
const dept =
|
|
(await prisma.department.findFirst({ where: { name: deptName } })) ||
|
|
(await prisma.department.findFirst({ where: { name: { contains: '研发' } } })) ||
|
|
(await prisma.department.findFirst({ where: { parentId: { not: null } } }));
|
|
if (!dept) throw new Error(`没有部门可挂 ${name}`);
|
|
const empRole = await prisma.role.findFirst({ where: { code: 'employee' } });
|
|
if (!empRole) throw new Error('没有 employee 角色');
|
|
let emp = await prisma.employee.findFirst({ where: { OR: [{ name }, { employeeNo }] } });
|
|
if (!emp) {
|
|
emp = await prisma.employee.create({
|
|
data: {
|
|
name,
|
|
employeeNo,
|
|
departmentId: dept.id,
|
|
title: '员工',
|
|
employmentStatus: 'LEFT',
|
|
remark: `OA 报销有此人(原部门 ${deptName}),人事花名册未导入,补建后挂单据,账号停用。`,
|
|
},
|
|
});
|
|
}
|
|
const user = await prisma.user.create({
|
|
data: {
|
|
username,
|
|
displayName: name,
|
|
passwordHash,
|
|
status: 'DISABLED',
|
|
employeeId: emp.id,
|
|
},
|
|
});
|
|
await prisma.userRole.create({ data: { userId: user.id, roleId: empRole.id } });
|
|
console.log('created leftover', name, user.id, 'dept', dept.name);
|
|
return user.id;
|
|
}
|
|
|
|
async function main() {
|
|
const admin = await prisma.user.findFirst({ where: { username: 'admin' } });
|
|
if (!admin) throw new Error('no admin');
|
|
await ensureUser('李乐菡', 'lilehan', '项目一部', 'E0115', admin.passwordHash);
|
|
|
|
const users = await prisma.user.findMany();
|
|
const byName = new Map(users.map((u) => [u.displayName, u.id]));
|
|
const ALIAS: Record<string, string> = { 张靖妍: '张婧妍' };
|
|
for (const [from, to] of Object.entries(ALIAS)) {
|
|
const uid = byName.get(to);
|
|
if (uid) byName.set(from, uid);
|
|
}
|
|
const expenses = await prisma.expenseClaim.findMany({ select: { id: true, remark: true, applicantId: true, status: true } });
|
|
let n = 0;
|
|
for (const e of expenses) {
|
|
const name = (e.remark || '').split(' / ')[0]?.trim();
|
|
if (!name || name === '系统管理员') continue;
|
|
const uid = byName.get(name);
|
|
if (!uid || uid === e.applicantId) continue;
|
|
await prisma.expenseClaim.update({ where: { id: e.id }, data: { applicantId: uid } });
|
|
n += 1;
|
|
}
|
|
const loans = await prisma.loanRecord.findMany({ select: { id: true, remark: true, applicantId: true } });
|
|
let ln = 0;
|
|
for (const e of loans) {
|
|
const name = (e.remark || '').split(' / ')[0]?.trim();
|
|
if (!name || name === '系统管理员') continue;
|
|
const uid = byName.get(name);
|
|
if (!uid || uid === e.applicantId) continue;
|
|
await prisma.loanRecord.update({ where: { id: e.id }, data: { applicantId: uid } });
|
|
ln += 1;
|
|
}
|
|
|
|
const approvers = await prisma.user.findMany({
|
|
where: { status: 'ACTIVE', roles: { some: { role: { code: { in: ['admin', 'finance', 'owner'] } } } } },
|
|
});
|
|
const pendingExp = await prisma.expenseClaim.findMany({ where: { status: 'PENDING' } });
|
|
const pendingLoan = await prisma.loanRecord.findMany({ where: { status: 'PENDING' } });
|
|
let tasks = 0;
|
|
for (const row of pendingExp) {
|
|
for (const u of approvers) {
|
|
const hit = await prisma.approvalTask.findFirst({
|
|
where: { bizType: 'EXPENSE_APPROVAL', bizId: row.id, assigneeId: u.id, status: 'PENDING' },
|
|
});
|
|
if (hit) continue;
|
|
await prisma.approvalTask.create({
|
|
data: {
|
|
title: `报销确认:${row.claimNo}`,
|
|
bizType: 'EXPENSE_APPROVAL',
|
|
bizId: row.id,
|
|
assigneeId: u.id,
|
|
},
|
|
});
|
|
tasks += 1;
|
|
}
|
|
}
|
|
for (const row of pendingLoan) {
|
|
for (const u of approvers) {
|
|
const hit = await prisma.approvalTask.findFirst({
|
|
where: { bizType: 'LOAN_APPROVAL', bizId: row.id, assigneeId: u.id, status: 'PENDING' },
|
|
});
|
|
if (hit) continue;
|
|
await prisma.approvalTask.create({
|
|
data: {
|
|
title: `借款确认:${row.loanNo}`,
|
|
bizType: 'LOAN_APPROVAL',
|
|
bizId: row.id,
|
|
assigneeId: u.id,
|
|
},
|
|
});
|
|
tasks += 1;
|
|
}
|
|
}
|
|
|
|
const left = await prisma.expenseClaim.count({ where: { applicantId: admin.id } });
|
|
console.log(JSON.stringify({ rematchedExpenses: n, rematchedLoans: ln, approvalTasks: tasks, adminStillHolds: left }));
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(() => prisma.$disconnect());
|