#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成《中国分子诊断市场与原料 2025-2027 分析报告》Word 版"""
import json
import os

from docx import Document
from docx.enum.section import WD_SECTION
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Cm, Pt, RGBColor

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
D = os.path.join(W, '数据/diag_market_2025_2027')
OUT = os.path.join(W, '销售分析/分子诊断市场与原料_2025-2027_分析报告.docx')

market = json.load(open(os.path.join(D, 'market.json'), encoding='utf-8'))
companies = json.load(open(os.path.join(D, 'companies.json'), encoding='utf-8'))
materials = json.load(open(os.path.join(D, 'materials.json'), encoding='utf-8'))
academic = json.load(open(os.path.join(D, 'academic.json'), encoding='utf-8'))
cust = json.load(open(os.path.join(D, 'customers_liu.json'), encoding='utf-8'))

doc = Document()
sec = doc.sections[0]
sec.top_margin = Cm(2.2); sec.bottom_margin = Cm(2.2)
sec.left_margin = Cm(2.4); sec.right_margin = Cm(2.4)

st = doc.styles['Normal']
st.font.name = 'Microsoft YaHei'
st.font.size = Pt(10.5)
st.element.rPr.rFonts.set(__import__('docx').oxml.ns.qn('w:eastAsia'), '微软雅黑')
st.paragraph_format.line_spacing = 1.45
st.paragraph_format.space_after = Pt(4)

# 页脚页码
from docx.oxml import OxmlElement as _OE
from docx.oxml.ns import qn as _qn
_fp = sec.footer.paragraphs[0]
_fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
_r = _fp.add_run()
_f1 = _OE('w:fldChar'); _f1.set(_qn('w:fldCharType'), 'begin')
_it = _OE('w:instrText'); _it.set(_qn('xml:space'), 'preserve'); _it.text = 'PAGE'
_f2 = _OE('w:fldChar'); _f2.set(_qn('w:fldCharType'), 'end')
_r._r.append(_f1); _r._r.append(_it); _r._r.append(_f2)


def H(text, level=1):
    h = doc.add_heading(text, level=level)
    for r in h.runs:
        r.font.name = 'Microsoft YaHei'
        r._element.rPr.rFonts.set(__import__('docx').oxml.ns.qn('w:eastAsia'), '微软雅黑')
        r.font.color.rgb = RGBColor(0x1F, 0x4E, 0x79)
    return h


def P(text, bold=False, size=10.5, color=None, align=None, italic=False):
    p = doc.add_paragraph()
    r = p.add_run(text)
    r.bold = bold; r.italic = italic; r.font.size = Pt(size)
    r.font.name = 'Microsoft YaHei'
    r._element.rPr.rFonts.set(__import__('docx').oxml.ns.qn('w:eastAsia'), '微软雅黑')
    if color:
        r.font.color.rgb = RGBColor(*color)
    if align:
        p.alignment = align
    return p


def T(headers, rows, widths=None, fs=9):
    t = doc.add_table(rows=1, cols=len(headers))
    t.style = 'Table Grid'
    t.alignment = WD_TABLE_ALIGNMENT.CENTER
    hdr = t.rows[0].cells
    for i, h in enumerate(headers):
        hdr[i].text = ''
        r = hdr[i].paragraphs[0].add_run(h)
        r.bold = True; r.font.size = Pt(fs)
        r.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
        r.font.name = 'Microsoft YaHei'
        r._element.rPr.rFonts.set(__import__('docx').oxml.ns.qn('w:eastAsia'), '微软雅黑')
        shd = __import__('docx').oxml.parse_xml(
            r'<w:shd {} w:fill="1F4E79"/>'.format(
                __import__('docx').oxml.ns.nsdecls('w')))
        hdr[i]._tc.get_or_add_tcPr().append(shd)
    for row in rows:
        cells = t.add_row().cells
        for i, v in enumerate(row):
            cells[i].text = ''
            r = cells[i].paragraphs[0].add_run('' if v is None else str(v))
            r.font.size = Pt(fs); r.font.name = 'Microsoft YaHei'
            r._element.rPr.rFonts.set(__import__('docx').oxml.ns.qn('w:eastAsia'), '微软雅黑')
    # 表头跨页重复
    try:
        from docx.oxml import OxmlElement
        from docx.oxml.ns import qn
        trPr = t.rows[0]._tr.get_or_add_trPr()
        th = OxmlElement('w:tblHeader'); th.set(qn('w:val'), 'true'); trPr.append(th)
    except Exception:
        pass
    if widths:
        for i, w in enumerate(widths):
            for row in t.rows:
                row.cells[i].width = Cm(w)
    doc.add_paragraph().paragraph_format.space_after = Pt(2)
    return t


def note(text):
    P('注：' + text, size=8.5, color=(0x6B, 0x76, 0x84))


# ---------------- 封面 ----------------
for _ in range(3):
    doc.add_paragraph()
P('中国分子诊断市场与上游原料', bold=True, size=26, align=WD_ALIGN_PARAGRAPH.CENTER,
  color=(0x1F, 0x4E, 0x79))
P('2025–2027 三年分析报告', bold=True, size=26, align=WD_ALIGN_PARAGRAPH.CENTER,
  color=(0x1F, 0x4E, 0x79))
doc.add_paragraph()
P('市场规模 · 量价变化 · 企业战略 · 原料格局 · 前沿技术', size=13,
  align=WD_ALIGN_PARAGRAPH.CENTER, color=(0x5F, 0x6B, 0x7A))
for _ in range(6):
    doc.add_paragraph()
P('宝锐生物 · 营销中心', size=11, align=WD_ALIGN_PARAGRAPH.CENTER)
P('2026 年 10 月', size=11, align=WD_ALIGN_PARAGRAPH.CENTER)
doc.add_page_break()

# ---------------- 目录 ----------------
H('目录', 1)
for item in ['一、摘要：五条判断',
             '二、板块一：分子诊断市场 2025–2027',
             '     2.1 全球市场：总量与结构',
             '     2.2 中国市场：可核数据与缺口',
             '     2.3 量价与政策：三条价格线',
             '     2.4 技术迭代：谁在扩张、谁在被淘汰',
             '     2.5 四小龙与发光五虎：三年要闻',
             '     2.6 战略矩阵与已落地产品',
             '三、板块二：分子诊断原料 2025–2027',
             '四、板块三：前沿技术（2025–2027 文献与专利）',
             '五、口径、缺口与使用限制']:
    P(item, size=10.5)
doc.add_page_break()

# ---------------- 口径声明 ----------------
H('阅读前：三条口径声明', 1)
P('1）全球市场与国内市场分开成表，不交叉换算。全球数据多为美元、厂商收入口径；中国数据多为人民币、'
  '含试剂+仪器+服务的行业规模口径，两者不可相加，也不做“中国占全球比例”的推导。', size=10)
P('2）2025 为实际值或机构已发布值；2026E、2027E 为预测值，表中以“E”标注，正文不把预测当既成事实。', size=10)
P('3）所有数字均可回溯到来源（见文末来源清单）；拿不到公开数据的细分，写“缺口”，不用比例硬拆、不做插值。', size=10)

# ---------------- 一、摘要 ----------------
H('一、摘要：五条判断', 1)
P('判断一｜全球：PCR 是现金牛，NGS 是增长极。', bold=True)
P('2025 年全球分子诊断 188 亿美元（GVR，厂商收入口径），2026E 194 亿美元；PCR 合计 106 亿美元、占 62.2%。'
  'NGS 113 亿美元口径更宽（含科研/农业），不可与总盘相加。Kalorama 第 19 版给出另一组交叉验证：'
  '2025 年全球 IVD 总盘 1,155.6 亿美元，其中分子 5 项加总 173.1 亿美元（15.0%），与 GVR 的 188 亿美元相差约 8%。', size=10)
P('判断二｜中国：总量缺中段值，分项可看趋势。', bold=True)
P('采用头豹/国金：2023 年 233 亿元 → 2028E 627 亿元（CAGR 21.9%）；2024–2027 按该 CAGR 推算为 '
  '284.0 / 346.2 / 422.0 / 514.4 亿元（推算值，已标注）。GMI 的 12.9 亿美元与“中商×占比”的相乘值均不采用。'
  '分项锚点：qPCR 试剂 2027E 184.8 亿元、基因检测 2025 年 487 亿元、测序仪及耗材 42 亿元。', size=10)
P('判断三｜价格：三条线同时压，量增价减。', bold=True)
P('HPV 集采平均降 53.9%（最高 73%）、自产试剂增值税 3%→13%、立项指南整合 662 项并按联检分档折扣；'
  '企业侧已出现“人份数增、试剂收入降”。', size=10)
P('判断四｜企业：九家六家营收下滑，战略三向分叉。', bold=True)
P('迈瑞 -9.38%、迈克 -14.84%、凯普 -17.89%、亚辉龙 -9.99%、硕世 -3.02%、新产业国内 -9.82%，安图持平。'
  '圣湘靠并购、达安靠自研测序、凯普靠甲基化、硕世靠出海；五虎分别补分子、生态与资本。', size=10)
P('判断五｜原料：酶类可量化，其余必须承认缺口。', bold=True)
P('酶类 52.0 → 57.2 → 62.9 亿元（2025–2027E，CAGR≈10%）；反应管/八联管 28.4 → 31.3 → 34.5 亿元'
  '（灼识，重建值）。dNTP、引物探针、缓冲与冻干保护剂三类无公开中国口径。', size=10)
doc.add_page_break()

# ---------------- 二、板块一 ----------------
H('二、板块一：分子诊断市场 2025–2027', 1)

H('2.1 全球市场：总量与结构', 2)
rows = []
for x in market['global']:
    if x['segment'].startswith('分子诊断'):
        rows.append([x['year'], x['segment'], x['value'], x['unit'], x['source_name'][:34]])
T(['年份', '口径', '规模', '单位', '来源'], rows, widths=[1.4, 4.6, 1.6, 1.6, 6.2])
doc.add_picture(os.path.join(D, 'charts/global_segments.png'), width=Cm(15.5))
P('图 1：全球分子诊断相关细分规模（2025 vs 2026E）。NGS 口径宽于临床分子诊断，仅作趋势参考。', size=8.5,
  color=(0x6B, 0x76, 0x84))
P('结构判断：qPCR 仍是最大单一技术（2025 年 66.7 亿美元），但增速只有 4.99%（2026–2035，Precedence）；'
  'dPCR 8.18 亿美元、CAGR 14.71%；等温扩增 2026E 67 亿美元、CAGR 12.3%；CRISPR 检测 35.1 亿美元、CAGR 17.19%。'
  '增长最快的三个细分（dPCR、CRISPR、等温）恰好都是“宝锐酶与预混体系可以切入”的方向。', size=10)

H('2.1b Kalorama 第 19 版：交叉验证与 2030 终点', 2)
_k = [x for x in market['global'] if 'Kalorama' in (x.get('source_name') or '')]
_rows = [[x['year'], x['segment'][:22], x['value'], x['unit'], (x.get('note') or '')[:30]] for x in _k]
T(['年份', '口径', '规模', '单位', '备注'], _rows, widths=[1.3, 4.6, 1.8, 1.4, 6.4], fs=8)
note('表中 2026E/2027E 为“按 Kalorama 2025→2030 CAGR 推算”的推算值，非报告原值；对外引用必须注明“推算”。')
P('交叉验证结论：①总盘——Kalorama 2025 年 1,155.6 亿美元、2030 年 1,504.5 亿美元，CAGR 5%，'
  '属于“温和增长”判断，与我们此前“PCR 稳、NGS 快”的结构判断一致；②分子——5 项加总 173.1 亿美元（15.0%），'
  '2030 年 260.2 亿美元（17.3%），分子占比在五年内提升 2.3 个百分点，是结构性增量；'
  '③增速差——分子肿瘤 CAGR 15%、HPV 分子 11%，远高于分子微生物的 4%，说明“感染类分子”已进入存量竞争，'
  '“肿瘤/遗传”才是增量。对宝锐的含义：酶与预混的增量应押在肿瘤与遗传赛道，而不是继续堆呼吸道感染。', size=10)

H('2.2 中国市场：可核数据与缺口', 2)
_cn = [x for x in market['china'] if x.get('value') is not None]
_cn.sort(key=lambda x: (0 if x['segment'].startswith('分子诊断（总盘）') else 1, x['year']))
rows = [[x['year'], x['segment'][:26], x['value'], x['unit'], x['source_name'][:34]] for x in _cn]
T(['年份', '分项', '规模', '单位', '来源'], rows, widths=[1.3, 5.0, 1.6, 1.2, 7.1], fs=8)
note('中国总盘采用头豹/国金：2023 为原值、2028E 为原报告预测，2024–2027 为按 CAGR 21.9% 推算的推算值；'
     '其余分项口径不同（试剂 / 仪器 / 产品+服务 / 含耗材），不可加总，也不能替代总盘。')
P('总量按已拍板口径：2023 年 233 亿元，2024–2027 推算 284.0 / 346.2 / 422.0 / 514.4 亿元，2028E 627 亿元，'
  '五年 CAGR 21.9%，高于全球平均（5%）。结构上，分子诊断占中国 IVD 的比重从 2023 年 15% 升至 2025E 16.4%、'
  '2028E 17.8%（头豹/国金）。需要提醒：该系列隐含增速远高于企业端实际营收增速（九家 2025 年多数下滑），'
  '使用时以“行业口径”表述，不与单一企业增速直接对照。', size=10)
P('分项上拿到六个可核锚点（口径各异，不可相加）：qPCR 试剂 2027E 184.8 亿元、qPCR 仪器 2027E 60.0 亿元、'
  'PCR 仪全品类 2025 年 160 亿元、dPCR 仪器 2025 年 32 亿元、基因检测（NGS 为主，含服务）2025 年 487 亿元、'
  '基因测序仪及耗材 2025 年 42 亿元、肿瘤分子检测 2025 年 93 亿元 / 2026E 100 亿元。'
  '这些数字的用途是“判断哪条线在长”，不是“加总出中国分子诊断总盘”。', size=10)

P('Kalorama 给出的中国 IVD 总量是另一个口径：2025 年 59.97 亿美元、2030 年 73.61 亿美元、CAGR 4%，'
  '占全球 5%。这与中商产业研究院的“2025 年中国 IVD 1,295 亿元”相差约 3 倍——原因是 Kalorama 只计'
  'IVD 产品销售额（试剂+仪器），中商含检验服务/终端收入。两组数字并列呈现，不做换算、不取平均；'
  '凡引用“中国 IVD 规模”，必须同时说明是哪一个口径。', size=10)

H('2.3 量价与政策：三条价格线', 2)
rows = [[x.get('item', '')[:26], x.get('change', '')[:90]] for x in market['price_policy'][:8]]
T(['政策/变量', '变化与影响'], rows, widths=[5.2, 10.2])
P('分析：这轮价格下行不是周期性促销，而是支付端改革。集采压的是中标价，增值税压的是出厂价，'
  '立项指南压的是终端收费——三条线同时作用，企业只能靠“量”和“结构”对冲。'
  '对上游原料的含义是：客户会更强地要求“同性能降价”，并把成本压力向酶与预混液传导；'
  '但同时，集采中标企业为保证供应稳定，更愿意接受国产原料替代进口——这是宝锐的窗口。', size=10)

H('2.4 技术迭代：谁在扩张、谁在被淘汰', 2)
rows = [[x.get('from', '')[:20], x.get('to', '')[:24], x.get('evidence', '')[:80]] for x in market['tech_shift'][:6]]
T(['被替代', '替代方向', '证据'], rows, widths=[3.6, 4.6, 7.2])
P('三条主线：①mNGS → tNGS，测序量降到百分之一、成本大降且可同流程检 DNA+RNA；'
  '②低多重/单靶标 → 超多重与联检 panel，政策按 >10 项 60% 计价，等于承认联检的边际成本优势；'
  '③按方法学定价 → 按服务产出统一定价，电泳等老方法失去价差空间，qPCR 与 dPCR 的“定量能力”变成新分水岭。', size=10)

H('2.5 四小龙与发光五虎：三年要闻', 2)
rows = []
for c in companies['companies']:
    evs = c.get('events', [])
    pick = evs[:1] + [e for e in evs[1:] if e.get('type') in ('获证', '并购', '战略', '合作')][:2]
    for e in pick[:3]:
        rows.append([c['name'], e.get('date', ''), e.get('type', ''), (e.get('fact') or '')[:60]])
T(['公司', '日期', '类型', '事件'], rows, widths=[2.6, 2.2, 1.8, 9.0], fs=8.5)
note('完整 65 条要闻与 38 个已落地产品见附表 companies.json；此处每家取 3 条代表性事件。')

H('2.6 战略矩阵与已落地产品', 2)
rows = []
for c in companies['companies']:
    prods = '；'.join(p['name'][:16] for p in c.get('products', [])[:3])
    rows.append([c['name'], c.get('group', ''), c.get('strategy', '')[:70], prods])
T(['公司', '阵营', '三年战略主线', '代表产品'], rows, widths=[2.4, 1.8, 8.2, 5.0], fs=8)
P('读法：四小龙的共性是“守核酸基本盘 + 找一个第二曲线”，差异在第二曲线选了什么——'
  '圣湘选并购（生物药、HLA、自免），达安选测序仪，凯普选甲基化早筛，硕世选海外渠道。'
  '发光五虎的共性是“发光主业被集采压住”，差异在补短板的方式——安图补分子、迈克补生态、迈瑞补资本、'
  '新产业自建分子 PCR、亚辉龙则先处理治理问题。对宝锐的含义：这九家里，真正会在 2026–2027 放量采购'
  '分子原料的是安图（飞朔）、达安（DAseq-60）、新产业（自建 PCR）与圣湘（POCT 放量）。', size=10)
doc.add_page_break()

# ---------------- 三、板块二 ----------------
H('三、板块二：分子诊断原料 2025–2027', 1)

H('3.1 原料结构：唯一有连续数据的是酶类', 2)
rows = []
_missing = []
for x in sorted(materials['market'], key=lambda y: (y['segment'], y['year'])):
    if x['value'] is None:
        _missing.append(x['segment'])
        continue
    rows.append([x['segment'][:24], x['year'], x['value'], x['unit'],
                 (x.get('source') or x.get('source_name') or '')[:34]])
T(['细分', '年份', '规模', '单位', '来源'], rows, widths=[4.6, 1.4, 1.8, 1.4, 7.2], fs=8)
if _missing:
    note('以下细分无公开中国口径，表中不列数值：' + '、'.join(sorted(set(_missing)))[:120])
doc.add_picture(os.path.join(D, 'charts/enzyme_trend.png'), width=Cm(11.5))
P('图 2：中国分子诊断酶类原料 2025–2027E（52.0 → 57.2 → 62.9 亿元，CAGR 约 10%）。', size=8.5,
  color=(0x6B, 0x76, 0x84))
P('结论：酶类是唯一可以量化趋势的细分，年增速约 10%，低于分子诊断终端 15–20% 的增速——'
  '说明原料端正在被下游降价挤压，单价承压、靠量补。'
  '耗材侧唯一补到量级的是反应管/八联管/密封膜：2025–2027E 为 28.4 / 31.3 / 34.5 亿元'
  '（灼识咨询，载于博日科技招股书；原文只给 2020 年 11 亿元、2030 年 46 亿元与 CAGR 10.1%，'
  '本报告按 CAGR 重建，标注为“重建值”）。dNTP、引物探针、缓冲与冻干保护剂三类无公开中国口径，'
  '不给占比、不外推，避免“看起来精确、其实拍脑袋”的数字。', size=10)

H('3.2 国产化与价格', 2)
rows = [[x.get('category', '')[:22], x.get('rate', ''), (x.get('evidence') or '')[:70]] for x in materials.get('localization', [])[:10]]
if rows:
    T(['品类', '国产化率', '依据'], rows, widths=[4.4, 2.6, 8.4], fs=8.5)
P('判断：酶类的国产化已从“能做”进入“敢用”阶段，但在高保真、逆转录与可冻干体系上，进口仍是默认选项。'
  '集采与增值税双重挤压下，客户端对国产替代的接受度在 2026 年会明显上升，前提是批间差与冻干兼容性数据过硬。', size=10)

H('3.3 原料企业要闻', 2)
rows = []
for c in materials.get('companies', []):
    for e in c.get('events', [])[:2]:
        rows.append([c['name'], e.get('date', ''), (e.get('fact') or '')[:64]])
T(['公司', '日期', '事件'], rows, widths=[3.0, 2.2, 10.2], fs=8.5)
note('原料企业共 8 家、22 条要闻；完整清单见 materials.json。')
doc.add_page_break()

# ---------------- 四、板块三 ----------------
H('四、板块三：前沿技术（2025–2027 文献与专利）', 1)
from collections import Counter
cnt = Counter(p['direction'] for p in academic['papers'])
P('本板块基于 46 篇论文（PMID/DOI 全部验真，46/46 通过）与 18 件专利；方向 A=全预混/冻干，B=耐抑制，'
  'C=LAMP/等温，D=跨学科（高分子/应用化学/蛋白质工程/纳米抗体），分布 %s。' %
  '、'.join('%s %d 篇' % (k, v) for k, v in sorted(cnt.items())), size=10)

H('4.1 四个方向的关键结论', 2)
rows = []
for d, name in [('A', '全预混/冻干'), ('B', '耐抑制'), ('C', 'LAMP/等温'), ('D', '跨学科')]:
    ps = [p for p in academic['papers'] if p['direction'] == d]
    if ps:
        top = ps[0]
        rows.append([name, len(ps), top['title'][:46], top['finding'][:70], top['evidence_level']])
T(['方向', '篇数', '代表研究', '关键结论', '证据等级'], rows, widths=[2.4, 1.2, 5.2, 6.0, 1.6], fs=8)

H('4.2 专利：谁在占位', 2)
rows = [[p['no'], p['title'][:34], p.get('applicant', '')[:20], p['year'], p.get('status', '')[:26]]
        for p in academic['patents'][:12]]
T(['专利号', '名称', '申请人', '年份', '法律状态'], rows, widths=[3.6, 5.6, 3.4, 1.2, 2.6], fs=8)
P('观察：国内专利集中在“冻干保护剂 + 全预混稳定剂”这一层（如 CN119876354B 全预混型荧光 PCR 稳定剂、'
  'CN120290518A Ab-Taq 冻干保护剂、CN121294625A 风干型 RT-qPCR），说明这条路线的专利壁垒正在形成；'
  '宝锐若要在冻干体系上做差异化，需要绕开“保护剂配方”这一层，转向“酶本身的热稳定改造 + 封盖工艺控制”。', size=10)

H('4.3 可落地到宝锐的三条建议', 2)
P('建议一：把冻干做成“配方—环境—残留水分”三位一体的指标，而不是只卖保护剂。', bold=True)
P('文献给出的可复现参数：封盖前环境湿度 15%RH、PEG8000 3%(w/v) + 甘露醇 0.5%(w/v)、冻干饼残留水分 3%(w/w)；'
  '另一条路线用海藻糖-羧甲基壳聚糖复合保护剂同时保酶活与抑制引物二聚体。宝锐现有 AS412-9 / AS212 系列'
  '可做平行验证，但对外承诺要落在“残留水分 ≤3.5% + 55℃ 加速 30 天”这类可测指标上。', size=10)
P('建议二：耐抑制别只换酶，要做“定向进化 + 添加剂”的组合矩阵。', bold=True)
P('2025–2027 的耐抑制研究集中在两条：聚合酶定向进化（提高对血液/粪便基质的耐受）与添加剂（BSA、明胶、'
  '甜菜碱、纳米材料）。对销售的含义是：客户抱怨“血样抑制”时，单换酶往往无效，应同时给缓冲体系与添加剂方案，'
  '并用同一套基质做对照实验。', size=10)
P('建议三：LAMP 是差异化机会，但证据等级还停在体外。', bold=True)
P('单引物 PSR/ASLAMP、冻干 LAMP + 侧向流、CRISPR 联用是三条主线；文献里 12 个月多温度稳定性数据已有，'
  '但临床样本验证少。对外话术应限定为“可提供冻干 LAMP 体系与稳定性数据”，不承诺“免提取、免仪器”的临床性能。', size=10)
doc.add_page_break()

# ---------------- 五、口径与缺口 ----------------
H('五、口径、缺口与使用限制', 1)
gaps = []
for name, src in [('市场', market), ('企业', companies), ('原料', materials), ('学术', academic)]:
    for g in (src.get('gaps') or [])[:4]:
        gaps.append([name, str(g)[:90]])
T(['板块', '缺口/限制'], gaps, widths=[1.8, 13.6], fs=8)
P('使用限制：①本报告的中国市场绝对额存在口径差，不可用于对外承诺或预算基线；②2026E/2027E 为机构预测，'
  '不是既成事实；③原料四类细分无公开规模，占比缺口已明确标注；④学术证据以体外为主，临床证据不足。', size=10)

H('附：主要来源', 2)
srcs = []
for x in market['global'][:4] + market['china'][:3]:
    srcs.append([x['source_name'][:40], x.get('publish_date', ''), x['source_url'][:60]])
T(['来源', '发布', '链接'], srcs, widths=[6.0, 1.8, 7.6], fs=8)



# ---------------- 六、客户专题 ----------------
doc.add_page_break()
H('六、客户专题：刘子研名下 7 家集团客户', 1)
P('说明：本专题只覆盖公开信息（公司公告、年报、官网、权威媒体），客户归属以官方客户清单为准（大客户部·刘子研）。'
  '“原料机会点”分“事实”与“推测”两类：事实可直接引用，推测必须由销售在拜访中验证后再用。', size=9.5)

_COMMENT = {
 '浙江东方基因': '自建“生物原料平台”（自产引物探针、抗原抗体、微球），2025 年净亏 5.74 亿元、研发投入同比 -20.54%，降本与国产替代诉求最强。'
                 '外购空间集中在酶与高端预混，不建议推引物探针；冻干技术已成熟，冻干兼容酶是切入点。',
 '复星诊断': '呼吸道小多重明确采用“冻干预分装、常温储运 + UNG 防污 + 18 个月效期”，与宝锐冻干体系、UNG 直接对应；'
             '与西门子合作推国产生化试剂，说明国产化意愿强。单测试价格敏感，需给“同性能降本”方案。',
 '迪安诊断': 'ICL 龙头，但自产产品仅占 3.80%，分子原料自用体量有限；收购迪谱（核酸质谱 + 纳米孔）意味着中期“自研替代”风险。'
             '短期机会在服务端降本与建库/提取试剂，不在大宗酶。',
 '珠海丽珠试剂': '与明德合作落地“全组份单人份冻干 + 实时荧光 PCR”，是 7 家里冻干原料需求最明确的一家；诊断板块 2025 年 -8.56%，降本诉求强。'
                 '决策权在试剂公司，不在丽珠集团，商务路径要直达。',
 '苏州华益美': '血筛主力是单管四合一（HBV/HCV/HIV-1+2）多重扩增，对耐抑制 Taq/RT 与 dNTP 有真实需求；自有提取闭环。'
               '2026 年尼帕病毒 PCR 标称 200 copies/mL，高灵敏体系需要优化酶浓度与缓冲。',
 '上海之江生物': '磁珠已自产并对外销售，磁珠不要主推；战略明确要自研“高性能工具酶”，中期是替代风险、短期仍需外购。'
                 '2025 年直接材料占营业成本 53.48%，对酶价极敏感；冻干新冠试剂已获 III 类证。',
 '厦门艾德生物': '肿瘤 PCR + NGS 双平台，试剂毛利率 89.95%，直接材料占营业成本 26.41% 且同比 +16.91%；前五供应商占采购 45.16%、明确多源化。'
                 '典型“高性能 + 多来源”客户，机会在高保真 Taq、M-MLV、UDG。',
}
for c in cust['customers']:
    H('6.%d %s' % (cust['customers'].index(c) + 1, c['name']), 2)
    f = c.get('finance_2025') or {}
    fin = '；'.join('%s：%s' % (k, v) for k, v in [('2025 营收', f.get('revenue')), ('净利', f.get('net_profit')),
                                                    ('同比', f.get('yoy') or f.get('yoy_revenue'))] if v)
    T(['项目', '内容'], [['代码', c.get('code') or '未上市/未披露'],
                         ['主营', (c.get('business') or '')[:80]],
                         ['2025 财务', fin or '未披露']], widths=[3.0, 12.4], fs=8.5)
    _ev = [[e.get('date', ''), e.get('type', ''), (e.get('fact') or '')[:70]] for e in c.get('events', [])[:4]]
    if _ev:
        P('2025–2027 关键动作', bold=True, size=10)
        T(['日期', '类型', '事件'], _ev, widths=[2.4, 2.0, 11.0], fs=8)
    _pr = [[(p.get('name') or '')[:40], p.get('platform', ''), p.get('target', '')[:24], p.get('approval_date', '')]
           for p in c.get('products', [])[:5]]
    if _pr:
        P('代表产品', bold=True, size=10)
        T(['产品', '平台', '靶标', '获证/上市'], _pr, widths=[7.0, 2.4, 4.0, 2.0], fs=8)
    _rel = c.get('raw_material_relevance', [])[:4]
    if _rel:
        P('原料机会点', bold=True, size=10)
        T(['判断', '依据', '性质'], [[(r.get('point') or '')[:80], (r.get('basis') or '—'), ''] for r in _rel],
          widths=[10.0, 4.0, 1.4], fs=8)
    _key = next((k for k in _COMMENT if k in c['name']), None)
    if _key:
        P('分析：' + _COMMENT[_key], size=10)
    P('来源：' + ('；'.join(sorted(set((e.get('source') or '') for e in c.get('events', []) if e.get('source'))))[:120] or '见附录'), size=8, color=(0x6B, 0x76, 0x84))


# ---------------- 附录 ----------------
doc.add_page_break()
H('附录 A：九家公司 2025–2027 全量要闻', 1)
_rows = []
for c in companies['companies']:
    for e in c.get('events', []):
        _rows.append([c['name'], e.get('date', ''), e.get('type', ''), (e.get('fact') or '')[:56]])
T(['公司', '日期', '类型', '事件'], _rows, widths=[2.2, 2.0, 1.6, 9.6], fs=7.5)
note('共 %d 条，按公司归集；完整字段（含来源链接）见 companies.json 与 HTML 工作台。' % len(_rows))

doc.add_page_break()
H('附录 B：已落地产品清单', 1)
_prow = []
for c in companies['companies']:
    for p_ in c.get('products', []):
        _prow.append([c['name'], (p_.get('name') or '')[:34], p_.get('platform', ''),
                      p_.get('approval_date', ''), p_.get('reg_no') or '—'])
T(['公司', '产品', '平台', '获证/上市', '注册证号'], _prow,
  widths=[2.2, 6.4, 1.8, 2.2, 3.2], fs=7.5)
note('共 %d 个产品；注册证号为空表示公开渠道未披露。' % len(_prow))

doc.save(OUT)
print('✅', OUT, '%.0f KB' % (os.path.getsize(OUT) / 1024))
