2026-08-28 15:06:01 +08:00
|
|
|
package logic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
|
|
"bj_power_mes/ent"
|
2026-08-31 12:58:28 +08:00
|
|
|
"bj_power_mes/ent/dailyplan"
|
|
|
|
|
"bj_power_mes/ent/materialrequest"
|
2026-08-28 15:06:01 +08:00
|
|
|
"bj_power_mes/ent/workorder"
|
2026-08-31 12:58:28 +08:00
|
|
|
"bj_power_mes/ent/workpiece"
|
2026-08-28 15:06:01 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type WorkOrderReq struct {
|
|
|
|
|
Id int `json:"id"`
|
|
|
|
|
WorkOrderNo string `json:"workOrderNo"`
|
|
|
|
|
ProductTypeId int `json:"productTypeId"`
|
|
|
|
|
ProductCode string `json:"productCode"`
|
|
|
|
|
ProductName string `json:"productName"`
|
|
|
|
|
Quantity int `json:"quantity"`
|
|
|
|
|
ProcessSeq string `json:"processSeq"`
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
PlanStart string `json:"planStart"`
|
|
|
|
|
PlanEnd string `json:"planEnd"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateWorkOrder 创建工单
|
|
|
|
|
func (s *Service) CreateWorkOrder(ctx context.Context, req WorkOrderReq, operator string) error {
|
|
|
|
|
if req.WorkOrderNo == "" {
|
|
|
|
|
return errors.New("工单号不能为空")
|
|
|
|
|
}
|
|
|
|
|
if req.ProductTypeId == 0 {
|
|
|
|
|
return errors.New("请选择产品类型")
|
|
|
|
|
}
|
|
|
|
|
if req.Quantity <= 0 {
|
|
|
|
|
req.Quantity = 1
|
|
|
|
|
}
|
|
|
|
|
status := req.Status
|
|
|
|
|
if status == "" {
|
|
|
|
|
status = "CREATED"
|
|
|
|
|
}
|
|
|
|
|
if req.ProcessSeq == "" {
|
2026-08-31 12:58:28 +08:00
|
|
|
req.ProcessSeq = FullProcessSeq
|
|
|
|
|
} else if err := ValidateProcessSeq(req.ProcessSeq); err != nil {
|
|
|
|
|
return err
|
2026-08-28 15:06:01 +08:00
|
|
|
}
|
2026-08-31 12:58:28 +08:00
|
|
|
req.ProcessSeq = NormalizeProcessSeq(req.ProcessSeq)
|
2026-08-28 15:06:01 +08:00
|
|
|
b := s.ctx.EntClient.WorkOrder.Create().
|
|
|
|
|
SetWorkOrderNo(req.WorkOrderNo).
|
|
|
|
|
SetProductTypeId(req.ProductTypeId).
|
|
|
|
|
SetProductCode(req.ProductCode).
|
|
|
|
|
SetProductName(req.ProductName).
|
|
|
|
|
SetQuantity(req.Quantity).
|
|
|
|
|
SetProcessSeq(req.ProcessSeq).
|
|
|
|
|
SetStatus(status)
|
|
|
|
|
if req.PlanStart != "" {
|
|
|
|
|
if t, err := parseDate(req.PlanStart); err == nil {
|
|
|
|
|
b.SetPlanStart(t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if req.PlanEnd != "" {
|
|
|
|
|
if t, err := parseDate(req.PlanEnd); err == nil {
|
|
|
|
|
b.SetPlanEnd(t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if _, err := b.Save(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
s.ctx.EventLog.Write(ctx, "work.order.create", req.WorkOrderNo, operator, "work_order", req.WorkOrderNo, "创建工单", map[string]any{"quantity": req.Quantity})
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Service) UpdateWorkOrder(ctx context.Context, req WorkOrderReq, operator string) error {
|
|
|
|
|
if req.Id == 0 {
|
|
|
|
|
return errors.New("缺少 id")
|
|
|
|
|
}
|
2026-08-31 12:58:28 +08:00
|
|
|
if req.ProcessSeq != "" {
|
|
|
|
|
if err := ValidateProcessSeq(req.ProcessSeq); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.ProcessSeq = NormalizeProcessSeq(req.ProcessSeq)
|
|
|
|
|
}
|
|
|
|
|
u := s.ctx.EntClient.WorkOrder.UpdateOneID(req.Id).SetProcessSeq(req.ProcessSeq)
|
|
|
|
|
if req.Status != "" {
|
|
|
|
|
u.SetStatus(req.Status)
|
|
|
|
|
}
|
|
|
|
|
if req.Quantity > 0 {
|
|
|
|
|
u.SetQuantity(req.Quantity)
|
|
|
|
|
}
|
|
|
|
|
if req.ProductCode != "" {
|
|
|
|
|
u.SetProductCode(req.ProductCode)
|
|
|
|
|
}
|
|
|
|
|
if req.ProductName != "" {
|
|
|
|
|
u.SetProductName(req.ProductName)
|
|
|
|
|
}
|
2026-08-28 15:06:01 +08:00
|
|
|
if req.PlanStart != "" {
|
|
|
|
|
if t, err := parseDate(req.PlanStart); err == nil {
|
|
|
|
|
u.SetPlanStart(t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if req.PlanEnd != "" {
|
|
|
|
|
if t, err := parseDate(req.PlanEnd); err == nil {
|
|
|
|
|
u.SetPlanEnd(t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if err := u.Exec(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
wo, err := s.ctx.EntClient.WorkOrder.Get(ctx, req.Id)
|
|
|
|
|
if err == nil {
|
|
|
|
|
s.ctx.EventLog.Write(ctx, "work.order.update", wo.WorkOrderNo, operator, "work_order", wo.WorkOrderNo, "更新工单", map[string]any{"status": req.Status})
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-31 12:58:28 +08:00
|
|
|
// ListWorkOrders 查询工单(支持工单号/状态/产品编码/产品名称 过滤)
|
|
|
|
|
func (s *Service) ListWorkOrders(ctx context.Context, orderNo, status, productCode, productName string) ([]*ent.WorkOrder, error) {
|
2026-08-28 15:06:01 +08:00
|
|
|
q := s.ctx.EntClient.WorkOrder.Query()
|
|
|
|
|
if orderNo != "" {
|
|
|
|
|
q = q.Where(workorder.WorkOrderNoContains(orderNo))
|
|
|
|
|
}
|
|
|
|
|
if status != "" {
|
|
|
|
|
q = q.Where(workorder.Status(status))
|
|
|
|
|
}
|
2026-08-31 12:58:28 +08:00
|
|
|
if productCode != "" {
|
|
|
|
|
q = q.Where(workorder.ProductCodeContains(productCode))
|
|
|
|
|
}
|
|
|
|
|
if productName != "" {
|
|
|
|
|
q = q.Where(workorder.ProductNameContains(productName))
|
|
|
|
|
}
|
2026-08-28 15:06:01 +08:00
|
|
|
return q.Order(ent.Desc(workorder.FieldID)).All(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Service) GetWorkOrder(ctx context.Context, id int) (*ent.WorkOrder, error) {
|
|
|
|
|
if id > 0 {
|
|
|
|
|
return s.ctx.EntClient.WorkOrder.Get(ctx, id)
|
|
|
|
|
}
|
|
|
|
|
return s.ctx.EntClient.WorkOrder.Query().Order(ent.Desc(workorder.FieldID)).First(ctx)
|
2026-08-31 12:58:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 工单状态白名单
|
|
|
|
|
var workOrderStatuses = map[string]bool{
|
|
|
|
|
"CREATED": true, "RELEASED": true, "IN_PROGRESS": true, "DONE": true,
|
|
|
|
|
"PENDING": true, "CONFIRMED": true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetWorkOrderStatus 工单状态流转(发布/开工/完工等),操作列快捷操作。
|
|
|
|
|
func (s *Service) SetWorkOrderStatus(ctx context.Context, id int, status, operator string) error {
|
|
|
|
|
if id <= 0 {
|
|
|
|
|
return errors.New("缺少 id")
|
|
|
|
|
}
|
|
|
|
|
if !workOrderStatuses[status] {
|
|
|
|
|
return errors.New("非法的工单状态")
|
|
|
|
|
}
|
|
|
|
|
if err := s.ctx.EntClient.WorkOrder.UpdateOneID(id).SetStatus(status).Exec(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if wo, err := s.ctx.EntClient.WorkOrder.Get(ctx, id); err == nil {
|
|
|
|
|
s.ctx.EventLog.Write(ctx, "work.order.status", wo.WorkOrderNo, operator, "work_order", wo.WorkOrderNo, "变更工单状态", map[string]any{"status": status})
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeleteWorkOrder 删除工单。
|
|
|
|
|
// 安全条件:① 状态仅限 已创建/待排产/已发布(未开始生产);② 无日排产记录;
|
|
|
|
|
// ③ 未生成备料单;④ 无任何工件生产数据。
|
|
|
|
|
func (s *Service) DeleteWorkOrder(ctx context.Context, id int, operator string) error {
|
|
|
|
|
if id <= 0 {
|
|
|
|
|
return errors.New("缺少 id")
|
|
|
|
|
}
|
|
|
|
|
wo, err := s.ctx.EntClient.WorkOrder.Get(ctx, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.New("工单不存在")
|
|
|
|
|
}
|
|
|
|
|
if wo.Status != "CREATED" && wo.Status != "PENDING" && wo.Status != "RELEASED" {
|
|
|
|
|
return errors.New("仅「已创建 / 待排产 / 已发布」状态的工单可删除,生产中或已完成的工单不能删除")
|
|
|
|
|
}
|
|
|
|
|
if n, err := s.ctx.EntClient.DailyPlan.Query().Where(dailyplan.OrderNo(wo.WorkOrderNo)).Count(ctx); err == nil && n > 0 {
|
|
|
|
|
return errors.New("该工单已有日排产记录,请先删除对应排产后再删除工单")
|
|
|
|
|
}
|
|
|
|
|
if n, err := s.ctx.EntClient.MaterialRequest.Query().Where(materialrequest.OrderNo(wo.WorkOrderNo)).Count(ctx); err == nil && n > 0 {
|
|
|
|
|
return errors.New("该工单已生成备料单,不能删除")
|
|
|
|
|
}
|
|
|
|
|
if n, err := s.ctx.EntClient.Workpiece.Query().Where(workpiece.OrderNo(wo.WorkOrderNo)).Count(ctx); err == nil && n > 0 {
|
|
|
|
|
return errors.New("该工单已有工件生产数据,不能删除")
|
|
|
|
|
}
|
|
|
|
|
if err := s.ctx.EntClient.WorkOrder.DeleteOneID(id).Exec(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
s.ctx.EventLog.Write(ctx, "work.order.delete", wo.WorkOrderNo, operator, "work_order", wo.WorkOrderNo, "删除工单", map[string]any{"status": wo.Status})
|
|
|
|
|
return nil
|
2026-08-28 15:06:01 +08:00
|
|
|
}
|