From 51025195a59429a6008dcd0d7bbfd78a80ddeb67 Mon Sep 17 00:00:00 2001 From: ZHENG XIAOYI Date: Mon, 2 Mar 2026 10:05:12 +0800 Subject: [PATCH] Re-migrate code --- controllers/auth_controller.go | 13 ++++++++ controllers/companies_controller.go | 22 ++++++++++++++ database/database.go | 46 +++++++++++++++++++++++++++++ models/models.go | 13 ++++++++ routes/routes.go | 2 ++ services/companies_service.go | 37 +++++++++++++++++++++++ services/services_test.go | 44 +++++++++++++++++++++++++++ 7 files changed, 177 insertions(+) diff --git a/controllers/auth_controller.go b/controllers/auth_controller.go index fb98d50..6ae2c68 100644 --- a/controllers/auth_controller.go +++ b/controllers/auth_controller.go @@ -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, "登出成功") +} diff --git a/controllers/companies_controller.go b/controllers/companies_controller.go index ebc62aa..07253ca 100644 --- a/controllers/companies_controller.go +++ b/controllers/companies_controller.go @@ -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, + }) +} diff --git a/database/database.go b/database/database.go index 8c1a31a..2512f6e 100644 --- a/database/database.go +++ b/database/database.go @@ -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"), + ) } diff --git a/models/models.go b/models/models.go index e3c9b58..b4266a0 100644 --- a/models/models.go +++ b/models/models.go @@ -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"` diff --git a/routes/routes.go b/routes/routes.go index 8923977..00b66d3 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -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) diff --git a/services/companies_service.go b/services/companies_service.go index ea61131..8b09494 100644 --- a/services/companies_service.go +++ b/services/companies_service.go @@ -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 +} diff --git a/services/services_test.go b/services/services_test.go index b4b1055..a77c955 100644 --- a/services/services_test.go +++ b/services/services_test.go @@ -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) +}