feat: 新增语音转文字(ASR)功能
- 后端:新增 /api/audio/transcribe 接口,调用 Ollama whisper 进行语音识别 - 前端:新增 AudioTranscribePage.vue 页面,支持 MP3/WAV/M4A/OGG/FLAC 等格式 - 注册路由、工具卡片、智能助手欢迎语更新 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,261 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""博昇内部培训平台 —— 培训素材内容库(供 PDF / DOCX / MP4 生成脚本共用)"""
|
||||
|
||||
COMPANY_INTRO = {
|
||||
"title": "博昇公司介绍",
|
||||
"sections": [
|
||||
("关于博昇", [
|
||||
"博昇,聚焦企业增长与 AI 落地的综合服务商。",
|
||||
"我们围绕企业从「资本运作」到「资质认定」再到「AI 转型」的完整成长链路,提供四大板块服务,陪伴企业走通从融资到增长、再到智能化升级的全过程。",
|
||||
]),
|
||||
("四大业务板块", [
|
||||
"一、资本咨询:股权规划、融资顾问、IPO 辅导,陪伴企业走通资本路径。",
|
||||
"二、资质认定辅导:高新技术企业、专精特新、专精特新「小巨人」的申报陪跑。",
|
||||
"三、AI 咨询与实施:AI 公开课、落地诊断、软件与定制开发。",
|
||||
"四、企业级 AI 工具与平台:按行业匹配知识库、办公智能体、工业/医疗 AI、内容生成、电商运营等方案。",
|
||||
]),
|
||||
("我们的价值主张", [
|
||||
"以「资本 + AI」双引擎驱动企业成长:用资本咨询帮企业解决钱与路径的问题,用 AI 落地帮企业解决效率与竞争力的升级问题。",
|
||||
"本培训平台用于新员工入职培训与全员持续学习,涵盖公司认知、产品知识、销售能力训练与统一考试验收。",
|
||||
]),
|
||||
],
|
||||
}
|
||||
|
||||
PRODUCT_LINES = {
|
||||
"title": "产品线总览与销售地图",
|
||||
"lines": [
|
||||
("资本咨询类(1.x)", [
|
||||
"引流课、企业内训/定制咨询、企业长期资本顾问合作。",
|
||||
"面向有融资、股权规划、上市需求的企业;销售重点是识别客户所处阶段并匹配资本工具。",
|
||||
]),
|
||||
("资质认定辅导类(2.x)", [
|
||||
"辅导高新技术企业认定、辅导专精特新认定、辅导专精特新「小巨人」。",
|
||||
"面向有资质申报、税收优惠与政策奖补需求的企业;销售重点是帮客户算清「够不够格、能省多少钱」。",
|
||||
]),
|
||||
("AI 咨询类(3.x)", [
|
||||
"AI 公开课(WorkBuddy、企微智能表格)、企业 AI 落地诊断(线上/线下)、AI 软件(一次性授权)、定制软件开发。",
|
||||
"面向想用 AI 提效但不知道从哪下手的客户;销售重点是小步快跑、POC 验证。",
|
||||
]),
|
||||
("企业级 AI 工具与平台(4.x)", [
|
||||
"企业级知识库、多智能体集成工作台、工业检测、机器视觉、医疗多组学、AI 短视频、GEO 优化、电商管理等 16 款产品。",
|
||||
"面向有明确行业场景的客户;销售重点是方案匹配与 ROI 论证。",
|
||||
]),
|
||||
],
|
||||
}
|
||||
|
||||
# —— 资质认定知识(06) ——
|
||||
QUALIFICATION_KNOWLEDGE = {
|
||||
"高企认定八条件": [
|
||||
"高新技术企业(高企)认定须同时满足八项条件:",
|
||||
"1. 注册成立一年以上;",
|
||||
"2. 取得对主要产品(服务)起核心支持作用的知识产权所有权(中国境内授权);",
|
||||
"3. 核心技术属于《国家重点支持的高新技术领域》;",
|
||||
"4. 科技人员占职工总数比例不低于 10%;",
|
||||
"5. 近三个会计年度研发费用总额占销售收入总额比例达标;",
|
||||
"6. 近一年高新技术产品(服务)收入占总收入比例不低于 60%;",
|
||||
"7. 创新能力评价达标(70 分以上);",
|
||||
"8. 申请前一年内未发生重大安全、重大质量事故或严重环境违法。",
|
||||
],
|
||||
"高企研发费用占比": [
|
||||
"按最近一年销售收入分档:",
|
||||
"· 小于 5000 万元(含):不低于 5%;",
|
||||
"· 5000 万元至 2 亿元(含):不低于 4%;",
|
||||
"· 2 亿元以上:不低于 3%。",
|
||||
"其中中国境内研发费用占比不低于 60%。",
|
||||
],
|
||||
"高企知识产权与税收优惠": [
|
||||
"Ⅰ类知识产权:发明专利、植物新品种、国家新药、集成电路布图设计专有权等(权重高);",
|
||||
"Ⅱ类知识产权:实用新型、外观设计、软件著作权(权重低)。",
|
||||
"高企核心收益:企业所得税税率从 25% 降至 15%,并享受研发费用加计扣除。",
|
||||
],
|
||||
"专精特新梯度培育": [
|
||||
"优质中小企业按三级梯度逐级培育、逐级申报:",
|
||||
"创新型中小企业 → 专精特新中小企业 → 专精特新小巨人企业。",
|
||||
"专精特新中小企业是骨干力量(专业化、精细化、特色化、新颖化);小巨人是中坚力量(掌握核心技术、细分市场占有率高)。",
|
||||
],
|
||||
"专精特新小巨人门槛(2026)": [
|
||||
"2026 年新修订办法适当提高门槛:",
|
||||
"· 营业收入提升至 5000 万元以上;",
|
||||
"· 近两年研发费用合计不低于 1200 万元;",
|
||||
"· Ⅰ类知识产权 4 项以上(须为企业自主申请、排名前三)。",
|
||||
"有效期 3 年,到期复核通过后延长 3 年。",
|
||||
],
|
||||
"高企与专精特新的区别": [
|
||||
"高企:科技部门主导,侧重技术创新与研发投入,享 15% 所得税优惠;",
|
||||
"专精特新:工信部门主导,侧重专业化、精细化、特色化、新颖化,获得重点培育与资源倾斜。",
|
||||
"两者可叠加申报,部分地区同年度多项认定可叠加奖励。",
|
||||
],
|
||||
"常见资质认证与补贴": [
|
||||
"ISO 三体系:ISO9001(质量)、ISO14001(环境)、ISO45001(职业健康安全)。",
|
||||
"另有 ISO27001(信息安全)、AAA 信用企业、知识产权贯标、两化融合、CMMI 等。",
|
||||
"政府奖补多仅限首次认证,需保留证书、合同、发票原件,认证机构须经国家认监委认可。",
|
||||
],
|
||||
}
|
||||
|
||||
# —— 资本运作知识(07) ——
|
||||
CAPITAL_KNOWLEDGE = {
|
||||
"股权融资 vs 债权融资": [
|
||||
"股权融资:出让部分股权引入资金,无需偿还、无固定利息,但稀释控制权与分红;",
|
||||
"债权融资:按期还本付息,不稀释股权但增加财务杠杆与偿债压力。",
|
||||
"先判断客户是「缺钱但想保控制权」还是「愿稀释换成长」,再匹配工具。",
|
||||
],
|
||||
"融资轮次与估值方法": [
|
||||
"轮次:种子轮(验证想法)→ 天使轮(团队+原型)→ A 轮(产品跑通)→ B 轮(规模化)→ C 轮及以后 → Pre-IPO。",
|
||||
"估值方法:收益法(DCF 现金流折现)、市场法(可比公司/交易乘数)、资产法(净资产/重置成本)。",
|
||||
"常见乘数:P/E(市值/净利润)、P/S(市值/营收)、P/B(市值/净资产)、PEG(P/E÷增速)。",
|
||||
],
|
||||
"尽职调查与核心条款": [
|
||||
"尽调三维度:业务尽调(模式、客户、市场、收入真实性)、财务尽调(现金流、负债、税务、关联交易)、法律尽调(股权、历史、知识产权、诉讼)。",
|
||||
"核心条款:估值与金额、董事会席位与一票否决权、反稀释、对赌/回购、优先清算权、竞业禁止、期权池(ESOP)。",
|
||||
],
|
||||
"对赌协议与退出方式": [
|
||||
"对赌(VAM):就未来业绩(净利润、营收、上市时间)达成的估值调整,未达成则创始人补偿现金或股权。",
|
||||
"退出:IPO(回报最高但门槛高)、并购(M&A)、股权回购、老股转让。",
|
||||
],
|
||||
"融资材料与路演": [
|
||||
"BP(10–20 页)核心逻辑:一句话定位 → 市场空间 → 产品/模式 → 竞争壁垒 → 团队 → 财务预测与融资计划。",
|
||||
"路演讲清「为什么是你、为什么是现在、钱怎么花、回报是什么」。",
|
||||
],
|
||||
}
|
||||
|
||||
# —— AI 落地知识(08) ——
|
||||
AI_KNOWLEDGE = {
|
||||
"大模型选型": [
|
||||
"闭源(GPT、Claude、文心、通义):开箱即用、性能稳,但数据出域、按量付费、定制受限;",
|
||||
"开源(Llama、Qwen、DeepSeek、GLM):可本地私有化、数据不出域、可微调,但需自建算力与运维。",
|
||||
"7B/13B 可在单卡量化运行;70B 以上需多卡或数据中心。按「任务难度 vs 成本」选型。",
|
||||
],
|
||||
"RAG 构建五步": [
|
||||
"RAG(检索增强生成)让大模型带着企业私域知识作答:",
|
||||
"① 文档解析切块 → ② 文本向量化 → ③ 向量入库 → ④ 检索召回 → ⑤ 拼进提示词生成。",
|
||||
"解决知识过时、幻觉、缺乏私有数据三大痛点。",
|
||||
],
|
||||
"混合检索与提示词工程": [
|
||||
"混合检索 = 向量语义检索 + 关键词检索(BM25)+ 重排(Rerank),兼顾语义与精确。",
|
||||
"提示词技巧:角色设定、明确任务与格式、few-shot 示例、思维链 CoT、设定约束、自检再输出。",
|
||||
],
|
||||
"智能体与 POC 三阶段": [
|
||||
"Agent 能力:工具调用(Function Calling)、任务规划、记忆、多智能体协作;MCP 是连接模型与工具的标准化协议。",
|
||||
"POC 三阶段:数据准备 → 模型与流程搭建 → 效果评估(准确率、召回率、延迟、幻觉率、ROI)。",
|
||||
],
|
||||
"私有化三模式": [
|
||||
"纯私有化(数据不出域、成本高)→ 混合部署(敏感模块私有、通用走云)→ SaaS/公有云 API(快但数据出域)。",
|
||||
"按行业合规要求与预算推荐。",
|
||||
],
|
||||
}
|
||||
|
||||
# —— 销售方法论(09) ——
|
||||
SALES_KNOWLEDGE = {
|
||||
"FABE 法则": [
|
||||
"F(Features 特征):产品本身的特点;",
|
||||
"A(Advantages 优势):比竞品好在哪;",
|
||||
"B(Benefits 利益):给客户的具体价值;",
|
||||
"E(Evidence 证据):案例、数据、演示、证言。",
|
||||
"从客户需求倒推,把特征翻译成客户听得懂的利益,并用证据收尾。",
|
||||
],
|
||||
"SPIN 提问法": [
|
||||
"S(Situation 背景):了解客户现状;",
|
||||
"P(Problem 困难):挖掘现状中的问题;",
|
||||
"I(Implication 暗示):放大问题不解决的后果;",
|
||||
"N(Need-payoff 需求回报):引导客户认同解决方案的价值。",
|
||||
"核心:让客户自己说出痛点、自己意识到价值。",
|
||||
],
|
||||
"LSCPA 异议处理": [
|
||||
"L(Listen 倾听)→ S(Share 同理)→ C(Clarify 澄清)→ P(Present 呈现)→ A(Ask 请求行动)。",
|
||||
"切忌直接反驳,先接情绪再解问题。",
|
||||
],
|
||||
"顾问式销售五步": [
|
||||
"① 建立信任(专业+真诚)→ ② 诊断需求(SPIN)→ ③ 提出方案(FABE)→ ④ 处理异议(LSCPA)→ ⑤ 促成与跟进。",
|
||||
"核心:从「卖产品」转向「帮客户解决问题」。",
|
||||
],
|
||||
"成交信号与技巧": [
|
||||
"成交信号:反复问价格付款、问交付时间、问细节售后、索要方案、沉默思考。",
|
||||
"技巧:直接成交、假设成交、选择成交、稀缺成交(须真实)、从众成交、让步成交。",
|
||||
],
|
||||
}
|
||||
|
||||
# —— 清单 / 检查表(DOCX 用) ——
|
||||
CHECKLISTS = {
|
||||
"高企认定八条件自查清单": [
|
||||
"□ 注册成立满一年以上",
|
||||
"□ 拥有核心支持作用的知识产权(中国境内授权)",
|
||||
"□ 核心技术属于国家重点支持领域",
|
||||
"□ 科技人员占比 ≥ 10%",
|
||||
"□ 研发费用占比达标(按最近一年销售收入分档)",
|
||||
"□ 高新技术产品收入占比 ≥ 60%",
|
||||
"□ 创新能力评价 ≥ 70 分",
|
||||
"□ 前一年无重大安全/质量/环境事故",
|
||||
],
|
||||
"专精特新申报自查表": [
|
||||
"□ 属于优质中小企业梯度培育范围(创新型 → 专精特新 → 小巨人)",
|
||||
"□ 小巨人:营业收入 ≥ 5000 万元",
|
||||
"□ 小巨人:近两年研发费用合计 ≥ 1200 万元",
|
||||
"□ 小巨人:Ⅰ类知识产权 ≥ 4 项(自主申请、排名前三)",
|
||||
"□ 有效期 3 年,到期前复核通过可延长",
|
||||
"□ 每年 5 月 31 日前在培育平台更新信息",
|
||||
],
|
||||
"融资尽调资料清单": [
|
||||
"□ 营业执照、公司章程、股东名册",
|
||||
"□ 近三年财务报表与审计报告",
|
||||
"□ 银行流水、纳税申报表",
|
||||
"□ 核心合同、客户与供应商清单",
|
||||
"□ 知识产权清单与权属证明",
|
||||
"□ 重大诉讼、行政处罚情况",
|
||||
"□ 商业计划书(BP)与财务预测模型",
|
||||
],
|
||||
"AI 落地 POC 检查表": [
|
||||
"□ 明确业务场景与验收标准",
|
||||
"□ 数据准备(清洗、标注、切块)",
|
||||
"□ 模型与检索流程搭建",
|
||||
"□ 设定评估指标(准确率、召回率、延迟、幻觉率、ROI)",
|
||||
"□ 小范围试点与迭代优化",
|
||||
"□ 安全合规(数据脱敏、权限、幻觉控制)",
|
||||
"□ 成本预算与持续运营计划",
|
||||
],
|
||||
}
|
||||
|
||||
# —— 模板(DOCX 用) ——
|
||||
TEMPLATES = {
|
||||
"销售周报模板": [
|
||||
"【本周业绩】",
|
||||
"· 拜访客户数:",
|
||||
"· 有效商机数:",
|
||||
"· 报价/签约金额:",
|
||||
"【重点客户跟进】",
|
||||
"· 客户名称 / 阶段 / 下一步动作 / 预计金额:",
|
||||
"【问题与求助】",
|
||||
"· 遇到的异议 / 需要的支持:",
|
||||
"【下周计划】",
|
||||
"· 重点目标客户与拜访安排:",
|
||||
],
|
||||
"顾问式销售演练脚本": [
|
||||
"开场(建立信任):自我介绍 + 说明来意 + 表明以客户问题为中心。",
|
||||
"诊断(SPIN 提问):先问背景,再挖困难,放大后果,引导价值。",
|
||||
"呈现(FABE):讲利益不讲功能,配合证据。",
|
||||
"异议(LSCPA):先听、再同理、澄清、呈现方案、请求下一步。",
|
||||
"促成(试探成交):把「买不买」变成「怎么买」。",
|
||||
],
|
||||
}
|
||||
|
||||
# —— 考试复习提纲 ——
|
||||
EXAM_REVIEW = {
|
||||
"title": "考试题库复习提纲",
|
||||
"sections": [
|
||||
("公司知识", [
|
||||
"博昇是聚焦企业增长与 AI 落地的综合服务商,双主营:资本咨询 + AI 落地。",
|
||||
"四大板块:资本咨询、资质认定辅导、AI 咨询与实施、企业级 AI 工具与平台。",
|
||||
]),
|
||||
("产品知识", [
|
||||
"四大产品分类:资本咨询类、资质认定辅导类、AI 咨询类、AI 工具平台类。",
|
||||
"重点记忆各产品的收费结构、佣金比例(仅推荐 / 参与谈单)、公开课奖励与报备规则。",
|
||||
]),
|
||||
("销售知识", [
|
||||
"FABE(特征/优势/利益/证据)、SPIN(背景/困难/暗示/需求回报)、LSCPA(倾听/同理/澄清/呈现/请求行动)。",
|
||||
"顾问式销售五步与常见成交技巧、大客户决策链分析。",
|
||||
]),
|
||||
("行业知识", [
|
||||
"高企八条件、研发费用占比、知识产权分类;专精特新梯度与小巨人门槛。",
|
||||
"融资轮次、估值方法、尽调维度、对赌与退出;AI 选型、RAG、Agent、私有化部署。",
|
||||
]),
|
||||
],
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,289 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""生成 15 份 DOCX 培训文档(python-docx)"""
|
||||
import json, os, sys
|
||||
from docx import Document
|
||||
from docx.shared import Pt, RGBColor, Cm
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.oxml.ns import qn
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(os.path.dirname(HERE), "word")
|
||||
sys.path.insert(0, HERE)
|
||||
import content as C
|
||||
|
||||
EAST = "宋体" # Word 兼容的中文字体
|
||||
|
||||
def set_font(run, size=11, bold=False, color=None):
|
||||
run.font.name = "Calibri"
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
if color:
|
||||
run.font.color.rgb = RGBColor(*color)
|
||||
r = run._element
|
||||
rPr = r.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
rFonts.set(qn("w:eastAsia"), EAST)
|
||||
|
||||
def add_para(doc, text, size=11, bold=False, color=None, align=None, space_after=6):
|
||||
p = doc.add_paragraph()
|
||||
if align:
|
||||
p.alignment = align
|
||||
p.paragraph_format.space_after = Pt(space_after)
|
||||
run = p.add_run(text)
|
||||
set_font(run, size, bold, color)
|
||||
return p
|
||||
|
||||
def add_heading(doc, text, level=1):
|
||||
p = doc.add_heading(level=level)
|
||||
run = p.add_run(text)
|
||||
size = {1: 16, 2: 13, 3: 11}[level]
|
||||
set_font(run, size, bold=True, color=(0x1f, 0x4e, 0x79))
|
||||
return p
|
||||
|
||||
def add_bullet(doc, text, size=11):
|
||||
p = doc.add_paragraph(style="List Bullet")
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
run = p.add_run(text)
|
||||
set_font(run, size)
|
||||
return p
|
||||
|
||||
def add_sections(doc, sections):
|
||||
for title, paras in sections:
|
||||
add_heading(doc, title, 2)
|
||||
for para in paras:
|
||||
add_para(doc, para)
|
||||
|
||||
def add_knowledge(doc, knowledge):
|
||||
for title, paras in knowledge.items():
|
||||
add_heading(doc, title, 2)
|
||||
for para in paras:
|
||||
add_para(doc, para)
|
||||
|
||||
def new_doc(title):
|
||||
doc = Document()
|
||||
add_para(doc, title, size=20, bold=True, color=(0x1f, 0x4e, 0x79), align=WD_ALIGN_PARAGRAPH.CENTER, space_after=4)
|
||||
add_para(doc, "博昇内部培训平台 · 学习资料", size=9, color=(0x80, 0x80, 0x80), align=WD_ALIGN_PARAGRAPH.CENTER, space_after=16)
|
||||
return doc
|
||||
|
||||
def save(doc, filename):
|
||||
doc.save(os.path.join(OUT, filename))
|
||||
print(" DOCX 生成:", filename)
|
||||
|
||||
def load_products():
|
||||
return json.load(open(os.path.join(HERE, "products.json")))["data"]
|
||||
|
||||
def load_courses():
|
||||
return json.load(open(os.path.join(HERE, "courses.json")))["data"]
|
||||
|
||||
def load_questions():
|
||||
data = json.load(open(os.path.join(HERE, "questions.json")))["data"]
|
||||
return data if isinstance(data, list) else data.get("items", [])
|
||||
|
||||
CAT_LABEL = {
|
||||
"capital_consulting": "资本咨询类",
|
||||
"qualification_counseling": "资质认定辅导类",
|
||||
"ai_consulting": "AI 咨询类",
|
||||
"ai_tools_platform": "企业级 AI 工具与平台",
|
||||
}
|
||||
|
||||
def products_table(doc, products, columns):
|
||||
"""生成产品速查表。columns: [(表头, 取值函数)]"""
|
||||
table = doc.add_table(rows=1, cols=len(columns))
|
||||
table.style = "Light Grid Accent 1"
|
||||
hdr = table.rows[0].cells
|
||||
for i, (h, _) in enumerate(columns):
|
||||
hdr[i].text = ""
|
||||
run = hdr[i].paragraphs[0].add_run(h)
|
||||
set_font(run, 10, bold=True)
|
||||
for p in products:
|
||||
row = table.add_row().cells
|
||||
for i, (_, fn) in enumerate(columns):
|
||||
row[i].text = ""
|
||||
run = row[i].paragraphs[0].add_run(fn(p))
|
||||
set_font(run, 9)
|
||||
doc.add_paragraph()
|
||||
|
||||
def main():
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
products = load_products()
|
||||
courses = load_courses()
|
||||
questions = load_questions()
|
||||
def cat(c): return [p for p in products if p["category"] == c]
|
||||
|
||||
# 01 新人入职学习计划
|
||||
doc = new_doc("新人入职学习计划")
|
||||
add_heading(doc, "第一阶段:公司认知(第 1 天)", 2)
|
||||
for t in ["阅读《公司介绍》PDF,了解博昇四大业务板块", "浏览公司介绍培训页,完成公司认知学习", "学习产品线总览,建立四大产品分类框架"]:
|
||||
add_bullet(doc, t)
|
||||
add_heading(doc, "第二阶段:产品知识(第 2-4 天)", 2)
|
||||
for t in ["精读四大产品手册:资本咨询 / 资质认定 / AI 咨询 / AI 工具平台", "重点记忆各产品收费、佣金(仅推荐 / 参与谈单)、报备规则", "完成产品知识自测题,目标正确率 90% 以上"]:
|
||||
add_bullet(doc, t)
|
||||
add_heading(doc, "第三阶段:销售能力(第 5-6 天)", 2)
|
||||
for t in ["学习 FABE / SPIN / LSCPA 三大方法论", "完成顾问式销售演练脚本练习", "观看销售技巧培训视频,记录话术要点"]:
|
||||
add_bullet(doc, t)
|
||||
add_heading(doc, "第四阶段:考试验收(第 7 天)", 2)
|
||||
for t in ["复习《考试题库复习提纲》", "完成正式考试,目标通过"]:
|
||||
add_bullet(doc, t)
|
||||
save(doc, "01_新人入职学习计划.docx")
|
||||
|
||||
# 02 产品佣金速查表
|
||||
doc = new_doc("产品佣金速查表")
|
||||
add_para(doc, "下表为全部在售产品的收费与佣金速查,销售谈单时对照使用。", size=10, color=(0x80,0x80,0x80))
|
||||
products_table(doc, products, [
|
||||
("编号", lambda p: p["code"]),
|
||||
("产品", lambda p: p["name"]),
|
||||
("分类", lambda p: CAT_LABEL.get(p["category"], p["category"])),
|
||||
("收费", lambda p: p.get("pricing") or "—"),
|
||||
("佣金(推荐)", lambda p: p.get("commission_recommend") or "—"),
|
||||
("佣金(谈单)", lambda p: p.get("commission_negotiate") or "—"),
|
||||
("公开课奖励", lambda p: p.get("public_course_bonus") or "—"),
|
||||
])
|
||||
save(doc, "02_产品佣金速查表.docx")
|
||||
|
||||
# 03 / 04 销售流程清单(从课程数据)
|
||||
def course_doc(filename, title, category):
|
||||
doc = new_doc(title)
|
||||
found = False
|
||||
for c in courses:
|
||||
if c["category"] != category:
|
||||
continue
|
||||
found = True
|
||||
add_heading(doc, c["name"], 1)
|
||||
field_map = [("目标客户", "target_customers"), ("禁区客户", "forbidden_customers"),
|
||||
("话术", "scripts"), ("销售流程", "sales_process"),
|
||||
("异议处理", "objection_handling"), ("避坑提醒", "delivery_pitfalls"),
|
||||
("报备规则", "report_rules")]
|
||||
for label, key in field_map:
|
||||
v = (c.get(key) or "").strip()
|
||||
if v:
|
||||
add_heading(doc, label, 2)
|
||||
for line in v.splitlines():
|
||||
if line.strip():
|
||||
add_bullet(doc, line.strip())
|
||||
if not found:
|
||||
add_para(doc, "(暂无该分类课程内容)")
|
||||
save(doc, filename)
|
||||
|
||||
course_doc("03_资本咨询销售流程清单.docx", "资本咨询销售流程清单", "capital_script")
|
||||
course_doc("04_资质认定销售流程清单.docx", "资质认定销售流程清单", "qualification_logic")
|
||||
|
||||
# 05-08 清单检查表
|
||||
def checklist_doc(filename, title, items):
|
||||
doc = new_doc(title)
|
||||
add_para(doc, "请逐项核对,完成打勾。", size=10, color=(0x80,0x80,0x80))
|
||||
for it in items:
|
||||
add_bullet(doc, it)
|
||||
save(doc, filename)
|
||||
|
||||
checklist_doc("05_高企认定八条件自查清单.docx", "高企认定八条件自查清单", C.CHECKLISTS["高企认定八条件自查清单"])
|
||||
checklist_doc("06_专精特新申报自查表.docx", "专精特新申报自查表", C.CHECKLISTS["专精特新申报自查表"])
|
||||
checklist_doc("07_融资尽调资料清单.docx", "融资尽调资料清单", C.CHECKLISTS["融资尽调资料清单"])
|
||||
checklist_doc("08_AI落地POC检查表.docx", "AI 落地 POC 检查表", C.CHECKLISTS["AI 落地 POC 检查表"])
|
||||
|
||||
# 09 顾问式销售演练脚本
|
||||
doc = new_doc("顾问式销售演练脚本")
|
||||
add_sections(doc, [(k, v) for k, v in C.SALES_KNOWLEDGE.items()])
|
||||
add_heading(doc, "演练任务", 2)
|
||||
for t in ["两人一组,一人扮演销售、一人扮演客户", "客户提出一个异议(如「价格太贵」),销售用 LSCPA 处理", "销售用 SPIN 挖掘客户 3 个痛点", "用 FABE 介绍一个本平台产品,含证据收尾"]:
|
||||
add_bullet(doc, t)
|
||||
save(doc, "09_顾问式销售演练脚本.docx")
|
||||
|
||||
# 10 SPIN 提问话术练习
|
||||
doc = new_doc("SPIN 提问话术练习")
|
||||
add_knowledge(doc, {"SPIN 提问法": C.SALES_KNOWLEDGE["SPIN 提问法"]})
|
||||
add_heading(doc, "练习:为下列场景设计 SPIN 问题", 2)
|
||||
for t in ["场景一:客户是制造业老板,想了解 AI 但担心成本", "场景二:客户有融资需求但不愿稀释股权", "场景三:客户想申报高企但不确定够不够格"]:
|
||||
add_heading(doc, t, 3)
|
||||
for q in ["背景问题 S:", "困难问题 P:", "暗示问题 I:", "需求回报问题 N:"]:
|
||||
add_bullet(doc, q + "________________________")
|
||||
save(doc, "10_SPIN提问话术练习.docx")
|
||||
|
||||
# 11 LSCPA 异议处理练习
|
||||
doc = new_doc("LSCPA 异议处理练习")
|
||||
add_knowledge(doc, {"LSCPA 异议处理": C.SALES_KNOWLEDGE["LSCPA 异议处理"]})
|
||||
add_heading(doc, "练习:用 LSCPA 处理下列异议", 2)
|
||||
for t in ["「你们收费太贵了」", "「我再考虑考虑」", "「别的公司报价更低」", "「资质认定能不能包过?」"]:
|
||||
add_heading(doc, t, 3)
|
||||
for step in ["倾听 L:", "同理 S:", "澄清 C:", "呈现 P:", "请求行动 A:"]:
|
||||
add_bullet(doc, step + "________________________")
|
||||
save(doc, "11_LSCPA异议处理练习.docx")
|
||||
|
||||
# 12 客户异议处理应对表
|
||||
doc = new_doc("客户异议处理应对表")
|
||||
objections = [
|
||||
("价格太贵", "先澄清:贵在哪(绝对值还是对比值),再讲 FABE 价值与 ROI,必要时让步非核心项。"),
|
||||
("再考虑考虑", "澄清犹豫的真实原因(预算/决策人/竞品),给出限时价值点或案例,约定下一步时间。"),
|
||||
("别的公司更便宜", "不贬低竞品,强调自身差异化优势与证据,帮客户算总拥有成本而非单价。"),
|
||||
("能不能包过", "诚实说明认定有客观标准,我们承诺专业陪跑与最大化通过率,不虚假承诺。"),
|
||||
("没预算", "挖掘隐性需求,讲清不做的后果(税负高/错过政策窗口),引导分期或组合方案。"),
|
||||
]
|
||||
table = doc.add_table(rows=1, cols=2)
|
||||
table.style = "Light Grid Accent 1"
|
||||
h = table.rows[0].cells
|
||||
for i, t in enumerate(["常见异议", "应对策略"]):
|
||||
h[i].text = ""
|
||||
run = h[i].paragraphs[0].add_run(t); set_font(run, 10, bold=True)
|
||||
for obj, resp in objections:
|
||||
row = table.add_row().cells
|
||||
for i, v in enumerate([obj, resp]):
|
||||
row[i].text = ""
|
||||
run = row[i].paragraphs[0].add_run(v); set_font(run, 9)
|
||||
save(doc, "12_客户异议处理应对表.docx")
|
||||
|
||||
# 13 销售周报模板
|
||||
doc = new_doc("销售周报模板")
|
||||
add_para(doc, "每周五下班前填写,提交给直属主管。", size=10, color=(0x80,0x80,0x80))
|
||||
for line in C.TEMPLATES["销售周报模板"]:
|
||||
add_para(doc, line)
|
||||
save(doc, "13_销售周报模板.docx")
|
||||
|
||||
# 14 考试模拟练习卷(取真实题库)
|
||||
doc = new_doc("考试模拟练习卷")
|
||||
add_para(doc, "本卷从平台题库抽取,共 %d 题,用于考前自测(答案与解析见末尾)。" % len(questions), size=10, color=(0x80,0x80,0x80))
|
||||
domain_label = {"company": "公司知识", "product": "产品知识", "sales": "销售知识"}
|
||||
type_label = {"single": "单选", "multiple": "多选", "judge": "判断"}
|
||||
for i, q in enumerate(questions, 1):
|
||||
stem = q["stem"]
|
||||
add_para(doc, "%d. [%s·%s] %s" % (i, type_label.get(q["type"], q["type"]), domain_label.get(q["domain"], q["domain"]), stem), bold=True)
|
||||
opts = q.get("options") or []
|
||||
if isinstance(opts, list):
|
||||
for o in opts:
|
||||
if isinstance(o, dict):
|
||||
add_para(doc, " %s. %s" % (o.get("key"), o.get("text")), size=10)
|
||||
else:
|
||||
add_para(doc, " - %s" % o, size=10)
|
||||
add_para(doc, "", size=6)
|
||||
add_heading(doc, "参考答案与解析", 1)
|
||||
for i, q in enumerate(questions, 1):
|
||||
ans = q.get("answer") or []
|
||||
ans_txt = "、".join(ans) if isinstance(ans, list) else str(ans)
|
||||
if q["type"] == "judge":
|
||||
ans_txt = "正确" if (ans and ans[0] in ("true", "正确")) else "错误"
|
||||
exp = q.get("explanation") or ""
|
||||
add_para(doc, "%d. 答案:%s" % (i, ans_txt), size=10)
|
||||
if exp:
|
||||
add_para(doc, " 解析:%s" % exp, size=9, color=(0x80,0x80,0x80))
|
||||
save(doc, "14_考试模拟练习卷.docx")
|
||||
|
||||
# 15 产品知识速查手册
|
||||
doc = new_doc("产品知识速查手册")
|
||||
add_para(doc, "全部 %d 个产品的完整字段速查,含说明、收费、佣金、规则。" % len(products), size=10, color=(0x80,0x80,0x80))
|
||||
for c in ["capital_consulting", "qualification_counseling", "ai_consulting", "ai_tools_platform"]:
|
||||
ps = cat(c)
|
||||
if not ps:
|
||||
continue
|
||||
add_heading(doc, CAT_LABEL[c], 1)
|
||||
for p in ps:
|
||||
add_heading(doc, "%s(%s)" % (p["name"], p["code"]), 2)
|
||||
for label, key in [("标签", "tags"), ("说明", "description"), ("收费", "pricing"),
|
||||
("佣金(推荐)", "commission_recommend"), ("佣金(谈单)", "commission_negotiate"),
|
||||
("公开课奖励", "public_course_bonus"), ("版本风险", "version_risk"), ("报备规则", "report_rules")]:
|
||||
v = (p.get(key) or "").strip()
|
||||
if v:
|
||||
add_bullet(doc, "%s:%s" % (label, v))
|
||||
save(doc, "15_产品知识速查手册.docx")
|
||||
|
||||
print("完成 15 份 DOCX")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""生成 10 个 MP4 培训视频(Pillow 渲染幻灯片 + ffmpeg 合成)"""
|
||||
import os, sys, subprocess, textwrap
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(os.path.dirname(HERE), "mp4")
|
||||
WORK = os.path.join(HERE, "slides")
|
||||
sys.path.insert(0, HERE)
|
||||
import content as C
|
||||
|
||||
W, H = 1280, 720
|
||||
FONT_PATH = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
|
||||
FONT_BOLD = "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc"
|
||||
|
||||
BG = (26, 60, 100) # 深蓝背景
|
||||
BG_TITLE = (15, 45, 80)
|
||||
ACCENT = (96, 165, 250) # 亮蓝
|
||||
WHITE = (255, 255, 255)
|
||||
GREY = (200, 210, 220)
|
||||
|
||||
def load_font(path, size, index=0):
|
||||
return ImageFont.truetype(path, size, index=index)
|
||||
|
||||
def wrap(text, width):
|
||||
# 按字符宽度折行(中文近似等宽)
|
||||
return textwrap.wrap(text, width=width)
|
||||
|
||||
def render_title_slide(path, title, subtitle):
|
||||
img = Image.new("RGB", (W, H), BG_TITLE)
|
||||
d = ImageDraw.Draw(img)
|
||||
# 顶部装饰条
|
||||
d.rectangle([0, 0, W, 10], fill=ACCENT)
|
||||
d.rectangle([0, H-10, W, H], fill=ACCENT)
|
||||
f_title = load_font(FONT_BOLD, 72)
|
||||
f_sub = load_font(FONT_PATH, 34)
|
||||
# 居中标题
|
||||
for i, line in enumerate(wrap(title, 14)):
|
||||
bbox = d.textbbox((0,0), line, font=f_title)
|
||||
tw = bbox[2]-bbox[0]
|
||||
d.text(((W-tw)//2, 250 + i*90), line, font=f_title, fill=WHITE)
|
||||
bbox = d.textbbox((0,0), subtitle, font=f_sub)
|
||||
sw = bbox[2]-bbox[0]
|
||||
d.text(((W-sw)//2, 470), subtitle, font=f_sub, fill=GREY)
|
||||
d.text((W-360, H-60), "博昇内部培训平台", font=load_font(FONT_PATH, 26), fill=GREY)
|
||||
img.save(path)
|
||||
|
||||
def render_slide(path, title, bullets, idx, total):
|
||||
img = Image.new("RGB", (W, H), BG)
|
||||
d = ImageDraw.Draw(img)
|
||||
d.rectangle([0, 0, W, 8], fill=ACCENT)
|
||||
f_title = load_font(FONT_BOLD, 52)
|
||||
f_bullet = load_font(FONT_PATH, 40)
|
||||
f_small = load_font(FONT_PATH, 24)
|
||||
# 标题
|
||||
d.text((60, 50), title, font=f_title, fill=WHITE)
|
||||
d.line([(60, 130), (W-60, 130)], fill=ACCENT, width=2)
|
||||
# 条目
|
||||
y = 170
|
||||
for b in bullets:
|
||||
lines = wrap(b, 24)
|
||||
for line in lines:
|
||||
if y > H - 100:
|
||||
break
|
||||
d.ellipse([60, y+16, 74, y+30], fill=ACCENT)
|
||||
d.text((92, y), line, font=f_bullet, fill=WHITE)
|
||||
y += 58
|
||||
# 页码 + 页脚
|
||||
d.text((W-220, H-60), "%d / %d" % (idx, total), font=f_small, fill=GREY)
|
||||
d.text((60, H-60), "博昇内部培训平台", font=f_small, fill=GREY)
|
||||
img.save(path)
|
||||
|
||||
def make_slides(title, items, max_bullets=6):
|
||||
"""items: list of (小标题, [要点]) → 拆分为多张幻灯片"""
|
||||
slides = []
|
||||
for sub, bullets in items:
|
||||
# 按 max_bullets 拆分
|
||||
for i in range(0, len(bullets), max_bullets):
|
||||
chunk = bullets[i:i+max_bullets]
|
||||
s_title = sub if i == 0 else sub + "(续)"
|
||||
slides.append((s_title, chunk))
|
||||
if not slides:
|
||||
slides = [("内容", ["(暂无内容)"])]
|
||||
return slides
|
||||
|
||||
def build_video(filename, title, items, title_dur=4, slide_dur=6):
|
||||
os.makedirs(WORK, exist_ok=True)
|
||||
slides = make_slides(title, items)
|
||||
total = len(slides) + 1
|
||||
# 渲染标题页 + 内容页
|
||||
render_title_slide(os.path.join(WORK, "s000.png"), title, "博昇内部培训平台 · 培训课程")
|
||||
for i, (sub, bullets) in enumerate(slides, 1):
|
||||
render_slide(os.path.join(WORK, "s%03d.png" % i), sub, bullets, i, total)
|
||||
# ffmpeg 合成
|
||||
n = total
|
||||
inputs = []
|
||||
for i in range(n):
|
||||
dur = title_dur if i == 0 else slide_dur
|
||||
inputs += ["-framerate", "25", "-loop", "1", "-t", str(dur), "-i", os.path.join(WORK, "s%03d.png" % i)]
|
||||
# 每个输入归一化后再 concat
|
||||
parts = []
|
||||
for i in range(n):
|
||||
parts.append("[%d:v]scale=1280:720,setsar=1,format=yuv420p[v%d]" % (i, i))
|
||||
concat = "".join("[v%d]" % i for i in range(n)) + "concat=n=%d:v=1:a=0[v]" % n
|
||||
fc = ";".join(parts) + ";" + concat
|
||||
out = os.path.join(OUT, filename)
|
||||
cmd = ["ffmpeg", "-y", *inputs,
|
||||
"-f", "lavfi", "-i", "anullsrc=channel_layout=stereo:sample_rate=44100",
|
||||
"-filter_complex", fc,
|
||||
"-map", "[v]", "-map", "%d:a" % n,
|
||||
"-c:v", "libx264", "-preset", "veryfast", "-crf", "23", "-pix_fmt", "yuv420p",
|
||||
"-r", "25", "-c:a", "aac", "-b:a", "64k", "-shortest",
|
||||
out]
|
||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
print(" MP4 失败:", filename, r.stderr[-300:])
|
||||
else:
|
||||
sz = os.path.getsize(out)
|
||||
print(" MP4 生成:", filename, "(%d 页, %.1f KB)" % (n, sz/1024))
|
||||
|
||||
def main():
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
videos = [
|
||||
("01_公司介绍.mp4", "公司介绍", C.COMPANY_INTRO["sections"]),
|
||||
("02_产品线总览.mp4", "产品线总览", C.PRODUCT_LINES["lines"]),
|
||||
("03_高企认定八大条件.mp4", "高企认定八大条件", [
|
||||
("八项条件", C.QUALIFICATION_KNOWLEDGE["高企认定八条件"]),
|
||||
("研发费用占比", C.QUALIFICATION_KNOWLEDGE["高企研发费用占比"]),
|
||||
]),
|
||||
("04_专精特新梯度培育.mp4", "专精特新梯度培育", [
|
||||
("梯度体系", C.QUALIFICATION_KNOWLEDGE["专精特新梯度培育"]),
|
||||
("小巨人门槛", C.QUALIFICATION_KNOWLEDGE["专精特新小巨人门槛(2026)"]),
|
||||
]),
|
||||
("05_资本咨询销售要点.mp4", "资本咨询销售要点", [
|
||||
("融资工具选择", C.CAPITAL_KNOWLEDGE["股权融资 vs 债权融资"]),
|
||||
("轮次与估值", C.CAPITAL_KNOWLEDGE["融资轮次与估值方法"]),
|
||||
("尽调与条款", C.CAPITAL_KNOWLEDGE["尽职调查与核心条款"]),
|
||||
]),
|
||||
("06_企业AI落地方法论.mp4", "企业 AI 落地方法论", [
|
||||
("大模型选型", C.AI_KNOWLEDGE["大模型选型"]),
|
||||
("RAG 构建五步", C.AI_KNOWLEDGE["RAG 构建五步"]),
|
||||
("私有化三模式", C.AI_KNOWLEDGE["私有化三模式"]),
|
||||
]),
|
||||
("07_顾问式销售FABE法则.mp4", "顾问式销售 · FABE 法则", [
|
||||
("FABE 法则", C.SALES_KNOWLEDGE["FABE 法则"]),
|
||||
("顾问式销售五步", C.SALES_KNOWLEDGE["顾问式销售五步"]),
|
||||
]),
|
||||
("08_SPIN提问技巧.mp4", "SPIN 提问技巧", [
|
||||
("SPIN 提问法", C.SALES_KNOWLEDGE["SPIN 提问法"]),
|
||||
]),
|
||||
("09_LSCPA异议处理.mp4", "LSCPA 异议处理", [
|
||||
("LSCPA 异议处理", C.SALES_KNOWLEDGE["LSCPA 异议处理"]),
|
||||
]),
|
||||
("10_销售成交技巧.mp4", "销售成交技巧", [
|
||||
("成交信号", C.SALES_KNOWLEDGE["成交信号与技巧"]),
|
||||
]),
|
||||
]
|
||||
for fn, title, items in videos:
|
||||
build_video(fn, title, items)
|
||||
print("完成 %d 个 MP4" % len(videos))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""生成 15 份 PDF 培训文档(reportlab)"""
|
||||
import json, os, sys
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(os.path.dirname(HERE), "pdf")
|
||||
sys.path.insert(0, HERE)
|
||||
import content as C
|
||||
|
||||
pdfmetrics.registerFont(UnicodeCIDFont("STSong-Light"))
|
||||
FONT = "STSong-Light"
|
||||
|
||||
def esc(s):
|
||||
return (s or "").replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
|
||||
def styles():
|
||||
ss = getSampleStyleSheet()
|
||||
ss["Title"].fontName = FONT
|
||||
ss["Heading1"].fontName = FONT
|
||||
ss["Heading1"].fontSize = 16
|
||||
ss["Heading1"].textColor = colors.HexColor("#1f4e79")
|
||||
body = ParagraphStyle("body", parent=ss["Normal"], fontName=FONT, fontSize=11, leading=18, spaceAfter=6)
|
||||
bullet = ParagraphStyle("bullet", parent=body, leftIndent=16, bulletIndent=4, spaceAfter=4)
|
||||
sub = ParagraphStyle("sub", parent=ss["Heading2"], fontName=FONT, fontSize=13, textColor=colors.HexColor("#2e6da4"), spaceBefore=10, spaceAfter=6)
|
||||
return ss, body, bullet, sub
|
||||
|
||||
def load_products():
|
||||
p = json.load(open(os.path.join(HERE, "products.json")))["data"]
|
||||
return p
|
||||
|
||||
def load_courses():
|
||||
return json.load(open(os.path.join(HERE, "courses.json")))["data"]
|
||||
|
||||
CAT_LABEL = {
|
||||
"capital_consulting": "资本咨询类",
|
||||
"qualification_counseling": "资质认定辅导类",
|
||||
"ai_consulting": "AI 咨询类",
|
||||
"ai_tools_platform": "企业级 AI 工具与平台",
|
||||
}
|
||||
|
||||
def add_products(story, products, body, sub, bullet):
|
||||
by_cat = {}
|
||||
for p in products:
|
||||
by_cat.setdefault(p["category"], []).append(p)
|
||||
order = ["capital_consulting", "qualification_counseling", "ai_consulting", "ai_tools_platform"]
|
||||
for cat in order:
|
||||
if cat not in by_cat:
|
||||
continue
|
||||
story.append(Paragraph(esc(CAT_LABEL.get(cat, cat)), sub))
|
||||
for p in by_cat[cat]:
|
||||
story.append(Paragraph("<b>%s(%s)</b>" % (esc(p["name"]), esc(p["code"])), body))
|
||||
fields = [
|
||||
("标签", p.get("tags")), ("说明", p.get("description")), ("收费", p.get("pricing")),
|
||||
("佣金(仅推荐)", p.get("commission_recommend")), ("佣金(参与谈单)", p.get("commission_negotiate")),
|
||||
("公开课奖励", p.get("public_course_bonus")), ("版本风险", p.get("version_risk")),
|
||||
("报备规则", p.get("report_rules")),
|
||||
]
|
||||
for k, v in fields:
|
||||
if v and str(v).strip():
|
||||
story.append(Paragraph("· %s:%s" % (k, esc(v)), bullet))
|
||||
|
||||
def add_sections(story, sections, body, sub):
|
||||
for title, paras in sections:
|
||||
story.append(Paragraph(esc(title), sub))
|
||||
for para in paras:
|
||||
story.append(Paragraph(esc(para), body))
|
||||
|
||||
def add_knowledge(story, knowledge, body, sub):
|
||||
for title, paras in knowledge.items():
|
||||
story.append(Paragraph(esc(title), sub))
|
||||
for para in paras:
|
||||
story.append(Paragraph(esc(para), body))
|
||||
|
||||
def build(filename, title, blocks):
|
||||
doc = SimpleDocTemplate(os.path.join(OUT, filename), pagesize=A4,
|
||||
topMargin=20*mm, bottomMargin=20*mm, leftMargin=20*mm, rightMargin=20*mm)
|
||||
ss, body, bullet, sub = styles()
|
||||
story = []
|
||||
story.append(Paragraph(esc(title), ss["Title"]))
|
||||
story.append(Spacer(1, 6*mm))
|
||||
story.append(Paragraph("博昇内部培训平台 · 学习资料", ParagraphStyle("tag", parent=body, fontSize=9, textColor=colors.grey)))
|
||||
story.append(Spacer(1, 6*mm))
|
||||
for kind, data in blocks:
|
||||
if kind == "sections":
|
||||
add_sections(story, data, body, sub)
|
||||
elif kind == "knowledge":
|
||||
add_knowledge(story, data, body, sub)
|
||||
elif kind == "products":
|
||||
add_products(story, data, body, sub, bullet)
|
||||
doc.build(story)
|
||||
print(" PDF 生成:", filename)
|
||||
|
||||
def main():
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
products = load_products()
|
||||
courses = load_courses()
|
||||
def cat(c): return [p for p in products if p["category"] == c]
|
||||
|
||||
jobs = [
|
||||
("01_公司介绍.pdf", "博昇公司介绍", [("sections", C.COMPANY_INTRO["sections"])]),
|
||||
("02_产品线总览与销售地图.pdf", "产品线总览与销售地图", [("sections", C.PRODUCT_LINES["lines"])]),
|
||||
("03_资本咨询类产品手册.pdf", "资本咨询类产品手册", [("products", cat("capital_consulting"))]),
|
||||
("04_资质认定辅导类产品手册.pdf", "资质认定辅导类产品手册", [("products", cat("qualification_counseling"))]),
|
||||
("05_AI咨询类产品手册.pdf", "AI 咨询类产品手册", [("products", cat("ai_consulting"))]),
|
||||
("06_企业级AI工具平台产品手册.pdf", "企业级 AI 工具与平台产品手册", [("products", cat("ai_tools_platform"))]),
|
||||
("07_高新技术企业认定政策解读.pdf", "高新技术企业认定政策解读", [("knowledge", {
|
||||
"高企认定八条件": C.QUALIFICATION_KNOWLEDGE["高企认定八条件"],
|
||||
"高企研发费用占比": C.QUALIFICATION_KNOWLEDGE["高企研发费用占比"],
|
||||
"知识产权与税收优惠": C.QUALIFICATION_KNOWLEDGE["高企知识产权与税收优惠"],
|
||||
})]),
|
||||
("08_专精特新企业认定政策解读.pdf", "专精特新企业认定政策解读", [("knowledge", {
|
||||
"梯度培育体系": C.QUALIFICATION_KNOWLEDGE["专精特新梯度培育"],
|
||||
"小巨人门槛(2026)": C.QUALIFICATION_KNOWLEDGE["专精特新小巨人门槛(2026)"],
|
||||
"高企与专精特新的区别": C.QUALIFICATION_KNOWLEDGE["高企与专精特新的区别"],
|
||||
"常见资质认证与补贴": C.QUALIFICATION_KNOWLEDGE["常见资质认证与补贴"],
|
||||
})]),
|
||||
("09_资本运作与股权融资基础讲义.pdf", "资本运作与股权融资基础讲义", [("knowledge", C.CAPITAL_KNOWLEDGE)]),
|
||||
("10_企业AI落地方法论讲义.pdf", "企业 AI 落地方法论讲义", [("knowledge", C.AI_KNOWLEDGE)]),
|
||||
("11_顾问式销售方法论讲义.pdf", "顾问式销售方法论讲义", [("knowledge", C.SALES_KNOWLEDGE)]),
|
||||
("12_销售话术手册_资本咨询.pdf", "销售话术手册:资本咨询", [("sections", course_sections(courses, "capital_script"))]),
|
||||
("13_销售话术手册_资质认定.pdf", "销售话术手册:资质认定", [("sections", course_sections(courses, "qualification_logic"))]),
|
||||
("14_新人入职培训手册.pdf", "新人入职培训手册", [
|
||||
("sections", C.COMPANY_INTRO["sections"]),
|
||||
("sections", C.PRODUCT_LINES["lines"]),
|
||||
("knowledge", C.SALES_KNOWLEDGE),
|
||||
]),
|
||||
("15_考试题库复习提纲.pdf", "考试题库复习提纲", [("sections", C.EXAM_REVIEW["sections"])]),
|
||||
]
|
||||
for fn, title, blocks in jobs:
|
||||
build(fn, title, blocks)
|
||||
print("完成 %d 份 PDF" % len(jobs))
|
||||
|
||||
def course_sections(courses, category):
|
||||
sections = []
|
||||
for c in courses:
|
||||
if c["category"] != category:
|
||||
continue
|
||||
paras = []
|
||||
field_map = [
|
||||
("目标客户", "target_customers"), ("禁区客户", "forbidden_customers"),
|
||||
("话术", "scripts"), ("销售流程", "sales_process"),
|
||||
("异议处理", "objection_handling"), ("避坑提醒", "delivery_pitfalls"),
|
||||
("报备规则", "report_rules"),
|
||||
]
|
||||
for label, key in field_map:
|
||||
v = c.get(key)
|
||||
if v and str(v).strip():
|
||||
paras.append("%s:%s" % (label, v.strip()))
|
||||
sections.append((c["name"], paras if paras else ["(暂无详细内容)"]))
|
||||
return sections
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user