feat(M10-A): 完成统一通知中心与投递闭环

This commit is contained in:
Codex
2026-08-11 09:17:18 +08:00
parent 41b9cf7349
commit 5d70114bab
46 changed files with 1724 additions and 74 deletions
@@ -0,0 +1,52 @@
import assert from 'node:assert/strict';
import {
createNotificationAdaptersFromEnvironment,
HttpNotificationAdapter,
NotificationError
} from '../dist/notifications/notification-service.js';
const requests = [];
const fetcher = async (url, init) => {
requests.push({ url: String(url), init });
return {
ok: true, status: 200,
headers: { get(name) { return name === 'x-request-id' ? 'provider-request-1' : null; } },
async json() { return { providerMessageId: 'provider-message-1' }; }
};
};
const adapters = createNotificationAdaptersFromEnvironment({
QIPAI_NOTIFICATION_WECHAT_GATEWAY_URL: 'https://gateway.example.test/wechat',
QIPAI_NOTIFICATION_WECHAT_GATEWAY_TOKEN: 'wechat-secret',
QIPAI_NOTIFICATION_WECOM_GATEWAY_URL: 'https://gateway.example.test/wecom',
QIPAI_NOTIFICATION_CLOUD_SPEAKER_GATEWAY_URL: 'https://gateway.example.test/speaker',
QIPAI_NOTIFICATION_HTTP_TIMEOUT_MS: '5000'
}, fetcher);
assert.deepEqual([...adapters.keys()], [
'IN_APP', 'WEBHOOK', 'WECHAT_SUBSCRIBE', 'WE_COM', 'CLOUD_SPEAKER'
]);
const input = { deliveryId: '31', tenantId: '7', channel: 'WECHAT_SUBSCRIBE',
recipientId: '21', title: '提醒', body: '订单已支付', payload: { orderId: '41' },
externalTemplateId: 'template-1' };
assert.deepEqual(await adapters.get('WECHAT_SUBSCRIBE').send(input), {
providerMessageId: 'provider-message-1'
});
assert.equal(requests[0].url, 'https://gateway.example.test/wechat');
assert.equal(requests[0].init.headers.authorization, 'Bearer wechat-secret');
assert.equal(requests[0].init.headers['idempotency-key'], 'notification:31');
assert.equal(JSON.parse(requests[0].init.body).externalTemplateId, 'template-1');
await adapters.get('WEBHOOK').send({ ...input, channel: 'WEBHOOK',
recipientId: 'https://store.example.test/hooks/notify' });
assert.equal(requests[1].url, 'https://store.example.test/hooks/notify');
await assert.rejects(
() => adapters.get('WEBHOOK').send({ ...input, channel: 'WEBHOOK',
recipientId: 'http://127.0.0.1/internal' }),
(error) => error instanceof NotificationError && error.code === 'NOTIFICATION_ENDPOINT_INVALID'
);
const failing = new HttpNotificationAdapter({ endpoint: 'https://gateway.example.test/fail',
fetcher: async () => ({ ok: false, status: 503, headers: { get() { return null; } },
async json() { return {}; } }) });
await assert.rejects(() => failing.send(input),
(error) => error instanceof NotificationError && error.code === 'NOTIFICATION_PROVIDER_HTTP_ERROR');
console.log('PASS: M10-A configured HTTP adapters enforce HTTPS, idempotency and provider failures.');