Files
2026-10-26 16:28:14 +08:00

342 lines
8.8 KiB
Go

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"))
}