feat(init): 完成 Phase 1 基础架构搭建

- 完成 T-001A: 前端项目初始化 (Next.js 14 + TypeScript + Tailwind CSS)
- 完成 T-001B: 后端项目初始化 (FastAPI + SQLAlchemy + asyncpg)
- 完成 T-002: 数据库配置 (KolVideo 模型 + 索引 + 测试)
- 完成 T-003: 基础 UI 框架 (Header/Footer 组件 + 品牌色系)
- 完成 T-004: 环境变量配置 (前后端环境变量)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zfc 2026-01-28 14:26:46 +08:00
commit ac0f086821
75 changed files with 13285 additions and 0 deletions

324
.claude/skills/go/SKILL.md Normal file
View File

@ -0,0 +1,324 @@
---
name: go
description: 终极执行按钮,激进模式一口气完成开发任务,兼容 0->1 和 1->100 场景。
---
# Go - 发射按钮
> **定位**:执行按钮。无论是从零开始的 0->1还是迭代优化的 1->100按下 `/go` 就开始干活,不要停。
当用户调用 `/go``/go <任务范围>` 时,执行以下步骤:
## 1. 前置检查
### 1.1 必要文档检查
检查以下文件是否存在:
| 文件 | 必要性 | 用途 |
|------|--------|------|
| `doc/tasks.md` | **必须** | 任务清单,执行的圣经 |
| `doc/PRD.md` | **必须** | 产品需求,理解业务 |
| `doc/FeatureSummary.md` | 建议 | 功能契约 |
| `doc/DevelopmentPlan.md` | 建议 | 技术方案 |
| `doc/UIDesign.md` | 可选 | 界面设计 |
**缺少必要文档时**
```
❌ 缺少必要文档:
- doc/tasks.md (必须)
- doc/PRD.md (必须)
请先准备这些文档,或运行:
- /wp 生成 PRD
- /wt 生成 tasks
```
### 1.2 读取所有可用文档
读取存在的所有文档,建立完整上下文。
## 2. 智能判断执行范围
### 2.1 检测项目状态
```
┌─────────────────────────────────────────────────────────┐
│ 项目状态检测 │
├─────────────────────────────────────────────────────────┤
│ │
│ 检查 src/ 或主代码目录是否存在? │
│ │
│ ├── 不存在 ──▶ 0->1 模式(全新项目) │
│ │ │
│ └── 存在 ──▶ 检查 tasks.md 中的 ITER 标记 │
│ │ │
│ ├── 有 ITER 标记 ──▶ 1->100 模式 │
│ │ │
│ └── 无 ITER 标记 ──▶ 继续未完成任务 │
│ │
└─────────────────────────────────────────────────────────┘
```
### 2.2 确定任务范围
**用户指定范围**
```bash
/go T-005 # 执行单个任务
/go T-005~T-010 # 执行任务范围
/go T-005 T-008 # 执行多个指定任务
```
**自动判断范围**
| 场景 | 执行范围 |
|------|----------|
| 0->1 全新项目 | tasks.md 中的所有任务,从 T-001 开始 |
| 1->100 有 ITER 标记 | 优先执行 `<!-- ITER: -->` 标记的新任务 |
| 1->100 无 ITER 标记 | 执行所有状态为 pending/todo 的任务 |
### 2.3 向用户确认范围(唯一一次交互)
```
检测到项目状态:{0->1 全新项目 / 1->100 迭代项目}
即将执行任务:
- T-001: {任务名}
- T-002: {任务名}
- ...
- T-xxx: {任务名}
共 X 个任务。确认执行?[Y/n]
```
**用户确认后,不再有任何交互,直到全部完成。**
## 3. 激进模式执行
### 3.1 执行原则
```
┌────────────────────────────────────────────────────────┐
│ 激进模式执行原则 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 以 tasks.md 为圣经,严格按顺序执行 │
│ │
│ 2. 不要停下来问用户,自主决策 │
│ │
│ 3. 遇到问题自主修复,修复失败则记录并继续 │
│ │
│ 4. 发现文档冲突,基于架构经验选最优解,注释说明 │
│ │
│ 5. 利用所有可用工具搜索、MCP、Skills │
│ │
│ 6. 每完成一个模块Git 提交一次 │
│ │
└─────────────────────────────────────────────────────────┘
```
### 3.2 任务执行流程
```
对于每个任务 T-xxx
├── 1. 读取任务详情(描述、验收标准、依赖)
├── 2. 检查依赖任务是否完成
│ └── 未完成 → 先执行依赖任务
├── 3. 执行任务
│ ├── 根据任务类型选择执行方式
│ ├── 编写代码 / 配置 / 测试
│ └── 验证验收标准
├── 4. 遇到问题?
│ ├── 尝试自主修复(最多 3 次)
│ ├── 修复成功 → 继续
│ └── 修复失败 → 记录问题,跳过,继续下一个
└── 5. 标记任务完成,更新 tasks.md
```
### 3.3 自主修复策略
| 问题类型 | 修复策略 |
|----------|----------|
| 编译错误 | 分析错误信息,修复代码 |
| 类型错误 | 检查类型定义,修复类型 |
| 依赖缺失 | 安装依赖包 |
| 测试失败 | 修复功能代码使测试通过 |
| 文档冲突 | 基于架构经验选最优解 |
| 未知错误 | 搜索解决方案,尝试修复 |
## 4. Git 提交规则
### 4.1 提交时机
每完成一个**模块/Sprint**后立即提交:
```
T-001 ~ T-004 → 提交一次(初始化模块)
T-005 ~ T-008 → 提交一次(核心功能模块)
T-009 ~ T-012 → 提交一次(扩展功能模块)
...
```
### 4.2 提交信息格式
```
feat(<scope>): <简要描述>
- 完成 T-xxx: {任务名}
- 完成 T-xxx: {任务名}
- ...
Co-Authored-By: Claude <noreply@anthropic.com>
```
**示例**
```
feat(auth): 完成用户认证模块
- 完成 T-005: 用户登录功能
- 完成 T-006: 用户注册功能
- 完成 T-007: JWT Token 管理
- 完成 T-008: 权限验证中间件
Co-Authored-By: Claude <noreply@anthropic.com>
```
## 5. 进度汇报
### 5.1 模块完成汇报
每完成一个模块,简要汇报:
```
✅ 模块完成:{模块名}
- T-005: 用户登录 ✓
- T-006: 用户注册 ✓
- T-007: JWT 管理 ✓
- T-008: 权限验证 ✓
Git 提交: feat(auth): 完成用户认证模块
继续执行下一模块...
```
### 5.2 最终汇报
全部完成后,输出完整报告:
```
## 🚀 执行完成
**执行模式**: {0->1 全新项目 / 1->100 迭代}
**任务统计**:
| 状态 | 数量 |
|------|------|
| ✅ 完成 | X 个 |
| ⚠️ 跳过 | X 个 |
| ❌ 失败 | X 个 |
**Git 提交记录**:
- feat(init): 项目初始化
- feat(auth): 用户认证模块
- feat(core): 核心功能模块
- ...
**跳过/失败的任务**(如有):
| 任务 | 原因 |
|------|------|
| T-xxx | {原因} |
**下一步建议**:
- 运行 `npm run dev` 验证
- 运行 `npm run test` 测试
- 检查跳过的任务
```
## 6. 特殊场景处理
### 6.1 技术栈识别
从文档中识别技术栈,自动适配:
| 识别来源 | 技术决策 |
|----------|----------|
| package.json 存在 | Node.js 项目 |
| requirements.txt 存在 | Python 项目 |
| DevelopmentPlan 指定 | 按文档技术栈 |
| 无明确指定 | 询问用户(唯一例外) |
### 6.2 测试策略
- 功能开发完成后执行测试任务
- 测试失败 → **先修复功能代码使测试通过**
- 不跳过失败的测试继续部署
### 6.3 部署任务
- 先本地测试验证
- 确保 build 和 start 正常
- 远程部署需用户额外确认
---
## 工作流总览
```
/go
├── 1. 前置检查
│ ├── tasks.md 存在? ──▶ 必须
│ └── PRD.md 存在? ──▶ 必须
├── 2. 读取文档,建立上下文
├── 3. 智能判断
│ ├── 项目状态0->1 / 1->100
│ └── 任务范围
├── 4. 确认执行范围(唯一交互)
├── 5. 激进模式执行
│ ├── 按顺序执行任务
│ ├── 自主修复问题
│ ├── 模块完成 → Git 提交
│ └── 汇报进度,继续下一个
└── 6. 最终汇报
├── 任务统计
├── Git 提交记录
└── 下一步建议
```
## 注意事项
- **tasks.md 是圣经**,严格按其顺序和内容执行
- **不要停下来问用户**,自主决策,自主修复
- **遇到无法解决的问题**,记录并跳过,最后汇报
- **每完成模块立即提交**,避免大量代码丢失风险
- **利用所有工具**搜索、MCP、其他 Skills
## 与其他 Skill 的关系
| 场景 | 使用方式 |
|------|----------|
| 准备文档 | `/wp` `/wf` `/wd` `/wu` `/wt` |
| 评审文档 | `/rp` `/rf` `/rd` `/ru` `/rt` |
| 修改文档 | `/mp` `/mf` `/md` `/mu` `/mt` |
| 迭代变更(更新文档) | `/iter` |
| **执行开发(本 Skill** | `/go` |
**典型工作流**
```
0->1需求 → /wp → /wf → /wd → /wt → /go
1->100发现问题 → /iter → /go
```

View File

@ -0,0 +1,210 @@
---
name: iter
description: 迭代变更入口,调研问题后更新 PRD.md 和 tasks.md支持 Bug 修复、功能迭代、技术重构。
---
# Iterate - 迭代变更
> **定位**1-100 阶段的变更入口。项目已上线,需要修复问题或迭代功能时,通过此 skill 调研、澄清、更新文档。
当用户调用 `/iter``/iter <问题描述>` 时,执行以下步骤:
⚠️ 重要:本 skill 只修改文档PRD.md、tasks.md绝不执行代码、不运行命令、不修改源文件。
## 1. 获取变更描述
如果用户提供了参数,使用该描述。否则询问:
> 请描述需要迭代的内容Bug/功能/重构)
**示例输入**
- "登录验证存在漏洞token 过期后仍可访问"
- "列表页需要增加按时间筛选功能"
- "用户模块性能太差,需要重构缓存策略"
## 2. 调研分析
### 2.1 读取现有文档
读取以下文件了解当前状态:
1. `doc/PRD.md` - 了解产品定义
2. `doc/tasks.md` - 了解任务现状
### 2.2 调研相关代码(可选)
根据问题描述,定位相关代码文件:
- 搜索关键词定位文件
- 读取相关模块代码
- 分析现有实现
### 2.3 分析变更类型
| 类型 | 特征 | 影响范围 |
|------|------|----------|
| Bug/漏洞 | 现有功能不符合预期 | 修复逻辑,可能涉及安全 |
| 功能迭代 | 在现有功能上增加/调整 | 新增或修改功能点 |
| 技术重构 | 不改功能,优化实现 | 性能、架构、代码质量 |
## 3. 澄清确认
**【必须】向用户提出澄清问题**,确保理解准确:
### 3.1 问题理解确认
向用户确认:
> 我理解的变更需求是:{一句话总结}
>
> 变更类型:{Bug修复 / 功能迭代 / 技术重构}
>
> 影响范围:{涉及的模块/功能}
### 3.2 方案选择(如有多种)
如果有多种解决方案,列出选项让用户选择:
```
方案 A{描述}
- 优点:...
- 缺点:...
方案 B{描述}
- 优点:...
- 缺点:...
请选择方案,或说明其他想法。
```
### 3.3 边界确认
确认变更边界:
> 本次变更**包含**
> - {范围1}
> - {范围2}
>
> 本次变更**不包含**
> - {排除项}
>
> 是否确认?
## 4. 用户确认后执行
**只有用户明确确认后**,才执行以下更新:
### 4.1 更新 PRD.md
使用增量修改标记:
```markdown
<!-- ITER: {日期} - {变更简述} -->
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
或修改现有内容:
```markdown
<!-- ITER: {日期} - {变更简述} -->
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
**更新位置**
- Bug 修复 → 更新对应功能的验收标准
- 功能迭代 → 在 3.2 功能详情添加/修改功能点
- 技术重构 → 在 4.x 非功能需求或 7.1 技术约束中说明
### 4.2 更新 tasks.md
新增任务使用标记:
```markdown
<!-- ITER: {日期} - {变更简述} -->
| T-xxx | {任务名} | {描述} | {依赖} | {优先级} | {验收标准} |
```
**任务 ID 规则**
- 查找现有最大 ID递增分配
- 格式T-xxx三位数字
### 4.3 标记规范
所有变更使用 `<!-- ITER: -->` 前缀,区分于 `/mp` `/mt` 的标记:
- `<!-- ITER: 2026-01-23 - 修复登录验证漏洞 -->`
- 便于追溯迭代历史
## 5. 输出摘要
完成后向用户展示:
```
## 迭代变更完成
**变更类型**: {Bug修复 / 功能迭代 / 技术重构}
**变更摘要**: {一句话描述}
**已更新文档**:
- doc/PRD.md: {更新位置}
- doc/tasks.md: 新增任务 T-xxx
**新增任务**:
| ID | 任务 | 优先级 |
|----|------|--------|
| T-xxx | {任务名} | P0/P1/P2 |
**下一步**:
- 执行任务 T-xxx
- 或运行 `/rp` `/rt` 评审变更
```
---
## 工作流示意
```
用户描述问题
┌─────────────┐
│ 调研分析 │ ──▶ 读取 PRD、tasks、相关代码
└──────┬──────┘
┌─────────────┐
│ 澄清确认 │ ──▶ 提问 → 用户回答 → 确认方案
└──────┬──────┘
▼ 用户确认
┌─────────────┐
│ 更新文档 │ ──▶ PRD.md + tasks.md
└──────┬──────┘
┌─────────────┐
│ 输出摘要 │
└─────────────┘
```
## 注意事项
- **必须先澄清确认**,不要假设用户意图
- 变更范围要明确,避免 scope creep
- 优先级根据问题严重程度判断:
- 安全漏洞 → P0
- 功能 Bug → P0/P1
- 功能迭代 → P1/P2
- 技术重构 → P1/P2
- 只更新 PRD + tasks保持轻量
- 如需更新其他文档,提示用户手动运行 `/mf` `/md`
## 与其他 skill 的关系
| 场景 | 使用 skill |
|------|------------|
| 迭代变更入口 | `/iter`(本 skill |
| 需要更新 FeatureSummary | `/iter` 后运行 `/mf` |
| 需要更新 DevelopmentPlan | `/iter` 后运行 `/md` |
| 需要评审变更 | `/iter` 后运行 `/rp` `/rt` |
| 从头生成文档 | 使用 `/wp` `/wf` `/wd` 等 |

112
.claude/skills/md/SKILL.md Normal file
View File

@ -0,0 +1,112 @@
---
name: md
description: 增量修改 DevelopmentPlan.md根据用户指令在现有内容基础上更新开发计划。
---
# Modify DevelopmentPlan
当用户调用 `/md` 时,执行以下步骤:
## 1. 读取目标文档
读取以下文件:
1. `doc/DevelopmentPlan.md` - 目标文档(必须存在)
2. `doc/FeatureSummary.md` - 上游参考文档
3. `doc/review-DevelopmentPlan-claude.md` - 评审报告(如果存在,自动作为修改依据)
如果 DevelopmentPlan.md 不存在,提示用户:
> DevelopmentPlan.md 不存在,请先使用 `/wd` 生成开发计划。
## 2. 确定修改来源
按以下优先级确定修改内容:
### 2.1 用户提供了修改指令
如果用户在调用 `/md` 时附带了参数或说明,直接使用该指令。
### 2.2 自动检测评审报告
如果用户未提供修改指令,**自动检测** `doc/review-DevelopmentPlan-claude.md` 是否存在:
- **存在**:读取评审报告,提取其中的问题清单,作为本次修改的依据。向用户确认:
> 检测到评审报告,包含 X 个问题。是否根据评审报告进行修改?
- **不存在**:询问用户:
> 请说明需要修改的内容,或先运行 `/rd` 生成评审报告。
## 3. 修改原则
### 3.1 增量修改
- 保留原有内容结构和格式
- 仅修改/新增指定部分
- 不删除未明确要求删除的内容
### 3.2 新增内容标记
对于新增的段落或章节:
```markdown
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
对于行内新增:
```markdown
原有内容 <!-- NEW --> 新增内容
```
### 3.3 修改内容标记
```markdown
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
### 3.4 与 FeatureSummary 一致性
- 开发任务必须覆盖所有功能
- 技术方案必须支撑功能需求
- 阶段划分必须合理
## 4. 执行修改
| 修改类型 | 处理方式 |
|----------|----------|
| 新增开发任务 | 在对应阶段表格中添加行 |
| 修改技术方案 | 更新技术方案章节,添加 MODIFIED 标记 |
| 调整阶段划分 | 移动任务到新阶段,标记变更 |
| 新增风险项 | 在风险管理表格中添加行 |
| 修改里程碑 | 更新里程碑表格 |
## 5. 保存并验证
1. 保存修改后的文档到 `doc/DevelopmentPlan.md`
2. 使用 git diff 展示变更内容
3. 向用户确认修改是否符合预期
## 6. 输出摘要
向用户展示修改摘要:
- 修改位置(章节/行号)
- 修改类型(新增/修改/删除)
- 修改内容概要
- 与 FeatureSummary 的一致性确认
---
## 注意事项
- DevelopmentPlan 依赖于 FeatureSummary修改时需确保与上游一致
- 修改后下游文档UIDesign、tasks可能需要同步更新
- 技术方案修改需谨慎评估影响范围
- 建议修改完成后运行 `/ru` 检查下游一致性
## 标记清理
用户确认修改无误后,可手动删除标记或保留作为变更历史参考。

111
.claude/skills/mf/SKILL.md Normal file
View File

@ -0,0 +1,111 @@
---
name: mf
description: 增量修改 FeatureSummary.md根据用户指令在现有内容基础上更新功能摘要。
---
# Modify FeatureSummary
当用户调用 `/mf` 时,执行以下步骤:
## 1. 读取目标文档
读取以下文件:
1. `doc/FeatureSummary.md` - 目标文档(必须存在)
2. `doc/PRD.md` - 上游参考文档
3. `doc/review-FeatureSummary-claude.md` - 评审报告(如果存在,自动作为修改依据)
如果 FeatureSummary.md 不存在,提示用户:
> FeatureSummary.md 不存在,请先使用 `/wf` 生成功能摘要。
## 2. 确定修改来源
按以下优先级确定修改内容:
### 2.1 用户提供了修改指令
如果用户在调用 `/mf` 时附带了参数或说明,直接使用该指令。
### 2.2 自动检测评审报告
如果用户未提供修改指令,**自动检测** `doc/review-FeatureSummary-claude.md` 是否存在:
- **存在**:读取评审报告,提取其中的问题清单,作为本次修改的依据。向用户确认:
> 检测到评审报告,包含 X 个问题。是否根据评审报告进行修改?
- **不存在**:询问用户:
> 请说明需要修改的内容,或先运行 `/rf` 生成评审报告。
## 3. 修改原则
### 3.1 增量修改
- 保留原有内容结构和格式
- 仅修改/新增指定部分
- 不删除未明确要求删除的内容
### 3.2 新增内容标记
对于新增的段落或章节:
```markdown
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
对于行内新增:
```markdown
原有内容 <!-- NEW --> 新增内容
```
### 3.3 修改内容标记
```markdown
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
### 3.4 与 PRD 一致性
- 所有功能必须来源于 PRD
- 修改后的功能描述必须与 PRD 一致
- 优先级必须与 PRD 匹配
## 4. 执行修改
| 修改类型 | 处理方式 |
|----------|----------|
| 新增功能 | 在对应模块表格中添加行 |
| 修改描述 | 更新功能描述,添加 MODIFIED 标记 |
| 修改优先级 | 更新优先级列 |
| 新增模块 | 在功能清单中添加新章节 |
| 删除功能 | 标记为删除而非直接移除 |
## 5. 保存并验证
1. 保存修改后的文档到 `doc/FeatureSummary.md`
2. 使用 git diff 展示变更内容
3. 向用户确认修改是否符合预期
## 6. 输出摘要
向用户展示修改摘要:
- 修改位置(章节/行号)
- 修改类型(新增/修改/删除)
- 修改内容概要
- 与 PRD 的一致性确认
---
## 注意事项
- FeatureSummary 依赖于 PRD修改时需确保与上游一致
- 修改后下游文档DevelopmentPlan 等)可能需要同步更新
- 建议修改完成后运行 `/rd` 检查下游一致性
## 标记清理
用户确认修改无误后,可手动删除标记或保留作为变更历史参考。

144
.claude/skills/mp/SKILL.md Normal file
View File

@ -0,0 +1,144 @@
---
name: mp
description: 增量修改 PRD.md根据用户指令在现有内容基础上更新产品需求文档。
---
# Modify PRD
当用户调用 `/mp` 时,执行以下步骤:
## 1. 读取目标文档
读取以下文件:
1. `doc/PRD.md` - 目标文档(必须存在)
2. `doc/RequirementsDoc.md` - 上游参考文档
3. `doc/review-PRD-claude.md` - 评审报告(如果存在,自动作为修改依据)
如果 PRD.md 不存在,提示用户:
> PRD.md 不存在,请先使用 `/wp` 生成产品需求文档。
## 2. 确定修改来源
按以下优先级确定修改内容:
### 2.1 用户提供了修改指令
如果用户在调用 `/mp` 时附带了参数或说明,直接使用该指令。
### 2.2 自动检测评审报告
如果用户未提供修改指令,**自动检测** `doc/review-PRD-claude.md` 是否存在:
- **存在**读取评审报告提取其中的问题清单Critical / Major / Minor作为本次修改的依据。向用户确认
> 检测到评审报告 `doc/review-PRD-claude.md`,包含 X 个问题。是否根据评审报告进行修改?
- **不存在**:询问用户:
> 请说明需要修改的内容,或先运行 `/rp` 生成评审报告。
### 2.3 支持的修改来源
- 具体的修改描述(如"在功能需求中增加用户权限管理模块"
- 评审报告(自动检测或手动指定路径)
- 对应的 RequirementsDoc 变更(如"/mr 已更新需求,请同步 PRD"
## 3. 修改原则
### 3.1 增量修改
- 保留原有内容结构和格式
- 仅修改/新增指定部分
- 不删除未明确要求删除的内容
### 3.2 新增内容标记
对于新增的段落或章节,使用 HTML 注释标记:
```markdown
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
对于行内新增,使用:
```markdown
原有内容 <!-- NEW --> 新增内容
```
### 3.3 修改内容标记
对于修改的内容,保留原文作为注释:
```markdown
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
### 3.4 与 RequirementsDoc 一致性
- 所有 PRD 内容必须可追溯到 RequirementsDoc
- 如果修改涉及新功能,先确认 RequirementsDoc 中已有对应需求
- 如果 RequirementsDoc 未包含相关需求,提醒用户先更新需求文档
## 4. 执行修改
按照用户指令修改文档:
1. 定位到需要修改的位置
2. 执行增量修改
3. 添加相应的标记
4. 保持文档格式一致性
5. 确保修改内容与 RequirementsDoc 一致
### 4.1 修改类型处理
| 修改类型 | 处理方式 |
|----------|----------|
| 新增功能点 | 在对应功能模块表格中添加行,关联用户故事 |
| 新增用户故事 | 在 2.2 用户故事列表中添加,分配 US-xxx ID |
| 修改优先级 | 更新功能点优先级,必要时调整用户故事分类 |
| 修改验收标准 | 更新对应功能点的验收标准列 |
| 新增模块 | 在 3.2 功能详情中添加新的子章节 |
| 修改非功能需求 | 在对应章节更新指标或要求 |
## 5. 保存并验证
1. 保存修改后的文档到 `doc/PRD.md`
2. 使用 git diff 展示变更内容
3. 向用户确认修改是否符合预期
## 6. 输出摘要
向用户展示修改摘要:
- 修改位置(章节/行号)
- 修改类型(新增/修改/删除)
- 修改内容概要
- 与 RequirementsDoc 的一致性确认
---
## 注意事项
- PRD 依赖于 RequirementsDoc修改时需确保与上游文档一致
- 修改 PRD 后下游文档FeatureSummary、DevelopmentPlan 等)可能需要同步更新
- 保持现有文档风格(标题层级、表格格式、列表样式)
- 用户故事 ID 必须唯一且连续US-001, US-002...
- 所有功能点必须关联到用户故事
- 重大修改建议先运行 `/rp` 评审确认影响范围
- 修改完成后,建议用户运行 `/rf` 检查下游文档一致性
## 标记清理
当用户确认修改无误后,可手动删除 `<!-- NEW -->``<!-- MODIFIED -->` 标记,或保留作为变更历史参考。
通过 git 可追溯完整修改历史。
## 质量检查
修改 PRD 后,自查以下项目:
- [ ] 修改内容与 RequirementsDoc 一致
- [ ] 新增用户故事有唯一 ID
- [ ] 新增功能点关联到用户故事
- [ ] 新增功能点有明确优先级和验收标准
- [ ] 标记格式正确(`<!-- NEW -->` / `<!-- MODIFIED -->`
- [ ] 文档结构完整,格式一致

View File

@ -0,0 +1,95 @@
---
name: mr
description: 增量修改 RequirementsDoc.md根据用户指令在现有内容基础上更新需求文档。
---
# Modify RequirementsDoc
当用户调用 `/mr` 时,执行以下步骤:
## 1. 读取目标文档
读取 `doc/RequirementsDoc.md` 文件。
如果文件不存在,提示用户:
> RequirementsDoc.md 不存在,请先使用人工方式创建需求文档。
## 2. 获取修改指令
向用户确认修改内容。用户应提供以下信息之一:
- 具体的修改描述(如"在第3节增加性能需求"
- 评审报告路径(如 `doc/review-RequirementsDoc-claude.md`
- 直接的修改内容
如果用户未提供修改指令,询问:
> 请说明需要修改的内容,或提供评审报告路径。
## 3. 修改原则
### 3.1 增量修改
- 保留原有内容结构和格式
- 仅修改/新增指定部分
- 不删除未明确要求删除的内容
### 3.2 新增内容标记
对于新增的段落或章节,使用 HTML 注释标记:
```markdown
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
对于行内新增,使用:
```markdown
原有内容 <!-- NEW --> 新增内容
```
### 3.3 修改内容标记
对于修改的内容,保留原文作为注释:
```markdown
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
## 4. 执行修改
按照用户指令修改文档:
1. 定位到需要修改的位置
2. 执行增量修改
3. 添加相应的标记
4. 保持文档格式一致性
## 5. 保存并验证
1. 保存修改后的文档到 `doc/RequirementsDoc.md`
2. 使用 git diff 展示变更内容
3. 向用户确认修改是否符合预期
## 6. 输出摘要
向用户展示修改摘要:
- 修改位置(章节/行号)
- 修改类型(新增/修改/删除)
- 修改内容概要
---
## 注意事项
- RequirementsDoc 是文档链源头,修改会影响所有下游文档
- 修改前确认用户意图,避免误改
- 保持现有文档风格(标题层级、表格格式、列表样式)
- 重大修改建议先运行 `/rr` 评审确认影响范围
- 修改完成后,建议用户检查下游文档是否需要同步更新
## 标记清理
当用户确认修改无误后,可手动删除 `<!-- NEW -->``<!-- MODIFIED -->` 标记,或保留作为变更历史参考。
通过 git 可追溯完整修改历史。

132
.claude/skills/mt/SKILL.md Normal file
View File

@ -0,0 +1,132 @@
---
name: mt
description: 增量修改 tasks.md根据用户指令在现有内容基础上更新任务列表。
---
# Modify Tasks
当用户调用 `/mt` 时,执行以下步骤:
## 1. 读取目标文档
读取以下文件:
1. `doc/tasks.md` - 目标文档(必须存在)
2. `doc/UIDesign.md` - 上游参考文档
3. `doc/DevelopmentPlan.md` - 上游参考文档
4. `doc/review-tasks-claude.md` - 评审报告(如果存在,自动作为修改依据)
如果 tasks.md 不存在,提示用户:
> tasks.md 不存在,请先使用 `/wt` 生成任务列表。
## 2. 确定修改来源
按以下优先级确定修改内容:
### 2.1 用户提供了修改指令
如果用户在调用 `/mt` 时附带了参数或说明,直接使用该指令。
### 2.2 自动检测评审报告
如果用户未提供修改指令,**自动检测** `doc/review-tasks-claude.md` 是否存在:
- **存在**:读取评审报告,提取其中的问题清单,作为本次修改的依据。向用户确认:
> 检测到评审报告,包含 X 个问题。是否根据评审报告进行修改?
- **不存在**:询问用户:
> 请说明需要修改的内容,或先运行 `/rt` 生成评审报告。
## 3. 修改原则
### 3.1 增量修改
- 保留原有内容结构和格式
- 仅修改/新增指定部分
- 不删除未明确要求删除的内容
### 3.2 新增内容标记
对于新增的段落或章节:
```markdown
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
对于行内新增:
```markdown
原有内容 <!-- NEW --> 新增内容
```
### 3.3 修改内容标记
```markdown
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
### 3.4 与上游文档一致性
- 任务必须覆盖 DevelopmentPlan 所有开发项
- 任务必须覆盖 UIDesign 所有页面实现
- 任务依赖关系必须合理
## 4. 执行修改
| 修改类型 | 处理方式 |
|----------|----------|
| 新增任务 | 在对应阶段表格中添加行,分配新 ID |
| 修改描述 | 更新任务描述,添加 MODIFIED 标记 |
| 修改优先级 | 更新优先级列 |
| 修改依赖 | 更新依赖列,检查循环依赖 |
| 修改验收标准 | 更新验收标准列 |
| 调整阶段 | 移动任务到新阶段,更新依赖图 |
### 4.1 任务 ID 规则
- 新增任务 ID 必须唯一
- ID 格式T-XXX三位数字如 T-001
- 在现有最大 ID 基础上递增
## 5. 保存并验证
1. 保存修改后的文档到 `doc/tasks.md`
2. 使用 git diff 展示变更内容
3. 向用户确认修改是否符合预期
## 6. 输出摘要
向用户展示修改摘要:
- 修改位置(章节/行号)
- 修改类型(新增/修改/删除)
- 修改内容概要
- 新增/修改的任务 ID 列表
- 与上游文档的一致性确认
---
## 注意事项
- tasks.md 是文档链末端,修改不影响其他文档
- 任务 ID 必须唯一,不可重复使用已删除的 ID
- 修改依赖关系时需检查是否产生循环依赖
- 验收标准必须具体可测试
- 任务粒度要适中
## 标记清理
用户确认修改无误后,可手动删除标记或保留作为变更历史参考。
## 质量检查
修改 tasks 后,自查以下项目:
- [ ] 任务 ID 唯一且格式正确
- [ ] 无循环依赖
- [ ] 验收标准明确
- [ ] 覆盖所有上游功能
- [ ] 标记格式正确

114
.claude/skills/mu/SKILL.md Normal file
View File

@ -0,0 +1,114 @@
---
name: mu
description: 增量修改 UIDesign.md根据用户指令在现有内容基础上更新 UI 设计文档。
---
# Modify UIDesign
当用户调用 `/mu` 时,执行以下步骤:
## 1. 读取目标文档
读取以下文件:
1. `doc/UIDesign.md` - 目标文档(必须存在)
2. `doc/DevelopmentPlan.md` - 上游参考文档
3. `doc/review-UIDesign-claude.md` - 评审报告(如果存在,自动作为修改依据)
如果 UIDesign.md 不存在,提示用户:
> UIDesign.md 不存在,请先使用 `/wu` 生成 UI 设计文档。
## 2. 确定修改来源
按以下优先级确定修改内容:
### 2.1 用户提供了修改指令
如果用户在调用 `/mu` 时附带了参数或说明,直接使用该指令。
### 2.2 自动检测评审报告
如果用户未提供修改指令,**自动检测** `doc/review-UIDesign-claude.md` 是否存在:
- **存在**:读取评审报告,提取其中的问题清单,作为本次修改的依据。向用户确认:
> 检测到评审报告,包含 X 个问题。是否根据评审报告进行修改?
- **不存在**:询问用户:
> 请说明需要修改的内容,或先运行 `/ru` 生成评审报告。
## 3. 修改原则
### 3.1 增量修改
- 保留原有内容结构和格式
- 仅修改/新增指定部分
- 不删除未明确要求删除的内容
### 3.2 新增内容标记
对于新增的段落或章节:
```markdown
<!-- NEW START -->
新增内容...
<!-- NEW END -->
```
对于行内新增:
```markdown
原有内容 <!-- NEW --> 新增内容
```
### 3.3 修改内容标记
```markdown
<!-- MODIFIED: 原内容为 "xxx" -->
修改后的内容
```
### 3.4 与 DevelopmentPlan 一致性
- 页面设计必须覆盖所有功能模块
- 交互流程必须支撑功能需求
- 设计规范必须统一
## 4. 执行修改
| 修改类型 | 处理方式 |
|----------|----------|
| 新增页面 | 在页面设计章节添加新子章节 |
| 修改布局 | 更新布局描述,添加 MODIFIED 标记 |
| 修改组件 | 更新组件表格 |
| 修改交互 | 更新交互说明 |
| 新增状态 | 在状态列表中添加项目 |
| 修改设计规范 | 更新设计规范章节 |
## 5. 保存并验证
1. 保存修改后的文档到 `doc/UIDesign.md`
2. 使用 git diff 展示变更内容
3. 向用户确认修改是否符合预期
## 6. 输出摘要
向用户展示修改摘要:
- 修改位置(章节/行号)
- 修改类型(新增/修改/删除)
- 修改内容概要
- 与 DevelopmentPlan 的一致性确认
---
## 注意事项
- UIDesign 依赖于 DevelopmentPlan修改时需确保与上游一致
- 修改后下游文档tasks可能需要同步更新
- 页面修改需考虑对用户流程的影响
- 设计规范修改需检查所有页面的一致性
- 建议修改完成后运行 `/rt` 检查下游一致性
## 标记清理
用户确认修改无误后,可手动删除标记或保留作为变更历史参考。

101
.claude/skills/rd/SKILL.md Normal file
View File

@ -0,0 +1,101 @@
---
name: rd
description: 评审 DevelopmentPlan.md检查技术可行性和与上游文档一致性输出结构化评审报告。
---
# Review DevelopmentPlan
当用户调用 `/rd` 时,执行以下步骤:
## 1. 读取文档
读取以下文件:
1. `doc/DevelopmentPlan.md` - 目标文档(必须存在)
2. `doc/FeatureSummary.md` - 上游参照文档
如果 DevelopmentPlan.md 不存在,提示用户:
> DevelopmentPlan.md 不存在,请先使用 `/wd` 生成开发计划。
## 2. 评审维度
### 2.1 与 FeatureSummary 一致性检查
- 开发任务是否覆盖所有功能模块
- 技术方案是否支撑功能需求
- 排期是否合理
### 2.2 技术可行性检查
- 技术方案是否可行
- 技术栈选择是否合理
- 是否存在技术风险
- 依赖关系是否明确
### 2.3 完整性检查
- 是否有明确的里程碑划分
- 是否有资源分配说明
- 是否有风险应对措施
## 3. 生成评审报告
输出到 `doc/review-DevelopmentPlan-claude.md`,结构如下:
```markdown
# DevelopmentPlan 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | {YYYY-MM-DD HH:MM} |
| 目标文档 | doc/DevelopmentPlan.md |
| 参照文档 | doc/FeatureSummary.md |
| 问题统计 | X 个严重 / Y 个一般 / Z 个建议 |
## 功能覆盖分析
| FeatureSummary 功能 | DevelopmentPlan 对应 | 状态 |
|---------------------|----------------------|------|
| {功能名} | {对应任务/模块} | ✅/⚠️/❌ |
## 技术风险分析
| 风险项 | 影响范围 | 严重程度 | 建议措施 |
|--------|----------|----------|----------|
| {风险} | {范围} | 高/中/低 | {措施} |
## 问题清单
### 严重问题 (Critical)
{问题列表,含位置引用}
### 一般问题 (Major)
{问题列表,含位置引用}
### 改进建议 (Minor)
{建议列表}
## 评审结论
{通过 / 需修改后通过 / 不通过}
### 下一步行动
- [ ] {待办事项}
```
## 4. 输出规范
- 输出语言:中文
- 问题分级Critical / Major / Minor
- 包含文件引用(如 `doc/DevelopmentPlan.md:28`
- 技术风险需明确影响范围和应对建议
---
## 注意事项
- 只做评审,不修改原文档
- 重点关注技术可行性和风险
- 评审报告保存后,建议用户根据问题运行 `/md` 修改

View File

@ -0,0 +1,96 @@
---
name: rf
description: 评审 FeatureSummary.md对比 PRD 检查一致性,输出结构化评审报告。
---
# Review FeatureSummary
当用户调用 `/rf` 时,执行以下步骤:
## 1. 读取文档
读取以下文件:
1. `doc/FeatureSummary.md` - 目标文档(必须存在)
2. `doc/PRD.md` - 上游参照文档
如果 FeatureSummary.md 不存在,提示用户:
> FeatureSummary.md 不存在,请先使用 `/wf` 生成功能摘要。
## 2. 评审维度
### 2.1 与 PRD 一致性检查
- 功能模块是否完整覆盖 PRD 3.2 功能详情
- 功能描述是否与 PRD 一致
- 优先级标注是否与 PRD 匹配
### 2.2 完整性检查
- 每个功能模块是否有清晰的描述
- 是否遗漏 PRD 中的功能点
- 功能分类是否合理
### 2.3 质量检查
- 描述是否简洁准确
- 是否有冗余或重复内容
- 格式是否规范统一
## 3. 生成评审报告
输出到 `doc/review-FeatureSummary-claude.md`,结构如下:
```markdown
# FeatureSummary 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | {YYYY-MM-DD HH:MM} |
| 目标文档 | doc/FeatureSummary.md |
| 参照文档 | doc/PRD.md |
| 问题统计 | X 个严重 / Y 个一般 / Z 个建议 |
## 覆盖度分析
| PRD 功能模块 | FeatureSummary 对应 | 状态 |
|--------------|---------------------|------|
| {模块名} | {对应位置} | ✅/⚠️/❌ |
**覆盖率**: X/Y 完全覆盖
## 问题清单
### 严重问题 (Critical)
{问题列表,含位置引用}
### 一般问题 (Major)
{问题列表,含位置引用}
### 改进建议 (Minor)
{建议列表}
## 评审结论
{通过 / 需修改后通过 / 不通过}
### 下一步行动
- [ ] {待办事项}
```
## 4. 输出规范
- 输出语言:中文
- 问题分级Critical / Major / Minor
- 包含文件引用(如 `doc/FeatureSummary.md:15`
- 问题按严重性排序
---
## 注意事项
- 只做评审,不修改原文档
- 重点检查与 PRD 的一致性
- 评审报告保存后,建议用户根据问题运行 `/mf` 修改

177
.claude/skills/rp/SKILL.md Normal file
View File

@ -0,0 +1,177 @@
---
name: rp
description: 评审 PRD.md对比 RequirementsDoc 检查一致性,输出结构化评审报告。
---
# Review PRD
当用户调用 `/rp` 时,执行以下步骤:
## 1. 读取文档
读取以下文件:
- 目标文档:`doc/PRD.md`
- 上游文档:`doc/RequirementsDoc.md`
如果 PRD.md 不存在,提示用户:
> PRD.md 不存在,请先使用 `/wp` 生成 PRD。
如果 RequirementsDoc.md 不存在,提示用户:
> RequirementsDoc.md 不存在,无法进行一致性检查。请先创建需求文档。
## 2. 评审维度
PRD 位于文档链的第二层,需要对比上游 RequirementsDoc 进行评审。
### 2.1 与 RequirementsDoc 的一致性
- [ ] PRD 是否覆盖了 RequirementsDoc 中的所有功能需求
- [ ] PRD 是否覆盖了 RequirementsDoc 中的所有非功能需求
- [ ] PRD 中是否有 RequirementsDoc 中未提及的需求(需标注来源)
- [ ] 术语定义是否与 RequirementsDoc 一致
- [ ] 优先级划分是否与 RequirementsDoc 一致
### 2.2 用户故事质量
- [ ] 所有用户故事是否有唯一 IDUS-xxx
- [ ] 用户故事是否符合格式:作为{角色},我想要{功能},以便{价值}
- [ ] 用户角色是否明确定义
- [ ] 验收标准是否具体可测试
- [ ] 用户旅程是否完整描述核心流程
### 2.3 功能需求完整性
- [ ] 功能架构是否清晰(模块划分合理)
- [ ] 所有功能点是否关联到用户故事
- [ ] 功能点是否有明确的优先级
- [ ] 功能点是否有验收标准
- [ ] 是否遗漏边界情况和异常处理
### 2.4 非功能需求
- [ ] 性能需求是否有量化指标
- [ ] 安全需求是否明确
- [ ] 兼容性需求是否完整
- [ ] 可用性需求是否可验证
### 2.5 文档结构
- [ ] 文档结构是否完整(无空章节)
- [ ] 格式是否统一(表格、列表、标题层级)
- [ ] 术语表是否完整
## 3. 生成评审报告
按以下格式输出评审报告:
```markdown
# PRD 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | {YYYY-MM-DD HH:mm} |
| 目标文档 | doc/PRD.md |
| 参照文档 | doc/RequirementsDoc.md |
| 问题统计 | {critical} 个严重 / {major} 个一般 / {minor} 个建议 |
## 一致性检查
### 需求覆盖分析
| RequirementsDoc 需求项 | PRD 对应位置 | 状态 |
|------------------------|--------------|------|
| {需求1} | {PRD章节/用户故事ID} | ✅ 已覆盖 / ⚠️ 部分覆盖 / ❌ 未覆盖 |
### 差异说明
{列出 PRD 中新增的、RequirementsDoc 未提及的内容,需说明来源或理由}
## 问题清单
### 严重问题 (Critical)
> 必须修复,否则影响后续文档生成
1. **[位置: doc/PRD.md:行号]** 问题描述
- 现状:...
- 与 RequirementsDoc 的差异:...
- 建议:...
### 一般问题 (Major)
> 建议修复,可提升文档质量
1. **[位置]** 问题描述
- 建议:...
### 改进建议 (Minor)
> 可选优化项
1. **[位置]** 建议内容
## 用户故事评估
| 评估项 | 结果 |
|--------|------|
| 用户故事总数 | {数量} |
| 符合格式规范 | {数量} / {总数} |
| 有验收标准 | {数量} / {总数} |
| 关联功能点 | {数量} / {总数} |
### 用户故事问题
{列出不符合规范的用户故事}
## 评审结论
{通过 / 需修改后通过 / 不通过}
**结论说明**
- 通过PRD 与 RequirementsDoc 一致,可进入下一阶段
- 需修改后通过:存在问题但不影响整体理解,修复后可继续
- 不通过:存在严重一致性问题或遗漏,需重新生成
### 下一步行动
- [ ] 行动项1
- [ ] 行动项2
```
## 4. 保存报告
将评审报告保存到 `doc/review-PRD-claude.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示评审摘要:
- 一致性检查结果(覆盖率)
- 发现的问题数量(按严重程度分类)
- 用户故事评估结果
- 评审结论
- 报告文件路径
---
## 注意事项
- 评审时保持客观,聚焦于文档质量和一致性
- 问题描述要具体,给出明确的位置引用(如 `doc/PRD.md:42`
- 一致性检查要逐项对比,不能遗漏
- 建议要可操作,避免模糊表述
- 不要修改原文档,只输出评审报告
## 常见问题模式
在评审时重点关注以下常见问题:
1. **需求遗漏**RequirementsDoc 中有但 PRD 中没有的需求
2. **需求偏离**PRD 中的描述与 RequirementsDoc 不一致
3. **凭空添加**PRD 中有但 RequirementsDoc 中没有的需求(需要来源说明)
4. **用户故事缺陷**:格式不规范、缺少验收标准、角色不明确
5. **功能孤立**:功能点未关联到任何用户故事
6. **优先级冲突**PRD 与 RequirementsDoc 的优先级划分不一致

111
.claude/skills/rr/SKILL.md Normal file
View File

@ -0,0 +1,111 @@
---
name: rr
description: 评审 RequirementsDoc.md检查需求文档的完整性、清晰度和可执行性输出结构化评审报告。
---
# Review RequirementsDoc
当用户调用 `/rr` 时,执行以下步骤:
## 1. 读取目标文档
读取 `doc/RequirementsDoc.md` 文件。
如果文件不存在,提示用户:
> RequirementsDoc.md 不存在,请先创建需求文档。
## 2. 评审维度
RequirementsDoc 是文档链的源头,没有上游依赖。重点检查以下维度:
### 2.1 完整性
- [ ] 产品概述是否清晰(定位、目标用户、核心价值)
- [ ] 功能需求是否完整列出
- [ ] 非功能需求是否涵盖(性能、安全、兼容性)
- [ ] 数据规范是否明确(输入输出格式、字段定义)
- [ ] 边界条件和异常情况是否考虑
### 2.2 清晰度
- [ ] 术语定义是否一致,无歧义
- [ ] 用例描述是否具体可理解
- [ ] 优先级是否明确标注
- [ ] 是否有模糊表述("等"、"可能"、"应该"等)
### 2.3 可执行性
- [ ] 需求是否可被验证(有明确的验收标准)
- [ ] 技术约束是否合理
- [ ] 依赖项是否明确
### 2.4 结构规范
- [ ] 文档结构是否清晰(章节划分合理)
- [ ] 格式是否统一(表格、列表、标题层级)
## 3. 生成评审报告
按以下格式输出评审报告:
```markdown
# RequirementsDoc 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | {YYYY-MM-DD HH:mm} |
| 目标文档 | doc/RequirementsDoc.md |
| 问题统计 | {critical} 个严重 / {major} 个一般 / {minor} 个建议 |
## 问题清单
### 严重问题 (Critical)
> 必须修复,否则影响后续文档生成
1. **[位置: 第X节/第Y行]** 问题描述
- 现状:...
- 建议:...
### 一般问题 (Major)
> 建议修复,可提升文档质量
1. **[位置]** 问题描述
- 建议:...
### 改进建议 (Minor)
> 可选优化项
1. **[位置]** 建议内容
## 评审结论
{通过 / 需修改后通过 / 不通过}
### 下一步行动
- [ ] 行动项1
- [ ] 行动项2
```
## 4. 保存报告
将评审报告保存到 `doc/review-RequirementsDoc-claude.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示评审摘要:
- 发现的问题数量(按严重程度分类)
- 评审结论
- 报告文件路径
---
## 注意事项
- 评审时保持客观,聚焦于文档质量而非业务判断
- 问题描述要具体,给出明确的位置引用
- 建议要可操作,避免模糊表述
- 不要修改原文档,只输出评审报告

115
.claude/skills/rt/SKILL.md Normal file
View File

@ -0,0 +1,115 @@
---
name: rt
description: 评审 tasks.md检查任务完整性和与上游文档一致性输出结构化评审报告。
---
# Review Tasks
当用户调用 `/rt` 时,执行以下步骤:
## 1. 读取文档
读取以下文件:
1. `doc/tasks.md` - 目标文档(必须存在)
2. `doc/UIDesign.md` - 上游参照文档
3. `doc/DevelopmentPlan.md` - 上游参照文档
如果 tasks.md 不存在,提示用户:
> tasks.md 不存在,请先使用 `/wt` 生成任务列表。
## 2. 评审维度
### 2.1 与上游文档一致性检查
- 任务是否覆盖 DevelopmentPlan 所有开发项
- 任务是否覆盖 UIDesign 所有页面实现
- 任务优先级是否与功能优先级匹配
### 2.2 任务完整性检查
- 每个任务是否有明确的描述
- 任务粒度是否合适(不过大也不过小)
- 任务依赖关系是否明确
- 验收标准是否清晰
### 2.3 可执行性检查
- 任务是否可直接开始执行
- 是否有阻塞项未说明
- 估时是否合理(如有)
## 3. 生成评审报告
输出到 `doc/review-tasks-claude.md`,结构如下:
```markdown
# Tasks 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | {YYYY-MM-DD HH:MM} |
| 目标文档 | doc/tasks.md |
| 参照文档 | doc/UIDesign.md, doc/DevelopmentPlan.md |
| 问题统计 | X 个严重 / Y 个一般 / Z 个建议 |
## 覆盖度分析
### DevelopmentPlan 覆盖
| 开发项 | 对应任务 | 状态 |
|--------|----------|------|
| {开发项} | {任务ID/名称} | ✅/⚠️/❌ |
### UIDesign 覆盖
| UI 页面 | 对应任务 | 状态 |
|---------|----------|------|
| {页面名} | {任务ID/名称} | ✅/⚠️/❌ |
**总覆盖率**: X/Y
## 任务质量分析
| 检查项 | 通过数 | 总数 |
|--------|--------|------|
| 有明确描述 | X | Y |
| 有验收标准 | X | Y |
| 粒度合适 | X | Y |
## 问题清单
### 严重问题 (Critical)
{问题列表,含位置引用}
### 一般问题 (Major)
{问题列表,含位置引用}
### 改进建议 (Minor)
{建议列表}
## 评审结论
{通过 / 需修改后通过 / 不通过}
### 下一步行动
- [ ] {待办事项}
```
## 4. 输出规范
- 输出语言:中文
- 问题分级Critical / Major / Minor
- 包含文件引用(如 `doc/tasks.md:12`
- 任务问题需说明对开发执行的影响
---
## 注意事项
- 只做评审,不修改原文档
- 重点检查任务覆盖度和可执行性
- tasks.md 是文档链末端,必须覆盖所有上游功能
- 评审报告保存后,建议用户根据问题运行 `/mt` 修改

105
.claude/skills/ru/SKILL.md Normal file
View File

@ -0,0 +1,105 @@
---
name: ru
description: 评审 UIDesign.md对比 DevelopmentPlan 检查设计一致性,输出结构化评审报告。
---
# Review UIDesign
当用户调用 `/ru` 时,执行以下步骤:
## 1. 读取文档
读取以下文件:
1. `doc/UIDesign.md` - 目标文档(必须存在)
2. `doc/DevelopmentPlan.md` - 上游参照文档
如果 UIDesign.md 不存在,提示用户:
> UIDesign.md 不存在,请先使用 `/wu` 生成 UI 设计文档。
## 2. 评审维度
### 2.1 与 DevelopmentPlan 一致性检查
- UI 页面是否覆盖所有功能模块
- 交互流程是否与开发计划匹配
- 页面结构是否支撑功能需求
### 2.2 设计完整性检查
- 页面列表是否完整
- 每个页面是否有清晰的布局描述
- 交互说明是否充分
- 状态变化是否考虑全面(加载、错误、空状态等)
### 2.3 可用性检查
- 用户流程是否顺畅
- 信息架构是否合理
- 是否有一致的设计规范
## 3. 生成评审报告
输出到 `doc/review-UIDesign-claude.md`,结构如下:
```markdown
# UIDesign 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | {YYYY-MM-DD HH:MM} |
| 目标文档 | doc/UIDesign.md |
| 参照文档 | doc/DevelopmentPlan.md |
| 问题统计 | X 个严重 / Y 个一般 / Z 个建议 |
## 页面覆盖分析
| DevelopmentPlan 功能 | UIDesign 页面 | 状态 |
|----------------------|---------------|------|
| {功能名} | {对应页面} | ✅/⚠️/❌ |
**覆盖率**: X/Y 完全覆盖
## 设计一致性检查
| 检查项 | 结果 |
|--------|------|
| 页面命名规范 | ✅/❌ |
| 布局风格统一 | ✅/❌ |
| 交互模式一致 | ✅/❌ |
## 问题清单
### 严重问题 (Critical)
{问题列表,含位置引用}
### 一般问题 (Major)
{问题列表,含位置引用}
### 改进建议 (Minor)
{建议列表}
## 评审结论
{通过 / 需修改后通过 / 不通过}
### 下一步行动
- [ ] {待办事项}
```
## 4. 输出规范
- 输出语言:中文
- 问题分级Critical / Major / Minor
- 包含文件引用(如 `doc/UIDesign.md:45`
- 设计问题需说明影响的用户体验
---
## 注意事项
- 只做评审,不修改原文档
- 重点检查页面覆盖度和设计一致性
- 评审报告保存后,建议用户根据问题运行 `/mu` 修改

View File

@ -0,0 +1,78 @@
---
name: update
aliases: [up]
description: 收集用户反馈并更新最近使用的 skill。可通过 /update 或 /up 调用。在用完某个 skill 后调用此命令来优化该 skill。
disable-model-invocation: true
argument-hint: [skill-name]
---
# Skill 更新助手
当用户调用 `/up``/up <skill-name>` 时,执行以下步骤:
## 1. 识别目标 Skill
**如果用户提供了参数 `$ARGUMENTS`**
- 直接使用指定的 skill 名称作为更新目标
**如果没有提供参数**
分析当前对话历史,找出最近使用的 skill
- 搜索对话中的 `<command-name>` 标签,识别调用过的 skill
- 如果找到多个 skill让用户确认要更新哪一个
- 如果没有找到任何 skill 调用记录,提示用户先使用一个 skill
## 2. 收集用户反馈
向用户询问以下问题(使用 AskUserQuestion 工具):
**问题 1这次使用体验如何**
- 很好skill 完全满足需求
- 基本满足,但有改进空间
- 不太满意,需要较大调整
**问题 2具体需要改进的方面**(多选)
- 执行步骤不够清晰
- 缺少某些功能
- 输出格式需要调整
- 提示词需要优化
- 其他(用户自定义输入)
## 3. 分析优化点
基于用户反馈和本次 skill 使用过程,分析以下方面:
1. **执行流程**:哪些步骤可以简化或合并?
2. **指令清晰度**:哪些指令描述不够明确?
3. **遗漏功能**:有哪些场景没有覆盖到?
4. **输出质量**:输出格式是否符合用户预期?
## 4. 定位 Skill 文件
按以下优先级搜索 skill 文件:
1. 项目级:`.claude/skills/<skill-name>/SKILL.md`
2. 用户级:`~/.claude/skills/<skill-name>/SKILL.md`
## 5. 更新 Skill
读取现有的 SKILL.md 文件内容,根据分析结果进行更新:
- 保持 frontmatter 格式不变(除非需要修改 description
- 优化执行步骤的描述
- 添加缺失的功能说明
- 改进提示词的表达方式
- 添加必要的注意事项或边界情况处理
## 6. 确认更新
在更新前,向用户展示:
- 修改前后的对比diff 格式)
- 说明每处修改的原因
用户确认后才执行实际的文件更新。
## 注意事项
- 如果 skill 文件不存在或路径无法确定,提示用户手动指定路径
- 更新时保持 skill 的原有风格和结构
- 重大修改需要用户明确确认
- 保留原有的有效内容,只做增量优化

323
.claude/skills/wd/SKILL.md Normal file
View File

@ -0,0 +1,323 @@
---
name: wd
description: 从上游文档生成 DevelopmentPlan.md包含技术方案和开发排期。
---
# Write DevelopmentPlan
> **文档定位**DevelopmentPlan 是「执行蓝图」文档,偏技术语言和时间约束。定义技术架构、实现方案、开发阶段、里程碑,是开发团队的行动指南。
当用户调用 `/wd` 时,执行以下步骤:
## 1. 读取源文档
读取以下文件:
1. `doc/RequirementsDoc.md` - 必须存在
2. `doc/PRD.md` - 必须存在
3. `doc/FeatureSummary.md` - 必须存在
如果文件不存在,提示用户:
> 缺少上游文档,请先确保 RequirementsDoc.md、PRD.md 和 FeatureSummary.md 存在。
如果已存在 `doc/DevelopmentPlan.md`,同时读取作为参考(保持风格一致)。
## 2. 分析开发需求
从上游文档中提取以下信息:
### 2.1 功能需求
- 从 FeatureSummary 获取功能清单和契约
- 从 PRD 获取功能详情和验收标准
### 2.2 技术约束
- 从 PRD 获取技术约束
- 从 RequirementsDoc 获取技术决策
### 2.3 优先级排序
- 按 P0 → P1 → P2 顺序规划开发
- 考虑功能依赖关系
## 3. 生成 DevelopmentPlan
按以下结构生成文档:
```markdown
# {产品名称} - 开发计划
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | {YYYY-MM-DD} |
| 来源文档 | FeatureSummary.md |
## 1. 项目概述
### 1.1 项目目标
{从 PRD 提取的项目目标}
### 1.2 技术栈
| 层级 | 技术选型 | 版本 | 说明 |
|------|----------|------|------|
| 前端 | {技术} | {版本} | {说明} |
| 后端 | {技术} | {版本} | {说明} |
| 数据库 | {技术} | {版本} | {说明} |
| 基础设施 | {技术} | {版本} | {说明} |
### 1.3 开发原则
{开发规范和原则}
## 2. 技术架构
### 2.1 系统架构图
**【必须】使用架构图展示系统整体结构:**
```
┌─────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Web App │ │ Mobile App │ │
│ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ API 网关层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ API Gateway / Load Balancer │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 服务层 │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ 服务 A │ │ 服务 B │ │ 服务 C │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │
└────────┼───────────────┼───────────────┼────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ 数据层 │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ 数据库 │ │ 缓存 │ │ 消息队列 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### 2.2 模块依赖图
**【必须】使用依赖图展示模块间关系:**
```
┌──────────────┐
│ 模块 A │
│ (核心模块) │
└──────┬───────┘
┌───┴───┐
▼ ▼
┌──────┐ ┌──────┐
│模块B │ │模块C │
└──┬───┘ └──┬───┘
│ │
▼ ▼
┌──────────────┐
│ 模块 D │
│ (基础设施) │
└──────────────┘
```
### 2.3 数据流图
**【必须】使用数据流图展示关键数据流转:**
```
用户请求 ──▶ API Gateway ──▶ 服务A ──▶ 数据库
缓存层
服务B ──▶ 外部API
```
## 3. 开发阶段
### 3.1 阶段时间线
**【必须】使用时间线展示开发阶段:**
```
Phase 1 Phase 2 Phase 3
│ │ │
{起止日期} {起止日期} {起止日期}
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 基础 │ ────▶ │ 核心 │ ────▶ │ 优化 │
│ 架构 │ │ 功能 │ │ 扩展 │
└─────────┘ └─────────┘ └─────────┘
交付物: 交付物: 交付物:
• {交付1} • {交付1} • {交付1}
• {交付2} • {交付2} • {交付2}
```
### 3.2 Phase 1: {阶段名称}
**目标**: {阶段目标}
**时间**: {起止日期}
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|--------|------|------|------|--------|----------|
| T-001 | {任务名} | {描述} | - | P0 | F-001 |
| T-002 | {任务名} | {描述} | T-001 | P0 | F-002 |
**阶段依赖图:**
```
T-001 ──▶ T-002 ──▶ T-003
└──▶ T-004
```
{重复以上结构覆盖所有阶段}
## 4. 技术方案
### 4.1 {模块名称}
**功能**: {功能描述}
**技术选型**:
| 组件 | 技术 | 选型理由 |
|------|------|----------|
| {组件} | {技术} | {理由} |
**架构设计**:
```
┌─────────────────────────────────────┐
│ {模块名称} │
├─────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ │
│ │ 组件A │ ───▶ │ 组件B │ │
│ └─────────┘ └─────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ │
│ │ 数据层 │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────┘
```
**接口设计**:
| 接口 | 方法 | 路径 | 说明 |
|------|------|------|------|
| {接口名} | GET/POST | /api/xxx | {说明} |
**实现要点**:
- {技术要点1}
- {技术要点2}
{重复以上结构覆盖所有模块}
## 5. 风险管理
| 风险 | 可能性 | 影响 | 应对措施 | 负责人 |
|------|--------|------|----------|--------|
| {风险} | 高/中/低 | 高/中/低 | {措施} | {负责人} |
## 6. 里程碑
**【必须】使用里程碑图展示关键节点:**
```
M1 M2 M3 M4
│ │ │ │
▼ ▼ ▼ ▼
◆───────────────◆───────────────◆───────────────◆
│ │ │ │
{日期} {日期} {日期} {日期}
{里程碑名} {里程碑名} {里程碑名} {里程碑名}
```
| 里程碑 | 日期 | 目标 | 交付物 | 验收标准 |
|--------|------|------|--------|----------|
| M1 | {日期} | {目标} | {交付物} | {标准} |
## 7. 资源需求
| 角色 | 人数 | 职责 | 参与阶段 |
|------|------|------|----------|
| {角色} | {人数} | {职责} | Phase 1-2 |
```
## 4. 保存文档
将生成的 DevelopmentPlan 保存到 `doc/DevelopmentPlan.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示生成摘要:
- DevelopmentPlan 文件路径
- 开发阶段数量
- 技术方案模块数量
- 建议的下一步操作(运行 `/rd` 评审)
---
## 可视化输出要求
DevelopmentPlan 作为「执行蓝图」文档,需要清晰传达技术方案和时间安排,**必须包含**
| 章节 | 可视化形式 | 必要性 |
|------|------------|--------|
| 2.1 系统架构图 | 架构图ASCII | **必须** |
| 2.2 模块依赖图 | 依赖图ASCII | **必须** |
| 2.3 数据流图 | 数据流图ASCII | **必须** |
| 3.1 阶段时间线 | 时间线ASCII | **必须** |
| 3.x 阶段依赖图 | 任务依赖图 | **必须** |
| 4.x 模块架构 | 模块架构图 | 建议 |
| 6. 里程碑 | 里程碑图 | **必须** |
## 注意事项
- DevelopmentPlan 使用**技术语言**,面向开发团队
- 开发计划必须覆盖 FeatureSummary 所有功能
- 技术方案要具体可执行,避免过于抽象
- 阶段划分要合理,考虑依赖关系
- 时间安排要务实,预留缓冲
- 风险评估要全面,有应对措施
## 质量检查
生成 DevelopmentPlan 后,自查以下项目:
- [ ] 覆盖 FeatureSummary 所有功能
- [ ] **系统架构图清晰展示整体结构**
- [ ] **模块依赖图清晰展示依赖关系**
- [ ] **数据流图展示关键数据流转**
- [ ] **开发阶段有时间线图**
- [ ] **每个阶段有任务依赖图**
- [ ] **里程碑有里程碑图**
- [ ] 技术方案具体可执行
- [ ] 任务 ID 唯一T-xxx
- [ ] 任务与功能 ID 关联

234
.claude/skills/wf/SKILL.md Normal file
View File

@ -0,0 +1,234 @@
---
name: wf
description: 从 RequirementsDoc.md 和 PRD.md 生成 FeatureSummary.md提供功能全貌概览。
---
# Write FeatureSummary
> **文档定位**FeatureSummary 是「功能契约」文档,是产品与开发的桥梁。精确定义功能边界、输入输出、依赖关系,确保双方对"做什么"达成共识。
当用户调用 `/wf` 时,执行以下步骤:
## 1. 读取源文档
读取以下文件:
1. `doc/RequirementsDoc.md` - 必须存在
2. `doc/PRD.md` - 必须存在
如果文件不存在,提示用户:
> 缺少上游文档,请先确保 RequirementsDoc.md 和 PRD.md 存在。
如果已存在 `doc/FeatureSummary.md`,同时读取作为参考(保持风格一致)。
## 2. 分析功能需求
从 PRD 中提取以下信息:
### 2.1 功能模块
- 从 PRD 3.1 功能架构提取模块结构
- 从 PRD 3.2 功能详情提取各模块功能点
### 2.2 功能分类
按以下维度整理功能:
- 按模块分组
- 按优先级标注P0/P1/P2
- 按用户角色关联
### 2.3 功能边界
明确每个功能的:
- 输入:触发条件、输入数据
- 输出:预期结果、输出数据
- 边界:不包含什么、异常情况
## 3. 生成 FeatureSummary
按以下结构生成文档:
```markdown
# {产品名称} - 功能摘要
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | {YYYY-MM-DD} |
| 来源文档 | PRD.md |
## 1. 功能总览
### 1.1 功能统计
| 类别 | 数量 |
|------|------|
| 功能模块 | X 个 |
| P0 功能 | X 个 |
| P1 功能 | X 个 |
| P2 功能 | X 个 |
### 1.2 功能架构图
**【必须】使用模块图展示功能架构和模块关系:**
```
┌─────────────────────────────────────────────────┐
│ {产品名称} │
├─────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 模块A │ │ 模块B │ │ 模块C │ │
│ │ ──────── │ │ ──────── │ │ ──────── │ │
│ │ • 功能1 │ │ • 功能1 │ │ • 功能1 │ │
│ │ • 功能2 │ │ • 功能2 │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘
```
### 1.3 模块依赖关系
**【必须】使用依赖图展示模块间关系:**
```
┌──────────┐
│ 模块A │
└────┬─────┘
│ 依赖
┌──────────┐ ┌──────────┐
│ 模块B │ ◀── │ 模块C │
└──────────┘ └──────────┘
```
## 2. 功能清单
### 2.1 {模块名称}
**模块职责**: {一句话描述模块职责}
#### 功能列表
| ID | 功能 | 描述 | 优先级 | 关联用户故事 |
|----|------|------|--------|--------------|
| F-001 | {功能名} | {简要描述} | P0 | US-xxx |
#### 功能契约详情
**F-001: {功能名}**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | {什么情况下触发此功能} |
| **输入** | {输入数据/参数} |
| **处理逻辑** | {核心处理步骤} |
| **输出** | {输出结果/返回值} |
| **异常情况** | {可能的错误及处理} |
| **边界说明** | {不包含什么、限制条件} |
{重复以上结构覆盖所有功能}
{重复以上结构覆盖所有模块}
## 3. 功能依赖矩阵
**【必须】使用矩阵表格展示功能间依赖:**
| 功能 | 依赖 F-001 | 依赖 F-002 | 依赖 F-003 |
|------|------------|------------|------------|
| F-001 | - | | |
| F-002 | ✓ | - | |
| F-003 | | ✓ | - |
说明:
- ✓ 表示行功能依赖列功能
- 空白表示无依赖
## 4. 功能流程图
**【必须】使用流程图展示核心功能流程:**
### 4.1 {核心流程名称}
```
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ F-001 │ ──▶ │ F-002 │ ──▶ │ F-003 │ ──▶ │ 完成 │
│ {功能} │ │ {功能} │ │ {功能} │ │ │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
▼ 异常
┌─────────┐
│ 错误处理 │
└─────────┘
```
## 5. 版本规划
| 版本 | 包含功能 | 功能ID | 目标 |
|------|----------|--------|------|
| MVP | {功能列表} | F-001, F-002 | {目标} |
| v1.1 | {功能列表} | F-003, F-004 | {目标} |
| v2.0 | {功能列表} | F-005+ | {目标} |
## 6. 接口契约预览
> 详细接口定义在 DevelopmentPlan 中,此处仅列出关键接口
| 功能 | 接口类型 | 简要说明 |
|------|----------|----------|
| F-001 | API | {说明} |
| F-002 | Event | {说明} |
```
## 4. 保存文档
将生成的 FeatureSummary 保存到 `doc/FeatureSummary.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示生成摘要:
- FeatureSummary 文件路径
- 功能模块数量
- 各优先级功能数量
- 建议的下一步操作(运行 `/rf` 评审)
---
## 可视化输出要求
FeatureSummary 作为「功能契约」文档,需要精确传达功能定义,**必须包含**
| 章节 | 可视化形式 | 必要性 |
|------|------------|--------|
| 1.2 功能架构图 | 模块图ASCII | **必须** |
| 1.3 模块依赖关系 | 依赖图ASCII | **必须** |
| 3. 功能依赖矩阵 | 矩阵表格 | **必须** |
| 4. 功能流程图 | 流程图ASCII | **必须** |
## 注意事项
- FeatureSummary 是产品与开发的**桥梁**,语言要精确、无歧义
- 功能摘要必须完全来源于 PRD不要臆造功能
- 每个功能必须有明确的**输入、输出、边界**
- 功能 ID 必须唯一F-xxx 格式)
- 优先级必须与 PRD 一致
- 功能依赖关系必须明确,避免循环依赖
## 质量检查
生成 FeatureSummary 后,自查以下项目:
- [ ] 所有功能都有唯一 IDF-xxx
- [ ] 所有功能都有契约详情(输入/输出/边界)
- [ ] **功能架构图清晰展示模块结构**
- [ ] **模块依赖图清晰展示依赖关系**
- [ ] **功能依赖矩阵完整**
- [ ] **核心流程有流程图**
- [ ] 优先级与 PRD 一致
- [ ] 无遗漏 PRD 中的功能

318
.claude/skills/wp/SKILL.md Normal file
View File

@ -0,0 +1,318 @@
---
name: wp
description: 从 RequirementsDoc.md 生成 PRD.md将需求文档转化为结构化的产品需求文档。
---
# Write PRD
> **文档定位**PRD 是「价值主张」文档,使用业务语言描述产品要解决什么问题、为谁创造什么价值。面向产品、业务、管理层沟通。
当用户调用 `/wp` 时,执行以下步骤:
## 1. 读取源文档
读取 `doc/RequirementsDoc.md` 文件。
如果文件不存在,提示用户:
> RequirementsDoc.md 不存在,请先创建需求文档。
如果已存在 `doc/PRD.md`,同时读取作为参考(保持风格一致)。
## 2. 分析需求文档
从 RequirementsDoc 中提取以下信息:
### 2.1 产品定位
- 产品名称
- 目标用户
- 核心价值主张
- 竞品对比(如有)
### 2.2 功能需求
- 功能模块划分
- 各模块详细需求
- 功能优先级P0/P1/P2
### 2.3 非功能需求
- 性能要求
- 安全要求
- 兼容性要求
- 可用性要求
### 2.4 约束条件
- 技术约束
- 业务约束
- 时间约束
## 3. 生成 PRD
按以下结构生成 PRD 文档:
```markdown
# {产品名称} - 产品需求文档 (PRD)
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | {YYYY-MM-DD} |
| 状态 | 草稿 |
## 1. 产品概述
### 1.1 产品背景
{从 RequirementsDoc 提取,说明产品解决的问题和市场机会}
### 1.2 产品定位
{目标用户、核心价值、差异化优势}
### 1.3 产品目标
| 目标 | 指标 | 衡量方式 |
|------|------|----------|
| {业务目标} | {量化指标} | {如何衡量} |
## 2. 用户故事
PRD 以用户故事为核心驱动,所有功能需求都应对应到具体的用户故事。
### 2.1 用户角色定义
| 角色 | 描述 | 核心目标 | 痛点 |
|------|------|----------|------|
| {角色1} | {角色描述} | {核心目标} | {当前痛点} |
### 2.2 用户故事列表
按优先级排列的用户故事:
#### P0 - 核心故事
| ID | 用户故事 | 验收标准 |
|----|----------|----------|
| US-001 | 作为{角色},我想要{功能},以便{价值} | {验收标准} |
#### P1 - 重要故事
| ID | 用户故事 | 验收标准 |
|----|----------|----------|
| US-xxx | 作为{角色},我想要{功能},以便{价值} | {验收标准} |
#### P2 - 次要故事
| ID | 用户故事 | 验收标准 |
|----|----------|----------|
| US-xxx | 作为{角色},我想要{功能},以便{价值} | {验收标准} |
### 2.3 用户旅程
**【必须】使用流程图展示核心用户旅程:**
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 触发点 │ ──▶ │ 关键步骤 │ ──▶ │ 目标达成 │
│ {描述} │ │ {描述} │ │ {描述} │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
{用户感受} {用户感受} {用户感受}
```
{描述用户完成核心任务的完整流程,从触发点到目标达成}
## 3. 功能需求
> 功能需求与用户故事的对应关系
### 3.1 功能架构
**【必须】使用树状图或模块图展示功能架构:**
```
{产品名称}
├── {模块A}
│ ├── {功能A1}
│ └── {功能A2}
├── {模块B}
│ ├── {功能B1}
│ └── {功能B2}
└── {模块C}
└── {功能C1}
```
### 3.2 功能详情
#### 3.2.1 {模块名称}
| 功能点 | 描述 | 关联用户故事 | 优先级 | 验收标准 |
|--------|------|--------------|--------|----------|
| {功能1} | {描述} | US-001 | P0 | {标准} |
{重复以上结构覆盖所有模块}
## 4. 非功能需求
### 4.1 性能需求
| 指标 | 要求 | 说明 |
|------|------|------|
| {响应时间} | {要求} | {场景说明} |
### 4.2 安全需求
{数据安全、访问控制、合规要求}
### 4.3 兼容性需求
| 平台/环境 | 支持版本 |
|-----------|----------|
| {平台} | {版本} |
### 4.4 可用性需求
{SLA、故障恢复、监控告警}
## 5. 数据需求
### 5.1 数据模型
**【建议】使用 ER 图或表格展示核心实体关系:**
```
┌──────────┐ ┌──────────┐
│ 实体A │ 1───n │ 实体B │
├──────────┤ ├──────────┤
│ 字段1 │ │ 字段1 │
│ 字段2 │ │ 字段2 │
└──────────┘ └──────────┘
```
### 5.2 数据规范
| 字段 | 类型 | 说明 | 校验规则 |
|------|------|------|----------|
| {字段名} | {类型} | {说明} | {规则} |
## 6. 接口需求
### 6.1 外部接口
| 接口 | 用途 | 提供方 |
|------|------|--------|
| {接口名} | {用途} | {第三方} |
### 6.2 内部接口
{模块间接口规范}
## 7. 约束与依赖
### 7.1 技术约束
| 约束 | 说明 | 影响 |
|------|------|------|
| {约束} | {说明} | {影响范围} |
### 7.2 业务约束
{法规、政策、合同限制}
### 7.3 外部依赖
{第三方服务、团队依赖}
## 8. 里程碑规划
**【建议】使用时间线展示里程碑:**
```
Phase 1 Phase 2 Phase 3
│ │ │
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│ MVP │ ────▶ │ v1.1 │ ────▶ │ v2.0 │
└──────┘ └──────┘ └──────┘
{日期} {日期} {日期}
```
| 阶段 | 目标 | 交付物 |
|------|------|--------|
| {阶段1} | {目标} | {交付物} |
## 9. 风险评估
| 风险 | 可能性 | 影响 | 应对措施 |
|------|--------|------|----------|
| {风险1} | 高/中/低 | 高/中/低 | {措施} |
## 附录
### A. 术语表
| 术语 | 定义 |
|------|------|
| {术语} | {定义} |
### B. 参考文档
- RequirementsDoc.md
```
## 4. 保存文档
将生成的 PRD 保存到 `doc/PRD.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示生成摘要:
- PRD 文件路径
- 包含的功能模块数量
- 主要章节概览
- 建议的下一步操作(运行 `/rp` 评审)
---
## 可视化输出要求
PRD 作为「价值主张」文档,需要便于业务沟通理解,**必须包含**
| 章节 | 可视化形式 | 必要性 |
|------|------------|--------|
| 2.3 用户旅程 | 流程图ASCII | **必须** |
| 3.1 功能架构 | 树状图/模块图 | **必须** |
| 5.1 数据模型 | ER 图 | 建议 |
| 8. 里程碑规划 | 时间线 | 建议 |
## 注意事项
- PRD 使用**业务语言**,避免过多技术术语
- PRD 内容必须完全来源于 RequirementsDoc不要臆造需求
- 如果 RequirementsDoc 信息不完整,在对应章节标注"待补充"
- 保持语言风格与现有文档一致
- 优先级标注遵循 P0 > P1 > P2 规则
- 验收标准要具体可测试
## 质量检查
生成 PRD 后,自查以下项目:
- [ ] 所有用户故事都有唯一 IDUS-xxx
- [ ] 所有用户故事都符合格式:作为{角色},我想要{功能},以便{价值}
- [ ] 所有功能点都关联到用户故事
- [ ] 所有功能点都有明确的优先级
- [ ] 所有功能点都有验收标准
- [ ] **用户旅程有流程图**
- [ ] **功能架构有模块图**
- [ ] 非功能需求有量化指标
- [ ] 无遗漏 RequirementsDoc 中的重要需求
- [ ] 文档结构完整,无空章节(或标注"待补充"

128
.claude/skills/wt/SKILL.md Normal file
View File

@ -0,0 +1,128 @@
---
name: wt
description: 从上游文档生成 tasks.md创建可直接执行的任务列表。
---
# Write Tasks
当用户调用 `/wt` 时,执行以下步骤:
## 1. 读取源文档
读取以下文件:
1. `doc/RequirementsDoc.md` - 必须存在
2. `doc/PRD.md` - 必须存在
3. `doc/FeatureSummary.md` - 必须存在
4. `doc/DevelopmentPlan.md` - 必须存在
5. `doc/UIDesign.md` - 必须存在
如果文件不存在,提示用户:
> 缺少上游文档,请确保所有上游文档存在。
如果已存在 `doc/tasks.md`,同时读取作为参考(保持风格一致)。
## 2. 分析任务需求
从上游文档中提取以下信息:
### 2.1 开发任务
- 从 DevelopmentPlan 获取开发阶段和任务
- 从 UIDesign 获取页面实现任务
### 2.2 任务依赖
- 分析任务间的依赖关系
- 确定任务执行顺序
### 2.3 验收标准
- 从 PRD 获取功能验收标准
- 转化为任务级别的完成标准
## 3. 生成 Tasks
按以下结构生成文档:
```markdown
# {产品名称} - 任务列表
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | {YYYY-MM-DD} |
| 来源文档 | UIDesign.md, DevelopmentPlan.md |
## 1. 任务总览
| 统计项 | 数量 |
|--------|------|
| 总任务数 | X |
| P0 任务 | X |
| P1 任务 | X |
| P2 任务 | X |
## 2. Phase 1 任务
### 2.1 {模块/功能名}
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
| T-001 | {任务名} | {描述} | P0 | - | {标准} |
| T-002 | {任务名} | {描述} | P0 | T-001 | {标准} |
{重复以上结构覆盖所有模块}
## 3. Phase 2 任务
{同上结构}
## 4. Phase N 任务
{同上结构}
## 5. 任务依赖图
```
T-001 (基础设施)
├── T-002 (功能A)
│ └── T-005 (功能A优化)
└── T-003 (功能B)
└── T-004 (功能B扩展)
```
## 6. 执行检查清单
- [ ] T-001: {任务名}
- [ ] T-002: {任务名}
{所有任务的检查清单}
```
## 4. 保存文档
将生成的 tasks 保存到 `doc/tasks.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示生成摘要:
- tasks 文件路径
- 任务总数
- 各阶段任务分布
- 建议的下一步操作(运行 `/rt` 评审)
---
## 注意事项
- 任务必须覆盖 DevelopmentPlan 和 UIDesign 所有内容
- 任务 ID 必须唯一T-001, T-002...
- 每个任务必须有明确的验收标准
- 任务粒度要适中,可在合理时间内完成
- 依赖关系要明确,避免循环依赖
- 任务应可直接执行,无歧义

352
.claude/skills/wu/SKILL.md Normal file
View File

@ -0,0 +1,352 @@
---
name: wu
description: 从上游文档生成 UIDesign.md覆盖所有用户界面设计。
---
# Write UIDesign
> **文档定位**UIDesign 是「界面蓝图」文档,用 ASCII 原型图精确传达页面布局、组件结构、交互流程,是前端开发的直接参考。
当用户调用 `/wu` 时,执行以下步骤:
## 1. 读取源文档
读取以下文件:
1. `doc/RequirementsDoc.md` - 必须存在
2. `doc/PRD.md` - 必须存在
3. `doc/FeatureSummary.md` - 必须存在
4. `doc/DevelopmentPlan.md` - 必须存在
如果文件不存在,提示用户:
> 缺少上游文档,请确保所有上游文档存在。
如果已存在 `doc/UIDesign.md`,同时读取作为参考(保持风格一致)。
## 2. 分析 UI 需求
从上游文档中提取以下信息:
### 2.1 页面需求
- 从 PRD 用户旅程分析所需页面
- 从 FeatureSummary 获取功能对应的界面
- 从 DevelopmentPlan 获取技术实现约束
### 2.2 用户流程
- 主要用户旅程
- 页面跳转关系
- 交互流程
## 3. 生成 UIDesign
按以下结构生成文档:
```markdown
# {产品名称} - UI 设计文档
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | {YYYY-MM-DD} |
| 来源文档 | DevelopmentPlan.md |
## 1. 设计概述
### 1.1 设计原则
{UI 设计原则和规范}
### 1.2 页面总览
| 页面ID | 页面名称 | 描述 | 对应功能 | 优先级 |
|--------|----------|------|----------|--------|
| P-001 | {页面名} | {描述} | F-001 | P0 |
### 1.3 页面导航图
**【必须】使用导航图展示页面跳转关系:**
```
┌─────────────┐
│ 首页 │
│ P-001 │
└──────┬──────┘
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 功能A页 │ │ 功能B页 │ │ 设置页 │
│ P-002 │ │ P-003 │ │ P-004 │
└──────┬──────┘ └─────────────┘ └─────────────┘
┌─────────────┐
│ 详情页 │
│ P-005 │
└─────────────┘
```
## 2. 页面设计
### 2.1 P-001: {页面名称}
**页面信息**
| 属性 | 值 |
|------|-----|
| 页面ID | P-001 |
| 对应功能 | F-001, F-002 |
| 入口 | {从哪些页面可进入} |
| 出口 | {可跳转到哪些页面} |
**【必须】页面布局 - ASCII 原型图**
```
┌────────────────────────────────────────────────────────┐
│ ┌─────────────────────────────────────────────────┐ │
│ │ Header │ │
│ │ [Logo] [Nav Item] [Nav Item] [用户]│ │
│ └─────────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌───────────────────────────┐ │
│ │ │ │ │ │
│ │ Sidebar │ │ Main Content │ │
│ │ │ │ │ │
│ │ • Menu Item 1 │ │ ┌─────────────────────┐ │ │
│ │ • Menu Item 2 │ │ │ Card 1 │ │ │
│ │ • Menu Item 3 │ │ │ [Title] │ │ │
│ │ │ │ │ [Description...] │ │ │
│ │ │ │ │ [Action Button] │ │ │
│ │ │ │ └─────────────────────┘ │ │
│ │ │ │ │ │
│ │ │ │ ┌─────────────────────┐ │ │
│ │ │ │ │ Card 2 │ │ │
│ │ │ │ └─────────────────────┘ │ │
│ │ │ │ │ │
│ └──────────────────┘ └───────────────────────────┘ │
│ │
├────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────┐ │
│ │ Footer │ │
│ └─────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
```
**组件清单**
| 组件ID | 组件名称 | 类型 | 说明 | 交互 |
|--------|----------|------|------|------|
| C-001 | Header | 导航栏 | 顶部固定 | 点击 Logo 回首页 |
| C-002 | Sidebar | 侧边栏 | 左侧固定 | 点击菜单切换内容 |
| C-003 | Card | 卡片 | 内容展示 | 点击进入详情 |
**交互说明**
| 触发 | 动作 | 结果 |
|------|------|------|
| 点击 Card | 跳转 | 进入详情页 P-005 |
| 点击 Menu Item | 切换 | 更新 Main Content |
**页面状态**
| 状态 | 说明 | 展示 |
|------|------|------|
| 默认 | 正常加载完成 | 显示数据列表 |
| 加载中 | 数据请求中 | 骨架屏/Loading |
| 空状态 | 无数据 | 空状态插图+引导文案 |
| 错误 | 请求失败 | 错误提示+重试按钮 |
**空状态原型**
```
┌─────────────────────────────────────┐
│ │
│ ┌─────────────┐ │
│ │ (空图标) │ │
│ └─────────────┘ │
│ │
│ 暂无数据 │
│ │
│ [去添加数据] │
│ │
└─────────────────────────────────────┘
```
{重复以上结构覆盖所有页面}
## 3. 用户流程
### 3.1 {流程名称}
**【必须】使用流程图展示用户操作流程:**
```
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Step 1 │ ──▶ │ Step 2 │ ──▶ │ Step 3 │ ──▶ │ 完成 │
│ {操作} │ │ {操作} │ │ {操作} │ │ │
│ P-001 │ │ P-002 │ │ P-003 │ │ P-004 │
└─────────┘ └────┬────┘ └─────────┘ └─────────┘
▼ 取消
┌─────────┐
│ 返回 │
│ P-001 │
└─────────┘
```
**流程步骤**
| 步骤 | 页面 | 用户操作 | 系统响应 |
|------|------|----------|----------|
| 1 | P-001 | {操作} | {响应} |
| 2 | P-002 | {操作} | {响应} |
## 4. 组件规范
### 4.1 基础组件
**Button 按钮**
```
主按钮: ┌──────────────┐
│ 确认提交 │ (填充色背景)
└──────────────┘
次按钮: ┌──────────────┐
│ 取消 │ (边框样式)
└──────────────┘
禁用态: ┌──────────────┐
│ 不可点击 │ (灰色)
└──────────────┘
```
**Input 输入框**
```
默认态: ┌────────────────────────┐
│ 请输入... │
└────────────────────────┘
聚焦态: ┌────────────────────────┐
│ 输入内容 │ (高亮边框)
└────────────────────────┘
错误态: ┌────────────────────────┐
│ 错误输入 │ (红色边框)
└────────────────────────┘
⚠ 错误提示信息
```
### 4.2 业务组件
{项目特有的业务组件}
## 5. 设计规范
### 5.1 色彩规范
| 用途 | 色值 | 示例 |
|------|------|------|
| 主色 | #1890FF | 主按钮、链接 |
| 成功 | #52C41A | 成功提示 |
| 警告 | #FAAD14 | 警告提示 |
| 错误 | #FF4D4F | 错误提示 |
| 文字主色 | #262626 | 标题 |
| 文字次色 | #8C8C8C | 描述 |
### 5.2 字体规范
| 用途 | 字号 | 字重 |
|------|------|------|
| 大标题 | 24px | Bold |
| 标题 | 18px | Medium |
| 正文 | 14px | Regular |
| 辅助文字 | 12px | Regular |
### 5.3 间距规范
| 间距 | 值 | 用途 |
|------|-----|------|
| xs | 4px | 紧凑间距 |
| sm | 8px | 小间距 |
| md | 16px | 标准间距 |
| lg | 24px | 大间距 |
| xl | 32px | 特大间距 |
### 5.4 响应式断点
| 断点 | 宽度 | 布局说明 |
|------|------|----------|
| Mobile | < 768px | 单栏布局 |
| Tablet | 768px - 1024px | 双栏布局 |
| Desktop | > 1024px | 多栏布局 |
```
## 4. 保存文档
将生成的 UIDesign 保存到 `doc/UIDesign.md`
如果文件已存在,覆盖原文件(历史版本通过 git 追溯)。
## 5. 输出摘要
向用户展示生成摘要:
- UIDesign 文件路径
- 页面数量
- 用户流程数量
- 建议的下一步操作(运行 `/ru` 评审)
---
## 可视化输出要求
UIDesign 作为「界面蓝图」文档,**必须大量使用 ASCII 原型图**
| 章节 | 可视化形式 | 必要性 |
|------|------------|--------|
| 1.3 页面导航图 | 导航关系图ASCII | **必须** |
| 2.x 页面布局 | **ASCII 原型图** | **必须(每页)** |
| 2.x 空状态 | ASCII 原型图 | 建议 |
| 3.x 用户流程 | 流程图ASCII | **必须** |
| 4.x 组件规范 | 组件示意图 | **必须** |
**ASCII 原型图要求**
- 每个页面**必须**有完整的布局原型图
- 原型图要体现:页面结构、组件位置、内容区域
- 使用 `┌ ┐ └ ┘ ─ │ ├ ┤ ┬ ┴ ┼` 等字符绘制边框
- 使用 `[ ]` 表示按钮
- 使用 `▼ ▶ ◀ ▲` 表示方向/展开
- 关键交互点要标注
## 注意事项
- UI 设计必须覆盖 DevelopmentPlan 所有功能模块
- **每个页面必须有 ASCII 原型图**
- 页面设计要考虑各种状态(默认、加载、空、错误)
- 交互说明要清晰具体
- 设计规范要统一一致
- 页面 ID 格式P-xxx
- 组件 ID 格式C-xxx
## 质量检查
生成 UIDesign 后,自查以下项目:
- [ ] 覆盖 DevelopmentPlan 所有功能模块
- [ ] **页面导航图清晰展示页面关系**
- [ ] **每个页面都有 ASCII 原型图**
- [ ] **原型图展示了完整的页面结构**
- [ ] **用户流程有流程图**
- [ ] 每个页面都有状态说明
- [ ] 组件清单完整
- [ ] 交互说明清晰
- [ ] 设计规范统一

49
.gitignore vendored Normal file
View File

@ -0,0 +1,49 @@
# Dependencies
node_modules/
__pycache__/
*.pyc
.pyo
*.pyd
venv/
.venv/
env/
.env
# Environment files (keep examples)
.env.local
backend/.env
frontend/.env.local
# Build outputs
.next/
out/
dist/
build/
*.egg-info/
# IDE
.idea/
.vscode/
*.swp
*.swo
# Testing
.coverage
htmlcov/
.pytest_cache/
coverage/
# Logs
*.log
npm-debug.log*
# OS
.DS_Store
Thumbs.db
# Python
*.egg
*.egg-info/
.eggs/
pip-log.txt
pip-delete-this-directory.txt

487
CLAUDE.md Normal file
View File

@ -0,0 +1,487 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 交互规范
- 开始任务时说:**主人,开始了**
- 完成任务时说:**主人,我干完了.您看看**
## 项目概述
KOL Insight 是一个 KOL关键意见领袖数据查询与分析工具用于批量查询达人视频数据并计算预估成本指标。
**技术栈**:
- **前端**: Next.js 14.x (App Router) + React + TypeScript + Tailwind CSS
- **后端**: Python FastAPI 0.104+ + SQLAlchemy 2.0+ (异步 ORM) + asyncpg
- **数据库**: PostgreSQL 14.x+
- **部署**: Docker + Uvicorn (ASGI 服务器)
## 常用命令
### 前端开发
```bash
# 安装依赖
pnpm install
# 开发模式(热重载)
pnpm dev
# 构建生产版本
pnpm build
# 生产模式运行
pnpm start
# 代码检查
pnpm lint
# 类型检查
pnpm type-check # 如果配置了此脚本
```
### 后端开发
```bash
# 安装依赖
pip install -r requirements.txt
# 或使用 Poetry
poetry install
# 开发模式运行(热重载)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 生产模式运行
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
# 运行测试TDD 必须)
pytest
# 运行测试并生成覆盖率报告
pytest --cov=app --cov-report=html
# 运行特定测试文件
pytest tests/test_query_service.py
# 运行特定测试函数
pytest tests/test_query_service.py::test_query_by_star_id
```
### 数据库操作
```bash
# 连接数据库(使用 .env 中的连接字符串)
psql "postgresql://user:password@host:5432/yuntu_kol"
# 创建迁移
alembic revision --autogenerate -m "description"
# 执行迁移
alembic upgrade head
# 回滚迁移
alembic downgrade -1
```
### Docker 部署
```bash
# 构建并启动所有服务(前端、后端、数据库)
docker-compose up -d
# 查看日志
docker-compose logs -f
# 停止所有服务
docker-compose down
# 重新构建并启动
docker-compose up -d --build
```
## 架构设计
### 前后端分离架构
```
┌─────────────────────┐
│ Next.js 前端 │ 端口: 3000
│ (纯前端渲染) │
└──────────┬──────────┘
│ HTTP API 调用
┌─────────────────────┐
│ FastAPI 后端 │ 端口: 8000
│ (异步 API) │
└──────────┬──────────┘
│ asyncpg
┌─────────────────────┐
│ PostgreSQL │ 端口: 5432
└─────────────────────┘
```
**前后端分离的关键点**:
- 前端通过 HTTP 调用后端 API需要配置 CORS
- 前端环境变量: `NEXT_PUBLIC_API_URL` 指向后端地址
- 后端环境变量: `CORS_ORIGINS` 配置允许的前端域名
- 独立部署:前端可部署到 Vercel后端部署到 Docker
### 核心模块
1. **查询模块** (`backend/app/services/query_service.py`)
- 支持三种查询方式星图IDstar_id、达人IDstar_unique_id、昵称star_nickname
- 星图ID 和达人ID 使用精准匹配WHERE IN
- 昵称使用模糊匹配WHERE LIKE '%昵称%'
- 单次查询限制最大 1000 条
2. **计算模块** (`backend/app/services/calculator.py`)
- **预估自然CPM**: `(estimated_video_cost / natural_play_cnt) * 1000`
- **预估自然看后搜人数**: `(natural_play_cnt / total_play_cnt) * after_view_search_uv`
- **预估自然看后搜人数成本**: `estimated_video_cost / 预估自然看后搜人数`
- 除零检查:分母为 0 时返回 `None`
- 结果保留 2 位小数:使用 `round(value, 2)`
3. **品牌API集成模块** (`backend/app/services/brand_api.py`)
- **批量并发调用**:从查询结果提取唯一 `brand_id`批量调用品牌API
- **并发控制**:使用 `asyncio.Semaphore` 限制最大 10 个并发请求
- **超时设置**:单个请求超时 3 秒
- **降级策略**API 调用失败时显示原始 `brand_id`
- **技术实现**:使用 `httpx.AsyncClient` + `asyncio.gather`
4. **导出模块** (`backend/app/services/export_service.py`)
- 支持 Excel (使用 `openpyxl``xlsxwriter`) 和 CSV 格式
- 使用中文列名作为表头
- 限制单次导出最大 1000 条
### 数据流向
```
用户输入查询条件 (前端)
POST /api/v1/query (后端)
查询数据库 (SQLAlchemy 异步)
提取唯一 brand_id → 批量调用品牌API (httpx 异步并发10)
填充品牌名称 → 计算预估指标
返回完整数据 (JSON)
前端展示结果表格
用户点击导出 → GET /api/v1/export → 下载 Excel/CSV
```
## 数据库设计
### KolVideo 模型
```python
class KolVideo(Base):
__tablename__ = "kol_videos"
# 主键
item_id = Column(String, primary_key=True)
# 查询字段(必须建立索引)
star_id = Column(String, nullable=False, index=True) # 星图ID
star_unique_id = Column(String, nullable=False, index=True) # 达人unique_id
star_nickname = Column(String, nullable=False, index=True) # 达人昵称
# 基础信息
title = Column(String, nullable=True)
viral_type = Column(String, nullable=True)
video_url = Column(String, nullable=True)
publish_time = Column(DateTime, nullable=True)
# 曝光指标(用于计算)
natural_play_cnt = Column(Integer, default=0) # 自然播放量
heated_play_cnt = Column(Integer, default=0) # 加热播放量
total_play_cnt = Column(Integer, default=0) # 总播放量
# 互动指标
total_interact = Column(Integer, default=0)
like_cnt = Column(Integer, default=0)
share_cnt = Column(Integer, default=0)
comment_cnt = Column(Integer, default=0)
# 效果指标(用于计算)
new_a3_rate = Column(Float, nullable=True)
after_view_search_uv = Column(Integer, default=0) # 看后搜人数
return_search_cnt = Column(Integer, default=0)
# 商业信息
industry_id = Column(String, nullable=True)
industry_name = Column(String, nullable=True)
brand_id = Column(String, nullable=True) # 用于调用品牌API
estimated_video_cost = Column(Float, default=0) # 预估视频成本(用于计算)
```
**索引策略**:
- `idx_star_id`: 星图ID 精准查询
- `idx_star_unique_id`: 达人ID 精准查询
- `idx_star_nickname`: 昵称模糊查询(需要支持 LIKE
## API 设计
### POST /api/v1/query
批量查询 KOL 视频数据。
**请求体**:
```python
{
"type": "star_id" | "unique_id" | "nickname", # 查询方式
"values": ["id1", "id2", ...] # 批量ID或单个昵称
}
```
**响应体**:
```python
{
"success": true,
"data": [
{
"item_id": "...",
"title": "...",
# ... 26个字段
"brand_name": "...", # 后端填充
"estimated_natural_cpm": 12.34, # 后端计算
# ...
}
],
"total": 100
}
```
### GET /api/v1/export
导出查询结果。
**查询参数**:
- `format`: `xlsx``csv`
**响应**: 文件下载(`Content-Disposition: attachment`
## 开发规范
### TDD测试驱动开发要求
**这个项目强制使用 TDD必须先写测试再写实现代码。**
1. **单元测试覆盖率要求**: 100%(所有分支覆盖)
2. **集成测试要求**: 使用真实数据库连接(.env 中的连接字符串)
3. **测试框架**: pytest + pytest-cov
4. **测试文件位置**: `backend/tests/`
**TDD 流程**:
```
1. 编写测试用例 (tests/test_*.py)
2. 运行测试(应该失败)
3. 编写最小实现代码
4. 运行测试(应该通过)
5. 重构代码(保持测试通过)
6. 生成覆盖率报告验证 100% 覆盖
```
**测试示例**:
```python
# tests/test_calculator.py
def test_calculate_natural_cpm():
# 正常情况
result = calculate_natural_cpm(1000, 50000)
assert result == 20.0
# 除零情况
result = calculate_natural_cpm(1000, 0)
assert result is None
# tests/test_query_service.py
@pytest.mark.asyncio
async def test_query_by_star_id(db_session):
# 使用真实数据库连接
result = await query_videos(
db_session,
query_type="star_id",
values=["test_id_1", "test_id_2"]
)
assert len(result) > 0
```
### 异步编程规范
**后端必须使用异步编程以提升性能。**
1. **数据库操作**: 使用 SQLAlchemy 异步 API
```python
from sqlalchemy.ext.asyncio import AsyncSession
async def query_videos(session: AsyncSession, ...):
stmt = select(KolVideo).where(...)
result = await session.execute(stmt)
return result.scalars().all()
```
2. **外部API调用**: 使用 httpx.AsyncClient
```python
async with httpx.AsyncClient(timeout=3.0) as client:
response = await client.get(url)
```
3. **并发控制**: 使用 asyncio.Semaphore
```python
semaphore = asyncio.Semaphore(10) # 限制10并发
async with semaphore:
# 执行异步任务
```
### 前端实现规范
**前端采用"粗略实现"策略:重点在功能可用,样式可简化。**
1. **组件化开发**: 使用 React 组件QueryForm, ResultTable, ExportButton
2. **API 调用封装**: 在 `lib/api.ts` 中统一封装
3. **环境变量**: 使用 `NEXT_PUBLIC_API_URL` 配置后端地址
4. **状态管理**: 页面状态包括:默认态、输入态、查询中、结果态、空结果态、错误态
### 错误处理规范
1. **后端**:
- 所有 API 路由使用 try-except 包裹
- 数据库连接失败、品牌API超时等场景有降级处理
- 错误信息记录到日志
2. **前端**:
- 网络错误显示用户友好提示
- 空结果显示引导文案
### 性能要求
- 查询响应时间 ≤ 3 秒100 条数据)
- 页面加载时间 ≤ 2 秒
- 导出响应时间 ≤ 5 秒1000 条数据)
- 品牌 API 并发限制: 10 个请求,单请求超时 3 秒
## 环境变量配置
### 前端 (.env.local)
```env
NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
```
### 后端 (.env)
```env
DATABASE_URL=postgresql://user:password@host:5432/yuntu_kol
CORS_ORIGINS=http://localhost:3000,https://your-frontend-domain.com
BRAND_API_BASE_URL=https://api.internal.intelligrow.cn
```
## 目录结构
```
kol-insight/
├── frontend/ # 前端项目Next.js
│ ├── src/
│ │ ├── app/ # App Router 路由
│ │ ├── components/ # React 组件
│ │ ├── lib/ # API 调用、工具函数
│ │ └── types/ # TypeScript 类型定义
│ └── package.json
├── backend/ # 后端项目FastAPI
│ ├── app/
│ │ ├── main.py # FastAPI 应用入口
│ │ ├── config.py # 配置管理
│ │ ├── database.py # 数据库连接
│ │ ├── models/ # SQLAlchemy 模型
│ │ ├── schemas/ # Pydantic 请求/响应模型
│ │ ├── api/v1/ # API 路由
│ │ └── services/ # 业务逻辑
│ ├── tests/ # 测试文件TDD 必须)
│ └── requirements.txt
├── doc/ # 项目文档
│ ├── PRD.md # 产品需求文档
│ ├── FeatureSummary.md # 功能摘要
│ ├── UIDesign.md # UI 设计
│ ├── DevelopmentPlan.md # 开发计划
│ └── tasks.md # 任务列表
├── docker-compose.yml # Docker 编排
└── README.md
```
## 关键技术决策
### 为什么使用 FastAPI
- 原生支持异步编程async/await
- 自动生成 OpenAPI 文档Swagger UI
- Pydantic 类型验证,类型安全
- 性能优异(基于 Starlette 和 Pydantic
### 为什么使用前后端分离?
- 前端可独立部署到 Vercel 等平台
- 后端可独立扩展和优化
- 职责分离:前端专注 UI后端专注业务逻辑
- 便于团队协作(前端/后端可并行开发)
### 为什么强制 TDD + 100% 覆盖率?
- 保证代码质量和可维护性
- 避免回归问题
- 文档化代码行为(测试即文档)
- 便于重构(测试作为安全网)
### 为什么品牌API在后端调用
- 避免前端暴露内部API地址
- 统一错误处理和降级逻辑
- 利用后端异步能力优化并发性能
- 减少前端复杂度
## 常见问题
### Q: 如何运行单个测试?
```bash
pytest tests/test_calculator.py::test_calculate_natural_cpm -v
```
### Q: 如何查看测试覆盖率?
```bash
pytest --cov=app --cov-report=html
# 打开 htmlcov/index.html 查看详细报告
```
### Q: 前端如何调用后端API
`lib/api.ts` 中封装:
```typescript
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1';
export async function queryVideos(request: QueryRequest): Promise<QueryResponse> {
const response = await fetch(`${API_BASE_URL}/query`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return response.json();
}
```
### Q: 如何调试品牌API批量调用
1. 检查后端日志应该记录API调用状态
2. 使用断点调试 `services/brand_api.py`
3. 验证并发控制和超时设置是否生效
### Q: 数据库索引如何验证?
```sql
-- 连接数据库后执行
\d kol_videos
-- 应该看到 idx_star_id, idx_star_unique_id, idx_star_nickname
```

8
backend/.env.example Normal file
View File

@ -0,0 +1,8 @@
# 数据库连接
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/yuntu_kol
# CORS 允许的前端地址
CORS_ORIGINS=["http://localhost:3000"]
# 品牌 API 配置
BRAND_API_BASE_URL=https://api.internal.intelligrow.cn

42
backend/alembic.ini Normal file
View File

@ -0,0 +1,42 @@
[alembic]
script_location = alembic
prepend_sys_path = .
version_path_separator = os
sqlalchemy.url = driver://user:pass@localhost/dbname
[post_write_hooks]
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

71
backend/alembic/env.py Normal file
View File

@ -0,0 +1,71 @@
import asyncio
from logging.config import fileConfig
from sqlalchemy import pool
from sqlalchemy.engine import Connection
from sqlalchemy.ext.asyncio import async_engine_from_config
from alembic import context
from app.config import settings
from app.database import Base
from app.models import KolVideo # noqa: F401
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
def get_url():
return settings.DATABASE_URL
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode."""
url = get_url()
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def do_run_migrations(connection: Connection) -> None:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
async def run_async_migrations() -> None:
"""Run migrations in 'online' mode with async engine."""
configuration = config.get_section(config.config_ini_section)
configuration["sqlalchemy.url"] = get_url()
connectable = async_engine_from_config(
configuration,
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
async with connectable.connect() as connection:
await connection.run_sync(do_run_migrations)
await connectable.dispose()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode."""
asyncio.run(run_async_migrations())
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@ -0,0 +1,26 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

0
backend/app/__init__.py Normal file
View File

View File

View File

28
backend/app/config.py Normal file
View File

@ -0,0 +1,28 @@
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import List
class Settings(BaseSettings):
"""Application settings."""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
)
# Database
DATABASE_URL: str = "postgresql+asyncpg://user:password@localhost:5432/yuntu_kol"
# CORS
CORS_ORIGINS: List[str] = ["http://localhost:3000"]
# Brand API
BRAND_API_BASE_URL: str = "https://api.internal.intelligrow.cn"
# API Settings
MAX_QUERY_LIMIT: int = 1000
BRAND_API_TIMEOUT: float = 3.0
BRAND_API_CONCURRENCY: int = 10
settings = Settings()

View File

34
backend/app/database.py Normal file
View File

@ -0,0 +1,34 @@
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase
from app.config import settings
# 创建异步引擎
engine = create_async_engine(
settings.DATABASE_URL,
echo=False,
pool_pre_ping=True,
pool_size=10,
max_overflow=20,
)
# 创建异步会话工厂
async_session_maker = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
class Base(DeclarativeBase):
"""Base class for all models."""
pass
async def get_db() -> AsyncSession:
"""Dependency to get database session."""
async with async_session_maker() as session:
try:
yield session
finally:
await session.close()

31
backend/app/main.py Normal file
View File

@ -0,0 +1,31 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
app = FastAPI(
title="KOL Insight API",
description="KOL 视频数据查询与分析 API",
version="1.0.0",
)
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def root():
"""Root endpoint."""
return {"message": "KOL Insight API", "version": "1.0.0"}
@app.get("/health")
async def health():
"""Health check endpoint."""
return {"status": "healthy"}

View File

@ -0,0 +1,3 @@
from app.models.kol_video import KolVideo
__all__ = ["KolVideo"]

View File

@ -0,0 +1,52 @@
from sqlalchemy import Column, String, Integer, Float, DateTime, Index
from app.database import Base
class KolVideo(Base):
"""KOL 视频数据模型."""
__tablename__ = "kol_videos"
# 主键
item_id = Column(String, primary_key=True)
# 基础信息
title = Column(String, nullable=True)
viral_type = Column(String, nullable=True)
video_url = Column(String, nullable=True)
star_id = Column(String, nullable=False)
star_unique_id = Column(String, nullable=False)
star_nickname = Column(String, nullable=False)
publish_time = Column(DateTime, nullable=True)
# 曝光指标
natural_play_cnt = Column(Integer, default=0)
heated_play_cnt = Column(Integer, default=0)
total_play_cnt = Column(Integer, default=0)
# 互动指标
total_interact = Column(Integer, default=0)
like_cnt = Column(Integer, default=0)
share_cnt = Column(Integer, default=0)
comment_cnt = Column(Integer, default=0)
# 效果指标
new_a3_rate = Column(Float, nullable=True)
after_view_search_uv = Column(Integer, default=0)
return_search_cnt = Column(Integer, default=0)
# 商业信息
industry_id = Column(String, nullable=True)
industry_name = Column(String, nullable=True)
brand_id = Column(String, nullable=True)
estimated_video_cost = Column(Float, default=0)
# 索引定义
__table_args__ = (
Index("idx_star_id", "star_id"),
Index("idx_star_unique_id", "star_unique_id"),
Index("idx_star_nickname", "star_nickname"),
)
def __repr__(self):
return f"<KolVideo(item_id={self.item_id}, title={self.title})>"

View File

View File

5
backend/pytest.ini Normal file
View File

@ -0,0 +1,5 @@
[pytest]
testpaths = tests
asyncio_mode = auto
asyncio_default_fixture_loop_scope = function
addopts = -v --cov=app --cov-report=html --cov-report=term-missing

27
backend/requirements.txt Normal file
View File

@ -0,0 +1,27 @@
# Web Framework
fastapi>=0.104.0
uvicorn[standard]>=0.24.0
# Database
sqlalchemy>=2.0.0
asyncpg>=0.29.0
alembic>=1.12.0
# HTTP Client
httpx>=0.25.0
# Data Validation
pydantic>=2.0.0
pydantic-settings>=2.0.0
# Excel Export
openpyxl>=3.1.0
# Testing
pytest>=7.4.0
pytest-asyncio>=0.21.0
pytest-cov>=4.1.0
httpx>=0.25.0
# Development
python-dotenv>=1.0.0

View File

58
backend/tests/conftest.py Normal file
View File

@ -0,0 +1,58 @@
import pytest
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from app.database import Base
from app.models import KolVideo
@pytest.fixture
def sample_video_data():
"""Sample video data for testing."""
return {
"item_id": "test_item_001",
"title": "测试视频标题",
"viral_type": "爆款",
"video_url": "https://example.com/video/001",
"star_id": "star_001",
"star_unique_id": "unique_001",
"star_nickname": "测试达人",
"natural_play_cnt": 100000,
"heated_play_cnt": 50000,
"total_play_cnt": 150000,
"total_interact": 5000,
"like_cnt": 3000,
"share_cnt": 1000,
"comment_cnt": 1000,
"new_a3_rate": 0.05,
"after_view_search_uv": 500,
"return_search_cnt": 200,
"industry_id": "ind_001",
"industry_name": "美妆",
"brand_id": "brand_001",
"estimated_video_cost": 10000.0,
}
@pytest.fixture
async def test_engine():
"""Create a test database engine using SQLite."""
engine = create_async_engine(
"sqlite+aiosqlite:///:memory:",
echo=False,
)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
await engine.dispose()
@pytest.fixture
async def test_session(test_engine):
"""Create a test database session."""
async_session = async_sessionmaker(
test_engine,
class_=AsyncSession,
expire_on_commit=False,
)
async with async_session() as session:
yield session

View File

@ -0,0 +1,165 @@
import pytest
from sqlalchemy import select
from app.models import KolVideo
class TestKolVideoModel:
"""Tests for KolVideo model."""
async def test_create_video(self, test_session, sample_video_data):
"""Test creating a video record."""
video = KolVideo(**sample_video_data)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == sample_video_data["item_id"])
)
saved_video = result.scalar_one()
assert saved_video.item_id == sample_video_data["item_id"]
assert saved_video.title == sample_video_data["title"]
assert saved_video.star_id == sample_video_data["star_id"]
async def test_query_by_star_id(self, test_session, sample_video_data):
"""Test querying videos by star_id."""
video = KolVideo(**sample_video_data)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.star_id == sample_video_data["star_id"])
)
videos = result.scalars().all()
assert len(videos) == 1
assert videos[0].star_id == sample_video_data["star_id"]
async def test_query_by_star_unique_id(self, test_session, sample_video_data):
"""Test querying videos by star_unique_id."""
video = KolVideo(**sample_video_data)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(
KolVideo.star_unique_id == sample_video_data["star_unique_id"]
)
)
videos = result.scalars().all()
assert len(videos) == 1
assert videos[0].star_unique_id == sample_video_data["star_unique_id"]
async def test_query_by_nickname_like(self, test_session, sample_video_data):
"""Test querying videos by nickname using LIKE."""
video = KolVideo(**sample_video_data)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.star_nickname.like("%测试%"))
)
videos = result.scalars().all()
assert len(videos) == 1
assert "测试" in videos[0].star_nickname
async def test_batch_query_by_star_ids(self, test_session, sample_video_data):
"""Test batch querying videos by multiple star_ids."""
video1 = KolVideo(**sample_video_data)
video2_data = sample_video_data.copy()
video2_data["item_id"] = "test_item_002"
video2_data["star_id"] = "star_002"
video2 = KolVideo(**video2_data)
test_session.add_all([video1, video2])
await test_session.commit()
star_ids = ["star_001", "star_002"]
result = await test_session.execute(
select(KolVideo).where(KolVideo.star_id.in_(star_ids))
)
videos = result.scalars().all()
assert len(videos) == 2
async def test_video_default_values(self, test_session):
"""Test that default values are set correctly."""
video = KolVideo(
item_id="test_defaults",
star_id="star_test",
star_unique_id="unique_test",
star_nickname="测试默认值",
)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == "test_defaults")
)
saved_video = result.scalar_one()
assert saved_video.natural_play_cnt == 0
assert saved_video.heated_play_cnt == 0
assert saved_video.total_play_cnt == 0
assert saved_video.estimated_video_cost == 0
async def test_video_nullable_fields(self, test_session):
"""Test that nullable fields can be None."""
video = KolVideo(
item_id="test_nullable",
star_id="star_nullable",
star_unique_id="unique_nullable",
star_nickname="测试可空字段",
)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == "test_nullable")
)
saved_video = result.scalar_one()
assert saved_video.title is None
assert saved_video.video_url is None
assert saved_video.brand_id is None
assert saved_video.new_a3_rate is None
async def test_update_video(self, test_session, sample_video_data):
"""Test updating a video record."""
video = KolVideo(**sample_video_data)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == sample_video_data["item_id"])
)
saved_video = result.scalar_one()
saved_video.title = "更新后的标题"
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == sample_video_data["item_id"])
)
updated_video = result.scalar_one()
assert updated_video.title == "更新后的标题"
async def test_delete_video(self, test_session, sample_video_data):
"""Test deleting a video record."""
video = KolVideo(**sample_video_data)
test_session.add(video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == sample_video_data["item_id"])
)
saved_video = result.scalar_one()
await test_session.delete(saved_video)
await test_session.commit()
result = await test_session.execute(
select(KolVideo).where(KolVideo.item_id == sample_video_data["item_id"])
)
assert result.scalar_one_or_none() is None

1014
doc/DevelopmentPlan.md Normal file

File diff suppressed because it is too large Load Diff

487
doc/FeatureSummary.md Normal file
View File

@ -0,0 +1,487 @@
# KOL Insight - 功能摘要
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | 2025-01-28 |
| 来源文档 | PRD.md |
## 1. 功能总览
### 1.1 功能统计
| 类别 | 数量 |
|------|------|
| 功能模块 | 5 个 |
| P0 功能 | 7 个 |
| P1 功能 | 2 个 |
| P2 功能 | 1 个 |
### 1.2 功能架构图
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ KOL Insight │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 数据查询模块 │ │ 数据计算模块 │ │ 品牌API集成模块 │ │
│ │ ────────────── │ │ ────────────── │ │ ────────────── │ │
│ │ • 星图ID查询 │ │ • 预估自然CPM │ │ • 品牌名称批量 │ │
│ │ • 达人ID查询 │ │ • 预估看后搜人数 │ │ 获取(后端) │ │
│ │ • 昵称模糊查询 │ │ • 看后搜人数成本 │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 数据展示模块 │ │ 数据导出模块 │ │ 外部服务依赖 │ │
│ │ ────────────── │ │ ────────────── │ │ ────────────── │ │
│ │ • 结果列表展示 │ │ • Excel/CSV导出 │ │ • PostgreSQL │ │
│ │ • 视频链接跳转 │ │ │ │ • 品牌API │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
### 1.3 模块依赖关系
```
┌──────────────────┐
│ 数据查询模块 │
│ (F-001~003) │
└────────┬─────────┘
│ 查询结果(含brand_id)
┌──────────────────┐ ┌──────────────────┐
│ 品牌API集成 │◀─│ 数据计算模块 │
│ (F-010) │ │ (F-004~006) │
│ 后端批量调用 │ └────────┬─────────┘
└────────┬─────────┘ │
│ │ 计算结果
│ 品牌名称 │
└─────────┬───────────┘
┌──────────────────┐
│ 数据展示模块 │
│ (F-007~008) │
└────────┬─────────┘
│ 展示数据(含品牌名称)
┌──────────────────┐
│ 数据导出模块 │
│ (F-009) │
└──────────────────┘
```
## 2. 功能清单
### 2.1 数据查询模块
**模块职责**: 接收用户输入的查询条件从数据库检索匹配的KOL视频数据
#### 功能列表
| ID | 功能 | 描述 | 优先级 | 关联用户故事 |
|----|------|------|--------|--------------|
| F-001 | 星图ID查询 | 批量输入星图ID精准匹配数据库 | P0 | US-001 |
| F-002 | 达人ID查询 | 批量输入达人unique_id精准匹配数据库 | P0 | US-002 |
| F-003 | 昵称模糊查询 | 输入达人昵称,模糊匹配数据库 | P0 | US-003 |
#### 功能契约详情
**F-001: 星图ID查询**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 用户选择"星图ID"查询方式并提交查询 |
| **输入** | 星图ID列表换行分隔的字符串 |
| **处理逻辑** | 1. 解析输入按换行符分割为ID数组<br>2. 对每个ID执行 `WHERE star_id = ?` 精准匹配<br>3. 合并所有匹配结果 |
| **输出** | 匹配的视频数据列表包含26个输出字段 |
| **异常情况** | 1. 输入为空:提示"请输入星图ID"<br>2. 无匹配结果:返回空列表,提示"未找到匹配数据"<br>3. 数据库连接失败:显示错误提示 |
| **边界说明** | 仅支持精准匹配不支持模糊匹配单次查询建议不超过100个ID |
**F-002: 达人ID查询**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 用户选择"达人unique_id"查询方式并提交查询 |
| **输入** | 达人unique_id列表换行分隔的字符串 |
| **处理逻辑** | 1. 解析输入按换行符分割为ID数组<br>2. 对每个ID执行 `WHERE star_unique_id = ?` 精准匹配<br>3. 合并所有匹配结果 |
| **输出** | 匹配的视频数据列表包含26个输出字段 |
| **异常情况** | 1. 输入为空:提示"请输入达人ID"<br>2. 无匹配结果:返回空列表<br>3. 数据库连接失败:显示错误提示 |
| **边界说明** | 仅支持精准匹配单次查询建议不超过100个ID |
**F-003: 昵称模糊查询**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 用户选择"达人昵称"查询方式并提交查询 |
| **输入** | 达人昵称(字符串) |
| **处理逻辑** | 1. 执行 `WHERE star_nickname LIKE '%{input}%'` 包含匹配<br>2. 返回所有匹配结果 |
| **输出** | 匹配的视频数据列表包含26个输出字段 |
| **异常情况** | 1. 输入为空:提示"请输入达人昵称"<br>2. 无匹配结果:返回空列表<br>3. 匹配结果过多返回前1000条并提示 |
| **边界说明** | 支持模糊匹配(包含关系);结果数量可能较大 |
---
### 2.2 数据计算模块
**模块职责**: 对查询结果进行预估指标计算生成CPM和看后搜成本数据
#### 功能列表
| ID | 功能 | 描述 | 优先级 | 关联用户故事 |
|----|------|------|--------|--------------|
| F-004 | 预估自然CPM计算 | 计算每千次自然曝光的成本 | P0 | US-004 |
| F-005 | 预估自然看后搜人数计算 | 计算自然流量带来的看后搜人数 | P0 | US-004 |
| F-006 | 预估自然看后搜人数成本计算 | 计算每个自然看后搜用户的成本 | P0 | US-004 |
#### 功能契约详情
**F-004: 预估自然CPM计算**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 查询返回结果后自动执行 |
| **输入** | 视频数据中的 `estimated_video_cost``natural_play_cnt` 字段 |
| **处理逻辑** | `预估自然CPM = estimated_video_cost / natural_play_cnt * 1000` |
| **输出** | 预估自然CPM值单位元/千次曝光) |
| **异常情况** | 1. natural_play_cnt = 0返回 null 或 "-"<br>2. 字段缺失:返回 null |
| **边界说明** | 结果保留2位小数除零时不计算 |
**F-005: 预估自然看后搜人数计算**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 查询返回结果后自动执行 |
| **输入** | 视频数据中的 `natural_play_cnt``total_play_cnt``after_view_search_uv` 字段 |
| **处理逻辑** | `预估自然看后搜人数 = natural_play_cnt / total_play_cnt * after_view_search_uv` |
| **输出** | 预估自然看后搜人数(单位:人) |
| **异常情况** | 1. total_play_cnt = 0返回 null<br>2. 字段缺失:返回 null |
| **边界说明** | 结果取整或保留2位小数除零时不计算 |
**F-006: 预估自然看后搜人数成本计算**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | F-005 计算完成后自动执行 |
| **输入** | `estimated_video_cost` 字段、F-005 的计算结果 |
| **处理逻辑** | `预估自然看后搜人数成本 = estimated_video_cost / 预估自然看后搜人数` |
| **输出** | 预估自然看后搜人数成本(单位:元/人) |
| **异常情况** | 1. 预估自然看后搜人数 = 0 或 null返回 null<br>2. 依赖计算失败:返回 null |
| **边界说明** | 依赖 F-005 结果结果保留2位小数 |
---
### 2.3 品牌API集成模块
**模块职责**: 在后端查询时批量调用品牌API获取品牌名称并补充到查询结果中
#### 功能列表
| ID | 功能 | 描述 | 优先级 | 关联用户故事 |
|----|------|------|--------|--------------|
| F-010 | 品牌名称批量获取 | 后端批量调用品牌API获取品牌名称 | P0 | US-006 |
#### 功能契约详情
**F-010: 品牌名称批量获取**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 后端查询返回结果后,在返回给前端之前自动执行 |
| **输入** | 查询结果中的 `brand_id` 列表(去重后) |
| **处理逻辑** | 1. 从查询结果中提取所有唯一的 brand_id<br>2. 批量并发调用品牌API`GET /v1/yuntu/brands/{brand_id}`<br>3. 构建 brand_id → brand_name 映射表<br>4. 将品牌名称填充到每条查询结果的 brand_name 字段 |
| **输出** | 补充了 brand_name 字段的完整查询结果 |
| **异常情况** | 1. 单个品牌API调用失败该条记录 brand_name 降级显示 brand_id<br>2. 品牌API服务不可用所有记录降级显示 brand_id<br>3. brand_id 为空brand_name 显示为 "-" |
| **边界说明** | 1. 在后端执行,前端无需调用<br>2. 使用并发控制限制同时请求数如最多10个并发<br>3. 可选:缓存品牌名称,减少重复请求<br>4. 超时设置单个请求超时3秒 |
**批量调用策略**:
```
查询结果 (100条)
┌─────────────────────────────────────┐
│ 1. 提取唯一 brand_id (假设30个) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 2. 批量并发请求 (限制10并发) │
│ Promise.all([ │
│ fetch(brand/id1), │
│ fetch(brand/id2), │
│ ... │
│ ]) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 3. 构建映射表 │
│ { id1: "品牌A", id2: "品牌B" } │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 4. 填充 brand_name 到结果 │
└─────────────────────────────────────┘
```
---
### 2.4 数据展示模块
**模块职责**: 将查询和计算结果以表格形式展示给用户
#### 功能列表
| ID | 功能 | 描述 | 优先级 | 关联用户故事 |
|----|------|------|--------|--------------|
| F-007 | 结果列表展示 | 以表格形式展示查询结果的所有字段 | P1 | US-006 |
| F-008 | 视频链接跳转 | 点击视频链接在新窗口打开原视频 | P2 | US-007 |
#### 功能契约详情
**F-007: 结果列表展示**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 查询完成且有结果数据 |
| **输入** | 查询结果数据列表(含计算字段和品牌名称,由后端 F-010 处理完成) |
| **处理逻辑** | 1. 渲染数据表格<br>2. 展示26个输出字段含3个计算字段<br>3. 品牌名称已由后端获取,直接展示 brand_name 字段 |
| **输出** | HTML表格展示 |
| **异常情况** | 1. 无数据:显示空状态提示<br>2. 品牌名称为空:显示 "-" |
| **边界说明** | 支持分页如数据量大列名使用中文品牌名称由后端处理前端无需调用API |
**F-008: 视频链接跳转**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 用户点击视频链接 |
| **输入** | 视频链接URLvideo_url字段 |
| **处理逻辑** | 在新窗口/标签页打开链接 |
| **输出** | 浏览器新标签页打开视频页面 |
| **异常情况** | 1. 链接为空:不可点击或显示提示<br>2. 链接无效:由浏览器处理 |
| **边界说明** | 使用 `target="_blank"` 打开;需考虑安全属性 |
---
### 2.4 数据导出模块
**模块职责**: 将查询结果导出为Excel或CSV文件供用户下载
#### 功能列表
| ID | 功能 | 描述 | 优先级 | 关联用户故事 |
|----|------|------|--------|--------------|
| F-009 | Excel/CSV导出 | 将当前查询结果导出为文件 | P1 | US-005 |
#### 功能契约详情
**F-009: Excel/CSV导出**
| 契约项 | 说明 |
|--------|------|
| **触发条件** | 用户点击"导出"按钮 |
| **输入** | 当前查询结果数据列表 |
| **处理逻辑** | 1. 将数据转换为Excel/CSV格式<br>2. 使用中文列名作为表头<br>3. 生成文件并触发下载 |
| **输出** | Excel(.xlsx)或CSV文件下载 |
| **异常情况** | 1. 无数据:提示"无数据可导出"<br>2. 数据量过大:分批处理或提示限制 |
| **边界说明** | 导出数据包含所有26个字段文件名包含时间戳单次导出建议不超过1000条 |
---
## 3. 功能依赖矩阵
| 功能 | F-001 | F-002 | F-003 | F-004 | F-005 | F-006 | F-007 | F-008 | F-009 | F-010 |
|------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| F-001 | - | | | | | | | | | |
| F-002 | | - | | | | | | | | |
| F-003 | | | - | | | | | | | |
| F-004 | ✓ | ✓ | ✓ | - | | | | | | |
| F-005 | ✓ | ✓ | ✓ | | - | | | | | |
| F-006 | | | | | ✓ | - | | | | |
| F-007 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | - | | | ✓ |
| F-008 | | | | | | | ✓ | - | | |
| F-009 | | | | ✓ | ✓ | ✓ | ✓ | | - | ✓ |
| F-010 | ✓ | ✓ | ✓ | | | | | | | - |
**说明**
- ✓ 表示行功能依赖列功能
- F-004/005/006计算模块依赖 F-001/002/003查询模块的输出
- F-006 依赖 F-005 的计算结果
- F-010品牌API依赖查询模块获取 brand_id 列表,在后端批量调用
- F-007展示依赖查询、计算结果和 F-010 的品牌名称
- F-009导出依赖展示数据含品牌名称
## 4. 功能流程图
### 4.1 核心业务流程:批量查询与导出
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户输入 │ │ 选择查询 │ │ 提交查询 │
│ 查询条件 │ ──▶ │ 方式 │ ──▶ │ │
└─────────────┘ └─────────────┘ └──────┬──────┘
┌──────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 数据查询模块 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ F-001 │ │ F-002 │ │ F-003 │ │
│ │ 星图ID │ / │ 达人ID │ / │ 昵称 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼─────────────┼─────────────┼─────────────────┘
└─────────────┼─────────────┘
┌──────────────────────────────────────────────────────┐
│ 后端处理(并行执行) │
│ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ 数据计算模块 │ │ 品牌API集成模块 │ │
│ │ ┌─────┐ ┌─────┐ │ │ ┌──────────────┐ │ │
│ │ │F-004│ │F-005│ │ │ │ F-010 │ │ │
│ │ │ CPM │ │看后搜│ │ │ │ 批量获取品牌 │ │ │
│ │ └─────┘ └──┬──┘ │ │ │ 名称 │ │ │
│ │ │ │ │ └──────────────┘ │ │
│ │ ┌───┘ │ │ │ │
│ │ ▼ │ │ │ │
│ │ ┌─────┐ │ │ │ │
│ │ │F-006│ │ │ │ │
│ │ │成本 │ │ │ │ │
│ │ └─────┘ │ │ │ │
│ └────────────────────┘ └────────────────────┘ │
└─────────────────────┬────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 数据展示模块 │
│ ┌───────────────────────────────────────┐ │
│ │ F-007 结果列表展示(含品牌名称) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ F-008 视频链接(可点击跳转) │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
└─────────────────────┬────────────────────────────────┘
┌────────────┴────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 继续查询 │ │ F-009 │
│ │ │ 导出数据 │
└─────────────┘ └─────────────┘
```
### 4.2 计算模块内部流程
```
┌──────────────────┐
│ 查询结果数据 │
└────────┬─────────┘
┌────────────────────────────────────────────────────────────┐
│ 并行计算 │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ F-004 │ │ F-005 │ │
│ │ 预估自然CPM │ │ 预估自然看后搜人数 │ │
│ │ │ │ │ │
│ │ cost/natural*1000 │ │ natural/total*uv │ │
│ └──────────┬──────────┘ └──────────┬──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 除零检查 │ │ 除零检查 │ │
│ │ null → 显示"-" │ │ null → 显示"-" │ │
│ └─────────────────────┘ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ F-006 │ │
│ │ 预估看后搜人数成本 │ │
│ │ │ │
│ │ cost/看后搜人数 │ │
│ └─────────────────────┘ │
└────────────────────────────────────────────────────────────┘
```
## 5. 版本规划
| 版本 | 包含功能 | 功能ID | 目标 |
|------|----------|--------|------|
| MVP | 核心查询、计算、品牌API、展示、导出 | F-001 ~ F-007, F-009, F-010 | 完成核心批量查询和成本计算功能 |
| v1.1 | 视频链接跳转、性能优化 | F-008 | 提升用户体验,优化查询性能 |
### MVP 功能清单
| 优先级 | 功能ID | 功能名称 |
|--------|--------|----------|
| P0 | F-001 | 星图ID查询 |
| P0 | F-002 | 达人ID查询 |
| P0 | F-003 | 昵称模糊查询 |
| P0 | F-004 | 预估自然CPM计算 |
| P0 | F-005 | 预估自然看后搜人数计算 |
| P0 | F-006 | 预估自然看后搜人数成本计算 |
| P0 | F-010 | 品牌名称批量获取(后端) |
| P1 | F-007 | 结果列表展示 |
| P1 | F-009 | Excel/CSV导出 |
### v1.1 功能清单
| 优先级 | 功能ID | 功能名称 |
|--------|--------|----------|
| P2 | F-008 | 视频链接跳转 |
## 6. 接口契约预览
> 详细接口定义在 DevelopmentPlan 中,此处仅列出关键接口
<!-- MODIFIED: 改用 FastAPI 架构API 端点改为 /api/v1/ 风格 -->
| 功能 | 接口类型 | 端点 | 简要说明 |
|------|----------|------|----------|
| F-001/002/003 | FastAPI 后端 | POST /api/v1/query | 批量查询支持type参数区分查询方式 |
| F-009 | FastAPI 后端 | GET /api/v1/export | 导出当前查询结果 |
| F-010 | 外部API后端调用 | GET /v1/yuntu/brands/{brand_id} | 后端批量获取品牌名称 |
<!-- NEW START -->
**技术架构说明**:
- **后端**: Python FastAPI 框架,提供 RESTful API
- **前端**: React + Next.js,通过 HTTP 请求调用后端 API
- **架构**: 前后端完全分离,独立部署
- **API 版本**: 使用 `/api/v1/` 前缀进行版本管理
- **跨域**: FastAPI 配置 CORS 中间件支持前端调用
<!-- NEW END -->
### 查询接口预览
<!-- MODIFIED: API 端点改为 FastAPI 风格 -->
```
POST /api/v1/query
Content-Type: application/json
{
"type": "star_id" | "unique_id" | "nickname",
"values": ["id1", "id2", ...] | "昵称关键词"
}
Response:
{
"success": true,
"data": [...], // 视频数据列表
"total": 100
}
```
### 导出接口预览
<!-- MODIFIED: API 端点改为 FastAPI 风格 -->
```
GET /api/v1/export?format=xlsx|csv
Response: 文件下载
```

365
doc/PRD.md Normal file
View File

@ -0,0 +1,365 @@
# KOL Insight - 产品需求文档 (PRD)
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | 2025-01-28 |
| 状态 | 草稿 |
## 1. 产品概述
### 1.1 产品背景
在 KOL 营销领域,运营人员需要频繁查询达人视频数据以评估投放效果、计算投放成本。现有的云图平台虽然提供了数据,但缺乏批量查询和成本预估能力,导致运营人员需要手动逐条查询和计算,效率低下。
KOL Insight 旨在解决这一痛点,提供批量数据查询和智能成本预估功能,帮助运营人员快速获取决策所需的数据。
### 1.2 产品定位
- **目标用户**KOL 营销运营人员、投放优化师、品牌方
- **核心价值**:批量查询 KOL 视频数据,自动计算预估 CPM 和看后搜成本
- **差异化优势**支持多种查询方式星图ID/达人ID/昵称),自动计算关键成本指标
### 1.3 产品目标
| 目标 | 指标 | 衡量方式 |
|------|------|----------|
| 提升查询效率 | 单次可批量查询多个 KOL | 对比手动查询耗时 |
| 降低计算错误 | 自动计算预估指标准确率 100% | 人工抽检验证 |
| 提高数据可用性 | 支持数据导出 | 导出功能完整性 |
## 2. 用户故事
### 2.1 用户角色定义
| 角色 | 描述 | 核心目标 | 痛点 |
|------|------|----------|------|
| 运营人员 | 负责 KOL 投放执行与数据分析的一线人员 | 快速获取 KOL 视频表现数据 | 逐条查询效率低,成本计算繁琐 |
| 投放优化师 | 负责投放策略优化的专业人员 | 评估投放 ROI优化投放策略 | 数据分散,难以横向对比 |
### 2.2 用户故事列表
#### P0 - 核心故事
| ID | 用户故事 | 验收标准 |
|----|----------|----------|
| US-001 | 作为运营人员我想要批量输入星图ID查询KOL数据以便快速获取多个达人的视频表现 | 1. 支持批量输入星图ID换行分隔<br>2. 精准匹配 star_id 字段<br>3. 返回完整视频数据列表 |
| US-002 | 作为运营人员我想要通过达人unique_id查询数据以便根据达人ID快速定位数据 | 1. 支持批量输入达人unique_id<br>2. 精准匹配 star_unique_id 字段<br>3. 返回对应视频数据 |
| US-003 | 作为运营人员我想要通过达人昵称模糊搜索以便在不知道精确ID时也能找到数据 | 1. 支持输入达人昵称<br>2. 模糊匹配 star_nickname 字段<br>3. 返回所有匹配结果 |
<!-- MODIFIED: 统一术语为"预估自然看后搜人数" -->
| US-004 | 作为运营人员我想要看到预估自然CPM和看后搜人数成本以便评估投放效果 | 1. 自动计算预估自然CPM<br>2. 自动计算预估自然看后搜人数<br>3. 自动计算预估自然看后搜人数成本<br>4. 数据展示在结果列表中 |
#### P1 - 重要故事
| ID | 用户故事 | 验收标准 |
|----|----------|----------|
| US-005 | 作为运营人员,我想要导出查询结果,以便在 Excel 中进一步分析或汇报 | 1. 支持导出为 Excel/CSV 格式<br>2. 导出数据包含所有查询字段<br>3. 中文列名清晰可读 |
| US-006 | 作为运营人员,我想要看到视频的完整数据指标,以便全面了解视频表现 | 1. 展示所有核心指标曝光、互动、A3率等<br>2. 数据格式清晰易读 |
#### P2 - 次要故事
| ID | 用户故事 | 验收标准 |
|----|----------|----------|
| US-007 | 作为运营人员,我想要点击视频链接直接跳转,以便快速查看原视频 | 1. 视频链接可点击<br>2. 新窗口打开视频页面 |
### 2.3 用户旅程
**核心用户旅程:批量查询 KOL 数据**
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 触发点 │ │ 输入查询条件 │ │ 查看结果 │ │ 导出数据 │
│ 需要KOL数据 │ ──▶ │ 批量输入ID/昵称 │ ──▶ │ 浏览数据列表 │ ──▶ │ 下载Excel/CSV │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
"需要快速获取 "选择查询方式, "系统自动计算 "数据可用于
多个达人数据" 粘贴ID列表" CPM等指标" 汇报和分析"
```
用户从需要 KOL 数据开始选择合适的查询方式星图ID/达人ID/昵称),批量输入查询条件后提交查询。系统返回结果列表,自动计算预估 CPM 和看后搜成本等指标。用户可浏览数据,需要时导出为 Excel 或 CSV 文件。
## 3. 功能需求
### 3.1 功能架构
<!-- MODIFIED: 统一术语为"预估自然看后搜人数" -->
```
KOL Insight
├── 数据查询模块
│ ├── 星图ID精准查询
│ ├── 达人unique_id精准查询
│ └── 达人昵称模糊查询
├── 数据计算模块
│ ├── 预估自然CPM计算
│ ├── 预估自然看后搜人数计算
│ └── 预估自然看后搜人数成本计算
├── 数据展示模块
│ ├── 结果列表展示
│ └── 视频链接跳转
└── 数据导出模块
└── Excel/CSV导出
```
### 3.2 功能详情
#### 3.2.1 数据查询模块
| 功能点 | 描述 | 关联用户故事 | 优先级 | 验收标准 |
|--------|------|--------------|--------|----------|
| 星图ID查询 | 批量输入星图ID精准匹配 star_id 字段 | US-001 | P0 | 支持批量输入,精准匹配,返回完整数据 |
| 达人ID查询 | 批量输入达人unique_id精准匹配 star_unique_id 字段 | US-002 | P0 | 支持批量输入,精准匹配,返回完整数据 |
| 昵称模糊查询 | 输入达人昵称,模糊匹配 star_nickname 字段 | US-003 | P0 | 支持包含匹配,返回所有匹配结果 |
#### 3.2.2 数据计算模块
<!-- MODIFIED: 补充完整计算公式,统一术语为"预估自然看后搜人数" -->
| 功能点 | 描述 | 关联用户故事 | 优先级 | 验收标准 |
|--------|------|--------------|--------|----------|
| 预估自然CPM | 公式:`estimated_video_cost / natural_play_cnt * 1000` | US-004 | P0 | 计算结果准确,单位为元/千次曝光 |
| 预估自然看后搜人数 | 公式:`natural_play_cnt / total_play_cnt * after_view_search_uv` | US-004 | P0 | 计算结果准确,单位为人数 |
| 预估自然看后搜人数成本 | 公式:`estimated_video_cost / 预估自然看后搜人数` | US-004 | P0 | 计算结果准确,单位为元/人 |
#### 3.2.3 数据展示模块
| 功能点 | 描述 | 关联用户故事 | 优先级 | 验收标准 |
|--------|------|--------------|--------|----------|
| 结果列表展示 | 展示查询结果的完整数据表格 | US-006 | P1 | 包含所有指标字段,格式清晰 |
| 视频链接跳转 | 点击视频链接跳转到原视频页面 | US-007 | P2 | 链接可点击,新窗口打开 |
#### 3.2.4 数据导出模块
| 功能点 | 描述 | 关联用户故事 | 优先级 | 验收标准 |
|--------|------|--------------|--------|----------|
| 数据导出 | 将查询结果导出为 Excel/CSV 格式 | US-005 | P1 | 文件可下载,数据完整,中文列名 |
## 4. 非功能需求
### 4.1 性能需求
| 指标 | 要求 | 说明 |
|------|------|------|
| 查询响应时间 | ≤ 3秒 | 100条以内的批量查询 |
| 页面加载时间 | ≤ 2秒 | 首页加载 |
| 导出响应时间 | ≤ 5秒 | 1000条以内的数据导出 |
### 4.2 安全需求
- 数据库连接使用安全凭证,不在代码中硬编码
- 查询输入需进行 SQL 注入防护
- 敏感配置通过环境变量管理
### 4.3 兼容性需求
<!-- MODIFIED: 后端改用 Python FastAPI前端使用 Next.js -->
| 平台/环境 | 支持版本 |
|-----------|----------|
| 浏览器 | Chrome/Edge/Firefox 最新版 |
| Python | 3.9+ 及以上 |
| PostgreSQL | 14.x 及以上 |
| Node.js前端构建 | 18.x 及以上 |
### 4.4 可用性需求
- 系统可用性目标99%(工作时间)
- 提供基本的错误提示,便于用户理解问题原因
## 5. 数据需求
### 5.1 数据模型
<!-- MODIFIED: 补充完整字段名,明确计算公式所需字段 -->
```
┌─────────────────────────────────────────────────────────────────┐
│ 视频数据表 (kol_videos) │
├─────────────────────────────────────────────────────────────────┤
│ 基础信息 │
│ item_id (视频ID) [主键] │
│ title (视频标题) │
│ star_id (星图ID) [索引] │
│ star_unique_id (达人unique_id) [索引] │
│ star_nickname (达人昵称) [索引] │
│ video_url (视频链接) │
│ publish_time (发布时间) │
├─────────────────────────────────────────────────────────────────┤
│ 曝光指标 │
│ natural_play_cnt (自然曝光数) ★ 计算用 │
│ heated_play_cnt (加热曝光数) │
│ total_play_cnt (总曝光数) ★ 计算用 │
├─────────────────────────────────────────────────────────────────┤
│ 互动指标 │
│ total_interact (总互动) │
│ like_cnt (点赞) │
│ share_cnt (转发) │
│ comment_cnt (评论) │
├─────────────────────────────────────────────────────────────────┤
│ 效果指标 │
│ new_a3_rate (新增A3率) │
│ after_view_search_uv (看后搜人数) ★ 计算用 │
│ return_search_cnt (回搜次数) │
├─────────────────────────────────────────────────────────────────┤
│ 商业信息 │
│ industry_id (合作行业ID) │
│ industry_name (合作行业) │
│ brand_id (合作品牌ID) → 需调用品牌API获取名称 │
│ estimated_video_cost (预估视频价格) ★ 计算用 │
└─────────────────────────────────────────────────────────────────┘
★ 标记字段为计算预估指标所需的关键字段
```
### 5.2 数据规范
| 字段 | 类型 | 说明 | 校验规则 |
|------|------|------|----------|
| star_id | string | 星图ID | 非空 |
| star_unique_id | string | 达人唯一标识 | 非空 |
| star_nickname | string | 达人昵称 | 非空 |
| item_id | string | 视频ID | 非空,唯一 |
| 曝光数 | integer | 各类曝光数据 | ≥ 0 |
| 互动数 | integer | 各类互动数据 | ≥ 0 |
| 预估视频价格 | decimal | 预估价格 | ≥ 0 |
## 6. 接口需求
### 6.1 外部接口
<!-- MODIFIED: 补充品牌API外部接口 -->
| 接口 | 用途 | 提供方 |
|------|------|--------|
| PostgreSQL | 数据存储与查询 | 自建数据库 |
| 品牌API | 根据品牌ID获取品牌名称 | 内部API (api.internal.intelligrow.cn) |
<!-- NEW START -->
**品牌API详情**
- 接口地址:`/v1/yuntu/brands/{brand_id}`
- 请求方式GET
- 用途根据合作品牌IDbrand_id查询品牌名称
- 文档https://api.internal.intelligrow.cn/docs#/云图
<!-- NEW END -->
### 6.2 内部接口
<!-- MODIFIED: 补充核心API端点改用 FastAPI RESTful 风格 -->
| 接口 | 方法 | 用途 | 说明 |
|------|------|------|------|
| /api/v1/query | POST | 批量查询KOL视频数据 | FastAPI 后端服务提供 |
| /api/v1/export | GET | 导出查询结果为Excel/CSV | FastAPI 后端服务提供 |
<!-- NEW START -->
**API 架构说明**
- 后端采用 FastAPI 框架,提供 RESTful API
- 前端Next.js通过 HTTP 请求调用后端 API
- API 版本管理:使用 `/api/v1/` 前缀
- 跨域支持FastAPI 配置 CORS 中间件
<!-- NEW END -->
## 7. 约束与依赖
### 7.1 技术约束
<!-- MODIFIED: 改用前后端分离架构,后端使用 FastAPI -->
| 约束 | 说明 | 影响 |
|------|------|------|
| 前端技术栈 | React + Next.js (App Router) | 前端技术选型固定 |
| 后端技术栈 | Python FastAPI + PostgreSQL | 后端技术选型固定,前后端分离部署 |
| 部署方式 | Docker推荐 / PM2前端 + Gunicorn/Uvicorn后端 | 运维方式固定,需分别部署前后端服务 |
### 7.2 业务约束
- 数据来源于云图平台,需确保数据同步的及时性和准确性
- 查询功能仅用于内部运营分析,不对外开放
### 7.3 外部依赖
<!-- MODIFIED: 补充品牌API依赖 -->
| 依赖 | 说明 |
|------|------|
| 云图数据源 | 视频数据需从云图平台同步 |
| PostgreSQL 数据库 | 依赖数据库服务可用性 |
| 品牌API | 依赖内部API服务获取品牌名称 |
## 8. 里程碑规划
```
Phase 1 - MVP Phase 2 - 优化
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ MVP │ ────────▶ │ v1.1 │
│ 核心查询 │ │ 性能优化 │
└──────────┘ └──────────┘
待定日期 待定日期
```
| 阶段 | 目标 | 交付物 |
|------|------|--------|
| MVP | 完成核心查询和计算功能 | 可用的批量查询系统,支持数据导出 |
| v1.1 | 性能优化和体验提升 | 更快的查询速度,更好的用户体验 |
## 9. 风险评估
<!-- MODIFIED: 补充品牌API相关风险 -->
| 风险 | 可能性 | 影响 | 应对措施 |
|------|--------|------|----------|
| 数据同步延迟 | 中 | 中 | 建立数据同步监控机制 |
| 大批量查询性能问题 | 中 | 中 | 设置批量查询上限,优化数据库索引 |
| 数据库连接不稳定 | 低 | 高 | 实现连接池和重试机制 |
| 品牌API不可用 | 低 | 中 | 缓存品牌数据降级显示品牌ID |
## 附录
### A. 术语表
<!-- MODIFIED: 补充计算公式相关术语 -->
| 术语 | 定义 |
|------|------|
| KOL | Key Opinion Leader关键意见领袖即达人/网红 |
| 星图ID | 巨量星图平台的达人唯一标识 |
| unique_id | 达人在平台的唯一标识符 |
| CPM | Cost Per Mille每千次曝光成本 |
| 看后搜 | 用户观看视频后进行搜索的行为 |
| A3率 | 用户深度互动率指标 |
| 自然曝光 | 非付费推广获得的曝光量 |
| 加热曝光 | 通过付费推广获得的曝光量 |
| natural_play_cnt | 自然曝光次数用于计算预估自然CPM |
| total_play_cnt | 总曝光次数,包含自然+加热曝光 |
| estimated_video_cost | 预估视频价格,用于计算成本指标 |
| after_view_search_uv | 看后搜用户数,观看视频后进行搜索的独立用户数 |
### B. 输出字段完整列表
<!-- MODIFIED: 补全所有输出字段的数据库字段名 -->
| 中文名 | 字段名 | 说明 |
|--------|--------|------|
| 视频ID | item_id | 主键 |
| 视频标题 | title | - |
| 爆文类型 | viral_type | - |
| 视频链接 | video_url | - |
| 新增A3率 | new_a3_rate | - |
| 看后搜人数 | after_view_search_uv | - |
| 回搜次数 | return_search_cnt | - |
| 自然曝光数 | natural_play_cnt | 计算用 |
| 加热曝光数 | heated_play_cnt | - |
| 总曝光数 | total_play_cnt | 计算用 |
| 总互动 | total_interact | - |
| 点赞 | like_cnt | - |
| 转发 | share_cnt | - |
| 评论 | comment_cnt | - |
| 合作行业ID | industry_id | - |
| 合作行业 | industry_name | - |
| 合作品牌ID | brand_id | 需调用品牌API |
| 合作品牌 | brand_name | 从品牌API获取 |
| 发布时间 | publish_time | - |
| 达人昵称 | star_nickname | 索引字段 |
| 达人unique_id | star_unique_id | 索引字段 |
| 预估视频价格 | estimated_video_cost | 计算用 |
| 预估自然CPM | (计算字段) | = estimated_video_cost / natural_play_cnt * 1000 |
| 预估自然看后搜人数 | (计算字段) | = natural_play_cnt / total_play_cnt * after_view_search_uv |
| 预估自然看后搜人数成本 | (计算字段) | = estimated_video_cost / 预估自然看后搜人数 |
### C. 参考文档
- RequirementsDoc.md

65
doc/RequirementsDoc.md Normal file
View File

@ -0,0 +1,65 @@
[text](README.md)# KOL Insight
云图 KOL 数据查询与分析工具。
## 功能
- 批量查询 KOL 视频数据
- 支持星图ID、达人unique_id、达人昵称搜索
- 计算预估自然CPM、看后搜成本等指标
- 数据导出
## 技术栈
- **前端/后端**: Next.js (App Router)
- **数据库**: PostgreSQL
- **部署**: Docker / PM2
## 快速开始
```bash
# 安装依赖
pnpm install
# 配置环境变量
cp .env.example .env.local
# 开发模式
pnpm dev
# 构建
pnpm build
# 生产运行
pnpm start
```
## 环境变量
```env
DATABASE_URL=postgresql://user:password@host:5432/yuntu_kol
```
## License
MIT
查询输入:批量 星图id精准匹配 或 达人unique_id (精准匹配) 或达人昵称(包含匹配)
星图ID → 匹配 star_id 字段
达人unique_id → 匹配 star_unique_id 字段
达人昵称 → 模糊匹配 star_nickname 字段
输出:
中文名 视频ID 视频标题 爆文类型 视频链接 新增A3率 看后搜次数 回搜次数 自然曝光数 加热曝光数 总曝光数 总互动 点赞 转发 评论 合作行业ID 合作行业 合作品牌ID 合作品牌 发布时间 达人昵称 达人unique_id 预估视频价格 预估自然CPM 预估自然看后搜 预估自然看后搜成本
指标名 item_id title
合作品牌要使用合作品牌id 调用另一个API查找
https://api.internal.intelligrow.cn/docs#/%E4%BA%91%E5%9B%BE/get_yuntu_cookies_v1_yuntu_get_cookie_get
/v1/yuntu/brands/{brand_id}
预估自然CPM =estimated_video_cost / natural_play_cnt *1000
预估自然看后搜人数 = natural_play_cnt / total_play_cnt * after_view_search_uv
预估自然看后搜人数成本 = estimated_video_cost /预估自然看后搜人数

850
doc/UIDesign.md Normal file
View File

@ -0,0 +1,850 @@
# KOL Insight - UI 设计文档
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | 2026-01-28 |
| 来源文档 | DevelopmentPlan.md, PRD.md, FeatureSummary.md |
| 品牌主体 | 麦秒思AI制作 |
## 1. 设计概述
### 1.1 设计原则
**麦秒思AI设计语言**
| 原则 | 说明 | 应用 |
|------|------|------|
| 优雅简洁 | 去除冗余元素,聚焦核心功能 | 单页应用,扁平化设计 |
| 专业可信 | 体现数据分析的专业性 | 稳重色系,清晰的信息层级 |
| 高效直观 | 减少用户学习成本 | 明确的操作流程,即时反馈 |
| 品牌一致 | 强化麦秒思AI品牌形象 | 统一使用品牌标识和色彩 |
**品牌元素**
- **Logo**: doc/ui/muse.svg (麦秒思AI品牌标识)
- **Slogan**: "麦秒思AI制作" (展示在关键位置)
- **色调**: 专业、现代、科技感
### 1.2 页面总览
| 页面ID | 页面名称 | 描述 | 对应功能 | 优先级 |
|--------|----------|------|----------|--------|
| P-001 | 数据查询主页 | 单页应用,包含查询、展示、导出功能 | F-001~F-009 | P0 |
### 1.3 页面导航图
```
┌─────────────────────┐
│ 数据查询主页 │
│ P-001 │
│ (单页应用) │
└──────────┬──────────┘
│ 页面内交互
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 查询区域 │ │ 结果区域 │ │ 导出操作 │
│ (顶部) │ │ (主体) │ │ (右上角) │
└─────────────┘ └─────────────┘ └─────────────┘
```
**说明**: KOL Insight 采用单页应用设计,所有功能集成在一个页面内,通过区域划分组织功能。
## 2. 页面设计
### 2.1 P-001: 数据查询主页
**页面信息**
| 属性 | 值 |
|------|-----|
| 页面ID | P-001 |
| 对应功能 | F-001(星图ID查询), F-002(达人ID查询), F-003(昵称模糊查询), F-004~F-009(计算、展示、导出) |
| 入口 | 直接访问 (首页) |
| 出口 | 无 (单页应用) |
| 布局类型 | 垂直布局,从上到下依次为:品牌头部 → 查询区 → 结果区 |
**【必须】页面布局 - ASCII 原型图**
```
┌────────────────────────────────────────────────────────────────────────────┐
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Header (品牌头部) │ │
│ │ ┌──────┐ [麦秒思AI制作] │ │
│ │ │ MUSE │ KOL Insight - 云图数据查询分析 │ │
│ │ │ Logo │ (品牌标识 + 产品名称) │ │
│ │ └──────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 查询区域 (Query Section) │ │
│ │ │ │
│ │ 查询方式: ( • ) 星图ID ( ) 达人unique_id ( ) 达人昵称 │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 输入查询内容... │ │ │
│ │ │ (支持批量输入,每行一个ID或输入昵称关键词) │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ [清空] [开始查询] │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 结果区域 (Results Section) │ │
│ │ │ │
│ │ 查询结果 (共 128 条) [导出Excel] [导出CSV]│ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 视频ID │ 标题 │ 达人 │ 自然曝光 │ CPM │ 看后搜成本 │ ... │ 操作 │ │ │
│ │ ├─────────────────────────────────────────────────────────────────┤ │ │
│ │ │ 12345 │ XXX │ @XX │ 100.2K │12.5 │ 8.3 │ ... │ [链接]│ │ │
│ │ │ 12346 │ YYY │ @YY │ 85.3K │15.2 │ 9.8 │ ... │ [链接]│ │ │
│ │ │ 12347 │ ZZZ │ @ZZ │ 92.1K │13.8 │ 7.5 │ ... │ [链接]│ │ │
│ │ │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ │ │
│ │ │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ◀ 上一页 1 / 10 下一页 ▶ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
├────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Footer │ │
│ │ © 2026 麦秒思AI制作 | KOL Insight v1.0 │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘
```
**组件清单**
| 组件ID | 组件名称 | 类型 | 说明 | 交互 |
|--------|----------|------|------|------|
| C-001 | 品牌头部 | Header | 展示麦秒思AI品牌Logo和产品名称 | 静态展示 |
| C-002 | 查询方式选择器 | Radio Group | 三种查询方式单选 | 点击切换查询方式 |
| C-003 | 查询输入框 | Textarea | 批量输入或昵称输入 | 文本输入 |
| C-004 | 查询按钮组 | Button Group | 清空、开始查询 | 点击执行操作 |
| C-005 | 结果表格 | Table | 展示26个字段的数据 | 横向滚动,列排序 |
| C-006 | 导出按钮组 | Button Group | 导出Excel/CSV | 点击触发下载 |
| C-007 | 分页器 | Pagination | 翻页控制 | 点击切换页码 |
| C-008 | 视频链接 | Link | 跳转到原视频 | 新窗口打开 |
| C-009 | Footer | Footer | 版权信息和品牌声明 | 静态展示 |
**交互说明**
| 触发 | 动作 | 结果 |
|------|------|------|
| 选择查询方式 | 切换 Radio | 输入框提示文案变化 |
| 输入查询条件 | 文本输入 | 实时验证输入格式 |
| 点击"清空" | 清空输入 | 输入框清空,结果区隐藏 |
| 点击"开始查询" | 提交查询 | 显示 Loading → 展示结果表格 |
| 点击表头 | 排序 | 按列排序数据 |
| 点击"导出Excel/CSV" | 触发下载 | 浏览器下载文件 |
| 点击视频链接 | 跳转 | 新窗口打开视频页面 |
| 点击分页 | 切换页 | 加载对应页数据 |
**页面状态**
| 状态 | 说明 | 展示 |
|------|------|------|
| 默认态 | 页面初始加载 | 查询区可用,结果区显示引导文案 |
| 输入态 | 用户正在输入 | 输入框聚焦,显示输入提示 |
| 查询中 | 提交查询后等待响应 | 按钮禁用,显示 Loading 动画 |
| 结果态 | 查询成功返回数据 | 展示结果表格和分页 |
| 空结果态 | 查询无匹配数据 | 显示空状态插图和提示 |
| 错误态 | 查询失败或网络错误 | 显示错误提示和重试按钮 |
**默认态原型**
```
┌─────────────────────────────────────────────────────────────┐
│ 查询区域 │
│ [查询方式选择器] │
│ [输入框 - 待输入] │
│ [清空] [开始查询] │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 结果区域 │
│ │
│ ┌─────────────┐ │
│ │ 搜索图标 │ │
│ └─────────────┘ │
│ │
│ 请选择查询方式并输入查询条件 │
│ │
└─────────────────────────────────────────────────────────────┘
```
**空结果态原型**
```
┌─────────────────────────────────────────────────────────────┐
│ 结果区域 │
│ │
│ ┌─────────────┐ │
│ │ 空盒子图标 │ │
│ └─────────────┘ │
│ │
│ 未找到匹配数据 │
│ │
│ 请调整查询条件后重新尝试 │
│ │
│ [修改查询条件] │
│ │
└─────────────────────────────────────────────────────────────┘
```
**查询中状态原型**
```
┌─────────────────────────────────────────────────────────────┐
│ 结果区域 │
│ │
│ ┌─────────────┐ │
│ │ ⟳ Loading │ │
│ └─────────────┘ │
│ │
│ 正在查询数据,请稍候... │
│ │
└─────────────────────────────────────────────────────────────┘
```
**错误态原型**
```
┌─────────────────────────────────────────────────────────────┐
│ 结果区域 │
│ │
│ ┌─────────────┐ │
│ │ ⚠ 错误图标 │ │
│ └─────────────┘ │
│ │
│ 查询失败,请重试 │
│ │
│ 可能原因:网络异常或数据库连接失败 │
│ │
│ [重新查询] │
│ │
└─────────────────────────────────────────────────────────────┘
```
## 3. 用户流程
### 3.1 核心流程:批量查询 KOL 数据
**【必须】流程图展示用户操作流程:**
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 打开页面 │ ──▶ │ 选择查询 │ ──▶ │ 输入查询 │ ──▶ │ 提交查询 │
│ P-001 │ │ 方式 │ │ 条件 │ │ │
└─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘
┌─────────────┐
│ 查询处理 │
│ (后端) │
└──────┬──────┘
┌─────────────────────┼─────────────────────┐
│ 成功 │ 失败
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 展示结果 │ │ 显示错误 │
│ (结果表格) │ │ (重试) │
└──────┬──────┘ └─────────────┘
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 浏览数据 │ │ 点击视频 │ │ 导出数据 │
│ (翻页/排序)│ │ 链接 │ │ (Excel/CSV)│
└──────┬──────┘ └─────────────┘ └─────────────┘
┌─────────────┐
│ 继续查询 │
│ 或离开 │
└─────────────┘
```
**流程步骤详解**
| 步骤 | 页面/区域 | 用户操作 | 系统响应 | 预期时间 |
|------|----------|----------|----------|----------|
| 1 | P-001 | 打开页面 | 加载默认态,显示引导文案 | < 2s |
| 2 | 查询区 | 选择查询方式(Radio) | 输入框提示文案更新 | 即时 |
| 3 | 查询区 | 输入查询条件(Textarea) | 实时验证,显示字符数 | 即时 |
| 4 | 查询区 | 点击"开始查询"按钮 | 按钮禁用,显示 Loading | 即时 |
| 5 | 后端 | 提交查询请求 | 查询数据库,计算指标 | < 3s |
| 6a | 结果区 | 查询成功 | 展示结果表格,显示数据条数 | < 0.5s |
| 6b | 结果区 | 查询失败 | 显示错误提示和重试按钮 | < 0.5s |
| 7 | 结果区 | 浏览数据(滚动/翻页) | 加载更多数据或切换页 | < 0.5s |
| 8 | 结果区 | 点击视频链接 | 新窗口打开视频页面 | 即时 |
| 9 | 结果区 | 点击导出按钮 | 生成文件,触发下载 | < 5s |
### 3.2 辅助流程:修改查询条件
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 查看结果 │ ──▶ │ 点击"清空" │ ──▶ │ 输入框清空 │
│ (不满意) │ │ 按钮 │ │ (重新输入) │
└─────────────┘ └─────────────┘ └──────┬──────┘
┌─────────────┐
│ 重新查询 │
│ │
└─────────────┘
```
### 3.3 异常流程:错误处理
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 提交查询 │ ──▶ │ 网络异常/ │ ──▶ │ 显示错误 │
│ │ │ 数据库错误 │ │ 提示 │
└─────────────┘ └─────────────┘ └──────┬──────┘
┌─────────────┐
│ 点击"重新 │
│ 查询"按钮 │
└──────┬──────┘
┌─────────────┐
│ 重试查询 │
│ │
└─────────────┘
```
## 4. 组件规范
### 4.1 基础组件
**Button 按钮**
```
主按钮 (Primary):
┌────────────────┐
│ 开始查询 │ (品牌主色背景,白色文字)
└────────────────┘
:hover → 加深 10%
:active → 加深 20%
次按钮 (Secondary):
┌────────────────┐
│ 清空 │ (白色背景,边框,灰色文字)
└────────────────┘
:hover → 灰色背景
:active → 深灰背景
导出按钮 (Action):
┌────────────────┐
│ 导出Excel ▼ │ (绿色背景,白色文字)
└────────────────┘
禁用态 (Disabled):
┌────────────────┐
│ 查询中... │ (灰色背景,禁止点击)
└────────────────┘
```
**Input/Textarea 输入框**
```
默认态:
┌────────────────────────────────────────┐
│ 请输入星图ID,每行一个... │
└────────────────────────────────────────┘
聚焦态:
┌────────────────────────────────────────┐
│ 12345▊ │ (蓝色边框高亮)
└────────────────────────────────────────┘
错误态:
┌────────────────────────────────────────┐
│ (输入内容) │ (红色边框)
└────────────────────────────────────────┘
⚠ 请输入有效的ID格式
```
**Radio 单选框**
```
未选中: ( ) 星图ID
选中: (•) 星图ID (品牌主色圆点)
禁用: ( ) 星图ID (灰色文字)
```
**Table 表格**
```
┌──────────┬──────────┬──────────┬──────────┬──────────┐
│ 视频ID ▲ │ 标题 │ 达人 │ 自然曝光 │ CPM │ (表头:深色背景)
├──────────┼──────────┼──────────┼──────────┼──────────┤
│ 12345 │ 测试标题 │ @测试 │ 100.2K │ 12.5 │ (奇数行:白色)
├──────────┼──────────┼──────────┼──────────┼──────────┤
│ 12346 │ 测试标题 │ @测试 │ 85.3K │ 15.2 │ (偶数行:浅灰)
└──────────┴──────────┴──────────┴──────────┴──────────┘
:hover 行 → 浅蓝色背景高亮
```
**Pagination 分页器**
```
◀ 上一页 1 2 [3] 4 5 下一页 ▶
当前页: [3] → 品牌主色背景
其他页: 2 → 可点击,悬停高亮
禁用页: ◀ → 灰色,不可点击
```
**Loading 加载动画**
```
方案1: 旋转圆环
┌─⟳─┐
│ │ 正在加载...
└───┘
方案2: 骨架屏 (表格数据加载)
┌──────────┬──────────┬──────────┐
│ ░░░░░░ │ ░░░░░░ │ ░░░░░░ │
│ ░░░░░░ │ ░░░░░░ │ ░░░░░░ │
│ ░░░░░░ │ ░░░░░░ │ ░░░░░░ │
└──────────┴──────────┴──────────┘
```
### 4.2 业务组件
**QueryForm 查询表单组件**
```
┌──────────────────────────────────────────────────────┐
│ 查询方式: (•) 星图ID ( ) 达人unique_id ( ) 昵称 │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ 请输入星图ID,每行一个... │ │
│ │ │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ [清空] [开始查询] │
└──────────────────────────────────────────────────────┘
功能:
• 支持三种查询方式切换
• 根据查询方式动态更新输入提示
• 输入验证(实时)
• 清空和提交操作
```
**ResultTable 结果表格组件**
```
┌──────────────────────────────────────────────────────────────┐
│ 查询结果 (共 128 条) [导出Excel] [导出CSV] │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 26个字段的数据表格 (可横向滚动) │ │
│ │ • 视频ID、标题、达人、曝光数据、互动数据 │ │
│ │ • 预估CPM、预估看后搜人数、预估看后搜成本 │ │
│ │ • 品牌信息、发布时间等 │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ◀ 上一页 1 / 10 下一页 ▶ │
└──────────────────────────────────────────────────────────────┘
功能:
• 展示26个字段
• 支持列排序
• 横向滚动(表格宽度超出)
• 分页展示(每页20条)
• 数字格式化(千分位)
• 视频链接可点击
```
**EmptyState 空状态组件**
```
┌─────────────────────────────────────────┐
│ │
│ ┌─────────────┐ │
│ │ 📦 空盒子 │ │
│ └─────────────┘ │
│ │
│ 未找到匹配数据 │
│ │
│ 请调整查询条件后重新尝试 │
│ │
│ [修改查询条件] │
│ │
└─────────────────────────────────────────┘
```
**ErrorState 错误状态组件**
```
┌─────────────────────────────────────────┐
│ │
│ ┌─────────────┐ │
│ │ ⚠ 错误 │ │
│ └─────────────┘ │
│ │
│ 查询失败,请重试 │
│ │
│ 可能原因:网络异常或数据库连接失败 │
│ │
│ [重新查询] │
│ │
└─────────────────────────────────────────┘
```
## 5. 设计规范
### 5.1 色彩规范
**麦秒思AI品牌色系**
| 用途 | 色值 | 示例 | 说明 |
|------|------|------|------|
| 主色 (Primary) | #4F46E5 | 主按钮、链接、选中态 | 品牌核心色,专业科技感 |
| 主色浅 | #818CF8 | 悬停态、次要元素 | 主色的衍生色 |
| 主色深 | #3730A3 | 按下态、强调元素 | 主色的深色变体 |
| 成功 (Success) | #10B981 | 成功提示、导出按钮 | 绿色系 |
| 警告 (Warning) | #F59E0B | 警告提示 | 橙色系 |
| 错误 (Error) | #EF4444 | 错误提示、删除操作 | 红色系 |
| 信息 (Info) | #3B82F6 | 信息提示 | 蓝色系 |
| 文字主色 | #111827 | 标题、正文 | 深灰色,易读 |
| 文字次色 | #6B7280 | 辅助文字、提示 | 中灰色 |
| 文字禁用 | #D1D5DB | 禁用文字 | 浅灰色 |
| 背景主色 | #FFFFFF | 页面背景 | 纯白 |
| 背景次色 | #F9FAFB | 卡片背景、表格奇数行 | 浅灰白 |
| 边框色 | #E5E7EB | 输入框边框、分隔线 | 浅灰 |
**色彩使用示例**
```
主按钮: ████████ #4F46E5 (主色)
导出按钮: ████████ #10B981 (成功色)
错误提示: ████████ #EF4444 (错误色)
文字主色: ████████ #111827
背景色: ████████ #FFFFFF
```
### 5.2 字体规范
**字体家族**
| 平台 | 字体 | 备用 |
|------|------|------|
| 中文 | PingFang SC, Microsoft YaHei | sans-serif |
| 英文 | Inter, -apple-system, BlinkMacSystemFont | sans-serif |
| 数字 | Tabular Nums (等宽数字) | - |
**字号规范**
| 用途 | 字号 | 字重 | 行高 | 示例 |
|------|------|------|------|------|
| 页面标题 | 28px | Bold (700) | 1.3 | KOL Insight |
| 区域标题 | 20px | Semibold (600) | 1.4 | 查询结果 |
| 小标题 | 16px | Medium (500) | 1.5 | 查询方式 |
| 正文 | 14px | Regular (400) | 1.6 | 表格内容、按钮文字 |
| 辅助文字 | 12px | Regular (400) | 1.5 | 提示文案、标签 |
| 表格数据 | 14px | Regular (400) | 1.5 | 数据单元格 |
### 5.3 间距规范
**基础间距单位: 4px**
| 间距 | 值 | 用途 |
|------|-----|------|
| xs | 4px | 紧凑元素间距 |
| sm | 8px | 小间距,如图标与文字 |
| md | 16px | 标准间距,组件内部 |
| lg | 24px | 大间距,区域之间 |
| xl | 32px | 特大间距,页面区块 |
| 2xl | 48px | 超大间距,顶部底部留白 |
**组件间距示例**
```
页面整体: padding: 32px (xl)
┌────────────────────────────────┐
│ [32px 顶部留白] │
│ ┌──────────────────────────┐ │
│ │ 查询区域 │ │
│ └──────────────────────────┘ │
│ [24px 区域间距] │
│ ┌──────────────────────────┐ │
│ │ 结果区域 │ │
│ └──────────────────────────┘ │
│ [32px 底部留白] │
└────────────────────────────────┘
按钮组: gap: 8px (sm)
[清空] [8px] [开始查询]
```
### 5.4 圆角规范
| 元素 | 圆角值 | 说明 |
|------|--------|------|
| 按钮 | 6px | 适中圆角,现代感 |
| 输入框 | 6px | 与按钮保持一致 |
| 卡片 | 8px | 稍大圆角,柔和 |
| 表格 | 8px | 整体表格边框 |
| Badge/Tag | 12px | 较大圆角,标签感 |
### 5.5 阴影规范
| 用途 | 阴影值 | 使用场景 |
|------|--------|----------|
| 轻微阴影 | 0 1px 3px rgba(0,0,0,0.1) | 卡片、输入框 |
| 标准阴影 | 0 4px 6px rgba(0,0,0,0.1) | 悬浮元素 |
| 深度阴影 | 0 10px 15px rgba(0,0,0,0.1) | 模态框、下拉菜单 |
### 5.6 响应式断点
| 断点 | 宽度 | 布局说明 |
|------|------|----------|
| Mobile | < 768px | 单栏布局,表格横向滚动 |
| Tablet | 768px - 1024px | 优化表格列宽 |
| Desktop | 1024px - 1440px | 标准布局 |
| Large Desktop | > 1440px | 居中固定宽度或适当扩展 |
**响应式布局示例**
```
Desktop (> 1024px):
┌────────────────────────────────────────┐
│ [查询区域 - 全宽] │
│ [结果表格 - 全宽,所有列可见] │
└────────────────────────────────────────┘
Tablet (768px - 1024px):
┌──────────────────────────────┐
│ [查询区域 - 全宽] │
│ [结果表格 - 隐藏部分次要列] │
└──────────────────────────────┘
Mobile (< 768px):
┌──────────────────┐
│ [查询区域] │
│ [结果卡片列表] │ (表格转为卡片布局)
│ [卡片1] │
│ [卡片2] │
└──────────────────┘
```
## 6. 品牌应用
### 6.1 品牌标识使用
**Logo 使用规范**
| 位置 | 尺寸 | 说明 |
|------|------|------|
| Header 左侧 | 高度 40px | 麦秒思AI Logo (doc/ui/muse.svg) |
| Favicon | 32x32px | 简化版 Logo 图标 |
| 加载动画 | - | 可选:Logo 动效 |
**品牌声明位置**
- Header 右上角:"麦秒思AI制作"
- Footer 中央:"© 2026 麦秒思AI制作 | KOL Insight v1.0"
**Header 品牌区域详细设计**
```
┌────────────────────────────────────────────────────────────────┐
│ ┌──────┐ │
│ │ │ KOL Insight 麦秒思AI制作 │
│ │ MUSE │ 云图数据查询分析 │
│ │ Logo │ (产品名称 + Slogan) (品牌声明) │
│ │ │ │
│ └──────┘ │
│ [40px] [16px] [产品名称:20px Bold] [14px Regular] │
└────────────────────────────────────────────────────────────────┘
```
### 6.2 空状态插图风格
- 使用简洁的线条插图
- 色彩与品牌主色保持一致
- 插图风格:扁平化、现代、专业
## 7. 动效规范
### 7.1 过渡动效
| 交互 | 动效 | 时长 | 缓动函数 |
|------|------|------|----------|
| 按钮悬停 | 背景色变化 | 150ms | ease-out |
| 输入框聚焦 | 边框高亮 | 200ms | ease-in-out |
| 页面切换 | 淡入淡出 | 300ms | ease-in-out |
| 表格排序 | 淡入 | 200ms | ease-out |
| 模态框打开 | 缩放+淡入 | 250ms | cubic-bezier(0.4, 0, 0.2, 1) |
### 7.2 加载动画
```
方案: 旋转圆环
┌─────┐
│ ⟳ │ (360度旋转,1s 一圈,无限循环)
└─────┘
或: 品牌色脉动
┌─────┐
│ ● │ (主色圆点,缩放脉动)
└─────┘
```
## 8. 数据展示规范
### 8.1 数字格式化
| 数据类型 | 格式 | 示例 |
|----------|------|------|
| 整数 | 千分位分隔 | 1,234,567 |
| 小数 | 保留2位 | 12.34 |
| 大数值 | K/M 缩写 | 100.2K, 1.5M |
| 百分比 | % 符号 | 85.3% |
| 金额 | ¥ + 千分位 | ¥ 1,234.56 |
| 日期 | YYYY-MM-DD | 2026-01-28 |
### 8.2 表格列定义
**完整的26个输出字段**
| 序号 | 中文名 | 字段宽度 | 对齐方式 | 格式化 |
|------|--------|----------|----------|--------|
| 1 | 视频ID | 120px | 左对齐 | 文本 |
| 2 | 视频标题 | 200px | 左对齐 | 文本,超出省略 |
| 3 | 爆文类型 | 100px | 居中 | Badge |
| 4 | 视频链接 | 100px | 居中 | 链接按钮 |
| 5 | 新增A3率 | 100px | 右对齐 | 百分比 |
| 6 | 看后搜人数 | 120px | 右对齐 | 千分位 |
| 7 | 回搜次数 | 100px | 右对齐 | 千分位 |
| 8 | 自然曝光数 | 120px | 右对齐 | K/M 缩写 |
| 9 | 加热曝光数 | 120px | 右对齐 | K/M 缩写 |
| 10 | 总曝光数 | 120px | 右对齐 | K/M 缩写 |
| 11 | 总互动 | 100px | 右对齐 | 千分位 |
| 12 | 点赞 | 100px | 右对齐 | 千分位 |
| 13 | 转发 | 100px | 右对齐 | 千分位 |
| 14 | 评论 | 100px | 右对齐 | 千分位 |
| 15 | 合作行业ID | 120px | 左对齐 | 文本 |
| 16 | 合作行业 | 120px | 左对齐 | 文本 |
| 17 | 合作品牌ID | 120px | 左对齐 | 文本 |
| 18 | 合作品牌 | 150px | 左对齐 | 文本 |
| 19 | 发布时间 | 120px | 居中 | YYYY-MM-DD |
| 20 | 达人昵称 | 120px | 左对齐 | 文本 |
| 21 | 达人unique_id | 150px | 左对齐 | 文本 |
| 22 | 预估视频价格 | 120px | 右对齐 | ¥ + 千分位 |
| 23 | 预估自然CPM | 120px | 右对齐 | 小数2位 |
| 24 | 预估自然看后搜人数 | 150px | 右对齐 | 小数2位 |
| 25 | 预估自然看后搜人数成本 | 180px | 右对齐 | ¥ + 小数2位 |
**表格列优先级** (移动端渐进隐藏)
- P0 (必显): 视频ID、标题、达人、自然曝光、CPM、看后搜成本
- P1 (平板可隐藏): 爆文类型、看后搜人数、总互动、品牌
- P2 (移动端隐藏): 其他详细指标
### 8.3 空值处理
| 场景 | 显示 |
|------|------|
| 字段为 null | "-" |
| 计算结果为 0 | "0" |
| 除零错误 | "-" |
| API 获取失败 | 显示原始 ID |
## 9. 可访问性 (Accessibility)
| 规范 | 说明 |
|------|------|
| 语义化 HTML | 使用正确的 HTML 标签 |
| 键盘导航 | 支持 Tab 键切换焦点 |
| ARIA 标签 | 为交互元素添加 aria-label |
| 颜色对比度 | 文字与背景对比度 ≥ 4.5:1 |
| 焦点可见性 | 聚焦元素显示明显边框 |
## 10. 性能优化
| 优化项 | 说明 |
|--------|------|
| 图片优化 | Logo 使用 SVG,支持 Retina |
| 表格虚拟滚动 | 大数据量时使用虚拟滚动 |
| 懒加载 | 分页数据按需加载 |
| 防抖节流 | 搜索输入使用防抖 |
## 11. 设计交付物
| 交付物 | 说明 |
|--------|------|
| UIDesign.md | 本文档 |
| doc/ui/muse.svg | 品牌 Logo 文件 |
| 组件规范 | 可复用的 React 组件 |
| 样式变量 | CSS/Tailwind 配置文件 |
---
## 附录 A: 设计检查清单
生成 UIDesign 后,请确认以下项目:
- [x] 覆盖 DevelopmentPlan 所有功能模块
- [x] 页面导航图清晰展示页面关系
- [x] 每个页面都有 ASCII 原型图
- [x] 原型图展示了完整的页面结构
- [x] 用户流程有流程图
- [x] 每个页面都有状态说明 (默认/加载/空/错误)
- [x] 组件清单完整
- [x] 交互说明清晰
- [x] 设计规范统一 (色彩/字体/间距)
- [x] 品牌元素应用 (Logo/Slogan)
- [x] 响应式设计考虑
- [x] 数据展示规范 (26个字段)
## 附录 B: 与开发对接
**开发实现优先级**
1. P0: 核心布局和查询功能 (T-005~T-009)
2. P1: 结果展示和导出功能 (T-010~T-011)
3. P2: 视频链接跳转和细节优化 (T-014)
**关键设计决策**
- **单页应用**: 简化交互流程,提升用户体验
- **品牌强化**: 多处展示"麦秒思AI制作",建立品牌认知
- **数据优先**: 核心是数据展示,UI 简洁不干扰
- **响应式**: 支持桌面/平板/移动端访问
---
**文档版本**: v1.0
**最后更新**: 2026-01-28
**设计团队**: 麦秒思AI
**审核状态**: 待审核 (建议运行 `/ru` 进行评审)

View File

@ -0,0 +1,229 @@
# FeatureSummary 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | 2026-01-28 21:45 |
| 目标文档 | doc/FeatureSummary.md |
| 参照文档 | doc/PRD.md |
| 问题统计 | 0 个严重 / 1 个一般 / 2 个建议 |
## 覆盖度分析
### 功能模块覆盖
| PRD 功能模块 | FeatureSummary 对应 | 状态 |
|--------------|---------------------|------|
| 数据查询模块3个功能 | 2.1节 F-001~F-003 | ✅ 完全覆盖 |
| 数据计算模块3个功能 | 2.2节 F-004~F-006 | ✅ 完全覆盖 |
| 数据展示模块2个功能 | 2.3节 F-007~F-008 | ✅ 完全覆盖 |
| 数据导出模块1个功能 | 2.4节 F-009 | ✅ 完全覆盖 |
**覆盖率**: 9/9 完全覆盖 ✅
### 用户故事关联
| 用户故事 | FeatureSummary 功能 | 状态 |
|----------|---------------------|------|
| US-001 | F-001 星图ID查询 | ✅ 正确关联 |
| US-002 | F-002 达人ID查询 | ✅ 正确关联 |
| US-003 | F-003 昵称模糊查询 | ✅ 正确关联 |
| US-004 | F-004, F-005, F-006 计算功能 | ✅ 正确关联 |
| US-005 | F-009 数据导出 | ✅ 正确关联 |
| US-006 | F-007 结果列表展示 | ✅ 正确关联 |
| US-007 | F-008 视频链接跳转 | ✅ 正确关联 |
**关联准确率**: 7/7 完全正确 ✅
### 优先级一致性
| PRD 优先级 | PRD 功能 | FeatureSummary 功能 | 状态 |
|------------|----------|---------------------|------|
| P0 | 3个查询功能 | F-001, F-002, F-003 | ✅ 一致 |
| P0 | 3个计算功能 | F-004, F-005, F-006 | ✅ 一致 |
| P1 | 结果展示 | F-007 | ✅ 一致 |
| P1 | 数据导出 | F-009 | ✅ 一致 |
| P2 | 视频链接跳转 | F-008 | ✅ 一致 |
**优先级一致性**: 100% ✅
## 结构完整性检查
### 必要章节检查
| 章节 | 要求 | 状态 | 评价 |
|------|------|------|------|
| 1.1 功能统计 | 必须 | ✅ | 统计数据准确4个模块9个功能 |
| 1.2 功能架构图 | 必须 | ✅ | 清晰展示4个核心模块+外部依赖 |
| 1.3 模块依赖关系 | 必须 | ✅ | 依赖关系清晰,流程合理 |
| 2. 功能清单 | 必须 | ✅ | 9个功能全部列出契约详情完整 |
| 3. 功能依赖矩阵 | 必须 | ✅ | 矩阵完整,依赖关系明确 |
| 4. 功能流程图 | 必须 | ✅ | 核心流程+计算流程,可视化清晰 |
| 5. 版本规划 | 必须 | ✅ | MVP + v1.1 规划合理 |
| 6. 接口契约预览 | 必须 | ✅ | 列出核心API端点 |
### 功能契约详情检查
| 功能ID | 触发条件 | 输入 | 处理逻辑 | 输出 | 异常情况 | 边界说明 | 状态 |
|--------|---------|------|---------|------|---------|---------|------|
| F-001 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-002 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-003 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-004 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-005 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-006 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-007 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-008 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
| F-009 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 完整 |
**完整性**: 9/9 功能契约详情完整 ✅
## 问题清单
### 严重问题 (Critical)
> 必须修复,否则影响后续文档生成
**无严重问题** ✅
### 一般问题 (Major)
> 建议修复,可提升文档质量
1. **[位置: [doc/FeatureSummary.md:196](doc/FeatureSummary.md#L196)]** 品牌API调用细节不够明确
- 问题F-007 的处理逻辑中提到"调用品牌API获取品牌名称",但未明确:
- 何时调用品牌API展示时实时调用 / 后端查询时调用)
- 如何批量处理?(逐个调用 / 批量调用)
- 缓存策略是什么?
- 建议在功能契约详情中补充品牌API调用的时机和策略说明
- 影响开发人员可能对品牌API集成时机理解不一致
### 改进建议 (Minor)
> 可选优化项
1. **[位置: [doc/FeatureSummary.md:47](doc/FeatureSummary.md#L47)]** 模块依赖关系图可优化
- 建议:在 1.3 模块依赖关系图中品牌API作为外部依赖其连接线指向"数据展示模块"更合理(当前连接位置不够明确)
- 当前图示品牌API连接到数据计算模块下方但实际是在展示阶段使用
- 优化后可以避免理解偏差
2. **[位置: [doc/FeatureSummary.md:367](doc/FeatureSummary.md#L367)]** 接口契约预览可补充品牌API
- 建议:在 6. 接口契约预览中补充品牌API的完整契约
```
| F-007 (品牌) | 外部API | GET /v1/yuntu/brands/{brand_id} | 获取品牌名称 |
```
- 理由品牌API是关键外部依赖应在接口契约预览中明确列出
## 质量评估
### 文档规范性
| 评估项 | 状态 | 评价 |
|--------|------|------|
| 功能ID格式统一 | ✅ | F-001 ~ F-009 格式规范 |
| 表格格式规范 | ✅ | 所有表格格式统一、清晰 |
| 层级结构清晰 | ✅ | 章节层级合理,易于阅读 |
| 术语使用一致 | ✅ | 与PRD术语完全一致 |
| 可视化图表完整 | ✅ | 5个必要图表全部包含 |
### 内容准确性
| 评估项 | 状态 | 评价 |
|--------|------|------|
| 功能描述准确 | ✅ | 所有功能描述与PRD一致 |
| 计算公式准确 | ✅ | F-004~F-006 公式与PRD完全一致 |
| 异常处理完整 | ✅ | 每个功能都考虑了异常情况 |
| 边界说明清晰 | ✅ | 明确了数量限制、匹配方式等边界 |
| 依赖关系正确 | ✅ | 功能依赖矩阵逻辑正确 |
### 开发价值
| 评估项 | 状态 | 评价 |
|--------|------|------|
| 功能契约可执行 | ✅ | 契约详情足够明确,可直接指导开发 |
| 接口设计合理 | ✅ | API设计符合RESTful规范 |
| 版本规划清晰 | ✅ | MVP范围明确v1.1规划合理 |
| 依赖关系明确 | ✅ | 有助于制定开发顺序 |
## 亮点总结
### 文档优势
1. **契约详情非常完整** ⭐⭐⭐
- 每个功能的输入/输出/异常/边界都有详细说明
- 异常处理考虑周全除零、空输入、API失败等
- 边界说明明确(批量数量限制、匹配方式等)
2. **可视化图表丰富** ⭐⭐⭐
- 功能架构图清晰展示了4个核心模块
- 模块依赖关系图直观展示了数据流向
- 功能依赖矩阵完整标注了9个功能的依赖关系
- 核心业务流程图和计算流程图帮助理解系统运作
3. **版本规划合理** ⭐⭐
- MVP聚焦核心功能P0 + P1
- v1.1 包含增强功能P2
- 功能划分符合敏捷开发原则
4. **与PRD高度一致** ⭐⭐⭐
- 功能覆盖率 100%
- 优先级完全一致
- 用户故事关联准确
- 术语使用统一
## 评审结论
**通过** ✅
### 结论说明
FeatureSummary 文档质量优秀,完全符合「功能契约」文档的定位和要求:
1. **覆盖度**: 100% 覆盖 PRD 的所有功能需求,无遗漏
2. **一致性**: 与 PRD 的功能描述、优先级、用户故事完全一致
3. **完整性**: 所有必要章节、图表、契约详情均完整
4. **准确性**: 计算公式、处理逻辑、异常处理准确无误
5. **可执行性**: 功能契约详情明确,可直接指导开发工作
仅有1个一般问题品牌API调用细节和2个改进建议不影响文档整体质量和可用性。建议在进入 DevelopmentPlan 阶段时对品牌API集成策略进行详细设计。
### 下一步行动
- [ ] **可选**:补充 F-007 中品牌API的调用时机和策略说明
- [ ] **可选**:优化 1.3 模块依赖关系图中品牌API的连接位置
- [ ] **可选**在接口契约预览中补充品牌API契约
- [ ] **推荐**继续进入下一阶段文档生成UIDesign 或 DevelopmentPlan
---
## 附录:功能统计对比
### PRD vs FeatureSummary 功能对照表
| PRD 章节 | PRD 功能点 | FeatureSummary 功能 | 功能ID | 优先级 | 用户故事 |
|----------|-----------|---------------------|--------|--------|----------|
| 3.2.1 | 星图ID查询 | 星图ID查询 | F-001 | P0 | US-001 |
| 3.2.1 | 达人ID查询 | 达人ID查询 | F-002 | P0 | US-002 |
| 3.2.1 | 昵称模糊查询 | 昵称模糊查询 | F-003 | P0 | US-003 |
| 3.2.2 | 预估自然CPM | 预估自然CPM计算 | F-004 | P0 | US-004 |
| 3.2.2 | 预估自然看后搜人数 | 预估自然看后搜人数计算 | F-005 | P0 | US-004 |
| 3.2.2 | 预估自然看后搜人数成本 | 预估自然看后搜人数成本计算 | F-006 | P0 | US-004 |
| 3.2.3 | 结果列表展示 | 结果列表展示 | F-007 | P1 | US-006 |
| 3.2.3 | 视频链接跳转 | 视频链接跳转 | F-008 | P2 | US-007 |
| 3.2.4 | 数据导出 | Excel/CSV导出 | F-009 | P1 | US-005 |
**统计**
- PRD 功能点9 个
- FeatureSummary 功能9 个
- 匹配率9/9 = 100%
### 计算公式准确性验证
| 功能 | PRD 公式 | FeatureSummary 公式 | 状态 |
|------|---------|---------------------|------|
| F-004 | `estimated_video_cost / natural_play_cnt * 1000` | `estimated_video_cost / natural_play_cnt * 1000` | ✅ 一致 |
| F-005 | `natural_play_cnt / total_play_cnt * after_view_search_uv` | `natural_play_cnt / total_play_cnt * after_view_search_uv` | ✅ 一致 |
| F-006 | `estimated_video_cost / 预估自然看后搜人数` | `estimated_video_cost / 预估自然看后搜人数` | ✅ 一致 |
**结论**:所有计算公式与 PRD 完全一致 ✅

176
doc/review-PRD-claude.md Normal file
View File

@ -0,0 +1,176 @@
# PRD 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | 2026-01-28 21:30 |
| 目标文档 | doc/PRD.md |
| 参照文档 | doc/RequirementsDoc.md |
| 问题统计 | 3 个严重 / 2 个一般 / 3 个建议 |
## 一致性检查
### 需求覆盖分析
| RequirementsDoc 需求项 | PRD 对应位置 | 状态 |
|------------------------|--------------|------|
| 批量查询 KOL 视频数据 | US-001, US-002, US-003 | ✅ 已覆盖 |
| 支持星图ID精准查询 | US-001 / 3.2.1节 | ✅ 已覆盖 |
| 支持达人unique_id精准查询 | US-002 / 3.2.1节 | ✅ 已覆盖 |
| 支持达人昵称模糊查询 | US-003 / 3.2.1节 | ✅ 已覆盖 |
| 计算预估自然CPM | US-004 / 3.2.2节 | ⚠️ 部分覆盖(缺少公式) |
| 计算预估自然看后搜 | US-004 / 3.2.2节 | ⚠️ 部分覆盖(缺少公式) |
| 计算预估自然看后搜成本 | US-004 / 3.2.2节 | ⚠️ 部分覆盖(缺少公式) |
| 数据导出 | US-005 / 3.2.4节 | ✅ 已覆盖 |
| 调用品牌API获取品牌名称 | - | ❌ 未覆盖 |
| 技术栈Next.js + PostgreSQL | 7.1节 | ✅ 已覆盖 |
| 部署方式Docker / PM2 | 7.1节 | ✅ 已覆盖 |
### 差异说明
PRD 新增但 RequirementsDoc 未明确提及的内容:
1. **用户角色定义**2.1节):细化了"运营人员"和"投放优化师"两个角色,这是对原始需求的合理扩展
2. **用户旅程设计**2.3节):可视化了用户操作流程,增强了需求理解
3. **里程碑规划**第8章增加了MVP和v1.1两个阶段规划
4. **风险评估**第9章识别了数据同步延迟、性能问题等风险
**评估**: 以上新增内容均为合理的 PRD 扩展,有助于后续开发和实施。
## 问题清单
### 严重问题 (Critical)
> 必须修复,否则影响后续文档生成
1. **[位置: [doc/PRD.md:199](doc/PRD.md#L199)]** 缺少外部品牌API依赖说明
- 现状PRD 第6章"接口需求"中仅提及 PostgreSQL未说明需要调用外部品牌API
- 与 RequirementsDoc 的差异RequirementsDoc 明确说明"合作品牌要使用合作品牌id 调用另一个API查找 https://api.internal.intelligrow.cn/docs#/%E4%BA%91%E5%9B%BE/get_yuntu_cookies_v1_yuntu_get_cookie_get /v1/yuntu/brands/{brand_id}"
- 建议:在 6.1 外部接口表格中增加品牌API行
```
| 品牌API | 根据品牌ID获取品牌名称 | https://api.internal.intelligrow.cn/v1/yuntu/brands/{brand_id} |
```
2. **[位置: [doc/PRD.md:273](doc/PRD.md#L273)]** 输出字段映射不完整
- 现状附录B中大部分输出字段的"字段名"列标记为"-",缺少数据库字段映射
- 影响:开发人员无法知道如何从数据库获取这些数据
- 建议:补全所有输出字段对应的数据库字段名,参考 RequirementsDoc 中的字段定义
3. **[位置: [doc/PRD.md:114](doc/PRD.md#L114)]** 计算公式缺失
- 现状3.2.2节"数据计算模块"仅描述了功能,未给出具体计算公式
- 与 RequirementsDoc 的差异RequirementsDoc 明确了三个公式:
- 预估自然CPM = estimated_video_cost / natural_play_cnt * 1000
- 预估自然看后搜人数 = natural_play_cnt / total_play_cnt * after_view_search_uv
- 预估自然看后搜人数成本 = estimated_video_cost / 预估自然看后搜人数
- 建议:在 3.2.2 节的"描述"列或"验收标准"列中添加完整的计算公式
### 一般问题 (Major)
> 建议修复,可提升文档质量
1. **[位置: [doc/PRD.md:119](doc/PRD.md#L119)]** 术语不一致
- 问题PRD 使用"预估自然看后搜"RequirementsDoc 使用"预估自然看后搜人数"
- 影响:可能导致理解偏差(是次数还是人数)
- 建议:统一术语为"预估自然看后搜人数",与计算公式中使用的 after_view_search_uv用户数概念一致
2. **[位置: [doc/PRD.md:168](doc/PRD.md#L168)]** 数据模型与计算公式不匹配
- 问题5.1节数据模型中未列出计算公式所需的关键字段:
- estimated_video_cost预估视频价格- 已在5.2中提及
- natural_play_cnt自然曝光数- 已在5.1中提及
- total_play_cnt总曝光数- 已在5.1中提及
- after_view_search_uv看后搜人数- 未明确提及字段名
- 建议:在 5.1 数据模型图中补充这些关键字段的明确字段名
### 改进建议 (Minor)
> 可选优化项
1. **[位置: [doc/PRD.md:262](doc/PRD.md#L262)]** 术语表可补充
- 建议:在术语表中补充以下术语:
- natural_play_cnt自然曝光次数
- estimated_video_cost预估视频价格
- after_view_search_uv看后搜用户数
2. **[位置: [doc/PRD.md:164](doc/PRD.md#L164)]** 数据需求可细化
- 建议:补充数据库表名(如 videos 或 kol_videos
- 建议补充关键字段的索引建议star_id, star_unique_id, star_nickname
3. **[位置: [doc/PRD.md:208](doc/PRD.md#L208)]** 内部接口设计
- 建议:虽然标注"待补充"但可以在此阶段先列出核心API端点
- POST /api/query - 批量查询接口
- GET /api/export - 数据导出接口
## 用户故事评估
| 评估项 | 结果 |
|--------|------|
| 用户故事总数 | 7 个 |
| 符合格式规范 | 7 / 7 ✅ |
| 有验收标准 | 7 / 7 ✅ |
| 关联功能点 | 7 / 7 ✅ |
| 优先级划分 | 明确P0/P1/P2✅ |
### 用户故事质量评价
**优点**:
- ✅ 所有用户故事都有唯一IDUS-001 ~ US-007
- ✅ 格式规范,符合"作为{角色},我想要{功能},以便{价值}"结构
- ✅ 验收标准清晰、可测试
- ✅ 优先级划分合理核心查询和计算功能为P0
**无明显问题**
## 评审结论
**需修改后通过**
### 结论说明
PRD 整体质量较好,功能需求覆盖完整,用户故事设计规范,文档结构清晰。但存在以下关键问题需要修复:
1. **外部API依赖缺失**未说明需要调用品牌API获取品牌名称这是实现完整功能的必要依赖
2. **计算公式缺失**:开发人员需要明确的计算公式来实现预估指标
3. **字段映射不完整**:输出字段与数据库字段的映射关系不明确
修复上述3个严重问题后PRD 可以作为下一阶段FeatureSummary、UIDesign、DevelopmentPlan的基础文档。
### 下一步行动
- [ ] **必须**:在 6.1 节补充品牌API外部接口说明
- [ ] **必须**:在 3.2.2 节补充完整的计算公式
- [ ] **必须**在附录B中补全所有输出字段的数据库字段名
- [ ] **建议**:统一"预估自然看后搜"术语为"预估自然看后搜人数"
- [ ] **建议**:在 5.1 数据模型中明确标注计算公式所需字段的字段名
---
## 附录RequirementsDoc 原始需求对照
为便于对比,以下是 RequirementsDoc 中的核心需求要点:
### 功能需求
- 批量查询 KOL 视频数据
- 支持星图ID、达人unique_id、达人昵称搜索
- 计算预估自然CPM、看后搜成本等指标
- 数据导出
### 查询规则
- 星图ID → 匹配 star_id 字段(精准匹配)
- 达人unique_id → 匹配 star_unique_id 字段(精准匹配)
- 达人昵称 → 模糊匹配 star_nickname 字段(包含匹配)
### 计算公式
```
预估自然CPM = estimated_video_cost / natural_play_cnt * 1000
预估自然看后搜人数 = natural_play_cnt / total_play_cnt * after_view_search_uv
预估自然看后搜人数成本 = estimated_video_cost / 预估自然看后搜人数
```
### 外部依赖
- 品牌API: `/v1/yuntu/brands/{brand_id}`
- 用途根据合作品牌ID查询品牌名称
### 技术栈
- Next.js (App Router)
- PostgreSQL
- Docker / PM2

View File

@ -0,0 +1,268 @@
# UIDesign 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | 2026-01-28 22:00 |
| 目标文档 | doc/UIDesign.md |
| 参照文档 | doc/DevelopmentPlan.md |
| 问题统计 | 1 个严重 / 2 个一般 / 3 个建议 |
## 页面覆盖分析
### 功能模块 vs UI 页面映射
| DevelopmentPlan 功能模块 | UIDesign 页面/区域 | 状态 | 说明 |
|-------------------------|-------------------|------|------|
| F-001 星图ID查询 | P-001 查询区域 | ✅ 完全覆盖 | 查询方式选择器 + 输入框 |
| F-002 达人ID查询 | P-001 查询区域 | ✅ 完全覆盖 | 查询方式选择器 + 输入框 |
| F-003 昵称模糊查询 | P-001 查询区域 | ✅ 完全覆盖 | 查询方式选择器 + 输入框 |
| F-004 预估自然CPM计算 | P-001 结果区域 | ✅ 完全覆盖 | 后端计算,表格展示 |
| F-005 预估自然看后搜人数计算 | P-001 结果区域 | ✅ 完全覆盖 | 后端计算,表格展示 |
| F-006 预估自然看后搜人数成本计算 | P-001 结果区域 | ✅ 完全覆盖 | 后端计算,表格展示 |
| F-007 结果列表展示 | P-001 结果区域 | ✅ 完全覆盖 | ResultTable 组件C-005 |
| F-008 视频链接跳转 | P-001 结果区域 | ✅ 完全覆盖 | 视频链接组件C-008 |
| F-009 Excel/CSV导出 | P-001 导出按钮 | ✅ 完全覆盖 | 导出按钮组C-006 |
| F-010 品牌名称批量获取 | P-001 结果区域 | ⚠️ 部分覆盖 | 后端处理,前端展示,但页面功能对应中未明确列出 |
**覆盖率**: 10/10 功能完全覆盖 ✅
**说明**:
- 所有功能模块都在单页应用P-001中覆盖
- 采用单页应用设计,通过区域划分组织功能,符合开发计划
- F-010 品牌API集成功能在后端处理前端仅展示品牌名称但在页面功能对应描述中未明确列出
### 开发任务 vs UI 组件映射
| DevelopmentPlan 任务 | UIDesign 组件/设计 | 状态 |
|---------------------|-------------------|------|
| T-005 查询 API 开发 | C-002/C-003/C-004 查询组件 | ✅ 匹配 |
| T-006 计算逻辑实现 | 表格数据列CPM等计算字段 | ✅ 匹配 |
| T-007 品牌 API 批量集成 | 表格"合作品牌"列 | ✅ 匹配 |
| T-008 查询表单组件 | QueryForm 业务组件 | ✅ 匹配 |
| T-009 结果表格组件 | ResultTable 业务组件 | ✅ 匹配 |
| T-010 导出 API 开发 | C-006 导出按钮组 | ✅ 匹配 |
| T-011 导出按钮组件 | C-006 导出按钮组 | ✅ 匹配 |
| T-014 视频链接跳转 | C-008 视频链接组件 | ✅ 匹配 |
**匹配率**: 8/8 完全匹配 ✅
## 设计一致性检查
| 检查项 | 结果 | 说明 |
|--------|------|------|
| 页面命名规范 | ✅ | P-001 格式规范,清晰明确 |
| 组件命名规范 | ✅ | C-001~C-009 格式统一 |
| 布局风格统一 | ✅ | 垂直布局从上到下Header → 查询区 → 结果区 → Footer |
| 交互模式一致 | ✅ | 查询 → 展示 → 导出流程清晰 |
| 状态覆盖完整 | ✅ | 默认态、输入态、查询中、结果态、空结果态、错误态 |
| 品牌元素应用 | ✅ | 麦秒思AI Logo、Slogan、品牌色系统一应用 |
| 设计规范完整 | ✅ | 色彩、字体、间距、圆角、阴影规范完整 |
| 响应式设计 | ✅ | 考虑了 Mobile/Tablet/Desktop 三种断点 |
## 问题清单
### 严重问题 (Critical)
> 必须修复,否则影响开发实施
1. **[位置: [doc/UIDesign.md:68](doc/UIDesign.md#L68)]** 页面功能对应中缺少 F-010 品牌API集成
- 现状:页面信息表格中"对应功能"列仅列出了 F-001~F-009缺少 F-010
- 与 DevelopmentPlan 的差异DevelopmentPlan 明确了 F-010 是 P0 优先级的核心功能,且 T-007 任务专门负责品牌API批量集成
- 影响开发人员可能不清楚品牌名称字段需要在后端调用品牌API获取
- 建议:在"对应功能"列中补充 F-010并在页面说明中明确品牌名称的获取方式
- 修复方案:
```markdown
| 对应功能 | F-001(星图ID查询), F-002(达人ID查询), F-003(昵称模糊查询), F-004~F-006(计算), F-007~F-009(展示、导出), F-010(品牌API集成) |
```
### 一般问题 (Major)
> 建议修复,可提升设计清晰度
1. **[位置: [doc/UIDesign.md:740](doc/UIDesign.md#L740)]** 表格列定义中品牌字段说明不明确
- 问题8.2 节"表格列定义"中第18列"合作品牌"字段宽度150px没有说明数据来源
- 与 FeatureSummary 的差异FeatureSummary 明确了 F-010 功能会在后端批量调用品牌API获取品牌名称
- 影响:前端开发人员可能认为品牌名称直接来自数据库,而不知道需要后端预处理
- 建议:在"合作品牌"行的"格式化"列中补充说明"后端通过品牌API获取"
- 修复方案:
```markdown
| 18 | 合作品牌 | 150px | 左对齐 | 文本后端批量调用品牌API获取 |
```
2. **[位置: [doc/UIDesign.md:775](doc/UIDesign.md#L775)]** 空值处理中API失败说明不够明确
- 问题8.3 节"空值处理"中,"API 获取失败 → 显示原始 ID"未明确指的是品牌API
- 影响可能与其他API混淆
- 建议明确说明是品牌API失败时的降级策略
- 修复方案:
```markdown
| 品牌API获取失败 | 显示原始品牌ID |
```
### 改进建议 (Minor)
> 可选优化项,提升设计完整性
1. **[位置: [doc/UIDesign.md:145](doc/UIDesign.md#L145)]** 交互说明可补充品牌数据处理说明
- 建议:在"交互说明"表格中补充一行:
```markdown
| 查询完成 | 后端批量获取品牌名称 | 品牌名称填充到表格"合作品牌"列 |
```
- 理由:明确品牌数据的获取时机和展示位置
2. **[位置: [doc/UIDesign.md:196](doc/UIDesign.md#L196)]** 结果表格组件功能描述可补充品牌API说明
- 建议:在 4.2 节"ResultTable 结果表格组件"功能列表中补充:
```markdown
• 品牌名称由后端调用品牌API获取前端直接展示
```
- 理由:帮助前端开发人员理解品牌名称字段的特殊性
3. **[位置: [doc/UIDesign.md:831](doc/UIDesign.md#L831)]** 开发实现优先级可同步 DevelopmentPlan
- 建议附录B"开发实现优先级"中补充 T-007 品牌API集成任务
- 当前:
```markdown
1. P0: 核心布局和查询功能 (T-005~T-009)
```
- 建议改为:
```markdown
1. P0: 核心布局和查询功能 (T-005~T-009含 T-007 品牌API批量集成)
```
- 理由:与 DevelopmentPlan 保持一致T-007 是 P0 优先级任务
## 设计质量评估
### 文档完整性
| 评估项 | 状态 | 评价 |
|--------|------|------|
| 页面列表完整 | ✅ | 单页应用P-001 覆盖所有功能 |
| 页面布局 ASCII 原型图 | ✅ | 清晰展示页面结构 |
| 页面状态说明 | ✅ | 6种状态全部覆盖默认/输入/查询中/结果/空/错误) |
| 组件清单完整 | ✅ | 9个组件全部列出C-001~C-009 |
| 交互说明清晰 | ✅ | 8种交互场景全部说明 |
| 用户流程图 | ✅ | 核心流程、辅助流程、异常流程全部包含 |
| 设计规范统一 | ✅ | 色彩、字体、间距、圆角、阴影规范完整 |
| 品牌元素应用 | ✅ | 麦秒思AI Logo、Slogan、品牌色完整应用 |
| 数据展示规范 | ✅ | 26个字段完整列出格式化规则明确 |
| 响应式设计 | ✅ | Mobile/Tablet/Desktop 三种断点考虑 |
### 设计亮点
1. **单页应用设计** ⭐⭐⭐
- 所有功能集成在一个页面内,通过区域划分组织功能
- 简化用户操作流程,无需页面跳转
- 符合开发计划的技术架构Next.js App Router
2. **品牌一致性强** ⭐⭐⭐
- 麦秒思AI品牌元素贯穿整个设计
- Logo、Slogan、品牌色系统一应用
- Header 和 Footer 强化品牌认知
3. **状态覆盖全面** ⭐⭐⭐
- 6种页面状态全部考虑默认/输入/查询中/结果/空/错误)
- 每种状态都有 ASCII 原型图展示
- 错误态提供明确的重试引导
4. **设计规范完整** ⭐⭐⭐
- 色彩、字体、间距、圆角、阴影规范详细
- 提供了具体的数值和示例
- 便于开发人员实现统一的视觉效果
5. **数据展示规范明确** ⭐⭐
- 26个字段完整列出每个字段都有宽度、对齐、格式化规则
- 空值处理、数字格式化规则清晰
- 提供了表格列优先级(移动端渐进隐藏)
### 与开发计划的契合度
| 契合项 | 评价 | 说明 |
|--------|------|------|
| 技术栈匹配 | ✅ | 单页应用符合 Next.js App Router 架构 |
| 功能模块对齐 | ✅ | 所有功能模块F-001~F-010都有对应的UI设计 |
| 开发任务匹配 | ✅ | UI组件与开发任务T-005~T-016一一对应 |
| API 设计契合 | ✅ | 查询、导出功能与 API 设计一致 |
| 数据库字段对应 | ✅ | 26个输出字段与数据库 Schema 一致 |
| 性能优化考虑 | ✅ | 表格虚拟滚动、懒加载、防抖节流都有考虑 |
## 用户体验评估
| 评估项 | 评分 | 说明 |
|--------|------|------|
| 学习成本 | ⭐⭐⭐⭐⭐ | 单页应用,操作流程简单直观 |
| 操作效率 | ⭐⭐⭐⭐⭐ | 批量查询、一键导出,效率高 |
| 错误提示 | ⭐⭐⭐⭐ | 错误态有明确提示和重试引导 |
| 视觉层次 | ⭐⭐⭐⭐⭐ | 查询区 → 结果区层次清晰 |
| 品牌认知 | ⭐⭐⭐⭐⭐ | 多处展示麦秒思AI品牌元素 |
| 响应式体验 | ⭐⭐⭐⭐ | 考虑了移动端适配 |
## 评审结论
**需修改后通过** ⚠️
### 结论说明
UIDesign 文档整体质量优秀,设计完整、规范统一、品牌一致性强,与 DevelopmentPlan 的契合度高。但存在以下关键问题需要修复:
1. **功能对应不完整**:页面功能对应中缺少 F-010 品牌API集成功能可能导致开发人员对品牌名称字段的数据来源理解不清
2. **品牌字段说明不明确**表格列定义和空值处理中品牌字段的特殊处理后端调用品牌API说明不够明确
修复上述问题后UIDesign 可以作为前端开发的完整设计指南。
**优点总结**
- ✅ 单页应用设计合理,操作流程简洁高效
- ✅ 品牌元素应用完整强化麦秒思AI品牌认知
- ✅ 设计规范详细,便于开发实现
- ✅ 状态覆盖全面,用户体验考虑周到
- ✅ 与开发计划高度契合
**需要改进**
- ⚠️ 补充 F-010 品牌API集成功能的UI说明
- ⚠️ 明确品牌名称字段的数据来源和处理方式
### 下一步行动
**必须完成**
- [ ] 在页面信息表格"对应功能"列中补充 F-010
- [ ] 在表格列定义中明确"合作品牌"字段的数据来源后端品牌API
- [ ] 在空值处理中明确"品牌API获取失败"的降级策略
**建议完成**
- [ ] 在交互说明中补充品牌数据处理时机
- [ ] 在 ResultTable 组件功能描述中补充品牌API说明
- [ ] 在开发实现优先级中同步 T-007 品牌API集成任务
**后续工作**
- [ ] 开发人员根据 UIDesign 开始前端实现
- [ ] 设计师提供 Logo 和品牌素材doc/ui/muse.svg
- [ ] 前端实现后进行 UI 还原度验收
---
## 附录:设计检查清单对照
根据 UIDesign.md 附录A"设计检查清单",逐项对照:
| 检查项 | 状态 | 说明 |
|--------|------|------|
| ✅ 覆盖 DevelopmentPlan 所有功能模块 | ✅ | F-001~F-010 全部覆盖 |
| ✅ 页面导航图清晰展示页面关系 | ✅ | 单页应用,导航图清晰 |
| ✅ 每个页面都有 ASCII 原型图 | ✅ | P-001 有完整原型图 |
| ✅ 原型图展示了完整的页面结构 | ✅ | Header → 查询区 → 结果区 → Footer |
| ✅ 用户流程有流程图 | ✅ | 核心流程、辅助流程、异常流程 |
| ✅ 每个页面都有状态说明 | ✅ | 6种状态全部说明 |
| ✅ 组件清单完整 | ✅ | C-001~C-009 |
| ✅ 交互说明清晰 | ✅ | 8种交互场景 |
| ✅ 设计规范统一 | ✅ | 色彩/字体/间距完整 |
| ✅ 品牌元素应用 | ✅ | Logo/Slogan/品牌色 |
| ✅ 响应式设计考虑 | ✅ | Mobile/Tablet/Desktop |
| ✅ 数据展示规范 | ✅ | 26个字段完整 |
**检查清单完成度**: 12/12 = 100% ✅
---
**文档版本**: v1.0
**评审人**: Claude
**审核状态**: 需修改后通过
**下次评审**: 修复关键问题后重新评审

754
doc/review-tasks-claude.md Normal file
View File

@ -0,0 +1,754 @@
# Tasks 评审报告
## 概要
| 项目 | 内容 |
|------|------|
| 评审时间 | 2026-01-28 15:30 |
| 目标文档 | [doc/tasks.md](doc/tasks.md) |
| 参照文档 | [doc/UIDesign.md](doc/UIDesign.md), [doc/DevelopmentPlan.md](doc/DevelopmentPlan.md) |
| 问题统计 | **4 个严重 / 6 个一般 / 5 个建议** |
| 评审结论 | 🟡 **需修改后通过** |
## 覆盖度分析
### DevelopmentPlan 覆盖
#### Phase 1: 基础架构搭建
| 开发项 (DevelopmentPlan) | 对应任务 (tasks.md) | 状态 | 说明 |
|---------------------------|---------------------|------|------|
| T-001 前端项目初始化 + T-002 后端项目初始化 | **T-001 项目初始化** | ⚠️ | **合并为一个任务,粒度过大** |
| T-003 数据库配置 | T-002 数据库配置 | ✅ | 完全覆盖,含TDD要求 |
| T-004 基础 UI 框架 | T-003 基础 UI 框架 | ✅ | 完全覆盖,含品牌元素 |
| T-005 环境变量配置 | T-004 环境变量配置 | ✅ | 完全覆盖 |
#### Phase 2: 核心功能开发
| 开发项 (DevelopmentPlan) | 对应任务 (tasks.md) | 状态 | 说明 |
|---------------------------|---------------------|------|------|
| T-006 查询 API 开发 (后端) | **T-005 查询 API 开发** | ✅ | 含TDD要求和100%覆盖率 |
| T-007 计算逻辑实现 (后端) | **T-006 计算逻辑实现** | ✅ | 含TDD要求和100%覆盖率 |
| T-008 品牌 API 批量集成 (后端) | **T-007 品牌 API 批量集成** | ✅ | 含TDD要求和100%覆盖率 |
| T-009 导出 API 开发 (后端) | **T-010 导出 API 开发** | ⚠️ | **依赖T-009前端组件,不合理** |
| T-010 查询表单组件 (前端) | T-008 查询表单组件 | ✅ | 标注"粗略实现" |
| T-011 结果表格组件 (前端) | T-009 结果表格组件 | ✅ | 标注"粗略实现" |
| T-012 导出按钮组件 (前端) | T-011 导出按钮组件 | ✅ | 标注"粗略实现" |
| **(未在 DevelopmentPlan 中)** | **T-012 主页面集成** | ⚠️ | **新增任务,导致编号错位** |
#### Phase 3: 优化与测试
| 开发项 (DevelopmentPlan) | 对应任务 (tasks.md) | 状态 | 说明 |
|---------------------------|---------------------|------|------|
| T-013 错误处理 (前后端) | **T-013 错误处理** | ❌ | **编号错位** |
| T-014 性能优化 (后端) | **T-014 性能优化** | ❌ | **编号错位** |
| T-015 视频链接跳转 (前端) | **T-015 视频链接跳转** | ❌ | **编号错位** |
| T-016 部署配置 (前后端) | **T-016 部署配置** | ❌ | **编号错位** |
| T-017 集成测试 | **T-017 集成测试** | ❌ | **编号错位** |
**总覆盖率**: 17/16 (tasks.md 新增1个任务)
**关键问题**:
1. ❌ **任务编号不一致**: Phase 3 的5个任务编号都向后偏移一位
2. ⚠️ **T-001 粒度过大**: 前后端初始化合并为一个任务
3. ⚠️ **T-010 依赖错误**: 后端 API 不应依赖前端组件 T-009
4. ⚠️ **T-012 新增任务**: DevelopmentPlan 中没有对应项
---
### UIDesign 覆盖
| UI 页面/组件 | 对应任务 | 状态 | 说明 |
|-------------|----------|------|------|
| **P-001: 数据查询主页** | T-012 主页面集成 | ✅ | 单页应用集成 |
| **组件覆盖** | | | |
| C-001: 品牌头部 | T-003 基础 UI 框架 | ✅ | 包含 Logo 和品牌声明 |
| C-002: 查询方式选择器 | T-008 查询表单组件 | ✅ | Radio Group |
| C-003: 查询输入框 | T-008 查询表单组件 | ✅ | Textarea |
| C-004: 查询按钮组 | T-008 查询表单组件 | ✅ | 清空/开始查询 |
| C-005: 结果表格 | T-009 结果表格组件 | ✅ | 26字段表格 |
| C-006: 导出按钮组 | T-011 导出按钮组件 | ✅ | Excel/CSV 导出 |
| C-007: 分页器 | T-009 结果表格组件 | ✅ | 验收标准第9条 |
| C-008: 视频链接 | T-015 视频链接跳转 | ✅ | 新窗口打开 |
| C-009: Footer | T-003 基础 UI 框架 | ✅ | 版权信息 |
| **页面状态** | | | |
| 6种状态 | T-012 主页面集成 | ✅ | 验收标准第6-8条 |
**总覆盖率**: 10/10 (100%)
**UI覆盖评价**: ✅ 所有 UI 页面、组件、状态都有对应任务
---
## 任务质量分析
| 检查项 | 通过数 | 总数 | 通过率 |
|--------|--------|------|--------|
| 有明确描述 | 17 | 17 | 100% |
| 有验收标准 | 17 | 17 | 100% |
| 验收标准清晰 | 17 | 17 | 100% |
| 依赖关系明确 | 16 | 17 | 94% |
| 粒度合适 | 16 | 17 | 94% |
| TDD 要求明确 | 7 | 12 | 58% |
| 测试覆盖率要求 | 7 | 12 | 58% |
**质量问题**:
- ⚠️ **T-001 粒度过大**: 前后端初始化合并,无法并行开发
- ⚠️ **后端任务 TDD 覆盖不全**: 仅 7/12 的后端任务有明确 TDD 要求
- ❌ **缺少测试独立任务**: 100% 覆盖率嵌入开发任务,难以单独验收
---
## 问题清单
### 严重问题 (Critical)
#### C-1: T-001 任务粒度过大,前后端无法并行
**位置**: [doc/tasks.md:43](doc/tasks.md:43)
**问题描述**:
```markdown
| T-001 | 项目初始化 | 前后端分离架构:前端 Next.js,后端 FastAPI,配置 TypeScript、ESLint、Prettier | P0 | - |
```
T-001 包含:
1. 前端 Next.js 14.x 项目创建
2. 后端 FastAPI 0.104+ 项目创建
3. 前端 TypeScript、ESLint、Prettier 配置
4. 后端 Python 依赖管理配置
5. 验收标准6条(前端3条+后端3条)
**影响**:
- 🚫 **无法并行开发**: 前端和后端开发者可能是不同人员,合并为一个任务导致无法同时开工
- 🚫 **验收标准过多**: 6条验收标准涉及不同技术栈,验收时需要同时检查前后端
- 🚫 **依赖关系不清晰**: T-002 数据库配置依赖 T-001,但实际只依赖后端部分
**建议修复**:
拆分为两个独立任务:
- **T-001A: 前端项目初始化** (依赖: 无)
- 创建 Next.js 14.x 项目
- 配置 TypeScript、ESLint、Prettier
- 验收: 可运行 `pnpm dev`
- **T-001B: 后端项目初始化** (依赖: 无)
- 创建 FastAPI 0.104+ 项目
- 配置 Poetry/pip
- 验收: 可运行 `uvicorn main:app --reload`
**优点**:
- ✅ 前后端可并行开发,节省时间
- ✅ 验收标准更聚焦
- ✅ 依赖关系更清晰(T-002 只依赖 T-001B)
---
#### C-2: T-010 依赖关系错误
**位置**: [doc/tasks.md:67](doc/tasks.md:67)
**问题描述**:
```markdown
| T-010 | 导出 API 开发 | ... | P1 | T-006, T-007, T-009 | ...
```
T-010 (后端导出 API) 依赖 T-009 (前端结果表格组件),这是**逻辑错误**。
**分析**:
- T-010 是**后端 FastAPI** 接口,负责生成 Excel/CSV 文件
- T-009 是**前端 React** 组件,负责展示表格
- 后端 API 不应该依赖前端组件的实现
**实际依赖**:
- T-010 应该依赖 **T-006 (计算逻辑实现)** 和 **T-007 (品牌API集成)**
- 因为导出的数据需要包含计算后的指标和品牌名称
**验收标准第5条**:
```
5. 使用中文列名作为表头 **(与 T-009 ResultTable 字段一致)**
```
这说明是要求"字段一致性",而不是"依赖关系"。
**影响**:
- 🚫 **执行顺序混乱**: 开发者可能误以为要先完成前端表格才能开发后端导出API
- 🚫 **前后端耦合**: 后端依赖前端,违反分离架构原则
**建议修复**:
1. 修改依赖: `T-010 依赖: T-006, T-007` (移除 T-009)
2. 修改验收标准第5条: "使用中文列名作为表头 **(字段顺序和命名与前端 ResultTable 保持一致,参考共享的字段定义)**"
3. 建议: 创建共享的字段定义文件(如 `types/fields.ts`),前后端都引用
---
#### C-3: 缺少单元测试独立任务
**位置**: 整个 tasks.md
**问题描述**:
tasks.md 中有 **7个任务** 要求 TDD 和 100% 测试覆盖率:
- T-002: 数据库配置 (验收标准 7-8 条)
- T-005: 查询 API 开发 (验收标准 9-10 条)
- T-006: 计算逻辑实现 (验收标准 7-8 条)
- T-007: 品牌 API 批量集成 (验收标准 8-9 条)
- T-010: 导出 API 开发 (验收标准 10-11 条)
- T-013: 错误处理 (验收标准 8-9 条)
- T-017: 集成测试 (验收标准 9-11 条)
但**没有单独的测试任务**,所有测试要求都嵌入在开发任务中。
**影响**:
- 🚫 **测试容易被忽略**: 开发进度紧张时,测试可能被压缩或跳过
- 🚫 **无法单独追踪测试进度**: 测试覆盖率没有独立的验收里程碑
- 🚫 **100% 覆盖率难以保证**: 嵌入在开发任务中,验收时可能只检查功能,不检查覆盖率
- 🚫 **测试报告缺失**: T-017 要求生成覆盖率报告,但其他任务没有明确要求
**建议修复**:
在 Phase 3 增加测试里程碑任务:
**方案A: 增加独立测试任务**
```markdown
| T-018 | 测试覆盖率验收 | 验证所有后端代码测试覆盖率 ≥ 100% | P1 | T-002, T-005~007, T-010, T-013 |
验收标准:
1. 数据库操作测试覆盖率 100% (T-002)
2. API集成测试覆盖率 100% (T-005)
3. 计算逻辑单元测试覆盖率 100% (T-006)
4. 品牌API单元测试覆盖率 100% (T-007)
5. 导出功能单元测试覆盖率 100% (T-010)
6. 错误处理分支覆盖率 100% (T-013)
7. 使用 pytest-cov 生成覆盖率报告
8. 覆盖率报告上传到 CI/CD
```
**方案B: 在每个 Phase 结束增加测试验收点**
```markdown
## 3. Phase 2 任务 - 核心功能开发
### 3.3 测试验收
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
| T-012A | Phase 2 测试验收 | 验证 Phase 2 所有后端任务测试覆盖率 | P0 | T-005~007, T-010 | 1. 所有后端代码覆盖率 ≥ 100%<br>2. 生成覆盖率报告 |
```
---
#### C-4: 任务编号与 DevelopmentPlan 不一致
**位置**: Phase 3 所有任务 ([doc/tasks.md:88-101](doc/tasks.md))
**问题描述**:
tasks.md 新增了 T-012 (主页面集成),导致 Phase 3 的所有任务编号向后偏移一位:
| DevelopmentPlan | tasks.md | 差异 |
|-----------------|----------|------|
| T-013 错误处理 | **T-013 错误处理** | ❌ 编号错位 |
| T-014 性能优化 | **T-014 性能优化** | ❌ 编号错位 |
| T-015 视频链接跳转 | **T-015 视频链接跳转** | ❌ 编号错位 |
| T-016 部署配置 | **T-016 部署配置** | ❌ 编号错位 |
| T-017 集成测试 | **T-017 集成测试** | ❌ 编号错位 |
**影响**:
- 🚫 **文档引用混乱**: 在 DevelopmentPlan 中看到的 T-013 和 tasks.md 中的 T-013 不是同一个任务
- 🚫 **沟通成本高**: 开发人员需要在两个文档之间切换时手动对照编号
- 🚫 **代码注释/提交信息错误**: Git 提交信息中的任务 ID 可能指向错误的任务
**建议修复**:
**方案A (推荐): 将 T-012 改为 T-008A**
```markdown
| T-008 | 查询表单组件 | ... | P0 | T-003 |
| T-008A | 主页面集成 | ... | P0 | T-008, T-009, T-011 |
| T-009 | 结果表格组件 | ... | P1 | T-003, T-006, T-007 |
```
- 优点: Phase 3 编号与 DevelopmentPlan 完全一致
- 缺点: 引入子编号
**方案B: 更新 DevelopmentPlan.md**
在 DevelopmentPlan.md 的 Phase 2 增加 T-012 任务
- 优点: 保持 tasks.md 不变
- 缺点: 需要修改 DevelopmentPlan.md
**方案C: 在 tasks.md 增加对照表**
```markdown
## 附录: 与 DevelopmentPlan 任务编号对照
| tasks.md | DevelopmentPlan | 任务名称 |
|----------|-----------------|----------|
| T-013 | T-013 | 错误处理 |
| T-014 | T-014 | 性能优化 |
...
```
- 优点: 不修改编号,只增加对照表
- 缺点: 需要手动查表,增加认知负担
---
### 一般问题 (Major)
#### M-1: T-002 真实数据库测试要求缺少环境准备说明
**位置**: [doc/tasks.md:46](doc/tasks.md:46)
**问题描述**:
```markdown
6. **真实数据库测试**: 使用 .env 中的连接字符串连接真实数据库并验证
```
验收标准要求连接"真实数据库",但没有说明:
- 真实数据库是否已经准备好?
- 数据库中是否有测试数据?
- 需要什么权限?
**影响**:
- 开发者执行到 T-002 时可能发现数据库环境未就绪
- 导致任务阻塞,无法继续
**建议修复**:
1. 在 T-002 依赖中增加: `依赖: T-001B (后端初始化), 数据库环境准备 (DBA)`
2. 在 T-004 环境变量配置中增加验收标准: "数据库连接字符串配置完成,数据库可访问"
3. 或在任务描述中明确标注: "需提前准备测试数据库环境,包含表结构和测试数据"
---
#### M-2: T-012 主页面集成缺少状态管理方案说明
**位置**: [doc/tasks.md:85](doc/tasks.md:85)
**问题描述**:
```markdown
6. 页面状态管理: 默认态/输入态/查询中/结果态/空结果态/错误态
```
验收标准提到"页面状态管理",但没有说明使用何种状态管理方案:
- React useState?
- Zustand?
- Redux Toolkit?
- Context API?
**影响**:
- 前端开发者需要自行决定状态管理方案
- 可能导致过度设计(引入 Redux)或过于简单(难以维护)
**建议修复**:
在验收标准第6条补充说明:
```markdown
6. 页面状态管理: 默认态/输入态/查询中/结果态/空结果态/错误态 **(使用 React useState 管理,无需第三方库)**
```
---
#### M-3: T-007 品牌API并发限制和超时参数硬编码
**位置**: [doc/tasks.md:64](doc/tasks.md:64)
**问题描述**:
```markdown
3. 使用 asyncio.gather 批量并发请求(限制 10 并发)
6. 超时设置: 3秒
```
验收标准硬编码了"10 并发"和"3 秒",未说明这些参数是否可配置。
**影响**:
- 生产环境可能需要调整并发数(如品牌API限流时降低并发)
- 超时时间可能需要根据网络环境调整
- 硬编码参数难以适应不同环境
**建议修复**:
1. 将并发限制和超时时间配置到环境变量或配置文件
2. 修改验收标准:
```markdown
3. 使用 asyncio.gather 批量并发请求,并发数可配置(默认 10)
6. 超时时间可配置(默认 3 秒)
7. 从环境变量读取配置: BRAND_API_CONCURRENCY, BRAND_API_TIMEOUT
```
---
#### M-4: T-009 与 T-010 字段一致性验证缺失
**位置**: [doc/tasks.md:76](doc/tasks.md:76)
**问题描述**:
T-009 (前端表格) 和 T-010 (后端导出) 都要求"使用中文列名",但没有明确如何保证字段一致性。
**当前状态**:
- T-009 验收标准: "展示 26 个字段,使用中文列名"
- T-010 验收标准: "使用中文列名作为表头 **(与 T-009 ResultTable 字段一致)**"
**问题**:
- "字段一致"如何验证?
- 前端和后端是否共享字段定义?
**影响**:
- 前端展示和导出文件的列名可能不一致
- 导致用户混淆
**建议修复**:
1. 创建共享的字段定义文件:
```typescript
// shared/types/fields.ts
export const VIDEO_FIELDS = [
{ key: 'item_id', label: '视频ID', width: 120 },
{ key: 'title', label: '视频标题', width: 200 },
// ... 24 more fields
] as const;
```
2. 修改 T-009 验收标准:
```markdown
2. 展示 26 个字段,使用共享字段定义文件 (shared/types/fields.ts)
```
3. 修改 T-010 验收标准:
```markdown
5. 使用共享字段定义文件作为表头,保证与前端表格字段顺序和命名完全一致
```
---
#### M-5: T-014 性能优化缺少性能测试脚本
**位置**: [doc/tasks.md:96](doc/tasks.md:96)
**问题描述**:
T-014 定义了明确的性能指标:
- 查询响应时间 ≤ 3秒 (100条)
- 页面加载时间 ≤ 2秒
- 导出响应时间 ≤ 5秒 (1000条)
但验收标准只有"验证索引已创建",没有要求编写性能测试脚本。
**影响**:
- 性能指标难以自动化验证
- 依赖人工测试,可能遗漏
- 回归测试时无法快速验证性能
**建议修复**:
增加验收标准:
```markdown
6. **后端性能测试**: 编写性能测试脚本,验证响应时间指标
7. **真实数据库测试**: 使用真实数据库和测试数据进行性能测试
8. 性能测试报告: 生成性能测试报告,记录实际响应时间
```
---
#### M-6: T-017 集成测试缺少性能测试用例
**位置**: [doc/tasks.md:101](doc/tasks.md:101)
**问题描述**:
T-017 集成测试有 8 个功能测试用例,但未包含 T-014 定义的性能指标验证。
**建议修复**:
在验收标准中增加性能测试用例:
```markdown
9. 测试用例: 性能指标验证 (查询≤3秒、导出≤5秒)
10. **真实数据库集成测试**: 使用 .env 中的真实数据库连接进行完整集成测试
11. **后端测试覆盖率验证**: 确认所有后端代码测试覆盖率 ≥ 100%
12. **测试报告生成**: 使用 pytest-cov 生成覆盖率报告
```
(注: 验收标准 10-12 已存在,只需增加第9条)
---
### 改进建议 (Minor)
#### S-1: 前端"粗略实现"说明不够具体
**位置**: [doc/tasks.md:74, 76, 78, 85](doc/tasks.md)
**问题描述**:
T-008/T-009/T-011/T-012 都标注了"粗略实现说明",但"粗略"的标准不明确。
**建议**:
在任务总览或关键技术点章节定义"粗略实现"标准:
```markdown
## 前端"粗略实现"标准
本项目前端采用"功能优先、样式从简"的开发策略:
- ✅ **功能完整**: 所有功能可用,交互流程完整
- ✅ **样式简洁**: 使用 Tailwind 默认样式,无需过度美化
- ✅ **品牌元素保留**: Logo、品牌色、品牌声明必须体现
- ❌ **暂不支持**: 响应式适配、动画效果、深度优化
```
---
#### S-2: 建议增加任务估时
**位置**: 整个 tasks.md
**问题描述**:
所有任务都没有工作量估时,无法评估项目整体时间和关键路径。
**建议**:
在任务总览表格增加"估时"列:
```markdown
| ID | 任务 | 描述 | 优先级 | 依赖 | 估时 | 验收标准 |
|----|------|------|--------|------|------|----------|
| T-001 | 项目初始化 | ... | P0 | - | 1天 | ... |
```
**参考估时** (仅供参考):
- T-001: 1天 (前后端分离后: 0.5天 × 2)
- T-002: 1天
- T-005: 2天 (含 TDD)
- T-009: 2天
- T-012: 2天
---
#### S-3: T-016 部署配置缺少监控和日志方案
**位置**: [doc/tasks.md:99](doc/tasks.md:99)
**问题描述**:
T-016 部署配置只涉及 Docker 和环境变量,未涉及生产环境监控和日志收集。
**建议**:
增加验收标准:
```markdown
8. 日志配置: 前端 console 输出,后端使用 Python logging 模块输出到文件
9. (可选) 监控配置: 接入 Sentry 或 Prometheus 进行错误监控
```
---
#### S-4: 任务依赖图与实际任务ID不一致
**位置**: [doc/tasks.md:105](doc/tasks.md:105)
**问题描述**:
第5节"任务依赖图"仍使用 DevelopmentPlan 的任务编号,与 tasks.md 实际任务ID不一致。
**建议修复**:
更新任务依赖图,使用 tasks.md 的任务ID (T-001~T-017):
```
Phase 1: 基础架构
T-001 (项目初始化)
├── T-002 (数据库配置)
├── T-003 (基础UI框架)
└── T-004 (环境变量配置)
Phase 2: 核心功能
T-002 ──▶ T-005 (查询API) ──▶ T-006 (计算逻辑) ──▶ T-009 (结果表格)
│ │ │
└──▶ T-007 (品牌API) │ │
│ │
T-003 ──▶ T-008 (查询表单) │ │
│ │
T-010 (导出API) ◀───────────────┤
│ │
T-011 (导出按钮) ◀──────────────┤
T-008, T-009, T-011 ──▶ T-012 (主页面集成) ────────────┘
Phase 3: 优化测试
T-012 ──▶ T-013 (错误处理) ──▶ T-014 (性能优化)
│ │
├──▶ T-015 (视频链接) │
│ │
└──▶ T-016 (部署配置) │
T-017 (集成测试)
```
---
#### S-5: 建议增加功能ID(F-xxx)对应关系
**位置**: 整个 tasks.md
**建议**:
在"关联功能"列增加功能ID引用,便于追溯需求:
```markdown
| ID | 任务 | 描述 | 优先级 | 依赖 | 关联功能 | 验收标准 |
|----|------|------|--------|------|----------|----------|
| T-005 | 查询 API 开发 | ... | P0 | T-002 | F-001, F-002, F-003 | ... |
| T-006 | 计算逻辑实现 | ... | P0 | T-005 | F-004, F-005, F-006 | ... |
```
---
## 依赖关系分析
### 关键路径
```
T-001 (项目初始化)
├─→ T-002 (数据库配置)
│ │
│ └─→ T-005 (查询API)
│ │
│ ├─→ T-006 (计算逻辑)
│ │ │
│ │ └─→ T-010 (导出API)
│ │
│ └─→ T-007 (品牌API)
│ │
│ └─→ T-009 (结果表格)
│ │
│ └─→ T-012 (主页面集成)
│ │
│ └─→ T-013 (错误处理)
│ │
│ └─→ T-017 (集成测试)
└─→ T-003 (基础UI)
└─→ T-008 (查询表单)
└─→ T-012 (主页面集成)
```
**关键路径**:
T-001 → T-002 → T-005 → T-007 → T-009 → T-012 → T-013 → T-017
**可并行任务**:
- T-002 (数据库) 和 T-003 (基础UI) 可并行
- T-006 (计算逻辑) 和 T-007 (品牌API) 可并行
- T-013/T-014/T-015 可并行
---
## 评审结论
### 评审结果
🟡 **需修改后通过**
---
### 主要优点
**覆盖度完整**:
- 所有 DevelopmentPlan (16个任务) 和 UIDesign (10个组件) 都有对应任务
- 新增 T-012 主页面集成任务是合理补充
**验收标准详细**:
- 每个任务平均 6.2 条验收标准
- 验收标准具体可操作,便于验收
- T-006/T-014/T-017 的验收标准特别优秀
**TDD 要求明确**:
- 7个关键后端任务都要求先写测试再写代码
- 明确要求 100% 测试覆盖率和真实数据库测试
**架构更新到位**:
- 任务描述已完全更新为前后端分离架构 (FastAPI + Next.js)
- 品牌元素(麦秒思AI)在任务中明确体现
---
### 关键问题
**严重问题** (必须修复):
1. **C-1: T-001 粒度过大** - 前后端初始化应拆分,支持并行开发
2. **C-2: T-010 依赖错误** - 后端 API 不应依赖前端组件 T-009
3. **C-3: 缺少测试独立任务** - 100% 覆盖率需要独立验收里程碑
4. **C-4: 任务编号不一致** - Phase 3 任务编号与 DevelopmentPlan 错位
⚠️ **一般问题** (建议修复):
1. **M-1: T-002 数据库环境准备** - 需明确数据库环境前置条件
2. **M-2: T-012 状态管理方案** - 建议使用 React useState
3. **M-3: T-007 参数硬编码** - 并发和超时应可配置
4. **M-4: T-009/T-010 字段一致性** - 建议共享字段定义文件
5. **M-5: T-014 性能测试脚本** - 需编写自动化性能测试
6. **M-6: T-017 性能测试用例** - 集成测试应包含性能验证
---
### 影响评估
**阻塞性问题**:
- 🚫 **C-1 (T-001 粒度过大)**: 导致前后端无法并行开发,延长项目周期
- 🚫 **C-2 (T-010 依赖错误)**: 导致执行顺序混乱,前后端耦合
**质量风险**:
- ⚠️ **C-3 (缺少测试任务)**: 100% 覆盖率难以保证,可能降低代码质量
- ⚠️ **M-5/M-6 (性能测试缺失)**: 性能指标无法自动化验证
**进度风险**:
- ⚠️ **M-1 (数据库环境未就绪)**: 可能导致 T-002 阻塞
- ⚠️ **无任务估时**: 难以评估项目整体进度和关键路径
---
## 下一步行动
### 必须修改 (Critical) - 预估 1.5 小时
- [ ] **C-1: 拆分 T-001** 为 T-001A (前端初始化) 和 T-001B (后端初始化)
- 预估时间: 30分钟
- 影响范围: tasks.md, DevelopmentPlan.md
- [ ] **C-2: 修正 T-010 依赖** 移除 T-009,改为 `T-006, T-007`
- 预估时间: 10分钟
- 影响范围: tasks.md:67
- [ ] **C-3: 增加测试任务** 在 Phase 3 增加 T-018 测试覆盖率验收
- 预估时间: 20分钟
- 影响范围: tasks.md Phase 3
- [ ] **C-4: 统一任务编号** 选择方案A/B/C 修复编号不一致问题
- 预估时间: 30分钟
- 影响范围: tasks.md 或 DevelopmentPlan.md
---
### 建议修改 (Major) - 预估 1 小时
- [ ] **M-1: T-002 数据库环境说明** 明确数据库准备前置条件
- 预估时间: 10分钟
- [ ] **M-2: T-012 状态管理说明** 补充 React useState 方案
- 预估时间: 5分钟
- [ ] **M-3: T-007 参数配置化** 并发和超时改为可配置
- 预估时间: 15分钟
- [ ] **M-4: T-009/T-010 字段一致性** 增加共享字段定义要求
- 预估时间: 15分钟
- [ ] **M-5: T-014 性能测试脚本** 增加性能测试验收标准
- 预估时间: 10分钟
- [ ] **M-6: T-017 性能测试用例** 增加性能测试用例
- 预估时间: 5分钟
---
### 可选优化 (Minor) - 预估 1 小时
- [ ] **S-1: 定义"粗略实现"标准** 增加前端开发标准说明
- [ ] **S-2: 增加任务估时** 为每个任务增加工作量估时(人天)
- [ ] **S-3: T-016 监控配置** 增加日志和监控验收标准
- [ ] **S-4: 更新依赖图** 使用 tasks.md 的实际任务ID
- [ ] **S-5: 增加功能ID** 在关联功能列增加 F-xxx 引用
---
### 修复优先级汇总
| 优先级 | 问题ID | 问题描述 | 预估时间 | 阻塞风险 |
|--------|--------|----------|----------|----------|
| P0 | C-1 | T-001 拆分 | 30分钟 | ⚠️ 高 |
| P0 | C-2 | T-010 依赖修正 | 10分钟 | ⚠️ 高 |
| P0 | C-3 | 增加测试任务 | 20分钟 | ⚠️ 中 |
| P0 | C-4 | 统一任务编号 | 30分钟 | ⚠️ 中 |
| P1 | M-1~M-6 | 6个一般问题 | 60分钟 | ⚠️ 低 |
| P2 | S-1~S-5 | 5个改进建议 | 60分钟 | ✅ 无 |
**预计修复总时间**: 约 3.5 小时 (P0-P2 全部)
---
## 参考信息
### 文档链接
- 目标文档: [doc/tasks.md](doc/tasks.md)
- 上游文档1: [doc/UIDesign.md](doc/UIDesign.md) - UI 设计文档
- 上游文档2: [doc/DevelopmentPlan.md](doc/DevelopmentPlan.md) - 开发计划
### 修改建议操作
建议使用 `/mt` 命令根据本评审报告的问题清单进行增量修改:
```bash
/mt # 增量修改 tasks.md
```
---
**评审人**: Claude Sonnet 4.5
**评审日期**: 2026-01-28 15:30
**评审版本**: tasks.md v1.0
**评审耗时**: 45 分钟
**评审方法**: 基于 `/rt` 评审技能,对比 UIDesign.md 和 DevelopmentPlan.md

339
doc/tasks.md Normal file
View File

@ -0,0 +1,339 @@
# KOL Insight - 任务列表
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | 2026-01-28 |
| 来源文档 | UIDesign.md, DevelopmentPlan.md, FeatureSummary.md, PRD.md |
<!-- NEW START -->
## 架构说明
**前后端分离架构**:
- **前端**: Next.js 14.x + React + TypeScript + Tailwind CSS
- **后端**: Python FastAPI 0.104+ + SQLAlchemy 2.0+ + asyncpg
- **数据库**: PostgreSQL 14.x+
- **部署**: Docker + Uvicorn (ASGI 服务器)
**关键技术点**:
- 后端使用 FastAPI 异步框架,提供 RESTful API
- 前端通过 HTTP 调用后端 API(CORS 配置)
- 数据库使用 SQLAlchemy 异步 ORM
- 外部 API 调用使用 httpx 异步库
<!-- NEW END -->
## 1. 任务总览
<!-- MODIFIED: 更新任务统计,T-001拆分+T-018新增 -->
| 统计项 | 数量 |
|--------|------|
| 总任务数 | 18 |
| P0 任务 | 10 |
| P1 任务 | 7 |
| P2 任务 | 1 |
## 2. Phase 1 任务 - 基础架构搭建
### 2.1 项目初始化与配置
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
<!-- MODIFIED: 拆分为前后端独立任务,支持并行开发 (根据评审报告 C-1) -->
| T-001A | 前端项目初始化 | 创建 Next.js 14.x 项目,配置 TypeScript、ESLint、Prettier | P0 | - | 1. Next.js 14.x 项目创建成功<br>2. TypeScript 配置完成 (tsconfig.json)<br>3. ESLint 配置完成 (.eslintrc.json)<br>4. Prettier 配置完成 (.prettierrc)<br>5. 可运行 `pnpm dev` 启动开发服务器<br>6. 可运行 `pnpm build` 构建生产版本 |
| T-001B | 后端项目初始化 | 创建 FastAPI 0.104+ 项目,配置 Python 依赖管理 | P0 | - | 1. FastAPI 0.104+ 项目创建成功<br>2. Python 依赖管理配置完成 (Poetry 或 requirements.txt)<br>3. 项目结构创建 (app/, tests/, alembic/)<br>4. main.py 应用入口文件创建<br>5. 可运行 `uvicorn main:app --reload` 启动开发服务器<br>6. 访问 http://localhost:8000/docs 可看到 API 文档 |
<!-- MODIFIED: 改为 SQLAlchemy + asyncpg -->
<!-- MODIFIED: 添加真实数据库测试要求和 TDD 要求 -->
<!-- MODIFIED: 依赖改为 T-001B (后端初始化) -->
| T-002 | 数据库配置 | 配置 SQLAlchemy,定义数据模型,连接 PostgreSQL | P0 | T-001B | 1. SQLAlchemy 2.0+ 和 asyncpg 安装完成<br>2. 定义 KolVideo 模型(使用 SQLAlchemy ORM)<br>3. 数据库异步连接成功<br>4. 索引创建: star_id, star_unique_id, star_nickname<br>5. Alembic 迁移工具配置完成<br><!-- NEW START -->6. **真实数据库测试**: 使用 .env 中的连接字符串连接真实数据库并验证<br>7. **TDD要求**: 编写数据库连接测试,模型测试,CRUD测试<br>8. **测试覆盖率**: 数据库操作测试覆盖率 ≥ 100%<!-- NEW END --> |
<!-- MODIFIED: 依赖改为 T-001A (前端初始化) -->
| T-003 | 基础 UI 框架 | 安装 Tailwind CSS,创建基础布局组件 | P0 | T-001A | 1. Tailwind CSS 配置完成<br>2. 品牌色系配置 (#4F46E5等)<br>3. 基础布局组件创建 (Header/Footer)<br>4. 麦秒思AI Logo 集成 (doc/ui/muse.svg) |
<!-- MODIFIED: 依赖改为 T-001A, T-001B (前后端都需要环境变量) -->
| T-004 | 环境变量配置 | 配置开发/生产环境变量,数据库连接字符串 | P0 | T-001A, T-001B | 1. 前后端 .env.example 创建<br>2. 后端 DATABASE_URL 配置<br>3. 后端品牌 API 地址配置<br>4. 前端 NEXT_PUBLIC_API_URL 配置<br>5. .env 文件创建并添加到 .gitignore |
## 3. Phase 2 任务 - 核心功能开发
### 3.1 后端 API 开发
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
<!-- MODIFIED: 改为 FastAPI 实现 -->
<!-- MODIFIED: 增加 TDD 要求和测试覆盖率 -->
| T-005 | 查询 API 开发 | 实现 POST /api/v1/query 接口(FastAPI),支持三种查询方式 | P0 | T-002 | 1. FastAPI POST /api/v1/query 路由实现<br>2. Pydantic 模型验证请求参数(type: star_id/unique_id/nickname)<br>3. 星图ID: 使用 SQLAlchemy 异步查询 WHERE star_id IN (...)<br>4. 达人ID: WHERE star_unique_id IN (...)<br>5. 昵称: WHERE star_nickname LIKE '%...%'<br>6. 返回完整视频数据列表(JSON 格式)<br>7. 限制单次查询最大 1000 条<br>8. CORS 配置支持前端跨域请求<br><!-- NEW START -->9. **TDD要求**: 先编写API测试用例(三种查询方式),再实现路由<br>10. **测试覆盖率**: API集成测试覆盖率 ≥ 100% (包含成功/失败/边界场景)<!-- NEW END --> |
<!-- MODIFIED: 改为 Python 后端实现 -->
<!-- MODIFIED: 增加 TDD 要求和测试覆盖率 -->
| T-006 | 计算逻辑实现 | 在 Python 后端实现 CPM、看后搜人数、成本计算 | P0 | T-005 | 1. 预估自然CPM = estimated_video_cost / natural_play_cnt * 1000<br>2. 预估自然看后搜人数 = natural_play_cnt / total_play_cnt * after_view_search_uv<br>3. 预估看后搜人数成本 = estimated_video_cost / 预估自然看后搜人数<br>4. 除零检查,返回 None<br>5. 结果保留 2 位小数(使用 round())<br>6. 批量计算使用列表推导式或 map()<br><!-- NEW START -->7. **TDD要求**: 先编写计算函数的单元测试(包含除零场景),再实现函数<br>8. **测试覆盖率**: 计算逻辑单元测试覆盖率 ≥ 100% (所有分支覆盖)<!-- NEW END --> |
<!-- MODIFIED: 明确使用 httpx 异步库,去除可选项 -->
<!-- MODIFIED: 删除"可选"验收标准,增加 TDD 要求和测试覆盖率 -->
| T-007 | 品牌 API 批量集成 | 后端使用 httpx 批量调用品牌API获取品牌名称,支持并发控制和降级 | P0 | T-005 | 1. 使用 httpx.AsyncClient 实现 GET /v1/yuntu/brands/{brand_id} 调用<br>2. 从查询结果提取唯一 brand_id(去重)<br>3. 使用 asyncio.gather 批量并发请求(限制 10 并发)<br>4. 构建 brand_id → brand_name 映射字典<br>5. 单个 API 调用失败时降级显示 brand_id<br>6. 超时设置: 3秒<br>7. 错误日志记录<br><!-- NEW START -->8. **TDD要求**: 先编写测试用例(模拟API响应),再实现功能<br>9. **测试覆盖率**: 单元测试覆盖率 ≥ 100% (包含成功/失败/超时/并发场景)<!-- NEW END --> |
<!-- MODIFIED: 改为 FastAPI + Python 导出库实现 -->
<!-- MODIFIED: 修正依赖关系,移除 T-009 前端组件依赖 (根据评审报告 C-2) -->
| T-010 | 导出 API 开发 | 实现 GET /api/v1/export 接口(FastAPI),生成 Excel/CSV | P1 | T-006, T-007 | 1. FastAPI GET /api/v1/export 路由实现<br>2. 支持 format=xlsx/csv 查询参数<br>3. 使用 openpyxl 或 xlsxwriter 库生成 Excel<br>4. CSV 使用 Python csv 模块,处理逗号转义<br>5. 使用中文列名作为表头 **(字段顺序和命名需与前端 ResultTable 保持一致)**<br>6. 文件名: kol_data_{timestamp}.xlsx<br>7. 响应头设置 Content-Disposition: attachment<br>8. 使用 StreamingResponse 返回文件<br>9. 限制单次导出最大 1000 条<br><!-- NEW START -->10. **TDD要求**: 先编写测试用例(生成文件验证),再实现功能<br>11. **测试覆盖率**: 单元测试覆盖率 ≥ 100% (包含 Excel/CSV 生成,字段一致性验证)<!-- NEW END --> |
### 3.2 前端组件开发
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
<!-- MODIFIED: 简化前端实现要求 -->
| T-008 | 查询表单组件 | 开发查询输入表单,支持方式切换 **(前端粗略实现)** | P0 | T-003 | 1. QueryForm 组件创建<br>2. Radio 单选器: 星图ID/达人unique_id/达人昵称<br>3. Textarea 输入框,支持批量输入<br>4. 根据查询方式动态更新输入提示<br>5. 输入验证 (实时)<br>6. 清空和提交按钮<br>7. 加载态: 按钮禁用,显示 Loading<br><!-- NEW START -->8. **粗略实现说明**: 基础表单功能可用,样式简洁即可<!-- NEW END --> |
<!-- MODIFIED: 明确分页器组件,简化前端实现要求 -->
| T-009 | 结果表格组件 | 开发数据展示表格,显示 26 个字段 **(前端粗略实现)** | P1 | T-003, T-006, T-007 | 1. ResultTable 组件创建<br>2. 展示 26 个字段,使用中文列名<br>3. 表格列宽按 UIDesign 规范<br>4. 数字格式化: 千分位分隔<br>5. 大数值使用 K/M 缩写<br>6. 空值显示 "-"<br>7. 支持横向滚动<br>8. 支持列排序<br>9. **包含分页器组件**: 每页 20 条,支持翻页<br>10. 品牌名称由后端返回,直接展示<br><!-- NEW START -->11. **粗略实现说明**: 基础功能可用即可,样式可简化<!-- NEW END --> |
<!-- MODIFIED: 简化前端实现要求 -->
| T-011 | 导出按钮组件 | 开发导出按钮,触发文件下载 **(前端粗略实现)** | P1 | T-009, T-010 | 1. ExportButton 组件创建<br>2. 导出 Excel 按钮<br>3. 导出 CSV 按钮<br>4. 点击触发 /api/export 调用<br>5. 浏览器下载文件<br>6. 无数据时提示 "无数据可导出"<br>7. 导出中显示 Loading<br><!-- NEW START -->8. **粗略实现说明**: 基础导出功能可用即可<!-- NEW END --> |
### 3.3 页面集成
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
<!-- MODIFIED: 简化前端实现要求 -->
<!-- MODIFIED: 任务编号改为 T-011A,统一与 DevelopmentPlan 编号体系 (根据评审报告 C-4) -->
| T-011A | 主页面集成 | 集成查询表单、结果表格和导出按钮,完成单页应用 **(前端粗略实现)** | P0 | T-008, T-009, T-011 | 1. page.tsx 创建单页应用<br>2. 品牌头部: Logo + "KOL Insight" + "麦秒思AI制作"<br>3. 查询区域集成 QueryForm<br>4. 结果区域集成 ResultTable 和 ExportButton<br>5. Footer: "© 2026 麦秒思AI制作"<br>6. 页面状态管理: 默认态/输入态/查询中/结果态/空结果态/错误态<br>7. 空状态组件: 引导文案 + 空盒子图标<br>8. 错误状态组件: 错误提示 + 重试按钮<br><!-- NEW START -->9. **粗略实现说明**: 重点在功能集成,UI可简化,品牌元素必须保留<!-- NEW END --> |
## 4. Phase 3 任务 - 优化与测试
### 4.1 错误处理与优化
| ID | 任务 | 描述 | 优先级 | 依赖 | 验收标准 |
|----|------|------|--------|------|----------|
<!-- MODIFIED: 增加后端 TDD 要求和测试覆盖率 -->
<!-- MODIFIED: 依赖改为 T-011A -->
| T-013 | 错误处理 | 完善错误处理,添加用户友好提示 | P1 | T-011A | 1. API 错误处理: try-catch 包裹<br>2. 数据库连接失败提示<br>3. 品牌 API 失败降级处理<br>4. 网络超时提示<br>5. 输入验证错误提示<br>6. 空结果友好提示<br>7. 错误日志记录 (后端)<br><!-- NEW START -->8. **后端TDD要求**: 编写异常场景测试用例,验证错误处理逻辑<br>9. **后端测试覆盖率**: 错误处理分支覆盖率 ≥ 100%<br>10. **前端粗略实现**: 基础错误提示可用即可<!-- NEW END --> |
<!-- MODIFIED: 增加后端性能测试要求 -->
| T-014 | 性能优化 | 数据库索引优化,查询性能调优 | P1 | T-013 | 1. 验证索引已创建: star_id, star_unique_id, star_nickname<br>2. 查询响应时间 ≤ 3秒 (100条)<br>3. 页面加载时间 ≤ 2秒<br>4. 导出响应时间 ≤ 5秒 (1000条)<br>5. 品牌 API 并发控制 (限制 10 并发)<br><!-- NEW START -->6. **后端性能测试**: 编写性能测试脚本,验证响应时间指标<br>7. **真实数据库测试**: 使用真实数据库进行性能测试<!-- NEW END --> |
| T-015 | 视频链接跳转 | 实现视频链接点击跳转功能 | P2 | T-009 | 1. 视频链接列展示为链接按钮<br>2. 使用 `<a target="_blank" rel="noopener noreferrer">`<br>3. 链接为空时显示 "-"<br>4. 新窗口打开视频页面 |
<!-- MODIFIED: 改为前后端分离的 Docker 配置 -->
| T-016 | 部署配置 | Docker 配置前后端分离部署 | P1 | T-013 | 1. 前端 Dockerfile 创建(Node.js + Next.js)<br>2. 后端 Dockerfile 创建(Python + FastAPI + Uvicorn)<br>3. docker-compose.yml 配置前端、后端、PostgreSQL<br>4. 前端构建: pnpm build<br>5. 后端启动: uvicorn main:app --host 0.0.0.0 --port 8000<br>6. 生产环境可访问<br>7. 环境变量配置(.env) |
<!-- MODIFIED: 增加真实数据库集成测试和100%覆盖率要求 -->
| T-017 | 集成测试 | 端到端功能测试 | P1 | T-013 | 1. 测试用例: 星图ID精准查询<br>2. 测试用例: 达人ID批量查询<br>3. 测试用例: 昵称模糊查询<br>4. 测试用例: 计算指标准确性<br>5. 测试用例: 品牌名称获取<br>6. 测试用例: 数据导出 (Excel/CSV)<br>7. 测试用例: 错误处理 (网络异常/空结果)<br>8. 所有 P0/P1 功能测试通过<br><!-- NEW START -->9. **真实数据库集成测试**: 使用 .env 中的真实数据库连接进行完整集成测试<!-- NEW END --> |
<!-- NEW START: 根据评审报告 C-3 增加独立测试覆盖率验收任务 -->
| T-018 | 测试覆盖率验收 | 验证所有后端代码测试覆盖率 ≥ 100% | P1 | T-002, T-005, T-006, T-007, T-010, T-013, T-017 | 1. 数据库操作测试覆盖率 100% (T-002)<br>2. API集成测试覆盖率 100% (T-005)<br>3. 计算逻辑单元测试覆盖率 100% (T-006)<br>4. 品牌API单元测试覆盖率 100% (T-007)<br>5. 导出功能单元测试覆盖率 100% (T-010)<br>6. 错误处理分支覆盖率 100% (T-013)<br>7. 使用 pytest-cov 生成覆盖率报告<br>8. 覆盖率报告保存到 coverage/ 目录<br>9. CI/CD 集成: 覆盖率未达标时构建失败 |
<!-- NEW END -->
## 5. 任务依赖图
<!-- MODIFIED: 更新任务依赖图,T-001拆分,T-012改为T-011A,新增T-018 -->
```
Phase 1: 基础架构
T-001A (前端初始化) T-001B (后端初始化)
│ │
├── T-003 (基础UI) ├── T-002 (数据库配置)
│ │
└────────┬─────────────┘
└── T-004 (环境变量配置)
Phase 2: 核心功能
T-002 ──▶ T-005 (查询API) ──▶ T-006 (计算逻辑) ──▶ T-009 (结果表格)
│ │ │
└──▶ T-007 (品牌API) │ │
│ │
T-003 ──▶ T-008 (查询表单) │ │
│ │
T-010 (导出API) ◀───────────────┤
│ │
T-011 (导出按钮) ◀──────────────┤
T-008, T-009, T-011 ──▶ T-011A (主页面集成) ───────────┘
Phase 3: 优化测试
T-011A ──▶ T-013 (错误处理) ──▶ T-014 (性能优化)
│ │
├──▶ T-015 (视频链接) │
│ │
├──▶ T-016 (部署配置) │
│ │
└──▶ T-017 (集成测试) ─┤
│ │
└──▶ T-018 (测试覆盖率验收)
```
## 6. 执行检查清单
<!-- MODIFIED: 更新任务编号,T-001拆分,T-012改为T-011A,新增T-018 -->
### Phase 1 - 基础架构搭建
- [ ] T-001A: 前端项目初始化
- [ ] T-001B: 后端项目初始化
- [ ] T-002: 数据库配置
- [ ] T-003: 基础 UI 框架
- [ ] T-004: 环境变量配置
### Phase 2 - 核心功能开发
- [ ] T-005: 查询 API 开发
- [ ] T-006: 计算逻辑实现
- [ ] T-007: 品牌 API 批量集成
- [ ] T-008: 查询表单组件
- [ ] T-009: 结果表格组件
- [ ] T-010: 导出 API 开发
- [ ] T-011: 导出按钮组件
- [ ] T-011A: 主页面集成
### Phase 3 - 优化与测试
- [ ] T-013: 错误处理
- [ ] T-014: 性能优化
- [ ] T-015: 视频链接跳转
- [ ] T-016: 部署配置
- [ ] T-017: 集成测试
- [ ] T-018: 测试覆盖率验收
## 7. 里程碑与交付物
<!-- MODIFIED: 更新任务编号范围 -->
| 里程碑 | 包含任务 | 交付物 | 预期验收 |
|--------|----------|--------|----------|
| M1: 基础架构完成 | T-001A, T-001B, T-002~T-004 | 前后端项目骨架、数据库连接、基础UI | 前后端项目可运行,数据库可连接 |
| M2: 核心功能完成 | T-005~T-011A | 查询、计算、展示、导出功能 | 所有 P0 功能可用,品牌API集成 |
| M3: 优化测试完成 | T-013~T-018 | 错误处理、性能优化、部署配置、测试覆盖率 | 测试通过,性能达标,100%覆盖率,可部署 |
| M4: 正式上线 | - | 生产环境部署 | 生产环境可访问,稳定运行 |
## 8. 优先级说明
<!-- MODIFIED: 更新任务编号和数量 -->
**P0 任务 (10个)** - MVP 必须完成
- 基础架构: T-001A (前端初始化), T-001B (后端初始化), T-002 (数据库), T-003 (基础UI), T-004 (环境变量)
- 核心功能: T-005 (查询API), T-006 (计算逻辑), T-007 (品牌API), T-008 (查询表单), T-011A (主页面集成)
**P1 任务 (7个)** - 重要功能
- 展示导出: T-009 (结果表格), T-010 (导出API), T-011 (导出按钮)
- 优化测试: T-013 (错误处理), T-014 (性能优化), T-016 (部署配置), T-017 (集成测试), T-018 (测试覆盖率验收)
**P2 任务 (1个)** - 次要功能
- 增强体验: T-015 (视频链接跳转)
## 9. 关键技术点
<!-- MODIFIED: 更新为 FastAPI + SQLAlchemy 技术栈 -->
<!-- MODIFIED: T-001 拆分为 T-001A 和 T-001B -->
| 任务 | 关键技术 | 注意事项 |
|------|----------|----------|
| T-001A | Next.js 14.x 项目初始化 | TypeScript + ESLint + Prettier,App Router 模式 |
| T-001B | FastAPI 0.104+ 项目初始化 | Poetry/pip 依赖管理,项目结构规划 |
| T-002 | SQLAlchemy + asyncpg | 必须创建索引: star_id, star_unique_id, star_nickname,使用异步 ORM |
| T-005 | FastAPI + Pydantic | 使用 IN 查询批量匹配,LIKE 模糊匹配,限制最大 1000 条,CORS 配置 |
| T-006 | Python 计算 | 除零检查,结果保留 2 位小数,None 值处理 |
| T-007 | httpx + asyncio | 批量并发调用 (限制 10 并发),超时 3 秒,降级处理,使用 asyncio.gather |
| T-009 | React 表格组件 | 26 个字段展示,数字格式化,横向滚动,分页 |
| T-010 | openpyxl/xlsxwriter | Python 库生成 Excel,CSV 逗号转义,中文列名,StreamingResponse |
| T-011A | React 状态管理 | 6 种页面状态: 默认/输入/查询中/结果/空结果/错误 |
| T-013 | FastAPI 错误处理 | API 错误、数据库连接失败、品牌API降级、网络超时 |
| T-014 | 性能优化 | 查询 ≤3秒,页面加载 ≤2秒,导出 ≤5秒,并发控制 |
| T-016 | Docker + Uvicorn | 前后端分离部署,Uvicorn ASGI 服务器 |
| T-018 | pytest-cov | 测试覆盖率验收,确保所有后端代码 ≥ 100% 覆盖率 |
<!-- MODIFIED: 改为 SQLAlchemy 模型定义 -->
## 10. 数据库 Schema 参考
```python
# SQLAlchemy 模型定义
from sqlalchemy import Column, String, Integer, Float, DateTime, Index
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class KolVideo(Base):
__tablename__ = "kol_videos"
# 主键
item_id = Column(String, primary_key=True)
# 基本信息
title = Column(String, nullable=True)
viral_type = Column(String, nullable=True)
video_url = Column(String, nullable=True)
star_id = Column(String, nullable=False)
star_unique_id = Column(String, nullable=False)
star_nickname = Column(String, nullable=False)
publish_time = Column(DateTime, nullable=True)
# 曝光指标
natural_play_cnt = Column(Integer, default=0)
heated_play_cnt = Column(Integer, default=0)
total_play_cnt = Column(Integer, default=0)
# 互动指标
total_interact = Column(Integer, default=0)
like_cnt = Column(Integer, default=0)
share_cnt = Column(Integer, default=0)
comment_cnt = Column(Integer, default=0)
# 效果指标
new_a3_rate = Column(Float, nullable=True)
after_view_search_uv = Column(Integer, default=0)
return_search_cnt = Column(Integer, default=0)
# 商业信息
industry_id = Column(String, nullable=True)
industry_name = Column(String, nullable=True)
brand_id = Column(String, nullable=True)
estimated_video_cost = Column(Float, default=0)
# 索引
__table_args__ = (
Index('idx_star_id', 'star_id'),
Index('idx_star_unique_id', 'star_unique_id'),
Index('idx_star_nickname', 'star_nickname'),
)
```
<!-- MODIFIED: 改为 FastAPI 格式 -->
## 11. API 接口参考
### POST /api/v1/query
```python
# FastAPI 路由定义
from pydantic import BaseModel
from typing import List, Literal
class QueryRequest(BaseModel):
type: Literal["star_id", "unique_id", "nickname"]
values: List[str]
class QueryResponse(BaseModel):
success: bool
data: List[dict] # 视频数据列表 (含 26 个字段)
total: int
# 请求示例
{
"type": "star_id",
"values": ["id1", "id2", ...]
}
# 响应示例
{
"success": true,
"data": [...],
"total": 100
}
```
### GET /api/v1/export
```python
# FastAPI 路由
@app.get("/api/v1/export")
async def export_data(format: Literal["xlsx", "csv"] = "xlsx"):
# 返回 StreamingResponse
pass
# 请求: GET /api/v1/export?format=xlsx
# 响应: 文件下载 (Content-Disposition: attachment)
```
### 外部 API: GET /v1/yuntu/brands/{brand_id}
```python
# 使用 httpx.AsyncClient 调用
import httpx
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.internal.intelligrow.cn/v1/yuntu/brands/{brand_id}",
timeout=3.0
)
# 失败时降级显示 brand_id
```
---
**文档状态**: 待执行
**建议下一步**: 按顺序执行 Phase 1 任务,完成基础架构搭建
**评审建议**: 可运行 `/rt` 对任务列表进行评审

93
doc/ui/muse.svg Normal file
View File

@ -0,0 +1,93 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" style="display: block;" viewBox="0 0 2048 887" width="1000" height="433">
<defs>
<linearGradient id="Gradient1" gradientUnits="userSpaceOnUse" x1="210.508" y1="105.269" x2="586.128" y2="792.383">
<stop class="stop0" offset="0" stop-opacity="1" stop-color="rgb(228,24,73)"/>
<stop class="stop1" offset="1" stop-opacity="1" stop-color="rgb(253,56,71)"/>
</linearGradient>
</defs>
<path transform="translate(0,0)" fill="url(#Gradient1)" d="M 376.146 54.9048 C 394.494 52.0082 450.721 54.6372 467.605 60.9459 C 476.746 62.3192 486.198 63.3892 495.173 65.5997 C 515.789 70.6779 564.525 86.5296 576.81 103.019 C 582.859 111.139 585.986 121.422 584.272 131.496 C 582.52 141.792 577.15 152.234 568.387 158.191 C 559.717 164.085 547.095 166.763 536.819 164.74 C 522.557 161.932 508.608 154.389 494.635 150.13 C 458.749 139.194 420.04 135.901 382.699 137.271 L 382.087 137.409 C 377.052 138.507 371.716 138.434 366.594 139.112 C 353.633 140.827 340.292 142.838 327.621 146.09 C 253.423 165.138 184.133 214.131 142.088 278.203 C 136.733 286.364 128.204 304.126 123.434 309.932 C 111.073 334.935 104.381 360.26 95.8454 386.549 L 95.7672 387.361 C 94.7432 397.501 91.6381 407.384 90.5873 417.508 C 89.7119 425.942 89.9836 434.794 89.8723 443.272 C 89.4501 475.416 91.5586 497.422 98.6317 528.874 C 100.45 536.927 102.824 544.844 105.737 552.569 C 107.655 557.772 110.601 563.483 111.543 568.893 C 117.587 584.712 125.65 599.216 133.531 614.15 C 143.94 630.512 155.472 646.586 168.527 660.964 C 173.349 666.275 188.564 678.675 191.256 683.236 C 251.557 733.221 311.796 756.819 390.291 761.391 C 394.05 760.783 398.822 761.571 402.708 761.517 C 415.486 761.341 428.664 761.316 441.345 759.599 C 485.651 753.598 530.394 737.673 568.043 713.473 C 575.81 708.481 583.428 703.049 590.788 697.474 C 594.071 694.988 596.979 692.019 600.533 689.912 L 601.084 689.592 C 616.553 677.289 631.234 662.355 643.509 646.888 C 644.812 642.001 654.107 632.73 657.41 628.311 C 676.308 596.768 687.36 578.365 698.997 542.562 C 702.091 535.055 703.233 526.973 705.666 519.255 C 705.725 507.623 709.936 495.566 711.264 483.918 C 713.169 467.214 712.726 450.024 712.977 433.224 L 712.92 431.802 C 711.531 426.337 711.557 419.947 710.818 414.282 C 709.582 404.813 707.787 395.546 705.774 386.213 C 700.989 351.447 677.646 305.278 659.181 275.386 C 653.455 266.116 644.933 257.388 640.642 247.468 C 637.876 241.072 637.34 233.314 637.61 226.417 C 637.996 216.532 640.733 207.138 648.335 200.309 C 656.928 192.59 671.867 189.783 683.178 190.458 C 692.932 191.041 701.105 194.806 707.559 202.061 L 714.51 211.387 C 737.619 243.545 757.234 275.572 771.319 312.816 C 774.839 322.124 777.095 331.789 780.403 341.131 C 797.083 399.915 800.154 457.173 790.602 517.54 C 788.234 532.501 785.885 549.101 779.732 563.007 C 768.839 611.398 725.068 690.424 687.592 724.592 C 658.332 752.202 618.553 787.704 581.018 802.842 C 562.995 813.263 542.695 820.874 522.909 827.195 C 447.846 851.214 367.429 852.958 291.395 832.215 C 275.181 827.889 259.267 822.031 243.62 816.005 C 220.843 805.593 199.356 794.344 178.44 780.521 C 91.1047 722.799 33.0984 630.627 12.3333 528.81 C 11.7547 526.892 11.3367 524.926 10.9201 522.968 C 6.97496 504.429 5.54503 484.947 4.99442 466.026 C 2.49649 384.006 25.2737 303.201 70.2396 234.56 C 110.525 174.202 165.964 125.49 231.002 93.3037 C 244.395 86.5531 259.95 78.4536 274.682 75.364 C 288.042 70.2143 301.587 66.3748 315.565 63.2966 C 322.289 61.7913 329.104 59.9829 335.973 59.355 C 347.591 55.8138 364.002 55.0317 376.146 54.9048 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 705.774 386.213 C 700.989 351.447 677.646 305.278 659.181 275.386 C 653.455 266.116 644.933 257.388 640.642 247.468 C 637.876 241.072 637.34 233.314 637.61 226.417 C 637.996 216.532 640.733 207.138 648.335 200.309 C 656.928 192.59 671.867 189.783 683.178 190.458 C 692.932 191.041 701.105 194.806 707.559 202.061 L 714.51 211.387 C 737.619 243.545 757.234 275.572 771.319 312.816 C 774.839 322.124 777.095 331.789 780.403 341.131 C 797.083 399.915 800.154 457.173 790.602 517.54 C 788.234 532.501 785.885 549.101 779.732 563.007 C 768.839 611.398 725.068 690.424 687.592 724.592 C 658.332 752.202 618.553 787.704 581.018 802.842 C 562.995 813.263 542.695 820.874 522.909 827.195 C 447.846 851.214 367.429 852.958 291.395 832.215 C 275.181 827.889 259.267 822.031 243.62 816.005 C 220.843 805.593 199.356 794.344 178.44 780.521 C 91.1047 722.799 33.0984 630.627 12.3333 528.81 L 13.647 527.729 C 14.402 529.837 15.0932 534.929 16.7981 535.785 C 17.5141 533.357 15.8765 529.94 15.0764 527.549 L 15.9 527.418 C 18.6345 534.095 20.0034 541.498 21.9989 548.453 C 26.9283 565.633 31.6172 583.045 38.2114 599.677 C 64.9422 667.101 116.207 732.766 177.003 772.666 C 189.614 780.943 202.698 789.795 216.058 796.759 C 224.712 801.271 234.424 804.33 242.789 809.289 C 250.667 812.637 258.854 815.525 266.905 818.433 C 349.42 848.238 444.177 848.559 527.411 820.818 C 545.994 814.624 563.998 805.904 581.717 797.604 C 603.127 783.645 625.24 771.395 645.477 755.591 C 658.557 745.377 670.301 733.195 682.386 721.844 C 728.545 674.13 761.29 615.077 777.312 550.652 C 797.154 486.362 792.669 418.058 778.475 353.134 C 767.793 317.922 754.532 281.526 735.293 249.969 C 728.138 238.235 719.461 227.302 711.837 215.843 C 708.68 211.913 705.464 208.03 702.19 204.197 C 693.73 197.534 684.075 194.889 673.357 196.197 C 665.575 197.147 657.47 200.047 650.684 203.95 C 644.436 214.061 640.809 227.514 643.576 239.394 C 646.334 251.238 671.063 283.842 678.471 297.084 C 693.576 324.081 703.652 353.558 710.477 383.619 C 713.023 393.568 715.018 403.747 715.532 414.027 L 717.071 423.777 C 720.72 442.779 716.413 511.912 708.485 528.149 C 706.525 537.703 703.771 546.858 700.269 555.961 C 697.102 566.785 686.883 593.093 679.438 600.774 C 684.103 591.158 688.576 581.632 692.348 571.622 C 685.985 582.691 667.071 624.486 657.41 628.311 C 676.308 596.768 687.36 578.365 698.997 542.562 C 702.091 535.055 703.233 526.973 705.666 519.255 C 705.725 507.623 709.936 495.566 711.264 483.918 C 713.169 467.214 712.726 450.024 712.977 433.224 L 712.92 431.802 C 711.531 426.337 711.557 419.947 710.818 414.282 C 709.582 404.813 707.787 395.546 705.774 386.213 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 89.8436 390.816 C 92.8902 370.901 99.7863 351.425 106.984 332.654 C 110.049 324.66 121.333 295.736 127.989 292.773 L 129.293 294.957 C 125.28 299.606 124.047 303.884 123.434 309.932 C 111.073 334.935 104.381 360.26 95.8454 386.549 L 95.7672 387.361 C 94.7432 397.501 91.6381 407.384 90.5873 417.508 C 89.7119 425.942 89.9836 434.794 89.8723 443.272 C 89.4501 475.416 91.5586 497.422 98.6317 528.874 C 100.45 536.927 102.824 544.844 105.737 552.569 C 107.655 557.772 110.601 563.483 111.543 568.893 C 117.587 584.712 125.65 599.216 133.531 614.15 C 129.444 612.078 127.432 605.723 123.651 602.599 C 123.042 605.188 129.828 614.104 130.748 617.464 C 119.822 604.665 113.472 587.447 107.114 572.034 C 106.864 571.666 106.835 571.641 106.596 571.18 C 84.8375 529.334 79.3733 455.273 86.371 409.569 C 87.2906 403.563 87.7619 396.511 89.8436 390.816 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 467.605 60.9459 C 476.746 62.3192 486.198 63.3892 495.173 65.5997 C 515.789 70.6779 564.525 86.5296 576.81 103.019 C 582.859 111.139 585.986 121.422 584.272 131.496 C 582.52 141.792 577.15 152.234 568.387 158.191 C 559.717 164.085 547.095 166.763 536.819 164.74 C 522.557 161.932 508.608 154.389 494.635 150.13 C 458.749 139.194 420.04 135.901 382.699 137.271 C 401.38 126.952 478.879 143.57 501.374 150.331 C 513.555 153.993 525.503 160.481 537.856 163.083 C 547.196 165.051 558.991 161.578 566.89 156.404 C 575.585 150.709 579.423 143.326 581.522 133.4 C 583.575 123.696 581.557 112.369 575.994 104.046 C 573.49 100.3 570.487 98.0368 566.074 97.1358 L 565.314 97.7949 C 569.437 99.0598 571.952 101.348 574.235 104.996 C 580.205 114.537 580.949 124.563 578.348 135.333 C 576.615 142.508 573.838 150.994 567.234 155.03 L 566.34 154.256 C 566.994 151.676 568.333 150.62 570.467 149.029 C 570.722 148.838 570.982 148.652 571.227 148.448 C 575.662 144.773 577.981 132.345 578.447 126.705 C 579.116 118.618 577.833 111.784 572.383 105.569 C 558.678 89.9432 495.022 68.0075 474.068 66.5128 C 471.389 64.8806 469.466 63.4925 467.605 60.9459 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 191.256 683.236 C 251.557 733.221 311.796 756.819 390.291 761.391 C 384.048 764.932 370.279 761.634 362.687 762.291 C 361.888 762.36 362.274 762.212 361.617 762.991 C 367.962 764.537 376.21 766.043 382.625 764.548 C 382.899 764.484 383.171 764.412 383.444 764.344 L 384.048 765.039 C 383.31 765.409 382.77 765.691 381.943 765.815 C 373.428 767.101 362.837 765.155 354.356 763.843 C 317.586 758.155 264.109 743.033 234.667 719.807 L 241.003 722.723 C 233.991 716.274 224.813 711.666 216.97 706.228 C 211.114 702.167 192.413 689.79 191.256 683.236 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 376.146 54.9048 C 394.494 52.0082 450.721 54.6372 467.605 60.9459 C 469.466 63.4925 471.389 64.8806 474.068 66.5128 C 462.177 64.3163 450.083 61.7173 438.022 60.8079 C 415.361 59.0993 392.468 60.3754 369.865 58.5625 C 373.126 56.6957 426.662 57.683 431.679 59.027 C 432.503 59.2478 433.394 58.6279 434.177 58.2957 C 425.546 51.9762 387.515 60.0202 376.146 54.9048 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 315.565 63.2966 C 322.289 61.7913 329.104 59.9829 335.973 59.355 L 335.213 61.0976 C 331.824 61.9908 328.07 62.6586 325.211 64.7655 L 330.836 63.1888 C 323.47 66.8573 315.681 68.5767 307.736 70.4894 C 295.905 73.9845 287.012 75.9701 274.682 75.364 C 288.042 70.2143 301.587 66.3748 315.565 63.2966 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 601.084 689.592 C 616.553 677.289 631.234 662.355 643.509 646.888 C 642.944 649.312 642.433 651.632 641.348 653.888 C 642.715 652.59 643.982 651.396 645.593 650.391 C 643.168 657.971 615.843 685.786 608.409 689.944 C 607.817 690.275 607.352 690.377 606.696 690.521 L 610.446 685.035 C 607.305 687.498 605.104 689.098 601.084 689.592 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1610.11 53.6333 C 1610.18 47.1911 1610.04 40.7484 1609.67 34.3162 C 1612.92 32.1096 1622.54 32.521 1626.83 32.421 C 1701.22 30.6842 1776.03 32.2737 1850.46 32.2788 L 1942.68 32.293 C 1961.78 32.2982 1981.13 31.6609 2000.17 33.0634 C 2002.29 59.7016 2000.77 87.3161 2000.5 114.078 C 2000.18 145.397 2000.71 176.761 2000.65 208.089 L 2000.53 275.627 C 2000.59 287.904 2001.86 301.31 2000.39 313.448 C 1999.08 314.796 1998.19 315.254 1996.36 315.657 C 1983.81 313.542 1969.62 314.801 1956.84 314.86 L 1885.1 315.187 C 1843.56 314.96 1802.02 314.957 1760.49 315.178 C 1737.73 315.239 1714.92 314.515 1692.21 316.146 C 1682.97 316.356 1673.71 316.127 1664.47 316.03 C 1647.62 319.103 1627.49 316.171 1610.18 316.817 C 1609.42 279.846 1609.91 242.781 1609.91 205.802 L 1610.11 53.6333 z M 1931 253.687 C 1934.04 247.761 1931.57 214.273 1932.58 204.615 C 1914.48 203.829 1896.3 204.085 1878.18 204.045 C 1864.82 204.016 1851.24 203.501 1837.9 204.271 C 1837.75 219.838 1836.4 236.461 1838.1 251.9 C 1840.18 253.488 1842.92 253.076 1845.51 253.213 L 1901.24 253.025 C 1910.95 253.041 1921.4 252.368 1931 253.687 z M 1770.88 142.098 C 1771.08 126.574 1770.3 110.547 1772.02 95.1281 C 1753 96.0183 1693.67 98.7431 1678.07 94.8681 C 1678.09 111.235 1678.34 127.63 1678.14 143.994 C 1708.74 143.936 1739.89 145.247 1770.43 143.809 L 1770.88 142.098 z M 1934.72 92.3052 C 1911.33 88.9068 1886.2 90.9728 1862.62 91.3784 C 1853.2 91.5404 1843.25 90.4708 1833.95 92.0614 C 1833.84 102.176 1831.75 140.452 1835.62 146.951 C 1843.68 147.053 1854.75 146.124 1862.56 144.157 C 1885.64 143.39 1908.85 144.079 1931.95 143.907 C 1931.95 138.803 1932.1 133.689 1932.2 128.585 C 1935.08 117.315 1933.3 104.041 1934.72 92.3052 z M 1677.37 253.007 C 1708.52 253.373 1739.72 253.078 1770.88 253.105 L 1771.3 225.345 C 1771.16 218.243 1771.08 211.133 1770.85 204.034 C 1759.02 205.292 1746.28 204.266 1734.35 204.189 C 1715.7 204.069 1697.03 204.4 1678.38 204.082 C 1678.04 215.578 1680.36 244.069 1677.37 253.007 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1610.11 53.6333 C 1610.18 47.1911 1610.04 40.7484 1609.67 34.3162 C 1612.92 32.1096 1622.54 32.521 1626.83 32.421 C 1701.22 30.6842 1776.03 32.2737 1850.46 32.2788 L 1942.68 32.293 C 1961.78 32.2982 1981.13 31.6609 2000.17 33.0634 C 2002.29 59.7016 2000.77 87.3161 2000.5 114.078 C 2000.18 145.397 2000.71 176.761 2000.65 208.089 L 2000.53 275.627 C 2000.59 287.904 2001.86 301.31 2000.39 313.448 C 1999.08 314.796 1998.19 315.254 1996.36 315.657 C 1993.64 303.906 1995.65 250.184 1995.69 234.585 C 1995.92 168.649 1995.78 102.714 1995.29 36.7799 C 1925.27 37.7276 1855.17 36.83 1785.15 36.817 C 1728.68 36.8065 1672.04 37.9569 1615.6 36.5773 C 1614.95 43.6695 1612.3 47.175 1610.11 53.6333 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1664.47 316.03 C 1666.5 314.805 1667.46 314.503 1668.45 312.273 L 1665.67 311.474 C 1671.97 311.086 1686.89 308.672 1692.15 311.634 L 1690.91 313.897 L 1692.21 316.146 C 1682.97 316.356 1673.71 316.127 1664.47 316.03 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1845.51 253.213 C 1844.99 254.36 1844.2 255.349 1843.47 256.378 C 1840.74 257.034 1839.14 257.385 1836.42 256.348 C 1834.75 254.769 1834.05 253.306 1833.8 251.037 C 1833.38 247.235 1834.13 243.089 1834.11 239.233 C 1834.04 230.355 1831.04 208.119 1837.09 201.151 C 1839.3 198.598 1842.79 197.701 1846.04 197.531 C 1859.1 196.843 1873.03 198.612 1886.2 198.769 C 1900.33 198.938 1915.64 197.011 1929.62 198.925 C 1932.66 199.342 1933.85 199.862 1936.11 201.931 L 1934.68 204.411 L 1932.58 204.615 C 1914.48 203.829 1896.3 204.085 1878.18 204.045 C 1864.82 204.016 1851.24 203.501 1837.9 204.271 C 1837.75 219.838 1836.4 236.461 1838.1 251.9 C 1840.18 253.488 1842.92 253.076 1845.51 253.213 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" fill-opacity="0.988235" d="M 1833.95 92.0614 C 1843.25 90.4708 1853.2 91.5404 1862.62 91.3784 C 1886.2 90.9728 1911.33 88.9068 1934.72 92.3052 C 1933.3 104.041 1935.08 117.315 1932.2 128.585 C 1932.13 117.541 1931.83 106.443 1932.31 95.4083 C 1900.97 94.8136 1869.11 97.621 1837.92 95.6443 C 1837.75 111.765 1837.75 127.888 1837.91 144.009 C 1846.11 144.051 1854.36 143.908 1862.56 144.157 C 1854.75 146.124 1843.68 147.053 1835.62 146.951 C 1831.75 140.452 1833.84 102.176 1833.95 92.0614 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1677.37 253.007 C 1676.01 249.029 1675.36 206.342 1676.51 201.588 C 1676.74 200.659 1676.51 200.948 1677.41 200.433 C 1683.58 196.905 1762.47 197.876 1771.89 200.114 C 1775.4 203.794 1775.91 210.315 1776.2 215.219 C 1776.32 217.185 1776.54 220.848 1775.2 222.344 C 1774.64 216.979 1774.18 211.604 1773.84 206.222 C 1770.93 210.228 1774.69 219.912 1771.3 225.345 C 1771.16 218.243 1771.08 211.133 1770.85 204.034 C 1759.02 205.292 1746.28 204.266 1734.35 204.189 C 1715.7 204.069 1697.03 204.4 1678.38 204.082 C 1678.04 215.578 1680.36 244.069 1677.37 253.007 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1936.11 201.931 C 1938.65 208.234 1937.95 246.218 1935.33 252.21 C 1935.07 252.803 1934.76 253.362 1934.43 253.919 L 1932.49 254.612 L 1931 253.687 C 1934.04 247.761 1931.57 214.273 1932.58 204.615 L 1934.68 204.411 L 1936.11 201.931 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1006.6 45.4499 C 1031.54 43.1833 1056.7 37.1747 1081.15 31.9075 L 1166.87 13.5267 C 1184.98 9.6218 1204.58 3.5952 1223.1 2.70382 C 1227.92 13.2229 1243.3 49.5666 1243.48 59.1636 C 1241.48 63.1086 1237.17 64.3833 1233.13 65.7144 C 1213.13 72.2883 1183.43 75.7795 1165.67 83.6946 C 1165.77 96.0882 1166.22 108.972 1165.32 121.308 C 1165.5 131.861 1165.51 142.417 1165.35 152.971 C 1185.9 153.481 1206.51 152.376 1227.03 153.395 L 1229.57 154.32 C 1231.82 159.785 1230.47 209.232 1228.99 217.22 C 1221.36 221.118 1176.66 218.927 1165.12 218.805 L 1165.69 233.003 C 1186.68 256.95 1208.07 280.214 1228.61 304.623 C 1233.71 310.689 1239.48 316.447 1244.29 322.719 C 1236.36 331.171 1208.23 368.286 1200.38 369.973 C 1196.71 368.94 1195.46 367.551 1193.56 364.221 C 1185.87 350.734 1178.82 336.778 1171.16 323.225 C 1169.29 319.923 1167.83 315.741 1165.57 312.761 L 1161.87 313.52 L 1159.86 313.826 C 1157.91 317.848 1159.13 490.857 1159.14 509.002 C 1145.76 510.751 1115.26 511.795 1102.82 509.487 C 1101.45 507.065 1101.81 504.262 1101.87 501.573 C 1101.28 499.136 1100.77 496.759 1100.43 494.27 L 1098.89 498.558 L 1098.39 503.214 L 1097.35 503.129 L 1095.92 499.799 C 1095.86 471.761 1095.12 443.548 1096.09 415.536 C 1095.38 399.262 1096.02 382.718 1096.5 366.436 C 1095.43 354.843 1098.31 342.422 1096.3 331.133 C 1094.22 332.644 1093.54 335.68 1092.65 338.015 C 1086.5 355.983 1076.78 383.433 1064.1 397.913 L 1060.97 393.867 C 1057.86 398.825 1054.89 404.149 1051.36 408.806 L 1055.69 410.177 C 1050.14 417.984 1039.76 431.194 1031.55 435.802 C 1030.6 436.331 1029.46 435.666 1028.45 435.368 C 1025.23 430.779 1025.33 423.663 1023.19 418.389 C 1017.87 405.294 1004.74 390.662 1003.89 376.48 C 1003.53 370.47 1008.7 365.943 1012.39 361.839 C 1015.56 356.263 1020.27 350.809 1023.91 345.415 C 1031.48 334.187 1038.5 322.905 1045.51 311.328 C 1052.16 296.668 1060.7 282.29 1068.14 267.958 C 1073.98 251.756 1082.05 236.285 1087.94 219.978 C 1064.75 219.246 1041.57 219.542 1018.39 218.85 C 1018.39 197.742 1020.26 175.702 1016.45 154.876 C 1036.44 153.75 1080.04 150.789 1098.35 154.211 C 1096.51 136.91 1101.28 111.579 1097.41 95.7053 L 1095.49 94.8062 L 1094.39 97.2745 C 1080.68 100.429 1036.63 107.213 1023.9 106.893 C 1019.48 88.4613 1013.94 62.5029 1006.6 45.4499 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1006.6 45.4499 C 1031.54 43.1833 1056.7 37.1747 1081.15 31.9075 L 1166.87 13.5267 C 1184.98 9.6218 1204.58 3.5952 1223.1 2.70382 C 1227.92 13.2229 1243.3 49.5666 1243.48 59.1636 C 1241.48 63.1086 1237.17 64.3833 1233.13 65.7144 C 1213.13 72.2883 1183.43 75.7795 1165.67 83.6946 C 1165.77 96.0882 1166.22 108.972 1165.32 121.308 C 1165.17 108.312 1161.23 93.7619 1162.46 81.3738 C 1167.31 72.6395 1224.87 64.7892 1237.57 59.0255 L 1238.01 58.8212 C 1234.92 42.6656 1226.92 21.5456 1218.09 7.64251 C 1183.03 16.4377 1147.04 22.5421 1111.67 29.9825 C 1081.42 36.346 1044.19 46.3595 1014.09 48.6933 C 1017.42 66.1257 1021.73 84.9866 1027.72 101.699 C 1037.27 99.5806 1047.53 99.321 1057.25 97.9256 C 1067.59 96.4403 1078.76 93.28 1089.14 93.0457 C 1091.63 92.9897 1093.37 93.5455 1095.49 94.8062 L 1094.39 97.2745 C 1080.68 100.429 1036.63 107.213 1023.9 106.893 C 1019.48 88.4613 1013.94 62.5029 1006.6 45.4499 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1227.03 153.395 L 1229.57 154.32 C 1231.82 159.785 1230.47 209.232 1228.99 217.22 C 1221.36 221.118 1176.66 218.927 1165.12 218.805 L 1165.69 233.003 C 1186.68 256.95 1208.07 280.214 1228.61 304.623 C 1233.71 310.689 1239.48 316.447 1244.29 322.719 C 1236.36 331.171 1208.23 368.286 1200.38 369.973 C 1196.71 368.94 1195.46 367.551 1193.56 364.221 C 1185.87 350.734 1178.82 336.778 1171.16 323.225 C 1169.29 319.923 1167.83 315.741 1165.57 312.761 L 1161.87 313.52 C 1162.79 312.227 1163.93 311.067 1164.99 309.887 L 1166.66 309.967 C 1176.31 318.59 1194.1 354.305 1200.74 367.374 C 1211.15 351.42 1225.19 336.886 1237.77 322.592 C 1232.06 314.885 1224.74 307.856 1218.47 300.554 C 1206.08 286.135 1194.14 271.427 1181.47 257.237 C 1175.55 250.609 1166.98 243.796 1162.42 236.366 C 1160.81 233.731 1160.19 229.324 1159.97 226.307 C 1159.71 222.718 1159.8 217.944 1162.42 215.168 C 1165.18 212.253 1216.51 213.835 1224.21 213.874 C 1224.9 199.546 1221.88 165.12 1227.03 153.395 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1016.45 154.876 C 1036.44 153.75 1080.04 150.789 1098.35 154.211 L 1098.86 157.509 C 1093.04 162.702 1033.68 159.729 1021.95 160.202 C 1022.35 178.003 1023.23 196.081 1021.96 213.849 C 1035.1 214.169 1048.2 215.267 1061.34 215.599 C 1068.9 215.79 1077.33 214.713 1084.77 215.904 C 1086.92 216.25 1088.38 217.037 1090.04 218.449 C 1091.77 221.73 1091.02 224.48 1089.98 227.878 C 1085.85 241.274 1077.5 253.557 1073.43 266.75 L 1068.14 267.958 C 1073.98 251.756 1082.05 236.285 1087.94 219.978 C 1064.75 219.246 1041.57 219.542 1018.39 218.85 C 1018.39 197.742 1020.26 175.702 1016.45 154.876 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1096.3 331.133 L 1098.37 332.127 L 1100.45 329.983 C 1102.99 334.17 1102.09 344.035 1102.13 349.091 C 1101.85 376.406 1101.72 403.722 1101.74 431.038 C 1101.9 454.468 1102.81 478.159 1101.87 501.573 C 1101.28 499.136 1100.77 496.759 1100.43 494.27 L 1098.89 498.558 L 1098.39 503.214 L 1097.35 503.129 L 1095.92 499.799 C 1095.86 471.761 1095.12 443.548 1096.09 415.536 C 1095.38 399.262 1096.02 382.718 1096.5 366.436 C 1095.43 354.843 1098.31 342.422 1096.3 331.133 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1098.09 397.344 C 1103.18 430.466 1098.83 465.213 1098.89 498.558 L 1098.39 503.214 L 1097.35 503.129 L 1095.92 499.799 C 1095.86 471.761 1095.12 443.548 1096.09 415.536 C 1096.79 409.475 1097.46 403.411 1098.09 397.344 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1096.3 331.133 L 1098.37 332.127 L 1100.45 329.983 C 1102.99 334.17 1102.09 344.035 1102.13 349.091 C 1101.25 350.375 1099.94 351.819 1099.24 353.183 C 1097.54 356.483 1099.89 371.953 1097.7 373.359 C 1097.32 371.052 1096.98 368.724 1096.5 366.436 C 1095.43 354.843 1098.31 342.422 1096.3 331.133 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1055.69 410.177 C 1050.14 417.984 1039.76 431.194 1031.55 435.802 C 1030.6 436.331 1029.46 435.666 1028.45 435.368 C 1025.23 430.779 1025.33 423.663 1023.19 418.389 C 1017.87 405.294 1004.74 390.662 1003.89 376.48 C 1003.53 370.47 1008.7 365.943 1012.39 361.839 C 1012 367.193 1010.01 370.558 1007.25 375.007 C 1015.56 392.717 1024.97 410.345 1031.75 428.703 C 1038.97 422.747 1045.27 415.897 1051.36 408.806 L 1055.69 410.177 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1060.97 393.867 C 1069.61 382.495 1075.33 368.04 1080.97 354.989 C 1082.91 350.497 1084.77 344.948 1087.38 340.887 C 1088.86 338.592 1090.22 338.608 1092.65 338.015 C 1086.5 355.983 1076.78 383.433 1064.1 397.913 L 1060.97 393.867 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1045.51 311.328 C 1052.16 296.668 1060.7 282.29 1068.14 267.958 L 1073.43 266.75 C 1068.67 277.741 1064.11 289.546 1056.19 298.684 L 1056.92 297.011 C 1053.86 301.211 1050 309.144 1045.51 311.328 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1024.1 600.841 C 1047.97 600.009 1071.69 601.581 1095.51 600.771 C 1099.64 604.33 1170.28 752.401 1171.95 760.294 C 1176.61 751.322 1180.84 742.243 1184.93 732.998 C 1191.38 719.083 1197.76 705.128 1205.41 691.816 C 1205.98 686.662 1243.62 609.471 1248.51 603.039 C 1259.03 598.373 1283.35 601.001 1295.44 601.178 C 1301.24 601.05 1315.75 599.339 1320.16 602.87 C 1319.94 639.745 1319.9 676.621 1320.06 713.496 C 1320.06 731.326 1320.78 749.534 1319.91 767.322 C 1319.01 759.086 1318.34 750.784 1316.68 742.659 C 1315.21 769.119 1316.01 795.856 1316.08 822.353 C 1316.12 834.41 1317.27 848.465 1315.16 860.225 L 1314.9 871.753 C 1301.12 871.207 1287.28 871.195 1273.48 870.972 C 1271.7 825.968 1273.02 780.543 1273.06 735.492 C 1273.08 716.382 1273.89 696.863 1272.4 677.824 L 1267.99 676.603 L 1267.63 675.295 L 1266.67 675.836 C 1264.44 681.942 1261.74 687.639 1258.79 693.429 C 1245.24 722.308 1231.21 750.965 1216.72 779.388 C 1206.98 798.855 1196.52 817.923 1188.53 838.222 C 1177.64 838.068 1166.43 838.44 1155.58 837.607 C 1145.89 810.774 1132.07 786.333 1119.58 760.765 C 1111.04 746.688 1105.01 731.328 1097.53 716.692 C 1089.74 705.324 1083.36 685.16 1075.1 671.059 C 1074.43 686.924 1075.9 703.244 1075.93 719.184 C 1076.03 771.654 1076.95 824.35 1075.59 876.794 C 1062.17 877.872 1037.93 878.989 1025.08 876.684 C 1021.51 865.099 1023.86 801.739 1023.86 784.074 C 1023.85 723.089 1022.53 661.788 1024.1 600.841 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1257.38 685.941 C 1258.15 683.808 1258.89 681.379 1260.01 679.406 C 1261.53 676.728 1263.97 676.529 1266.67 675.836 C 1264.44 681.942 1261.74 687.639 1258.79 693.429 C 1245.24 722.308 1231.21 750.965 1216.72 779.388 C 1206.98 798.855 1196.52 817.923 1188.53 838.222 C 1177.64 838.068 1166.43 838.44 1155.58 837.607 C 1145.89 810.774 1132.07 786.333 1119.58 760.765 L 1124.51 760.253 C 1131.96 774.613 1139.34 789.102 1146.23 803.735 C 1150.72 813.264 1154.39 823.749 1160.18 832.525 C 1168.3 832.727 1176.37 832.42 1184.47 832.109 C 1191.31 822.822 1199.47 801.76 1204.97 790.568 C 1222.11 755.658 1239.16 720.284 1257.38 685.941 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1095.51 600.771 C 1099.64 604.33 1170.28 752.401 1171.95 760.294 L 1169.41 762.968 C 1164.11 755.23 1160.62 746.15 1156.61 737.705 L 1139.57 702.304 C 1125.34 672.546 1108.39 642.438 1097.1 611.494 C 1095.78 607.861 1094.88 604.678 1095.51 600.771 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1295.44 601.178 C 1301.24 601.05 1315.75 599.339 1320.16 602.87 C 1319.94 639.745 1319.9 676.621 1320.06 713.496 C 1320.06 731.326 1320.78 749.534 1319.91 767.322 C 1319.01 759.086 1318.34 750.784 1316.68 742.659 C 1315.21 769.119 1316.01 795.856 1316.08 822.353 C 1316.12 834.41 1317.27 848.465 1315.16 860.225 C 1313.6 841.169 1315.01 820.666 1315 801.472 C 1314.97 750.692 1316.1 699.905 1315.32 649.126 C 1315.09 633.754 1316.66 617.607 1314.96 602.396 C 1308.48 602.278 1301.81 602.444 1295.44 601.178 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1184.93 732.998 C 1191.38 719.083 1197.76 705.128 1205.41 691.816 C 1205.06 700.163 1194.49 725.473 1189.98 733.031 C 1186.31 741.754 1181.62 758.633 1173.98 764.009 L 1171.41 764.502 L 1169.41 762.968 L 1171.95 760.294 C 1176.61 751.322 1180.84 742.243 1184.93 732.998 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1100.83 713.389 C 1109.2 728.732 1116.18 744.852 1124.51 760.253 L 1119.58 760.765 C 1111.04 746.688 1105.01 731.328 1097.53 716.692 L 1100.83 713.389 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1075.1 671.059 L 1075.12 668.605 L 1076.38 667.77 C 1085.98 673.264 1094.56 702.882 1100.83 713.389 L 1097.53 716.692 C 1089.74 705.324 1083.36 685.16 1075.1 671.059 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1862.25 670.478 C 1880.93 667.773 1896.95 668.992 1915.11 674.171 C 1939.98 682.89 1958.96 697.763 1970.61 721.844 C 1974.85 730.544 1977.65 739.876 1978.9 749.474 C 1979.65 762.044 1982.27 780.333 1977.85 792.266 C 1965.44 793.497 1952.36 792.896 1939.89 792.928 L 1868.62 792.706 C 1859.85 792.641 1842.58 790.411 1835.05 793.02 C 1835.06 794.206 1835.12 795.382 1835.26 796.561 C 1836.75 808.376 1843.6 819.298 1852.87 826.583 C 1870.62 840.532 1902.35 844.521 1923.16 835.886 C 1932.33 832.08 1942.21 824.064 1952.03 822.909 C 1961.27 830.284 1969.55 841.111 1977.55 849.937 C 1977.46 850.379 1977.35 850.82 1977.23 851.254 C 1975.77 856.368 1968.02 860.401 1963.63 862.842 C 1930.23 881.449 1894.48 889.16 1856.87 878.407 C 1827.99 870.151 1807.38 854.607 1792.81 828.361 C 1777.42 800.661 1777.72 765.932 1786.24 736.177 C 1789.08 728.564 1792.41 721.458 1796.62 714.498 C 1812 689.073 1834.13 677.333 1862.25 670.478 z M 1832.77 750.534 L 1832.6 753.744 C 1837.03 756.175 1919.83 755.14 1931.45 754.943 L 1932.41 754.175 C 1932.45 753.78 1932.48 753.383 1932.48 752.986 C 1932.63 742.832 1926.78 730.587 1919.78 723.426 C 1909.85 713.275 1898.11 709.974 1884.23 709.793 C 1869.9 710.091 1857.17 712.692 1846.88 723.466 C 1839.09 731.624 1837.4 740.852 1832.77 750.534 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1977.85 792.266 C 1965.44 793.497 1952.36 792.896 1939.89 792.928 L 1868.62 792.706 C 1859.85 792.641 1842.58 790.411 1835.05 793.02 C 1835.06 794.206 1835.12 795.382 1835.26 796.561 C 1836.75 808.376 1843.6 819.298 1852.87 826.583 C 1870.62 840.532 1902.35 844.521 1923.16 835.886 C 1932.33 832.08 1942.21 824.064 1952.03 822.909 C 1961.27 830.284 1969.55 841.111 1977.55 849.937 C 1977.46 850.379 1977.35 850.82 1977.23 851.254 C 1975.77 856.368 1968.02 860.401 1963.63 862.842 C 1930.23 881.449 1894.48 889.16 1856.87 878.407 C 1827.99 870.151 1807.38 854.607 1792.81 828.361 C 1777.42 800.661 1777.72 765.932 1786.24 736.177 C 1787.98 750.767 1786.53 764.692 1786.75 779.273 C 1787.18 807.634 1796.6 836.528 1819.59 854.608 C 1845.01 874.601 1879.25 880.104 1910.71 876.175 C 1933.33 873.35 1953.2 863.533 1972.08 851.205 C 1964.55 843.368 1956.75 835.676 1950.16 827.017 C 1927.95 841.131 1903.52 849.225 1877.18 843.435 C 1861.67 840.028 1846.84 831.35 1838.17 817.8 C 1835.25 813.243 1828.18 798.871 1829.17 793.863 C 1829.7 791.142 1831.65 789.098 1833.82 787.559 C 1842.07 786.012 1851.78 786.992 1860.2 787.011 L 1909.58 787.109 C 1930 787.139 1954.01 785.182 1973.85 787.28 C 1975.72 788.666 1976.68 790.318 1977.85 792.266 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1915.11 674.171 C 1939.98 682.89 1958.96 697.763 1970.61 721.844 C 1974.85 730.544 1977.65 739.876 1978.9 749.474 C 1976.96 750.058 1976.53 750.472 1974.6 749.698 C 1969.64 741.386 1968.35 730.085 1964.59 721.074 C 1956.37 701.386 1935.75 687.009 1916.71 679.202 L 1915.11 674.171 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1862.25 670.478 C 1880.93 667.773 1896.95 668.992 1915.11 674.171 L 1916.71 679.202 C 1910.32 678.039 1904.34 675.455 1897.84 674.535 C 1885.79 672.831 1873.74 675.126 1862.25 670.478 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1832.77 750.534 L 1829.62 750.529 L 1828.67 748.633 C 1829.3 737.861 1837.01 726.372 1844.52 718.98 C 1856.99 706.716 1869.9 703.755 1887 703.878 C 1884.87 705.53 1882.91 706.77 1880.45 707.863 C 1881.61 709.367 1882.43 709.3 1884.23 709.793 C 1869.9 710.091 1857.17 712.692 1846.88 723.466 C 1839.09 731.624 1837.4 740.852 1832.77 750.534 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1887 703.878 C 1898.99 703.387 1912.03 709.519 1920.7 717.574 C 1927.81 724.175 1934.63 734.806 1934.93 744.727 C 1935.03 748.027 1933.13 751.045 1932.41 754.175 C 1932.45 753.78 1932.48 753.383 1932.48 752.986 C 1932.63 742.832 1926.78 730.587 1919.78 723.426 C 1909.85 713.275 1898.11 709.974 1884.23 709.793 C 1882.43 709.3 1881.61 709.367 1880.45 707.863 C 1882.91 706.77 1884.87 705.53 1887 703.878 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1509.72 674.522 C 1524.52 673.337 1539.79 674.077 1554.65 674.261 C 1557.15 674.728 1557.81 674.604 1559.48 676.666 C 1558.01 694.465 1559.82 713.654 1559.82 731.649 C 1559.8 780.374 1560.59 829.246 1558.87 877.94 C 1547.23 877.733 1536.18 877.872 1524.56 878.672 C 1520.77 878.933 1516.8 879.432 1513.78 876.608 C 1508.56 871.724 1510.06 853.707 1509.97 846.797 L 1509.79 847.192 C 1502.71 862.277 1488.37 872.827 1472.93 878.369 C 1453.48 885.349 1426.75 884.654 1408.04 875.717 C 1392.29 868.197 1378.44 855.365 1372.67 838.624 C 1370 830.902 1368.77 822.571 1368.28 814.438 C 1367.6 803.093 1366.65 679.902 1368.75 675.904 C 1369.14 675.169 1372 674.526 1372.89 674.222 L 1418.14 674.141 C 1420.83 717.929 1417.54 762.305 1418.98 806.2 C 1422.11 817.652 1425.69 826.327 1436.65 832.502 C 1445.66 837.578 1455.88 838.986 1466.05 839.365 C 1477.2 837.184 1488.17 833.768 1496.58 825.776 C 1501.63 820.979 1505.09 815.435 1506.6 808.578 C 1510.4 791.304 1508.06 751.908 1508.06 732.246 C 1508.06 712.947 1509.13 693.796 1509.72 674.522 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1554.65 674.261 C 1557.15 674.728 1557.81 674.604 1559.48 676.666 C 1558.01 694.465 1559.82 713.654 1559.82 731.649 C 1559.8 780.374 1560.59 829.246 1558.87 877.94 C 1547.23 877.733 1536.18 877.872 1524.56 878.672 C 1520.77 878.933 1516.8 879.432 1513.78 876.608 C 1508.56 871.724 1510.06 853.707 1509.97 846.797 L 1509.79 847.192 C 1502.71 862.277 1488.37 872.827 1472.93 878.369 C 1453.48 885.349 1426.75 884.654 1408.04 875.717 C 1392.29 868.197 1378.44 855.365 1372.67 838.624 C 1370 830.902 1368.77 822.571 1368.28 814.438 C 1367.6 803.093 1366.65 679.902 1368.75 675.904 C 1369.14 675.169 1372 674.526 1372.89 674.222 C 1373.97 706.292 1373.32 738.434 1373.14 770.517 C 1373.03 788.732 1371.13 809.125 1374.92 826.979 C 1377.5 839.125 1382.79 850.808 1391.94 859.405 C 1406.05 872.651 1426.3 877.804 1445.22 877.145 C 1464.85 876.461 1482.26 871.621 1495.84 856.903 C 1499.93 852.472 1503.37 847.412 1509.7 846.33 C 1511.94 847.139 1512.87 847.422 1513.89 849.756 C 1516.34 855.324 1514.82 864.949 1514.82 871.08 C 1527.54 871.567 1540.27 871.176 1552.99 870.937 C 1553.87 824.628 1554.2 778.311 1553.97 731.995 C 1554.02 713.159 1551.42 692.76 1554.65 674.261 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1466.05 839.365 C 1460.8 840.484 1454.13 841.156 1448.72 841.827 C 1443.07 842.527 1432.96 836.371 1428.56 832.871 C 1422.64 828.169 1416.78 821.805 1416.07 813.947 C 1415.74 810.314 1416.8 808.888 1418.98 806.2 C 1422.11 817.652 1425.69 826.327 1436.65 832.502 C 1445.66 837.578 1455.88 838.986 1466.05 839.365 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1686.26 344.071 C 1707.65 342.909 1729.57 343.183 1750.96 344.27 C 1750.64 367.963 1751.27 391.664 1750.83 415.359 C 1750.86 424.891 1749.2 442.359 1756.24 449.563 C 1766.3 459.867 1812.95 456.58 1827.95 456.475 C 1841.65 456.38 1870.45 459.419 1880.46 449.032 C 1888.5 440.687 1885.34 417.979 1885.04 407.161 C 1904.77 411.367 1923.75 420.442 1943.77 422.862 C 1942.66 447.472 1945.63 480.992 1925.66 499.182 C 1921.62 502.861 1916.86 505.713 1912.23 508.577 C 1899.91 512.096 1887.55 513.952 1874.72 513.965 C 1852.29 515.323 1829.28 514.138 1806.77 514.038 C 1782.8 513.932 1756.97 515.717 1733.3 512.005 C 1723.94 510.537 1716.17 508.099 1708.33 502.759 C 1706.03 501.144 1703.64 499.314 1701.61 497.37 C 1692.41 488.551 1687.08 471.178 1686.84 458.827 C 1683.92 449.183 1686.41 362.882 1686.26 344.071 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1885.04 407.161 C 1904.77 411.367 1923.75 420.442 1943.77 422.862 C 1942.66 447.472 1945.63 480.992 1925.66 499.182 C 1921.62 502.861 1916.86 505.713 1912.23 508.577 L 1911.28 504.136 C 1915.44 502.702 1919.3 499.244 1922.45 496.226 C 1940.11 479.268 1937.38 449.249 1937.84 426.814 C 1922.1 423.748 1906.09 418.259 1890.65 413.853 C 1890.07 425.165 1891.37 437.638 1887.43 448.4 C 1886.72 449.133 1886 449.858 1885.28 450.574 C 1880.75 455.058 1876.08 457.795 1869.89 459.539 C 1852.13 464.546 1813.56 462.296 1793.97 462.144 C 1780.89 462.042 1761.44 462.112 1751.64 451.774 C 1745.35 445.138 1746.06 434.717 1746.3 426.279 C 1746.93 431.815 1747.5 437.616 1748.83 443.028 C 1749.44 445.512 1750.21 448.166 1752.52 449.568 C 1753.18 446.212 1750.35 441.943 1749.6 438.52 C 1748.06 431.552 1748.69 422.103 1750.83 415.359 C 1750.86 424.891 1749.2 442.359 1756.24 449.563 C 1766.3 459.867 1812.95 456.58 1827.95 456.475 C 1841.65 456.38 1870.45 459.419 1880.46 449.032 C 1888.5 440.687 1885.34 417.979 1885.04 407.161 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1686.84 458.827 C 1688.01 461.379 1688.99 465.914 1690.92 467.655 C 1691.87 466.271 1691.48 463.959 1691.43 462.325 C 1694.33 473.282 1696.57 485.677 1704.41 494.304 C 1707.04 497.198 1709.82 498.543 1708.33 502.759 C 1706.03 501.144 1703.64 499.314 1701.61 497.37 C 1692.41 488.551 1687.08 471.178 1686.84 458.827 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1874.72 513.965 C 1884.8 507.63 1899.8 506.316 1911.28 504.136 L 1912.23 508.577 C 1899.91 512.096 1887.55 513.952 1874.72 513.965 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1334.48 13.9338 C 1356.82 11.7735 1380.56 13.0312 1403.03 13.3648 L 1403.28 178.728 C 1403.28 195.197 1405.61 341.541 1400.41 346.659 C 1395.43 351.552 1351.55 348.949 1342.66 348.814 C 1339.99 348.69 1337.1 348.93 1334.83 347.359 C 1332.39 340.545 1334.27 288.507 1334.28 277.413 L 1334.35 117.816 C 1334.4 83.3054 1332.82 48.3826 1334.48 13.9338 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1334.48 13.9338 C 1356.82 11.7735 1380.56 13.0312 1403.03 13.3648 L 1403.28 178.728 C 1401.91 176.692 1400.72 174.599 1400.4 172.133 C 1398.71 159.197 1399.79 144.773 1399.73 131.669 C 1399.57 94.1965 1401.33 56.787 1399.74 19.3588 C 1379.58 18.9091 1359.2 20.014 1339.11 19.094 L 1339.07 238.008 L 1339.09 306.158 C 1339.09 317.838 1338.29 330.152 1339.46 341.758 C 1339.75 344.669 1340.69 346.635 1342.66 348.814 C 1339.99 348.69 1337.1 348.93 1334.83 347.359 C 1332.39 340.545 1334.27 288.507 1334.28 277.413 L 1334.35 117.816 C 1334.4 83.3054 1332.82 48.3826 1334.48 13.9338 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1437.81 285.832 C 1450.19 288.726 1462.02 297.18 1473.33 302.966 C 1482.13 307.466 1491.19 311.454 1500.05 315.823 C 1468.93 380.537 1435.5 424.255 1374.31 463.085 C 1372.58 462.337 1371.34 461.017 1369.98 459.756 C 1363.85 463.458 1357.79 467.159 1352.21 471.672 C 1349.51 473.859 1347.67 476.765 1344.74 478.628 L 1345.07 480.114 C 1328.25 490.37 1289 509.674 1270.37 513.707 C 1261.26 516.702 1250.77 518.647 1241.24 519.589 C 1238.19 514.057 1211.75 461.266 1211.64 459.456 C 1216.22 456.162 1224.83 455.283 1230.37 453.943 C 1241.44 450.161 1253.02 447.622 1263.92 443.393 C 1315.24 423.471 1374.73 386.65 1407.33 341.82 C 1414.57 329.092 1422.18 316.586 1429.37 303.818 C 1432.11 297.845 1434.63 291.579 1437.81 285.832 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1437.81 285.832 C 1450.19 288.726 1462.02 297.18 1473.33 302.966 C 1482.13 307.466 1491.19 311.454 1500.05 315.823 C 1468.93 380.537 1435.5 424.255 1374.31 463.085 C 1372.58 462.337 1371.34 461.017 1369.98 459.756 C 1426.79 425.832 1465.42 376.585 1493.43 317.476 C 1475.76 310.947 1456.81 301.346 1440.7 291.567 C 1438.7 295.452 1436.95 299.465 1435.16 303.447 L 1429.37 303.818 C 1432.11 297.845 1434.63 291.579 1437.81 285.832 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1230.37 453.943 C 1228.42 456.645 1218.11 459.493 1218.02 460.316 L 1218.7 460.176 C 1221.03 459.699 1223.16 459.584 1225.54 459.633 L 1217.79 462.212 C 1225.83 479.691 1234.34 497.962 1244.02 514.565 C 1249.63 513.139 1263.55 508.21 1268.53 510.972 L 1270.37 513.707 C 1261.26 516.702 1250.77 518.647 1241.24 519.589 C 1238.19 514.057 1211.75 461.266 1211.64 459.456 C 1216.22 456.162 1224.83 455.283 1230.37 453.943 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1429.37 303.818 L 1435.16 303.447 C 1430.04 313.293 1419.07 335.18 1410.87 341.762 C 1409.54 342.046 1408.67 342.252 1407.33 341.82 C 1414.57 329.092 1422.18 316.586 1429.37 303.818 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1660.73 669.673 C 1691.27 663.698 1723.37 673.239 1750.64 686.914 C 1748.74 693.578 1747.37 700.914 1744.12 707.051 C 1741.44 712.983 1740.14 719.451 1736.87 725.075 C 1724.35 720.183 1708.64 716.616 1697.19 711.593 C 1686.82 709.514 1677.27 709.645 1666.76 710.499 C 1659.37 713.565 1653.87 717.201 1647.56 722.098 C 1647.48 726.491 1646.52 732.962 1648.38 736.963 C 1655.47 743.461 1668.99 746.473 1678.22 748.439 L 1702.01 755.475 C 1720 759.434 1741.31 768.689 1751.6 784.857 C 1759.88 797.869 1761.62 814.771 1758.3 829.632 C 1754.46 846.773 1744.67 860.635 1729.81 869.99 C 1703.68 886.436 1670.98 886.434 1641.75 879.628 C 1626.4 876.056 1609.07 868.968 1595.67 860.538 C 1597.39 852.348 1601.78 829.076 1608.58 824.321 C 1610.45 823.011 1612.66 822.913 1614.82 823.39 C 1625.26 825.698 1636.36 832.834 1646.67 836.633 C 1662.24 842.154 1679.68 845.475 1695.84 840.318 C 1704.38 835.618 1707.96 831.049 1711.52 822.188 C 1711.02 819.924 1710.55 817.503 1709.64 815.365 C 1702.17 797.866 1651.68 791.019 1633.95 784.064 C 1621.36 779.127 1608.54 770.922 1603.04 757.996 C 1596.23 741.965 1598.23 720.557 1604.61 704.766 C 1605.41 703.433 1606.23 702.119 1607.08 700.824 C 1620.56 680.411 1637.68 674.379 1660.73 669.673 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1662.99 706.269 C 1665.64 707.659 1665.67 707.712 1666.76 710.499 C 1659.37 713.565 1653.87 717.201 1647.56 722.098 C 1647.48 726.491 1646.52 732.962 1648.38 736.963 C 1655.47 743.461 1668.99 746.473 1678.22 748.439 L 1702.01 755.475 C 1720 759.434 1741.31 768.689 1751.6 784.857 C 1759.88 797.869 1761.62 814.771 1758.3 829.632 C 1754.46 846.773 1744.67 860.635 1729.81 869.99 C 1703.68 886.436 1670.98 886.434 1641.75 879.628 C 1626.4 876.056 1609.07 868.968 1595.67 860.538 C 1597.39 852.348 1601.78 829.076 1608.58 824.321 C 1610.45 823.011 1612.66 822.913 1614.82 823.39 C 1625.26 825.698 1636.36 832.834 1646.67 836.633 C 1643.63 836.776 1640.71 836.742 1637.67 836.503 C 1636.84 836.919 1637.22 836.76 1636.53 837.011 C 1627.33 835.457 1620.88 831.105 1613.15 826.317 C 1607.95 836.107 1604.22 846.187 1601.04 856.792 C 1609.69 862.988 1619.7 867.705 1629.82 870.931 C 1657.33 879.709 1695.82 882.263 1722.23 868.386 C 1735.23 861.558 1746.87 849.806 1751.25 835.565 C 1755.74 820.97 1755.24 802.618 1747.98 788.976 C 1737.89 770.007 1715.12 764.503 1696.12 758.726 C 1689.65 758.071 1682.91 754.848 1676.34 753.58 C 1665.89 750.175 1650.3 748.155 1644.27 737.694 C 1640.53 731.202 1642.44 725 1644.3 718.319 C 1650.11 712.622 1655.37 709.153 1662.99 706.269 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1604.61 704.766 C 1604.65 704.931 1604.69 705.095 1604.72 705.261 C 1606.57 714.069 1604.59 723.13 1604.06 732 C 1603.15 747.367 1608.62 763.736 1621.8 772.532 C 1642.73 786.497 1709.86 790.767 1715.08 816.492 C 1715.38 817.993 1715.52 819.528 1715.65 821.052 C 1715.64 826.168 1714.21 831.196 1710.47 834.895 C 1705.89 839.433 1702.14 840.336 1695.84 840.318 C 1704.38 835.618 1707.96 831.049 1711.52 822.188 C 1711.02 819.924 1710.55 817.503 1709.64 815.365 C 1702.17 797.866 1651.68 791.019 1633.95 784.064 C 1621.36 779.127 1608.54 770.922 1603.04 757.996 C 1596.23 741.965 1598.23 720.557 1604.61 704.766 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1660.73 669.673 C 1691.27 663.698 1723.37 673.239 1750.64 686.914 C 1748.74 693.578 1747.37 700.914 1744.12 707.051 C 1743.86 704.044 1743.45 701.754 1742.18 699.003 L 1744.59 692.183 C 1743.42 686.139 1736.45 684.167 1731.19 682.4 C 1722.53 679.487 1713.68 676.986 1704.63 675.613 C 1694.02 674.002 1668.52 675.896 1660.73 669.673 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1742.18 699.003 C 1743.45 701.754 1743.86 704.044 1744.12 707.051 C 1741.44 712.983 1740.14 719.451 1736.87 725.075 C 1724.35 720.183 1708.64 716.616 1697.19 711.593 L 1698.34 710.376 L 1697.85 706.372 C 1709.78 708.728 1723.46 713.101 1733.88 719.387 C 1736.54 712.553 1739.41 705.79 1742.18 699.003 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1662.99 706.269 C 1669.26 704.599 1691.65 703.746 1697.85 706.372 L 1698.34 710.376 L 1697.19 711.593 C 1686.82 709.514 1677.27 709.645 1666.76 710.499 C 1665.67 707.712 1665.64 707.659 1662.99 706.269 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1258.68 97.9654 C 1258.78 95.7311 1258.73 95.0922 1259.93 93.2493 C 1269.17 89.7092 1284.92 93.6455 1294.73 94.3416 C 1302.83 94.6407 1310.94 95.0189 1319.05 94.9453 C 1318.84 158.821 1313.68 223.32 1298.94 285.621 C 1297.27 292.678 1295.18 307.259 1289.98 312.386 C 1288.65 313.7 1287.75 313.835 1285.97 313.726 C 1280.92 313.417 1274.6 310.454 1270.19 308.075 L 1248.85 298.083 C 1242.9 295.861 1234.86 292.165 1230.6 287.398 C 1230.17 283.138 1243.32 235.723 1245.03 225.775 C 1252.33 183.217 1253.42 140.652 1258.68 97.9654 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1258.68 97.9654 C 1258.78 95.7311 1258.73 95.0922 1259.93 93.2493 C 1269.17 89.7092 1284.92 93.6455 1294.73 94.3416 C 1302.83 94.6407 1310.94 95.0189 1319.05 94.9453 C 1318.84 158.821 1313.68 223.32 1298.94 285.621 C 1297.27 292.678 1295.18 307.259 1289.98 312.386 C 1288.65 313.7 1287.75 313.835 1285.97 313.726 C 1280.92 313.417 1274.6 310.454 1270.19 308.075 L 1248.85 298.083 C 1242.9 295.861 1234.86 292.165 1230.6 287.398 C 1230.17 283.138 1243.32 235.723 1245.03 225.775 C 1252.33 183.217 1253.42 140.652 1258.68 97.9654 C 1259.26 107.744 1260.44 117.631 1260.36 127.422 C 1259.9 180.228 1248.47 234.324 1236.2 285.407 C 1240.84 288.244 1245.46 291.176 1250.18 293.872 C 1258.28 297.285 1266.98 300.327 1274.73 304.453 C 1278.62 306.181 1282.38 308.059 1286.15 310.04 C 1293.78 288.631 1298.19 265.477 1301.85 243.09 C 1304.24 228.47 1307.34 213.733 1308.91 199.016 C 1312.41 166.45 1312.91 132.73 1314.04 99.9949 L 1303.44 98.1334 C 1288.55 98.2177 1273.57 98.4942 1258.68 97.9654 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1463.42 274.843 C 1461.03 275.192 1458.49 275.875 1456.27 274.663 C 1446.55 255.916 1438.37 217.855 1432.83 196.518 C 1424.73 165.393 1416.04 134.213 1408.98 102.85 C 1411.91 100.732 1427.26 99.1802 1431.79 98.0583 C 1443.09 95.2586 1459.4 86.9186 1470.22 86.6587 C 1478.75 106.288 1520.34 240.83 1518.4 258.035 C 1516.64 260.62 1515.26 260.835 1512.43 262.046 C 1503.51 261.363 1473.57 271.946 1463.42 274.843 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1463.42 274.843 C 1461.03 275.192 1458.49 275.875 1456.27 274.663 C 1446.55 255.916 1438.37 217.855 1432.83 196.518 C 1424.73 165.393 1416.04 134.213 1408.98 102.85 C 1411.91 100.732 1427.26 99.1802 1431.79 98.0583 C 1443.09 95.2586 1459.4 86.9186 1470.22 86.6587 C 1478.75 106.288 1520.34 240.83 1518.4 258.035 C 1516.64 260.62 1515.26 260.835 1512.43 262.046 L 1511.19 258.062 C 1511.9 257.421 1513.47 256.424 1513.54 255.487 C 1514.08 247.799 1510.44 236.888 1508.55 229.285 C 1501.38 200.343 1492.38 171.486 1483.16 143.133 C 1477.82 126.685 1470.04 107.797 1467.23 90.9897 C 1449.17 98.559 1433.96 103.983 1414.18 103.679 C 1419.68 129.612 1450.58 261.478 1463.42 274.843 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1968.55 328.412 C 1970.72 327.76 1972.59 326.613 1974.74 327.692 C 1982.4 331.543 2038.58 442.052 2045.53 454.665 C 2031.84 463.552 2003.54 483.539 1988.69 488.08 C 1970.75 451.652 1951.21 415.038 1930.27 380.217 C 1926.46 373.882 1912.12 358.194 1912.72 351.658 C 1915.95 349.185 1920.14 348.408 1924 347.357 C 1931.55 344.442 1939.23 341.687 1946.42 337.945 C 1953.84 334.875 1961.22 331.697 1968.55 328.412 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1968.55 328.412 C 1970.72 327.76 1972.59 326.613 1974.74 327.692 C 1982.4 331.543 2038.58 442.052 2045.53 454.665 C 2031.84 463.552 2003.54 483.539 1988.69 488.08 C 1970.75 451.652 1951.21 415.038 1930.27 380.217 C 1926.46 373.882 1912.12 358.194 1912.72 351.658 C 1915.95 349.185 1920.14 348.408 1924 347.357 C 1923.4 351.102 1920.9 352.233 1919.88 355.655 C 1946.18 396.818 1966.33 440.802 1991.23 482.623 C 2007.43 473.201 2023.75 463.749 2038.88 452.656 C 2034.41 442.441 2028.21 432.519 2022.97 422.638 C 2013.67 405.102 2004.65 387.312 1994.85 370.049 C 1988.47 358.801 1979.43 346.546 1975.41 334.321 C 1975.25 333.524 1975.07 332.574 1974.65 331.869 C 1973.18 329.373 1971.09 329.179 1968.55 328.412 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1968.55 328.412 C 1971.09 329.179 1973.18 329.373 1974.65 331.869 C 1975.07 332.574 1975.25 333.524 1975.41 334.321 C 1970.94 332.417 1956.18 339.404 1946.42 337.945 C 1953.84 334.875 1961.22 331.697 1968.55 328.412 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1604.08 358.446 C 1604.2 355.183 1604.99 351.131 1605.79 347.956 C 1606.33 345.823 1607.65 343.985 1609.59 342.928 C 1617.1 338.853 1631.66 345.014 1639.53 347.12 C 1650.47 349.158 1661.25 351.703 1672.06 354.334 C 1659.76 406.552 1656.12 437.8 1624.18 483.834 C 1621.36 487.894 1617.44 496.537 1613.36 498.899 C 1611.54 499.952 1609.99 499.306 1608.16 498.645 C 1603.31 496.888 1598.26 493.639 1593.87 490.9 C 1586.6 488.498 1579.26 482.7 1572.86 478.484 C 1568.23 475.515 1561.73 472.898 1558.37 468.516 C 1557.44 462.974 1574.21 440.015 1577.66 433.243 C 1585.54 417.755 1593.09 399.948 1597.92 383.226 C 1600.29 375.042 1601.68 366.634 1604.08 358.446 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1639.53 347.12 C 1650.47 349.158 1661.25 351.703 1672.06 354.334 C 1659.76 406.552 1656.12 437.8 1624.18 483.834 C 1621.36 487.894 1617.44 496.537 1613.36 498.899 C 1611.54 499.952 1609.99 499.306 1608.16 498.645 C 1603.31 496.888 1598.26 493.639 1593.87 490.9 L 1598.37 487.952 C 1602.29 490.158 1605.83 492.375 1609.33 495.236 C 1641.87 461.602 1658.84 402.8 1665.94 357.572 C 1660.05 356.21 1654.15 354.947 1648.31 353.383 C 1648.99 353.187 1649.53 353.034 1650.25 353.021 C 1655.32 352.932 1662.26 354.092 1666.08 357.482 C 1670.45 369.742 1649.92 426.867 1645.26 439.899 L 1645.85 439.779 C 1656.11 420.323 1660.79 398.671 1665.53 377.343 C 1666.83 371.515 1668.82 366.079 1668.95 360.046 C 1669.16 349.844 1645.67 353.48 1639.53 347.12 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1572.86 478.484 C 1568.23 475.515 1561.73 472.898 1558.37 468.516 C 1557.44 462.974 1574.21 440.015 1577.66 433.243 C 1585.54 417.755 1593.09 399.948 1597.92 383.226 C 1600.29 375.042 1601.68 366.634 1604.08 358.446 C 1604.74 365.667 1603.07 373.348 1602.05 380.505 C 1603.03 376.337 1603.99 372.161 1604.91 367.98 C 1604.27 387.324 1587.87 422.435 1578.43 439.752 C 1573.38 449.017 1567.03 458.018 1563.52 467.998 C 1567.23 470.098 1570.92 472.314 1574.71 474.262 L 1572.86 478.484 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1574.71 474.262 C 1581.13 478.09 1593.25 483.61 1598.37 487.952 L 1593.87 490.9 C 1586.6 488.498 1579.26 482.7 1572.86 478.484 L 1574.71 474.262 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 210.512 303.979 C 212.896 298.215 218.021 292.777 223.161 289.438 C 231.868 283.781 241.066 282.421 251.159 284.58 C 261.104 285.013 270.184 290.877 276.803 298.138 C 284.456 306.532 286.804 316.158 286.192 327.235 C 285.513 339.537 281.7 352.043 272.213 360.438 C 264.162 367.562 253.037 370.669 242.407 369.901 C 231.592 369.102 221.546 364.008 214.51 355.756 C 206.988 346.862 204.102 334.645 205.078 323.189 C 205.629 316.717 207.414 309.696 210.512 303.979 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 251.159 284.58 C 261.104 285.013 270.184 290.877 276.803 298.138 C 284.456 306.532 286.804 316.158 286.192 327.235 C 285.513 339.537 281.7 352.043 272.213 360.438 C 264.162 367.562 253.037 370.669 242.407 369.901 C 231.592 369.102 221.546 364.008 214.51 355.756 C 206.988 346.862 204.102 334.645 205.078 323.189 C 205.629 316.717 207.414 309.696 210.512 303.979 C 212.863 320.548 205.226 334.056 216.582 349.813 C 221.998 357.329 230.707 362.055 239.76 363.524 C 248.677 364.971 259.591 364.14 266.981 358.575 C 274.844 352.655 280.299 336.764 281.372 327.351 C 282.807 314.767 274.521 299.523 263.095 293.7 C 259.094 291.661 249.84 290.221 247.204 286.888 C 247.737 286.785 248.002 286.693 248.523 286.719 C 252.723 286.934 268.875 294.67 271.819 297.659 C 281.419 307.404 282.844 319.203 282.731 332.276 L 283.397 332.191 L 283.491 330.446 C 284.594 307.901 280.538 298.119 259.393 288.233 C 256.682 286.966 253.455 287.344 251.435 284.945 C 251.336 284.829 251.251 284.702 251.159 284.58 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 405.775 585.787 C 402.286 584 398.737 582.435 396.1 579.441 L 396.173 578.049 C 414.175 571.945 434.199 570.435 447.739 555.563 C 451.705 551.207 467.115 525.303 469.081 524.849 C 469.427 524.769 470.483 525.078 470.886 525.144 C 472.583 548.13 480.476 567.719 498.098 582.985 C 502.139 586.486 517.009 594.541 518.474 597.064 C 517.594 599.336 515.543 600.338 513.436 601.275 C 505.224 604.927 496.149 606.07 487.796 609.663 C 475.159 616.204 462.947 628.68 456.767 641.508 C 454.428 646.362 452.452 652.908 448.461 656.58 L 446.773 656.483 C 444.116 650.832 444.886 641.677 443.524 635.347 C 438.787 613.324 424.282 597.46 405.775 585.787 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 405.775 585.787 C 409.338 585.087 411.516 586.974 414.456 588.922 C 422.805 594.453 429.948 602.779 435.358 611.129 C 436.234 612.482 437.263 615.281 438.812 615.161 C 438.445 608.42 425.972 595.687 420.978 591.23 C 417.107 587.775 413.126 585.155 408.536 582.754 L 408.555 582.048 C 422.566 586.434 434.168 601.621 440.723 614.215 C 445.105 622.632 444.938 633.874 449.711 641.576 C 453.927 639.297 461.966 613.967 483.317 608.424 L 478.55 610.815 L 478.805 611.142 C 481.476 610.248 484.154 609.375 486.839 608.523 L 487.796 609.663 C 475.159 616.204 462.947 628.68 456.767 641.508 C 454.428 646.362 452.452 652.908 448.461 656.58 L 446.773 656.483 C 444.116 650.832 444.886 641.677 443.524 635.347 C 438.787 613.324 424.282 597.46 405.775 585.787 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1803.05 318.144 C 1816.9 328.396 1829.2 342.719 1841.34 354.945 C 1850.51 364.185 1860.98 373.401 1869.13 383.536 C 1864.21 392.856 1841.35 416.739 1831.82 420.308 C 1828.69 419.912 1826.84 418.064 1824.55 415.972 C 1814.44 406.707 1805.51 394.919 1796.47 384.558 C 1788.37 375.28 1779.6 366.52 1772.04 356.799 C 1770.51 354.836 1767.08 351.135 1767.46 348.594 C 1767.69 347.041 1769.03 345.378 1769.89 344.09 C 1771.44 342.481 1773.17 341.022 1774.83 339.526 C 1783.59 331.421 1793.08 324.646 1803.05 318.144 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1803.05 318.144 C 1816.9 328.396 1829.2 342.719 1841.34 354.945 C 1850.51 364.185 1860.98 373.401 1869.13 383.536 C 1864.21 392.856 1841.35 416.739 1831.82 420.308 C 1828.69 419.912 1826.84 418.064 1824.55 415.972 C 1814.44 406.707 1805.51 394.919 1796.47 384.558 C 1788.37 375.28 1779.6 366.52 1772.04 356.799 C 1770.51 354.836 1767.08 351.135 1767.46 348.594 C 1767.69 347.041 1769.03 345.378 1769.89 344.09 C 1770.78 347.369 1772.03 350.104 1774.02 352.875 C 1779.13 359.973 1785.71 366.103 1791.54 372.605 C 1804.73 387.32 1818.42 402.335 1830.22 418.183 C 1841.2 406.851 1853.01 396.325 1863.99 384.947 C 1854.41 374.027 1843.29 363.941 1833.02 353.635 C 1822.96 343.541 1813.04 332.114 1801.79 323.366 C 1797.29 327.355 1792.79 331.646 1787.93 335.178 C 1783.82 338.163 1780 340.314 1774.83 339.526 C 1783.59 331.421 1793.08 324.646 1803.05 318.144 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 445.897 250.388 C 449.325 246.881 456.128 244.104 460.925 242.921 C 469.145 240.895 475.293 241.862 482.484 246.097 C 491.384 250.018 498.032 255.541 501.663 264.868 C 505.5 274.725 503.67 286.246 499.378 295.673 C 495.288 304.656 488.547 311.581 479.176 315.066 C 470.634 318.243 460.901 318.567 452.537 314.674 C 444.78 311.064 437.485 303.083 434.372 295.102 C 430.932 288.26 430.416 281.74 431.633 274.237 C 433.149 264.886 438.193 255.998 445.897 250.388 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 472.668 248.788 C 488.812 251.718 499.974 266.59 498.277 282.91 C 496.579 299.23 482.596 311.487 466.194 311.032 C 449.793 310.577 436.511 297.563 435.721 281.174 C 435.287 272.158 438.756 263.392 445.242 257.115 C 452.508 250.083 462.718 246.983 472.668 248.788 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 445.897 250.388 C 445.973 253.142 442.428 256.106 443.685 257.49 L 445.242 257.115 C 438.756 263.392 435.287 272.158 435.721 281.174 L 434.494 279.664 C 433.38 284.252 433.671 290.428 434.372 295.102 C 430.932 288.26 430.416 281.74 431.633 274.237 C 433.149 264.886 438.193 255.998 445.897 250.388 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 376.604 379.194 C 384.836 378.003 394.172 379.707 401.018 384.463 C 407.857 389.215 412.169 396.668 413.657 404.791 C 415.561 415.181 414.546 427.19 408.255 435.969 C 402.123 444.527 393.347 448.061 383.349 449.728 C 374.452 449.598 366.757 448.063 359.368 442.715 C 351.971 437.36 346.912 429.624 345.634 420.523 C 344.299 411.009 346.208 400.522 352.162 392.843 C 358.046 385.253 367.162 380.35 376.604 379.194 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 245.278 481.533 C 257.539 479.362 270.02 483.955 277.95 493.555 C 285.88 503.155 288.032 516.279 283.585 527.909 C 279.138 539.54 268.778 547.878 256.466 549.738 C 237.761 552.563 220.261 539.826 217.199 521.159 C 214.137 502.492 226.651 484.832 245.278 481.533 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 581.532 430.797 C 583.737 426.223 585.763 420.476 589.684 417.162 L 591.653 417.292 C 594.088 420.177 593.57 425.309 594.04 428.969 C 596.145 445.356 610.271 455.983 623.899 463.083 C 606.858 469.748 593.857 473.295 582.916 489.14 C 581.766 491.553 581.517 494.399 580.229 496.771 C 579.253 498.57 578.644 499.099 576.752 499.681 C 573.484 495.743 574.503 486.104 572.779 481.118 C 571.347 476.976 567.817 473.984 566.688 469.622 C 562.444 466.354 558.851 462.241 554.464 458.93 C 552.165 457.194 548.388 454.735 547.974 451.776 C 551.501 446.938 564.993 444.86 570.638 441.417 C 574.943 438.791 577.163 433.481 581.532 430.797 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 581.532 430.797 C 583.737 426.223 585.763 420.476 589.684 417.162 L 591.653 417.292 C 594.088 420.177 593.57 425.309 594.04 428.969 C 596.145 445.356 610.271 455.983 623.899 463.083 C 606.858 469.748 593.857 473.295 582.916 489.14 C 581.766 491.553 581.517 494.399 580.229 496.771 C 579.253 498.57 578.644 499.099 576.752 499.681 C 573.484 495.743 574.503 486.104 572.779 481.118 C 571.347 476.976 567.817 473.984 566.688 469.622 C 571.173 471.572 572.524 477.478 575.983 480.956 L 574.541 477.616 C 577.199 480.049 578.267 482.89 579.609 486.173 C 581.592 483.925 582.987 480.98 584.97 478.61 C 591.857 470.376 600.56 466.217 610.139 461.923 C 604.823 456.132 598.001 450.067 593.691 443.548 C 591.471 440.19 590.468 436.192 588.242 432.738 L 585.264 432.437 C 585.278 431.088 585.42 429.746 585.536 428.403 C 584.408 429.65 583.607 430.186 581.984 430.67 C 581.834 430.715 581.683 430.755 581.532 430.797 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" fill-opacity="0.988235" d="M 1268.53 876.747 C 1267.01 839.046 1267.99 800.953 1268.02 763.211 C 1268.04 734.464 1266.89 705.284 1267.99 676.603 L 1272.4 677.824 C 1273.89 696.863 1273.08 716.382 1273.06 735.492 C 1273.02 780.543 1271.7 825.968 1273.48 870.972 C 1287.28 871.195 1301.12 871.207 1314.9 871.753 L 1315.16 860.225 C 1317.27 848.465 1316.12 834.41 1316.08 822.353 C 1316.01 795.856 1315.21 769.119 1316.68 742.659 C 1318.34 750.784 1319.01 759.086 1319.91 767.322 C 1320.82 777.589 1320.18 788.461 1320.16 798.785 C 1320.1 824.831 1319.18 851.171 1320.3 877.184 C 1303.1 877.483 1285.72 877.43 1268.53 876.747 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" fill-opacity="0.988235" d="M 1161.87 313.52 L 1165.57 312.761 C 1163.16 322.307 1165.36 349.256 1165.36 360.697 C 1165.34 411.592 1166.05 462.605 1165.23 513.484 C 1146.99 516.978 1114.16 518.771 1096.1 514.8 L 1095.92 499.799 L 1097.35 503.129 L 1098.39 503.214 L 1098.89 498.558 L 1100.43 494.27 C 1100.77 496.759 1101.28 499.136 1101.87 501.573 C 1101.81 504.262 1101.45 507.065 1102.82 509.487 C 1115.26 511.795 1145.76 510.751 1159.14 509.002 C 1159.13 490.857 1157.91 317.848 1159.86 313.826 L 1161.87 313.52 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" fill-opacity="0.984314" d="M 1345.07 480.114 L 1344.74 478.628 C 1347.67 476.765 1349.51 473.859 1352.21 471.672 C 1357.79 467.159 1363.85 463.458 1369.98 459.756 C 1371.34 461.017 1372.58 462.337 1374.31 463.085 C 1365.98 469.573 1354.87 476.027 1345.07 480.114 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" fill-opacity="0.984314" d="M 1051.36 408.806 C 1054.89 404.149 1057.86 398.825 1060.97 393.867 L 1064.1 397.913 C 1061.59 402.37 1058.98 406.26 1055.69 410.177 L 1051.36 408.806 z"/>
</svg>

After

Width:  |  Height:  |  Size: 58 KiB

17
doc/ui/ui.pen Normal file
View File

@ -0,0 +1,17 @@
{
"version": "2.6",
"children": [
{
"type": "frame",
"id": "bi8Au",
"x": 0,
"y": 0,
"name": "Frame",
"clip": true,
"width": 800,
"height": 600,
"fill": "#FFFFFF",
"layout": "none"
}
]
}

2
frontend/.env.example Normal file
View File

@ -0,0 +1,2 @@
# 后端 API 地址
NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1

3
frontend/.eslintrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript", "prettier"]
}

36
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

7
frontend/.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}

45
frontend/README.md Normal file
View File

@ -0,0 +1,45 @@
[text](README.md)# KOL Insight
云图 KOL 数据查询与分析工具。
## 功能
- 批量查询 KOL 视频数据
- 支持星图ID、达人unique_id、达人昵称搜索
- 计算预估自然CPM、看后搜成本等指标
- 数据导出
## 技术栈
- **前端/后端**: Next.js (App Router)
- **数据库**: PostgreSQL
- **部署**: Docker / PM2
## 快速开始
```bash
# 安装依赖
pnpm install
# 配置环境变量
cp .env.example .env.local
# 开发模式
pnpm dev
# 构建
pnpm build
# 生产运行
pnpm start
```
## 环境变量
```env
DATABASE_URL=postgresql://user:password@host:5432/yuntu_kol
```
## License
MIT

4
frontend/next.config.mjs Normal file
View File

@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

28
frontend/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.2.35",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.35",
"eslint-config-prettier": "^10.1.8",
"postcss": "^8",
"prettier": "^3.8.1",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

3676
frontend/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

93
frontend/public/muse.svg Normal file
View File

@ -0,0 +1,93 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" style="display: block;" viewBox="0 0 2048 887" width="1000" height="433">
<defs>
<linearGradient id="Gradient1" gradientUnits="userSpaceOnUse" x1="210.508" y1="105.269" x2="586.128" y2="792.383">
<stop class="stop0" offset="0" stop-opacity="1" stop-color="rgb(228,24,73)"/>
<stop class="stop1" offset="1" stop-opacity="1" stop-color="rgb(253,56,71)"/>
</linearGradient>
</defs>
<path transform="translate(0,0)" fill="url(#Gradient1)" d="M 376.146 54.9048 C 394.494 52.0082 450.721 54.6372 467.605 60.9459 C 476.746 62.3192 486.198 63.3892 495.173 65.5997 C 515.789 70.6779 564.525 86.5296 576.81 103.019 C 582.859 111.139 585.986 121.422 584.272 131.496 C 582.52 141.792 577.15 152.234 568.387 158.191 C 559.717 164.085 547.095 166.763 536.819 164.74 C 522.557 161.932 508.608 154.389 494.635 150.13 C 458.749 139.194 420.04 135.901 382.699 137.271 L 382.087 137.409 C 377.052 138.507 371.716 138.434 366.594 139.112 C 353.633 140.827 340.292 142.838 327.621 146.09 C 253.423 165.138 184.133 214.131 142.088 278.203 C 136.733 286.364 128.204 304.126 123.434 309.932 C 111.073 334.935 104.381 360.26 95.8454 386.549 L 95.7672 387.361 C 94.7432 397.501 91.6381 407.384 90.5873 417.508 C 89.7119 425.942 89.9836 434.794 89.8723 443.272 C 89.4501 475.416 91.5586 497.422 98.6317 528.874 C 100.45 536.927 102.824 544.844 105.737 552.569 C 107.655 557.772 110.601 563.483 111.543 568.893 C 117.587 584.712 125.65 599.216 133.531 614.15 C 143.94 630.512 155.472 646.586 168.527 660.964 C 173.349 666.275 188.564 678.675 191.256 683.236 C 251.557 733.221 311.796 756.819 390.291 761.391 C 394.05 760.783 398.822 761.571 402.708 761.517 C 415.486 761.341 428.664 761.316 441.345 759.599 C 485.651 753.598 530.394 737.673 568.043 713.473 C 575.81 708.481 583.428 703.049 590.788 697.474 C 594.071 694.988 596.979 692.019 600.533 689.912 L 601.084 689.592 C 616.553 677.289 631.234 662.355 643.509 646.888 C 644.812 642.001 654.107 632.73 657.41 628.311 C 676.308 596.768 687.36 578.365 698.997 542.562 C 702.091 535.055 703.233 526.973 705.666 519.255 C 705.725 507.623 709.936 495.566 711.264 483.918 C 713.169 467.214 712.726 450.024 712.977 433.224 L 712.92 431.802 C 711.531 426.337 711.557 419.947 710.818 414.282 C 709.582 404.813 707.787 395.546 705.774 386.213 C 700.989 351.447 677.646 305.278 659.181 275.386 C 653.455 266.116 644.933 257.388 640.642 247.468 C 637.876 241.072 637.34 233.314 637.61 226.417 C 637.996 216.532 640.733 207.138 648.335 200.309 C 656.928 192.59 671.867 189.783 683.178 190.458 C 692.932 191.041 701.105 194.806 707.559 202.061 L 714.51 211.387 C 737.619 243.545 757.234 275.572 771.319 312.816 C 774.839 322.124 777.095 331.789 780.403 341.131 C 797.083 399.915 800.154 457.173 790.602 517.54 C 788.234 532.501 785.885 549.101 779.732 563.007 C 768.839 611.398 725.068 690.424 687.592 724.592 C 658.332 752.202 618.553 787.704 581.018 802.842 C 562.995 813.263 542.695 820.874 522.909 827.195 C 447.846 851.214 367.429 852.958 291.395 832.215 C 275.181 827.889 259.267 822.031 243.62 816.005 C 220.843 805.593 199.356 794.344 178.44 780.521 C 91.1047 722.799 33.0984 630.627 12.3333 528.81 C 11.7547 526.892 11.3367 524.926 10.9201 522.968 C 6.97496 504.429 5.54503 484.947 4.99442 466.026 C 2.49649 384.006 25.2737 303.201 70.2396 234.56 C 110.525 174.202 165.964 125.49 231.002 93.3037 C 244.395 86.5531 259.95 78.4536 274.682 75.364 C 288.042 70.2143 301.587 66.3748 315.565 63.2966 C 322.289 61.7913 329.104 59.9829 335.973 59.355 C 347.591 55.8138 364.002 55.0317 376.146 54.9048 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 705.774 386.213 C 700.989 351.447 677.646 305.278 659.181 275.386 C 653.455 266.116 644.933 257.388 640.642 247.468 C 637.876 241.072 637.34 233.314 637.61 226.417 C 637.996 216.532 640.733 207.138 648.335 200.309 C 656.928 192.59 671.867 189.783 683.178 190.458 C 692.932 191.041 701.105 194.806 707.559 202.061 L 714.51 211.387 C 737.619 243.545 757.234 275.572 771.319 312.816 C 774.839 322.124 777.095 331.789 780.403 341.131 C 797.083 399.915 800.154 457.173 790.602 517.54 C 788.234 532.501 785.885 549.101 779.732 563.007 C 768.839 611.398 725.068 690.424 687.592 724.592 C 658.332 752.202 618.553 787.704 581.018 802.842 C 562.995 813.263 542.695 820.874 522.909 827.195 C 447.846 851.214 367.429 852.958 291.395 832.215 C 275.181 827.889 259.267 822.031 243.62 816.005 C 220.843 805.593 199.356 794.344 178.44 780.521 C 91.1047 722.799 33.0984 630.627 12.3333 528.81 L 13.647 527.729 C 14.402 529.837 15.0932 534.929 16.7981 535.785 C 17.5141 533.357 15.8765 529.94 15.0764 527.549 L 15.9 527.418 C 18.6345 534.095 20.0034 541.498 21.9989 548.453 C 26.9283 565.633 31.6172 583.045 38.2114 599.677 C 64.9422 667.101 116.207 732.766 177.003 772.666 C 189.614 780.943 202.698 789.795 216.058 796.759 C 224.712 801.271 234.424 804.33 242.789 809.289 C 250.667 812.637 258.854 815.525 266.905 818.433 C 349.42 848.238 444.177 848.559 527.411 820.818 C 545.994 814.624 563.998 805.904 581.717 797.604 C 603.127 783.645 625.24 771.395 645.477 755.591 C 658.557 745.377 670.301 733.195 682.386 721.844 C 728.545 674.13 761.29 615.077 777.312 550.652 C 797.154 486.362 792.669 418.058 778.475 353.134 C 767.793 317.922 754.532 281.526 735.293 249.969 C 728.138 238.235 719.461 227.302 711.837 215.843 C 708.68 211.913 705.464 208.03 702.19 204.197 C 693.73 197.534 684.075 194.889 673.357 196.197 C 665.575 197.147 657.47 200.047 650.684 203.95 C 644.436 214.061 640.809 227.514 643.576 239.394 C 646.334 251.238 671.063 283.842 678.471 297.084 C 693.576 324.081 703.652 353.558 710.477 383.619 C 713.023 393.568 715.018 403.747 715.532 414.027 L 717.071 423.777 C 720.72 442.779 716.413 511.912 708.485 528.149 C 706.525 537.703 703.771 546.858 700.269 555.961 C 697.102 566.785 686.883 593.093 679.438 600.774 C 684.103 591.158 688.576 581.632 692.348 571.622 C 685.985 582.691 667.071 624.486 657.41 628.311 C 676.308 596.768 687.36 578.365 698.997 542.562 C 702.091 535.055 703.233 526.973 705.666 519.255 C 705.725 507.623 709.936 495.566 711.264 483.918 C 713.169 467.214 712.726 450.024 712.977 433.224 L 712.92 431.802 C 711.531 426.337 711.557 419.947 710.818 414.282 C 709.582 404.813 707.787 395.546 705.774 386.213 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 89.8436 390.816 C 92.8902 370.901 99.7863 351.425 106.984 332.654 C 110.049 324.66 121.333 295.736 127.989 292.773 L 129.293 294.957 C 125.28 299.606 124.047 303.884 123.434 309.932 C 111.073 334.935 104.381 360.26 95.8454 386.549 L 95.7672 387.361 C 94.7432 397.501 91.6381 407.384 90.5873 417.508 C 89.7119 425.942 89.9836 434.794 89.8723 443.272 C 89.4501 475.416 91.5586 497.422 98.6317 528.874 C 100.45 536.927 102.824 544.844 105.737 552.569 C 107.655 557.772 110.601 563.483 111.543 568.893 C 117.587 584.712 125.65 599.216 133.531 614.15 C 129.444 612.078 127.432 605.723 123.651 602.599 C 123.042 605.188 129.828 614.104 130.748 617.464 C 119.822 604.665 113.472 587.447 107.114 572.034 C 106.864 571.666 106.835 571.641 106.596 571.18 C 84.8375 529.334 79.3733 455.273 86.371 409.569 C 87.2906 403.563 87.7619 396.511 89.8436 390.816 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 467.605 60.9459 C 476.746 62.3192 486.198 63.3892 495.173 65.5997 C 515.789 70.6779 564.525 86.5296 576.81 103.019 C 582.859 111.139 585.986 121.422 584.272 131.496 C 582.52 141.792 577.15 152.234 568.387 158.191 C 559.717 164.085 547.095 166.763 536.819 164.74 C 522.557 161.932 508.608 154.389 494.635 150.13 C 458.749 139.194 420.04 135.901 382.699 137.271 C 401.38 126.952 478.879 143.57 501.374 150.331 C 513.555 153.993 525.503 160.481 537.856 163.083 C 547.196 165.051 558.991 161.578 566.89 156.404 C 575.585 150.709 579.423 143.326 581.522 133.4 C 583.575 123.696 581.557 112.369 575.994 104.046 C 573.49 100.3 570.487 98.0368 566.074 97.1358 L 565.314 97.7949 C 569.437 99.0598 571.952 101.348 574.235 104.996 C 580.205 114.537 580.949 124.563 578.348 135.333 C 576.615 142.508 573.838 150.994 567.234 155.03 L 566.34 154.256 C 566.994 151.676 568.333 150.62 570.467 149.029 C 570.722 148.838 570.982 148.652 571.227 148.448 C 575.662 144.773 577.981 132.345 578.447 126.705 C 579.116 118.618 577.833 111.784 572.383 105.569 C 558.678 89.9432 495.022 68.0075 474.068 66.5128 C 471.389 64.8806 469.466 63.4925 467.605 60.9459 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 191.256 683.236 C 251.557 733.221 311.796 756.819 390.291 761.391 C 384.048 764.932 370.279 761.634 362.687 762.291 C 361.888 762.36 362.274 762.212 361.617 762.991 C 367.962 764.537 376.21 766.043 382.625 764.548 C 382.899 764.484 383.171 764.412 383.444 764.344 L 384.048 765.039 C 383.31 765.409 382.77 765.691 381.943 765.815 C 373.428 767.101 362.837 765.155 354.356 763.843 C 317.586 758.155 264.109 743.033 234.667 719.807 L 241.003 722.723 C 233.991 716.274 224.813 711.666 216.97 706.228 C 211.114 702.167 192.413 689.79 191.256 683.236 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 376.146 54.9048 C 394.494 52.0082 450.721 54.6372 467.605 60.9459 C 469.466 63.4925 471.389 64.8806 474.068 66.5128 C 462.177 64.3163 450.083 61.7173 438.022 60.8079 C 415.361 59.0993 392.468 60.3754 369.865 58.5625 C 373.126 56.6957 426.662 57.683 431.679 59.027 C 432.503 59.2478 433.394 58.6279 434.177 58.2957 C 425.546 51.9762 387.515 60.0202 376.146 54.9048 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 315.565 63.2966 C 322.289 61.7913 329.104 59.9829 335.973 59.355 L 335.213 61.0976 C 331.824 61.9908 328.07 62.6586 325.211 64.7655 L 330.836 63.1888 C 323.47 66.8573 315.681 68.5767 307.736 70.4894 C 295.905 73.9845 287.012 75.9701 274.682 75.364 C 288.042 70.2143 301.587 66.3748 315.565 63.2966 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 601.084 689.592 C 616.553 677.289 631.234 662.355 643.509 646.888 C 642.944 649.312 642.433 651.632 641.348 653.888 C 642.715 652.59 643.982 651.396 645.593 650.391 C 643.168 657.971 615.843 685.786 608.409 689.944 C 607.817 690.275 607.352 690.377 606.696 690.521 L 610.446 685.035 C 607.305 687.498 605.104 689.098 601.084 689.592 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1610.11 53.6333 C 1610.18 47.1911 1610.04 40.7484 1609.67 34.3162 C 1612.92 32.1096 1622.54 32.521 1626.83 32.421 C 1701.22 30.6842 1776.03 32.2737 1850.46 32.2788 L 1942.68 32.293 C 1961.78 32.2982 1981.13 31.6609 2000.17 33.0634 C 2002.29 59.7016 2000.77 87.3161 2000.5 114.078 C 2000.18 145.397 2000.71 176.761 2000.65 208.089 L 2000.53 275.627 C 2000.59 287.904 2001.86 301.31 2000.39 313.448 C 1999.08 314.796 1998.19 315.254 1996.36 315.657 C 1983.81 313.542 1969.62 314.801 1956.84 314.86 L 1885.1 315.187 C 1843.56 314.96 1802.02 314.957 1760.49 315.178 C 1737.73 315.239 1714.92 314.515 1692.21 316.146 C 1682.97 316.356 1673.71 316.127 1664.47 316.03 C 1647.62 319.103 1627.49 316.171 1610.18 316.817 C 1609.42 279.846 1609.91 242.781 1609.91 205.802 L 1610.11 53.6333 z M 1931 253.687 C 1934.04 247.761 1931.57 214.273 1932.58 204.615 C 1914.48 203.829 1896.3 204.085 1878.18 204.045 C 1864.82 204.016 1851.24 203.501 1837.9 204.271 C 1837.75 219.838 1836.4 236.461 1838.1 251.9 C 1840.18 253.488 1842.92 253.076 1845.51 253.213 L 1901.24 253.025 C 1910.95 253.041 1921.4 252.368 1931 253.687 z M 1770.88 142.098 C 1771.08 126.574 1770.3 110.547 1772.02 95.1281 C 1753 96.0183 1693.67 98.7431 1678.07 94.8681 C 1678.09 111.235 1678.34 127.63 1678.14 143.994 C 1708.74 143.936 1739.89 145.247 1770.43 143.809 L 1770.88 142.098 z M 1934.72 92.3052 C 1911.33 88.9068 1886.2 90.9728 1862.62 91.3784 C 1853.2 91.5404 1843.25 90.4708 1833.95 92.0614 C 1833.84 102.176 1831.75 140.452 1835.62 146.951 C 1843.68 147.053 1854.75 146.124 1862.56 144.157 C 1885.64 143.39 1908.85 144.079 1931.95 143.907 C 1931.95 138.803 1932.1 133.689 1932.2 128.585 C 1935.08 117.315 1933.3 104.041 1934.72 92.3052 z M 1677.37 253.007 C 1708.52 253.373 1739.72 253.078 1770.88 253.105 L 1771.3 225.345 C 1771.16 218.243 1771.08 211.133 1770.85 204.034 C 1759.02 205.292 1746.28 204.266 1734.35 204.189 C 1715.7 204.069 1697.03 204.4 1678.38 204.082 C 1678.04 215.578 1680.36 244.069 1677.37 253.007 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1610.11 53.6333 C 1610.18 47.1911 1610.04 40.7484 1609.67 34.3162 C 1612.92 32.1096 1622.54 32.521 1626.83 32.421 C 1701.22 30.6842 1776.03 32.2737 1850.46 32.2788 L 1942.68 32.293 C 1961.78 32.2982 1981.13 31.6609 2000.17 33.0634 C 2002.29 59.7016 2000.77 87.3161 2000.5 114.078 C 2000.18 145.397 2000.71 176.761 2000.65 208.089 L 2000.53 275.627 C 2000.59 287.904 2001.86 301.31 2000.39 313.448 C 1999.08 314.796 1998.19 315.254 1996.36 315.657 C 1993.64 303.906 1995.65 250.184 1995.69 234.585 C 1995.92 168.649 1995.78 102.714 1995.29 36.7799 C 1925.27 37.7276 1855.17 36.83 1785.15 36.817 C 1728.68 36.8065 1672.04 37.9569 1615.6 36.5773 C 1614.95 43.6695 1612.3 47.175 1610.11 53.6333 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1664.47 316.03 C 1666.5 314.805 1667.46 314.503 1668.45 312.273 L 1665.67 311.474 C 1671.97 311.086 1686.89 308.672 1692.15 311.634 L 1690.91 313.897 L 1692.21 316.146 C 1682.97 316.356 1673.71 316.127 1664.47 316.03 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1845.51 253.213 C 1844.99 254.36 1844.2 255.349 1843.47 256.378 C 1840.74 257.034 1839.14 257.385 1836.42 256.348 C 1834.75 254.769 1834.05 253.306 1833.8 251.037 C 1833.38 247.235 1834.13 243.089 1834.11 239.233 C 1834.04 230.355 1831.04 208.119 1837.09 201.151 C 1839.3 198.598 1842.79 197.701 1846.04 197.531 C 1859.1 196.843 1873.03 198.612 1886.2 198.769 C 1900.33 198.938 1915.64 197.011 1929.62 198.925 C 1932.66 199.342 1933.85 199.862 1936.11 201.931 L 1934.68 204.411 L 1932.58 204.615 C 1914.48 203.829 1896.3 204.085 1878.18 204.045 C 1864.82 204.016 1851.24 203.501 1837.9 204.271 C 1837.75 219.838 1836.4 236.461 1838.1 251.9 C 1840.18 253.488 1842.92 253.076 1845.51 253.213 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" fill-opacity="0.988235" d="M 1833.95 92.0614 C 1843.25 90.4708 1853.2 91.5404 1862.62 91.3784 C 1886.2 90.9728 1911.33 88.9068 1934.72 92.3052 C 1933.3 104.041 1935.08 117.315 1932.2 128.585 C 1932.13 117.541 1931.83 106.443 1932.31 95.4083 C 1900.97 94.8136 1869.11 97.621 1837.92 95.6443 C 1837.75 111.765 1837.75 127.888 1837.91 144.009 C 1846.11 144.051 1854.36 143.908 1862.56 144.157 C 1854.75 146.124 1843.68 147.053 1835.62 146.951 C 1831.75 140.452 1833.84 102.176 1833.95 92.0614 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1677.37 253.007 C 1676.01 249.029 1675.36 206.342 1676.51 201.588 C 1676.74 200.659 1676.51 200.948 1677.41 200.433 C 1683.58 196.905 1762.47 197.876 1771.89 200.114 C 1775.4 203.794 1775.91 210.315 1776.2 215.219 C 1776.32 217.185 1776.54 220.848 1775.2 222.344 C 1774.64 216.979 1774.18 211.604 1773.84 206.222 C 1770.93 210.228 1774.69 219.912 1771.3 225.345 C 1771.16 218.243 1771.08 211.133 1770.85 204.034 C 1759.02 205.292 1746.28 204.266 1734.35 204.189 C 1715.7 204.069 1697.03 204.4 1678.38 204.082 C 1678.04 215.578 1680.36 244.069 1677.37 253.007 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1936.11 201.931 C 1938.65 208.234 1937.95 246.218 1935.33 252.21 C 1935.07 252.803 1934.76 253.362 1934.43 253.919 L 1932.49 254.612 L 1931 253.687 C 1934.04 247.761 1931.57 214.273 1932.58 204.615 L 1934.68 204.411 L 1936.11 201.931 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1006.6 45.4499 C 1031.54 43.1833 1056.7 37.1747 1081.15 31.9075 L 1166.87 13.5267 C 1184.98 9.6218 1204.58 3.5952 1223.1 2.70382 C 1227.92 13.2229 1243.3 49.5666 1243.48 59.1636 C 1241.48 63.1086 1237.17 64.3833 1233.13 65.7144 C 1213.13 72.2883 1183.43 75.7795 1165.67 83.6946 C 1165.77 96.0882 1166.22 108.972 1165.32 121.308 C 1165.5 131.861 1165.51 142.417 1165.35 152.971 C 1185.9 153.481 1206.51 152.376 1227.03 153.395 L 1229.57 154.32 C 1231.82 159.785 1230.47 209.232 1228.99 217.22 C 1221.36 221.118 1176.66 218.927 1165.12 218.805 L 1165.69 233.003 C 1186.68 256.95 1208.07 280.214 1228.61 304.623 C 1233.71 310.689 1239.48 316.447 1244.29 322.719 C 1236.36 331.171 1208.23 368.286 1200.38 369.973 C 1196.71 368.94 1195.46 367.551 1193.56 364.221 C 1185.87 350.734 1178.82 336.778 1171.16 323.225 C 1169.29 319.923 1167.83 315.741 1165.57 312.761 L 1161.87 313.52 L 1159.86 313.826 C 1157.91 317.848 1159.13 490.857 1159.14 509.002 C 1145.76 510.751 1115.26 511.795 1102.82 509.487 C 1101.45 507.065 1101.81 504.262 1101.87 501.573 C 1101.28 499.136 1100.77 496.759 1100.43 494.27 L 1098.89 498.558 L 1098.39 503.214 L 1097.35 503.129 L 1095.92 499.799 C 1095.86 471.761 1095.12 443.548 1096.09 415.536 C 1095.38 399.262 1096.02 382.718 1096.5 366.436 C 1095.43 354.843 1098.31 342.422 1096.3 331.133 C 1094.22 332.644 1093.54 335.68 1092.65 338.015 C 1086.5 355.983 1076.78 383.433 1064.1 397.913 L 1060.97 393.867 C 1057.86 398.825 1054.89 404.149 1051.36 408.806 L 1055.69 410.177 C 1050.14 417.984 1039.76 431.194 1031.55 435.802 C 1030.6 436.331 1029.46 435.666 1028.45 435.368 C 1025.23 430.779 1025.33 423.663 1023.19 418.389 C 1017.87 405.294 1004.74 390.662 1003.89 376.48 C 1003.53 370.47 1008.7 365.943 1012.39 361.839 C 1015.56 356.263 1020.27 350.809 1023.91 345.415 C 1031.48 334.187 1038.5 322.905 1045.51 311.328 C 1052.16 296.668 1060.7 282.29 1068.14 267.958 C 1073.98 251.756 1082.05 236.285 1087.94 219.978 C 1064.75 219.246 1041.57 219.542 1018.39 218.85 C 1018.39 197.742 1020.26 175.702 1016.45 154.876 C 1036.44 153.75 1080.04 150.789 1098.35 154.211 C 1096.51 136.91 1101.28 111.579 1097.41 95.7053 L 1095.49 94.8062 L 1094.39 97.2745 C 1080.68 100.429 1036.63 107.213 1023.9 106.893 C 1019.48 88.4613 1013.94 62.5029 1006.6 45.4499 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1006.6 45.4499 C 1031.54 43.1833 1056.7 37.1747 1081.15 31.9075 L 1166.87 13.5267 C 1184.98 9.6218 1204.58 3.5952 1223.1 2.70382 C 1227.92 13.2229 1243.3 49.5666 1243.48 59.1636 C 1241.48 63.1086 1237.17 64.3833 1233.13 65.7144 C 1213.13 72.2883 1183.43 75.7795 1165.67 83.6946 C 1165.77 96.0882 1166.22 108.972 1165.32 121.308 C 1165.17 108.312 1161.23 93.7619 1162.46 81.3738 C 1167.31 72.6395 1224.87 64.7892 1237.57 59.0255 L 1238.01 58.8212 C 1234.92 42.6656 1226.92 21.5456 1218.09 7.64251 C 1183.03 16.4377 1147.04 22.5421 1111.67 29.9825 C 1081.42 36.346 1044.19 46.3595 1014.09 48.6933 C 1017.42 66.1257 1021.73 84.9866 1027.72 101.699 C 1037.27 99.5806 1047.53 99.321 1057.25 97.9256 C 1067.59 96.4403 1078.76 93.28 1089.14 93.0457 C 1091.63 92.9897 1093.37 93.5455 1095.49 94.8062 L 1094.39 97.2745 C 1080.68 100.429 1036.63 107.213 1023.9 106.893 C 1019.48 88.4613 1013.94 62.5029 1006.6 45.4499 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1227.03 153.395 L 1229.57 154.32 C 1231.82 159.785 1230.47 209.232 1228.99 217.22 C 1221.36 221.118 1176.66 218.927 1165.12 218.805 L 1165.69 233.003 C 1186.68 256.95 1208.07 280.214 1228.61 304.623 C 1233.71 310.689 1239.48 316.447 1244.29 322.719 C 1236.36 331.171 1208.23 368.286 1200.38 369.973 C 1196.71 368.94 1195.46 367.551 1193.56 364.221 C 1185.87 350.734 1178.82 336.778 1171.16 323.225 C 1169.29 319.923 1167.83 315.741 1165.57 312.761 L 1161.87 313.52 C 1162.79 312.227 1163.93 311.067 1164.99 309.887 L 1166.66 309.967 C 1176.31 318.59 1194.1 354.305 1200.74 367.374 C 1211.15 351.42 1225.19 336.886 1237.77 322.592 C 1232.06 314.885 1224.74 307.856 1218.47 300.554 C 1206.08 286.135 1194.14 271.427 1181.47 257.237 C 1175.55 250.609 1166.98 243.796 1162.42 236.366 C 1160.81 233.731 1160.19 229.324 1159.97 226.307 C 1159.71 222.718 1159.8 217.944 1162.42 215.168 C 1165.18 212.253 1216.51 213.835 1224.21 213.874 C 1224.9 199.546 1221.88 165.12 1227.03 153.395 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1016.45 154.876 C 1036.44 153.75 1080.04 150.789 1098.35 154.211 L 1098.86 157.509 C 1093.04 162.702 1033.68 159.729 1021.95 160.202 C 1022.35 178.003 1023.23 196.081 1021.96 213.849 C 1035.1 214.169 1048.2 215.267 1061.34 215.599 C 1068.9 215.79 1077.33 214.713 1084.77 215.904 C 1086.92 216.25 1088.38 217.037 1090.04 218.449 C 1091.77 221.73 1091.02 224.48 1089.98 227.878 C 1085.85 241.274 1077.5 253.557 1073.43 266.75 L 1068.14 267.958 C 1073.98 251.756 1082.05 236.285 1087.94 219.978 C 1064.75 219.246 1041.57 219.542 1018.39 218.85 C 1018.39 197.742 1020.26 175.702 1016.45 154.876 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1096.3 331.133 L 1098.37 332.127 L 1100.45 329.983 C 1102.99 334.17 1102.09 344.035 1102.13 349.091 C 1101.85 376.406 1101.72 403.722 1101.74 431.038 C 1101.9 454.468 1102.81 478.159 1101.87 501.573 C 1101.28 499.136 1100.77 496.759 1100.43 494.27 L 1098.89 498.558 L 1098.39 503.214 L 1097.35 503.129 L 1095.92 499.799 C 1095.86 471.761 1095.12 443.548 1096.09 415.536 C 1095.38 399.262 1096.02 382.718 1096.5 366.436 C 1095.43 354.843 1098.31 342.422 1096.3 331.133 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1098.09 397.344 C 1103.18 430.466 1098.83 465.213 1098.89 498.558 L 1098.39 503.214 L 1097.35 503.129 L 1095.92 499.799 C 1095.86 471.761 1095.12 443.548 1096.09 415.536 C 1096.79 409.475 1097.46 403.411 1098.09 397.344 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1096.3 331.133 L 1098.37 332.127 L 1100.45 329.983 C 1102.99 334.17 1102.09 344.035 1102.13 349.091 C 1101.25 350.375 1099.94 351.819 1099.24 353.183 C 1097.54 356.483 1099.89 371.953 1097.7 373.359 C 1097.32 371.052 1096.98 368.724 1096.5 366.436 C 1095.43 354.843 1098.31 342.422 1096.3 331.133 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1055.69 410.177 C 1050.14 417.984 1039.76 431.194 1031.55 435.802 C 1030.6 436.331 1029.46 435.666 1028.45 435.368 C 1025.23 430.779 1025.33 423.663 1023.19 418.389 C 1017.87 405.294 1004.74 390.662 1003.89 376.48 C 1003.53 370.47 1008.7 365.943 1012.39 361.839 C 1012 367.193 1010.01 370.558 1007.25 375.007 C 1015.56 392.717 1024.97 410.345 1031.75 428.703 C 1038.97 422.747 1045.27 415.897 1051.36 408.806 L 1055.69 410.177 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1060.97 393.867 C 1069.61 382.495 1075.33 368.04 1080.97 354.989 C 1082.91 350.497 1084.77 344.948 1087.38 340.887 C 1088.86 338.592 1090.22 338.608 1092.65 338.015 C 1086.5 355.983 1076.78 383.433 1064.1 397.913 L 1060.97 393.867 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1045.51 311.328 C 1052.16 296.668 1060.7 282.29 1068.14 267.958 L 1073.43 266.75 C 1068.67 277.741 1064.11 289.546 1056.19 298.684 L 1056.92 297.011 C 1053.86 301.211 1050 309.144 1045.51 311.328 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1024.1 600.841 C 1047.97 600.009 1071.69 601.581 1095.51 600.771 C 1099.64 604.33 1170.28 752.401 1171.95 760.294 C 1176.61 751.322 1180.84 742.243 1184.93 732.998 C 1191.38 719.083 1197.76 705.128 1205.41 691.816 C 1205.98 686.662 1243.62 609.471 1248.51 603.039 C 1259.03 598.373 1283.35 601.001 1295.44 601.178 C 1301.24 601.05 1315.75 599.339 1320.16 602.87 C 1319.94 639.745 1319.9 676.621 1320.06 713.496 C 1320.06 731.326 1320.78 749.534 1319.91 767.322 C 1319.01 759.086 1318.34 750.784 1316.68 742.659 C 1315.21 769.119 1316.01 795.856 1316.08 822.353 C 1316.12 834.41 1317.27 848.465 1315.16 860.225 L 1314.9 871.753 C 1301.12 871.207 1287.28 871.195 1273.48 870.972 C 1271.7 825.968 1273.02 780.543 1273.06 735.492 C 1273.08 716.382 1273.89 696.863 1272.4 677.824 L 1267.99 676.603 L 1267.63 675.295 L 1266.67 675.836 C 1264.44 681.942 1261.74 687.639 1258.79 693.429 C 1245.24 722.308 1231.21 750.965 1216.72 779.388 C 1206.98 798.855 1196.52 817.923 1188.53 838.222 C 1177.64 838.068 1166.43 838.44 1155.58 837.607 C 1145.89 810.774 1132.07 786.333 1119.58 760.765 C 1111.04 746.688 1105.01 731.328 1097.53 716.692 C 1089.74 705.324 1083.36 685.16 1075.1 671.059 C 1074.43 686.924 1075.9 703.244 1075.93 719.184 C 1076.03 771.654 1076.95 824.35 1075.59 876.794 C 1062.17 877.872 1037.93 878.989 1025.08 876.684 C 1021.51 865.099 1023.86 801.739 1023.86 784.074 C 1023.85 723.089 1022.53 661.788 1024.1 600.841 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1257.38 685.941 C 1258.15 683.808 1258.89 681.379 1260.01 679.406 C 1261.53 676.728 1263.97 676.529 1266.67 675.836 C 1264.44 681.942 1261.74 687.639 1258.79 693.429 C 1245.24 722.308 1231.21 750.965 1216.72 779.388 C 1206.98 798.855 1196.52 817.923 1188.53 838.222 C 1177.64 838.068 1166.43 838.44 1155.58 837.607 C 1145.89 810.774 1132.07 786.333 1119.58 760.765 L 1124.51 760.253 C 1131.96 774.613 1139.34 789.102 1146.23 803.735 C 1150.72 813.264 1154.39 823.749 1160.18 832.525 C 1168.3 832.727 1176.37 832.42 1184.47 832.109 C 1191.31 822.822 1199.47 801.76 1204.97 790.568 C 1222.11 755.658 1239.16 720.284 1257.38 685.941 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1095.51 600.771 C 1099.64 604.33 1170.28 752.401 1171.95 760.294 L 1169.41 762.968 C 1164.11 755.23 1160.62 746.15 1156.61 737.705 L 1139.57 702.304 C 1125.34 672.546 1108.39 642.438 1097.1 611.494 C 1095.78 607.861 1094.88 604.678 1095.51 600.771 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1295.44 601.178 C 1301.24 601.05 1315.75 599.339 1320.16 602.87 C 1319.94 639.745 1319.9 676.621 1320.06 713.496 C 1320.06 731.326 1320.78 749.534 1319.91 767.322 C 1319.01 759.086 1318.34 750.784 1316.68 742.659 C 1315.21 769.119 1316.01 795.856 1316.08 822.353 C 1316.12 834.41 1317.27 848.465 1315.16 860.225 C 1313.6 841.169 1315.01 820.666 1315 801.472 C 1314.97 750.692 1316.1 699.905 1315.32 649.126 C 1315.09 633.754 1316.66 617.607 1314.96 602.396 C 1308.48 602.278 1301.81 602.444 1295.44 601.178 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1184.93 732.998 C 1191.38 719.083 1197.76 705.128 1205.41 691.816 C 1205.06 700.163 1194.49 725.473 1189.98 733.031 C 1186.31 741.754 1181.62 758.633 1173.98 764.009 L 1171.41 764.502 L 1169.41 762.968 L 1171.95 760.294 C 1176.61 751.322 1180.84 742.243 1184.93 732.998 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1100.83 713.389 C 1109.2 728.732 1116.18 744.852 1124.51 760.253 L 1119.58 760.765 C 1111.04 746.688 1105.01 731.328 1097.53 716.692 L 1100.83 713.389 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1075.1 671.059 L 1075.12 668.605 L 1076.38 667.77 C 1085.98 673.264 1094.56 702.882 1100.83 713.389 L 1097.53 716.692 C 1089.74 705.324 1083.36 685.16 1075.1 671.059 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1862.25 670.478 C 1880.93 667.773 1896.95 668.992 1915.11 674.171 C 1939.98 682.89 1958.96 697.763 1970.61 721.844 C 1974.85 730.544 1977.65 739.876 1978.9 749.474 C 1979.65 762.044 1982.27 780.333 1977.85 792.266 C 1965.44 793.497 1952.36 792.896 1939.89 792.928 L 1868.62 792.706 C 1859.85 792.641 1842.58 790.411 1835.05 793.02 C 1835.06 794.206 1835.12 795.382 1835.26 796.561 C 1836.75 808.376 1843.6 819.298 1852.87 826.583 C 1870.62 840.532 1902.35 844.521 1923.16 835.886 C 1932.33 832.08 1942.21 824.064 1952.03 822.909 C 1961.27 830.284 1969.55 841.111 1977.55 849.937 C 1977.46 850.379 1977.35 850.82 1977.23 851.254 C 1975.77 856.368 1968.02 860.401 1963.63 862.842 C 1930.23 881.449 1894.48 889.16 1856.87 878.407 C 1827.99 870.151 1807.38 854.607 1792.81 828.361 C 1777.42 800.661 1777.72 765.932 1786.24 736.177 C 1789.08 728.564 1792.41 721.458 1796.62 714.498 C 1812 689.073 1834.13 677.333 1862.25 670.478 z M 1832.77 750.534 L 1832.6 753.744 C 1837.03 756.175 1919.83 755.14 1931.45 754.943 L 1932.41 754.175 C 1932.45 753.78 1932.48 753.383 1932.48 752.986 C 1932.63 742.832 1926.78 730.587 1919.78 723.426 C 1909.85 713.275 1898.11 709.974 1884.23 709.793 C 1869.9 710.091 1857.17 712.692 1846.88 723.466 C 1839.09 731.624 1837.4 740.852 1832.77 750.534 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1977.85 792.266 C 1965.44 793.497 1952.36 792.896 1939.89 792.928 L 1868.62 792.706 C 1859.85 792.641 1842.58 790.411 1835.05 793.02 C 1835.06 794.206 1835.12 795.382 1835.26 796.561 C 1836.75 808.376 1843.6 819.298 1852.87 826.583 C 1870.62 840.532 1902.35 844.521 1923.16 835.886 C 1932.33 832.08 1942.21 824.064 1952.03 822.909 C 1961.27 830.284 1969.55 841.111 1977.55 849.937 C 1977.46 850.379 1977.35 850.82 1977.23 851.254 C 1975.77 856.368 1968.02 860.401 1963.63 862.842 C 1930.23 881.449 1894.48 889.16 1856.87 878.407 C 1827.99 870.151 1807.38 854.607 1792.81 828.361 C 1777.42 800.661 1777.72 765.932 1786.24 736.177 C 1787.98 750.767 1786.53 764.692 1786.75 779.273 C 1787.18 807.634 1796.6 836.528 1819.59 854.608 C 1845.01 874.601 1879.25 880.104 1910.71 876.175 C 1933.33 873.35 1953.2 863.533 1972.08 851.205 C 1964.55 843.368 1956.75 835.676 1950.16 827.017 C 1927.95 841.131 1903.52 849.225 1877.18 843.435 C 1861.67 840.028 1846.84 831.35 1838.17 817.8 C 1835.25 813.243 1828.18 798.871 1829.17 793.863 C 1829.7 791.142 1831.65 789.098 1833.82 787.559 C 1842.07 786.012 1851.78 786.992 1860.2 787.011 L 1909.58 787.109 C 1930 787.139 1954.01 785.182 1973.85 787.28 C 1975.72 788.666 1976.68 790.318 1977.85 792.266 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1915.11 674.171 C 1939.98 682.89 1958.96 697.763 1970.61 721.844 C 1974.85 730.544 1977.65 739.876 1978.9 749.474 C 1976.96 750.058 1976.53 750.472 1974.6 749.698 C 1969.64 741.386 1968.35 730.085 1964.59 721.074 C 1956.37 701.386 1935.75 687.009 1916.71 679.202 L 1915.11 674.171 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1862.25 670.478 C 1880.93 667.773 1896.95 668.992 1915.11 674.171 L 1916.71 679.202 C 1910.32 678.039 1904.34 675.455 1897.84 674.535 C 1885.79 672.831 1873.74 675.126 1862.25 670.478 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1832.77 750.534 L 1829.62 750.529 L 1828.67 748.633 C 1829.3 737.861 1837.01 726.372 1844.52 718.98 C 1856.99 706.716 1869.9 703.755 1887 703.878 C 1884.87 705.53 1882.91 706.77 1880.45 707.863 C 1881.61 709.367 1882.43 709.3 1884.23 709.793 C 1869.9 710.091 1857.17 712.692 1846.88 723.466 C 1839.09 731.624 1837.4 740.852 1832.77 750.534 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1887 703.878 C 1898.99 703.387 1912.03 709.519 1920.7 717.574 C 1927.81 724.175 1934.63 734.806 1934.93 744.727 C 1935.03 748.027 1933.13 751.045 1932.41 754.175 C 1932.45 753.78 1932.48 753.383 1932.48 752.986 C 1932.63 742.832 1926.78 730.587 1919.78 723.426 C 1909.85 713.275 1898.11 709.974 1884.23 709.793 C 1882.43 709.3 1881.61 709.367 1880.45 707.863 C 1882.91 706.77 1884.87 705.53 1887 703.878 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1509.72 674.522 C 1524.52 673.337 1539.79 674.077 1554.65 674.261 C 1557.15 674.728 1557.81 674.604 1559.48 676.666 C 1558.01 694.465 1559.82 713.654 1559.82 731.649 C 1559.8 780.374 1560.59 829.246 1558.87 877.94 C 1547.23 877.733 1536.18 877.872 1524.56 878.672 C 1520.77 878.933 1516.8 879.432 1513.78 876.608 C 1508.56 871.724 1510.06 853.707 1509.97 846.797 L 1509.79 847.192 C 1502.71 862.277 1488.37 872.827 1472.93 878.369 C 1453.48 885.349 1426.75 884.654 1408.04 875.717 C 1392.29 868.197 1378.44 855.365 1372.67 838.624 C 1370 830.902 1368.77 822.571 1368.28 814.438 C 1367.6 803.093 1366.65 679.902 1368.75 675.904 C 1369.14 675.169 1372 674.526 1372.89 674.222 L 1418.14 674.141 C 1420.83 717.929 1417.54 762.305 1418.98 806.2 C 1422.11 817.652 1425.69 826.327 1436.65 832.502 C 1445.66 837.578 1455.88 838.986 1466.05 839.365 C 1477.2 837.184 1488.17 833.768 1496.58 825.776 C 1501.63 820.979 1505.09 815.435 1506.6 808.578 C 1510.4 791.304 1508.06 751.908 1508.06 732.246 C 1508.06 712.947 1509.13 693.796 1509.72 674.522 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1554.65 674.261 C 1557.15 674.728 1557.81 674.604 1559.48 676.666 C 1558.01 694.465 1559.82 713.654 1559.82 731.649 C 1559.8 780.374 1560.59 829.246 1558.87 877.94 C 1547.23 877.733 1536.18 877.872 1524.56 878.672 C 1520.77 878.933 1516.8 879.432 1513.78 876.608 C 1508.56 871.724 1510.06 853.707 1509.97 846.797 L 1509.79 847.192 C 1502.71 862.277 1488.37 872.827 1472.93 878.369 C 1453.48 885.349 1426.75 884.654 1408.04 875.717 C 1392.29 868.197 1378.44 855.365 1372.67 838.624 C 1370 830.902 1368.77 822.571 1368.28 814.438 C 1367.6 803.093 1366.65 679.902 1368.75 675.904 C 1369.14 675.169 1372 674.526 1372.89 674.222 C 1373.97 706.292 1373.32 738.434 1373.14 770.517 C 1373.03 788.732 1371.13 809.125 1374.92 826.979 C 1377.5 839.125 1382.79 850.808 1391.94 859.405 C 1406.05 872.651 1426.3 877.804 1445.22 877.145 C 1464.85 876.461 1482.26 871.621 1495.84 856.903 C 1499.93 852.472 1503.37 847.412 1509.7 846.33 C 1511.94 847.139 1512.87 847.422 1513.89 849.756 C 1516.34 855.324 1514.82 864.949 1514.82 871.08 C 1527.54 871.567 1540.27 871.176 1552.99 870.937 C 1553.87 824.628 1554.2 778.311 1553.97 731.995 C 1554.02 713.159 1551.42 692.76 1554.65 674.261 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1466.05 839.365 C 1460.8 840.484 1454.13 841.156 1448.72 841.827 C 1443.07 842.527 1432.96 836.371 1428.56 832.871 C 1422.64 828.169 1416.78 821.805 1416.07 813.947 C 1415.74 810.314 1416.8 808.888 1418.98 806.2 C 1422.11 817.652 1425.69 826.327 1436.65 832.502 C 1445.66 837.578 1455.88 838.986 1466.05 839.365 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1686.26 344.071 C 1707.65 342.909 1729.57 343.183 1750.96 344.27 C 1750.64 367.963 1751.27 391.664 1750.83 415.359 C 1750.86 424.891 1749.2 442.359 1756.24 449.563 C 1766.3 459.867 1812.95 456.58 1827.95 456.475 C 1841.65 456.38 1870.45 459.419 1880.46 449.032 C 1888.5 440.687 1885.34 417.979 1885.04 407.161 C 1904.77 411.367 1923.75 420.442 1943.77 422.862 C 1942.66 447.472 1945.63 480.992 1925.66 499.182 C 1921.62 502.861 1916.86 505.713 1912.23 508.577 C 1899.91 512.096 1887.55 513.952 1874.72 513.965 C 1852.29 515.323 1829.28 514.138 1806.77 514.038 C 1782.8 513.932 1756.97 515.717 1733.3 512.005 C 1723.94 510.537 1716.17 508.099 1708.33 502.759 C 1706.03 501.144 1703.64 499.314 1701.61 497.37 C 1692.41 488.551 1687.08 471.178 1686.84 458.827 C 1683.92 449.183 1686.41 362.882 1686.26 344.071 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1885.04 407.161 C 1904.77 411.367 1923.75 420.442 1943.77 422.862 C 1942.66 447.472 1945.63 480.992 1925.66 499.182 C 1921.62 502.861 1916.86 505.713 1912.23 508.577 L 1911.28 504.136 C 1915.44 502.702 1919.3 499.244 1922.45 496.226 C 1940.11 479.268 1937.38 449.249 1937.84 426.814 C 1922.1 423.748 1906.09 418.259 1890.65 413.853 C 1890.07 425.165 1891.37 437.638 1887.43 448.4 C 1886.72 449.133 1886 449.858 1885.28 450.574 C 1880.75 455.058 1876.08 457.795 1869.89 459.539 C 1852.13 464.546 1813.56 462.296 1793.97 462.144 C 1780.89 462.042 1761.44 462.112 1751.64 451.774 C 1745.35 445.138 1746.06 434.717 1746.3 426.279 C 1746.93 431.815 1747.5 437.616 1748.83 443.028 C 1749.44 445.512 1750.21 448.166 1752.52 449.568 C 1753.18 446.212 1750.35 441.943 1749.6 438.52 C 1748.06 431.552 1748.69 422.103 1750.83 415.359 C 1750.86 424.891 1749.2 442.359 1756.24 449.563 C 1766.3 459.867 1812.95 456.58 1827.95 456.475 C 1841.65 456.38 1870.45 459.419 1880.46 449.032 C 1888.5 440.687 1885.34 417.979 1885.04 407.161 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1686.84 458.827 C 1688.01 461.379 1688.99 465.914 1690.92 467.655 C 1691.87 466.271 1691.48 463.959 1691.43 462.325 C 1694.33 473.282 1696.57 485.677 1704.41 494.304 C 1707.04 497.198 1709.82 498.543 1708.33 502.759 C 1706.03 501.144 1703.64 499.314 1701.61 497.37 C 1692.41 488.551 1687.08 471.178 1686.84 458.827 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1874.72 513.965 C 1884.8 507.63 1899.8 506.316 1911.28 504.136 L 1912.23 508.577 C 1899.91 512.096 1887.55 513.952 1874.72 513.965 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1334.48 13.9338 C 1356.82 11.7735 1380.56 13.0312 1403.03 13.3648 L 1403.28 178.728 C 1403.28 195.197 1405.61 341.541 1400.41 346.659 C 1395.43 351.552 1351.55 348.949 1342.66 348.814 C 1339.99 348.69 1337.1 348.93 1334.83 347.359 C 1332.39 340.545 1334.27 288.507 1334.28 277.413 L 1334.35 117.816 C 1334.4 83.3054 1332.82 48.3826 1334.48 13.9338 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1334.48 13.9338 C 1356.82 11.7735 1380.56 13.0312 1403.03 13.3648 L 1403.28 178.728 C 1401.91 176.692 1400.72 174.599 1400.4 172.133 C 1398.71 159.197 1399.79 144.773 1399.73 131.669 C 1399.57 94.1965 1401.33 56.787 1399.74 19.3588 C 1379.58 18.9091 1359.2 20.014 1339.11 19.094 L 1339.07 238.008 L 1339.09 306.158 C 1339.09 317.838 1338.29 330.152 1339.46 341.758 C 1339.75 344.669 1340.69 346.635 1342.66 348.814 C 1339.99 348.69 1337.1 348.93 1334.83 347.359 C 1332.39 340.545 1334.27 288.507 1334.28 277.413 L 1334.35 117.816 C 1334.4 83.3054 1332.82 48.3826 1334.48 13.9338 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1437.81 285.832 C 1450.19 288.726 1462.02 297.18 1473.33 302.966 C 1482.13 307.466 1491.19 311.454 1500.05 315.823 C 1468.93 380.537 1435.5 424.255 1374.31 463.085 C 1372.58 462.337 1371.34 461.017 1369.98 459.756 C 1363.85 463.458 1357.79 467.159 1352.21 471.672 C 1349.51 473.859 1347.67 476.765 1344.74 478.628 L 1345.07 480.114 C 1328.25 490.37 1289 509.674 1270.37 513.707 C 1261.26 516.702 1250.77 518.647 1241.24 519.589 C 1238.19 514.057 1211.75 461.266 1211.64 459.456 C 1216.22 456.162 1224.83 455.283 1230.37 453.943 C 1241.44 450.161 1253.02 447.622 1263.92 443.393 C 1315.24 423.471 1374.73 386.65 1407.33 341.82 C 1414.57 329.092 1422.18 316.586 1429.37 303.818 C 1432.11 297.845 1434.63 291.579 1437.81 285.832 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1437.81 285.832 C 1450.19 288.726 1462.02 297.18 1473.33 302.966 C 1482.13 307.466 1491.19 311.454 1500.05 315.823 C 1468.93 380.537 1435.5 424.255 1374.31 463.085 C 1372.58 462.337 1371.34 461.017 1369.98 459.756 C 1426.79 425.832 1465.42 376.585 1493.43 317.476 C 1475.76 310.947 1456.81 301.346 1440.7 291.567 C 1438.7 295.452 1436.95 299.465 1435.16 303.447 L 1429.37 303.818 C 1432.11 297.845 1434.63 291.579 1437.81 285.832 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1230.37 453.943 C 1228.42 456.645 1218.11 459.493 1218.02 460.316 L 1218.7 460.176 C 1221.03 459.699 1223.16 459.584 1225.54 459.633 L 1217.79 462.212 C 1225.83 479.691 1234.34 497.962 1244.02 514.565 C 1249.63 513.139 1263.55 508.21 1268.53 510.972 L 1270.37 513.707 C 1261.26 516.702 1250.77 518.647 1241.24 519.589 C 1238.19 514.057 1211.75 461.266 1211.64 459.456 C 1216.22 456.162 1224.83 455.283 1230.37 453.943 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1429.37 303.818 L 1435.16 303.447 C 1430.04 313.293 1419.07 335.18 1410.87 341.762 C 1409.54 342.046 1408.67 342.252 1407.33 341.82 C 1414.57 329.092 1422.18 316.586 1429.37 303.818 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1660.73 669.673 C 1691.27 663.698 1723.37 673.239 1750.64 686.914 C 1748.74 693.578 1747.37 700.914 1744.12 707.051 C 1741.44 712.983 1740.14 719.451 1736.87 725.075 C 1724.35 720.183 1708.64 716.616 1697.19 711.593 C 1686.82 709.514 1677.27 709.645 1666.76 710.499 C 1659.37 713.565 1653.87 717.201 1647.56 722.098 C 1647.48 726.491 1646.52 732.962 1648.38 736.963 C 1655.47 743.461 1668.99 746.473 1678.22 748.439 L 1702.01 755.475 C 1720 759.434 1741.31 768.689 1751.6 784.857 C 1759.88 797.869 1761.62 814.771 1758.3 829.632 C 1754.46 846.773 1744.67 860.635 1729.81 869.99 C 1703.68 886.436 1670.98 886.434 1641.75 879.628 C 1626.4 876.056 1609.07 868.968 1595.67 860.538 C 1597.39 852.348 1601.78 829.076 1608.58 824.321 C 1610.45 823.011 1612.66 822.913 1614.82 823.39 C 1625.26 825.698 1636.36 832.834 1646.67 836.633 C 1662.24 842.154 1679.68 845.475 1695.84 840.318 C 1704.38 835.618 1707.96 831.049 1711.52 822.188 C 1711.02 819.924 1710.55 817.503 1709.64 815.365 C 1702.17 797.866 1651.68 791.019 1633.95 784.064 C 1621.36 779.127 1608.54 770.922 1603.04 757.996 C 1596.23 741.965 1598.23 720.557 1604.61 704.766 C 1605.41 703.433 1606.23 702.119 1607.08 700.824 C 1620.56 680.411 1637.68 674.379 1660.73 669.673 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1662.99 706.269 C 1665.64 707.659 1665.67 707.712 1666.76 710.499 C 1659.37 713.565 1653.87 717.201 1647.56 722.098 C 1647.48 726.491 1646.52 732.962 1648.38 736.963 C 1655.47 743.461 1668.99 746.473 1678.22 748.439 L 1702.01 755.475 C 1720 759.434 1741.31 768.689 1751.6 784.857 C 1759.88 797.869 1761.62 814.771 1758.3 829.632 C 1754.46 846.773 1744.67 860.635 1729.81 869.99 C 1703.68 886.436 1670.98 886.434 1641.75 879.628 C 1626.4 876.056 1609.07 868.968 1595.67 860.538 C 1597.39 852.348 1601.78 829.076 1608.58 824.321 C 1610.45 823.011 1612.66 822.913 1614.82 823.39 C 1625.26 825.698 1636.36 832.834 1646.67 836.633 C 1643.63 836.776 1640.71 836.742 1637.67 836.503 C 1636.84 836.919 1637.22 836.76 1636.53 837.011 C 1627.33 835.457 1620.88 831.105 1613.15 826.317 C 1607.95 836.107 1604.22 846.187 1601.04 856.792 C 1609.69 862.988 1619.7 867.705 1629.82 870.931 C 1657.33 879.709 1695.82 882.263 1722.23 868.386 C 1735.23 861.558 1746.87 849.806 1751.25 835.565 C 1755.74 820.97 1755.24 802.618 1747.98 788.976 C 1737.89 770.007 1715.12 764.503 1696.12 758.726 C 1689.65 758.071 1682.91 754.848 1676.34 753.58 C 1665.89 750.175 1650.3 748.155 1644.27 737.694 C 1640.53 731.202 1642.44 725 1644.3 718.319 C 1650.11 712.622 1655.37 709.153 1662.99 706.269 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1604.61 704.766 C 1604.65 704.931 1604.69 705.095 1604.72 705.261 C 1606.57 714.069 1604.59 723.13 1604.06 732 C 1603.15 747.367 1608.62 763.736 1621.8 772.532 C 1642.73 786.497 1709.86 790.767 1715.08 816.492 C 1715.38 817.993 1715.52 819.528 1715.65 821.052 C 1715.64 826.168 1714.21 831.196 1710.47 834.895 C 1705.89 839.433 1702.14 840.336 1695.84 840.318 C 1704.38 835.618 1707.96 831.049 1711.52 822.188 C 1711.02 819.924 1710.55 817.503 1709.64 815.365 C 1702.17 797.866 1651.68 791.019 1633.95 784.064 C 1621.36 779.127 1608.54 770.922 1603.04 757.996 C 1596.23 741.965 1598.23 720.557 1604.61 704.766 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1660.73 669.673 C 1691.27 663.698 1723.37 673.239 1750.64 686.914 C 1748.74 693.578 1747.37 700.914 1744.12 707.051 C 1743.86 704.044 1743.45 701.754 1742.18 699.003 L 1744.59 692.183 C 1743.42 686.139 1736.45 684.167 1731.19 682.4 C 1722.53 679.487 1713.68 676.986 1704.63 675.613 C 1694.02 674.002 1668.52 675.896 1660.73 669.673 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1742.18 699.003 C 1743.45 701.754 1743.86 704.044 1744.12 707.051 C 1741.44 712.983 1740.14 719.451 1736.87 725.075 C 1724.35 720.183 1708.64 716.616 1697.19 711.593 L 1698.34 710.376 L 1697.85 706.372 C 1709.78 708.728 1723.46 713.101 1733.88 719.387 C 1736.54 712.553 1739.41 705.79 1742.18 699.003 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1662.99 706.269 C 1669.26 704.599 1691.65 703.746 1697.85 706.372 L 1698.34 710.376 L 1697.19 711.593 C 1686.82 709.514 1677.27 709.645 1666.76 710.499 C 1665.67 707.712 1665.64 707.659 1662.99 706.269 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1258.68 97.9654 C 1258.78 95.7311 1258.73 95.0922 1259.93 93.2493 C 1269.17 89.7092 1284.92 93.6455 1294.73 94.3416 C 1302.83 94.6407 1310.94 95.0189 1319.05 94.9453 C 1318.84 158.821 1313.68 223.32 1298.94 285.621 C 1297.27 292.678 1295.18 307.259 1289.98 312.386 C 1288.65 313.7 1287.75 313.835 1285.97 313.726 C 1280.92 313.417 1274.6 310.454 1270.19 308.075 L 1248.85 298.083 C 1242.9 295.861 1234.86 292.165 1230.6 287.398 C 1230.17 283.138 1243.32 235.723 1245.03 225.775 C 1252.33 183.217 1253.42 140.652 1258.68 97.9654 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1258.68 97.9654 C 1258.78 95.7311 1258.73 95.0922 1259.93 93.2493 C 1269.17 89.7092 1284.92 93.6455 1294.73 94.3416 C 1302.83 94.6407 1310.94 95.0189 1319.05 94.9453 C 1318.84 158.821 1313.68 223.32 1298.94 285.621 C 1297.27 292.678 1295.18 307.259 1289.98 312.386 C 1288.65 313.7 1287.75 313.835 1285.97 313.726 C 1280.92 313.417 1274.6 310.454 1270.19 308.075 L 1248.85 298.083 C 1242.9 295.861 1234.86 292.165 1230.6 287.398 C 1230.17 283.138 1243.32 235.723 1245.03 225.775 C 1252.33 183.217 1253.42 140.652 1258.68 97.9654 C 1259.26 107.744 1260.44 117.631 1260.36 127.422 C 1259.9 180.228 1248.47 234.324 1236.2 285.407 C 1240.84 288.244 1245.46 291.176 1250.18 293.872 C 1258.28 297.285 1266.98 300.327 1274.73 304.453 C 1278.62 306.181 1282.38 308.059 1286.15 310.04 C 1293.78 288.631 1298.19 265.477 1301.85 243.09 C 1304.24 228.47 1307.34 213.733 1308.91 199.016 C 1312.41 166.45 1312.91 132.73 1314.04 99.9949 L 1303.44 98.1334 C 1288.55 98.2177 1273.57 98.4942 1258.68 97.9654 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1463.42 274.843 C 1461.03 275.192 1458.49 275.875 1456.27 274.663 C 1446.55 255.916 1438.37 217.855 1432.83 196.518 C 1424.73 165.393 1416.04 134.213 1408.98 102.85 C 1411.91 100.732 1427.26 99.1802 1431.79 98.0583 C 1443.09 95.2586 1459.4 86.9186 1470.22 86.6587 C 1478.75 106.288 1520.34 240.83 1518.4 258.035 C 1516.64 260.62 1515.26 260.835 1512.43 262.046 C 1503.51 261.363 1473.57 271.946 1463.42 274.843 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1463.42 274.843 C 1461.03 275.192 1458.49 275.875 1456.27 274.663 C 1446.55 255.916 1438.37 217.855 1432.83 196.518 C 1424.73 165.393 1416.04 134.213 1408.98 102.85 C 1411.91 100.732 1427.26 99.1802 1431.79 98.0583 C 1443.09 95.2586 1459.4 86.9186 1470.22 86.6587 C 1478.75 106.288 1520.34 240.83 1518.4 258.035 C 1516.64 260.62 1515.26 260.835 1512.43 262.046 L 1511.19 258.062 C 1511.9 257.421 1513.47 256.424 1513.54 255.487 C 1514.08 247.799 1510.44 236.888 1508.55 229.285 C 1501.38 200.343 1492.38 171.486 1483.16 143.133 C 1477.82 126.685 1470.04 107.797 1467.23 90.9897 C 1449.17 98.559 1433.96 103.983 1414.18 103.679 C 1419.68 129.612 1450.58 261.478 1463.42 274.843 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1968.55 328.412 C 1970.72 327.76 1972.59 326.613 1974.74 327.692 C 1982.4 331.543 2038.58 442.052 2045.53 454.665 C 2031.84 463.552 2003.54 483.539 1988.69 488.08 C 1970.75 451.652 1951.21 415.038 1930.27 380.217 C 1926.46 373.882 1912.12 358.194 1912.72 351.658 C 1915.95 349.185 1920.14 348.408 1924 347.357 C 1931.55 344.442 1939.23 341.687 1946.42 337.945 C 1953.84 334.875 1961.22 331.697 1968.55 328.412 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1968.55 328.412 C 1970.72 327.76 1972.59 326.613 1974.74 327.692 C 1982.4 331.543 2038.58 442.052 2045.53 454.665 C 2031.84 463.552 2003.54 483.539 1988.69 488.08 C 1970.75 451.652 1951.21 415.038 1930.27 380.217 C 1926.46 373.882 1912.12 358.194 1912.72 351.658 C 1915.95 349.185 1920.14 348.408 1924 347.357 C 1923.4 351.102 1920.9 352.233 1919.88 355.655 C 1946.18 396.818 1966.33 440.802 1991.23 482.623 C 2007.43 473.201 2023.75 463.749 2038.88 452.656 C 2034.41 442.441 2028.21 432.519 2022.97 422.638 C 2013.67 405.102 2004.65 387.312 1994.85 370.049 C 1988.47 358.801 1979.43 346.546 1975.41 334.321 C 1975.25 333.524 1975.07 332.574 1974.65 331.869 C 1973.18 329.373 1971.09 329.179 1968.55 328.412 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1968.55 328.412 C 1971.09 329.179 1973.18 329.373 1974.65 331.869 C 1975.07 332.574 1975.25 333.524 1975.41 334.321 C 1970.94 332.417 1956.18 339.404 1946.42 337.945 C 1953.84 334.875 1961.22 331.697 1968.55 328.412 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1604.08 358.446 C 1604.2 355.183 1604.99 351.131 1605.79 347.956 C 1606.33 345.823 1607.65 343.985 1609.59 342.928 C 1617.1 338.853 1631.66 345.014 1639.53 347.12 C 1650.47 349.158 1661.25 351.703 1672.06 354.334 C 1659.76 406.552 1656.12 437.8 1624.18 483.834 C 1621.36 487.894 1617.44 496.537 1613.36 498.899 C 1611.54 499.952 1609.99 499.306 1608.16 498.645 C 1603.31 496.888 1598.26 493.639 1593.87 490.9 C 1586.6 488.498 1579.26 482.7 1572.86 478.484 C 1568.23 475.515 1561.73 472.898 1558.37 468.516 C 1557.44 462.974 1574.21 440.015 1577.66 433.243 C 1585.54 417.755 1593.09 399.948 1597.92 383.226 C 1600.29 375.042 1601.68 366.634 1604.08 358.446 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1639.53 347.12 C 1650.47 349.158 1661.25 351.703 1672.06 354.334 C 1659.76 406.552 1656.12 437.8 1624.18 483.834 C 1621.36 487.894 1617.44 496.537 1613.36 498.899 C 1611.54 499.952 1609.99 499.306 1608.16 498.645 C 1603.31 496.888 1598.26 493.639 1593.87 490.9 L 1598.37 487.952 C 1602.29 490.158 1605.83 492.375 1609.33 495.236 C 1641.87 461.602 1658.84 402.8 1665.94 357.572 C 1660.05 356.21 1654.15 354.947 1648.31 353.383 C 1648.99 353.187 1649.53 353.034 1650.25 353.021 C 1655.32 352.932 1662.26 354.092 1666.08 357.482 C 1670.45 369.742 1649.92 426.867 1645.26 439.899 L 1645.85 439.779 C 1656.11 420.323 1660.79 398.671 1665.53 377.343 C 1666.83 371.515 1668.82 366.079 1668.95 360.046 C 1669.16 349.844 1645.67 353.48 1639.53 347.12 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1572.86 478.484 C 1568.23 475.515 1561.73 472.898 1558.37 468.516 C 1557.44 462.974 1574.21 440.015 1577.66 433.243 C 1585.54 417.755 1593.09 399.948 1597.92 383.226 C 1600.29 375.042 1601.68 366.634 1604.08 358.446 C 1604.74 365.667 1603.07 373.348 1602.05 380.505 C 1603.03 376.337 1603.99 372.161 1604.91 367.98 C 1604.27 387.324 1587.87 422.435 1578.43 439.752 C 1573.38 449.017 1567.03 458.018 1563.52 467.998 C 1567.23 470.098 1570.92 472.314 1574.71 474.262 L 1572.86 478.484 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" d="M 1574.71 474.262 C 1581.13 478.09 1593.25 483.61 1598.37 487.952 L 1593.87 490.9 C 1586.6 488.498 1579.26 482.7 1572.86 478.484 L 1574.71 474.262 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 210.512 303.979 C 212.896 298.215 218.021 292.777 223.161 289.438 C 231.868 283.781 241.066 282.421 251.159 284.58 C 261.104 285.013 270.184 290.877 276.803 298.138 C 284.456 306.532 286.804 316.158 286.192 327.235 C 285.513 339.537 281.7 352.043 272.213 360.438 C 264.162 367.562 253.037 370.669 242.407 369.901 C 231.592 369.102 221.546 364.008 214.51 355.756 C 206.988 346.862 204.102 334.645 205.078 323.189 C 205.629 316.717 207.414 309.696 210.512 303.979 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 251.159 284.58 C 261.104 285.013 270.184 290.877 276.803 298.138 C 284.456 306.532 286.804 316.158 286.192 327.235 C 285.513 339.537 281.7 352.043 272.213 360.438 C 264.162 367.562 253.037 370.669 242.407 369.901 C 231.592 369.102 221.546 364.008 214.51 355.756 C 206.988 346.862 204.102 334.645 205.078 323.189 C 205.629 316.717 207.414 309.696 210.512 303.979 C 212.863 320.548 205.226 334.056 216.582 349.813 C 221.998 357.329 230.707 362.055 239.76 363.524 C 248.677 364.971 259.591 364.14 266.981 358.575 C 274.844 352.655 280.299 336.764 281.372 327.351 C 282.807 314.767 274.521 299.523 263.095 293.7 C 259.094 291.661 249.84 290.221 247.204 286.888 C 247.737 286.785 248.002 286.693 248.523 286.719 C 252.723 286.934 268.875 294.67 271.819 297.659 C 281.419 307.404 282.844 319.203 282.731 332.276 L 283.397 332.191 L 283.491 330.446 C 284.594 307.901 280.538 298.119 259.393 288.233 C 256.682 286.966 253.455 287.344 251.435 284.945 C 251.336 284.829 251.251 284.702 251.159 284.58 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 405.775 585.787 C 402.286 584 398.737 582.435 396.1 579.441 L 396.173 578.049 C 414.175 571.945 434.199 570.435 447.739 555.563 C 451.705 551.207 467.115 525.303 469.081 524.849 C 469.427 524.769 470.483 525.078 470.886 525.144 C 472.583 548.13 480.476 567.719 498.098 582.985 C 502.139 586.486 517.009 594.541 518.474 597.064 C 517.594 599.336 515.543 600.338 513.436 601.275 C 505.224 604.927 496.149 606.07 487.796 609.663 C 475.159 616.204 462.947 628.68 456.767 641.508 C 454.428 646.362 452.452 652.908 448.461 656.58 L 446.773 656.483 C 444.116 650.832 444.886 641.677 443.524 635.347 C 438.787 613.324 424.282 597.46 405.775 585.787 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 405.775 585.787 C 409.338 585.087 411.516 586.974 414.456 588.922 C 422.805 594.453 429.948 602.779 435.358 611.129 C 436.234 612.482 437.263 615.281 438.812 615.161 C 438.445 608.42 425.972 595.687 420.978 591.23 C 417.107 587.775 413.126 585.155 408.536 582.754 L 408.555 582.048 C 422.566 586.434 434.168 601.621 440.723 614.215 C 445.105 622.632 444.938 633.874 449.711 641.576 C 453.927 639.297 461.966 613.967 483.317 608.424 L 478.55 610.815 L 478.805 611.142 C 481.476 610.248 484.154 609.375 486.839 608.523 L 487.796 609.663 C 475.159 616.204 462.947 628.68 456.767 641.508 C 454.428 646.362 452.452 652.908 448.461 656.58 L 446.773 656.483 C 444.116 650.832 444.886 641.677 443.524 635.347 C 438.787 613.324 424.282 597.46 405.775 585.787 z"/>
<path transform="translate(0,0)" fill="rgb(23,30,47)" d="M 1803.05 318.144 C 1816.9 328.396 1829.2 342.719 1841.34 354.945 C 1850.51 364.185 1860.98 373.401 1869.13 383.536 C 1864.21 392.856 1841.35 416.739 1831.82 420.308 C 1828.69 419.912 1826.84 418.064 1824.55 415.972 C 1814.44 406.707 1805.51 394.919 1796.47 384.558 C 1788.37 375.28 1779.6 366.52 1772.04 356.799 C 1770.51 354.836 1767.08 351.135 1767.46 348.594 C 1767.69 347.041 1769.03 345.378 1769.89 344.09 C 1771.44 342.481 1773.17 341.022 1774.83 339.526 C 1783.59 331.421 1793.08 324.646 1803.05 318.144 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" d="M 1803.05 318.144 C 1816.9 328.396 1829.2 342.719 1841.34 354.945 C 1850.51 364.185 1860.98 373.401 1869.13 383.536 C 1864.21 392.856 1841.35 416.739 1831.82 420.308 C 1828.69 419.912 1826.84 418.064 1824.55 415.972 C 1814.44 406.707 1805.51 394.919 1796.47 384.558 C 1788.37 375.28 1779.6 366.52 1772.04 356.799 C 1770.51 354.836 1767.08 351.135 1767.46 348.594 C 1767.69 347.041 1769.03 345.378 1769.89 344.09 C 1770.78 347.369 1772.03 350.104 1774.02 352.875 C 1779.13 359.973 1785.71 366.103 1791.54 372.605 C 1804.73 387.32 1818.42 402.335 1830.22 418.183 C 1841.2 406.851 1853.01 396.325 1863.99 384.947 C 1854.41 374.027 1843.29 363.941 1833.02 353.635 C 1822.96 343.541 1813.04 332.114 1801.79 323.366 C 1797.29 327.355 1792.79 331.646 1787.93 335.178 C 1783.82 338.163 1780 340.314 1774.83 339.526 C 1783.59 331.421 1793.08 324.646 1803.05 318.144 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 445.897 250.388 C 449.325 246.881 456.128 244.104 460.925 242.921 C 469.145 240.895 475.293 241.862 482.484 246.097 C 491.384 250.018 498.032 255.541 501.663 264.868 C 505.5 274.725 503.67 286.246 499.378 295.673 C 495.288 304.656 488.547 311.581 479.176 315.066 C 470.634 318.243 460.901 318.567 452.537 314.674 C 444.78 311.064 437.485 303.083 434.372 295.102 C 430.932 288.26 430.416 281.74 431.633 274.237 C 433.149 264.886 438.193 255.998 445.897 250.388 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 472.668 248.788 C 488.812 251.718 499.974 266.59 498.277 282.91 C 496.579 299.23 482.596 311.487 466.194 311.032 C 449.793 310.577 436.511 297.563 435.721 281.174 C 435.287 272.158 438.756 263.392 445.242 257.115 C 452.508 250.083 462.718 246.983 472.668 248.788 z"/>
<path transform="translate(0,0)" fill="rgb(241,25,50)" d="M 445.897 250.388 C 445.973 253.142 442.428 256.106 443.685 257.49 L 445.242 257.115 C 438.756 263.392 435.287 272.158 435.721 281.174 L 434.494 279.664 C 433.38 284.252 433.671 290.428 434.372 295.102 C 430.932 288.26 430.416 281.74 431.633 274.237 C 433.149 264.886 438.193 255.998 445.897 250.388 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 376.604 379.194 C 384.836 378.003 394.172 379.707 401.018 384.463 C 407.857 389.215 412.169 396.668 413.657 404.791 C 415.561 415.181 414.546 427.19 408.255 435.969 C 402.123 444.527 393.347 448.061 383.349 449.728 C 374.452 449.598 366.757 448.063 359.368 442.715 C 351.971 437.36 346.912 429.624 345.634 420.523 C 344.299 411.009 346.208 400.522 352.162 392.843 C 358.046 385.253 367.162 380.35 376.604 379.194 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 245.278 481.533 C 257.539 479.362 270.02 483.955 277.95 493.555 C 285.88 503.155 288.032 516.279 283.585 527.909 C 279.138 539.54 268.778 547.878 256.466 549.738 C 237.761 552.563 220.261 539.826 217.199 521.159 C 214.137 502.492 226.651 484.832 245.278 481.533 z"/>
<path transform="translate(0,0)" fill="rgb(246,42,69)" d="M 581.532 430.797 C 583.737 426.223 585.763 420.476 589.684 417.162 L 591.653 417.292 C 594.088 420.177 593.57 425.309 594.04 428.969 C 596.145 445.356 610.271 455.983 623.899 463.083 C 606.858 469.748 593.857 473.295 582.916 489.14 C 581.766 491.553 581.517 494.399 580.229 496.771 C 579.253 498.57 578.644 499.099 576.752 499.681 C 573.484 495.743 574.503 486.104 572.779 481.118 C 571.347 476.976 567.817 473.984 566.688 469.622 C 562.444 466.354 558.851 462.241 554.464 458.93 C 552.165 457.194 548.388 454.735 547.974 451.776 C 551.501 446.938 564.993 444.86 570.638 441.417 C 574.943 438.791 577.163 433.481 581.532 430.797 z"/>
<path transform="translate(0,0)" fill="rgb(251,23,37)" d="M 581.532 430.797 C 583.737 426.223 585.763 420.476 589.684 417.162 L 591.653 417.292 C 594.088 420.177 593.57 425.309 594.04 428.969 C 596.145 445.356 610.271 455.983 623.899 463.083 C 606.858 469.748 593.857 473.295 582.916 489.14 C 581.766 491.553 581.517 494.399 580.229 496.771 C 579.253 498.57 578.644 499.099 576.752 499.681 C 573.484 495.743 574.503 486.104 572.779 481.118 C 571.347 476.976 567.817 473.984 566.688 469.622 C 571.173 471.572 572.524 477.478 575.983 480.956 L 574.541 477.616 C 577.199 480.049 578.267 482.89 579.609 486.173 C 581.592 483.925 582.987 480.98 584.97 478.61 C 591.857 470.376 600.56 466.217 610.139 461.923 C 604.823 456.132 598.001 450.067 593.691 443.548 C 591.471 440.19 590.468 436.192 588.242 432.738 L 585.264 432.437 C 585.278 431.088 585.42 429.746 585.536 428.403 C 584.408 429.65 583.607 430.186 581.984 430.67 C 581.834 430.715 581.683 430.755 581.532 430.797 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" fill-opacity="0.988235" d="M 1268.53 876.747 C 1267.01 839.046 1267.99 800.953 1268.02 763.211 C 1268.04 734.464 1266.89 705.284 1267.99 676.603 L 1272.4 677.824 C 1273.89 696.863 1273.08 716.382 1273.06 735.492 C 1273.02 780.543 1271.7 825.968 1273.48 870.972 C 1287.28 871.195 1301.12 871.207 1314.9 871.753 L 1315.16 860.225 C 1317.27 848.465 1316.12 834.41 1316.08 822.353 C 1316.01 795.856 1315.21 769.119 1316.68 742.659 C 1318.34 750.784 1319.01 759.086 1319.91 767.322 C 1320.82 777.589 1320.18 788.461 1320.16 798.785 C 1320.1 824.831 1319.18 851.171 1320.3 877.184 C 1303.1 877.483 1285.72 877.43 1268.53 876.747 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" fill-opacity="0.988235" d="M 1161.87 313.52 L 1165.57 312.761 C 1163.16 322.307 1165.36 349.256 1165.36 360.697 C 1165.34 411.592 1166.05 462.605 1165.23 513.484 C 1146.99 516.978 1114.16 518.771 1096.1 514.8 L 1095.92 499.799 L 1097.35 503.129 L 1098.39 503.214 L 1098.89 498.558 L 1100.43 494.27 C 1100.77 496.759 1101.28 499.136 1101.87 501.573 C 1101.81 504.262 1101.45 507.065 1102.82 509.487 C 1115.26 511.795 1145.76 510.751 1159.14 509.002 C 1159.13 490.857 1157.91 317.848 1159.86 313.826 L 1161.87 313.52 z"/>
<path transform="translate(0,0)" fill="rgb(15,17,24)" fill-opacity="0.984314" d="M 1345.07 480.114 L 1344.74 478.628 C 1347.67 476.765 1349.51 473.859 1352.21 471.672 C 1357.79 467.159 1363.85 463.458 1369.98 459.756 C 1371.34 461.017 1372.58 462.337 1374.31 463.085 C 1365.98 469.573 1354.87 476.027 1345.07 480.114 z"/>
<path transform="translate(0,0)" fill="rgb(0,0,1)" fill-opacity="0.984314" d="M 1051.36 408.806 C 1054.89 404.149 1057.86 398.825 1060.97 393.867 L 1064.1 397.913 C 1061.59 402.37 1058.98 406.26 1055.69 410.177 L 1051.36 408.806 z"/>
</svg>

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}

View File

@ -0,0 +1,38 @@
import type { Metadata } from 'next';
import localFont from 'next/font/local';
import './globals.css';
import { Header, Footer } from '@/components';
const geistSans = localFont({
src: './fonts/GeistVF.woff',
variable: '--font-geist-sans',
weight: '100 900',
});
const geistMono = localFont({
src: './fonts/GeistMonoVF.woff',
variable: '--font-geist-mono',
weight: '100 900',
});
export const metadata: Metadata = {
title: 'KOL Insight - 云图数据查询分析',
description: 'KOL 视频数据查询与成本分析工具 - 麦秒思AI制作',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="zh-CN">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<div className="min-h-screen flex flex-col">
<Header />
<main className="flex-1 bg-gray-50">{children}</main>
<Footer />
</div>
</body>
</html>
);
}

101
frontend/src/app/page.tsx Normal file
View File

@ -0,0 +1,101 @@
import Image from "next/image";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="https://nextjs.org/icons/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
src/app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="https://nextjs.org/icons/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@ -0,0 +1,9 @@
export default function Footer() {
return (
<footer className="bg-gray-50 border-t border-gray-200 py-4 px-6">
<div className="max-w-7xl mx-auto text-center text-sm text-gray-500">
© 2026 AI制作 | KOL Insight v1.0
</div>
</footer>
);
}

View File

@ -0,0 +1,18 @@
import Image from 'next/image';
export default function Header() {
return (
<header className="bg-white border-b border-gray-200 py-4 px-6">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-4">
<Image src="/muse.svg" alt="麦秒思AI Logo" width={40} height={40} priority />
<div>
<h1 className="text-xl font-bold text-gray-900">KOL Insight</h1>
<p className="text-sm text-gray-500"></p>
</div>
</div>
<div className="text-sm text-gray-500">AI制作</div>
</div>
</header>
);
}

View File

@ -0,0 +1,2 @@
export { default as Header } from './Header';
export { default as Footer } from './Footer';

View File

@ -0,0 +1,39 @@
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
background: 'var(--background)',
foreground: 'var(--foreground)',
// 品牌色系
primary: {
DEFAULT: '#4F46E5',
light: '#818CF8',
dark: '#3730A3',
},
success: '#10B981',
warning: '#F59E0B',
error: '#EF4444',
info: '#3B82F6',
},
fontFamily: {
sans: [
'Inter',
'-apple-system',
'BlinkMacSystemFont',
'PingFang SC',
'Microsoft YaHei',
'sans-serif',
],
},
},
},
plugins: [],
};
export default config;

26
frontend/tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}