| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 | package iiimport (	"fmt"	"reflect"	"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 设置唯一键}// 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, len(c.Fields))	for i, field := range c.Fields {		f[i] = 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	}	// 校验必填	for key := range c.RequiredMap {		if _, ok := doc[key]; !ok {			return errRequired(key, doc)		}	}	// 填充配置文件中已存在的字段	fList := c.PrepareNew()	for _, e := range fList {		if _, ok := doc[e.Key]; ok {			continue		}		doc[e.Key] = e.Value	}	doc[Creator] = u.ID()	doc[CreationTime] = mo.NewDateTime()	return nil}// PrepareUpdate 准备更新的数据func (c *ItemInfo) PrepareUpdate(doc mo.M, u User) error {	for k, v := range doc {		field, ok := c.Field(k)		if !ok {			return errUnknownFiled(c.Name, k)		}		if err := field.Validate(v); err != nil {			v, err = field.Convert(v)			if err != nil {				return err			}		}		doc[k] = v	}	doc[LastUpdater] = u.ID()	return nil}// PrepareFilter 检查 key 是否包在 itemName 中, 防止 SQL 注入func (c *ItemInfo) PrepareFilter(filter mo.D) error {	for _, ele := range filter {		if _, ok := c.Field(ele.Key); !ok {			return errUnknownFiled(c.Name, ele.Key)		}	}	return nil}func (c *ItemInfo) Field(name string) (FieldInfo, bool) {	switch name {	case ID:		return idInfo, true	case Creator:		return creator, true	case CreationTime:		return creationTime, true	default:		idx, ok := c.FieldMap[name]		if !ok {			return FieldInfo{}, false		}		return c.Fields[idx], true	}}// Lookup 检查错误并返回 ItemInfo.Fields 中已配置的 Lookup 过滤器// 当 Lookup 为有效配置时, 检查 Lookup.From 是否存在于 Items 内以及检查 FieldInfo.Fields 内的字段是否存在于该 ItemInfo 内func (c *ItemInfo) Lookup(items Items) ([]mo.D, error) {	lookFilter := make([]mo.D, 0)	for _, field := range c.Fields {		if !field.HasLookup() {			continue		}		info, ok := items.Has(c.Name.Database() + "." + field.Lookup.From)		if !ok {			return nil, fmt.Errorf("iteminfo: %s.%s.Lookup.From: %s: item not found", c.Name, field.Name, field.Lookup.From)		}		if _, ok = info.Field(field.Lookup.ForeignField); !ok {			return nil, fmt.Errorf("iteminfo: %s.%s.Lookup.Foreign: %s: not found in iteminfo: %s", c.Name, field.Name, field.Lookup.ForeignField, info.Name)		}		for _, extField := range field.Fields {			_, ok = info.Field(extField.Name)			if ok {				continue			}			return nil, fmt.Errorf("iteminfo: %s.%s.Fields: %s: not found in iteminfo: %s", c.Name, field.Name, extField.Name, info.Name)		}		lookFilter = append(lookFilter, field.Looker().Pipeline())	}	return lookFilter, nil}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) LookupField() []FieldInfo {	look := make([]FieldInfo, 0)	for _, field := range c.Fields {		if !field.HasLookup() {			continue		}		look = append(look, field)	}	return look}
 |