v4.0.17企业微信API插件优化

This commit is contained in:
2026-09-06 09:36:49 +08:00
parent 0e711be6c6
commit 192a401227
36 changed files with 2416 additions and 103 deletions
+43
View File
@@ -3,7 +3,10 @@ import { maskContactPhone } from './phone';
import {
clampWecomPluginPageSize,
isWecomPluginEnabled,
matchWecomPluginByApiKey,
parseWecomPluginDateRange,
parseWecomPluginMetricsKind,
toWecomPluginMetricsView,
toWecomPluginUserView,
verifyWecomPluginApiKey,
wecomPluginMetricsPeriod,
@@ -23,6 +26,17 @@ describe('verifyWecomPluginApiKey', () => {
});
});
describe('matchWecomPluginByApiKey', () => {
it('returns the matching enabled instance', () => {
const rows = [
{ id: '1', apiKey: 'alpha-key-aaaa' },
{ id: '2', apiKey: 'beta-key-bbbbb' },
];
expect(matchWecomPluginByApiKey('beta-key-bbbbb', rows)?.id).toBe('2');
expect(matchWecomPluginByApiKey('missing', rows)).toBeNull();
});
});
describe('isWecomPluginEnabled', () => {
it('requires both the switch and a non-empty key', () => {
expect(isWecomPluginEnabled({ WECOM_PLUGIN_ENABLED: 'true', WECOM_PLUGIN_API_KEY: 'k' })).toBe(
@@ -52,6 +66,35 @@ describe('clampWecomPluginPageSize / parseWecomPluginMetricsKind', () => {
});
});
describe('parseWecomPluginDateRange / toWecomPluginMetricsView', () => {
it('parses YYYY-MM-DD range with inclusive end day', () => {
const range = parseWecomPluginDateRange('2026-09-01', '2026-09-03');
expect(range?.gte?.toISOString()).toBe(new Date('2026-09-01T00:00:00+08:00').toISOString());
expect(range?.lt?.toISOString()).toBe(new Date('2026-09-04T00:00:00+08:00').toISOString());
});
it('exposes newStores alias on metrics stats', () => {
const view = toWecomPluginMetricsView({
usersTotal: 1,
usersIncrement: 0,
partnersTotal: 1,
partnersIncrement: 0,
storesTotal: 10,
storesIncrement: 2,
ordersTotal: 0,
ordersIncrement: 0,
orderAmountTotal: 0,
orderAmountIncrement: 0,
redeemsTotal: 0,
redeemsIncrement: 0,
redeemAmountTotal: 0,
redeemAmountIncrement: 0,
});
expect(view.newStores).toBe(2);
expect(view.storesIncrement).toBe(2);
});
});
describe('toWecomPluginUserView', () => {
it('masks phone and fills empty nickname', () => {
expect(toWecomPluginUserView({ userNo: 'DK1', nickname: null, phone: '13800138000' })).toEqual({
+60 -2
View File
@@ -1,6 +1,11 @@
import { maskContactPhone } from './phone';
import { shanghaiYmd, startOfShanghaiDay } from './shanghai-date';
import { wecomReportPeriod, type WecomReportKind, type WecomReportPeriod } from './wecom-report';
import { addShanghaiDays, shanghaiYmd, startOfShanghaiDay } from './shanghai-date';
import {
wecomReportPeriod,
type WecomReportKind,
type WecomReportPeriod,
type WecomReportStats,
} from './wecom-report';
export const WECOM_PLUGIN_PAGE_SIZE_DEFAULT = 5;
export const WECOM_PLUGIN_PAGE_SIZE_MAX = 10;
@@ -53,6 +58,20 @@ export function isWecomPluginEnabled(env: {
return env.WECOM_PLUGIN_ENABLED === 'true' && Boolean(String(env.WECOM_PLUGIN_API_KEY ?? '').trim());
}
/** 在少量启用实例中做常量时间匹配(长度不等仍会先失败) */
export function matchWecomPluginByApiKey<T extends { apiKey: string }>(
provided: string,
candidates: T[],
): T | null {
const got = String(provided ?? '').trim();
if (!got) return null;
let hit: T | null = null;
for (const row of candidates) {
if (verifyWecomPluginApiKey(got, row.apiKey)) hit = row;
}
return hit;
}
export function wecomPluginMetricsPeriod(
kind: WecomPluginMetricsKind,
now = new Date(),
@@ -74,6 +93,45 @@ export function wecomPluginMetricsPeriod(
return { ...period, kind };
}
export type WecomPluginDateRange = { gte?: Date; lt?: Date };
/** 插件时间筛选:from/to 支持 YYYY-MM-DD 或 ISO;日期含当天全天 */
export function parseWecomPluginDateRange(
fromRaw?: string | null,
toRaw?: string | null,
): WecomPluginDateRange | undefined {
const from = parseWecomPluginDate(fromRaw);
const to = parseWecomPluginDate(toRaw);
if (!from && !to) return undefined;
const range: WecomPluginDateRange = {};
if (from) range.gte = from;
if (to) {
range.lt = isWecomPluginDateOnly(toRaw) ? addShanghaiDays(startOfShanghaiDay(to), 1) : to;
}
return range;
}
function isWecomPluginDateOnly(raw?: string | null): boolean {
return /^\d{4}-\d{2}-\d{2}$/.test(String(raw ?? '').trim());
}
function parseWecomPluginDate(raw?: string | null): Date | null {
const v = String(raw ?? '').trim();
if (!v) return null;
if (/^\d{4}-\d{2}-\d{2}$/.test(v)) {
return startOfShanghaiDay(new Date(`${v}T00:00:00+08:00`));
}
const d = new Date(v);
return Number.isFinite(d.getTime()) ? d : null;
}
export function toWecomPluginMetricsView(stats: WecomReportStats) {
return {
...stats,
newStores: stats.storesIncrement,
};
}
export function toWecomPluginUserView(user: {
userNo: string;
nickname?: string | null;