* feat: support product yarn batches * feat: improve product yarn ratio workflow * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
141 lines
3.7 KiB
Go
141 lines
3.7 KiB
Go
package logic
|
||
|
||
import (
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
"math"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"muyu-apiserver/model"
|
||
"muyu-apiserver/rpc/inventory/pb"
|
||
)
|
||
|
||
func nullString(v string) sql.NullString {
|
||
return sql.NullString{String: v, Valid: v != ""}
|
||
}
|
||
|
||
func nullableJSON(v string) sql.NullString {
|
||
v = strings.TrimSpace(v)
|
||
return sql.NullString{String: v, Valid: v != ""}
|
||
}
|
||
|
||
func parseDecimalString(v string) float64 {
|
||
n, _ := strconv.ParseFloat(strings.TrimSpace(v), 64)
|
||
return n
|
||
}
|
||
|
||
type yarnRatioPayload struct {
|
||
Warp []yarnRatioItem `json:"warp"`
|
||
Weft []yarnRatioItem `json:"weft"`
|
||
}
|
||
|
||
type yarnRatioItem struct {
|
||
YarnID string `json:"yarn_id"`
|
||
YarnId string `json:"yarnId"`
|
||
Ratio float64 `json:"ratio"`
|
||
}
|
||
|
||
func (i yarnRatioItem) yarnID() string {
|
||
if i.YarnID != "" {
|
||
return i.YarnID
|
||
}
|
||
return i.YarnId
|
||
}
|
||
|
||
func validateProductBatchInput(batch *pb.ProductBatchInput) error {
|
||
if batch == nil {
|
||
return nil
|
||
}
|
||
return validateYarnRatio(batch.YarnRatio)
|
||
}
|
||
|
||
func validateYarnRatio(raw string) error {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return nil
|
||
}
|
||
var payload yarnRatioPayload
|
||
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
|
||
return fmt.Errorf("纱线配比格式无效")
|
||
}
|
||
if err := validateYarnRatioSide("经线", payload.Warp); err != nil {
|
||
return err
|
||
}
|
||
return validateYarnRatioSide("纬线", payload.Weft)
|
||
}
|
||
|
||
func validateYarnRatioSide(label string, items []yarnRatioItem) error {
|
||
if len(items) == 0 {
|
||
return nil
|
||
}
|
||
total := 0
|
||
for _, item := range items {
|
||
if strings.TrimSpace(item.yarnID()) == "" {
|
||
return fmt.Errorf("%s纱线不能为空", label)
|
||
}
|
||
if item.Ratio < 1 || item.Ratio > 10 || math.Trunc(item.Ratio) != item.Ratio {
|
||
return fmt.Errorf("%s使用配比必须为 1-10 的整数", label)
|
||
}
|
||
total += int(item.Ratio)
|
||
}
|
||
if total != 10 {
|
||
return fmt.Errorf("%s配比合计必须为 10,当前为 %d", label, total)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func batchInputToModelFields(batch *pb.ProductBatchInput, target *model.InvProductBatch) error {
|
||
if batch == nil {
|
||
target.ProductionProcess = nullString(defaultProcess)
|
||
return nil
|
||
}
|
||
if err := validateProductBatchInput(batch); err != nil {
|
||
return err
|
||
}
|
||
target.YarnRatio = nullableJSON(batch.YarnRatio)
|
||
target.WarpWeightGM = parseDecimalString(batch.WarpWeightGM)
|
||
target.WeftWeightGM = parseDecimalString(batch.WeftWeightGM)
|
||
process := strings.TrimSpace(batch.ProductionProcess)
|
||
if process == "" {
|
||
process = defaultProcess
|
||
}
|
||
target.ProductionProcess = nullString(process)
|
||
target.Remark = nullString(batch.Remark)
|
||
return nil
|
||
}
|
||
|
||
func batchToPb(batch *model.InvProductBatch) *pb.ProductBatchInfo {
|
||
return &pb.ProductBatchInfo{
|
||
BatchId: batch.BatchId,
|
||
ProductId: batch.ProductId,
|
||
BatchNo: batch.BatchNo,
|
||
YarnRatio: batch.YarnRatio.String,
|
||
WarpWeightGM: fmt.Sprintf("%.4f", batch.WarpWeightGM),
|
||
WeftWeightGM: fmt.Sprintf("%.4f", batch.WeftWeightGM),
|
||
ProductionProcess: batch.ProductionProcess.String,
|
||
Remark: batch.Remark.String,
|
||
Status: batch.Status,
|
||
CreatedAt: batch.CreatedAt.Format("2006-01-02 15:04:05"),
|
||
UpdatedAt: batch.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||
}
|
||
}
|
||
|
||
func productToPb(product *model.InvProduct) *pb.ProductInfo {
|
||
return &pb.ProductInfo{
|
||
ProductId: product.ProductId,
|
||
ProductName: product.ProductName,
|
||
ImageUrl: product.ImageUrl,
|
||
Spec: product.Spec,
|
||
Color: product.Color,
|
||
SalesPrice: fmt.Sprintf("%.2f", product.SalesPrice),
|
||
Remark: product.Remark.String,
|
||
Status: product.Status,
|
||
CreatedAt: product.CreatedAt.Format("2006-01-02 15:04:05"),
|
||
UpdatedAt: product.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||
ColorNo: product.ColorNo,
|
||
ProductCode: product.ProductCode,
|
||
}
|
||
}
|