Add employee code assignment function
This commit is contained in:
53
AGENTS.md
53
AGENTS.md
@@ -38,12 +38,51 @@ swag init -g main.go # Alternative command
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### Project Structure
|
||||
- **controllers/**: HTTP request handlers, use helper functions (GetCurrentUser, BindJSON, ErrorResponse, SuccessResponse)
|
||||
- **services/**: Business logic layer, return errors to controllers
|
||||
- **models/**: Data models with JSON and GORM tags, DTOs for API
|
||||
- **middleware/**: JWT authentication and admin role checks
|
||||
- **database/**: SQLite/PostgreSQL connection and migrations
|
||||
```
|
||||
backend-go/
|
||||
├── config/ # Configuration management
|
||||
│ └── config.go # Config loading (.env, config.yaml, env vars)
|
||||
├── controllers/ # HTTP request handlers
|
||||
│ ├── auth_controller.go # Auth: login, profile, password change
|
||||
│ ├── companies_controller.go # Company CRUD
|
||||
│ ├── employees_controller.go # Employee serials: generate, query, update, revoke, qrcode
|
||||
│ ├── helper.go # Helper functions (GetCurrentUser, BindJSON, Response)
|
||||
│ └── serials_controller.go # Company serials: generate, query, update, revoke, qrcode
|
||||
├── database/ # Database connection and migrations
|
||||
│ └── database.go # GORM init, AutoMigrate
|
||||
├── docs/ # Swagger documentation (auto-generated)
|
||||
├── logger/ # Structured logging
|
||||
│ └── logger.go # Zap logger wrapper
|
||||
├── middleware/ # Middleware
|
||||
│ └── auth.go # JWT auth, Admin permission check
|
||||
├── models/ # Data models and DTOs
|
||||
│ └── models.go # User, Company, Serial, EmployeeSerial and DTOs
|
||||
├── routes/ # Route configuration
|
||||
│ └── routes.go # API route registration
|
||||
├── services/ # Business logic layer
|
||||
│ ├── auth_service.go # Auth: validate user, generate token, password management
|
||||
│ ├── companies_service.go # Company CRUD
|
||||
│ ├── employees_service.go # Employee serials: generate, query, update, revoke, qrcode
|
||||
│ ├── serials_service.go # Company serials: generate, query, update, revoke, qrcode
|
||||
│ └── services_test.go # Unit tests
|
||||
├── tests/ # Integration tests
|
||||
│ └── main_test.go # End-to-end tests
|
||||
├── data/ # SQLite data directory
|
||||
├── main.go # Application entry point
|
||||
├── go.mod # Go dependencies
|
||||
├── Makefile # Build tasks
|
||||
└── .env.example # Environment variable example
|
||||
```
|
||||
|
||||
### Layer Responsibilities
|
||||
- **controllers/**: Receive HTTP requests, validate params, call services, return responses
|
||||
- **services/**: Business logic, data processing, database interaction, return results or errors
|
||||
- **models/**: Data structure definitions, GORM models, API request/response DTOs
|
||||
- **middleware/**: Authentication and authorization
|
||||
- **routes/**: Route registration, connect controllers to router
|
||||
|
||||
### Import Organization
|
||||
Standard imports followed by third-party imports, then project imports (sorted alphabetically):
|
||||
@@ -61,8 +100,8 @@ import (
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
- **Controllers**: `AuthController`, `SerialsController`, `CompaniesController`
|
||||
- **Services**: `AuthService`, `SerialsService`, `CompaniesService`
|
||||
- **Controllers**: `AuthController`, `SerialsController`, `CompaniesController`, `EmployeeSerialsController`
|
||||
- **Services**: `AuthService`, `SerialsService`, `CompaniesService`, `EmployeeSerialsService`
|
||||
- **Models**: `User`, `Company`, `Serial` (use PascalCase for exported structs)
|
||||
- **DTOs**: `LoginDTO`, `ChangePasswordDTO`, `UpdateProfileDTO` (DTO suffix)
|
||||
- **Functions**: `ValidateUser`, `Generate`, `Query` (PascalCase for exported)
|
||||
|
||||
21
README.md
21
README.md
@@ -31,6 +31,7 @@ backend-go/
|
||||
├── controllers/ # 控制器层,处理 HTTP 请求
|
||||
│ ├── auth_controller.go # 认证相关接口
|
||||
│ ├── companies_controller.go # 企业管理接口
|
||||
│ ├── employees_controller.go # 员工赋码接口
|
||||
│ ├── helper.go # 控制器通用辅助函数
|
||||
│ └── serials_controller.go # 序列号管理接口
|
||||
├── database/ # 数据库连接和操作
|
||||
@@ -50,6 +51,7 @@ backend-go/
|
||||
├── services/ # 业务逻辑层
|
||||
│ ├── auth_service.go # 认证业务逻辑
|
||||
│ ├── companies_service.go # 企业管理业务逻辑
|
||||
│ ├── employees_service.go # 员工赋码业务逻辑
|
||||
│ ├── serials_service.go # 序列号业务逻辑
|
||||
│ └── services_test.go # 服务层单元测试
|
||||
├── tests/ # 集成测试
|
||||
@@ -253,6 +255,22 @@ swag init -g main.go
|
||||
| PUT | `/api/companies/:companyName` | 更新企业信息 | 是 | 管理员 |
|
||||
| DELETE | `/api/companies/:companyName` | 删除企业 | 是 | 管理员 |
|
||||
|
||||
### 员工赋码
|
||||
|
||||
| 方法 | 路径 | 描述 | 需要认证 | 角色 |
|
||||
| ---- | -------------------------------------- | ------------------ | -------- | ------ |
|
||||
| POST | `/api/employee-serials/generate` | 生成员工序列号 | 是 | 管理员 |
|
||||
| GET | `/api/employee-serials` | 获取员工序列号列表 | 是 | 任何 |
|
||||
| GET | `/api/employee-serials/:serial/query` | 查询员工序列号信息 | 否 | 任何 |
|
||||
| POST | `/api/employee-serials/:serial/qrcode` | 生成员工二维码 | 是 | 任何 |
|
||||
| PUT | `/api/employee-serials/:serial` | 更新员工序列号信息 | 是 | 管理员 |
|
||||
| POST | `/api/employee-serials/:serial/revoke` | 吊销员工序列号 | 是 | 管理员 |
|
||||
|
||||
**员工序列号特点**:
|
||||
- 无有效期限制(与企业赋码不同)
|
||||
- 包含部门(department)和员工姓名(employeeName)信息
|
||||
- 序列号格式: `EMP26xxxxxx`(EMP + 年份后两位 + 6位随机字符)
|
||||
|
||||
## 测试
|
||||
|
||||
### 运行所有测试
|
||||
@@ -278,9 +296,10 @@ go tool cover -html=coverage.out
|
||||
|
||||
### 当前测试覆盖
|
||||
|
||||
- **services/**: 包含 AuthService 和 SerialsService 的完整单元测试
|
||||
- **services/**: 包含 AuthService、SerialsService 和 EmployeeSerialsService 的完整单元测试
|
||||
- 用户认证测试(登录、获取用户信息、修改密码、更新资料)
|
||||
- 序列号管理测试(生成、查询、更新、吊销、分页列表)
|
||||
- 员工赋码测试(生成、查询、更新、吊销、二维码生成)
|
||||
- **tests/**: 集成测试(健康检查、登录流程)
|
||||
|
||||
## 代码检查
|
||||
|
||||
228
controllers/employees_controller.go
Normal file
228
controllers/employees_controller.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.beifan.cn/trace-system/backend-go/models"
|
||||
"git.beifan.cn/trace-system/backend-go/services"
|
||||
)
|
||||
|
||||
// EmployeeSerialsController 员工序列号控制器
|
||||
type EmployeeSerialsController struct {
|
||||
employeeSerialsService services.EmployeeSerialsService
|
||||
}
|
||||
|
||||
// NewEmployeeSerialsController 创建员工序列号控制器实例
|
||||
func NewEmployeeSerialsController() *EmployeeSerialsController {
|
||||
return &EmployeeSerialsController{
|
||||
employeeSerialsService: services.EmployeeSerialsService{},
|
||||
}
|
||||
}
|
||||
|
||||
// Generate 生成员工序列号
|
||||
// @Summary 生成员工序列号
|
||||
// @Description 生成指定数量的员工序列号(无有效期)
|
||||
// @Tags 员工赋码管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param generateData body models.GenerateEmployeeSerialDTO true "生成数据"
|
||||
// @Success 200 {object} models.DataResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 401 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Router /employee-serials/generate [post]
|
||||
func (c *EmployeeSerialsController) Generate(ctx *gin.Context) {
|
||||
userModel, ok := GetCurrentUser(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var generateData models.GenerateEmployeeSerialDTO
|
||||
if !BindJSON(ctx, &generateData) {
|
||||
return
|
||||
}
|
||||
|
||||
serials, err := c.employeeSerialsService.Generate(
|
||||
generateData.CompanyName,
|
||||
generateData.Department,
|
||||
generateData.EmployeeName,
|
||||
generateData.Quantity,
|
||||
userModel.ID,
|
||||
)
|
||||
if err != nil {
|
||||
ErrorResponse(ctx, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(ctx, "成功生成 "+strconv.Itoa(len(serials))+" 个员工序列号", gin.H{
|
||||
"serials": serials,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateQRCode 生成员工二维码
|
||||
// @Summary 生成员工二维码
|
||||
// @Description 为指定员工序列号生成查询二维码
|
||||
// @Tags 员工赋码管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param serialNumber path string true "序列号"
|
||||
// @Param qrCodeData body models.QRCodeDTO false "二维码数据"
|
||||
// @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 /employee-serials/{serialNumber}/qrcode [post]
|
||||
func (c *EmployeeSerialsController) GenerateQRCode(ctx *gin.Context) {
|
||||
serialNumber := ctx.Param("serialNumber")
|
||||
|
||||
var qrCodeData models.QRCodeDTO
|
||||
if !BindJSON(ctx, &qrCodeData) {
|
||||
return
|
||||
}
|
||||
|
||||
protocol := "http"
|
||||
if ctx.Request.TLS != nil {
|
||||
protocol = "https"
|
||||
}
|
||||
|
||||
qrCodeBase64, queryUrl, err := c.employeeSerialsService.GenerateQRCode(
|
||||
serialNumber,
|
||||
qrCodeData.BaseUrl,
|
||||
ctx.Request.Host,
|
||||
protocol,
|
||||
)
|
||||
if err != nil {
|
||||
ErrorResponse(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(ctx, "员工二维码生成成功", gin.H{
|
||||
"qrCodeData": qrCodeBase64,
|
||||
"queryUrl": queryUrl,
|
||||
})
|
||||
}
|
||||
|
||||
// Query 查询员工序列号信息
|
||||
// @Summary 查询员工序列号信息
|
||||
// @Description 查询指定员工序列号的详细信息
|
||||
// @Tags 员工赋码查询
|
||||
// @Produce json
|
||||
// @Param serialNumber path string true "序列号"
|
||||
// @Success 200 {object} models.DataResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 404 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Router /employee-serials/{serialNumber}/query [get]
|
||||
func (c *EmployeeSerialsController) Query(ctx *gin.Context) {
|
||||
serialNumber := ctx.Param("serialNumber")
|
||||
|
||||
serial, err := c.employeeSerialsService.Query(serialNumber)
|
||||
if err != nil {
|
||||
ErrorResponse(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(ctx, "查询成功", gin.H{
|
||||
"serial": serial,
|
||||
})
|
||||
}
|
||||
|
||||
// FindAll 获取员工序列号列表
|
||||
// @Summary 获取员工序列号列表
|
||||
// @Description 获取员工序列号列表,支持分页和搜索
|
||||
// @Tags 员工赋码管理
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param page query int false "页码"
|
||||
// @Param limit query int false "每页数量"
|
||||
// @Param search query string false "搜索关键词"
|
||||
// @Success 200 {object} models.PaginationResponse
|
||||
// @Failure 401 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Router /employee-serials [get]
|
||||
func (c *EmployeeSerialsController) FindAll(ctx *gin.Context) {
|
||||
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
||||
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "20"))
|
||||
search := ctx.DefaultQuery("search", "")
|
||||
|
||||
serials, total, totalPages, err := c.employeeSerialsService.FindAll(page, limit, search)
|
||||
if err != nil {
|
||||
ErrorResponse(ctx, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(ctx, "获取员工序列号列表成功", gin.H{
|
||||
"data": serials,
|
||||
"pagination": gin.H{
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"total": total,
|
||||
"totalPages": totalPages,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Update 更新员工序列号信息
|
||||
// @Summary 更新员工序列号信息
|
||||
// @Description 更新指定员工序列号的信息
|
||||
// @Tags 员工赋码管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param serialNumber path string true "序列号"
|
||||
// @Param updateData body models.UpdateEmployeeSerialDTO true "更新数据"
|
||||
// @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 /employee-serials/{serialNumber} [put]
|
||||
func (c *EmployeeSerialsController) Update(ctx *gin.Context) {
|
||||
serialNumber := ctx.Param("serialNumber")
|
||||
|
||||
var updateData models.UpdateEmployeeSerialDTO
|
||||
if !BindJSON(ctx, &updateData) {
|
||||
return
|
||||
}
|
||||
|
||||
serial, err := c.employeeSerialsService.Update(serialNumber, updateData)
|
||||
if err != nil {
|
||||
ErrorResponse(ctx, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(ctx, "员工序列号信息更新成功", gin.H{
|
||||
"serial": serial,
|
||||
})
|
||||
}
|
||||
|
||||
// Revoke 吊销员工序列号
|
||||
// @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}/revoke [post]
|
||||
func (c *EmployeeSerialsController) Revoke(ctx *gin.Context) {
|
||||
serialNumber := ctx.Param("serialNumber")
|
||||
|
||||
err := c.employeeSerialsService.Revoke(serialNumber)
|
||||
if err != nil {
|
||||
ErrorResponse(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(ctx, "员工序列号吊销成功")
|
||||
}
|
||||
@@ -112,6 +112,7 @@ func AutoMigrate() {
|
||||
&models.User{},
|
||||
&models.Company{},
|
||||
&models.Serial{},
|
||||
&models.EmployeeSerial{},
|
||||
); err != nil {
|
||||
logger.Fatal("数据库迁移失败", logger.Err(err))
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
||||
module git.beifan.cn/trace-system/backend-go
|
||||
|
||||
go 1.26
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
|
||||
@@ -163,3 +163,35 @@ type CompanyUpdateRequest struct {
|
||||
CompanyName string `json:"companyName"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
}
|
||||
|
||||
// EmployeeSerial 员工序列号模型
|
||||
type EmployeeSerial struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
SerialNumber string `gorm:"uniqueIndex;size:255" json:"serialNumber"`
|
||||
CompanyName string `gorm:"index;size:255" json:"companyName"`
|
||||
Department string `gorm:"size:255" json:"department"`
|
||||
EmployeeName string `gorm:"size:255" json:"employeeName"`
|
||||
IsActive bool `gorm:"default:true" json:"isActive"`
|
||||
CreatedBy *uint `json:"createdBy"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
User *User `gorm:"foreignKey:CreatedBy" json:"user,omitempty"`
|
||||
Company *Company `gorm:"foreignKey:CompanyName;references:CompanyName" json:"company,omitempty"`
|
||||
}
|
||||
|
||||
// GenerateEmployeeSerialDTO 生成员工序列号请求数据
|
||||
type GenerateEmployeeSerialDTO struct {
|
||||
CompanyName string `json:"companyName" validate:"required"`
|
||||
Department string `json:"department" validate:"required"`
|
||||
EmployeeName string `json:"employeeName" validate:"required"`
|
||||
Quantity int `json:"quantity" validate:"min=1,max=1000"`
|
||||
}
|
||||
|
||||
// UpdateEmployeeSerialDTO 员工序列号更新请求数据
|
||||
type UpdateEmployeeSerialDTO struct {
|
||||
CompanyName string `json:"companyName,omitempty" validate:"omitempty"`
|
||||
Department string `json:"department,omitempty" validate:"omitempty"`
|
||||
EmployeeName string `json:"employeeName,omitempty" validate:"omitempty"`
|
||||
IsActive *bool `json:"isActive,omitempty"`
|
||||
}
|
||||
|
||||
@@ -52,4 +52,16 @@ func SetupAPIRoutes(r *gin.RouterGroup) {
|
||||
companiesRoutes.PUT("/:companyName", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), companiesController.Update)
|
||||
companiesRoutes.DELETE("/:companyName", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), companiesController.Delete)
|
||||
}
|
||||
|
||||
// 员工赋码路由
|
||||
employeeSerialsController := controllers.NewEmployeeSerialsController()
|
||||
employeeSerialsRoutes := r.Group("/employee-serials")
|
||||
{
|
||||
employeeSerialsRoutes.POST("/generate", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Generate)
|
||||
employeeSerialsRoutes.POST("/:serialNumber/qrcode", middleware.JWTAuthMiddleware(), employeeSerialsController.GenerateQRCode)
|
||||
employeeSerialsRoutes.GET("/:serialNumber/query", employeeSerialsController.Query)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
260
services/employees_service.go
Normal file
260
services/employees_service.go
Normal file
@@ -0,0 +1,260 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
qr "github.com/yeqown/go-qrcode/v2"
|
||||
"github.com/yeqown/go-qrcode/writer/standard"
|
||||
|
||||
"git.beifan.cn/trace-system/backend-go/database"
|
||||
"git.beifan.cn/trace-system/backend-go/models"
|
||||
)
|
||||
|
||||
// EmployeeSerialsService 员工序列号服务
|
||||
type EmployeeSerialsService struct{}
|
||||
|
||||
// Generate 生成员工序列号
|
||||
func (s *EmployeeSerialsService) Generate(
|
||||
companyName string,
|
||||
department string,
|
||||
employeeName string,
|
||||
quantity int,
|
||||
userId uint,
|
||||
) ([]models.EmployeeSerial, error) {
|
||||
var serials []models.EmployeeSerial
|
||||
|
||||
// 检查公司是否存在,不存在则创建
|
||||
var company models.Company
|
||||
result := database.DB.Where("company_name = ?", companyName).First(&company)
|
||||
if result.Error != nil {
|
||||
company = models.Company{
|
||||
CompanyName: companyName,
|
||||
IsActive: true,
|
||||
}
|
||||
result = database.DB.Create(&company)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("创建公司失败: %w", result.Error)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成序列号前缀 (EMP + 年份后两位)
|
||||
serialPrefix := fmt.Sprintf("EMP%d", time.Now().Year()%100)
|
||||
|
||||
// 预生成所有序列号
|
||||
serialNumbers := make(map[string]bool)
|
||||
for i := 0; i < quantity; {
|
||||
randomBytes := make([]byte, 3)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
return nil, fmt.Errorf("生成随机数失败: %w", err)
|
||||
}
|
||||
randomPart := hex.EncodeToString(randomBytes)[:6]
|
||||
serialNumber := fmt.Sprintf("%s%s", serialPrefix, randomPart)
|
||||
|
||||
if serialNumbers[serialNumber] {
|
||||
continue
|
||||
}
|
||||
|
||||
var existingSerial models.EmployeeSerial
|
||||
checkResult := database.DB.Where("serial_number = ?", serialNumber).First(&existingSerial)
|
||||
if checkResult.Error != nil {
|
||||
serialNumbers[serialNumber] = true
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
for serialNumber := range serialNumbers {
|
||||
serial := models.EmployeeSerial{
|
||||
SerialNumber: strings.ToUpper(serialNumber),
|
||||
CompanyName: companyName,
|
||||
Department: department,
|
||||
EmployeeName: employeeName,
|
||||
CreatedBy: &userId,
|
||||
IsActive: true,
|
||||
}
|
||||
serials = append(serials, serial)
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
result = database.DB.Create(&serials)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("保存员工序列号失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return serials, nil
|
||||
}
|
||||
|
||||
// Query 查询员工序列号信息
|
||||
func (s *EmployeeSerialsService) Query(serialNumber string) (*models.EmployeeSerial, error) {
|
||||
var serial models.EmployeeSerial
|
||||
result := database.DB.Preload("User").Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询员工序列号失败: %w", errors.New("序列号不存在"))
|
||||
}
|
||||
|
||||
return &serial, nil
|
||||
}
|
||||
|
||||
// FindAll 获取员工序列号列表
|
||||
func (s *EmployeeSerialsService) FindAll(page int, limit int, search string) ([]models.EmployeeSerial, int, int, error) {
|
||||
var serials []models.EmployeeSerial
|
||||
var total int64
|
||||
|
||||
offset := (page - 1) * limit
|
||||
db := database.DB.Preload("User")
|
||||
|
||||
// 搜索条件
|
||||
if search != "" {
|
||||
db = db.Where("serial_number LIKE ? OR company_name LIKE ? OR department LIKE ? OR employee_name LIKE ?",
|
||||
"%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
countQuery := db.Model(&models.EmployeeSerial{})
|
||||
if search != "" {
|
||||
countQuery = countQuery.Where("serial_number LIKE ? OR company_name LIKE ? OR department LIKE ? OR employee_name LIKE ?",
|
||||
"%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%")
|
||||
}
|
||||
countQuery.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
result := db.Model(&models.EmployeeSerial{}).Order("created_at DESC").Offset(offset).Limit(limit).Find(&serials)
|
||||
if result.Error != nil {
|
||||
return nil, 0, 0, fmt.Errorf("查询员工序列号列表失败: %w", result.Error)
|
||||
}
|
||||
|
||||
totalPages := (int(total) + limit - 1) / limit
|
||||
|
||||
return serials, int(total), totalPages, nil
|
||||
}
|
||||
|
||||
// Update 更新员工序列号信息
|
||||
func (s *EmployeeSerialsService) Update(serialNumber string, updateData models.UpdateEmployeeSerialDTO) (*models.EmployeeSerial, error) {
|
||||
var serial models.EmployeeSerial
|
||||
result := database.DB.Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询员工序列号失败: %w", errors.New("序列号不存在"))
|
||||
}
|
||||
|
||||
if updateData.CompanyName != "" {
|
||||
// 检查公司是否存在
|
||||
var company models.Company
|
||||
companyResult := database.DB.Where("company_name = ?", updateData.CompanyName).First(&company)
|
||||
if companyResult.Error != nil {
|
||||
company = models.Company{
|
||||
CompanyName: updateData.CompanyName,
|
||||
IsActive: true,
|
||||
}
|
||||
database.DB.Create(&company)
|
||||
}
|
||||
|
||||
serial.CompanyName = updateData.CompanyName
|
||||
}
|
||||
|
||||
if updateData.Department != "" {
|
||||
serial.Department = updateData.Department
|
||||
}
|
||||
|
||||
if updateData.EmployeeName != "" {
|
||||
serial.EmployeeName = updateData.EmployeeName
|
||||
}
|
||||
|
||||
if updateData.IsActive != nil {
|
||||
serial.IsActive = *updateData.IsActive
|
||||
}
|
||||
|
||||
result = database.DB.Save(&serial)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("更新员工序列号失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return &serial, nil
|
||||
}
|
||||
|
||||
// Revoke 吊销员工序列号
|
||||
func (s *EmployeeSerialsService) Revoke(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("序列号不存在"))
|
||||
}
|
||||
|
||||
if !serial.IsActive {
|
||||
return fmt.Errorf("员工序列号状态无效: %w", errors.New("序列号已被吊销"))
|
||||
}
|
||||
|
||||
serial.IsActive = false
|
||||
result = database.DB.Save(&serial)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("吊销员工序列号失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateQRCode 生成员工二维码
|
||||
func (s *EmployeeSerialsService) GenerateQRCode(
|
||||
serialNumber string,
|
||||
baseUrl string,
|
||||
requestHost string,
|
||||
protocol string,
|
||||
) (string, string, error) {
|
||||
var serial models.EmployeeSerial
|
||||
result := database.DB.Preload("User").Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
||||
if result.Error != nil {
|
||||
return "", "", fmt.Errorf("查询员工序列号失败: %w", errors.New("序列号不存在"))
|
||||
}
|
||||
|
||||
if !serial.IsActive {
|
||||
return "", "", fmt.Errorf("员工序列号状态无效: %w", errors.New("序列号已被禁用"))
|
||||
}
|
||||
|
||||
// 确定查询 URL
|
||||
if baseUrl == "" {
|
||||
baseUrl = fmt.Sprintf("%s://%s/query.html", protocol, requestHost)
|
||||
}
|
||||
|
||||
var queryURL string
|
||||
if strings.Contains(baseUrl, "?") {
|
||||
queryURL = fmt.Sprintf("%s&serial=%s", baseUrl, serial.SerialNumber)
|
||||
} else {
|
||||
queryURL = fmt.Sprintf("%s?serial=%s", baseUrl, serial.SerialNumber)
|
||||
}
|
||||
|
||||
// 生成二维码到临时文件
|
||||
filePath := fmt.Sprintf("temp_qr_%s.png", uuid.New().String())
|
||||
writer, err := standard.New(filePath, standard.WithQRWidth(6))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("二维码写入器创建失败: %w", err)
|
||||
}
|
||||
|
||||
qrc, errCode := qr.New(queryURL)
|
||||
if errCode != nil {
|
||||
os.Remove(filePath)
|
||||
return "", "", fmt.Errorf("二维码创建失败: %w", errCode)
|
||||
}
|
||||
|
||||
if errSave := qrc.Save(writer); errSave != nil {
|
||||
os.Remove(filePath)
|
||||
return "", "", fmt.Errorf("二维码保存失败: %w", errSave)
|
||||
}
|
||||
|
||||
// 读取文件并转换为 Base64
|
||||
fileBytes, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
os.Remove(filePath)
|
||||
return "", "", fmt.Errorf("读取二维码文件失败: %w", err)
|
||||
}
|
||||
os.Remove(filePath)
|
||||
|
||||
qrCodeBase64 := base64.StdEncoding.EncodeToString(fileBytes)
|
||||
|
||||
return qrCodeBase64, queryURL, nil
|
||||
}
|
||||
@@ -31,12 +31,14 @@ func TestMain(m *testing.M) {
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.User{})
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.Serial{})
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.EmployeeSerial{})
|
||||
|
||||
exitCode := m.Run()
|
||||
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.User{})
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.Serial{})
|
||||
database.DB.Unscoped().Where("1 = 1").Delete(&models.EmployeeSerial{})
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
@@ -411,3 +413,363 @@ func TestSerialsService_Update_Success(t *testing.T) {
|
||||
database.DB.Unscoped().Where("company_name = ?", "UpdateCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
// ==================== EmployeeSerialsService 测试 ====================
|
||||
|
||||
func TestEmployeeSerialsService_Generate_Success(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin1",
|
||||
Password: string(password),
|
||||
Name: "员工管理员1",
|
||||
Email: "empadmin1@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, err := service.Generate("EmpTestCompany", "技术部", "张三", 5, user.ID)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, serials, 5)
|
||||
for _, serial := range serials {
|
||||
assert.Equal(t, "EmpTestCompany", serial.CompanyName)
|
||||
assert.Equal(t, "技术部", serial.Department)
|
||||
assert.Equal(t, "张三", serial.EmployeeName)
|
||||
assert.True(t, serial.IsActive)
|
||||
assert.True(t, strings.HasPrefix(serial.SerialNumber, "EMP"))
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
|
||||
database.DB.Unscoped().Where("company_name = ?", "EmpTestCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Generate_CreateNewCompany(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin2",
|
||||
Password: string(password),
|
||||
Name: "员工管理员2",
|
||||
Email: "empadmin2@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, err := service.Generate("NewEmpCompany", "市场部", "李四", 3, user.ID)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, serials, 3)
|
||||
|
||||
var company models.Company
|
||||
result := database.DB.Where("company_name = ?", "NewEmpCompany").First(&company)
|
||||
assert.NoError(t, result.Error)
|
||||
assert.Equal(t, "NewEmpCompany", company.CompanyName)
|
||||
assert.True(t, company.IsActive)
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "NewEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Query_Success(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin3",
|
||||
Password: string(password),
|
||||
Name: "员工管理员3",
|
||||
Email: "empadmin3@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("QueryEmpCompany", "财务部", "王五", 1, user.ID)
|
||||
|
||||
serialNumber := serials[0].SerialNumber
|
||||
result, err := service.Query(serialNumber)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, strings.ToUpper(serialNumber), strings.ToUpper(result.SerialNumber))
|
||||
assert.Equal(t, "QueryEmpCompany", result.CompanyName)
|
||||
assert.Equal(t, "财务部", result.Department)
|
||||
assert.Equal(t, "王五", result.EmployeeName)
|
||||
assert.True(t, result.IsActive)
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "QueryEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Query_NotFound(t *testing.T) {
|
||||
service := EmployeeSerialsService{}
|
||||
_, err := service.Query("NONEXISTENTEMP")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "序列号不存在")
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_FindAll_Success(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin4",
|
||||
Password: string(password),
|
||||
Name: "员工管理员4",
|
||||
Email: "empadmin4@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("ListEmpCompany", "人事部", "赵六", 10, user.ID)
|
||||
|
||||
result, total, totalPages, err := service.FindAll(1, 5, "")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 5)
|
||||
assert.GreaterOrEqual(t, total, 10)
|
||||
assert.Greater(t, totalPages, 0)
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "ListEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_FindAll_WithSearch(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin5",
|
||||
Password: string(password),
|
||||
Name: "员工管理员5",
|
||||
Email: "empadmin5@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("SearchEmpCompany", "研发部", "钱七", 5, user.ID)
|
||||
|
||||
result, _, _, err := service.FindAll(1, 10, "SearchEmpCompany")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, len(result), 0)
|
||||
assert.Equal(t, "SearchEmpCompany", result[0].CompanyName)
|
||||
|
||||
result2, _, _, err2 := service.FindAll(1, 10, "研发部")
|
||||
assert.NoError(t, err2)
|
||||
assert.Greater(t, len(result2), 0)
|
||||
|
||||
result3, _, _, err3 := service.FindAll(1, 10, "钱七")
|
||||
assert.NoError(t, err3)
|
||||
assert.Greater(t, len(result3), 0)
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "SearchEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Update_Success(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin6",
|
||||
Password: string(password),
|
||||
Name: "员工管理员6",
|
||||
Email: "empadmin6@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("UpdateEmpCompany", "运营部", "孙八", 1, user.ID)
|
||||
|
||||
serialNumber := serials[0].SerialNumber
|
||||
isActive := false
|
||||
|
||||
updateData := models.UpdateEmployeeSerialDTO{
|
||||
CompanyName: "UpdatedEmpCompany",
|
||||
Department: "新部门",
|
||||
EmployeeName: "新名字",
|
||||
IsActive: &isActive,
|
||||
}
|
||||
|
||||
result, err := service.Update(serialNumber, updateData)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, "UpdatedEmpCompany", result.CompanyName)
|
||||
assert.Equal(t, "新部门", result.Department)
|
||||
assert.Equal(t, "新名字", result.EmployeeName)
|
||||
assert.False(t, result.IsActive)
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "UpdateEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Where("company_name = ?", "UpdatedEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Update_NotFound(t *testing.T) {
|
||||
service := EmployeeSerialsService{}
|
||||
updateData := models.UpdateEmployeeSerialDTO{
|
||||
CompanyName: "TestCompany",
|
||||
}
|
||||
|
||||
_, err := service.Update("NONEXISTENT", updateData)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "序列号不存在")
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Revoke_Success(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin7",
|
||||
Password: string(password),
|
||||
Name: "员工管理员7",
|
||||
Email: "empadmin7@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("RevokeEmpCompany", "测试部", "周九", 1, user.ID)
|
||||
|
||||
serialNumber := serials[0].SerialNumber
|
||||
err := service.Revoke(serialNumber)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
var revokedSerial models.EmployeeSerial
|
||||
database.DB.Where("serial_number = ?", serialNumber).First(&revokedSerial)
|
||||
assert.False(t, revokedSerial.IsActive)
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "RevokeEmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Revoke_NotFound(t *testing.T) {
|
||||
service := EmployeeSerialsService{}
|
||||
err := service.Revoke("NONEXISTENT")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "序列号不存在")
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_Revoke_AlreadyRevoked(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin8",
|
||||
Password: string(password),
|
||||
Name: "员工管理员8",
|
||||
Email: "empadmin8@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("RevokeEmpCompany2", "行政部", "吴十", 1, user.ID)
|
||||
|
||||
serialNumber := serials[0].SerialNumber
|
||||
service.Revoke(serialNumber)
|
||||
err := service.Revoke(serialNumber)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "已被吊销")
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "RevokeEmpCompany2").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_GenerateQRCode_Success(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin9",
|
||||
Password: string(password),
|
||||
Name: "员工管理员9",
|
||||
Email: "empadmin9@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("QREmpCompany", "产品部", "郑十一", 1, user.ID)
|
||||
|
||||
serialNumber := serials[0].SerialNumber
|
||||
qrCodeBase64, queryUrl, err := service.GenerateQRCode(serialNumber, "", "localhost:3000", "http")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, qrCodeBase64)
|
||||
assert.NotEmpty(t, queryUrl)
|
||||
assert.Contains(t, queryUrl, serialNumber)
|
||||
assert.Contains(t, queryUrl, "query.html")
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "QREmpCompany").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_GenerateQRCode_NotFound(t *testing.T) {
|
||||
service := EmployeeSerialsService{}
|
||||
_, _, err := service.GenerateQRCode("NONEXISTENT", "", "localhost:3000", "http")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "序列号不存在")
|
||||
}
|
||||
|
||||
func TestEmployeeSerialsService_GenerateQRCode_Inactive(t *testing.T) {
|
||||
var user models.User
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
||||
user = models.User{
|
||||
Username: "empadmin10",
|
||||
Password: string(password),
|
||||
Name: "员工管理员10",
|
||||
Email: "empadmin10@example.com",
|
||||
Role: "admin",
|
||||
}
|
||||
database.DB.Create(&user)
|
||||
|
||||
service := EmployeeSerialsService{}
|
||||
serials, _ := service.Generate("QREmpCompany2", "设计部", "王十二", 1, user.ID)
|
||||
|
||||
serialNumber := serials[0].SerialNumber
|
||||
service.Revoke(serialNumber)
|
||||
|
||||
_, _, err := service.GenerateQRCode(serialNumber, "", "localhost:3000", "http")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "已被禁用")
|
||||
|
||||
for _, serial := range serials {
|
||||
database.DB.Unscoped().Delete(&serial)
|
||||
}
|
||||
database.DB.Unscoped().Where("company_name = ?", "QREmpCompany2").Delete(&models.Company{})
|
||||
database.DB.Unscoped().Delete(&user)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user