Files
GiteaToFeishuMsg/design.md
2025-12-02 13:27:52 +08:00

237 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 调用
- [ ] 添加错误处理和日志
- [ ] 编写配置文件
- [ ] 编写部署说明
- [ ] 测试与验证