Files
dukang/packages/domain/src/wecom-plugin.ts
T

88 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { maskContactPhone } from './phone';
import { shanghaiYmd, startOfShanghaiDay } from './shanghai-date';
import { wecomReportPeriod, type WecomReportKind, type WecomReportPeriod } from './wecom-report';
export const WECOM_PLUGIN_PAGE_SIZE_DEFAULT = 5;
export const WECOM_PLUGIN_PAGE_SIZE_MAX = 10;
export const WECOM_PLUGIN_METRICS_KINDS = ['today', 'daily', 'weekly', 'monthly'] as const;
export type WecomPluginMetricsKind = (typeof WECOM_PLUGIN_METRICS_KINDS)[number];
export type WecomPluginMetricsPeriod = Omit<WecomReportPeriod, 'kind'> & {
kind: WecomPluginMetricsKind;
};
export function isWecomPluginMetricsKind(v: string): v is WecomPluginMetricsKind {
return (WECOM_PLUGIN_METRICS_KINDS as readonly string[]).includes(v);
}
export function parseWecomPluginMetricsKind(raw?: string | null): WecomPluginMetricsKind | null {
const v = String(raw ?? '').trim().toLowerCase();
if (!v) return 'today';
return isWecomPluginMetricsKind(v) ? v : null;
}
export function clampWecomPluginPageSize(raw?: string | number | null): number {
const n = typeof raw === 'number' ? raw : Number(raw);
if (!Number.isFinite(n) || n <= 0) return WECOM_PLUGIN_PAGE_SIZE_DEFAULT;
return Math.min(WECOM_PLUGIN_PAGE_SIZE_MAX, Math.max(1, Math.floor(n)));
}
export function clampWecomPluginPage(raw?: string | number | null): number {
const n = typeof raw === 'number' ? raw : Number(raw);
if (!Number.isFinite(n) || n <= 0) return 1;
return Math.min(100, Math.floor(n));
}
/** 长度必须相同后再逐字符 XOR,避免短密码走快速失败路径时的明显差异(仍非密码学级) */
export function verifyWecomPluginApiKey(provided: string, expected?: string | null): boolean {
const exp = String(expected ?? '');
const got = String(provided ?? '');
if (!exp || !got || exp.length !== got.length) return false;
let diff = 0;
for (let i = 0; i < exp.length; i++) {
diff |= exp.charCodeAt(i) ^ got.charCodeAt(i);
}
return diff === 0;
}
export function isWecomPluginEnabled(env: {
WECOM_PLUGIN_ENABLED?: string;
WECOM_PLUGIN_API_KEY?: string;
}): boolean {
return env.WECOM_PLUGIN_ENABLED === 'true' && Boolean(String(env.WECOM_PLUGIN_API_KEY ?? '').trim());
}
export function wecomPluginMetricsPeriod(
kind: WecomPluginMetricsKind,
now = new Date(),
): WecomPluginMetricsPeriod {
if (kind === 'today') {
const start = startOfShanghaiDay(now);
const ymd = shanghaiYmd(start);
return {
kind,
start,
endExclusive: now,
periodKey: ymd,
title: `今日(${ymd}`,
rangeLabel: ymd,
incrementLabel: '今日新增',
};
}
const period = wecomReportPeriod(kind as WecomReportKind, now);
return { ...period, kind };
}
export function toWecomPluginUserView(user: {
userNo: string;
nickname?: string | null;
phone?: string | null;
}): { userNo: string; nickname: string; phone: string } {
return {
userNo: user.userNo,
nickname: user.nickname?.trim() || '—',
phone: maskContactPhone(user.phone),
};
}