Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

287 lines
6.5 KiB
Go

package officialwebsiteser
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
"time"
officialwebsitemod "91porn-server/models/v/officialWebsitemod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
// ─────────────────────────────────────────
// List Helper Types and Functions
// ─────────────────────────────────────────
// CountResp 通用操作返回
type CountResp struct {
Count int64 `json:"count"`
}
// PageReq 分页请求
type PageReq struct {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
func (r *PageReq) FindOptions() *options.FindOptions {
page := r.Page
if page <= 0 {
page = 1
}
pageSize := r.PageSize
if pageSize <= 0 {
pageSize = 20
}
skip := (page - 1) * pageSize
return options.Find().SetSkip(skip).SetLimit(pageSize).SetSort(officialwebsitemod.M{"createdAt": -1})
}
func parseObjectIDFilter(filter officialwebsitemod.M, key string, raw *string) error {
if raw == nil || *raw == "" {
return nil
}
id, err := primitive.ObjectIDFromHex(*raw)
if err != nil {
return fmt.Errorf("invalid %s: %w", key, err)
}
filter[key] = id
return nil
}
func parseJSONFilter(filter officialwebsitemod.M, key string, raw *string, out interface{}) error {
if raw == nil || *raw == "" {
return nil
}
if err := json.Unmarshal([]byte(*raw), out); err != nil {
return fmt.Errorf("invalid %s json: %w", key, err)
}
v := reflect.ValueOf(out)
if v.Kind() == reflect.Ptr {
filter[key] = v.Elem().Interface()
return nil
}
filter[key] = out
return nil
}
// ────────────────────────────────────────
// Update Helper Functions
// ─────────────────────────────────────────
func modelToMap(doc interface{}) (officialwebsitemod.M, error) {
raw, err := bson.Marshal(doc)
if err != nil {
return nil, err
}
data := officialwebsitemod.M{}
if err := bson.Unmarshal(raw, &data); err != nil {
return nil, err
}
delete(data, "_id")
delete(data, "createdAt")
delete(data, "updatedAt")
delete(data, "deletedAt")
return data, nil
}
func modelChanged(oldDoc interface{}, newDoc interface{}) (bool, error) {
oldMap, err := modelToMap(oldDoc)
if err != nil {
return false, err
}
newMap, err := modelToMap(newDoc)
if err != nil {
return false, err
}
return !reflect.DeepEqual(oldMap, newMap), nil
}
func buildModelUpdateDoc(doc interface{}) (officialwebsitemod.M, error) {
data, err := modelToMap(doc)
if err != nil {
return nil, err
}
data["updatedAt"] = time.Now().UTC()
return officialwebsitemod.M{"$set": data}, nil
}
func buildPointerUpdateDoc(oldDoc interface{}, updateDoc interface{}) (officialwebsitemod.M, error) {
oldVal, err := structValueOf(oldDoc, "old doc")
if err != nil {
return nil, err
}
newVal, err := structValueOf(updateDoc, "update doc")
if err != nil {
return nil, err
}
set := officialwebsitemod.M{}
collectPointerChanges(set, oldVal, newVal)
if len(set) == 0 {
return nil, nil
}
set["updatedAt"] = time.Now().UTC()
return officialwebsitemod.M{"$set": set}, nil
}
func structValueOf(doc interface{}, label string) (reflect.Value, error) {
val := reflect.ValueOf(doc)
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return reflect.Value{}, errors.New(label + " is nil")
}
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return reflect.Value{}, errors.New(label + " must be struct")
}
return val, nil
}
func collectPointerChanges(set officialwebsitemod.M, oldVal reflect.Value, newVal reflect.Value) {
newType := newVal.Type()
for i := 0; i < newVal.NumField(); i++ {
addPointerFieldChange(set, oldVal, newVal.Field(i), newType.Field(i))
}
}
func addPointerFieldChange(set officialwebsitemod.M, oldVal reflect.Value, newField reflect.Value, fieldInfo reflect.StructField) {
if fieldInfo.Name == "ID" {
return
}
if !newField.IsValid() || newField.Kind() != reflect.Ptr || newField.IsNil() {
return
}
oldField := findStructFieldByName(oldVal, fieldInfo.Name)
if !oldField.IsValid() {
return
}
newValue := derefValue(newField)
oldValue := derefValue(oldField)
if !newValue.IsValid() || !oldValue.IsValid() {
return
}
if reflect.DeepEqual(oldValue.Interface(), newValue.Interface()) {
return
}
key := bsonFieldName(fieldInfo)
if key == "" {
return
}
set[key] = newValue.Interface()
}
func findStructFieldByName(val reflect.Value, name string) reflect.Value {
if !val.IsValid() {
return reflect.Value{}
}
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return reflect.Value{}
}
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return reflect.Value{}
}
if field := val.FieldByName(name); field.IsValid() {
return field
}
valType := val.Type()
for i := 0; i < val.NumField(); i++ {
fieldType := valType.Field(i)
if !fieldType.Anonymous {
continue
}
field := findStructFieldByName(val.Field(i), name)
if field.IsValid() {
return field
}
}
return reflect.Value{}
}
func derefValue(v reflect.Value) reflect.Value {
for v.IsValid() && v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{}
}
v = v.Elem()
}
return v
}
func bsonFieldName(field reflect.StructField) string {
if tag := field.Tag.Get("bson"); tag != "" {
name := strings.Split(tag, ",")[0]
if name != "" && name != "-" {
return name
}
}
if tag := field.Tag.Get("json"); tag != "" {
name := strings.Split(tag, ",")[0]
if name != "" && name != "-" {
return name
}
}
return field.Name
}
// ─────────────────────────────────────────
// Pointer helpers
// ─────────────────────────────────────────
func stringOrZero(v *string) string {
if v == nil {
return ""
}
return *v
}
func intOrZero(v *int) int {
if v == nil {
return 0
}
return *v
}
func objectIDOrZero(v *primitive.ObjectID) primitive.ObjectID {
if v == nil {
return primitive.NilObjectID
}
return *v
}
func int64OrZero(v *int64) int64 {
if v == nil {
return 0
}
return *v
}
func boolOrZero(v *bool) bool {
if v == nil {
return false
}
return *v
}
func isNilObjectID(id primitive.ObjectID) bool {
return id == primitive.NilObjectID
}