Go workspace (go.work) with 5 microservices + shared library: - gateway (8080), auth-service (8081), purchaser-service (8082) - textile-service (8083), washing-service (8084) - shared: proto definitions, common packages Infrastructure: docker-compose for local dev, K8s manifests for K3s cluster deployment (mysql/redis/etcd + traefik ingress). Frontend (iloom-flatten) added as git submodule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
214 lines
5.8 KiB
Go
214 lines
5.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/muyuqingfeng/iloom/purchaser-service/internal/model"
|
|
"github.com/muyuqingfeng/iloom/purchaser-service/internal/repository"
|
|
"github.com/muyuqingfeng/iloom/shared/pkg/types"
|
|
)
|
|
|
|
type PlanService struct {
|
|
repo *repository.PlanRepo
|
|
db *sql.DB
|
|
}
|
|
|
|
func NewPlanService(repo *repository.PlanRepo, db *sql.DB) *PlanService {
|
|
return &PlanService{repo: repo, db: db}
|
|
}
|
|
|
|
type CreatePlanRequest 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"`
|
|
Remark string `json:"remark"`
|
|
YarnRatios []CreateYarnRatio `json:"yarn_ratios"`
|
|
}
|
|
|
|
type CreateYarnRatio struct {
|
|
YarnName string `json:"yarn_name"`
|
|
YarnType string `json:"yarn_type"`
|
|
Ratio float64 `json:"ratio"`
|
|
AmountPerMeter float64 `json:"amount_per_meter"`
|
|
TotalAmount float64 `json:"total_amount"`
|
|
}
|
|
|
|
type UpdatePlanRequest 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"`
|
|
}
|
|
|
|
func (s *PlanService) List(ctx context.Context, companyID string, page types.Pagination, status string) ([]model.ProductionPlan, int, error) {
|
|
page.Normalize()
|
|
return s.repo.List(ctx, companyID, page, status)
|
|
}
|
|
|
|
func (s *PlanService) Create(ctx context.Context, companyID, userID string, req CreatePlanRequest) (*model.ProductionPlan, error) {
|
|
now := time.Now()
|
|
planCode := fmt.Sprintf("PP%s%03d", now.Format("20060102"), now.UnixMilli()%1000)
|
|
|
|
var remarkPtr *string
|
|
if req.Remark != "" {
|
|
remarkPtr = &req.Remark
|
|
}
|
|
|
|
plan := &model.ProductionPlan{
|
|
ID: uuid.New().String(),
|
|
PlanCode: planCode,
|
|
PurchaserID: companyID,
|
|
ProductName: req.ProductName,
|
|
FabricCode: req.FabricCode,
|
|
Color: req.Color,
|
|
ColorCode: req.ColorCode,
|
|
TargetQuantity: req.TargetQuantity,
|
|
CompletedQuantity: 0,
|
|
ProductionPrice: req.ProductionPrice,
|
|
YarnUsagePerMeter: req.YarnUsagePerMeter,
|
|
Status: string(types.PlanPending),
|
|
Remark: remarkPtr,
|
|
CreatedBy: &userID,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
var yarnRatios []model.YarnRatio
|
|
for _, yr := range req.YarnRatios {
|
|
var yarnTypePtr *string
|
|
if yr.YarnType != "" {
|
|
yarnTypePtr = &yr.YarnType
|
|
}
|
|
yarnRatios = append(yarnRatios, model.YarnRatio{
|
|
ID: uuid.New().String(),
|
|
PlanID: plan.ID,
|
|
YarnName: yr.YarnName,
|
|
YarnType: yarnTypePtr,
|
|
Ratio: yr.Ratio,
|
|
AmountPerMeter: yr.AmountPerMeter,
|
|
TotalAmount: yr.TotalAmount,
|
|
CompanyID: &companyID,
|
|
CreatedAt: now,
|
|
})
|
|
}
|
|
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("begin tx: %w", err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
if err := s.repo.Create(ctx, tx, plan); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(yarnRatios) > 0 {
|
|
if err := s.repo.CreateYarnRatios(ctx, tx, yarnRatios); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, fmt.Errorf("commit tx: %w", err)
|
|
}
|
|
|
|
plan.YarnRatios = yarnRatios
|
|
return plan, nil
|
|
}
|
|
|
|
func (s *PlanService) GetByID(ctx context.Context, id, companyID string) (*model.ProductionPlan, error) {
|
|
plan, err := s.repo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plan == nil {
|
|
return nil, fmt.Errorf("plan not found")
|
|
}
|
|
if plan.PurchaserID != companyID {
|
|
return nil, fmt.Errorf("plan not found")
|
|
}
|
|
|
|
steps, err := s.repo.GetSteps(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
plan.ProcessSteps = steps
|
|
|
|
inventory, err := s.repo.GetInventory(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
plan.InventoryRecords = inventory
|
|
|
|
return plan, nil
|
|
}
|
|
|
|
func (s *PlanService) Update(ctx context.Context, id, companyID string, req UpdatePlanRequest) error {
|
|
plan, err := s.repo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if plan == nil || plan.PurchaserID != companyID {
|
|
return fmt.Errorf("plan not found")
|
|
}
|
|
|
|
if req.ProductName != "" {
|
|
plan.ProductName = req.ProductName
|
|
}
|
|
if req.FabricCode != "" {
|
|
plan.FabricCode = req.FabricCode
|
|
}
|
|
if req.Color != "" {
|
|
plan.Color = req.Color
|
|
}
|
|
if req.ColorCode != "" {
|
|
plan.ColorCode = req.ColorCode
|
|
}
|
|
if req.TargetQuantity > 0 {
|
|
plan.TargetQuantity = req.TargetQuantity
|
|
}
|
|
if req.ProductionPrice != nil {
|
|
plan.ProductionPrice = req.ProductionPrice
|
|
}
|
|
if req.YarnUsagePerMeter != nil {
|
|
plan.YarnUsagePerMeter = req.YarnUsagePerMeter
|
|
}
|
|
if req.Status != "" {
|
|
plan.Status = req.Status
|
|
}
|
|
if req.Remark != "" {
|
|
plan.Remark = &req.Remark
|
|
}
|
|
|
|
return s.repo.Update(ctx, plan)
|
|
}
|
|
|
|
func (s *PlanService) Delete(ctx context.Context, id, companyID string) error {
|
|
return s.repo.Delete(ctx, id, companyID)
|
|
}
|
|
|
|
func (s *PlanService) AssignFactory(ctx context.Context, planID, factoryID, factoryType string) error {
|
|
return s.repo.AssignFactory(ctx, planID, factoryID, factoryType)
|
|
}
|
|
|
|
func (s *PlanService) GetSteps(ctx context.Context, planID string) ([]model.ProcessStep, error) {
|
|
return s.repo.GetSteps(ctx, planID)
|
|
}
|
|
|
|
func (s *PlanService) GetInventory(ctx context.Context, planID string) ([]model.InventoryRecord, error) {
|
|
return s.repo.GetInventory(ctx, planID)
|
|
}
|