package handler import ( "log" "net/http" "github.com/gin-gonic/gin" "github.com/muyuqingfeng/iloom/purchaser-service/internal/service" "github.com/muyuqingfeng/iloom/shared/pkg/response" "github.com/muyuqingfeng/iloom/shared/pkg/types" "github.com/muyuqingfeng/iloom/shared/pkg/websocket" ) type PlanHandler struct { svc *service.PlanService wsHub *websocket.Hub } func NewPlanHandler(svc *service.PlanService, wsHub *websocket.Hub) *PlanHandler { return &PlanHandler{svc: svc, wsHub: wsHub} } type CreatePlanReq struct { ProductName string `json:"product_name" binding:"required"` FabricCode string `json:"fabric_code" binding:"required"` Color string `json:"color" binding:"required"` ColorCode string `json:"color_code"` TargetQuantity float64 `json:"target_quantity" binding:"required,gt=0"` ProductionPrice *float64 `json:"production_price"` YarnUsagePerMeter *float64 `json:"yarn_usage_per_meter"` Remark string `json:"remark"` YarnRatios []CreateYarnRatioReq `json:"yarn_ratios"` } type CreateYarnRatioReq struct { YarnName string `json:"yarn_name" binding:"required"` YarnType string `json:"yarn_type"` Ratio float64 `json:"ratio" binding:"required"` AmountPerMeter float64 `json:"amount_per_meter"` TotalAmount float64 `json:"total_amount"` } type UpdatePlanReq struct { ProductName string `json:"product_name"` FabricCode string `json:"fabric_code"` Color string `json:"color"` ColorCode string `json:"color_code"` TargetQuantity float64 `json:"target_quantity"` ProductionPrice *float64 `json:"production_price"` YarnUsagePerMeter *float64 `json:"yarn_usage_per_meter"` Status string `json:"status"` Remark string `json:"remark"` } type AssignFactoryReq struct { FactoryID string `json:"factory_id" binding:"required"` FactoryType string `json:"factory_type" binding:"required"` } func (h *PlanHandler) List(c *gin.Context) { companyID := c.GetHeader("X-Company-ID") if companyID == "" { response.BadRequest(c, "missing company id") return } var page types.Pagination if err := c.ShouldBindQuery(&page); err != nil { response.BadRequest(c, "invalid pagination") return } page.Normalize() status := c.Query("status") plans, total, err := h.svc.List(c.Request.Context(), companyID, page, status) if err != nil { log.Printf("list plans error: %v", err) response.InternalError(c, "failed to list plans") return } response.OKWithMeta(c, plans, &response.Meta{ Page: page.Page, PageSize: page.PageSize, Total: total, HasMore: page.Page*page.PageSize < total, }) } func (h *PlanHandler) Create(c *gin.Context) { companyID := c.GetHeader("X-Company-ID") userID := c.GetHeader("X-User-ID") if companyID == "" || userID == "" { response.BadRequest(c, "missing user or company id") return } var req CreatePlanReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } svcReq := service.CreatePlanRequest{ ProductName: req.ProductName, FabricCode: req.FabricCode, Color: req.Color, ColorCode: req.ColorCode, TargetQuantity: req.TargetQuantity, ProductionPrice: req.ProductionPrice, YarnUsagePerMeter: req.YarnUsagePerMeter, Remark: req.Remark, } for _, yr := range req.YarnRatios { svcReq.YarnRatios = append(svcReq.YarnRatios, service.CreateYarnRatio{ YarnName: yr.YarnName, YarnType: yr.YarnType, Ratio: yr.Ratio, AmountPerMeter: yr.AmountPerMeter, TotalAmount: yr.TotalAmount, }) } plan, err := h.svc.Create(c.Request.Context(), companyID, userID, svcReq) if err != nil { log.Printf("create plan error: %v", err) response.InternalError(c, "failed to create plan") return } h.wsHub.BroadcastToCompany(companyID, &websocket.Event{ Table: "production_plans", Action: "INSERT", }) c.JSON(http.StatusCreated, response.Response{ Code: 0, Message: "success", Data: plan, }) } func (h *PlanHandler) Get(c *gin.Context) { companyID := c.GetHeader("X-Company-ID") if companyID == "" { response.BadRequest(c, "missing company id") return } id := c.Param("id") plan, err := h.svc.GetByID(c.Request.Context(), id, companyID) if err != nil { log.Printf("get plan error: %v", err) response.NotFound(c, "plan not found") return } response.OK(c, plan) } func (h *PlanHandler) Update(c *gin.Context) { companyID := c.GetHeader("X-Company-ID") if companyID == "" { response.BadRequest(c, "missing company id") return } id := c.Param("id") var req UpdatePlanReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } svcReq := service.UpdatePlanRequest{ ProductName: req.ProductName, FabricCode: req.FabricCode, Color: req.Color, ColorCode: req.ColorCode, TargetQuantity: req.TargetQuantity, ProductionPrice: req.ProductionPrice, YarnUsagePerMeter: req.YarnUsagePerMeter, Status: req.Status, Remark: req.Remark, } if err := h.svc.Update(c.Request.Context(), id, companyID, svcReq); err != nil { log.Printf("update plan error: %v", err) response.InternalError(c, "failed to update plan") return } h.wsHub.BroadcastToCompany(companyID, &websocket.Event{ Table: "production_plans", Action: "UPDATE", }) response.OK(c, nil) } func (h *PlanHandler) Delete(c *gin.Context) { companyID := c.GetHeader("X-Company-ID") if companyID == "" { response.BadRequest(c, "missing company id") return } id := c.Param("id") if err := h.svc.Delete(c.Request.Context(), id, companyID); err != nil { log.Printf("delete plan error: %v", err) response.InternalError(c, "failed to delete plan") return } h.wsHub.BroadcastToCompany(companyID, &websocket.Event{ Table: "production_plans", Action: "DELETE", }) response.OK(c, nil) } func (h *PlanHandler) AssignFactory(c *gin.Context) { companyID := c.GetHeader("X-Company-ID") if companyID == "" { response.BadRequest(c, "missing company id") return } planID := c.Param("id") var req AssignFactoryReq if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.svc.AssignFactory(c.Request.Context(), planID, req.FactoryID, req.FactoryType); err != nil { log.Printf("assign factory error: %v", err) response.InternalError(c, "failed to assign factory") return } h.wsHub.BroadcastToCompany(companyID, &websocket.Event{ Table: "plan_factories", Action: "INSERT", }) response.OK(c, nil) } func (h *PlanHandler) GetSteps(c *gin.Context) { planID := c.Param("id") steps, err := h.svc.GetSteps(c.Request.Context(), planID) if err != nil { log.Printf("get steps error: %v", err) response.InternalError(c, "failed to get steps") return } response.OK(c, steps) } func (h *PlanHandler) GetInventory(c *gin.Context) { planID := c.Param("id") records, err := h.svc.GetInventory(c.Request.Context(), planID) if err != nil { log.Printf("get inventory error: %v", err) response.InternalError(c, "failed to get inventory") return } response.OK(c, records) }