docs: complete canvas-dev workflow with prompts, templates and examples
This commit is contained in:
parent
544b3c5766
commit
76e7a9a0fb
|
|
@ -0,0 +1,56 @@
|
||||||
|
# 🎨 Canvas白板驱动开发工作流
|
||||||
|
|
||||||
|
> 图形是第一公民,代码是白板的序列化形式
|
||||||
|
|
||||||
|
## 核心理念
|
||||||
|
|
||||||
|
```
|
||||||
|
传统开发:代码 → 口头沟通 → 脑补架构 → 代码失控
|
||||||
|
Canvas方式:代码 ⇄ 白板 ⇄ AI ⇄ 人类(白板为单一真相源)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 痛点 | 解法 |
|
||||||
|
|:---|:---|
|
||||||
|
| 🤖 AI看不懂项目结构 | ✅ AI直接读白板JSON,秒懂架构 |
|
||||||
|
| 🧠 人类记不住复杂依赖 | ✅ 连线清晰,牵一发动全身一目了然 |
|
||||||
|
| 💬 团队协作靠嘴说 | ✅ 指着白板讲,新人5分钟看懂 |
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
canvas-dev/
|
||||||
|
├── README.md # 本文件 - 工作流概述
|
||||||
|
├── workflow.md # 完整工作流步骤(线性流程)
|
||||||
|
├── prompts/
|
||||||
|
│ ├── 01-架构分析.md # 从代码生成白板的提示词
|
||||||
|
│ ├── 02-白板驱动编码.md # 根据白板生成代码的提示词
|
||||||
|
│ └── 03-白板同步检查.md # 校验白板与代码一致性
|
||||||
|
├── templates/
|
||||||
|
│ ├── project.canvas # Obsidian Canvas 项目模板
|
||||||
|
│ └── module.canvas # 单模块白板模板
|
||||||
|
└── examples/
|
||||||
|
└── demo-project.canvas # 示例项目白板
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 准备工具
|
||||||
|
- [Obsidian](https://obsidian.md/) - 免费开源白板工具
|
||||||
|
- AI助手(Claude/GPT-4,需支持读取Canvas JSON)
|
||||||
|
|
||||||
|
### 2. 生成项目架构白板
|
||||||
|
```bash
|
||||||
|
# 将项目代码路径提供给AI,使用架构分析提示词
|
||||||
|
# AI自动生成 .canvas 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 用白板驱动开发
|
||||||
|
- 在白板上画出新模块和依赖关系
|
||||||
|
- 导出白板JSON发送给AI
|
||||||
|
- AI根据白板生成/修改代码
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [Canvas白板驱动开发详解](../../documents/02-方法论/图形化AI协作-Canvas白板驱动开发.md)
|
||||||
|
- [白板驱动开发系统提示词](../../prompts/01-系统提示词/AGENTS.md/12/AGENTS.md)
|
||||||
|
- [胶水编程](../../documents/00-基础指南/胶水编程.md)
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"x": -100,
|
||||||
|
"y": -350,
|
||||||
|
"width": 400,
|
||||||
|
"height": 60,
|
||||||
|
"text": "# 📦 电商系统架构白板\n\n演示项目 - 用户、商品、订单管理"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group-frontend",
|
||||||
|
"type": "group",
|
||||||
|
"x": -600,
|
||||||
|
"y": -250,
|
||||||
|
"width": 250,
|
||||||
|
"height": 500,
|
||||||
|
"label": "🖥️ 前端"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group-api",
|
||||||
|
"type": "group",
|
||||||
|
"x": -300,
|
||||||
|
"y": -250,
|
||||||
|
"width": 250,
|
||||||
|
"height": 500,
|
||||||
|
"label": "🌐 API网关"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group-service",
|
||||||
|
"type": "group",
|
||||||
|
"x": 0,
|
||||||
|
"y": -250,
|
||||||
|
"width": 300,
|
||||||
|
"height": 500,
|
||||||
|
"label": "⚙️ 业务服务"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group-infra",
|
||||||
|
"type": "group",
|
||||||
|
"x": 350,
|
||||||
|
"y": -250,
|
||||||
|
"width": 300,
|
||||||
|
"height": 500,
|
||||||
|
"label": "🔧 基础设施"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fe-web",
|
||||||
|
"type": "text",
|
||||||
|
"x": -580,
|
||||||
|
"y": -200,
|
||||||
|
"width": 210,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# Web App\n\nReact + TypeScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fe-mobile",
|
||||||
|
"type": "text",
|
||||||
|
"x": -580,
|
||||||
|
"y": -100,
|
||||||
|
"width": 210,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# Mobile App\n\nReact Native"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api-gateway",
|
||||||
|
"type": "text",
|
||||||
|
"x": -280,
|
||||||
|
"y": -200,
|
||||||
|
"width": 210,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# API Gateway\n\n- 路由分发\n- 认证鉴权\n- 限流熔断"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "svc-user",
|
||||||
|
"type": "text",
|
||||||
|
"x": 20,
|
||||||
|
"y": -200,
|
||||||
|
"width": 260,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# UserService\n\n- 用户注册/登录\n- 个人信息管理\n- 权限校验"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "svc-product",
|
||||||
|
"type": "text",
|
||||||
|
"x": 20,
|
||||||
|
"y": -80,
|
||||||
|
"width": 260,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# ProductService\n\n- 商品CRUD\n- 库存管理\n- 分类搜索"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "svc-order",
|
||||||
|
"type": "text",
|
||||||
|
"x": 20,
|
||||||
|
"y": 40,
|
||||||
|
"width": 260,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# OrderService\n\n- 下单流程\n- 订单状态机\n- 退款处理"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "svc-payment",
|
||||||
|
"type": "text",
|
||||||
|
"x": 20,
|
||||||
|
"y": 160,
|
||||||
|
"width": 260,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# PaymentService\n\n- 支付网关对接\n- 账单管理"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "infra-db",
|
||||||
|
"type": "text",
|
||||||
|
"x": 370,
|
||||||
|
"y": -200,
|
||||||
|
"width": 260,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# PostgreSQL\n\n主数据库",
|
||||||
|
"color": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "infra-cache",
|
||||||
|
"type": "text",
|
||||||
|
"x": 370,
|
||||||
|
"y": -100,
|
||||||
|
"width": 260,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# Redis\n\n缓存 + 会话",
|
||||||
|
"color": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "infra-mq",
|
||||||
|
"type": "text",
|
||||||
|
"x": 370,
|
||||||
|
"y": 0,
|
||||||
|
"width": 260,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# RabbitMQ\n\n异步消息队列",
|
||||||
|
"color": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "infra-es",
|
||||||
|
"type": "text",
|
||||||
|
"x": 370,
|
||||||
|
"y": 100,
|
||||||
|
"width": 260,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# Elasticsearch\n\n商品搜索引擎",
|
||||||
|
"color": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "external-stripe",
|
||||||
|
"type": "text",
|
||||||
|
"x": 370,
|
||||||
|
"y": 200,
|
||||||
|
"width": 260,
|
||||||
|
"height": 60,
|
||||||
|
"text": "# Stripe API\n\n外部支付服务",
|
||||||
|
"color": "6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "e-web-gw",
|
||||||
|
"fromNode": "fe-web",
|
||||||
|
"toNode": "api-gateway",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "HTTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-mobile-gw",
|
||||||
|
"fromNode": "fe-mobile",
|
||||||
|
"toNode": "api-gateway",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "HTTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-gw-user",
|
||||||
|
"fromNode": "api-gateway",
|
||||||
|
"toNode": "svc-user",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "/users/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-gw-product",
|
||||||
|
"fromNode": "api-gateway",
|
||||||
|
"toNode": "svc-product",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "/products/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-gw-order",
|
||||||
|
"fromNode": "api-gateway",
|
||||||
|
"toNode": "svc-order",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "/orders/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-order-user",
|
||||||
|
"fromNode": "svc-order",
|
||||||
|
"toNode": "svc-user",
|
||||||
|
"fromSide": "top",
|
||||||
|
"toSide": "bottom",
|
||||||
|
"label": "校验用户"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-order-product",
|
||||||
|
"fromNode": "svc-order",
|
||||||
|
"toNode": "svc-product",
|
||||||
|
"fromSide": "top",
|
||||||
|
"toSide": "bottom",
|
||||||
|
"label": "扣减库存"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-order-payment",
|
||||||
|
"fromNode": "svc-order",
|
||||||
|
"toNode": "svc-payment",
|
||||||
|
"fromSide": "bottom",
|
||||||
|
"toSide": "top",
|
||||||
|
"label": "发起支付"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-user-db",
|
||||||
|
"fromNode": "svc-user",
|
||||||
|
"toNode": "infra-db",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-product-db",
|
||||||
|
"fromNode": "svc-product",
|
||||||
|
"toNode": "infra-db",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-order-db",
|
||||||
|
"fromNode": "svc-order",
|
||||||
|
"toNode": "infra-db",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-user-cache",
|
||||||
|
"fromNode": "svc-user",
|
||||||
|
"toNode": "infra-cache",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "会话"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-product-es",
|
||||||
|
"fromNode": "svc-product",
|
||||||
|
"toNode": "infra-es",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "搜索"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-order-mq",
|
||||||
|
"fromNode": "svc-order",
|
||||||
|
"toNode": "infra-mq",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "订单事件"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-payment-stripe",
|
||||||
|
"fromNode": "svc-payment",
|
||||||
|
"toNode": "external-stripe",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "支付请求"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# 01-架构分析提示词
|
||||||
|
|
||||||
|
> 从现有代码自动生成 Obsidian Canvas 架构白板
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- 接手新项目,快速理解架构
|
||||||
|
- 为现有项目建立可视化文档
|
||||||
|
- 准备 Code Review 或技术分享
|
||||||
|
|
||||||
|
## 提示词
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
你是一个代码架构分析专家。请分析以下项目结构,生成 Obsidian Canvas 格式的架构白板。
|
||||||
|
|
||||||
|
## 输入
|
||||||
|
项目路径:{PROJECT_PATH}
|
||||||
|
分析粒度:{GRANULARITY} (file/class/service)
|
||||||
|
|
||||||
|
## 输出要求
|
||||||
|
生成符合 Obsidian Canvas JSON 格式的 .canvas 文件,包含:
|
||||||
|
|
||||||
|
1. **节点 (nodes)**:
|
||||||
|
- 每个模块/文件/类作为一个节点
|
||||||
|
- 节点包含:id, type, x, y, width, height, text
|
||||||
|
- 按功能分区布局(如:API层左侧,数据层右侧)
|
||||||
|
|
||||||
|
2. **连线 (edges)**:
|
||||||
|
- 表示模块间的依赖/调用关系
|
||||||
|
- 包含:id, fromNode, toNode, fromSide, toSide, label
|
||||||
|
- label 标注关系类型(调用/继承/依赖/数据流)
|
||||||
|
|
||||||
|
3. **分组 (groups)**:
|
||||||
|
- 按功能域分组(如:用户模块、支付模块)
|
||||||
|
- 用颜色区分不同层级
|
||||||
|
|
||||||
|
## Canvas JSON 结构示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "node1",
|
||||||
|
"type": "text",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 200,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# UserService\n- createUser()\n- getUser()"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "edge1",
|
||||||
|
"fromNode": "node1",
|
||||||
|
"toNode": "node2",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "调用"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 分析步骤
|
||||||
|
1. 扫描项目目录结构
|
||||||
|
2. 识别入口文件和核心模块
|
||||||
|
3. 分析 import/require 语句提取依赖关系
|
||||||
|
4. 识别数据库操作、API调用、外部服务
|
||||||
|
5. 按调用层级布局节点位置
|
||||||
|
6. 生成完整的 .canvas JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
```
|
||||||
|
请分析 /home/user/my-project 项目,生成文件级别的架构白板。
|
||||||
|
重点关注:
|
||||||
|
- API 路由和处理函数
|
||||||
|
- 数据库模型和操作
|
||||||
|
- 外部服务调用
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出文件
|
||||||
|
|
||||||
|
生成的 `.canvas` 文件可直接用 Obsidian 打开查看和编辑。
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
# 02-白板驱动编码提示词
|
||||||
|
|
||||||
|
> 根据 Canvas 白板架构图生成/修改代码
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- 新功能开发:先画白板,再生成代码
|
||||||
|
- 架构重构:修改白板连线,AI同步重构代码
|
||||||
|
- 模块拆分:在白板拆分节点,AI生成新文件
|
||||||
|
|
||||||
|
## 提示词
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
你是一个根据架构白板生成代码的专家。请根据以下 Obsidian Canvas 白板 JSON,生成对应的代码实现。
|
||||||
|
|
||||||
|
## 输入
|
||||||
|
Canvas JSON:
|
||||||
|
```json
|
||||||
|
{CANVAS_JSON}
|
||||||
|
```
|
||||||
|
|
||||||
|
技术栈:{TECH_STACK}
|
||||||
|
目标目录:{TARGET_DIR}
|
||||||
|
|
||||||
|
## 解析规则
|
||||||
|
|
||||||
|
1. **节点 → 文件/类**
|
||||||
|
- 节点 text 中的标题 → 文件名/类名
|
||||||
|
- 节点 text 中的列表项 → 方法/函数
|
||||||
|
- 节点颜色/分组 → 模块归属
|
||||||
|
|
||||||
|
2. **连线 → 依赖关系**
|
||||||
|
- fromNode → toNode = import/调用关系
|
||||||
|
- edge label 决定关系类型:
|
||||||
|
- "调用" → 函数调用
|
||||||
|
- "继承" → class extends
|
||||||
|
- "依赖" → import
|
||||||
|
- "数据流" → 参数传递
|
||||||
|
|
||||||
|
3. **分组 → 目录结构**
|
||||||
|
- 同一分组的节点放在同一目录
|
||||||
|
- 分组名称 → 目录名
|
||||||
|
|
||||||
|
## 输出要求
|
||||||
|
|
||||||
|
1. 生成完整的文件结构
|
||||||
|
2. 每个文件包含:
|
||||||
|
- 正确的 import 语句(根据连线)
|
||||||
|
- 类/函数定义(根据节点内容)
|
||||||
|
- 调用关系实现(根据连线方向)
|
||||||
|
3. 添加必要的类型注解和注释
|
||||||
|
4. 遵循技术栈的最佳实践
|
||||||
|
|
||||||
|
## 输出格式
|
||||||
|
|
||||||
|
```
|
||||||
|
文件:{文件路径}
|
||||||
|
```{语言}
|
||||||
|
{代码内容}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
```
|
||||||
|
根据以下白板生成 Python FastAPI 项目代码:
|
||||||
|
|
||||||
|
{粘贴 .canvas 文件内容}
|
||||||
|
|
||||||
|
技术栈:Python 3.11 + FastAPI + SQLAlchemy
|
||||||
|
目标目录:/home/user/my-api
|
||||||
|
```
|
||||||
|
|
||||||
|
## 增量更新模式
|
||||||
|
|
||||||
|
当白板有修改时,使用以下提示词:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
白板已更新,请对比新旧版本,只修改变化的部分:
|
||||||
|
|
||||||
|
旧白板:{OLD_CANVAS_JSON}
|
||||||
|
新白板:{NEW_CANVAS_JSON}
|
||||||
|
|
||||||
|
输出:
|
||||||
|
1. 需要新增的文件
|
||||||
|
2. 需要修改的文件(只输出 diff)
|
||||||
|
3. 需要删除的文件
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
# 03-白板同步检查提示词
|
||||||
|
|
||||||
|
> 校验白板与实际代码的一致性
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- PR/MR 合并前检查白板是否需要更新
|
||||||
|
- 定期审计架构文档准确性
|
||||||
|
- 发现代码中的隐式依赖
|
||||||
|
|
||||||
|
## 提示词
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
你是一个代码与架构一致性检查专家。请对比以下白板和代码,找出不一致之处。
|
||||||
|
|
||||||
|
## 输入
|
||||||
|
|
||||||
|
Canvas 白板 JSON:
|
||||||
|
```json
|
||||||
|
{CANVAS_JSON}
|
||||||
|
```
|
||||||
|
|
||||||
|
项目代码路径:{PROJECT_PATH}
|
||||||
|
|
||||||
|
## 检查项
|
||||||
|
|
||||||
|
1. **节点完整性**
|
||||||
|
- 白板中的节点是否都有对应的代码文件/类?
|
||||||
|
- 代码中是否有白板未记录的重要模块?
|
||||||
|
|
||||||
|
2. **连线准确性**
|
||||||
|
- 白板连线是否反映真实的 import/调用关系?
|
||||||
|
- 代码中是否有白板未标注的依赖?
|
||||||
|
|
||||||
|
3. **分组正确性**
|
||||||
|
- 白板分组是否与目录结构一致?
|
||||||
|
- 是否有跨分组的异常依赖?
|
||||||
|
|
||||||
|
## 输出格式
|
||||||
|
|
||||||
|
### 🔴 严重不一致(必须修复)
|
||||||
|
| 类型 | 白板 | 代码 | 建议 |
|
||||||
|
|:---|:---|:---|:---|
|
||||||
|
| 缺失节点 | - | UserService.py | 添加到白板 |
|
||||||
|
| 错误连线 | A→B | A不调用B | 删除连线 |
|
||||||
|
|
||||||
|
### 🟡 轻微不一致(建议修复)
|
||||||
|
| 类型 | 白板 | 代码 | 建议 |
|
||||||
|
|:---|:---|:---|:---|
|
||||||
|
| 命名不一致 | user_service | UserService | 统一命名 |
|
||||||
|
|
||||||
|
### 🟢 一致性良好
|
||||||
|
- 节点覆盖率:{X}%
|
||||||
|
- 连线准确率:{Y}%
|
||||||
|
|
||||||
|
### 📋 修复建议
|
||||||
|
1. {具体修复步骤}
|
||||||
|
2. {具体修复步骤}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自动化脚本(可选)
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
canvas_sync_check.py - 白板与代码一致性检查脚本
|
||||||
|
|
||||||
|
用法:python canvas_sync_check.py project.canvas /path/to/project
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def load_canvas(canvas_path):
|
||||||
|
with open(canvas_path) as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def extract_imports(py_file):
|
||||||
|
"""提取 Python 文件的 import 关系"""
|
||||||
|
with open(py_file) as f:
|
||||||
|
tree = ast.parse(f.read())
|
||||||
|
imports = []
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.Import):
|
||||||
|
for alias in node.names:
|
||||||
|
imports.append(alias.name)
|
||||||
|
elif isinstance(node, ast.ImportFrom):
|
||||||
|
if node.module:
|
||||||
|
imports.append(node.module)
|
||||||
|
return imports
|
||||||
|
|
||||||
|
def check_consistency(canvas, project_path):
|
||||||
|
"""对比白板节点与实际文件"""
|
||||||
|
canvas_nodes = {n['text'].split('\n')[0].strip('# ')
|
||||||
|
for n in canvas.get('nodes', [])}
|
||||||
|
|
||||||
|
actual_files = set()
|
||||||
|
for py_file in Path(project_path).rglob('*.py'):
|
||||||
|
actual_files.add(py_file.stem)
|
||||||
|
|
||||||
|
missing_in_canvas = actual_files - canvas_nodes
|
||||||
|
missing_in_code = canvas_nodes - actual_files
|
||||||
|
|
||||||
|
return {
|
||||||
|
'missing_in_canvas': missing_in_canvas,
|
||||||
|
'missing_in_code': missing_in_code,
|
||||||
|
'coverage': len(canvas_nodes & actual_files) / len(actual_files) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("用法: python canvas_sync_check.py <canvas_file> <project_path>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
canvas = load_canvas(sys.argv[1])
|
||||||
|
result = check_consistency(canvas, sys.argv[2])
|
||||||
|
|
||||||
|
print(f"覆盖率: {result['coverage']:.1f}%")
|
||||||
|
if result['missing_in_canvas']:
|
||||||
|
print(f"白板缺失: {result['missing_in_canvas']}")
|
||||||
|
if result['missing_in_code']:
|
||||||
|
print(f"代码缺失: {result['missing_in_code']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD 集成
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/canvas-check.yml
|
||||||
|
name: Canvas Sync Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**.py'
|
||||||
|
- '**.canvas'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Check canvas consistency
|
||||||
|
run: python scripts/canvas_sync_check.py docs/architecture.canvas src/
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "module-main",
|
||||||
|
"type": "text",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 280,
|
||||||
|
"height": 150,
|
||||||
|
"text": "# ModuleName\n\n## 职责\n- 功能描述1\n- 功能描述2\n\n## 公开接口\n- method1()\n- method2()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "module-dep1",
|
||||||
|
"type": "text",
|
||||||
|
"x": -350,
|
||||||
|
"y": 0,
|
||||||
|
"width": 200,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# 依赖模块1\n\n被本模块调用",
|
||||||
|
"color": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "module-dep2",
|
||||||
|
"type": "text",
|
||||||
|
"x": 350,
|
||||||
|
"y": 0,
|
||||||
|
"width": 200,
|
||||||
|
"height": 100,
|
||||||
|
"text": "# 下游模块\n\n调用本模块",
|
||||||
|
"color": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "note-design",
|
||||||
|
"type": "text",
|
||||||
|
"x": 0,
|
||||||
|
"y": 200,
|
||||||
|
"width": 280,
|
||||||
|
"height": 100,
|
||||||
|
"text": "## 📝 设计决策\n\n- 为什么这样设计?\n- 有哪些权衡?",
|
||||||
|
"color": "6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "edge-dep1",
|
||||||
|
"fromNode": "module-main",
|
||||||
|
"toNode": "module-dep1",
|
||||||
|
"fromSide": "left",
|
||||||
|
"toSide": "right",
|
||||||
|
"label": "调用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-dep2",
|
||||||
|
"fromNode": "module-dep2",
|
||||||
|
"toNode": "module-main",
|
||||||
|
"fromSide": "left",
|
||||||
|
"toSide": "right",
|
||||||
|
"label": "调用"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "group-api",
|
||||||
|
"type": "group",
|
||||||
|
"x": -400,
|
||||||
|
"y": -200,
|
||||||
|
"width": 300,
|
||||||
|
"height": 400,
|
||||||
|
"label": "🌐 API 层"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group-service",
|
||||||
|
"type": "group",
|
||||||
|
"x": 0,
|
||||||
|
"y": -200,
|
||||||
|
"width": 300,
|
||||||
|
"height": 400,
|
||||||
|
"label": "⚙️ 服务层"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "group-data",
|
||||||
|
"type": "group",
|
||||||
|
"x": 400,
|
||||||
|
"y": -200,
|
||||||
|
"width": 300,
|
||||||
|
"height": 400,
|
||||||
|
"label": "💾 数据层"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-api-user",
|
||||||
|
"type": "text",
|
||||||
|
"x": -380,
|
||||||
|
"y": -150,
|
||||||
|
"width": 260,
|
||||||
|
"height": 120,
|
||||||
|
"text": "# UserAPI\n\n- POST /users\n- GET /users/{id}\n- PUT /users/{id}\n- DELETE /users/{id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-api-order",
|
||||||
|
"type": "text",
|
||||||
|
"x": -380,
|
||||||
|
"y": 0,
|
||||||
|
"width": 260,
|
||||||
|
"height": 120,
|
||||||
|
"text": "# OrderAPI\n\n- POST /orders\n- GET /orders/{id}\n- GET /orders/user/{user_id}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-service-user",
|
||||||
|
"type": "text",
|
||||||
|
"x": 20,
|
||||||
|
"y": -150,
|
||||||
|
"width": 260,
|
||||||
|
"height": 120,
|
||||||
|
"text": "# UserService\n\n- create_user()\n- get_user()\n- update_user()\n- delete_user()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-service-order",
|
||||||
|
"type": "text",
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"width": 260,
|
||||||
|
"height": 120,
|
||||||
|
"text": "# OrderService\n\n- create_order()\n- get_order()\n- get_user_orders()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-model-user",
|
||||||
|
"type": "text",
|
||||||
|
"x": 420,
|
||||||
|
"y": -150,
|
||||||
|
"width": 260,
|
||||||
|
"height": 120,
|
||||||
|
"text": "# User Model\n\n- id: int\n- name: str\n- email: str\n- created_at: datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-model-order",
|
||||||
|
"type": "text",
|
||||||
|
"x": 420,
|
||||||
|
"y": 0,
|
||||||
|
"width": 260,
|
||||||
|
"height": 120,
|
||||||
|
"text": "# Order Model\n\n- id: int\n- user_id: int (FK)\n- total: decimal\n- status: str"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "node-db",
|
||||||
|
"type": "text",
|
||||||
|
"x": 420,
|
||||||
|
"y": 150,
|
||||||
|
"width": 260,
|
||||||
|
"height": 80,
|
||||||
|
"text": "# Database\n\nPostgreSQL",
|
||||||
|
"color": "4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "edge-api-user-service",
|
||||||
|
"fromNode": "node-api-user",
|
||||||
|
"toNode": "node-service-user",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "调用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-api-order-service",
|
||||||
|
"fromNode": "node-api-order",
|
||||||
|
"toNode": "node-service-order",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "调用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-service-user-model",
|
||||||
|
"fromNode": "node-service-user",
|
||||||
|
"toNode": "node-model-user",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "操作"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-service-order-model",
|
||||||
|
"fromNode": "node-service-order",
|
||||||
|
"toNode": "node-model-order",
|
||||||
|
"fromSide": "right",
|
||||||
|
"toSide": "left",
|
||||||
|
"label": "操作"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-order-user-dep",
|
||||||
|
"fromNode": "node-service-order",
|
||||||
|
"toNode": "node-service-user",
|
||||||
|
"fromSide": "top",
|
||||||
|
"toSide": "bottom",
|
||||||
|
"label": "依赖"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-model-user-db",
|
||||||
|
"fromNode": "node-model-user",
|
||||||
|
"toNode": "node-db",
|
||||||
|
"fromSide": "bottom",
|
||||||
|
"toSide": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-model-order-db",
|
||||||
|
"fromNode": "node-model-order",
|
||||||
|
"toNode": "node-db",
|
||||||
|
"fromSide": "bottom",
|
||||||
|
"toSide": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edge-order-user-fk",
|
||||||
|
"fromNode": "node-model-order",
|
||||||
|
"toNode": "node-model-user",
|
||||||
|
"fromSide": "top",
|
||||||
|
"toSide": "bottom",
|
||||||
|
"label": "FK: user_id"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue