feat(wechat): add Intent Transaction Layer with unified task schema
Introduce Draft → Confirm → Commit flow for WeChat schedule intents behind feature flags, plus h5_tasks dual-write/read aggregation and rollout scripts so reminders and automations get explicit user confirmation before persisting. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { resolveScheduleTimestamp } from './schedule-time.mjs';
|
||||
import { buildScheduledTaskCreatePayload } from './scheduled-task-intent.mjs';
|
||||
import { isUnifiedTasksEnabled } from './task-unified-config.mjs';
|
||||
|
||||
async function maybeSyncUnifiedTask({
|
||||
taskUnifiedService,
|
||||
userId,
|
||||
kind,
|
||||
committed,
|
||||
env,
|
||||
}) {
|
||||
if (!isUnifiedTasksEnabled(env) || !taskUnifiedService) return null;
|
||||
try {
|
||||
return await taskUnifiedService.syncFromCommit({ userId, kind, committed });
|
||||
} catch (err) {
|
||||
console.warn?.(
|
||||
'[intent-transaction-commit] unified task sync failed open:',
|
||||
err instanceof Error ? err.message : err,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function commitIntentDraft({
|
||||
draft,
|
||||
userId,
|
||||
scheduleService,
|
||||
scheduledTaskService,
|
||||
taskUnifiedService = null,
|
||||
timezone = 'Asia/Shanghai',
|
||||
env = process.env,
|
||||
}) {
|
||||
if (!draft || draft.status !== 'draft') throw new Error('草稿不可提交');
|
||||
const payload = draft.payload ?? {};
|
||||
const kind = draft.draftType;
|
||||
|
||||
if (kind === 'timed_reminder') {
|
||||
if (!scheduleService) throw new Error('scheduleService 不可用');
|
||||
const remindAt = resolveScheduleTimestamp({
|
||||
localString: payload.remindLocal,
|
||||
timezone,
|
||||
fieldName: '提醒时间',
|
||||
});
|
||||
const item = await scheduleService.createItem({
|
||||
userId,
|
||||
kind: 'event',
|
||||
title: payload.title ?? draft.title,
|
||||
startAt: remindAt,
|
||||
timezone,
|
||||
sourceChannel: payload.sourceChannel ?? 'wechat',
|
||||
sourceMessageId: payload.sourceMessageId ?? draft.sourceMessageId,
|
||||
sourceText: payload.sourceText ?? draft.sourceText,
|
||||
metadata: { source: 'intent_transaction_layer' },
|
||||
});
|
||||
const reminder = await scheduleService.createReminder({
|
||||
userId,
|
||||
itemId: item.id,
|
||||
remindAt,
|
||||
channel: 'wechat',
|
||||
});
|
||||
const committed = { kind, item, reminder };
|
||||
await maybeSyncUnifiedTask({ taskUnifiedService, userId, kind, committed, env });
|
||||
return committed;
|
||||
}
|
||||
|
||||
if (kind === 'create_todo') {
|
||||
if (!scheduleService) throw new Error('scheduleService 不可用');
|
||||
const item = await scheduleService.createItem({
|
||||
userId,
|
||||
kind: 'task',
|
||||
title: payload.title ?? draft.title,
|
||||
timezone,
|
||||
sourceChannel: payload.sourceChannel ?? 'wechat',
|
||||
sourceMessageId: payload.sourceMessageId ?? draft.sourceMessageId,
|
||||
sourceText: payload.sourceText ?? draft.sourceText,
|
||||
metadata: { source: 'intent_transaction_layer' },
|
||||
});
|
||||
const committed = { kind, item };
|
||||
await maybeSyncUnifiedTask({ taskUnifiedService, userId, kind, committed, env });
|
||||
return committed;
|
||||
}
|
||||
|
||||
if (kind === 'create_daily_todo_digest') {
|
||||
if (!scheduleService) throw new Error('scheduleService 不可用');
|
||||
const subscription = await scheduleService.createDailyTodoDigest({
|
||||
userId,
|
||||
hour: payload.hour,
|
||||
minute: payload.minute ?? 0,
|
||||
timezone,
|
||||
channel: 'wechat',
|
||||
sourceChannel: payload.sourceChannel ?? 'wechat',
|
||||
sourceMessageId: payload.sourceMessageId ?? draft.sourceMessageId,
|
||||
sourceText: payload.sourceText ?? draft.sourceText,
|
||||
});
|
||||
const committed = { kind, subscription };
|
||||
await maybeSyncUnifiedTask({ taskUnifiedService, userId, kind, committed, env });
|
||||
return committed;
|
||||
}
|
||||
|
||||
if (kind === 'create_balance_alert') {
|
||||
if (!scheduleService) throw new Error('scheduleService 不可用');
|
||||
const subscription = await scheduleService.createBalanceLowAlert({
|
||||
userId,
|
||||
thresholdCents: payload.thresholdCents,
|
||||
channel: 'wechat',
|
||||
sourceChannel: payload.sourceChannel ?? 'wechat',
|
||||
sourceMessageId: payload.sourceMessageId ?? draft.sourceMessageId,
|
||||
sourceText: payload.sourceText ?? draft.sourceText,
|
||||
});
|
||||
const committed = { kind, subscription };
|
||||
await maybeSyncUnifiedTask({ taskUnifiedService, userId, kind, committed, env });
|
||||
return committed;
|
||||
}
|
||||
|
||||
if (kind === 'scheduled_task') {
|
||||
if (!scheduledTaskService) throw new Error('scheduledTaskService 不可用');
|
||||
const createPayload = payload.createPayload ?? buildScheduledTaskCreatePayload(payload.intentDetail ?? payload, {
|
||||
userId,
|
||||
sourceChannel: 'wechat',
|
||||
sourceMessageId: payload.sourceMessageId ?? draft.sourceMessageId,
|
||||
sourceText: payload.sourceText ?? draft.sourceText,
|
||||
timezone,
|
||||
});
|
||||
const task = await scheduledTaskService.createTask(createPayload);
|
||||
const committed = { kind, task };
|
||||
await maybeSyncUnifiedTask({ taskUnifiedService, userId, kind, committed, env });
|
||||
return committed;
|
||||
}
|
||||
|
||||
throw new Error(`不支持的草稿类型:${kind}`);
|
||||
}
|
||||
Reference in New Issue
Block a user