Initial commit: 风影 OA 全栈源码(API/Web/Flutter/IM)
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
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());
|
||||
Reference in New Issue
Block a user