feat: project_type.

This commit is contained in:
2026-04-30 20:46:14 +08:00
parent b8dfa150b2
commit 79515007b7
5 changed files with 313 additions and 3 deletions
+42 -3
View File
@@ -36,7 +36,7 @@ func NewGatewayAdminController() *GatewayAdminController {
}
// List 获取网关列表
// GET /api/gateways?keyword=&isSold=&projectType=
// GET /api/gateways?keyword=&isSold=&projectType=&regionId=
func (gc *GatewayAdminController) List(c *gin.Context) {
var items []models.Gateway
query := gc.DB.Model(&models.Gateway{}).Order("region_id ASC, created_at DESC")
@@ -57,6 +57,14 @@ func (gc *GatewayAdminController) List(c *gin.Context) {
if projectType := c.Query("projectType"); projectType != "" {
query = query.Where("project_type = ?", projectType)
}
if regionIDStr := strings.TrimSpace(c.Query("regionId")); regionIDStr != "" {
regionID, err := strconv.ParseUint(regionIDStr, 10, 32)
if err != nil || regionID == 0 {
writeError(c, http.StatusBadRequest, "regionId参数无效")
return
}
query = query.Where("region_id = ?", uint32(regionID))
}
if err := query.Find(&items).Error; err != nil {
writeError(c, http.StatusInternalServerError, "查询网关列表失败")
@@ -78,6 +86,11 @@ func (gc *GatewayAdminController) Create(c *gin.Context) {
writeError(c, http.StatusBadRequest, err.Error())
return
}
projectType, err := gc.findGatewayProjectType(payload.ProjectType)
if err != nil {
writeError(c, http.StatusBadRequest, err.Error())
return
}
// 处理售出逻辑:如果已售出但未提供时间,默认为当前时间 (2026-04-29)
var soldAt *time.Time
@@ -96,7 +109,7 @@ func (gc *GatewayAdminController) Create(c *gin.Context) {
Name: strings.TrimSpace(payload.Name),
RegionID: payload.RegionID,
Location: strings.TrimSpace(payload.Location),
ProjectType: strings.TrimSpace(payload.ProjectType),
ProjectType: projectType.Code,
IsSold: payload.IsSold,
SoldAt: soldAt,
}
@@ -131,12 +144,17 @@ func (gc *GatewayAdminController) Update(c *gin.Context) {
writeError(c, http.StatusBadRequest, err.Error())
return
}
projectType, err := gc.findGatewayProjectType(payload.ProjectType)
if err != nil {
writeError(c, http.StatusBadRequest, err.Error())
return
}
// 更新字段
record.Name = strings.TrimSpace(payload.Name)
record.RegionID = payload.RegionID
record.Location = strings.TrimSpace(payload.Location)
record.ProjectType = strings.TrimSpace(payload.ProjectType)
record.ProjectType = projectType.Code
record.IsSold = payload.IsSold
// 核心逻辑:处理售出时间
@@ -196,6 +214,24 @@ func (gc *GatewayAdminController) findByID(id string) (models.Gateway, error) {
return record, nil
}
func (gc *GatewayAdminController) findGatewayProjectType(projectTypeCode string) (models.ProjectType, error) {
var projectType models.ProjectType
code := strings.TrimSpace(strings.ToLower(projectTypeCode))
if code == "" {
return projectType, errors.New("projectType is required")
}
if err := gc.DB.Where("code = ? AND is_active = ?", code, true).First(&projectType).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return projectType, errors.New("projectType is invalid")
}
return projectType, errors.New("failed to query project type")
}
if !projectType.SupportsGateway {
return projectType, errors.New("current project type does not support gateways")
}
return projectType, nil
}
// 辅助方法:验证输入数据
func validateGatewayPayload(payload gatewayPayload) error {
mac := strings.TrimSpace(payload.MAC)
@@ -208,6 +244,9 @@ func validateGatewayPayload(payload gatewayPayload) error {
if strings.TrimSpace(payload.Name) == "" {
return errors.New("网关名称是必填项")
}
if strings.TrimSpace(payload.ProjectType) == "" {
return errors.New("项目类型是必填项")
}
return nil
}
+185
View File
@@ -0,0 +1,185 @@
package controllers
import (
"errors"
"hr_receiver/config"
"hr_receiver/models"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type ProjectTypeAdminController struct {
DB *gorm.DB
}
type projectTypePayload struct {
Code string `json:"code"`
Name string `json:"name"`
Description string `json:"description"`
SupportsGateway bool `json:"supportsGateway"`
IsActive bool `json:"isActive"`
Sort int `json:"sort"`
}
func NewProjectTypeAdminController() *ProjectTypeAdminController {
return &ProjectTypeAdminController{DB: config.DB}
}
func (pc *ProjectTypeAdminController) List(c *gin.Context) {
var items []models.ProjectType
query := pc.DB.Model(&models.ProjectType{}).Order("sort ASC, id ASC")
if keyword := strings.TrimSpace(c.Query("keyword")); keyword != "" {
likeValue := "%" + keyword + "%"
query = query.Where("code LIKE ? OR name LIKE ? OR description LIKE ?", likeValue, likeValue, likeValue)
}
if supportsGatewayStr := strings.TrimSpace(c.Query("supportsGateway")); supportsGatewayStr != "" {
supportsGateway, err := strconv.ParseBool(supportsGatewayStr)
if err != nil {
writeError(c, http.StatusBadRequest, "invalid supportsGateway")
return
}
query = query.Where("supports_gateway = ?", supportsGateway)
}
if isActiveStr := strings.TrimSpace(c.Query("isActive")); isActiveStr != "" {
isActive, err := strconv.ParseBool(isActiveStr)
if err != nil {
writeError(c, http.StatusBadRequest, "invalid isActive")
return
}
query = query.Where("is_active = ?", isActive)
}
if err := query.Find(&items).Error; err != nil {
writeError(c, http.StatusInternalServerError, "failed to query project types")
return
}
writeSuccess(c, http.StatusOK, "query success", items)
}
func (pc *ProjectTypeAdminController) Create(c *gin.Context) {
var payload projectTypePayload
if err := c.ShouldBindJSON(&payload); err != nil {
writeError(c, http.StatusBadRequest, err.Error())
return
}
if err := validateProjectTypePayload(payload); err != nil {
writeError(c, http.StatusBadRequest, err.Error())
return
}
record := models.ProjectType{
Code: normalizeProjectTypeCode(payload.Code),
Name: strings.TrimSpace(payload.Name),
Description: strings.TrimSpace(payload.Description),
SupportsGateway: payload.SupportsGateway,
IsActive: payload.IsActive,
Sort: payload.Sort,
}
if err := pc.DB.Create(&record).Error; err != nil {
writeProjectTypeDBError(c, err)
return
}
writeSuccess(c, http.StatusCreated, "create success", record)
}
func (pc *ProjectTypeAdminController) Update(c *gin.Context) {
record, err := pc.findByID(c.Param("id"))
if err != nil {
respondProjectTypeLookupError(c, err)
return
}
var payload projectTypePayload
if err := c.ShouldBindJSON(&payload); err != nil {
writeError(c, http.StatusBadRequest, err.Error())
return
}
if err := validateProjectTypePayload(payload); err != nil {
writeError(c, http.StatusBadRequest, err.Error())
return
}
record.Code = normalizeProjectTypeCode(payload.Code)
record.Name = strings.TrimSpace(payload.Name)
record.Description = strings.TrimSpace(payload.Description)
record.SupportsGateway = payload.SupportsGateway
record.IsActive = payload.IsActive
record.Sort = payload.Sort
if err := pc.DB.Save(&record).Error; err != nil {
writeProjectTypeDBError(c, err)
return
}
writeSuccess(c, http.StatusOK, "update success", record)
}
func (pc *ProjectTypeAdminController) Delete(c *gin.Context) {
record, err := pc.findByID(c.Param("id"))
if err != nil {
respondProjectTypeLookupError(c, err)
return
}
var gatewayCount int64
if err := pc.DB.Model(&models.Gateway{}).Where("project_type = ?", record.Code).Count(&gatewayCount).Error; err != nil {
writeError(c, http.StatusInternalServerError, "failed to check gateway references")
return
}
if gatewayCount > 0 {
writeError(c, http.StatusConflict, "current project type is referenced by gateways")
return
}
if err := pc.DB.Delete(&record).Error; err != nil {
writeError(c, http.StatusInternalServerError, "failed to delete project type")
return
}
writeSuccess(c, http.StatusOK, "delete success", nil)
}
func (pc *ProjectTypeAdminController) findByID(id string) (models.ProjectType, error) {
var record models.ProjectType
numericID, err := strconv.ParseUint(strings.TrimSpace(id), 10, 64)
if err != nil {
return record, gorm.ErrRecordNotFound
}
if err := pc.DB.First(&record, numericID).Error; err != nil {
return record, err
}
return record, nil
}
func validateProjectTypePayload(payload projectTypePayload) error {
if normalizeProjectTypeCode(payload.Code) == "" {
return errors.New("code is required")
}
if strings.TrimSpace(payload.Name) == "" {
return errors.New("name is required")
}
return nil
}
func normalizeProjectTypeCode(code string) string {
return strings.TrimSpace(strings.ToLower(code))
}
func respondProjectTypeLookupError(c *gin.Context, err error) {
if errors.Is(err, gorm.ErrRecordNotFound) {
writeError(c, http.StatusNotFound, "project type not found")
return
}
writeError(c, http.StatusInternalServerError, "failed to query project type")
}
func writeProjectTypeDBError(c *gin.Context, err error) {
if strings.Contains(strings.ToLower(err.Error()), "unique") {
writeError(c, http.StatusConflict, "project type code already exists")
return
}
writeError(c, http.StatusInternalServerError, "failed to persist project type")
}