Files
backend-go/README.md
T
2026-06-05 18:00:14 +08:00

475 lines
19 KiB
Markdown
Raw 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.
# 浙江贝凡溯源赋码平台 - 后端服务 (Go 版本)
这是一个使用 Go 语言开发的溯源赋码平台后端服务,提供权限下发、产品溯源、项目工单、售后工单等功能。
## 技术栈
- **Web 框架**: Gin (高性能 HTTP 框架)
- **ORM**: GORM (Go 对象关系映射)
- **数据库**:
- SQLite (默认,适用于开发和轻量级部署)
- PostgreSQL (生产环境推荐)
- **认证**: JWT (JSON Web Token) - golang-jwt/jwt/v5
- **密码加密**: bcrypt (golang.org/x/crypto)
- **二维码生成**: yeqown/go-qrcode/v2
- **API 文档**: Swagger/OpenAPI (swaggo/swag)
- **CORS**: gin-contrib/cors (跨域资源共享)
- **配置管理**:
- Viper (环境变量和配置文件)
- gotenv (.env 文件加载)
- **日志**: Zap (高性能结构化日志)
- **验证**: go-playground/validator
- **测试**: Testify (测试框架)
- **工具**: UUID (github.com/google/uuid)
## 项目结构
```
backend-go/
├── config/ # 配置管理
│ └── config.go # 配置加载和解析(支持 .env 文件和环境变量)
├── controllers/ # 控制器层,处理 HTTP 请求
│ ├── aftersales_controller.go # 售后工单接口
│ ├── auth_controller.go # 认证相关接口
│ ├── dashboard_controller.go # 控制台统计接口
│ ├── employees_controller.go # 员工赋码接口
│ ├── helper.go # 控制器通用辅助函数
│ ├── product_traces_controller.go # 产品溯源接口
│ ├── project_orders_controller.go # 项目工单接口
│ └── users_controller.go # 员工主档管理接口(仅管理员)
├── database/ # 数据库连接和操作
│ └── database.go # 数据库初始化、连接池配置
├── docs/ # Swagger API 文档(自动生成)
│ ├── docs.go # Swagger 文档定义
│ ├── swagger.json # Swagger JSON 格式文档
│ └── swagger.yaml # Swagger YAML 格式文档
├── logger/ # 日志管理
│ └── logger.go # 结构化日志(使用 Zap)
├── middleware/ # 中间件层
│ └── auth.go # JWT 认证、管理员/技术员权限检查
├── models/ # 数据模型和 DTO
│ └── models.go # User、Company、EmployeeSerial、ProductTrace、AftersalesOrder、ProjectOrder 等模型定义
├── routes/ # 路由配置
│ └── routes.go # API 路由注册
├── services/ # 业务逻辑层
│ ├── aftersales_service.go # 售后工单业务逻辑
│ ├── auth_service.go # 认证业务逻辑
│ ├── dashboard_service.go # 控制台统计业务逻辑
│ ├── employees_service.go # 员工赋码业务逻辑
│ ├── product_traces_service.go # 产品溯源业务逻辑
│ ├── project_orders_service.go # 项目工单业务逻辑
│ ├── aftersales_service_test.go # 售后工单单元测试
│ ├── services_test.go # 认证/员工赋码单元测试
│ ├── users_service.go # 员工主档/后台账号业务逻辑
│ └── users_service_test.go # 员工主档/后台账号单元测试
├── tests/ # 集成测试
│ └── main_test.go # 端到端测试
├── data/ # 数据目录(SQLite 数据库存储位置)
├── main.go # 应用程序入口
├── go.mod # Go 模块依赖
├── go.sum # Go 模块校验和
├── Makefile # 构建和开发任务
├── .env.example # 环境变量示例
├── .env # 环境变量配置(需手动创建)
├── .golangci.yml # 代码检查配置
└── .gitignore # Git 忽略文件
```
## 快速开始
### 1. 安装依赖
```bash
cd backend-go
go mod download
# 或使用 Makefile
make deps
```
### 2. 配置环境变量
复制 `.env.example` 文件为 `.env` 并根据需要修改:
```bash
cp .env.example .env
```
**重要**: 生产环境请务必修改 `JWT_SECRET` 环境变量!
### 3. 使用 Makefile(推荐)
```bash
# 启动开发服务器
make run
# 编译项目
make build
# 运行测试
make test
# 生成测试覆盖率报告
make test-coverage
# 代码质量检查
make quality
# 清理构建文件
make clean
```
### 4. 手动启动
```bash
go run main.go
```
服务器将在 http://localhost:3000 上运行。
### 5. 配置管理
项目采用 **YAML 结构化管理 + 环境变量动态覆盖** 的方案:
#### 配置文件优先级(从高到低)
1. **环境变量** (最高优先级) - 格式:`APP_配置项`
2. **.env 文件** - 本地开发环境配置
3. **config.yaml** - 默认配置文件
4. **内置默认值** (最低优先级)
#### 环境变量命名规则
所有环境变量使用 `APP_` 前缀,多级配置使用 `_` 连接:
```bash
# 示例
APP_SERVER_PORT=8080 # 覆盖 server.port
APP_DATABASE_DRIVER=postgres # 覆盖 database.driver
APP_DATABASE_POSTGRES_HOST=db.com # 覆盖 database.postgres.host
APP_JWT_SECRET=my-secret-key # 覆盖 jwt.secret
```
#### 常用配置项
| 环境变量 | 对应配置项 | 说明 | 默认值 |
|---------|-----------|------|--------|
| `APP_SERVER_PORT` | server.port | 服务器端口 | 3000 |
| `APP_SERVER_ENVIRONMENT` | server.environment | 运行环境 | development |
| `APP_DATABASE_DRIVER` | database.driver | 数据库驱动 | sqlite |
| `APP_DATABASE_SQLITE_PATH` | database.sqlite.path | SQLite 路径 | ./data/database.sqlite |
| `APP_DATABASE_POSTGRES_HOST` | database.postgres.host | PostgreSQL 主机 | localhost |
| `APP_DATABASE_POSTGRES_PORT` | database.postgres.port | PostgreSQL 端口 | 5432 |
| `APP_DATABASE_POSTGRES_USER` | database.postgres.user | PostgreSQL 用户名 | trace |
| `APP_DATABASE_POSTGRES_PASSWORD` | database.postgres.password | PostgreSQL 密码 | trace123 |
| `APP_DATABASE_POSTGRES_DBNAME` | database.postgres.dbname | PostgreSQL 数据库名 | trace |
| `APP_JWT_SECRET` | jwt.secret | JWT 签名密钥 | your-secret-key... |
| `APP_JWT_EXPIRE` | jwt.expire | JWT 过期时间(秒) | 7200 |
#### 快速配置示例
**开发环境(.env**:
```bash
# 使用 SQLite
APP_DATABASE_DRIVER=sqlite
APP_DATABASE_SQLITE_PATH=./data/dev.sqlite
```
**生产环境(环境变量)**:
```bash
# 使用 PostgreSQL
export APP_SERVER_PORT=8080
export APP_SERVER_ENVIRONMENT=production
export APP_DATABASE_DRIVER=postgres
export APP_DATABASE_POSTGRES_HOST=prod-db.example.com
export APP_DATABASE_POSTGRES_PASSWORD=secure-password
export APP_JWT_SECRET=your-production-secret-key
```
### 6. 测试 API
**健康检查**:
```bash
curl -X GET http://localhost:3000/api/health
```
**用户登录**:
```bash
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Beifan@2026"}'
```
首次启动且用户表为空时,系统会自动创建默认管理员账号(请在生产环境立即修改密码):
- username: `admin`
- password: `Beifan@2026`
## API 文档
项目使用 Swagger 生成交互式 API 文档。
启动服务器后,访问以下地址查看完整的 API 文档:
```
http://localhost:3000/swagger/index.html
```
### Swagger 文档功能
- **交互式测试**: 直接在浏览器中测试 API 端点
- **请求/响应示例**: 查看每个接口的请求参数和响应格式
- **认证支持**: 支持 Bearer Token 认证,可以输入 JWT 令牌进行测试
- **按分组浏览**: API 按功能模块分组(认证、控制台、权限下发、产品溯源、项目工单、售后工单等)
### 重新生成 Swagger 文档
如果修改了代码中的 API 注解,需要重新生成 Swagger 文档:
```bash
# 生成文档
make swagger
# 或直接执行
go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g main.go
# 文档将生成在 docs/ 目录下
```
### 认证路由
| 方法 | 路径 | 描述 | 需要认证 |
| ---- | --------------------------- | ----------------------- | -------- |
| POST | `/api/auth/login` | 用户登录,返回 JWT 令牌 | 否 |
| POST | `/api/auth/logout` | 用户登出 | 是 |
| GET | `/api/auth/profile` | 获取当前用户信息 | 是 |
| PUT | `/api/auth/profile` | 更新用户信息 | 是 |
| POST | `/api/auth/change-password` | 修改密码 | 是 |
> 错误提示已做用户友好化,例如登录失败统一返回:`用户名或密码不正确`。
### 控制台
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
| ---- | ---- | ---- | -------- | ---- |
| GET | `/api/dashboard/stats` | 获取工单统计和最近售后工单 | 是 | 管理员 |
### 权限下发(员工赋码)
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
| ---- | -------------------------------------- | ------------------ | -------- | ------ |
| POST | `/api/employee-serials/generate` | 生成员工权限码 | 是 | 管理员 |
| GET | `/api/employee-serials` | 获取员工权限码列表 | 是 | 管理员 |
| GET | `/api/employee-serials/:serialNumber/query` | 查询员工权限码信息 | 否 | 任何 |
| POST | `/api/employee-serials/:serialNumber/qrcode` | 生成员工二维码 | 是 | 管理员 |
| PATCH | `/api/employee-serials/:serialNumber` | 更新员工权限码信息 | 是 | 管理员 |
| PUT | `/api/employee-serials/:serialNumber` | 更新员工权限码信息 | 是 | 管理员 |
| POST | `/api/employee-serials/:serialNumber/revoke` | 吊销员工权限码 | 是 | 管理员 |
**员工权限码特点**:
- 无有效期限制
- 创建员工主档时会自动生成 1 个员工码,并通过 `employeeId` 绑定员工
- 包含岗位(position)和员工姓名(employeeName)信息
- 序列号格式: `EMP26xxxxxx`(EMP + 年份后两位 + 6位随机字符)
### 产品溯源
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
| ---- | ---- | ---- | -------- | ---- |
| GET | `/api/product-traces/:serialNumber/query` | 公开查询产品溯源信息 | 否 | 任何 |
| POST | `/api/product-traces` | 创建产品溯源 | 是 | 管理员 |
| GET | `/api/product-traces` | 产品溯源列表 | 是 | 管理员 |
| GET | `/api/product-traces/:serialNumber` | 产品溯源详情 | 是 | 管理员 |
| PATCH | `/api/product-traces/:serialNumber` | 更新产品溯源 | 是 | 管理员 |
| POST | `/api/product-traces/:serialNumber/qrcode` | 生成产品二维码 | 是 | 管理员 |
| POST | `/api/product-traces/:serialNumber/wechat-qrcode` | 上传公众号二维码 | 是 | 管理员 |
| POST | `/api/product-traces/:serialNumber/revoke` | 停用产品溯源 | 是 | 管理员 |
| DELETE | `/api/product-traces/:serialNumber` | 删除产品溯源 | 是 | 管理员 |
**产品溯源字段顺序**:
企业名称、地址、电话、设备信息、质保期、出厂日期、产品序列号、官网链接(可选)、公众号二维码(可选)。
### 项目工单
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
| ---- | ---- | ---- | -------- | ---- |
| GET | `/api/project-orders/:serialNumber/query` | 公开查询项目工单 | 否 | 任何 |
| POST | `/api/project-orders/:serialNumber/site-images` | 上传现场图片 | 否 | 任何 |
| POST | `/api/project-orders/:serialNumber/complete` | 工程师提交完成 | 否 | 任何 |
| POST | `/api/project-orders` | 创建项目工单 | 是 | 管理员/技术员 |
| GET | `/api/project-orders` | 项目工单列表 | 是 | 管理员/技术员 |
| GET | `/api/project-orders/:serialNumber` | 项目工单详情 | 是 | 管理员/技术员 |
| PATCH | `/api/project-orders/:serialNumber` | 更新项目工单 | 是 | 管理员/技术员 |
| POST | `/api/project-orders/:serialNumber/qrcode` | 生成项目工单二维码 | 是 | 管理员/技术员 |
| POST | `/api/project-orders/:serialNumber/submit` | 后台提交项目完成 | 是 | 管理员/技术员 |
| POST | `/api/project-orders/:serialNumber/reassign` | 工单分配 | 是 | 管理员 |
| POST | `/api/project-orders/:serialNumber/force-close` | 强制完成 | 是 | 管理员 |
| DELETE | `/api/project-orders/:serialNumber` | 删除项目工单 | 是 | 管理员 |
**项目工单特点**:
- 用于现场勘查、现场实施等项目任务
- 工单号格式: `zjbf-xm-YYMMDDNN`
- 现场图片最多 18 张
- 仅需要工程师签名,无客户签字环节
- 完成状态使用 `已完成`
### 售后工单
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
| ------ | --------------------------------------------- | -------------------------- | -------- | --------------- |
| GET | `/api/aftersales/:serialNumber/query` | 公开查询工单(脱敏) | 否 | 任何 |
| POST | `/api/aftersales/:serialNumber/confirm` | 客户授权/未授权确认 | 否 | 任何 |
| POST | `/api/aftersales` | 创建售后工单 | 是 | 管理员/技术员 |
| GET | `/api/aftersales` | 工单列表(支持筛选) | 是 | 管理员/技术员 |
| GET | `/api/aftersales/:serialNumber` | 工单详情 | 是 | 管理员/技术员 |
| PATCH | `/api/aftersales/:serialNumber` | 更新工单(仅负责人或管理员)| 是 | 管理员/技术员 |
| POST | `/api/aftersales/:serialNumber/qrcode` | 生成工单二维码 | 是 | 管理员/技术员 |
| POST | `/api/aftersales/:serialNumber/submit` | 提交客户确认 | 是 | 管理员/技术员 |
| POST | `/api/aftersales/:serialNumber/reassign` | 工单分配(重新分配技术员) | 是 | 管理员 |
| POST | `/api/aftersales/:serialNumber/force-close` | 强制关闭工单 | 是 | 管理员 |
| DELETE | `/api/aftersales/:serialNumber` | 删除工单 | 是 | 管理员 |
**售后工单特点**:
- 服务类型枚举:`software`(软件故障)、`hardware`(硬件故障)、`maintenance`(售后维保)
- 工单号格式: `zjbf-sh-YYMMDDNN`(年份后 2 位 + 月份 2 位 + 日期 2 位 + 当天序号至少 2 位,例:`zjbf-sh-26052801`
- 序号按天重置,软删除工单不释放编号(避免回收造成混淆)
- 工单里的企业名称是售后客户信息,只保存在工单中
- 二维码扫码后客户在网页签名(canvas)后点「已授权」确认;选择「未授权」需填写退回原因
- 签名以 PNG dataURL 形式持久化到工单(`signature` 字段),管理员详情页可查看留底
- 签名校验:必须为 `data:image/png;base64,``data:image/jpeg;base64,` 前缀,解码后 200B500KB
- 客户确认接口每分钟同一工单最多 5 次请求
- 工单状态机: `created``pending_confirmation``closed` / `rejected`,被退回后可重新提交
- 公开查询不返回手机号(脱敏)
### 权限下发(仅管理员)
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
| ------ | ----------------------------------- | -------------------------- | -------- | ------------- |
| GET | `/api/users/assignable` | 可分配用户列表(用于售后) | 是 | 管理员/技术员 |
| POST | `/api/employees` | 创建员工并自动生成员工码 | 是 | 管理员 |
| GET | `/api/employees` | 员工列表(分页+筛选) | 是 | 管理员 |
| PATCH | `/api/employees/:id` | 更新员工姓名/电话/工号/岗位/角色 | 是 | 管理员 |
| POST | `/api/employees/:id/reset-password` | 重置后台账号密码 | 是 | 管理员 |
| DELETE | `/api/employees/:id` | 删除员工 | 是 | 管理员 |
**员工角色**:
- `admin`:管理员,拥有全部后台权限,包括权限下发、产品溯源、工单分配、强制关闭和删除工单
- `technician`:技术员,仅拥有工单模块权限,可创建/处理工单,可使用 `assignable` 查询可分配同事
- `employee`:员工,无后台登录权限,不需要密码,仅用于员工主档和员工码查询
**保护规则**:
- 不能删除自己;不能将自己的 admin 角色降级;不能删除最后一个 admin
- 创建 `admin` / `technician` 必须设置初始密码,密码 bcrypt 加密存储
- 创建 `employee` 不要求密码,且不能登录后台
## 测试
### 运行所有测试
```bash
# 运行所有测试
go test -v ./...
# 仅运行服务层单元测试
cd services && go test -v -cover
# 仅运行集成测试
go test -v ./tests/...
```
### 生成测试覆盖率报告
```bash
# 生成覆盖率报告
go test -v ./services/... -coverprofile=coverage.out
go tool cover -html=coverage.out
```
### 当前测试覆盖
- **services/**: 包含 AuthService、EmployeeSerialsService、AftersalesService、ProjectOrdersService、ProductTracesService 和 UsersService 的单元测试
- 用户认证测试(登录、获取用户信息、修改密码、更新资料)
- 员工赋码测试(生成、查询、更新、吊销、二维码生成)
- 售后工单测试(YYMMDDNN 序号生成、状态机、客户确认手机号校验、强制关闭)
- 项目工单测试(创建、完成、现场图片、工程师签名)
- 产品溯源测试(创建、查询、二维码、公众号二维码)
- 权限下发测试(创建员工自动生成员工码、重复工号、自降级保护、最后管理员保护、密码重置)
- **tests/**: 集成测试(健康检查、登录流程)
## 代码检查
使用 golangci-lint 进行代码检查:
```bash
golangci-lint run ./...
```
## 部署
### 编译为二进制文件
```bash
# Linux/Mac
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o trace-backend
# Windows
go build -o trace-backend.exe main.go
# macOS
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o trace-backend
```
### 运行
```bash
./trace-backend
```
## 数据库迁移
### SQLite 到 PostgreSQL 的迁移
修改 `.env` 文件中的数据库配置:
```bash
# 将数据库驱动改为 postgres
DATABASE_DRIVER=postgres
# PostgreSQL 配置
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=trace
POSTGRES_PASSWORD=trace123
POSTGRES_DB=trace
POSTGRES_SSLMODE=disable
```
然后重新启动应用即可:
```bash
./trace-backend
```
**注意**: 切换数据库后,原有 SQLite 中的数据不会自动迁移到 PostgreSQL。需要使用数据库迁移工具(如 pgloader)手动迁移数据。
## 贡献指南
1. 克隆项目
2. 创建新功能分支
3. 提交更改
4. 推送到远程仓库
5. 创建 Pull Request
### 代码风格要求
- 使用 gofmt 自动格式化代码
- 遵循 Go 官方编码规范
- 使用 golangci-lint 进行代码检查
- 保持代码简洁和高效
## 许可证
MIT License