package controllers import ( "encoding/json" "errors" "hr_receiver/config" "hr_receiver/models" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type ProductDefinitionAdminController struct { DB *gorm.DB } type productDefinitionPayload struct { Code string `json:"code"` Name string `json:"name"` Category string `json:"category"` Description string `json:"description"` ParameterSchema string `json:"parameterSchema"` TrackSerialNumber bool `json:"trackSerialNumber"` IsActive bool `json:"isActive"` Sort int `json:"sort"` } func NewProductDefinitionAdminController() *ProductDefinitionAdminController { return &ProductDefinitionAdminController{DB: config.DB} } // @Summary 获取产品定义列表 // @Description 查询产品定义列表,支持按关键词/分类/启用状态筛选 // @Tags 产品定义管理 // @Produce json // @Param keyword query string false "关键词(代码/名称/描述模糊搜索)" // @Param category query string false "分类筛选" // @Param isActive query bool false "是否启用" // @Security BearerAuth // @Success 200 {object} SwagAPIResponse "查询成功" // @Router /admin/product-definitions [get] func (pc *ProductDefinitionAdminController) List(c *gin.Context) { var items []models.ProductDefinition query := pc.DB.Model(&models.ProductDefinition{}).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 category := strings.TrimSpace(c.Query("category")); category != "" { query = query.Where("category = ?", strings.ToLower(category)) } 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 product definitions") return } writeSuccess(c, http.StatusOK, "query success", items) } // @Summary 创建产品定义 // @Description 创建新的产品定义 // @Tags 产品定义管理 // @Accept json // @Produce json // @Param productDefinition body productDefinitionPayload true "产品定义信息" // @Security BearerAuth // @Success 201 {object} SwagAPIResponse "创建成功" // @Failure 400 {object} SwagAPIResponse "请求参数错误" // @Router /admin/product-definitions [post] func (pc *ProductDefinitionAdminController) Create(c *gin.Context) { var payload productDefinitionPayload if err := c.ShouldBindJSON(&payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } if err := validateProductDefinitionPayload(payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } record := models.ProductDefinition{ Code: payload.Code, Name: payload.Name, Category: payload.Category, Description: payload.Description, ParameterSchema: payload.ParameterSchema, TrackSerialNumber: payload.TrackSerialNumber, IsActive: payload.IsActive, Sort: payload.Sort, } if err := pc.DB.Create(&record).Error; err != nil { writeProductDefinitionDBError(c, err) return } writeSuccess(c, http.StatusCreated, "create success", record) } // @Summary 更新产品定义 // @Description 更新指定产品定义的信息 // @Tags 产品定义管理 // @Accept json // @Produce json // @Param id path int true "产品定义ID" // @Param productDefinition body productDefinitionPayload true "更新信息" // @Security BearerAuth // @Success 200 {object} SwagAPIResponse "更新成功" // @Failure 400 {object} SwagAPIResponse "请求参数错误" // @Failure 404 {object} SwagAPIResponse "产品定义不存在" // @Router /admin/product-definitions/{id} [put] func (pc *ProductDefinitionAdminController) Update(c *gin.Context) { record, err := pc.findByID(c.Param("id")) if err != nil { respondProductDefinitionLookupError(c, err) return } var payload productDefinitionPayload if err := c.ShouldBindJSON(&payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } if err := validateProductDefinitionPayload(payload); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } record.Code = payload.Code record.Name = payload.Name record.Category = payload.Category record.Description = payload.Description record.ParameterSchema = payload.ParameterSchema record.TrackSerialNumber = payload.TrackSerialNumber record.IsActive = payload.IsActive record.Sort = payload.Sort if err := pc.DB.Save(&record).Error; err != nil { writeProductDefinitionDBError(c, err) return } writeSuccess(c, http.StatusOK, "update success", record) } // @Summary 删除产品定义 // @Description 删除指定产品定义(被库存或模板引用的无法删除) // @Tags 产品定义管理 // @Produce json // @Param id path int true "产品定义ID" // @Security BearerAuth // @Success 200 {object} SwagAPIResponse "删除成功" // @Failure 404 {object} SwagAPIResponse "产品定义不存在" // @Failure 409 {object} SwagAPIResponse "已被引用无法删除" // @Router /admin/product-definitions/{id} [delete] func (pc *ProductDefinitionAdminController) Delete(c *gin.Context) { record, err := pc.findByID(c.Param("id")) if err != nil { respondProductDefinitionLookupError(c, err) return } var inventoryCount int64 if err := pc.DB.Model(&models.ProductInventory{}).Where("product_code = ?", record.Code).Count(&inventoryCount).Error; err != nil { writeError(c, http.StatusInternalServerError, "failed to check inventory references") return } if inventoryCount > 0 { writeError(c, http.StatusConflict, "current product definition is referenced by inventory") return } var templateCount int64 if err := pc.DB.Model(&models.ProjectProductTemplate{}).Where("product_code = ?", record.Code).Count(&templateCount).Error; err != nil { writeError(c, http.StatusInternalServerError, "failed to check project template references") return } if templateCount > 0 { writeError(c, http.StatusConflict, "current product definition is referenced by project templates") return } if err := pc.DB.Delete(&record).Error; err != nil { writeError(c, http.StatusInternalServerError, "failed to delete product definition") return } writeSuccess(c, http.StatusOK, "delete success", nil) } func (pc *ProductDefinitionAdminController) findByID(id string) (models.ProductDefinition, error) { var record models.ProductDefinition 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 validateProductDefinitionPayload(payload productDefinitionPayload) error { if strings.TrimSpace(payload.Code) == "" { return errors.New("code is required") } if strings.TrimSpace(payload.Name) == "" { return errors.New("name is required") } if strings.TrimSpace(payload.Category) == "" { return errors.New("category is required") } if strings.TrimSpace(payload.ParameterSchema) != "" && !json.Valid([]byte(strings.TrimSpace(payload.ParameterSchema))) { return errors.New("parameterSchema must be valid JSON") } return nil } func respondProductDefinitionLookupError(c *gin.Context, err error) { if errors.Is(err, gorm.ErrRecordNotFound) { writeError(c, http.StatusNotFound, "product definition not found") return } writeError(c, http.StatusInternalServerError, "failed to query product definition") } func writeProductDefinitionDBError(c *gin.Context, err error) { if strings.Contains(strings.ToLower(err.Error()), "unique") { writeError(c, http.StatusConflict, "product code already exists") return } writeError(c, http.StatusInternalServerError, "failed to persist product definition") }