R50 通知推送:dispatcher 投递服务、业务事件接入与员工推送开关
这个提交包含在:
@@ -0,0 +1,140 @@
|
||||
const Fastify = require('fastify');
|
||||
|
||||
const meRoutes = require('../../../src/routes/me');
|
||||
const { AppError } = require('../../../src/utils/AppError');
|
||||
|
||||
function createPrismaState(overrides = {}) {
|
||||
const auditEntries = [];
|
||||
const employeeRecord = {
|
||||
id: 'employee-1',
|
||||
employee_no: 'E001',
|
||||
name: '测试员工',
|
||||
phone: '13800000000',
|
||||
email: '[email protected]',
|
||||
department_id: 'department-1',
|
||||
direct_manager_id: 'manager-1',
|
||||
status: 'active',
|
||||
notification_push_enabled: true,
|
||||
roles: [{ role_code: 'employee' }],
|
||||
wecom_identity_binding: null,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
return {
|
||||
prisma: {
|
||||
employee: {
|
||||
findUnique: vi.fn().mockResolvedValue(employeeRecord),
|
||||
update: vi.fn().mockImplementation(async ({ data }) => {
|
||||
employeeRecord.notification_push_enabled = data.notification_push_enabled;
|
||||
return {
|
||||
id: employeeRecord.id,
|
||||
notification_push_enabled: employeeRecord.notification_push_enabled,
|
||||
};
|
||||
}),
|
||||
},
|
||||
auditLog: {
|
||||
create: vi.fn().mockImplementation(async ({ data }) => {
|
||||
auditEntries.push(data);
|
||||
return { id: `audit-${auditEntries.length}` };
|
||||
}),
|
||||
},
|
||||
},
|
||||
employeeRecord,
|
||||
auditEntries,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildApp(prisma) {
|
||||
const app = Fastify();
|
||||
app.decorate('prisma', prisma);
|
||||
app.decorate('authenticate', async (request) => {
|
||||
if (!request.headers.authorization) {
|
||||
throw new AppError({
|
||||
code: 'AUTH_001',
|
||||
message: '未登录',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
|
||||
request.employee = {
|
||||
id: 'employee-1',
|
||||
permissionCodes: [],
|
||||
};
|
||||
});
|
||||
app.setErrorHandler((error, request, reply) => {
|
||||
if (error instanceof AppError) {
|
||||
return reply.status(error.statusCode).send({
|
||||
requestId: request.id,
|
||||
error: {
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
details: error.details ?? {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return reply.status(500).send({
|
||||
requestId: request.id,
|
||||
error: {
|
||||
code: 'SYS_001',
|
||||
message: '服务器内部错误',
|
||||
details: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await app.register(meRoutes, { prefix: '/api' });
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('me routes', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('returns notificationPushEnabled in /me response', async () => {
|
||||
const { prisma } = createPrismaState({ notification_push_enabled: false });
|
||||
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).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'employee-1',
|
||||
notificationPushEnabled: false,
|
||||
}),
|
||||
);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('updates notification preferences for current employee', async () => {
|
||||
const { prisma, employeeRecord, auditEntries } = createPrismaState();
|
||||
const app = await buildApp(prisma);
|
||||
|
||||
const response = await app.inject({
|
||||
method: 'PATCH',
|
||||
url: '/api/me/notification-preferences',
|
||||
headers: { authorization: 'Bearer test-token' },
|
||||
payload: {
|
||||
notificationPushEnabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json().data).toEqual({ notificationPushEnabled: false });
|
||||
expect(employeeRecord.notification_push_enabled).toBe(false);
|
||||
expect(auditEntries[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
action: 'EMPLOYEE_NOTIFICATION_PREFERENCE_UPDATED',
|
||||
}),
|
||||
);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户