76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
80 lines
3.0 KiB
TypeScript
80 lines
3.0 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
|
|
const prisma = new PrismaClient();
|
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
const KEYS = ['ccDaily', 'ccWeekly', 'ccMonthly'] as const;
|
|
|
|
function rolesOf(emp: { users: { roles: { role: { code: string } }[] }[] }) {
|
|
return [...new Set(emp.users.flatMap((u) => u.roles.map((r) => r.role.code)))];
|
|
}
|
|
|
|
function isDirector(emp: { title?: string | null; jobRole?: string | null; users: { roles: { role: { code: string } }[] }[] }) {
|
|
if (/总监/.test(`${emp.title || ''}${emp.jobRole || ''}`)) return true;
|
|
return rolesOf(emp).some((r) => r.endsWith('_director') || r === 'biz_director' || r === 'owner');
|
|
}
|
|
|
|
function parseIds(raw?: string | null) {
|
|
if (!raw) return [];
|
|
return [...new Set(raw.split(/[,,、\s]+/).filter((s) => UUID_RE.test(s)))];
|
|
}
|
|
|
|
async function main() {
|
|
const depts = await prisma.department.findMany({ where: { tenantId: '1' }, select: { id: true, parentId: true } });
|
|
const byId = new Map(depts.map((d) => [d.id, d]));
|
|
const people = await prisma.employee.findMany({
|
|
where: { tenantId: '1' },
|
|
include: { users: { select: { roles: { select: { role: { select: { code: true } } } } } } },
|
|
});
|
|
const byDept = new Map<string, typeof people>();
|
|
for (const e of people) {
|
|
const list = byDept.get(e.departmentId) || [];
|
|
list.push(e);
|
|
byDept.set(e.departmentId, list);
|
|
}
|
|
|
|
function scopeDeptIds(start: string, selfId: string) {
|
|
const scope: string[] = [];
|
|
const seen = new Set<string>();
|
|
let cur: string | null | undefined = start;
|
|
while (cur && !seen.has(cur)) {
|
|
seen.add(cur);
|
|
scope.push(cur);
|
|
const hasDir = (byDept.get(cur) || []).some((e) => e.id !== selfId && isDirector(e));
|
|
if (hasDir) break;
|
|
cur = byId.get(cur)?.parentId;
|
|
}
|
|
return new Set(scope);
|
|
}
|
|
|
|
let n = 0;
|
|
for (const emp of people) {
|
|
const scope = scopeDeptIds(emp.departmentId, emp.id);
|
|
const dirs = people.filter((d) => scope.has(d.departmentId) && d.id !== emp.id && d.id !== emp.managerId && isDirector(d));
|
|
const dirIds = dirs.map((d) => d.id);
|
|
const allowed = new Set(dirIds);
|
|
const data: Partial<Record<(typeof KEYS)[number], string | null>> = {};
|
|
for (const key of KEYS) {
|
|
const cur = parseIds(emp[key]);
|
|
let next = cur.filter((id) => allowed.has(id));
|
|
if (!next.length && dirIds.length && cur.some((id) => id === emp.managerId || !allowed.has(id))) {
|
|
next = dirIds;
|
|
}
|
|
const joined = next.length ? next.join(',') : null;
|
|
if (joined !== (emp[key] || null)) data[key] = joined;
|
|
}
|
|
if (!Object.keys(data).length) continue;
|
|
await prisma.employee.update({ where: { id: emp.id }, data });
|
|
n += 1;
|
|
console.log(`${emp.name}: ${JSON.stringify(data)}`);
|
|
}
|
|
console.log(`updated ${n} / ${people.length} employee cc fields`);
|
|
}
|
|
|
|
main()
|
|
.then(async () => prisma.$disconnect())
|
|
.catch(async (e) => {
|
|
console.error(e);
|
|
await prisma.$disconnect();
|
|
process.exit(1);
|
|
});
|