Re-migrate code

This commit is contained in:
2026-03-02 12:57:07 +08:00
parent 6070df659a
commit 4f18028a7b
7 changed files with 99 additions and 11 deletions

View File

@@ -125,7 +125,7 @@ func (s *AuthService) ValidateUser(username, password string) (models.User, erro
var user models.User var user models.User
err := database.DB.Where("username = ?", username).First(&user).Error err := database.DB.Where("username = ?", username).First(&user).Error
if err != nil { if err != nil {
return models.User{}, errors.New("用户名或密码错误") return models.User{}, errors.New("用户名或密码不正确")
} }
return user, nil 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 ### Response Format
**Success response**: **Success response**:
```json ```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**: **Error response**:
```json ```json
{ {

View File

@@ -240,6 +240,8 @@ swag init -g main.go
| PUT | `/api/auth/profile` | 更新用户信息 | 是 | | PUT | `/api/auth/profile` | 更新用户信息 | 是 |
| POST | `/api/auth/change-password` | 修改密码 | 是 | | POST | `/api/auth/change-password` | 修改密码 | 是 |
> 错误提示已做用户友好化,例如登录失败统一返回:`用户名或密码不正确`。
### 序列号管理 ### 序列号管理
| 方法 | 路径 | 描述 | 需要认证 | 角色 | | 方法 | 路径 | 描述 | 需要认证 | 角色 |
@@ -279,6 +281,12 @@ swag init -g main.go
| PUT | `/api/employee-serials/:serialNumber` | 更新员工序列号信息 | 是 | 管理员 | | PUT | `/api/employee-serials/:serialNumber` | 更新员工序列号信息 | 是 | 管理员 |
| POST | `/api/employee-serials/:serialNumber/revoke` | 吊销员工序列号 | 是 | 管理员 | | POST | `/api/employee-serials/:serialNumber/revoke` | 吊销员工序列号 | 是 | 管理员 |
### Node 对齐说明
- Go 版路由已与 `backend-node` 对齐(含 `PATCH` 更新接口、企业详情、企业吊销、删除企业下单个序列号)。
- 企业统计与企业详情接口统一返回 `{ message, data }` 结构。
- 序列号相关返回字段已对齐前端使用(如 `serialNumber``validUntil``createdBy``status`)。
**员工序列号特点**: **员工序列号特点**:
- 无有效期限制(与企业赋码不同) - 无有效期限制(与企业赋码不同)
- 包含部门department和员工姓名employeeName信息 - 包含部门department和员工姓名employeeName信息

View File

@@ -52,6 +52,7 @@ func (c *EmployeeSerialsController) Generate(ctx *gin.Context) {
generateData.EmployeeName, generateData.EmployeeName,
generateData.Quantity, generateData.Quantity,
userModel.ID, userModel.ID,
generateData.SerialPrefix,
) )
if err != nil { if err != nil {
ErrorResponse(ctx, http.StatusInternalServerError, err.Error()) ErrorResponse(ctx, http.StatusInternalServerError, err.Error())
@@ -226,3 +227,28 @@ func (c *EmployeeSerialsController) Revoke(ctx *gin.Context) {
SuccessResponse(ctx, "员工序列号吊销成功") 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, "员工序列号删除成功")
}

View File

@@ -199,6 +199,7 @@ type GenerateEmployeeSerialDTO struct {
Department string `json:"department" validate:"required"` Department string `json:"department" validate:"required"`
EmployeeName string `json:"employeeName" validate:"required"` EmployeeName string `json:"employeeName" validate:"required"`
Quantity int `json:"quantity" validate:"min=1,max=1000"` Quantity int `json:"quantity" validate:"min=1,max=1000"`
SerialPrefix string `json:"serialPrefix,omitempty"`
} }
// UpdateEmployeeSerialDTO 员工序列号更新请求数据 // UpdateEmployeeSerialDTO 员工序列号更新请求数据

View File

@@ -71,5 +71,6 @@ func SetupAPIRoutes(r *gin.RouterGroup) {
employeeSerialsRoutes.GET("/", middleware.JWTAuthMiddleware(), employeeSerialsController.FindAll) employeeSerialsRoutes.GET("/", middleware.JWTAuthMiddleware(), employeeSerialsController.FindAll)
employeeSerialsRoutes.PUT("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Update) employeeSerialsRoutes.PUT("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Update)
employeeSerialsRoutes.POST("/:serialNumber/revoke", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Revoke) employeeSerialsRoutes.POST("/:serialNumber/revoke", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Revoke)
employeeSerialsRoutes.DELETE("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Delete)
} }
} }

View File

@@ -3,6 +3,7 @@ package services
import ( import (
"errors" "errors"
"fmt" "fmt"
"sort"
"time" "time"
"gorm.io/gorm" "gorm.io/gorm"
@@ -311,8 +312,14 @@ func (s *CompaniesService) GetStats() (map[string]any, error) {
return nil, errors.New("查询序列号统计失败") 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) companyCount := len(companies)
serialCount := len(serials) serialCount := len(serials)
employeeSerialCount := len(employeeSerials)
activeCount := 0 activeCount := 0
for _, serial := range serials { for _, serial := range serials {
if serial.IsActive && (serial.ValidUntil == nil || serial.ValidUntil.After(now)) { if serial.IsActive && (serial.ValidUntil == nil || serial.ValidUntil.After(now)) {
@@ -355,22 +362,41 @@ func (s *CompaniesService) GetStats() (map[string]any, error) {
} }
recentSerials := make([]map[string]any, 0) recentSerials := make([]map[string]any, 0)
for i, serial := range serials { // 添加企业序列号
if i >= 10 { for _, serial := range serials {
break
}
recentSerials = append(recentSerials, map[string]any{ recentSerials = append(recentSerials, map[string]any{
"serialNumber": serial.SerialNumber, "serialNumber": serial.SerialNumber,
"companyName": serial.CompanyName, "companyName": serial.CompanyName,
"isActive": serial.IsActive, "isActive": serial.IsActive,
"createdAt": serial.CreatedAt, "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{ return map[string]any{
"overview": map[string]any{ "overview": map[string]any{
"totalCompanies": companyCount, "totalCompanies": companyCount,
"totalSerials": serialCount, "totalSerials": serialCount,
"totalEmployeeSerials": employeeSerialCount,
"activeSerials": activeCount, "activeSerials": activeCount,
"inactiveSerials": inactiveCount, "inactiveSerials": inactiveCount,
}, },

View File

@@ -28,6 +28,7 @@ func (s *EmployeeSerialsService) Generate(
employeeName string, employeeName string,
quantity int, quantity int,
userId uint, userId uint,
serialPrefix string,
) ([]models.EmployeeSerial, error) { ) ([]models.EmployeeSerial, error) {
var serials []models.EmployeeSerial 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) serialNumbers := make(map[string]bool)
@@ -199,6 +204,22 @@ func (s *EmployeeSerialsService) Revoke(serialNumber string) error {
return nil 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 生成员工二维码 // GenerateQRCode 生成员工二维码
func (s *EmployeeSerialsService) GenerateQRCode( func (s *EmployeeSerialsService) GenerateQRCode(
serialNumber string, serialNumber string,