R64 A1-A6:新员工入门引导

这个提交包含在:
2026-04-14 16:45:10 +08:00
父节点 86644a6766
当前提交 7ac0de660c
共修改 12 个文件,包含 453 行新增和 5 行删除
+24
查看文件
@@ -28,6 +28,22 @@ function parseNotificationPreferences(body) {
return parsed.data;
}
function buildProfileCompleteness(employee) {
const hasEmergencyContact = Boolean(employee?.emergency_contact_name && employee?.emergency_contact_phone);
const hasBankAccount = Boolean(employee?.bank_account_encrypted && employee?.bank_name);
const hasAvatar = Boolean(employee?.avatar_path);
const hasIdNumber = Boolean(employee?.id_number_encrypted);
const completedCount = [hasEmergencyContact, hasBankAccount, hasAvatar, hasIdNumber].filter(Boolean).length;
return {
hasEmergencyContact,
hasBankAccount,
hasAvatar,
hasIdNumber,
score: completedCount * 25,
};
}
async function meRoutes(fastify) {
fastify.get(
"/me",
@@ -46,6 +62,12 @@ async function meRoutes(fastify) {
department_id: true,
direct_manager_id: true,
status: true,
emergency_contact_name: true,
emergency_contact_phone: true,
bank_account_encrypted: true,
bank_name: true,
avatar_path: true,
id_number_encrypted: true,
notification_push_enabled: true,
roles: { select: { role_code: true } },
wecom_identity_binding: { select: { external_user_id: true, created_at: true } },
@@ -53,6 +75,7 @@ async function meRoutes(fastify) {
});
const roleCodes = (employee?.roles ?? []).map((r) => r.role_code);
const profileCompleteness = buildProfileCompleteness(employee);
return sendData(reply, request, {
id: employee?.id ?? request.employee.id,
employeeNo: employee?.employee_no ?? "",
@@ -64,6 +87,7 @@ async function meRoutes(fastify) {
status: employee?.status ?? "active",
notificationPushEnabled: employee?.notification_push_enabled ?? true,
roleCodes,
profileCompleteness,
wecomBinding: employee?.wecom_identity_binding ? { externalUserId: employee.wecom_identity_binding.external_user_id, createdAt: employee.wecom_identity_binding.created_at?.toISOString?.() ?? null } : null,
});
},
+42
查看文件
@@ -14,6 +14,12 @@ function createPrismaState(overrides = {}) {
department_id: 'department-1',
direct_manager_id: 'manager-1',
status: 'active',
emergency_contact_name: null,
emergency_contact_phone: null,
bank_account_encrypted: '',
bank_name: '',
avatar_path: null,
id_number_encrypted: null,
notification_push_enabled: true,
roles: [{ role_code: 'employee' }],
wecom_identity_binding: null,
@@ -107,12 +113,48 @@ describe('me routes', () => {
expect.objectContaining({
id: 'employee-1',
notificationPushEnabled: false,
profileCompleteness: {
hasEmergencyContact: false,
hasBankAccount: false,
hasAvatar: false,
hasIdNumber: false,
score: 0,
},
}),
);
await app.close();
});
it('calculates profileCompleteness score from completed profile fields', async () => {
const { prisma } = createPrismaState({
emergency_contact_name: '联系人',
emergency_contact_phone: '13900000000',
bank_account_encrypted: 'ENC',
bank_name: '招商银行',
avatar_path: null,
id_number_encrypted: null,
});
const app = await buildApp(prisma);
const response = await app.inject({
method: 'GET',
url: '/api/me',
headers: { authorization: 'Bearer test-token' },
});
expect(response.statusCode).toBe(200);
expect(response.json().data.profileCompleteness).toEqual({
hasEmergencyContact: true,
hasBankAccount: true,
hasAvatar: false,
hasIdNumber: false,
score: 50,
});
await app.close();
});
it('updates notification preferences for current employee', async () => {
const { prisma, employeeRecord, auditEntries } = createPrismaState();
const app = await buildApp(prisma);