feat(wechat): add Portal worker for daily news morning draft auto push

Run scheduled page generation before push time and write WeChat drafts from admin config so news morning reports no longer require manual pushes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-11 08:48:02 +08:00
parent 93aa35515e
commit 62caf4134b
9 changed files with 607 additions and 18 deletions
@@ -10,6 +10,8 @@ import { startScheduleReminderWorker } from '../schedule-reminder-worker.mjs';
import { startScheduledTaskWorker } from '../scheduled-task-worker.mjs';
import { isScheduledTaskWorkerEnabled } from '../scheduled-task-worker-config.mjs';
import { startHealthBaselineWorker } from '../health-baseline-worker.mjs';
import { startWechatNewsMorningDraftWorker } from '../wechat-news-morning-draft-worker.mjs';
import { createWechatNewsMorningDraftService } from '../wechat-news-morning-draft.mjs';
import { createHealthEventNotificationService } from '../health-event-notification-service.mjs';
import { isPassiveCanaryRuntime } from './portal-runtime-role.mjs';
import { loadWechatMpModule } from '../wechat-mp-loader.mjs';
@@ -72,6 +74,10 @@ export async function bootstrapPortalIntegrationServices({
startScheduledTaskWorker,
startHealthBaselineWorkerFn =
startHealthBaselineWorker,
startWechatNewsMorningDraftWorkerFn =
startWechatNewsMorningDraftWorker,
createWechatNewsMorningDraftServiceFn =
createWechatNewsMorningDraftService,
createPageEditSessionServiceFn =
createPageEditSessionService,
createToolGatewayFn = createToolGateway,
@@ -337,6 +343,38 @@ export async function bootstrapPortalIntegrationServices({
);
}
const wechatNewsMorningDraftService = createWechatNewsMorningDraftServiceFn(pool, {
mpConfig: wechatMpConfig,
h5Root,
memindLibRoot: codeRoot || h5Root,
env,
});
let wechatNewsMorningDraftWorker = null;
if (
!isPassiveCanaryRuntime(env)
&& wechatMpConfig?.enabled
&& userAuth
&& tkmindProxy
) {
wechatNewsMorningDraftWorker = startWechatNewsMorningDraftWorkerFn({
wechatNewsMorningDraftService,
mpConfig: wechatMpConfig,
userAuth,
tkmindProxy,
agentRunGateway,
cursorExecutorPolicyService: wechatCursorExecutorPolicyService,
sessionSnapshotService,
pool,
h5Root,
env,
logger,
});
if (wechatNewsMorningDraftWorker?.runOnce) {
logger.log?.('WeChat news morning draft worker enabled');
}
}
let healthBaselineWorker = null;
if (!isPassiveCanaryRuntime(env) && healthDataRuntime) {
const healthEventNotificationService = createHealthEventNotificationService({
@@ -414,6 +452,8 @@ export async function bootstrapPortalIntegrationServices({
notificationDispatcher,
scheduleReminderWorker,
scheduledTaskWorker,
wechatNewsMorningDraftService,
wechatNewsMorningDraftWorker,
healthBaselineWorker,
subscriptionExpiryTimer,
mindSpacePageEditSession,
@@ -173,6 +173,14 @@ function createSetup(overrides = {}) {
reminderOptions = receivedOptions;
return { id: 'reminder-worker' };
},
createWechatNewsMorningDraftServiceFn(pool, options) {
calls.push(['news-morning-draft-service', pool, options]);
return { id: 'news-morning-draft-service' };
},
startWechatNewsMorningDraftWorkerFn(receivedOptions) {
calls.push(['news-morning-draft-worker']);
return { id: 'news-morning-draft-worker', runOnce: async () => {} };
},
createPageEditSessionServiceFn(receivedOptions) {
calls.push(['page-edit']);
pageEditOptions = receivedOptions;
@@ -449,6 +457,9 @@ test('preserves notification, recharge, reminder, and page-edit wiring', async (
setup.notificationDispatcher,
});
assert.equal(result.scheduleReminderWorker.id, 'reminder-worker');
assert.equal(result.wechatNewsMorningDraftWorker.id, 'news-morning-draft-worker');
assert.ok(setup.calls.some(([name]) => name === 'news-morning-draft-service'));
assert.ok(setup.calls.some(([name]) => name === 'news-morning-draft-worker'));
assert.deepEqual(captured.pageEditOptions, {
apiTarget: 'http://api',
apiSecret: 'secret',
@@ -498,6 +509,7 @@ test('preserves subscription timer work, logs, and unref', async () => {
test('keeps optional integrations disabled and contains timer failures', async () => {
const setup = createSetup({
env: {},
wechatMpConfig: { enabled: false },
mindSpacePublicFinish: null,
scheduleService: null,
subscriptionService: {
@@ -546,6 +558,7 @@ test('keeps optional integrations disabled and contains timer failures', async (
null,
);
assert.equal(result.scheduleReminderWorker, null);
assert.equal(result.wechatNewsMorningDraftWorker, null);
assert.equal(
captured.notificationOptions.sendWechatTextToUser,
null,
@@ -575,6 +588,7 @@ test('passive candidate runtime disables singleton reminder and subscription tim
const captured = setup.getCaptured();
assert.equal(result.scheduleReminderWorker, null);
assert.equal(result.wechatNewsMorningDraftWorker, null);
assert.equal(result.subscriptionExpiryTimer, null);
assert.equal(captured.reminderOptions, undefined);
assert.equal(captured.timerCallback, undefined);