Re-migrate code

This commit is contained in:
2026-03-02 10:05:12 +08:00
parent d1e6f15745
commit 51025195a5
7 changed files with 177 additions and 0 deletions

View File

@@ -163,3 +163,16 @@ func (c *AuthController) UpdateProfile(ctx *gin.Context) {
"user": profile,
})
}
// Logout 登出
// @Summary 用户登出
// @Description 用户登出JWT 无状态,前端清理令牌即可)
// @Tags 认证
// @Produce json
// @Security BearerAuth
// @Success 200 {object} models.BaseResponse
// @Failure 401 {object} models.ErrorResponse
// @Router /auth/logout [post]
func (c *AuthController) Logout(ctx *gin.Context) {
SuccessResponse(ctx, "登出成功")
}

View File

@@ -198,3 +198,25 @@ func (c *CompaniesController) Delete(ctx *gin.Context) {
"message": "企业删除成功",
})
}
// StatsOverview 获取企业统计概览
// @Summary 获取企业统计概览
// @Description 获取企业、企业赋码、员工赋码的统计数据
// @Tags 企业管理
// @Produce json
// @Security BearerAuth
// @Success 200 {object} models.DataResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
// @Router /companies/stats/overview [get]
func (c *CompaniesController) StatsOverview(ctx *gin.Context) {
stats, err := c.companiesService.GetStatsOverview()
if err != nil {
ErrorResponse(ctx, http.StatusInternalServerError, err.Error())
return
}
SuccessResponse(ctx, "获取企业统计概览成功", gin.H{
"overview": stats,
})
}

View File

@@ -4,6 +4,7 @@ import (
"os"
"path/filepath"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
@@ -118,4 +119,49 @@ func AutoMigrate() {
}
logger.Info("数据库迁移成功")
// 创建默认管理员用户
seedAdminUser()
}
// seedAdminUser 创建默认管理员用户
func seedAdminUser() {
var count int64
DB.Model(&models.User{}).Count(&count)
if count > 0 {
return
}
// 默认管理员账号
adminUsername := "admin"
adminPassword := "Beifan@2026"
adminName := "管理员"
adminEmail := "admin@example.com"
// 加密密码
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost)
if err != nil {
logger.Warn("管理员密码加密失败", logger.Err(err))
return
}
admin := models.User{
Username: adminUsername,
Password: string(hashedPassword),
Name: adminName,
Email: adminEmail,
Role: "admin",
}
if err := DB.Create(&admin).Error; err != nil {
logger.Warn("创建默认管理员用户失败", logger.Err(err))
return
}
logger.Info("已创建默认管理员用户",
logger.String("username", adminUsername),
logger.String("password", adminPassword),
logger.String("role", "admin"),
)
}

View File

@@ -164,6 +164,19 @@ type CompanyUpdateRequest struct {
IsActive *bool `json:"isActive"`
}
// CompanyStatsOverviewDTO 企业统计概览
type CompanyStatsOverviewDTO struct {
TotalCompanies int64 `json:"totalCompanies"`
ActiveCompanies int64 `json:"activeCompanies"`
InactiveCompanies int64 `json:"inactiveCompanies"`
TotalSerials int64 `json:"totalSerials"`
ActiveSerials int64 `json:"activeSerials"`
RevokedSerials int64 `json:"revokedSerials"`
TotalEmployeeSerials int64 `json:"totalEmployeeSerials"`
ActiveEmployeeSerials int64 `json:"activeEmployeeSerials"`
RevokedEmployeeSerials int64 `json:"revokedEmployeeSerials"`
}
// EmployeeSerial 员工序列号模型
type EmployeeSerial struct {
ID uint `gorm:"primaryKey" json:"id"`

View File

@@ -25,6 +25,7 @@ func SetupAPIRoutes(r *gin.RouterGroup) {
authRoutes := r.Group("/auth")
{
authRoutes.POST("/login", authController.Login)
authRoutes.POST("/logout", middleware.JWTAuthMiddleware(), authController.Logout)
authRoutes.GET("/profile", middleware.JWTAuthMiddleware(), authController.GetProfile)
authRoutes.PUT("/profile", middleware.JWTAuthMiddleware(), authController.UpdateProfile)
authRoutes.POST("/change-password", middleware.JWTAuthMiddleware(), authController.ChangePassword)
@@ -47,6 +48,7 @@ func SetupAPIRoutes(r *gin.RouterGroup) {
companiesController := controllers.NewCompaniesController()
companiesRoutes := r.Group("/companies")
{
companiesRoutes.GET("/stats/overview", middleware.JWTAuthMiddleware(), companiesController.StatsOverview)
companiesRoutes.GET("/", middleware.JWTAuthMiddleware(), companiesController.FindAll)
companiesRoutes.POST("/", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), companiesController.Create)
companiesRoutes.PUT("/:companyName", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), companiesController.Update)

View File

@@ -110,3 +110,40 @@ func (s *CompaniesService) Delete(companyName string) error {
return nil
}
// GetStatsOverview 获取企业统计概览
func (s *CompaniesService) GetStatsOverview() (*models.CompanyStatsOverviewDTO, error) {
stats := &models.CompanyStatsOverviewDTO{}
if err := database.DB.Model(&models.Company{}).Count(&stats.TotalCompanies).Error; err != nil {
return nil, errors.New("统计企业总数失败")
}
if err := database.DB.Model(&models.Company{}).Where("is_active = ?", true).Count(&stats.ActiveCompanies).Error; err != nil {
return nil, errors.New("统计启用企业数量失败")
}
stats.InactiveCompanies = stats.TotalCompanies - stats.ActiveCompanies
if err := database.DB.Model(&models.Serial{}).Count(&stats.TotalSerials).Error; err != nil {
return nil, errors.New("统计企业赋码总数失败")
}
if err := database.DB.Model(&models.Serial{}).Where("is_active = ?", true).Count(&stats.ActiveSerials).Error; err != nil {
return nil, errors.New("统计有效企业赋码数量失败")
}
stats.RevokedSerials = stats.TotalSerials - stats.ActiveSerials
if err := database.DB.Model(&models.EmployeeSerial{}).Count(&stats.TotalEmployeeSerials).Error; err != nil {
return nil, errors.New("统计员工赋码总数失败")
}
if err := database.DB.Model(&models.EmployeeSerial{}).Where("is_active = ?", true).Count(&stats.ActiveEmployeeSerials).Error; err != nil {
return nil, errors.New("统计有效员工赋码数量失败")
}
stats.RevokedEmployeeSerials = stats.TotalEmployeeSerials - stats.ActiveEmployeeSerials
return stats, nil
}

View File

@@ -773,3 +773,47 @@ func TestEmployeeSerialsService_GenerateQRCode_Inactive(t *testing.T) {
database.DB.Unscoped().Where("company_name = ?", "QREmpCompany2").Delete(&models.Company{})
database.DB.Unscoped().Delete(&user)
}
func TestCompaniesService_GetStatsOverview_Success(t *testing.T) {
var user models.User
password, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
user = models.User{
Username: "statsadmin",
Password: string(password),
Name: "统计管理员",
Email: "statsadmin@example.com",
Role: "admin",
}
database.DB.Create(&user)
serialService := SerialsService{}
companySerials, _ := serialService.Generate("StatsCompany", 2, 30, user.ID, "STAT")
employeeSerialsService := EmployeeSerialsService{}
employeeSerials, _ := employeeSerialsService.Generate("StatsCompany", "技术部", "测试员工", 2, user.ID)
_ = serialService.Revoke(companySerials[0].SerialNumber)
_ = employeeSerialsService.Revoke(employeeSerials[0].SerialNumber)
companiesService := CompaniesService{}
stats, err := companiesService.GetStatsOverview()
assert.NoError(t, err)
assert.NotNil(t, stats)
assert.GreaterOrEqual(t, stats.TotalCompanies, int64(1))
assert.GreaterOrEqual(t, stats.TotalSerials, int64(2))
assert.GreaterOrEqual(t, stats.TotalEmployeeSerials, int64(2))
assert.GreaterOrEqual(t, stats.RevokedSerials, int64(1))
assert.GreaterOrEqual(t, stats.RevokedEmployeeSerials, int64(1))
for _, serial := range companySerials {
database.DB.Unscoped().Delete(&serial)
}
for _, serial := range employeeSerials {
database.DB.Unscoped().Delete(&serial)
}
database.DB.Unscoped().Where("company_name = ?", "StatsCompany").Delete(&models.Company{})
database.DB.Unscoped().Delete(&user)
}