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:
@@ -11,6 +11,7 @@
|
||||
// createOpsApi -> /api/ops/v1/* plaza operations console (ops_role)
|
||||
import express from 'express';
|
||||
import { sendData, sendError } from './api-response.mjs';
|
||||
import { PLAN_CATALOG, tokensToCallsApprox } from './billing-subscription.mjs';
|
||||
import { hasOpsRole } from './plaza-ops.mjs';
|
||||
import { mapPlazaError } from './plaza-posts.mjs';
|
||||
|
||||
@@ -42,6 +43,7 @@ export function createAdminApi({
|
||||
plazaPosts,
|
||||
plazaOps,
|
||||
wechatAdmin,
|
||||
subscriptionService,
|
||||
}) {
|
||||
function requireAdmin(req, res, next) {
|
||||
if (!req.currentUser || req.currentUser.role !== 'admin') {
|
||||
@@ -272,6 +274,44 @@ export function createAdminApi({
|
||||
res.json(await wechatAdmin.listDeliveries(req.query));
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/balance-alerts', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.listBalanceAlerts(req.query));
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/balance-alerts', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.createBalanceAlert(req.body ?? {});
|
||||
if (!result.ok) return res.status(400).json({ message: result.message });
|
||||
res.status(201).json(result);
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/balance-alerts/:id/cancel', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.cancelBalanceAlert(req.params.id);
|
||||
if (!result.ok) return res.status(404).json({ message: result.message ?? '取消失败' });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/balance-alerts/:id/resume', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.resumeBalanceAlert(req.params.id);
|
||||
if (!result.ok) return res.status(404).json({ message: result.message ?? '恢复失败' });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.get('/wechat/web-notifications', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
res.json(await wechatAdmin.listWebNotifications(req.query));
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/web-notifications', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.createWebNotification(req.body ?? {});
|
||||
if (!result.ok) return res.status(400).json({ message: result.message });
|
||||
res.status(201).json(result);
|
||||
});
|
||||
|
||||
adminApi.post('/wechat/users/:userId/route/clear', requireAdmin, async (req, res) => {
|
||||
if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
|
||||
const result = await wechatAdmin.clearRouteForUser(req.params.userId);
|
||||
@@ -407,6 +447,61 @@ export function createAdminApi({
|
||||
}
|
||||
});
|
||||
|
||||
// ── Subscription management ───────────────────────────────────────────────
|
||||
|
||||
adminApi.get('/subscriptions', requireAdmin, async (req, res) => {
|
||||
if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
|
||||
const { userId, status, page, pageSize } = req.query;
|
||||
const result = await subscriptionService.listSubscriptions({ userId, status, page, pageSize });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.get('/subscriptions/plans', requireAdmin, (_req, res) => {
|
||||
res.json(
|
||||
Object.entries(PLAN_CATALOG).map(([planType, def]) => ({
|
||||
planType,
|
||||
...def,
|
||||
approxCalls: tokensToCallsApprox(def.periodTokens),
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
adminApi.get('/users/:userId/subscription', requireAdmin, async (req, res) => {
|
||||
if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
|
||||
const sub = await subscriptionService.getActiveSubscription(req.params.userId);
|
||||
res.json({ subscription: sub });
|
||||
});
|
||||
|
||||
adminApi.post('/users/:userId/subscription', requireAdmin, async (req, res) => {
|
||||
if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
|
||||
const { planType, durationDays, note } = req.body ?? {};
|
||||
if (!planType) return res.status(400).json({ message: '缺少 planType' });
|
||||
const result = await subscriptionService.grantSubscription(
|
||||
req.params.userId,
|
||||
planType,
|
||||
durationDays ? Number(durationDays) : undefined,
|
||||
req.currentUser.id,
|
||||
note,
|
||||
);
|
||||
if (!result.ok) return res.status(400).json({ message: result.message });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.delete('/users/:userId/subscription', requireAdmin, async (req, res) => {
|
||||
if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
|
||||
const result = await subscriptionService.cancelSubscription(
|
||||
req.params.userId,
|
||||
req.currentUser.id,
|
||||
);
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
adminApi.post('/subscriptions/expire-stale', requireAdmin, async (_req, res) => {
|
||||
if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
|
||||
const count = await subscriptionService.expireStaleSubscriptions();
|
||||
res.json({ expiredCount: count });
|
||||
});
|
||||
|
||||
return adminApi;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user