Files
hr_data_analyzer/controllers/product_definition_admin.go
T
2026-05-04 16:20:46 +08:00

242 lines
8.0 KiB
Go

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")
}