Skip to content

开发指南

本文档基于以下源文件编写:

  • AGENTS.md -- 项目入口信息:常用命令、数据库规范、编码约定、API 文档规范、Git 约定、构建部署
  • Makefile -- 构建、测试、代码生成、Docker 等全部 Make 目标
  • Dockerfile -- 多阶段 Docker 构建流程与运行时环境
  • sqlc.yaml -- sqlc 代码生成配置(引擎、输入输出路径、生成选项)
  • go.mod -- Go 模块声明、版本要求与依赖列表

目录

  1. 开发环境搭建
  2. 常用命令
  3. 编码约定
  4. 数据库操作规范
  5. API 文档规范
  6. 测试策略
  7. 构建变体
  8. Git 提交约定

1. 开发环境搭建

1.1 后端依赖

工具版本要求说明
Go1.26go.mod 声明的最低版本
SQLite运行时自动嵌入使用 modernc.org/sqlite(纯 Go 实现),无需系统安装 C 库
ffmpeg / ffprobe任意近期版本音频转码与元数据探测
sqlclatestSQL 代码生成:go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
swaglatestSwagger 文档生成(make swagger 会自动安装)
UPX可选生产构建时自动压缩二进制;未安装则跳过

1.2 前端依赖

工具版本要求说明
Flutter3.29+跨平台 UI 框架
Dart3.7+随 Flutter 捆绑

前端代码位于 songloft-player/(独立仓库),开发时需先 clone 或初始化子模块。

1.3 快速验证

bash
make deps      # 下载 Go 依赖
make version   # 检查 Go 版本
make run       # 启动 dev 模式(admin/admin,端口 58091)
               # 访问 http://localhost:58091/swagger/index.html 验证

章节来源


2. 常用命令

2.1 后端命令

命令用途
make run启动 dev 模式(含 Swagger + pprof,默认 admin/admin/58091)
make build编译开发版(完整版,嵌入前端,-tags dev
make build-lite编译开发版(精简版,不嵌入前端,-tags "dev,lite"
make build-prod编译生产版(完整版,嵌入前端)
make build-prod-lite编译生产版(精简版,不嵌入前端)
make test运行全部测试
make test-short快速测试(跳过集成测试)
make test-unitinternal/ 下的单元测试
make check一键检查:fmt + vet + test
make sqlc重新生成 sqlc 代码(改了 queries/*.sql 后必跑)
make swagger重新生成 Swagger 文档
make bump TYPE=patch升级版本号并打 tag

2.2 前端构建命令

前端构建产物统一输出到 songloft-player-build/(不是 songloft-player/build/)。

命令用途
make build-frontend-web-embedded嵌入模式 Web(隐藏 API 地址 UI)
make build-frontend-web独立部署 Web
make build-frontend-{linux,windows,macos,android,ios}各平台客户端
make build-frontend-all当前系统支持的所有平台

2.3 前端开发

bash
cd songloft-player && flutter run -d chrome                                   # standalone
cd songloft-player && flutter run -d chrome --dart-define=DEPLOY_MODE=embedded # embedded

章节来源


3. 编码约定

3.1 项目结构

后端遵循标准 Go layoutinternal/ 防止外部包直接导入:

internal/
├── app/          # 应用入口:初始化、路由注册
├── database/     # 数据库打开、迁移、Repository、UnitOfWork
├── handlers/     # HTTP handler 层
├── middleware/    # 认证、日志等中间件
├── models/       # 数据模型定义
├── services/     # 业务逻辑层
└── ...

3.2 核心约定

  • 路由:Chi v5 + JWT 双 Token,路由注册集中在 internal/app/routers.go
  • 依赖注入:service 层只接收 Repository 接口,不接收 DB 实例
  • 日志:标准库 slog,不引入第三方框架
  • HTTP 错误:统一使用 respondError 函数
  • API 响应格式:RESTful 直返,禁止 {code, data, message} 信封;错误统一 {"error": "...", "detail": "..."}

3.3 数据访问层(无 ORM)

场景工具说明
固定 SQLsqlcdatabase/queries/*.sql 编写,make sqlc 生成 Go 代码
动态 SQL(变长 WHERE/SET)squirrel*_repository.go 中使用,禁止拼字符串
跨表写(事务)RunInTx + UnitOfWork同一 *sql.Tx 下操作多个 Repository

章节来源


4. 数据库操作规范

本节内容属于项目铁律,所有开发者必须严格遵守。完整访问栈:goose 迁移 → sqlc 固定 SQL → squirrel 动态 SQL → Repository → UnitOfWork

4.1 Schema 变更(迁移)

  • 迁移文件放在 internal/database/migrations/000N_xxx.sql
  • 启动时 goose.Up 自动执行,无需手动操作
  • 禁止直接手动 ALTER data/songloft.db

4.2 固定 SQL(sqlc)

  • internal/database/queries/{table}.sql 中编写 SQL,修改后执行 make sqlc
  • 生成产物在 internal/database/sqlc/必须入库

sqlc 配置(sqlc.yaml)关键选项:engine: sqliteschema 指向迁移目录、emit_interface: true 生成接口便于测试、emit_empty_slices: true 空结果返回 [] 而非 nil

4.3 动态 SQL 与事务

  • 变长 WHERE/SET*_repository.go 中使用 squirrel 构建,禁止拼接字符串
  • 跨表写必须使用 db.RunInTx(ctx, func(ctx, uow)),UnitOfWork 提供同一事务下的所有 Repository
  • 禁止 service 层手动 BeginTx,否则 SQLite 会 SQLITE_BUSY 死锁

4.4 错误语义与内置数据

  • Repository 未命中统一返回 database.ErrNotFound,service 用 errors.Is 判别
  • 迁移预置歌单 id=1「收藏」、id=2「电台收藏」,及 music_path/jwt_secret/source_* 默认 config -- 测试断言行数时记得扣除

章节来源


5. API 文档规范

本节内容属于项目铁律。API 文档由 swaggo/swag 从代码注释生成,是前端开发与外部集成的唯一来源

5.1 核心原则

**凡在 internal/app/routers.go(含其子注册函数)中注册的 handler,必须有 swag 注释。没有豁免。**哪怕是动态路由、catch-all、反代端点,也必须写 -- 在 @Description 中说明即可。

5.2 必填字段(每个 handler 至少 7 项)

go
// @Summary <一行中文摘要>
// @Description <详细描述;说清副作用/默认值/错误码触发条件>
// @Tags <业务分组,中文>
// @Produce json
// @Success 200 {object} <返回类型> "<说明>"
// @Security BearerAuth
// @Router /<path> [<method>]

补充字段:有请求体加 @Accept json + @Param ... body;有错误路径加 @Failure;公开端点省略 @Security

5.3 业务 Tag 列表

复用已有 tag,不要随手造新 tag:歌曲管理、歌单管理、电台与 HLS、扫描管理、配置管理、缓存管理、JS 插件、数据备份、设置、升级、认证。

5.4 多别名与 catch-all 路由

  • 多条 alias 路径 → 每条单写一行 @Router
  • r.HandleFunc(...) catch-all → 列出所有实际方法,每个一行 @Router
  • 动态路径 → @Description 注明「动态路由,OpenAPI 仅作占位」

5.5 修改后验证流程

bash
make swagger                                  # 生成文档
grep '<your-new-path>' docs/swagger.json      # 确认新路径存在
make run                                       # 在 Swagger UI 中目测

生成的 docs/swagger.jsondocs/swagger.yamldocs/docs.go 必须入库

章节来源


6. 测试策略

6.1 核心原则

  • 测试文件 *_test.go 与被测源码同目录
  • 禁止手写 mock DB -- 使用 testutil.OpenMemoryDB(t) 跑真实 :memory: SQLite + 真实 Repository
go
func TestSongService(t *testing.T) {
    db := testutil.OpenMemoryDB(t)
    repo := database.NewSongRepository(db)
    svc := services.NewSongService(repo)
    // ... 测试真实行为
}

优势:覆盖真实 SQL 路径、内存库启动极快、自动执行 goose 迁移保证 schema 一致。

6.2 注意事项

  • 迁移预置了内置歌单和默认 config,断言行数时必须扣除
  • 依赖注入设计保证 service 可独立测试,不需要也不应该 mock 数据库层

6.3 测试命令

bash
make test          # 全部测试
make test-short    # 跳过集成测试
make test-unit     # 仅 internal/
make test-coverage # 覆盖率报告
make bench         # 性能测试

章节来源


7. 构建变体

7.1 两个正交维度

维度可选值含义
VERSIONdev / X.Y.Zdev 自动启用 -tags dev(Swagger + pprof)
BUILD_TYPElite / 空(fulllite 不嵌入前端资源

禁止混合使用(如 BUILD_TYPE=dev)。

7.2 构建矩阵

VERSIONBUILD_TYPEMake 目标build tags
devfullmake builddev
devlitemake build-litedev,lite
X.Y.Zfullmake build-prod
X.Y.Zlitemake build-prod-litelite

7.3 CGO 与交叉编译

Makefile 全局 CGO_ENABLED=0,SQLite 使用纯 Go 实现(modernc.org/sqlite),支持全平台交叉编译:

bash
make build-cross GOOS=linux GOARCH=amd64 OUTPUT=songloft-linux-amd64
make build-cross GOOS=linux GOARCH=arm64 OUTPUT=songloft-linux-arm64
make build-cross GOOS=windows GOARCH=amd64 OUTPUT=songloft.exe

生产构建自动检测 UPX 并压缩。编译时通过 -ldflags 注入 VersionGitCommitBuildTimeBuildTypeinternal/version 包。

7.4 Docker 构建

多阶段构建:golang:1.26-alpine(编译阶段,BuildKit 缓存挂载加速) → alpine:latest(运行时,含 ffmpeg、ALSA、ca-certificates)。

bash
make docker-build                                               # 测试镜像
docker build --build-arg VERSION=2.10.0 -t songloft .           # 完整版
docker build --build-arg LITE_BUILD=true -t songloft:lite .     # 精简版

7.5 前端嵌入路径

完整版构建时前端产物必须位于 songloft-player-build/web-embedded/(不是 songloft-player/build/),由 internal/app/embed.go 嵌入。

章节来源


8. Git 提交约定

8.1 提交格式

遵循 Conventional Commitstype(scope): description

常见 type:feat(新功能)、fix(Bug 修复)、refactor(重构)、docs(文档)、test(测试)、chore(构建/配置)、perf(性能优化)。

feat(cache): add custom cache directory support
fix(fingerprint): remove invalid length threshold in ExtractFingerprint

8.2 禁止事项

  • 提交信息禁止添加 Co-Authored-By 尾部标记

8.3 子模块引用父仓库 Issue

子模块的 commit 引用父仓库 issue 时,必须使用完整路径:

fix(player): handle CORS error, see songloft-org/songloft#155   # 正确
fix(player): handle CORS error, see #155                         # 错误(解析为子模块 issue)

8.4 版本发布

bash
make bump              # patch(2.10.0 → 2.10.1)
make bump TYPE=minor   # minor(2.10.0 → 2.11.0)
make bump TYPE=major   # major(2.10.0 → 3.0.0)

Push tag 后由 .github/workflows/release.yml 自动构建发布。

章节来源