package module import ( "context" "strconv" "strings" "time" "go-common/app/interface/main/app-resource/conf" moduledao "go-common/app/interface/main/app-resource/dao/module" "go-common/app/interface/main/app-resource/model/module" "go-common/library/ecode" "go-common/library/log" ) var ( _emptylist = []*module.ResourcePool{} ) // Service module service. type Service struct { dao *moduledao.Dao tick time.Duration resourceCache map[string]*module.ResourcePool conditionsCache map[int]*module.Condition } // New new a module service. func New(c *conf.Config) (s *Service) { s = &Service{ dao: moduledao.New(c), tick: time.Duration(c.Tick), resourceCache: map[string]*module.ResourcePool{}, conditionsCache: make(map[int]*module.Condition), } s.loadCache() go s.loadproc() return } func (s *Service) FormCondition(versions []*module.Versions) (res map[string]map[string]int) { res = make(map[string]map[string]int) for _, pools := range versions { var ( re map[string]int ok bool ) for _, resource := range pools.Resource { if re, ok = res[pools.PoolName]; !ok { re = make(map[string]int) res[pools.PoolName] = re } var tmpVer int switch tmp := resource.Version.(type) { case string: tmpVer, _ = strconv.Atoi(tmp) case float64: tmpVer = int(tmp) } re[resource.ResourceName] = tmpVer } } return } // List get All or by poolname func (s *Service) List(c context.Context, mobiApp, device, platform, poolName, env string, build, sysver, level, scale, arch int, versions []*module.Versions, now time.Time) (res []*module.ResourcePool) { var ( resTmp []*module.ResourcePool versionsMap = s.FormCondition(versions) ) if poolName != "" { if pool, ok := s.resourceCache[poolName]; ok { resTmp = append(resTmp, pool) } } else { for _, l := range s.resourceCache { resTmp = append(resTmp, l) } } if len(resTmp) == 0 { res = _emptylist return } for _, resPool := range resTmp { var ( existRes = map[string]*module.Resource{} existResTotal = map[string]struct{}{} resPoolTmp = &module.ResourcePool{Name: resPool.Name} ok bool ) for _, re := range resPool.Resources { if re == nil { continue } if !s.checkCondition(c, mobiApp, device, platform, env, build, sysver, level, scale, arch, re.Condition, now) { continue } var t *module.Resource if _, ok = existResTotal[re.Name]; ok { continue } if t, ok = existRes[re.Name]; ok { if re.Increment == module.Total { tmp := &module.Resource{} *tmp = *t tmp.TotalMD5 = re.MD5 existResTotal[tmp.Name] = struct{}{} resPoolTmp.Resources = append(resPoolTmp.Resources, tmp) continue } } else { var ( resVer map[string]int ver int ) if resVer, ok = versionsMap[resPool.Name]; ok { if ver, ok = resVer[re.Name]; ok { if re.Increment == module.Incremental && re.FromVer != ver { continue } } else if !ok && re.Increment == module.Incremental { continue } } else if !ok && re.Increment == module.Incremental { continue } tmp := &module.Resource{} *tmp = *re existRes[tmp.Name] = tmp if re.Increment == module.Total { tmp.TotalMD5 = re.MD5 existResTotal[tmp.Name] = struct{}{} resPoolTmp.Resources = append(resPoolTmp.Resources, tmp) } } } if len(resPoolTmp.Resources) == 0 { continue } res = append(res, resPoolTmp) } return } // Resource get by poolname and resourcename func (s *Service) Resource(c context.Context, mobiApp, device, platform, poolName, resourceName, env string, ver, build, sysver, level, scale, arch int, now time.Time) (res *module.Resource, err error) { if resPoolTmp, ok := s.resourceCache[poolName]; ok { if resPoolTmp == nil { err = ecode.NothingFound return } var ( resTmp *module.Resource existRes = map[string]struct{}{} ) for _, resTmp = range resPoolTmp.Resources { if resTmp == nil { continue } if resTmp != nil && resTmp.Name == resourceName { if !s.checkCondition(c, mobiApp, device, platform, env, build, sysver, level, scale, arch, resTmp.Condition, now) { continue } if ver == 0 { if resTmp.Increment == module.Incremental { continue } } else { if resTmp.Increment == module.Incremental && resTmp.FromVer != ver { continue } } if resTmp.Increment == module.Total && resTmp.Version == ver { err = ecode.NotModified break } if _, ok := existRes[resTmp.Name]; !ok { res = &module.Resource{} *res = *resTmp existRes[resTmp.Name] = struct{}{} } if resTmp.Increment == module.Total && res != nil { res.TotalMD5 = resTmp.MD5 break } } } } if err != nil { return } if res == nil { err = ecode.NothingFound } return } func (s *Service) checkCondition(c context.Context, mobiApp, device, platform, env string, build, sysver, level, scale, arch int, condition *module.Condition, now time.Time) bool { if condition == nil { return true } if env == module.EnvRelease && condition.Valid == 0 { return false } else if env == module.EnvTest && condition.ValidTest == 0 { return false } else if env == module.EnvDefault && condition.Default != 1 { return false } if !condition.STime.Time().IsZero() && now.Unix() < int64(condition.STime) { return false } if !condition.ETime.Time().IsZero() && now.Unix() > int64(condition.ETime) { return false } NETX: for column, cv := range condition.Columns { switch column { case "plat": // whith list for _, v := range cv { if strings.TrimSpace(v.Value) == platform { continue NETX } } return false case "mobi_app": // whith list for _, v := range cv { if strings.TrimSpace(v.Value) == mobiApp { continue NETX } } return false case "device": // blace list for _, v := range cv { if strings.TrimSpace(v.Value) == device { return false } } case "build": // build < lt gt > build ge >= build, le <= build for _, v := range cv { value, _ := strconv.Atoi(strings.TrimSpace(v.Value)) if invalidModelBuild(build, value, v.Condition) { return false } } case "sysver": if sysver > 0 { for _, v := range cv { value, _ := strconv.Atoi(strings.TrimSpace(v.Value)) if invalidModelBuild(sysver, value, v.Condition) { return false } } } case "scale": // whith list if scale > 0 { for _, v := range cv { value, _ := strconv.Atoi(strings.TrimSpace(v.Value)) if value == scale { continue NETX } } return false } case "arch": // whith list if arch > 0 { for _, v := range cv { value, _ := strconv.Atoi(strings.TrimSpace(v.Value)) if value == arch { continue NETX } } return false } } } return true } // ModuleUpdateCache update module cache func (s *Service) ModuleUpdateCache() (err error) { err = s.loadCache() return } // load update cache func (s *Service) loadCache() (err error) { configsTmp, err := s.dao.ResourceConfig(context.TODO()) if err != nil { log.Error("s.dao.ResourceConfig error(%v)", err) return } limitTmp, err := s.dao.ResourceLimit(context.TODO()) if err != nil { log.Error("s.dao.ResourceLimit error(%v)", err) return } for _, config := range configsTmp { if limit, ok := limitTmp[config.ID]; ok { config.Columns = limit } } s.conditionsCache = configsTmp log.Info("module conditions success") tmpResourceDev, err := s.dao.ModuleDev(context.TODO()) if err != nil { log.Error("s.dao.ModuleDev error(%v)", err) return } tmpResources, err := s.dao.ModuleAll(context.TODO()) if err != nil { log.Error("s.dao.ModuleAll error(%v)", err) return } tmpResourcePoolCaches := map[string]*module.ResourcePool{} for _, resPool := range tmpResourceDev { if resPool == nil { continue } var tmpResourcePoolCache = &module.ResourcePool{ID: resPool.ID, Name: resPool.Name} for _, res := range resPool.Resources { if res == nil { continue } if re, ok := tmpResources[res.ID]; ok { var tmpre []*module.Resource for _, r := range re { if r.URL == "" || r.MD5 == "" { continue } if c, ok := s.conditionsCache[r.ResID]; ok { r.Condition = c // all level if c != nil { for column, cv := range c.Columns { switch column { case "level": for _, v := range cv { value, _ := strconv.Atoi(strings.TrimSpace(v.Value)) r.Level = value } } } } r.IsWifi = c.IsWifi } tmpre = append(tmpre, r) } if len(tmpre) == 0 { continue } tmpResourcePoolCache.Resources = append(tmpResourcePoolCache.Resources, tmpre...) } } tmpResourcePoolCaches[resPool.Name] = tmpResourcePoolCache } s.resourceCache = tmpResourcePoolCaches log.Info("module resources success") return } // cacheproc load cache data func (s *Service) loadproc() { for { time.Sleep(s.tick) s.loadCache() } } // invalidModelBuild model build func invalidModelBuild(srcBuild, cfgBuild int, cfgCond string) bool { if cfgBuild != 0 && cfgCond != "" { switch cfgCond { case "lt": if cfgBuild <= srcBuild { return true } case "le": if cfgBuild < srcBuild { return true } case "ge": if cfgBuild > srcBuild { return true } case "gt": if cfgBuild >= srcBuild { return true } } } return false }