| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 | package iiimport (	"fmt"	"reflect"	"strings"	"golib/features/mo")var (	errUnknownFiled = func(name Name, key string) error {		return fmt.Errorf("unknown filed: %s.%s", name, key)	})// ItemInfo XML 配置, 每个 XML 应当包含 _id 字段type ItemInfo struct {	Name   Name        `xml:"Name,attr"`	Label  string      `xml:"Label,attr"`	Fields []FieldInfo `xml:"Fields>Field"`	FieldMap    map[string]int	RequiredMap map[string]int // 必填	UniqueMap   map[string]int // 需要调用 SetUnique 设置唯一键}func (c *ItemInfo) ForkName(name string) string {	return c.Name.Database() + "." + name}// Open 使用 Name 包含的数据库和表然后打开一个操作func (c *ItemInfo) Open(client *mo.Client) *mo.Shortcut {	return mo.NewShortcut(client.Database(c.Name.Database()).Collection(c.Name.Collection()))}// PrepareNew 创一个列表, 包含所有 Fields 的 name 和默认值func (c *ItemInfo) PrepareNew() mo.D {	f := make(mo.D, 0, len(c.Fields))	for _, field := range c.Fields {		if field.Name == mo.ID.Key() {			if !field.Required {				continue			}		}		f = append(f, mo.E{Key: field.Name, Value: field.DefaultValue()})	}	return f}// PrepareInsert 准备插入的数据func (c *ItemInfo) PrepareInsert(doc mo.M, u User) error {	for key, val := range doc {		field, ok := c.Field(key)		if !ok {			// 特殊处理 _id			if key == mo.ID.Key() {				if oid, ok := val.(mo.ObjectID); !(ok && !oid.IsZero()) {					return fmt.Errorf("invalid ObjectID: %s(%v)", reflect.TypeOf(val), val)				}			}			// 不允许添加配置文件中不存在的字段			return errUnknownFiled(c.Name, key)		}		// 校验和格式化数据		if err := field.Validate(val); err != nil {			val, err = field.Convert(val)			if err != nil {				return err			}		}		doc[field.Name] = val	}	// 填充配置文件中已存在的字段	fList := c.PrepareNew()	for _, e := range fList {		if _, ok := doc[e.Key]; ok {			continue		}		doc[e.Key] = e.Value	}	// 校验必填	for key := range c.RequiredMap {		if _, ok := doc[key]; !ok {			return errRequired(key, doc)		}	}	doc[Creator] = u.ID()	doc[CreationTime] = mo.NewDateTime()	return nil}func (c *ItemInfo) prepareUpdateObject(k string, v any) (any, error) {	fieldName, subFieldName, ok := strings.Cut(k, ".")	if !ok {		return nil, errUnknownFiled(c.Name, k)	}	field, fo := c.Field(fieldName)	if !fo {		return nil, errUnknownFiled(c.Name, fieldName)	}	if field.Type != mo.TypeObject {		return nil, errTypeReturn(&field, v)	}	subField, so := field.SubField(subFieldName)	if !so {		return nil, errUnknownFiled(c.Name, k)	}	if err := subField.Validate(v); err == nil {		return v, nil	}	return subField.Convert(v)}func (c *ItemInfo) prepareUpdateArray(k string, v any) (any, error) {	name := strings.Split(k, ".")	if len(name) < 2 {		return nil, errUnknownFiled(c.Name, k)	}	fieldName := name[0]	field, ok := c.Field(fieldName)	if !ok {		return nil, errUnknownFiled(c.Name, fieldName)	}	if field.Type != mo.TypeArray {		return nil, errTypeReturn(&field, v)	}	if field.Items == FieldItemsObject {		if len(name) != 3 {			return nil, errUnknownFiled(c.Name, k)		}		subFieldName := name[2]		subField, o := field.SubField(subFieldName)		if !o {			return nil, errUnknownFiled(c.Name, fieldName+"."+subFieldName)		}		if err := subField.Validate(v); err == nil {			return v, nil		}		return subField.Convert(v)	} else {		if err := field.Validate(v); err == nil {			return v, nil		}		return field.Convert(v)	}}func (c *ItemInfo) PrepareUpdater(updater mo.D, u User) error {	hasSetter := false	for i, e := range updater {		switch e.Key {		case mo.PoSet:			doc, err := mo.Convert.ME(e.Value.(mo.D))			if err != nil {				return err			}			if err = c.PrepareUpdate(doc); err != nil {				return err			}			if e.Key == mo.PoSet {				doc[LastUpdater] = u.ID()				doc[LastModified] = mo.NewDateTime()				hasSetter = true			}			update, err := mo.Convert.DE(doc)			if err != nil {				return err			}			updater[i] = mo.E{Key: e.Key, Value: update}		default:			for _, ev := range e.Value.(mo.D) {				// 对于非 mo.PoSet 类型的更新, 仅判断字段是否存在, 不再为其检测和转换数据类型				if _, ok := c.Field(ev.Key); !ok {					return errUnknownFiled(c.Name, ev.Key)				}			}		}	}	if !hasSetter {		updater = append(mo.D{			{Key: mo.PoSet, Value: mo.D{				{Key: LastUpdater, Value: u.ID()},				{Key: LastModified, Value: mo.NewDateTime()},			}}},			updater...,		)	}	return nil}// PrepareUpdate 准备更新的数据func (c *ItemInfo) PrepareUpdate(doc mo.M) error {	for k, v := range doc {		if k == mo.ID.Key() {			return fmt.Errorf("_id value can not be update")		}		var err error		field, ok := c.Field(k)		if !ok {			switch strings.Count(k, ".") {			case 1:				if v, err = c.prepareUpdateObject(k, v); err != nil {					return err				}			case 2:				if v, err = c.prepareUpdateArray(k, v); err != nil {					return err				}			default:				return errUnknownFiled(c.Name, k)			}		} else {			if err = field.Validate(v); err != nil {				v, err = field.Convert(v)				if err != nil {					return err				}			}		}		doc[k] = v	}	return nil}// PrepareFilter 检查 key 是否包在 itemName 中, 防止 SQL 注入func (c *ItemInfo) PrepareFilter(filter mo.D) error {	for _, ele := range filter {		// 不检查包含 . 的字段, 用于子 map 查找		if strings.ContainsRune(ele.Key, '.') {			continue		}		if _, ok := c.Field(ele.Key); !ok && !strings.HasPrefix(ele.Key, "$") {			return errUnknownFiled(c.Name, ele.Key)		}		// if array, ok := ele.Value.(mo.A); ok {		// 	for _, ai := range array {		// 		ai.(mo.D)		// 	}		//		// }	}	return nil}func (c *ItemInfo) Field(name string) (FieldInfo, bool) {	if field, ok := internalField[name]; ok {		if idx, o := c.FieldMap[name]; o {			return c.Fields[idx], true		}		return field, true	}	idx, ok := c.FieldMap[name]	if !ok {		return FieldInfo{}, false	}	return c.Fields[idx], true}func (c *ItemInfo) FieldType(t mo.Type) []FieldInfo {	fields := make([]FieldInfo, 0)	for _, field := range c.Fields {		if field.Type == t {			fields = append(fields, field)		}	}	return fields}
 |