Add smart ACK provider for WeChat MP replies
Replace fixed ackText with a rule-based AckProvider that picks response templates by message type and intent (translate, summary, rewrite, poster, ppt, mindmap, code, search, schedule). Pure sync, zero I/O, auto-falls back to config.ackText on any error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { createWechatAdminService } from './wechat-admin.mjs';
|
||||
|
||||
test('wechat admin service lists and manages balance alerts', async () => {
|
||||
const queries = [];
|
||||
const pool = {
|
||||
async query(sql, params = []) {
|
||||
queries.push(sql);
|
||||
if (sql.includes('FROM h5_balance_alert_subscriptions s')) {
|
||||
return [[{ id: 'alert-1', user_id: 'user-1', username: 'alice', display_name: 'Alice', threshold_cents: 2000, channel: 'wechat', status: 'active', next_run_at: 1, last_run_at: null, last_notified_balance_cents: null, attempts: 0, last_error: null, source_text: '余额低于 20 元提醒我', created_at: 1, updated_at: 1 }]];
|
||||
}
|
||||
if (sql.includes('INSERT INTO h5_balance_alert_subscriptions')) {
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
if (sql.includes("UPDATE h5_balance_alert_subscriptions SET status = 'cancelled'")) {
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
if (sql.includes("UPDATE h5_balance_alert_subscriptions SET status = 'active'")) {
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
return [[]];
|
||||
},
|
||||
};
|
||||
|
||||
const service = createWechatAdminService(pool, { config: { enabled: true } });
|
||||
const listed = await service.listBalanceAlerts();
|
||||
assert.equal(listed.balanceAlerts.length, 1);
|
||||
const created = await service.createBalanceAlert({ userId: 'user-1', thresholdCents: 2000, sourceText: '余额低于 20 元提醒我' });
|
||||
assert.equal(created.ok, true);
|
||||
assert.ok(queries.some((sql) => sql.includes('h5_balance_alert_subscriptions')));
|
||||
});
|
||||
|
||||
test('wechat admin service creates multi-channel notification for all users', async () => {
|
||||
const queries = [];
|
||||
const wechatSent = [];
|
||||
const pool = {
|
||||
async query(sql) {
|
||||
queries.push(sql);
|
||||
if (sql.includes('SELECT id') && sql.includes('FROM h5_users')) {
|
||||
return [[{ id: 'user-1' }, { id: 'user-2' }], []];
|
||||
}
|
||||
if (sql.includes('INSERT INTO h5_user_notifications')) {
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
if (sql.includes('FROM h5_user_notifications n')) {
|
||||
return [[{ id: 'n1', user_id: 'user-1', username: 'john', display_name: 'John', channel: 'web', notification_type: 'manual', title: '测试通知', body: '这是一条网页端测试消息', status: 'unread', read_at: null, created_at: 1, updated_at: 1 }], []];
|
||||
}
|
||||
return [[], []];
|
||||
},
|
||||
};
|
||||
|
||||
const service = createWechatAdminService(pool, {
|
||||
config: { enabled: true },
|
||||
sendWechatTextToUser: async (userId, text) => {
|
||||
wechatSent.push({ userId, text });
|
||||
},
|
||||
});
|
||||
const created = await service.createWebNotification({
|
||||
audience: 'all',
|
||||
channels: ['web', 'wechat'],
|
||||
notificationType: 'manual',
|
||||
title: '测试通知',
|
||||
body: '这是一条网页端测试消息',
|
||||
});
|
||||
assert.equal(created.ok, true);
|
||||
assert.equal(created.created, 2);
|
||||
assert.equal(created.wechatSent, 2);
|
||||
assert.equal(created.targets, 2);
|
||||
assert.equal(wechatSent.length, 2);
|
||||
|
||||
const listed = await service.listWebNotifications({ status: 'unread', limit: 10 });
|
||||
assert.equal(listed.notifications.length, 1);
|
||||
assert.equal(listed.notifications[0].title, '测试通知');
|
||||
assert.ok(queries.some((sql) => sql.includes('h5_user_notifications')));
|
||||
});
|
||||
Reference in New Issue
Block a user