docs/share-images.md
分享图片用于把个股分析和市场复盘转换为适合社交平台传播的 1080px 长图。个股和大盘使用两套独立的信息结构,但共用 DSA 品牌、仓库标识 ZhuLinsen/daily_stock_analysis 和风险声明。GitHub 区不放二维码;小红书区域由部署配置决定,未配置时整块隐藏,避免 fork 或私有部署默认宣传维护者账号。
现有通知链路不需要手工组装图片数据:
个股 AnalysisResult
-> AnalysisResult.to_dict() 结构化 JSON + 稳定 Markdown
-> share_image 优先读取 JSON,Markdown 兼容回退
-> 个股决策卡 HTML
-> wkhtmltoimage / markdown-to-file / Playwright 输出 PNG
大盘 MarketOverview + market_light + LLM 复盘
-> MarketAnalyzer 生成 market_review_payload + 稳定 Markdown
-> share_image 优先读取 payload,Markdown 兼容回退
-> 市场复盘卡 HTML
-> wkhtmltoimage / markdown-to-file / Playwright 输出 PNG
MARKDOWN_TO_IMAGE_CHANNELS、MD2IMG_ENGINE、MARKDOWN_TO_IMAGE_MAX_CHARS 继续控制哪些通知渠道转图、使用哪个引擎以及最大输入长度。转换失败时仍回退为文本通知。
小红书品牌使用以下可选配置,四项全部留空即关闭该区域:
SHARE_IMAGE_XIAOHONGSHU_URL=https://example.com/my-xiaohongshu
SHARE_IMAGE_XIAOHONGSHU_HANDLE=@我的账号
SHARE_IMAGE_XIAOHONGSHU_ID=123456789
SHARE_IMAGE_XIAOHONGSHU_QR_PATH=assets/my-xiaohongshu-qr.png
二维码路径支持绝对路径或相对项目根目录路径;冻结桌面后端也会从 PyInstaller 资源目录解析相对路径。账号 URL 只接受 http:// 或 https://。二维码在转图时以内嵌 Data URI 渲染,不依赖运行时网络。
浏览器版历史个股报告、市场复盘和完整报告抽屉右上角都会显示“分享”按钮。页面加载报告时不会生成图片;只有用户点击“分享”后,页面才调用 GET /api/v1/history/{record_id}/share-image 按需生成或读取缓存 PNG。支持文件分享的浏览器会在图片准备好后提示“再次点击分享”,由第二次点击同步打开系统分享面板,避免异步生成过程使浏览器的用户激活状态失效;其他浏览器会在首次生成完成后直接下载 PNG。如果系统分享面板打开失败,除用户主动取消外也会自动回退下载已经生成的 PNG。
Electron 桌面运行时默认不展示该按钮。当前 Windows/macOS 打包版不会随包分发 wkhtmltoimage、markdown-to-file 或 Playwright/Chromium renderer,避免桌面用户在页面加载时就命中 share_image_unavailable 失败态。
Web 手工生成不受 MARKDOWN_TO_IMAGE_CHANNELS 限制,但服务端仍需配置可用的 MD2IMG_ENGINE。使用 Playwright 时先执行:
cd apps/dsa-web
npm ci
npx playwright install chromium
结构化数据用于精确填充名称、动作、评分、价格、宽度和板块等字段;Markdown 仍负责兼容旧调用以及计划、风险等文本章节。部分历史 JSON 只覆盖其中实际存在的字段,不会清空 Markdown 中已有的行情、技术参考或执行点位。模板不根据分数自行推导操作,也不补造价格或指标。字段为 N/A、-、空值或没有对应模块时,相关卡片自动隐藏。
| 图片区域 | 项目字段 / 生成来源 | 填充规则 |
|---|---|---|
| 股票名称、代码 | AnalysisResult.name、AnalysisResult.code | 直接读取结构化字段,Markdown 标题仅作回退 |
| 操作、评分、趋势 | action_label / operation_advice、sentiment_score、trend_prediction、confidence_level | 使用最终校准结果,评分范围 0–100,并标明结论置信度 |
| 核心结论 | dashboard.core_conclusion.one_sentence | 没有时隐藏 |
| 市场快照 | market_snapshot | 当前/收盘价、涨跌幅、量比、换手率按可用字段展示;数据源进入底部声明 |
| 执行计划 | dashboard.battle_plan.sniper_points | 只展示理想/确认买入、止损和首个目标价格;复杂触发条件保留在完整报告 |
| 技术参考 | dashboard.data_perspective | 展示均线状态、趋势分、MA5 乖离、支撑和压力;结构化量比/换手已进入快照时不重复展示冗长量能描述,旧记录缺失结构化量能时仍保留 Markdown 兜底 |
| 下一步观察 | dashboard.phase_decision | 展示行动窗口、下次检查时间和最多两条观察条件 |
| 催化与风险 | dashboard.intelligence | positive_catalysts 与 risk_alerts 最多各展示 2 条短摘要 |
| 持仓建议 | core_conclusion.position_advice | 只区分未持仓和已持仓,仓位、建仓、风控长文保留在完整报告 |
模板支持项目当前的中文、英文和韩文报告标签,海报栏目、指标标签和底部声明跟随报告语言。一个“决策仪表盘”只有一只股票时会自动使用个股卡;包含多只股票时保留多股报告布局,避免错误地把第一只股票当成整份报告。强烈买入、Strong Buy 等复合动作会保留完整动作标签。
| 图片区域 | 项目字段 / 生成来源 | 填充规则 |
|---|---|---|
| 日期、市场范围 | MarketOverview.date、复盘区域 | 生成 A股/美股/港股/日股/韩股市场复盘标题;多市场报告逐段匹配 market_review_payload.markets |
| 市场信号 | market_light.score、temperature_label、label、guidance | 使用确定性市场灯号结果,不由模板二次评分 |
| 指数表现 | MarketOverview.indices、color_scheme | 最多展示 4 个主要指数的最新值和涨跌幅;结构化 payload 持久化生成时的 green_up / red_up 颜色语义 |
| 市场宽度 | up_count、down_count、limit_up_count、limit_down_count、total_amount | 仅在数据源支持且报告包含结构化数据时展示 |
| 信号拆解 | market_light.dimensions | 只展示 available != false 的确定性评分;不把不支持的维度占位分 50 当作真实数据 |
| 强弱板块 | sectors.top、sectors.bottom | 领涨、领跌各展示 Top 3;没有板块榜的市场自动隐藏 |
| 资金观察 | 复盘“资金与情绪”章节 | 提炼涨跌比、增量成交和资金风格,不把成交额或新闻推断伪装成净流入 |
| 重点跟踪 | 复盘“明日交易计划”的关注/回避方向 | 最多各展示 2 个板块或主题;当前 payload 没有 leader_stocks,因此不编造重点个股 |
| 明日策略 | 复盘“明日交易计划”章节 | 展示结论、仓位区间和失效条件,不与重点跟踪重复 |
| 风险提示 | 复盘“风险提示”章节 | 最多展示 2 条,过滤重复免责声明 |
模板仍接受项目生成的 Markdown;新链路会额外传入 AnalysisResult.to_dict() 或 market_review_payload。仅调试兼容回退时,可以准备一份最小个股报告:
## 🟢 贵州茅台 (600519)
> 2026-07-31 15:00 | 评分: **72** | 看多
### 📌 核心结论
**买入**: 趋势偏强,等待回踩支撑后分批执行。
| 持仓情况 | 操作建议 |
| --- | --- |
| 空仓者 | 等待回踩确认,不追高。 |
| 持仓者 | 继续持有,跌破止损位退出。 |
### 🎯 作战计划
| 点位类型 | 价格 |
| --- | --- |
| 理想买入点 | 1420-1450 |
| 次优买入点 | 1380-1400 |
| 止损位 | 1350 |
| 目标位 | 1580 |
生成 HTML 预览:
from pathlib import Path
from src.share_image import ShareImageBranding, build_share_image_html
markdown_text = Path("reports/example.md").read_text(encoding="utf-8")
branding = ShareImageBranding(
xiaohongshu_url="https://example.com/my-xiaohongshu",
xiaohongshu_handle="@我的账号",
xiaohongshu_qr_path="assets/my-xiaohongshu-qr.png",
)
html = build_share_image_html(markdown_text, branding=branding)
Path("share-preview.html").write_text(html, encoding="utf-8")
按真实运行数据预览时传入结构化结果:
html = build_share_image_html(
markdown_text,
structured_payload=analysis_result.to_dict(),
)
实际通知转 PNG 仍调用:
from src.md2img import markdown_to_image
png_bytes = markdown_to_image(
markdown_text,
structured_payload=analysis_result.to_dict(),
)
大盘报告应沿用 MarketAnalyzer 生成的“盘面信号、指数结构、板块主线、消息催化、明日交易计划、风险提示”章节;不建议在外部另造一套字段名称,否则模板会按缺失字段处理。
color_scheme,旧记录则从最终报告颜色标记恢复;模板不按市场地区硬编码涨跌色。ZhuLinsen/daily_stock_analysis,不生成二维码。