package srv_videoinfo import ( "bytes" "encoding/json" "fmt" "strconv" "strings" "time" ) // UnmarshalJSON 兼容 id 使用数字字符串,以及 param 里的表单写法。 func (r *VideoInfoUpdateReq) UnmarshalJSON(data []byte) error { data = bytes.TrimSpace(data) if len(data) == 0 || bytes.Equal(data, []byte("null")) { return nil } var raw map[string]json.RawMessage if err := json.Unmarshal(data, &raw); err != nil { return err } if value, ok := raw["id"]; ok { next, keep, err := normalizeIntRaw(value) if err != nil { return fmt.Errorf("id %w", err) } if keep && next != nil && !bytes.Equal(bytes.TrimSpace(next), []byte("null")) { if err := json.Unmarshal(next, &r.Id); err != nil { return err } } } if value, ok := raw["param"]; ok && !bytes.Equal(bytes.TrimSpace(value), []byte("null")) { if err := json.Unmarshal(value, &r.Param); err != nil { return err } } return nil } // UnmarshalJSON 兼容后台表单里的空字符串、数字字符串,以及分类排序的多种写法。 func (v *VideoInfoUpdate) UnmarshalJSON(data []byte) error { data = bytes.TrimSpace(data) if len(data) == 0 || bytes.Equal(data, []byte("null")) { return nil } normalized, err := normalizeVideoUpdateJSON(data) if err != nil { return err } type alias VideoInfoUpdate var out alias if err := json.Unmarshal(normalized, &out); err != nil { return err } *v = VideoInfoUpdate(out) return nil } func normalizeVideoUpdateJSON(data []byte) ([]byte, error) { var raw map[string]json.RawMessage if err := json.Unmarshal(data, &raw); err != nil { return nil, err } for _, key := range []string{"addedTime", "updatedAt", "issuedAt", "secondUpdatedAt"} { value, ok := raw[key] if !ok { continue } next, err := normalizeTimeRaw(value) if err != nil { return nil, err } if next == nil { delete(raw, key) continue } raw[key] = next } for _, key := range []string{"categoryId", "sort", "creatorId", "height", "width", "videoTime", "indexNow", "mediaType", "status"} { value, ok := raw[key] if !ok { continue } next, keep, err := normalizeIntRaw(value) if err != nil { return nil, fmt.Errorf("%s %w", key, err) } if !keep { continue } if next == nil { delete(raw, key) continue } raw[key] = next } for _, key := range []string{"actressIds", "tags"} { value, ok := raw[key] if !ok { continue } next, keep, err := normalizeIntSliceRaw(value) if err != nil { return nil, fmt.Errorf("%s %w", key, err) } if !keep { continue } if next == nil { delete(raw, key) continue } raw[key] = next } if value, ok := raw["categoryRanks"]; ok { next, keep, err := normalizeCategoryRanksRaw(value, raw["categoryId"]) if err != nil { return nil, err } if !keep { delete(raw, "categoryRanks") } else { raw["categoryRanks"] = next } } return json.Marshal(raw) } func normalizeTimeRaw(raw json.RawMessage) (json.RawMessage, error) { raw = bytes.TrimSpace(raw) if len(raw) == 0 || bytes.Equal(raw, []byte("null")) { return raw, nil } var text string if err := json.Unmarshal(raw, &text); err != nil { return nil, fmt.Errorf("时间格式错误") } text = strings.TrimSpace(text) if text == "" { return nil, nil } parsed, err := parseFlexTime(text) if err != nil { return nil, err } return json.Marshal(parsed) } func parseFlexTime(text string) (time.Time, error) { if parsed, err := time.Parse(time.RFC3339Nano, text); err == nil { return parsed, nil } if parsed, err := time.Parse(time.RFC3339, text); err == nil { return parsed, nil } for _, layout := range []string{"2006-01-02 15:04:05", "2006-01-02T15:04:05", "2006-01-02"} { if parsed, err := time.ParseInLocation(layout, text, time.Local); err == nil { return parsed, nil } } return time.Time{}, fmt.Errorf("时间格式错误: %s", text) } // normalizeIntRaw 把数字或数字字符串整理成 JSON 数字。空字符串表示删除该字段。 func normalizeIntRaw(raw json.RawMessage) (json.RawMessage, bool, error) { raw = bytes.TrimSpace(raw) if len(raw) == 0 { return nil, false, nil } if bytes.Equal(raw, []byte("null")) { return raw, true, nil } if raw[0] != '"' { var number json.Number if err := json.Unmarshal(raw, &number); err != nil { return nil, false, fmt.Errorf("数字格式错误") } if _, err := number.Int64(); err != nil { return nil, false, fmt.Errorf("数字格式错误") } return raw, true, nil } var text string if err := json.Unmarshal(raw, &text); err != nil { return nil, false, fmt.Errorf("数字格式错误") } text = strings.TrimSpace(text) if text == "" { return nil, true, nil } value, err := strconv.ParseInt(text, 10, 64) if err != nil { return nil, false, fmt.Errorf("数字格式错误: %s", text) } encoded, err := json.Marshal(value) return encoded, true, err } func normalizeIntSliceRaw(raw json.RawMessage) (json.RawMessage, bool, error) { raw = bytes.TrimSpace(raw) if len(raw) == 0 || bytes.Equal(raw, []byte("null")) { return raw, true, nil } if bytes.Equal(raw, []byte(`""`)) { return nil, true, nil } if raw[0] != '[' { return nil, false, fmt.Errorf("数组格式错误") } var items []json.RawMessage if err := json.Unmarshal(raw, &items); err != nil { return nil, false, err } values := make([]int64, 0, len(items)) for _, item := range items { next, keep, err := normalizeIntRaw(item) if err != nil { return nil, false, err } if !keep || next == nil || bytes.Equal(bytes.TrimSpace(next), []byte("null")) { return nil, false, fmt.Errorf("数字格式错误") } var value int64 if err := json.Unmarshal(next, &value); err != nil { return nil, false, err } values = append(values, value) } encoded, err := json.Marshal(values) return encoded, true, err } func normalizeCategoryRanksRaw(raw, categoryIDRaw json.RawMessage) (json.RawMessage, bool, error) { raw = bytes.TrimSpace(raw) if len(raw) == 0 || bytes.Equal(raw, []byte("null")) || bytes.Equal(raw, []byte(`""`)) { return nil, false, nil } fallbackID := int64FromRaw(categoryIDRaw) if raw[0] != '[' { item, ok, err := normalizeCategoryRankItem(raw, fallbackID) if err != nil { return nil, false, err } if !ok { return nil, false, nil } encoded, err := json.Marshal([]categoryRankJSON{item}) return encoded, true, err } var items []json.RawMessage if err := json.Unmarshal(raw, &items); err != nil { return nil, false, fmt.Errorf("分类排序格式错误") } if len(items) == 0 { encoded, err := json.Marshal([]categoryRankJSON{}) return encoded, true, err } cleaned := make([]categoryRankJSON, 0, len(items)) for _, item := range items { rank, ok, err := normalizeCategoryRankItem(item, fallbackID) if err != nil { return nil, false, err } if !ok { continue } cleaned = append(cleaned, rank) } if len(cleaned) == 0 { return nil, false, nil } encoded, err := json.Marshal(cleaned) return encoded, true, err } type categoryRankJSON struct { Id int64 `json:"id"` Rank int64 `json:"rank"` } func normalizeCategoryRankItem(raw json.RawMessage, fallbackID int64) (categoryRankJSON, bool, error) { raw = bytes.TrimSpace(raw) if len(raw) == 0 || bytes.Equal(raw, []byte("null")) { return categoryRankJSON{}, false, nil } if raw[0] != '{' { if bytes.Equal(raw, []byte("null")) || bytes.Equal(raw, []byte(`""`)) || rawIsZero(raw) { return categoryRankJSON{}, false, nil } rank := int64FromRaw(raw) if rank == 0 { return categoryRankJSON{}, false, fmt.Errorf("分类排序格式错误") } if fallbackID <= 0 { return categoryRankJSON{}, false, fmt.Errorf("分类排序缺少分类ID") } return categoryRankJSON{Id: fallbackID, Rank: rank}, true, nil } var fields map[string]json.RawMessage if err := json.Unmarshal(raw, &fields); err != nil { return categoryRankJSON{}, false, fmt.Errorf("分类排序格式错误") } id := int64FromRaw(fields["id"]) if id <= 0 { id = int64FromRaw(fields["categoryId"]) } rank := int64FromRaw(fields["rank"]) if id <= 0 && !rawHasValue(fields["rank"]) { return categoryRankJSON{}, false, nil } if id <= 0 { id = fallbackID } if id <= 0 { return categoryRankJSON{}, false, fmt.Errorf("分类排序缺少分类ID") } return categoryRankJSON{Id: id, Rank: rank}, true, nil } func rawHasValue(raw json.RawMessage) bool { raw = bytes.TrimSpace(raw) return len(raw) > 0 && !bytes.Equal(raw, []byte("null")) && !bytes.Equal(raw, []byte(`""`)) } func int64FromRaw(raw json.RawMessage) int64 { next, keep, err := normalizeIntRaw(raw) if err != nil || !keep || next == nil || bytes.Equal(bytes.TrimSpace(next), []byte("null")) { return 0 } var value int64 if err := json.Unmarshal(next, &value); err != nil { return 0 } return value } func rawIsZero(raw json.RawMessage) bool { next, keep, err := normalizeIntRaw(raw) return err == nil && keep && bytes.Equal(bytes.TrimSpace(next), []byte("0")) }