# -*- coding: utf-8 -*- """Generate 客户分级与黑名单规则.docx with tables and basic typography.""" from pathlib import Path from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml import OxmlElement from docx.oxml.ns import qn from docx.shared import Inches, Pt, RGBColor def set_cell_shading(cell, fill: str) -> None: """fill: hex without # e.g. EAEAEA""" tc = cell._tc tcPr = tc.get_or_add_tcPr() shd = OxmlElement("w:shd") shd.set(qn("w:fill"), fill) shd.set(qn("w:val"), "clear") tcPr.append(shd) def set_table_borders(table) -> None: tbl = table._tbl tblPr = tbl.tblPr if tblPr is None: tblPr = OxmlElement("w:tblPr") tbl.insert(0, tblPr) borders = OxmlElement("w:tblBorders") for edge in ("top", "left", "bottom", "right", "insideH", "insideV"): el = OxmlElement(f"w:{edge}") el.set(qn("w:val"), "single") el.set(qn("w:sz"), "4") el.set(qn("w:space"), "0") el.set(qn("w:color"), "333333") borders.append(el) tblPr.append(borders) def add_heading_doc(doc, text: str, level: int) -> None: p = doc.add_heading(text, level=level) for run in p.runs: run.font.name = "PingFang SC" run._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") def add_para(doc, text: str, bold: bool = False) -> None: p = doc.add_paragraph() run = p.add_run(text) run.font.name = "PingFang SC" run._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") run.font.size = Pt(10.5) if bold: run.bold = True def add_bullet_list(doc, items: list) -> None: for t in items: p = doc.add_paragraph(style="List Bullet") run = p.add_run(t) run.font.name = "PingFang SC" run._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") run.font.size = Pt(10.5) def add_table(doc, headers: list, rows: list, header_fill: str = "EAEAEA") -> None: table = doc.add_table(rows=1 + len(rows), cols=len(headers)) table.style = "Table Grid" set_table_borders(table) hdr_cells = table.rows[0].cells for i, h in enumerate(headers): hdr_cells[i].text = h set_cell_shading(hdr_cells[i], header_fill) for p in hdr_cells[i].paragraphs: p.alignment = WD_ALIGN_PARAGRAPH.LEFT for r in p.runs: r.bold = True r.font.size = Pt(9.5) r.font.name = "PingFang SC" r._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") for ri, row in enumerate(rows): cells = table.rows[ri + 1].cells for ci, val in enumerate(row): cells[ci].text = str(val) for p in cells[ci].paragraphs: for r in p.runs: r.font.size = Pt(9.5) r.font.name = "PingFang SC" r._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") doc.add_paragraph() def main() -> None: out = Path.home() / "Desktop" / "客户分级与黑名单规则.docx" doc = Document() section = doc.sections[0] section.page_height = Inches(11.69) section.page_width = Inches(8.27) section.left_margin = Inches(0.75) section.right_margin = Inches(0.75) section.top_margin = Inches(0.85) section.bottom_margin = Inches(0.85) t = doc.add_paragraph() t.alignment = WD_ALIGN_PARAGRAPH.CENTER r = t.add_run("客户分级(ABC)、风险标签与黑名单规则") r.bold = True r.font.size = Pt(17) r.font.name = "PingFang SC" r._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") st = doc.add_paragraph() st.alignment = WD_ALIGN_PARAGRAPH.CENTER rr = st.add_run("版本:草案 · 适用于 ToB 合同、收款与开票场景(可按业务微调阈值)") rr.font.size = Pt(9.5) rr.font.color.rgb = RGBColor(0x55, 0x55, 0x55) rr.font.name = "PingFang SC" rr._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") doc.add_paragraph() add_heading_doc(doc, "一、客户分级(ABC)", level=1) add_heading_doc(doc, "1. 分级目的", level=2) add_bullet_list( doc, [ "资源投入:授信、账期、折扣、专属服务与响应时效。", "风控:A 类可放宽流程,C 类收紧合同与收款条款。", "统计:报表、提成、续约策略分层。", ], ) add_heading_doc(doc, "2. 维度与计分(建议每年或每季复核)", level=2) add_table( doc, ["维度", "说明", "权重示例"], [ ["贡献度", "近 12 个月含税回款、毛利、合同金额", "35%"], ["履约与回款", "逾期次数、最长逾期天数、DSO", "25%"], ["合作稳定性", "合作月数、续约、纠纷/索赔次数", "20%"], ["战略价值", "品牌、标杆案例、增量潜力、独家合作", "20%"], ], ) add_para(doc, "可将各维度标准化为 0–100 分,加权得综合分,再划档;或直接按「硬性门槛 + 一票否决」定级。") add_heading_doc(doc, "3. 等级定义与标准(示例)", level=2) add_table( doc, ["等级", "综合分(示例)", "典型特征"], [ ["A", "≥ 80", "回款好、贡献高或战略客户;近 12 个月无严重逾期、无未结重大纠纷"], ["B", "50–79", "正常合作;偶有短逾期已结清;贡献中等"], ["C", "< 50", "新客观察期、小额长尾、或回款/纠纷问题较多"], ], ) add_heading_doc(doc, "4. 硬性调整(建议写死规则)", level=2) add_bullet_list( doc, [ "近 12 个月出现单次逾期 > 60 天或累计逾期 ≥ 3 次:最高不超过 B。", "存在未结法律诉讼或重大合规调查:暂定为 C,直至结案。", "新签约 90 天内且无足够交易数据:默认 C 或 B(观察),标签单独标「新客观察」。", ], ) add_heading_doc(doc, "5. 分级权益与约束(示例)", level=2) add_table( doc, ["项目", "A", "B", "C"], [ ["标准账期", "可按合同约定略优", "标准", "缩短或预付比例提高"], ["授信额度", "可申请较高额度", "中等", "低或零,款到发货"], ["价格/折扣", "可申请专项政策", "标准政策", "原则上无额外折扣"], ["合同审批", "可简化部分条款", "标准", "法务/财务加签"], ["服务响应", "优先 SLA", "标准", "标准(不承诺加急)"], ], ) add_heading_doc(doc, "二、风险标签", level=1) add_heading_doc(doc, "1. 设计原则", level=2) add_bullet_list( doc, [ "标签≠等级:同一客户可有多个标签;黑名单与部分标签可联动自动拦截。", "来源:系统规则自动打标 + 人工标注;重要标签变更留痕。", "有效期:部分标签可设过期时间(如「短期资金链紧张」6 个月复核)。", ], ) add_para(doc, "建议字段:标签代码、名称、级别、来源、备注、生效时间。") add_heading_doc(doc, "2. 信用与回款", level=2) add_table( doc, ["标签代码", "名称", "级别", "典型触发"], [ ["CR-01", "逾期预警", "中", "当前存在逾期或近 6 个月有逾期"], ["CR-02", "严重拖欠", "高", "逾期 > 30 天未结或历史坏账记录"], ["CR-03", "频繁改账期", "低", "一年内申请延长账期 ≥ 3 次"], ], ) add_heading_doc(doc, "3. 合规与法律", level=2) add_table( doc, ["标签代码", "名称", "级别", "典型触发"], [ ["CP-01", "资质存疑", "中", "证照过期、抬头与付款主体不一致"], ["CP-02", "涉诉/仲裁", "高", "与我方或第三方重大未结诉讼"], ["CP-03", "制裁/高风险地区", "高", "名单筛查命中(按公司合规清单)"], ], ) add_heading_doc(doc, "4. 经营与履约", level=2) add_table( doc, ["标签代码", "名称", "级别", "典型触发"], [ ["OP-01", "频繁争议", "中", "合同争议、扣款争议次数超阈值"], ["OP-02", "车辆/资产高风险", "中–高", "违章、事故、骗保嫌疑等业务规则(按公司业务定义)"], ["OP-03", "信息不实", "中", "虚假联系人、空头公司等核实结果"], ], ) add_heading_doc(doc, "5. 操作与流程", level=2) add_table( doc, ["标签代码", "名称", "级别", "典型触发"], [ ["PR-01", "开票异常", "低", "频繁退票、抬头变更异常"], ["PR-02", "多头签约", "低", "同一实控人多主体分散签约"], ], ) add_para(doc, "级别建议:低 / 中 / 高 / 致命(致命仅建议与黑名单联动)。", bold=True) add_heading_doc(doc, "6. 标签与系统行为(示例)", level=2) add_table( doc, ["级别", "建议动作"], [ ["低", "仅展示与报表,不拦截"], ["中", "合同/大额订单增加审批节点;降低默认授信"], ["高", "禁止新增授信;新合同须法务/财务双签;可限制开票额度"], ["致命", "与黑名单规则对齐,禁止新业务或仅允许收尾"], ], ) add_heading_doc(doc, "三、黑名单规则", level=1) add_heading_doc(doc, "1. 黑名单分级(建议)", level=2) add_table( doc, ["级别", "名称", "含义"], [ ["L1", "观察名单", "重点监控,限制部分权限"], ["L2", "业务冻结", "禁止新签合同、新增订单/车辆,允许履约收尾与收款"], ["L3", "全面禁止", "禁止新业务;按法务意见处理尾款与争议"], ], ) add_heading_doc(doc, "2. 进入条件(由风控委员会或授权人最终裁定)", level=2) add_para(doc, "进入 L2(业务冻结)——示例:", bold=True) add_bullet_list( doc, [ "恶意拖欠:应付账款逾期 > 90 天且经书面催收无效。", "欺诈或伪造:伪造资质、虚构交易、骗取发票或补贴等查实。", "重大违约:单方毁约造成损失达约定金额或内部红线。", "司法:被列为失信被执行人,或对我方提起恶意诉讼并造成实质风险。", "合规:命中内外部黑名单库、制裁名单且公司政策要求停止合作。", ], ) add_para(doc, "进入 L3(全面禁止)——示例:", bold=True) add_bullet_list( doc, [ "L2 情形加重且法务/高管批准。", "刑事立案或监管机构明确要求终止合作。", ], ) add_para(doc, "进入 L1(观察名单)——示例:", bold=True) add_bullet_list( doc, [ "连续 2 次逾期 > 15 天。", "存在未结争议金额超过阈值。", "高风险标签(如 CP-02、CR-02)未解除超过 90 天。", ], ) add_heading_doc(doc, "3. 系统与业务规则(落地)", level=2) add_bullet_list( doc, [ "客户主数据:标记黑名单级别、原因代码、生效日、操作人、附件(催收记录、判决书等)。", "合同:L2/L3 禁止新建合同;进行中合同可走「例外审批」仅允许极少数变更(如收款账户)。", "订单/开票:L1 可设单笔限额;L2/L3 禁止新开票或仅允许红冲/收尾类单据(按税务合规)。", "消息:销售、运营界面显著提示,避免误接单。", ], ) add_heading_doc(doc, "4. 移出与复议", level=2) add_bullet_list( doc, [ "L1:满足连续 6 个月无新增逾期、争议结案,由销售发起,财务复核后可移除。", "L2/L3:须还清欠款或达成书面和解 + 法务/风控签字;L3 须高管或委员会批准。", "全程保留审计日志;禁止个人擅自移出。", ], ) add_heading_doc(doc, "5. 与 ABC、标签的联动(建议)", level=2) add_bullet_list( doc, [ "进入 L2/L3 → 客户等级强制为 C(或显示「黑名单-冻结」覆盖原等级)。", "标签 CR-02、CP-02、CP-03 在确认后自动建议进入 L1 或触发 L2 审批流。", "A 类客户进入黑名单须升级审批(防止误伤大客户)。", ], ) add_heading_doc(doc, "四、模块数据模型提示(可选)", level=1) add_bullet_list( doc, [ "客户表:abc_level、blacklist_level、blacklist_reason_code、blacklist_since、abc_reviewed_at。", "客户标签表:多对多,tag_id、source(rule/manual)、severity、expires_at。", "规则引擎表(可选):规则 ID、条件 JSON、动作(打标 / 调级 / 推审批)。", ], ) foot = doc.add_paragraph() rr2 = foot.add_run( "本文档由 ONE-OS 项目辅助生成,正式使用前请经法务、财务与业务负责人审定阈值与审批链。" ) rr2.font.size = Pt(9) rr2.font.color.rgb = RGBColor(0x44, 0x44, 0x44) rr2.font.name = "PingFang SC" rr2._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC") doc.save(out) print(f"Wrote: {out}") if __name__ == "__main__": main()