737efb89a3
支付/核销补 HQ 备注且手机号不脱敏;结算通知按城市、合伙人、笔数和累计金额分块推送。 Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
4.8 KiB
TypeScript
158 lines
4.8 KiB
TypeScript
/** 企微结算账单正文:分字段块 + 字节上限(机器人 markdown 约 4096 字节) */
|
||
|
||
export type WecomBankLike = {
|
||
bankAccountName?: string | null;
|
||
bankName?: string | null;
|
||
bankBranch?: string | null;
|
||
bankAccountNo?: string | null;
|
||
};
|
||
|
||
export function formatYuan(n: number): string {
|
||
return `¥${Number(n).toFixed(2)}`;
|
||
}
|
||
|
||
export function formatPercent(rate: number): string {
|
||
const p = Number(rate) * 100;
|
||
if (!Number.isFinite(p)) return '—';
|
||
const rounded = Math.round(p * 100) / 100;
|
||
const text = Number.isInteger(rounded) ? String(rounded) : String(rounded);
|
||
return `${text}%`;
|
||
}
|
||
|
||
export function formatPartnerDashLabel(
|
||
company?: string | null,
|
||
name?: string | null,
|
||
): string {
|
||
const person = (name || '').trim() || '—';
|
||
const org = (company || '').trim();
|
||
return org ? `${org}-${person}` : person;
|
||
}
|
||
|
||
export function wecomBankParts(bank?: WecomBankLike | null): {
|
||
payee: string;
|
||
accountNo: string;
|
||
bankBranch: string;
|
||
} {
|
||
if (!bank) return { payee: '未登记', accountNo: '—', bankBranch: '—' };
|
||
const payee = bank.bankAccountName?.trim() || '';
|
||
const accountNo = bank.bankAccountNo?.trim() || '';
|
||
const bankBranch = bank.bankName?.trim() || bank.bankBranch?.trim() || '';
|
||
if (!payee && !accountNo) {
|
||
return { payee: '未登记', accountNo: '—', bankBranch: bankBranch || '—' };
|
||
}
|
||
return {
|
||
payee: payee || '—',
|
||
accountNo: accountNo || '—',
|
||
bankBranch: bankBranch || '—',
|
||
};
|
||
}
|
||
|
||
export function formatWecomFieldBlock(rows: Array<[string, string]>): string {
|
||
return rows.map(([k, v]) => `${k}:${v}`).join('\n');
|
||
}
|
||
|
||
export function joinLimited(names: string[], unit: string, max = 8): string {
|
||
const uniq = [...new Set(names.map((n) => n.trim()).filter(Boolean))];
|
||
if (!uniq.length) return '—';
|
||
if (uniq.length <= max) return uniq.join('、');
|
||
return `${uniq.slice(0, max).join('、')} 等${uniq.length}${unit}`;
|
||
}
|
||
|
||
const BILL_DIGEST_MAX_BYTES = 3200;
|
||
|
||
export function capBillBlocks(blocks: string[]): string {
|
||
if (!blocks.length) return '无';
|
||
const out: string[] = [];
|
||
let bytes = 0;
|
||
let shown = 0;
|
||
for (const block of blocks) {
|
||
const extra = (shown ? 2 : 0) + Buffer.byteLength(block, 'utf8');
|
||
if (shown > 0 && bytes + extra > BILL_DIGEST_MAX_BYTES) {
|
||
out.push(`另有 ${blocks.length - shown} 笔未列出,请到 HQ 查看`);
|
||
break;
|
||
}
|
||
out.push(block);
|
||
bytes += extra;
|
||
shown += 1;
|
||
}
|
||
return out.join('\n\n');
|
||
}
|
||
|
||
export function formatStoreBillWecomBlock(row: {
|
||
cityName: string;
|
||
storeName: string;
|
||
partnerLabel: string;
|
||
redeemCount: number;
|
||
redeemAmount: number;
|
||
settlementRate: number;
|
||
amount: number;
|
||
bank?: WecomBankLike | null;
|
||
}): string {
|
||
const bank = wecomBankParts(row.bank);
|
||
return formatWecomFieldBlock([
|
||
['城市', row.cityName || '—'],
|
||
['门店', row.storeName || '—'],
|
||
['合伙人', row.partnerLabel || '—'],
|
||
['核销笔数', String(row.redeemCount)],
|
||
['核销金额', formatYuan(row.redeemAmount)],
|
||
['结算比例', formatPercent(row.settlementRate)],
|
||
['账单', formatYuan(row.amount)],
|
||
['收款人', bank.payee],
|
||
['收款账户', bank.accountNo],
|
||
['收款开户行', bank.bankBranch],
|
||
]);
|
||
}
|
||
|
||
export function formatPartnerBillWecomBlock(row: {
|
||
cityName: string;
|
||
partnerLabel: string;
|
||
orderCount?: number;
|
||
redeemCount?: number;
|
||
orderCommission: number;
|
||
redeemCommission: number;
|
||
amount: number;
|
||
bank?: WecomBankLike | null;
|
||
}): string {
|
||
const bank = wecomBankParts(row.bank);
|
||
const rows: Array<[string, string]> = [
|
||
['城市', row.cityName || '—'],
|
||
['合伙人', row.partnerLabel || '—'],
|
||
];
|
||
if (row.orderCount != null) rows.push(['订单笔数', String(row.orderCount)]);
|
||
if (row.redeemCount != null) rows.push(['核销笔数', String(row.redeemCount)]);
|
||
rows.push(
|
||
['订单佣金', formatYuan(row.orderCommission)],
|
||
['核销佣金', formatYuan(row.redeemCommission)],
|
||
['账单', formatYuan(row.amount)],
|
||
['收款人', bank.payee],
|
||
['收款账户', bank.accountNo],
|
||
['收款开户行', bank.bankBranch],
|
||
);
|
||
return formatWecomFieldBlock(rows);
|
||
}
|
||
|
||
export function formatLogisticsBillWecomBlock(row: {
|
||
cityNames: string;
|
||
partnerNames: string;
|
||
providerName: string;
|
||
settlementMethod: string;
|
||
orderCount: number;
|
||
bottleCount: number;
|
||
amount: number;
|
||
bank?: WecomBankLike | null;
|
||
}): string {
|
||
const bank = wecomBankParts(row.bank);
|
||
return formatWecomFieldBlock([
|
||
['城市', row.cityNames || '—'],
|
||
['合伙人', row.partnerNames || '—'],
|
||
['承运商', row.providerName || '—'],
|
||
['结算方式', row.settlementMethod || '—'],
|
||
['发货笔数', String(row.orderCount)],
|
||
['瓶数', String(row.bottleCount)],
|
||
['账单', formatYuan(row.amount)],
|
||
['收款人', bank.payee],
|
||
['收款账户', bank.accountNo],
|
||
['收款开户行', bank.bankBranch],
|
||
]);
|
||
}
|