From 4f18028a7bb66ee946b6a5d4de19bb6b6d13084f Mon Sep 17 00:00:00 2001 From: ZHENG XIAOYI Date: Mon, 2 Mar 2026 12:57:07 +0800 Subject: [PATCH] Re-migrate code --- AGENTS.md | 7 ++++- README.md | 8 ++++++ controllers/employees_controller.go | 26 ++++++++++++++++++ models/models.go | 1 + routes/routes.go | 1 + services/companies_service.go | 42 +++++++++++++++++++++++------ services/employees_service.go | 25 +++++++++++++++-- 7 files changed, 99 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f5a0dc6..97290c9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -125,7 +125,7 @@ func (s *AuthService) ValidateUser(username, password string) (models.User, erro var user models.User err := database.DB.Where("username = ?", username).First(&user).Error if err != nil { - return models.User{}, errors.New("用户名或密码错误") + return models.User{}, errors.New("用户名或密码不正确") } return user, nil } @@ -149,6 +149,9 @@ func (c *AuthController) Login(ctx *gin.Context) { } ``` +- Use `ErrorResponse` for API errors so messages are user-friendly and consistent. +- Do not leak raw DB/stack errors to clients; log internal details and return safe messages. + ### Response Format **Success response**: ```json @@ -158,6 +161,8 @@ func (c *AuthController) Login(ctx *gin.Context) { } ``` +Some endpoints also return Node-compatible top-level keys (e.g. `serial`, `serials`, `pagination`) to preserve frontend compatibility. + **Error response**: ```json { diff --git a/README.md b/README.md index 7b84a50..302ff78 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,8 @@ swag init -g main.go | PUT | `/api/auth/profile` | 更新用户信息 | 是 | | POST | `/api/auth/change-password` | 修改密码 | 是 | +> 错误提示已做用户友好化,例如登录失败统一返回:`用户名或密码不正确`。 + ### 序列号管理 | 方法 | 路径 | 描述 | 需要认证 | 角色 | @@ -279,6 +281,12 @@ swag init -g main.go | PUT | `/api/employee-serials/:serialNumber` | 更新员工序列号信息 | 是 | 管理员 | | POST | `/api/employee-serials/:serialNumber/revoke` | 吊销员工序列号 | 是 | 管理员 | +### Node 对齐说明 + +- Go 版路由已与 `backend-node` 对齐(含 `PATCH` 更新接口、企业详情、企业吊销、删除企业下单个序列号)。 +- 企业统计与企业详情接口统一返回 `{ message, data }` 结构。 +- 序列号相关返回字段已对齐前端使用(如 `serialNumber`、`validUntil`、`createdBy`、`status`)。 + **员工序列号特点**: - 无有效期限制(与企业赋码不同) - 包含部门(department)和员工姓名(employeeName)信息 diff --git a/controllers/employees_controller.go b/controllers/employees_controller.go index c582161..296c4b8 100644 --- a/controllers/employees_controller.go +++ b/controllers/employees_controller.go @@ -52,6 +52,7 @@ func (c *EmployeeSerialsController) Generate(ctx *gin.Context) { generateData.EmployeeName, generateData.Quantity, userModel.ID, + generateData.SerialPrefix, ) if err != nil { ErrorResponse(ctx, http.StatusInternalServerError, err.Error()) @@ -226,3 +227,28 @@ func (c *EmployeeSerialsController) Revoke(ctx *gin.Context) { SuccessResponse(ctx, "员工序列号吊销成功") } + +// Delete 删除员工序列号 +// @Summary 删除员工序列号 +// @Description 删除指定员工序列号(物理删除) +// @Tags 员工赋码管理 +// @Produce json +// @Security BearerAuth +// @Param serialNumber path string true "序列号" +// @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 /employee-serials/{serialNumber} [delete] +func (c *EmployeeSerialsController) Delete(ctx *gin.Context) { + serialNumber := ctx.Param("serialNumber") + + err := c.employeeSerialsService.Delete(serialNumber) + if err != nil { + ErrorResponse(ctx, http.StatusBadRequest, err.Error()) + return + } + + SuccessResponse(ctx, "员工序列号删除成功") +} diff --git a/models/models.go b/models/models.go index e2de948..eaab9f8 100644 --- a/models/models.go +++ b/models/models.go @@ -199,6 +199,7 @@ type GenerateEmployeeSerialDTO struct { Department string `json:"department" validate:"required"` EmployeeName string `json:"employeeName" validate:"required"` Quantity int `json:"quantity" validate:"min=1,max=1000"` + SerialPrefix string `json:"serialPrefix,omitempty"` } // UpdateEmployeeSerialDTO 员工序列号更新请求数据 diff --git a/routes/routes.go b/routes/routes.go index 83702d9..895fbaf 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -71,5 +71,6 @@ func SetupAPIRoutes(r *gin.RouterGroup) { employeeSerialsRoutes.GET("/", middleware.JWTAuthMiddleware(), employeeSerialsController.FindAll) employeeSerialsRoutes.PUT("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Update) employeeSerialsRoutes.POST("/:serialNumber/revoke", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Revoke) + employeeSerialsRoutes.DELETE("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Delete) } } diff --git a/services/companies_service.go b/services/companies_service.go index a16d7f6..69f5400 100644 --- a/services/companies_service.go +++ b/services/companies_service.go @@ -3,6 +3,7 @@ package services import ( "errors" "fmt" + "sort" "time" "gorm.io/gorm" @@ -311,8 +312,14 @@ func (s *CompaniesService) GetStats() (map[string]any, error) { return nil, errors.New("查询序列号统计失败") } + var employeeSerials []models.EmployeeSerial + if err := database.DB.Order("created_at DESC").Find(&employeeSerials).Error; err != nil { + return nil, errors.New("查询员工序列号统计失败") + } + companyCount := len(companies) serialCount := len(serials) + employeeSerialCount := len(employeeSerials) activeCount := 0 for _, serial := range serials { if serial.IsActive && (serial.ValidUntil == nil || serial.ValidUntil.After(now)) { @@ -355,24 +362,43 @@ func (s *CompaniesService) GetStats() (map[string]any, error) { } recentSerials := make([]map[string]any, 0) - for i, serial := range serials { - if i >= 10 { - break - } + // 添加企业序列号 + for _, serial := range serials { recentSerials = append(recentSerials, map[string]any{ "serialNumber": serial.SerialNumber, "companyName": serial.CompanyName, "isActive": serial.IsActive, "createdAt": serial.CreatedAt, + "type": "company", }) } + // 添加员工序列号 + for _, serial := range employeeSerials { + recentSerials = append(recentSerials, map[string]any{ + "serialNumber": serial.SerialNumber, + "companyName": serial.CompanyName, + "isActive": serial.IsActive, + "createdAt": serial.CreatedAt, + "type": "employee", + "department": serial.Department, + "employeeName": serial.EmployeeName, + }) + } + // 按创建时间排序,保留最新的10条 + sort.Slice(recentSerials, func(i, j int) bool { + return recentSerials[i]["createdAt"].(time.Time).After(recentSerials[j]["createdAt"].(time.Time)) + }) + if len(recentSerials) > 10 { + recentSerials = recentSerials[:10] + } return map[string]any{ "overview": map[string]any{ - "totalCompanies": companyCount, - "totalSerials": serialCount, - "activeSerials": activeCount, - "inactiveSerials": inactiveCount, + "totalCompanies": companyCount, + "totalSerials": serialCount, + "totalEmployeeSerials": employeeSerialCount, + "activeSerials": activeCount, + "inactiveSerials": inactiveCount, }, "monthlyStats": monthlyItems, "recentCompanies": recentCompanies, diff --git a/services/employees_service.go b/services/employees_service.go index 7eaef27..20c278d 100644 --- a/services/employees_service.go +++ b/services/employees_service.go @@ -28,6 +28,7 @@ func (s *EmployeeSerialsService) Generate( employeeName string, quantity int, userId uint, + serialPrefix string, ) ([]models.EmployeeSerial, error) { var serials []models.EmployeeSerial @@ -45,8 +46,12 @@ func (s *EmployeeSerialsService) Generate( } } - // 生成序列号前缀 (EMP + 年份后两位) - serialPrefix := fmt.Sprintf("EMP%d", time.Now().Year()%100) + // 生成序列号前缀 + if serialPrefix == "" { + serialPrefix = fmt.Sprintf("EMP%d", time.Now().Year()%100) + } + // 标准化前缀(转大写,去除空格) + serialPrefix = strings.ToUpper(strings.TrimSpace(serialPrefix)) // 预生成所有序列号 serialNumbers := make(map[string]bool) @@ -199,6 +204,22 @@ func (s *EmployeeSerialsService) Revoke(serialNumber string) error { return nil } +// Delete 删除员工序列号(物理删除) +func (s *EmployeeSerialsService) Delete(serialNumber string) error { + var serial models.EmployeeSerial + result := database.DB.Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial) + if result.Error != nil { + return fmt.Errorf("查询员工序列号失败: %w", errors.New("序列号不存在")) + } + + result = database.DB.Delete(&serial) + if result.Error != nil { + return fmt.Errorf("删除员工序列号失败: %w", result.Error) + } + + return nil +} + // GenerateQRCode 生成员工二维码 func (s *EmployeeSerialsService) GenerateQRCode( serialNumber string,