package service import ( "database/sql" "encoding/json" "fmt" "strings" "time" "unicode/utf8" "cmautobuy/admin/model" "cmautobuy/admin/repository" ) const maxShopeeSKUFieldRunes = 500 // UpdateShopeeSKUManual 保存采购员对现有蝦皮规格的人工修正。 // 只修改颜色、尺码和建议说明;规格原文、身份字段和既有采购数据保持不变。 func UpdateShopeeSKUManual(db *sql.DB, actor *model.User, goodsID, skuRecordID, color, size, advice string, now time.Time) error { if actor == nil || actor.Status != model.UserActive { return ErrUnauthenticated } goodsID = strings.TrimSpace(goodsID) skuRecordID = strings.TrimSpace(skuRecordID) color = strings.TrimSpace(color) size = strings.TrimSpace(size) advice = strings.TrimSpace(advice) if goodsID == "" { return invalidFieldInput("shopee_goods_id", "蝦皮商品编号不能为空") } if skuRecordID == "" { return invalidFieldInput("sku_record_id", "蝦皮规格记录编号不能为空") } if color == "" { return invalidFieldInput("color", "颜色不能为空") } if size == "" { return invalidFieldInput("size", "尺码不能为空") } for _, field := range []struct { name, label, value string }{{"color", "颜色", color}, {"size", "尺码", size}, {"advice", "建议说明", advice}} { if utf8.RuneCountInString(field.value) > maxShopeeSKUFieldRunes { return invalidFieldInput(field.name, "%s最多 500 个字符", field.label) } } tx, err := db.Begin() if err != nil { return err } defer tx.Rollback() state, err := repository.GetShopeeSKUManualState(tx, skuRecordID) if err != nil { return err } if state == nil || state.GoodsID != goodsID { return invalidFieldInput("sku_record_id", "蝦皮规格不存在或不属于当前商品,请刷新页面后重试") } sources, err := decodeShopeeSKUProvenance(state.FieldSources) if err != nil { return fmt.Errorf("读取蝦皮规格字段来源失败: %w", err) } observedTimes, err := decodeShopeeSKUProvenance(state.FieldObservedAt) if err != nil { return fmt.Errorf("读取蝦皮规格字段时间失败: %w", err) } at := now.UTC().Format(model.TimeLayout) for _, field := range []string{"color", "size", "advice"} { sources[field] = "manual" observedTimes[field] = at } sourcesJSON, err := json.Marshal(sources) if err != nil { return err } timesJSON, err := json.Marshal(observedTimes) if err != nil { return err } updated, err := repository.UpdateShopeeSKUManualFields(tx, skuRecordID, goodsID, color, size, advice, string(sourcesJSON), string(timesJSON), at) if err != nil { return err } if !updated { return invalidFieldInput("sku_record_id", "蝦皮规格已发生变化,请刷新页面后重试") } return tx.Commit() } func decodeShopeeSKUProvenance(raw string) (map[string]string, error) { values := map[string]string{} if strings.TrimSpace(raw) == "" { return values, nil } if err := json.Unmarshal([]byte(raw), &values); err != nil { return nil, err } return values, nil }