package handler import ( "errors" "strconv" "github.com/gin-gonic/gin" "github.com/wmms/wmms/internal/middleware" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/internal/repository" "github.com/wmms/wmms/internal/service" "github.com/wmms/wmms/pkg/response" ) func organizationID(c *gin.Context) (int64, bool) { id := middleware.GetCurrentOrganizationID(c) if id <= 0 { response.BadRequest(c, "请选择有效的公司") return 0, false } return id, true } func pathID(c *gin.Context) (int64, bool) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil || id <= 0 { response.BadRequest(c, "invalid id") return 0, false } return id, true } func bind(c *gin.Context, value interface{}) bool { if err := c.ShouldBindJSON(value); err != nil { response.BadRequest(c, err.Error()) return false } return true } func (h *Handler) ListOrganizations(c *gin.Context) { items, err := h.organizationSvc.List() if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } func (h *Handler) ListRoles(c *gin.Context) { items, err := h.organizationSvc.ListRoles() if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } func (h *Handler) ListPermissions(c *gin.Context) { items, err := h.organizationSvc.ListPermissions() if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } func (h *Handler) UpdateRole(c *gin.Context) { id, ok := pathID(c) if !ok { return } var req model.UpdateRoleRequest if !bind(c, &req) { return } if err := h.organizationSvc.UpdateRole(id, &req); err != nil { if errors.Is(err, service.ErrProtectedRole) { response.Error(c, 409, err.Error()) return } response.BadRequest(c, err.Error()) return } response.SuccessWithMessage(c, "Role updated successfully", nil) } func (h *Handler) CreateOrganization(c *gin.Context) { var item model.Organization if !bind(c, &item) { return } if err := h.organizationSvc.Create(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Created(c, "Organization created successfully", item) } func (h *Handler) UpdateOrganization(c *gin.Context) { id, ok := pathID(c) if !ok { return } var item model.Organization if !bind(c, &item) { return } item.ID = id if err := h.organizationSvc.Update(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) } func (h *Handler) ListMaterialCategories(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } items, err := h.masterDataSvc.ListCategories(orgID) if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } func (h *Handler) CreateMaterialCategory(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } var item model.MaterialCategory if !bind(c, &item) { return } item.OrganizationID = orgID if err := h.masterDataSvc.CreateCategory(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Created(c, "Material category created successfully", item) } func (h *Handler) UpdateMaterialCategory(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } id, ok := pathID(c) if !ok { return } var item model.MaterialCategory if !bind(c, &item) { return } item.ID, item.OrganizationID = id, orgID if err := h.masterDataSvc.UpdateCategory(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) } func (h *Handler) ListUnits(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } items, err := h.masterDataSvc.ListUnits(orgID) if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } func (h *Handler) CreateUnit(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } var item model.Unit if !bind(c, &item) { return } item.OrganizationID = orgID if err := h.masterDataSvc.CreateUnit(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Created(c, "Unit created successfully", item) } func (h *Handler) UpdateUnit(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } id, ok := pathID(c) if !ok { return } var item model.Unit if !bind(c, &item) { return } item.ID, item.OrganizationID = id, orgID if err := h.masterDataSvc.UpdateUnit(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) } func (h *Handler) ListCodeRules(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } items, err := h.masterDataSvc.ListCodeRules(orgID) if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } // GetNextMaterialCode 预览按小类规则自动生成的物料编码。新增物料的人要用它 // 填表,所以只要求登录,不要求管理员。 func (h *Handler) GetNextMaterialCode(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } categoryID, err := strconv.ParseInt(c.Query("category_id"), 10, 64) if err != nil || categoryID <= 0 { response.BadRequest(c, "invalid category_id") return } next, err := h.masterDataSvc.NextMaterialCode(orgID, categoryID) if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, next) } func (h *Handler) CreateCodeRule(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } var item model.CodeRule if !bind(c, &item) { return } item.OrganizationID = orgID if err := h.masterDataSvc.CreateCodeRule(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Created(c, "Code rule created successfully", item) } func (h *Handler) UpdateCodeRule(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } id, ok := pathID(c) if !ok { return } var item model.CodeRule if !bind(c, &item) { return } item.ID, item.OrganizationID = id, orgID if err := h.masterDataSvc.UpdateCodeRule(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) } func (h *Handler) ListPartners(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } // 分页参数的默认值与上限跟物料等列表接口保持一致,前端不必记两套规则 limit := 20 offset := 0 if l := c.Query("limit"); l != "" { if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 { limit = parsed if limit > 100 { limit = 100 } } } if o := c.Query("offset"); o != "" { if parsed, err := strconv.Atoi(o); err == nil && parsed >= 0 { offset = parsed } } items, total, err := h.partnerSvc.List(orgID, c.Query("role"), limit, offset, c.Query("category")) if err != nil { response.BadRequest(c, err.Error()) return } if items == nil { items = []model.BusinessPartner{} } response.PaginatedSuccess(c, items, limit, offset, total) } func (h *Handler) CreatePartner(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } var item model.BusinessPartner if !bind(c, &item) { return } item.OrganizationID = orgID if err := h.partnerSvc.Create(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Created(c, "Partner created successfully", item) } func (h *Handler) UpdatePartner(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } id, ok := pathID(c) if !ok { return } var item model.BusinessPartner if !bind(c, &item) { return } item.ID, item.OrganizationID = id, orgID if err := h.partnerSvc.Update(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) } func (h *Handler) ListWarehouses(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } items, err := h.warehouseSvc.List(orgID) if err != nil { response.InternalError(c, err.Error()) return } response.Success(c, items) } func (h *Handler) CreateWarehouse(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } var item model.Warehouse if !bind(c, &item) { return } item.OrganizationID = orgID if err := h.warehouseSvc.Create(&item); err != nil { // 每公司最多一个仓库:唯一约束冲突回 409 并给出可操作提示,而不是笼统的 400。 if errors.Is(err, repository.ErrDuplicateWarehouse) { response.Error(c, 409, err.Error()) return } response.BadRequest(c, err.Error()) return } response.Created(c, "Warehouse created successfully", item) } func (h *Handler) UpdateWarehouse(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } id, ok := pathID(c) if !ok { return } var item model.Warehouse if !bind(c, &item) { return } item.ID, item.OrganizationID = id, orgID if err := h.warehouseSvc.Update(&item); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) } func (h *Handler) CreateWarehouseLocation(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } warehouseID, ok := pathID(c) if !ok { return } var item model.WarehouseLocation if !bind(c, &item) { return } item.WarehouseID = warehouseID if err := h.warehouseSvc.CreateLocation(&item, orgID); err != nil { // 每仓库最多一个库位:唯一约束冲突回 409,仓库不属于本公司仍回 400。 if errors.Is(err, repository.ErrDuplicateLocation) { response.Error(c, 409, err.Error()) return } response.BadRequest(c, err.Error()) return } response.Created(c, "Location created successfully", item) } func (h *Handler) UpdateWarehouseLocation(c *gin.Context) { orgID, ok := organizationID(c) if !ok { return } warehouseID, ok := pathID(c) if !ok { return } locationID, err := strconv.ParseInt(c.Param("locationId"), 10, 64) if err != nil || locationID <= 0 { response.BadRequest(c, "invalid location id") return } var item model.WarehouseLocation if !bind(c, &item) { return } item.ID, item.WarehouseID = locationID, warehouseID if err := h.warehouseSvc.UpdateLocation(&item, orgID); err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, item) }