diff --git a/Makefile b/Makefile index 4c9ba05..e3fd31b 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,12 @@ deps: @echo "安装依赖..." @go mod tidy +# 生成 Swagger 文档 +swagger: + @echo "生成 Swagger 文档..." + @swag init -g main.go + @echo "Swagger 文档已生成在 docs/ 目录" + # 初始化数据库 init-db: @echo "初始化数据库..." diff --git a/README.md b/README.md index 69bcbae..b2b5d8c 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ - **认证**: 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 文件加载) @@ -188,6 +190,35 @@ curl -X POST http://localhost:3000/api/auth/login \ ## API 文档 +项目使用 Swagger 生成交互式 API 文档。 + +启动服务器后,访问以下地址查看完整的 API 文档: + +``` +http://localhost:3000/swagger/index.html +``` + +### Swagger 文档功能 + +- **交互式测试**: 直接在浏览器中测试 API 端点 +- **请求/响应示例**: 查看每个接口的请求参数和响应格式 +- **认证支持**: 支持 Bearer Token 认证,可以输入 JWT 令牌进行测试 +- **按分组浏览**: API 按功能模块分组(认证、序列号管理、企业管理等) + +### 重新生成 Swagger 文档 + +如果修改了代码中的 API 注解,需要重新生成 Swagger 文档: + +```bash +# 确保已安装 swag 工具 +go install github.com/swaggo/swag/cmd/swag@latest + +# 生成文档 +swag init -g main.go + +# 文档将生成在 docs/ 目录下 +``` + ### 认证路由 | 方法 | 路径 | 描述 | 需要认证 | diff --git a/controllers/auth_controller.go b/controllers/auth_controller.go index 17c3154..fb98d50 100644 --- a/controllers/auth_controller.go +++ b/controllers/auth_controller.go @@ -28,9 +28,9 @@ func NewAuthController() *AuthController { // @Accept json // @Produce json // @Param loginData body models.LoginDTO true "登录数据" -// @Success 200 {object} gin.H{message: string, accessToken: string, user: models.UserDTO} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} +// @Success 200 {object} models.LoginResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse // @Router /auth/login [post] func (c *AuthController) Login(ctx *gin.Context) { var loginData models.LoginDTO @@ -78,8 +78,8 @@ func (c *AuthController) Login(ctx *gin.Context) { // @Tags 认证 // @Produce json // @Security BearerAuth -// @Success 200 {object} models.UserDTO -// @Failure 401 {object} gin.H{message: string} +// @Success 200 {object} models.DataResponse +// @Failure 401 {object} models.ErrorResponse // @Router /auth/profile [get] func (c *AuthController) GetProfile(ctx *gin.Context) { userModel, ok := GetCurrentUser(ctx) @@ -106,9 +106,9 @@ func (c *AuthController) GetProfile(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param passwordData body models.ChangePasswordDTO true "密码修改数据" -// @Success 200 {object} gin.H{message: string} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} +// @Success 200 {object} models.BaseResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse // @Router /auth/change-password [post] func (c *AuthController) ChangePassword(ctx *gin.Context) { userModel, ok := GetCurrentUser(ctx) @@ -138,9 +138,9 @@ func (c *AuthController) ChangePassword(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param profileData body models.UpdateProfileDTO true "用户信息更新数据" -// @Success 200 {object} models.UserDTO -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} +// @Success 200 {object} models.DataResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse // @Router /auth/profile [put] func (c *AuthController) UpdateProfile(ctx *gin.Context) { userModel, ok := GetCurrentUser(ctx) diff --git a/controllers/companies_controller.go b/controllers/companies_controller.go index 54f0957..ebc62aa 100644 --- a/controllers/companies_controller.go +++ b/controllers/companies_controller.go @@ -30,9 +30,9 @@ func NewCompaniesController() *CompaniesController { // @Param page query int false "页码" // @Param limit query int false "每页数量" // @Param search query string false "搜索关键词" -// @Success 200 {object} gin.H{message: string, data: []models.Company, pagination: gin.H{page: int, limit: int, total: int, totalPages: int}} -// @Failure 401 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.PaginationResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /companies [get] func (c *CompaniesController) FindAll(ctx *gin.Context) { page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) @@ -66,12 +66,12 @@ func (c *CompaniesController) FindAll(ctx *gin.Context) { // @Accept json // @Produce json // @Security BearerAuth -// @Param companyData body gin.H{companyName: string} true "企业数据" -// @Success 201 {object} gin.H{message: string, company: models.Company} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 409 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Param companyData body models.CompanyDataRequest true "企业数据" +// @Success 201 {object} models.CompanyResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 409 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /companies [post] func (c *CompaniesController) Create(ctx *gin.Context) { var companyData struct { @@ -113,13 +113,13 @@ func (c *CompaniesController) Create(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param companyName path string true "企业名称" -// @Param companyData body gin.H{companyName?: string, isActive?: bool} true "企业数据" -// @Success 200 {object} gin.H{message: string, company: models.Company} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 404 {object} gin.H{message: string} -// @Failure 409 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Param companyData body models.CompanyUpdateRequest true "企业数据" +// @Success 200 {object} models.CompanyResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 409 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /companies/{companyName} [put] func (c *CompaniesController) Update(ctx *gin.Context) { companyName := ctx.Param("companyName") @@ -167,11 +167,11 @@ func (c *CompaniesController) Update(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param companyName path string true "企业名称" -// @Success 200 {object} gin.H{message: string} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 404 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.BaseResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /companies/{companyName} [delete] func (c *CompaniesController) Delete(ctx *gin.Context) { companyName := ctx.Param("companyName") diff --git a/controllers/serials_controller.go b/controllers/serials_controller.go index 9e38ec0..59ac0c3 100644 --- a/controllers/serials_controller.go +++ b/controllers/serials_controller.go @@ -30,10 +30,10 @@ func NewSerialsController() *SerialsController { // @Produce json // @Security BearerAuth // @Param generateData body models.GenerateSerialDTO true "生成数据" -// @Success 200 {object} gin.H{message: string, serials: []models.Serial} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.DataResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials/generate [post] func (c *SerialsController) Generate(ctx *gin.Context) { userModel, ok := GetCurrentUser(ctx) @@ -70,10 +70,10 @@ func (c *SerialsController) Generate(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param generateData body models.GenerateWithPrefixDTO true "生成数据" -// @Success 200 {object} gin.H{message: string, serials: []models.Serial} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.DataResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials/generate-with-prefix [post] func (c *SerialsController) GenerateWithPrefix(ctx *gin.Context) { userModel, ok := GetCurrentUser(ctx) @@ -112,11 +112,11 @@ func (c *SerialsController) GenerateWithPrefix(ctx *gin.Context) { // @Security BearerAuth // @Param serialNumber path string true "序列号" // @Param qrCodeData body models.QRCodeDTO false "二维码数据" -// @Success 200 {object} gin.H{message: string, qrCodeData: string, queryUrl: string} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 404 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.QRCodeResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials/{serialNumber}/qrcode [post] func (c *SerialsController) GenerateQRCode(ctx *gin.Context) { serialNumber := ctx.Param("serialNumber") @@ -154,10 +154,10 @@ func (c *SerialsController) GenerateQRCode(ctx *gin.Context) { // @Tags 序列号查询 // @Produce json // @Param serialNumber path string true "序列号" -// @Success 200 {object} gin.H{message: string, serial: models.Serial} -// @Failure 400 {object} gin.H{message: string} -// @Failure 404 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.DataResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials/{serialNumber}/query [get] func (c *SerialsController) Query(ctx *gin.Context) { serialNumber := ctx.Param("serialNumber") @@ -180,11 +180,11 @@ func (c *SerialsController) Query(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param page query int false "页码" -// @Param limit query int false "每页数量" +// @Param limit query int false " "每页数量" // @Param search query string false "搜索关键词" -// @Success 200 {object} gin.H{message: string, data: []models.Serial, pagination: gin.H{page: int, limit: int, total: int, totalPages: int}} -// @Failure 401 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.PaginationResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials [get] func (c *SerialsController) FindAll(ctx *gin.Context) { page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) @@ -217,11 +217,11 @@ func (c *SerialsController) FindAll(ctx *gin.Context) { // @Security BearerAuth // @Param serialNumber path string true "序列号" // @Param updateData body models.UpdateSerialDTO true "更新数据" -// @Success 200 {object} gin.H{message: string, serial: models.Serial} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 404 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.DataResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials/{serialNumber} [put] func (c *SerialsController) Update(ctx *gin.Context) { serialNumber := ctx.Param("serialNumber") @@ -249,11 +249,11 @@ func (c *SerialsController) Update(ctx *gin.Context) { // @Produce json // @Security BearerAuth // @Param serialNumber path string true "序列号" -// @Success 200 {object} gin.H{message: string} -// @Failure 400 {object} gin.H{message: string} -// @Failure 401 {object} gin.H{message: string} -// @Failure 404 {object} gin.H{message: string} -// @Failure 500 {object} gin.H{message: string} +// @Success 200 {object} models.BaseResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 401 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse // @Router /serials/{serialNumber}/revoke [post] func (c *SerialsController) Revoke(ctx *gin.Context) { serialNumber := ctx.Param("serialNumber") diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..2861856 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,1173 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/auth/change-password": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "修改当前登录用户的密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "修改密码", + "parameters": [ + { + "description": "密码修改数据", + "name": "passwordData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ChangePasswordDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/auth/login": { + "post": { + "description": "验证用户身份并返回 JWT 令牌", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "登录数据", + "name": "loginData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.LoginDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.LoginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/auth/profile": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取当前登录用户的个人信息", + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "获取用户信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新当前登录用户的个人信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "更新用户信息", + "parameters": [ + { + "description": "用户信息更新数据", + "name": "profileData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UpdateProfileDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/companies": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取企业列表,支持分页和搜索", + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "获取企业列表", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页数量", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "搜索关键词", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PaginationResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建新的企业", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "创建企业", + "parameters": [ + { + "description": "企业数据", + "name": "companyData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CompanyDataRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/models.CompanyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/companies/{companyName}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新企业信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "更新企业信息", + "parameters": [ + { + "type": "string", + "description": "企业名称", + "name": "companyName", + "in": "path", + "required": true + }, + { + "description": "企业数据", + "name": "companyData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CompanyUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CompanyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "删除企业", + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "删除企业", + "parameters": [ + { + "type": "string", + "description": "企业名称", + "name": "companyName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取序列号列表,支持分页和搜索", + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "获取序列号列表", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": " ", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "搜索关键词", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PaginationResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/generate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "生成指定数量的序列号", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "生成序列号", + "parameters": [ + { + "description": "生成数据", + "name": "generateData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.GenerateSerialDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/generate-with-prefix": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "生成带有指定前缀的序列号", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "带前缀生成序列号", + "parameters": [ + { + "description": "生成数据", + "name": "generateData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.GenerateWithPrefixDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新指定序列号的信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "更新序列号信息", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + }, + { + "description": "更新数据", + "name": "updateData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UpdateSerialDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}/qrcode": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "为指定序列号生成查询二维码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "生成二维码", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + }, + { + "description": "二维码数据", + "name": "qrCodeData", + "in": "body", + "schema": { + "$ref": "#/definitions/models.QRCodeDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.QRCodeResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}/query": { + "get": { + "description": "查询指定序列号的详细信息", + "produces": [ + "application/json" + ], + "tags": [ + "序列号查询" + ], + "summary": "查询序列号信息", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}/revoke": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "吊销指定序列号", + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "吊销序列号", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "models.BaseResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "models.ChangePasswordDTO": { + "type": "object", + "required": [ + "currentPassword", + "newPassword" + ], + "properties": { + "currentPassword": { + "type": "string" + }, + "newPassword": { + "type": "string", + "minLength": 6 + } + } + }, + "models.Company": { + "type": "object", + "properties": { + "companyName": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isActive": { + "type": "boolean" + }, + "updatedAt": { + "type": "string" + } + } + }, + "models.CompanyDataRequest": { + "type": "object", + "required": [ + "companyName" + ], + "properties": { + "companyName": { + "type": "string" + } + } + }, + "models.CompanyResponse": { + "type": "object", + "properties": { + "company": { + "$ref": "#/definitions/models.Company" + }, + "message": { + "type": "string" + } + } + }, + "models.CompanyUpdateRequest": { + "type": "object", + "properties": { + "companyName": { + "type": "string" + }, + "isActive": { + "type": "boolean" + } + } + }, + "models.DataResponse": { + "type": "object", + "properties": { + "data": {}, + "message": { + "type": "string" + } + } + }, + "models.ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "models.GenerateSerialDTO": { + "type": "object", + "required": [ + "companyName" + ], + "properties": { + "companyName": { + "type": "string" + }, + "quantity": { + "type": "integer", + "maximum": 1000, + "minimum": 1 + }, + "validDays": { + "type": "integer", + "maximum": 3650, + "minimum": 1 + } + } + }, + "models.GenerateWithPrefixDTO": { + "type": "object", + "required": [ + "companyName" + ], + "properties": { + "companyName": { + "type": "string" + }, + "quantity": { + "type": "integer", + "maximum": 1000, + "minimum": 1 + }, + "serialPrefix": { + "type": "string" + }, + "validDays": { + "type": "integer", + "maximum": 3650, + "minimum": 1 + } + } + }, + "models.LoginDTO": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string", + "minLength": 6 + }, + "username": { + "type": "string" + } + } + }, + "models.LoginResponse": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + }, + "message": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/models.UserDTO" + } + } + }, + "models.Pagination": { + "type": "object", + "properties": { + "limit": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "totalPages": { + "type": "integer" + } + } + }, + "models.PaginationResponse": { + "type": "object", + "properties": { + "data": {}, + "message": { + "type": "string" + }, + "pagination": { + "$ref": "#/definitions/models.Pagination" + } + } + }, + "models.QRCodeDTO": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string" + } + } + }, + "models.QRCodeResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "qrCodeData": { + "type": "string" + }, + "queryUrl": { + "type": "string" + } + } + }, + "models.UpdateProfileDTO": { + "type": "object", + "required": [ + "email", + "name" + ], + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "models.UpdateSerialDTO": { + "type": "object", + "properties": { + "companyName": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "validUntil": { + "type": "string" + } + } + }, + "models.UserDTO": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "role": { + "type": "string" + }, + "username": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "BearerAuth": { + "description": "Bearer token 认证", + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "localhost:8080", + BasePath: "/api", + Schemes: []string{}, + Title: "Trace System API", + Description: "防伪溯源系统 API 文档", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..358a3b2 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,1149 @@ +{ + "swagger": "2.0", + "info": { + "description": "防伪溯源系统 API 文档", + "title": "Trace System API", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "host": "localhost:8080", + "basePath": "/api", + "paths": { + "/auth/change-password": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "修改当前登录用户的密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "修改密码", + "parameters": [ + { + "description": "密码修改数据", + "name": "passwordData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ChangePasswordDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/auth/login": { + "post": { + "description": "验证用户身份并返回 JWT 令牌", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "登录数据", + "name": "loginData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.LoginDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.LoginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/auth/profile": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取当前登录用户的个人信息", + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "获取用户信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新当前登录用户的个人信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "更新用户信息", + "parameters": [ + { + "description": "用户信息更新数据", + "name": "profileData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UpdateProfileDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/companies": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取企业列表,支持分页和搜索", + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "获取企业列表", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "每页数量", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "搜索关键词", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PaginationResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建新的企业", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "创建企业", + "parameters": [ + { + "description": "企业数据", + "name": "companyData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CompanyDataRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/models.CompanyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/companies/{companyName}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新企业信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "更新企业信息", + "parameters": [ + { + "type": "string", + "description": "企业名称", + "name": "companyName", + "in": "path", + "required": true + }, + { + "description": "企业数据", + "name": "companyData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CompanyUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CompanyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "删除企业", + "produces": [ + "application/json" + ], + "tags": [ + "企业管理" + ], + "summary": "删除企业", + "parameters": [ + { + "type": "string", + "description": "企业名称", + "name": "companyName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取序列号列表,支持分页和搜索", + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "获取序列号列表", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": " ", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "搜索关键词", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PaginationResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/generate": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "生成指定数量的序列号", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "生成序列号", + "parameters": [ + { + "description": "生成数据", + "name": "generateData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.GenerateSerialDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/generate-with-prefix": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "生成带有指定前缀的序列号", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "带前缀生成序列号", + "parameters": [ + { + "description": "生成数据", + "name": "generateData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.GenerateWithPrefixDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新指定序列号的信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "更新序列号信息", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + }, + { + "description": "更新数据", + "name": "updateData", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UpdateSerialDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}/qrcode": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "为指定序列号生成查询二维码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "生成二维码", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + }, + { + "description": "二维码数据", + "name": "qrCodeData", + "in": "body", + "schema": { + "$ref": "#/definitions/models.QRCodeDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.QRCodeResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}/query": { + "get": { + "description": "查询指定序列号的详细信息", + "produces": [ + "application/json" + ], + "tags": [ + "序列号查询" + ], + "summary": "查询序列号信息", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/serials/{serialNumber}/revoke": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "吊销指定序列号", + "produces": [ + "application/json" + ], + "tags": [ + "序列号管理" + ], + "summary": "吊销序列号", + "parameters": [ + { + "type": "string", + "description": "序列号", + "name": "serialNumber", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "models.BaseResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "models.ChangePasswordDTO": { + "type": "object", + "required": [ + "currentPassword", + "newPassword" + ], + "properties": { + "currentPassword": { + "type": "string" + }, + "newPassword": { + "type": "string", + "minLength": 6 + } + } + }, + "models.Company": { + "type": "object", + "properties": { + "companyName": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isActive": { + "type": "boolean" + }, + "updatedAt": { + "type": "string" + } + } + }, + "models.CompanyDataRequest": { + "type": "object", + "required": [ + "companyName" + ], + "properties": { + "companyName": { + "type": "string" + } + } + }, + "models.CompanyResponse": { + "type": "object", + "properties": { + "company": { + "$ref": "#/definitions/models.Company" + }, + "message": { + "type": "string" + } + } + }, + "models.CompanyUpdateRequest": { + "type": "object", + "properties": { + "companyName": { + "type": "string" + }, + "isActive": { + "type": "boolean" + } + } + }, + "models.DataResponse": { + "type": "object", + "properties": { + "data": {}, + "message": { + "type": "string" + } + } + }, + "models.ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "models.GenerateSerialDTO": { + "type": "object", + "required": [ + "companyName" + ], + "properties": { + "companyName": { + "type": "string" + }, + "quantity": { + "type": "integer", + "maximum": 1000, + "minimum": 1 + }, + "validDays": { + "type": "integer", + "maximum": 3650, + "minimum": 1 + } + } + }, + "models.GenerateWithPrefixDTO": { + "type": "object", + "required": [ + "companyName" + ], + "properties": { + "companyName": { + "type": "string" + }, + "quantity": { + "type": "integer", + "maximum": 1000, + "minimum": 1 + }, + "serialPrefix": { + "type": "string" + }, + "validDays": { + "type": "integer", + "maximum": 3650, + "minimum": 1 + } + } + }, + "models.LoginDTO": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string", + "minLength": 6 + }, + "username": { + "type": "string" + } + } + }, + "models.LoginResponse": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + }, + "message": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/models.UserDTO" + } + } + }, + "models.Pagination": { + "type": "object", + "properties": { + "limit": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "totalPages": { + "type": "integer" + } + } + }, + "models.PaginationResponse": { + "type": "object", + "properties": { + "data": {}, + "message": { + "type": "string" + }, + "pagination": { + "$ref": "#/definitions/models.Pagination" + } + } + }, + "models.QRCodeDTO": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string" + } + } + }, + "models.QRCodeResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "qrCodeData": { + "type": "string" + }, + "queryUrl": { + "type": "string" + } + } + }, + "models.UpdateProfileDTO": { + "type": "object", + "required": [ + "email", + "name" + ], + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "models.UpdateSerialDTO": { + "type": "object", + "properties": { + "companyName": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "validUntil": { + "type": "string" + } + } + }, + "models.UserDTO": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "role": { + "type": "string" + }, + "username": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "BearerAuth": { + "description": "Bearer token 认证", + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..18a338e --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,742 @@ +basePath: /api +definitions: + models.BaseResponse: + properties: + message: + type: string + type: object + models.ChangePasswordDTO: + properties: + currentPassword: + type: string + newPassword: + minLength: 6 + type: string + required: + - currentPassword + - newPassword + type: object + models.Company: + properties: + companyName: + type: string + createdAt: + type: string + id: + type: integer + isActive: + type: boolean + updatedAt: + type: string + type: object + models.CompanyDataRequest: + properties: + companyName: + type: string + required: + - companyName + type: object + models.CompanyResponse: + properties: + company: + $ref: '#/definitions/models.Company' + message: + type: string + type: object + models.CompanyUpdateRequest: + properties: + companyName: + type: string + isActive: + type: boolean + type: object + models.DataResponse: + properties: + data: {} + message: + type: string + type: object + models.ErrorResponse: + properties: + error: + type: string + message: + type: string + type: object + models.GenerateSerialDTO: + properties: + companyName: + type: string + quantity: + maximum: 1000 + minimum: 1 + type: integer + validDays: + maximum: 3650 + minimum: 1 + type: integer + required: + - companyName + type: object + models.GenerateWithPrefixDTO: + properties: + companyName: + type: string + quantity: + maximum: 1000 + minimum: 1 + type: integer + serialPrefix: + type: string + validDays: + maximum: 3650 + minimum: 1 + type: integer + required: + - companyName + type: object + models.LoginDTO: + properties: + password: + minLength: 6 + type: string + username: + type: string + required: + - password + - username + type: object + models.LoginResponse: + properties: + accessToken: + type: string + message: + type: string + user: + $ref: '#/definitions/models.UserDTO' + type: object + models.Pagination: + properties: + limit: + type: integer + page: + type: integer + total: + type: integer + totalPages: + type: integer + type: object + models.PaginationResponse: + properties: + data: {} + message: + type: string + pagination: + $ref: '#/definitions/models.Pagination' + type: object + models.QRCodeDTO: + properties: + baseUrl: + type: string + type: object + models.QRCodeResponse: + properties: + message: + type: string + qrCodeData: + type: string + queryUrl: + type: string + type: object + models.UpdateProfileDTO: + properties: + email: + type: string + name: + type: string + required: + - email + - name + type: object + models.UpdateSerialDTO: + properties: + companyName: + type: string + isActive: + type: boolean + validUntil: + type: string + type: object + models.UserDTO: + properties: + createdAt: + type: string + email: + type: string + id: + type: integer + name: + type: string + role: + type: string + username: + type: string + type: object +host: localhost:8080 +info: + contact: + email: support@swagger.io + name: API Support + url: http://www.swagger.io/support + description: 防伪溯源系统 API 文档 + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: http://swagger.io/terms/ + title: Trace System API + version: "1.0" +paths: + /auth/change-password: + post: + consumes: + - application/json + description: 修改当前登录用户的密码 + parameters: + - description: 密码修改数据 + in: body + name: passwordData + required: true + schema: + $ref: '#/definitions/models.ChangePasswordDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 修改密码 + tags: + - 认证 + /auth/login: + post: + consumes: + - application/json + description: 验证用户身份并返回 JWT 令牌 + parameters: + - description: 登录数据 + in: body + name: loginData + required: true + schema: + $ref: '#/definitions/models.LoginDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.LoginResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: 用户登录 + tags: + - 认证 + /auth/profile: + get: + description: 获取当前登录用户的个人信息 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DataResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 获取用户信息 + tags: + - 认证 + put: + consumes: + - application/json + description: 更新当前登录用户的个人信息 + parameters: + - description: 用户信息更新数据 + in: body + name: profileData + required: true + schema: + $ref: '#/definitions/models.UpdateProfileDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DataResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 更新用户信息 + tags: + - 认证 + /companies: + get: + description: 获取企业列表,支持分页和搜索 + parameters: + - description: 页码 + in: query + name: page + type: integer + - description: 每页数量 + in: query + name: limit + type: integer + - description: 搜索关键词 + in: query + name: search + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.PaginationResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 获取企业列表 + tags: + - 企业管理 + post: + consumes: + - application/json + description: 创建新的企业 + parameters: + - description: 企业数据 + in: body + name: companyData + required: true + schema: + $ref: '#/definitions/models.CompanyDataRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/models.CompanyResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "409": + description: Conflict + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 创建企业 + tags: + - 企业管理 + /companies/{companyName}: + delete: + description: 删除企业 + parameters: + - description: 企业名称 + in: path + name: companyName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 删除企业 + tags: + - 企业管理 + put: + consumes: + - application/json + description: 更新企业信息 + parameters: + - description: 企业名称 + in: path + name: companyName + required: true + type: string + - description: 企业数据 + in: body + name: companyData + required: true + schema: + $ref: '#/definitions/models.CompanyUpdateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.CompanyResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "409": + description: Conflict + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 更新企业信息 + tags: + - 企业管理 + /serials: + get: + description: 获取序列号列表,支持分页和搜索 + parameters: + - description: 页码 + in: query + name: page + type: integer + - description: ' ' + in: query + name: limit + type: integer + - description: 搜索关键词 + in: query + name: search + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.PaginationResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 获取序列号列表 + tags: + - 序列号管理 + /serials/{serialNumber}: + put: + consumes: + - application/json + description: 更新指定序列号的信息 + parameters: + - description: 序列号 + in: path + name: serialNumber + required: true + type: string + - description: 更新数据 + in: body + name: updateData + required: true + schema: + $ref: '#/definitions/models.UpdateSerialDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DataResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 更新序列号信息 + tags: + - 序列号管理 + /serials/{serialNumber}/qrcode: + post: + consumes: + - application/json + description: 为指定序列号生成查询二维码 + parameters: + - description: 序列号 + in: path + name: serialNumber + required: true + type: string + - description: 二维码数据 + in: body + name: qrCodeData + schema: + $ref: '#/definitions/models.QRCodeDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.QRCodeResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 生成二维码 + tags: + - 序列号管理 + /serials/{serialNumber}/query: + get: + description: 查询指定序列号的详细信息 + parameters: + - description: 序列号 + in: path + name: serialNumber + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DataResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: 查询序列号信息 + tags: + - 序列号查询 + /serials/{serialNumber}/revoke: + post: + description: 吊销指定序列号 + parameters: + - description: 序列号 + in: path + name: serialNumber + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 吊销序列号 + tags: + - 序列号管理 + /serials/generate: + post: + consumes: + - application/json + description: 生成指定数量的序列号 + parameters: + - description: 生成数据 + in: body + name: generateData + required: true + schema: + $ref: '#/definitions/models.GenerateSerialDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DataResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 生成序列号 + tags: + - 序列号管理 + /serials/generate-with-prefix: + post: + consumes: + - application/json + description: 生成带有指定前缀的序列号 + parameters: + - description: 生成数据 + in: body + name: generateData + required: true + schema: + $ref: '#/definitions/models.GenerateWithPrefixDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DataResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + security: + - BearerAuth: [] + summary: 带前缀生成序列号 + tags: + - 序列号管理 +securityDefinitions: + BearerAuth: + description: Bearer token 认证 + in: header + name: Authorization + type: apiKey +swagger: "2.0" diff --git a/go.mod b/go.mod index 3ae6420..a7c9bcd 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module git.beifan.cn/trace-system/backend-go go 1.26 require ( + github.com/gin-contrib/cors v1.7.6 github.com/gin-gonic/gin v1.11.0 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/uuid v1.6.0 @@ -10,6 +11,9 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.1 + github.com/swaggo/swag v1.16.6 github.com/yeqown/go-qrcode/v2 v2.2.5 github.com/yeqown/go-qrcode/writer/standard v1.3.0 go.uber.org/zap v1.27.1 @@ -20,35 +24,47 @@ require ( ) require ( - github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/fogleman/gg v1.3.0 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.0 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect github.com/yeqown/reedsolomon v1.0.0 // indirect - go.uber.org/mock v0.5.0 // indirect + go.uber.org/mock v0.6.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/image v0.10.0 // indirect - golang.org/x/mod v0.32.0 // indirect + golang.org/x/mod v0.33.0 // indirect golang.org/x/sync v0.19.0 // indirect - golang.org/x/tools v0.41.0 // indirect + golang.org/x/tools v0.42.0 // indirect ) require ( - github.com/bytedance/sonic v1.14.0 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/spec v0.22.3 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-json v0.10.5 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect @@ -69,12 +85,12 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/net v0.49.0 // indirect + golang.org/x/arch v0.24.0 // indirect + golang.org/x/net v0.50.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 471384d..449878d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,11 @@ -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -14,26 +18,57 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= +github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= +github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= @@ -83,12 +118,12 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= @@ -104,19 +139,27 @@ github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjb github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY= +github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk= github.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw= github.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34= @@ -126,16 +169,16 @@ github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y= +golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= @@ -144,14 +187,15 @@ golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M= golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -180,11 +224,11 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/main.go b/main.go index c9eb775..c979552 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,34 @@ package main +// @title Trace System API +// @version 1.0 +// @description 防伪溯源系统 API 文档 +// @termsOfService http://swagger.io/terms/ + +// @contact.name API Support +// @contact.url http://www.swagger.io/support +// @contact.email support@swagger.io + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host localhost:8080 +// @BasePath /api +// @securityDefinitions.apikey BearerAuth +// @in header +// @name Authorization +// @description Bearer token 认证 + import ( "git.beifan.cn/trace-system/backend-go/config" "git.beifan.cn/trace-system/backend-go/database" + _ "git.beifan.cn/trace-system/backend-go/docs" "git.beifan.cn/trace-system/backend-go/logger" "git.beifan.cn/trace-system/backend-go/routes" + "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) func main() { @@ -45,18 +68,14 @@ func main() { r.Use(gin.Recovery()) // 启用 CORS - r.Use(func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") - c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") - c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE") - - if c.Request.Method == "OPTIONS" { - c.AbortWithStatus(204) - return - } - c.Next() - }) + r.Use(cors.New(cors.Config{ + AllowOrigins: []string{"*"}, + AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, + AllowHeaders: []string{"Origin", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"}, + ExposeHeaders: []string{"Content-Length"}, + AllowCredentials: true, + MaxAge: 12 * 3600, + })) // 配置路由 routes.SetupRoutes(r) @@ -65,10 +84,14 @@ func main() { api := r.Group("/api") routes.SetupAPIRoutes(api) + // Swagger 文档路由 + r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + // 启动服务器 port := cfg.Server.Port logger.Info("服务器运行在 http://localhost:" + port) - logger.Info("API 文档: http://localhost:" + port + "/api/health") + logger.Info("Swagger 文档: http://localhost:" + port + "/swagger/index.html") + logger.Info("健康检查: http://localhost:" + port + "/api/health") logger.Info("环境: " + cfg.Server.Environment) if err := r.Run(":" + port); err != nil { diff --git a/models/models.go b/models/models.go index 47770eb..c1bf6e2 100644 --- a/models/models.go +++ b/models/models.go @@ -22,13 +22,13 @@ type User struct { // Company 模型 type Company struct { - ID uint `gorm:"primaryKey"` - CompanyName string `gorm:"uniqueIndex;size:255"` - IsActive bool `gorm:"default:true"` - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt gorm.DeletedAt `gorm:"index"` - Serials []Serial `gorm:"foreignKey:CompanyName;references:CompanyName"` + ID uint `gorm:"primaryKey" json:"id"` + CompanyName string `gorm:"uniqueIndex;size:255" json:"companyName"` + IsActive bool `gorm:"default:true" json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + Serials []Serial `gorm:"foreignKey:CompanyName;references:CompanyName" json:"-"` } // Serial 模型 @@ -100,3 +100,66 @@ type UpdateSerialDTO struct { type QRCodeDTO struct { BaseUrl string `json:"baseUrl,omitempty"` } + +// LoginResponse 登录响应 +type LoginResponse struct { + Message string `json:"message"` + AccessToken string `json:"accessToken"` + User UserDTO `json:"user"` +} + +// BaseResponse 基础响应 +type BaseResponse struct { + Message string `json:"message"` +} + +// ErrorResponse 错误响应 +type ErrorResponse struct { + Message string `json:"message"` + Error string `json:"error,omitempty"` +} + +// DataResponse 数据响应 +type DataResponse struct { + Message string `json:"message"` + Data any `json:"data"` +} + +// PaginationResponse 分页响应 +type PaginationResponse struct { + Message string `json:"message"` + Data any `json:"data"` + Pagination Pagination `json:"pagination"` +} + +// Pagination 分页信息 +type Pagination struct { + Page int `json:"page"` + Limit int `json:"limit"` + Total int64 `json:"total"` + TotalPages int `json:"totalPages"` +} + +// QRCodeResponse 二维码响应 +type QRCodeResponse struct { + Message string `json:"message"` + QRCodeData string `json:"qrCodeData"` + QueryURL string `json:"queryUrl"` +} + +// CompanyResponse 企业响应 +type CompanyResponse struct { + Message string `json:"message"` + Company Company `json:"company"` +} + +// CompanyDataRequest 企业数据请求 +type CompanyDataRequest struct { + CompanyName string `json:"companyName" validate:"required"` +} + +// CompanyUpdateRequest 企业更新请求 +type CompanyUpdateRequest struct { + CompanyName string `json:"companyName"` + IsActive *bool `json:"isActive"` +} diff --git a/services/data/database.sqlite b/services/data/database.sqlite index bf97415..22d2fd8 100644 Binary files a/services/data/database.sqlite and b/services/data/database.sqlite differ