# Gitea 到飞书 Webhook 中转服务设计 ## 1. 概述 本服务作为 Gitea Webhook 和飞书机器人之间的中转层,接收 Gitea 发送的工单事件(创建、更新、关闭等),将其转换为更直观美观的飞书卡片消息,并转发给飞书群聊。 ## 2. 系统架构 ### 2.1 组件图 ```mermaid graph TD A[Gitea] -->|发送 Webhook 事件| B(中转服务) B -->|验证 & 解析| C{消息转换器} C -->|生成飞书卡片| D[飞书 API 客户端] D -->|POST 请求| E[飞书机器人] E --> F[飞书群聊] B --> G[日志存储] B --> H[错误处理] ``` ### 2.2 数据流 1. **Gitea 触发事件**:用户在 Gitea 的 Kanban 中创建、更新或关闭工单。 2. **Webhook 发送**:Gitea 向预设的中转服务 URL 发送 HTTP POST 请求,载荷为 JSON。 3. **中转服务接收**:Express 服务器在 `/webhook/gitea` 端点接收请求。 4. **验证与解析**:验证请求签名(可选),解析 JSON 载荷。 5. **消息转换**:根据事件类型提取关键字段,映射为飞书卡片消息结构。 6. **飞书 API 调用**:使用飞书机器人的 Webhook URL 发送卡片消息。 7. **响应与日志**:记录成功/失败日志,返回适当 HTTP 状态码。 ## 3. Gitea Webhook 数据结构 基于 Gitea 1.20+ 的 Webhook 文档,工单事件(Issue)的 JSON 示例: ```json { "action": "opened", "issue": { "id": 123, "number": 45, "title": "工单标题", "body": "工单描述", "state": "open", "created_at": "2025-12-02T05:00:00Z", "updated_at": "2025-12-02T05:00:00Z", "closed_at": null, "user": { "id": 1, "login": "user1", "full_name": "用户姓名" }, "assignee": { "id": 2, "login": "user2", "full_name": "指派给" }, "labels": [ {"name": "bug", "color": "d73a4a"} ], "milestone": { "title": "里程碑" } }, "repository": { "id": 1, "name": "project", "full_name": "org/project", "html_url": "https://gitea.example.com/org/project" }, "sender": { "id": 1, "login": "user1" } } ``` **支持的事件类型**: - `opened`:工单创建 - `closed`:工单关闭 - `reopened`:工单重新打开 - `edited`:工单编辑 - `assigned`:指派 - `unassigned`:取消指派 - `labeled`:添加标签 - `unlabeled`:移除标签 ## 4. 飞书卡片消息格式 飞书卡片消息使用交互式卡片模板。基本结构: ```json { "msg_type": "interactive", "card": { "config": { "wide_screen_mode": true }, "header": { "title": { "tag": "plain_text", "content": "工单状态更新" }, "template": "blue" // 根据状态变化颜色 }, "elements": [ { "tag": "div", "text": { "tag": "lark_md", "content": "**工单标题**:工单标题" } }, { "tag": "div", "text": { "tag": "lark_md", "content": "**状态**:打开" } }, { "tag": "div", "text": { "tag": "lark_md", "content": "**创建者**:用户姓名" } }, { "tag": "div", "text": { "tag": "lark_md", "content": "**优先级**:高" } }, { "tag": "action", "actions": [ { "tag": "button", "text": { "tag": "plain_text", "content": "查看工单" }, "type": "primary", "url": "https://gitea.example.com/org/project/issues/45" } ] } ] } } ``` ## 5. 消息转换逻辑 ### 5.1 事件类型映射 | Gitea 事件 | 飞书卡片标题 | 颜色模板 | |------------|--------------|----------| | opened | 新工单创建 | blue | | closed | 工单关闭 | red | | reopened | 工单重新打开 | orange | | edited | 工单已编辑 | green | | assigned | 工单已指派 | purple | | unassigned | 工单取消指派 | grey | ### 5.2 字段提取 - **标题**:`issue.title` - **状态**:`issue.state` (open/closed) - **创建者**:`issue.user.full_name` 或 `issue.user.login` - **链接**:`repository.html_url` + `/issues/` + `issue.number` - **优先级**:从 `issue.labels` 中查找包含 "priority:" 的标签,或默认 "中" ### 5.3 转换规则 1. 如果 `action` 是 `opened`,卡片标题为 "新工单创建"。 2. 如果 `action` 是 `closed`,卡片标题为 "工单关闭",并显示关闭时间。 3. 如果存在 `assignee`,添加 "指派给" 字段。 4. 标签列表可显示为标签组(可选)。 ## 6. 配置项 服务需要以下配置: - `PORT`:服务监听端口(默认 3000) - `FEISHU_WEBHOOK_URL`:飞书机器人 Webhook URL - `GITEA_WEBHOOK_SECRET`:Gitea Webhook 密钥(用于验证) - `LOG_LEVEL`:日志级别(info, debug, error) ## 7. 部署说明 ### 7.1 本地运行 ```bash npm install npm start ``` ### 7.2 Docker 部署 ```dockerfile FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 CMD ["node", "src/server.js"] ``` ### 7.3 环境变量 在 `.env` 文件中设置: ```env PORT=3000 FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/xxx GITEA_WEBHOOK_SECRET=your_secret ``` ## 8. 待办事项 - [x] 收集需求并明确功能范围 - [x] 设计系统架构和数据流 - [x] 确定 Gitea webhook 数据结构 - [x] 确定飞书机器人消息格式 - [x] 设计消息转换逻辑 - [ ] 创建项目结构 - [ ] 实现 Express 服务器 - [ ] 实现 webhook 接收端点 - [ ] 实现消息转换器 - [ ] 实现飞书 API 调用 - [ ] 添加错误处理和日志 - [ ] 编写配置文件 - [ ] 编写部署说明 - [ ] 测试与验证