287 lines
6.5 KiB
Go
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
|
|
}
|
|
|