HRWD-0..9 实现人事生命周期企微通讯录同步
这个提交包含在:
+177
-4
@@ -12,6 +12,13 @@ const { AppError } = require("../utils/AppError");
|
||||
const { sendData } = require("../utils/reply");
|
||||
const { buildAuditLogData } = require("../utils/audit");
|
||||
const { FEATURE_FLAG_KEYS, evaluateFeatureFlag } = require("../services/featureFlags");
|
||||
const {
|
||||
getLatestWecomDirectorySync,
|
||||
mapWecomDirectoryState,
|
||||
mapWecomDirectorySyncRun,
|
||||
normalizeOperation,
|
||||
syncWecomDirectoryForEmployee,
|
||||
} = require("../services/integrations/wecomDirectory");
|
||||
const { listOrganizationDepartments } = require("../services/organization");
|
||||
const { StorageNotFoundError, openReadStream, parseStoragePath, writeBuffer } = require("../services/storage");
|
||||
|
||||
@@ -71,6 +78,10 @@ const OFFBOARDING_SCHEMA = z.object({
|
||||
notes: z.string().trim().max(500).nullable().optional(),
|
||||
}).strict();
|
||||
|
||||
const WECOM_DIRECTORY_RETRY_SCHEMA = z.object({
|
||||
operation: z.enum(["create", "update", "disable"]).optional(),
|
||||
}).strict();
|
||||
|
||||
const CONTRACT_CREATE_SCHEMA = z.object({
|
||||
contractNo: z.string().trim().min(1).max(100),
|
||||
contractType: CONTRACT_TYPE_SCHEMA,
|
||||
@@ -155,6 +166,50 @@ function canManageLifecycle(fastify, request) {
|
||||
return fastify.hasPermission(request.employee, "system.users") || fastify.hasPermission(request.employee, "system.settings") || fastify.hasPermission(request.employee, "*");
|
||||
}
|
||||
|
||||
function canUseWecomDirectorySync(fastify) {
|
||||
return Boolean(
|
||||
fastify?.prisma?.wecomDirectorySyncRun
|
||||
&& fastify?.prisma?.wecomIdentityBinding
|
||||
&& fastify?.prisma?.featureFlag,
|
||||
);
|
||||
}
|
||||
|
||||
async function trySyncWecomDirectory(fastify, request, options) {
|
||||
if (!canUseWecomDirectorySync(fastify)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const operation = normalizeOperation(options.operation);
|
||||
const result = await syncWecomDirectoryForEmployee({
|
||||
prisma: fastify.prisma,
|
||||
config: fastify.config,
|
||||
employeeId: options.employeeId,
|
||||
actorId: request.employee.id,
|
||||
operation,
|
||||
});
|
||||
|
||||
if (fastify.prisma.auditLog?.create) {
|
||||
await fastify.prisma.auditLog.create({
|
||||
data: buildAuditLogData(request, {
|
||||
actorId: request.employee.id,
|
||||
action: options.auditAction ?? `WECOM_DIRECTORY_${operation.toUpperCase()}`,
|
||||
targetType: "employee",
|
||||
targetId: options.employeeId,
|
||||
extra: {
|
||||
operation,
|
||||
status: result.status,
|
||||
code: result.code,
|
||||
skipped: Boolean(result.skipped),
|
||||
syncRunId: result.run?.id ?? null,
|
||||
externalUserId: result.run?.externalUserId ?? null,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function canUploadAvatar(fastify, request, employeeId) {
|
||||
return request.employee.id === employeeId || fastify.hasPermission(request.employee, "system.users");
|
||||
}
|
||||
@@ -535,6 +590,13 @@ async function buildEmployeeProfile(prisma, employeeId) {
|
||||
roles: { select: { role_code: true } },
|
||||
department: { select: { id: true, name: true } },
|
||||
direct_manager: { select: { id: true, employee_no: true, name: true, avatar_path: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.employeeChecklistTask.findMany({ where: { employee_id: employeeId }, orderBy: [{ created_at: "desc" }] }),
|
||||
@@ -584,6 +646,7 @@ async function buildEmployeeProfile(prisma, employeeId) {
|
||||
avatarUrl: buildAvatarUrl(employee.direct_manager.id, employee.direct_manager.avatar_path),
|
||||
}
|
||||
: null,
|
||||
wecomDirectory: mapWecomDirectoryState(employee),
|
||||
},
|
||||
lifecycle: {
|
||||
events: lifecycleEvents.map(mapLifecycleEvent),
|
||||
@@ -745,6 +808,90 @@ async function employeesRoutes(fastify) {
|
||||
return sendData(reply, request, stats);
|
||||
});
|
||||
|
||||
fastify.get("/employees/:id/wecom-directory-sync", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
await ensureFeatureEnabled(fastify, request);
|
||||
if (!canManageLifecycle(fastify, request)) {
|
||||
throw new AppError({ code: "PERM_001", message: "无权限", statusCode: 403, details: { permission: "system.users" } });
|
||||
}
|
||||
if (!canUseWecomDirectorySync(fastify)) {
|
||||
throw buildValidationError({ featureFlag: "hr_wecom_directory_sync_v1_unavailable" }, 409);
|
||||
}
|
||||
|
||||
const employeeId = parseId(request.params?.id);
|
||||
const employee = await fastify.prisma.employee.findUnique({
|
||||
where: { id: employeeId },
|
||||
select: {
|
||||
id: true,
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!employee) {
|
||||
throw new AppError({ code: "SYS_404", message: "Not Found", statusCode: 404 });
|
||||
}
|
||||
|
||||
const runs = await fastify.prisma.wecomDirectorySyncRun.findMany({
|
||||
where: { employee_id: employeeId },
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 20,
|
||||
});
|
||||
|
||||
return sendData(reply, request, {
|
||||
employeeId,
|
||||
wecomDirectory: mapWecomDirectoryState({ binding: employee.wecom_identity_binding, runs }),
|
||||
items: runs.map(mapWecomDirectorySyncRun),
|
||||
});
|
||||
});
|
||||
|
||||
fastify.post("/employees/:id/wecom-directory-sync/retry", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
await ensureFeatureEnabled(fastify, request);
|
||||
if (!canManageLifecycle(fastify, request)) {
|
||||
throw new AppError({ code: "PERM_001", message: "无权限", statusCode: 403, details: { permission: "system.users" } });
|
||||
}
|
||||
if (!canUseWecomDirectorySync(fastify)) {
|
||||
throw buildValidationError({ featureFlag: "hr_wecom_directory_sync_v1_unavailable" }, 409);
|
||||
}
|
||||
|
||||
const employeeId = parseId(request.params?.id);
|
||||
const body = parseBody(WECOM_DIRECTORY_RETRY_SCHEMA, request.body);
|
||||
const employee = await fastify.prisma.employee.findUnique({
|
||||
where: { id: employeeId },
|
||||
select: {
|
||||
id: true,
|
||||
status: true,
|
||||
employment_status: true,
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!employee) {
|
||||
throw new AppError({ code: "SYS_404", message: "Not Found", statusCode: 404 });
|
||||
}
|
||||
|
||||
const operation = body.operation
|
||||
?? (employee.status === "archived" || employee.employment_status === "inactive"
|
||||
? "disable"
|
||||
: employee.wecom_identity_binding?.external_user_id
|
||||
? "update"
|
||||
: "create");
|
||||
|
||||
const result = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId,
|
||||
operation,
|
||||
auditAction: "WECOM_DIRECTORY_RETRY",
|
||||
});
|
||||
const state = await getLatestWecomDirectorySync(fastify.prisma, employeeId);
|
||||
|
||||
return sendData(reply, request, {
|
||||
employeeId,
|
||||
operation,
|
||||
result,
|
||||
wecomDirectory: state,
|
||||
});
|
||||
});
|
||||
|
||||
fastify.get("/employees/:id/profile", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
await ensureFeatureEnabled(fastify, request);
|
||||
const employeeId = parseId(request.params?.id);
|
||||
@@ -1176,8 +1323,18 @@ async function employeesRoutes(fastify) {
|
||||
updated_by: request.employee.id,
|
||||
},
|
||||
});
|
||||
const shouldSyncDirectory =
|
||||
managerMode
|
||||
&& (
|
||||
Object.prototype.hasOwnProperty.call(body, "departmentId")
|
||||
|| Object.prototype.hasOwnProperty.call(body, "directManagerId")
|
||||
|| Object.prototype.hasOwnProperty.call(body, "title")
|
||||
);
|
||||
const directorySync = shouldSyncDirectory
|
||||
? await trySyncWecomDirectory(fastify, request, { employeeId, operation: "update" })
|
||||
: null;
|
||||
const profile = await buildEmployeeProfile(fastify.prisma, employeeId);
|
||||
return sendData(reply, request, profile);
|
||||
return sendData(reply, request, { ...profile, wecomDirectorySync: directorySync });
|
||||
});
|
||||
|
||||
fastify.post("/employees/:id/onboarding/start", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
@@ -1211,8 +1368,12 @@ async function employeesRoutes(fastify) {
|
||||
const checklist = buildDefaultChecklist("onboarding", employeeId).map((item) => ({ ...item, due_date: body.probationEndDate ? new Date(`${body.probationEndDate}T00:00:00.000Z`) : null }));
|
||||
await tx.employeeChecklistTask.createMany({ data: checklist, skipDuplicates: false });
|
||||
});
|
||||
const directorySync = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId,
|
||||
operation: "create",
|
||||
});
|
||||
const profile = await buildEmployeeProfile(fastify.prisma, employeeId);
|
||||
return sendData(reply, request, profile);
|
||||
return sendData(reply, request, { ...profile, wecomDirectorySync: directorySync });
|
||||
});
|
||||
|
||||
fastify.post("/employees/:id/regularize", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
@@ -1258,8 +1419,12 @@ async function employeesRoutes(fastify) {
|
||||
notes: body.notes,
|
||||
});
|
||||
});
|
||||
const directorySync = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId,
|
||||
operation: "update",
|
||||
});
|
||||
const profile = await buildEmployeeProfile(fastify.prisma, employeeId);
|
||||
return sendData(reply, request, profile);
|
||||
return sendData(reply, request, { ...profile, wecomDirectorySync: directorySync });
|
||||
});
|
||||
|
||||
fastify.post("/employees/:id/offboarding/start", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
@@ -1341,8 +1506,16 @@ async function employeesRoutes(fastify) {
|
||||
});
|
||||
}
|
||||
|
||||
const directorySync = result.completedOffboarding
|
||||
? await trySyncWecomDirectory(fastify, request, { employeeId, operation: "disable" })
|
||||
: null;
|
||||
const profile = await buildEmployeeProfile(fastify.prisma, employeeId);
|
||||
return sendData(reply, request, { ...profile.lifecycle, autoAdvanced: result.autoChanged });
|
||||
return sendData(reply, request, {
|
||||
...profile.lifecycle,
|
||||
autoAdvanced: result.autoChanged,
|
||||
wecomDirectory: profile.employee.wecomDirectory,
|
||||
wecomDirectorySync: directorySync,
|
||||
});
|
||||
}
|
||||
|
||||
fastify.post("/employees/:id/checklist-tasks/:taskId/complete", { preHandler: fastify.authenticate }, async (request, reply) => {
|
||||
|
||||
@@ -268,6 +268,15 @@ const FEATURE_FLAG_BACKEND_IMPACT = Object.freeze({
|
||||
routes: ["/api/employees", "/api/performance/cycles", "/api/performance/reviews"],
|
||||
},
|
||||
],
|
||||
hr_wecom_directory_sync_v1: [
|
||||
{
|
||||
label: "企微通讯录同步",
|
||||
routes: [
|
||||
"/api/employees/:id/wecom-directory-sync",
|
||||
"/api/employees/:id/wecom-directory-sync/retry",
|
||||
],
|
||||
},
|
||||
],
|
||||
todo_board_v1: [
|
||||
{
|
||||
label: "待办工作台",
|
||||
|
||||
+158
-2
@@ -12,6 +12,10 @@ const {
|
||||
} = require("../services/employeeDirectoryFilters");
|
||||
const { applyEmployeeTransfer } = require("../services/employeeLifecycle");
|
||||
const { FEATURE_FLAG_KEYS, evaluateFeatureFlag } = require("../services/featureFlags");
|
||||
const {
|
||||
mapWecomDirectoryState,
|
||||
syncWecomDirectoryForEmployee,
|
||||
} = require("../services/integrations/wecomDirectory");
|
||||
const { AppError } = require("../utils/AppError");
|
||||
const { sendData } = require("../utils/reply");
|
||||
const { buildAuditLogData } = require("../utils/audit");
|
||||
@@ -53,6 +57,7 @@ const createBodySchema = z.object({
|
||||
hireDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
||||
roles: z.array(roleCodeSchema).min(1).optional(),
|
||||
password: z.string().trim().min(1).optional(),
|
||||
syncWecomDirectory: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const updateBodySchema = z.object({
|
||||
@@ -218,6 +223,7 @@ function mapEmployeeSummary(row) {
|
||||
hireDate: row.hire_date?.toISOString?.()?.slice(0, 10) ?? null,
|
||||
avatarUrl: row.avatar_path ? `/api/avatars/${row.id}` : null,
|
||||
roleCodes: row.roles.map((item) => item.role_code),
|
||||
wecomDirectory: mapWecomDirectoryState(row),
|
||||
createdAt: row.created_at?.toISOString?.() ?? null,
|
||||
updatedAt: row.updated_at?.toISOString?.() ?? null,
|
||||
};
|
||||
@@ -445,6 +451,48 @@ async function setEmployeeAccountStatus(tx, request, options) {
|
||||
return updated;
|
||||
}
|
||||
|
||||
function canUseWecomDirectorySync(fastify) {
|
||||
return Boolean(
|
||||
fastify?.prisma?.wecomDirectorySyncRun
|
||||
&& fastify?.prisma?.wecomIdentityBinding
|
||||
&& fastify?.prisma?.featureFlag,
|
||||
);
|
||||
}
|
||||
|
||||
async function trySyncWecomDirectory(fastify, request, options) {
|
||||
if (!canUseWecomDirectorySync(fastify)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = await syncWecomDirectoryForEmployee({
|
||||
prisma: fastify.prisma,
|
||||
config: fastify.config,
|
||||
employeeId: options.employeeId,
|
||||
actorId: request.employee.id,
|
||||
operation: options.operation,
|
||||
});
|
||||
|
||||
if (fastify.prisma.auditLog?.create) {
|
||||
await fastify.prisma.auditLog.create({
|
||||
data: buildAuditLogData(request, {
|
||||
actorId: request.employee.id,
|
||||
action: `WECOM_DIRECTORY_${String(options.operation ?? "update").toUpperCase()}`,
|
||||
targetType: "employee",
|
||||
targetId: options.employeeId,
|
||||
extra: {
|
||||
status: result.status,
|
||||
code: result.code,
|
||||
skipped: Boolean(result.skipped),
|
||||
syncRunId: result.run?.id ?? null,
|
||||
externalUserId: result.run?.externalUserId ?? null,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function generateOneTimePassword() {
|
||||
const password = `ResetA1${crypto.randomBytes(6).toString("hex")}`;
|
||||
const errors = validatePassword(password);
|
||||
@@ -978,6 +1026,13 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
department: { select: { id: true, name: true } },
|
||||
direct_manager: { select: { id: true, employee_no: true, name: true } },
|
||||
roles: { select: { role_code: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
fastify.prisma.employee.count({ where }),
|
||||
@@ -1033,6 +1088,13 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
roles: { select: { role_code: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1126,10 +1188,18 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
where: { employee_id: created.employee.id },
|
||||
select: { role_code: true },
|
||||
});
|
||||
const directorySync =
|
||||
body.syncWecomDirectory === false
|
||||
? null
|
||||
: await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId: created.employee.id,
|
||||
operation: "create",
|
||||
});
|
||||
|
||||
return sendData(reply, request, {
|
||||
...mapEmployeeSummary({ ...created.employee, roles }),
|
||||
oneTimePassword: created.oneTimePassword,
|
||||
wecomDirectorySync: directorySync,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -1173,6 +1243,10 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
});
|
||||
});
|
||||
|
||||
for (const employeeId of body.employeeIds) {
|
||||
await trySyncWecomDirectory(fastify, request, { employeeId, operation: "update" });
|
||||
}
|
||||
|
||||
return sendData(reply, request, {
|
||||
summary: { total: body.employeeIds.length, updated: body.employeeIds.length },
|
||||
employeeIds: body.employeeIds,
|
||||
@@ -1221,6 +1295,10 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
});
|
||||
});
|
||||
|
||||
for (const employeeId of body.employeeIds) {
|
||||
await trySyncWecomDirectory(fastify, request, { employeeId, operation: "update" });
|
||||
}
|
||||
|
||||
return sendData(reply, request, {
|
||||
summary: { total: body.employeeIds.length, updated: body.employeeIds.length },
|
||||
employeeIds: body.employeeIds,
|
||||
@@ -1268,6 +1346,10 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
});
|
||||
});
|
||||
|
||||
for (const employeeId of body.employeeIds) {
|
||||
await trySyncWecomDirectory(fastify, request, { employeeId, operation: "disable" });
|
||||
}
|
||||
|
||||
return sendData(reply, request, {
|
||||
summary: { total: body.employeeIds.length, updated: body.employeeIds.length },
|
||||
employeeIds: body.employeeIds,
|
||||
@@ -1312,6 +1394,10 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
});
|
||||
});
|
||||
|
||||
for (const employeeId of body.employeeIds) {
|
||||
await trySyncWecomDirectory(fastify, request, { employeeId, operation: "update" });
|
||||
}
|
||||
|
||||
return sendData(reply, request, {
|
||||
summary: { total: body.employeeIds.length, updated: body.employeeIds.length },
|
||||
employeeIds: body.employeeIds,
|
||||
@@ -1459,7 +1545,21 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return sendData(reply, request, result);
|
||||
const wecomDirectorySync = [];
|
||||
for (const item of result.imported) {
|
||||
const syncResult = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId: item.employeeId,
|
||||
operation: "create",
|
||||
});
|
||||
if (syncResult) {
|
||||
wecomDirectorySync.push(syncResult);
|
||||
}
|
||||
}
|
||||
|
||||
return sendData(reply, request, {
|
||||
...result,
|
||||
wecomDirectorySync,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1520,6 +1620,13 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
roles: { select: { role_code: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!current) {
|
||||
@@ -1589,6 +1696,13 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
roles: { select: { role_code: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1618,10 +1732,23 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const shouldSyncDirectory =
|
||||
Object.prototype.hasOwnProperty.call(body, "name")
|
||||
|| Object.prototype.hasOwnProperty.call(body, "phone")
|
||||
|| Object.prototype.hasOwnProperty.call(body, "email")
|
||||
|| Object.prototype.hasOwnProperty.call(body, "departmentId")
|
||||
|| Object.prototype.hasOwnProperty.call(body, "title");
|
||||
const directorySync = shouldSyncDirectory
|
||||
? await trySyncWecomDirectory(fastify, request, { employeeId: userId, operation: "update" })
|
||||
: null;
|
||||
|
||||
return sendData(
|
||||
reply,
|
||||
request,
|
||||
mapEmployeeDetail(updated, { config: fastify.config, logger: request.log ?? fastify.log }),
|
||||
{
|
||||
...mapEmployeeDetail(updated, { config: fastify.config, logger: request.log ?? fastify.log }),
|
||||
wecomDirectorySync: directorySync,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1718,6 +1845,13 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
roles: { select: { role_code: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!current) {
|
||||
@@ -1783,6 +1917,13 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
roles: { select: { role_code: true } },
|
||||
wecom_identity_binding: {
|
||||
select: { external_user_id: true, external_open_user_id: true },
|
||||
},
|
||||
wecom_directory_sync_runs: {
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1870,10 +2011,15 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
|
||||
return updated;
|
||||
});
|
||||
const directorySync = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId: userId,
|
||||
operation: "disable",
|
||||
});
|
||||
|
||||
return sendData(reply, request, {
|
||||
id: employee.id,
|
||||
status: employee.status,
|
||||
wecomDirectorySync: directorySync,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -1903,10 +2049,15 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
now,
|
||||
});
|
||||
});
|
||||
const directorySync = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId: userId,
|
||||
operation: "disable",
|
||||
});
|
||||
|
||||
return sendData(reply, request, {
|
||||
id: employee.id,
|
||||
status: employee.status,
|
||||
wecomDirectorySync: directorySync,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -1928,10 +2079,15 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
now,
|
||||
});
|
||||
});
|
||||
const directorySync = await trySyncWecomDirectory(fastify, request, {
|
||||
employeeId: userId,
|
||||
operation: "update",
|
||||
});
|
||||
|
||||
return sendData(reply, request, {
|
||||
id: employee.id,
|
||||
status: employee.status,
|
||||
wecomDirectorySync: directorySync,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
在新工单中引用
屏蔽一个用户