支持企微通讯录缺失手机号(07-26-internal-ops-recovery-baseline)
这个提交包含在:
+19
-14
@@ -58,10 +58,15 @@ const normalizedEmailSchema = z
|
||||
.max(200)
|
||||
.transform((value) => value.toLowerCase());
|
||||
|
||||
const nullablePhoneSchema = z.preprocess(
|
||||
(value) => (typeof value === "string" && value.trim() === "" ? null : value),
|
||||
z.string().trim().min(6).max(30).nullable().optional(),
|
||||
);
|
||||
|
||||
const createBodySchema = z.object({
|
||||
employeeNo: z.string().trim().min(1).max(50).optional(),
|
||||
name: z.string().min(1).max(100),
|
||||
phone: z.string().min(6).max(30),
|
||||
phone: nullablePhoneSchema,
|
||||
email: normalizedEmailSchema,
|
||||
departmentId: z.string().uuid().optional(),
|
||||
title: z.string().min(1).max(100).optional(),
|
||||
@@ -74,7 +79,7 @@ const createBodySchema = z.object({
|
||||
|
||||
const updateBodySchema = z.object({
|
||||
name: z.string().trim().min(1).max(100).optional(),
|
||||
phone: z.string().trim().min(6).max(30).optional(),
|
||||
phone: nullablePhoneSchema,
|
||||
email: normalizedEmailSchema.optional(),
|
||||
departmentId: z.string().uuid().optional(),
|
||||
title: z.string().trim().min(1).max(100).optional(),
|
||||
@@ -98,8 +103,8 @@ const sensitiveBodySchema = z
|
||||
})
|
||||
.strict();
|
||||
|
||||
const USER_IMPORT_REQUIRED_HEADERS = ["employeeNo", "name", "phone", "email", "departmentName"];
|
||||
const USER_IMPORT_OPTIONAL_HEADERS = ["title", "directManagerEmployeeNo", "roles", "hireDate"];
|
||||
const USER_IMPORT_REQUIRED_HEADERS = ["employeeNo", "name", "email", "departmentName"];
|
||||
const USER_IMPORT_OPTIONAL_HEADERS = ["phone", "title", "directManagerEmployeeNo", "roles", "hireDate"];
|
||||
const USER_IMPORT_ALLOWED_EXTENSIONS = new Set([".csv", ".xlsx"]);
|
||||
const USER_IMPORT_ALLOWED_MIME_TYPES = new Set([
|
||||
"text/csv",
|
||||
@@ -773,11 +778,9 @@ function validateImportRowFields(row) {
|
||||
pushImportRowError(row, "name", "too_long");
|
||||
}
|
||||
|
||||
if (!normalized.phone) {
|
||||
pushImportRowError(row, "phone", "required");
|
||||
} else if (normalized.phone.length < 6) {
|
||||
if (normalized.phone && normalized.phone.length < 6) {
|
||||
pushImportRowError(row, "phone", "too_short");
|
||||
} else if (normalized.phone.length > 30) {
|
||||
} else if (normalized.phone && normalized.phone.length > 30) {
|
||||
pushImportRowError(row, "phone", "too_long");
|
||||
}
|
||||
|
||||
@@ -834,7 +837,7 @@ function createDepartmentResolutionMap(departments) {
|
||||
function createEmployeeLookupMaps(employees) {
|
||||
return {
|
||||
byEmployeeNo: new Map(employees.map((item) => [item.employee_no, item])),
|
||||
byPhone: new Map(employees.map((item) => [item.phone, item])),
|
||||
byPhone: new Map(employees.filter((item) => item.phone).map((item) => [item.phone, item])),
|
||||
byEmail: new Map(employees.map((item) => [item.email, item])),
|
||||
};
|
||||
}
|
||||
@@ -912,7 +915,9 @@ async function buildUserImportPreview(prisma, rawRows, options = {}) {
|
||||
pushImportRowError(row, "employeeNo", "duplicate_existing");
|
||||
}
|
||||
|
||||
const phoneConflict = existingLookup.byPhone.get(row.normalized.phone);
|
||||
const phoneConflict = row.normalized.phone
|
||||
? existingLookup.byPhone.get(row.normalized.phone)
|
||||
: null;
|
||||
if (phoneConflict) {
|
||||
pushImportRowError(row, "phone", "duplicate_existing");
|
||||
}
|
||||
@@ -1012,7 +1017,7 @@ async function createSystemUser(tx, options) {
|
||||
? { employee_no: options.employeeNo.trim() }
|
||||
: {}),
|
||||
name: options.name,
|
||||
phone: options.phone,
|
||||
phone: options.phone || null,
|
||||
email: options.email.trim().toLowerCase(),
|
||||
department_id: options.departmentId,
|
||||
title: options.title ?? "员工",
|
||||
@@ -1573,7 +1578,7 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
const employee = await createSystemUser(tx, {
|
||||
employeeNo: row.normalized.employeeNo,
|
||||
name: row.normalized.name,
|
||||
phone: row.normalized.phone,
|
||||
phone: row.normalized.phone || null,
|
||||
email: row.normalized.email,
|
||||
departmentId: row.resolvedDepartmentId,
|
||||
title: row.normalized.title,
|
||||
@@ -1742,7 +1747,7 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
if (typeof body.name === "string" && body.name !== current.name) {
|
||||
changes.name = { before: current.name, after: body.name };
|
||||
}
|
||||
if (typeof body.phone === "string" && body.phone !== current.phone) {
|
||||
if (Object.prototype.hasOwnProperty.call(body, "phone") && (body.phone ?? null) !== current.phone) {
|
||||
changes.phone = { before: current.phone, after: body.phone };
|
||||
}
|
||||
if (typeof body.email === "string" && body.email !== current.email) {
|
||||
@@ -1768,7 +1773,7 @@ async function usersRoutes(fastify, opts = {}) {
|
||||
where: { id: userId },
|
||||
data: {
|
||||
...(typeof body.name === "string" ? { name: body.name } : {}),
|
||||
...(typeof body.phone === "string" ? { phone: body.phone } : {}),
|
||||
...(Object.prototype.hasOwnProperty.call(body, "phone") ? { phone: body.phone ?? null } : {}),
|
||||
...(typeof body.email === "string" ? { email: body.email } : {}),
|
||||
...(typeof body.departmentId === "string" ? { department_id: body.departmentId } : {}),
|
||||
...(typeof body.title === "string" ? { title: body.title } : {}),
|
||||
|
||||
在新工单中引用
屏蔽一个用户