/** 企微结算账单正文:字段块 / markdown_v2 表格 + 字节上限(机器人约 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'); } function escapeWecomTableCell(v: string): string { const text = String(v ?? '') .replace(/\|/g, '|') .replace(/\r?\n/g, ' ') .trim(); return text || '—'; } /** 企微 markdown_v2 键值表:表头为标题,下列为字段/值 */ export function formatWecomKvTable(header: string, rows: Array<[string, string]>): string { const lines = [`| ${escapeWecomTableCell(header)} | |`, '| :--- | ---: |']; for (const [k, v] of rows) { lines.push(`| ${escapeWecomTableCell(k)} | ${escapeWecomTableCell(v)} |`); } return lines.join('\n'); } export function filterNonZeroWecomBills(rows: T[]): T[] { return rows.filter((r) => Number(r.amount) > 0); } 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: { partnerLabel: string; period: string; orderCount?: number; orderAmount?: number; orderCommission: number; redeemCount?: number; redeemAmount?: number; redeemCommission: number; amount: number; bank?: WecomBankLike | null; }): string { const bank = wecomBankParts(row.bank); return formatWecomKvTable(row.partnerLabel || '—', [ ['账期', row.period || '—'], ['订单数量', String(row.orderCount ?? 0)], ['订单金额', formatYuan(row.orderAmount ?? 0)], ['订单佣金', formatYuan(row.orderCommission)], ['核销单数量', String(row.redeemCount ?? 0)], ['核销金额', formatYuan(row.redeemAmount ?? 0)], ['核销佣金', formatYuan(row.redeemCommission)], ['累计金额', formatYuan(row.amount)], ['收款人', bank.payee], ['收款银行账号', bank.accountNo], ['开户行', bank.bankBranch], ]); } 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], ]); }