From 42223969db21fc3191283ec5751824b1f3ea1790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=BB=E9=95=BF=E5=8F=91?= Date: Thu, 3 Sep 2026 15:42:05 +0800 Subject: [PATCH] Initial commit of Tmall new-product analysis loop v9.2.5. Co-authored-by: Cursor --- .gitignore | 20 + CHANGELOG_V9.1.0.md | 25 + CHANGELOG_V9.2.0.md | 41 + CHANGELOG_V9.2.1.md | 14 + CHANGELOG_V9.2.2.md | 7 + CHANGELOG_V9.2.3.md | 6 + CHANGELOG_V9.2.4.md | 22 + CHANGELOG_V9.2.5.md | 11 + PROMPT_ENGINEERING_V9.2.4.md | 84 + PROMPT_ENGINEERING_V9.2.5.md | 37 + README.md | 32 + REFERENCE_REPORT_V9.2.5.html | 271 ++ REPORT_OUTPUT_STANDARD_V9.2.4.md | 40 + REPORT_OUTPUT_STANDARD_V9.2.5.md | 12 + REPORT_OUTPUT_STANDARD_V9.2.md | 32 + VERSION.txt | 1 + .../.codex-plugin/plugin.json | 23 + .../sansong-tmall-product-report/README.md | 10 + .../assets/capture-extension/README.md | 3 + .../assets/capture-extension/manifest.json | 26 + .../assets/capture-extension/page_bridge.js | 6 + .../capture-extension/service_worker.js | 187 + .../assets/taobao_detail_fields_v8.3.6.xlsx | Bin 0 -> 9165 bytes .../scripts/normalize_capture.mjs | 52 + .../tmall-product-analysis-report/SKILL.md | 54 + collector-extension-standalone/README.md | 5 + collector-extension-standalone/manifest.json | 26 + collector-extension-standalone/page_bridge.js | 6 + .../service_worker.js | 196 + extension/background.js | 576 +++ extension/manifest.json | 40 + extension/page-review-hook.js | 123 + extension/popup.html | 43 + extension/popup.js | 124 + .../taobao_detail_fields_v8.3.6.xlsx | Bin 0 -> 9165 bytes monitor-template.csv | 8 + server/ai_client.py | 148 + server/analysis.py | 1362 ++++++ server/cacert.pem | 3892 +++++++++++++++++ server/config.example.json | 13 + server/prompt_templates.py | 60 + server/rebuild_report.py | 27 + server/renderer.py | 686 +++ server/report.css | 303 ++ server/report.html | 5 + server/report.js | 26 + server/server.py | 531 +++ server/start.command | 3 + server/start_windows.bat | 5 + start-tmall-ai.command | 50 + stop-tmall-ai.command | 10 + tests/test_evidence_only_report.py | 320 ++ tests/test_v910_report_regressions.py | 60 + tests/test_v920_image_driven_report.py | 75 + tests/test_version_consistency_v924.py | 21 + 55 files changed, 9760 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG_V9.1.0.md create mode 100644 CHANGELOG_V9.2.0.md create mode 100644 CHANGELOG_V9.2.1.md create mode 100644 CHANGELOG_V9.2.2.md create mode 100644 CHANGELOG_V9.2.3.md create mode 100644 CHANGELOG_V9.2.4.md create mode 100644 CHANGELOG_V9.2.5.md create mode 100644 PROMPT_ENGINEERING_V9.2.4.md create mode 100644 PROMPT_ENGINEERING_V9.2.5.md create mode 100644 README.md create mode 100644 REFERENCE_REPORT_V9.2.5.html create mode 100644 REPORT_OUTPUT_STANDARD_V9.2.4.md create mode 100644 REPORT_OUTPUT_STANDARD_V9.2.5.md create mode 100644 REPORT_OUTPUT_STANDARD_V9.2.md create mode 100644 VERSION.txt create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/.codex-plugin/plugin.json create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/README.md create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/README.md create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/manifest.json create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/page_bridge.js create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/service_worker.js create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/assets/taobao_detail_fields_v8.3.6.xlsx create mode 100755 codex-plugin-toolkit/sansong-tmall-product-report/scripts/normalize_capture.mjs create mode 100644 codex-plugin-toolkit/sansong-tmall-product-report/skills/tmall-product-analysis-report/SKILL.md create mode 100644 collector-extension-standalone/README.md create mode 100644 collector-extension-standalone/manifest.json create mode 100644 collector-extension-standalone/page_bridge.js create mode 100644 collector-extension-standalone/service_worker.js create mode 100644 extension/background.js create mode 100644 extension/manifest.json create mode 100644 extension/page-review-hook.js create mode 100644 extension/popup.html create mode 100644 extension/popup.js create mode 100644 field-reference/taobao_detail_fields_v8.3.6.xlsx create mode 100644 monitor-template.csv create mode 100644 server/ai_client.py create mode 100644 server/analysis.py create mode 100644 server/cacert.pem create mode 100644 server/config.example.json create mode 100644 server/prompt_templates.py create mode 100644 server/rebuild_report.py create mode 100644 server/renderer.py create mode 100644 server/report.css create mode 100644 server/report.html create mode 100644 server/report.js create mode 100644 server/server.py create mode 100755 server/start.command create mode 100644 server/start_windows.bat create mode 100755 start-tmall-ai.command create mode 100755 stop-tmall-ai.command create mode 100644 tests/test_evidence_only_report.py create mode 100644 tests/test_v910_report_regressions.py create mode 100644 tests/test_v920_image_driven_report.py create mode 100644 tests/test_version_consistency_v924.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..37bce90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# secrets +server/config.json +.env +.env.* + +# python +__pycache__/ +*.py[cod] +.pytest_cache/ +.venv/ +venv/ + +# generated +*.log +server/reports/ +server/monitor_data/ + +# os +.DS_Store +Thumbs.db diff --git a/CHANGELOG_V9.1.0.md b/CHANGELOG_V9.1.0.md new file mode 100644 index 0000000..f657f15 --- /dev/null +++ b/CHANGELOG_V9.1.0.md @@ -0,0 +1,25 @@ +# V9.1.0 修复清单 + +## 报告逻辑 +- 评论由原文罗列改为消费者洞察优先,原文/买家秀进入折叠证据层。 +- 评论洞察强制进入新品方案 consumerInsightInputs。 +- 主图与详情共享核心表达链,新增“主图承诺 → 详情证明 → 评论验证 → 断层 → 下一款动作”连贯性检查。 +- 主图/详情先展示整组逻辑,再展开逐图证据。 +- 标题渲染器禁止 dict/list 字面量泄漏。 +- 空的设计资产卡不再显示占位内容。 +- AI视觉增加 reference / optimization / exploration 来源等级。 + +## 数据状态 +- SKU 未采集不再显示为 0。 +- 结构化 SKU 数量与 SKU 图片数量严格分开。 + +## 图片稳定性 +- 报告生成时自动缓存证据图片到 server/reports/assets///。 +- HTML 优先引用本地缓存,加载失败自动回退原始 URL。 +- 服务端按图片扩展名返回正确 MIME 类型。 +- main / detail / sku / buyerShow 保持独立目录和证据来源。 + +## 验证 +- Python 语法检查通过。 +- 自动测试 16/16 通过。 +- 使用历史真实分析 JSON 重建报告通过:V9.1.0、无 Dict 泄漏、无 SKU 0 误报、主图/详情折叠结构正常。 diff --git a/CHANGELOG_V9.2.0.md b/CHANGELOG_V9.2.0.md new file mode 100644 index 0000000..fc52518 --- /dev/null +++ b/CHANGELOG_V9.2.0.md @@ -0,0 +1,41 @@ +# V9.2.0 图片驱动专业报告版 + +## 核心目标 + +把报告从“模型分析很多、页面全部展示”升级为“分析可以深,报告必须轻”。最终阅读顺序统一为:图片 → 专业意见 → 专业建议 → 详细证据。 + +## 分析与 Prompt + +- 删除用户可见的 `[数据事实] / [分析判断] / [设计建议] / [证据不足]` 拼接要求。 +- SYS 改为专业顾问语气:不做简单好坏评分,使用“当前表达 / 可强化 / 需要关注 / 专业建议”。 +- Task B 主图:输出 `role / message / professionalAdvice`,构图、色彩、OCR 等深度字段进入 `evidenceDetail`。 +- Task C 标题:输出 `professionalOpinion / currentExpression / reinforce / recommendedActions`。 +- Task D 详情:按 4–7 个信息任务聚合,选择代表图;不再默认渲染 20 张流水账。 +- Task F 新品:只要有商品基础证据且有主图或详情,至少输出 1 套证据可追溯方案;证据完整时输出 2–3 套。 +- Task G Composer:增加字段长度限制,直接输出 V9.2 图片驱动报告结构,同时保留原有 evidence-only 校验字段。 + +## Renderer + +- 封面使用第一张商品主图,去掉边框、背景套卡和大阴影。 +- 总览只保留 3 个重点:商品定位、主要产品资产、下一款优先方向。 +- 标题改为轻量三栏:当前表达 / 可强化 / 专业建议。 +- 主图默认直接显示图片与短意见,不再先折叠图库。 +- 主图 × 详情 × 评论连贯性改为状态矩阵。 +- 详情页按信息任务展示代表图,其余图片只做证据,不重复显示“暂无语义结论”。 +- SKU 改为图片拼图 + 用户选择顺序,结构未采集只作为小状态提示。 +- 评论改为买家秀 + 消费者洞察并排。 +- 商品价值改为场景 / 产品 / 服务 / 可强化方向。 +- 新品方案引用当前商品图片,显示产品调整、SKU、主图、详情策略。 +- 图片加载失败时隐藏失败图并显示轻量 fallback,不再出现灰色故障框。 + +## 证据与稳定性 + +- 保留 V9.1 `localUrl → sourceUrl` 图片缓存与回退逻辑。 +- SKU 图片数量不会当作 SKU 数量。 +- 新增 evidence-backed launch fallback:AI 偶发漏方案时,只使用当前真实证据形成最小“现有基础优化”方案。 +- 不补充未采集市场规模、转化率、利润、趋势或竞品结论。 + +## 测试 + +- 19/19 tests passed。 +- 新增 V9.2 图片驱动渲染、无边框封面、必出新品方案测试。 diff --git a/CHANGELOG_V9.2.1.md b/CHANGELOG_V9.2.1.md new file mode 100644 index 0000000..17a0013 --- /dev/null +++ b/CHANGELOG_V9.2.1.md @@ -0,0 +1,14 @@ +# V9.2.1 图片加载修复版 + +## 修复 +- 修复采集完成后报告图片可能全部无法加载的问题。 +- 图片缓存改为多策略请求头重试:天猫 Referer、淘宝 Referer、淘宝首页 Referer、无 Referer。 +- 支持 protocol-relative (`//...`) 与 data:image 图片进入本地缓存。 +- 缓存结果增加图片文件签名校验,不再只依赖 Content-Type。 +- 本地缓存失败时保留原始 sourceUrl,浏览器继续尝试远程回退。 +- Renderer 取消 `referrerpolicy=no-referrer`,避免阻断部分电商 CDN 回退加载。 +- 图片本地资源仍通过 `/reports/assets///...` 提供。 + +## 回归验证 +- 21/21 tests passed. +- SKU 缺失不等于 0、图片驱动报告、新品方案、图片缓存优先级等 V9.2.0 行为保持不变。 diff --git a/CHANGELOG_V9.2.2.md b/CHANGELOG_V9.2.2.md new file mode 100644 index 0000000..fe4a91a --- /dev/null +++ b/CHANGELOG_V9.2.2.md @@ -0,0 +1,7 @@ +# V9.2.2 + +## P0 图片链路修复 +- 修复 standalone / legacy collector 已采集 `images.main/detail/sku`,但因缺少 strict-v1 provenance 在 `analysis._normalize_image_groups()` 被全部丢弃的问题。 +- legacy/standalone 模式现在保留采集器已经分组的 main/detail/sku,并生成兼容 provenance。 +- buyerShow 仍只从评论记录建立,避免页面图误归类为买家秀。 +- 保留 V9.2.1 本地图片缓存与远程 URL 回退。 diff --git a/CHANGELOG_V9.2.3.md b/CHANGELOG_V9.2.3.md new file mode 100644 index 0000000..b40b3a3 --- /dev/null +++ b/CHANGELOG_V9.2.3.md @@ -0,0 +1,6 @@ +# V9.2.3 版本一致性修复 + +- 修复主扩展 `manifest.json` 已是 9.2.2,但 `popup.js` 仍固定校验 9.2.1,导致错误提示“插件 9.2.1 / 后端 9.2.2”。 +- 统一主扩展、standalone 扩展、capture-extension、后端、AI client、analysis engine、renderer、启动脚本、VERSION 为 9.2.3。 +- 保留 V9.2.2 图片归一化与图片缓存修复。 +- 新增版本一致性自检。 diff --git a/CHANGELOG_V9.2.4.md b/CHANGELOG_V9.2.4.md new file mode 100644 index 0000000..3373cac --- /dev/null +++ b/CHANGELOG_V9.2.4.md @@ -0,0 +1,22 @@ +# V9.2.5 Final — Owner + Professional User Report + +## 内容工程 +- SYS Prompt 改为老板/专业用户双层阅读目标。 +- Task B:主图输出 imageRole + visualSignals + businessMeaning + nextAction。 +- Task C:标题压缩为 currentExpression + reinforce + recommendedActions。 +- Task D:详情按4-6个任务组输出 visualSignals + nextAction。 +- Task E:收敛成老板决策摘要,不再生产大量重复长文。 +- Task F:新品方案增加 sourceType(证据驱动优化 / 反馈驱动升级 / 探索性方向),默认只输出短决策字段。 +- Task G:最终 Schema 与 V9.2.5 报告布局完全对齐。 + +## 渲染工程 +- 报告顺序改为:老板速览 → 新品方向 → 标题 → 主图 → 详情 → SKU → 评论。 +- 去掉独立“商品核心竞争表达”章节,避免与总览重复。 +- 主图/详情以图片信息标签为主要文字层。 +- 主图承接矩阵默认 details 折叠。 +- 新品方案改成3列决策卡。 +- 图片全部 contain,取消 cover 裁切;封面无边框。 + +## 兼容 +- 保留旧字段回退:旧 message/professionalAdvice 仍可转换为单个短标签/动作。 +- 保留 V9.2.3 图片缓存、sourceUrl/localUrl 回退与 legacy 图片归一化。 diff --git a/CHANGELOG_V9.2.5.md b/CHANGELOG_V9.2.5.md new file mode 100644 index 0000000..a4da897 --- /dev/null +++ b/CHANGELOG_V9.2.5.md @@ -0,0 +1,11 @@ +# V9.2.5 Final — Big Visual / Big Type + +- 报告从轻量Dashboard进一步升级为Editorial式大图大字商业报告。 +- 首屏、老板速览、主图、详情、SKU、买家秀全面放大。 +- 主图由5列小图改为2列大图,第5张可单独放大。 +- 详情由3列卡片改为交错大图模块。 +- SKU由4列缩略图改为2列大图;买家秀默认2x2。 +- 所有图片继续使用 contain,完整保留图片文字与商品主体。 +- visualSignals 从胶囊小标签改为大字信息行。 +- Prompt进一步压缩,避免Renderer硬截断。 +- 新品可见层只保留产品动作与页面动作。 diff --git a/PROMPT_ENGINEERING_V9.2.4.md b/PROMPT_ENGINEERING_V9.2.4.md new file mode 100644 index 0000000..9d05d8f --- /dev/null +++ b/PROMPT_ENGINEERING_V9.2.4.md @@ -0,0 +1,84 @@ +# Prompt Engineering V9.2.5 + +## 总目标 +报告服务两类用户: +- 老板/决策者:30秒内回答为什么能卖、什么值得继承、下一款先做什么。 +- 专业商品用户:从图片中快速提取卖点、规格、证明与可执行动作。 + +核心原则:分析可以深,默认报告必须轻;图片信息优先于AI解释。 + +## SYS Prompt +新增硬规则: +- 同一结论默认只展示一次。 +- 图片逐张提炼 `visualSignals`,禁止用长段 `message` 作为默认输出。 +- 探索性新品必须标记 `sourceType=探索性方向`。 +- 图片完整显示优先,Renderer 使用 contain。 + +## Task B — 主图商业信息提炼 +默认字段: +- `imageRole`:图片承担的商业任务,<=10字 +- `visualSignals`:2-4个图片可核验信息,每项<=12字 +- `businessMeaning`:内部解释,<=28字 +- `nextAction`:下一款动作,<=22字 +- `evidenceDetail`:构图/OCR/颜色等深层分析,默认折叠 + +## Task C — 标题压缩 +默认字段: +- `professionalOpinion` <=55字 +- `currentExpression`:短词组 +- `reinforce`:短词组 +- `recommendedActions`:<=28字 + +## Task D — 详情证明信息 +按4-6个任务组,不逐张流水账: +- `type` +- `representativeImageId` +- `visualSignals` +- `businessMeaning` +- `nextAction` + +## Task E — 老板决策摘要 +只输出可供最终报告压缩的: +- 为什么能卖 +- 值得继承 +- 下一款优先 +不再要求大段 opportunity map / top drivers / top risks 默认展示。 + +## Task F — 新品方向 +每套方案默认只保留: +- `sourceType`:证据驱动优化 / 反馈驱动升级 / 探索性方向 +- `name` +- `whyThisPlan` +- `productAction` +- `pageAction` +- `validation` +- `visualReferenceImageIds` + +## Task G — 最终编排 +顺序固定: +1. 老板速览 +2. 下一款方向 +3. 标题 +4. 主图在卖什么 +5. 详情页证明什么 +6. SKU怎么选 +7. 消费者实际感受到什么 + +商品价值不再独立重复一章,合并进老板速览。 + +## 字数控制 +- professionalOpinion <=55字 +- ownerOverview headline/note <=28字 +- imageRole <=10字 +- visualSignal <=12字 +- nextAction <=22字 +- review insight <=35字 +- plan why <=45字 +- plan product/page action <=35字 + +## 证据规则 +所有结论继续绑定 `evidenceIds`。图片信息标签必须来源于: +- 图片可读文字 +- 图片可见产品/场景 +- 与该图片明确关联的页面参数 +不得用市场常识补齐。 diff --git a/PROMPT_ENGINEERING_V9.2.5.md b/PROMPT_ENGINEERING_V9.2.5.md new file mode 100644 index 0000000..5339b99 --- /dev/null +++ b/PROMPT_ENGINEERING_V9.2.5.md @@ -0,0 +1,37 @@ +# Prompt Engineering V9.2.5 + +## 目标 +默认报告从“信息面板”升级为“大图大字决策报告”。模型不负责写长分析,而负责把真实证据压成短判断、图片信息标签和下一步动作。 + +## 总原则 +- 大图讲事实,大字讲判断,短句讲动作。 +- 老板先看:为什么能卖 / 值得继承 / 下一款优先。 +- 专业用户再看:标题 / 主图 / 详情 / SKU / 消费者。 +- 图片文字、规格、材质、权益、功能优先转成 visualSignals。 +- 同一结论默认只出现一次。 +- 未采集经营数据不推断市场规模、转化、利润。 + +## 可见字段硬限制 +- verdict/professionalOpinion <= 45字 +- owner headline <= 18字;note <= 22字 +- title keyword <= 10字 +- main imageRole <= 8字;visualSignals 2-3项,每项<=10字;nextAction<=18字 +- detail visualSignals 2-3项,每项<=10字;nextAction<=18字 +- SKU professionalOpinion <=35字;recommendedAction<=24字 +- review insight <=28字 +- plan productAction/pageAction <=28字 + +## Task B 主图 +输出 imageRole + visualSignals + nextAction。visualSignals 必须是图上真实信息,如“40支新疆棉”“免费仓储”“六件套”。禁止“高级感强”“表达清晰”这类抽象词充当图片信息。 + +## Task D 详情 +按4-6个信息任务组:品牌服务 / 套件组成 / 材质品质 / 产品功能 / 使用便利 / 花型选择 / 用户验证。每组只放代表图、2-3个 visualSignals、1个 nextAction。 + +## Task E 老板摘要 +只回答三个问题:为什么能卖、值得继承、下一款优先。 + +## Task F 新品 +默认最多3套,区分:证据驱动优化 / 反馈驱动升级 / 探索性方向。可见层只展示产品动作和页面动作。whyThisPlan/validation 可作为内部证据字段。 + +## Task G 编排 +顺序固定:老板速览 → 下一款先做什么 → 标题 → 主图 → 详情 → SKU → 消费者。商品价值不再独立成章。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..95191c2 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# 三笙 · 商品开品分析报告引擎 V9.2.5 + +V9.2.5 是“大图大字决策报告”最终版:老板先看结论,专业用户再看图片证据。大图讲事实,大字讲判断,短句讲动作。 + +## 最终报告结构 + +1. 老板速览:为什么能卖 / 值得继承 / 下一款优先 +2. 下一款方向:证据驱动优化 / 反馈驱动升级 / 探索性方向 +3. 标题表达:已表达 / 建议强化 +4. 主图在卖什么:完整图片 + 图片信息标签 + 1条动作 +5. 详情页证明什么:代表图 + 图片信息标签 + 1条动作 +6. SKU怎么选:SKU图 + 选择维度与顺序 +7. 消费者实际感受到什么:买家秀 + 聚合体验信号 + +## V9.2.5 关键变化 + +- Prompt 从“解释图片”改为“提炼图片上的商业信息”。 +- 主图/详情新增 `visualSignals`,优先输出图片可核验的卖点与参数标签。 +- 同一结论默认只展示一次,详细推理留在 evidenceDetail。 +- 主图 × 详情 × 评论承接矩阵默认折叠。 +- 商品价值不再单独重复一章,吸收到老板速览。 +- 新品方案改为横向决策卡,只保留为什么做 / 产品动作 / 页面动作,并标记来源等级。 +- 图片统一 `object-fit: contain`,封面、主图、详情、SKU、买家秀、方案参考图均优先完整展示,不裁切商品信息。 +- SKU图片数量继续与SKU结构数量分离。 +- 保留 V9.2.3 已修复的图片 HTML 标签、图片缓存与旧采集格式兼容能力。 + +详细规则见 `REPORT_OUTPUT_STANDARD_V9.2.5.md`、`PROMPT_ENGINEERING_V9.2.5.md` 与 `CHANGELOG_V9.2.5.md`。 + +## 集成约定(V9.2.5) + +- `server/prompt_templates.py` 提供提示词模板注册表与商品变量说明。业务代码传入结构化变量(商品、参数、图片、评论、问答、程序统计、证据账本),不再自行拼接上下文。 +- 报告数据保存为 `/reports/.json`;页面使用独立的 `server/report.html` + `server/report.js` 通过 `data` 参数加载 JSON。可直接把这两个静态文件部署到其它服务端,UI 与后端数据接口解耦。 diff --git a/REFERENCE_REPORT_V9.2.5.html b/REFERENCE_REPORT_V9.2.5.html new file mode 100644 index 0000000..bc4df81 --- /dev/null +++ b/REFERENCE_REPORT_V9.2.5.html @@ -0,0 +1,271 @@ + + + + + +本屋创品大学生宿舍床品六件套|大图大字决策报告 + + + + + +
+
+
PRODUCT DECISION REPORT · BIG VISUAL
+

本屋创品大学生宿舍床品六件套

+
¥597.55 · 公开销量 2000+ · 评论 47 · SKU图 30 · 参数 24
+
宿舍一站式床品定位清晰。下一款优先解决 规格选择、品质证明、花色分类。
+
+
+ 商品封面图 +
图片暂不可用
+
+
+ +
+
01 · BOSS VIEW

老板 30 秒看懂

+
+
为什么能卖宿舍一站式买齐

全棉品质 + 记忆棉睡感 + 新生服务。

+
值得继承40支全棉 + 单品拆解

免费仓储与质保继续作为新生权益。

+
下一款重点更好选 · 更可证 · 更好看

降低选择成本,不继续堆卖点。

+
+
+ +
+
02 · NEXT PRODUCT

下一款先做什么

+

优先做规格透明化;气候/睡感适配作为体验升级,花色风格化作为探索方向。

+
+
+ 方案A +
图片暂不可用
+
方案 A · 证据驱动

规格透明化升级

产品
统一六件套,明确床型、厚度、克重。
页面
主图前置规格,详情增加床型对照。
+
+ 方案B +
图片暂不可用
+
方案 B · 反馈驱动

气候与睡感适配

产品
按季节区分被芯克重,明确支撑参数。
页面
用气候/睡感指引替代概念堆叠。
+
+ 方案C +
图片暂不可用
+
方案 C · 探索方向

花色风格化重组

产品
29种花色先分风格,再选具体花型。
页面
先选风格,再进入SKU选择。
+
+
+ +
+
03 · TITLE

标题只解决一个问题:说清楚

+

“三件套 / 六件套”存在冲突;建议统一六件套,并突出 40支斜纹全棉。

+
当前标题本屋创品大学生宿舍床上三件套纯棉寝室床单人被套六件套被子全套
+
已表达大学生宿舍 · 纯棉 · 六件套
建议强化40支斜纹 · 规格统一 · 风格词
+
+ +
+
04 · MAIN IMAGES

主图到底在卖什么

+

图片本身就是成交语言。下面只保留图片里的核心商业信息,以及下一步动作。

+
+
+ 主图1 +
图片暂不可用
+

宿舍一站式

学生床品TOP1 · 免费仓储 · 质保赠品
专业动作 · 突出“免费仓储”
+ 主图2 +
图片暂不可用
+

六件套清单

六件套 · 尺寸 · 一次买齐
专业动作 · SKU同步编号
+ 主图3 +
图片暂不可用
+

全棉品质

100%新疆棉 · 高支数 · 亲肤裸睡
专业动作 · 直接标明40支
+ 主图4 +
图片暂不可用
+

床垫支撑

A2 Pro · 包裹感 · 支撑力
专业动作 · 明确4cm / 6cm
+ 主图5 +
图片暂不可用
+

枕被功能

记忆枕 · 泡芙被 · A类/7A级
专业动作 · 补检测依据
+
+
+ +
+
05 · DETAIL PAGE

详情页证明了什么

+

不再逐张解释,只保留最关键的信息任务与证明内容。

+
+
+ 品牌与服务 +
图片暂不可用
+

品牌与服务

轻装上阵 · 免费仓储
专业动作 · 补仓储流程
+ 套件组成 +
图片暂不可用
+

套件组成

六件套 · 尺寸清单
专业动作 · 继续前置
+ 材质品质 +
图片暂不可用
+

材质品质

新疆棉 · 40支斜纹 · 全棉
专业动作 · 参数具体化
+ 产品功能 +
图片暂不可用
+

产品功能

A2 Pro记忆棉 · 支撑回弹
专业动作 · 补厚度密度
+ 使用便利 +
图片暂不可用
+

使用便利

床笠 · 宿舍床型
专业动作 · 补适配范围
+ 花型选择 +
图片暂不可用
+

花型选择

29种花色 · OOTD
专业动作 · 先按风格分组
+
+
+ +
+
06 · SKU

先解决适配,再解决审美

+

SKU不是“有多少图”,而是用户能不能快速选到适合自己的组合。

+
+
+ SKU 1 +
图片暂不可用
+
+ SKU 2 +
图片暂不可用
+
+ SKU 3 +
图片暂不可用
+
+ SKU 4 +
图片暂不可用
+
+ SKU 5 +
图片暂不可用
+
+ SKU 6 +
图片暂不可用
+
+ SKU 7 +
图片暂不可用
+
+ SKU 8 +
图片暂不可用
+
+
+
+
01套件:三 / 四 / 五件套
+
02床型:80×190 / 90×190 / 90×200
+
03床垫:4cm / 6cm
+
04被芯:4.5斤 / 6斤
+
05花色:按风格再选具体花型
+
+
建议固定选择路径:套件 → 床型 → 厚度 → 被芯 → 花色。
+
+
+
+ +
+
07 · CONSUMER

消费者真正感受到什么

+

买家秀是证据。结论只看三件事:稳定体验、体验摩擦、下一款动作。

+
+
+ 买家秀 1 +
图片暂不可用
+
+ 买家秀 2 +
图片暂不可用
+
+ 买家秀 3 +
图片暂不可用
+
+ 买家秀 4 +
图片暂不可用
+
+
+
稳定体验柔软亲肤 · 透气 · 记忆棉回弹
+
体验摩擦色差 · 包装洁净 · 尺寸适配
+
下一款动作规格透明 + 品控证明 + 花色分组
+
+
+
+ +
三笙 · 大图大字商品决策报告 · 基于当前商品页面、图片、参数与评论证据。
+ \ No newline at end of file diff --git a/REPORT_OUTPUT_STANDARD_V9.2.4.md b/REPORT_OUTPUT_STANDARD_V9.2.4.md new file mode 100644 index 0000000..a74778d --- /dev/null +++ b/REPORT_OUTPUT_STANDARD_V9.2.4.md @@ -0,0 +1,40 @@ +# V9.2.5 报告输出标准 + +## 1. 阅读优先级 +图片信息 > 核心判断 > 专业动作 > 详细证据。 + +老板默认只需前两章即可完成方向判断;专业用户继续阅读图片证据与执行细节。 + +## 2. 字数硬限制 +- 核心判断:55字以内 +- 老板速览 headline/note:各28字以内 +- 主图 imageRole:10字以内 +- 主图 visualSignals:2-4项,每项12字以内 +- 主图 nextAction:22字以内 +- 详情 visualSignals:2-4项,每项12字以内 +- 详情 nextAction:22字以内 +- 评论 insight:35字以内 +- 新品 whyThisPlan:45字以内 +- 新品 productAction/pageAction:各35字以内 + +## 3. 图片规则 +- 所有商品图默认完整展示:`object-fit: contain`。 +- 不允许为了卡片整齐裁掉图片文字、产品主体或规格信息。 +- 封面无边框、无卡片套、无大阴影。 +- 详情只选4-6张代表图默认展示,其余保留为证据。 + +## 4. 主图/详情内容规则 +默认展示:图片 + 2-4个 visualSignals + 1条 nextAction。 +不默认展示:构图、OCR、颜色、材质识别过程、长段 businessMeaning。 + +## 5. 新品方案规则 +- A:证据驱动优化 +- B:反馈驱动升级 +- C:探索性方向(如存在) +探索性方向必须显式标记,不能伪装成当前商品已验证事实。 + +## 6. 禁止项 +- 禁止 `[数据事实][分析判断][设计建议][证据不足]` +- 禁止同一结论在一个章节重复三次 +- 禁止通过 Renderer 用省略号硬截断长段落充当“精简” +- 禁止 SKU 图片数当作 SKU 数量 diff --git a/REPORT_OUTPUT_STANDARD_V9.2.5.md b/REPORT_OUTPUT_STANDARD_V9.2.5.md new file mode 100644 index 0000000..c0766d3 --- /dev/null +++ b/REPORT_OUTPUT_STANDARD_V9.2.5.md @@ -0,0 +1,12 @@ +# V9.2.5 大图大字报告输出标准 + +1. 首屏:商品图占主要视觉,标题 48px,核心判断 26px。 +2. 老板速览:3个大结论,不使用密集小卡。 +3. 新品:3张大图决策卡;只展示产品动作、页面动作。 +4. 主图:桌面端2列,每图约560px高,第5张可单独放大。 +5. 详情:单组大图+大标题,交错排版;图片完整 contain。 +6. SKU:2列大图,右侧用大字展示选择路径。 +7. 评论:默认4张大买家秀 + 3-4条大字体验结论。 +8. 禁止 `object-fit: cover` 裁掉图片文字或商品主体。 +9. 默认可见正文不靠省略号截断,Prompt阶段直接生成短文本。 +10. 视觉原则:大图讲事实,大字讲判断,小字只做补充。 diff --git a/REPORT_OUTPUT_STANDARD_V9.2.md b/REPORT_OUTPUT_STANDARD_V9.2.md new file mode 100644 index 0000000..a4a1b01 --- /dev/null +++ b/REPORT_OUTPUT_STANDARD_V9.2.md @@ -0,0 +1,32 @@ +# V9.2 报告输出标准 + +## 阅读层级 + +1. 商品封面图 + 一条专业意见 +2. 商品重点(最多 3 项) +3. 标题表达(当前表达 / 可强化 / 专业建议) +4. 主图表达(图片直接展示,每图只保留角色、信息、建议) +5. 详情页逻辑(4–7 个信息任务组 + 代表图) +6. SKU 选择逻辑(代表图 + 选择顺序) +7. 消费者反馈(买家秀 + 聚合洞察) +8. 商品核心竞争表达(场景 / 产品 / 服务 / 可强化) +9. 新品建议(1–3 套证据可追溯方案) + +## 用户可见语言 + +使用:专业意见、当前表达、可进一步强化、需要关注、专业建议、下一款建议。 + +不使用:做得好/差、主要问题、整体有效点、整体断层、严重错误,以及 `[数据事实][分析判断][设计建议]` 等模型内部标签。 + +## 图片规则 + +- cover:第一张主图,Renderer 不加边框与卡片底。 +- main:直接展示,建议最多 5–8 张。 +- detail:默认只展示 4–7 张代表图,其余为证据。 +- sku:默认 8 张代表图拼图;图片数不等于结构化 SKU 数。 +- buyerShow:默认 6 张买家秀。 +- 图片加载优先 `localUrl`,失败回退 `sourceUrl`。 + +## 新品规则 + +有基础商品证据且存在主图/详情时,至少形成 1 套方案。方案必须能回溯 evidenceIds;无法确定的规格写待验证,不得凭空补市场趋势、利润、转化或竞品结论。 diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 0000000..f9458b8 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1 @@ +9.2.5 diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/.codex-plugin/plugin.json b/codex-plugin-toolkit/sansong-tmall-product-report/.codex-plugin/plugin.json new file mode 100644 index 0000000..ae67f93 --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/.codex-plugin/plugin.json @@ -0,0 +1,23 @@ +{ + "name": "sansong-tmall-product-report", + "version": "9.0.0", + "description": "三笙跨类目电商商品采集、分析、开品与AI视觉报告插件。", + "author": { + "name": "三笙市场洞察" + }, + "skills": "./skills/", + "interface": { + "displayName": "三笙电商商品分析报告", + "shortDescription": "跨类目采集商品证据并输出图文开品报告。", + "longDescription": "根据类目、标题、主图、详情、SKU、参数、评论与问答动态生成有证据链的电商商品分析报告、新品设计方案和AI视觉参数。", + "developerName": "三笙市场洞察", + "category": "Productivity", + "capabilities": ["Read", "Write", "Analysis", "Reporting"], + "brandColor": "#00AFC5", + "defaultPrompt": [ + "基于最新采集生成商品分析报告", + "检查商品字段采集是否完整", + "输出下一款开品卖点与落地方案" + ] + } +} diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/README.md b/codex-plugin-toolkit/sansong-tmall-product-report/README.md new file mode 100644 index 0000000..09c3aed --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/README.md @@ -0,0 +1,10 @@ +# 三笙电商商品分析报告插件 v9.2.5 + +用于把淘宝/天猫当前商品采集结果转成跨类目商品分析、新品设计和 AI 视觉报告。 + +- `skills/tmall-product-analysis-report/SKILL.md`:跨类目分析与报告规范。 +- `scripts/normalize_capture.mjs`:采集 JSON 标准化。 +- `assets/capture-extension/`:通用商品页采集扩展参考。 +- `assets/taobao_detail_fields_v8.3.6.xlsx`:保持原文件名的跨类目字段对照表。 + +报告明确展开标题、主图、详情、SKU、评论、商品价值结构、设计资产分级及 AI 生成规格;用户只负责采集并生成。 diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/README.md b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/README.md new file mode 100644 index 0000000..6a937f5 --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/README.md @@ -0,0 +1,3 @@ +# 三笙电商商品详情采集扩展 v9.2.5 + +跨类目采集商品标题、类目候选、主图、详情、SKU、动态参数、评论和问答。家纺仅为分析模板分支,不限制采集字段。 diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/manifest.json b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/manifest.json new file mode 100644 index 0000000..08f74fd --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/manifest.json @@ -0,0 +1,26 @@ +{ + "manifest_version": 3, + "name": "三笙商品详情采集", + "version": "9.2.5", + "description": "跨类目解析商品标题、主图、详情、活动、SKU、动态参数、评论与问答。", + "permissions": ["activeTab", "scripting", "tabs", "alarms", "cookies"], + "host_permissions": [ + "http://127.0.0.1:3300/*", + "http://localhost:3300/*", + "http://127.0.0.1/*", + "http://localhost/*", + "https://*.taobao.com/*", + "https://item.jd.com/*", + "https://*.jd.com/*", + "https://item.taobao.com/*", + "https://detail.tmall.com/*", + "https://*.tmall.com/*" + ], + "content_scripts": [{ + "matches": ["http://127.0.0.1/*", "http://localhost/*"], + "js": ["page_bridge.js"], + "run_at": "document_start" + }], + "background": { "service_worker": "service_worker.js" }, + "action": { "default_title": "重新采集当前商品页" } +} diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/page_bridge.js b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/page_bridge.js new file mode 100644 index 0000000..8220b43 --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/page_bridge.js @@ -0,0 +1,6 @@ +window.addEventListener('message',event=>{ + if(event.source!==window||event.data?.type!=='SANSONG_COLLECTOR_PING')return + chrome.runtime.sendMessage({type:'SANSONG_COLLECTOR_WAKE'},response=>{ + window.postMessage({type:'SANSONG_COLLECTOR_PONG',requestId:event.data.requestId,installed:true,version:chrome.runtime.getManifest().version,connected:Boolean(response?.connected)},'*') + }) +}) diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/service_worker.js b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/service_worker.js new file mode 100644 index 0000000..8c18b35 --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/service_worker.js @@ -0,0 +1,187 @@ +const API_BASE='http://127.0.0.1:3300/api/collector/product-page' + +async function capture(tabId){ + const result=await chrome.scripting.executeScript({target:{tabId},func:()=>{ + const clean=value=>String(value||'').replace(/\s+/g,' ').trim() + const uniq=rows=>[...new Set(rows.filter(Boolean))] + const bodyText=document.body?.innerText||'' + const lines=bodyText.split(/\n+/).map(clean).filter(Boolean) + const url=location.href + const itemId=url.match(/[?&]id=(\d+)/)?.[1]||url.match(/\/(\d+)\.html/)?.[1]||'' + const skuId=url.match(/[?&]skuId=(\d+)/)?.[1]||'' + const platform=/tmall/i.test(location.hostname)?'tmall':/taobao/i.test(location.hostname)?'taobao':/jd\.com/i.test(location.hostname)?'jd':'unknown' + const title=clean(document.querySelector('h1')?.textContent||document.querySelector('meta[property="og:title"]')?.content||document.title) + const readAfter=patterns=>{const row=lines.find(line=>patterns.some(pattern=>pattern.test(line)));if(!row)return'';const parts=row.split(/[::]/);return clean(parts.length>1?parts.slice(1).join(':'):row)} + const shop=clean(document.querySelector('[class*="shop-name"],[class*="shopName"],[class*="seller-name"],[class*="sellerName"]')?.textContent)||readAfter([/^店铺/,/^店名/,/^卖家/]) + const priceText=clean(document.querySelector('[class*="promotionPrice"],[class*="salePrice"],[class*="price"]')?.textContent||'') + const currentPrice=clean(priceText.match(/[¥¥]?\s*(\d+(?:\.\d+)?)/)?.[1]||bodyText.match(/(?:券后|到手价|活动价|价格)\s*[¥¥]?\s*(\d+(?:\.\d+)?)/)?.[1]||'') + const originalPrice=clean(bodyText.match(/(?:原价|划线价|优惠前)\s*[¥¥]?\s*(\d+(?:\.\d+)?)/)?.[1]||'') + const sold=clean(bodyText.match(/(?:已售|付款|销量|累计销量)\s*([0-9.万wW+]+)/)?.[1]||'') + const publicReviewCount=clean(bodyText.match(/(?:累计评价|全部评价|商品评价|评论)\s*([0-9.万wW+]+)/)?.[1]||'') + const ranking=clean(bodyText.match(/(?:热销榜|排行榜|榜第)\s*第?\s*([0-9]+)\s*名/)?.[0]||'') + + const meta={} + document.querySelectorAll('meta').forEach(el=>{const key=el.getAttribute('property')||el.getAttribute('name');const value=el.getAttribute('content');if(key&&value&&!/cookie|authorization|token|password/i.test(key))meta[key]=value}) + const jsonld=[] + document.querySelectorAll('script[type="application/ld+json"]').forEach(el=>{try{jsonld.push(JSON.parse(el.textContent||'null'))}catch{}}) + + const badImage=/avatar|icon|logo|sprite|loading|placeholder|qrcode|qr-code|emoji|badge/i + const imageRows=[...document.images].map(img=>({ + url:img.currentSrc||img.src, + width:img.naturalWidth||img.width||0, + height:img.naturalHeight||img.height||0, + alt:clean(img.alt), + context:clean(`${img.alt||''} ${img.closest('[class],[id]')?.className||''} ${img.closest('[class],[id]')?.id||''}`) + })).filter(row=>/^https?:\/\//.test(row.url)&&!badImage.test(`${row.url} ${row.context}`)&&(row.width>=120||row.height>=120)) + const uniqueImages=rows=>{const seen=new Set();return rows.filter(row=>{const key=row.url.replace(/_[0-9]+x[0-9]+[^/]*$/,'');if(seen.has(key))return false;seen.add(key);return true})} + const reviewRows=uniqueImages(imageRows.filter(row=>/review|comment|rate|evaluate|buyer|show|评价|评论|买家秀|晒单/i.test(row.context))) + const detailRows=uniqueImages(imageRows.filter(row=>/detail|description|desc|richtext|module|详情|图文|描述/i.test(row.context)&&!reviewRows.some(x=>x.url===row.url))) + const skuRows=uniqueImages(imageRows.filter(row=>/sku|prop|variant|option|规格|颜色|尺码|型号|口味/i.test(row.context)&&!reviewRows.some(x=>x.url===row.url)&&!detailRows.some(x=>x.url===row.url))) + const mainRows=uniqueImages(imageRows.filter(row=>/gallery|thumb|preview|main|swiper|carousel|主图|轮播/i.test(row.context)&&!reviewRows.some(x=>x.url===row.url)&&!detailRows.some(x=>x.url===row.url))).slice(0,30) + const allImages=uniqueImages(imageRows) + + const blockedAttribute=/优惠|券|立减|包邮|发货|送达|价保|退货|客服|店铺|销量|付款|评论|评价|问答/ + const attributes=[] + const attributeSeen=new Set() + const addAttribute=(name,value,source='dom')=>{name=clean(name).replace(/[::]$/,'');value=clean(value);if(!name||!value||name.length>36||value.length>600||blockedAttribute.test(name))return;const key=`${name}\u0000${value}`;if(attributeSeen.has(key))return;attributeSeen.add(key);attributes.push({name,value,source})} + document.querySelectorAll('table tr,[class*="attribute"] li,[class*="Attribute"] li,[class*="parameter"] li,[class*="Parameter"] li,[class*="property"] li,[class*="Property"] li,[class*="attr"] li').forEach(el=>{ + const cells=[...el.querySelectorAll(':scope > th,:scope > td,:scope > span,:scope > div')].map(x=>clean(x.textContent)).filter(Boolean) + if(cells.length>=2)addAttribute(cells[0],cells.slice(1).join(' '),'attribute_dom') + else{const text=clean(el.textContent);const match=text.match(/^([^::]{1,36})[::]\s*(.{1,600})$/);if(match)addAttribute(match[1],match[2],'attribute_dom')} + }) + lines.forEach(line=>{const match=line.match(/^([^::]{1,30})[::]\s*(.{1,400})$/);if(match)addAttribute(match[1],match[2],'page_text')}) + + const breadcrumb=uniq([...document.querySelectorAll('[class*="breadcrumb"] a,[class*="crumb"] a,[class*="category"] a')].map(el=>clean(el.textContent))).filter(x=>x.length<=40).slice(0,12) + const categoryCandidates=uniq([...breadcrumb,...attributes.filter(x=>/类目|分类|品类|产品类型|商品类型/.test(x.name)).map(x=>x.value)]).slice(0,20) + + const variants=[] + const variantSeen=new Set() + const addVariant=row=>{const name=clean(row.name);if(!name||name.length>180)return;const key=`${clean(row.group)}\u0000${name}`;if(variantSeen.has(key))return;variantSeen.add(key);variants.push({...row,name,group:clean(row.group),source:row.source||'sku_dom'})} + document.querySelectorAll('[class*="sku"],[class*="Sku"],[class*="variant"],[class*="Variant"],[class*="prop"] ,[class*="Prop"]').forEach(container=>{ + const group=clean(container.querySelector('[class*="title"],[class*="label"],[class*="name"]')?.textContent||'') + container.querySelectorAll('button,[role="option"],li,[class*="item"],[class*="Item"]').forEach(el=>{ + const name=clean(el.getAttribute('title')||el.getAttribute('aria-label')||el.textContent) + const image=el.querySelector('img')?.currentSrc||el.querySelector('img')?.src||'' + addVariant({group,name,image,selected:el.getAttribute('aria-selected')==='true'||/selected|active|current/i.test(el.className),disabled:el.getAttribute('aria-disabled')==='true'||el.hasAttribute('disabled')}) + }) + }) + const variantKeywords=/(颜色|色号|尺码|尺寸|规格|型号|版本|配置|容量|净含量|口味|香型|套装|套餐|款式|适用|包装|重量|数量|码数|度数|功率|内存|存储)/ + lines.filter(line=>variantKeywords.test(line)&&line.length<=500).slice(0,180).forEach(line=>line.split(/[,,]/).map(clean).filter(Boolean).forEach(name=>addVariant({group:'',name,source:'page_text'}))) + + const promotionPattern=/(优惠|券|立减|满减|包邮|免运费|价保|退货|退款|七天无理由|预计.*发货|送达|大促|活动|限时|到手价|店铺优惠|赠品|买一送一)/i + const promotions=uniq(lines.filter(line=>promotionPattern.test(line)&&line.length<=120)).slice(0,100).map(text=>({text,source:'page_text'})) + + const questions=[] + document.querySelectorAll('[class*="question"],[class*="Question"],[class*="ask"],[class*="Ask"]').forEach((el,index)=>{ + const text=clean(el.innerText);if(!text)return + const question=clean(el.querySelector('[class*="question"],[class*="title"]')?.textContent||text.match(/(.{2,160}[??])/)?.[1]) + const answer=clean(el.querySelector('[class*="answer"]')?.textContent||'') + if(question)questions.push({id:`question-${index+1}`,question,answer,source:'question_dom'}) + }) + + const reviewSelectors=['[class*="review-item"]','[class*="comment-item"]','[class*="rate-item"]','[class*="ReviewItem"]','[class*="CommentItem"]','[data-testid*="review"]'] + const reviews=[...document.querySelectorAll(reviewSelectors.join(','))].slice(0,500).map((el,index)=>{ + const content=clean(el.innerText);if(content.length<6||content.length>3000)return null + const images=uniq([...el.querySelectorAll('img')].map(img=>img.currentSrc||img.src).filter(src=>/^https?:\/\//.test(src)&&!badImage.test(src))) + const date=content.match(/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?(?:\s+\d{1,2}:\d{2})?/)?.[0]||'' + const appendContent=clean(content.match(/(?:追评|追加评价)[::\s]*(.{2,800})/)?.[1]||'') + const sku=clean(el.querySelector('[class*="sku"],[class*="spec"],[class*="attr"]')?.textContent||'') + const merchantReply=clean(el.querySelector('[class*="reply"],[class*="seller"]')?.textContent||'') + const rating=Number(el.getAttribute('data-score')||el.getAttribute('data-rating')||el.querySelector('[data-score]')?.getAttribute('data-score'))||null + return{id:el.getAttribute('data-id')||el.id||`review-${index+1}`,content,appendContent,date,rating,sku,hasImage:images.length>0,images,merchantReply,source:'review_dom'} + }).filter(Boolean) + + const collection={ + publicReviewCount, + reviewActual:reviews.length, + questionActual:questions.length, + reviewCompleteness:reviews.length?'visible_content_collected':'no_visible_review_nodes', + reviewStopReason:reviews.length?'visible_dom_exhausted':'reviews_not_open_or_platform_folded', + capturedAt:new Date().toISOString(), + collectorVersion:chrome.runtime.getManifest().version, + platform + } + const structured={ + meta:{...collection,url}, + category:{candidates:categoryCandidates,breadcrumb}, + product:{itemId,skuId,title,shop,brand:attributes.find(item=>/^品牌$/.test(item.name))?.value||'',url,platform}, + sales:{currentPrice,originalPrice,sold,ranking}, + promotions,attributes,sku:variants,questions,reviews, + images:{all:allImages,main:mainRows,detail:detailRows.slice(0,120),sku:skuRows.slice(0,120),buyerShow:reviewRows.slice(0,160)}, + collection,pageText:bodyText + } + return{structured,...structured,jsonld,meta} + }}) + return result?.[0]?.result +} + +const lastCaptured=new Map() +function isProductUrl(url=''){return /^https:\/\/(?:item\.jd\.com\/\d+\.html|item\.taobao\.com\/item\.htm|detail\.tmall\.com\/item\.htm)/i.test(url)} + +async function reportAuthState(){ + try{ + const [taobaoCookies,jdCookies]=await Promise.all([chrome.cookies.getAll({domain:'.taobao.com'}),chrome.cookies.getAll({domain:'.jd.com'})]) + const response=await fetch(`${API_BASE}/auth-heartbeat`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({version:chrome.runtime.getManifest().version,platforms:{taobao:{loggedIn:taobaoCookies.some(cookie=>/tracknick|cookie2|_tb_token_|sgcookie/i.test(cookie.name))},jd:{loggedIn:jdCookies.some(cookie=>/pin|thor|unick/i.test(cookie.name))}}})}) + return response.ok + }catch(error){console.error('[三笙登录态上报]',error);return false} +} + +async function captureAndSend(tab,{force=false}={}){ + if(!tab?.id||!isProductUrl(tab.url))return + const key=`${tab.id}:${tab.url}` + if(!force&&Date.now()-(lastCaptured.get(key)||0)<30_000)return + lastCaptured.set(key,Date.now()) + try{ + await chrome.action.setBadgeText({tabId:tab.id,text:'采'}) + await chrome.action.setBadgeBackgroundColor({tabId:tab.id,color:'#2563EB'}) + const payload=await capture(tab.id) + const response=await fetch(`${API_BASE}/capture`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}) + if(!response.ok)throw new Error(await response.text()) + await chrome.action.setBadgeText({tabId:tab.id,text:'OK'}) + await chrome.action.setBadgeBackgroundColor({tabId:tab.id,color:'#155C4E'}) + setTimeout(()=>chrome.action.setBadgeText({tabId:tab.id,text:''}),2500) + }catch(error){ + lastCaptured.delete(key) + await chrome.action.setBadgeText({tabId:tab.id,text:'!'}) + await chrome.action.setBadgeBackgroundColor({tabId:tab.id,color:'#A54F43'}) + console.error('[三笙商品采集]',error) + } +} + +chrome.tabs.onUpdated.addListener((tabId,changeInfo,tab)=>{if(changeInfo.status==='complete'&&isProductUrl(tab.url))setTimeout(()=>captureAndSend({...tab,id:tabId}),2500)}) +chrome.tabs.onRemoved.addListener(tabId=>{for(const key of lastCaptured.keys())if(key.startsWith(`${tabId}:`))lastCaptured.delete(key)}) +chrome.action.onClicked.addListener(tab=>captureAndSend(tab,{force:true})) + +let queueBusy=false +let queueBackoffMs=1000 +async function processCaptureQueue(){ + if(queueBusy)return + queueBusy=true + let tab + try{ + const response=await fetch(`${API_BASE}/next`) + if(response.status===204){queueBackoffMs=Math.min(queueBackoffMs*1.5,15_000);return} + if(!response.ok)throw new Error(`任务队列 HTTP ${response.status}`) + const task=await response.json() + tab=await chrome.tabs.create({url:task.url,active:false}) + await new Promise((resolve,reject)=>{ + const timeout=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(listener);reject(new Error('商品页加载超时'))},45_000) + const listener=(tabId,changeInfo,updated)=>{if(tabId===tab.id&&changeInfo.status==='complete'){clearTimeout(timeout);chrome.tabs.onUpdated.removeListener(listener);resolve(updated)}} + chrome.tabs.onUpdated.addListener(listener) + }) + await new Promise(resolve=>setTimeout(resolve,3500)) + await captureAndSend(await chrome.tabs.get(tab.id),{force:true}) + queueBackoffMs=1000 + }catch(error){console.error('[三笙采集队列]',error);queueBackoffMs=Math.min(queueBackoffMs*2,30_000)} + finally{if(tab?.id)await chrome.tabs.remove(tab.id).catch(()=>{});queueBusy=false;setTimeout(processCaptureQueue,queueBackoffMs)} +} + +chrome.runtime.onInstalled.addListener(()=>{chrome.alarms.create('sansong-product-queue',{periodInMinutes:.5});processCaptureQueue()}) +chrome.runtime.onStartup.addListener(()=>{chrome.alarms.create('sansong-product-queue',{periodInMinutes:.5});processCaptureQueue()}) +chrome.alarms.onAlarm.addListener(alarm=>{if(alarm.name==='sansong-product-queue')processCaptureQueue();if(alarm.name==='sansong-auth-heartbeat')reportAuthState()}) +chrome.alarms.create('sansong-product-queue',{periodInMinutes:.5}) +chrome.alarms.create('sansong-auth-heartbeat',{periodInMinutes:1}) +processCaptureQueue() +reportAuthState() +chrome.runtime.onMessage.addListener((message,_sender,sendResponse)=>{if(message?.type!=='SANSONG_COLLECTOR_WAKE')return;reportAuthState().then(connected=>sendResponse({connected})).catch(()=>sendResponse({connected:false}));return true}) +chrome.cookies.onChanged.addListener(change=>{if(/(?:^|\.)(?:taobao|tmall|jd)\.com$/i.test(change.cookie.domain))setTimeout(reportAuthState,300)}) diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/assets/taobao_detail_fields_v8.3.6.xlsx b/codex-plugin-toolkit/sansong-tmall-product-report/assets/taobao_detail_fields_v8.3.6.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a70873bdacfa3f25fba3d7499a0e071cf60584a6 GIT binary patch literal 9165 zcma)i1yoyG*DclpMT&coP@uS5ad#^c+=F{k99jw#hZ3CN?k>e$iWc|cP_#Hj`_g;g z|L%V~zHf||jB`$Qa@O1{bDy==K6@T@C1ex=1Ox;$gdkdgkxkz?!vnn4iim*l9Dek4 zVE1r#vp08kwrBHnbf}-`Q0?Ksd3pk@c4AO1zqie8HqzDgLLO-1(XkM!F&Z31@ZPA)G*~YxmWmOXV|!L3n?8q>yj+ZiNiCgGgKzvAnC^yylwuC zIBB+A$$l$k=y98!)K1(!rNif%m zmvhHuNEk_WY|quYyRYwpXj{9h$5)}i>dy(=Y9^zaFzfF2v~nJ1%`MzfWW_bI?`8h&gH;)U>>}7{E3E=Gg$%( ztV=Rp>@il@cq!j6+rI~WyiLGDgjO(6DFV@3!b&hHcSeQBxv8h_2DoVZg?40yY(mR~ z@JW-$=7sZzamxEPU@??b$HT5Pd?+0i97M7yxcZUs?}G*h0wp*N)>#;jb)#)lVxMg4 z0m6m)lL7&|0x85?EVRf=JZ%z*c?1f*PjYiOtQ=HiLYJ-N>Fzkb;o|{@CHuv(M@Y<; zahTKAaU4-?q3iptuW0t-3+;Trc00T)G{R|5`}nFttU0s*0snz!*8RnB^jMi>&kZ&(;19k0f*P18yrA3Q<7U_; z|GIKtU)HEU@jzABI$|!Y1E<~g>FN3RgA)e-djhJ?O01b;#!c`D%=o70MOm})LINGi zFlK#;)JvVq)Mea}-tcIr zcce;Y#?7@0z5;zAZsoz!-n%9nqEGqi!@&}BdTC))=Qt^YpNRX*Q}Euv2(|pww+|(q zG$CIduMK~?P3%k{>$1LIc~bQnK_Me<6kj2Ur-yaZQiz%gQ~GsWBYKyc<^6`j<42{p z<|;LYX;K@`vtLtCQOnjr=_;HtGn)}^r4z{$#~Oi9Pzx2{N%|8}* z-harc@tMHhon6ePx%w?AnZtkJx<|yS;=?A8)e12>XO7 zW{LLy+-65K*T6Q`u51J=VsmZdbxsXc)ps(<+rE_1H#T|AwXY;s5ij3XKC>|KM##{| zz-sg}%_?`$cNrZOcm9fIa!Z+7pTe^EG1stN4erjQsj>dgdhhlhD*blHnB&J)scYQq z(a<&4QbehENFRC9n_`1*kbp@x(Qd;}G0sUe|+}H$R9o<5L$# zBI`dbQcKJoWehHkf>0Dc$)YmT;j2o4QC9gKj)=o zTvsH*K*V1%?JTNZ&(*$VzT=IJ2D7hd1s^*vy^ku(`*UU2sHt0@6=J#bgYC6_9J3W& zzpwC%OdXNdk)#E5nb+%kMN|aHCgcclB*VC``;W3g6D0KMS#SVUd9((!}7ST6y zL~5he{puY>XT%sEYYC@Dzkh3ZCagfP@>&v^TVJRg(m6BVu5_BI?7&q4Nvh7FC`w9` z7NADEO`b^4T8r)vn-um`I3a&8o`FIlG8TWvk1QbJ_o1%9o+Q$p6Jk3#F(e&dUG8nR zrMf?F*>CeXJJME$d?tMmKQoNh1eFA%3`EU!2`jOLt))@h*oN!sv|FH`DFbY}HFj?$ zvkepMjr}mz@_YBKiQu_q*SqM2Eo-79Q=toI!dTN!mUL|Z!;XPQrwW2=h%x7X*>*?+_Y_l{Sfj z4vCz?>5(41RdO!t3zTW@kUPexL$P5BwPGnKMzK@1vLg3`w#X|l{OqHq0m*mY-BaW@ zN6LTXao%n-Xh@K7LHVfgUm-+_lk+4LO*4>&M<`#qn4&m6!uW^N!$O^BPVll5O^@ky7)vmR

+yVX0Dwc!iT z1-o$HNd9)~_k;#)0|G(7a1Y^!zmh_Ey0hj|KT$|W_`r{dQ>spgqENYO_LTdi0(~3% z>-6XRcPE`z96GyN(;=d#)y}#emx>F- zcpS+w+rHEPV&eOjJ>gDuezMY?fJYP9XCwD;$67_XBb(Ut$@Hu1G+N9kT8 zH*JZX=W#xCZ)ErSww3lH=WWw?Qdm?GBi&SXRLP!C@fNF!8l!B1)KrLHiiVN)22rBo zEmC^=N_#w1-lq>k|JNN@vJH0gonOsLx3x77Nt;j;Y>5sd>*|M7)8#2vzi{q1C?DTX7H?h3gBamks3 z`kcIq?9{{fczGYOC{c&9df10e8#d+Deu@{5j(k-RXrVz;d%Y^Q6oQ~$R3^yRvYh-A z-_{j@eqoD~xuX=gLsn?9Y*rYmc4?`cQCe9+75bP~tJk z#Ee1Kiqiu2;%jo`qi)@Yi|VGj{+)*T;~v=DyHD_HOxqAb4TLWgfbhq8zEq}&@2QFl!rq}_xuA9taA*Dtt??}e z1T)UpE8Mx&684%BrZy{*5`%Fm@j@7AGWDZR6N;Nm{*m~w;7wvX>#fj(CfvdMhWg223z#pLz)I z!*z5rG@4-}Jlo22LO$#ACps+Mvhwj{VUslRMf{=t=pwn!iY>*98j zp3;`iEa4<_JCll1G$k#GnnO)-;g1hbku`>m9Q8j6!aBv8UUjEOM3;U{Q0qyJ4H~*t zpD(?uvdSyWU+~^mYX@$`sP$p6075{v%h&s&%#_p2Gqb4H!1cr;yYbhYyKqltTR^jF zLva2|k2^P73=RkDFUqA2RR-3I2|qNd$FzS^2kmJLUE3&X26C>176r>abnhhHi1PT>ykH>dLXPvR#V>pAyDD)J>k ztD#yb>xYSH#pe!}XEF_?%|?ADIpKLQlJ|m_kQMnVo<{A;7hO3$coV4^B&vA{dA&T7 zrjN0DBIR?@zV1_8tAhq2#@m}q4rlc@WbGd#U4lpxOR1H$VwLf8ISMnJnt^<~g0~@E z2mCo`)^}qz3R~(?byk;Z^u#+Or+zvsW_@>2%K|LKH(E?jL>mXNqZe-ZRvlasnbw$4 z@U`=1sCscmiW%-?1lnu6o+70n(0uz{y7ZJu~&{RtZDS?uEGTi8<9eMll=j zOHtr`v*6=%6jEjwZk)am>(@gmA^Awq^A%r8Pipl3%wA4kp-af2OvZj=`PI=)Y6K_| zAJulAFOt8&xGF{Du6IJPt=?+73j9*DlwjUFA>o(hcB57OvodH4)9T7C+ddNe{L zL~ON;JKdyeEb8kyd6l$vSR0y=UbqmZhA~0)%yo9A@keuT+J%L}#T(|#jE1}C=x|4l z)AeAT>|W0YC(3t6wOt-=sJnSH9e4_UomTghG>Pn3z@F=!4i1FBs}?hc-A%DtwhRqP z6Dsg=0>JxWH5&46gk%8@3qNg1@%r@A?RN@b`5sj^mQe;ysbaok=jY}MXzgPN*(>Zk zRmYUJtVtx%xCgYVA{uPZsxPm1E(ll}? zIME!9ZVAC~_l?Yn;O-xFt8?=kRUX2)oxPkei6|1$#)_`&eC9conY<-W`=E+XTXO{8 zUrvR)p7>(}C`u!1d-`Szrz&#THDvIVBHPovb97&7ERu2)Ut5~{(3x+BMRLXW_Fgs6 z5$Tob7@6LcZhWg==5fpS#*tcS!fFn0lP27wCpACZo}OCX_+$wU5+4igvz9~K-QP(b zyRwq=1Tvlj4hjZlKZJ!5SBq{;o7`aTWbzV&?@vEBi6RRny4j47$b8l?QnEGAZ%xK9 zxj7s&x&xVcon}=F(JKhxF1AaqZ<0UZ(>zOYb|)`)J3X;0KS3OlNL!9%PUdQ(?Tzb8(*6#7m{H2*9B3sJ*_!1gx3CRZo7D+X+<5Q zg_G5>$kwvsg`FH{4bGuw!f4uUD)aVEoHo^P1Po?2ldYCUfNIod08bRjB+i1D1bt|( z-jf2!rqAj1NJpr1&^)qrNndDBorWqyfI>O3Rd##v&JHtbM5*j+LKL`T53K^|nYGC6nVl$DdbXhjL5y!sZ zI+lXb;~^`JJMDE+?1`{p&;H^5F=Vh`%68`2 zw%`2c3qhmVCE|#@)yAsTZKf?e?Y!MAJSn!q34a1Hj#pH!04orj?2Fu44oI#t5soE)D2uevV%j z8kIw5jK!F4T>$B)FJ}y7GaGAj2M#8V%DZkCzRH$oHn(QbjB?aXFF4bd-~r#B8hkxy z%D3&mVZWNwPkU!Qob(I%-*@c0x*q=RC5h}P1#Vr&}Nv(}(y2JyK2y7IVoT&3n#C}w68 z=DSO@{JE#vtkYJOWZ{@XQ0!&4N&)JUaJ|NR4-I$bCn&^y7-?5__8KBbgLJqtRrxXJ zYo{Zf$g+1P$x~zl)&lO_0`4N8k6cfdn3kJ@H>x2?G?G{khcWkC_gE~wr|iUW&AiDG zU6vHuH8zdUek}(-APpVG6lQQqiO3YlqE_Y`E2+|H+#tt#hbxO%SL>H;Ru_z#s*szu zDRoLy%s!9xtm+IRM7_ewbhV^;03Lb&0r8Ua5Y`Mmcp~3>yUYN9fcKM6A4H z)M6t_wS5k$ed@TOch_6cqV_M{mJ#ysrs7fLF=UlJ8}cli@NyKJiAO^=s0H|nOq0wt z*;kCxcNX&}3#p-?iqerqE6y1f0N$bqXKh+~!Xv&NsqctHRvn zPK`D%m9}bSe3z;4Z5ru?U?6Kb&nHcEz-^kw`h}@2f=w0=+eGP$wzk%~9R562{C8RU z1mibLO#4;?wM%6MTV09;CdT?S5rH#V?y}p|*9|}JY>U$a^4SPtG9T{EkqQI}Ye|*Q zx~Pi8zsbq6S-#*dLE%&isaNKl|0(`IZCb zv|3ZeroJj@T1rDi2)cMzrR7DjWv{Fm9$G#aM=3?BQBk<%6oqM@_3^fy_3d?*Zr4;v zI-BVk65)a8RbK!gquUsWD|2MArjh3^YwMTxDT)!jPp8*NLu_R^J=-QSEh->Xk~8Yr z-2O!Lj&uc z*!Nh769hl5@{0Z|?2V>_Oj)`FHa#X$SuE5J^^IUeJt|N|EL^Q1m&@zcu(&Tl{~4js zcOF5+m3Z&PkLCiG$rgpr`=Slmhk}^|1F|v0(cec4Goop6DilC7xo zEgy_v@HZYmKc((0LuVVZOUXwl%(Zt6Sox~YO`r>U5`Y!$E!uws5?F@ibdn7}d;a1< z@xtZ6@c#M##8Dvw-75<0f3mO@EeeW83cX?*v8fcObs`20*e4_AP|?=?Cf9* zxCmkfW^;A{Il+Iia&~hxgTR|^*6c237WQV=Aa*Ve4nB5xkN;o*{*6hk<>dnU|3P%N zva+=RNjqD-bp$y<{sS=kzs~?rYni!OgCHUR_CNGy0JVap2;dDLFG$depWDjP%#4$V zgNq+P%`Pg;{;&Tl`agvJtz$pIe`^GQlNk$_g2?_}_WhgF|E*^K#qa2p2wc*pcydyz z`z_39a~|DpF*vyX0;%C{JY+v+(49tv99eGTS?CA#4L{g(e<_yG_WXmav~52MP+F+$5Pp&lV)RZ7R)Co4PD( zO!PPfBPM#XyIORR3Uew1XFdQ^Eu<@+4v7fpdb3UJHl-HZHRR+6g=^9X;$NheOqZgC z3{ar1J{a2j^HO1TYNcxCJJZc+`>A{bhYn05Z_ynIJ$fRDXH+t1jYgH|ei?!ezj;Ka zgqEYG45XZ$An;)}`F(YOjeb|= z`Z$T-PYK9W;(0}wHyTj~LERA@5YgF81><^4L>h$Spczq5;G7n*l_1c_XfH)sbNm1J z)#uy(POEgH#T+@zp;LckTbbZJJr7ZfKIU>Fzj=L`aU*UKNt;xO%s7Q}0$?g|?xhq8 zMVr0US*pUv>EfbMeUB`n+J$;9MAA=6-#B|~nnQG+M@NLjB3>^}2x%-IUAvd)e(wt5CzAes6v69soRCPO-(mCNe`9p^*U|48+NglOt z>gk&rEQ0aZr@2?^7`(4#THlJFjbL#@6h}oo=R3~hkS#|n7mOKfj0N-#$G+V#_%IC` z$RFItYEc$m|9Y|0DaJRru_2+=KC?}%u7rq0fc)Q|w}2D+*DEjJ_u-Fd_|y2$M-cut zML@_4V1f_OZvp22J&^FH>7O#`Kc-#qsTbVzPto*GfIsD)e*j*i{b%v#PlP{}nST%_ z{zmwZI?bObf6f;FK?#J*J^z=$|E>1?HFx|685tJ_#NERzN~PaH9F$`ac?3&G7&L literal 0 HcmV?d00001 diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/scripts/normalize_capture.mjs b/codex-plugin-toolkit/sansong-tmall-product-report/scripts/normalize_capture.mjs new file mode 100755 index 0000000..c39d9e9 --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/scripts/normalize_capture.mjs @@ -0,0 +1,52 @@ +#!/usr/bin/env node +import fs from 'node:fs' + +const input = process.argv[2] +if (!input) { + console.error('Usage: node scripts/normalize_capture.mjs ') + process.exit(1) +} + +const payload = JSON.parse(fs.readFileSync(input, 'utf8')) +const raw = payload.raw || payload.structured || payload +const images = raw.images || raw.image_groups || {} +const product = raw.product || {} +const sales = raw.sales || {} + +const out = { + schemaVersion: '9.0.0', + category: raw.category || {candidates: raw.categoryCandidates || [], breadcrumb: raw.breadcrumb || []}, + product: { + itemId: product.itemId || raw.itemId || '', + skuId: product.skuId || raw.skuId || '', + title: product.title || raw.title || payload.title || '', + shop: product.shop || raw.shopName || '', + brand: product.brand || '', + category: product.category || raw.categoryName || '', + url: product.url || raw.url || payload.url || '' + }, + sales: { + currentPrice: sales.currentPrice || raw.price || '', + sold: sales.sold || raw.sold || '', + ranking: sales.ranking || '' + }, + counts: { + attributes: Array.isArray(raw.attributes) ? raw.attributes.length : 0, + sku: Array.isArray(raw.sku) ? raw.sku.length : 0, + promotions: Array.isArray(raw.promotions) ? raw.promotions.length : 0, + reviews: Array.isArray(raw.reviews) ? raw.reviews.length : 0, + questions: Array.isArray(raw.questions) ? raw.questions.length : 0, + mainImages: Array.isArray(images.main || images.hero) ? (images.main || images.hero).length : 0, + detailImages: Array.isArray(images.detail) ? images.detail.length : 0, + skuImages: Array.isArray(images.sku) ? images.sku.length : 0, + buyerShowImages: Array.isArray(images.buyerShow || images.review) ? (images.buyerShow || images.review).length : 0 + }, + attributes: raw.attributes || [], + sku: raw.sku || [], + promotions: raw.promotions || [], + reviews: raw.reviews || [], + questions: raw.questions || [], + images +} + +console.log(JSON.stringify(out, null, 2)) diff --git a/codex-plugin-toolkit/sansong-tmall-product-report/skills/tmall-product-analysis-report/SKILL.md b/codex-plugin-toolkit/sansong-tmall-product-report/skills/tmall-product-analysis-report/SKILL.md new file mode 100644 index 0000000..0a43cb2 --- /dev/null +++ b/codex-plugin-toolkit/sansong-tmall-product-report/skills/tmall-product-analysis-report/SKILL.md @@ -0,0 +1,54 @@ +--- +name: tmall-product-analysis-report +description: Generate evidence-based, cross-category ecommerce product analysis reports from captured title, hero images, detail images, promotions, SKU, attributes, reviews and questions. +--- + +# 三笙电商商品分析报告引擎 + +本技能把真实电商商品数据转成完整阅读型报告和下一款 AI 视觉方案。用户只负责采集与生成,不要求用户在报告中操作流程。 + +## 工作顺序 + +1. 读取类目、标题、参数、SKU、主图、详情图、评论、买家图和问答。 +2. 自动识别类目及分析维度。家纺只是分支,不是默认模板。 +3. 建立证据编号,并把内容分为数据事实、分析判断、设计建议和证据不足。 +4. 输出商品总览、商品画像、标题、主图、详情、SKU、评价、商品价值、新品设计和 AI 生成企划。 + +## 类目分支 + +- 服饰:尺码、版型、面料、做工、色差、上身效果。 +- 食品:规格、口味、配料、保质期、复购、包装。 +- 美妆:功效宣称、肤质、成分、使用反馈、敏感风险。 +- 数码:配置、兼容性、性能、售后、使用问题。 +- 家纺:结构规格、材质触感、颜色花型、工艺、洗后表现、场景适配。 +- 其他:从已采标题、参数、SKU 和评论自动形成维度。 + +## 证据规则 + +- 主图、详情图、SKU 图和评论图严格分源,并绑定对应 imageEvidenceId。 +- 评论只验证购后体验,问答只表示购买前顾虑。 +- 单商品特征不等于竞争力;没有竞品数据时写“商品价值结构”。 +- “存在”不等于“必须继承”。设计结论分为已验证资产、候选方向和 AI 探索变量。 +- 缺少可靠数据时显示“未采集 / 当前不可分析”,不得用常识补齐。 + +## 报告结构 + +1. 商品总览与证据边界。 +2. 动态商品画像。 +3. 标题与营销表达拆解。 +4. 主图逐张深度分析。 +5. 详情页分段分析。 +6. SKU 与参数矩阵。 +7. 评论、问答和买家实拍。 +8. 商品价值结构。 +9. 新品方案与设计资产分级。 +10. AI 视觉生成企划。 +11. 样品和页面落地验证。 + +## AI 视觉输出 + +必须包含 Reference DNA、Design Delta、Must Keep、Must Change,以及 Product、Style、Scene、Photography、Material Rendering、Composition、Negative Rules。产品图和电商主图逐张写清生成目的、拍摄方式、构图和证明任务。 + +## 页面设计 + +使用白色背景和高对比深色文字,青绿到青蓝渐变只承担品牌、进度和重点强调。商品图保持原比例;大段内容用图文并排、卡片分层和表格呈现,桌面与移动端均需可读。 diff --git a/collector-extension-standalone/README.md b/collector-extension-standalone/README.md new file mode 100644 index 0000000..f598679 --- /dev/null +++ b/collector-extension-standalone/README.md @@ -0,0 +1,5 @@ +# 三笙电商商品详情采集扩展 v9.2.5 + +跨类目采集标题、类目候选、主图、详情图、SKU 图、评论图、动态参数、变体、促销、评论与问答。字段由页面结构和文本自动发现,不绑定家纺 BOM。 + +此目录是独立采集扩展参考实现;完整工具请使用根目录 `extension/` 与 `start-tmall-ai.command`。 diff --git a/collector-extension-standalone/manifest.json b/collector-extension-standalone/manifest.json new file mode 100644 index 0000000..08f74fd --- /dev/null +++ b/collector-extension-standalone/manifest.json @@ -0,0 +1,26 @@ +{ + "manifest_version": 3, + "name": "三笙商品详情采集", + "version": "9.2.5", + "description": "跨类目解析商品标题、主图、详情、活动、SKU、动态参数、评论与问答。", + "permissions": ["activeTab", "scripting", "tabs", "alarms", "cookies"], + "host_permissions": [ + "http://127.0.0.1:3300/*", + "http://localhost:3300/*", + "http://127.0.0.1/*", + "http://localhost/*", + "https://*.taobao.com/*", + "https://item.jd.com/*", + "https://*.jd.com/*", + "https://item.taobao.com/*", + "https://detail.tmall.com/*", + "https://*.tmall.com/*" + ], + "content_scripts": [{ + "matches": ["http://127.0.0.1/*", "http://localhost/*"], + "js": ["page_bridge.js"], + "run_at": "document_start" + }], + "background": { "service_worker": "service_worker.js" }, + "action": { "default_title": "重新采集当前商品页" } +} diff --git a/collector-extension-standalone/page_bridge.js b/collector-extension-standalone/page_bridge.js new file mode 100644 index 0000000..8220b43 --- /dev/null +++ b/collector-extension-standalone/page_bridge.js @@ -0,0 +1,6 @@ +window.addEventListener('message',event=>{ + if(event.source!==window||event.data?.type!=='SANSONG_COLLECTOR_PING')return + chrome.runtime.sendMessage({type:'SANSONG_COLLECTOR_WAKE'},response=>{ + window.postMessage({type:'SANSONG_COLLECTOR_PONG',requestId:event.data.requestId,installed:true,version:chrome.runtime.getManifest().version,connected:Boolean(response?.connected)},'*') + }) +}) diff --git a/collector-extension-standalone/service_worker.js b/collector-extension-standalone/service_worker.js new file mode 100644 index 0000000..c215724 --- /dev/null +++ b/collector-extension-standalone/service_worker.js @@ -0,0 +1,196 @@ +const API_BASE='http://127.0.0.1:3300/api/collector/product-page' + +async function capture(tabId){ + const result=await chrome.scripting.executeScript({target:{tabId},func:()=>{ + const clean=value=>String(value||'').replace(/\s+/g,' ').trim() + const uniq=rows=>[...new Set(rows.filter(Boolean))] + const bodyText=document.body?.innerText||'' + const lines=bodyText.split(/\n+/).map(clean).filter(Boolean) + const url=location.href + const itemId=url.match(/[?&]id=(\d+)/)?.[1]||url.match(/\/(\d+)\.html/)?.[1]||'' + const skuId=url.match(/[?&]skuId=(\d+)/)?.[1]||'' + const platform=/tmall/i.test(location.hostname)?'tmall':/taobao/i.test(location.hostname)?'taobao':/jd\.com/i.test(location.hostname)?'jd':'unknown' + const title=clean(document.querySelector('h1')?.textContent||document.querySelector('meta[property="og:title"]')?.content||document.title) + const readAfter=patterns=>{const row=lines.find(line=>patterns.some(pattern=>pattern.test(line)));if(!row)return'';const parts=row.split(/[::]/);return clean(parts.length>1?parts.slice(1).join(':'):row)} + const shop=clean(document.querySelector('[class*="shop-name"],[class*="shopName"],[class*="seller-name"],[class*="sellerName"]')?.textContent)||readAfter([/^店铺/,/^店名/,/^卖家/]) + const priceText=clean(document.querySelector('[class*="promotionPrice"],[class*="salePrice"],[class*="price"]')?.textContent||'') + const currentPrice=clean(priceText.match(/[¥¥]?\s*(\d+(?:\.\d+)?)/)?.[1]||bodyText.match(/(?:券后|到手价|活动价|价格)\s*[¥¥]?\s*(\d+(?:\.\d+)?)/)?.[1]||'') + const originalPrice=clean(bodyText.match(/(?:原价|划线价|优惠前)\s*[¥¥]?\s*(\d+(?:\.\d+)?)/)?.[1]||'') + const sold=clean(bodyText.match(/(?:已售|付款|销量|累计销量)\s*([0-9.万wW+]+)/)?.[1]||'') + const publicReviewCount=clean(bodyText.match(/(?:累计评价|全部评价|商品评价|评论)\s*([0-9.万wW+]+)/)?.[1]||'') + const ranking=clean(bodyText.match(/(?:热销榜|排行榜|榜第)\s*第?\s*([0-9]+)\s*名/)?.[0]||'') + + const meta={} + document.querySelectorAll('meta').forEach(el=>{const key=el.getAttribute('property')||el.getAttribute('name');const value=el.getAttribute('content');if(key&&value&&!/cookie|authorization|token|password/i.test(key))meta[key]=value}) + const jsonld=[] + document.querySelectorAll('script[type="application/ld+json"]').forEach(el=>{try{jsonld.push(JSON.parse(el.textContent||'null'))}catch{}}) + + const badImage=/avatar|icon|logo|sprite|loading|placeholder|qrcode|qr-code|emoji|badge/i + const imageRows=[...document.images].map(img=>({ + url:img.currentSrc||img.src, + width:img.naturalWidth||img.width||0, + height:img.naturalHeight||img.height||0, + alt:clean(img.alt), + context:clean(`${img.alt||''} ${img.closest('[class],[id]')?.className||''} ${img.closest('[class],[id]')?.id||''}`) + })).filter(row=>/^https?:\/\//.test(row.url)&&!badImage.test(`${row.url} ${row.context}`)&&(row.width>=120||row.height>=120)) + const uniqueImages=rows=>{const seen=new Set();return rows.filter(row=>{const key=row.url.replace(/_[0-9]+x[0-9]+[^/]*$/,'');if(seen.has(key))return false;seen.add(key);return true})} + const reviewRows=uniqueImages(imageRows.filter(row=>/review|comment|rate|evaluate|buyer|show|评价|评论|买家秀|晒单/i.test(row.context))) + const detailRows=uniqueImages(imageRows.filter(row=>/detail|description|desc|richtext|module|详情|图文|描述/i.test(row.context)&&!reviewRows.some(x=>x.url===row.url))) + const skuRows=uniqueImages(imageRows.filter(row=>/sku|prop|variant|option|规格|颜色|尺码|型号|口味/i.test(row.context)&&!reviewRows.some(x=>x.url===row.url)&&!detailRows.some(x=>x.url===row.url))) + const mainRows=uniqueImages(imageRows.filter(row=>/gallery|thumb|preview|main|swiper|carousel|主图|轮播/i.test(row.context)&&!reviewRows.some(x=>x.url===row.url)&&!detailRows.some(x=>x.url===row.url))).slice(0,30) + const allImages=uniqueImages(imageRows) + + const blockedAttribute=/优惠|券|立减|包邮|发货|送达|价保|退货|客服|店铺|销量|付款|评论|评价|问答/ + const attributes=[] + const attributeSeen=new Set() + const addAttribute=(name,value,source='dom')=>{name=clean(name).replace(/[::]$/,'');value=clean(value);if(!name||!value||name.length>36||value.length>600||blockedAttribute.test(name))return;const key=`${name}\u0000${value}`;if(attributeSeen.has(key))return;attributeSeen.add(key);attributes.push({name,value,source})} + document.querySelectorAll('table tr,[class*="attribute"] li,[class*="Attribute"] li,[class*="parameter"] li,[class*="Parameter"] li,[class*="property"] li,[class*="Property"] li,[class*="attr"] li').forEach(el=>{ + const cells=[...el.querySelectorAll(':scope > th,:scope > td,:scope > span,:scope > div')].map(x=>clean(x.textContent)).filter(Boolean) + if(cells.length>=2)addAttribute(cells[0],cells.slice(1).join(' '),'attribute_dom') + else{const text=clean(el.textContent);const match=text.match(/^([^::]{1,36})[::]\s*(.{1,600})$/);if(match)addAttribute(match[1],match[2],'attribute_dom')} + }) + lines.forEach(line=>{const match=line.match(/^([^::]{1,30})[::]\s*(.{1,400})$/);if(match)addAttribute(match[1],match[2],'page_text')}) + + const breadcrumb=uniq([...document.querySelectorAll('[class*="breadcrumb"] a,[class*="crumb"] a,[class*="category"] a')].map(el=>clean(el.textContent))).filter(x=>x.length<=40).slice(0,12) + const categoryCandidates=uniq([...breadcrumb,...attributes.filter(x=>/类目|分类|品类|产品类型|商品类型/.test(x.name)).map(x=>x.value)]).slice(0,20) + + const variants=[] + const variantSeen=new Set() + const addVariant=row=>{const name=clean(row.name);if(!name||name.length>180)return;const key=`${clean(row.group)}\u0000${name}`;if(variantSeen.has(key))return;variantSeen.add(key);variants.push({...row,name,group:clean(row.group),source:row.source||'sku_dom'})} + document.querySelectorAll('[class*="sku"],[class*="Sku"],[class*="variant"],[class*="Variant"],[class*="prop"] ,[class*="Prop"]').forEach(container=>{ + const group=clean(container.querySelector('[class*="title"],[class*="label"],[class*="name"]')?.textContent||'') + container.querySelectorAll('button,[role="option"],li,[class*="item"],[class*="Item"]').forEach(el=>{ + const name=clean(el.getAttribute('title')||el.getAttribute('aria-label')||el.textContent) + const image=el.querySelector('img')?.currentSrc||el.querySelector('img')?.src||'' + addVariant({group,name,image,selected:el.getAttribute('aria-selected')==='true'||/selected|active|current/i.test(el.className),disabled:el.getAttribute('aria-disabled')==='true'||el.hasAttribute('disabled')}) + }) + }) + const variantKeywords=/(颜色|色号|尺码|尺寸|规格|型号|版本|配置|容量|净含量|口味|香型|套装|套餐|款式|适用|包装|重量|数量|码数|度数|功率|内存|存储)/ + lines.filter(line=>variantKeywords.test(line)&&line.length<=500).slice(0,180).forEach(line=>line.split(/[,,]/).map(clean).filter(Boolean).forEach(name=>addVariant({group:'',name,source:'page_text'}))) + + const promotionPattern=/(优惠|券|立减|满减|包邮|免运费|价保|退货|退款|七天无理由|预计.*发货|送达|大促|活动|限时|到手价|店铺优惠|赠品|买一送一)/i + const promotions=uniq(lines.filter(line=>promotionPattern.test(line)&&line.length<=120)).slice(0,100).map(text=>({text,source:'page_text'})) + + const questions=[] + document.querySelectorAll('[class*="question"],[class*="Question"],[class*="ask"],[class*="Ask"]').forEach((el,index)=>{ + const text=clean(el.innerText);if(!text)return + const question=clean(el.querySelector('[class*="question"],[class*="title"]')?.textContent||text.match(/(.{2,160}[??])/)?.[1]) + const answer=clean(el.querySelector('[class*="answer"]')?.textContent||'') + if(question)questions.push({id:`question-${index+1}`,question,answer,source:'question_dom'}) + }) + + const reviewSelectors=['[class*="review-item"]','[class*="comment-item"]','[class*="rate-item"]','[class*="ReviewItem"]','[class*="CommentItem"]','[data-testid*="review"]'] + const reviews=[...document.querySelectorAll(reviewSelectors.join(','))].slice(0,500).map((el,index)=>{ + const content=clean(el.innerText);if(content.length<6||content.length>3000)return null + const images=uniq([...el.querySelectorAll('img')].map(img=>img.currentSrc||img.src).filter(src=>/^https?:\/\//.test(src)&&!badImage.test(src))) + const date=content.match(/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?(?:\s+\d{1,2}:\d{2})?/)?.[0]||'' + const appendContent=clean(content.match(/(?:追评|追加评价)[::\s]*(.{2,800})/)?.[1]||'') + const sku=clean(el.querySelector('[class*="sku"],[class*="spec"],[class*="attr"]')?.textContent||'') + const merchantReply=clean(el.querySelector('[class*="reply"],[class*="seller"]')?.textContent||'') + const rating=Number(el.getAttribute('data-score')||el.getAttribute('data-rating')||el.querySelector('[data-score]')?.getAttribute('data-score'))||null + return{id:el.getAttribute('data-id')||el.id||`review-${index+1}`,content,appendContent,date,rating,sku,hasImage:images.length>0,images,merchantReply,source:'review_dom'} + }).filter(Boolean) + + const collection={ + publicReviewCount, + reviewActual:reviews.length, + questionActual:questions.length, + reviewCompleteness:reviews.length?'visible_content_collected':'no_visible_review_nodes', + reviewStopReason:reviews.length?'visible_dom_exhausted':'reviews_not_open_or_platform_folded', + capturedAt:new Date().toISOString(), + collectorVersion:chrome.runtime.getManifest().version, + platform + } + const structured={ + meta:{...collection,url,imageClassificationVersion:'strict-v1',collectorVersion:'9.2.2'}, + category:{candidates:categoryCandidates,breadcrumb}, + product:{itemId,skuId,title,shop,brand:attributes.find(item=>/^品牌$/.test(item.name))?.value||'',url,platform}, + sales:{currentPrice,originalPrice,sold,ranking}, + promotions,attributes,sku:variants,questions,reviews, + images:(()=>{ + const main=mainRows, detail=detailRows.slice(0,120), sku=skuRows.slice(0,120), buyerShow=reviewRows.slice(0,160); + const provenance=[ + ...main.map(url=>({url,group:'main',source:'dom_main_gallery'})), + ...detail.map(url=>({url,group:'detail',source:'dom_product_description'})), + ...sku.map(url=>({url,group:'sku',source:'dom_sku_selector'})), + ...buyerShow.map(url=>({url,group:'buyerShow',source:'review_record'})) + ]; + return {all:allImages,main,detail,sku,buyerShow,provenance,classification:{version:'strict-v1',classifiedCount:allImages.length,groupsAreDisjoint:true}}; + })(), + collection,pageText:bodyText + } + return{structured,...structured,jsonld,meta} + }}) + return result?.[0]?.result +} + +const lastCaptured=new Map() +function isProductUrl(url=''){return /^https:\/\/(?:item\.jd\.com\/\d+\.html|item\.taobao\.com\/item\.htm|detail\.tmall\.com\/item\.htm)/i.test(url)} + +async function reportAuthState(){ + try{ + const [taobaoCookies,jdCookies]=await Promise.all([chrome.cookies.getAll({domain:'.taobao.com'}),chrome.cookies.getAll({domain:'.jd.com'})]) + const response=await fetch(`${API_BASE}/auth-heartbeat`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({version:chrome.runtime.getManifest().version,platforms:{taobao:{loggedIn:taobaoCookies.some(cookie=>/tracknick|cookie2|_tb_token_|sgcookie/i.test(cookie.name))},jd:{loggedIn:jdCookies.some(cookie=>/pin|thor|unick/i.test(cookie.name))}}})}) + return response.ok + }catch(error){console.error('[三笙登录态上报]',error);return false} +} + +async function captureAndSend(tab,{force=false}={}){ + if(!tab?.id||!isProductUrl(tab.url))return + const key=`${tab.id}:${tab.url}` + if(!force&&Date.now()-(lastCaptured.get(key)||0)<30_000)return + lastCaptured.set(key,Date.now()) + try{ + await chrome.action.setBadgeText({tabId:tab.id,text:'采'}) + await chrome.action.setBadgeBackgroundColor({tabId:tab.id,color:'#2563EB'}) + const payload=await capture(tab.id) + const response=await fetch(`${API_BASE}/capture`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}) + if(!response.ok)throw new Error(await response.text()) + await chrome.action.setBadgeText({tabId:tab.id,text:'OK'}) + await chrome.action.setBadgeBackgroundColor({tabId:tab.id,color:'#155C4E'}) + setTimeout(()=>chrome.action.setBadgeText({tabId:tab.id,text:''}),2500) + }catch(error){ + lastCaptured.delete(key) + await chrome.action.setBadgeText({tabId:tab.id,text:'!'}) + await chrome.action.setBadgeBackgroundColor({tabId:tab.id,color:'#A54F43'}) + console.error('[三笙商品采集]',error) + } +} + +chrome.tabs.onUpdated.addListener((tabId,changeInfo,tab)=>{if(changeInfo.status==='complete'&&isProductUrl(tab.url))setTimeout(()=>captureAndSend({...tab,id:tabId}),2500)}) +chrome.tabs.onRemoved.addListener(tabId=>{for(const key of lastCaptured.keys())if(key.startsWith(`${tabId}:`))lastCaptured.delete(key)}) +chrome.action.onClicked.addListener(tab=>captureAndSend(tab,{force:true})) + +let queueBusy=false +let queueBackoffMs=1000 +async function processCaptureQueue(){ + if(queueBusy)return + queueBusy=true + let tab + try{ + const response=await fetch(`${API_BASE}/next`) + if(response.status===204){queueBackoffMs=Math.min(queueBackoffMs*1.5,15_000);return} + if(!response.ok)throw new Error(`任务队列 HTTP ${response.status}`) + const task=await response.json() + tab=await chrome.tabs.create({url:task.url,active:false}) + await new Promise((resolve,reject)=>{ + const timeout=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(listener);reject(new Error('商品页加载超时'))},45_000) + const listener=(tabId,changeInfo,updated)=>{if(tabId===tab.id&&changeInfo.status==='complete'){clearTimeout(timeout);chrome.tabs.onUpdated.removeListener(listener);resolve(updated)}} + chrome.tabs.onUpdated.addListener(listener) + }) + await new Promise(resolve=>setTimeout(resolve,3500)) + await captureAndSend(await chrome.tabs.get(tab.id),{force:true}) + queueBackoffMs=1000 + }catch(error){console.error('[三笙采集队列]',error);queueBackoffMs=Math.min(queueBackoffMs*2,30_000)} + finally{if(tab?.id)await chrome.tabs.remove(tab.id).catch(()=>{});queueBusy=false;setTimeout(processCaptureQueue,queueBackoffMs)} +} + +chrome.runtime.onInstalled.addListener(()=>{chrome.alarms.create('sansong-product-queue',{periodInMinutes:.5});processCaptureQueue()}) +chrome.runtime.onStartup.addListener(()=>{chrome.alarms.create('sansong-product-queue',{periodInMinutes:.5});processCaptureQueue()}) +chrome.alarms.onAlarm.addListener(alarm=>{if(alarm.name==='sansong-product-queue')processCaptureQueue();if(alarm.name==='sansong-auth-heartbeat')reportAuthState()}) +chrome.alarms.create('sansong-product-queue',{periodInMinutes:.5}) +chrome.alarms.create('sansong-auth-heartbeat',{periodInMinutes:1}) +processCaptureQueue() +reportAuthState() +chrome.runtime.onMessage.addListener((message,_sender,sendResponse)=>{if(message?.type!=='SANSONG_COLLECTOR_WAKE')return;reportAuthState().then(connected=>sendResponse({connected})).catch(()=>sendResponse({connected:false}));return true}) +chrome.cookies.onChanged.addListener(change=>{if(/(?:^|\.)(?:taobao|tmall|jd)\.com$/i.test(change.cookie.domain))setTimeout(reportAuthState,300)}) diff --git a/extension/background.js b/extension/background.js new file mode 100644 index 0000000..4b8c833 --- /dev/null +++ b/extension/background.js @@ -0,0 +1,576 @@ +const SERVER='http://127.0.0.1:17962'; +const RUN_GAP=8*60*1000; +const sleep=ms=>new Promise(r=>setTimeout(r,ms)); +const isProduct=u=>/^https:\/\/detail\.tmall\.com\/item\.htm/i.test(u||'') || /item\.taobao\.com\/item\.htm/i.test(u||''); + +async function setStatus(tabId,patch){ + const key='status_'+tabId; const old=(await chrome.storage.local.get(key))[key]||{}; + await chrome.storage.local.set({[key]:{...old,...patch,updatedAt:Date.now()}}); +} +async function health(){ try{const r=await fetch(SERVER+'/health');return r.ok?await r.json():{ok:false}}catch(e){return {ok:false,error:String(e)}} } + +chrome.runtime.onMessage.addListener((m,s,send)=>{ + if(m?.type==='ANALYZE_CURRENT_NOW'){ + (async()=>{ + try{ + const tabId=m.tabId; + const [{result:pageInfo}]=await chrome.scripting.executeScript({ + target:{tabId},world:'MAIN', + func:()=>({url:location.href,itemId:(location.href.match(/[?&]id=(\d+)/)||[])[1]||''}) + }); + const itemId=pageInfo?.itemId||''; + if(!itemId){send({ok:false,error:'当前页面没有识别到商品ID'});return} + let raw=null; + try{ + const got=await chrome.storage.local.get('tmall_last_raw_'+itemId); + raw=got['tmall_last_raw_'+itemId]||null; + }catch(e){} + if(raw){ + const hasFacts=!!(raw.product?.title||raw.product?.shop||raw.sales?.currentPrice||raw.sku?.length||raw.attributes?.length||raw.images?.main?.length); + if(!hasFacts)raw=null; + } + if(!raw){ + // 没有历史raw时,做一次“轻采集”:复用 collector,但立即给 stop flag, + // 只保留当前页面已经可见/已捕获的数据,不再长时间采评论。 + await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:()=>{window.__TMALL_AI_STOP_REVIEW__=true;}}); + const [{result:r}]=await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:collector,args:[true]}); + raw=r; + } + if(!raw){send({ok:false,error:'当前没有可用于分析的数据'});return} + + raw.collection=raw.collection||{}; + raw.collection.analysisMode='current_snapshot'; + raw.collection.reviewCollectionComplete=!!raw.collection.reviewCollectionComplete; + raw.collection.reviewCompleteness=raw.collection.reviewCollectionComplete?'confirmed':'partial'; + raw.collection.analysisStartedBeforeReviewComplete=!raw.collection.reviewCollectionComplete; + raw.collection.analysisStartedAt=new Date().toISOString(); + + const resp=await fetch('http://127.0.0.1:17962/api/analyze-current',{ + method:'POST',headers:{'Content-Type':'application/json'}, + body:JSON.stringify({raw,allowPartialReviews:true}) + }); + const j=await resp.json().catch(()=>({ok:false,error:'后端返回无法解析'})); + send({...j,reviewCount:raw.reviews?.length||0}); + }catch(e){send({ok:false,error:String(e)})} + })(); + return true; + } + if(m?.type==='STOP_COLLECTION'){(async()=>{try{await chrome.scripting.executeScript({target:{tabId:m.tabId},world:'MAIN',func:()=>{window.__TMALL_AI_STOP_REVIEW__=true;}});send({ok:true})}catch(e){send({ok:false,error:String(e)})}})();return true;} + if(m?.type==='RUN_CURRENT'){ run(m.tabId,true).then(x=>send(x)).catch(e=>send({ok:false,error:String(e)})); return true; } + if(m?.type==='HEALTH'){ health().then(send); return true; } +}); + +async function run(tabId,manual){ + const tab=await chrome.tabs.get(tabId); + if(!isProduct(tab.url)) throw new Error('当前标签页不是天猫/淘宝商品详情页'); + const h=await health(); + if(!h.ok){ await setStatus(tabId,{state:'server_offline',message:'本地 AI 服务未启动'}); throw new Error('本地 AI 服务未启动,请先运行 根目录的「start-tmall-ai.command」'); } + if(!h.modelConfigured){ await setStatus(tabId,{state:'model_unconfigured',message:'模型未配置:请填写 本地设置页'}); throw new Error('模型未配置。请先填写 本地设置页 的 api_key 与 model。'); } + const [{result:pageState}]=await chrome.scripting.executeScript({ + target:{tabId},world:'MAIN', + func:async()=>{ + if(document.readyState==='loading') await new Promise(resolve=>document.addEventListener('DOMContentLoaded',resolve,{once:true})); + for(let i=0;i<8&&(document.body?.innerText||'').trim().length<120;i++) await new Promise(r=>setTimeout(r,350)); + const body=(document.body?.innerText||'').replace(/\s+/g,' ').slice(0,5000); + const login=/login\.taobao\.com|login\.tmall\.com/i.test(location.hostname)||(/密码登录|短信登录|手机扫码登录/.test(body)&&!/商品评价|宝贝详情/.test(body)); + const verification=/验证码|滑块验证|安全验证|访问过于频繁|操作频繁/.test(body); + return {url:location.href,login,verification,bodyLength:(document.body?.innerText||'').length}; + } + }); + if(pageState?.login){await setStatus(tabId,{state:'login_required',message:'淘宝要求登录。请先在当前浏览器登录淘宝,再刷新商品页重试。',progress:0});throw new Error('淘宝商品页未登录:请登录后刷新当前商品页再采集')} + if(pageState?.verification){await setStatus(tabId,{state:'verification_required',message:'页面出现淘宝安全验证,请手动完成后刷新重试。',progress:0});throw new Error('页面出现淘宝安全验证,请手动完成验证后重试')} + if(!isProduct(pageState?.url)){throw new Error('商品页已跳转,当前不是可采集的淘宝/天猫详情页')} + await setStatus(tabId,{state:'collecting',message:'正在采集当前商品:主图、SKU、详情、参数、问答与评论。不会采集推荐商品…',progress:10}); + await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:()=>{window.__TMALL_AI_STOP_REVIEW__=false;window.__TMALL_AI_REVIEW_PROGRESS__={count:0,round:0,stagnant:0,mode:'启动',elapsedMs:0,preview:[]};}}); + let collectorDone=false,collectorError=null;const startedAt=Date.now(); + const collectorPromise=chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:collector,args:[manual]}).then(v=>v).catch(e=>{collectorError=e;return null}).finally(()=>{collectorDone=true}); + while(!collectorDone){try{const rr=await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:()=>window.__TMALL_AI_REVIEW_PROGRESS__||null});const live=rr?.[0]?.result;if(live)await setStatus(tabId,{state:'collecting_reviews',message:`评论采集中:${live.count||0} 条 · ${live.mode||'处理中'}${live.stagnant?` · ${live.stagnant}轮无新增`:''}`,progress:Math.min(67,18+(Number(live.round||0)*2)),reviewLive:{...live,elapsedMs:Date.now()-startedAt}})}catch(e){}await sleep(600)} + const collectorResult=await collectorPromise; + if(collectorError)throw new Error('页面采集脚本执行失败:'+String(collectorError?.message||collectorError)); + const raw=collectorResult?.[0]?.result; + if(!raw?.product?.itemId) throw new Error('未识别到 itemId,请确认商品页已正常打开并刷新后重试'); + const hasProductFacts=!!(raw.product.title||raw.product.shop||raw.sales?.currentPrice||raw.sku?.length||raw.attributes?.length||raw.images?.main?.length); + if(!hasProductFacts){ + await setStatus(tabId,{state:'product_empty',message:'已识别商品ID,但页面商品信息为空。请确认已登录、详情已加载后刷新重试。',progress:0}); + throw new Error('商品字段为空:请确认淘宝已登录且商品详情加载完成,然后刷新页面重试'); + } + try{ + await chrome.storage.local.set({['tmall_last_raw_'+(raw?.product?.itemId||'unknown')]:raw}); + try{ + const p=(raw?.product?.parameterCollection)||{}; + await chrome.storage.local.set({['tmall_param_status_'+(raw?.product?.itemId||'unknown')]:p}); + }catch(e){} + + }catch(e){} + + await chrome.storage.local.set({['last_raw_'+raw.product.itemId]:raw}); + if(!raw.collection?.reviewCollectionComplete){ + const reason=raw.collection?.reviewStopReason||'unknown'; + const human=reason==='human_verification_required';const stopped=reason==='user_stopped'; + const noReviewData=!raw.reviews?.length&&!raw.collection?.networkReviewResponses; + const folded=raw.collection?.foldedDefaultReviewCount?`;平台另折叠 ${raw.collection.foldedDefaultReviewCount} 条默认好评,未提供逐条内容`:''; + const msg=stopped?`已终止采集,并保存当前 ${raw.reviews?.length||0} 条评论。可随时继续。`:human?`评论已保存 ${raw.reviews?.length||0} 条,页面出现验证。请手动完成验证后继续。`:noReviewData?'页面未释放可读取的评论数据。商品信息已保存;请在当前页打开“评价/评论”后再次点击继续采集。':`评论已保存 ${raw.reviews?.length||0} 条,尚未确认完整${folded}。已停止低效空转,可继续采集。`; + await setStatus(tabId,{state:stopped?'reviews_stopped':'reviews_incomplete',message:msg,progress:68,itemId:raw.product.itemId,reviewStopReason:reason,reviewLive:raw.collection?.reviewLive||null,parameterStatus:raw.product?.parameterCollection||null}); + return {ok:false,needsReviewResume:true,message:msg,reviewStopReason:reason,rawStats:{title:raw.product?.title||'',reviews:raw.reviews?.length||0,questions:raw.questions?.length||0,sku:raw.sku?.length||0,attributes:raw.attributes?.length||0,promotions:raw.promotions?.length||0,images:raw.images?.all?.length||0}}; + } + await setStatus(tabId,{state:'sending',message:`评论完整性已确认,共 ${raw.reviews?.length||0} 条。正在生成新品开品闭环…`,progress:78,itemId:raw.product.itemId}); + const r=await fetch(SERVER+'/api/analyze',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(raw)}); + const j=await r.json(); if(!r.ok||!j.ok) throw new Error(j.error||'提交分析失败'); + await setStatus(tabId,{state:'analyzing',message:'正在分析全方位产品体验并生成解决方案',progress:82,taskId:j.taskId,taskUrl:j.taskUrl}); + await chrome.tabs.create({url:j.taskUrl}); + return {ok:true,taskId:j.taskId,rawStats:{title:raw.product?.title||'',reviews:raw.reviews?.length||0,questions:raw.questions?.length||0,sku:raw.sku?.length||0,attributes:raw.attributes?.length||0,promotions:raw.promotions?.length||0,images:raw.images?.all?.length||0}}; +} + +async function collector(manual=false){ + const delay=ms=>new Promise(r=>setTimeout(r,ms)); + const text=(el)=>el?.innerText?.trim()||el?.textContent?.trim()||''; + const clean=s=>(s||'').replace(/\s+/g,' ').trim(); + const uniq=a=>[...new Set((a||[]).filter(Boolean))]; + let queryRoots=[]; let queryRootsAt=0; + const refreshQueryRoots=()=>{ + if(Date.now()-queryRootsAt<1200&&queryRoots.length)return; + const roots=[document]; + for(let i=0;i{refreshQueryRoots();const out=[];for(const root of queryRoots){try{out.push(...root.querySelectorAll(s))}catch(e){}}return [...new Set(out)]}; + const assetUrl=u=>{u=String(u||'').trim();if(u.startsWith('//'))u='https:'+u;return u.replace(/&/g,'&')}; + const srcOf=img=>assetUrl(img?.currentSrc||img?.src||img?.getAttribute?.('data-src')||img?.getAttribute?.('data-ks-lazyload')||img?.getAttribute?.('data-lazyload-src')||''); + const url=new URL(location.href); const itemId=url.searchParams.get('id')||location.href.match(/[?&]id=(\d+)/)?.[1]||''; const skuId=url.searchParams.get('skuId')||''; + const bodyClone=document.body?.cloneNode(true); + try{bodyClone?.querySelectorAll?.('[class*=recommend],[class*=Recommend],[class*=guess],[class*=Guess],[class*=related],[class*=Related],[class*=similar],[class*=Similar],[class*=猜你喜欢],[id*=recommend],[id*=guess]').forEach(n=>n.remove())}catch(e){} + const bodyText=clean(bodyClone?.innerText||document.body?.innerText||''); + const captured=[]; + const reviews=[]; const questions=[]; + const reviewNetMeta={totals:[],hasMoreFalse:false,lastPageFlags:[],pageHints:[],networkReviewObjects:0,networkResponses:0}; + const reviewStartedAt=Date.now(); + const processedCaptureKeys=new Set(); + function dedupReviewCount(){const m=new Set();for(const r of reviews){const k=clean(r.content||'').replace(/\s+/g,'').slice(0,320);if(k)m.add(k)}return m.size} + function publishReviewProgress(round=0,stagnant=0,mode='DOM',extra={}){const seen=new Set(),preview=[];for(let i=reviews.length-1;i>=0&&preview.length<3;i--){const c=clean(reviews[i]?.content||'');const k=c.replace(/\s+/g,'').slice(0,320);if(k&&!seen.has(k)){seen.add(k);preview.push(c.slice(0,70)+(c.length>70?'…':''))}}window.__TMALL_AI_REVIEW_PROGRESS__={ + count:dedupReviewCount(), + rawCount:reviews.length, + networkReviewObjects:reviewNetMeta.networkReviewObjects, + networkResponses:reviewNetMeta.networkResponses, + round,stagnant,mode, + elapsedMs:Date.now()-reviewStartedAt, + preview, + capturePolicy:'passive_code_and_network', + extraRequestsCreated:0, + safety:'no_api_replay_no_auth_bypass_no_rate_limit_bypass', + ...extra + }} + + const looksQuestion=(c)=>{c=clean(c);return /问大家|更多回答|查看全部问答|大家问|^问[::\s]|请问|请教|会不会|有没有|能不能|是否|怎么|如何|多少|适合|兼容|保质期|[??]$/.test(c)}; + const addQuestion=(o,source='unknown')=>{ const q=clean(o?.question||o?.title||o?.ask||o?.content||o?.text||''); const a=clean(o?.answer||o?.reply||o?.answers?.[0]?.content||''); if(q.length>2)questions.push({question:q,answer:a,source}); }; + const reviewImageUrl=v=>assetUrl(typeof v==='string'?v:(v?.url||v?.picUrl||v?.imageUrl||v?.src||v?.bigUrl||v?.thumbnail||'')); + const validReviewImage=u=>/^https?:\/\//.test(u)&&!/(?:avatar|sns_logo|credit|logo|icon|defaultRate|sprite|\/tps\/|\/tfs\/TB1[^/]+-\d{1,4}-\d{1,4}\.png)/i.test(u); + const addReview=(o,source='unknown')=>{ if(!o)return; const c=clean(o.content||o.rateContent||o.feedback||o.comment||o.text||o.contentText||''); if(c.length<3)return; if(looksQuestion(c)){addQuestion({question:c},source);return;} const media=uniq((o.images||o.pics||o.photos||[]).map(reviewImageUrl)).filter(validReviewImage).slice(0,24);reviews.push({content:c,appendContent:clean(o.appendContent||o.append||o.appendComment||''),rating:o.rating||o.rate||o.star||o.score||o.starLevel||'',sku:clean(o.sku||o.skuInfo||o.auctionSku||o.skuMap||''),date:clean(o.date||o.time||o.gmtCreate||o.rateDate||''),images:media,likes:o.likes||o.likeCount||0,source});}; + function scanPageMeta(obj){ + if(!obj||typeof obj!=='object'||Array.isArray(obj))return; + for(const [k,v] of Object.entries(obj)){ + const key=String(k).toLowerCase(); + if(/^(total|totalcount|total_count|reviewcount|ratecount|commentcount|totalnum|totalnumber)$/.test(key)){ + const n=Number(String(v).replace(/,/g,'')); if(Number.isFinite(n)&&n>=0&&n<10000000)reviewNetMeta.totals.push(n); + } + if(/^(hasmore|has_more|hasnext|has_next|hasnextpage|has_next_page|more)$/.test(key) && (v===false||v===0||v==='false'||v==='0')) reviewNetMeta.hasMoreFalse=true; + if(/^(lastpage|islast|is_last|islastpage|is_last_page|last|end|isend|is_end)$/.test(key) && (v===true||v===1||v==='true'||v==='1')) reviewNetMeta.lastPageFlags.push(true); + if(/^(page|pageno|page_no|pagenum|page_num|currentpage|current_page)$/.test(key)){ + const n=Number(v); if(Number.isFinite(n)&&n>0)reviewNetMeta.pageHints.push(n); + } + } + } + function walk(obj,depth=0,sourceUrl=''){ + if(!obj||depth>10)return; + if(Array.isArray(obj)){for(const x of obj.slice(0,2500))walk(x,depth+1,sourceUrl);return;} + if(typeof obj!=='object')return; + scanPageMeta(obj); + const ks=Object.keys(obj); const qaUrl=/ask|question|wdj|qa/i.test(sourceUrl||''); + const hasQ=ks.some(k=>/question|ask/i.test(k)); const hasA=ks.some(k=>/answer|reply/i.test(k)); + if(qaUrl || (hasQ&&hasA)) addQuestion(obj,sourceUrl||'network'); + else if(ks.some(k=>/rateContent|feedback|comment|contentText|reviewContent|rateText/i.test(k)) || ((('content'in obj)||('text'in obj))&&(/review|rate|comment|feedback|evaluate|评价|评论/i.test(sourceUrl||'') || ks.some(k=>/star|rating|sku|rate|append|user|buyer|date|time|pic|photo/i.test(k))))){ + const before=reviews.length; addReview(obj,sourceUrl||'network'); if(reviews.length>before)reviewNetMeta.networkReviewObjects++; + } + for(const k of ks){ const v=obj[k]; if(v&&typeof v==='object')walk(v,depth+1,sourceUrl); } + } + // V8.1.5:评论网络监听由 page-review-hook.js 在 document_start 提前安装。 + // 这里禁止再次重置 __TMALL_AI_CAPTURE__,否则会丢失用户打开商品页后已经收到的评论响应。 + // 如果极端情况下 MAIN-world hook 未安装,只记录状态并继续使用 DOM / inline JSON 兜底,不主动重放接口。 + if(!Array.isArray(window.__TMALL_AI_CAPTURE__)) window.__TMALL_AI_CAPTURE__=[]; + if(!Array.isArray(window.__TMALL_AI_SOURCE_SNAPSHOTS__)) window.__TMALL_AI_SOURCE_SNAPSHOTS__=[]; + + const visible=(el)=>{try{const r=el.getBoundingClientRect?.();return !r||(r.width>0&&r.height>0)}catch(e){return true}}; + const textControl=(patterns)=>{ + const matches=$all('button,a,[role=tab],[role=button],li,div,span').filter(el=>{ + const t=clean(text(el)); + return visible(el)&&t&&t.length<=36&&patterns.some(pattern=>pattern.test(t)); + }); + return matches.sort((a,b)=>{ + const ai=/^(BUTTON|A)$/.test(a.tagName)||a.getAttribute('role')==='tab'||a.getAttribute('role')==='button'; + const bi=/^(BUTTON|A)$/.test(b.tagName)||b.getAttribute('role')==='tab'||b.getAttribute('role')==='button'; + return Number(bi)-Number(ai)||clean(text(a)).length-clean(text(b)).length; + })[0]; + }; + const activatePanel=async(patterns)=>{ + const control=textControl(patterns);if(!control)return false; + try{control.scrollIntoView({block:'center'});await delay(220);control.click();await delay(1000);queryRootsAt=0;return true}catch(e){return false} + }; + const reviewDrawerOpen=()=>{ + const scrollBox=$all('[class*=comments],[class*=Comments],[class*=commentList],[class*=CommentList]').find(el=>{ + try{const style=getComputedStyle(el);return visible(el)&&/(auto|scroll)/.test(style.overflowY)&&el.clientHeight>180} + catch(e){return false} + }); + const drawer=$all('[class*=Drawer],[class*=drawer]').find(el=>visible(el)&&/用户评价/.test(clean(text(el)))); + return Boolean(scrollBox&&drawer); + }; + const clickControl=async(el,waitMs=900)=>{ + if(!el)return false; + try{ + el.scrollIntoView({block:'center',inline:'nearest'});await delay(220); + el.click();await delay(waitMs);queryRootsAt=0;return true; + }catch(e){return false} + }; + const openReviewDrawer=async()=>{ + if(reviewDrawerOpen())return true; + for(let attempt=0;attempt<4;attempt++){ + queryRootsAt=0; + const showAll=$all('button,a,[role=button],div,span').filter(el=>{ + const t=clean(text(el));return visible(el)&&/^(查看全部评价|查看全部评论|全部评价|全部评论)$/.test(t); + }).sort((a,b)=>{ + const score=el=>/ShowButton/i.test(String(el.className||''))?4:(/^(BUTTON|A)$/.test(el.tagName)||el.getAttribute('role')==='button'?3:/footer/i.test(String(el.className||''))?1:2); + return score(b)-score(a)||clean(text(b)).length-clean(text(a)).length; + })[0]; + if(showAll){publishReviewProgress(pagesVisited,0,'打开全部评价');await clickControl(showAll,1200);if(reviewDrawerOpen())return true;} + const reviewTab=$all('button,a,[role=tab],[role=button],div,span').filter(el=>visible(el)&&clean(text(el))==='用户评价').sort((a,b)=>{ + const ac=/tabTitleItem/i.test(String(a.className||''));const bc=/tabTitleItem/i.test(String(b.className||''));return Number(bc)-Number(ac); + })[0]; + if(reviewTab){publishReviewProgress(pagesVisited,0,'定位用户评价');await clickControl(/tabTitle/i.test(String(reviewTab.parentElement?.className||''))?reviewTab.parentElement:reviewTab,700);} + else{window.scrollBy(0,700);await delay(500)} + } + return reviewDrawerOpen(); + }; + // V5: Collect all currently accessible effective reviews. Resume from page-local checkpoint when possible. + const checkpointKey='__TMALL_AI_REVIEWS_V62__'+itemId; + async function idbOpen(){return await new Promise((resolve,reject)=>{try{const req=indexedDB.open('TMALL_AI_V62',1);req.onupgradeneeded=()=>{const db=req.result;if(!db.objectStoreNames.contains('checkpoints'))db.createObjectStore('checkpoints')};req.onsuccess=()=>resolve(req.result);req.onerror=()=>reject(req.error)}catch(e){reject(e)}})} + async function loadCheckpoint(){try{const db=await idbOpen();return await new Promise((resolve,reject)=>{const tx=db.transaction('checkpoints','readonly');const req=tx.objectStore('checkpoints').get(checkpointKey);req.onsuccess=()=>resolve(req.result||{});req.onerror=()=>reject(req.error)})}catch(e){try{return JSON.parse(localStorage.getItem(checkpointKey)||'{}')||{}}catch(_){return {}}}} + async function saveCheckpoint(data){try{const db=await idbOpen();await new Promise((resolve,reject)=>{const tx=db.transaction('checkpoints','readwrite');tx.objectStore('checkpoints').put(data,checkpointKey);tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error)})}catch(e){try{localStorage.setItem(checkpointKey,JSON.stringify({...data,reviews:(data.reviews||[]).slice(-1500),questions:(data.questions||[]).slice(-500)}))}catch(_){}}} + let checkpoint=await loadCheckpoint(); checkpoint=checkpoint||{}; + for(const r of (checkpoint.reviews||[])) addReview(r); for(const q of (checkpoint.questions||[])) addQuestion(q,q.source||'checkpoint'); + let collectionComplete=false, stopReason='unknown', pagesVisited=Number(checkpoint.round||0), explicitEnd=false; + const domReviewSeen=new Set(); + function collectVisibleReviews(){ + const selectors=[ + '[class^="Comment--"]','[class^="comment--"]','[class*=rate-item]','[class*=rateItem]','[class*=review-item]','[class*=reviewItem]', + '[class*=comment-item]','[class*=commentItem]','[class*=feedback-item]','[class*=feedbackItem]', + '[data-testid*=review-card]','[data-testid=review]','[class*=ReviewItem]','[class*=CommentItem]','[class*=RateItem]','[class*=reviewCard]','[class*=commentCard]','[class*=rateCard]' + ]; + const rawNodes=uniq(selectors.flatMap(s=>{try{return $all(s)}catch(e){return[]}})); + const genericNodes=$all('article,[role=article],li,section,div').filter(el=>{ + const raw=String(el.innerText||'').replace(/\u00a0/g,' ').trim(); + return visible(el)&&raw.length>=10&&raw.length<=1500&&/20\d{2}[-/.年]\s*\d{1,2}[-/.月]\s*\d{1,2}/.test(raw)&&/[。!?!?,,]/.test(raw); + }); + const genericAtomic=genericNodes.filter(n=>![...n.children].some(child=>genericNodes.includes(child)&&String(child.innerText||'').trim().length>=10)); + const nodes=uniq([...rawNodes,...genericAtomic]).filter(n=>!rawNodes.some(o=>o!==n&&o.contains?.(n))&&!genericAtomic.some(o=>o!==n&&o.contains?.(n))); // 只保留独立评论卡,避免父子容器重复/合并 + for(const b of nodes){ + const contentNode=b.querySelector?.('[class^="content--"],[class^="Content--"],[class*=comment-content],[class*=review-content],[class*=rate-content],[data-testid*=content]'); + const c=clean(text(contentNode)||text(b)); + if(c.length<8||c.length>1800) continue; + const k=c.replace(/\s+/g,'').slice(0,240); + if(domReviewSeen.has(k)) continue; + domReviewSeen.add(k); + const ratingText=clean(b.querySelector?.('[class*=star],[class*=score],[class*=rating]')?.getAttribute?.('aria-label')||text(b.querySelector?.('[class*=star],[class*=score],[class*=rating]'))); + const rm=ratingText.match(/([1-5](?:\.\d)?)/); + const sku=clean(text(b.querySelector?.('[class*=sku],[class*=spec],[class*=auction]'))); + const metaText=clean(text(b.querySelector?.('time,[class*=date],[class*=time],[class^="meta--"]'))); + const date=metaText.match(/20\d{2}[^\d]?\d{1,2}[^\d]?\d{1,2}/)?.[0]||metaText; + const purchased=metaText.match(/已购[::]?\s*(.+)$/)?.[1]||''; + const appendContent=clean((clean(text(b)).match(/(?:追评|追加评价)[::]\s*(.{2,1200})/)||[])[1]||''); + const images=uniq([...b.querySelectorAll?.('img')||[]].map(srcOf)).filter(validReviewImage).slice(0,16); + addReview({content:c,appendContent,rating:rm?.[1]||'',sku:purchased||sku,date,images},'dom_visible'); + } + } + function scrollReviewContainers(){ + let moved=false; + const boxes=$all('[class*=comments],[class*=Comments],[class*=commentList],[class*=CommentList],[class*=drawer],[class*=Drawer]').filter(el=>{ + try{return visible(el)&&el.scrollHeight>el.clientHeight+8} + catch(e){return false} + }); + for(const box of boxes){ + try{ + const old=box.scrollTop; const step=Math.max(420,Math.floor(box.clientHeight*.82)); + box.scrollTop=Math.min(box.scrollHeight-box.clientHeight,old+step); + if(box.scrollTop>old+2)moved=true; + box.dispatchEvent(new Event('scroll',{bubbles:true})); + }catch(e){} + } + return moved; + } + function collectVisibleQuestions(){ + const selectors=['[class*=question]','[class*=Question]','[class*=ask]','[class*=Ask]','[class*=qa]','[class*=Qa]']; + const nodes=uniq(selectors.flatMap(sel=>{try{return $all(sel)}catch(e){return[]}})); + for(const b of nodes){const c=clean(text(b));if(c.length>=4&&c.length<=1800&&(/问大家|更多回答|查看全部问答|请问|请教|会不会|有没有|能不能|是否|怎么|如何|多少|适合|兼容|保质期|[??]/.test(c)))addQuestion({question:c},'dom_question');} + } + // Trigger review/question areas if clickable text exists. + // 先按用户通常的顺序打开详情/规格区域;新版页面常把参数放在折叠面板内。 + await activatePanel([/^(?:宝贝)?详情$/,/商品详情/,/规格参数|商品参数|产品参数/]); + // 必须确认“查看全部评价”抽屉已打开;只点“用户评价”页签不算成功。 + const reviewDrawerWasOpened=await openReviewDrawer(); + // V8.0.4:实时、可终止、快速停滞检测。优先分页/加载更多,不再长时间整页空转。 + let stagnant=0;const MAX_ROUNDS=40;publishReviewProgress(pagesVisited,0,reviewDrawerWasOpened?'评论抽屉已打开':'读取页面可见评论',{reviewDrawerOpened:reviewDrawerWasOpened}); + for(let attempt=0;attempt{const t=clean(text(el));const dis=el.getAttribute?.('disabled')!=null||el.getAttribute?.('aria-disabled')==='true'||/disabled|next-disabled/.test(String(el.className||''));const r=el.getBoundingClientRect?.();return !dis&&t&&t.length<20&&/下一页|下页|查看更多|加载更多|更多评价|展开更多|下一批/.test(t)&&(!r||(r.width>0&&r.height>0))}); + const disabledNext=navNodes.find(el=>{const t=clean(text(el));const dis=el.getAttribute?.('disabled')!=null||el.getAttribute?.('aria-disabled')==='true'||/disabled|next-disabled/.test(String(el.className||''));return dis&&/下一页|下页/.test(t)}); + if(next){try{next.scrollIntoView({block:'center'});await delay(180);next.click();publishReviewProgress(round,stagnant,'加载下一批');await delay(1100+Math.floor(Math.random()*500))}catch(e){}} + else{const rev=document.querySelector('[class*=review],[class*=rate],[class*=comment],[data-testid*=review]');try{rev?.scrollIntoView({block:'center'})}catch(e){}for(let i=0;i<4;i++){if(window.__TMALL_AI_STOP_REVIEW__)break;window.scrollBy(0,650);scrollReviewContainers();await delay(240);collectVisibleReviews();collectVisibleQuestions()}await delay(550)} + if(window.__TMALL_AI_STOP_REVIEW__){stopReason='user_stopped';publishReviewProgress(round,stagnant,'用户终止');break} + // 每轮即时解析已捕获网络响应 + const capNow=(window.__TMALL_AI_CAPTURE__||[]).slice(-120);for(const c of capNow){try{const ck=String(c.__safeKey||[c.method||'',c.url||'',String(c.text||'').slice(0,240)].join('|'));if(processedCaptureKeys.has(ck))continue;processedCaptureKeys.add(ck);if(/review|rate|comment|feed/i.test(c.url||''))reviewNetMeta.networkResponses++;let z=(c.text||'').replace(/^\s*[\w$.]+\(/,'').replace(/\)\s*;?\s*$/,'');try{walk(JSON.parse(z),0,c.url||'')}catch(_){const m=z.match(/\{[\s\S]*\}/);if(m)try{walk(JSON.parse(m[0]),0,c.url||'')}catch(__){}}}catch(e){}} + collectVisibleReviews();collectVisibleQuestions();const after=dedupReviewCount();stagnant=after<=before?stagnant+1:0;publishReviewProgress(round+1,stagnant,next?'翻页/加载更多':'短滚动',{lastDelta:after-before}); + const bodyNow=clean(document.body?.innerText||'');const noMoreText=/没有更多(?:评价|评论)|已显示全部(?:评价|评论)|到底了|暂无更多(?:评价|评论)/.test(bodyNow);const captcha=/验证码|滑块|安全验证|访问过于频繁|操作频繁/.test(bodyNow); + if(captcha){stopReason='human_verification_required';publishReviewProgress(round+1,stagnant,'需要人工验证');break} + if((disabledNext||noMoreText)&&stagnant>=1){explicitEnd=true;collectionComplete=true;stopReason='explicit_last_page';publishReviewProgress(round+1,stagnant,'页面明确结束');break} + // 连续4轮没有新增才停止;给异步评论接口和虚拟列表更多响应时间,但仍避免长时间空转。 + if(stagnant>=4){collectionComplete=false;explicitEnd=false;stopReason='no_progress_after_4_rounds';publishReviewProgress(round+1,stagnant,'无新增,停止空转');break} + await saveCheckpoint({reviews,questions,round:round+1,updatedAt:Date.now()}); + } + await delay(300); + // V8.1.5 安全模式: + // 只消费页面在正常浏览过程中已经产生的真实 fetch/XHR 响应。 + // 不根据 performance resource URL 主动重放请求,避免额外接口调用、重复页和频控风险。 + captured.push(...(window.__TMALL_AI_CAPTURE__||[]).slice(-100)); + + // 同时读取页面已经内嵌的脚本/JSON快照,不产生任何网络请求。 + const sourceSnapshots=(window.__TMALL_AI_SOURCE_SNAPSHOTS__||[]).slice(-12); + for(const snap of sourceSnapshots){ + captured.push({ + url:'page-source://inline-script', + method:'SOURCE', + status:200, + text:String(snap?.text||'').slice(0,1200000), + passive:true + }); + } + for(const c of captured){ + const ck=String(c.__safeKey||[c.method||'',c.url||'',String(c.text||'').slice(0,240)].join('|')); + if(processedCaptureKeys.has(ck)) continue; + processedCaptureKeys.add(ck); + if(/review|rate|comment|feed/i.test(c.url||''))reviewNetMeta.networkResponses++; + let s=c.text||''; s=s.replace(/^\s*[\w$.]+\(/,'').replace(/\)\s*;?\s*$/,''); + try{walk(JSON.parse(s),0,c.url||'')}catch(e){ const m=s.match(/\{[\s\S]*\}/);if(m)try{walk(JSON.parse(m[0]),0,c.url||'')}catch(_){}} + } + // DOM fallback:仍按“单个评论卡”解析,不读取整块评价容器,避免把多条买家评论合并。 + collectVisibleReviews(); collectVisibleQuestions(); + // 评论采集完成后再切换问大家,读取页面正常加载出来的问答,不额外调用接口。 + const qaNow=$all('a,button,div,span,[role=tab]').find(el=>{const t=clean(text(el));return t&&t.length<32&&/问大家|大家问/.test(t)}); + if(qaNow){ + try{ + qaNow.scrollIntoView({block:'center'});await delay(220);qaNow.click();await delay(1000); + for(let i=0;i<3;i++){collectVisibleQuestions();window.scrollBy(0,420);await delay(220)} + collectVisibleQuestions(); + }catch(e){} + } + // 评论完成后重新打开详情/规格面板,避免问大家面板覆盖参数区域。 + await activatePanel([/^(?:宝贝)?详情$/,/商品详情/,/规格参数|商品参数|产品参数/]); + // 像正常浏览一样有限滚动详情容器,触发页面自身的图片懒加载;不创建额外接口请求。 + const detailRootSelectors=['#J_DivItemDesc','#description','[id="description"]','[class*="detailContent"]','[class*="DetailContent"]','[class*="detail-content"]','[class*="descV8"]','[class^="desc--"]','[data-testid*="product-description"]','[class*="ProductDetail"]','[class*="productDetail"]','[class*="detailDesc"]','[class*="DetailDesc"]','[class*="moduleDesc"]','[class*="ModuleDesc"]','[class*="contentDetail"]','[class*="ContentDetail"]','[class*="aplus"]','[class*="Aplus"]']; + const detailRoots=uniq(detailRootSelectors.flatMap(sel=>{try{return $all(sel)}catch(e){return[]}})); + const detailScanUrls=[]; + const scanRootImages=root=>{try{for(const img of root.querySelectorAll('img')){const u=srcOf(img);if(u&&!detailScanUrls.includes(u))detailScanUrls.push(u)}}catch(e){}}; + for(const root of detailRoots.slice(0,4)){ + try{ + const box=root.getBoundingClientRect();const top=Math.max(0,window.scrollY+box.top-120);const height=Math.max(box.height,root.scrollHeight||0);const rounds=Math.min(14,Math.max(1,Math.ceil(height/700))); + for(let i=0;in.remove())}catch(e){} + const liveBodyRaw=freshClone?.innerText||document.body?.innerText||''; + const liveBodyText=clean(liveBodyRaw); + const firstText=(selectors)=>{for(const sel of selectors){try{const v=clean(text(document.querySelector(sel)));if(v)return v}catch(e){}}return''}; + const meta=(selectors)=>{for(const sel of selectors){try{const v=clean(document.querySelector(sel)?.getAttribute?.('content')||'');if(v)return v}catch(e){}}return''}; + const scalar=v=>(typeof v==='string'||typeof v==='number')?clean(String(v)):''; + const structured={titles:[],shops:[],brands:[],categories:[],prices:[],sold:[],sku:[],attrs:[],promos:[],images:[],imageRecords:[]}; + const addSignal=(group,value,max=300)=>{value=scalar(value);if(value&&value.length<=max&&!structured[group].includes(value))structured[group].push(value)}; + const roots=[]; + for(const script of $all('script[type="application/ld+json"],script#__NEXT_DATA__,script[type="application/json"]')){ + const raw=String(script.textContent||'').trim();if(!raw||raw.length>6000000)continue; + try{roots.push(JSON.parse(raw))}catch(e){} + } + for(const key of ['__INITIAL_STATE__','__INIT_DATA__','__DATA__','__SSR_DATA__','__APOLLO_STATE__']){ + try{const value=window[key];if(value&&typeof value==='object')roots.push(value)}catch(e){} + } + const seenStructured=new WeakSet();let structuredNodes=0; + function walkProduct(obj,path='',depth=0){ + if(!obj||typeof obj!=='object'||depth>11||structuredNodes>30000||seenStructured.has(obj))return; + seenStructured.add(obj);structuredNodes++; + if(Array.isArray(obj)){for(const x of obj.slice(0,2000))walkProduct(x,path,depth+1);return} + const objName=scalar(obj.attrName||obj.propertyName||obj.propName||obj.name||obj.label||obj.key); + const objValue=scalar(obj.attrValue||obj.propertyValue||obj.valueName||obj.value||obj.text); + if(objName&&objValue&&/(?:attr|param|property|prop|feature|规格|参数|属性)/i.test(path))structured.attrs.push({name:objName,value:objValue}); + for(const [key,value] of Object.entries(obj)){ + const lower=String(key).toLowerCase(),next=path?path+'.'+lower:lower; + if(value&&typeof value==='object'){walkProduct(value,next,depth+1);continue} + const valueText=scalar(value);if(!valueText)continue; + if(/^(title|itemtitle|item_title|auctiontitle|producttitle|subject|name)$/.test(lower)&&/(?:item|product|auction|detail|schema|offers?|^$)/i.test(path))addSignal('titles',valueText,260); + if(/^(shopname|shop_name|shoptitle|sellername|seller_name|storename|store_name|nick)$/.test(lower))addSignal('shops',valueText,160); + if(/^(brand|brandname|brand_name)$/.test(lower))addSignal('brands',valueText,120); + if(/^(category|categoryname|category_name|catname)$/.test(lower))addSignal('categories',valueText,160); + if(/^(price|pricetext|price_text|promotionprice|promotion_price|saleprice|sale_price|pricevalue)$/.test(lower))addSignal('prices',valueText,80); + if(/^(sold|soldcount|sold_count|sales|salescount|sales_count|monthsold|monthlysales)$/.test(lower))addSignal('sold',valueText,80); + if(/(?:sku|property|prop|spec)/i.test(path)&&/^(name|text|title|value|valuename|value_name)$/.test(lower))addSignal('sku',valueText,100); + if(/(?:promo|promotion|activity|coupon|discount|benefit|service)/i.test(next))addSignal('promos',valueText,220); + if(/(?:image|img|pic|picture|thumb)/i.test(lower)&&/^https?:\/\/|^\/\//i.test(valueText)){ + const imageUrl=assetUrl(valueText);addSignal('images',imageUrl,1200); + if(imageUrl&&!structured.imageRecords.some(x=>x.url===imageUrl&&x.path===next))structured.imageRecords.push({url:imageUrl,path:next}); + } + } + } + for(const root of roots)walkProduct(root); + const relevantScripts=$all('script').map(s=>String(s.textContent||'')).filter(s=>s.length>20&&s.length<=6000000&&(s.includes(itemId)||/itemTitle|auctionTitle|shopName|skuBase|priceInfo|promotion/i.test(s))).slice(0,16); + const scriptCorpus=relevantScripts.join('\n').slice(0,10000000); + const scriptString=(keys,max=300)=>{ + for(const key of keys){ + const escaped=key.replace(/[.*+?^${}()|[\]\\]/g,'\\$&'); + const m=scriptCorpus.match(new RegExp('["\\\']'+escaped+'["\\\']\\s*:\\s*"((?:\\\\.|[^"\\\\]){1,'+max+'})"','i')); + if(m){try{return clean(JSON.parse('"'+m[1]+'"'))}catch(e){return clean(m[1].replace(/\\u([0-9a-f]{4})/gi,(_,h)=>String.fromCharCode(parseInt(h,16))))}} + } + return''; + }; + const validProductTitle=value=>{ + value=clean(value).replace(/[-_–—|]\s*(淘宝网|天猫|淘宝).*$/,''); + if(value.length<4||value.length>260)return''; + if(/用户评价|累计评价|商品评价|问大家|参数信息|规格参数|图文详情|商品详情|登录|验证码/.test(value))return''; + return value; + }; + const titleCandidates=[ + meta(['meta[property="og:title"]','meta[name="twitter:title"]']), + ...structured.titles, + scriptString(['itemTitle','auctionTitle','productTitle']), + firstText(['[data-testid="item-title"]','[data-testid*=item-title]','h1[class*="ItemTitle"]','h1[class*="itemTitle"]','[class*="ItemTitle--"]','[class*="itemTitle--"]','h1']), + clean(document.title) + ]; + const title=titleCandidates.map(validProductTitle).find(Boolean)||''; + const visiblePriceTexts=$all('[class*=price],[class*=Price],[data-testid*=price]').filter(el=>{const r=el.getBoundingClientRect?.();return !r||(r.width>0&&r.height>0&&r.top<1600)}).map(text).map(clean).filter(x=>/\d/.test(x)).slice(0,30); + const metaPrice=meta(['meta[property="product:price:amount"]','meta[property="og:price:amount"]','meta[itemprop="price"]']); + const visiblePrice=(visiblePriceTexts.join(' ')+' '+liveBodyText.slice(0,6000)).match(/(?:到手价|券后价|活动价|促销价|价格)?\s*[¥¥]\s*(\d{1,7}(?:\.\d{1,2})?)/)?.[1]||(liveBodyText.slice(0,6000).match(/(?:到手价|券后价|活动价|促销价|价格)\s*[::]?\s*(\d{1,7}(?:\.\d{1,2})?)/)||[])[1]||''; + const fallbackPrice=structured.prices[0]||scriptString(['promotionPrice','salePrice','priceText','price'],80); + const priceValue=(String(metaPrice).match(/\d{1,7}(?:\.\d{1,2})?/)||[])[0]||visiblePrice||(String(fallbackPrice).match(/\d{1,7}(?:\.\d{1,2})?/)||[])[0]||''; + const soldMatch=liveBodyText.match(/(?:已售|销量|月销|付款|成交)\s*([\d,.万+]+)/); + const soldValue=soldMatch?.[1]||structured.sold[0]||scriptString(['soldCount','salesCount','monthSold','monthlySales'],80); + const rankMatch=liveBodyText.match(/(?:热销榜|榜单|排名)[^\d]{0,8}(?:第)?\s*(\d+)\s*名?/); + const shop=firstText(['[data-testid*=shop]','[class*=shopName]','[class*=shop-name]','[class*=ShopName]','[class*=sellerName]','[class*=SellerName]','[class*=storeName]','[class*=StoreName]'])||meta(['meta[name="seller"]'])||structured.shops[0]||scriptString(['shopName','shopTitle','sellerName','storeName'],160); + const domSkuTexts=$all('[class*=sku] button,[class*=sku] li,[class*=sku] label,[class*=sku] [role=button],[class*=Sku] button,[class*=Sku] li,[class*=prop] button,[class*=Prop] button,[data-testid*=sku] button').map(text).map(clean); + const skuTexts=uniq([...domSkuTexts,...structured.sku]).filter(x=>x&&x.length<100&&!/加入购物车|立即购买|领券购买|购买|收藏|客服|分享|确定|取消|数量/.test(x)).slice(0,160); + // 参数必采:同时读取属性列表、规格参数、表格、dt/dd 等常见结构;只保留短文本,避免把整块详情正文误当参数。 + const paramPairs=[]; + const validParam=(name,value)=>{ + if(!name||!value||name.length>60||value.length>220)return false; + if(/^\d+$/.test(name)||/(?:window\.|console\.|function\b|frontend_data|performance\.|[{}\[\]"'=;<>])/i.test(name))return false; + if(/(?:console\.log|window\.|performance\.now|frontend_data)/i.test(value))return false; + return /[\u4e00-\u9fffA-Za-z]/.test(name); + }; + const pushParam=(name,value)=>{name=clean(name);value=clean(value);if(!validParam(name,value))return;const k=name+'\u0000'+value;if(!paramPairs.some(x=>x._k===k))paramPairs.push({name,value,_k:k});}; + const parseParamText=(s)=>{s=clean(s);if(!s||s.length>260)return;const m=s.match(/^([^::]{1,60})[::]\s*(.{1,220})$/);if(m)pushParam(m[1],m[2]);}; + for(const el of $all('[class*=attr] li,[class*=Attr] li,[class*=parameter] li,[class*=Parameter] li,[class*=prop] li,[class*=Prop] li,[class*=specification] li,[class*=Specification] li,[class*=attribute] li,[class*=Attribute] li')) parseParamText(text(el)); + for(const dl of $all('dl')){const dts=[...dl.querySelectorAll('dt')],dds=[...dl.querySelectorAll('dd')];for(let i=0;i=2)pushParam(cells[0],cells.slice(1).join(' '));} + // 兜底读取页面中常见“键:值”短行。 + const paramTextNodes=$all('[class*=attr],[class*=parameter],[class*=specification],[class*=attribute]').slice(0,80); + for(const el of paramTextNodes){for(const line of String(text(el)||'').split(/\n+/).slice(0,80))parseParamText(line);} + // 淘宝新版经常移除参数容器 class,但可见页面仍保留“字段:值”短行。 + for(const line of String(liveBodyRaw||'').split(/\n+/).slice(0,5000))parseParamText(line); + for(const el of $all('li,dt,dd,tr,td,th,[role=listitem]').slice(0,3000)){ + for(const line of String(el.innerText||'').split(/\n+/).slice(0,12))parseParamText(line); + } + // 当前淘宝新版参数是“值 + 字段名”或“字段名 + 值”两列,没有冒号。 + for(const el of $all('[class*="emphasisParamsInfoItem--"],[class*="generalParamsInfoItem--"]').slice(0,400)){ + const children=[...el.children].filter(child=>clean(child.getAttribute?.('title')||text(child))); + if(children.length<2)continue; + const values=children.map(child=>clean(child.getAttribute?.('title')||text(child))); + const isGeneral=/generalParamsInfoItem/i.test(String(el.className||'')); + const label=isGeneral?values[0]:values[1]; + const value=isGeneral?values[1]:values[0]; + pushParam(label,value); + } + for(const pair of structured.attrs)pushParam(pair.name,pair.value); + const attrs=paramPairs.slice(0,240).map(({name,value})=>({name,value})); + const PARAM_GROUPS={ + '基础身份':['品牌','品类','型号','货号','名称'], + '规格选择':['规格','尺寸','尺码','颜色','容量','净含量','口味','版本','数量','包装'], + '材质/成分/配置':['材质','面料','成分','配料','填充','配置','处理器','内存','存储'], + '功能与适用':['功能','功效','适用','肤质','兼容','性能','版型','风格','场景'], + '工艺与使用':['工艺','做工','使用','洗护','洗涤','清洗','保养','食用方法'], + '合规与服务':['产地','等级','执行标准','保质期','生产日期','认证','质保','售后'] + }; + function evaluateParameterCollectionJS(list){ + const found=[],missing=[];const keys=(list||[]).map(x=>clean(x.name)).filter(Boolean); + for(const [canon,aliases] of Object.entries(PARAM_GROUPS)){ + const hit=(list||[]).some(x=>aliases.some(a=>clean(x.name).includes(a))&&clean(x.value)); + (hit?found:missing).push(canon); + } + const state=(list||[]).length>=3?'complete':((list||[]).length?'partial':'missing'); + return {state,stateText:state==='complete'?'动态参数已采集':state==='partial'?'参数样本较少':'未识别到有效参数',count:(list||[]).length,keys:keys.slice(0,50),found,missing:state==='missing'?['页面动态参数']:[]}; + } + const parameterCollection=evaluateParameterCollectionJS(attrs); + const promoDom=$all('[class*=promo],[class*=Promo],[class*=coupon],[class*=Coupon],[class*=activity],[class*=Activity],[class*=discount],[class*=Discount],[class*=service],[class*=Service]').map(text).map(clean); + const promoLines=String(liveBodyRaw||'').split(/\n+/).map(clean).filter(x=>x.length>=2&&x.length<=180&&/(减|券|折|赠|包邮|价保|退|换新|补贴|立减|满|活动|优惠|会员|服务)/.test(x)); + const promoTexts=uniq([...promoDom,...promoLines,...structured.promos]).filter(x=>x&&x.length>=2&&x.length<=220&&/(减|券|折|赠|包邮|价保|退|换新|补贴|立减|满|活动|优惠|会员|服务)/.test(x)).slice(0,120); + const isRecImg=img=>{try{return !!img.closest('[class*=recommend],[class*=Recommend],[class*=guess],[class*=Guess],[class*=related],[class*=Related],[class*=similar],[class*=Similar],[id*=recommend],[id*=guess]')}catch(e){return false}}; + const validImg=u=>/^https?:\/\//.test(u)&&!/icon|logo|avatar|sprite|shopmanager|\/tps\/|\/tfs\/TB1[^/]+-\d{1,4}-\d{1,4}\.png|(?:-|_)rate(?:\.|_|-)|rate_livephoto|tbbala|alicdn.*\.gif/i.test(u); + const deReview=new Map(); + for(const r of reviews){ + const k=clean(r.content).replace(/\s+/g,'').slice(0,320);if(!k)continue; + if(!deReview.has(k))deReview.set(k,{...r,images:uniq((r.images||[]).map(reviewImageUrl)).filter(validReviewImage)}); + else{const old=deReview.get(k);old.images=uniq([...(old.images||[]),...(r.images||[]).map(reviewImageUrl)]).filter(validReviewImage).slice(0,24)} + } + const reviewRows=[...deReview.values()]; + const buyerCandidates=uniq(reviewRows.flatMap(r=>r.images||[])).filter(validReviewImage); + const metaImage=assetUrl(meta(['meta[property="og:image"]','meta[name="twitter:image"]','meta[itemprop="image"]'])); + const isMainRegionImg=img=>{try{if(isRecImg(img)||img.closest('[class*=review],[class*=Review],[class*=rate],[class*=Rate],[class*=comment],[class*=Comment],[class*=detail],[class*=Detail],[class*=desc],[class*=Desc]'))return false;const r=img.getBoundingClientRect();return r.top<1800&&r.bottom>-80&&r.width>=34&&r.height>=34&&r.left{try{if(isRecImg(img)||img.closest('[class*=review],[class*=Review],[class*=rate],[class*=Rate],[class*=comment],[class*=Comment],[class*=detail],[class*=Detail],[class*=desc],[class*=Desc]'))return false;const inGallery=!!img.closest(mainGallerySelector);const w=img.naturalWidth||img.width||img.getBoundingClientRect().width;const h=img.naturalHeight||img.height||img.getBoundingClientRect().height;return inGallery?(w>=34&&h>=34):isMainRegionImg(img)}catch(e){return false}}; + const mainDom=$all(mainGallerySelector+' img').filter(isMainGalleryImg).map(srcOf); + const skuDom=$all('[class*="SkuContent--"] img,[class*="skuContent--"] img,[class*="SkuItem--"] img,[class*="skuItem--"] img,[data-testid*="sku"] img,[class*="sku"] [role="button"] img,[class*="Sku"] [role="button"] img').filter(img=>!isRecImg(img)).map(srcOf); + const detailDom=uniq([...detailScanUrls,...detailRoots.flatMap(root=>{try{return [...root.querySelectorAll('img')].filter(img=>!isRecImg(img)).map(srcOf)}catch(e){return[]}})]); + const structuredMain=structured.imageRecords.filter(x=>/(?:main|gallery|itempic|auctionpic|primary|header)/i.test(x.path)&&!/(?:detail|desc|review|rate|comment|sku|prop|recommend|shop)/i.test(x.path)).map(x=>x.url); + const structuredDetail=structured.imageRecords.filter(x=>/(?:detail|desc|module|content|aplus)/i.test(x.path)&&!/(?:review|rate|comment|recommend|shop)/i.test(x.path)).map(x=>x.url); + const structuredSku=structured.imageRecords.filter(x=>/(?:sku|prop|spec)/i.test(x.path)&&!/(?:review|rate|comment)/i.test(x.path)).map(x=>x.url); + const buyerSet=new Set(buyerCandidates); + const skuImages=uniq([...skuDom,...structuredSku]).filter(validImg).filter(u=>!buyerSet.has(u)).slice(0,80);const skuSet=new Set(skuImages); + const main=uniq([metaImage,...mainDom,...structuredMain]).filter(validImg).filter(u=>!buyerSet.has(u)&&!skuSet.has(u)).slice(0,24);const mainSet=new Set(main); + const detail=uniq([...detailDom,...structuredDetail]).filter(validImg).filter(u=>!buyerSet.has(u)&&!skuSet.has(u)&&!mainSet.has(u)).slice(0,160); + const buyer=buyerCandidates.slice(0,200); + const allPageCandidates=uniq($all('img').filter(img=>!isRecImg(img)).map(srcOf)).filter(validImg); + const allImgs=uniq([...main,...detail,...skuImages,...buyer]); + const provenance=[]; + for(const u of main)provenance.push({url:u,group:'main',source:u===metaImage?'meta_og_image':structuredMain.includes(u)?'structured_main_gallery':'dom_main_gallery'}); + for(const u of detail)provenance.push({url:u,group:'detail',source:structuredDetail.includes(u)?'structured_product_description':'dom_product_description'}); + for(const u of skuImages)provenance.push({url:u,group:'sku',source:structuredSku.includes(u)?'structured_sku_selector':'dom_sku_selector'}); + reviewRows.forEach((r,reviewIndex)=>{for(const u of r.images||[])if(buyerSet.has(u))provenance.push({url:u,group:'buyerShow',source:'review_record',reviewIndex:reviewIndex+1,reviewSource:r.source||'unknown'})}); + const deQ=new Map(); for(const q of questions){const k=clean(q.question).replace(/\s+/g,'').slice(0,260);if(k&&!deQ.has(k))deQ.set(k,q)} + const publicRaw=(liveBodyText.match(/(?:评价|评论)[^\d]{0,8}([\d,.]+\s*万?\+?)/)||[])[1]||''; + const parseCount=v=>{v=String(v||'').replace(/,/g,'').trim();const m=v.match(/([\d.]+)\s*(万)?/);if(!m)return null;return Math.round(parseFloat(m[1])*(m[2]?10000:1))}; + const publicNumeric=parseCount(publicRaw); + const foldedDefaultMatch=liveBodyText.match(/已折叠\s*([\d,.]+)\s*条[^\n]{0,30}评价/); + const defaultPraiseMatch=liveBodyText.match(/近半年\s*([\d,.]+)\s*位买家默认好评/); + const networkTotal=reviewNetMeta.totals.length?Math.max(...reviewNetMeta.totals.filter(n=>Number.isFinite(n))):null; + if(publicNumeric&&deReview.size>=publicNumeric){collectionComplete=true;explicitEnd=true;stopReason='matched_public_review_count';} + else if(networkTotal!==null&&networkTotal>0&&deReview.size>=networkTotal){collectionComplete=true;explicitEnd=true;stopReason='matched_network_review_total';} + else if((reviewNetMeta.hasMoreFalse||reviewNetMeta.lastPageFlags.length>0) && deReview.size>0){collectionComplete=true;explicitEnd=true;stopReason='network_explicit_end';} + // 严禁“无新增/无下一页控件”单独作为完整依据 + const completeness=collectionComplete?'confirmed':'unconfirmed'; + await saveCheckpoint({reviews:[...deReview.values()],questions:[...deQ.values()],round:pagesVisited,updatedAt:Date.now(),complete:collectionComplete,stopReason,completeness,networkTotal,publicNumeric,reviewNetMeta}); + return { + meta:{platform:location.hostname.includes('tmall')?'tmall':'taobao',collectedAt:new Date().toISOString(),url:location.href,collectorVersion:'9.2.2',imageClassificationVersion:'strict-v1',networkCaptures:captured.length,structuredRoots:roots.length}, + product:{itemId,skuId,title,shop,brand:structured.brands[0]||scriptString(['brandName','brand'],120),category:structured.categories[0]||scriptString(['categoryName','catName'],160),url:location.href,parameterCollection}, + sales:{currentPrice:priceValue,sold:soldValue,ranking:rankMatch?.[1]||''}, + sku:skuTexts.map(name=>({name})),attributes:attrs,promotions:promoTexts.map(text=>({text})), + images:{main,detail,sku:skuImages,buyerShow:buyer,all:allImgs,provenance,classification:{version:'strict-v1',classifiedCount:allImgs.length,unclassifiedCount:Math.max(0,allPageCandidates.length-allImgs.length),groupsAreDisjoint:true}}, + reviews:reviewRows,questions:[...deQ.values()],pageText:liveBodyText.slice(0,120000),collection:{reviewActual:deReview.size,questionActual:deQ.size,reviewDrawerOpened:reviewDrawerWasOpened,reviewCollectionComplete:collectionComplete,reviewCompleteness:collectionComplete?'confirmed':'unconfirmed',reviewStopReason:stopReason,reviewPagesVisited:pagesVisited,publicReviewCount:publicRaw,publicReviewCountNumeric:publicNumeric,foldedDefaultReviewCount:foldedDefaultMatch?parseCount(foldedDefaultMatch[1]):null,defaultPraiseBuyerCount:defaultPraiseMatch?parseCount(defaultPraiseMatch[1]):null,networkReviewTotal:networkTotal,networkReviewObjects:reviewNetMeta.networkReviewObjects,networkReviewResponses:reviewNetMeta.networkResponses,networkExplicitEnd:reviewNetMeta.hasMoreFalse||reviewNetMeta.lastPageFlags.length>0,explicitEnd,reviewLive:window.__TMALL_AI_REVIEW_PROGRESS__||null} + }; +} diff --git a/extension/manifest.json b/extension/manifest.json new file mode 100644 index 0000000..23b7efc --- /dev/null +++ b/extension/manifest.json @@ -0,0 +1,40 @@ +{ + "manifest_version": 3, + "name": "三笙 · 电商商品分析闭环 V9.2.5", + "version": "9.2.5", + "description": "跨类目电商商品分析:动态参数与SKU采集、图文分源、评论证据、开品与AI视觉方案。", + "permissions": [ + "scripting", + "storage", + "tabs" + ], + "host_permissions": [ + "https://detail.tmall.com/*", + "https://*.tmall.com/*", + "https://*.taobao.com/*", + "https://*.alicdn.com/*", + "http://127.0.0.1:17962/*" + ], + "background": { + "service_worker": "background.js" + }, + "action": { + "default_popup": "popup.html", + "default_title": "三笙 · 电商商品分析闭环 V9.2.5" + }, + "content_scripts": [ + { + "matches": [ + "https://detail.tmall.com/*", + "https://*.tmall.com/*", + "https://item.taobao.com/*", + "https://*.taobao.com/*" + ], + "js": [ + "page-review-hook.js" + ], + "run_at": "document_start", + "world": "MAIN" + } + ] +} diff --git a/extension/page-review-hook.js b/extension/page-review-hook.js new file mode 100644 index 0000000..ba1a202 --- /dev/null +++ b/extension/page-review-hook.js @@ -0,0 +1,123 @@ +(() => { + if (window.__TMALL_AI_SAFE_REVIEW_HOOK__) return; + window.__TMALL_AI_SAFE_REVIEW_HOOK__ = true; + + const MAX_RECORDS = 100; + const MAX_TEXT = 1200000; + const MATCH = /review|rate|comment|feedback|evaluate|buyer|remark|ratedetail|ask|question|wdj|评价|评论|问大家/i; + + window.__TMALL_AI_CAPTURE__ = Array.isArray(window.__TMALL_AI_CAPTURE__) + ? window.__TMALL_AI_CAPTURE__ : []; + window.__TMALL_AI_SOURCE_SNAPSHOTS__ = Array.isArray(window.__TMALL_AI_SOURCE_SNAPSHOTS__) + ? window.__TMALL_AI_SOURCE_SNAPSHOTS__ : []; + + function pushCapture(rec) { + try { + if (!rec || !MATCH.test(String(rec.url || ""))) return; + const text = String(rec.text || "").slice(0, MAX_TEXT); + const key = [rec.method || "", rec.url || "", text.slice(0, 240)].join("|"); + const list = window.__TMALL_AI_CAPTURE__; + if (list.some(x => x && x.__safeKey === key)) return; + list.push({...rec, text, __safeKey:key, capturedAt:Date.now(), captureMode:"passive_network"}); + while (list.length > MAX_RECORDS) list.shift(); + } catch (_) {} + } + + // Passive fetch interception: clones only the response the page already requested. + try { + const originalFetch = window.fetch; + if (typeof originalFetch === "function") { + window.fetch = async function(...args) { + const response = await originalFetch.apply(this, args); + try { + const req = args[0]; + const opt = args[1] || {}; + const url = String(req?.url || req || ""); + if (MATCH.test(url)) { + const clone = response.clone(); + clone.text().then(text => { + pushCapture({ + url, + method:String(opt.method || req?.method || "GET"), + status:response.status, + text + }); + }).catch(() => {}); + } + } catch (_) {} + return response; + }; + } + } catch (_) {} + + // Passive XHR interception: reads response after normal page request completes. + try { + const open = XMLHttpRequest.prototype.open; + const send = XMLHttpRequest.prototype.send; + XMLHttpRequest.prototype.open = function(method, url, ...rest) { + this.__tmall_ai_url = String(url || ""); + this.__tmall_ai_method = String(method || "GET"); + return open.call(this, method, url, ...rest); + }; + XMLHttpRequest.prototype.send = function(...args) { + if (MATCH.test(this.__tmall_ai_url || "")) { + this.addEventListener("load", () => { + try { + const txt = typeof this.responseText === "string" ? this.responseText : ""; + pushCapture({ + url:this.__tmall_ai_url, + method:this.__tmall_ai_method || "XHR", + status:this.status, + text:txt + }); + } catch (_) {} + }, {once:true}); + } + return send.apply(this, args); + }; + } catch (_) {} + + // Page-code snapshots. Only read script text already present in the DOM. + function snapshotScripts() { + try { + const out = []; + const scripts = document.scripts ? Array.from(document.scripts) : []; + for (const s of scripts) { + const txt = String(s.textContent || ""); + if (txt.length < 20) continue; + if (!/review|rate|comment|evaluate|feedback|question|ask|评价|评论|问大家/i.test(txt)) continue; + out.push({ + source:"inline_script", + text:txt.slice(0, MAX_TEXT), + capturedAt:Date.now() + }); + if (out.length >= 12) break; + } + window.__TMALL_AI_SOURCE_SNAPSHOTS__ = out; + } catch (_) {} + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", snapshotScripts, {once:true}); + } else { + snapshotScripts(); + } + + // Mutation is only used to refresh already-present inline JSON snapshots. + try { + let timer = 0; + const mo = new MutationObserver(() => { + clearTimeout(timer); + timer = setTimeout(snapshotScripts, 1200); + }); + mo.observe(document.documentElement, {childList:true, subtree:true}); + } catch (_) {} + + window.__TMALL_AI_SAFE_CAPTURE_STATUS__ = () => ({ + mode:"passive_code_and_network", + networkRecords:(window.__TMALL_AI_CAPTURE__ || []).length, + sourceSnapshots:(window.__TMALL_AI_SOURCE_SNAPSHOTS__ || []).length, + extraRequestsCreated:0, + installed:true + }); +})(); diff --git a/extension/popup.html b/extension/popup.html new file mode 100644 index 0000000..d604e1b --- /dev/null +++ b/extension/popup.html @@ -0,0 +1,43 @@ + +

三笙 · 电商商品分析引擎

+
采集当前商品后自动识别类目和分析维度,生成标题、主图、详情、SKU、评论、新品设计与 AI 视觉报告。
+
检查本地服务…
等待当前商品页
+
当前商品监测数据
未检测
+ + + + + + +
不会自动采集;不会采集推荐商品。完整采集模式会继续采到可确认末页;快速分析可随时使用当前已采数据生成报告。
+
+
分析方式
+
+ + +
+ +
分析不会清空采集进度。评论可继续采;报告会明确标注当前评论数量与完整性。
+
+ + diff --git a/extension/popup.js b/extension/popup.js new file mode 100644 index 0000000..755e950 --- /dev/null +++ b/extension/popup.js @@ -0,0 +1,124 @@ +const SERVER='http://127.0.0.1:17962'; +const EXPECTED_SERVER_VERSION='9.2.5'; +let backendCompatible=false; +const server=document.getElementById('server'),status=document.getElementById('status'),bar=document.getElementById('bar'),monitorStatus=document.getElementById('monitorStatus'),runBtn=document.getElementById('run'),stopBtn=document.getElementById('stop'),reviewLive=document.getElementById('reviewLive'),liveCount=document.getElementById('liveCount'),liveNet=document.getElementById('liveNet'),liveRound=document.getElementById('liveRound'),liveStagnant=document.getElementById('liveStagnant'),liveMode=document.getElementById('liveMode'),liveElapsed=document.getElementById('liveElapsed'),livePreview=document.getElementById('livePreview'); +const paramLive=document.getElementById('paramLive'),paramState=document.getElementById('paramState'),paramCount=document.getElementById('paramCount'),paramTags=document.getElementById('paramTags'),paramMissing=document.getElementById('paramMissing'); +async function active(){return (await chrome.tabs.query({active:true,currentWindow:true}))[0]} +function itemIdFromUrl(u){try{return new URL(u).searchParams.get('id')||((u||'').match(/[?&]id=(\d+)/)||[])[1]||''}catch(e){return''}} +async function refresh(){ + const h=await chrome.runtime.sendMessage({type:'HEALTH'}); + backendCompatible=!!(h?.ok && h.serverVersion===EXPECTED_SERVER_VERSION); + if(!h?.ok){ + server.textContent='本地服务未启动 ✕'; + server.className='state warn'; + }else if(!backendCompatible){ + server.textContent=`版本不一致 ✕ 插件 ${EXPECTED_SERVER_VERSION} / 后端 ${h.serverVersion||'未知'},请启动本包最新服务`; + server.className='state warn'; + }else{ + server.textContent=`本地服务 ✓ V${h.serverVersion} · ${h.modelConfigured?'模型已配置':'模型未配置'}`; + server.className='state ok'; + } + const t=await active(); if(!t)return; const id=itemIdFromUrl(t.url||''); + if(!id){status.textContent='请打开天猫/淘宝商品详情页';monitorStatus.textContent='当前页面未识别商品ID';return} + const k='status_'+t.id; const d=(await chrome.storage.local.get(k))[k]; + if(d){status.textContent=d.message||d.state;bar.style.width=(d.progress||0)+'%';runBtn.textContent=(d.state==='reviews_incomplete'||d.state==='reviews_stopped')?'继续采集评论':'开始深度采集';const lp=d.reviewLive;const activeCollect=d.state==='collecting'||d.state==='collecting_reviews';stopBtn.style.display=activeCollect?'block':'none';reviewLive.style.display=(lp||activeCollect||d.state==='reviews_incomplete'||d.state==='reviews_stopped')?'block':'none';const ps=d.parameterStatus; + paramLive.style.display=ps?'block':'none'; + if(ps){ + paramState.textContent=ps.stateText||'已采集'; + paramCount.textContent=ps.count??0; + paramTags.innerHTML=(ps.keys||[]).slice(0,10).map(k=>`${String(k).replace(/[<>&]/g,s=>({'<':'<','>':'>','&':'&'}[s]))}`).join('')||'暂无'; + paramMissing.textContent='缺失项:'+((ps.missing||[]).length?(ps.missing.join('、')):'无'); + } + if(lp){liveCount.textContent=lp.count??0;liveNet.textContent=lp.networkReviewObjects??0;liveRound.textContent=lp.round??0;liveStagnant.textContent=lp.stagnant??0;liveMode.textContent=lp.mode||'—';liveElapsed.textContent=Math.round((lp.elapsedMs||0)/1000)+'s';livePreview.innerHTML=(lp.preview||[]).map((x,i)=>`
${i+1}. ${String(x).replace(/[<>&]/g,c=>({'<':'<','>':'>','&':'&'}[c]))}
`).join('')||'等待评论数据…'}}else{status.textContent='已识别当前商品,等待手动开始。';bar.style.width='0%';runBtn.textContent='开始深度采集';stopBtn.style.display='none';reviewLive.style.display='none'} + try{const r=await fetch(`${SERVER}/api/monitor/${encodeURIComponent(id)}`);const j=await r.json();monitorStatus.textContent=j.ok&&j.days?`已导入 ${j.days} 天 · ${j.firstSales||'—'} → ${j.lastSales||'—'}`:'未导入(可选)'}catch(e){monitorStatus.textContent='本地服务启动后可导入'} +} +runBtn.onclick=async()=>{ + if(!backendCompatible){status.textContent='插件与本地后端版本不一致,请先双击本包“start-tmall-ai.command”';return} + const t=await active();status.textContent='开始采集当前商品…';try{const r=await chrome.runtime.sendMessage({type:'RUN_CURRENT',tabId:t.id});if(r?.needsReviewResume){status.textContent=r.message||'评论还没到末页,请继续采集';runBtn.textContent='继续采集评论'}else if(!r?.ok){status.textContent='采集失败:'+(r?.error||'未知错误')}}catch(e){status.textContent='采集失败:'+String(e)}setTimeout(refresh,500)}; +document.getElementById('open').onclick=()=>chrome.tabs.create({url:SERVER+'/'}); +document.getElementById('importMonitor').onclick=()=>document.getElementById('monitorFile').click(); +document.getElementById('monitorFile').onchange=async e=>{ + const f=e.target.files?.[0];if(!f)return; + if(!backendCompatible){monitorStatus.textContent='导入失败:插件与后端版本不一致,请启动本包最新服务';e.target.value='';return} + const t=await active();const itemId=itemIdFromUrl(t?.url||''); + if(!itemId){monitorStatus.textContent='请先打开目标商品页';e.target.value='';return} + monitorStatus.textContent='正在解析并导入…'; + try{ + const buf=await f.arrayBuffer(); + let binary='';const bytes=new Uint8Array(buf); + for(let i=0;i{ + const r=await fetch(SERVER+'/api/monitor-import',{ + method:'POST',headers:{'Content-Type':'application/json'}, + body:JSON.stringify({itemId,filename:f.name,dataBase64:base64,forceBind}) + }); + const j=await r.json().catch(()=>({ok:false,error:'服务返回内容无法解析'})); + return {r,j}; + }; + let {r,j}=await send(false); + if(!j.ok && j.code==='ITEM_ID_MISMATCH' && j.canForce){ + const fileId=(j.fileItemIds||[])[0]||'未知'; + const yes=confirm(`监测文件中的商品ID为 ${fileId},当前页面商品ID为 ${itemId}。 + +如果这份文件确实属于当前商品,点击“确定”后按当前商品绑定;否则点击“取消”。`); + if(yes)({r,j}=await send(true)); + } + if(j.ok){ + monitorStatus.textContent=`已导入 ${j.days} 天 · ${j.firstSales||'—'} → ${j.lastSales||'—'}`; + }else{ + const extra=j.fileItemIds?.length?`(文件商品ID:${j.fileItemIds.slice(0,5).join(', ')})`:''; + monitorStatus.textContent=`导入失败:${j.error||'未知错误'}${extra}`; + } + }catch(err){ + monitorStatus.textContent='导入失败:'+String(err); + } + e.target.value=''; +}; +document.getElementById('clearMonitor').onclick=async()=>{const t=await active();const itemId=itemIdFromUrl(t?.url||'');if(!itemId)return;try{await fetch(SERVER+'/api/monitor-clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({itemId})});monitorStatus.textContent='已清除'}catch(e){monitorStatus.textContent='清除失败'}}; +refresh();setInterval(refresh,1300); + +stopBtn.onclick=async()=>{const t=await active();stopBtn.disabled=true;stopBtn.textContent='正在终止…';try{const r=await chrome.runtime.sendMessage({type:'STOP_COLLECTION',tabId:t?.id});status.textContent=r?.ok?'已发送终止指令,正在保存当前进度…':'终止失败,请重试'}catch(e){status.textContent='终止失败:'+String(e)}setTimeout(()=>{stopBtn.disabled=false;stopBtn.textContent='终止采集并保存进度'},1000)}; + +const analyzeNow=document.getElementById('analyzeNow'); +const modeQuick=document.getElementById('modeQuick'); +const modeFull=document.getElementById('modeFull'); +let analysisMode='quick'; + +function setMode(mode){ + analysisMode=mode; + modeQuick?.classList.toggle('active',mode==='quick'); + modeFull?.classList.toggle('active',mode==='full'); + if(analyzeNow){ + analyzeNow.textContent=mode==='quick'?'基于当前数据开始分析':'完整采集后自动分析'; + analyzeNow.disabled=mode==='full'; + } +} +modeQuick && (modeQuick.onclick=()=>setMode('quick')); +modeFull && (modeFull.onclick=()=>setMode('full')); +setMode('quick'); + +analyzeNow && (analyzeNow.onclick=async()=>{ + if(!backendCompatible){ + status.textContent='插件与本地后端版本不一致,请先启动本包最新服务'; + return; + } + const t=await active(); + if(!t?.id){status.textContent='请先打开目标商品页';return} + analyzeNow.disabled=true; + analyzeNow.textContent='正在提交当前数据…'; + try{ + const r=await chrome.runtime.sendMessage({type:'ANALYZE_CURRENT_NOW',tabId:t.id}); + if(r?.ok){ + status.textContent=`已开始分析当前数据${r.reviewCount!=null?` · 当前评论 ${r.reviewCount} 条`:''}`; + if(r.taskUrl) chrome.tabs.create({url:/^https?:\/\//i.test(r.taskUrl)?r.taskUrl:SERVER+r.taskUrl}); + }else{ + status.textContent='无法开始分析:'+(r?.error||'未知错误'); + } + }catch(e){ + status.textContent='无法开始分析:'+String(e); + }finally{ + analyzeNow.disabled=false; + analyzeNow.textContent='基于当前数据开始分析'; + } +}); diff --git a/field-reference/taobao_detail_fields_v8.3.6.xlsx b/field-reference/taobao_detail_fields_v8.3.6.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a70873bdacfa3f25fba3d7499a0e071cf60584a6 GIT binary patch literal 9165 zcma)i1yoyG*DclpMT&coP@uS5ad#^c+=F{k99jw#hZ3CN?k>e$iWc|cP_#Hj`_g;g z|L%V~zHf||jB`$Qa@O1{bDy==K6@T@C1ex=1Ox;$gdkdgkxkz?!vnn4iim*l9Dek4 zVE1r#vp08kwrBHnbf}-`Q0?Ksd3pk@c4AO1zqie8HqzDgLLO-1(XkM!F&Z31@ZPA)G*~YxmWmOXV|!L3n?8q>yj+ZiNiCgGgKzvAnC^yylwuC zIBB+A$$l$k=y98!)K1(!rNif%m zmvhHuNEk_WY|quYyRYwpXj{9h$5)}i>dy(=Y9^zaFzfF2v~nJ1%`MzfWW_bI?`8h&gH;)U>>}7{E3E=Gg$%( ztV=Rp>@il@cq!j6+rI~WyiLGDgjO(6DFV@3!b&hHcSeQBxv8h_2DoVZg?40yY(mR~ z@JW-$=7sZzamxEPU@??b$HT5Pd?+0i97M7yxcZUs?}G*h0wp*N)>#;jb)#)lVxMg4 z0m6m)lL7&|0x85?EVRf=JZ%z*c?1f*PjYiOtQ=HiLYJ-N>Fzkb;o|{@CHuv(M@Y<; zahTKAaU4-?q3iptuW0t-3+;Trc00T)G{R|5`}nFttU0s*0snz!*8RnB^jMi>&kZ&(;19k0f*P18yrA3Q<7U_; z|GIKtU)HEU@jzABI$|!Y1E<~g>FN3RgA)e-djhJ?O01b;#!c`D%=o70MOm})LINGi zFlK#;)JvVq)Mea}-tcIr zcce;Y#?7@0z5;zAZsoz!-n%9nqEGqi!@&}BdTC))=Qt^YpNRX*Q}Euv2(|pww+|(q zG$CIduMK~?P3%k{>$1LIc~bQnK_Me<6kj2Ur-yaZQiz%gQ~GsWBYKyc<^6`j<42{p z<|;LYX;K@`vtLtCQOnjr=_;HtGn)}^r4z{$#~Oi9Pzx2{N%|8}* z-harc@tMHhon6ePx%w?AnZtkJx<|yS;=?A8)e12>XO7 zW{LLy+-65K*T6Q`u51J=VsmZdbxsXc)ps(<+rE_1H#T|AwXY;s5ij3XKC>|KM##{| zz-sg}%_?`$cNrZOcm9fIa!Z+7pTe^EG1stN4erjQsj>dgdhhlhD*blHnB&J)scYQq z(a<&4QbehENFRC9n_`1*kbp@x(Qd;}G0sUe|+}H$R9o<5L$# zBI`dbQcKJoWehHkf>0Dc$)YmT;j2o4QC9gKj)=o zTvsH*K*V1%?JTNZ&(*$VzT=IJ2D7hd1s^*vy^ku(`*UU2sHt0@6=J#bgYC6_9J3W& zzpwC%OdXNdk)#E5nb+%kMN|aHCgcclB*VC``;W3g6D0KMS#SVUd9((!}7ST6y zL~5he{puY>XT%sEYYC@Dzkh3ZCagfP@>&v^TVJRg(m6BVu5_BI?7&q4Nvh7FC`w9` z7NADEO`b^4T8r)vn-um`I3a&8o`FIlG8TWvk1QbJ_o1%9o+Q$p6Jk3#F(e&dUG8nR zrMf?F*>CeXJJME$d?tMmKQoNh1eFA%3`EU!2`jOLt))@h*oN!sv|FH`DFbY}HFj?$ zvkepMjr}mz@_YBKiQu_q*SqM2Eo-79Q=toI!dTN!mUL|Z!;XPQrwW2=h%x7X*>*?+_Y_l{Sfj z4vCz?>5(41RdO!t3zTW@kUPexL$P5BwPGnKMzK@1vLg3`w#X|l{OqHq0m*mY-BaW@ zN6LTXao%n-Xh@K7LHVfgUm-+_lk+4LO*4>&M<`#qn4&m6!uW^N!$O^BPVll5O^@ky7)vmR

+yVX0Dwc!iT z1-o$HNd9)~_k;#)0|G(7a1Y^!zmh_Ey0hj|KT$|W_`r{dQ>spgqENYO_LTdi0(~3% z>-6XRcPE`z96GyN(;=d#)y}#emx>F- zcpS+w+rHEPV&eOjJ>gDuezMY?fJYP9XCwD;$67_XBb(Ut$@Hu1G+N9kT8 zH*JZX=W#xCZ)ErSww3lH=WWw?Qdm?GBi&SXRLP!C@fNF!8l!B1)KrLHiiVN)22rBo zEmC^=N_#w1-lq>k|JNN@vJH0gonOsLx3x77Nt;j;Y>5sd>*|M7)8#2vzi{q1C?DTX7H?h3gBamks3 z`kcIq?9{{fczGYOC{c&9df10e8#d+Deu@{5j(k-RXrVz;d%Y^Q6oQ~$R3^yRvYh-A z-_{j@eqoD~xuX=gLsn?9Y*rYmc4?`cQCe9+75bP~tJk z#Ee1Kiqiu2;%jo`qi)@Yi|VGj{+)*T;~v=DyHD_HOxqAb4TLWgfbhq8zEq}&@2QFl!rq}_xuA9taA*Dtt??}e z1T)UpE8Mx&684%BrZy{*5`%Fm@j@7AGWDZR6N;Nm{*m~w;7wvX>#fj(CfvdMhWg223z#pLz)I z!*z5rG@4-}Jlo22LO$#ACps+Mvhwj{VUslRMf{=t=pwn!iY>*98j zp3;`iEa4<_JCll1G$k#GnnO)-;g1hbku`>m9Q8j6!aBv8UUjEOM3;U{Q0qyJ4H~*t zpD(?uvdSyWU+~^mYX@$`sP$p6075{v%h&s&%#_p2Gqb4H!1cr;yYbhYyKqltTR^jF zLva2|k2^P73=RkDFUqA2RR-3I2|qNd$FzS^2kmJLUE3&X26C>176r>abnhhHi1PT>ykH>dLXPvR#V>pAyDD)J>k ztD#yb>xYSH#pe!}XEF_?%|?ADIpKLQlJ|m_kQMnVo<{A;7hO3$coV4^B&vA{dA&T7 zrjN0DBIR?@zV1_8tAhq2#@m}q4rlc@WbGd#U4lpxOR1H$VwLf8ISMnJnt^<~g0~@E z2mCo`)^}qz3R~(?byk;Z^u#+Or+zvsW_@>2%K|LKH(E?jL>mXNqZe-ZRvlasnbw$4 z@U`=1sCscmiW%-?1lnu6o+70n(0uz{y7ZJu~&{RtZDS?uEGTi8<9eMll=j zOHtr`v*6=%6jEjwZk)am>(@gmA^Awq^A%r8Pipl3%wA4kp-af2OvZj=`PI=)Y6K_| zAJulAFOt8&xGF{Du6IJPt=?+73j9*DlwjUFA>o(hcB57OvodH4)9T7C+ddNe{L zL~ON;JKdyeEb8kyd6l$vSR0y=UbqmZhA~0)%yo9A@keuT+J%L}#T(|#jE1}C=x|4l z)AeAT>|W0YC(3t6wOt-=sJnSH9e4_UomTghG>Pn3z@F=!4i1FBs}?hc-A%DtwhRqP z6Dsg=0>JxWH5&46gk%8@3qNg1@%r@A?RN@b`5sj^mQe;ysbaok=jY}MXzgPN*(>Zk zRmYUJtVtx%xCgYVA{uPZsxPm1E(ll}? zIME!9ZVAC~_l?Yn;O-xFt8?=kRUX2)oxPkei6|1$#)_`&eC9conY<-W`=E+XTXO{8 zUrvR)p7>(}C`u!1d-`Szrz&#THDvIVBHPovb97&7ERu2)Ut5~{(3x+BMRLXW_Fgs6 z5$Tob7@6LcZhWg==5fpS#*tcS!fFn0lP27wCpACZo}OCX_+$wU5+4igvz9~K-QP(b zyRwq=1Tvlj4hjZlKZJ!5SBq{;o7`aTWbzV&?@vEBi6RRny4j47$b8l?QnEGAZ%xK9 zxj7s&x&xVcon}=F(JKhxF1AaqZ<0UZ(>zOYb|)`)J3X;0KS3OlNL!9%PUdQ(?Tzb8(*6#7m{H2*9B3sJ*_!1gx3CRZo7D+X+<5Q zg_G5>$kwvsg`FH{4bGuw!f4uUD)aVEoHo^P1Po?2ldYCUfNIod08bRjB+i1D1bt|( z-jf2!rqAj1NJpr1&^)qrNndDBorWqyfI>O3Rd##v&JHtbM5*j+LKL`T53K^|nYGC6nVl$DdbXhjL5y!sZ zI+lXb;~^`JJMDE+?1`{p&;H^5F=Vh`%68`2 zw%`2c3qhmVCE|#@)yAsTZKf?e?Y!MAJSn!q34a1Hj#pH!04orj?2Fu44oI#t5soE)D2uevV%j z8kIw5jK!F4T>$B)FJ}y7GaGAj2M#8V%DZkCzRH$oHn(QbjB?aXFF4bd-~r#B8hkxy z%D3&mVZWNwPkU!Qob(I%-*@c0x*q=RC5h}P1#Vr&}Nv(}(y2JyK2y7IVoT&3n#C}w68 z=DSO@{JE#vtkYJOWZ{@XQ0!&4N&)JUaJ|NR4-I$bCn&^y7-?5__8KBbgLJqtRrxXJ zYo{Zf$g+1P$x~zl)&lO_0`4N8k6cfdn3kJ@H>x2?G?G{khcWkC_gE~wr|iUW&AiDG zU6vHuH8zdUek}(-APpVG6lQQqiO3YlqE_Y`E2+|H+#tt#hbxO%SL>H;Ru_z#s*szu zDRoLy%s!9xtm+IRM7_ewbhV^;03Lb&0r8Ua5Y`Mmcp~3>yUYN9fcKM6A4H z)M6t_wS5k$ed@TOch_6cqV_M{mJ#ysrs7fLF=UlJ8}cli@NyKJiAO^=s0H|nOq0wt z*;kCxcNX&}3#p-?iqerqE6y1f0N$bqXKh+~!Xv&NsqctHRvn zPK`D%m9}bSe3z;4Z5ru?U?6Kb&nHcEz-^kw`h}@2f=w0=+eGP$wzk%~9R562{C8RU z1mibLO#4;?wM%6MTV09;CdT?S5rH#V?y}p|*9|}JY>U$a^4SPtG9T{EkqQI}Ye|*Q zx~Pi8zsbq6S-#*dLE%&isaNKl|0(`IZCb zv|3ZeroJj@T1rDi2)cMzrR7DjWv{Fm9$G#aM=3?BQBk<%6oqM@_3^fy_3d?*Zr4;v zI-BVk65)a8RbK!gquUsWD|2MArjh3^YwMTxDT)!jPp8*NLu_R^J=-QSEh->Xk~8Yr z-2O!Lj&uc z*!Nh769hl5@{0Z|?2V>_Oj)`FHa#X$SuE5J^^IUeJt|N|EL^Q1m&@zcu(&Tl{~4js zcOF5+m3Z&PkLCiG$rgpr`=Slmhk}^|1F|v0(cec4Goop6DilC7xo zEgy_v@HZYmKc((0LuVVZOUXwl%(Zt6Sox~YO`r>U5`Y!$E!uws5?F@ibdn7}d;a1< z@xtZ6@c#M##8Dvw-75<0f3mO@EeeW83cX?*v8fcObs`20*e4_AP|?=?Cf9* zxCmkfW^;A{Il+Iia&~hxgTR|^*6c237WQV=Aa*Ve4nB5xkN;o*{*6hk<>dnU|3P%N zva+=RNjqD-bp$y<{sS=kzs~?rYni!OgCHUR_CNGy0JVap2;dDLFG$depWDjP%#4$V zgNq+P%`Pg;{;&Tl`agvJtz$pIe`^GQlNk$_g2?_}_WhgF|E*^K#qa2p2wc*pcydyz z`z_39a~|DpF*vyX0;%C{JY+v+(49tv99eGTS?CA#4L{g(e<_yG_WXmav~52MP+F+$5Pp&lV)RZ7R)Co4PD( zO!PPfBPM#XyIORR3Uew1XFdQ^Eu<@+4v7fpdb3UJHl-HZHRR+6g=^9X;$NheOqZgC z3{ar1J{a2j^HO1TYNcxCJJZc+`>A{bhYn05Z_ynIJ$fRDXH+t1jYgH|ei?!ezj;Ka zgqEYG45XZ$An;)}`F(YOjeb|= z`Z$T-PYK9W;(0}wHyTj~LERA@5YgF81><^4L>h$Spczq5;G7n*l_1c_XfH)sbNm1J z)#uy(POEgH#T+@zp;LckTbbZJJr7ZfKIU>Fzj=L`aU*UKNt;xO%s7Q}0$?g|?xhq8 zMVr0US*pUv>EfbMeUB`n+J$;9MAA=6-#B|~nnQG+M@NLjB3>^}2x%-IUAvd)e(wt5CzAes6v69soRCPO-(mCNe`9p^*U|48+NglOt z>gk&rEQ0aZr@2?^7`(4#THlJFjbL#@6h}oo=R3~hkS#|n7mOKfj0N-#$G+V#_%IC` z$RFItYEc$m|9Y|0DaJRru_2+=KC?}%u7rq0fc)Q|w}2D+*DEjJ_u-Fd_|y2$M-cut zML@_4V1f_OZvp22J&^FH>7O#`Kc-#qsTbVzPto*GfIsD)e*j*i{b%v#PlP{}nST%_ z{zmwZI?bObf6f;FK?#J*J^z=$|E>1?HFx|685tJ_#NERzN~PaH9F$`ac?3&G7&L literal 0 HcmV?d00001 diff --git a/monitor-template.csv b/monitor-template.csv new file mode 100644 index 0000000..70ab802 --- /dev/null +++ b/monitor-template.csv @@ -0,0 +1,8 @@ +日期,itemId,商品标题,销量展示值,榜单排名,价格,活动 +2026-08-21,,当前商品,5000+,22,899, +2026-08-22,,当前商品,5000+,20,899, +2026-08-23,,当前商品,1万+,17,899,直降 +2026-08-24,,当前商品,1万+,15,899,直降 +2026-08-25,,当前商品,2万+,12,891,满减+直降 +2026-08-26,,当前商品,2万+,8,891,满减+直降 +2026-08-27,,当前商品,2万+,6,891,满减+直降 diff --git a/server/ai_client.py b/server/ai_client.py new file mode 100644 index 0000000..98d4d3e --- /dev/null +++ b/server/ai_client.py @@ -0,0 +1,148 @@ +import json, os, re, ssl, time, urllib.request, urllib.error +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +SERVER_VERSION = '9.2.5' + +def load_config(): + cfg = {} + p = Path(os.getenv('AI_CONFIG_PATH') or ROOT/'config.json').expanduser() + cfg['_config_path'] = str(p) + cfg['_config_error'] = '' + if p.exists(): + try: + raw = p.read_text('utf-8').strip() + data = json.loads(raw) + if not isinstance(data, dict): + raise ValueError('config.json 顶层必须是 JSON 对象') + cfg.update(data) + except json.JSONDecodeError as e: + cfg['_config_error'] = f'config.json JSON 格式错误:第 {e.lineno} 行第 {e.colno} 列:{e.msg}' + except Exception as e: + cfg['_config_error'] = f'{type(e).__name__}: {e}' + else: + cfg['_config_error'] = 'config.json 不存在' + cfg['api_base'] = os.getenv('AI_API_BASE', cfg.get('api_base','https://api.bananarouter.com/v1')).rstrip('/') + cfg['api_key'] = os.getenv('AI_API_KEY', cfg.get('api_key','')) + cfg['model'] = os.getenv('AI_MODEL', cfg.get('model','')) + cfg['chat_path'] = str(cfg.get('chat_path','/chat/completions')) + cfg['models_path'] = str(cfg.get('models_path','/models')) + try: cfg['temperature'] = float(os.getenv('AI_TEMPERATURE', cfg.get('temperature',0.2))) + except Exception: cfg['temperature'] = 0.2 + try: cfg['timeout'] = int(os.getenv('AI_TIMEOUT', cfg.get('timeout',120))) + except Exception: cfg['timeout'] = 120 + try: cfg['retries'] = max(0, int(cfg.get('retries',2))) + except Exception: cfg['retries'] = 2 + cfg['extra_headers'] = cfg.get('extra_headers',{}) or {} + cfg['ca_bundle'] = str(cfg.get('ca_bundle','') or '') + cfg['ssl_verify'] = bool(cfg.get('ssl_verify', True)) + return cfg + +def configured(): + c=load_config() + key=str(c.get('api_key') or '').strip(); model=str(c.get('model') or '').strip() + return bool(not c.get('_config_error') and key and model and not key.startswith('YOUR_') and not model.startswith('YOUR_')) + +def _ssl_context(c): + if not c.get('ssl_verify', True): + raise RuntimeError('ssl_verify=false 被禁止。V6.2 不允许关闭 HTTPS 证书校验。') + cafile = c.get('ca_bundle') or '' + bundled = ROOT/'cacert.pem' + if not cafile and bundled.exists(): cafile=str(bundled) + if cafile: + if not Path(cafile).exists(): + raise RuntimeError(f'ca_bundle 文件不存在:{cafile}') + ctx=ssl.create_default_context(cafile=cafile) + else: + try: + import certifi + ctx=ssl.create_default_context(cafile=certifi.where()) + except Exception: + ctx=ssl.create_default_context() + try: ctx.minimum_version=ssl.TLSVersion.TLSv1_2 + except Exception: pass + return ctx + +def _headers(c): + h={'Content-Type':'application/json','Accept':'application/json','Authorization':'Bearer '+c['api_key'],'User-Agent':'TmallAIProductAnalysis/9.2.5'} + h.update(c.get('extra_headers',{})) + return h + +def _join(base,path): + path='/' + str(path or '').lstrip('/') + return base.rstrip('/') + path + +def probe_model_api(timeout=15): + c=load_config() + if c.get('_config_error'): + return {'ok':False,'stage':'config','error':c['_config_error']} + if not c.get('api_key') or not c.get('model'): + return {'ok':False,'stage':'config','error':'api_key 或 model 为空'} + try: + req=urllib.request.Request(_join(c['api_base'],c['models_path']),headers=_headers(c),method='GET') + with urllib.request.urlopen(req,timeout=min(timeout,c['timeout']),context=_ssl_context(c)) as r: + obj=json.loads(r.read().decode('utf-8','ignore') or '{}') + ids=[x.get('id') for x in (obj.get('data') or []) if isinstance(x,dict)] + return {'ok':True,'stage':'models','http':200,'modelFound':(c['model'] in ids) if ids else None,'model':c['model'],'modelsCount':len(ids)} + except urllib.error.HTTPError as e: + body=e.read().decode('utf-8','ignore') + return {'ok':False,'stage':'models','http':e.code,'error':body[:500] or str(e)} + except ssl.SSLCertVerificationError as e: + return {'ok':False,'stage':'ssl','error':f'SSL证书验证失败:{e}. V6.2 已优先使用 certifi;如仍失败,请确认 Python/代理没有替换证书链。'} + except Exception as e: + return {'ok':False,'stage':'network','error':str(e)} + +def _parse_json(s): + if isinstance(s, dict): return s + s = (s or '').strip() + s = re.sub(r'^```(?:json)?\s*|\s*```$', '', s, flags=re.I|re.S) + try: return json.loads(s) + except Exception: + a=s.find('{'); b=s.rfind('}') + if a>=0 and b>a: return json.loads(s[a:b+1]) + raise + +def _do_post(c,payload): + req=urllib.request.Request(_join(c['api_base'],c['chat_path']),data=json.dumps(payload,ensure_ascii=False).encode('utf-8'),headers=_headers(c),method='POST') + with urllib.request.urlopen(req,timeout=c['timeout'],context=_ssl_context(c)) as r: + return json.loads(r.read().decode('utf-8','ignore')) + +def chat_json(system, user, images=None, max_tokens=5000): + c=load_config() + if c.get('_config_error'): + raise RuntimeError(c['_config_error']) + if not c.get('api_key') or not c.get('model'): + raise RuntimeError('未配置模型。请复制 config.example.json 为 config.json,并填写 api_key 与 model。') + content=[{'type':'text','text':user}] + for url in (images or [])[:12]: + if isinstance(url,str) and url.startswith(('http://','https://','data:image/')): + content.append({'type':'image_url','image_url':{'url':url}}) + base_payload={ + 'model':c['model'], + 'messages':[{'role':'system','content':system},{'role':'user','content':content if len(content)>1 else user}], + 'temperature':c['temperature'], + 'response_format':{'type':'json_object'}, + 'max_tokens':max_tokens + } + variants=[base_payload] + # Some OpenAI-compatible gateways/models reject response_format, temperature, or max_tokens. + v2=dict(base_payload); v2.pop('response_format',None); variants.append(v2) + v3=dict(v2); v3.pop('temperature',None); v3['max_completion_tokens']=v3.pop('max_tokens',max_tokens); variants.append(v3) + last=None + for attempt in range(c['retries']+1): + for payload in variants: + try: + obj=_do_post(c,payload) + try: return _parse_json(obj['choices'][0]['message']['content']) + except Exception as e: raise RuntimeError(f'模型返回无法解析为 JSON: {e}') + except urllib.error.HTTPError as e: + body=e.read().decode('utf-8','ignore') + last=RuntimeError(f'模型接口 HTTP {e.code}: {body[:900]}') + if e.code not in (400,408,409,429,500,502,503,504): raise last + except ssl.SSLCertVerificationError as e: + raise RuntimeError(f'SSL证书验证失败:{e}。V6.2 已使用 certifi CA;请检查本机 Python/代理证书链。') + except Exception as e: + last=RuntimeError(f'模型接口调用失败: {e}') + if attempt=1;第三类创新若不是直接证据结论必须标 sourceType=探索性方向。 +- productExperience.parameterFacts>=2、productExperience.gates>=2、validationLoop.rows>=3,继续用于工程证据校验,但默认报告不展示。 +- 禁止用户可见字段出现[数据事实][分析判断][设计建议][证据不足]。 +''' + +FINAL_REPORT_REPAIR_PROMPT='''任务G-R|修复 V9.2.5 最终报告 JSON。只修复结构缺失和证据引用,不扩写正文。 +必须保留短文本与图片信息优先规则。 +强制最小结构:reportSummary.title/verdict非空;ownerOverview.cards>=3;productExperience.parameterFacts>=2;productExperience.gates>=2;newProductPlans.plans>=1;validationLoop.rows>=3。 +如果 customerExperience 有评论证据,优先保留 experienceSignals;无评论时允许为空。 +所有 evidenceIds 只能来自 ACTUAL_EVIDENCE。不要输出 Markdown、解释或包装层。 +''' + +TOPICS={ + '颜值/配色':['好看','漂亮','颜色','色彩','高级','颜值','款式','花色','图案','搭配'], + '面料/触感':['柔软','舒服','舒适','亲肤','手感','面料','纯棉','全棉','透气','粗糙','刮'], + '厚薄/季节':['薄','厚','厚实','轻薄','冬天','夏天','季节'], + '色差':['色差','偏黄','偏白','偏暗','颜色不一样','实物和图片'], + '做工/破损':['做工','线头','走线','缝','破','开线','油污','质量'], + '尺寸/适配':['尺寸','大小','合适','床笠','床单','被套','罩不住','紧张'], + '起球/掉毛':['起球','掉毛','粘毛','浮毛'], + '缩水/掉色':['缩水','掉色','褪色','变形'], + '气味':['味道','气味','异味'], + '物流/服务':['物流','发货','客服','包装','快递','售后'], + '价格/价值感':['划算','性价比','价格','便宜','值','贵'] + ,'尺码/上身':['尺码','码数','偏大','偏小','合身','显瘦','上身','版型'] + ,'口味/复购':['口味','味道','好吃','难吃','甜','咸','回购','复购'] + ,'成分/肤感':['成分','肤感','吸收','刺激','过敏','敏感','闷痘'] + ,'配置/性能':['配置','兼容','性能','速度','续航','发热','卡顿','连接'] +} + +def _txt(v): return '' if v is None else str(v) +def _arr(v): return v if isinstance(v,list) else [] +def _uniq(xs): + out=[]; seen=set() + for x in xs or []: + k=_txt(x).strip() + if k and k not in seen: seen.add(k); out.append(x) + return out + +def _question_like(c): + c=_txt(c).strip() + return bool(re.search(r'问大家|更多回答|查看全部问答|大家问|^问[::\s]|请问|会不会|有没有|多少价|(?:起球|缩水|掉色|尺寸).*[??]',c)) + +BAD_TITLES = ('用户评价', '累计评价', '商品评价', '问大家', '参数信息', '规格参数', '图文详情', '商品详情') +BAD_PARAM_CODE = re.compile(r'(?:^\d+$|window\.|console\.|function\b|frontend_data|performance\.|[{}\[\]"\'=;<>])', re.I) +NEGATIVE_REVIEW_KEYS = ('不够', '一般', '不会复购', '偏小', '偏大', '缩水', '掉色', '起球', '掉毛', '异味', '破损', '开线', '色差', '不合身', '难吃', '变质', '漏液', '过敏', '刺激', '不兼容', '卡顿', '发热', '续航差') +IMAGE_GROUPS = ('main', 'detail', 'sku', 'buyerShow') +BAD_IMAGE_URL = re.compile(r'(?:avatar|sns_logo|credit|logo|icon|sprite|shopmanager|/tps/|/tfs/TB1[^/]+-\d{1,4}-\d{1,4}\.png|tps-\d{1,3}-\d{1,3}|defaultRate)', re.I) +BAD_PAGE_IMAGE_URL = re.compile(r'(?:[-_]rate(?:\.|_|-)|rate_livephoto|tbbala|/tps/|/tfs/TB1[^/]+-\d{1,4}-\d{1,4}\.png)', re.I) + +def _clean_product_title(v): + value=re.sub(r'\s+',' ',_txt(v)).strip() + suffix=r'(?:[-_||·\s]*)?(?:(?:www\.)?(?:tmall|taobao)\.com)?(?:天猫(?:网)?|淘宝网?)' + previous=None + while value and value!=previous: + previous=value + value=re.sub(suffix+r'\s*$','',value,flags=re.I).rstrip('-_||· ') + return value + +def _valid_title(v): + value=_clean_product_title(v) + return value if 4<=len(value)<=260 and not any(x in value for x in BAD_TITLES) and not re.search(r'登录|验证码|访问受限',value) else '' + +def _valid_param(name,value): + name=re.sub(r'\s+',' ',_txt(name)).strip(); value=re.sub(r'\s+',' ',_txt(value)).strip() + if not name or not value or len(name)>60 or len(value)>220:return False + if BAD_PARAM_CODE.search(name) or re.search(r'(?:console\.log|window\.|performance\.now|frontend_data)',value,re.I):return False + return bool(re.search(r'[\u4e00-\u9fffA-Za-z]',name)) + +def _image_url(value): + if isinstance(value,dict): + value=next((_txt(value.get(k)) for k in ('url','picUrl','imageUrl','src','bigUrl','thumbnail') if value.get(k)), '') + value=_txt(value).strip().replace('&','&') + if value.startswith('//'):value='https:'+value + return value if value.startswith(('http://','https://','data:image/')) and not BAD_IMAGE_URL.search(value) else '' + +def _clean_image_urls(values): + return _uniq([url for url in (_image_url(x) for x in values or []) if url]) + +def _normalize_image_groups(raw, reviews): + source=raw.get('images',{}) if isinstance(raw.get('images'),dict) else {} + meta=raw.get('meta',{}) if isinstance(raw.get('meta'),dict) else {} + strict=(meta.get('imageClassificationVersion')=='strict-v1' and isinstance(source.get('provenance'),list)) + review_links={};legacy_review_links={} + for review_index,review in enumerate(reviews,1): + cleaned=_clean_image_urls(review.get('images',[]));review['images']=cleaned + for url in cleaned: + review_links.setdefault(url,[]).append(review_index) + if review.get('source')=='dom_visible':legacy_review_links.setdefault(url,[]).append(review_index) + incoming=[] + for record in source.get('provenance',[]) or []: + if not isinstance(record,dict):continue + url=_image_url(record.get('url'));group=record.get('group');origin=_txt(record.get('source')).strip() + if url and group in IMAGE_GROUPS and origin:incoming.append({'url':url,'group':group,'source':origin}) + groups={key:[] for key in IMAGE_GROUPS};provenance=[];assigned=set() + buyer_candidates=[] + if strict: + buyer_candidates=[x['url'] for x in incoming if x['group']=='buyerShow' and x['source']=='review_record' and x['url'] in review_links] + else: + buyer_candidates=list(legacy_review_links) + for url in _uniq(buyer_candidates): + if url in assigned:continue + assigned.add(url);groups['buyerShow'].append(url) + link_index=review_links if strict else legacy_review_links + for review_index in link_index.get(url,[]):provenance.append({'url':url,'group':'buyerShow','source':'review_record','reviewIndex':review_index,'reviewEvidenceId':f'REV_{review_index:06d}'}) + if strict: + allowed_sources={'sku':{'dom_sku_selector','structured_sku_selector'},'main':{'meta_og_image','dom_main_gallery','structured_main_gallery'},'detail':{'dom_product_description','structured_product_description'}} + for group in ('sku','main','detail'): + for record in incoming: + if record['group']!=group or record['source'] not in allowed_sources[group] or record['url'] in assigned:continue + if group in ('main','detail','sku') and BAD_PAGE_IMAGE_URL.search(record['url']):continue + assigned.add(record['url']);groups[group].append(record['url']);provenance.append(record) + else: + # V9.2.3 legacy/standalone compatibility: + # Older and standalone collectors already separate main/detail/sku arrays but do not emit strict-v1 provenance. + # Do NOT discard these verified-by-collector groups merely because provenance metadata is absent. + legacy_sources={'main':'legacy_group_main','detail':'legacy_group_detail','sku':'legacy_group_sku'} + for group in ('main','detail','sku'): + for raw_url in source.get(group,[]) or []: + url=_image_url(raw_url) + if not url or url in assigned or BAD_PAGE_IMAGE_URL.search(url):continue + assigned.add(url);groups[group].append(url) + provenance.append({'url':url,'group':group,'source':legacy_sources[group]}) + source_values=[] + for key in IMAGE_GROUPS: + if isinstance(source.get(key),list):source_values.extend(source.get(key)) + original_count=len(set(_clean_image_urls(source_values))) + all_images=_uniq(sum((groups[k] for k in IMAGE_GROUPS),[])) + policy='strict-v1' if strict else 'legacy-review-only' + previous_excluded=(source.get('classification',{}) or {}).get('unverifiedExcludedCount',0) + try:previous_excluded=max(0,int(previous_excluded)) + except:previous_excluded=0 + return {**groups,'all':all_images,'provenance':provenance,'classification':{'version':policy,'classifiedCount':len(all_images),'unverifiedExcludedCount':max(previous_excluded,original_count-len(all_images),0),'groupsAreDisjoint':True}} + +def _normalize_questions(values): + out=[];seen=set() + def add(q,a='',source='unknown'): + q=re.sub(r'^(?:问大家|大家问)\s*[·\d]*\s*','',_txt(q)).strip(' ,,。') + q=re.sub(r'\s*(?:更多回答|查看全部问答)\s*$','',q).strip(' ,,。') + if len(q)<3:return + key=re.sub(r'[\s,,。!??]','',q) + if key in seen:return + seen.add(key);out.append({'question':q[:1800],'answer':_txt(a).strip()[:1800],'source':source}) + for x in values or []: + if isinstance(x,str):x={'question':x} + if not isinstance(x,dict):continue + q=_txt(x.get('question') or x.get('title') or x.get('ask') or x.get('content')).strip() + a=_txt(x.get('answer') or x.get('reply')).strip(); source=x.get('source') or 'questions_input' + chunks=re.findall(r'(?:^|\s)问[::\s]+(.+?)(?=\s*更多回答|\s*查看全部问答|\s+问[::\s]|$)',q) + if chunks: + for chunk in chunks:add(chunk,'',source) + else:add(q,a,source) + return out + +def _normalize_question(x, source='unknown'): + if isinstance(x,str): return {'question':x.strip(),'answer':'','source':source} + if not isinstance(x,dict): return None + q=_txt(x.get('question') or x.get('title') or x.get('ask') or x.get('content')).strip() + a=_txt(x.get('answer') or x.get('reply')).strip() + if len(q)<3:return None + return {'question':q[:1800],'answer':a[:1800],'source':x.get('source') or source} + +def normalize(raw): + r=dict(raw or {}) + reviews=[]; questions=_normalize_questions(r.get('questions',[]) or []); seen=set(); qseen={re.sub(r'\s+','',x['question'])[:260] for x in questions} + for x in r.get('reviews',[]) or []: + if isinstance(x,str): x={'content':x} + c=_txt(x.get('content') or x.get('text')).strip(); append=_txt(x.get('appendContent') or x.get('append')).strip() + if re.fullmatch(r'\d{4}年\d{1,2}月\d{1,2}日已购[::].+',c):continue + if re.search(r'该用户(?:未填写评价内容|没有填写评价|觉得商品非常好)',c): + if append:c=append;append='' + else:continue + if len(c)<3 or c in ('此用户没有填写评价','系统默认好评'): continue + if _question_like(c): + qq=_normalize_question({'question':c,'source':x.get('source') or 'misclassified_review'},'misclassified_review') + if qq: + k=re.sub(r'\s+','',qq['question'])[:260] + if k not in qseen:qseen.add(k);questions.append(qq) + continue + key=re.sub(r'\s+','',c)[:260] + if key in seen: continue + seen.add(key) + reviews.append({'content':c[:2200],'appendContent':append[:1200], + 'rating':x.get('rating') or x.get('score') or x.get('star'),'sku':_txt(x.get('sku') or x.get('skuInfo')).strip()[:260], + 'date':_txt(x.get('date') or x.get('time')).strip()[:100],'images':_clean_image_urls(x.get('images',[]) or [])[:24],'likes':x.get('likes'),'source':x.get('source') or ''}) + r['reviews']=reviews; r['questions']=questions[:5000] + attrs=[];attr_seen=set() + for x in r.get('attributes',[]) or []: + if not isinstance(x,dict):continue + name=x.get('name');value=x.get('value') + if not _valid_param(name,value):continue + key=(re.sub(r'\s+','',_txt(name)),re.sub(r'\s+','',_txt(value))) + if key in attr_seen:continue + attr_seen.add(key);attrs.append({'name':_txt(name).strip(),'value':_txt(value).strip()}) + r['attributes']=attrs[:500] + product=dict(r.get('product') or {}) + product['title']=_valid_title(product.get('title')) + r['product']=product + r['images']=_normalize_image_groups(r,reviews) + return r + +def _attribute_rows(raw): + return [x for x in raw.get('attributes',[]) if isinstance(x,dict) and _valid_param(x.get('name'),x.get('value'))] + +def _find_attr(attrs, words): + for i,x in enumerate(attrs,1): + name=_txt(x.get('name')) + if any(word in name for word in words):return x,f'ATTR_{i:04d}' + return None,'' + +CATEGORY_TEMPLATES={ + 'apparel':{ + 'label':'服饰','keywords':('服饰','女装','男装','童装','连衣裙','衬衫','裤子','外套','内衣','鞋','尺码','版型'), + 'dimensions':( + ('尺码与适配',('尺码','尺寸','适用身高','适用体重','码数')), + ('版型与轮廓',('版型','衣长','裤型','领型','袖长','腰型')), + ('面料与成分',('面料','材质','成分','里料','含量')), + ('做工与细节',('工艺','做工','缝制','拉链','纽扣')), + ('颜色与款式',('颜色','色号','款式','图案','风格')), + ('洗护与标准',('洗护','洗涤','执行标准','安全类别','等级')), + )}, + 'food':{ + 'label':'食品','keywords':('食品','零食','饮料','咖啡','茶','牛奶','饼干','坚果','冲饮','口味','配料','保质期','净含量'), + 'dimensions':( + ('规格与净含量',('规格','净含量','重量','数量','包装规格')), + ('口味与食用体验',('口味','风味','甜度','食用方法')), + ('配料与营养',('配料','成分','营养','能量','蛋白质')), + ('保质期与储存',('保质期','生产日期','储存','保存')), + ('产地与合规',('产地','生产许可证','执行标准','厂名','厂址')), + ('包装与运输',('包装','包装方式','运输','破损')), + )}, + 'beauty':{ + 'label':'美妆','keywords':('美妆','护肤','彩妆','面霜','精华','乳液','面膜','口红','粉底','防晒','肤质','功效'), + 'dimensions':( + ('功效宣称',('功效','功能','效果','宣称')), + ('适用肤质与人群',('肤质','适用人群','适用部位','肌肤')), + ('成分与配方',('成分','配方','主要成分','净含量')), + ('使用方式',('使用方法','使用步骤','用法','频次')), + ('敏感与风险',('敏感','注意事项','禁忌','过敏')), + ('备案与保质期',('备案','批准文号','执行标准','保质期','生产日期')), + )}, + 'digital':{ + 'label':'数码','keywords':('数码','手机','电脑','平板','耳机','相机','键盘','鼠标','充电器','处理器','内存','存储','兼容'), + 'dimensions':( + ('核心配置',('型号','处理器','内存','存储','容量','功率','分辨率')), + ('兼容性',('兼容','适用型号','系统','接口','协议')), + ('性能与续航',('性能','续航','电池','速度','刷新率','传输')), + ('版本与规格',('版本','配置','颜色','尺寸','重量')), + ('使用问题',('使用说明','连接','安装','故障')), + ('质保与售后',('质保','保修','售后','认证','执行标准')), + )}, + 'home_textile':{ + 'label':'家纺','keywords':('家纺','床品','四件套','三件套','被套','床单','床笠','枕套','被芯','枕芯','毛毯'), + 'dimensions':( + ('结构与规格',('件数','套件组成','尺寸','规格','床型')), + ('材质与触感',('面料','材质','成分','支数','密度','克重')), + ('颜色与花型',('颜色','花型','图案','风格')), + ('工艺与做工',('工艺','织造','印染','缝制')), + ('洗后表现',('洗护','洗涤','缩水','掉色','起球')), + ('场景适配',('适用场景','适用人群','季节')), + )}, +} + +def detect_category(raw): + product=raw.get('product') or {}; attrs=_attribute_rows(raw) + corpus=' '.join([_txt(product.get('category')),_txt(product.get('title'))]+[f"{x.get('name')} {x.get('value')}" for x in attrs]+[_txt(x.get('group'))+' '+_txt(x.get('name')) for x in raw.get('sku',[]) if isinstance(x,dict)]) + scores={key:sum(1 for word in spec['keywords'] if word and word in corpus) for key,spec in CATEGORY_TEMPLATES.items()} + key=max(scores,key=scores.get) if scores and max(scores.values()) else 'generic' + if key=='generic': + return {'key':'generic','label':_txt(product.get('category')).strip() or '通用商品','confidence':0.35,'dimensions':()} + total=max(1,sum(scores.values())) + spec=CATEGORY_TEMPLATES[key] + return {'key':key,'label':spec['label'],'confidence':round(min(0.98,0.55+scores[key]/total*0.4),2),'dimensions':spec['dimensions']} + +def _category_parameter_facts(raw, limit=12): + attrs=_attribute_rows(raw); route=detect_category(raw); used=set(); facts=[] + dimensions=route.get('dimensions') or () + if not dimensions: + dimensions=tuple((_txt(x.get('name')).strip(),(_txt(x.get('name')).strip(),)) for x in attrs[:8]) + for label,aliases in dimensions: + matches=[];refs=[] + for i,item in enumerate(attrs,1): + if i in used:continue + name=_txt(item.get('name')).strip() + if any(alias and alias in name for alias in aliases): + used.add(i);matches.append(item);refs.append(f'ATTR_{i:04d}') + if not matches:continue + value=';'.join(f"{x.get('name')}:{x.get('value')}" for x in matches) + facts.append({'label':label,'value':concise_fact(value,240),'productMeaning':'当前类目需要核对的页面产品定义','evidenceIds':refs}) + if len(facts)>=limit:return facts + for i,item in enumerate(attrs,1): + if i in used:continue + facts.append({'label':_txt(item.get('name')),'value':concise_fact(item.get('value'),180),'productMeaning':'页面采集参数,开发新品前与实物或资料核对','evidenceIds':[f'ATTR_{i:04d}']}) + if len(facts)>=limit:break + return facts + +def _category_gates(raw, issue=None): + issue=issue or _review_issue(raw); route=detect_category(raw); attrs=_attribute_rows(raw); gates=[] + if issue: + gates.append({'priority':'P0','label':f"评论问题:{issue['label']}",'action':issue['action'],'businessReason':concise_fact(issue['text'],72),'evidenceIds':[issue['evidenceId']]}) + for fact in _category_parameter_facts(raw,6): + gates.append({'priority':'P0' if len(gates)<3 else 'P1','label':f"核对{fact['label']}",'action':f"核对样品、SKU、参数和详情页中的{fact['label']}是否一致",'businessReason':f"{route['label']}商品的关键选择信息必须可追溯",'evidenceIds':fact['evidenceIds']}) + return gates[:7] + +def _category_validation_rows(raw): + route=detect_category(raw); facts=_category_parameter_facts(raw,8); refs=_uniq(sum((_arr(x.get('evidenceIds')) for x in facts),[])); issue=_review_issue(raw) + issue_refs=[issue['evidenceId']] if issue else [] + return [ + {'stage':'商品定义','owner':'商品企划 / 商品运营','hypothesis':'','metrics':f"锁定{route['label']}商品的关键参数、SKU和目标场景",'successMeaning':'商品定义与已采参数逐项对应','nextAction':'通过后发起打样;不一致则退回定义','evidenceIds':refs}, + {'stage':'样品与资料','owner':'供应链 / 品控','hypothesis':'','metrics':'核对实物、标签、包装、合规资料和页面宣称','successMeaning':'页面信息与真实商品一致','nextAction':'通过后锁版;不一致则暂停对应宣称','evidenceIds':refs}, + {'stage':'场景验证','owner':'商品企划 / 品控','hypothesis':'','metrics':'按当前类目关键使用场景验证规格、功能和体验','successMeaning':'验证结果有记录并可回溯到SKU','nextAction':'通过后进入页面制作;不通过则调整产品','evidenceIds':refs+issue_refs}, + {'stage':'页面验收','owner':'视觉与内容','hypothesis':'','metrics':'逐项核对主图、详情图、SKU、参数和营销表达','successMeaning':'图片来源清晰且没有结构、颜色或功能冲突','nextAction':'通过后上架;不通过则重新采集或修改页面','evidenceIds':refs}, + {'stage':'首批反馈','owner':'客服与数据','hypothesis':'','metrics':'按SKU记录咨询、评论、退款售后原话与问题批次','successMeaning':'反馈可以归因到具体规格和问题类型','nextAction':'保留通过SKU;集中问题返回供应链整改','evidenceIds':issue_refs}, + ] + +def _product_label(raw): + product=raw.get('product',{}); title=_valid_title(product.get('title')) + if title:return title + route=detect_category(raw); attrs=_attribute_rows(raw); parts=[] + for words in [('品牌',),('品类','类别','商品类型'),('型号','款式','口味','色号')]: + x,_=_find_attr(attrs,words) + if x and _txt(x.get('value')).strip() not in parts:parts.append(_txt(x.get('value')).strip()) + return ' '.join(parts[:3]) or route['label'] or '当前商品' + +def _review_ref(raw, predicate): + for i,x in enumerate(raw.get('reviews',[]) or [],1): + text=_txt(x.get('content'))+' '+_txt(x.get('appendContent')) + if predicate(text):return text.strip(),f'REV_{i:06d}' + return '','' + +def _buyer_evidence(raw, limit=24): + images=raw.get('images',{}) or {}; buyer=images.get('buyerShow',[]) or []; index_by_url={url:i for i,url in enumerate(buyer,1)} + out=[];seen=set() + for review_index,review in enumerate(raw.get('reviews',[]) or [],1): + for url in review.get('images',[]) or []: + image_index=index_by_url.get(url) + if not image_index or url in seen:continue + seen.add(url);out.append({'imageEvidenceId':f'IMG_BUYERSHOW_{image_index:04d}','reviewEvidenceId':f'REV_{review_index:06d}','quote':concise_fact(review.get('content'),72),'sku':_txt(review.get('sku')).strip(),'date':_txt(review.get('date')).strip(),'sourceLabel':'评论原图'}) + if len(out)>=limit:return out + return out + +REVIEW_OUTCOME_RULES=( + ('花型与空间呈现',('好看','漂亮','可爱','高级','颜值','搭配儿童房','风格也搭','氛围感','少女心'),'明确反馈花型好看、可爱或能搭配房间'), + ('触感与睡感',('柔软','舒服','舒适','亲肤','滑滑','光滑','手感很好','手感特别'),'明确反馈柔软、舒服、亲肤或光滑'), + ('儿童接受度',('孩子很喜欢','孩子特别喜欢','宝宝超喜欢','宝宝很喜欢','女儿很喜欢'),'明确反馈孩子、宝宝或女儿喜欢'), + ('质量与做工',('做工也很好','做工细节都很棒','做工好精致','质量真的很好','质量特别好','质量非常好','品质真不错','质感很好'),'明确反馈质量或做工表现好'), + ('复购意愿',('再买一套','下次还会来','下次再回购','认准你家'),'明确表达再次购买、回购或认准店铺'), +) + +REVIEW_ISSUE_RULES=( + ('size_fit','尺寸与床型适配',r'罩不住|铺不满|盖不住|垂边不够|床单.{0,10}(?:短|小|窄|不够)|(?:尺寸|大小).{0,8}(?:偏小|偏大|不合适|不符)|被套.{0,8}(?:偏小|偏大|不合适|不符)','按评论对应 SKU 在实际床垫铺装,记录床单垂边、床笠包裹和被套适配后再锁版'), + ('wash_stability','首洗缩水或掉色',r'缩水|掉色|褪色|洗后.{0,8}(?:变形|发硬|变小|小了)','按标示洗护完成首洗,对比洗前洗后尺寸、颜色和表面状态'), + ('pilling_shedding','起球或掉毛',r'起球|掉毛|粘毛|浮毛','完成面料与成品洗后起球、掉毛检查,不通过则调整面料或后整理'), + ('odor','气味',r'异味|刺鼻|味道.{0,8}(?:大|重|难闻)','检查面料、印染、填充与包装来源,成品散味验收通过后再入仓'), + ('sewing_breakage','缝制或破损',r'破损|开线|脱线|线头.{0,8}(?:多|严重)|走线.{0,8}(?:歪|差)|缝.{0,8}(?:开|坏)','复核裁剪、缝制、锁边和出货外观,问题批次返修后复检'), + ('material_mismatch','材质信息不一致',r'不是纯棉|不是全棉|材质.{0,8}(?:不符|不一样)|面料.{0,8}(?:不符|不一样)|货不对板','核对供应资料、水洗标、吊牌、样品和页面材质表述,一处不一致即暂停宣称'), + ('packaging_transfer','包装污染或转移',r'包装.{0,10}(?:染|脏|掉色)|油墨|印字.{0,8}(?:沾|染)|包装袋.{0,8}(?:沾|染)','做包装接触与运输模拟,检查油墨、颜色、气味和污渍转移'), + ('pillow_filling','枕芯或填充支撑',r'枕头.{0,8}(?:太高|太低|太硬|太软|塌)|枕芯.{0,8}(?:太高|太低|太硬|太软|塌)|填充.{0,8}(?:少|不均|结团)','核对填充重量、均匀度和支撑感,按套件 BOM 留样验收'), + ('color_difference','实物色差',r'色差|实物.{0,8}(?:偏黄|偏白|偏暗|颜色不一样)|图片.{0,8}(?:不一样|差很多)','自然光核对实物色号与页面图片,页面补充真实色差说明'), +) + +def _review_issue(raw): + reviews=raw.get('reviews',[]) or [] + route=detect_category(raw) + dynamic_rules={ + 'apparel':( + ('apparel_size','尺码或版型适配',r'尺码|码数|偏大|偏小|不合身|显胖|版型.{0,8}(?:不符|奇怪)','按评论对应SKU复核尺码表、版型数据和真人试穿效果'), + ('apparel_workmanship','服饰做工问题',r'开线|脱线|线头|拉链|纽扣|破损','复核对应批次的缝制、辅料和出货外观'), + ('apparel_color','服饰图实色差',r'色差|实物.{0,8}颜色|图片.{0,8}不一样','在统一自然光下核对实物色号与页面图片'), + ), + 'food':( + ('food_taste','口味反馈分化',r'难吃|太甜|太咸|口味.{0,8}(?:怪|淡|重)','按口味SKU统计反馈并复核配方与页面描述'), + ('food_package','食品包装或运输问题',r'漏液|胀包|破包|碎了|包装.{0,8}(?:破|坏)','复核包装密封、内衬和运输模拟结果'), + ('food_freshness','新鲜度或保质风险',r'变质|过期|不新鲜|临期|发霉','核对生产批次、保质期、储存和出库规则'), + ), + 'beauty':( + ('beauty_sensitive','敏感或刺激反馈',r'过敏|刺激|刺痛|泛红|闷痘|爆痘','按肤质和使用方式复核对应SKU与成分风险提示'), + ('beauty_claim','功效体验不一致',r'没效果|无效|不吸收|搓泥|拔干|油腻','拆分功效宣称、使用方法与真实反馈,降低过强承诺'), + ('beauty_package','美妆包装问题',r'漏液|泵头|瓶口|包装.{0,8}(?:坏|破)','复核包材、密封和运输跌落测试'), + ), + 'digital':( + ('digital_compat','兼容或连接问题',r'不兼容|连不上|连接.{0,8}(?:失败|断)|识别不了','按设备型号、系统和接口复核兼容列表'), + ('digital_performance','性能或续航问题',r'卡顿|发热|死机|续航.{0,8}(?:差|短)|掉电|速度慢','按配置版本复测性能、温度、续航和稳定性'), + ('digital_quality','数码质量问题',r'失灵|故障|坏了|杂音|断触|黑屏','定位批次和故障场景,复核出厂测试与售后记录'), + ), + } + for key,label,pattern,action in dynamic_rules.get(route['key'],()): + rx=re.compile(pattern,re.I) + for i,review in enumerate(reviews,1): + if not isinstance(review,dict):continue + text=(' '.join(x for x in (_txt(review.get('content')).strip(),_txt(review.get('appendContent')).strip()) if x)).strip() + if text and rx.search(text): + return {'key':key,'label':label,'action':action,'text':text,'evidenceId':f'REV_{i:06d}','sku':_txt(review.get('sku')).strip()} + for key,label,pattern,action in REVIEW_ISSUE_RULES: + rx=re.compile(pattern,re.I) + for i,review in enumerate(reviews,1): + if not isinstance(review,dict):continue + text=(' '.join(x for x in (_txt(review.get('content')).strip(),_txt(review.get('appendContent')).strip()) if x)).strip() + if text and rx.search(text): + return {'key':key,'label':label,'action':action,'text':text,'evidenceId':f'REV_{i:06d}','sku':_txt(review.get('sku')).strip()} + for i,review in enumerate(reviews,1): + if not isinstance(review,dict):continue + text=(' '.join(x for x in (_txt(review.get('content')).strip(),_txt(review.get('appendContent')).strip()) if x)).strip() + if text and any(k in text for k in NEGATIVE_REVIEW_KEYS): + return {'key':'other','label':'评论明确问题','action':'按评论原文定位对应 SKU 和批次,复核实物后再决定是否修版','text':text,'evidenceId':f'REV_{i:06d}','sku':_txt(review.get('sku')).strip()} + return {} + +HOME_TEXTILE_FACT_GROUPS=( + ('适用场景 / 人群',('适用场景','适用人群','适用对象'),'用于锁定使用场景与商品表达'), + ('套件 BOM',('套件组成','套件件数','件数','组合形式','套件种类'),'用于逐件核对套装构成与包装清单'), + ('材质 / 成分',('成分含量','床单面料材质','被面材质','被里材质','枕套材质','面料材质','棉种类','材质'),'用于核对供应资料、水洗标与页面宣称'), + ('支数 / 密度 / 克重',('面料支数','纱线支数','织物密度','面料密度','克重'),'用于锁定面料规格,不等同于检测结果'), + ('床型 / 成品尺寸',('适用床尺寸','床品尺寸','被套尺寸','床单尺寸','床笠尺寸','枕套尺寸','尺寸'),'用于床型铺装和 SKU 适配校验'), + ('工艺 / 结构',('床品工艺','织造工艺','印染工艺','面料工艺','绗缝工艺','填充工艺','工艺','款式'),'用于样品结构与生产工艺核对'), + ('等级 / 标准',('产品等级','安全类别','执行标准','质量等级'),'用于核对吊牌、检测资料与页面口径'), + ('洗护说明',('洗涤说明','洗护说明','洗涤方式','护理方法'),'用于首洗验证和售后说明'), +) + +def _home_textile_parameter_facts(raw, limit=10): + attrs=_attribute_rows(raw);used=set();facts=[] + for label,words,meaning in HOME_TEXTILE_FACT_GROUPS: + matches=[];refs=[] + for i,item in enumerate(attrs,1): + if i in used:continue + name=_txt(item.get('name')).strip() + if any(word in name for word in words): + used.add(i);matches.append(item);refs.append(f'ATTR_{i:04d}') + if not matches:continue + value=';'.join((f"{x.get('name')}:{x.get('value')}" if len(matches)>1 else _txt(x.get('value'))) for x in matches) + facts.append({'label':label,'value':concise_fact(value,220),'productMeaning':meaning,'evidenceIds':refs}) + if len(facts)>=limit:return facts + for i,item in enumerate(attrs,1): + if i in used:continue + facts.append({'label':_txt(item.get('name')),'value':concise_fact(item.get('value'),180),'productMeaning':'页面采集参数,批量上架前与实物核对','evidenceIds':[f'ATTR_{i:04d}']}) + if len(facts)>=limit:break + return facts + +def _home_textile_gates(raw, issue=None): + attrs=_attribute_rows(raw);issue=issue or _review_issue(raw) + material,material_id=_find_attr(attrs,('成分含量','床单面料材质','被面材质','被里材质','材质','棉种类')) + pieces,pieces_id=_find_attr(attrs,('套件组成','套件件数','件数','组合形式')) + size,size_id=_find_attr(attrs,('适用床尺寸','床品尺寸','被套尺寸','床单尺寸','尺寸')) + wash,wash_id=_find_attr(attrs,('洗涤说明','洗护说明','洗涤方式','护理方法')) + grade,grade_id=_find_attr(attrs,('产品等级','安全类别','执行标准')) + gates=[] + if pieces:gates.append({'priority':'P0','label':'套件 BOM 一致','action':'逐件核对样品、包装清单、SKU 名称和详情页的品类、件数与尺寸','businessReason':'套件构成必须与页面参数一致','evidenceIds':[pieces_id]}) + if material or grade:gates.append({'priority':'P0','label':'材质与标签一致','action':'核对供应资料、水洗标、吊牌、样品和页面的成分、等级与执行标准','businessReason':'页面宣称必须能回溯到真实资料','evidenceIds':[x for x in (material_id,grade_id) if x]}) + if size:gates.append({'priority':'P0','label':'床型与尺寸实测','action':'按页面覆盖床型和床垫高度逐一铺装,记录床单垂边、床笠包裹与被套适配','businessReason':'每个 SKU 通过实床铺装后才能锁版','evidenceIds':[size_id]}) + gates.append({'priority':'P0','label':'首洗前后验收','action':'按标示洗护完成首洗,记录各部件尺寸、颜色、起球掉毛和外观变化','businessReason':'结果须符合页面声明及企业验收标准','evidenceIds':[wash_id] if wash_id else []}) + gates.append({'priority':'P1','label':'缝制与填充验收','action':'检查走线、锁边、拉链、线头、污渍;含枕芯或被芯时同步检查填充重量与均匀度','businessReason':'成品外观与套件完整性通过后再入仓','evidenceIds':[pieces_id] if pieces_id else []}) + gates.append({'priority':'P1','label':'气味与包装转移','action':'完成密封、运输模拟和开箱检查,记录气味、油墨、颜色与污渍转移','businessReason':'包装与商品接触后不得污染成品','evidenceIds':[]}) + if issue:gates.insert(0,{'priority':'P0','label':f"评论问题:{issue['label']}",'action':issue['action'],'businessReason':concise_fact(issue['text'],72),'evidenceIds':[issue['evidenceId']]}) + return gates[:7] + +def _home_textile_validation_rows(raw): + attrs=_attribute_rows(raw);all_refs=[f'ATTR_{i:04d}' for i in range(1,min(len(attrs),8)+1)] + issue=_review_issue(raw);issue_refs=[issue['evidenceId']] if issue else [] + return [ + {'stage':'商品定义','owner':'商品企划 / 商品运营','hypothesis':'','metrics':'锁定目标场景、套件 BOM、材质工艺、床型尺寸与 SKU','successMeaning':'商品定义表与页面已采参数逐项对应','nextAction':'通过后发起打样;不一致则退回定义','evidenceIds':all_refs}, + {'stage':'资料与标签','owner':'供应链 / 品控','hypothesis':'','metrics':'核对供应资料、水洗标、吊牌、包装和样品','successMeaning':'成分、等级、标准与洗护口径一致','nextAction':'通过后确认样品;不一致则暂停宣称','evidenceIds':all_refs}, + {'stage':'实床与首洗','owner':'品控 / 供应链','hypothesis':'','metrics':'按 SKU 实床铺装;首洗前后记录尺寸、掉色、起球掉毛与外观','successMeaning':'符合页面声明及企业验收标准,记录可追溯','nextAction':'通过后锁版;不通过则调整尺寸、面料或工艺','evidenceIds':all_refs+issue_refs}, + {'stage':'成品与包装','owner':'品控 / 仓配','hypothesis':'','metrics':'检查缝制、填充、污渍、气味及包装接触转移','successMeaning':'成品与包装验收项全部通过','nextAction':'通过后入仓;问题批次返修复检','evidenceIds':issue_refs}, + {'stage':'页面验收','owner':'视觉与内容','hypothesis':'','metrics':'逐项核对主图、详情、SKU 表、尺寸图、材质与洗护说明','successMeaning':'页面图片来源清晰,且与样品、包装、标签一致','nextAction':'通过后上架;不通过则停止投放','evidenceIds':all_refs}, + {'stage':'首批反馈','owner':'客服与数据','hypothesis':'','metrics':'按 SKU 记录咨询、评论、退款售后原话与问题批次','successMeaning':'反馈均可回溯到 SKU、订单和问题类型','nextAction':'保留通过 SKU;集中问题返回供应链整改','evidenceIds':[]}, + ] + +def _review_outcomes(raw): + reviews=raw.get('reviews',[]) or [];out=[] + for label,keys,result in REVIEW_OUTCOME_RULES: + matched=[] + for i,x in enumerate(reviews,1): + if not isinstance(x,dict):continue + text=_txt(x.get('content'))+' '+_txt(x.get('appendContent')) + if any(k in text for k in keys):matched.append(f'REV_{i:06d}') + if matched:out.append({'label':label,'value':f'{len(matched)} 条评论{result}','note':f'评论结果 · {len(matched)}/{len(reviews)}','evidenceIds':matched}) + return out + +def _find_outcome(outcomes, label): + for item in outcomes or []: + if isinstance(item,dict) and item.get('label')==label:return item + return {} + +def _generic_selling_points(raw, base=None, issue=None): + base=base or baseline(raw); issue=issue or _review_issue(raw); route=detect_category(raw) + points=[] + for fact in _category_parameter_facts(raw,5): + value=concise_fact(fact.get('value'),52) + if not value:continue + points.append({ + 'slogan':f"{fact.get('label')}:{value}", + 'consumerValue':f"让消费者直接确认{route['label']}商品的{fact.get('label')}", + 'evidenceSource':'页面参数', + 'productAction':f"主图或详情页用实物、参数或资料证明{fact.get('label')}", + 'evidenceIds':fact.get('evidenceIds') or [] + }) + outcomes=_review_outcomes(raw) + for outcome in outcomes[:2]: + refs=_arr(outcome.get('evidenceIds')) + if refs:points.append({'slogan':concise_fact(outcome.get('value'),46),'consumerValue':'把评论已验证体验转成页面可理解的利益表达','evidenceSource':'评论聚合','productAction':'保留对应产品体验,并用实拍或细节证据承接','evidenceIds':refs[:8]}) + if issue: + points.append({'slogan':f"优先修正{issue['label']}",'consumerValue':'避免下一款重复当前已出现的问题','evidenceSource':'评论明确问题','productAction':issue['action'],'evidenceIds':[issue['evidenceId']]}) + unique=[];seen=set() + for point in points: + key=point.get('slogan') + if key and key not in seen:seen.add(key);unique.append(point) + return unique[:6] + +def _derive_next_selling_points(raw, base=None, issue=None): + if detect_category(raw)['key']!='home_textile': + return _generic_selling_points(raw,base,issue) + base=base or baseline(raw); issue=issue or _review_issue(raw) + attrs=_attribute_rows(raw); outcomes=_review_outcomes(raw) + title=_txt((raw.get('product') or {}).get('title')).strip() + sku_text=' '.join(_txt(x.get('name') or x.get('text') or x.get('value')) for x in raw.get('sku',[]) if isinstance(x,dict))[:3000] + attr_text=' '.join(f"{x.get('name')}:{x.get('value')}" for x in attrs)[:3000] + corpus=' '.join([title,sku_text,attr_text]) + size,size_id=_find_attr(attrs,('适用床尺寸','床品尺寸','被套尺寸','床单尺寸','床笠尺寸','尺寸')) + material,material_id=_find_attr(attrs,('成分含量','床单面料材质','被面材质','被里材质','枕套材质','面料材质','材质','棉种类')) + craft,craft_id=_find_attr(attrs,('床品工艺','织造工艺','面料密度','面料支数','工艺')) + pattern,pattern_id=_find_attr(attrs,('主图案类型','风格','颜色分类','款式')) + pieces,pieces_id=_find_attr(attrs,('件数','套件组成','套件件数','组合形式')) + touch=_find_outcome(outcomes,'触感与睡感') + design=_find_outcome(outcomes,'花型与空间呈现') + size_outcome=_find_outcome(outcomes,'儿童接受度') if '儿童' in corpus else {} + points=[] + def add(slogan, consumer, evidence, action, refs): + refs=[x for x in _uniq(refs) if x] + if not refs:return + points.append({'slogan':slogan,'consumerValue':consumer,'evidenceSource':evidence,'productAction':action,'evidenceIds':refs}) + has_dorm_scene=bool(re.search(r'宿舍|大学|大学生',corpus)) + has_small_bed=bool(re.search(r'0\.?9|0.9|1\.?2|1.2|90|120',corpus)) + scene='宿舍床专用' if has_dorm_scene else ('小床专用' if has_small_bed else '目标床型专用') + if size: + size_value=_txt(size.get('value')) + if has_dorm_scene and re.search(r'0\.?9|0.9|90',size_value+corpus): + add('0.9m 宿舍床专用,不用猜尺寸','消费者一眼知道这套床品适配哪张宿舍床','页面尺寸参数、目标场景与 SKU 信息', '首图拍 0.9m 宿舍床效果;详情页单独列被套、床单/床笠、枕套尺寸', [size_id,pieces_id]) + elif has_small_bed: + add('小床尺寸单独讲清,不让用户猜','消费者一眼知道这套床品适配哪张床','页面尺寸参数与 SKU 信息', '首图拍对应床型效果;详情页单独列被套、床单/床笠、枕套尺寸', [size_id,pieces_id]) + else: + add(f'{scene},尺寸直接看清','减少买错床型和规格的理解成本','页面尺寸参数', '按主销床型分别拍铺设边界和套件尺寸图', [size_id,pieces_id]) + if re.search(r'床笠',corpus) and re.search(r'床单',corpus): + add('床笠款 / 床单款分开买,不混规格','消费者能判断自己要包床垫款还是普通床单款','SKU 与参数中同时出现床笠、床单', '分标题、分 SKU、分主图、分详情页说明两种结构', [x for x in (size_id,pieces_id) if x]) + elif re.search(r'床笠',corpus): + add('床笠款包住床垫,铺上不跑位','强调包裹、防滑和宿舍床垫适配','页面 SKU/参数出现床笠', '拍四角包裹、床垫厚度、洗后回弹', [size_id,pieces_id]) + if material: + mat=_txt(material.get('value')) + add(('100%棉基础面料,贴身不将就' if '100' in mat or '棉' in mat else '基础面料信息讲清楚'),'消费者知道材质承诺来自页面参数','页面材质/成分参数', '材质页同时展示水洗标、吊牌和面料近景', [material_id]+_arr(touch.get('evidenceIds'))[:3]) + if touch.get('evidenceIds'): + add('全棉要柔软,不要偏硬刮肤','把“全棉”转成可感知的触感利益','评论聚合中有柔软、亲肤或舒服反馈', '打样增加触感验收、柔软后整理和首洗后手感复检', _arr(touch.get('evidenceIds'))[:6]+([material_id] if material_id else [])) + if issue and issue.get('key')=='wash_stability': + add('首洗后尺寸稳定,洗完还能铺得平','消费者买床品要省心耐用','评论明确出现缩水或掉色反馈', '每批做首洗尺寸、颜色和外观记录,页面展示洗前洗后结果', [issue.get('evidenceId'),size_id]) + elif issue and issue.get('key')=='size_fit': + add('铺上合适,边界不尴尬','消费者最怕买回去罩不住、铺不满','评论明确出现尺寸适配问题', '按 SKU 实床铺设并记录床单垂边、床笠包裹和被套适配', [issue.get('evidenceId'),size_id]) + if design.get('evidenceIds') or pattern: + add(('耐看花色,宿舍日常好打理' if has_dorm_scene else '耐看花色,日常好打理'),'花色不是只讲好看,要讲耐看、耐脏、适合日常','评论花型反馈或页面花型参数', '保留已验证花型方向;主图补自然光、局部纹样和整套陈列', _arr(design.get('evidenceIds'))[:5]+([pattern_id] if pattern_id else [])) + if craft: + add('工艺参数讲明白,品质感有出处','支数、密度、印染或织造信息不只放参数表','页面工艺/支数/密度参数', '详情页用面料近景、工艺说明和水洗标承接参数', [craft_id]) + return points[:6] + +def _apply_selling_points(exp, raw, base=None): + if not isinstance(exp,dict):return exp + base=base or baseline(raw); issue=_review_issue(raw); points=_derive_next_selling_points(raw,base,issue) + plans=exp.setdefault('newProductPlans',{}) + plan_list=plans.setdefault('plans',[]) + if plan_list and isinstance(plan_list[0],dict): + existing=_arr(plan_list[0].get('sellingPoints')) + plan_list[0]['sellingPoints']=points[:5] or existing + if points: + kept=[x for x in _arr(plan_list[0].get('specs')) if not (isinstance(x,dict) and x.get('label')=='下一款明确卖点')] + plan_list[0]['specs']=[{'label':'下一款明确卖点','value':' / '.join(x['slogan'] for x in points[:3])}]+kept + if points: + overview=exp.setdefault('ownerOverview',{}) + cards=[x for x in overview.setdefault('cards',[]) if not (isinstance(x,dict) and x.get('label')=='下一款明确卖点')] + cards.append({'label':'下一款明确卖点','headline':points[0]['slogan'],'bullets':[x['slogan'] for x in points[1:3]],'tone':'good','evidenceIds':points[0].get('evidenceIds',[])}) + overview['cards']=cards + exp['contentGovernance']={ + 'role':'专业电商报告输出总监', + 'structure':'商品产品分析报告', + 'sourceRule':'页面参数、主图/详情/SKU/评论图、评论统计、问答和监测数据分来源呈现', + 'evidenceRule':'每个结论必须引用 evidenceIds;缺失证据不补写', + 'visualRule':'报告渲染采用时尚网页设计美学:大图完整展示、清晰层级、短结论、证据卡片化' + } + return exp + +def _generic_platform_overview(raw, base=None): + base=base or baseline(raw); route=detect_category(raw); attrs=_attribute_rows(raw); sales=raw.get('sales') or {}; issue=_review_issue(raw) + price=_txt(sales.get('currentPrice')).strip(); sold=_txt(sales.get('sold') or sales.get('cumulativeSales')).strip(); facts=[] + facts.append({'label':'商品是什么','value':_product_label(raw),'note':f"自动识别:{route['label']}",'evidenceIds':['P_TITLE'] if _valid_title((raw.get('product') or {}).get('title')) else []}) + if price or sold:facts.append({'label':'价格与销量','value':' · '.join(x for x in (f'¥{price}' if price else '',f'公开销量 {sold}' if sold else '') if x),'note':'采集时页面公开值','evidenceIds':[x for x in ('S_CURRENTPRICE' if price else '','S_SOLD' if sold else '') if x]}) + if attrs:facts.append({'label':'动态参数','value':f'已采集 {len(attrs)} 项','note':';'.join(f"{x.get('name')}:{x.get('value')}" for x in attrs[:3]),'evidenceIds':[f'ATTR_{i:04d}' for i in range(1,min(3,len(attrs))+1)]}) + outcomes=_review_outcomes(raw); actions=[] + if outcomes:actions.append({'label':'保留','value':'保留评论已验证的实际体验','note':outcomes[0].get('value'),'evidenceIds':outcomes[0].get('evidenceIds') or []}) + if issue:actions.append({'label':'修正','value':issue['action'],'note':issue['label'],'evidenceIds':[issue['evidenceId']]}) + refs=[f'ATTR_{i:04d}' for i in range(1,min(3,len(attrs))+1)] + if refs:actions.append({'label':'下一步','value':f"按{route['label']}关键维度核对样品、SKU和页面一致性",'note':'当前证据支持商品拆解与新品开发','evidenceIds':refs}) + public=_txt(base.get('publicReviewCount')).strip();sample=f"已采有效评论 {base.get('reviewCount',0)}"+(f" / 页面公开 {public}" if public else '') + sample+=';评论采集已确认完整' if base.get('reviewCollectionComplete') else ';评论采集未确认完整' + return {'chapterTitle':'商品分析结论','verdict':'先保留已确认信息,再处理评论问题和页面缺口','facts':facts,'outcomes':outcomes[:5],'actions':actions,'checks':_category_gates(raw,issue)[:3],'questions':[{'label':'购买问答原文','value':concise_fact(x.get('question'),64),'evidenceIds':[f'QA_{i:05d}']} for i,x in enumerate(raw.get('questions',[])[:2],1) if isinstance(x,dict)],'sampleNote':sample,'decisionBoundary':{'supported':'商品拆解、样品验证、规格校准、页面修正和新品方案','excluded':'生产规模、利润、转化、市场容量','required':'完整评论、流量转化、退款售后、成本毛利'}} + +def build_platform_overview(raw, base=None): + if detect_category(raw)['key']!='home_textile': + return _generic_platform_overview(raw,base) + base=base or baseline(raw);attrs=_attribute_rows(raw);sales=raw.get('sales',{}) or {};topics=base.get('topics',[]) or [] + label=_product_label(raw);price=_txt(sales.get('currentPrice')).strip();sold=_txt(sales.get('sold') or sales.get('cumulativeSales')).strip() + material,material_id=_find_attr(attrs,('床单面料材质','被面材质','材质','成分')) + thread,thread_id=_find_attr(attrs,('面料支数',));pieces,pieces_id=_find_attr(attrs,('件数','套件组成')) + size_attr,size_id=_find_attr(attrs,('适用床尺寸','床品尺寸','尺寸'));audience,audience_id=_find_attr(attrs,('适用人群',)) + craft,craft_id=_find_attr(attrs,('床品工艺','织造工艺','工艺'));grade,grade_id=_find_attr(attrs,('产品等级','安全类别','执行标准')) + promotions=[x for x in raw.get('promotions',[]) or [] if isinstance(x,dict) and _txt(x.get('text')).strip()] + issue=_review_issue(raw);neg_text=issue.get('text','');neg_id=issue.get('evidenceId','') + facts=[] + product_parts=[];product_refs=[] + for x,eid in ((material,material_id),(thread,thread_id),(pieces,pieces_id),(craft,craft_id)): + if x:product_parts.append(f"{x.get('name')}:{x.get('value')}");product_refs.append(eid) + facts.append({'label':'商品是什么','value':label,'note':';'.join(product_parts[:3]),'evidenceIds':product_refs[:3]}) + if audience:facts.append({'label':'页面适用人群','value':audience.get('value'),'note':'页面参数原文','evidenceIds':[audience_id]}) + if price or sold: + facts.append({'label':'价格与销量','value':' · '.join(x for x in (f'¥{price}' if price else '',f'公开销量 {sold}' if sold else '') if x),'note':'采集时页面公开值','evidenceIds':[x for x in ('S_CURRENTPRICE' if price else '','S_SOLD' if sold else '') if x]}) + if promotions:facts.append({'label':'页面活动','value':concise_fact(promotions[0].get('text'),72),'note':'活动以采集时页面展示为准','evidenceIds':['PROMO_0001']}) + outcomes=_review_outcomes(raw) + checks=[] + if size_attr:checks.append({'priority':'运营校验','label':'核对床型与套件尺寸','value':size_attr.get('value'),'evidenceIds':[size_id]}) + if neg_id:checks.append({'priority':'明确反馈','label':f"重点核对{issue['label']}",'value':concise_fact(neg_text,72),'evidenceIds':[neg_id]}) + if grade:checks.append({'priority':'页面参数','label':grade.get('name'),'value':grade.get('value'),'evidenceIds':[grade_id]}) + questions=[] + for i,q in enumerate(raw.get('questions',[])[:3],1): + text=concise_fact(q.get('question'),64) if isinstance(q,dict) else '' + if text:questions.append({'label':'购买问答原文','value':text,'evidenceIds':[f'QA_{i:05d}']}) + outcome_map={x.get('label'):x for x in outcomes} + appearance=outcome_map.get('花型与空间呈现',{});touch=outcome_map.get('触感与睡感',{}) + positive_refs=_uniq((appearance.get('evidenceIds') or [])+(touch.get('evidenceIds') or [])) + actions=[] + if positive_refs:actions.append({'label':'保留','value':'保留评论已验证的花型呈现与触感表达','note':'按本次有效评论统计','evidenceIds':positive_refs}) + if issue:actions.append({'label':'修正','value':issue['action'],'note':f"评论明确出现{issue['label']}",'evidenceIds':[neg_id]}) + actions.append({'label':'下一步','value':'进入样品验证;同步核对成本、家纺验收项与页面一致性','note':'当前证据支持样品和页面校准','evidenceIds':[x for x in (size_id,neg_id) if x]}) + verdict='保留已验证体验,优先核对尺寸、成本和页面一致性' + public=_txt(base.get('publicReviewCount')).strip() + sample=f"已采有效评论 {base.get('reviewCount',0)}"+(f" / 页面公开 {public}" if public else '') + sample+=';评论采集已确认完整' if base.get('reviewCollectionComplete') else ';评论采集未确认完整' + return {'chapterTitle':'商品分析结论','verdict':verdict,'facts':facts[:4],'outcomes':outcomes[:5],'actions':actions,'checks':checks[:3],'questions':questions[:2],'sampleNote':sample, + 'decisionBoundary':{'supported':'商品拆解、样品验证、规格校准、页面信息修正','excluded':'生产规模、利润、转化、市场容量','required':'完整评论、流量转化、退款售后、成本毛利'}} + +def _core_solution_ok(exp): + if not isinstance(exp,dict):return False + checks=( + bool(_txt((exp.get('reportSummary') or {}).get('title')).strip()), + bool(_txt((exp.get('reportSummary') or {}).get('verdict')).strip()), + isinstance((exp.get('ownerOverview') or {}).get('cards'),list) and len((exp.get('ownerOverview') or {}).get('cards'))>=3, + isinstance((exp.get('productExperience') or {}).get('parameterFacts'),list) and len((exp.get('productExperience') or {}).get('parameterFacts'))>=2, + isinstance((exp.get('productExperience') or {}).get('gates'),list) and len((exp.get('productExperience') or {}).get('gates'))>=2, + isinstance((exp.get('newProductPlans') or {}).get('plans'),list) and len((exp.get('newProductPlans') or {}).get('plans'))>=1, + isinstance((exp.get('validationLoop') or {}).get('rows'),list) and len((exp.get('validationLoop') or {}).get('rows'))>=3, + ) + return all(checks) + +def core_solution_diagnostics(exp): + exp=exp if isinstance(exp,dict) else {} + specs=( + ('reportSummary.title',bool(_txt((exp.get('reportSummary') or {}).get('title')).strip()),1,1), + ('reportSummary.verdict',bool(_txt((exp.get('reportSummary') or {}).get('verdict')).strip()),1,1), + ('ownerOverview.cards',isinstance((exp.get('ownerOverview') or {}).get('cards'),list),len((exp.get('ownerOverview') or {}).get('cards') or []),3), + ('productExperience.parameterFacts',isinstance((exp.get('productExperience') or {}).get('parameterFacts'),list),len((exp.get('productExperience') or {}).get('parameterFacts') or []),2), + ('productExperience.gates',isinstance((exp.get('productExperience') or {}).get('gates'),list),len((exp.get('productExperience') or {}).get('gates') or []),2), + ('newProductPlans.plans',isinstance((exp.get('newProductPlans') or {}).get('plans'),list),len((exp.get('newProductPlans') or {}).get('plans') or []),1), + ('validationLoop.rows',isinstance((exp.get('validationLoop') or {}).get('rows'),list),len((exp.get('validationLoop') or {}).get('rows') or []),3), + ) + checks=[] + for field,right_type,actual,minimum in specs: + ok=bool(right_type and actual>=minimum) + checks.append({'field':field,'actual':actual,'minimum':minimum,'ok':ok}) + return {'ok':all(x['ok'] for x in checks),'checks':checks,'missing':[x['field'] for x in checks if not x['ok']]} + +def repair_core_solution_with_evidence(draft, raw, base=None): + """Fill only missing core report structure from deterministic, evidence-backed facts.""" + draft=dict(draft or {}) + evidence=build_fallback_experience(raw,base) + valid_ids={x.get('id') for x in evidence_ledger(normalize(raw)) if x.get('id')} + scalar_rules=(('reportSummary','title'),('reportSummary','verdict')) + list_rules=( + ('ownerOverview','cards',3), + ('productExperience','parameterFacts',2),('productExperience','gates',2), + ('newProductPlans','plans',1),('validationLoop','rows',3), + ) + for section,key in scalar_rules: + current=dict(draft.get(section) or {}) + source=evidence.get(section) or {} + if not _txt(current.get(key)).strip():current[key]=source.get(key,'') + draft[section]=current + for section,key,minimum in list_rules: + current=dict(draft.get(section) or {}) + source=evidence.get(section) or {} + values=_filter_evidence_items(current.get(key),valid_ids) + source_values=_filter_evidence_items(source.get(key),valid_ids) + if section=='ownerOverview' and key=='cards' and len(source_values)=minimum:break + if isinstance(item,dict) and item not in values:values.append(item) + current[key]=values;draft[section]=current + return draft + +def build_generic_fallback_experience(raw, base=None): + raw=normalize(raw);base=base or baseline(raw);route=detect_category(raw);attrs=_attribute_rows(raw);sales=raw.get('sales') or {};issue=_review_issue(raw) + label=_product_label(raw);review_count=base.get('reviewCount',0);question_count=base.get('questionCount',0);sold=_txt(sales.get('sold')).strip();price=_txt(sales.get('currentPrice')).strip() + refs=[f'ATTR_{i:04d}' for i in range(1,min(len(attrs),8)+1)];facts=_category_parameter_facts(raw);gates=_category_gates(raw,issue);points=_generic_selling_points(raw,base,issue) + main_ids=[f'IMG_MAIN_{i:04d}' for i,_ in enumerate((raw.get('images') or {}).get('main',[])[:8],1)] + visual=[] + roles=('商品全貌与第一印象','核心卖点与使用场景','规格、款式或选择信息','材质、成分或配置证据','细节、功能或实际效果','服务、合规或补充说明') + for i,eid in enumerate(main_ids): + role=roles[min(i,len(roles)-1)] + visual.append({'label':f'{i+1:02d} / 已核验主图','headline':role,'explanation':'','imageEvidenceId':eid,'frameInfo':'按原图完整展示,识别主体、场景、配色、文字与信息层级','persuasionTask':role,'weakness':'','action':'确保本图承担独立购买确认任务,并与参数和SKU一致','evidenceIds':[eid]}) + owner=[ + {'label':'商品定义','headline':label,'bullets':[f"自动识别为{route['label']}"],'tone':'normal','evidenceIds':['P_TITLE'] if _valid_title((raw.get('product') or {}).get('title')) else refs[:1]}, + {'label':'公开销售事实','headline':' · '.join(x for x in (f'¥{price}' if price else '',f'销量 {sold}' if sold else '') if x) or '价格或销量未采集','bullets':[],'tone':'normal','evidenceIds':[x for x in ('S_CURRENTPRICE' if price else '','S_SOLD' if sold else '') if x]}, + {'label':'当前用户信号','headline':f'有效评论 {review_count} 条','bullets':[x.get('topic') for x in (base.get('topics') or [])[:3]],'tone':'good','evidenceIds':[f'REV_{i:06d}' for i in range(1,min(review_count,3)+1)]}, + {'label':'当前明确问题','headline':issue.get('label') if issue else '评论未形成明确问题','bullets':[concise_fact(issue.get('text'),58)] if issue else [],'tone':'risk' if issue else 'normal','evidenceIds':[issue.get('evidenceId')] if issue else refs[:1]}, + ] + stages=[] + for i,topic in enumerate((base.get('topics') or [])[:3],1): + examples=topic.get('examples') or [];evidence=[] + for j,review in enumerate(raw.get('reviews',[]) or [],1): + text=_txt(review.get('content')) + if any(k in text for k in TOPICS.get(topic.get('topic'),[])):evidence.append(f'REV_{j:06d}') + if evidence:stages.append({'key':f'signal{i}','stageNo':f'{i:02d}','label':topic.get('topic'),'headline':f"{topic.get('mentions')} 条评论提及",'userExpectation':'','positiveConfirmation':concise_fact(examples[0],90) if examples else '', 'friction':'','businessImpact':'将该信号对应到具体SKU和页面证据','quotes':[{'type':'评论','text':concise_fact(examples[0],90),'evidenceId':evidence[0]}] if examples else [],'evidenceIds':evidence[:8]}) + if len(stages)<2 and refs: + for i,fact in enumerate(facts[:2],len(stages)+1):stages.append({'key':f'fact{i}','stageNo':f'{i:02d}','label':fact.get('label'),'headline':fact.get('value'),'userExpectation':'看清商品关键信息','positiveConfirmation':'页面参数已采集','friction':'','businessImpact':'与SKU和详情页保持一致','quotes':[],'evidenceIds':fact.get('evidenceIds') or []}) + plan_specs=[{'label':x.get('label'),'value':x.get('value')} for x in facts[:6]] + exp={ + 'reportSummary':{'title':f'{label}产品分析报告','subtitle':f'{route["label"]} · 有效评论 {review_count} 条 · 参数 {len(attrs)} 项','verdict':'保留有证据的商品价值,先修正明确问题,再形成差异化新品','verdictDetail':'页面事实、图片、评论和问答分来源呈现。','monitorSummary':''}, + 'platformOverview':_generic_platform_overview(raw,base), + 'ownerOverview':{'chapterTitle':f'{route["label"]}商品事实、用户信号与下一款动作','chapterDeck':'','thesis':'','sampleSummary':f'有效评论 {review_count} 条,购买问答 {question_count} 条','cards':owner}, + 'visualCommerce':{'chapterTitle':'主图逐张承担不同购买确认任务','chapterDeck':'','items':visual}, + 'customerExperience':{'chapterTitle':'当前评论只形成用户信号,不扩大为普遍结论','chapterDeck':'','stats':[{'label':'有效评论','value':str(review_count)},{'label':'购买问答','value':str(question_count)},{'label':'有效参数','value':str(len(attrs))}],'journeyTitle':'评论体验、问题与购买顾虑分开呈现','journeyDeck':'','stages':stages,'buyerEvidence':{'title':'评论原图与对应评价','items':_buyer_evidence(raw)},'signals':{'title':'当前用户信号','positive':[],'questions':[],'risks':[{'label':issue.get('label'),'countOrNote':'明确评论文本','evidenceIds':[issue.get('evidenceId')]}] if issue else []}}, + 'productExperience':{'chapterTitle':f'{route["label"]}动态产品定义','chapterDeck':'','parameterFacts':facts,'thesis':'','thesisDetail':'','gates':gates}, + 'newProductPlans':{'chapterTitle':'稳健优化、设计升级与差异探索','chapterDeck':'','plans':[{'type':'稳健优化款','name':f'{label}优化款','positioning':'保留已确认价值,优先修正评论问题与页面信息冲突','sellingPoints':points,'specs':plan_specs,'manufacturingGate':{'title':'产品验证','content':'按当前类目关键参数核对样品、SKU、标签、包装和页面'},'visualCommerce':{'title':'主图与详情','content':'首图确认商品全貌,后续图片依次证明规格、材质/成分/配置、使用效果与服务'},'evidenceIds':_uniq((refs[:5])+([issue.get('evidenceId')] if issue else []))}], 'commercialSummary':{'title':'从当前商品保留什么','thesis':'只复用有证据的体验和信息结构','keep':points[0].get('slogan') if points else '页面已采参数','fix':issue.get('label') if issue else '页面与商品一致性','prove':'样品、SKU、参数、图片和评论可以相互核对'}}, + 'validationLoop':{'chapterTitle':f'{route["label"]}新品验证闭环','chapterDeck':'','rows':_category_validation_rows(raw)} + } + return _apply_selling_points(exp,raw,base) + +def build_fallback_experience(raw, base=None): + if detect_category(raw)['key']!='home_textile': + return build_generic_fallback_experience(raw,base) + raw=normalize(raw);base=base or baseline(raw);attrs=_attribute_rows(raw);sales=raw.get('sales',{});label=_product_label(raw) + sold=_txt(sales.get('sold') or sales.get('cumulativeSales')).strip();price=_txt(sales.get('currentPrice')).strip() + topics=base.get('topics',[]) or []; top=topics[:3];review_outcomes=_review_outcomes(raw) + outcome_counts={x.get('label'):len(x.get('evidenceIds') or []) for x in review_outcomes} + topic_cards=[] + for t in top: + topic_cards.append({'label':t.get('topic'),'headline':f"{t.get('mentions',0)} 条评论提及",'explanation':'','bullets':[],'tone':'good','evidenceIds':[]}) + issue=_review_issue(raw);neg_text=issue.get('text','');neg_id=issue.get('evidenceId','') + size_attr,size_attr_id=_find_attr(attrs,('适用床尺寸','床品尺寸','尺寸')) + material,material_id=_find_attr(attrs,('床单面料材质','被面材质','材质','成分')) + thread,thread_id=_find_attr(attrs,('面料支数',)) + pieces,pieces_id=_find_attr(attrs,('件数','套件组成')) + craft,craft_id=_find_attr(attrs,('床品工艺','织造工艺','工艺')) + grade,grade_id=_find_attr(attrs,('产品等级','安全类别','执行标准')) + attr_refs=[x for x in (material_id,thread_id,pieces_id,size_attr_id,craft_id,grade_id) if x] + top_text='、'.join(f"{x.get('label')} {len(x.get('evidenceIds') or [])}/{base.get('reviewCount',0)}" for x in review_outcomes[:3]) or '当前评论未形成可统计结果' + review_count=base.get('reviewCount',0);question_count=base.get('questionCount',0) + customer_results=[] + if outcome_counts.get('触感与睡感'):customer_results.append(f"{outcome_counts['触感与睡感']} 条确认触感") + if outcome_counts.get('花型与空间呈现'):customer_results.append(f"{outcome_counts['花型与空间呈现']} 条确认花型") + sales_bits=[] + if sold:sales_bits.append(f'公开销量 {sold}') + if price:sales_bits.append(f'采集价 ¥{price}') + sales_bits.extend([f'有效评论 {review_count} 条',f'购买问答 {question_count} 条']) + owner_cards=[ + {'label':'销售事实','headline':' · '.join(sales_bits[:2]) or '页面未展示销量与价格','bullets':sales_bits[2:],'tone':'normal','evidenceIds':[x for x in ('S_SOLD' if sold else '', 'S_CURRENTPRICE' if price else '') if x]}, + {'label':'评论已验证体验','headline':top_text,'bullets':['按本次实际评论文本统计'],'tone':'good','evidenceIds':[]}, + {'label':'当前明确问题','headline':(f"1 条评论明确出现{issue['label']}" if issue else '现有评论未出现规则可识别的明确问题'),'bullets':([concise_fact(neg_text,52)] if neg_text else []),'tone':'risk' if neg_id else 'normal','evidenceIds':[neg_id] if neg_id else []}, + {'label':'参数与信任','headline':f'已采 {len(attrs)} 项有效参数','bullets':[f"{x.get('name')}:{x.get('value')}" for x in attrs[:2]],'tone':'normal','evidenceIds':attr_refs[:3]}, + {'label':'下一款怎么开','headline':('保留已验证体验,先修正'+issue['label'] if issue else '保留已验证体验,进入家纺样品验收'),'bullets':['BOM、样品、标签、包装与页面口径必须一致'],'tone':'good','evidenceIds':([neg_id] if neg_id else [])+attr_refs[:2]}, + ] + promotions=[x for x in raw.get('promotions',[]) or [] if isinstance(x,dict) and _txt(x.get('text')).strip()] + if promotions: + owner_cards.insert(1,{'label':'页面活动','headline':concise_fact(promotions[0].get('text'),46),'bullets':['报告只记录采集时页面展示内容'],'tone':'normal','evidenceIds':['PROMO_0001']}) + main_ids=[] + for i,url in enumerate((raw.get('images',{}) or {}).get('main',[]) or [],1): + if re.search(r'(?:avatar|sns_logo|shopmanager|tps-\d{1,3}-\d{1,3}|logo)',_txt(url),re.I):continue + main_ids.append(f'IMG_MAIN_{i:04d}') + if len(main_ids)>=3:break + visual_items=[] + visual_defs=[ + ('01 / 已核验主图','首图:看清商品全貌','主体、套件形态、花型面积与页面文字必须同屏核对','保留原图比例,优先检查商品是否完整、文案是否遮挡主体'), + ('02 / 已核验主图','辅图:补充花型与搭配','观察配色、纹样密度、床品铺陈方式和儿童房场景信息','把可见花型、配色和套件组成拆成可复用拍摄清单'), + ('03 / 已核验主图','辅图:承接规格信息','核对床型、件数、尺寸和页面参数是否一致','图上出现的规格表达必须与 SKU、参数表和详情页保持一致') + ] + for eid,defs in zip(main_ids,visual_defs): + item_label,headline,task,action=defs + visual_items.append({'label':item_label,'headline':headline,'explanation':'','imageEvidenceId':eid,'frameInfo':'按原图完整展示,重点看主体、花型、配色、场景、文案层级','persuasionTask':task,'weakness':'','action':action,'evidenceIds':[eid]}) + detail_id='' + for i,url in enumerate((raw.get('images',{}) or {}).get('detail',[]) or [],1): + if re.search(r'(?:avatar|sns_logo|shopmanager|tps-\d{1,3}-\d{1,3}|logo)',_txt(url),re.I):continue + detail_id=f'IMG_DETAIL_{i:04d}';break + if detail_id: + visual_items.append({'label':'04 / 已核验详情图','headline':'商品详情页原图','explanation':'','imageEvidenceId':detail_id,'frameInfo':'完整展示','persuasionTask':'来源:商品详情描述容器','weakness':'','action':'图片文字与页面参数表逐项核对','evidenceIds':[detail_id]}) + positive=[] + for outcome in review_outcomes[:5]: + count=len(outcome.get('evidenceIds') or []) + positive.append({'label':f"{outcome.get('label')}:{count}/{review_count} 条验证",'countOrNote':str(count),'evidenceIds':outcome.get('evidenceIds') or []}) + first_positive=(top[0] if top else {'topic':'有效评论','mentions':review_count}) + quote1,qid1=_review_ref(raw,lambda text:any(k in text for k in ('好看','漂亮','颜色','图案','花色'))) + quote2,qid2=_review_ref(raw,lambda text:any(k in text for k in ('舒服','柔软','亲肤','手感'))) + stages=[ + {'key':'see','stageNo':'01','label':'看到商品','headline':f"{first_positive.get('topic')}是当前最强评论信号",'userExpectation':'看清商品与花型','positiveConfirmation':f"{first_positive.get('mentions',0)} 条有效评论提及{first_positive.get('topic')}",'friction':'','businessImpact':'首图完整展示商品与花型','quotes':[{'type':'评论','text':quote1,'evidenceId':qid1}] if quote1 else [],'evidenceIds':[qid1] if qid1 else []}, + {'key':'choose','stageNo':'02','label':'选择规格','headline':'规格必须对应实际床型与套件 BOM','userExpectation':'一次看清床型、件数和各部件尺寸','positiveConfirmation':_txt(size_attr.get('value')) if size_attr else '','friction':(f"1 条评论明确出现{issue['label']}" if issue else ''),'businessImpact':'页面、样品与包装统一规格口径','quotes':[{'type':'评论','text':neg_text,'evidenceId':neg_id}] if neg_id else [],'evidenceIds':[x for x in (size_attr_id,neg_id) if x]}, + {'key':'use','stageNo':'03','label':'实际使用','headline':'触感反馈已形成明确正向信号','userExpectation':'柔软、亲肤、睡感舒适','positiveConfirmation':next((f"{x.get('mentions')} 条有效评论提及{x.get('topic')}" for x in topics if x.get('topic')=='面料/触感'),''),'friction':'','businessImpact':'材质页参数与评论原话并列证明','quotes':[{'type':'评论','text':quote2,'evidenceId':qid2}] if quote2 else [],'evidenceIds':[qid2] if qid2 else []}, + ] + param_facts=_home_textile_parameter_facts(raw) + material_value=_txt(material.get('value')) if material else '' + size_value=_txt(size_attr.get('value')) if size_attr else '' + plan_specs=[] + if material_value:plan_specs.append({'label':'材质','value':material_value}) + if thread:plan_specs.append({'label':'面料支数','value':thread.get('value')}) + if size_value:plan_specs.append({'label':'SKU','value':size_value}) + if craft:plan_specs.append({'label':'工艺','value':craft.get('value')}) + return _apply_selling_points({ + 'reportSummary':{'title':f'{label}产品分析报告','subtitle':';'.join(sales_bits),'verdict':'保留已验证体验,优先核对尺寸、成本和页面一致性','verdictDetail':'只使用已采页面参数与实际评论。','monitorSummary':''}, + 'platformOverview':build_platform_overview(raw,base), + 'ownerOverview':{'chapterTitle':f"{top_text},{issue['label']+'需先修正' if issue else '进入家纺样品验收'}",'chapterDeck':'','thesis':'','sampleSummary':f'有效评论 {review_count} 条,购买问答 {question_count} 条','cards':owner_cards[:6]}, + 'visualCommerce':{'chapterTitle':'完整展示商品,再依次解释花型、材质与规格','chapterDeck':'','items':visual_items}, + 'customerExperience':{'chapterTitle':','.join(customer_results)+(f";1 条明确出现{issue['label']}" if issue else ''),'chapterDeck':'','stats':[{'label':'有效评论','value':str(review_count),'note':''},{'label':'购买问答','value':str(question_count),'note':''},{'label':'有效参数','value':str(len(attrs)),'note':''}], 'journeyTitle':'从看到商品到首洗:只呈现评论已验证结果','journeyDeck':'','stages':stages,'buyerEvidence':{'title':'评论原图与对应评价','items':_buyer_evidence(raw)},'signals':{'title':'评论结果与购买前关注点','positive':positive,'questions':[{'label':f'采集到 {question_count} 条购买问答','countOrNote':str(question_count),'evidenceIds':[]} ] if question_count else [],'risks':[{'label':concise_fact(neg_text,44),'countOrNote':'1 条明确文本','evidenceIds':[neg_id]}] if neg_id else []}}, + 'productExperience':{'chapterTitle':'家纺开品底板:BOM、材质、尺寸与首洗逐项过门','chapterDeck':'','parameterFacts':param_facts,'thesis':'','thesisDetail':'','gates':_home_textile_gates(raw,issue)}, + 'newProductPlans':{'chapterTitle':('只做一个方向:保留已验证体验,先修正'+issue['label'] if issue else '只做一个方向:保留已验证体验,先完成家纺验收'),'chapterDeck':'','plans':[{'type':'主销款','name':f'{label}规格校准款','positioning':('沿用现有评论确认的花型与触感表达,优先修正'+issue['label'] if issue else '沿用现有评论确认的体验表达,先完成家纺样品验收'),'sellingPoints':_derive_next_selling_points(raw,base,issue),'specs':plan_specs,'manufacturingGate':{'title':'制造闸门','content':'核对套件 BOM、材质标签与各床型铺装;完成首洗、缝制填充、气味包装验收'},'visualCommerce':{'title':'首图与商详','content':'首图完整展示商品;后续图依次说明花型材质、套件清单、床型尺寸和洗护证据'},'evidenceIds':([neg_id] if neg_id else [])+attr_refs}], 'commercialSummary':{'title':'从当前商品保留什么','thesis':'只复用评论已确认的体验','keep':top_text,'fix':(issue['label'] if issue else '家纺样品与页面一致性'),'prove':'BOM、样品、标签、包装、页面五处一致'}}, + 'validationLoop':{'chapterTitle':'六道家纺验收门决定上架、返修或停止','chapterDeck':'','rows':_home_textile_validation_rows(raw)} + }, raw, base) + +def concise_fact(value,limit=48): + text=re.sub(r'\s+',' ',_txt(value)).strip() + return text if len(text)<=limit else text[:limit].rstrip(',。; ')+'…' + +REPORT_LANGUAGE_REPLACEMENTS=( + ('客户为什么购买','评论已验证体验'),('用户为什么购买','评论已验证体验'),('用户为什么买','评论已验证体验'), + ('成交原因','现有证据'),('决定成交','影响页面理解'),('推动选择','提供选择信息'), + ('提升转化','验证页面效果'),('降低退货','验证售后结果'),('省心型大众爆款','当前高销量商品'),('爆款','当前商品'), + ('建议立项验证','进入样品验证'),('有条件立项验证','先补齐验证项'),('暂不建议立项','暂停新增样品动作'), + ('立项判断','产品分析结论'),('立项','样品验证'),('不成立','需修正'),('成立','已确认'),('诊断报告','产品分析报告'),('商品诊断','商品分析'), + ('经营结论','产品分析结论'),('经营判断','产品分析结论'),('经营总览','产品摘要'),('老板决策总览','产品摘要'), + ('经营与开品报告','产品分析报告'),('商品经营结论','商品分析结论'),('商品经营','商品产品'), + ('建议进入样品验证','进入样品验证'),('再做量产决策','再安排生产测算'),('不做量产决策','不输出生产规模判断'), + ('量产前','批量上架前'),('量产判断','生产规模判断'),('量产规模','生产规模'),('量产测算','生产测算'), + ('有条件样品验证','样品校准分析'), +) + +def _sanitize_report_language(value): + if isinstance(value,dict):return {k:_sanitize_report_language(v) for k,v in value.items()} + if isinstance(value,list):return [_sanitize_report_language(x) for x in value] + if not isinstance(value,str):return value + for source,target in REPORT_LANGUAGE_REPLACEMENTS:value=value.replace(source,target) + return value + +def _separate_qa_signals(exp): + customer=exp.get('customerExperience') or {};signals=customer.get('signals') or {} + questions=list(signals.get('questions') or []);risks=[] + for item in signals.get('risks') or []: + if not isinstance(item,dict):continue + refs=[x for x in item.get('evidenceIds',[]) or [] if isinstance(x,str)] + if refs and all(x.startswith('QA_') for x in refs): + moved=dict(item);moved['label']='购买前关注:'+_txt(item.get('label')).removeprefix('购买前关注:') + questions.append(moved) + else:risks.append(item) + signals['questions']=questions;signals['risks']=risks;customer['signals']=signals + for stage in customer.get('stages') or []: + if not isinstance(stage,dict):continue + refs=[x for x in stage.get('evidenceIds',[]) or [] if isinstance(x,str)] + quotes=[x for x in stage.get('quotes',[]) or [] if isinstance(x,dict)] + only_questions=bool(refs) and all(x.startswith('QA_') for x in refs) + if only_questions or (quotes and all(_txt(x.get('type')) in ('问答','购买问答') for x in quotes)): + if stage.get('friction') and not stage.get('userExpectation'):stage['userExpectation']=stage['friction'] + stage['friction']='' + return exp + +def _filter_evidence_items(values, valid_ids, image=False): + out=[] + for item in values or []: + if not isinstance(item,dict):continue + refs=[x for x in item.get('evidenceIds',[]) or [] if x in valid_ids] + image_id=item.get('imageEvidenceId') if image else '' + if image_id in valid_ids and image_id not in refs:refs.append(image_id) + if not refs:continue + clean=dict(item);clean['evidenceIds']=refs;out.append(clean) + return out + +def _evidence_backed_solution(exp, valid_ids): + exp=dict(exp or {}) + owner=exp.get('ownerOverview') or {};owner['cards']=_filter_evidence_items(owner.get('cards'),valid_ids);exp['ownerOverview']=owner + visual=exp.get('visualCommerce') or {};visual['items']=_filter_evidence_items(visual.get('items'),valid_ids,True);exp['visualCommerce']=visual + customer=exp.get('customerExperience') or {};customer['stages']=_filter_evidence_items(customer.get('stages'),valid_ids) + signals=customer.get('signals') or {} + for key in ('positive','questions','risks'):signals[key]=_filter_evidence_items(signals.get(key),valid_ids) + customer['signals']=signals + ci=customer.get('consumerInsights') or {} + ci['experienceSignals']=_filter_evidence_items(ci.get('experienceSignals'),valid_ids) + ci['productImplications']=_filter_evidence_items(ci.get('productImplications'),valid_ids) + customer['consumerInsights']=ci;exp['customerExperience']=customer + product=exp.get('productExperience') or {} + product['parameterFacts']=_filter_evidence_items(product.get('parameterFacts'),valid_ids) + product['gates']=_filter_evidence_items(product.get('gates'),valid_ids);exp['productExperience']=product + plans=exp.get('newProductPlans') or {};plans['plans']=_filter_evidence_items(plans.get('plans'),valid_ids)[:3];exp['newProductPlans']=plans + validation=exp.get('validationLoop') or {};rows=_filter_evidence_items(validation.get('rows'),valid_ids) + validation['rows']=[x for x in rows if _txt(x.get('owner')).strip() and _txt(x.get('owner')).strip()!='项目负责人'];exp['validationLoop']=validation + return exp + +def ensure_experience_solution(analysis): + raw=normalize(analysis.get('facts',{}) or {}) + analysis['facts']=raw + analysis['baseline']=baseline(raw) + analysis['evidenceLedger']=evidence_ledger(raw) + was_fallback=bool((analysis.get('meta') or {}).get('fallbackReport')) + analysis.setdefault('meta',{})['engineVersion']=ENGINE_VERSION + route=detect_category(raw) + analysis['meta']['industryTemplate']=route['key'] + analysis['meta']['categoryLabel']=route['label'] + analysis['meta']['categoryConfidence']=route['confidence'] + analysis['meta']['analysisDimensions']=route['dimensions'] + analysis['meta']['imageSourcePolicy']=(raw.get('images',{}).get('classification',{}) or {}).get('version') or 'legacy-review-only' + valid_ids={x.get('id') for x in analysis['evidenceLedger'] if x.get('id')} + exp=_evidence_backed_solution(analysis.get('experienceSolution') or {},valid_ids) + if not _arr((exp.get('newProductPlans') or {}).get('plans')) and _arr((analysis.get('launchPlans') or {}).get('plans')): + exp.setdefault('newProductPlans',{})['plans']=_filter_evidence_items((analysis.get('launchPlans') or {}).get('plans'),valid_ids)[:3] + if (analysis.get('launchPlans') or {}).get('professionalOpinion'): + exp['newProductPlans']['professionalOpinion']=(analysis.get('launchPlans') or {}).get('professionalOpinion') + exp=_separate_qa_signals(exp) + exp=_sanitize_report_language(_apply_selling_points(exp,raw,analysis['baseline'])) + analysis['experienceSolution']=exp + analysis['meta'].pop('fallbackReport',None) + analysis['meta']['fallbackReport']=False + if was_fallback: + analysis['meta']['reportReady']=False + analysis['meta']['reportBlockedReason']='历史分析标记为兜底结果,本版本拒绝重新渲染为正式报告。' + return analysis + validation=core_solution_diagnostics(exp) + analysis['meta']['reportValidation']=validation + if not validation['ok']: + analysis['meta']['reportReady']=False + analysis['meta']['reportBlockedReason']='模型未生成完整且可追溯的电商商品分析结论,本次不输出兜底报告。未通过字段:'+', '.join(validation['missing']) + return analysis + analysis['meta']['reportReady']=True + analysis['meta'].pop('reportBlockedReason',None) + customer=exp.setdefault('customerExperience',{}) + customer['stats']=[{'label':'有效评论','value':str(analysis['baseline'].get('reviewCount',0))},{'label':'购买问答','value':str(analysis['baseline'].get('questionCount',0))},{'label':'有效参数','value':str(analysis['baseline'].get('attributeCount',0))}] + customer['buyerEvidence']={'title':'评论原图与对应评价','items':_buyer_evidence(raw)} + exp.pop('shopperDecision',None) + plans=exp.setdefault('newProductPlans',{}) + summary=exp.setdefault('reportSummary',{}) + summary['title']=_clean_product_title(re.sub(r'(床品|商品)商品经营',r'\1产品',_txt(summary.get('title')))) + analysis['experienceSolution']=exp + return analysis + +def evidence_ledger(raw): + ev=[] + def add(eid,etype,label,value,source='page',meta=None): + if value in (None,'',[],{}): return + ev.append({'id':eid,'type':etype,'label':label,'value':value,'source':source,'meta':meta or {}}) + p=raw.get('product',{}); s=raw.get('sales',{}) + for k,label in [('title','商品标题'),('shop','店铺'),('brand','品牌'),('category','类目'),('itemId','Item ID'),('skuId','SKU ID')]: add('P_'+k.upper(),'product',label,p.get(k),'page') + for k,label in [('currentPrice','当前采集价格'),('sold','销量公开值'),('cumulativeSales','累计销量'),('monthlySales','月销量'),('ranking','榜单/排名')]: add('S_'+k.upper(),'sales',label,s.get(k),'page') + for i,x in enumerate(raw.get('monitoring',[])[:30],1): add(f'MON_{i:03d}','monitoring','商品监测数据',x,'monitoring') + for i,x in enumerate(raw.get('sku',[])[:500],1): add(f'SKU_{i:04d}','sku','SKU',x,'page') + for i,x in enumerate(raw.get('attributes',[])[:500],1): add(f'ATTR_{i:04d}','attribute','页面属性',x,'page_claim') + for i,x in enumerate(raw.get('promotions',[])[:300],1): add(f'PROMO_{i:04d}','promotion','促销/服务',x,'page_claim') + for i,x in enumerate(raw.get('reviews',[]),1): add(f'REV_{i:06d}','review','消费者评价',x,'consumer') + for i,x in enumerate(raw.get('questions',[])[:2000],1): add(f'QA_{i:05d}','question','购买问答',x,'consumer') + images=raw.get('images',{}) or {};provenance=images.get('provenance',[]) or [] + for group in IMAGE_GROUPS: + for i,u in enumerate(images.get(group,[])[:1000],1): + records=[x for x in provenance if isinstance(x,dict) and x.get('group')==group and x.get('url')==u] + source=records[0].get('source') if records else '' + meta={'group':group,'source':source,'classification':(images.get('classification',{}) or {}).get('version')} + if group=='buyerShow': + meta['reviewEvidenceIds']=_uniq([x.get('reviewEvidenceId') for x in records if x.get('reviewEvidenceId')]) + add(f'IMG_{group.upper()}_{i:04d}','image',group,u,'verified_image',meta) + return ev + +def baseline(raw): + rev=raw.get('reviews',[]); buckets={'good':0,'mid':0,'bad':0,'unknown':0}; topic=Counter(); examples=defaultdict(list); sku_topic=defaultdict(Counter) + for x in rev: + try:s=float(x.get('rating')) + except:s=0 + if s>=4:buckets['good']+=1 + elif s==3:buckets['mid']+=1 + elif s>0:buckets['bad']+=1 + else:buckets['unknown']+=1 + c=x['content']; sku=x.get('sku') or '未识别SKU' + for t,keys in TOPICS.items(): + if any(k in c for k in keys): + topic[t]+=1; sku_topic[sku][t]+=1 + if len(examples[t])<8: examples[t].append(c[:220]) + collection=raw.get('collection') or {} + return {'reviewCount':len(rev),'questionCount':len(raw.get('questions',[])),'ratingBuckets':buckets, + 'topics':[{'topic':k,'mentions':v,'sampleRate':round(v/max(1,len(rev)),4),'examples':examples[k][:6]} for k,v in topic.most_common(24)], + 'skuTopicSignals':[{'sku':sku,'topics':[{'topic':k,'mentions':v} for k,v in cnt.most_common(8)]} for sku,cnt in sorted(sku_topic.items(),key=lambda kv:sum(kv[1].values()),reverse=True)[:30]], + 'imageCounts':{k:len(raw.get('images',{}).get(k,[]) or []) for k in ['main','detail','sku','buyerShow','all']}, + 'skuCount':len(raw.get('sku',[]) or []),'attributeCount':len(raw.get('attributes',[]) or []),'promotionCount':len(raw.get('promotions',[]) or []), + 'reviewCollectionComplete':bool(collection.get('reviewCollectionComplete')),'reviewStopReason':collection.get('reviewStopReason') or '', + 'publicReviewCount':collection.get('publicReviewCount') or '', 'reviewPagesVisited':collection.get('reviewPagesVisited') or 0} + +def compact_evidence(ledger, allowed_types=None, limit=400): + xs=[x for x in ledger if not allowed_types or x['type'] in allowed_types] + return xs[:limit] + +def require_refs(obj, valid): + if isinstance(obj,dict): + if isinstance(obj.get('evidenceIds'),list): + obj['evidenceIds']=[x for x in obj['evidenceIds'] if x in valid] + if not obj['evidenceIds']: + obj['insufficientEvidence']=True + obj.setdefault('missingEvidence',['未找到可核验引用']) + try: obj['confidence']=min(float(obj.get('confidence',0.4)),0.45) + except: obj['confidence']=0.35 + for v in obj.values(): require_refs(v,valid) + elif isinstance(obj,list): + for v in obj: require_refs(v,valid) + return obj + +def call(prompt,payload,max_tokens,images=None): + # 统一模板入口:业务任务只提供 task 文本和变量值,接入其他后端时可替换 registry。 + try: + task = build_task_prompt(prompt, tuple(payload.keys()) if isinstance(payload, dict) else ('evidence',)) + user = task+'\n'+STYLE_RULES+'\nACTUAL_EVIDENCE:\n'+json.dumps(payload,ensure_ascii=False) + return chat_json(build_system_prompt(SYS), user, images=images, max_tokens=max_tokens),None + except Exception as e:return {},str(e) + +def batch_review_analysis(raw, valid, progress=lambda *a:None, enabled=True): + reviews=raw.get('reviews',[]) + if not enabled or not reviews: return {'batches':[],'aggregate':{}}, None + BATCH=160 + summaries=[]; errors=[] + total=max(1,(len(reviews)+BATCH-1)//BATCH) + for bi,start in enumerate(range(0,len(reviews),BATCH),1): + chunk=[] + for j,r in enumerate(reviews[start:start+BATCH],start+1): + chunk.append({'evidenceId':f'REV_{j:06d}','content':r.get('content','')[:520],'appendContent':r.get('appendContent','')[:260],'rating':r.get('rating'),'sku':r.get('sku'),'date':r.get('date')}) + prompt='''任务|评论批次研究。不要做泛泛总结,只抽取能影响开品的消费者事实。输出: +{"batchFindings":[{"finding":"","type":"purchase_driver|satisfaction|pain|expectation_gap|prepurchase_concern|sku_signal|usage_scene","businessMeaning":"","evidenceIds":[],"confidence":0}],"representativeQuotes":[{"text":"原文","evidenceId":"REV_000001","role":""}]} +要求:每条 finding 最多引用 8 个真实评论ID;不要估算全量次数。''' + z,e=call(prompt,{'batchIndex':bi,'reviews':chunk},5200) + if e: errors.append(f'batch {bi}: {e}') + summaries.append(require_refs(z,valid)) + pct=78+int(8*bi/total) + progress('review',f'processing {bi}/{total}',pct) + prompt='''任务|全量评论商业聚合。输入包含:程序对全部评论的统计 + 每批模型结论。请形成能直接影响商品定义、页面表达与下一款方案的消费者洞察,不得做评论罗列。每一项必须回答‘这说明什么、对商品意味着什么、下一款如何处理’;不得把批次出现次数当全量次数,只有 programStats 中的次数可以写为数量。 +输出: +{ +"consumerThesis":{"statement":"","businessMeaning":"","evidenceIds":[],"confidence":0}, +"purchaseDrivers":[{"driver":"","whatUserIsBuying":"","commercialValue":"","productAction":"","evidenceIds":[],"confidence":0}], +"satisfactionDrivers":[{"driver":"","commercialValue":"","keepAction":"","evidenceIds":[],"confidence":0}], +"painPoints":[{"problem":"","severity":"high|medium|low","businessImpact":"","productAction":"","evidenceIds":[],"confidence":0}], +"expectationGaps":[{"expected":"","actual":"","commercialRisk":"","productOpportunity":"","evidenceIds":[],"confidence":0}], +"prePurchaseConcerns":[{"concern":"","conversionImpact":"","pageAction":"","evidenceIds":[],"confidence":0}], +"skuSignals":[{"sku":"","signal":"","decision":"","evidenceIds":[],"confidence":0}], +"verbatimQuotes":[{"text":"原文","evidenceId":"REV_000001","role":""}] +}''' + payload={'programStats':baseline(raw),'batchSummaries':summaries} + z,e=call(prompt,payload,9000) + if e: errors.append('aggregate: '+e) + return {'batches':summaries,'aggregate':require_refs(z,valid)}, '; '.join(errors) if errors else None + +def question_analysis(raw, valid, enabled=True): + qs=raw.get('questions',[]) + if not enabled or not qs:return {},None + payload=[] + for i,q in enumerate(qs[:1200],1): payload.append({'evidenceId':f'QA_{i:05d}','question':q.get('question','')[:700],'answer':q.get('answer','')[:700]}) + prompt='''任务|购买前疑虑研究。这里全部是问大家/购买问答,不是差评。回答用户在下单前担心什么、页面哪里没有解释清楚、哪些疑虑被真实买后评论验证。不得把提问本身当产品缺陷。 +输出:{"prePurchaseThesis":{"statement":"","commercialMeaning":"","evidenceIds":[],"confidence":0},"concerns":[{"concern":"","whyItBlocksPurchase":"","isProductRiskOrInfoGap":"product_risk|information_gap|unknown","commercialAction":"","evidenceIds":[],"confidence":0}],"validatedByReviews":[{"questionConcern":"","reviewReality":"","decision":"","evidenceIds":[],"confidence":0}],"unansweredQuestions":[{"question":"","pageFix":"","evidenceIds":[],"confidence":0}]} +只引用真实 QA_/REV_ ID。''' + z,e=call(prompt,{'questions':payload,'reviewStats':baseline(raw)},7200) + return require_refs(z,valid),e + + +def _evidence_backed_launch_fallback(raw, valid_ids): + """V9.2: 只用已有证据构造最小新品方向,避免证据充足时整章为空。""" + title=_txt((raw.get('product') or {}).get('title')).strip() or '当前商品' + refs=[] + for eid in ('P_TITLE','S_CURRENTPRICE','S_SOLD'): + if eid in valid_ids:refs.append(eid) + attr_refs=sorted([x for x in valid_ids if x.startswith('ATTR_')])[:4] + review_refs=sorted([x for x in valid_ids if x.startswith('REV_')])[:2] + image_refs=sorted([x for x in valid_ids if x.startswith('IMG_MAIN_')])[:2] + refs=_uniq(refs+attr_refs+review_refs+image_refs) + if not refs:return {'strategyMode':'mixed','professionalOpinion':'','plans':[]} + changes=[] + if attr_refs:changes.append({'action':'优先核对并统一当前商品的规格、材质与页面口径','evidenceIds':attr_refs[:2]}) + if review_refs:changes.append({'action':'把消费者已验证体验与差异反馈转成下一款产品选择说明','evidenceIds':review_refs}) + return { + 'strategyMode':'mixed', + 'professionalOpinion':'保留当前商品已有证据支持的产品逻辑,下一款优先把规格选择、体验表达和页面证明做得更清楚。', + 'plans':[{ + 'type':'现有基础优化','name':_clean_product_title(title)+' 优化款', + 'whyThisPlan':'基于当前商品参数、图片与消费者反馈做有限优化,不引入无证据的新材料或市场结论。', + 'visualReferenceImageIds':image_refs,'productChanges':changes, + 'skuStrategy':'先按真实结构化SKU与参数建立选择层级;缺失项待补采后再细化。', + 'mainImagePlan':['首图保持商品全貌与核心场景','后续图片依次解释规格、材质/配置与证明信息'], + 'detailPagePlan':['按主图提出的信息逐项解释和证明','消费者反馈只作为购后体验证据'], + 'validationItems':['样品、SKU、参数、图片与页面文字口径一致'], + 'evidenceIds':refs,'confidence':0.55 + }] + } + +def analyze(raw, progress=lambda *a:None): + raw=dict(raw or {}); model_available=raw.pop('_model_available',None) + model_configured=configured(); use_ai=model_configured if model_available is None else bool(model_available and model_configured) + raw=normalize(raw); base=baseline(raw); ledger=evidence_ledger(raw); valid={x['id'] for x in ledger} + route=detect_category(raw) + out={'meta':{'modelConfigured':model_configured,'modelUsed':use_ai,'engineVersion':ENGINE_VERSION,'actualOnly':True,'dynamicReport':True,'industryTemplate':route['key'],'categoryLabel':route['label'],'categoryConfidence':route['confidence'],'analysisDimensions':route['dimensions']},'facts':raw,'baseline':base,'evidenceLedger':ledger,'modelErrors':{}} + + progress('evidence','processing',28) + limitations=[] + if not base['reviewCollectionComplete']: + limitations.append('评论采集未确认到达平台可访问末页;当前报告使用本次实际获取的全部评论,不宣称为平台全量。') + if base['questionCount']==0: limitations.append('未采集到问大家,无法判断下单前主要疑虑。') + if not raw.get('sales',{}).get('sold') and not raw.get('sales',{}).get('cumulativeSales'): limitations.append('未采集到销量公开值,不能判断经营表现。') + out['dataQuality']={'grade':'A' if base['reviewCollectionComplete'] and base['reviewCount']>=100 and base['imageCounts']['main']>=3 else 'B+' if base['reviewCount']>=100 else 'B' if base['reviewCount']>=30 else 'C', + 'reviewSample':base['reviewCount'],'reviewCollectionComplete':base['reviewCollectionComplete'],'reviewStopReason':base['reviewStopReason'], + 'publicReviewCount':base['publicReviewCount'],'questionSample':base['questionCount'],'images':base['imageCounts'],'skuCount':base['skuCount'],'attributeCount':base['attributeCount'],'promotionCount':base['promotionCount'],'knownLimitations':limitations} + progress('evidence','done',32) + + # 先做报告策划:决定这款商品最需要讲清楚什么,而不是先套章节。 + progress('planner','processing',34) + if use_ai: + prompt='''任务0|报告策划。先判断“这款商品最需要被讲清楚的产品问题是什么”,再决定报告内容和顺序。输出: +{ +"coreStory":"一句具体产品分析主线", +"decisionQuestion":"运营者最需要看清的一个产品问题", +"primaryQuestions":["3-6个具体问题"], +"sectionPlan":[{"module":"executive|facts|positioning|fashion|main_visual|title_promo|detail|consumer|qa|pain|sku|competition|opportunity|plans|gates","title":"必须是针对该商品的结果型标题","priority":100,"reason":"为什么需要出现"}], +"deemphasize":[{"module":"","reason":"为什么当前证据下不重点展开"}] +} +要求:executive/facts/plans/gates必须出现;其他模块按实际证据和商业价值动态排序。标题禁止使用空泛的“消费者分析”“视觉分析”。''' + z,e=call(prompt,{'baseline':base,'facts':compact_evidence(ledger,{'product','sales','attribute','promotion','sku'},260)},5000) + if e: out['modelErrors']['planner']=e + out['reportPlan']=require_refs(z,valid) + else: out['reportPlan']={} + progress('planner','done',38) + + progress('product','processing',40) + if use_ai: + prompt='''任务A|商品价值结构。输出具体到产品定义与运营动作: +{"positioning":{"statement":"","decisionImpact":"","evidenceIds":[],"confidence":0},"valuePillars":[{"name":"","customerValue":"","businessMeaning":"","mustKeep":"","evidenceIds":[],"confidence":0}],"claimVsProof":[{"claim":"","status":"consumer_supported|page_claim_only|conflicted|not_enough_evidence","commercialRisk":"","evidenceIds":[],"confidence":0}],"positioningRisks":[{"risk":"","businessImpact":"","action":"","evidenceIds":[],"confidence":0}]}''' + z,e=call(prompt,compact_evidence(ledger,{'product','sales','sku','attribute','promotion','review'},320),6200) + if e:out['modelErrors']['product']=e + out['productDecision']=require_refs(z,valid) + else: out['productDecision']={} + progress('product','done',48) + + progress('fashion','processing',49) + fashion_imgs=(raw.get('images',{}).get('main',[])[:6]+raw.get('images',{}).get('detail',[])[:6]) + if use_ai and fashion_imgs: + prompt='''任务A2|时尚与设计资产判断。只分析实际图片与页面文字中的色彩、花型/纹样、材质视觉、风格语言、细节工艺和人群审美表达。禁止把当前商品风格说成市场趋势,因为没有外部趋势数据。 +输出:{"fashionThesis":{"statement":"","commercialMeaning":"","evidenceIds":[],"confidence":0},"designAssets":[{"asset":"","observation":"","commercialValue":"","developmentAction":"","evidenceIds":[],"confidence":0}],"styleSignals":[{"signal":"","whatItMeansForProduct":"","evidenceIds":[],"confidence":0}],"fashionRisks":[{"risk":"","why":"","action":"","evidenceIds":[],"confidence":0}],"trendBoundary":"没有外部趋势/竞品数据时不能判断流行度与生命周期"}''' + z,e=call(prompt,{'evidence':compact_evidence(ledger,{'product','sku','attribute','image'},280)},7200,fashion_imgs) + if e:out['modelErrors']['fashion']=e + out['fashionDecision']=require_refs(z,valid) + else: out['fashionDecision']={} + progress('fashion','done',50) + + progress('visual','processing',51) + main_imgs=raw.get('images',{}).get('main',[])[:8] + if use_ai and main_imgs: + prompt='''任务B|主图商业信息提炼。目标不是解释图片,而是让专业用户一眼看到“这张图在卖什么”。逐张提炼图片中可核验的商品信息标签,再给一个短动作。 +输出: +{"visualThesis":{"professionalOpinion":"<=45字","evidenceIds":[]},"coreClaims":[],"imageRoles":[{"imageEvidenceId":"IMG_MAIN_0001","imageRole":"<=8字","visualSignals":["2-3个,每项<=10字"],"businessMeaning":"<=22字","nextAction":"<=18字","evidenceIds":[],"confidence":0}],"evidenceDetail":[{"imageEvidenceId":"IMG_MAIN_0001","composition":"","textInfo":"","productSubject":"","patternColor":"","evidenceIds":[]}]} +visualSignals必须来自图片可见文字、商品、场景或已确认参数,例如“40支新疆棉/六件套/免费仓储”,不能写抽象评价。 +''' + z,e=call(prompt,{'evidence':compact_evidence(ledger,{'product','sales','image','attribute'},220)},6800,main_imgs) + if e:out['modelErrors']['visual']=e + out['visualDecision']=require_refs(z,valid) + else: out['visualDecision']={} + progress('visual','done',57) + + progress('merchandising','processing',58) + if use_ai: + prompt='''任务C|标题表达压缩。只回答三件事:当前已经表达什么、还应强化什么、建议怎么改。不要输出解释型长段落。 +输出: +{"titleAnalysis":{"professionalOpinion":"<=45字","currentExpression":[{"value":"<=10字","evidenceIds":[]}],"reinforce":[{"topic":"<=10字","evidenceIds":[]}],"recommendedActions":[{"action":"<=22字","evidenceIds":[]}],"recommendedTitles":[]},"promotionAnalysis":{}} +''' + z,e=call(prompt,{'evidence':compact_evidence(ledger,{'product','sales','promotion','sku','attribute'},300)},7200) + if e:out['modelErrors']['merchandising']=e + out['merchandisingDecision']=require_refs(z,valid) + else: out['merchandisingDecision']={} + progress('merchandising','done',64) + + progress('detail','processing',65) + detail_imgs=raw.get('images',{}).get('detail',[])[:20] + if use_ai and detail_imgs: + prompt='''任务D|详情页证明信息提炼。按4-6个信息任务组选代表图,不按图片流水账。每组只提炼2-4个图片信息标签和1条专业动作。 +输出: +{"detailThesis":{"professionalOpinion":"<=45字","evidenceIds":[]},"contentGroups":[{"type":"品牌服务|套件组成|材质品质|产品功能|使用便利|花型选择|用户验证|其他","representativeImageId":"IMG_DETAIL_0001","visualSignals":["2-3项,每项<=10字"],"businessMeaning":"<=22字","nextAction":"<=18字","imageEvidenceIds":[],"evidenceIds":[]}],"continuityChecks":[{"claim":"<=24字","mainImageEvidenceIds":[],"detailEvidenceIds":[],"status":"closed_loop|detail_missing|main_missing|conflict|review_pending","nextAction":"<=24字","evidenceIds":[]}],"pageSequence":[],"evidenceDetail":[]} +''' + z,e=call(prompt,{'visualDecision':out.get('visualDecision',{}),'evidence':compact_evidence(ledger,{'product','attribute','promotion','image'},360)},8200,detail_imgs) + if e:out['modelErrors']['detail']=e + out['detailDecision']=require_refs(z,valid) + else: out['detailDecision']={} + progress('detail','done',72) + + progress('review','processing',74) + rb,e=batch_review_analysis(raw,valid,progress,enabled=use_ai) + if e: out['modelErrors']['review']=e + out['reviewBatchAnalysis']=rb.get('batches',[]) + out['consumerResearch']=rb.get('aggregate',{}) + progress('review','done',86) + + progress('qa','processing',87) + qz,qe=question_analysis(raw,valid,enabled=use_ai) + if qe: out['modelErrors']['qa']=qe + out['questionResearch']=qz or {} + progress('qa','done',89) + + progress('competition','processing',90) + out['competitionDecision']={'status':'missing_competitor_dataset','statement':'当前只有单品证据,不输出相对竞品的价格、设计、材质或营销优劣。','commercialMeaning':'单品报告只分析当前商品的页面表达、参数定义和购后反馈。','requiredCompetitorSet':['同核心搜索词','同目标场景','同主销规格','同可比价格带'],'compareDimensions':['当前价/主销价','销量/榜单公开值','标题槽位','主图内容结构','材质/工艺宣称','SKU结构','促销服务','评论痛点','详情证明方式']} + progress('competition','done',91) + + progress('strategy','processing',92) + if use_ai: + context={'reportPlan':out.get('reportPlan',{}),'dataQuality':out['dataQuality'],'baseline':base,'productDecision':out.get('productDecision',{}),'visualDecision':out.get('visualDecision',{}),'merchandisingDecision':out.get('merchandisingDecision',{}),'detailDecision':out.get('detailDecision',{}),'consumerResearch':out.get('consumerResearch',{}),'questionResearch':out.get('questionResearch',{}),'fashionDecision':out.get('fashionDecision',{}),'competitionDecision':out.get('competitionDecision',{}),'evidence':compact_evidence(ledger,{'product','sales','sku','attribute','promotion','review','question'},420)} + prompt='''任务E|老板决策摘要。不要写大段商业分析,只形成可供最终编排器使用的三个答案:为什么能卖、值得继承、下一款优先。 +输出: +{"executiveDecision":{"verdict":"<=45字","oneSentence":"<=55字","ifOnlyOneAction":"<=35字","evidenceIds":[]},"whyItSells":[{"mechanism":"<=20字","evidenceIds":[]}],"mustKeep":[{"item":"<=20字","evidenceIds":[]}],"mustFix":[{"item":"<=20字","fixAction":"<=28字","evidenceIds":[]}],"decisionGates":[]} +''' + z,e=call(prompt,context,9800) + if e:out['modelErrors']['strategy']=e + out['commercialDecision']=require_refs(z,valid) + else: out['commercialDecision']={} + progress('strategy','done',94) + + progress('plans','processing',95) + if use_ai: + context={'reportPlan':out.get('reportPlan',{}),'commercialDecision':out.get('commercialDecision',{}),'consumerResearch':out.get('consumerResearch',{}),'questionResearch':out.get('questionResearch',{}),'fashionDecision':out.get('fashionDecision',{}),'competitionDecision':out.get('competitionDecision',{}),'productDecision':out.get('productDecision',{}),'visualDecision':out.get('visualDecision',{}),'merchandisingDecision':out.get('merchandisingDecision',{}),'detailDecision':out.get('detailDecision',{}),'baseline':base} + prompt='''任务F|下一款方案。面向老板先做决策卡,再让专业用户展开细节。每套默认只保留:来源等级、为什么做、产品动作、页面动作、验证项。 +至少1套,证据充分2-3套。 +来源等级: +- 证据驱动优化:直接来自当前商品页面/参数/评论; +- 反馈驱动升级:直接来自消费者体验差异/购前关注; +- 探索性方向:设计创新或扩展想法,必须明确标记,不能当作已验证结论。 +输出: +{"professionalOpinion":"<=45字","plans":[{"sourceType":"证据驱动优化|反馈驱动升级|探索性方向","type":"现有基础优化|强化体验区分|强化设计选择|其他","name":"<=18字","whyThisPlan":"<=32字","visualReferenceImageIds":[],"productAction":"<=28字","pageAction":"<=28字","validation":"<=20字","evidenceIds":[],"confidence":0}],"nextValidation":[]} +''' + z,e=call(prompt,context,9800) + if e:out['modelErrors']['plans']=e + out['launchPlans']=require_refs(z,valid) + if not _arr((out.get('launchPlans') or {}).get('plans')): + out['launchPlans']=_evidence_backed_launch_fallback(raw,valid) + else: out['launchPlans']={} + progress('plans','done',98) + + # 最终编辑层:把前面的事实与分析统一收敛为“全方位产品体验解决方案”。 + progress('editor','processing',98) + if use_ai: + context={ + 'monitoring':raw.get('monitoring',[]), 'baseline':base, 'categoryRoute':route, + 'productDecision':out.get('productDecision',{}), 'fashionDecision':out.get('fashionDecision',{}), + 'visualDecision':out.get('visualDecision',{}), 'merchandisingDecision':out.get('merchandisingDecision',{}), + 'detailDecision':out.get('detailDecision',{}), 'consumerResearch':out.get('consumerResearch',{}), + 'questionResearch':out.get('questionResearch',{}), 'commercialDecision':out.get('commercialDecision',{}), + 'launchPlans':out.get('launchPlans',{}), + 'evidence':compact_evidence(ledger,{'monitoring','product','sales','sku','attribute','promotion','review','question','image'},650) + } + prompt=FINAL_REPORT_PROMPT + imgs=(raw.get('images',{}).get('main',[])[:8]+raw.get('images',{}).get('sku',[])[:8]+raw.get('images',{}).get('detail',[])[:16]+raw.get('images',{}).get('buyerShow',[])[:8]) + z,e=call(prompt,context,12000,imgs) + if e: out['modelErrors']['experienceSolution']=e + out['experienceSolution']=require_refs(z,valid) + else: + out['experienceSolution']={} + result=ensure_experience_solution(out) + if use_ai and not result['meta'].get('reportReady'): + first_validation=result['meta'].get('reportValidation') or core_solution_diagnostics(result.get('experienceSolution')) + progress('editor','processing repair',99) + repair_payload={ + 'failedChecks':first_validation.get('checks',[]), + 'draft':result.get('experienceSolution') or {}, + 'evidence':compact_evidence(ledger,{'monitoring','product','sales','sku','attribute','promotion','review','question','image'},650), + } + repaired,repair_error=call(FINAL_REPORT_REPAIR_PROMPT,repair_payload,12000,imgs) + if repair_error:out['modelErrors']['experienceSolutionRepair']=repair_error + if repaired: + out['experienceSolution']=require_refs(repaired,valid) + result=ensure_experience_solution(out) + result['meta']['modelRepairAttempted']=True + result['meta']['initialReportValidation']=first_validation + if use_ai and not result['meta'].get('reportReady'): + before=result['meta'].get('reportValidation') or core_solution_diagnostics(result.get('experienceSolution')) + out['experienceSolution']=repair_core_solution_with_evidence(result.get('experienceSolution'),raw,base) + out['meta'].update(result.get('meta') or {}) + out['meta']['evidenceStructuralRepairApplied']=True + out['meta']['preStructuralRepairValidation']=before + result=ensure_experience_solution(out) + progress('editor','done' if result['meta'].get('reportReady') else 'failed',99) + return result diff --git a/server/cacert.pem b/server/cacert.pem new file mode 100644 index 0000000..6faad41 --- /dev/null +++ b/server/cacert.pem @@ -0,0 +1,3892 @@ + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Label: "TunTrust Root CA" +# Serial: 108534058042236574382096126452369648152337120275 +# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 +# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb +# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS RSA Root CA 2021" +# Serial: 76817823531813593706434026085292783742 +# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 +# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d +# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS ECC Root CA 2021" +# Serial: 137515985548005187474074462014555733966 +# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 +# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 +# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 1977337328857672817 +# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 +# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe +# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus ECC Root CA" +# Serial: 630369271402956006249506845124680065938238527194 +# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 +# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 +# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus Root CA" +# Serial: 387574501246983434957692974888460947164905180485 +# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc +# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 +# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Label: "HiPKI Root CA - G1" +# Serial: 60966262342023497858655262305426234976 +# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 +# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 +# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 159662223612894884239637590694 +# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc +# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 +# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 159662495401136852707857743206 +# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 +# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 +# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj +# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj +# Label: "Telia Root CA v2" +# Serial: 7288924052977061235122729490515358 +# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 +# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd +# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 1 2020" +# Serial: 165870826978392376648679885835942448534 +# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed +# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 +# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 1 2020" +# Serial: 126288379621884218666039612629459926992 +# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e +# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 +# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS ECC P384 Root G5" +# Serial: 13129116028163249804115411775095713523 +# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed +# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee +# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS RSA4096 Root G5" +# Serial: 11930366277458970227240571539258396554 +# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 +# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 +# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root R1 O=Certainly +# Subject: CN=Certainly Root R1 O=Certainly +# Label: "Certainly Root R1" +# Serial: 188833316161142517227353805653483829216 +# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 +# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af +# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root E1 O=Certainly +# Subject: CN=Certainly Root E1 O=Certainly +# Label: "Certainly Root E1" +# Serial: 8168531406727139161245376702891150584 +# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 +# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b +# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication ECC RootCA1" +# Serial: 15446673492073852651 +# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 +# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 +# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA1" +# Serial: 113562791157148395269083148143378328608 +# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 +# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a +# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA2" +# Serial: 58605626836079930195615843123109055211 +# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c +# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 +# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root E46" +# Serial: 88989738453351742415770396670917916916 +# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01 +# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a +# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83 +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root R46" +# Serial: 156256931880233212765902055439220583700 +# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5 +# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38 +# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06 +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS RSA Root CA 2022" +# Serial: 148535279242832292258835760425842727825 +# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da +# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca +# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS ECC Root CA 2022" +# Serial: 26605119622390491762507526719404364228 +# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5 +# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39 +# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43 +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA ECC TLS 2021" +# Serial: 81873346711060652204712539181482831616 +# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8 +# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd +# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8 +-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w +LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w +CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 +MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF +Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X +tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 +AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 +KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD +aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu +CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo +9H1/IISpQuQo +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA RSA TLS 2021" +# Serial: 111436099570196163832749341232207667876 +# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2 +# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48 +# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM +MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx +MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 +MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD +QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z +4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv +Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ +kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs +GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln +nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh +3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD +0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy +geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 +ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB +c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI +pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs +o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ +qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw +xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM +rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 +AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR +0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY +o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 +dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE +oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. +# Label: "TrustAsia Global Root CA G3" +# Serial: 576386314500428537169965010905813481816650257167 +# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04 +# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7 +# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08 +-----BEGIN CERTIFICATE----- +MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM +BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe +Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw +IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU +cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS +T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK +AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1 +nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep +qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA +yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs +hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX +zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv +kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT +f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA +uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB +o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih +MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E +BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4 +wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2 +XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1 +JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j +ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV +VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx +xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on +AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d +7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj +gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV ++Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo +FGWsJwt0ivKH +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. +# Label: "TrustAsia Global Root CA G4" +# Serial: 451799571007117016466790293371524403291602933463 +# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb +# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a +# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c +-----BEGIN CERTIFICATE----- +MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw +WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y +MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD +VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz +dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx +s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw +LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij +YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD +pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE +AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR +UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj +/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== +-----END CERTIFICATE----- + +# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH +# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH +# Label: "Telekom Security TLS ECC Root 2020" +# Serial: 72082518505882327255703894282316633856 +# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd +# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec +# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1 +-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw +CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH +bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw +MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx +JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE +AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O +tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP +f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA +MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di +z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn +27iQ7t0l +-----END CERTIFICATE----- + +# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH +# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH +# Label: "Telekom Security TLS RSA Root 2023" +# Serial: 44676229530606711399881795178081572759 +# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2 +# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93 +# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj +MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 +eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy +MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC +REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG +A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9 +cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV +cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA +U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6 +Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug +BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy +8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J +co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg +8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8 +rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12 +mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg ++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX +gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 +p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ +pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm +9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw +M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd +GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+ +CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t +xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+ +w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK +L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj +X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q +ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm +dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA CYBER Root CA" +# Serial: 85076849864375384482682434040119489222 +# MD5 Fingerprint: 0b:33:a0:97:52:95:d4:a9:fd:bb:db:6e:a3:55:5b:51 +# SHA1 Fingerprint: f6:b1:1c:1a:83:38:e9:7b:db:b3:a8:c8:33:24:e0:2d:9c:7f:26:66 +# SHA256 Fingerprint: 3f:63:bb:28:14:be:17:4e:c8:b6:43:9c:f0:8d:6d:56:f0:b7:c4:05:88:3a:56:48:a3:34:42:4d:6b:3e:c5:58 +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ +MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 +IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5 +WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FO +LUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1sTs6P +40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxF +avcokPFhV8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/ +34bKS1PE2Y2yHer43CdTo0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684i +JkXXYJndzk834H/nY62wuFm40AZoNWDTNq5xQwTxaWV4fPMf88oon1oglWa0zbfu +j3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK/c/WMw+f+5eesRycnupf +Xtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkHIuNZW0CP +2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDA +S9TMfAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDA +oS/xUgXJP+92ZuJF2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzC +kHDXShi8fgGwsOsVHkQGzaRP6AzRwyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW +5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83QOGt4A1WNzAd +BgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB +AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0t +tGlTITVX1olNc79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn +68xDiBaiA9a5F/gZbG0jAn/xX9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNn +TKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDRIG4kqIQnoVesqlVYL9zZyvpoBJ7t +RCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq/p1hvIbZv97Tujqx +f36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0RFxbI +Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz +8ppy6rBePm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4 +NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX +xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6 +t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign Root CA12 O=Cybertrust Japan Co., Ltd. +# Subject: CN=SecureSign Root CA12 O=Cybertrust Japan Co., Ltd. +# Label: "SecureSign Root CA12" +# Serial: 587887345431707215246142177076162061960426065942 +# MD5 Fingerprint: c6:89:ca:64:42:9b:62:08:49:0b:1e:7f:e9:07:3d:e8 +# SHA1 Fingerprint: 7a:22:1e:3d:de:1b:06:ac:9e:c8:47:70:16:8e:3c:e5:f7:6b:06:f4 +# SHA256 Fingerprint: 3f:03:4b:b5:70:4d:44:b2:d0:85:45:a0:20:57:de:93:eb:f3:90:5f:ce:72:1a:cb:c7:30:c0:6d:da:ee:90:4e +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u +LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgw +NTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD +eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS +b290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3emhF +KxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mt +p7JIKwccJ/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zd +J1M3s6oYwlkm7Fsf0uZlfO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gur +FzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBFEaCeVESE99g2zvVQR9wsMJvuwPWW0v4J +hscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1UefNzFJM3IFTQy2VYzxV4+K +h9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsF +AAOCAQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6Ld +mmQOmFxv3Y67ilQiLUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJ +mBClnW8Zt7vPemVV2zfrPIpyMpcemik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA +8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPSvWKErI4cqc1avTc7bgoitPQV +55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhgaaaI5gdka9at/ +yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd. +# Subject: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd. +# Label: "SecureSign Root CA14" +# Serial: 575790784512929437950770173562378038616896959179 +# MD5 Fingerprint: 71:0d:72:fa:92:19:65:5e:89:04:ac:16:33:f0:bc:d5 +# SHA1 Fingerprint: dd:50:c0:f7:79:b3:64:2e:74:a2:b8:9d:9f:d3:40:dd:bb:f0:f2:4f +# SHA256 Fingerprint: 4b:00:9c:10:34:49:4f:9a:b5:6b:ba:3b:a1:d6:27:31:fc:4d:20:d8:95:5a:dc:ec:10:a9:25:60:72:61:e3:38 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM +BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u +LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw +NzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD +eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS +b290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh1oq/ +FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOg +vlIfX8xnbacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy +6pJxaeQp8E+BgQQ8sqVb1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo +/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9J +kdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOEkJTRX45zGRBdAuVwpcAQ +0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSxjVIHvXib +y8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac +18izju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs +0Wq2XSqypWa9a4X0dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIAB +SMbHdPTGrMNASRZhdCyvjG817XsYAFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVL +ApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeqYR3r6/wtbyPk +86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E +rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ib +ed87hwriZLoAymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopT +zfFP7ELyk+OZpDc8h7hi2/DsHzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHS +DCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPGFrojutzdfhrGe0K22VoF3Jpf1d+4 +2kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6qnsb58Nn4DSEC5MUo +FlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/OfVy +K4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6 +dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl +Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB +365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c +JRNItX+S +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd. +# Subject: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd. +# Label: "SecureSign Root CA15" +# Serial: 126083514594751269499665114766174399806381178503 +# MD5 Fingerprint: 13:30:fc:c4:62:a6:a9:de:b5:c1:68:af:b5:d2:31:47 +# SHA1 Fingerprint: cb:ba:83:c8:c1:5a:5d:f1:f9:73:6f:ca:d7:ef:28:13:06:4a:07:7d +# SHA256 Fingerprint: e7:78:f0:f0:95:fe:84:37:29:cd:1a:00:82:17:9e:53:14:a9:c2:91:44:28:05:e1:fb:1d:8f:b6:b8:88:6c:3a +-----BEGIN CERTIFICATE----- +MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw +UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM +dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy +NTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpDeWJl +cnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBSb290 +IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5GdCx4 +wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSR +ZHX+AezB2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT +9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp +4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6 +bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 2 2023" +# Serial: 153168538924886464690566649552453098598 +# MD5 Fingerprint: e1:09:ed:d3:60:d4:56:1b:47:1f:b7:0c:5f:1b:5f:85 +# SHA1 Fingerprint: 2d:b0:70:ee:71:94:af:69:68:17:db:79:ce:58:9f:a0:6b:96:f7:87 +# SHA256 Fingerprint: 05:52:e6:f8:3f:df:65:e8:fa:96:70:e6:66:df:28:a4:e2:13:40:b5:10:cb:e5:25:66:f9:7c:4f:b9:4b:2b:d1 +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEJSIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUw +OTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCTcfKr +i3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNE +gXtRr90zsWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8 +k12b9py0i4a6Ibn08OhZWiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCT +Rphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl +2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LULQyReS2tNZ9/WtT5PeB+U +cSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIvx9gvdhFP +/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bS +uREVMweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+ +0bpwHJwh5Q8xaRfX/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4N +DfTisl01gLmB1IRpkQLLddCNxbU9CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+ +XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ5Dw1t61 +GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tI +FoE9c+CeJyrrd6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67n +riv6uvw8l5VAk1/DLQOj7aRvU9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTR +VFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4nj8+AybmTNudX0KEPUUDAxxZiMrc +LmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdijYQ6qgYF/6FKC0ULn +4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff/vtD +hQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsG +koHU6XCPpz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46 +ls/pdu4D58JDUjxqgejBWoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aS +Ecr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/5usWDiJFAbzdNpQ0qTUmiteXue4Icr80 +knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jtn/mtd+ArY0+ew+43u3gJ +hJ65bvspmZDogNOfJA== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia TLS ECC Root CA O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia TLS ECC Root CA O=TrustAsia Technologies, Inc. +# Label: "TrustAsia TLS ECC Root CA" +# Serial: 310892014698942880364840003424242768478804666567 +# MD5 Fingerprint: 09:48:04:77:d2:fc:65:93:71:66:b1:11:95:4f:06:8c +# SHA1 Fingerprint: b5:ec:39:f3:a1:66:37:ae:c3:05:94:57:e2:be:11:be:b7:a1:7f:36 +# SHA256 Fingerprint: c0:07:6b:9e:f0:53:1f:b1:a6:56:d6:7c:4e:be:97:cd:5d:ba:a4:1e:f4:45:98:ac:c2:48:98:78:c9:2d:87:11 +-----BEGIN CERTIFICATE----- +MIICMTCCAbegAwIBAgIUNnThTXxlE8msg1UloD5Sfi9QaMcwCgYIKoZIzj0EAwMw +WDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgRUNDIFJvb3QgQ0EwHhcNMjQw +NTE1MDU0MTU2WhcNNDQwNTE1MDU0MTU1WjBYMQswCQYDVQQGEwJDTjElMCMGA1UE +ChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1c3RB +c2lhIFRMUyBFQ0MgUm9vdCBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLh/pVs/ +AT598IhtrimY4ZtcU5nb9wj/1WrgjstEpvDBjL1P1M7UiFPoXlfXTr4sP/MSpwDp +guMqWzJ8S5sUKZ74LYO1644xST0mYekdcouJtgq7nDM1D9rs3qlKH8kzsaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQULIVTu7FDzTLqnqOH/qKYqKaT6RAw +DgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMFRH18MtYYZI9HlaVQ01 +L18N9mdsd0AaRuf4aFtOJx24mH1/k78ITcTaRTChD15KeAIxAKORh/IRM4PDwYqR +OkwrULG9IpRdNYlzg8WbGf60oenUoWa2AaU2+dhoYSi3dOGiMQ== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia TLS RSA Root CA O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia TLS RSA Root CA O=TrustAsia Technologies, Inc. +# Label: "TrustAsia TLS RSA Root CA" +# Serial: 160405846464868906657516898462547310235378010780 +# MD5 Fingerprint: 3b:9e:c3:86:0f:34:3c:6b:c5:46:c4:8e:1d:e7:19:12 +# SHA1 Fingerprint: a5:46:50:c5:62:ea:95:9a:1a:a7:04:6f:17:58:c7:29:53:3d:03:fa +# SHA256 Fingerprint: 06:c0:8d:7d:af:d8:76:97:1e:b1:12:4f:e6:7f:84:7e:c0:c7:a1:58:d3:ea:53:cb:e9:40:e2:ea:97:91:f4:c3 +-----BEGIN CERTIFICATE----- +MIIFgDCCA2igAwIBAgIUHBjYz+VTPyI1RlNUJDxsR9FcSpwwDQYJKoZIhvcNAQEM +BQAwWDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcN +MjQwNTE1MDU0MTU3WhcNNDQwNTE1MDU0MTU2WjBYMQswCQYDVQQGEwJDTjElMCMG +A1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1 +c3RBc2lhIFRMUyBSU0EgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAMMWuBtqpERz5dZO9LnPWwvB0ZqB9WOwj0PBuwhaGnrhB3YmH49pVr7+ +NmDQDIPNlOrnxS1cLwUWAp4KqC/lYCZUlviYQB2srp10Zy9U+5RjmOMmSoPGlbYJ +Q1DNDX3eRA5gEk9bNb2/mThtfWza4mhzH/kxpRkQcwUqwzIZheo0qt1CHjCNP561 +HmHVb70AcnKtEj+qpklz8oYVlQwQX1Fkzv93uMltrOXVmPGZLmzjyUT5tUMnCE32 +ft5EebuyjBza00tsLtbDeLdM1aTk2tyKjg7/D8OmYCYozza/+lcK7Fs/6TAWe8Tb +xNRkoDD75f0dcZLdKY9BWN4ArTr9PXwaqLEX8E40eFgl1oUh63kd0Nyrz2I8sMeX +i9bQn9P+PN7F4/w6g3CEIR0JwqH8uyghZVNgepBtljhb//HXeltt08lwSUq6HTrQ +UNoyIBnkiz/r1RYmNzz7dZ6wB3C4FGB33PYPXFIKvF1tjVEK2sUYyJtt3LCDs3+j +TnhMmCWr8n4uIF6CFabW2I+s5c0yhsj55NqJ4js+k8UTav/H9xj8Z7XvGCxUq0DT +bE3txci3OE9kxJRMT6DNrqXGJyV1J23G2pyOsAWZ1SgRxSHUuPzHlqtKZFlhaxP8 +S8ySpg+kUb8OWJDZgoM5pl+z+m6Ss80zDoWo8SnTq1mt1tve1CuBAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLgHkXlcBvRG/XtZylomkadFK/hT +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAIZtqBSBdGBanEqT3 +Rz/NyjuujsCCztxIJXgXbODgcMTWltnZ9r96nBO7U5WS/8+S4PPFJzVXqDuiGev4 +iqME3mmL5Dw8veWv0BIb5Ylrc5tvJQJLkIKvQMKtuppgJFqBTQUYo+IzeXoLH5Pt +7DlK9RME7I10nYEKqG/odv6LTytpEoYKNDbdgptvT+Bz3Ul/KD7JO6NXBNiT2Twp +2xIQaOHEibgGIOcberyxk2GaGUARtWqFVwHxtlotJnMnlvm5P1vQiJ3koP26TpUJ +g3933FEFlJ0gcXax7PqJtZwuhfG5WyRasQmr2soaB82G39tp27RIGAAtvKLEiUUj +pQ7hRGU+isFqMB3iYPg6qocJQrmBktwliJiJ8Xw18WLK7nn4GS/+X/jbh87qqA8M +pugLoDzga5SYnH+tBuYc6kIQX+ImFTw3OffXvO645e8D7r0i+yiGNFjEWn9hongP +XvPKnbwbPKfILfanIhHKA9jnZwqKDss1jjQ52MjqjZ9k4DewbNfFj8GQYSbbJIwe +SsCI3zWQzj8C9GRh3sfIB5XeMhg6j6JCQCTl1jNdfK7vsU1P1FeQNWrcrgSXSYk0 +ly4wBOeY99sLAZDBHwo/+ML+TvrbmnNzFrwFuHnYWa8G5z9nODmxfKuU4CkUpijy +323imttUQ/hHWKNddBWcwauwxzQ= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 2 2023" +# Serial: 139766439402180512324132425437959641711 +# MD5 Fingerprint: 96:b4:78:09:f0:09:cb:77:eb:bb:1b:4d:6f:36:bc:b6 +# SHA1 Fingerprint: a5:5b:d8:47:6c:8f:19:f7:4c:f4:6d:6b:b6:c2:79:82:22:df:54:8b +# SHA256 Fingerprint: 8e:82:21:b2:e7:d4:00:78:36:a1:67:2f:0d:cc:29:9c:33:bc:07:d3:16:f1:32:fa:1a:20:6d:58:71:50:f1:ce +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEVWIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUw +OTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1sJkK +F8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE +7CUXFId/MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFe +EMbsh2aJgWi6zCudR3Mfvc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6 +lHPTGGkKSv/BAQP/eX+1SH977ugpbzZMlWGG2Pmic4ruri+W7mjNPU0oQvlFKzIb +RlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3YG14C8qKXO0elg6DpkiV +jTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq9107PncjLgc +jmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZx +TnXonMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ +ARZZaBhDM7DS3LAaQzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nk +hbDhezGdpn9yo7nELC7MmVcOIQxFAZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knF +NXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqvyREBuH +kV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14 +QvBukEdHjqOSMo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4 +pZt+UPJ26oUFKidBK7GB0aL2QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q +3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xDUmPBEcrCRbH0O1P1aa4846XerOhU +t7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V4U/M5d40VxDJI3IX +cI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuodNv8 +ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT +2vFp4LJiTZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs +7dpn1mKmS00PaaLJvOwiS5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNP +gofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAst +Nl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L+KIkBI3Y4WNeApI02phh +XBxvWHZks/wCuPWdCg== +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign RSA TLS Root CA 2022 - 1 O=SwissSign AG +# Subject: CN=SwissSign RSA TLS Root CA 2022 - 1 O=SwissSign AG +# Label: "SwissSign RSA TLS Root CA 2022 - 1" +# Serial: 388078645722908516278762308316089881486363258315 +# MD5 Fingerprint: 16:2e:e4:19:76:81:85:ba:8e:91:58:f1:15:ef:72:39 +# SHA1 Fingerprint: 81:34:0a:be:4c:cd:ce:cc:e7:7d:cc:8a:d4:57:e2:45:a0:77:5d:ce +# SHA256 Fingerprint: 19:31:44:f4:31:e0:fd:db:74:07:17:d4:de:92:6a:57:11:33:88:4b:43:60:d3:0e:27:29:13:cb:e6:60:ce:41 +-----BEGIN CERTIFICATE----- +MIIFkzCCA3ugAwIBAgIUQ/oMX04bgBhE79G0TzUfRPSA7cswDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkGA1UE +AxMiU3dpc3NTaWduIFJTQSBUTFMgUm9vdCBDQSAyMDIyIC0gMTAeFw0yMjA2MDgx +MTA4MjJaFw00NzA2MDgxMTA4MjJaMFExCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxT +d2lzc1NpZ24gQUcxKzApBgNVBAMTIlN3aXNzU2lnbiBSU0EgVExTIFJvb3QgQ0Eg +MjAyMiAtIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLKmjiC8NX +vDVjvHClO/OMPE5Xlm7DTjak9gLKHqquuN6orx122ro10JFwB9+zBvKK8i5VUXu7 +LCTLf5ImgKO0lPaCoaTo+nUdWfMHamFk4saMla+ju45vVs9xzF6BYQ1t8qsCLqSX +5XH8irCRIFucdFJtrhUnWXjyCcplDn/L9Ovn3KlMd/YrFgSVrpxxpT8q2kFC5zyE +EPThPYxr4iuRR1VPuFa+Rd4iUU1OKNlfGUEGjw5NBuBwQCMBauTLE5tzrE0USJIt +/m2n+IdreXXhvhCxqohAWVTXz8TQm0SzOGlkjIHRI36qOTw7D59Ke4LKa2/KIj4x +0LDQKhySio/YGZxH5D4MucLNvkEM+KRHBdvBFzA4OmnczcNpI/2aDwLOEGrOyvi5 +KaM2iYauC8BPY7kGWUleDsFpswrzd34unYyzJ5jSmY0lpx+Gs6ZUcDj8fV3oT4MM +0ZPlEuRU2j7yrTrePjxF8CgPBrnh25d7mUWe3f6VWQQvdT/TromZhqwUtKiE+shd +OxtYk8EXlFXIC+OCeYSf8wCENO7cMdWP8vpPlkwGqnj73mSiI80fPsWMvDdUDrta +clXvyFu1cvh43zcgTFeRc5JzrBh3Q4IgaezprClG5QtO+DdziZaKHG29777YtvTK +wP1H8K4LWCDFyB02rpeNUIMmJCn3nTsPBQIDAQABo2MwYTAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBRvjmKLk0Ow4UD2p8P98Q+4 +DxU4pTAdBgNVHQ4EFgQUb45ii5NDsOFA9qfD/fEPuA8VOKUwDQYJKoZIhvcNAQEL +BQADggIBAKwsKUF9+lz1GpUYvyypiqkkVHX1uECry6gkUSsYP2OprphWKwVDIqO3 +10aewCoSPY6WlkDfDDOLazeROpW7OSltwAJsipQLBwJNGD77+3v1dj2b9l4wBlgz +Hqp41eZUBDqyggmNzhYzWUUo8aWjlw5DI/0LIICQ/+Mmz7hkkeUFjxOgdg3XNwwQ +iJb0Pr6VvfHDffCjw3lHC1ySFWPtUnWK50Zpy1FVCypM9fJkT6lc/2cyjlUtMoIc +gC9qkfjLvH4YoiaoLqNTKIftV+Vlek4ASltOU8liNr3CjlvrzG4ngRhZi0Rjn9UM +ZfQpZX+RLOV/fuiJz48gy20HQhFRJjKKLjpHE7iNvUcNCfAWpO2Whi4Z2L6MOuhF +LhG6rlrnub+xzI/goP+4s9GFe3lmozm1O2bYQL7Pt2eLSMkZJVX8vY3PXtpOpvJp +zv1/THfQwUY1mFwjmwJFQ5Ra3bxHrSL+ul4vkSkphnsh3m5kt8sNjzdbowhq6/Td +Ao9QAwKxuDdollDruF/UKIqlIgyKhPBZLtU30WHlQnNYKoH3dtvi4k0NX/a3vgW0 +rk4N3hY9A4GzJl5LuEsAz/+MF7psYC0nhzck5npgL7XTgwSqT0N1osGDsieYK7EO +gLrAhV5Cud+xYJHT6xh+cHiudoO+cVrQkOPKwRYlZ0rwtnu64ZzZ +-----END CERTIFICATE----- + +# Issuer: CN=OISTE Server Root ECC G1 O=OISTE Foundation +# Subject: CN=OISTE Server Root ECC G1 O=OISTE Foundation +# Label: "OISTE Server Root ECC G1" +# Serial: 47819833811561661340092227008453318557 +# MD5 Fingerprint: 42:a7:d2:35:ae:02:92:db:19:76:08:de:2f:05:b4:d4 +# SHA1 Fingerprint: 3b:f6:8b:09:ae:2a:92:7b:ba:e3:8d:3f:11:95:d9:e6:44:0c:45:e2 +# SHA256 Fingerprint: ee:c9:97:c0:c3:0f:21:6f:7e:3b:8b:30:7d:2b:ae:42:41:2d:75:3f:c8:21:9d:af:d1:52:0b:25:72:85:0f:49 +-----BEGIN CERTIFICATE----- +MIICNTCCAbqgAwIBAgIQI/nD1jWvjyhLH/BU6n6XnTAKBggqhkjOPQQDAzBLMQsw +CQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwY +T0lTVEUgU2VydmVyIFJvb3QgRUNDIEcxMB4XDTIzMDUzMTE0NDIyOFoXDTQ4MDUy +NDE0NDIyN1owSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5kYXRp +b24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IEVDQyBHMTB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABBcv+hK8rBjzCvRE1nZCnrPoH7d5qVi2+GXROiFPqOujvqQy +cvO2Ackr/XeFblPdreqqLiWStukhEaivtUwL85Zgmjvn6hp4LrQ95SjeHIC6XG4N +2xml4z+cKrhAS93mT6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQ3 +TYhlz/w9itWj8UnATgwQb0K0nDAdBgNVHQ4EFgQUN02IZc/8PYrVo/FJwE4MEG9C +tJwwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCpKjAd0MKfkFFR +QD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxgZzFDJe0CMQCSia7pXGKD +YmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE Server Root RSA G1 O=OISTE Foundation +# Subject: CN=OISTE Server Root RSA G1 O=OISTE Foundation +# Label: "OISTE Server Root RSA G1" +# Serial: 113845518112613905024960613408179309848 +# MD5 Fingerprint: 23:a7:9e:d4:70:b8:b9:14:57:41:8a:7e:44:59:e2:68 +# SHA1 Fingerprint: f7:00:34:25:94:88:68:31:e4:34:87:3f:70:fe:86:b3:86:9f:f0:6e +# SHA256 Fingerprint: 9a:e3:62:32:a5:18:9f:fd:db:35:3d:fd:26:52:0c:01:53:95:d2:27:77:da:c5:9d:b5:7b:98:c0:89:a6:51:e6 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBL +MQswCQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UE +AwwYT0lTVEUgU2VydmVyIFJvb3QgUlNBIEcxMB4XDTIzMDUzMTE0MzcxNloXDTQ4 +MDUyNDE0MzcxNVowSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5k +YXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IFJTQSBHMTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAKqu9KuCz/vlNwvn1ZatkOhLKdxVYOPM +vLO8LZK55KN68YG0nnJyQ98/qwsmtO57Gmn7KNByXEptaZnwYx4M0rH/1ow00O7b +rEi56rAUjtgHqSSY3ekJvqgiG1k50SeH3BzN+Puz6+mTeO0Pzjd8JnduodgsIUzk +ik/HEzxux9UTl7Ko2yRpg1bTacuCErudG/L4NPKYKyqOBGf244ehHa1uzjZ0Dl4z +O8vbUZeUapU8zhhabkvG/AePLhq5SvdkNCncpo1Q4Y2LS+VIG24ugBA/5J8bZT8R +tOpXaZ+0AOuFJJkk9SGdl6r7NH8CaxWQrbueWhl/pIzY+m0o/DjH40ytas7ZTpOS +jswMZ78LS5bOZmdTaMsXEY5Z96ycG7mOaES3GK/m5Q9l3JUJsJMStR8+lKXHiHUh +sd4JJCpM4rzsTGdHwimIuQq6+cF0zowYJmXa92/GjHtoXAvuY8BeS/FOzJ8vD+Ho +mnqT8eDI278n5mUpezbgMxVz8p1rhAhoKzYHKyfMeNhqhw5HdPSqoBNdZH702xSu ++zrkL8Fl47l6QGzwBrd7KJvX4V84c5Ss2XCTLdyEr0YconosP4EmQufU2MVshGYR +i3drVByjtdgQ8K4p92cIiBdcuJd5z+orKu5YM+Vt6SmqZQENghPsJQtdLEByFSnT +kCz3GkPVavBpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU +8snBDw1jALvsRQ5KH7WxszbNDo0wHQYDVR0OBBYEFPLJwQ8NYwC77EUOSh+1sbM2 +zQ6NMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQwFAAOCAgEANGd5sjrG5T33 +I3K5Ce+SrScfoE4KsvXaFwyihdJ+klH9FWXXXGtkFu6KRcoMQzZENdl//nk6HOjG +5D1rd9QhEOP28yBOqb6J8xycqd+8MDoX0TJD0KqKchxRKEzdNsjkLWd9kYccnbz8 +qyiWXmFcuCIzGEgWUOrKL+mlSdx/PKQZvDatkuK59EvV6wit53j+F8Bdh3foZ3dP +AGav9LEDOr4SfEE15fSmG0eLy3n31r8Xbk5l8PjaV8GUgeV6Vg27Rn9vkf195hfk +gSe7BYhW3SCl95gtkRlpMV+bMPKZrXJAlszYd2abtNUOshD+FKrDgHGdPY3ofRRs +YWSGRqbXVMW215AWRqWFyp464+YTFrYVI8ypKVL9AMb2kI5Wj4kI3Zaq5tNqqYY1 +9tVFeEJKRvwDyF7YZvZFZSS0vod7VSCd9521Kvy5YhnLbDuv0204bKt7ph6N/Ome +/msVuduCmsuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3 +J8tRd/iWkx7P8nd9H0aTolkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2 +wq1yVAb+axj5d9spLFKebXd7Yv0PTY6YMjAwcRLWJTXjn/hvnLXrahut6hDTlhZy +BiElxky8j3C7DOReIoMt0r7+hVu05L0= +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno TLS Root CA 2023 O=Microsec Ltd. +# Subject: CN=e-Szigno TLS Root CA 2023 O=Microsec Ltd. +# Label: "e-Szigno TLS Root CA 2023" +# Serial: 71934828665710877219916191754 +# MD5 Fingerprint: 6a:e9:99:74:a5:da:5e:f1:d9:2e:f2:c8:d1:86:8b:71 +# SHA1 Fingerprint: 6f:9a:d5:d5:df:e8:2c:eb:be:37:07:ee:4f:4f:52:58:29:41:d1:fe +# SHA256 Fingerprint: b4:91:41:50:2d:00:66:3d:74:0f:2e:7e:c3:40:c5:28:00:96:26:66:12:1a:36:d0:9c:f7:dd:2b:90:38:4f:b4 +-----BEGIN CERTIFICATE----- +MIICzzCCAjGgAwIBAgINAOhvGHvWOWuYSkmYCjAKBggqhkjOPQQDBDB1MQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xFzAVBgNVBGEMDlZBVEhVLTIzNTg0NDk3MSIwIAYDVQQDDBllLVN6aWdubyBU +TFMgUm9vdCBDQSAyMDIzMB4XDTIzMDcxNzE0MDAwMFoXDTM4MDcxNzE0MDAwMFow +dTELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYwFAYDVQQKDA1NaWNy +b3NlYyBMdGQuMRcwFQYDVQRhDA5WQVRIVS0yMzU4NDQ5NzEiMCAGA1UEAwwZZS1T +emlnbm8gVExTIFJvb3QgQ0EgMjAyMzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAE +AGgP36J8PKp0iGEKjcJMpQEiFNT3YHdCnAo4YKGMZz6zY+n6kbCLS+Y53wLCMAFS +AL/fjO1ZrTJlqwlZULUZwmgcAOAFX9pQJhzDrAQixTpN7+lXWDajwRlTEArRzT/v +SzUaQ49CE0y5LBqcvjC2xN7cS53kpDzLLtmt3999Cd8ukv+ho2MwYTAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUWYQCYlpGePVd3I8K +ECgj3NXW+0UwHwYDVR0jBBgwFoAUWYQCYlpGePVd3I8KECgj3NXW+0UwCgYIKoZI +zj0EAwQDgYsAMIGHAkIBLdqu9S54tma4n7Zwf2Z0z+yOfP7AAXmazlIC58PRDHpt +y7Ve7hekm9sEdu4pKeiv+62sUvTXK9Z3hBC9xdIoaDQCQTV2WnXzkoYI9bIeCvZl +C9p2x1L/Cx6AcCIwwzPbGO2E14vs7dOoY4G1VnxHx1YwlGhza9IuqbnZLBwpvQy6 +uWWL +-----END CERTIFICATE----- diff --git a/server/config.example.json b/server/config.example.json new file mode 100644 index 0000000..aa24e1c --- /dev/null +++ b/server/config.example.json @@ -0,0 +1,13 @@ +{ + "api_base": "https://api.bananarouter.com/v1", + "api_key": "", + "model": "gpt-5.5", + "chat_path": "/chat/completions", + "models_path": "/models", + "temperature": 0.2, + "timeout": 120, + "retries": 2, + "ssl_verify": true, + "ca_bundle": "", + "extra_headers": {} +} diff --git a/server/prompt_templates.py b/server/prompt_templates.py new file mode 100644 index 0000000..3eb130f --- /dev/null +++ b/server/prompt_templates.py @@ -0,0 +1,60 @@ +"""可复用的提示词模板注册表。 + +后端只负责准备 evidence/context,模板负责描述任务;接入其它项目时可直接 +替换模板或覆盖变量,不需要在业务代码里拼接提示词。 +""" +from dataclasses import dataclass +import json +from pathlib import Path + +PRODUCT_VARIABLES = { + "product": "商品基础信息:标题、商品ID、类目、品牌、价格、销量等页面字段", + "attributes": "商品参数/规格:仅包含页面实际采集到的名称和值", + "images": "图片证据:主图、详情图、SKU图、评论原图及其 evidenceId", + "reviews": "消费者评论:原文、评分、SKU、日期和评论 evidenceId", + "questions": "问大家/购买问答:问题、回答及 QA evidenceId", + "programStats": "程序统计:评论数量、图片数量、参数数量等可计算指标", + "evidence": "统一证据账本;模型只能引用其中的 evidenceId", +} + +@dataclass(frozen=True) +class PromptTemplate: + name: str + body: str + variables: tuple = () + + def render(self, **values): + missing = [v for v in self.variables if v not in values] + if missing: + raise ValueError(f"模板 {self.name} 缺少变量: {', '.join(missing)}") + return self.body.format(**values) + +TEMPLATES = { + "system": PromptTemplate("system", "{system}\n\n可用商品变量说明:\n{variable_schema}", ("system", "variable_schema")), + "task": PromptTemplate("task", "{task}\n\n输入变量:{input_variables}", ("task", "input_variables")), +} + +def register_template(name, body, variables=()): + """覆盖或新增模板,供宿主项目在启动时注入行业提示词。""" + TEMPLATES[name] = PromptTemplate(name, body, tuple(variables)) + +def render_template(name, **values): + if name not in TEMPLATES: + raise KeyError(f"未知提示词模板: {name}") + return TEMPLATES[name].render(**values) + +def variable_schema(): + return json.dumps(PRODUCT_VARIABLES, ensure_ascii=False, indent=2) + +def build_system_prompt(system_text): + override=Path(__file__).with_name('prompts.json') + if override.exists(): + try: + data=json.loads(override.read_text('utf-8')) + if data.get('system',{}).get('body'): return data['system']['body'].format(system=system_text, variable_schema=variable_schema()) + except Exception: pass + return TEMPLATES["system"].render(system=system_text, variable_schema=variable_schema()) + +def build_task_prompt(task_text, variables=("evidence",)): + names = "、".join(f"{name}({PRODUCT_VARIABLES.get(name, '任务上下文变量')})" for name in variables) + return TEMPLATES["task"].render(task=task_text, input_variables=names) diff --git a/server/rebuild_report.py b/server/rebuild_report.py new file mode 100644 index 0000000..60be868 --- /dev/null +++ b/server/rebuild_report.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from analysis import ensure_experience_solution +from renderer import render + + +def main(): + parser = argparse.ArgumentParser(description='从已有分析 JSON 重建完整事实报告,不调用模型 API。') + parser.add_argument('input', type=Path) + parser.add_argument('--output', type=Path) + args = parser.parse_args() + + source = args.input.resolve() + analysis = json.loads(source.read_text('utf-8')) + result = ensure_experience_solution(analysis) + output = (args.output or source.with_name(source.stem + '_fixed.html')).resolve() + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(render(result, source.stem), 'utf-8') + output.with_suffix('.json').write_text(json.dumps(result, ensure_ascii=False, indent=2), 'utf-8') + print(output) + + +if __name__ == '__main__': + main() diff --git a/server/renderer.py b/server/renderer.py new file mode 100644 index 0000000..2b493ba --- /dev/null +++ b/server/renderer.py @@ -0,0 +1,686 @@ +import html +import re +from analysis import ensure_experience_solution + +UNCERTAIN = ('可能', '或许', '推测', '无法判断', '不能判断', '证据不足', '尚不能', '不足以', '未知', '未识别', '不代表', '待验证') + +def H(v): return html.escape('' if v is None else str(v)) +def arr(v): return v if isinstance(v, list) else [] +def safe(v): return '' if v in (None, '', [], {}) else (str(v) if not isinstance(v,(dict,list,tuple)) else '') + +def text_of(v, keys=('text','point','statement','label','value','driver','problem','concern','signal','item')): + if v in (None,'',[],{}): return '' + if isinstance(v,str): return v + if isinstance(v,(int,float)): return str(v) + if isinstance(v,dict): + for k in keys: + if v.get(k) not in (None,'',[],{}): return text_of(v.get(k),keys) + return '' + return '' + +def concise(v, limit=90, verified=True): + text = re.sub(r'\s+', ' ', safe(v)).strip() + if not text: return '' + parts = [x.strip() for x in re.split(r'(?<=[。!?;])', text) if x.strip()] + if verified: + text = ''.join(x for x in parts if not any(word in x for word in UNCERTAIN)) + if not text: return '' + if len(text) <= limit: return text + cut = max(text.rfind(mark, 0, limit + 1) for mark in ',。;:') + if cut < limit // 2: cut = limit + return text[:cut].rstrip(',。;: ') + '…' + +def evidence_index(a): return {x.get('id'): x for x in arr(a.get('evidenceLedger')) if isinstance(x, dict) and x.get('id')} +def image_entry(eid, idx, allowed_groups=None): + e = idx.get(eid) or {}; value = e.get('value') + group=(e.get('meta') or {}).get('group') + if e.get('type')!='image' or not isinstance(value,str) or not value.startswith(('http://','https://','data:image/')):return None + if allowed_groups and group not in allowed_groups:return None + return e +def image_url(eid, idx, allowed_groups=None): + e=image_entry(eid,idx,allowed_groups) + if not e:return '' + return (e.get('meta') or {}).get('localUrl') or e.get('value') + +def image_source(e): + if not isinstance(e,dict):return ('','') + meta=e.get('meta') or {}; return (meta.get('localUrl') or e.get('value') or '', meta.get('sourceUrl') or e.get('value') or '') + +def img_tag(url, alt='商品图片', fallback=''): + if not url:return '' + fb=fallback if fallback and fallback!=url else '' + attrs=f' data-fallback="{H(fb)}"' if fb else '' + return f'{H(alt)}' + +def product_hero(visual, idx): + for item in arr(visual.get('items')): + if isinstance(item, dict): + url = image_url(safe(item.get('imageEvidenceId')), idx, {'main'}) + if url: return url + return '' + +def action_list(text, limit=5): + items = [] + for value in re.split(r'[;。]\s*', safe(text)): + value = concise(value, 64) + if value and value not in items: items.append(value) + return '

    ' + ''.join(f'
  • {H(x)}
  • ' for x in items[:limit]) + '
' if items else '' + +def owner_cards(cards): + out = [] + for x in arr(cards)[:6]: + if not isinstance(x, dict): continue + headline = concise(x.get('headline'), 46) + bullets = [concise(v, 42) for v in arr(x.get('bullets'))] + bullets = [v for v in bullets if v][:2] + if not headline: continue + detail = '
    ' + ''.join(f'
  • {H(v)}
  • ' for v in bullets) + '
' if bullets else '' + out.append(f'
{H(x.get("label"))}{H(headline)}{detail}
') + return ''.join(out) + +def overview_rows(items, kind): + out = [] + for x in arr(items)[:6]: + if not isinstance(x, dict): continue + label = concise(x.get('label'), 24, False) + value = concise(x.get('value'), 84, False) + note = concise(x.get('note'), 42, False) + priority = concise(x.get('priority'), 14, False) + if not label or not value: continue + meta = priority or note + out.append( + f'
' + + (f'{H(meta)}' if meta else '') + + f'{H(label)}

{H(value)}

' + ) + return ''.join(out) + +def platform_overview_html(overview): + facts = overview_rows(overview.get('facts'), 'fact') + outcomes = overview_rows(arr(overview.get('outcomes'))[:3], 'reason') + actions = overview_rows(overview.get('actions'), 'check') + groups = ''.join([ + f'

交易与产品事实

{facts}
' if facts else '', + f'

购后体验验证

{outcomes}
' if outcomes else '', + f'

平台经营建议

{actions}
' if actions else '', + ]) + verdict = concise(overview.get('verdict'), 70, False) + sample = concise(overview.get('sampleNote'), 64, False) + return '
' + ( + f'
产品分析结论{H(verdict)}' + + (f'{H(sample)}' if sample else '') + '
' if verdict else '' + ) + f'
{groups}
' + +def decision_boundary_html(base): + reviews = base.get('reviewCount', 0) + public = safe(base.get('publicReviewCount')) + coverage = f'已采有效评论 {reviews}' + (f' / 页面公开 {public}' if public else '') + status = '评论采集已确认完整' if base.get('reviewCollectionComplete') else '评论采集未确认完整' + items = [ + ('证据覆盖', coverage, status), + ('本报告支持', '商品拆解、样品验证、规格校准、页面信息修正', '基于当前页面参数与已采评论'), + ('适用范围', '不输出生产规模、利润、转化或市场容量', '当前数据不含对应经营指标'), + ('补充数据', '完整评论、流量转化、退款售后、成本毛利', '补齐后再进入经营测算'), + ] + return '
' + ''.join( + f'
{H(label)}{H(value)}{H(note)}
' + for label, value, note in items + ) + '
' + +def review_results_html(overview): + def rows(items, tone): + out=[] + for x in arr(items)[:6]: + if not isinstance(x,dict):continue + label=concise(x.get('label'),24,False);value=concise(x.get('value'),100,False);note=concise(x.get('note') or x.get('priority'),26,False) + if not label or not value:continue + out.append(f'
'+(f'{H(note)}' if note else '')+f'{H(label)}

{H(value)}

') + return ''.join(out) + outcomes=rows(overview.get('outcomes'),'positive') + risks=rows([x for x in arr(overview.get('checks')) if isinstance(x,dict) and x.get('priority')=='明确反馈'],'risk') + questions=rows(overview.get('questions'),'question') + return ''.join([ + f'

评论确认的结果

{outcomes}
' if outcomes else '', + f'

明确问题

{risks}
' if risks else '', + f'

购买问答反馈

{questions}
' if questions else '', + ]) + +def visual_items(items, idx): + out = [] + for x in arr(items): + if not isinstance(x, dict): continue + eid=safe(x.get('imageEvidenceId'));entry=image_entry(eid,idx,{'main','detail'}) + if not entry: continue + url=entry.get('value');group=(entry.get('meta') or {}).get('group');source=(entry.get('meta') or {}).get('source') + source_label='商品主图画廊' if group=='main' else '商品详情描述区域' + if source not in ('meta_og_image','dom_main_gallery','structured_main_gallery','dom_product_description','structured_product_description'):continue + headline = concise(x.get('headline'), 42) + frame = concise(x.get('frameInfo'), 90) + subject = concise(x.get('productSubject'), 90) + pattern = concise(x.get('patternColor'), 90) + text_info = concise(x.get('textInfo'), 90) + hierarchy = concise(x.get('informationHierarchy'), 90) + task = concise(x.get('contentTask') or x.get('persuasionTask'), 72) + weakness = concise(x.get('weakness'), 72) + action = concise(x.get('borrowableAction') or x.get('action') or x.get('nextProductAction'), 82) + facts = ''.join([ + f'
画面内容{H(frame)}
' if frame else '', + f'
商品主体{H(subject)}
' if subject else '', + f'
花型配色{H(pattern)}
' if pattern else '', + f'
文案信息{H(text_info)}
' if text_info else '', + f'
层级关系{H(hierarchy)}
' if hierarchy else '', + f'
表达任务{H(task)}
' if task else '', + f'
明确问题{H(weakness)}
' if weakness else '', + f'
可借鉴动作{H(action)}
' if action else '' + ]) + out.append('
' + img_tag(url, '商品主图' if group=='main' else '商品详情图') + '
来源:' + H(source_label) + '

' + H(headline or ('已核验主图' if group=='main' else '已核验详情图')) + '

' + facts + '
') + return ''.join(out) + +def buyer_evidence_html(block, idx): + groups={} + for item in arr((block or {}).get('items'))[:24]: + if not isinstance(item,dict):continue + image_id=safe(item.get('imageEvidenceId'));review_id=safe(item.get('reviewEvidenceId')) + image=image_entry(image_id,idx,{'buyerShow'});review=idx.get(review_id) or {};meta=(image or {}).get('meta') or {} + if not image or review.get('type')!='review' or review_id not in arr(meta.get('reviewEvidenceIds')):continue + value=review.get('value') if isinstance(review.get('value'),dict) else {};quote=concise(value.get('content'),90,False) + if not quote:continue + details=' · '.join(x for x in (concise(value.get('sku'),42,False),concise(value.get('date'),18,False)) if x) + group=groups.setdefault(review_id,{'quote':quote,'details':details,'images':[]}) + group['images'].append(image.get('value')) + if not groups:return '' + title=H((block or {}).get('title') or '评论原图与对应评价') + image_count=sum(len(x['images']) for x in groups.values()) + out=[] + for review_id,group in groups.items(): + gallery=''.join( + '
'+img_tag(url,f'{review_id} 评论原图 {i}')+f'
原图 {i:02d}
' + for i,url in enumerate(group['images'],1) + ) + out.append('
评论证据 · '+H(review_id)+'
'+H(group['quote'])+'
'+(''+H(group['details'])+'' if group['details'] else '')+'
') + return f'
评论图片证据

{title} · {len(groups)} 条评价 / {image_count} 张原图

每组图片只关联同一条评论,按原比例完整展示;不作为商品主图、详情图或 SKU 图使用。

'+''.join(out)+'
' + +def stats_html(stats): + out = [] + for x in arr(stats)[:6]: + if isinstance(x, dict) and safe(x.get('value')): + out.append(f'
{H(concise(x.get("label"), 16, False))}{H(concise(x.get("value"), 18, False))}
') + return ''.join(out) + +def journey_html(stages): + xs = [x for x in arr(stages) if isinstance(x, dict)] + tabs, panels = [], [] + for i, x in enumerate(xs): + positive = concise(x.get('positiveConfirmation'), 70); friction = concise(x.get('friction'), 70) + if not positive and not friction: continue + active = ' active' if not tabs else '' + tabs.append(f'') + facts = (f'
已确认

{H(positive)}

' if positive else '') + (f'
明确问题

{H(friction)}

' if friction else '') + quotes = [] + for q in arr(x.get('quotes'))[:2]: + if isinstance(q, dict) and safe(q.get('text')): quotes.append(f'
  • {H(q.get("type"))}{H(concise(q.get("text"), 90, False))}
  • ') + panels.append(f'
    第 {H(x.get("stageNo") or i + 1)} 阶段

    {H(x.get("label"))}

    {H(concise(x.get("headline"), 54))}

    {facts}
    ' + ('
      ' + ''.join(quotes) + '
    ' if quotes else '') + '
    ') + return '
    ' + ''.join(tabs) + '
    ' + ''.join(panels) if tabs else '' + +def signals_html(signals): + if not signals: return '' + values = [] + for key, tone in [('positive', 'good'), ('questions', 'ask'), ('risks', 'risk')]: + for x in arr(signals.get(key))[:5]: + label = concise(x.get('label'), 42) if isinstance(x, dict) else '' + if label: values.append(f'{H(label)}') + return '
    已确认体验信号
    ' + ''.join(values) + '
    ' if values else '' + +def param_html(xs): + out = [] + for x in arr(xs)[:8]: + value = concise(x.get('value'), 82) if isinstance(x, dict) else '' + if value: out.append(f'
    {H(x.get("label"))}{H(value)}
    ') + return ''.join(out) + +def gates_html(xs): + out = [] + for x in arr(xs)[:6]: + action = concise(x.get('action'), 92) if isinstance(x, dict) else '' + if action: out.append(f'
    {H(x.get("priority"))} · {H(concise(x.get("label"), 24, False))}{H(action)}
    ') + return ''.join(out) + +def plans_html(xs): + out = [] + for x in arr(xs)[:3]: + if not isinstance(x, dict): continue + selling = selling_points_html(x.get('sellingPoints')) + specs = [] + for spec in arr(x.get('specs')): + value = concise(spec.get('value'), 82) if isinstance(spec, dict) else '' + if value: specs.append(f'
    {H(spec.get("label"))}{H(value)}
    ') + mg = x.get('manufacturingGate') or {}; vc = x.get('visualCommerce') or {} + out.append('
    ' + H(x.get('type')) + '

    ' + H(concise(x.get('name'), 30, False)) + '

    ' + H(concise(x.get('positioning'), 110)) + '

    ' + selling + '
    ' + ''.join(specs) + '

    ' + H(mg.get('title') or '制造闸门') + '

    ' + action_list(mg.get('content')) + '

    ' + H(vc.get('title') or '首图与商详') + '

    ' + action_list(vc.get('content')) + '
    ') + return ''.join(out) + +def selling_points_html(xs): + out=[] + for x in arr(xs)[:5]: + if not isinstance(x,dict):continue + slogan=concise(x.get('slogan'),34,False) + value=concise(x.get('consumerValue'),54,False) + source=concise(x.get('evidenceSource'),32,False) + action=concise(x.get('productAction'),64,False) + if not slogan:continue + out.append(f'
    {H(slogan)}'+(f'

    {H(value)}

    ' if value else '')+(f'{H(source)}' if source else '')+(f'{H(action)}' if action else '')+'
    ') + return '
    NEXT PRODUCT SELLING POINTS

    下一款明确卖点

    '+''.join(out)+'
    ' if out else '' + +def governance_html(gov): + if not isinstance(gov,dict):return '' + items=[('报告角色',gov.get('role')),('结构标准',gov.get('structure')),('内容来源',gov.get('sourceRule')),('证据规则',gov.get('evidenceRule'))] + rows=''.join(f'
    {H(label)}{H(concise(value,64,False))}
    ' for label,value in items if safe(value)) + return '
    '+rows+'
    ' if rows else '' + +def attr_lookup(raw, names): + for x in arr(raw.get('attributes')): + if not isinstance(x,dict):continue + name=safe(x.get('name')) + if any(k in name for k in names) and safe(x.get('value')):return safe(x.get('value')) + return '' + +def product_profile_html(raw, sales, base): + rows=[ + ('一级品类', safe((raw.get('product') or {}).get('category')) or '床上用品'), + ('商品标题', safe((raw.get('product') or {}).get('title'))), + ('价格定位', ('¥'+safe(sales.get('currentPrice'))) if safe(sales.get('currentPrice')) else ''), + ('公开销量', safe(sales.get('sold')) or safe(sales.get('cumulativeSales'))), + ('目标人群', attr_lookup(raw, ['适用人群','适用对象'])), + ('核心场景', attr_lookup(raw, ['适用场景','适用床尺寸','床品尺寸'])), + ('产品结构', attr_lookup(raw, ['件数','套件组成','组合形式','款式'])), + ('材质成分', attr_lookup(raw, ['成分含量','床单面料材质','被面材质','材质','棉种类'])), + ('支数密度', ' / '.join(x for x in [attr_lookup(raw,['面料支数','纱线支数']), attr_lookup(raw,['面料密度','织物密度'])] if x)), + ('工艺结构', attr_lookup(raw, ['床品工艺','织造工艺','印染工艺','工艺'])), + ('风格花型', ' / '.join(x for x in [attr_lookup(raw,['风格']), attr_lookup(raw,['主图案类型','颜色分类'])] if x)), + ('评论样本', f'{base.get("reviewCount",0)} 条有效评论'), + ] + cells=''.join(f'
    {H(k)}{H(concise(v,78,False))}
    ' for k,v in rows if safe(v)) + return '
    '+cells+'
    ' if cells else '' + +def deal_logic_html(plans): + points=[] + for plan in arr(plans.get('plans'))[:1]: + if isinstance(plan,dict):points=arr(plan.get('sellingPoints')) + rows=[] + for i,x in enumerate(points[:5],1): + if not isinstance(x,dict):continue + rows.append(f'
    {i:02d}{H(concise(x.get("slogan"),34,False))}

    {H(concise(x.get("consumerValue"),62,False))}

    {H(concise(x.get("evidenceSource"),36,False))}
    ') + return '
    '+''.join(rows)+'
    ' if rows else '' + +def feature_power_html(overview, product_exp): + cards=[] + for x in arr(overview.get('cards')): + if not isinstance(x,dict):continue + label=safe(x.get('label')); headline=concise(x.get('headline'),42,False) + if not headline or label in ('销售事实','页面活动'):continue + tone=safe(x.get('tone')) or 'normal' + cards.append(f'
    {H(label)}{H(headline)}
    ') + for x in arr(product_exp.get('gates'))[:3]: + if not isinstance(x,dict):continue + cards.append(f'
    {H(x.get("priority"))}{H(concise(x.get("label"),34,False))}

    {H(concise(x.get("action"),64,False))}

    ') + return '
    '+''.join(cards[:8])+'
    ' if cards else '' + +def marketing_html(raw): + product=raw.get('product') or {}; title=safe(product.get('title')) + slots=[ + ('品类词', '床品 / 三件套' if re.search(r'床品|三件套|四件套|被套|床单|床笠', title) else ''), + ('人群词', attr_lookup(raw,['适用人群']) or ('学生' if '学生' in title else '')), + ('场景词', attr_lookup(raw,['适用床尺寸','适用场景'])), + ('材质词', attr_lookup(raw,['床单面料材质','被面材质','成分含量','材质'])), + ('工艺词', attr_lookup(raw,['面料支数','面料密度','床品工艺','织造工艺'])), + ('风格词', attr_lookup(raw,['风格','主图案类型'])), + ] + slot_html=''.join(f'
    {H(k)}{H(concise(v,62,False))}
    ' for k,v in slots if safe(v)) + promos=''.join(f'
  • {H(concise(x.get("text"),58,False))}
  • ' for x in arr(raw.get('promotions'))[:4] if isinstance(x,dict) and safe(x.get('text'))) + return '
    商品标题'+H(concise(title,90,False))+'
    '+slot_html+'
    页面营销表达
      '+promos+'
    ' + +def visual_status_html(images, verified_visual): + rows=[('主图',len(arr(images.get('main'))),bool(arr(images.get('main')))),('详情图',len(arr(images.get('detail'))),bool(arr(images.get('detail')))),('SKU 图',len(arr(images.get('sku'))),bool(arr(images.get('sku')))),('评论实拍',len(arr(images.get('buyerShow'))),bool(arr(images.get('buyerShow'))))] + cards=''.join(f'
    {H(k)}{n}{"已采集" if ok else "未采集"}
    ' for k,n,ok in rows) + body=verified_visual or '

    主图/详情图未形成可核验图片条目时,本章只展示来源状态,不用评论图片替代主图或详情图。

    ' + return '
    '+cards+'
    '+body+'
    ' + +def competition_html(plans, product_exp, overview): + point='' + for plan in arr(plans.get('plans'))[:1]: + if isinstance(plan,dict) and arr(plan.get('sellingPoints')): + point=concise(plan['sellingPoints'][0].get('slogan'),42,False) + risks=[concise(x.get('label'),34,False) for x in arr(product_exp.get('gates'))[:3] if isinstance(x,dict)] + return '
    核心价值'+H(point or '已确认体验可复用')+'

    来自页面参数与评论聚合,不写市场推断。

    基础能力类目关键参数、规格与页面需要和真实商品一致

    基础能力必须被主图、详情和参数共同证明。

    风险点'+H(' / '.join(x for x in risks if x) or '按评论明确问题处理')+'

    风险只按真实评论和参数校验项呈现。

    ' + +def validation_rows(xs): + out = [] + for x in arr(xs)[:6]: + if isinstance(x, dict) and any(safe(x.get(k)) for k in ('stage','metrics','successMeaning','nextAction')): + stage=concise(x.get('stage'),18,False);owner=concise(x.get('owner'),24,False) + if not owner:continue + out.append(f'{H(stage)}{H(owner)}{H(concise(x.get("metrics"), 62))}{H(concise(x.get("successMeaning"), 58))}{H(concise(x.get("nextAction"), 62))}') + return ''.join(out) + + +# V9.2 图片驱动专业报告。分析可以深,默认报告必须轻。 +DISPLAY_MARKERS = ('[数据事实]','[分析判断]','[设计建议]','[证据不足]','数据事实:','分析判断:','设计建议:') + +def clean_display(v, limit=120): + t=text_of(v) if not isinstance(v,str) else v + if not t:return '' + t=re.sub(r'\*\*|__','',t) + for m in DISPLAY_MARKERS:t=t.replace(m,'') + t=re.sub(r'\s*/\s*',';',t) + t=re.sub(r'\s+',' ',t).strip(' ;/') + return concise(t,limit,False) + +def first_text(*values, limit=100): + for v in values: + t=clean_display(v,limit) + if t:return t + return '' + +def _entries(idx, group, limit=200): + return [(eid,e) for eid,e in idx.items() if e.get('type')=='image' and (e.get('meta') or {}).get('group')==group][:limit] + +def _visual_figure(e, alt, css=''): + src,fb=image_source(e) + if not src:return '
    图片未采集
    ' + return '
    '+img_tag(src,alt,fb)+'
    图片暂不可用
    ' + +def _section(no,title,deck,body,sid): + return f'
    {H(no)}

    {H(title)}

    {H(deck)}

    {body}
    ' + +def _opinion(text): + text=clean_display(text,45) + return f'
    {H(text)}
    ' if text else '' + +def _chips(values, limit=3): + vals=[] + for v in arr(values)[:limit]: + t=clean_display(v.get('value') if isinstance(v,dict) else v,10) + if t and t not in vals: vals.append(t) + return '
    '+H(' · '.join(vals))+'
    ' if vals else '' + +def _owner_overview(exp, raw, base, sales): + src=arr((exp.get('ownerOverview') or {}).get('cards')) + wanted=['为什么能卖','值得继承','下一款优先'] + mapped={k:None for k in wanted} + for x in src: + if not isinstance(x,dict): continue + label=clean_display(x.get('label'),16); headline=clean_display(x.get('headline'),20); note=clean_display(x.get('note'),22) + if not headline: continue + for k in wanted: + if k in label and mapped[k] is None: mapped[k]=(headline,note) + # Compatibility fallback from old labels. + legacy={'为什么能卖':['商品定位','场景价值'],'值得继承':['主要产品资产','产品价值'],'下一款优先':['下一款优先方向','可强化方向']} + for x in src: + if not isinstance(x,dict): continue + label=clean_display(x.get('label'),18); headline=clean_display(x.get('headline'),20) + for target,keys in legacy.items(): + if mapped[target] is None and any(k in label for k in keys) and headline: mapped[target]=(headline,'') + pd=arr((exp.get('productValue') or {}).get('items')) + for x in pd: + if not isinstance(x,dict): continue + typ=clean_display(x.get('type'),16); val=first_text(x.get('value'),limit=30) + if typ=='场景价值' and mapped['为什么能卖'] is None: mapped['为什么能卖']=(val,'') + elif typ=='产品价值' and mapped['值得继承'] is None: mapped['值得继承']=(val,'') + elif typ=='可强化方向' and mapped['下一款优先'] is None: mapped['下一款优先']=(val,'') + title=clean_display((raw.get('product') or {}).get('title'),36) + mapped['为什么能卖']=mapped['为什么能卖'] or (title or '当前商品场景与产品组合', '') + mapped['值得继承']=mapped['值得继承'] or (f'已确认 {base.get("attributeCount",0)} 项产品参数', '') + mapped['下一款优先']=mapped['下一款优先'] or ('规格更清楚 · 品质更可证 · 图片更好选', '') + cards=[] + for k in wanted: + headline,note=mapped[k] + cards.append(f'
    {H(k)}{H(headline)}'+(f'

    {H(note)}

    ' if note else '')+'
    ') + return '
    '+''.join(cards)+'
    ' + +def _overview(exp, raw, base, sales): + cards=[] + source=arr((exp.get('ownerOverview') or {}).get('cards')) + preferred={'商品定位':None,'主要产品资产':None,'下一款优先方向':None} + for x in source: + if not isinstance(x,dict):continue + label=clean_display(x.get('label'),18);headline=clean_display(x.get('headline'),42) + if not headline:continue + low=label+headline + if preferred['商品定位'] is None and any(k in low for k in ('定位','场景','人群','商品定义')):preferred['商品定位']=headline + elif preferred['主要产品资产'] is None and any(k in low for k in ('产品','材质','资产','体验','参数')):preferred['主要产品资产']=headline + elif preferred['下一款优先方向'] is None and any(k in low for k in ('下一款','动作','优化','调整')):preferred['下一款优先方向']=headline + pd=(exp.get('productValue') or {}).get('items') or [] + for x in pd: + if not isinstance(x,dict):continue + typ=clean_display(x.get('type'),16);val=clean_display(x.get('value'),42) + if typ=='场景价值' and not preferred['商品定位']:preferred['商品定位']=val + if typ=='产品价值' and not preferred['主要产品资产']:preferred['主要产品资产']=val + if typ=='可强化方向' and not preferred['下一款优先方向']:preferred['下一款优先方向']=val + title=clean_display((raw.get('product') or {}).get('title'),60) + attrs=arr(raw.get('attributes')) + attr_text=' / '.join(clean_display(x.get('value'),30) for x in attrs[:6] if isinstance(x,dict) and clean_display(x.get('value'),30)) + preferred['商品定位']=preferred['商品定位'] or title or '当前商品定位以页面事实为准' + preferred['主要产品资产']=preferred['主要产品资产'] or clean_display(attr_text,70) or f'已采 {base.get("attributeCount",0)} 项商品参数' + preferred['下一款优先方向']=preferred['下一款优先方向'] or '优先把规格、图片表达和商品页面口径统一' + labels=[('商品定位',preferred['商品定位'],'先看这款商品解决什么场景'),('主要产品资产',preferred['主要产品资产'],'只保留当前证据可以确认的资产'),('下一款优先方向',preferred['下一款优先方向'],'把分析转成下一款动作')] + return '
    '+''.join(f'
    {H(l)}{H(v)}

    {H(n)}

    ' for l,v,n in labels)+'
    ' + +def _title_v92(raw, exp, a): + block=exp.get('titleAnalysis') or (a.get('merchandisingDecision') or {}).get('titleAnalysis') or {} + title=clean_display(block.get('originalTitle') or (raw.get('product') or {}).get('title'),130) + opinion=first_text(block.get('professionalOpinion'),block.get('thesis'),limit=58) or '标题优先统一场景、规格与关键品质参数。' + current=[] + for x in arr(block.get('currentExpression'))[:6]: + if isinstance(x,dict): t=first_text(x.get('value'),x.get('text'),limit=10) + else: t=clean_display(x,14) + if t and t not in current: current.append(t) + if not current: + for kw in ('大学生宿舍','学生宿舍','六件套','七件套','全棉','纯棉','40支','四季'): + if kw in title: current.append(kw) + reinforce=[] + for x in arr(block.get('reinforce'))[:5]: + t=first_text(x.get('topic') if isinstance(x,dict) else x,limit=14) + if t and t not in reinforce: reinforce.append(t) + actions=[first_text(x.get('action') if isinstance(x,dict) else x,limit=28) for x in arr(block.get('recommendedActions'))[:2]] + if actions and not reinforce: reinforce=actions + return _opinion(opinion)+f'
    当前标题{H(title)}
    已表达{H(" · ".join(current[:5]) or "按当前标题识别")}
    建议强化{H(" · ".join(reinforce[:5]) or (actions[0] if actions else "规格统一 · 品质参数"))}
    ' + +def _main_v92(exp,a,idx): + block=exp.get('visualCommerce') or {}; vd=a.get('visualDecision') or {} + opinion=first_text(block.get('professionalOpinion'),(vd.get('visualThesis') or {}).get('professionalOpinion'),limit=58) or '主图优先让用户看懂商品信息,再决定是否展开详细分析。' + items=arr(block.get('items')) or arr(vd.get('imageRoles')) or arr(vd.get('mainImageContent')) + byid={safe(x.get('imageEvidenceId')):x for x in items if isinstance(x,dict)} + cards=[] + for n,(eid,e) in enumerate(_entries(idx,'main',5),1): + x=byid.get(eid,{}) + role=first_text(x.get('imageRole'),x.get('role'),x.get('contentTask'),limit=8) or f'主图{n}' + signals=arr(x.get('visualSignals')) + if not signals: + # Compatibility: old message becomes one compact signal, never a paragraph. + msg=first_text(x.get('message'),x.get('observation'),x.get('productSubject'),limit=14) + if msg: signals=[msg] + action=first_text(x.get('nextAction'),x.get('professionalAdvice'),x.get('nextProductAction'),x.get('action'),limit=18) + copy=f'

    {H(role)}

    主图 {n:02d}
    {_chips(signals)}'+(f'

    专业动作 · {H(action)}

    ' if action else '')+'
    ' + cards.append('
    '+_visual_figure(e,'商品主图 '+str(n),'main-ratio')+copy+'
    ') + continuity=_continuity_v92(exp,a) + folded=f'
    查看主图 × 详情 × 评论承接关系{continuity}
    ' if continuity else '' + return _opinion(opinion)+'
    '+''.join(cards)+'
    '+folded + +def _status_label(kind,value): + v=safe(value) + maps={ + 'main':{'expressed':('已表达','ok'),'partial':('部分表达','mid'),'missing':('未覆盖','none')}, + 'detail':{'proven':('已证明','ok'),'partial':('部分证明','mid'),'missing':('未覆盖','none')}, + 'review':{'validated':('有验证','ok'),'partial':('部分验证','mid'),'missing':('暂无验证','none')}, + } + if v in maps.get(kind,{}):return maps[kind][v] + low=v.lower() + if any(k in low for k in ('closed','已表达','已证明','有反馈','已解释','有验证')):return ('已覆盖','ok') + if any(k in low for k in ('partial','有限','部分','pending','待')):return ('部分','mid') + if not v:return ('—','none') + return (clean_display(v,18),'mid') + +def _continuity_v92(exp,a): + block=exp.get('expressionContinuity') or {};checks=arr(block.get('coreClaims')) or arr((a.get('detailDecision') or {}).get('continuityChecks')) + rows=[] + for x in checks[:6]: + if not isinstance(x,dict):continue + claim=clean_display(x.get('claim'),30) + if not claim:continue + ml,mc=_status_label('main',x.get('mainStatus') or x.get('mainImageRole') or ('expressed' if x.get('mainImageEvidenceIds') else '')) + dl,dc=_status_label('detail',x.get('detailStatus') or x.get('detailProof') or ('proven' if x.get('detailEvidenceIds') else '')) + rl,rc=_status_label('review',x.get('reviewStatus') or x.get('reviewValidation')) + adv=first_text(x.get('professionalAdvice'),x.get('nextProductAction'),x.get('gap'),limit=45) + rows.append(f'{H(claim)}{H(ml)}{H(dl)}{H(rl)}{H(adv or "继续保持页面口径一致")}') + if not rows:return '' + return '
    '+''.join(rows)+'
    核心表达主图详情评论动作
    ' + +def _detail_v92(exp,a,idx): + block=exp.get('detailCommerce') or {}; dd=a.get('detailDecision') or {} + opinion=first_text(block.get('professionalOpinion'),(dd.get('detailThesis') or {}).get('professionalOpinion'),limit=58) or '详情页优先展示能证明商品价值的代表图。' + groups=arr(block.get('contentGroups')) or arr(dd.get('contentGroups')) + entries=dict(_entries(idx,'detail',30)); cards=[] + for i,x in enumerate(groups[:6],1): + if not isinstance(x,dict): continue + eid=safe(x.get('representativeImageId') or x.get('imageEvidenceId')); e=entries.get(eid) + if not e and entries: e=list(entries.values())[min(i-1,len(entries)-1)] + if not e: continue + typ=first_text(x.get('type'),x.get('stage'),limit=10) or '详情信息' + signals=arr(x.get('visualSignals')) + if not signals: + msg=first_text(x.get('message'),x.get('businessMeaning'),limit=14) + if msg: signals=[msg] + action=first_text(x.get('nextAction'),x.get('professionalAdvice'),x.get('nextPageAction'),limit=24) + cards.append('
    '+_visual_figure(e,typ,'wide-ratio')+f'

    {H(typ)}

    {_chips(signals)}'+(f'

    专业动作 · {H(action)}

    ' if action else '')+'
    ') + collected=len(entries) + note=f'
    详情图 {collected} 张 · 默认展示 {len(cards)} 个信息任务代表图;其余图片保留为证据。
    ' + return _opinion(opinion)+('
    '+''.join(cards)+'
    ' if cards else '
    详情图片已采集,当前未形成可靠分组。
    ')+note + +def _sku_dimensions(raw): + product=raw.get('product') or {};title=safe(product.get('title'));attrs=arr(raw.get('attributes'));sku=arr(raw.get('sku')) + dims=[] + def add(name,vals,source=''): + vals=[clean_display(x,24) for x in vals if clean_display(x,24)] + if vals and name not in [d[0] for d in dims]:dims.append((name,vals[:5])) + if '床单' in title or '床笠' in title:add('款式',[x for x in ('床单','床笠') if x in title]) + for x in attrs: + if not isinstance(x,dict):continue + n=safe(x.get('name'));v=safe(x.get('value')) + if any(k in n for k in ('尺寸','床尺寸','规格')) and v:add('尺寸',[v]) + elif any(k in n for k in ('颜色','花色','图案','款式')) and v:add('花色/款式',[v]) + elif any(k in n for k in ('件数','套件','组合')) and v:add('套件',[v]) + if sku: + names=[] + for x in sku[:20]: + if isinstance(x,dict): + val=first_text(x.get('name'),x.get('title'),x.get('value'),x.get('properties'),limit=40) + if val:names.append(val) + if names:add('SKU规格',names) + return dims[:4] + +def _sku_v92(exp,a,raw,idx): + block=exp.get('skuAnalysis') or a.get('skuVisualAnalysis') or {} + entries=_entries(idx,'sku',30); structured=arr(raw.get('sku')) + opinion=first_text(block.get('professionalOpinion'),limit=90) + dims=[] + for x in arr(block.get('selectionDimensions')): + if not isinstance(x,dict):continue + name=clean_display(x.get('name'),18);values=[clean_display(v,24) for v in arr(x.get('values')) if clean_display(v,24)] + if name:dims.append((name,values)) + if not dims:dims=_sku_dimensions(raw) + order=[clean_display(x,18) for x in arr(block.get('recommendedOrder')) if clean_display(x,18)] or [x[0] for x in dims] + if not opinion: + opinion='当前SKU更适合按清晰选择层级组织:先解决规格是否适配,再处理组合和外观偏好。' + mosaic=''.join(_visual_figure(e,'SKU '+str(i+1),'square-ratio') for i,(eid,e) in enumerate(entries[:8])) + steps=''.join(f'
    {i:02d}{H(name)}'+(f'{H(" / ".join(vals[:3]))}' if vals else '')+'
    ' for i,(name,vals) in enumerate(dims or [(x,[]) for x in order],1)) + advice=first_text(block.get('recommendedAction'),limit=55) or ('建议按“'+' → '.join(order)+'”分层选择。' if order else '建议先建立规格层级,再展示外观选择。') + status='结构化SKU '+(str(len(structured))+' 个' if structured else '未完整采集')+f' · SKU图片 {len(entries)} 张' + return _opinion(opinion)+f'
    {mosaic}
    {steps}
    {H(advice)}
    {H(status)};SKU图片数量不等于SKU数量。
    ' + +def _consumer_v92(exp,a,idx): + block=exp.get('customerExperience') or {}; cr=a.get('consumerResearch') or {}; ci=block.get('consumerInsights') or {} + opinion=first_text(block.get('professionalOpinion'),(cr.get('consumerThesis') or {}).get('statement'),limit=58) or '消费者反馈只保留可指导下一款的真实体验信号。' + signals=arr(ci.get('experienceSignals')) + if not signals: + mapping=[('稳定体验',cr.get('satisfactionDrivers')),('体验差异',cr.get('expectationGaps')),('购前关注',cr.get('prePurchaseConcerns'))] + for typ,values in mapping: + for x in arr(values)[:1]: signals.append({'type':typ,'insight':text_of(x)}) + buyer=_entries(idx,'buyerShow',12) + gallery=''.join(_visual_figure(e,'买家秀 '+str(i+1),'square-ratio') for i,(eid,e) in enumerate(buyer[:4])) + rows=[] + for x in signals[:4]: + if not isinstance(x,dict): continue + typ=clean_display(x.get('type'),16) or '消费者信号'; ins=first_text(x.get('insight'),x.get('statement'),x.get('driver'),x.get('problem'),limit=38) + if ins: rows.append(f'
    {H(typ)}{H(ins)}
    ') + gallery_html=gallery or '
    未采集买家秀图片
    ' + rows_html=''.join(rows) or '
    当前评论尚未形成可靠聚合洞察
    ' + return _opinion(opinion)+f'
    {gallery_html}
    {rows_html}
    ' + +def _value_v92(exp,a): + block=exp.get('productValue') or {};items=arr(block.get('items')) + if not items: + pd=a.get('productDecision') or {};pillars=arr(pd.get('valuePillars')) + for x in pillars[:3]: + if isinstance(x,dict):items.append({'type':clean_display(x.get('name'),18) or '产品价值','value':first_text(x.get('customerValue'),x.get('businessMeaning'),limit=40),'explanation':first_text(x.get('mustKeep'),limit=55)}) + if not items: + items=[{'type':'场景价值','value':'当前商品使用场景','explanation':'以标题、参数与图片证据为准'},{'type':'产品价值','value':'当前商品核心配置','explanation':'只保留页面已确认参数'},{'type':'服务价值','value':'页面已展示服务','explanation':'不推断未采集服务'},{'type':'可强化方向','value':'规格与证明表达','explanation':'优先保证图片、参数、SKU口径一致'}] + cards=[] + for x in items[:4]: + if not isinstance(x,dict):continue + typ=clean_display(x.get('type'),18);val=first_text(x.get('value'),x.get('customerValue'),limit=42);desc=first_text(x.get('explanation'),x.get('commercialValue'),x.get('mustKeep'),limit=58) + if val:cards.append(f'
    {H(typ)}{H(val)}'+(f'

    {H(desc)}

    ' if desc else '')+'
    ') + return _opinion(block.get('professionalOpinion'))+'
    '+''.join(cards)+'
    ' + +def _plan_action_list(values, limit=3): + out=[] + for x in arr(values)[:limit]: + t=first_text(x.get('action') if isinstance(x,dict) else x,x.get('item') if isinstance(x,dict) else '',limit=55) + if t:out.append(t) + return out + +def _plans_v92(exp,a,idx): + block=exp.get('newProductPlans') or {}; source=arr(block.get('plans')) or arr((a.get('launchPlans') or {}).get('plans')) + opinion=first_text(block.get('professionalOpinion'),(a.get('launchPlans') or {}).get('professionalOpinion'),limit=58) or '优先从证据最强的优化方向开始,再看反馈升级与探索方向。' + cards=[] + for i,x in enumerate(source[:3],1): + if not isinstance(x,dict): continue + source_type=first_text(x.get('sourceType'),limit=10) + typ=first_text(x.get('type'),limit=14) + if not source_type: + source_type='证据驱动优化' if i==1 else ('反馈驱动升级' if i==2 else '探索性方向') + name=first_text(x.get('name'),limit=22) or ('新品方向 '+str(i)) + why=first_text(x.get('whyThisPlan'),x.get('positioning'),limit=48) + product=first_text(x.get('productAction'),limit=38) + if not product: + acts=_plan_action_list(x.get('productChanges'),1) or _plan_action_list(x.get('mustChange'),1); product=acts[0] if acts else '' + page=first_text(x.get('pageAction'),limit=38) + if not page: + main=_plan_action_list(x.get('mainImagePlan'),1); detail=_plan_action_list(x.get('detailPagePlan'),1); page=';'.join((main+detail)[:2]) + refs=arr(x.get('visualReferenceImageIds')); ref_e=None + for eid in refs: + if eid in idx and (idx[eid].get('meta') or {}).get('group') in ('main','detail','sku'): ref_e=idx[eid]; break + if not ref_e: + mains=_entries(idx,'main',5); ref_e=mains[min(i-1,len(mains)-1)][1] if mains else None + visual=_visual_figure(ref_e,'方案参考图','plan-ratio') if ref_e else '
    当前商品视觉参考
    ' + body=f'
    方案 {chr(64+i)} · {H(source_type)}

    {H(name)}

    '+(f'
    产品
    {H(product)}
    ' if product else '')+(f'
    页面
    {H(page)}
    ' if page else '')+'
    ' + cards.append(f'
    {visual}{body}
    ') + if not cards: return _opinion(opinion)+'
    当前证据未形成可追溯新品方案。
    ' + return _opinion(opinion)+'
    '+''.join(cards)+'
    ' + +def render(a, task_id): + a=ensure_experience_solution(a) + if not (a.get('meta') or {}).get('reportReady'): raise ValueError((a.get('meta') or {}).get('reportBlockedReason') or '本次没有可输出的证据结论') + raw=a.get('facts') or {}; product=raw.get('product') or {}; sales=raw.get('sales') or {}; base=a.get('baseline') or {}; exp=a.get('experienceSolution') or {}; idx=evidence_index(a) + summary=exp.get('reportSummary') or {} + title=clean_display(summary.get('title') or product.get('title') or '商品分析报告',60) + verdict=first_text(summary.get('verdict'),limit=58) or '基于当前商品页面、图片、参数与评论证据形成开品建议。' + cover_id=safe(summary.get('coverImageId')); cover_e=idx.get(cover_id) if cover_id else None + if not cover_e: + mains=_entries(idx,'main',1); cover_e=mains[0][1] if mains else None + price='¥'+safe(sales.get('currentPrice')) if safe(sales.get('currentPrice')) else '价格未采集' + sold=safe(sales.get('sold') or sales.get('cumulativeSales')) or '销量未采集' + meta_line=f'{price} · 公开销量 {sold} · 评论 {base.get("reviewCount",0)} · SKU图 {len(_entries(idx,"sku",999))} · 参数 {base.get("attributeCount",0)}' + nav='' + cover=_visual_figure(cover_e,'商品封面图','cover-ratio') if cover_e else '
    主图未采集
    ' + hero=f'
    PRODUCT DECISION REPORT · BIG VISUAL · V9.2.5

    {H(title)}

    {H(meta_line)}

    {H(clean_display(verdict,45))}
    {cover}
    ' + doc=''+H(title)+''+nav+hero+_section('01','老板 30 秒看懂','只看结果,不看分析过程',_owner_overview(exp,raw,base,sales),'decision')+_section('02','下一款先做什么','先给决策,再看证据',_plans_v92(exp,a,idx),'plan')+_section('03','标题只解决一个问题:说清楚','保留表达与动作',_title_v92(raw,exp,a),'title')+_section('04','主图到底在卖什么','图片本身就是成交语言',_main_v92(exp,a,idx),'main')+_section('05','详情页证明了什么','大图展示证明内容',_detail_v92(exp,a,idx),'detail')+_section('06','先解决适配,再解决审美','SKU看选择路径',_sku_v92(exp,a,raw,idx),'sku')+_section('07','消费者真正感受到什么','买家秀是证据',_consumer_v92(exp,a,idx),'review')+f'
    Task {H(task_id)} · 三笙商品开品分析报告引擎 V9.2.5 · 未采集经营指标不作推断
    ' + return doc diff --git a/server/report.css b/server/report.css new file mode 100644 index 0000000..146101f --- /dev/null +++ b/server/report.css @@ -0,0 +1,303 @@ + +:root{ + --paper:#fff;--paper2:#f5f2ee;--ink:#171614;--muted:#6b6760; + --line:#ddd8d1;--dark:#181715;--dark2:#24211d;--cream:#ebe4db; + --good:#3f6f58;--risk:#9a4d43;--ask:#9a7a4f;--accent:#b88b5b +} +*{box-sizing:border-box}html{scroll-behavior:smooth} +body{margin:0;background:var(--paper2);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC","Helvetica Neue",Arial,sans-serif} +.wrap{width:min(1380px,calc(100% - 72px));margin:auto} +nav{height:64px;position:sticky;top:0;z-index:40;background:rgba(255,255,255,.95);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)} +.navin{height:100%;display:flex;align-items:center;justify-content:space-between}.brand{font-size:13px;font-weight:800} +.navlinks{display:flex;gap:5px}.navlinks a{text-decoration:none;font-size:11px;color:#6f685f;padding:8px 10px;border-radius:999px}.navlinks a:hover{background:#201e1b;color:#fff} +.hero{min-height:0;background:linear-gradient(180deg,#fff 0%,#f8f4ef 100%);padding:72px 0 60px} +.hero-grid{display:grid;grid-template-columns:1fr .72fr;gap:58px;align-items:center} +.hero-grid.text-only{grid-template-columns:minmax(0,1100px)} +.kicker{font-size:11px;letter-spacing:0;color:#938980;margin-bottom:16px} +h1{margin:0;font-size:56px;line-height:1.06;letter-spacing:0;font-weight:720} +.hero-copy{font-size:19px;line-height:1.78;color:#625c55;max-width:900px;margin:28px 0 0} +.hero-copy-block h1{max-width:840px}.fact-line{font-size:13px;line-height:1.6;color:#6b645d;margin:24px 0 0}.hero-verdict{margin-top:34px;padding:20px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.hero-verdict span,.hero-actions>span{display:block;font-size:10px;color:#918981;margin-bottom:8px}.hero-verdict strong{display:block;font-size:25px;line-height:1.35}.hero-actions{margin-top:22px}.hero-actions .gate{padding:9px 0;grid-template-columns:155px 1fr}.hero-actions .gate b{font-size:12px}.hero-actions .gate span{font-size:13px;line-height:1.5} +.hero-card{background:#f5f7f6;border:1px solid var(--line);padding:27px} +.hero-card span{font-size:10px;color:#8e857c}.hero-card strong{display:block;font-size:35px;line-height:1.22;margin:10px 0} +.hero-card p{font-size:14px;line-height:1.72;color:#68615a;margin:0} +.hero-kpis{display:grid;grid-template-columns:repeat(2,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line);margin-top:22px} +.hero-kpi{background:#fff;padding:15px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)} +.hero-kpi small{font-size:10px;color:#918981}.hero-kpi b{display:block;font-size:23px;margin-top:5px} + +.chapter{padding:68px 0}.white{background:#fff}.soft{background:#f4f6f5}.ink{background:var(--dark);color:#fff} +.section-head{display:grid;grid-template-columns:1fr .47fr;gap:44px;align-items:end;margin-bottom:40px} +.section-index{display:flex;gap:19px}.section-index>span{font-size:12px;color:#958c83;margin-top:9px} +.section-kicker{display:block;font-size:10px;letter-spacing:0;color:#948a81;margin-bottom:9px} +h2{font-size:44px;line-height:1.16;letter-spacing:0;margin:0} +.section-head.compact{display:block;margin-bottom:24px}.section-head.compact h2{font-size:48px;max-width:1050px}.evidence-note{font-size:12px;color:#8a8178;margin:0 0 24px} +.section-head>p{font-size:15px;line-height:1.72;color:#6b645d;margin:0}.ink .section-head>p{color:#c5bdb5} + +.shopper-decision{border-top:1px solid var(--line);border-bottom:1px solid var(--line)} +.decision-boundary{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--line);border-left:1px solid var(--line);margin-bottom:24px} +.decision-boundary article{padding:17px;background:#f7f8f7;border-right:1px solid var(--line);border-bottom:1px solid var(--line);min-width:0} +.decision-boundary span{display:block;font-size:10px;color:#817970}.decision-boundary strong{display:block;font-size:15px;line-height:1.48;margin:7px 0;overflow-wrap:anywhere}.decision-boundary small{display:block;font-size:10px;line-height:1.5;color:#777f7a} +.shopper-verdict{display:grid;grid-template-columns:120px 1fr;gap:18px;padding:24px 0;border-bottom:1px solid var(--line);align-items:start} +.shopper-verdict>span{font-size:10px;color:#7f776f;margin-top:6px}.shopper-verdict strong{font-size:24px;line-height:1.4}.shopper-verdict small{grid-column:2;font-size:11px;color:#77706a;margin-top:-8px} +.shopper-groups{display:grid;grid-template-columns:1fr 1fr}.shopper-group{padding:24px 26px 26px 0;border-bottom:1px solid var(--line)}.shopper-group:nth-child(even){padding-left:26px;padding-right:0;border-left:1px solid var(--line)}.shopper-group.full{grid-column:1/-1;padding-left:0;border-left:0}.shopper-group.full .shopper-grid{grid-template-columns:repeat(3,1fr)} +.shopper-group h3{font-size:16px;margin:0 0 14px}.shopper-grid{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--line);border-left:1px solid var(--line)} +.shopper-row{padding:14px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);min-width:0}.shopper-row strong{display:block;font-size:12px;line-height:1.4}.shopper-row p{font-size:14px;line-height:1.55;margin:6px 0 0;overflow-wrap:anywhere}.shopper-meta{display:block;font-size:9px;color:#817970;margin-bottom:7px}.shopper-row.check{background:#f3f6f4}.shopper-row.question{background:#faf8f4} +.owner-thesis{font-size:22px;line-height:1.45;margin:-8px 0 12px;max-width:980px} + +.owner{border:1px solid var(--line);background:#faf8f5} +.owner>header{padding:28px 30px;border-bottom:1px solid var(--line)} +.owner>header>span{font-size:10px;color:#8f867d}.owner>header h3{font-size:34px;line-height:1.3;letter-spacing:0;margin:9px 0} +.owner>header p{font-size:13px;color:#736b63;margin:0} +.owner-grid{display:grid;grid-template-columns:repeat(3,1fr)} +.owner-card{background:#fff;padding:21px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);min-height:0}.owner-card strong{font-size:21px;line-height:1.38}.owner-card ul{padding-left:16px;margin:12px 0 0}.owner-card li{font-size:12px;line-height:1.5;margin:4px 0} +.owner-card:nth-child(3n){border-right:0}.owner-card.wide{grid-column:span 2} +.owner-card span{font-size:10px;color:#918981}.owner-card strong{display:block;font-size:27px;line-height:1.3;margin:10px 0} +.owner-card p{font-size:14px;line-height:1.72;color:#68625b;margin:0}.owner-card ul{padding-left:18px;margin:13px 0 0} +.owner-card li{font-size:13px;line-height:1.58;margin:5px 0;color:#5e5852}.owner-card.good strong{color:var(--good)}.owner-card.risk strong{color:var(--risk)} + +.story{margin-top:28px}.story-item{display:grid;grid-template-columns:.9fr 1.1fr;border-top:1px solid var(--line);padding:25px 0;gap:38px;align-items:start}.story-item:last-child{border-bottom:1px solid var(--line)} +.story-media{margin:0;background:#fff;border:1px solid var(--line);display:flex;justify-content:center;align-items:flex-start;padding:10px}.story-media img{width:100%;height:auto;object-fit:contain;display:block} +.story-copy>span{font-size:10px;color:#918981}.story-copy h3{font-size:25px;line-height:1.3;margin:9px 0 18px}.story-copy>p{font-size:15px;line-height:1.75;color:#68625b} +.story-copy .source-badge{display:inline-block;color:#405e4b;border:1px solid #b9c8bf;padding:6px 8px;background:#f3f7f4} +.evidence{display:grid;grid-template-columns:1fr;border-left:1px solid var(--line);border-top:1px solid var(--line);margin-top:18px} +.evidence div{padding:13px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)} +.evidence span{display:block;font-size:10px;color:#918981}.evidence b{display:block;font-size:13px;line-height:1.55;margin-top:4px} +.evidence .action{background:#eef2f0} + +.param-strip{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line)} +.param{background:#fff;padding:16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.param span{font-size:10px;color:#918981}.param b{display:block;font-size:13px;line-height:1.55;margin-top:6px} + +.voice-stats{display:grid;grid-template-columns:repeat(6,1fr);border:1px solid #4a443e} +.voice-stat{padding:17px;border-right:1px solid #4a443e}.voice-stat:last-child{border-right:0} +.voice-stat span{display:block;font-size:10px;color:#aaa199}.voice-stat strong{display:block;font-size:28px;margin:6px 0}.voice-stat small{font-size:10px;color:#bbb2aa} +.result-block{margin-top:28px}.result-block h3{font-size:17px;margin:0 0 12px}.result-grid{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid #4a443e;border-left:1px solid #4a443e} +.result-row{padding:16px;border-right:1px solid #4a443e;border-bottom:1px solid #4a443e;min-width:0}.result-row>span{display:block;font-size:9px;color:#aaa199;margin-bottom:7px}.result-row strong{display:block;font-size:15px;line-height:1.4}.result-row p{font-size:13px;line-height:1.6;color:#d1c9c1;margin:7px 0 0;overflow-wrap:anywhere}.result-row.positive{background:#29312c}.result-row.risk{background:#342724}.result-row.question{background:#302d27} +.journey-wrap{margin-top:28px;border:1px solid #4a443e;background:#282520} +.journey-head{padding:22px;border-bottom:1px solid #4a443e}.journey-head span{font-size:10px;color:#aaa199}.journey-head h3{font-size:25px;line-height:1.28;margin:8px 0}.journey-head p{font-size:14px;line-height:1.7;color:#c4bcb4;margin:0} +.journey-tabs{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid #4a443e} +.jtab{padding:15px;border-right:1px solid #4a443e;background:#24211e;color:#d2cac2;cursor:pointer}.jtab:last-child{border-right:0}.jtab.active{background:#fff;color:#1a1815} +.jtab small{font-size:9px;display:block;opacity:.65}.jtab b{font-size:13px;display:block;margin-top:5px} +.jpanel{display:none;padding:22px}.jpanel.active{display:block} +.jpanel>header span{font-size:10px;color:#aaa199}.jpanel>header h4{font-size:28px;margin:8px 0}.jpanel>header p{font-size:14px;color:#c5bdb5} +.jgrid{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid #4a443e;border-top:1px solid #4a443e;margin-top:20px} +.jgrid section{padding:15px;border-right:1px solid #4a443e;border-bottom:1px solid #4a443e}.jgrid span{font-size:10px;color:#aaa199}.jgrid p{font-size:13px;line-height:1.6;color:#d1c9c1;margin:5px 0 0} +.jmeta{display:flex;gap:9px;margin-top:16px}.jmeta span{font-size:10px;border:1px solid #514a43;padding:6px 9px;border-radius:999px;color:#c7bfb7} +.quotes{margin:16px 0 0;padding-left:20px}.quotes li{font-size:13px;color:#cfc7bf;line-height:1.65;margin:7px 0} +.buyer-evidence{margin-top:24px;border:1px solid #4a514d;background:#252a27;padding:22px}.buyer-evidence>header{padding:0 0 18px;border-bottom:1px solid #4a514d}.buyer-evidence>header span{font-size:10px;color:#aeb8b2}.buyer-evidence>header h3{font-size:24px;margin:7px 0}.buyer-evidence>header p{font-size:12px;line-height:1.55;color:#aeb8b2;margin:0}.buyer-groups{display:grid;gap:18px;padding-top:18px}.buyer-review-group{display:grid;grid-template-columns:minmax(220px,300px) minmax(0,1fr);gap:18px;border:1px solid #48504b;background:#202522;padding:16px;min-width:0}.buyer-review-copy{padding:4px}.buyer-review-copy span{font-size:10px;color:#aeb8b2}.buyer-review-copy blockquote{font-size:14px;line-height:1.65;margin:10px 0;color:#f0f3f1;overflow-wrap:anywhere}.buyer-review-copy small{display:block;font-size:10px;line-height:1.5;color:#aeb8b2}.buyer-gallery{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;align-items:start}.buyer-gallery figure{margin:0;background:#fff;min-width:0}.buyer-gallery img{width:100%;height:auto;object-fit:contain;display:block}.buyer-gallery figcaption{padding:7px 8px;color:#68716c;background:#eef1ef;font-size:9px;border-top:1px solid #d8dedb} + +.signal-atlas{margin-top:24px;border:1px solid #4a443e;padding:22px;background:#26231f} +.signal-atlas span.label{font-size:10px;color:#aaa199}.signal-atlas h3{font-size:30px;margin:8px 0}.signal-atlas p{font-size:14px;color:#c5bdb5} +.signals{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}.signal{font-size:12px;border:1px solid #575049;border-radius:999px;padding:8px 11px}.signal.good{color:#bfd4bd}.signal.ask{color:#ddc9a9}.signal.risk{color:#e2b1a9} + +.product-box{display:grid;grid-template-columns:.82fr 1.18fr;gap:25px} +.param-card,.product-conclusion{background:#fff;border:1px solid var(--line);padding:25px} +.param-card h3,.product-conclusion h3{font-size:31px;line-height:1.28;margin:0 0 17px} +.prow{display:grid;grid-template-columns:135px 1fr;gap:18px;padding:13px 0;border-top:1px solid var(--line)}.prow:first-of-type{border-top:0} +.prow span{font-size:10px;color:#918981}.prow b{font-size:14px;line-height:1.6} +.product-conclusion>p{font-size:15px;line-height:1.76;color:#68625b} +.gate{padding:14px 0;border-top:1px solid var(--line);display:grid;grid-template-columns:145px 1fr;gap:20px}.gate b{font-size:13px}.gate span{font-size:14px;line-height:1.65;color:#67615a} + +.plans{display:grid;grid-template-columns:1fr 1fr;gap:20px} +.plan{border:1px solid #4a443e;background:#282520;padding:24px}.plan>span{font-size:10px;color:#aaa199}.plan h3{font-size:27px;line-height:1.25;margin:9px 0}.plan>p{font-size:13px;line-height:1.65;color:#c9c1b9} +.plan-specs{display:grid;grid-template-columns:1fr 1fr;border-left:1px solid #4a443e;border-top:1px solid #4a443e;margin-top:19px} +.plan-specs div{padding:13px;border-right:1px solid #4a443e;border-bottom:1px solid #4a443e}.plan-specs span{font-size:10px;color:#aaa199;display:block}.plan-specs b{font-size:13px;line-height:1.55;display:block;margin-top:4px} +.plan-section{padding:15px 0;border-top:1px solid #4a443e}.plan-section h4{font-size:17px;margin:0 0 6px}.plan-section p{font-size:13px;line-height:1.65;color:#c6beb6;margin:0} +.plan-actions{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:18px}.plan-actions h4{font-size:15px;margin:0 0 7px}.action-list{padding-left:17px;margin:0}.action-list li{font-size:12px;line-height:1.55;color:#c6beb6;margin:5px 0} +.commercial{margin-top:26px;border:1px solid #4a443e}.commercial header{padding:21px;border-bottom:1px solid #4a443e} +.commercial header span{font-size:10px;color:#aaa199}.commercial header strong{font-size:27px;display:block;margin-top:6px} +.commercial-grid{display:grid;grid-template-columns:repeat(3,1fr)}.commercial-grid p{padding:20px;margin:0;border-right:1px solid #4a443e}.commercial-grid p:last-child{border-right:0} +.commercial-grid span{font-size:10px;color:#aaa199;display:block}.commercial-grid b{font-size:14px;line-height:1.62;display:block;margin-top:5px} +.governance{background:#181715;color:#fff;border-top:1px solid #2f2b26;border-bottom:1px solid #2f2b26}.governance-grid{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid #3b352e}.governance article{padding:16px 18px;border-right:1px solid #3b352e}.governance span{display:block;font-size:9px;color:#bfb4a8;margin-bottom:7px}.governance b{font-size:12px;line-height:1.55;color:#f5efe8} +.selling-points{margin-top:22px;border:1px solid #4d443a;background:#211e1a}.selling-points header{padding:18px 18px 0}.selling-points header span{font-size:9px;color:#bfb4a8}.selling-points h4{font-size:22px;margin:6px 0 14px}.selling-points>div{display:grid;grid-template-columns:repeat(2,1fr);border-top:1px solid #4d443a;border-left:1px solid #4d443a}.selling-points article{padding:16px;border-right:1px solid #4d443a;border-bottom:1px solid #4d443a;min-width:0}.selling-points article strong{display:block;font-size:18px;line-height:1.35;color:#fff}.selling-points article p{font-size:12px;line-height:1.55;color:#d5ccc2;margin:8px 0}.selling-points article small{display:block;font-size:10px;color:#b8ab9c}.selling-points article b{display:block;font-size:12px;line-height:1.55;color:#d8bea1;margin-top:8px} +.profile-board{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line);background:#fff}.profile-cell{padding:16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);min-width:0}.profile-cell span,.market-slot span{display:block;font-size:10px;color:#918981}.profile-cell b,.market-slot b{display:block;font-size:14px;line-height:1.55;margin-top:6px;overflow-wrap:anywhere} +.logic-chain{display:grid;grid-template-columns:repeat(5,1fr);gap:12px}.logic-step{background:#fff;border:1px solid var(--line);padding:20px;min-height:180px}.logic-step span{font-size:11px;color:#918981}.logic-step strong{display:block;font-size:22px;line-height:1.28;margin:10px 0}.logic-step p{font-size:13px;line-height:1.6;color:#67615a;margin:0 0 12px}.logic-step small{font-size:10px;color:#8c847b} +.feature-grid{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid #4a443e;border-top:1px solid #4a443e}.feature-card{padding:20px;border-right:1px solid #4a443e;border-bottom:1px solid #4a443e;min-height:150px;background:#24211e}.feature-card span{font-size:10px;color:#aaa199}.feature-card strong{display:block;font-size:23px;line-height:1.32;margin:9px 0;color:#fff}.feature-card p{font-size:12px;line-height:1.58;color:#cbc3bb;margin:0}.feature-card.good strong{color:#c6dcc4}.feature-card.risk strong{color:#e0b1a9} +.marketing-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:18px}.marketing-grid>article{background:#fff;border:1px solid var(--line);padding:22px;min-width:0}.marketing-grid>article>span{font-size:10px;color:#918981}.marketing-grid strong{display:block;font-size:26px;line-height:1.35;margin:9px 0 18px}.marketing-grid ul{margin:14px 0 0;padding-left:18px}.marketing-grid li{font-size:14px;line-height:1.65;margin:7px 0}.market-slots{display:grid;grid-template-columns:repeat(2,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line)}.market-slot{padding:14px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)} +.visual-states{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line);margin-bottom:18px}.visual-state{background:#fff;padding:17px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.visual-state span{font-size:10px;color:#918981}.visual-state strong{display:block;font-size:28px;margin:5px 0}.visual-state small{font-size:10px;color:#77706a}.visual-state.ready strong{color:var(--good)}.visual-state.missing strong{color:var(--risk)} +.competition-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.competition-grid article{border:1px solid var(--line);background:#fff;padding:24px;min-height:190px}.competition-grid span{font-size:10px;color:#918981}.competition-grid strong{display:block;font-size:28px;line-height:1.28;margin:9px 0}.competition-grid p{font-size:13px;line-height:1.65;color:#67615a;margin:0}.competition-grid .strong strong{color:var(--good)}.competition-grid .mid strong{color:var(--ask)}.competition-grid .weak strong{color:var(--risk)} +.validation-wrap{margin-top:28px;border:1px solid #4a443e;background:#221f1b;padding:22px;overflow-x:auto}.validation-wrap header{margin-bottom:14px}.validation-wrap span{font-size:10px;color:#aaa199}.validation-wrap h3{font-size:26px;margin:7px 0;color:#fff}.validation-wrap .validation{min-width:980px} + +.validation{width:100%;border-collapse:collapse;background:#fff;border:1px solid var(--line)} +.validation th,.validation td{text-align:left;vertical-align:top;padding:13px;border-bottom:1px solid var(--line)}.validation th{font-size:10px;color:#8e857c}.validation td{font-size:12px;line-height:1.55}.validation tr:last-child td{border-bottom:0} +.footer{padding:38px 0 52px;font-size:11px;color:#8c847b} +.reveal{opacity:1;transform:none} +@media(max-width:1000px){.wrap{width:min(1380px,calc(100% - 28px))}.hero-grid,.section-head,.story-item,.product-box,.marketing-grid{grid-template-columns:1fr}.decision-boundary{grid-template-columns:1fr 1fr}.owner-grid,.voice-stats,.journey-tabs,.jgrid,.param-strip,.result-grid,.governance-grid,.profile-board,.feature-grid,.visual-states{grid-template-columns:1fr 1fr}.logic-chain,.competition-grid{grid-template-columns:1fr}.owner-card.wide{grid-column:auto}.plans{grid-template-columns:1fr}.commercial-grid{grid-template-columns:1fr}#validate .wrap{overflow-x:auto}.validation{min-width:980px}.shopper-group.full .shopper-grid{grid-template-columns:1fr 1fr}.buyer-review-group{grid-template-columns:240px minmax(0,1fr)}.buyer-gallery{grid-template-columns:repeat(2,minmax(0,1fr))}} +@media(max-width:680px){nav{height:auto}.navin{min-height:56px}.navlinks{display:none}.hero{padding:42px 0 34px}h1{font-size:38px}.hero-shot{order:-1;padding:8px}.hero-verdict strong{font-size:20px}.hero-actions .gate{grid-template-columns:1fr;gap:4px}.chapter{padding:46px 0}.section-head.compact h2{font-size:34px}.decision-boundary{grid-template-columns:1fr}.owner-grid,.voice-stats,.journey-tabs,.jgrid,.param-strip,.hero-kpis,.plan-specs,.plan-actions,.shopper-groups,.shopper-grid,.shopper-group.full .shopper-grid,.result-grid,.governance-grid,.selling-points>div,.profile-board,.feature-grid,.visual-states,.market-slots{grid-template-columns:1fr}.shopper-verdict{grid-template-columns:1fr;gap:7px}.shopper-verdict small{grid-column:1;margin-top:0}.shopper-group,.shopper-group:nth-child(even),.shopper-group.full{grid-column:1;padding:20px 0;border-left:0}.buyer-evidence{padding:16px}.buyer-review-group{grid-template-columns:1fr;padding:12px}.buyer-gallery{grid-template-columns:repeat(2,minmax(0,1fr))}.owner-card{min-height:auto}.section-index{gap:12px}.story-media{height:auto}.story-copy h3{font-size:23px}.owner-thesis{font-size:19px}.logic-step,.feature-card,.competition-grid article{min-height:0}.marketing-grid strong{font-size:22px}} + +.hero-shot{min-height:360px;background:#f8f5f1;border:1px solid var(--line);display:flex;align-items:center;justify-content:center;padding:14px;box-shadow:0 24px 80px rgba(39,31,24,.08)}.hero-shot img,.story-media img{width:100%;height:auto;object-fit:contain;display:block}.scroll-progress{position:fixed;left:0;top:0;width:100%;height:2px;z-index:90}.scroll-progress span{display:block;height:100%;background:#1d211f;transform-origin:left;transform:scaleX(0)} +.loading-page{min-height:100vh;background:#eef1ef;padding:34px;color:#171a18}.loading-shell{width:min(1180px,100%);margin:auto;background:#fff;border:1px solid #d8dedb}.loading-head{display:flex;justify-content:space-between;gap:30px;padding:34px 36px 28px;border-bottom:1px solid var(--line)}.loading-head h1{font-size:42px;line-height:1.12;margin:9px 0 12px}.loading-head p{font-size:14px;line-height:1.6;color:#646b67;margin:0;max-width:720px}.loading-head>strong{font-size:42px;line-height:1;color:#556d5a}.loading-evidence,.loading-work{padding:25px 36px;border-bottom:1px solid var(--line)}.loading-section-title{display:flex;justify-content:space-between;align-items:baseline;gap:20px;margin-bottom:15px}.loading-section-title>span{font-size:16px;font-weight:750}.loading-section-title>small{font-size:11px;color:#717873}.loading-stats{display:grid;grid-template-columns:repeat(7,1fr);border-top:1px solid var(--line);border-left:1px solid var(--line)}.loading-stats>div{padding:13px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.loading-stats span{display:block;font-size:10px;color:#727a75}.loading-stats b{display:block;font-size:23px;margin-top:6px}.loading-images{display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:8px;margin-top:14px}.loading-images figure{margin:0;border:1px solid var(--line);background:#f5f7f6}.loading-images img{display:block;width:100%;aspect-ratio:3/4;object-fit:contain;background:#fff}.loading-images figcaption{font-size:9px;color:#69706c;padding:6px 7px;border-top:1px solid var(--line)}.loading-steps{display:grid;grid-template-columns:repeat(2,1fr);border-top:1px solid var(--line);border-left:1px solid var(--line)}.loading-step{display:grid;grid-template-columns:10px 1fr auto;align-items:center;gap:10px;padding:11px 13px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.loading-step i{width:8px;height:8px;border:1px solid #929a95;border-radius:50%}.loading-step span{font-size:12px}.loading-step b{font-size:10px;color:#7b837e;font-weight:600}.loading-step.done i{background:#556d5a;border-color:#556d5a}.loading-step.done b{color:#556d5a}.loading-step.processing{background:#f1f5f2}.loading-step.processing i{background:#171a18;border-color:#171a18}.loading-step.not_collected{background:#faf8f5}.loading-step.not_collected i{border-color:#a88a64}.loading-step.not_collected b{color:#8d6f4c}.loading-step.failed{background:#fff2f0}.loading-step.failed i{background:#a2463d;border-color:#a2463d}.loading-step.failed b{color:#a2463d}.loading-page .topic-bar{height:5px;background:#e5e9e6;margin:0 36px 28px}.loading-page .topic-bar i{display:block;height:100%;background:#556d5a;transition:width .35s ease}.loading-page .error{color:#a2463d;white-space:pre-wrap;margin:0 36px 28px}.loading-page .eyebrow{font-size:10px;color:#7c847f} +@media(max-width:680px){h1{font-size:38px}} +@media(max-width:800px){.loading-page{padding:0}.loading-shell{border-left:0;border-right:0}.loading-head{padding:25px 18px 22px}.loading-head h1{font-size:32px}.loading-head>strong{font-size:28px}.loading-evidence,.loading-work{padding:21px 18px}.loading-section-title{display:block}.loading-section-title small{display:block;margin-top:6px}.loading-stats{grid-template-columns:repeat(2,1fr)}.loading-images{grid-template-columns:repeat(4,1fr)}.loading-steps{grid-template-columns:1fr}.loading-page .topic-bar{margin:0 18px 24px}} + +/* V9:白色阅读背景,青绿到青蓝为品牌强调色。 */ +:root{--paper:#fff;--paper2:#f5faf9;--ink:#10191d;--muted:#60757c;--line:#dbe8e8;--dark:#071114;--dark2:#0d2025;--good:#00a98f;--risk:#d34f5f;--ask:#167ca7;--accent:#00cdb0;--accent2:#008fd8;--gradient:linear-gradient(110deg,#00d9b6 0%,#00acd0 56%,#008bd8 100%)} +body{background:#fff;color:var(--ink)}nav{background:rgba(255,255,255,.96);border-color:#dce9e9;box-shadow:0 8px 26px rgba(8,54,63,.06)} +.brand{background:var(--gradient);-webkit-background-clip:text;background-clip:text;color:transparent;font-weight:900}.navlinks a{color:#526d74}.navlinks a:hover{background:#071114;color:#fff} +.hero{background:linear-gradient(180deg,#fff 0%,#f1fbf9 100%);position:relative;overflow:hidden}.hero:before{content:"";position:absolute;inset:0 0 auto auto;width:46%;height:5px;background:var(--gradient)} +.kicker,.section-kicker{color:#008fae;font-weight:800;letter-spacing:.08em}.hero-verdict{border-color:#bfe6e1}.hero-shot{background:#fff;border-color:#cfe4e4;box-shadow:0 28px 70px rgba(0,105,128,.13)} +.scroll-progress span{background:var(--gradient)}.soft{background:#f5faf9}.white{background:#fff}.ink{background:#071114;color:#f2fbfc}.ink .section-kicker{color:#12d8bb}.ink .section-head h2{color:#fff} +.evidence-tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:22px}.evidence-tags b{font-size:10px;padding:7px 10px;border:1px solid #b9e2df;border-radius:999px;background:#fff;color:#24717b}.evidence-tags b:nth-child(2){border-color:#9dd5ef;color:#117ba7}.evidence-tags b:nth-child(3){background:var(--gradient);color:#fff;border:0}.evidence-tags b:nth-child(4){color:#708187} +.title-original{padding:26px 28px;border:1px solid var(--line);background:#071114;color:#fff}.title-original span{display:block;font-size:10px;color:#7fb9c1;margin-bottom:9px}.title-original strong{font-size:25px;line-height:1.5} +.title-segments{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line)}.title-segments article{padding:18px;background:#fff;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.title-segments span,.analysis-strip span{display:block;font-size:10px;color:#698087}.title-segments strong{display:block;font-size:17px;line-height:1.4;margin:8px 0}.title-segments p{font-size:12px;line-height:1.6;color:#5c7076;margin:0} +.analysis-strip{display:grid;grid-template-columns:repeat(3,1fr);margin-top:18px;border:1px solid #cde4e3;background:#effaf8}.analysis-strip article{padding:18px;border-right:1px solid #cde4e3}.analysis-strip article:last-child{border:0}.analysis-strip b{display:block;margin-top:8px;font-size:13px;line-height:1.6} +.image-analysis-list{display:grid;gap:24px}.image-analysis{display:grid;grid-template-columns:minmax(300px,.9fr) minmax(0,1.1fr);border:1px solid var(--line);background:#fff;box-shadow:0 16px 42px rgba(15,63,72,.07)}.image-analysis figure{margin:0;padding:12px;background:#f7fbfa;border-right:1px solid var(--line)}.image-analysis img{display:block;width:100%;height:auto;max-height:720px;object-fit:contain;background:#fff}.image-analysis figcaption{padding:10px 4px 0;font-size:10px;color:#668087}.image-copy{display:grid;grid-template-columns:1fr 1fr;align-content:start}.image-copy>div{padding:17px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.image-copy span{display:block;font-size:10px;color:#668087}.image-copy b{display:block;font-size:13px;line-height:1.62;margin-top:6px;overflow-wrap:anywhere} +.empty-state{padding:30px;border:1px dashed #a8cdca;background:#f3fbf9;color:#61777d;text-align:center;font-size:13px}.table-scroll{overflow:auto}.table-scroll .validation{min-width:840px} +.asset-levels,.dna-grid,.parameter-grid,.shot-grid{display:grid;grid-template-columns:repeat(4,1fr);border-left:1px solid var(--line);border-top:1px solid var(--line);margin-top:24px}.asset-levels article,.dna-grid article,.parameter-grid article,.shot-grid article{padding:20px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);background:#fff;color:var(--ink)}.asset-levels span,.dna-grid span,.parameter-grid span,.shot-grid span{font-size:10px;color:#008ca7}.asset-levels li,.asset-levels p,.dna-grid p,.parameter-grid p,.shot-grid p{font-size:13px;line-height:1.62;color:#526a70}.shot-grid strong{display:block;font-size:18px;line-height:1.4;margin:9px 0}.shot-grid small{font-size:10px;color:#75898e}.subhead{font-size:22px;margin:34px 0 0} +.ink .plans{grid-template-columns:repeat(3,1fr)}.ink .plan{border-color:#24444a;background:#0d2025}.ink .asset-levels article{background:#0d2025;color:#fff;border-color:#31565a}.ink .asset-levels li,.ink .asset-levels p{color:#bad0d3} +@media(max-width:1000px){.title-segments,.analysis-strip,.asset-levels,.dna-grid,.parameter-grid,.shot-grid{grid-template-columns:1fr 1fr}.image-analysis{grid-template-columns:1fr}.image-analysis figure{border-right:0;border-bottom:1px solid var(--line)}.ink .plans{grid-template-columns:1fr}} +@media(max-width:620px){.title-segments,.analysis-strip,.asset-levels,.dna-grid,.parameter-grid,.shot-grid,.image-copy{grid-template-columns:1fr}} + + +/* V9.1 information hierarchy additions — preserves existing palette */ +.evidence-fold{margin-top:18px;border-top:1px solid var(--line);padding-top:14px}.evidence-fold>summary{cursor:pointer;font-size:13px;font-weight:700;padding:10px 0;list-style:none}.evidence-fold>summary::-webkit-details-marker{display:none}.evidence-fold>summary:after{content:'+';float:right}.evidence-fold[open]>summary:after{content:'-'} +.insight-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:18px 0}.insight-card{padding:18px;border:1px solid var(--line);background:var(--card,#fff)}.insight-card span,.consumer-thesis span,.continuity header span{display:block;font-size:11px;color:var(--muted);margin-bottom:7px}.insight-card strong,.consumer-thesis strong{display:block;font-size:18px;line-height:1.45}.insight-card p{margin:8px 0 0;font-size:13px;line-height:1.55;color:var(--muted)} +.consumer-thesis{padding:20px 0;border-bottom:1px solid var(--line);margin-bottom:14px}.continuity{margin:22px 0}.continuity header h3{margin:0 0 12px}.sku-status{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-bottom:16px}.sku-status article{padding:16px;border:1px solid var(--line);background:var(--card,#fff)}.sku-status span{display:block;font-size:11px;color:var(--muted);margin-bottom:6px}.sku-status strong{font-size:15px;line-height:1.4}.recommended-titles{margin-top:14px;padding:16px;border:1px solid var(--line)}.recommended-titles>span{font-size:11px;color:var(--muted)}.recommended-titles ul{margin:8px 0 0;padding-left:18px}.image-error{opacity:.18;min-height:120px;background:repeating-linear-gradient(45deg,transparent,transparent 8px,rgba(127,127,127,.08) 8px,rgba(127,127,127,.08) 16px)} +@media(max-width:900px){.insight-grid{grid-template-columns:1fr 1fr}.sku-status{grid-template-columns:1fr 1fr}}@media(max-width:560px){.insight-grid,.sku-status{grid-template-columns:1fr}} + +/* ========================================================= + V9.2.0 IMAGE-DRIVEN PROFESSIONAL REPORT + 图片 > 专业意见 > 专业建议 > 证据 + ========================================================= */ +:root{ + --v92-ink:#17202a;--v92-text:#465364;--v92-muted:#8a95a3;--v92-line:#e8edf2; + --v92-soft:#f7f9fb;--v92-blue:#396bc8;--v92-blue-soft:#f2f6fd;--v92-white:#fff; +} +body{background:#fff;color:var(--v92-text);font-size:14px;line-height:1.55} +.wrap{width:min(1180px,calc(100% - 40px));margin:auto} +.v92-nav{height:52px;position:sticky;top:0;z-index:50;background:rgba(255,255,255,.96);backdrop-filter:blur(10px);border-bottom:1px solid var(--v92-line);box-shadow:none} +.v92-nav .wrap{height:100%;display:flex;align-items:center;justify-content:space-between;gap:18px} +.v92-nav .brand{font-size:13px;color:var(--v92-ink);font-weight:760;background:none;-webkit-background-clip:initial;background-clip:initial} +.v92-links{display:flex;gap:18px;overflow:auto}.v92-links a{font-size:12px;color:var(--v92-muted);text-decoration:none;white-space:nowrap;padding:0}.v92-links a:hover{background:none;color:var(--v92-blue)} +.v92-hero{padding:42px 0 30px;border-bottom:1px solid var(--v92-line);background:#fff;min-height:0} +.v92-hero:before{display:none}.v92-hero-grid{display:grid;grid-template-columns:1.08fr .92fr;gap:42px;align-items:center} +.v92-kicker{font-size:10px;font-weight:800;color:var(--v92-blue);letter-spacing:.08em;margin-bottom:8px} +.v92-hero h1{margin:0;color:var(--v92-ink);font-size:32px;line-height:1.2;letter-spacing:-.02em;font-weight:720} +.v92-meta{font-size:12px;color:var(--v92-muted);margin:10px 0 0} +.v92-cover{min-width:0}.v92-cover .visual{background:transparent;border:0;border-radius:0;box-shadow:none}.v92-cover img{display:block;width:100%;max-height:390px;object-fit:contain;background:transparent} +.cover-ratio{aspect-ratio:auto!important}.v92-cover-empty{min-height:280px;display:grid;place-items:center;background:var(--v92-soft);color:var(--v92-muted)} +.v92-opinion{margin:20px 0 0;padding:14px 16px;background:var(--v92-blue-soft);border-left:3px solid var(--v92-blue);border-radius:8px;display:grid;grid-template-columns:78px 1fr;gap:14px} +.v92-opinion span{font-size:10px;color:var(--v92-blue);font-weight:800}.v92-opinion strong{font-size:15px;line-height:1.55;color:var(--v92-ink);font-weight:700} +.v92-section{padding:42px 0;background:#fff}.v92-section+.v92-section{border-top:1px solid var(--v92-line)} +.v92-head{display:flex;align-items:baseline;gap:10px;margin-bottom:18px}.v92-head>span{font-size:10px;color:var(--v92-blue);font-weight:800}.v92-head h2{margin:0;font-size:21px;color:var(--v92-ink);line-height:1.25}.v92-head p{margin:0 0 0 auto;font-size:11px;color:var(--v92-muted)} +.v92-section>.wrap>.v92-opinion{margin-top:0;margin-bottom:18px} +.v92-summary3{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--v92-line);border-bottom:1px solid var(--v92-line)} +.v92-summary3 article{padding:15px 17px;border-right:1px solid var(--v92-line)}.v92-summary3 article:last-child{border-right:0} +.v92-summary3 span,.v92-title3 span{display:block;font-size:10px;color:var(--v92-muted);font-weight:700}.v92-summary3 b,.v92-title3 b{display:block;margin-top:5px;color:var(--v92-ink);font-size:13px;line-height:1.5}.v92-summary3 p{margin:4px 0 0;color:var(--v92-muted);font-size:11px} +.v92-title-current{padding:14px 16px;background:var(--v92-soft);border-radius:8px;color:var(--v92-ink);margin-bottom:13px}.v92-title-current small{display:block;font-size:10px;color:var(--v92-muted);margin-bottom:4px} +.v92-title3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.v92-title3 article{padding:15px;border:1px solid var(--v92-line);border-radius:9px;background:#fff} +.v92-flow{display:flex;gap:7px;align-items:center;flex-wrap:wrap;margin:7px 0 17px}.v92-flow .node{font-size:11px;color:var(--v92-ink);font-weight:700;padding:6px 9px;background:var(--v92-soft);border-radius:99px}.v92-flow i{font-style:normal;color:#b7c0ca} +.v92-main-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}.v92-main-card{min-width:0}.visual{margin:0;position:relative;background:var(--v92-soft);border-radius:9px;overflow:hidden}.visual img{width:100%;height:100%;display:block;object-fit:cover}.main-ratio{aspect-ratio:4/5}.wide-ratio{aspect-ratio:16/10}.square-ratio{aspect-ratio:1/1}.plan-ratio{aspect-ratio:4/3} +.image-fallback{display:none;width:100%;height:100%;min-height:120px;align-items:center;justify-content:center;color:var(--v92-muted);font-size:10px;text-align:center;padding:16px}.visual.failed .image-fallback{display:flex}.visual.failed{background:var(--v92-soft)} +.v92-main-copy{padding:8px 1px 0}.v92-main-copy span{display:block;color:var(--v92-ink);font-size:12px;font-weight:760}.v92-main-copy b{display:block;margin-top:2px;color:var(--v92-text);font-size:11px;line-height:1.45}.v92-main-copy p{margin:3px 0 0;color:var(--v92-muted);font-size:10px;line-height:1.45} +.v92-table-wrap{overflow:auto;margin-top:18px}.v92-matrix{width:100%;border-collapse:collapse;min-width:720px}.v92-matrix th,.v92-matrix td{padding:10px 11px;border-bottom:1px solid var(--v92-line);text-align:left;font-size:11px;vertical-align:top}.v92-matrix th{font-size:9px;color:var(--v92-muted);background:var(--v92-soft)}.v92-matrix .ok{color:#3c765e;font-weight:800}.v92-matrix .mid{color:#a06d2f;font-weight:800}.v92-matrix .none{color:#9aa3ae} +.v92-detail-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.v92-detail-card{border:1px solid var(--v92-line);border-radius:10px;overflow:hidden;background:#fff}.v92-detail-card>div:last-child{padding:12px}.v92-detail-card span{font-size:9px;color:var(--v92-blue);font-weight:800}.v92-detail-card b{display:block;margin-top:3px;color:var(--v92-ink);font-size:13px}.v92-detail-card p{margin:4px 0 0;color:var(--v92-muted);font-size:10.5px}.v92-evidence-note{margin-top:12px;font-size:10px;color:var(--v92-muted)} +.v92-sku-layout{display:grid;grid-template-columns:1.15fr .85fr;gap:24px;align-items:start}.v92-sku-mosaic{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.v92-choice{border-left:1px solid var(--v92-line);padding-left:20px}.v92-choice-row{display:grid;grid-template-columns:36px 1fr;gap:10px;padding:9px 0;border-bottom:1px solid var(--v92-line)}.v92-choice-row span{font-size:9px;color:var(--v92-blue);font-weight:800}.v92-choice-row b{font-size:12px;color:var(--v92-ink)}.v92-choice-row small{grid-column:2;font-size:9px;color:var(--v92-muted)}.v92-choice-note{margin-top:13px;padding:12px;background:var(--v92-blue-soft);border-radius:8px;font-size:11px;color:#345077}.v92-status{display:block;margin-top:9px;font-size:9px;color:var(--v92-muted)} +.v92-review-layout{display:grid;grid-template-columns:1.15fr .85fr;gap:24px;align-items:start}.v92-buyer-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.v92-insights{border-top:1px solid var(--v92-line)}.v92-insights article{padding:12px 0;border-bottom:1px solid var(--v92-line)}.v92-insights span{display:block;font-size:9px;color:var(--v92-muted);font-weight:750}.v92-insights b{display:block;margin-top:3px;color:var(--v92-ink);font-size:12.5px}.v92-insights p{margin:4px 0 0;color:var(--v92-muted);font-size:10.5px} +.v92-value-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}.v92-value-grid article{padding:15px;border-top:2px solid var(--v92-line);background:var(--v92-soft);border-radius:0 0 8px 8px}.v92-value-grid span{font-size:9px;color:var(--v92-muted);font-weight:800}.v92-value-grid b{display:block;margin-top:5px;color:var(--v92-ink);font-size:13px}.v92-value-grid p{margin:4px 0 0;color:var(--v92-muted);font-size:10.5px} +.v92-plan-stack{display:grid;gap:16px}.v92-plan{display:grid;grid-template-columns:190px 1fr 1fr;gap:20px;padding-top:16px;border-top:1px solid var(--v92-line)}.v92-plan:first-child{border-top:0;padding-top:0}.v92-plan-copy small{font-size:9px;color:var(--v92-blue);font-weight:800}.v92-plan-copy h3{margin:3px 0 7px;color:var(--v92-ink);font-size:17px}.v92-plan-copy p{margin:0;color:var(--v92-muted);font-size:11px}.v92-plan-points{display:grid;gap:8px}.v92-plan-points>div{padding:10px 12px;background:var(--v92-soft);border-radius:8px}.v92-plan-points span{display:block;font-size:9px;color:var(--v92-muted);font-weight:750}.v92-plan-points b{display:block;margin-top:3px;color:var(--v92-ink);font-size:11px}.v92-plan-placeholder{aspect-ratio:4/3;background:var(--v92-soft);display:grid;place-items:center;color:var(--v92-muted);font-size:10px;border-radius:9px} +.v92-empty{padding:18px;background:var(--v92-soft);color:var(--v92-muted);font-size:11px;border-radius:8px}.v92-footer{padding:28px 0 40px;border-top:1px solid var(--v92-line);font-size:10px;color:var(--v92-muted);background:#fff} +.image-error{display:none!important} +@media(max-width:900px){.v92-hero-grid{grid-template-columns:1fr}.v92-summary3,.v92-title3,.v92-detail-grid,.v92-value-grid{grid-template-columns:1fr}.v92-main-grid{grid-template-columns:repeat(2,1fr)}.v92-sku-layout,.v92-review-layout{grid-template-columns:1fr}.v92-choice{border-left:0;border-top:1px solid var(--v92-line);padding:16px 0 0}.v92-plan{grid-template-columns:1fr}.v92-head p{display:none}} +@media(max-width:560px){.wrap{width:min(100% - 24px,1180px)}.v92-hero h1{font-size:27px}.v92-opinion{grid-template-columns:1fr;gap:5px}.v92-main-grid{grid-template-columns:1fr 1fr}.v92-sku-mosaic,.v92-buyer-grid{grid-template-columns:repeat(2,1fr)}} + +/* ========================================================= + V9.2.5 OWNER + PROFESSIONAL USER FINAL REPORT + 老板先决策;专业用户再看图片证据 + 图片完整展示,不裁切 + ========================================================= */ +.visual img{object-fit:contain!important;background:var(--v92-soft)} +.v92-cover .visual,.v92-cover img{background:transparent!important} +.main-ratio{aspect-ratio:auto!important;height:310px} +.wide-ratio{aspect-ratio:auto!important;height:260px} +.square-ratio{aspect-ratio:auto!important;height:145px} +.plan-ratio{aspect-ratio:auto!important;height:190px} +.v92-buyer-grid .square-ratio{height:160px} +.v92-cover .cover-ratio{height:auto;min-height:300px;max-height:420px} +.v92-cover .cover-ratio img{height:auto!important;max-height:420px;width:100%;object-fit:contain!important} + +.v924-decision3 article{padding:15px 16px} +.v924-decision3 b{font-size:14px} +.v924-decision3 p{font-size:10.5px;margin-top:4px} + +.v924-title2{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px} +.v924-title2 article{padding:13px 14px;border:1px solid var(--v92-line);border-radius:8px;background:#fff} +.v924-title2 span{display:block;font-size:9px;color:var(--v92-muted);font-weight:760} +.v924-title2 b{display:block;margin-top:5px;color:var(--v92-ink);font-size:12.5px;line-height:1.45} + +.v924-image-info{padding-top:8px} +.v924-row-title{display:flex;align-items:baseline;justify-content:space-between;gap:8px} +.v924-row-title strong{font-size:12px;color:var(--v92-ink)} +.v924-row-title span{font-size:8px;color:var(--v92-muted)} +.v924-chips{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px} +.v924-chips i{font-style:normal;padding:3px 6px;border-radius:99px;background:var(--v92-soft);color:#586675;font-size:9px;line-height:1.35} +.v924-image-info p,.v924-detail-info p{margin:7px 0 0;font-size:10px;color:var(--v92-text);line-height:1.4} +.v924-image-info p b{font-size:8px;color:var(--v92-blue);margin-right:5px} + +.v924-details{margin-top:15px;border-top:1px solid var(--v92-line)} +.v924-details summary{padding:12px 0;cursor:pointer;color:var(--v92-blue);font-size:11px;font-weight:700;list-style:none} +.v924-details summary::-webkit-details-marker{display:none} +.v924-details .v92-table-wrap{margin-top:0} + +.v924-detail-info{padding:10px 11px!important} +.v924-detail-info strong{font-size:12px!important;color:var(--v92-ink)} + +.v924-plan-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px} +.v924-plan-card{border:1px solid var(--v92-line);border-radius:9px;overflow:hidden;background:#fff} +.v924-plan-body{padding:12px} +.v924-plan-eyebrow{font-size:8px;color:var(--v92-blue);font-weight:800} +.v924-plan-body h3{margin:4px 0 8px;color:var(--v92-ink);font-size:15px;line-height:1.3} +.v924-plan-body>p{margin:0 0 8px;color:var(--v92-muted);font-size:10.5px;line-height:1.45} +.v924-plan-body dl{margin:0} +.v924-plan-body dl div{display:grid;grid-template-columns:36px 1fr;gap:6px;padding:7px 0;border-top:1px solid var(--v92-line)} +.v924-plan-body dt{font-size:8px;color:var(--v92-muted);font-weight:760} +.v924-plan-body dd{margin:0;font-size:10.5px;color:var(--v92-text);line-height:1.45} + +/* V9.2.5 removes repeated explanatory subcopy from default reading layer. */ +.v92-evidence-note{font-size:9px} +.v92-insights article p{display:none} +.v92-summary3 p:empty{display:none} + +@media(max-width:900px){ + .v924-title2,.v924-plan-grid{grid-template-columns:1fr} + .main-ratio{height:340px}.wide-ratio{height:300px}.plan-ratio{height:240px} +} +@media(max-width:560px){ + .main-ratio{height:280px}.wide-ratio{height:240px}.square-ratio{height:150px}.plan-ratio{height:220px} +} + + +/* ========================================================= + V9.2.5 BIG VISUAL / BIG TYPE FINAL REPORT + Big images tell facts; big type tells judgement; short lines tell actions. + ========================================================= */ +:root{--v925-ink:#141b24;--v925-text:#44505d;--v925-muted:#8a95a3;--v925-line:#e8edf2;--v925-soft:#f6f8fa;--v925-blue:#315fba;--v925-blue-soft:#eef4ff} +.wrap{width:min(1240px,calc(100% - 56px))} +.v92-nav .wrap{height:58px}.v92-nav .brand{font-size:15px}.v92-links{gap:22px}.v92-links a{font-size:13px} +.v92-hero{padding:58px 0 42px}.v92-hero-grid{grid-template-columns:.9fr 1.1fr;gap:54px;align-items:center} +.v92-kicker{font-size:12px;letter-spacing:.08em}.v92-hero h1{font-size:48px;line-height:1.12;letter-spacing:-.035em;margin:10px 0 14px}.v92-meta{font-size:14px} +.v925-hero-claim{margin-top:28px;font-size:26px;line-height:1.48;font-weight:800;color:var(--v925-ink);max-width:620px} +.v92-cover .cover-ratio{min-height:420px;max-height:530px}.v92-cover .cover-ratio img{max-height:530px!important} +.v92-section{padding:70px 0}.v92-head{margin-bottom:28px}.v92-head>span{font-size:12px}.v92-head h2{font-size:36px;line-height:1.2;letter-spacing:-.025em}.v92-head p{font-size:12px} +.v925-opinion{display:block!important;padding:0!important;background:transparent!important;border:0!important;margin:0 0 34px!important;max-width:940px}.v925-opinion strong{font-size:22px!important;line-height:1.55!important;color:var(--v925-ink)!important} +.v92-summary3{gap:28px;border:0}.v92-summary3 article{padding:20px 0 0;border-right:0!important;border-top:2px solid var(--v925-ink)}.v92-summary3 span{font-size:13px}.v92-summary3 b{font-size:28px;line-height:1.25;margin-top:9px}.v92-summary3 p{font-size:15px;line-height:1.5;margin-top:8px} +.v92-title-current{padding:18px 20px;border-radius:12px;font-size:17px;margin-bottom:18px}.v92-title-current small{font-size:12px}.v924-title2{gap:18px}.v924-title2 article{padding:22px;border-radius:12px}.v924-title2 span{font-size:12px}.v924-title2 b{font-size:22px;line-height:1.35;margin-top:7px} +.visual{background:var(--v925-soft);border-radius:14px}.visual img{object-fit:contain!important}.main-ratio,.wide-ratio,.square-ratio,.plan-ratio{aspect-ratio:auto} +.v92-main-grid{grid-template-columns:1fr 1fr;gap:28px}.v925-main-card:nth-child(5){grid-column:1/-1;max-width:620px}.v925-main-card .main-ratio{height:560px}.v925-main-card:nth-child(5) .main-ratio{height:620px} +.v925-image-info{padding:16px 2px 0}.v925-row-title{display:flex;justify-content:space-between;align-items:baseline;gap:12px}.v925-row-title h3{font-size:24px;margin:0;color:var(--v925-ink)}.v925-row-title span{font-size:12px;color:var(--v925-muted)} +.v925-signals{margin-top:9px;font-size:18px;line-height:1.5;font-weight:760;color:var(--v925-ink)}.v925-action{margin:9px 0 0!important;font-size:14px!important;line-height:1.45!important;color:var(--v925-blue)!important;font-weight:800!important} +.v924-details{margin-top:26px}.v924-details summary{font-size:14px;padding:16px 0} +.v925-detail-list{display:grid;gap:54px}.v925-detail-block{display:grid;grid-template-columns:1.2fr .8fr;gap:34px;align-items:center}.v925-detail-block:nth-child(even){grid-template-columns:.8fr 1.2fr}.v925-detail-block:nth-child(even) .visual{order:2}.v925-detail-block .wide-ratio{min-height:520px}.v925-detail-copy h3{font-size:30px;line-height:1.2;margin:0 0 14px;color:var(--v925-ink)}.v925-detail-copy .v925-signals{font-size:20px}.v925-detail-copy .v925-action{font-size:15px!important}.v92-evidence-note{font-size:12px;margin-top:24px} +.v92-sku-layout{grid-template-columns:1.3fr .7fr;gap:36px}.v92-sku-mosaic{grid-template-columns:repeat(2,1fr);gap:12px}.v92-sku-mosaic .square-ratio{height:300px}.v92-choice{padding-left:24px}.v92-choice-row{padding:17px 0;grid-template-columns:42px 1fr}.v92-choice-row span{font-size:11px}.v92-choice-row b{font-size:21px}.v92-choice-row small{font-size:13px;line-height:1.45}.v92-choice-note{margin-top:22px;padding:18px;border-radius:12px;font-size:17px;font-weight:760}.v92-status{font-size:11px;margin-top:12px} +.v92-review-layout{grid-template-columns:1.15fr .85fr;gap:38px}.v92-buyer-grid{grid-template-columns:1fr 1fr;gap:12px}.v92-buyer-grid .square-ratio{height:330px}.v92-insights article{padding:20px 0}.v92-insights span{font-size:12px}.v92-insights b{font-size:24px;line-height:1.35;margin-top:6px} +.v925-plan-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}.v925-plan-card{border:1px solid var(--v925-line);border-radius:16px;overflow:hidden;background:#fff}.v925-plan-card .plan-ratio{height:300px}.v925-plan-body{padding:22px}.v925-plan-eyebrow{font-size:12px;color:var(--v925-blue);font-weight:800}.v925-plan-body h3{font-size:25px;line-height:1.25;margin:6px 0 14px;color:var(--v925-ink)}.v925-plan-body dl{margin:0}.v925-plan-body dl div{display:grid;grid-template-columns:52px 1fr;gap:10px;padding:12px 0;border-top:1px solid var(--v925-line)}.v925-plan-body dt{font-size:12px;color:var(--v925-muted);font-weight:800}.v925-plan-body dd{margin:0;font-size:15px;color:var(--v925-ink);line-height:1.5} +.v92-footer{font-size:12px;padding:34px 0 54px} +@media(max-width:900px){.wrap{width:min(100% - 28px,1240px)}.v92-hero-grid,.v92-sku-layout,.v92-review-layout,.v925-detail-block,.v925-detail-block:nth-child(even){grid-template-columns:1fr}.v92-hero h1{font-size:38px}.v925-hero-claim{font-size:21px}.v92-summary3,.v924-title2,.v925-plan-grid{grid-template-columns:1fr}.v92-main-grid{grid-template-columns:1fr}.v925-main-card:nth-child(5){grid-column:auto;max-width:none}.v925-detail-block:nth-child(even) .visual{order:0}.v92-head h2{font-size:30px}.v92-head p{display:none}.v92-sku-mosaic{grid-template-columns:1fr 1fr}.v92-buyer-grid{grid-template-columns:1fr 1fr}} +@media(max-width:560px){.v92-hero h1{font-size:32px}.v925-hero-claim{font-size:19px}.v925-main-card .main-ratio,.v925-main-card:nth-child(5) .main-ratio{height:440px}.v925-detail-block .wide-ratio{min-height:400px}.v92-sku-mosaic .square-ratio,.v92-buyer-grid .square-ratio{height:220px}} +/* JSON report view */ +.report-app .wrap{max-width:1180px;margin:0 auto;padding:0 24px} +.report-section{padding:42px 0;border-bottom:1px solid #e8ece7} +.report-section h2{margin:0 0 22px;font-size:28px} +.report-app .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px} +.report-app .card{background:#fff;border:1px solid #e1e7e0;border-radius:14px;padding:18px;box-shadow:0 5px 18px #172b1a0b} +.report-app .card h3{margin:0 0 10px;font-size:17px}.report-app .card p{margin:7px 0;line-height:1.6} +.report-app .gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:18px} +.report-app figure{margin:0;background:#f4f7f3;border-radius:12px;overflow:hidden}.report-app figure img{width:100%;height:220px;object-fit:contain;display:block}.report-app figcaption{padding:7px 10px;font-size:12px;color:#647064} diff --git a/server/report.html b/server/report.html new file mode 100644 index 0000000..d190202 --- /dev/null +++ b/server/report.html @@ -0,0 +1,5 @@ + + +商品开品分析报告 +

    正在加载报告数据…

    + diff --git a/server/report.js b/server/report.js new file mode 100644 index 0000000..d75fefa --- /dev/null +++ b/server/report.js @@ -0,0 +1,26 @@ +/* 独立报告视图:页面只负责渲染 JSON,不在 Python 中拼接 HTML。 */ +const esc = v => String(v ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); +const arr = v => Array.isArray(v) ? v : []; +const text = (v, keys=['value','label','headline','note','insight','action','name']) => { + if (Array.isArray(v)) return v.map(x => text(x, keys)).filter(Boolean).join(' · '); + if (typeof v === 'string' || typeof v === 'number') return String(v); + if (v && typeof v === 'object') for (const k of keys) if (v[k] != null && v[k] !== '') return text(v[k], keys); + return ''; +}; +function cards(items, fields=['headline','note']) { + return arr(items).map(x => `

    ${esc(text(x,['label','name','type']))}

    ${fields.map(k=>text(x,[k])).filter(Boolean).map(v=>`

    ${esc(v)}

    `).join('')}
    `).join(''); +} +function images(data, group, limit=8) { + const list = arr(data.evidenceLedger).filter(e => e?.type === 'image' && e?.meta?.group === group).slice(0,limit); + return list.map(e => `
    商品${esc(group)}
    ${esc(e.id)}
    `).join('') || '

    未采集

    '; +} +function section(id, title, body) { return `

    ${esc(title)}

    ${body}
    `; } +async function main() { + const q = new URLSearchParams(location.search); const source = window.REPORT_DATA || q.get('data'); + if (!source) throw new Error('缺少 data 参数'); + const data = await fetch(source).then(r => r.json()); const exp = data.experienceSolution || {}; const raw = data.facts || {}; const p = raw.product || {}; const s = raw.sales || {}; + const summary = exp.reportSummary || {}; document.title = summary.title || p.title || '商品开品分析报告'; + const owner = exp.ownerOverview?.cards || []; const plans = exp.newProductPlans?.plans || []; const title = exp.titleAnalysis || {}; const visual = exp.visualCommerce || {}; const detail = exp.detailCommerce || {}; + document.getElementById('app').innerHTML = `
    PRODUCT DECISION REPORT · JSON VIEW

    ${esc(summary.title || p.title || '商品分析报告')}

    ${esc(summary.verdict || '')}

    价格 ${esc(s.currentPrice || '未采集')} · 评论 ${esc(data.baseline?.reviewCount || 0)}
    ${section('decision','老板速览',`
    ${cards(owner)}
    `)}${section('plan','下一款方向',`
    ${cards(plans,['sourceType','whyThisPlan','productAction','pageAction'])}
    `)}${section('title','标题表达',`

    ${esc(title.professionalOpinion || '')}

    `)}${section('main','主图在卖什么',`
    ${cards(visual.items,['imageRole','visualSignals','nextAction'])}
    `)}${section('detail','详情页证明什么',`
    ${cards(detail.contentGroups,['type','visualSignals','nextAction'])}
    `)}${section('sku','SKU怎么选',`

    ${esc(exp.skuAnalysis?.professionalOpinion || '')}

    ${cards(exp.skuAnalysis?.selectionDimensions,['name','values','evidenceIds'])}`)}${section('review','消费者实际感受到什么',`${cards(exp.customerExperience?.consumerInsights?.experienceSignals,['type','insight'])}`)}
    数据来自本次采集证据;页面由独立 HTML + JSON 渲染
    `; +} +main().catch(e => { document.getElementById('app').innerHTML = `

    报告加载失败:${esc(e.message)}

    `; }); diff --git a/server/server.py b/server/server.py new file mode 100644 index 0000000..f008687 --- /dev/null +++ b/server/server.py @@ -0,0 +1,531 @@ +import json, os, threading, time, uuid, traceback, html, base64, csv, io, zipfile, re, mimetypes, hashlib, urllib.request, urllib.error +import xml.etree.ElementTree as ET +from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler +from urllib.parse import urlparse, unquote_to_bytes +from pathlib import Path +from analysis import analyze, normalize +from renderer import render +from ai_client import configured, load_config, probe_model_api +from prompt_templates import TEMPLATES, PRODUCT_VARIABLES + +ROOT=Path(__file__).resolve().parent +PROMPT_CONFIG=ROOT/'prompts.json' +REPORTS=ROOT/'reports'; REPORTS.mkdir(exist_ok=True) +MONITOR_DIR=ROOT/'monitor_data'; MONITOR_DIR.mkdir(exist_ok=True) +PORT=int(os.getenv('TMALL_AI_PORT','17962')) +SERVER_VERSION='9.2.5' +MODEL_PROBE={'ok':False,'stage':'startup','error':'尚未检测'} +MODEL_PROBE_AT=0 +TASKS={} +LOCK=threading.Lock() + + +IMAGE_ASSET_ROOT=REPORTS/'assets'; IMAGE_ASSET_ROOT.mkdir(exist_ok=True) + +def _asset_ext(url, content_type=''): + c=(content_type or '').split(';',1)[0].strip().lower() + mapping={'image/jpeg':'.jpg','image/png':'.png','image/webp':'.webp','image/gif':'.gif','image/avif':'.avif'} + if c in mapping:return mapping[c] + path=urlparse(str(url or '')).path.lower() + for ext in ('.webp','.jpg','.jpeg','.png','.gif','.avif'): + if ext in path:return '.jpg' if ext=='.jpeg' else ext + return '.img' + +def _normalize_asset_url(url): + url=str(url or '').strip().replace('&','&') + if url.startswith('//'):url='https:'+url + return url + +def _looks_like_image(data, content_type=''): + if not data:return False + c=(content_type or '').split(';',1)[0].strip().lower() + if c.startswith('image/'):return True + sig=data[:16] + return (sig.startswith(b'\xff\xd8\xff') or sig.startswith(b'\x89PNG\r\n\x1a\n') or sig[:6] in (b'GIF87a',b'GIF89a') or (len(sig)>=12 and sig[:4]==b'RIFF' and sig[8:12]==b'WEBP') or b'ftypavif' in data[:32]) + +def _download_image(url, timeout=18): + """Download a commerce image with several anti-hotlink compatible header strategies.""" + url=_normalize_asset_url(url) + strategies=[ + {'Referer':'https://detail.tmall.com/','Origin':'https://detail.tmall.com'}, + {'Referer':'https://item.taobao.com/','Origin':'https://item.taobao.com'}, + {'Referer':'https://www.taobao.com/'}, + {}, + ] + last=None + for extra in strategies: + try: + headers={ + 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/151 Safari/537.36', + 'Accept':'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', + 'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8', + 'Cache-Control':'no-cache', + } + headers.update(extra) + req=urllib.request.Request(url,headers=headers) + with urllib.request.urlopen(req,timeout=timeout) as r: + data=r.read(16*1024*1024+1) + if len(data)>16*1024*1024:raise ValueError('image too large') + ctype=r.headers.get('Content-Type','') + if not _looks_like_image(data,ctype):raise ValueError('not image response') + return data,ctype + except Exception as ex:last=ex + raise last or RuntimeError('image download failed') + +def cache_report_images(result, task_id): + """Persist report images under /reports/assets. Keep the source URL as a browser fallback.""" + root=IMAGE_ASSET_ROOT/task_id; root.mkdir(parents=True,exist_ok=True) + ok=failed=skipped=0 + for e in result.get('evidenceLedger') or []: + if not isinstance(e,dict) or e.get('type')!='image':continue + url=_normalize_asset_url(e.get('value')) + meta=e.setdefault('meta',{}) + if not url: + skipped+=1;continue + # Keep source URL even when caching fails so the renderer can still try the browser. + meta['sourceUrl']=url + group=re.sub(r'[^a-zA-Z0-9_-]+','_',str(meta.get('group') or 'other')) + eid=re.sub(r'[^a-zA-Z0-9_-]+','_',str(e.get('id') or hashlib.sha1(url.encode()).hexdigest()[:12])) + group_dir=root/group; group_dir.mkdir(parents=True,exist_ok=True) + try: + if url.startswith('data:image/'): + head,payload=url.split(',',1) + data=base64.b64decode(payload) if ';base64' in head else unquote_to_bytes(payload) + ctype=head[5:].split(';',1)[0] + elif url.startswith(('http://','https://')): + data,ctype=_download_image(url) + else: + raise ValueError('unsupported image url') + ext=_asset_ext(url,ctype); target=group_dir/(eid+ext); target.write_bytes(data) + # Browser URL is absolute-from-server-root and therefore works from every report route. + meta['localUrl']=f'/reports/assets/{task_id}/{group}/{target.name}' + meta['cacheStatus']='success'; meta.pop('cacheError',None); ok+=1 + except Exception as ex: + meta.pop('localUrl',None); meta['cacheStatus']='failed'; meta['cacheError']=str(ex)[:220]; failed+=1 + result.setdefault('meta',{})['imageCache']={'success':ok,'failed':failed,'skipped':skipped,'mode':'local-first-multi-retry-remote-fallback'} + return result + +def jdump(o): return json.dumps(o,ensure_ascii=False).encode('utf-8') + + +def _norm_key(k): return re.sub(r'\s+','',str(k or '')).lower() +MONITOR_ALIASES={ + 'date':['日期','采集日期','监测日期','统计日期','date','day','capturedate','capturedat','capturedAt'], + 'itemId':['商品id','itemid','item_id','id','商品链接','链接','url','商品url'], + 'sales':['销量展示值','销量展示','销量','销售量','salesdisplay','sales','sold','销量档位','已售'], + 'rank':['榜单排名','排名','rank','ranking','榜单','类目排名'], + 'price':['价格','当前价','当前价格','到手价','price','currentprice'], + 'promotion':['活动','促销','promotion','promotions','活动信息','优惠'], + 'title':['标题','商品标题','title','name','商品名称'] +} +def _pick(row,name): + m={_norm_key(k):v for k,v in (row or {}).items()} + for a in MONITOR_ALIASES[name]: + if _norm_key(a) in m:return m[_norm_key(a)] + return '' +def _xlsx_rows(data): + # 标准 xlsx 第一张工作表;纯标准库解析,用户本机无需安装 openpyxl。 + with zipfile.ZipFile(io.BytesIO(data)) as z: + shared=[] + if 'xl/sharedStrings.xml' in z.namelist(): + root=ET.fromstring(z.read('xl/sharedStrings.xml')) + ns={'a':'http://schemas.openxmlformats.org/spreadsheetml/2006/main'} + for si in root.findall('a:si',ns): shared.append(''.join(t.text or '' for t in si.findall('.//a:t',ns))) + wb=ET.fromstring(z.read('xl/workbook.xml')) + rel=ET.fromstring(z.read('xl/_rels/workbook.xml.rels')) + relmap={x.attrib.get('Id'):x.attrib.get('Target') for x in rel} + ns={'a':'http://schemas.openxmlformats.org/spreadsheetml/2006/main','r':'http://schemas.openxmlformats.org/officeDocument/2006/relationships'} + sh=wb.find('a:sheets/a:sheet',ns); rid=sh.attrib.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}id') + target=relmap.get(rid,'worksheets/sheet1.xml').lstrip('/') + if not target.startswith('xl/'): target='xl/'+target + root=ET.fromstring(z.read(target)) + rows=[] + for rr in root.findall('.//a:sheetData/a:row',ns): + vals=[] + for c in rr.findall('a:c',ns): + t=c.attrib.get('t'); v=c.find('a:v',ns); val='' if v is None else (v.text or '') + if t=='s' and val!='': + try: val=shared[int(val)] + except: pass + elif t=='inlineStr': + val=''.join(x.text or '' for x in c.findall('.//a:t',ns)) + vals.append(val) + rows.append(vals) + if not rows:return [] + head=[str(x).strip() for x in rows[0]] + return [{head[i]:(r[i] if i=8:break + if len(preview)>=8:break + return { + 'counts':{ + 'parameters':len(clean.get('attributes') or []),'reviews':len(clean.get('reviews') or []), + 'questions':len(clean.get('questions') or []),'main':len(images.get('main') or []), + 'detail':len(images.get('detail') or []),'sku':len(images.get('sku') or []), + 'buyerShow':len(images.get('buyerShow') or []), + }, + 'publicReviewCount':collection.get('publicReviewCount') or '', + 'reviewComplete':bool(collection.get('reviewCollectionComplete')), + 'previewImages':preview, + } + +def worker(tid, raw): + try: + evidence_summary=task_evidence_summary(raw);counts=evidence_summary['counts'] + set_task(tid,status='analyzing',progress=28,evidenceSummary=evidence_summary,steps={'collect':'done','evidence':'waiting','planner':'waiting','product':'waiting','fashion':'waiting','visual':'not_collected' if not counts['main'] else 'waiting','merchandising':'waiting','detail':'not_collected' if not counts['detail'] else 'waiting','review':'waiting','qa':'waiting','competition':'waiting','strategy':'waiting','plans':'waiting','editor':'waiting','report':'waiting'}) + def progress(step,state,pct): + with LOCK: + t=TASKS[tid]; t['progress']=pct + if step=='visual' and not counts['main']:t['steps'][step]='not_collected' + elif step=='detail' and not counts['detail']:t['steps'][step]='not_collected' + else:t['steps'][step]=state + result=analyze(raw,progress) + if not (result.get('meta') or {}).get('reportReady'): + (REPORTS/f'{tid}.failed.json').write_text(json.dumps(result,ensure_ascii=False,indent=2),'utf-8') + raise RuntimeError((result.get('meta') or {}).get('reportBlockedReason') or '模型未生成可输出报告,本次不使用兜底内容。') + set_task(tid,status='rendering',progress=95) + TASKS[tid]['steps']['report']='processing' + result=cache_report_images(result,tid) + (REPORTS/f'{tid}.json').write_text(json.dumps(result,ensure_ascii=False,indent=2),'utf-8') + # 报告页面是静态 HTML;数据单独由 JSON 接口提供,便于部署和独立迭代 UI。 + set_task(tid,status='complete',progress=100,reportUrl=f'/report.html?data=/reports/{tid}.json',analysisUrl=f'/reports/{tid}.json') + TASKS[tid]['steps']['report']='done' + except Exception as e: + with LOCK: + task=TASKS.setdefault(tid,{}) + for key,state in (task.get('steps') or {}).items(): + if str(state).startswith('processing'):task['steps'][key]='failed' + set_task(tid,status='error',error=str(e),trace=traceback.format_exc(),progress=100) + +def task_page(tid): + return f'''行业平台报告生成中
    SANXIAN · PRODUCT INTELLIGENCE

    正在生成商品产品分析报告

    按真实采集证据完成核验、评论聚合、主图分析、详情参数解读、活动分析、产品定义、推荐方案与落地方式。

    8%
    本次证据
    生成进度缺失资料会明确标记,不会补写

    ''' + +TASK_JS=''' +const labels={collect:'商品页面采集',evidence:'证据清洗与建库',planner:'报告结构编排',product:'商品与参数核验',fashion:'设计资产梳理',visual:'商品主图核验',merchandising:'标题与活动核验',detail:'商品详情图核验',review:'评论体验聚合',qa:'购买问答整理',competition:'竞品数据边界',strategy:'产品结论生成',plans:'开品建议生成',editor:'最终报告汇总与校验',report:'图文报告渲染'}; +const stateText={done:'已完成',processing:'处理中',waiting:'待处理',not_collected:'未采到',failed:'失败'}; +const statLabels={parameters:'有效参数',reviews:'有效评论',questions:'去重问答',main:'商品主图',detail:'详情图',sku:'SKU 图',buyerShow:'评论原图'}; +function fillEvidence(summary){ + const counts=summary?.counts||{}; const stats=document.getElementById('evidenceStats'); stats.replaceChildren(); + Object.entries(statLabels).forEach(([key,label])=>{const card=document.createElement('div');const s=document.createElement('span');s.textContent=label;const b=document.createElement('b');b.textContent=String(counts[key]||0);card.append(s,b);stats.append(card)}); + const state=document.getElementById('reviewState'); const publicCount=summary?.publicReviewCount; state.textContent=(publicCount?`已采 ${counts.reviews||0} / 页面公开 ${publicCount} · `:`已采 ${counts.reviews||0} 条 · `)+(summary?.reviewComplete?'评论采集已确认完整':'评论采集未确认完整'); + const preview=document.getElementById('imagePreview'); preview.replaceChildren(); + (summary?.previewImages||[]).forEach((item,i)=>{const fig=document.createElement('figure');const img=document.createElement('img');img.src=item.url;img.alt=item.label+' '+(i+1);img.referrerPolicy='no-referrer';const cap=document.createElement('figcaption');cap.textContent=item.label;fig.append(img,cap);preview.append(fig)}); + preview.hidden=!preview.children.length; +} +function fillSteps(steps){const box=document.getElementById('steps');box.replaceChildren();Object.entries(steps||{}).forEach(([key,state])=>{const row=document.createElement('div');row.className='loading-step '+String(state).split(' ')[0];const mark=document.createElement('i');const name=document.createElement('span');name.textContent=labels[key]||key;const value=document.createElement('b');value.textContent=String(state).startsWith('processing')?'处理中':(stateText[state]||state);row.append(mark,name,value);box.append(row)})} +async function tick(){ + const r=await fetch('/api/task/'+encodeURIComponent(window.TASK_ID)); const d=await r.json(); + document.getElementById('bar').style.width=(d.progress||0)+'%'; + document.getElementById('percent').textContent=(d.progress||0)+'%'; fillEvidence(d.evidenceSummary); fillSteps(d.steps); + if(d.status==='complete'){ location.href=d.reportUrl; return; } + if(d.status==='error'){ document.getElementById('title').textContent='本次未生成报告'; document.getElementById('desc').textContent='模型结论未达到完整性与证据引用要求,不显示兜底内容。'; document.getElementById('err').textContent=d.error||'未生成可输出结论'; return; } + setTimeout(tick,900); +} tick(); +''' + +def save_config(data): + p=ROOT/'config.json' + old={} + if p.exists(): + try: old=json.loads(p.read_text('utf-8')) + except Exception: old={} + key=str(data.get('api_key','')).strip() + if not key: key=str(old.get('api_key','')).strip() + cfg={ + 'api_base':str(data.get('api_base') or old.get('api_base') or 'https://api.bananarouter.com/v1').strip().rstrip('/'), + 'api_key':key, + 'model':str(data.get('model') or old.get('model') or 'gpt-5.5').strip(), + 'chat_path':'/chat/completions','models_path':'/models','temperature':0.2,'timeout':120,'retries':2, + 'ssl_verify':True,'ca_bundle':'','extra_headers':{} + } + tmp=p.with_suffix('.json.tmp') + tmp.write_text(json.dumps(cfg,ensure_ascii=False,indent=2),'utf-8') + tmp.replace(p) + return cfg + +def control_page(): + global MODEL_PROBE, MODEL_PROBE_AT + c=load_config(); is_cfg=configured() + if is_cfg and (time.time()-MODEL_PROBE_AT>30): + MODEL_PROBE=probe_model_api(timeout=12); MODEL_PROBE_AT=time.time() + api_ok=bool(MODEL_PROBE.get('ok')) + base=html.escape(str(c.get('api_base') or 'https://api.bananarouter.com/v1')) + model=html.escape(str(c.get('model') or 'gpt-5.5')) + cfg_err=html.escape(str(c.get('_config_error') or '')) + probe=html.escape(str(MODEL_PROBE.get('error') or '')) + key_state='已保存' if str(c.get('api_key') or '').strip() and not str(c.get('api_key')).startswith('YOUR_') else '未填写' + return f'''三笙 · 电商商品分析闭环 V{SERVER_VERSION}
    ONE-CLICK LOCAL SERVICE

    三笙 · 电商商品分析引擎 V{SERVER_VERSION}

    单商品手动深采;类目不限,参数与SKU动态识别,评论可持续采集,也可随时基于当前数据开始分析。
    +
    本地服务
    运行正常 · V{SERVER_VERSION}
    模型配置
    {'已配置 '+model if is_cfg else '待配置'}
    API连接
    {'连通 ✓' if api_ok else '待检测/失败'}
    +

    首次只需要填一次 API Key

    默认已为 BananaRouter + gpt-5.5 配好。API Key 只保存在本机 server/config.json,不会写入 Chrome 扩展。

    + +
    {'配置错误:'+cfg_err if cfg_err else ('API错误:'+probe if (is_cfg and not api_ok and probe) else '')}
    +

    下一步

    Chrome 只需加载一次 extension 文件夹。之后每次使用:双击启动 → 打开已登录的天猫商品页 → 可选导入监测数据 → 点击扩展“开始深度采集”。

    + ''' + +def prompt_page(): + saved={} + if PROMPT_CONFIG.exists(): + try: saved=json.loads(PROMPT_CONFIG.read_text('utf-8')) + except Exception: saved={} + items={k:{'name':v.name,'body':saved.get(k,{}).get('body',v.body),'variables':list(v.variables)} for k,v in TEMPLATES.items()} + return '提示词配置

    提示词可视化配置

    编辑后保存,下一次分析任务生效。

    商品变量说明

    '+Object.entries(PRODUCT_VARIABLES).map(([k,v])=>`
    ${html.escape(k)}
    ${html.escape(v)}
    `).join('')+'
    '+Object.entries(items).map(([k,v])=>`

    ${html.escape(k)}

    变量:${html.escape(v.variables.join('、')||'自定义')}

    `).join('')+'
    ' + +class H(BaseHTTPRequestHandler): + def log_message(self, fmt, *args): print('[HTTP]',fmt%args) + def cors(self): + self.send_header('Access-Control-Allow-Origin','*');self.send_header('Access-Control-Allow-Headers','Content-Type');self.send_header('Access-Control-Allow-Methods','GET,POST,OPTIONS') + def send_json(self,obj,code=200): + b=jdump(obj);self.send_response(code);self.cors();self.send_header('Content-Type','application/json; charset=utf-8');self.send_header('Content-Length',str(len(b)));self.end_headers();self.wfile.write(b) + def send_file(self,p,ctype): + p=Path(p) + if not p.exists(): self.send_error(404);return + b=p.read_bytes();self.send_response(200);self.cors();self.send_header('Content-Type',ctype);self.send_header('Content-Length',str(len(b)));self.end_headers();self.wfile.write(b) + def do_OPTIONS(self): self.send_response(204);self.cors();self.end_headers() + def do_POST(self): + global MODEL_PROBE, MODEL_PROBE_AT + if self.path=='/api/prompts': + try: + n=int(self.headers.get('Content-Length','0')); data=json.loads(self.rfile.read(n).decode('utf-8') or '{}') + templates=data.get('templates') if isinstance(data.get('templates'),dict) else {} + clean={k:{'body':str(v.get('body',''))[:100000]} for k,v in templates.items() if k in TEMPLATES and isinstance(v,dict)} + PROMPT_CONFIG.write_text(json.dumps(clean,ensure_ascii=False,indent=2),'utf-8'); self.send_json({'ok':True}) + except Exception as e: self.send_json({'ok':False,'error':str(e)},400) + return + if self.path=='/api/monitor-import': + try: + n=int(self.headers.get('Content-Length','0')) + data=json.loads(self.rfile.read(n).decode('utf-8') or '{}') + item_id=_norm_item_id(data.get('itemId')) + filename=str(data.get('filename') or '') + force_bind=bool(data.get('forceBind')) + if not item_id: + self.send_json({'ok':False,'error':'缺少当前商品 itemId','code':'MISSING_ITEM_ID'},400); return + blob=base64.b64decode(data.get('dataBase64') or '') + parsed=parse_monitor_file(filename,blob) + file_ids=sorted({ _norm_item_id(_pick(r,'itemId')) for r in parsed if _norm_item_id(_pick(r,'itemId')) }) + if file_ids and item_id not in file_ids and not force_bind: + self.send_json({ + 'ok':False, + 'code':'ITEM_ID_MISMATCH', + 'error':'监测文件中的商品ID与当前商品不一致', + 'currentItemId':item_id, + 'fileItemIds':file_ids[:20], + 'canForce':len(file_ids)==1 + },409); return + rows=normalize_monitor_rows(parsed,item_id,force_bind=force_bind) + if not rows: + self.send_json({ + 'ok':False, + 'code':'NO_VALID_ROWS', + 'error':'没有识别到当前商品的有效“日期 + 销量”数据', + 'fileItemIds':file_ids[:20] + },400); return + monitor_path(item_id).with_suffix('.json').write_text(json.dumps(rows,ensure_ascii=False,indent=2),'utf-8') + self.send_json({ + 'ok':True,'days':len(rows), + 'firstSales':rows[0].get('salesDisplay'), + 'lastSales':rows[-1].get('salesDisplay'), + 'itemId':item_id + }); return + except Exception as e: + self.send_json({'ok':False,'code':'IMPORT_EXCEPTION','error':str(e)},400); return + if self.path=='/api/monitor-clear': + try: + n=int(self.headers.get('Content-Length','0')); data=json.loads(self.rfile.read(n).decode('utf-8') or '{}'); p=monitor_path(data.get('itemId')).with_suffix('.json') + if p.exists(): p.unlink() + self.send_json({'ok':True}); return + except Exception as e: self.send_json({'ok':False,'error':str(e)},400); return + if self.path=='/api/config': + try: + n=int(self.headers.get('Content-Length','0')); data=json.loads(self.rfile.read(n).decode('utf-8') or '{}') + save_config(data) + MODEL_PROBE=probe_model_api(timeout=18); MODEL_PROBE_AT=time.time() + if not MODEL_PROBE.get('ok'): self.send_json({'ok':False,'error':MODEL_PROBE.get('error') or 'API连接失败','probe':MODEL_PROBE},400); return + self.send_json({'ok':True,'model':load_config().get('model'),'probe':MODEL_PROBE}); return + except Exception as e: self.send_json({'ok':False,'error':str(e)},400); return + if self.path not in ('/api/analyze','/api/analyze-current'): self.send_error(404);return + try: + c=load_config(); model_ready=configured() + MODEL_PROBE=probe_model_api(timeout=15) if model_ready else {'ok':False,'stage':'config','error':'模型未配置,使用事实报告'} + MODEL_PROBE_AT=time.time() + n=int(self.headers.get('Content-Length','0')) + payload=json.loads(self.rfile.read(n).decode('utf-8') or '{}') + if self.path=='/api/analyze-current': + raw=payload.get('raw') if isinstance(payload,dict) else None + allow_partial=True + else: + raw=payload + allow_partial=False + if not isinstance(raw,dict): + self.send_json({'ok':False,'error':'分析数据格式错误:缺少 raw 商品数据'},400); return + raw['_model_available']=bool(MODEL_PROBE.get('ok')) + item_id=str(raw.get('product',{}).get('itemId') or '') + if not item_id: + self.send_json({'ok':False,'error':'采集数据缺少商品 itemId,请刷新商品页后重新采集'},400); return + has_facts=bool(raw.get('product',{}).get('title') or raw.get('product',{}).get('shop') or raw.get('sales',{}).get('currentPrice') or raw.get('sku') or raw.get('attributes') or raw.get('images',{}).get('main')) + if not has_facts: + self.send_json({'ok':False,'error':'采集数据中商品字段为空,请确认淘宝已登录且详情加载完成'},400); return + if not bool((raw.get('collection') or {}).get('reviewCollectionComplete')) and not allow_partial: + self.send_json({'ok':False,'error':'评论尚未确认完整。如需立即分析,请使用“基于当前数据开始分析”。'},409); return + mp=monitor_path(item_id).with_suffix('.json') + if mp.exists(): + try: raw['monitoring']=json.loads(mp.read_text('utf-8')) + except Exception: raw['monitoring']=[] + tid='tmall_'+str(item_id or 'item')+'_'+time.strftime('%Y%m%d_%H%M%S')+'_'+uuid.uuid4().hex[:5] + summary=task_evidence_summary(raw);counts=summary['counts'] + TASKS[tid]={'status':'queued','progress':20,'evidenceSummary':summary,'steps':{'collect':'done','evidence':'waiting','planner':'waiting','product':'waiting','fashion':'waiting','visual':'not_collected' if not counts['main'] else 'waiting','merchandising':'waiting','detail':'not_collected' if not counts['detail'] else 'waiting','review':'waiting','qa':'waiting','competition':'waiting','strategy':'waiting','plans':'waiting','editor':'waiting','report':'waiting'},'createdAt':time.time()} + threading.Thread(target=worker,args=(tid,raw),daemon=True).start() + self.send_json({'ok':True,'taskId':tid,'taskUrl':f'http://127.0.0.1:{PORT}/task/{tid}'}) + except Exception as e: self.send_json({'ok':False,'error':str(e)},400) + def do_GET(self): + global MODEL_PROBE, MODEL_PROBE_AT + p=urlparse(self.path).path + if p=='/health': + c=load_config(); self.send_json({'ok':True,'serverVersion':SERVER_VERSION,'modelConfigured':configured(),'model':c.get('model') or None,'apiBase':c.get('api_base') or None,'configPath':c.get('_config_path'),'configError':c.get('_config_error') or None,'modelProbe':MODEL_PROBE,'port':PORT}); return + if p=='/api/prompts': + saved={} + if PROMPT_CONFIG.exists(): + try: saved=json.loads(PROMPT_CONFIG.read_text('utf-8')) + except Exception: pass + self.send_json({'ok':True,'variables':PRODUCT_VARIABLES,'templates':{k:{'body':saved.get(k,{}).get('body',v.body),'variables':list(v.variables)} for k,v in TEMPLATES.items()}}); return + if p=='/prompts': + b=prompt_page().encode('utf-8'); self.send_response(200); self.send_header('Content-Type','text/html; charset=utf-8'); self.send_header('Content-Length',str(len(b))); self.end_headers(); self.wfile.write(b); return + if p=='/version': + self.send_json({'ok':True,'serverVersion':SERVER_VERSION,'port':PORT}); return + if p=='/api/model-test': + MODEL_PROBE=probe_model_api(timeout=18); MODEL_PROBE_AT=time.time(); self.send_json(MODEL_PROBE,200 if MODEL_PROBE.get('ok') else 502); return + if p.startswith('/api/monitor/'): + item_id=p.rsplit('/',1)[-1]; mp=monitor_path(item_id).with_suffix('.json') + if not mp.exists(): self.send_json({'ok':True,'days':0}); return + try: + rows=json.loads(mp.read_text('utf-8')); self.send_json({'ok':True,'days':len(rows),'firstSales':rows[0].get('salesDisplay') if rows else None,'lastSales':rows[-1].get('salesDisplay') if rows else None,'rows':rows}); return + except Exception as e: self.send_json({'ok':False,'error':str(e)},500); return + if p.startswith('/api/task/'): + tid=p.rsplit('/',1)[-1]; t=TASKS.get(tid); self.send_json(t if t else {'error':'task not found'},200 if t else 404); return + if p.startswith('/task/'): + tid=p.rsplit('/',1)[-1]; b=task_page(tid).encode();self.send_response(200);self.send_header('Content-Type','text/html; charset=utf-8');self.send_header('Content-Length',str(len(b)));self.end_headers();self.wfile.write(b);return + if p=='/task.js': + b=TASK_JS.encode();self.send_response(200);self.send_header('Content-Type','text/javascript; charset=utf-8');self.send_header('Content-Length',str(len(b)));self.end_headers();self.wfile.write(b);return + if p=='/report.css': self.send_file(ROOT/'report.css','text/css; charset=utf-8'); return + if p=='/report.js': self.send_file(ROOT/'report.js','text/javascript; charset=utf-8'); return + if p=='/report.html': self.send_file(ROOT/'report.html','text/html; charset=utf-8'); return + if p.startswith('/reports/'): + f=(REPORTS/p.split('/reports/',1)[1]).resolve() + if REPORTS.resolve() not in f.parents:self.send_error(403);return + ctype=mimetypes.guess_type(str(f))[0] or 'application/octet-stream' + if f.suffix=='.json':ctype='application/json; charset=utf-8' + elif f.suffix=='.html':ctype='text/html; charset=utf-8' + self.send_file(f,ctype);return + if p=='/': + b=control_page().encode('utf-8');self.send_response(200);self.send_header('Content-Type','text/html; charset=utf-8');self.send_header('Content-Length',str(len(b)));self.end_headers();self.wfile.write(b);return + self.send_error(404) + +def main(): + print(f'Sansong Ecommerce Product Loop V{SERVER_VERSION} running: http://127.0.0.1:{PORT}',flush=True) + print('Dedicated port 17962. Manual single-product collection; no recommendation scraping.',flush=True) + ThreadingHTTPServer(('127.0.0.1',PORT),H).serve_forever() +if __name__=='__main__': main() +DYNAMIC_PARAM_FAMILIES = { + '身份与合规':['品牌','产地','型号','执行标准','备案','许可证','等级'], + '规格与选择':['尺寸','尺码','规格','颜色','口味','容量','重量','净含量','版本','配置'], + '材质与成分':['材质','面料','成分','配料','配方','填充物'], + '功能与适用':['功能','功效','适用','肤质','人群','场景','兼容'], + '工艺与使用':['工艺','做工','版型','洗护','使用方法','保质期','储存','续航','性能'], +} +def evaluate_parameter_collection(attrs): + keys=list((attrs or {}).keys()) + norm = {str(k).strip():str(v).strip() for k,v in (attrs or {}).items() if str(v).strip()} + found=[] + for canon, aliases in DYNAMIC_PARAM_FAMILIES.items(): + hit=False + for k,v in norm.items(): + if any(a in k for a in aliases) and v: + hit=True; break + if hit: found.append(canon) + status = 'collected' if norm else 'missing' + return { + 'state':status, + 'stateText': {'collected':'参数已采集并等待类目分析','missing':'未识别到有效参数'}.get(status,'参数已采集'), + 'count': len(norm), + 'keys': keys[:50], + 'found': found, + 'missing': [] + } diff --git a/server/start.command b/server/start.command new file mode 100755 index 0000000..68fdeaf --- /dev/null +++ b/server/start.command @@ -0,0 +1,3 @@ +#!/bin/bash +cd "$(dirname "$0")/.." +exec ./start-tmall-ai.command diff --git a/server/start_windows.bat b/server/start_windows.bat new file mode 100644 index 0000000..0d6d4aa --- /dev/null +++ b/server/start_windows.bat @@ -0,0 +1,5 @@ +@echo off +cd /d %~dp0 +if not exist config.json copy config.example.json config.json +python server.py +pause diff --git a/start-tmall-ai.command b/start-tmall-ai.command new file mode 100755 index 0000000..a4cca87 --- /dev/null +++ b/start-tmall-ai.command @@ -0,0 +1,50 @@ +#!/bin/bash +ROOT="$(cd "$(dirname "$0")" && pwd)" +PORT=17962 +VERSION="9.2.5" +SERVER="$ROOT/server" +LOG="$SERVER/v9_1_0.log" + +health_version() { + curl -fsS "http://127.0.0.1:$PORT/health" 2>/dev/null | python3 -c 'import sys,json +try: print(json.load(sys.stdin).get("serverVersion","")) +except: print("")' 2>/dev/null +} + +RUNNING="$(health_version || true)" +if [ "$RUNNING" = "$VERSION" ]; then + open "http://127.0.0.1:$PORT/" + exit 0 +fi + +if [ -n "$RUNNING" ]; then + PID="$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null | head -1 || true)" + [ -n "$PID" ] && kill "$PID" 2>/dev/null || true + sleep 1 +fi + +if lsof -nP -iTCP:$PORT -sTCP:LISTEN >/dev/null 2>&1; then + osascript -e 'display alert "启动失败" message "端口 17962 被其他程序占用,请先关闭占用进程。" as critical' 2>/dev/null || true + exit 1 +fi + +cd "$SERVER" +[ -f config.json ] || cp config.example.json config.json + +osascript <> '$LOG' 2>&1" +end tell +APPLESCRIPT + +for i in {1..40}; do + V="$(health_version || true)" + if [ "$V" = "$VERSION" ]; then + open "http://127.0.0.1:$PORT/" + exit 0 + fi + sleep 0.3 +done + +open -a TextEdit "$LOG" 2>/dev/null || true +exit 1 diff --git a/stop-tmall-ai.command b/stop-tmall-ai.command new file mode 100755 index 0000000..755b442 --- /dev/null +++ b/stop-tmall-ai.command @@ -0,0 +1,10 @@ +#!/bin/bash +PORT=17962 +V=$(curl -fsS "http://127.0.0.1:$PORT/health" 2>/dev/null | python3 -c 'import sys,json +try: print(json.load(sys.stdin).get("serverVersion","")) +except: print("")' 2>/dev/null || true) +if [ -n "$V" ]; then + PID="$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null | head -1 || true)" + [ -n "$PID" ] && kill "$PID" 2>/dev/null || true +fi +osascript -e 'display notification "本地服务已停止" with title "三笙电商商品分析"' 2>/dev/null || true diff --git a/tests/test_evidence_only_report.py b/tests/test_evidence_only_report.py new file mode 100644 index 0000000..b510a52 --- /dev/null +++ b/tests/test_evidence_only_report.py @@ -0,0 +1,320 @@ +import sys +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / 'server')) + +from analysis import analyze, ensure_experience_solution, normalize, _review_issue, _buyer_evidence, detect_category, _category_parameter_facts, core_solution_diagnostics, repair_core_solution_with_evidence +from renderer import render +from server import TASK_JS, task_evidence_summary, task_page + + +class EvidenceOnlyReportTest(unittest.TestCase): + def setUp(self): + self.analysis = { + 'facts': { + 'product': {'title': '用户评价·51', 'itemId': '900653753390'}, + 'sales': {'sold': '700+', 'currentPrice': '395.25'}, + 'attributes': [ + {'name': '1024', 'value': '"lg",'}, + {'name': 'console.log("onload]', 'value': 'performance.now()'}, + {'name': '面料支数', 'value': '60支'}, + {'name': '床单面料材质', 'value': '全棉'}, + {'name': '适用床尺寸', 'value': '1.2m床三件套'}, + ], + 'reviews': [ + {'content': '2025年4月18日已购:【四件套】测试规格'}, + {'content': '花色好看,摸起来很舒服', 'images': ['https://img.alicdn.com/review-rate.jpg'], 'source': 'dom_visible'}, + {'content': '1米2床单尺寸小,床边不够'}, + ], + 'questions': [ + {'question': '问大家·5 问 有味道吗 更多回答 问 盖上舒服吗 更多回答'}, + {'question': '问 有味道吗 更多回答'}, + ], + 'collection': { + 'reviewCollectionComplete': False, + 'publicReviewCount': '51', + 'reviewStopReason': 'no_progress_after_4_rounds', + 'reviewPagesVisited': 35, + }, + 'images': {'main': [ + 'https://img.alicdn.com/tps-120-60.png', + 'https://img.alicdn.com/item_pic.jpg', + ], 'detail': [], 'sku': [], 'buyerShow': [], 'all': []}, + }, + 'experienceSolution': {}, + 'meta': {}, + } + + def test_empty_model_result_does_not_generate_fallback_report(self): + result = ensure_experience_solution(self.analysis) + self.assertFalse(result['meta']['fallbackReport']) + self.assertFalse(result['meta']['reportReady']) + self.assertIn('不输出兜底报告', result['meta']['reportBlockedReason']) + self.assertEqual('', result['facts']['product']['title']) + self.assertEqual(['面料支数', '床单面料材质', '适用床尺寸'], [x['name'] for x in result['facts']['attributes']]) + self.assertEqual(2, result['baseline']['reviewCount']) + self.assertEqual(2, result['baseline']['questionCount']) + cards = result['experienceSolution']['ownerOverview']['cards'] + self.assertTrue(cards) + self.assertEqual('下一款明确卖点', cards[0]['label']) + self.assertIn('ATTR_0003', cards[0]['evidenceIds']) + self.assertEqual([], result['experienceSolution']['productExperience']['gates']) + with self.assertRaisesRegex(ValueError, '不输出兜底报告'): + render(result, 'fixture') + + def test_analysis_runs_without_model_channel(self): + raw = dict(self.analysis['facts']) + raw['_model_available'] = False + result = analyze(raw) + self.assertFalse(result['meta']['modelUsed']) + self.assertEqual('9.2.5', result['meta']['engineVersion']) + self.assertEqual('home_textile', result['meta']['industryTemplate']) + self.assertFalse(result['meta']['fallbackReport']) + self.assertFalse(result['meta']['reportReady']) + + def test_historical_fallback_json_is_rejected(self): + result = ensure_experience_solution({ + 'facts': self.analysis['facts'], + 'experienceSolution': {'reportSummary': {'title': '旧报告', 'verdict': '旧结论'}}, + 'meta': {'fallbackReport': True}, + }) + self.assertFalse(result['meta']['reportReady']) + self.assertIn('历史分析标记为兜底结果', result['meta']['reportBlockedReason']) + + def test_dormitory_spec_never_invents_12m_issue_and_title_is_clean(self): + facts = { + 'product': {'title': '宿舍七件套新生床品-tmall.com天猫', 'itemId': '2'}, + 'sales': {'sold': '1万+', 'currentPrice': '637.5'}, + 'attributes': [ + {'name': '套件组成', 'value': '被芯、床垫、床单、被套、枕芯、枕套、床帘'}, + {'name': '适用床尺寸', 'value': '80×190cm、90×190cm、90×200cm;床垫厚4cm/6cm'}, + {'name': '面料材质', 'value': '聚酯纤维'}, + {'name': '适用人群', 'value': '学生'}, + ], + 'reviews': [ + {'content': '七件配齐,开学直接用,尺寸合适'}, + {'content': '枕套实物和页面有色差', 'sku': '90×190cm / 4cm床垫'}, + ], + 'questions': [], 'images': {}, + } + clean = normalize(facts);issue = _review_issue(clean) + self.assertEqual('宿舍七件套新生床品', clean['product']['title']) + self.assertEqual('实物色差', issue['label']) + self.assertNotIn('1.2m', str(issue)) + self.assertNotIn('1米2', str(issue)) + + def test_real_12m_comment_keeps_12m_evidence(self): + clean = normalize(self.analysis['facts']);issue = _review_issue(clean) + self.assertIn('1米2床单尺寸小,床边不够', issue['text']) + self.assertEqual('尺寸与床型适配', issue['label']) + + def test_apparel_uses_dynamic_category_dimensions(self): + raw=normalize({'product':{'title':'女士宽松连衣裙'},'attributes':[{'name':'尺码','value':'S M L XL'},{'name':'面料成分','value':'棉 95% 氨纶 5%'},{'name':'版型','value':'宽松'}],'reviews':[{'content':'尺码合身,版型显瘦'}],'images':{}}) + route=detect_category(raw) + facts=_category_parameter_facts(raw) + self.assertEqual('apparel',route['key']) + self.assertIn('尺码与适配',[x['label'] for x in facts]) + self.assertIn('面料与成分',[x['label'] for x in facts]) + self.assertNotIn('套件 BOM',[x['label'] for x in facts]) + + def test_food_beauty_and_digital_route_to_their_own_dimensions(self): + fixtures=[ + ('food',{'product':{'title':'坚果零食礼盒'},'attributes':[{'name':'净含量','value':'750g'},{'name':'配料表','value':'坚果'}]}), + ('beauty',{'product':{'title':'敏感肌保湿精华'},'attributes':[{'name':'主要成分','value':'透明质酸'},{'name':'适用肤质','value':'敏感肌'}]}), + ('digital',{'product':{'title':'无线蓝牙耳机'},'attributes':[{'name':'兼容系统','value':'iOS/Android'},{'name':'续航时间','value':'30小时'}]}), + ] + for expected,data in fixtures: + data.update({'reviews':[],'images':{}}) + clean=normalize(data); route=detect_category(clean) + self.assertEqual(expected,route['key']) + self.assertTrue(_category_parameter_facts(clean)) + + def test_complete_evidence_backed_ai_output_is_rendered_and_sanitized(self): + facts = { + 'product': {'title': '全棉三件套-taobao.com淘宝网', 'itemId': '3'}, + 'sales': {}, + 'attributes': [ + {'name': '床单面料材质', 'value': '全棉'}, + {'name': '套件组成', 'value': '床单、被套、枕套'}, + {'name': '适用床尺寸', 'value': '0.9m床三件套'}, + ], + 'reviews': [{'content': '摸起来柔软舒服'}], + 'questions': [{'question': '会不会起球?'}], + 'images': {}, + } + exp = { + 'reportSummary': {'title': '省心型大众爆款,提升转化', 'verdict': '先完成样品验证'}, + 'ownerOverview': {'chapterTitle': '页面事实与评论结果已形成', 'cards': [ + {'label': '客户为什么购买', 'headline': '决定成交', 'evidenceIds': ['REV_000001']}, + {'label': '页面产品定义', 'headline': '全棉三件套', 'evidenceIds': ['ATTR_0001', 'ATTR_0002']}, + {'label': '下一款怎么开', 'headline': '先校准规格', 'evidenceIds': ['ATTR_0003']}, + ]}, + 'customerExperience': { + 'chapterTitle': '1 条评论确认柔软触感', 'journeyTitle': '只显示评论已确认结果', + 'stats': [{'label': '有效评论', 'value': '1'}], + 'stages': [ + {'stageNo': '01', 'label': '实际使用', 'positiveConfirmation': '1 条确认柔软舒服', 'evidenceIds': ['REV_000001']}, + {'stageNo': '02', 'label': '选择规格', 'positiveConfirmation': '页面标注0.9m床三件套', 'evidenceIds': ['ATTR_0003']}, + ], + 'signals': {'positive': [], 'questions': [], 'risks': [ + {'label': '起球产品缺陷', 'evidenceIds': ['QA_00001']}, + ]}, + }, + 'productExperience': {'chapterTitle': '材质、BOM与床型必须一致', 'parameterFacts': [ + {'label': '材质 / 成分', 'value': '全棉', 'evidenceIds': ['ATTR_0001']}, + {'label': '套件 BOM', 'value': '床单、被套、枕套', 'evidenceIds': ['ATTR_0002']}, + ], 'gates': [ + {'priority': 'P0', 'label': '材质核对', 'action': '核对标签与样品', 'evidenceIds': ['ATTR_0001']}, + {'priority': 'P0', 'label': '规格实测', 'action': '按0.9m床铺装', 'evidenceIds': ['ATTR_0003']}, + ]}, + 'newProductPlans': {'plans': [ + {'type': '主销款', 'name': '主销', 'positioning': '保持全棉三件套定义', 'evidenceIds': ['ATTR_0001']}, + {'name': '无证据升级', 'evidenceIds': []}, + {'type': '升级款', 'name': '第三套', 'positioning': '按BOM校准', 'evidenceIds': ['ATTR_0002']}, + ], 'chapterTitle': '只输出有证据的开品方向'}, + 'validationLoop': {'chapterTitle': '三道验证门决定是否上架', 'rows': [ + {'stage': '商品定义', 'owner': '商品企划 / 商品运营', 'metrics': '核对定义', 'successMeaning': '参数一致', 'nextAction': '进入打样', 'evidenceIds': ['ATTR_0001']}, + {'stage': '规格实测', 'owner': '供应链 / 品控', 'metrics': '实床铺装', 'successMeaning': '规格适配', 'nextAction': '锁定SKU', 'evidenceIds': ['ATTR_0003']}, + {'stage': '页面验收', 'owner': '视觉与内容', 'metrics': '核对页面', 'successMeaning': '页面一致', 'nextAction': '上架', 'evidenceIds': ['ATTR_0002']}, + ]}, + } + result = ensure_experience_solution({'facts': facts, 'experienceSolution': exp, 'meta': {}}) + output = str(result['experienceSolution']) + labels = [x['label'] for x in result['experienceSolution']['productExperience']['parameterFacts']] + signals = result['experienceSolution']['customerExperience']['signals'] + self.assertTrue(result['meta']['reportReady']) + self.assertEqual('全棉三件套', result['facts']['product']['title']) + self.assertIn('套件 BOM', labels) + self.assertIn('材质 / 成分', labels) + self.assertEqual([], signals['risks']) + self.assertTrue(signals['questions'][0]['label'].startswith('购买前关注:')) + self.assertEqual(2, len(result['experienceSolution']['newProductPlans']['plans'])) + self.assertNotIn('无证据升级', output) + self.assertNotIn('项目负责人', output) + self.assertNotIn('客户为什么购买', output) + self.assertNotIn('提升转化', output) + self.assertNotIn('决定成交', output) + self.assertNotIn('爆款', output) + doc = render(result, 'evidence-only') + self.assertIn('老板速览', doc) + self.assertIn('下一款先做什么', doc) + self.assertIn('V9.2.5', doc) + self.assertNotIn('项目负责人', doc) + + def test_incomplete_final_editor_output_is_repaired_from_real_evidence(self): + facts = { + 'product': {'title': '学生宿舍全棉三件套', 'itemId': 'repair-1'}, + 'sales': {'sold': '1000+', 'currentPrice': '129'}, + 'attributes': [ + {'name': '床单面料材质', 'value': '全棉'}, + {'name': '套件组成', 'value': '床单、被套、枕套'}, + {'name': '适用床尺寸', 'value': '0.9m床三件套'}, + {'name': '产品等级', 'value': '合格品'}, + ], + 'reviews': [ + {'content': '面料柔软,尺寸合适'}, + {'content': '花色好看,宿舍床铺上正好'}, + ], + 'questions': [{'question': '洗后会不会缩水?'}], + 'images': {}, + } + repaired = repair_core_solution_with_evidence({}, facts) + result = ensure_experience_solution({'facts': facts, 'experienceSolution': repaired, 'meta': {}}) + self.assertTrue(result['meta']['reportReady']) + self.assertTrue(result['meta']['reportValidation']['ok']) + self.assertEqual([], result['meta']['reportValidation']['missing']) + self.assertFalse(result['meta']['fallbackReport']) + self.assertIn('evidenceIds', result['experienceSolution']['validationLoop']['rows'][0]) + + + def test_legacy_grouped_images_are_preserved_without_strict_provenance(self): + raw={ + 'product':{'title':'测试商品','itemId':'1'}, + 'reviews':[], + 'images':{ + 'main':['https://img.alicdn.com/item_pic.jpg'], + 'detail':['https://img.alicdn.com/detail_01.jpg'], + 'sku':['https://img.alicdn.com/sku_01.jpg'], + 'buyerShow':[] + } + } + clean=normalize(raw) + self.assertEqual(['https://img.alicdn.com/item_pic.jpg'], clean['images']['main']) + self.assertEqual(['https://img.alicdn.com/detail_01.jpg'], clean['images']['detail']) + self.assertEqual(['https://img.alicdn.com/sku_01.jpg'], clean['images']['sku']) + self.assertEqual('legacy-review-only', clean['images']['classification']['version']) + sources={x['group']:x['source'] for x in clean['images']['provenance'] if x['group'] in ('main','detail','sku')} + self.assertEqual('legacy_group_main', sources['main']) + self.assertEqual('legacy_group_detail', sources['detail']) + self.assertEqual('legacy_group_sku', sources['sku']) + + def test_validation_reports_exact_missing_fields(self): + validation = core_solution_diagnostics({'reportSummary': {'title': '有标题'}}) + self.assertFalse(validation['ok']) + self.assertNotIn('reportSummary.title', validation['missing']) + self.assertIn('reportSummary.verdict', validation['missing']) + self.assertIn('validationLoop.rows', validation['missing']) + + def test_task_progress_exposes_real_evidence_counts(self): + raw = self.analysis['facts'] + summary = task_evidence_summary(raw) + self.assertEqual(3, summary['counts']['parameters']) + self.assertEqual(2, summary['counts']['reviews']) + self.assertEqual(1, summary['counts']['main']) + self.assertEqual(0, summary['counts']['detail']) + self.assertEqual(1, summary['counts']['buyerShow']) + self.assertEqual('51', summary['publicReviewCount']) + self.assertFalse(summary['reviewComplete']) + page = task_page('fixture') + self.assertIn('本次证据', page) + self.assertIn('生成进度', page) + self.assertIn('evidenceStats', page) + self.assertIn("not_collected:'未采到'", TASK_JS) + self.assertIn("visual:'商品主图核验'", TASK_JS) + self.assertIn("detail:'商品详情图核验'", TASK_JS) + self.assertIn("editor:'最终报告汇总与校验'", TASK_JS) + self.assertIn("failed:'失败'", TASK_JS) + self.assertNotIn("visual:'主图成交'", TASK_JS) + + def test_strict_image_groups_are_disjoint_and_traceable(self): + shared = 'https://img.alicdn.com/shared-review.jpg' + buyer_two = 'https://img.alicdn.com/second-review.jpg' + main = 'https://img.alicdn.com/verified-main.jpg' + structured_main = 'https://img.alicdn.com/structured-main.jpg' + detail = 'https://img.alicdn.com/verified-detail.jpg' + structured_detail = 'https://img.alicdn.com/structured-detail.jpg' + facts = { + 'meta': {'imageClassificationVersion': 'strict-v1'}, + 'product': {'title': '真实商品标题', 'itemId': '1'}, + 'sales': {}, 'attributes': [], 'questions': [], + 'reviews': [{'content': '评论原文', 'images': [shared, buyer_two], 'source': 'dom_visible'}], + 'images': { + 'main': [shared, main, structured_main], 'detail': [shared, detail, structured_detail], 'sku': [], 'buyerShow': [shared, buyer_two], + 'provenance': [ + {'url': shared, 'group': 'buyerShow', 'source': 'review_record'}, + {'url': buyer_two, 'group': 'buyerShow', 'source': 'review_record'}, + {'url': shared, 'group': 'main', 'source': 'dom_main_gallery'}, + {'url': main, 'group': 'main', 'source': 'dom_main_gallery'}, + {'url': structured_main, 'group': 'main', 'source': 'structured_main_gallery'}, + {'url': detail, 'group': 'detail', 'source': 'dom_product_description'}, + {'url': structured_detail, 'group': 'detail', 'source': 'structured_product_description'}, + ], + }, + } + clean = normalize(facts) + groups = clean['images'] + self.assertEqual([shared, buyer_two], groups['buyerShow']) + self.assertEqual([main, structured_main], groups['main']) + self.assertEqual([detail, structured_detail], groups['detail']) + self.assertTrue(groups['classification']['groupsAreDisjoint']) + self.assertEqual(6, len(set(groups['buyerShow'] + groups['main'] + groups['detail']))) + buyer_items = _buyer_evidence(clean) + buyer = buyer_items[0] + self.assertEqual('REV_000001', buyer['reviewEvidenceId']) + self.assertEqual(2, len(buyer_items)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_v910_report_regressions.py b/tests/test_v910_report_regressions.py new file mode 100644 index 0000000..5f44915 --- /dev/null +++ b/tests/test_v910_report_regressions.py @@ -0,0 +1,60 @@ +import sys, unittest +from pathlib import Path +ROOT=Path(__file__).resolve().parents[1] +sys.path.insert(0,str(ROOT/'server')) +from renderer import render, image_source, img_tag +from unittest.mock import patch +import tempfile +import server as server_module +from analysis import ensure_experience_solution + +class V910RegressionTest(unittest.TestCase): + def base_result(self): + facts={'product':{'title':'测试商品'},'sales':{},'attributes':[{'name':'材质','value':'全棉'},{'name':'规格','value':'700g'}], 'reviews':[{'content':'使用后很顺滑'}], 'questions':[], 'sku':[], 'images':{'main':['https://img.example.com/a.jpg'],'sku':['https://img.example.com/s.jpg']}} + exp={'reportSummary':{'title':'测试报告','verdict':'先核对商品结构'},'ownerOverview':{'chapterTitle':'页面事实与评论结果已形成','cards':[{'label':'结论','headline':'先核对规格','evidenceIds':['ATTR_0001']},{'label':'产品','headline':'材质明确','evidenceIds':['ATTR_0001']},{'label':'动作','headline':'统一页面口径','evidenceIds':['ATTR_0002']}]},'customerExperience':{'stages':[{'label':'使用','positiveConfirmation':'顺滑','evidenceIds':['REV_000001']},{'label':'规格','positiveConfirmation':'700g','evidenceIds':['ATTR_0002']}], 'consumerInsights':{'coreSatisfaction':[{'driver':'顺滑体验','evidenceIds':['REV_000001']}]}},'productExperience':{'parameterFacts':[{'label':'材质','value':'全棉','evidenceIds':['ATTR_0001']},{'label':'规格','value':'700g','evidenceIds':['ATTR_0002']}], 'gates':[{'label':'材质','action':'核对','evidenceIds':['ATTR_0001']},{'label':'规格','action':'核对','evidenceIds':['ATTR_0002']}]},'newProductPlans':{'plans':[{'type':'稳健优化','name':'规格清晰化','whyThisPlan':'减少理解成本','evidenceIds':['ATTR_0002']}]},'validationLoop':{'rows':[{'stage':'定义','owner':'商品运营','metrics':'核对','successMeaning':'一致','nextAction':'进入下一步','evidenceIds':['ATTR_0001']},{'stage':'规格','owner':'供应链','metrics':'核对','successMeaning':'一致','nextAction':'锁定','evidenceIds':['ATTR_0002']},{'stage':'页面','owner':'视觉','metrics':'核对','successMeaning':'一致','nextAction':'发布','evidenceIds':['ATTR_0002']} ]}} + r=ensure_experience_solution({'facts':facts,'experienceSolution':exp,'meta':{}}) + for e in r['evidenceLedger']: + if e.get('id')=='IMG_MAIN_0001': + e.setdefault('meta',{})['localUrl']='/reports/assets/test/main/IMG_MAIN_0001.jpg';e['meta']['sourceUrl']=e['value'] + return r + + def test_missing_sku_not_rendered_as_zero(self): + doc=render(self.base_result(),'test') + self.assertNotIn('SKU 0',doc) + self.assertIn('结构化SKU 未完整采集',doc) + self.assertIn('SKU图片数量不等于SKU数量',doc) + + def test_renderer_prefers_local_image_and_keeps_remote_fallback(self): + entry={'value':'https://img.example.com/a.jpg','meta':{'localUrl':'/reports/assets/test/main/IMG_MAIN_0001.jpg','sourceUrl':'https://img.example.com/a.jpg'}} + local,remote=image_source(entry) + html=img_tag(local,'商品主图',remote) + self.assertIn('/reports/assets/test/main/IMG_MAIN_0001.jpg',html) + self.assertIn('https://img.example.com/a.jpg',html) + self.assertIn('data-fallback',html) + + def test_renderer_never_prints_python_dict_literal(self): + r=self.base_result() + r['experienceSolution']['titleAnalysis']={ + 'currentExpression':[{'value':'信息明确','evidenceIds':['ATTR_0001']}], + 'reinforce':[{'topic':'规格需拆清','evidenceIds':['ATTR_0002']}] + } + doc=render(r,'test') + self.assertNotIn("{'point':",doc) + self.assertIn('信息明确',doc) + + def test_cache_report_images_writes_local_url_and_preserves_source(self): + class FakeResponse: + headers={'Content-Type':'image/jpeg'} + def __enter__(self):return self + def __exit__(self,*a):return False + def read(self,n=-1):return b'\xff\xd8\xffFAKEJPEG' + result={'evidenceLedger':[{'id':'IMG_MAIN_0001','type':'image','value':'https://img.example.com/a.jpg','meta':{'group':'main'}}]} + with tempfile.TemporaryDirectory() as td, patch.object(server_module,'IMAGE_ASSET_ROOT',Path(td)), patch('server.urllib.request.urlopen',return_value=FakeResponse()): + out=server_module.cache_report_images(result,'task1') + meta=out['evidenceLedger'][0]['meta'] + self.assertEqual('success',meta['cacheStatus']) + self.assertEqual('https://img.example.com/a.jpg',meta['sourceUrl']) + self.assertTrue(meta['localUrl'].startswith('/reports/assets/task1/main/')) + self.assertTrue(list((Path(td)/'task1'/'main').glob('IMG_MAIN_0001.*'))) + +if __name__=='__main__':unittest.main() diff --git a/tests/test_v920_image_driven_report.py b/tests/test_v920_image_driven_report.py new file mode 100644 index 0000000..a840690 --- /dev/null +++ b/tests/test_v920_image_driven_report.py @@ -0,0 +1,75 @@ +import sys, unittest +from pathlib import Path +ROOT=Path(__file__).resolve().parents[1] +sys.path.insert(0,str(ROOT/'server')) +from analysis import ensure_experience_solution, _evidence_backed_launch_fallback, evidence_ledger, normalize +from renderer import render, img_tag, image_source + +class V920ImageDrivenReportTest(unittest.TestCase): + def fixture(self): + facts={ + 'meta':{'imageClassificationVersion':'strict-v1'}, + 'product':{'title':'学生宿舍七件套纯棉床笠床单四季子母被'}, + 'sales':{'currentPrice':'637.5','sold':'1万+'}, + 'attributes':[{'name':'面料支数','value':'40支'},{'name':'床单面料材质','value':'全棉'},{'name':'适用床尺寸','value':'90×190cm'},{'name':'套件组成','value':'七件套'}], + 'sku':[], + 'reviews':[{'content':'面料柔软透气,宿舍用很方便'},{'content':'枕头有点偏软'}], + 'questions':[], + 'images':{'main':['https://img.example.com/m1.jpg','https://img.example.com/m2.jpg'],'detail':['https://img.example.com/d1.jpg','https://img.example.com/d2.jpg'],'sku':['https://img.example.com/s1.jpg'],'buyerShow':['https://img.example.com/b1.jpg'],'provenance':[{'url':'https://img.example.com/m1.jpg','group':'main','source':'dom_main_gallery'},{'url':'https://img.example.com/m2.jpg','group':'main','source':'dom_main_gallery'},{'url':'https://img.example.com/d1.jpg','group':'detail','source':'dom_product_description'},{'url':'https://img.example.com/d2.jpg','group':'detail','source':'dom_product_description'},{'url':'https://img.example.com/s1.jpg','group':'sku','source':'dom_sku'},{'url':'https://img.example.com/b1.jpg','group':'buyerShow','source':'review_record'}]} + } + led=evidence_ledger(normalize(facts)); ids={x['id'] for x in led} + exp={ + 'reportSummary':{'title':'宿舍七件套商品分析报告','verdict':'当前围绕宿舍一站式买齐形成商品逻辑,下一款优先把规格与睡感选择说清。','coverImageId':'IMG_MAIN_0001'}, + 'ownerOverview':{'cards':[{'label':'商品定位','headline':'宿舍一站式七件套','evidenceIds':['P_TITLE']},{'label':'主要产品资产','headline':'40支全棉 + 七件套','evidenceIds':['ATTR_0001','ATTR_0002']},{'label':'下一款优先方向','headline':'规格与睡感更易选','evidenceIds':['ATTR_0003','REV_000002']}]}, + 'titleAnalysis':{'professionalOpinion':'场景与套件范围清楚,建议进一步强化40支品质参数。','currentExpression':[{'label':'场景','value':'学生宿舍','evidenceIds':['P_TITLE']}],'reinforce':[{'topic':'40支全棉','reason':'品质参数可前置','evidenceIds':['ATTR_0001','ATTR_0002']}],'recommendedActions':[{'action':'统一标题与SKU口径','evidenceIds':['P_TITLE']}]}, + 'visualCommerce':{'professionalOpinion':'主图先建立套装认知,再解释具体产品。','sequence':['一站式','套件清单'], 'items':[{'imageEvidenceId':'IMG_MAIN_0001','role':'一站式套装','message':'建立宿舍整套购买认知','professionalAdvice':'场景更贴近真实宿舍','evidenceIds':['IMG_MAIN_0001']},{'imageEvidenceId':'IMG_MAIN_0002','role':'套件清单','message':'解释具体包含内容','professionalAdvice':'补尺寸差异','evidenceIds':['IMG_MAIN_0002']}]}, + 'detailCommerce':{'professionalOpinion':'详情负责承接材质和规格证明。','contentGroups':[{'type':'材质品质','representativeImageId':'IMG_DETAIL_0001','message':'解释40支全棉','professionalAdvice':'前置品质证明','evidenceIds':['IMG_DETAIL_0001','ATTR_0001']}]}, + 'customerExperience':{'professionalOpinion':'评论验证了柔软透气,同时出现睡感差异。','consumerInsights':{'experienceSignals':[{'type':'稳定体验','insight':'柔软透气','implication':'继续保留全棉','evidenceIds':['REV_000001']},{'type':'体验差异','insight':'枕头偏软','implication':'增加睡感说明','evidenceIds':['REV_000002']}]},'stages':[{'label':'使用','positiveConfirmation':'柔软透气','evidenceIds':['REV_000001']},{'label':'睡感','positiveConfirmation':'软硬有差异','evidenceIds':['REV_000002']}]}, + 'productExperience':{'parameterFacts':[{'label':'支数','value':'40支','evidenceIds':['ATTR_0001']},{'label':'材质','value':'全棉','evidenceIds':['ATTR_0002']}], 'gates':[{'label':'规格','action':'核对','evidenceIds':['ATTR_0003']},{'label':'套件','action':'核对','evidenceIds':['ATTR_0004']}]}, + 'newProductPlans':{'professionalOpinion':'下一款围绕规格更清楚与睡感区分优化。','plans':[{'type':'现有基础优化','name':'宿舍七件套2.0','whyThisPlan':'保留全棉与七件套逻辑,强化规格选择。','visualReferenceImageIds':['IMG_MAIN_0001'],'productChanges':[{'action':'明确床型尺寸','evidenceIds':['ATTR_0003']}],'skuStrategy':'款式→尺寸→花色','mainImagePlan':['首图保留一站式'],'detailPagePlan':['紧接材质证明'],'evidenceIds':['P_TITLE','ATTR_0001','ATTR_0003','IMG_MAIN_0001']}]}, + 'validationLoop':{'rows':[{'stage':'定义','owner':'商品运营','metrics':'核对','successMeaning':'一致','nextAction':'打样','evidenceIds':['P_TITLE']},{'stage':'规格','owner':'供应链','metrics':'实测','successMeaning':'一致','nextAction':'锁定','evidenceIds':['ATTR_0003']},{'stage':'页面','owner':'视觉','metrics':'核对','successMeaning':'一致','nextAction':'发布','evidenceIds':['IMG_MAIN_0001']}]} + } + return ensure_experience_solution({'facts':facts,'experienceSolution':exp,'meta':{}}) + + def test_renderer_is_image_driven_and_professional(self): + doc=render(self.fixture(),'v920') + for text in ('老板速览','下一款先做什么','主图到底在卖什么','详情页证明了什么','先解决适配,再解决审美','消费者真正感受到什么'): + self.assertIn(text,doc) + self.assertIn('v92-main-grid',doc) + self.assertIn('v925-detail-list',doc) + self.assertIn('v92-sku-mosaic',doc) + self.assertIn('v92-buyer-grid',doc) + self.assertIn('v925-plan-grid',doc) + self.assertNotIn('[数据事实]',doc) + self.assertNotIn('[分析判断]',doc) + self.assertNotIn('[设计建议]',doc) + self.assertNotIn('整体有效点',doc) + self.assertNotIn('主要问题',doc) + + def test_cover_has_no_card_border_wrapper(self): + doc=render(self.fixture(),'v920') + self.assertIn('class="v92-cover"',doc) + self.assertIn('cover-ratio',doc) + self.assertNotIn('hero-shot',doc) + + def test_launch_fallback_never_empty_with_real_evidence(self): + facts=normalize(self.fixture()['facts']) + ids={x['id'] for x in evidence_ledger(facts)} + plans=_evidence_backed_launch_fallback(facts,ids) + self.assertGreaterEqual(len(plans.get('plans') or []),1) + self.assertTrue(plans['plans'][0]['evidenceIds']) + +if __name__=='__main__':unittest.main() + + +def test_img_tag_remote_fallback_does_not_force_no_referrer(): + html = img_tag('https://img.example.com/a.jpg', '图', 'https://img.example.com/b.jpg') + assert 'referrerpolicy="no-referrer"' not in html + assert 'data-fallback="https://img.example.com/b.jpg"' in html + + +def test_image_source_keeps_remote_when_cache_failed(): + e={'type':'image','value':'https://img.example.com/a.jpg','meta':{'sourceUrl':'https://img.example.com/a.jpg','cacheStatus':'failed'}} + src,fb=image_source(e) + assert src=='https://img.example.com/a.jpg' + assert fb=='https://img.example.com/a.jpg' diff --git a/tests/test_version_consistency_v924.py b/tests/test_version_consistency_v924.py new file mode 100644 index 0000000..09af5d7 --- /dev/null +++ b/tests/test_version_consistency_v924.py @@ -0,0 +1,21 @@ +import json, re +from pathlib import Path + +ROOT=Path(__file__).resolve().parents[1] +EXPECTED='9.2.5' + +def test_version_consistency(): + assert (ROOT/'VERSION.txt').read_text().strip()==EXPECTED + for rel in ['extension/manifest.json','collector-extension-standalone/manifest.json','codex-plugin-toolkit/sansong-tmall-product-report/assets/capture-extension/manifest.json']: + data=json.loads((ROOT/rel).read_text(encoding='utf-8')) + assert data['version']==EXPECTED, rel + popup=(ROOT/'extension/popup.js').read_text(encoding='utf-8') + assert f"EXPECTED_SERVER_VERSION='{EXPECTED}'" in popup + server=(ROOT/'server/server.py').read_text(encoding='utf-8') + assert f"SERVER_VERSION='{EXPECTED}'" in server or f'SERVER_VERSION = \'{EXPECTED}\'' in server + analysis=(ROOT/'server/analysis.py').read_text(encoding='utf-8') + assert f"ENGINE_VERSION='{EXPECTED}'" in analysis + ai=(ROOT/'server/ai_client.py').read_text(encoding='utf-8') + assert f"SERVER_VERSION = '{EXPECTED}'" in ai + start=(ROOT/'start-tmall-ai.command').read_text(encoding='utf-8') + assert f'VERSION="{EXPECTED}"' in start