| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 | package iiimport (	"errors"	"fmt"	"reflect"	"strings"		"golib/v3/features/mo")var (	errUnknownField     = errors.New("unknown field: ")	errUnknownFieldCall = func(name Name, key string) error {		return errors.Join(errUnknownField, fmt.Errorf("%s.%s", name, key))	})func IsUnknownField(err error) bool {	return errors.Is(err, errUnknownField)}// 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) ForkDb(name string) Name {	return Name(c.Name.Database() + "." + name)}func (c *ItemInfo) IsRequired(field string) bool {	_, ok := c.requiredMap[field]	return ok}func (c *ItemInfo) IsUnique(field string) bool {	_, ok := c.uniqueMap[field]	return ok}func (c *ItemInfo) RequiredCheck(doc mo.M) error {	for key := range c.requiredMap {		if _, ok := doc[key]; !ok {			return errRequired(key, doc)		}	}	return nil}func (c *ItemInfo) CopyMap(doc mo.M) (mo.M, error) {	m := make(mo.M)	for key, val := range doc {		switch key {		case ID, Creator, CreationTime, LastModified, LastUpdater:			continue		}		field, ok := c.Field(key)		if !ok {			continue		}		v, err := field.Convert(val)		if err != nil {			return nil, err		}		m[key] = v	}	return mo.DeepCopy(m)}// prepareInsert 创一个列表, 包含所有 Fields 的 name 和默认值func (c *ItemInfo) prepareInsert(doc mo.M) {	for _, field := range c.Fields {		if _, ok := doc[field.Name]; ok {			continue		}		if field.Name == mo.OID && !field.Required {			continue // 当 XML 配置了 _id 但是并未指定 Required 通常用于捕捉 _id 而不是将其参与计算		}		doc[field.Name] = field.DefaultValue()	}}// 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.OID {				if oid, ok := val.(mo.ObjectID); !(ok && !oid.IsZero()) {					return fmt.Errorf("invalid ObjectID: %s(%v)", reflect.TypeOf(val), val)				}			}			// 不允许添加配置文件中不存在的字段			return errUnknownFieldCall(c.Name, key)		}		v, err := field.ConvertWithValidate(val)		if err != nil {			return err		}		doc[field.Name] = v	}	// 填充配置文件中已存在的字段	c.prepareInsert(doc)	// 校验必填, TODO 可能不会起作用	if err := c.RequiredCheck(doc); err != nil {		return err	}	if u != nil {		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, errUnknownFieldCall(c.Name, k)	}	field, fo := c.Field(fieldName)	if !fo {		return nil, errUnknownFieldCall(c.Name, fieldName)	}	if field.Type != mo.TypeObject {		return nil, errTypeReturn(&field, v)	}	subField, so := field.SubField(subFieldName)	if !so {		return nil, errUnknownFieldCall(c.Name, k)	}	return subField.ConvertWithValidate(v)}func (c *ItemInfo) prepareUpdateArray(k string, v any) (any, error) {	name := strings.Split(k, ".")	if len(name) < 2 {		return nil, errUnknownFieldCall(c.Name, k)	}	fieldName := name[0]	field, ok := c.Field(fieldName)	if !ok {		return nil, errUnknownFieldCall(c.Name, fieldName)	}	if field.Type != mo.TypeArray {		return nil, errTypeReturn(&field, v)	}	if field.Items == FieldItemsObject {		if len(name) != 3 {			return nil, errUnknownFieldCall(c.Name, k)		}		subFieldName := name[2]		subField, o := field.SubField(subFieldName)		if !o {			return nil, errUnknownFieldCall(c.Name, fieldName+"."+subFieldName)		}		return subField.ConvertWithValidate(v)	} else {		return field.ConvertWithValidate(v)	}}func (c *ItemInfo) PrepareUpdater(updater mo.D, u User) error {	hasSetter := false	for i, ele := range updater {		switch ele.Key {		case mo.PoSet:			doc, err := mo.ToM(ele.Value.(mo.D))			if err != nil {				return err			}			if err = c.PrepareUpdate(doc); err != nil {				return err			}			if ele.Key == mo.PoSet {				if u != nil {					doc[LastUpdater] = u.ID()				}				doc[LastModified] = mo.NewDateTime()				hasSetter = true			}			update, err := mo.ToD(doc)			if err != nil {				return err			}			updater[i] = mo.E{Key: ele.Key, Value: update}		case mo.PoSetOnInsert:			date := ele.Value.(mo.D)			date = append(date, mo.E{Key: CreationTime, Value: mo.NewDateTime()})			updater[i] = mo.E{Key: ele.Key, Value: date}			hasSetter = true		default:			for _, ev := range ele.Value.(mo.D) {				// 对于非 mo.PoSet 类型的更新, 仅判断字段是否存在, 不再为其检测和转换数据类型				if _, ok := c.Field(ev.Key); !ok {					return errUnknownFieldCall(c.Name, ev.Key)				}			}		}	}	if !hasSetter {		var d mo.D		if u != nil {			d = append(d, mo.E{				Key:   LastUpdater,				Value: u.ID(),			})		}		d = append(d, mo.E{			Key:   LastModified,			Value: mo.NewDateTime(),		})		updater = append(mo.D{{Key: mo.PoSet, Value: d}}, updater...)	}	return nil}// PrepareUpdate 准备更新的数据func (c *ItemInfo) PrepareUpdate(doc mo.M) error {	for k, v := range doc {		if k == mo.OID {			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 errUnknownFieldCall(c.Name, k)			}		} else {			v, err = field.ConvertWithValidate(v)			if err != nil {				return err			}		}		doc[k] = v	}	return nil}// QueryFilterCheck 检查 key 是否包在 itemName 中, 防止 SQL 注入func (c *ItemInfo) QueryFilterCheck(filter mo.D) error {	for _, ele := range filter {		if strings.ContainsRune(ele.Key, '.') { // 检查子 map 查找			mainKey, subKey, _ := strings.Cut(ele.Key, ".")			mainField, found := c.Field(mainKey)			if !found {				return errUnknownFieldCall(c.Name, mainKey)			}			_, found = mainField.SubField(subKey)			if !found {				return errUnknownFieldCall(c.Name, ele.Key)			}		} else {			if _, found := c.Field(ele.Key); !found {				return errUnknownFieldCall(c.Name, ele.Key)			}		}	}	return nil}// ResolveFilter 从 params 中根据 itemInfo 解析出已知的 filed 与未知的 filed// 对于已知的 filed 会将其值转换为对应类型的数据func (c *ItemInfo) ResolveFilter(params mo.D) (known, unknown mo.D, err error) {	for _, ele := range params {		field, found := c.Field(ele.Key)		if !found {			unknown = append(unknown, ele)			continue		}		val, err := field.Convert(ele.Value)		if err != nil {			return nil, nil, err		}		known = append(known, mo.E{Key: ele.Key, Value: val})	}	return}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}func (c *ItemInfo) getUniques() (ks []string) {	for k := range c.uniqueMap {		ks = append(ks, k)	}	for _, field := range c.Fields {		for k := range field.uniqueMap {			ks = append(ks, k)		}	}	return ks}
 |