| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- # -*- coding: utf-8 -*-
- """
- 生成交付文档:S9 运营指标与智慧看板、运营诊断、运营改善、系统集成
- (业务需求描述 / 蓝图设计方案 / 测试用例 / 三联对照表)
- 内容对齐:项目合同里的技术规范 2.6 / 2.6.1~2.6.3 / 2.7(数据见 _gen_s9_modules_data.py)。
- 输出根目录:交付文档 OneDrive 路径(与 S1 同级)。
- """
- from __future__ import annotations
- from pathlib import Path
- from docx import Document
- from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
- from docx.oxml import OxmlElement
- from docx.oxml.ns import qn
- from docx.shared import Cm, Pt, RGBColor
- from openpyxl import Workbook, load_workbook
- from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
- from openpyxl.utils import get_column_letter
- from _gen_s9_modules_data import MODULES
- BASE = Path(r"C:\Users\skygu\OneDrive\Projects\AIDOP\项目\项目管理\产互联项目管理\交付文档")
- REPO_DOC = Path(__file__).resolve().parent
- ACCEPTANCE_XLSX = REPO_DOC / ".feishu-cache" / "full-acceptance-backup-20260728" / "Ai-DOP全量功能验收对照表_系统166项版.xlsx"
- MODULE_OVERVIEW_MD = REPO_DOC / "Ai-DOP系统各模块情况一览表.md"
- FONT = "微软雅黑"
- BLUE = RGBColor(0, 0, 0)
- HEADER_FILL = "D6E3F0"
- CN_NUM = "零一二三四五六七八九十"
- TODAY = "2026-09-04"
- TECH_SPEC_NAME = "项目合同里的技术规范"
- thin = Side(style="thin", color="000000")
- THIN_BORDER = Border(left=thin, right=thin, top=thin, bottom=thin)
- HDR_FILL = PatternFill("solid", fgColor="1F3864")
- HDR_FONT = Font(name=FONT, bold=True, color="FFFFFF", size=10)
- CELL_FONT = Font(name=FONT, size=10)
- WRAP = Alignment(wrap_text=True, vertical="center")
- # 模块正文数据:_gen_s9_modules_data.MODULES(对齐技术规范书 2.6 / 2.7)
- # ─────────────────────────────────────────────────────────────
- # Word 辅助
- # ─────────────────────────────────────────────────────────────
- def set_run_font(run, *, size=None, bold=None, color=None):
- run.font.name = FONT
- r_pr = run._element.get_or_add_rPr()
- r_fonts = r_pr.rFonts
- if r_fonts is None:
- r_fonts = OxmlElement("w:rFonts")
- r_pr.insert(0, r_fonts)
- r_fonts.set(qn("w:ascii"), FONT)
- r_fonts.set(qn("w:hAnsi"), FONT)
- r_fonts.set(qn("w:eastAsia"), FONT)
- if size is not None:
- run.font.size = size
- if bold is not None:
- run.bold = bold
- if color is not None:
- run.font.color.rgb = color
- def add_para(doc, text="", *, size=10.5, bold=False, color=None, align=None, space_after=6, style=None):
- p = doc.add_paragraph(style=style) if style else doc.add_paragraph()
- if align is not None:
- p.alignment = align
- p.paragraph_format.space_after = Pt(space_after)
- p.paragraph_format.line_spacing = 1.5
- run = p.add_run(text)
- set_run_font(run, size=Pt(size), bold=bold, color=color)
- return p
- def add_page_break(doc):
- p = doc.add_paragraph()
- p.add_run().add_break(WD_BREAK.PAGE)
- def shade_cell(cell, fill: str):
- tc_pr = cell._element.get_or_add_tcPr()
- old = tc_pr.find(qn("w:shd"))
- if old is not None:
- tc_pr.remove(old)
- shd = OxmlElement("w:shd")
- shd.set(qn("w:val"), "clear")
- shd.set(qn("w:color"), "auto")
- shd.set(qn("w:fill"), fill)
- tc_pr.append(shd)
- def set_cell_text(cell, text, *, bold=False, color=None, size=10.5, center=False):
- cell.text = ""
- p = cell.paragraphs[0]
- if center:
- p.alignment = WD_ALIGN_PARAGRAPH.CENTER
- run = p.add_run(str(text))
- set_run_font(run, size=Pt(size), bold=bold, color=color)
- def add_info_table(doc, doc_no: str):
- table = doc.add_table(rows=5, cols=2)
- table.style = "Table Grid"
- rows = [
- ("文档编号", doc_no),
- ("版本", "V0.4"),
- ("密级", "内部"),
- ("编制单位", "智造易项目组"),
- ("编制日期", TODAY),
- ]
- for i, (k, v) in enumerate(rows):
- set_cell_text(table.rows[i].cells[0], k, bold=True, center=True)
- set_cell_text(table.rows[i].cells[1], v, center=True)
- shade_cell(table.rows[i].cells[0], "D6E3F0")
- for row in table.rows:
- for cell in row.cells:
- cell.width = Cm(7)
- def add_version_table(doc):
- table = doc.add_table(rows=5, cols=4)
- table.style = "Table Grid"
- headers = ["日期", "版本", "修订人", "变更说明"]
- for i, h in enumerate(headers):
- set_cell_text(table.rows[0].cells[i], h, bold=True, color=RGBColor(0, 0, 0), center=True)
- shade_cell(table.rows[0].cells[i], HEADER_FILL)
- data = [
- ("2026-07-30", "V0.1", "智造易项目组", "初版建立"),
- ("2026-07-30", "V0.2", "智造易项目组", "对齐项目合同里的技术规范需求与功能要求;统一 REQ/FUNC 编号与版式"),
- ("2026-09-04", "V0.3", "智造易项目组", "九宫格含 S9 全场关键指标看板;数据对接服务归入系统集成"),
- ("2026-09-04", "V0.4", "智造易项目组", "统一系统名称与文档版式,并修订范围与用词"),
- ]
- for r, row in enumerate(data, 1):
- for c, val in enumerate(row):
- set_cell_text(table.rows[r].cells[c], val, center=True)
- def add_cover(doc, module_title: str, doc_type: str, doc_no: str):
- for _ in range(3):
- add_para(doc, "")
- add_para(doc, module_title, size=28, bold=True, color=BLUE, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=12)
- add_para(doc, doc_type, size=22, bold=True, color=BLUE, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=10)
- add_para(doc, "Ai-DOP智慧运营管理系统", size=13, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=18)
- add_info_table(doc, doc_no)
- add_page_break(doc)
- add_para(doc, "版本记录", size=16, bold=True, space_after=12)
- add_version_table(doc)
- add_page_break(doc)
- add_para(doc, "目录", size=16, bold=True, space_after=8)
- add_page_break(doc)
- def heading1(doc, text: str):
- p = doc.add_paragraph(style="Heading 1")
- p.clear()
- run = p.add_run(text)
- set_run_font(run, size=Pt(16), bold=True)
- return p
- def heading2(doc, text: str):
- p = doc.add_paragraph(style="Heading 2")
- p.clear()
- run = p.add_run(text)
- set_run_font(run, size=Pt(14), bold=True)
- return p
- def heading3(doc, text: str):
- p = doc.add_paragraph(style="Heading 3")
- p.clear()
- run = p.add_run(text)
- set_run_font(run, size=Pt(12), bold=True)
- return p
- def write_brd(mod: dict, out_path: Path):
- doc = Document()
- section = doc.sections[0]
- section.top_margin = Cm(2)
- section.bottom_margin = Cm(2)
- section.left_margin = Cm(2.5)
- section.right_margin = Cm(2.5)
- add_cover(doc, mod["module_title"], "业务需求描述", mod["doc_no_brd"])
- heading1(doc, "1 范围说明")
- add_para(doc, f"本文件描述「{mod['module_title']}」业务需求,系统名称为 Ai-DOP智慧运营管理系统。")
- add_para(doc, f"报价范围对齐:{mod['quote_scope']}。")
- add_para(doc, f"需求与功能要求以{TECH_SPEC_NAME}对应章节为准,并与报价单、三联对照表一致;不得偏离技术规范已列明的能力边界。")
- add_para(doc, "需求编号格式 REQ-S9-nnn;与功能编号、测试编号通过三联对照表串联。")
- if mod.get("intro"):
- add_para(doc, mod["intro"])
- heading1(doc, "2 技术规范追溯")
- for t in mod.get("tech_spec", []):
- if mod.get("intro") and t == mod["intro"]:
- continue
- add_para(doc, t)
- features = [f for f in mod["features"] if f.get("impl_status") != "未实现"]
- for i, feat in enumerate(features, 1):
- heading1(doc, f"({CN_NUM[i]}){feat['name']}({feat['req']})")
- if feat.get("tech_clause"):
- heading2(doc, "技术规范条款")
- add_para(doc, feat["tech_clause"])
- heading2(doc, "功能说明")
- for t in feat["func_desc"]:
- add_para(doc, t)
- heading2(doc, "业务描述")
- for t in feat["biz_desc"]:
- add_para(doc, t)
- add_para(doc, f"对应功能编号:{feat['func']};系统路径:{feat['path']}。", bold=True)
- heading1(doc, f"({CN_NUM[len(features) + 1]})权限管理汇总")
- for feat in features:
- add_para(doc, f"{feat['name']}:{feat['roles']}")
- from _delivery_docx_polish import force_all_text_black, rebuild_static_toc
- rebuild_static_toc(doc)
- force_all_text_black(doc)
- out_path.parent.mkdir(parents=True, exist_ok=True)
- doc.save(out_path)
- print("BRD", out_path)
- def write_bbp(mod: dict, out_path: Path):
- doc = Document()
- section = doc.sections[0]
- section.top_margin = Cm(2)
- section.bottom_margin = Cm(2)
- section.left_margin = Cm(2.5)
- section.right_margin = Cm(2.5)
- add_cover(doc, mod["module_title"], "业务蓝图设计方案", mod["doc_no_bbp"])
- heading1(doc, "1 总体业务方案")
- heading2(doc, "1.1 目标和宗旨")
- add_para(doc, f"落实报价范围「{mod['quote_scope']}」,并将{TECH_SPEC_NAME}中对应功能要求转化为可配置、可验收的功能方案。")
- if mod.get("intro"):
- add_para(doc, mod["intro"])
- for t in mod.get("tech_spec", [])[:3]:
- if mod.get("intro") and t == mod["intro"]:
- continue
- add_para(doc, t)
- heading2(doc, "1.2 总体业务流程图")
- add_para(doc, "总体路径:系统集成数据对接服务采集与回写 → 指标日批计算 → 九宫格总览(含 S9 全场关键指标看板)→ 智慧诊断 → 改善建档派单跟踪与效果复盘闭环。")
- heading2(doc, "1.3 方案设计")
- add_para(doc, "前端:Vue3 + Element Plus;动态看板与智慧诊断闭环页。后端:Admin.NET + 数据对接服务分层(贴源/标准/明细/指标)+ 出站回写队列;对接方式含数据库直连与 HTTP API。")
- for idx, feat in enumerate([f for f in mod["features"] if f.get("impl_status") != "未实现"], 2):
- heading1(doc, f"{idx} {feat['name']}({feat['func']})")
- heading2(doc, f"{idx}.1 目标/宗旨")
- add_para(doc, feat["goal"])
- if feat.get("tech_clause"):
- add_para(doc, f"规范追溯:{feat['tech_clause']}", bold=True)
- heading2(doc, f"{idx}.2 业务流程图")
- add_para(doc, "流程步骤见下表(文字流程图)。")
- table = doc.add_table(rows=1 + len(feat["flow_steps"]), cols=6)
- table.style = "Table Grid"
- headers = ["步骤", "活动", "角色", "操作", "输入", "输出"]
- for c, h in enumerate(headers):
- set_cell_text(table.rows[0].cells[c], h, bold=True, color=RGBColor(0, 0, 0), center=True)
- shade_cell(table.rows[0].cells[c], "4472C4")
- for r, step in enumerate(feat["flow_steps"], 1):
- for c, val in enumerate(step):
- set_cell_text(table.rows[r].cells[c], val, size=9)
- add_para(doc, "")
- heading2(doc, f"{idx}.3 业务流程说明")
- for t in feat["biz_desc"]:
- add_para(doc, t)
- heading2(doc, f"{idx}.4 业务流程规则")
- for t in feat["rules"]:
- add_para(doc, "• " + t)
- heading2(doc, f"{idx}.5 权限管理需求")
- add_para(doc, feat["roles"])
- heading2(doc, f"{idx}.6 系统接口集成")
- add_para(doc, feat["interfaces"])
- heading2(doc, f"{idx}.7 报表需求")
- add_para(doc, feat["reports"])
- add_para(doc, f"对应需求:{feat['req']};路由:{feat['route']}。", bold=True)
- from _delivery_docx_polish import force_all_text_black, rebuild_static_toc
- rebuild_static_toc(doc)
- force_all_text_black(doc)
- out_path.parent.mkdir(parents=True, exist_ok=True)
- doc.save(out_path)
- print("BBP", out_path)
- # ─────────────────────────────────────────────────────────────
- # Excel:测试用例 / 三联对照
- # ─────────────────────────────────────────────────────────────
- def style_header(ws, row, cols):
- for c in range(1, cols + 1):
- cell = ws.cell(row=row, column=c)
- cell.fill = HDR_FILL
- cell.font = HDR_FONT
- cell.alignment = WRAP
- cell.border = THIN_BORDER
- def autosize(ws, max_width=48):
- for col in ws.columns:
- letter = get_column_letter(col[0].column)
- width = 12
- for cell in col:
- if cell.value:
- width = min(max_width, max(width, len(str(cell.value)) + 2))
- ws.column_dimensions[letter].width = width
- def write_test_xlsx(mod: dict, out_path: Path):
- wb = Workbook()
- # 目录
- ws = wb.active
- ws.title = "目录"
- ws["B2"] = f"{mod['module_title']}业务场景目录"
- ws["B2"].font = Font(name=FONT, bold=True, size=14)
- headers = ["序号", "测试分场景", "TC数量", "关联FUNC", "关联REQ"]
- for i, h in enumerate(headers, 2):
- ws.cell(row=3, column=i, value=h)
- style_header(ws, 3, 6)
- for i, feat in enumerate(mod["features"], 1):
- ws.cell(row=3 + i, column=2, value=i)
- ws.cell(row=3 + i, column=3, value=feat["name"])
- ws.cell(row=3 + i, column=4, value=f"{len(feat['tcs'])} 条")
- ws.cell(row=3 + i, column=5, value=feat["func"])
- ws.cell(row=3 + i, column=6, value=feat["req"])
- # 各场景 sheet
- tc_headers = [
- "用例编号", "测试内容", "测试步骤", "系统功能", "预期结果",
- "测试结果1st", "测试结果2nd", "角色", "登录用户名", "相关单据编号",
- "备注", "TC编号", "关联FUNC", "关联REQ",
- ]
- for si, feat in enumerate(mod["features"], 1):
- name = f"{si} {feat['name']}"[:31]
- w = wb.create_sheet(name)
- w["A1"] = "详细测试结果"
- w["A1"].font = Font(name=FONT, bold=True, size=12)
- w["K3"] = "测试内容"
- w["K4"] = feat["name"]
- w["M4"] = f"{si}"
- for c, h in enumerate(tc_headers, 1):
- w.cell(row=10, column=c, value=h)
- style_header(w, 10, len(tc_headers))
- for r, tc in enumerate(feat["tcs"], 12):
- raw, code, typ, title, steps, expect = tc
- vals = [
- raw, title, steps, title, expect,
- "", "", "运营专员/运维", "AIDOPDemo", "",
- typ, code, feat["func"], feat["req"],
- ]
- for c, v in enumerate(vals, 1):
- cell = w.cell(row=r, column=c, value=v)
- cell.font = CELL_FONT
- cell.alignment = WRAP
- cell.border = THIN_BORDER
- w.row_dimensions[r].height = 60
- autosize(w)
- autosize(ws)
- out_path.parent.mkdir(parents=True, exist_ok=True)
- wb.save(out_path)
- print("TC ", out_path)
- def write_triple_xlsx(mod: dict, out_path: Path):
- wb = Workbook()
- ws0 = wb.active
- ws0.title = "验收范围说明"
- ws0["A1"] = "项"
- ws0["B1"] = "说明"
- style_header(ws0, 1, 2)
- rows = [
- ("验收范围基线", "产互联 ai-dop 技术规范及现行报价单"),
- ("验收编号", "只有纳入技术规范验收的 FUNC 填写同号 AC"),
- ("关联TC", "测试用例编号,用于支撑 AC 验收结论"),
- ("报价对齐", mod["quote_scope"]),
- ("系统扩展功能", "保留原 FUNC/TC 作为系统资料;本表已标注验收范围"),
- ]
- for i, (a, b) in enumerate(rows, 2):
- ws0.cell(row=i, column=1, value=a).font = CELL_FONT
- ws0.cell(row=i, column=2, value=b).font = CELL_FONT
- ws = wb.create_sheet("TC-FUNC-REQ对照表")
- headers = [
- "需求编号", "需求名称", "功能编号", "验收编号", "验收范围",
- "功能名称", "测试编号", "测试内容", "原始编号", "类型", "实际系统菜单路径",
- ]
- for c, h in enumerate(headers, 1):
- ws.cell(row=1, column=c, value=h)
- style_header(ws, 1, len(headers))
- r = 2
- for feat in mod["features"]:
- for tc in feat["tcs"]:
- raw, code, typ, title, _steps, _expect = tc
- vals = [
- feat["req"], feat["name"], feat["func"], feat.get("ac", ""),
- feat.get("scope", ""), feat["name"], code, title, raw, typ, feat["path"],
- ]
- for c, v in enumerate(vals, 1):
- cell = ws.cell(row=r, column=c, value=v)
- cell.font = CELL_FONT
- cell.alignment = WRAP
- cell.border = THIN_BORDER
- r += 1
- ws2 = wb.create_sheet("覆盖度统计")
- h2 = ["模块", "REQ", "FUNC", "TC(正常)", "TC(异常)", "TC合计", "实际系统菜单路径"]
- for c, h in enumerate(h2, 1):
- ws2.cell(row=1, column=c, value=h)
- style_header(ws2, 1, len(h2))
- total_n = total_e = 0
- for i, feat in enumerate(mod["features"], 2):
- n = sum(1 for t in feat["tcs"] if t[2] == "正常")
- e = sum(1 for t in feat["tcs"] if t[2] == "异常")
- total_n += n
- total_e += e
- vals = [feat["name"], 1, 1, n, e, n + e, feat["path"]]
- for c, v in enumerate(vals, 1):
- ws2.cell(row=i, column=c, value=v).font = CELL_FONT
- last = len(mod["features"]) + 2
- ws2.cell(row=last, column=1, value="合计").font = Font(name=FONT, bold=True)
- ws2.cell(row=last, column=2, value=len(mod["features"]))
- ws2.cell(row=last, column=3, value=len({f["func"] for f in mod["features"]}))
- ws2.cell(row=last, column=4, value=total_n)
- ws2.cell(row=last, column=5, value=total_e)
- ws2.cell(row=last, column=6, value=total_n + total_e)
- for sheet in (ws0, ws, ws2):
- autosize(sheet)
- out_path.parent.mkdir(parents=True, exist_ok=True)
- wb.save(out_path)
- print("TRI", out_path)
- def update_acceptance_xlsx():
- if not ACCEPTANCE_XLSX.exists():
- print("SKIP acceptance: not found", ACCEPTANCE_XLSX)
- return
- # 聚合 TC(去重保序)
- tc_map: dict[str, list[str]] = {}
- req_map: dict[str, str] = {}
- meta_map: dict[str, dict] = {}
- for mod in MODULES.values():
- for feat in mod["features"]:
- func = feat["func"]
- req_map[func] = feat["req"] if func not in req_map else (
- req_map[func] if feat["req"] in req_map[func] else f"{req_map[func]}; {feat['req']}"
- )
- tc_map.setdefault(func, [])
- for tc in feat["tcs"]:
- if tc[1] not in tc_map[func]:
- tc_map[func].append(tc[1])
- meta_map[func] = {
- "name": feat["name"] if func not in meta_map else meta_map[func]["name"],
- "ac": feat.get("ac", ""),
- "route": feat.get("route", feat.get("path", "")),
- }
- wb = load_workbook(ACCEPTANCE_XLSX)
- for sheet_name in ("全量FUNC-AC对照", "S9"):
- if sheet_name not in wb.sheetnames:
- continue
- ws = wb[sheet_name]
- headers = {ws.cell(1, c).value: c for c in range(1, ws.max_column + 1)}
- col_func = headers.get("功能编号")
- col_req = headers.get("关联REQ")
- col_tcn = headers.get("TC数量")
- col_tc = headers.get("关联TC")
- col_name = headers.get("功能名称")
- col_ac = headers.get("验收编号")
- col_route = headers.get("系统路由/入口")
- col_mod = headers.get("模块")
- col_modname = headers.get("模块名称")
- if not col_func:
- continue
- existing = set()
- last_row = 1
- for r in range(2, ws.max_row + 1):
- func = ws.cell(r, col_func).value
- if func:
- existing.add(func)
- last_row = r
- if func in tc_map:
- tcs = tc_map[func]
- if col_req:
- ws.cell(r, col_req).value = req_map.get(func, "待补")
- if col_tcn:
- ws.cell(r, col_tcn).value = len(tcs)
- if col_tc:
- ws.cell(r, col_tc).value = "; ".join(tcs)
- # 追加缺失 FUNC(如 ChatBI S9-007)
- for func, tcs in tc_map.items():
- if func in existing:
- continue
- last_row += 1
- meta = meta_map.get(func, {})
- if col_mod:
- ws.cell(last_row, col_mod).value = "S9"
- if col_modname:
- ws.cell(last_row, col_modname).value = "运营指标与平台扩展"
- ws.cell(last_row, col_func).value = func
- if col_name:
- ws.cell(last_row, col_name).value = meta.get("name", "")
- if col_ac:
- ws.cell(last_row, col_ac).value = meta.get("ac", "")
- if col_req:
- ws.cell(last_row, col_req).value = req_map.get(func, "")
- if col_tcn:
- ws.cell(last_row, col_tcn).value = len(tcs)
- if col_tc:
- ws.cell(last_row, col_tc).value = "; ".join(tcs)
- if col_route:
- ws.cell(last_row, col_route).value = meta.get("route", "")
- wb.save(ACCEPTANCE_XLSX)
- print("UPD", ACCEPTANCE_XLSX)
- dest = BASE / "Ai-DOP全量功能验收对照表.xlsx"
- try:
- wb2 = load_workbook(ACCEPTANCE_XLSX)
- wb2.save(dest)
- print("UPD", dest)
- except Exception as e:
- print("SKIP write delivery acceptance:", e)
- alt = BASE / "Ai-DOP全量功能验收对照表_已补S9诊断改善集成.xlsx"
- try:
- wb3 = load_workbook(ACCEPTANCE_XLSX)
- wb3.save(alt)
- print("UPD", alt)
- except Exception as e:
- print("SKIP alt acceptance:", e)
- def update_module_overview():
- if not MODULE_OVERVIEW_MD.exists():
- return
- text = MODULE_OVERVIEW_MD.read_text(encoding="utf-8")
- row_updates = {
- "S9 运营指标与智慧看板": "| 10 | S9 运营指标与智慧看板 | 完成 | 完成 | 待完成 | 完成 | 完成 | 待完成 | 待完成 | 待完成 | 主体已实现;部门看板/S8预警演示数据待补;文档已对齐实现现状 |",
- "运营诊断": "| 11 | 运营诊断 | 完成 | 待完成 | 待完成 | 完成 | 完成 | 待完成 | 待完成 | 待完成 | 交互诊断已实现;正式诊断报告落库/导出待补 |",
- "运营改善": "| 12 | 运营改善 | 完成 | 待完成 | 待完成 | 完成 | 完成 | 待完成 | 待完成 | 待完成 | 建档/审批/验证已通;独立派单通道待增强;FUNC并入S9-006 |",
- "ChatBI": "| 13 | ChatBI | 部分完成 | 待完成 | 待完成 | 完成 | 完成 | 待完成 | 待完成 | 待完成 | KPI聚合问答MVP已上线;NL2SQL/历史/独立页待补(FUNC-S9-007) |",
- "系统集成": "| 16 | 系统集成 | 部分完成 | 待完成 | 待完成 | 完成 | 完成 | 待完成 | 待完成 | 待完成 | 001~003为占位;真实同步在数据对接服务;企业SSO未落地 |",
- }
- lines = text.splitlines()
- out = []
- for line in lines:
- replaced = False
- if line.startswith("|"):
- cols = [c.strip() for c in line.split("|")]
- if len(cols) > 2:
- for key, new_row in row_updates.items():
- if cols[2] == key or cols[2].startswith(key):
- out.append(new_row)
- replaced = True
- break
- if not replaced:
- out.append(line)
- text2 = "\n".join(out) + "\n"
- # 汇总:需求/蓝图含 ChatBI 后为 13
- text2 = text2.replace("| 需求文档 | 12 | 0 | 4 |", "| 需求文档 | 13 | 0 | 3 |")
- text2 = text2.replace("| 蓝图设计 | 12 | 0 | 4 |", "| 蓝图设计 | 13 | 0 | 3 |")
- text2 = text2.replace("| 需求文档 | 13 | 0 | 3 |", "| 需求文档 | 13 | 0 | 3 |")
- MODULE_OVERVIEW_MD.write_text(text2, encoding="utf-8")
- print("UPD", MODULE_OVERVIEW_MD)
- def main():
- BASE.mkdir(parents=True, exist_ok=True)
- for mod in MODULES.values():
- folder = BASE / mod["folder"]
- folder.mkdir(parents=True, exist_ok=True)
- write_brd(mod, folder / mod["brd_name"])
- write_bbp(mod, folder / mod["bbp_name"])
- write_test_xlsx(mod, folder / mod["tc_name"])
- write_triple_xlsx(mod, folder / mod["triple_name"])
- update_acceptance_xlsx()
- update_module_overview()
- print("DONE")
- if __name__ == "__main__":
- main()
|