141 lines
3.7 KiB
Go
Raw Normal View History

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,
}
}