chore: commit pending local changes (2026-09-17 github sync)
This commit is contained in:
parent
beca611f29
commit
6ef74388c1
@ -40,14 +40,18 @@ HT_DELIVERY_CONFIRM = "bid_delivery_confirm"
|
||||
HT_FLOW_CONFIRM = "flow_plan_confirm" # 流程裁剪确认(引擎通用类型,pipeline_flow_plans 机制)
|
||||
HT_TEMPLATE_CONFIRM = "tech_template_confirm" # 技术方案模版/骨架确认(纯技术方案流程,2026-09-14 批2)
|
||||
|
||||
BLOCKING_HT_TYPES = (HT_TENDER_FILE, HT_FLOW_CONFIRM, HT_TEMPLATE_CONFIRM)
|
||||
BLOCKING_HT_TYPES = (HT_TENDER_FILE, HT_FLOW_CONFIRM)
|
||||
# 2026-09-03 起只保留「等招标文件」为全局阻塞:
|
||||
# - HT_HUMAN_DOCS(等人类资质文件)降级为章节级依赖——只阻塞商务章(等资料准备),技术/报价章不受影响;
|
||||
# - HT_QC_ESCALATION(QC 轮次用尽)降级为章节级依赖——只阻塞依赖该产出类型的章节,其余章节照常流转。
|
||||
# 2026-09-11 增 HT_FLOW_CONFIRM:裁剪计划待用户确认期间全局阻塞(确认后才按计划派发,
|
||||
# 防「用户在确认、对账器按旧流程抢跑」)。驳回后的暂停由 bid_flow 查 pipeline_flow_plans 状态兜住。
|
||||
# 2026-09-14 增 HT_TEMPLATE_CONFIRM:技术方案模版骨架待用户确认期间全局阻塞(用户明确要
|
||||
# 「模版由用户确认后」才开写章节——骨架没确认就编写 = 方向错了全白写)。
|
||||
# 2026-09-17 阻塞语义分流:HT_TEMPLATE_CONFIRM 移出全局阻塞——
|
||||
# - 纯技术方案流程:骨架确认卡的是全流程,但 T3 段本就显式 waiting(确认前不派任何任务),
|
||||
# 全局阻塞是重复保险,移除后行为不变;
|
||||
# - 标准投标流程(模版裁剪链挂入后):裁剪确认只关技术章的事,商务/报价章与其他维度
|
||||
# 照常流转——若全局阻塞,用户晚确认一天模版,整个标书停摆(过度阻塞)。
|
||||
# 技术章的门禁在 C 段按章节卡(tech_tpl_confirmed 才派技术章编写)。
|
||||
|
||||
# ── QC 契合度审核类型(解析产出逐类审核)──
|
||||
QC_TYPE_SCORING = "scoring_items" # 评分项 + 得分规则
|
||||
|
||||
@ -142,6 +142,54 @@ def _clean_md_cell(text):
|
||||
return t.strip()
|
||||
|
||||
|
||||
# ── 内联 markdown → Word run(2026-09-17 用户报障:**粗体** 原样带星号落 docx)──
|
||||
_MD_BOLD_RE = re.compile(r'\*\*([^*]+)\*\*')
|
||||
|
||||
|
||||
def _strip_inline_code(s):
|
||||
"""去掉行内代码反引号(docx 无等宽语义,保留文字)。"""
|
||||
return re.sub(r'`([^`]*)`', r'\1', s or '')
|
||||
|
||||
|
||||
def _add_runs_with_inline_md(para, text):
|
||||
"""把一段文本按内联 markdown 拆成 runs 写进段落:**粗体**→加粗 run,
|
||||
`code`→去反引号,制表符→空格,其余原样。无标记时等价 add_paragraph(text)。"""
|
||||
t = (text or '').replace('\t', ' ')
|
||||
pos = 0
|
||||
for m in _MD_BOLD_RE.finditer(t):
|
||||
if m.start() > pos:
|
||||
para.add_run(_strip_inline_code(t[pos:m.start()]))
|
||||
r = para.add_run(_strip_inline_code(m.group(1)))
|
||||
r.bold = True
|
||||
pos = m.end()
|
||||
if pos < len(t):
|
||||
para.add_run(_strip_inline_code(t[pos:]))
|
||||
if not t:
|
||||
para.add_run('')
|
||||
|
||||
|
||||
# 项目符号(半角 - * 必须跟空白;全角 •/·/●/◦/- 可不跟空白,用户实测 "•\t**xx**")
|
||||
_MD_BULLET_RE = re.compile(r'^\s*(?:[-*]\s+|(?:[•·●◦]|-)\s*)(?=\S)')
|
||||
# 小节号标题:"1.3 xxx" / "2.1.3 xxx"(点分编号开头)
|
||||
_MD_NUMHEAD_RE = re.compile(r'^\s*(\d+(?:\.\d+)+)[\s、.\.]*(\S.*)$')
|
||||
|
||||
|
||||
def _numhead_heading(line):
|
||||
"""识别小节号标题行,返回 (level, text) 或 None。
|
||||
|
||||
保守判定防误伤正文:编号后标题 ≤40 字且不含句号("1.3 倍冗余……" 这类
|
||||
以小数开头的正文句子不当标题)。level = 点分段数(1.3→2,2.1.3→3),封顶 4。
|
||||
"""
|
||||
m = _MD_NUMHEAD_RE.match(line or '')
|
||||
if not m:
|
||||
return None
|
||||
no, rest = m.group(1), m.group(2).strip()
|
||||
if not rest or len(rest) > 40 or '。' in rest:
|
||||
return None
|
||||
level = min(4, no.count('.') + 1)
|
||||
return level, "%s %s" % (no, rest)
|
||||
|
||||
|
||||
def _write_docx(path, title, subtitle, chapters):
|
||||
"""用 python-docx 写标书。返回 (ok, page_hint_or_err)。
|
||||
|
||||
@ -247,14 +295,20 @@ def _write_docx(path, title, subtitle, chapters):
|
||||
doc.add_paragraph("[配图缺失(下载失败):%s %s]" % (alt or "", url[:80]))
|
||||
img_fail += 1
|
||||
continue
|
||||
_nh = _numhead_heading(t)
|
||||
if t.startswith("### "):
|
||||
doc.add_heading(t[4:], level=3)
|
||||
doc.add_heading(_strip_inline_code(t[4:].replace('**', '')), level=3)
|
||||
elif t.startswith("## "):
|
||||
doc.add_heading(t[3:], level=2)
|
||||
doc.add_heading(_strip_inline_code(t[3:].replace('**', '')), level=2)
|
||||
elif t.startswith("# "):
|
||||
doc.add_heading(t[2:], level=2)
|
||||
elif t.lstrip().startswith(("- ", "* ")):
|
||||
doc.add_paragraph(t.lstrip()[2:], style="List Bullet")
|
||||
doc.add_heading(_strip_inline_code(t[2:].replace('**', '')), level=2)
|
||||
elif _nh is not None:
|
||||
# 小节号标题("1.3 xxx")→ 真 Word 标题层级(目录/导航可用)
|
||||
doc.add_heading(_strip_inline_code(_nh[1].replace('**', '')), level=_nh[0])
|
||||
elif _MD_BULLET_RE.match(t) and not t.lstrip().startswith('**'):
|
||||
# 项目符号行(- * • · ● ◦ -,可跟制表符)→ List Bullet + 内联粗体
|
||||
_bp = doc.add_paragraph(style="List Bullet")
|
||||
_add_runs_with_inline_md(_bp, _MD_BULLET_RE.sub('', t))
|
||||
elif _is_table_sep(t):
|
||||
continue # 游离分隔行(无表头)跳过
|
||||
elif t.strip().startswith('|') and t.strip().endswith('|'):
|
||||
@ -269,7 +323,9 @@ def _write_docx(path, title, subtitle, chapters):
|
||||
except Exception:
|
||||
doc.add_paragraph(t)
|
||||
else:
|
||||
doc.add_paragraph(t)
|
||||
# 普通段落/编号行:**粗体** 拆 run 加粗,`code` 去反引号,\t→空格
|
||||
_pp = doc.add_paragraph()
|
||||
_add_runs_with_inline_md(_pp, t)
|
||||
doc.add_page_break()
|
||||
doc.save(path)
|
||||
if img_ok or img_fail or tbl_ok:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user