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

14 KiB
Raw Permalink Blame History

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 deploy/script/prepare.sh

格式: TOML,支持环境变量替换 (${ENV_VAR})。

环境变量: 在 deploy/.env 中定义 (模板:deploy/.env.example)。

访问方式: 通过 config.GlobalConfig.* 单例。

关键配置部分

[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 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. 独立运行后端 (可选)

如果基础设施已在外部运行:

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. 创建默认模板

开发

代码结构模式

服务层:

// 包级单例
var AuthSvc *AuthService

// 在 service.Init() 中初始化
func Init(cfg *config.Config, repos *repository.Repositories) error {
    auth_service.AuthSvc = auth_service.NewAuthService(repos)
    // ...
}

仓储层:

// 通过 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),
        // ...
    }
}

错误处理:

// 带状态码的类型化错误
return status.GenErrWithCustomMsg(
    status.StatusServiceInternalError,
    "operation failed",
)

Protobuf 代码生成

源文件: proto/yoresee_doc/v1/yoresee_doc.proto

生成的 Go 代码: pkg/gen/ (gitignored)

重新生成:

# 在仓库根目录
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 或测试套件。使用以下命令验证更改:

go build ./...
go vet ./...

部署

Docker 镜像

Dockerfile 用途
Dockerfile 开发镜像 (含 Delve 调试器)
Dockerfile.prod 生产 API 服务器
Dockerfile.snapshot.prod 生产快照工作器
Dockerfile.notification.prod 生产通知工作器
Dockerfile.search-sync.prod 生产搜索同步工作器

启动顺序

生产环境容器 CMD:

./migrate.out && ./db_init.out && ./es_init.out && ./main.out

工作器是独立的容器,有自己的入口点。

健康检查

Docker Compose 使用 wget 检查 /health 端点:

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 签名密钥

许可证

[在此填写您的许可证]

贡献

[在此填写您的贡献指南]