Files
2026-08-11 19:59:18 +08:00

484 lines
14 KiB
Markdown
Raw Permalink 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.
# Yoresee Doc 后端
Yoresee Doc 协作文档管理平台的后端服务,基于 Go 和 Connect RPC 构建。
## 概述
Yoresee Doc 后端提供:
- **文档管理**:创建、编辑、版本控制、组织文档,支持附件
- **知识库**:层级化知识库组织
- **用户与权限控制**:用户组、组织结构、成员管理、邀请
- **实时协作**:Yjs 快照持久化和冲突解决
- **全文搜索**:Elasticsearch 集成实现文档搜索
- **通知系统**:基于 RabbitMQ 的事件驱动通知
## 架构
### 高层结构
```
┌─────────────────────────────────────────────────────────┐
│ API 层 │
│ Connect RPC 处理器 (HTTP/2 + gRPC-web) │
│ - 认证拦截器 (JWT) │
│ - CORS 支持 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 服务层 │
│ 业务逻辑,使用包级单例 │
│ - auth_service, document_service 等 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 仓储层 │
│ 使用 GORM (PostgreSQL) 的数据访问 │
│ - 通过 repository.NewRepositories() 构造 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ - PostgreSQL (主数据存储) │
│ - Redis (缓存、会话、锁) │
│ - MinIO (文件存储) │
│ - Elasticsearch (搜索索引) │
│ - RabbitMQ (消息队列) │
│ - Consul (服务发现/配置) │
└─────────────────────────────────────────────────────────┘
```
### 项目结构
```
cmd/
├── main.go # API 服务器入口
├── migrate/ # 数据库迁移 (GORM AutoMigrate)
├── db_init/ # 种子数据 (管理员用户、默认分组、模板)
├── es_init/ # Elasticsearch 索引初始化
├── notification-worker/ # 通知消费者
├── search-sync-worker/ # 搜索索引同步消费者
└── snapshot-worker/ # Yjs 快照持久化消费者
internal/
├── bootstrap/ # 链式初始化器 (配置 → 基础设施 → 服务)
├── config/ # Viper TOML 配置加载器
├── transport/
│ ├── connectserver/ # HTTP 多路复用、RPC 处理器、认证拦截器
│ └── grpcserver/ # 各服务的 RPC 实现
├── service/ # 业务逻辑 (领域服务)
│ ├── <domain>_service/ # 服务实现
│ ├── interface/ # 服务接口
│ └── mq_service/ # 消息队列抽象
├── repository/ # 数据访问层
│ ├── <domain>_repo/ # 仓储实现
│ └── init.go # 仓储构造
├── model/ # GORM 模型定义 (16 个模型)
├── dto/ # 数据传输对象
├── mapper/ # 模型 ↔ DTO 映射器
├── types/ # 领域类型
└── status/ # 类型化错误码
pkg/
├── storage/ # 基础设施客户端 (DB、Redis、MinIO、ES、Consul)
├── mq/ # 消息队列接口 + RabbitMQ 实现
├── cache/ # 缓存工具
├── lock/ # 分布式锁
├── key/ # 共享缓存键
├── errs/ # 共享错误
├── constant/ # 共享常量
└── gen/ # 生成的 protobuf 代码 (gitignored)
```
## 前置要求
- **Go 1.24+**
- **PostgreSQL 15+** 支持 `ltree` 扩展
- **Redis 7+**
- **MinIO** (或 S3 兼容对象存储)
- **RabbitMQ 3+**
- **Consul 1.16+**
- **Elasticsearch 8+** (可选,用于搜索功能)
所有基础设施服务可通过项目的 Docker Compose 配置启动。
## 配置
### 配置文件
配置从 `config.toml` (gitignored) 加载,由 `deploy/script/prepare.sh` 从 `config.toml.tmpl` 生成。
**生成:**
```bash
# 在仓库根目录
bash deploy/script/prepare.sh
```
**格式:** TOML,支持环境变量替换 (`${ENV_VAR}`)。
**环境变量:** 在 `deploy/.env` 中定义 (模板:`deploy/.env.example`)。
**访问方式:** 通过 `config.GlobalConfig.*` 单例。
### 关键配置部分
```toml
[server]
grpc_port = 9090 # 原生 gRPC 端口
grpc_web_port = 8080 # gRPC-web 端口 (浏览器客户端)
[database]
host = "localhost"
port = 5432
user = "root"
password = "your_password"
name = "yoresee_doc_db"
[redis]
host = "localhost"
port = 6379
password = "your_redis_password"
db = 0
[consul]
enabled = true
address = "localhost:8500"
scheme = "http"
token = "yoresee_doc_root_token"
prefix = "yoresee_doc"
[minio]
endpoint = "localhost:9000"
access_key = "minioadmin"
secret_key = "minioadmin"
bucket = "yoresee_doc"
use_ssl = false
[elasticsearch]
enabled = true
addresses = ["http://localhost:9200"]
username = "elastic"
password = "your_password"
index_prefix = "yoresee_doc"
[mq_config.rabbitmq]
url = "amqp://guest:guest@localhost:5672/"
[backend]
system_name = "yoresee_doc"
internal_rpc_key = "yoresee_doc_internal_key"
[backend.jwt]
secret = "yoresee_doc_jwt_secret_key"
expire = 86400 # 24 小时
refresh_expire = 604800 # 7 天
[backend.security]
password_hash_cost = 12
max_login_attempts = 5
login_lock_duration = 30 # 分钟
```
## 快速开始
### 1. 启动基础设施
在仓库根目录:
```bash
# 生成配置文件
bash deploy/script/configure.sh # 交互式设置
# 或
cp deploy/.env.example deploy/.env
bash deploy/script/prepare.sh # 非交互式
# 通过 Docker Compose 启动所有服务
bash deploy/script/start.sh dev up
```
这将启动:
- PostgreSQL、Redis、Consul、MinIO、RabbitMQ、Elasticsearch
- 后端 API 服务器 (自动迁移 + 种子数据)
- 后台工作器 (快照、通知、搜索同步)
- 前端、协作服务、nginx
### 2. 独立运行后端 (可选)
如果基础设施已在外部运行:
```bash
cd backend
# 运行迁移 + 种子数据
go run ./cmd/migrate
go run ./cmd/db_init
go run ./cmd/es_init
# 启动 API 服务器
go run ./cmd/main.go
```
### 3. 验证
健康检查端点:
- `GET /health` — 存活检查
- `GET /readyz` — 就绪检查 (检查 DB + Redis)
- `GET /livez` — 存活检查别名
## API 结构
### Connect RPC
后端使用 Connect RPC (非原生 gRPC) 暴露两个 HTTP 监听器:
| 端口 | 用途 | 协议 |
|------|------|------|
| 9090 | 原生 gRPC | HTTP/2 |
| 8080 | gRPC-web | HTTP/1.1+ (浏览器客户端) |
两个监听器都使用 h2c (HTTP/2 明文)。
### 服务
API 组织为以下服务:
- **AuthService**:登录、注册、个人资料管理
- **DocumentService**:文档 CRUD、版本控制、附件、模板
- **KnowledgeBaseService**:知识库管理
- **MembershipService**:用户组、组织结构、成员关系
- **InvitationService**:邀请创建和管理
- **NotificationService**:通知 CRUD、已读状态
- **CommentService**:文档评论
- **SettingService**:系统设置
- **SystemService**:健康检查、系统信息
### 认证
基于 JWT 的认证,以下端点无需认证:
- `AuthService.Login`
- `AuthService.Register`
- `SystemService.Health`
- `SystemService.SystemInfo`
所有其他端点需要在 `Authorization: Bearer <token>` 头中提供有效的 JWT。
## 后台工作器
### 快照工作器
**用途:** 将 Yjs 文档快照持久化到数据库。
**队列:** 消费 RabbitMQ 主题中的脏文档通知。
**流程:**
1. 协作服务在编辑后将文档标记为脏
2. 快照工作器接收通知
3. 从协作服务获取当前 Yjs 状态
4. 将快照持久化到 `document_yjs_snapshots` 表
### 通知工作器
**用途:** 处理和投递通知。
**队列:** 消费 RabbitMQ 通知主题。
### 搜索同步工作器
**用途:** 保持 Elasticsearch 索引与数据库同步。
**队列:** 消费 RabbitMQ 中的文档/知识库变更。
**流程:**
1. 文档/知识库被创建/更新/删除
2. 工作器接收事件
3. 相应更新 Elasticsearch 索引
## 数据库
### 模式管理
**迁移:** 基于模型结构体定义的 GORM `AutoMigrate`。
**位置:** `cmd/migrate/migration.go`
**流程:**
1. 创建 PostgreSQL `ltree` 扩展 (用于层级组织结构)
2. 自动迁移所有 16 个模型
3. 创建自定义索引 (最近访问项的唯一约束)
4. 应用手动迁移 (例如,删除已弃用的列)
**模型:**
- User (用户)
- Document, DocumentVersion, DocumentYjsSnapshot, DocumentComment (文档相关)
- KnowledgeBase (知识库)
- Template (模板)
- Attachment (附件)
- Invitation, InvitationRecord (邀请相关)
- Notification (通知)
- UserGroupMeta, MembershipRelation (用户组和成员关系)
- OrgNodeMeta (组织节点)
- RecentDocument, RecentKnowledgeBase, RecentTemplate (最近访问)
### 种子数据
**位置:** `cmd/db_init/`
**流程:**
1. 创建管理员用户 (用户名:`admin`,密码来自配置)
2. 创建默认权限
3. 创建默认用户组
4. 创建默认模板
## 开发
### 代码结构模式
**服务层:**
```go
// 包级单例
var AuthSvc *AuthService
// 在 service.Init() 中初始化
func Init(cfg *config.Config, repos *repository.Repositories) error {
auth_service.AuthSvc = auth_service.NewAuthService(repos)
// ...
}
```
**仓储层:**
```go
// 通过 Repositories 结构体构造
type Repositories struct {
DB *gorm.DB
Redis *redis.Client
Document *document_repo.DocumentRepository
// ...
}
func NewRepositories(db *gorm.DB, redis *redis.Client) *Repositories {
return &Repositories{
Document: document_repo.NewDocumentRepository(db, redis),
// ...
}
}
```
**错误处理:**
```go
// 带状态码的类型化错误
return status.GenErrWithCustomMsg(
status.StatusServiceInternalError,
"operation failed",
)
```
### Protobuf 代码生成
**源文件:** `proto/yoresee_doc/v1/yoresee_doc.proto`
**生成的 Go 代码:** `pkg/gen/` (gitignored)
**重新生成:**
```bash
# 在仓库根目录
bash deploy/script/gen_proto.sh
```
**目标:**
- `backend/pkg/gen` — Go 代码
- `collab-go/pkg/gen` — Go 代码
- `frontend/src/gen` — TypeScript ES 模块
- `collab/src/gen` — Node gRPC 代码
### 代码验证
未配置 linter 或测试套件。使用以下命令验证更改:
```bash
go build ./...
go vet ./...
```
## 部署
### Docker 镜像
| Dockerfile | 用途 |
|------------|------|
| `Dockerfile` | 开发镜像 (含 Delve 调试器) |
| `Dockerfile.prod` | 生产 API 服务器 |
| `Dockerfile.snapshot.prod` | 生产快照工作器 |
| `Dockerfile.notification.prod` | 生产通知工作器 |
| `Dockerfile.search-sync.prod` | 生产搜索同步工作器 |
### 启动顺序
生产环境容器 CMD:
```bash
./migrate.out && ./db_init.out && ./es_init.out && ./main.out
```
工作器是独立的容器,有自己的入口点。
### 健康检查
Docker Compose 使用 `wget` 检查 `/health` 端点:
```yaml
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://backend:9090/health"]
interval: 5s
timeout: 3s
retries: 20
start_period: 30s
```
## 基础设施端口
| 服务 | 默认端口 |
|------|----------|
| 后端 gRPC | 9090 |
| 后端 gRPC-web | 8080 |
| 后端调试 (Delve) | 2345 |
| PostgreSQL | 5432 |
| Redis | 6379 |
| Consul | 8500 |
| MinIO API | 9000 |
| MinIO 控制台 | 9001 |
| RabbitMQ AMQP | 5672 |
| RabbitMQ 管理界面 | 15672 |
| Elasticsearch | 9200 |
## 关键依赖
- **Connect RPC** (`connectrpc.com/connect`) — 现代 RPC 框架
- **GORM** (`gorm.io/gorm`) — PostgreSQL ORM
- **go-redis** (`github.com/redis/go-redis/v9`) — Redis 客户端
- **MinIO Go** (`github.com/minio/minio-go/v7`) — S3 兼容存储
- **Viper** (`github.com/spf13/viper`) — 配置管理
- **Logrus** (`github.com/sirupsen/logrus`) — 结构化日志
- **JWT** (`github.com/golang-jwt/jwt/v5`) — 认证
- **amqp091-go** (`github.com/rabbitmq/amqp091-go`) — RabbitMQ 客户端
- **improbable-eng/grpc-web** — 浏览器客户端的 gRPC-web 代理
- **Snowflake** (`github.com/bwmarrin/snowflake`) — 分布式 ID 生成
## 环境变量
所有环境变量在 `deploy/.env` 中定义 (参见 `deploy/.env.example` 获取模板)。
关键变量:
- `POSTGRES_*` — 数据库连接
- `REDIS_*` — Redis 连接
- `CONSUL_*` — Consul 配置
- `MINIO_*` — MinIO/S3 配置
- `ELASTICSEARCH_*` — Elasticsearch 配置
- `RABBITMQ_*` — RabbitMQ 配置
- `BACKEND_*` — 后端服务配置
- `JWT_SECRET` — JWT 签名密钥
## 许可证
[在此填写您的许可证]
## 贡献
[在此填写您的贡献指南]