package repository import ( "context" "database/sql" "fmt" "github.com/muyuqingfeng/iloom/washing-service/internal/model" ) type PlanRepo struct { db *sql.DB } func NewPlanRepo(db *sql.DB) *PlanRepo { return &PlanRepo{db: db} } func (r *PlanRepo) List(ctx context.Context, companyID string) ([]model.WashingPlan, error) { query := `SELECT wp.id, wp.plan_code, wp.company_id, wp.product_id, wp.washing_factory_id, wp.planned_meters, wp.washing_price, wp.estimated_shrinkage_rate, wp.estimated_washed_meters, wp.estimated_total_cost, wp.actual_shrinkage_rate, wp.actual_washed_meters, wp.actual_total_cost, wp.washing_date, wp.status, wp.notes, wp.created_by, wp.created_at, wp.updated_at, COALESCE(p.product_name, '') AS product_name FROM ilm_washing_plan wp LEFT JOIN ilm_product p ON wp.product_id = p.id WHERE wp.washing_factory_id = ? ORDER BY wp.created_at DESC` rows, err := r.db.QueryContext(ctx, query, companyID) if err != nil { return nil, fmt.Errorf("list washing plans: %w", err) } defer rows.Close() return scanPlans(rows) } func (r *PlanRepo) GetByID(ctx context.Context, id string) (*model.WashingPlan, error) { query := `SELECT wp.id, wp.plan_code, wp.company_id, wp.product_id, wp.washing_factory_id, wp.planned_meters, wp.washing_price, wp.estimated_shrinkage_rate, wp.estimated_washed_meters, wp.estimated_total_cost, wp.actual_shrinkage_rate, wp.actual_washed_meters, wp.actual_total_cost, wp.washing_date, wp.status, wp.notes, wp.created_by, wp.created_at, wp.updated_at, COALESCE(p.product_name, '') AS product_name FROM ilm_washing_plan wp LEFT JOIN ilm_product p ON wp.product_id = p.id WHERE wp.id = ?` plan := &model.WashingPlan{} err := r.db.QueryRowContext(ctx, query, id).Scan( &plan.ID, &plan.PlanCode, &plan.CompanyID, &plan.ProductID, &plan.WashingFactoryID, &plan.PlannedMeters, &plan.WashingPrice, &plan.EstimatedShrinkageRate, &plan.EstimatedWashedMeters, &plan.EstimatedTotalCost, &plan.ActualShrinkageRate, &plan.ActualWashedMeters, &plan.ActualTotalCost, &plan.WashingDate, &plan.Status, &plan.Notes, &plan.CreatedBy, &plan.CreatedAt, &plan.UpdatedAt, &plan.ProductName, ) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, fmt.Errorf("get washing plan by id: %w", err) } return plan, nil } func (r *PlanRepo) UpdateStatus(ctx context.Context, id, status string) error { query := `UPDATE ilm_washing_plan SET status = ?, updated_at = NOW() WHERE id = ?` result, err := r.db.ExecContext(ctx, query, status, id) if err != nil { return fmt.Errorf("update plan status: %w", err) } affected, err := result.RowsAffected() if err != nil { return fmt.Errorf("rows affected: %w", err) } if affected == 0 { return fmt.Errorf("washing plan not found") } return nil } func (r *PlanRepo) UpdateActuals(ctx context.Context, tx *sql.Tx, id string, shrinkage, meters, cost float64) error { query := `UPDATE ilm_washing_plan SET actual_shrinkage_rate = ?, actual_washed_meters = ?, actual_total_cost = ?, status = 'completed', washing_date = NOW(), updated_at = NOW() WHERE id = ?` _, err := tx.ExecContext(ctx, query, shrinkage, meters, cost, id) if err != nil { return fmt.Errorf("update plan actuals: %w", err) } return nil } func (r *PlanRepo) Pending(ctx context.Context, companyID string) ([]model.WashingPlan, error) { query := `SELECT wp.id, wp.plan_code, wp.company_id, wp.product_id, wp.washing_factory_id, wp.planned_meters, wp.washing_price, wp.estimated_shrinkage_rate, wp.estimated_washed_meters, wp.estimated_total_cost, wp.actual_shrinkage_rate, wp.actual_washed_meters, wp.actual_total_cost, wp.washing_date, wp.status, wp.notes, wp.created_by, wp.created_at, wp.updated_at, COALESCE(p.product_name, '') AS product_name FROM ilm_washing_plan wp LEFT JOIN ilm_product p ON wp.product_id = p.id WHERE wp.washing_factory_id = ? AND wp.status IN ('pending', 'washing') ORDER BY wp.created_at DESC` rows, err := r.db.QueryContext(ctx, query, companyID) if err != nil { return nil, fmt.Errorf("list pending plans: %w", err) } defer rows.Close() return scanPlans(rows) } func (r *PlanRepo) Completed(ctx context.Context, companyID string) ([]model.WashingPlan, error) { query := `SELECT wp.id, wp.plan_code, wp.company_id, wp.product_id, wp.washing_factory_id, wp.planned_meters, wp.washing_price, wp.estimated_shrinkage_rate, wp.estimated_washed_meters, wp.estimated_total_cost, wp.actual_shrinkage_rate, wp.actual_washed_meters, wp.actual_total_cost, wp.washing_date, wp.status, wp.notes, wp.created_by, wp.created_at, wp.updated_at, COALESCE(p.product_name, '') AS product_name FROM ilm_washing_plan wp LEFT JOIN ilm_product p ON wp.product_id = p.id WHERE wp.washing_factory_id = ? AND wp.status = 'completed' ORDER BY wp.washing_date DESC` rows, err := r.db.QueryContext(ctx, query, companyID) if err != nil { return nil, fmt.Errorf("list completed plans: %w", err) } defer rows.Close() return scanPlans(rows) } func (r *PlanRepo) CountByStatus(ctx context.Context, companyID string) (total, pending, completed int, err error) { query := `SELECT COUNT(*), SUM(CASE WHEN status IN ('pending', 'washing') THEN 1 ELSE 0 END), SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) FROM ilm_washing_plan WHERE washing_factory_id = ?` err = r.db.QueryRowContext(ctx, query, companyID).Scan(&total, &pending, &completed) if err != nil { return 0, 0, 0, fmt.Errorf("count by status: %w", err) } return } func scanPlans(rows *sql.Rows) ([]model.WashingPlan, error) { var plans []model.WashingPlan for rows.Next() { var plan model.WashingPlan if err := rows.Scan( &plan.ID, &plan.PlanCode, &plan.CompanyID, &plan.ProductID, &plan.WashingFactoryID, &plan.PlannedMeters, &plan.WashingPrice, &plan.EstimatedShrinkageRate, &plan.EstimatedWashedMeters, &plan.EstimatedTotalCost, &plan.ActualShrinkageRate, &plan.ActualWashedMeters, &plan.ActualTotalCost, &plan.WashingDate, &plan.Status, &plan.Notes, &plan.CreatedBy, &plan.CreatedAt, &plan.UpdatedAt, &plan.ProductName, ); err != nil { return nil, fmt.Errorf("scan washing plan: %w", err) } plans = append(plans, plan) } return plans, nil }