Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
345 lines
14 KiB
Python
345 lines
14 KiB
Python
# -*- 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()
|