package controllers import ( "errors" "hr_receiver/config" "hr_receiver/models" "net/http" "strconv" "strings" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // GatewayAdminController 处理网关相关的HTTP请求 type GatewayAdminController struct { DB *gorm.DB } // gatewayPayload 用于接收前端JSON数据的结构体 type gatewayPayload struct { MAC string `json:"mac"` Name string `json:"name"` RegionID uint32 `json:"regionId"` Location string `json:"location"` ProjectType string `json:"projectType"` IsSold bool `json:"isSold"` // SoldAt 是可选字段。如果 IsSold 为 true 且未传此字段,后端自动设为当前时间 SoldAt *time.Time `json:"soldAt"` } // NewGatewayAdminController 初始化控制器 func NewGatewayAdminController() *GatewayAdminController { return &GatewayAdminController{DB: config.DB} } // List 获取网关列表 // GET /api/gateways?keyword=&isSold=&projectType= func (gc *GatewayAdminController) List(c *gin.Context) { var items []models.Gateway query := gc.DB.Model(&models.Gateway{}).Order("region_id ASC, created_at DESC") // 模糊搜索 if keyword := strings.TrimSpace(c.Query("keyword")); keyword != "" { likeValue := "%" + keyword + "%" query = query.Where("name LIKE ? OR mac LIKE ?", likeValue, likeValue) } // 按售出状态筛选 (可选) if soldStr := c.Query("isSold"); soldStr != "" { isSold, _ := strconv.ParseBool(soldStr) query = query.Where("is_sold = ?", isSold) } // 按项目类型筛选 (可选) if projectType := c.Query("projectType"); projectType != "" { query = query.Where("project_type = ?", projectType) } if err := query.Find(&items).Error; err != nil { writeError(c, http.StatusInternalServerError, "查询网关列表失败") return } writeSuccess(c, http.StatusOK, "查询成功", items) } // Create 创建新网关 // POST /api/gateways func (gc *GatewayAdminController) Create(c *gin.Context) { var payload gatewayPayload if err := c.ShouldBindJSON(&payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } if err := validateGatewayPayload(payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } // 处理售出逻辑:如果已售出但未提供时间,默认为当前时间 (2026-04-29) var soldAt *time.Time if payload.IsSold { if payload.SoldAt == nil { // 设置为指定时间:2020-04-29 specificTime := time.Date(2026, 4, 29, 0, 0, 0, 0, time.Local) soldAt = &specificTime } else { soldAt = payload.SoldAt } } record := models.Gateway{ MAC: strings.ToUpper(strings.TrimSpace(payload.MAC)), Name: strings.TrimSpace(payload.Name), RegionID: payload.RegionID, Location: strings.TrimSpace(payload.Location), ProjectType: strings.TrimSpace(payload.ProjectType), IsSold: payload.IsSold, SoldAt: soldAt, } if err := gc.DB.Create(&record).Error; err != nil { if strings.Contains(strings.ToLower(err.Error()), "duplicate") { writeError(c, http.StatusConflict, "该MAC地址的网关已存在") return } writeError(c, http.StatusInternalServerError, "保存网关失败") return } writeSuccess(c, http.StatusCreated, "创建成功", record) } // Update 更新网关信息 // PUT /api/gateways/:id func (gc *GatewayAdminController) Update(c *gin.Context) { record, err := gc.findByID(c.Param("id")) if err != nil { respondGatewayLookupError(c, err) return } var payload gatewayPayload if err := c.ShouldBindJSON(&payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } if err := validateGatewayPayload(payload); 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.IsSold = payload.IsSold // 核心逻辑:处理售出时间 // 情况1: 标记为已售出,但 SoldAt 为空 -> 自动填充指定时间 (2026-04-29) // 情况2: 标记为已售出,且提供了 SoldAt -> 使用提供的值 // 情况3: 标记为未售出 -> 强制 SoldAt 为 nil (防止数据残留) if payload.IsSold { if payload.SoldAt == nil { specificTime := time.Date(2026, 4, 29, 0, 0, 0, 0, time.Local) record.SoldAt = &specificTime } else { record.SoldAt = payload.SoldAt } } else { record.SoldAt = nil } if err := gc.DB.Save(&record).Error; err != nil { writeError(c, http.StatusInternalServerError, "更新网关失败") return } writeSuccess(c, http.StatusOK, "更新成功", record) } // Delete 删除网关 // DELETE /api/gateways/:id func (gc *GatewayAdminController) Delete(c *gin.Context) { record, err := gc.findByID(c.Param("id")) if err != nil { respondGatewayLookupError(c, err) return } // 建议:如果网关已售出,禁止删除,防止数据丢失 if record.IsSold { writeError(c, http.StatusForbidden, "已售出的网关禁止删除") return } if err := gc.DB.Delete(&record).Error; err != nil { writeError(c, http.StatusInternalServerError, "删除网关失败") return } writeSuccess(c, http.StatusOK, "删除成功", nil) } // 辅助方法:根据ID查找记录 func (gc *GatewayAdminController) findByID(id string) (models.Gateway, error) { var record models.Gateway numericID, err := strconv.ParseUint(strings.TrimSpace(id), 10, 64) if err != nil { return record, gorm.ErrRecordNotFound } if err := gc.DB.First(&record, numericID).Error; err != nil { return record, err } return record, nil } // 辅助方法:验证输入数据 func validateGatewayPayload(payload gatewayPayload) error { mac := strings.TrimSpace(payload.MAC) if mac == "" { return errors.New("MAC地址是必填项") } if len(mac) < 12 { return errors.New("MAC地址格式不正确") } if strings.TrimSpace(payload.Name) == "" { return errors.New("网关名称是必填项") } return nil } // 错误处理函数 func respondGatewayLookupError(c *gin.Context, err error) { if errors.Is(err, gorm.ErrRecordNotFound) { writeError(c, http.StatusNotFound, "未找到该网关") return } writeError(c, http.StatusInternalServerError, "查询网关时出错") }