开发指南
本文档基于以下源文件编写:
- AGENTS.md -- 项目入口信息:常用命令、数据库规范、编码约定、API 文档规范、Git 约定、构建部署
- Makefile -- 构建、测试、代码生成、Docker 等全部 Make 目标
- Dockerfile -- 多阶段 Docker 构建流程与运行时环境
- sqlc.yaml -- sqlc 代码生成配置(引擎、输入输出路径、生成选项)
- go.mod -- Go 模块声明、版本要求与依赖列表
目录
1. 开发环境搭建
1.1 后端依赖
| 工具 | 版本要求 | 说明 |
|---|---|---|
| Go | 1.26 | go.mod 声明的最低版本 |
| SQLite | 运行时自动嵌入 | 使用 modernc.org/sqlite(纯 Go 实现),无需系统安装 C 库 |
| ffmpeg / ffprobe | 任意近期版本 | 音频转码与元数据探测 |
| sqlc | latest | SQL 代码生成:go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest |
| swag | latest | Swagger 文档生成(make swagger 会自动安装) |
| UPX | 可选 | 生产构建时自动压缩二进制;未安装则跳过 |
1.2 前端依赖
| 工具 | 版本要求 | 说明 |
|---|---|---|
| Flutter | 3.29+ | 跨平台 UI 框架 |
| Dart | 3.7+ | 随 Flutter 捆绑 |
前端代码位于 songloft-player/(独立仓库),开发时需先 clone 或初始化子模块。
1.3 快速验证
make deps # 下载 Go 依赖
make version # 检查 Go 版本
make run # 启动 dev 模式(admin/admin,端口 58091)
# 访问 http://localhost:58091/swagger/index.html 验证章节来源
- go.mod:1-3 -- 模块名与 Go 版本要求
- Makefile:3-4 --
CGO_ENABLED=0与GO_VERSION - Dockerfile:59-69 -- 运行时镜像依赖
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-unit | 仅 internal/ 下的单元测试 |
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 前端开发
cd songloft-player && flutter run -d chrome # standalone
cd songloft-player && flutter run -d chrome --dart-define=DEPLOY_MODE=embedded # embedded章节来源
- Makefile:97-108 --
build/build-lite目标 - Makefile:57-95 -- 前端构建目标
- AGENTS.md:30-49 -- 常用命令速查表
3. 编码约定
3.1 项目结构
后端遵循标准 Go layout,internal/ 防止外部包直接导入:
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)
| 场景 | 工具 | 说明 |
|---|---|---|
| 固定 SQL | sqlc | database/queries/*.sql 编写,make sqlc 生成 Go 代码 |
| 动态 SQL(变长 WHERE/SET) | squirrel | *_repository.go 中使用,禁止拼字符串 |
| 跨表写(事务) | RunInTx + UnitOfWork | 同一 *sql.Tx 下操作多个 Repository |
章节来源
- AGENTS.md:70-77 -- 后端编码约定
- go.mod:6-10 -- 核心依赖(squirrel、chi、jwt)
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: sqlite、schema 指向迁移目录、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 -- 测试断言行数时记得扣除
章节来源
- AGENTS.md:54-66 -- 数据库规范(铁律)
- sqlc.yaml:1-17 -- sqlc 完整配置
- go.mod:6 -- squirrel 依赖;go.mod:14 -- goose 依赖
5. API 文档规范
本节内容属于项目铁律。API 文档由 swaggo/swag 从代码注释生成,是前端开发与外部集成的唯一来源。
5.1 核心原则
**凡在 internal/app/routers.go(含其子注册函数)中注册的 handler,必须有 swag 注释。没有豁免。**哪怕是动态路由、catch-all、反代端点,也必须写 -- 在 @Description 中说明即可。
5.2 必填字段(每个 handler 至少 7 项)
// @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 修改后验证流程
make swagger # 生成文档
grep '<your-new-path>' docs/swagger.json # 确认新路径存在
make run # 在 Swagger UI 中目测生成的 docs/swagger.json、docs/swagger.yaml、docs/docs.go 必须入库。
章节来源
- AGENTS.md:82-123 -- API 文档规范(铁律)
- Makefile:329-337 --
make swagger目标
6. 测试策略
6.1 核心原则
- 测试文件
*_test.go与被测源码同目录 - 禁止手写 mock DB -- 使用
testutil.OpenMemoryDB(t)跑真实:memory:SQLite + 真实 Repository
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 测试命令
make test # 全部测试
make test-short # 跳过集成测试
make test-unit # 仅 internal/
make test-coverage # 覆盖率报告
make bench # 性能测试章节来源
- AGENTS.md:65-66 -- 测试规范
- Makefile:206-234 -- 测试 Make 目标
7. 构建变体
7.1 两个正交维度
| 维度 | 可选值 | 含义 |
|---|---|---|
| VERSION | dev / X.Y.Z | dev 自动启用 -tags dev(Swagger + pprof) |
| BUILD_TYPE | lite / 空(full) | lite 不嵌入前端资源 |
禁止混合使用(如 BUILD_TYPE=dev)。
7.2 构建矩阵
| VERSION | BUILD_TYPE | Make 目标 | build tags |
|---|---|---|---|
| dev | full | make build | dev |
| dev | lite | make build-lite | dev,lite |
| X.Y.Z | full | make build-prod | 无 |
| X.Y.Z | lite | make build-prod-lite | lite |
7.3 CGO 与交叉编译
Makefile 全局 CGO_ENABLED=0,SQLite 使用纯 Go 实现(modernc.org/sqlite),支持全平台交叉编译:
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 注入 Version、GitCommit、BuildTime、BuildType 到 internal/version 包。
7.4 Docker 构建
多阶段构建:golang:1.26-alpine(编译阶段,BuildKit 缓存挂载加速) → alpine:latest(运行时,含 ffmpeg、ALSA、ca-certificates)。
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 嵌入。
章节来源
- AGENTS.md:178-185 -- 构建标签与矩阵
- Makefile:1-4 --
CGO_ENABLED=0 - Makefile:19-26 -- LDFLAGS 注入
- Dockerfile:1-57 -- 多阶段构建流程
8. Git 提交约定
8.1 提交格式
遵循 Conventional Commits:type(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 ExtractFingerprint8.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 版本发布
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 自动构建发布。
章节来源
- AGENTS.md:169-173 -- Git 提交约定
- Makefile:339-341 --
make bump目标
