M-Doc 是一个功能强大的企业级文档管理系统,支持组织管理、文档版本控制、多人协作、权限管理和智能搜索。基于 Go + Gin + GORM 构建的高性能 RESTful API 后端,配合 Nuxt 3 前端,为团队提供优雅的文档协作体验。
| 企业微信 | |
|---|---|
![]() | ![]() |
| QQ群:1021660914 点击链接加入群聊【木雷坞开源家】 | 扫描上方二维码加入微信群 |
git clone https://cnb.cool/mliev/mdoc/mdoc-server
cd mdoc-server
# 初始化前端子模块
git submodule update --init --recursive
# 复制配置文件
cp config.yaml.example config.yaml
# 编辑配置文件,配置数据库、Redis 等
vim config.yaml
# 后端依赖
go mod tidy
# 前端依赖(可选,如果需要本地开发前端)
cd frontend
pnpm install
cd ..
应用启动时会自动执行数据库迁移,创建所需表结构。
后端服务:
# 启动后端 API(监听 http://localhost:8080)
go run main.go
前端服务(可选,用于开发):
# 在新终端启动前端开发服务器(监听 http://localhost:3000)
cd frontend
pnpm dev
mdoc-server/ ├── main.go # 程序入口 ├── go.mod # Go 模块定义 ├── config.yaml.example # 配置文件示例 ├── config.yaml # 实际配置文件(不提交) ├── CLAUDE.md # Claude Code 开发指南 ├── LICENSE # MIT 许可证 │ ├── app/ # 应用核心代码 │ ├── controller/ # 控制器层(HTTP 请求处理) │ │ ├── auth_controller.go # 用户认证 │ │ ├── oidc_controller.go # OIDC 认证 │ │ ├── organization_controller.go # 组织管理 │ │ ├── document_controller.go # 文档管理 │ │ ├── version_controller.go # 版本管理 │ │ ├── article_controller.go # 文章管理 │ │ ├── share_controller.go # 分享功能 │ │ ├── upload_controller.go # 文件上传 │ │ └── user_controller.go # 用户管理 │ │ │ ├── service/ # 服务层(业务逻辑) │ │ ├── auth_service.go │ │ ├── oidc_service.go │ │ ├── organization_service.go │ │ ├── document_service.go │ │ ├── version_service.go │ │ ├── article_service.go │ │ ├── permission_service.go # 权限控制 │ │ ├── permission_cache_service.go # 权限缓存 │ │ ├── share_service.go │ │ └── upload_service.go │ │ │ ├── dao/ # 数据访问层 │ │ ├── user_dao.go │ │ ├── organization_dao.go │ │ ├── document_dao.go │ │ ├── version_dao.go │ │ ├── article_dao.go │ │ └── document_share_dao.go │ │ │ ├── model/ # 数据模型(GORM) │ │ ├── user.go # 用户模型 │ │ ├── organization.go # 组织模型 │ │ ├── document.go # 文档模型 │ │ ├── document_version.go # 文档版本 │ │ ├── article.go # 文章实体 │ │ ├── article_version.go # 文章版本快照 │ │ ├── document_version_article.go # 版本-文章关联 │ │ └── document_share.go # 文档分享 │ │ │ ├── dto/ # 数据传输对象 │ │ ├── auth_dto.go │ │ ├── organization_dto.go │ │ ├── document_dto.go │ │ ├── article_dto.go │ │ ├── version_dto.go │ │ └── share_dto.go │ │ │ ├── middleware/ # 中间件 │ │ ├── auth_middleware.go # JWT 认证 │ │ ├── cors_middleware.go # CORS 处理 │ │ └── uuid_middleware.go # 访客 UUID │ │ │ ├── listener/ # 事件监听器 │ │ ├── article_logger_listener.go │ │ ├── document_logger_listener.go │ │ └── vectorize_listener.go # AI 向量化 │ │ │ ├── cron/ # 定时任务 │ │ └── orphan_article_cleaner.go # 孤立文章清理 │ │ │ └── constants/ # 常量定义 │ └── reserved_slugs.go │ ├── cmd/ # 命令行入口 │ ├── run.go # 启动逻辑 │ └── start.go # Start 函数(可被企业版扩展) │ ├── config/ # 配置管理 │ ├── assembly.go # 依赖注入配置 │ ├── server.go # 服务器配置 │ └── autoload/ # 自动加载配置 │ ├── base.go │ ├── database.go # 数据库配置 │ ├── redis.go # Redis 配置 │ ├── router.go # 路由配置 │ ├── middleware.go # 中间件配置 │ ├── s3.go # S3 配置 │ ├── migration.go # 数据库迁移 │ └── vectorstore.go # 向量数据库 │ ├── pkg/ # 公共包(可被企业版导入) │ ├── helper/ # 辅助函数 │ ├── interfaces/ # 接口定义 │ └── service/ # 服务实现 │ ├── auth/ # JWT 工具 │ ├── base62/ # Base62 编码 │ ├── cache/ # 缓存服务 │ ├── commit/ # Git 风格提交 │ ├── config/ # 配置服务 │ ├── cron/ # 定时任务 │ ├── database/ # 数据库服务 │ ├── env/ # 环境变量 │ ├── event/ # 事件系统 │ ├── http_server/ # HTTP 服务器 │ ├── logger/ # 日志服务 │ ├── markdown/ # Markdown 渲染 │ ├── migration/ # 数据库迁移 │ ├── redis/ # Redis 服务 │ ├── reload/ # 配置重载 │ ├── vectorize/ # 向量化服务 │ └── vectorstore/ # 向量存储(Qdrant/pgvector) │ ├── frontend/ # 前端子模块(Git 子模块) │ ├── pages/ # Nuxt 页面 │ ├── components/ # Vue 组件 │ ├── composables/ # 组合式 API │ ├── stores/ # Pinia 状态管理 │ ├── services/ # API 服务层 │ ├── middleware/ # 路由中间件 │ ├── types/ # TypeScript 类型 │ └── nuxt.config.ts # Nuxt 配置 │ ├── migrations/ # 数据库迁移文件 ├── tests/ # 测试文件 ├── examples/ # 示例代码 ├── docs/ # 文档 ├── templates/ # 模板文件 └── public/ # 静态资源
| 技术 | 版本 | 描述 |
|---|---|---|
| Go | 1.25.0 | 编程语言 |
| Gin | 1.11.0 | Web 框架 |
| GORM | 1.31.1 | ORM 框架 |
| MySQL/PostgreSQL/SQLite | - | 关系型数据库 |
| Redis | - | 缓存和会话存储 |
| Qdrant | - | 向量数据库(AI 搜索) |
| Viper | 1.21.0 | 配置管理 |
| Zap | 1.27.1 | 结构化日志 |
| JWT | 5.3.1 | JSON Web Tokens |
| AWS SDK | v2 | S3 对象存储 |
| LangChain Go | 0.1.14 | AI 集成 |
| Gomarkdown | - | Markdown 解析 |
| Bluemonday | 1.0.27 | XSS 防护 |
| 技术 | 版本 | 描述 |
|---|---|---|
| Nuxt 3 | 3.20.2 | Vue.js 框架 + SSR |
| Vue 3 | - | 渐进式前端框架 |
| TypeScript | - | 类型安全 |
| Pinia | - | 状态管理 |
| shadcn-vue | - | UI 组件库 |
| Tailwind CSS | - | 样式框架 |
| Monaco Editor | - | 代码编辑器 |
| MD Editor v3 | - | Markdown 编辑器 |
| Mermaid | - | 图表渲染 |
| Prism.js | - | 代码高亮 |
┌─────────────────────────────────┐ │ HTTP Layer │ │ (Gin Router & Middleware) │ ├─────────────────────────────────┤ │ Controller Layer │ │ (Request/Response Handling) │ ├─────────────────────────────────┤ │ Service Layer │ │ (Business Logic) │ ├─────────────────────────────────┤ │ DAO Layer │ │ (Data Access Objects) │ ├─────────────────────────────────┤ │ Model Layer │ │ (Database Models) │ └─────────────────────────────────┘
项目采用 Assembly 模式实现依赖注入,组件按顺序初始化:
// 初始化顺序在 config/assembly.go 中定义
envAssembly.Env{} // 环境变量
configAssembly.Config{} // 配置加载
loggerAssembly.Logger{} // 日志系统
databaseAssembly.Database{} // 数据库
redisAssembly.Redis{} // Redis
cacheAssembly.Cache{} // 缓存层
通过 helper.GetDatabase(), helper.GetConfig() 等方法访问共享依赖。
User (用户) ├─> UserOrganization (用户-组织关联) │ └─> Organization (组织) │ └─> Document (文档) │ ├─> DocumentVersion (文档版本) │ │ └─> DocumentVersionArticle (版本-文章关联) │ │ └─> Article (文章实体) │ │ └─> ArticleVersion (文章版本快照) │ └─> DocumentShare (文档分享)
stable_key 在版本间保持身份组织级别角色:
文档可见性:
# 用户注册 POST /api/auth/register # 用户登录 POST /api/auth/login # 获取当前用户信息 GET /api/auth/me # 更新个人资料 PUT /api/auth/profile # 修改密码 POST /api/auth/change-password # 上传头像 POST /api/auth/avatar # OIDC 授权 GET /api/auth/oidc/authorize # OIDC 回调 POST /api/auth/oidc/callback
# 列出组织 GET /api/organizations # 获取组织详情 GET /api/organizations/:orgSlug # 创建组织 POST /api/organizations # 更新组织 PUT /api/organizations/:orgSlug # 删除组织 DELETE /api/organizations/:orgSlug # 转让组织所有权 POST /api/organizations/:orgSlug/transfer # 添加成员 POST /api/organizations/:orgSlug/members # 更新成员角色 PUT /api/organizations/:orgSlug/members/:userId # 移除成员 DELETE /api/organizations/:orgSlug/members/:userId
# 列出文档 GET /api/documents GET /api/organizations/:orgSlug/documents # 获取文档详情 GET /api/organizations/:orgSlug/documents/:docSlug # 创建文档 POST /api/organizations/:orgSlug/documents # 更新文档 PUT /api/documents/:id # 删除文档 DELETE /api/documents/:id
# 创建版本 POST /api/organizations/:orgSlug/documents/:docSlug/versions # Fork 版本 POST /api/versions/:id/fork # 版本对比 GET /api/diff/versions/:v1/:v2
# 创建文章 POST /api/versions/:id/articles # 更新文章 PUT /api/versions/:id/articles/:articleId # 删除文章 DELETE /api/versions/:id/articles/:articleId # 发布文章 POST /api/versions/:id/articles/:articleId/publish # 合并文章 POST /api/versions/:id/articles/merge # 检查合并冲突 GET /api/versions/:id/articles/:articleId/merge-conflict # 强制合并 POST /api/versions/:id/articles/:articleId/force-merge # 回退版本 POST /api/versions/:id/articles/:articleId/revert
# 创建分享链接 POST /api/organizations/:orgSlug/documents/:docSlug/share # 获取分享详情 GET /api/share/:shareId # 验证分享密码 POST /api/share/:shareId/verify
# 上传文件(支持图片等) POST /api/upload
# 完整健康检查 GET /health # 简单健康检查 GET /health/simple
响应示例:
{
"code": 200,
"message": "健康检查成功",
"data": {
"status": "UP",
"timestamp": 1703123456,
"services": {
"database": {
"status": "UP"
},
"redis": {
"status": "UP"
}
}
}
}
# 服务器配置
server:
mode: release # debug, release, test
addr: ":8080"
# HTTP 服务配置
http:
load_static: false
static_mode: embed # disk 或 embed
static_dir: []
# 数据库配置(MySQL 示例)
database:
halt_on_migration_failure: true
host: localhost
port: 3306
username: root
password: password
database: mdoc_db
charset: utf8mb4
parse_time: true
loc: Local
max_idle_conns: 10
max_open_conns: 100
conn_max_lifetime: 3600
# PostgreSQL 配置(可选)
# postgres:
# host: localhost
# port: 5432
# username: postgres
# password: password
# database: mdoc_db
# sslmode: disable
# Redis 配置
redis:
host: localhost
port: 6379
password: ""
db: 0
pool_size: 10
min_idle_conns: 5
# JWT 配置
jwt:
secret: your-secret-key-change-this-in-production
expire_hours: 24
# OIDC 配置(可选)
oidc:
enabled: false
issuer: "https://your-oidc-provider.com"
client_id: "your-client-id"
client_secret: "your-client-secret"
redirect_uri: "http://localhost:3000/auth/callback"
scopes:
- openid
- profile
- email
# CORS 配置
cors:
allow_origins:
- "*"
allow_methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allow_headers:
- "Origin"
- "Content-Type"
- "Authorization"
在 config.yaml 中添加:
s3:
endpoint: "https://s3.amazonaws.com" # 或 MinIO 地址
region: "us-east-1"
access_key_id: "your-access-key"
secret_access_key: "your-secret-key"
bucket: "mdoc-uploads"
use_path_style: false # MinIO 设置为 true
在 config.yaml 中添加:
vectorstore:
type: qdrant # 目前仅支持 qdrant
qdrant:
url: "http://localhost:6333"
api_key: "" # 可选
collection_name: "mdoc_articles"
log:
level: info # debug, info, warn, error
filename: logs/app.log
max_size: 100 # MB
max_age: 30 # days
max_backups: 10
compress: true
后端开发:
# 安装依赖
go mod tidy
# 运行应用
go run main.go
# 或使用 Air 实现热重载(需要安装 Air)
air
前端开发:
cd frontend
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 配置后端 API 地址(创建 .env.local)
echo "NUXT_PUBLIC_API_BASE=http://localhost:8080" > .env.local
使用 DAO 层封装数据库操作:
// 示例:通过 DAO 查询用户
import "cnb.cool/mliev/mdoc/mdoc-server/app/dao"
func SomeService() error {
user, err := dao.GetUserByEmail("user@example.com")
if err != nil {
return err
}
// 使用 user...
}
使用 helper.GetDatabase() 进行自定义查询:
import "cnb.cool/mliev/mdoc/mdoc-server/internal/helper"
func CustomQuery() error {
db := helper.GetHelper().GetDatabase()
var results []Model
return db.Where("status = ?", "active").Find(&results).Error
}
app/controller/):type MyController struct{}
func (c MyController) SomeMethod(ctx *gin.Context) dto.Response {
// 业务逻辑
return dto.Response{
Code: 200,
Message: "成功",
Data: data,
}
}
config/autoload/router.go):authApi.GET("/my-endpoint", deps.WrapHandler(controller.MyController{}.SomeMethod))
app/model/):type MyModel struct {
ID int64 `gorm:"primarykey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
func (MyModel) TableName() string {
return "my_models"
}
config/autoload/migration.go):func (receiver Migration) Get() []any {
return []any{
&model.MyModel{},
// ...其他模型
}
}
gofmt 或 go fmt 格式化代码go vet 进行静态检查dto.Response 结构体构建镜像:
docker build -t mdoc-server:latest .
运行容器:
docker run -d \
-p 8080:8080 \
-v $(pwd)/config.yaml:/app/config.yaml \
--name mdoc-server \
mdoc-server:latest
创建 docker-compose.yml:
version: '3.8'
services:
mdoc-server:
build: .
ports:
- "8080:8080"
volumes:
- ./config.yaml:/app/config.yaml
- ./logs:/app/logs
depends_on:
- mysql
- redis
environment:
- GIN_MODE=release
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: mdoc_db
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
启动服务:
docker-compose up -d
# 编译
go build -o mdoc-server main.go
# 运行
./mdoc-server
# 或使用 systemd 管理服务
sudo cp mdoc-server /usr/local/bin/
sudo systemctl start mdoc-server
sudo systemctl enable mdoc-server
支持通过环境变量覆盖配置文件:
export SERVER_ADDR=":8080"
export DB_HOST=localhost
export DB_PORT=3306
export DB_USERNAME=root
export DB_PASSWORD=password
export DB_DATABASE=mdoc_db
export REDIS_HOST=localhost
export REDIS_PORT=6379
export JWT_SECRET=your-secret-key
无需重启服务即可重载配置(不会中断连接):
# 发送 SIGHUP 信号
kill -HUP <pid>
# 或使用 systemctl(如果使用 systemd)
sudo systemctl reload mdoc-server
服务支持优雅关闭,确保正在处理的请求完成:
# 发送 SIGTERM 或 SIGINT
kill -TERM <pid>
# 或
Ctrl+C
# 完整健康检查(包括数据库、Redis)
curl http://localhost:8080/health
# 简单健康检查
curl http://localhost:8080/health/simple
日志存储在 logs/app.log,使用 Zap 进行结构化记录:
// 记录结构化日志
helper.Logger().Info("用户创建成功",
zap.String("email", email),
zap.Int64("user_id", userID),
)
定时任务在 app/cron/ 中定义,由 Cron 服务自动调度:
// 示例:孤立文章清理任务
// 每天凌晨 2 点执行
@daily
func CleanOrphanArticles() {
// 清理逻辑...
}
请求头:
Authorization: Bearer <token> - JWT 认证(已登录用户)X-Visitor-UUID: <uuid> - 访客标识(分享链接访问)响应格式:
{
"code": 200,
"message": "成功",
"data": { ... }
}
在 frontend/.env.local 中配置后端地址:
NUXT_PUBLIC_API_BASE=http://localhost:8080
// 使用 composable
const { apiFetch } = useApi()
const data = await apiFetch('/api/documents')
// 使用便捷方法
import { useApiGet, useApiPost } from '~/composables/useApi'
const docs = await useApiGet('/api/documents')
await useApiPost('/api/documents', { title: 'New Doc' })
// 使用 service 层
import { documentService } from '~/services'
const docs = await documentService.list()
A: 在 config.yaml 中配置 OIDC 参数:
oidc:
enabled: true
issuer: "https://accounts.google.com" # Google
client_id: "your-client-id"
client_secret: "your-client-secret"
redirect_uri: "http://localhost:3000/auth/callback"
A: 修改 config.yaml 中的数据库配置,使用对应的数据库配置节:
database: 配置postgres: 配置sqlite: 配置A: 执行以下命令:
git submodule update --init --recursive
cd frontend
git fetch
git checkout main
A: 需要配置向量数据库:
config.yaml 中配置:vectorstore:
type: qdrant
qdrant:
url: "http://localhost:6333"
collection_name: "mdoc_articles"
A: 项目启动时会自动执行 AutoMigrate(),根据 Model 定义创建或更新表结构。如需手动控制,可修改 config.yaml 中的 halt_on_migration_failure 配置。
A: 在 config.yaml 中添加 S3 配置(支持 AWS S3、MinIO 等兼容服务):
s3:
endpoint: "https://s3.amazonaws.com"
region: "us-east-1"
access_key_id: "your-access-key"
secret_access_key: "your-secret-key"
bucket: "mdoc-uploads"
详细的开发规范请参考 CLAUDE.md,包括:
欢迎贡献代码、报告问题或提出建议!
git checkout -b feature/AmazingFeaturegit commit -m 'Add some AmazingFeature'git push origin feature/AmazingFeature本项目可以二次开发用于商业用途,但是禁止发布衍生版本。具体见 授权协议
Copyright © 2024-present, cnb.cool
如有任何问题或需要帮助,欢迎通过上方联系方式加入社区!