/** * @author chengliang * @date 2026/1/12 21:55 * @brief * **/ package service import ( "dsbqj-admin/model/mongo/hotupdate" "dsbqj-admin/pkg/logger" "errors" "fmt" "strconv" "strings" "sync" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) var validProj = map[string]bool{ "bzst": true, "ds20": true, } var validOs = map[string]bool{ "ios": true, "android": true, } type THotUpdateVerManager struct { mu sync.RWMutex versionMap map[string]map[string]*TGetVersionRsp // proj:os:versionInfo versionDB *hotupdate.TVersionDB } var ( verManager *THotUpdateVerManager once sync.Once ) func GetTHotUpdateVerManager() *THotUpdateVerManager { once.Do(func() { verManager = &THotUpdateVerManager{ versionMap: make(map[string]map[string]*TGetVersionRsp), versionDB: hotupdate.NewVersionService(), } }) return verManager } func (this *THotUpdateVerManager) Init() { for proj, _ := range validProj { for os, _ := range validOs { _, err := this.GetMaxPubVerInfo(proj, os) if err != nil { logger.Error(err.Error()) } } } logger.Info("version manager init success, version info:%#v", this.versionMap) } func (this *THotUpdateVerManager) GetMaxPubVerInfo(proj, os string) (*TGetVersionRsp, error) { if proj == "" || os == "" { return nil, errors.New("project or os can't be empty") } if !validProj[proj] { return nil, fmt.Errorf("project:%s invalid", proj) } if !validOs[os] { return nil, fmt.Errorf("os:%s invalid", os) } if ver, err := this.getVersionMapBy(proj, os); err == nil { return ver, nil } else { versionRsp, err := this.findDBMaxPubVersion(proj, os) if err != nil { return nil, err } this.updateVersionMap(proj, os, versionRsp) return versionRsp, nil } } func (this *THotUpdateVerManager) reloadPubVerBy(proj, os string) error { versionRsp, err := this.findDBMaxPubVersion(proj, os) if err != nil { return err } this.updateVersionMap(proj, os, versionRsp) return nil } // 内部API调用 func (this *THotUpdateVerManager) GetMaxVerInfo(os string) (*TGetVersionRsp, error) { if os == "" { return nil, errors.New("project or os can't be empty") } return this.findDBMaxVersion("", os) } // 内部API调用 func (this *THotUpdateVerManager) AddVersion(req *TAddVersionReq) error { if !this.isInvalid(req) { return errors.New("version is invalid") } mVersion := &hotupdate.MVersionInfo{ Proj: req.Proj, Os: req.Os, Version: req.Version, PackageUrl: req.PackageUrl, RemoteVersionUrl: req.RemoteVersionUrl, RemoteManifestUrl: req.RemoteManifestUrl, Status: hotupdate.DEF_VER_STATUS_NONE, } return this.versionDB.Create(mVersion) } func (this *THotUpdateVerManager) ChangeStatus(id string, status int16) (*TGetVersionRsp, error) { if id == "" { return nil, errors.New("id can't be empty") } if status != hotupdate.DEF_VER_STATUS_NONE && status != hotupdate.DEF_VER_STATUS_PUBLIC { return nil, fmt.Errorf("invalid status %d", status) } updateMap := make(map[string]interface{}) updateMap["status"] = status mVersion, err := this.versionDB.Update(id, updateMap) if err != nil { return nil, err } logger.Info("after update mVersion", mVersion) err = this.reloadPubVerBy(mVersion.Proj, mVersion.Os) vRsp := &TGetVersionRsp{} if err != nil { logger.Warn(fmt.Errorf("reloadPubVerBy error: %v", err).Error()) } else { vRsp.FromMVersion(mVersion) } return vRsp, nil } // 内部API调用 分页获取version list func (this *THotUpdateVerManager) GetVersionList(start, limit int64) ([]*TGetVersionRsp, error) { opts := options.Find(). SetSort(bson.M{"created_at": -1}). // 按版本降序 SetSkip(start - 1). SetLimit(limit) dbVersionArr, err := this.versionDB.Find(nil, opts) if err != nil { return nil, err } rspArr := make([]*TGetVersionRsp, 0, len(dbVersionArr)) for _, mVersion := range dbVersionArr { vRsp := &TGetVersionRsp{} vRsp.FromMVersion(&mVersion) rspArr = append(rspArr, vRsp) } return rspArr, nil } func (this *THotUpdateVerManager) getVersionMapBy(proj, os string) (*TGetVersionRsp, error) { this.mu.RLock() defer this.mu.RUnlock() if _, ok := this.versionMap[proj]; !ok { return nil, errors.New("project or os can't be empty") } if ver, ok := this.versionMap[proj][os]; ok { return ver, nil } else { return nil, errors.New("version not found") } } func (this *THotUpdateVerManager) updateVersionMap(proj, os string, versionRsp *TGetVersionRsp) { this.mu.Lock() defer this.mu.Unlock() logger.Info(fmt.Sprintf("updateVersionMap proj:%s, os:%s, ver:%s", proj, os, versionRsp.Version)) if _, ok := this.versionMap[proj]; !ok { this.versionMap[proj] = make(map[string]*TGetVersionRsp) } logger.Info("before updateVersionMap %#v", this.versionMap[proj][os]) this.versionMap[proj][os] = versionRsp logger.Info("after updateVersionMap %#v", this.versionMap[proj][os]) } // 版本号是递增的 func (this *THotUpdateVerManager) isInvalid(req *TAddVersionReq) bool { curVersion, err := this.findDBMaxVersion(req.Proj, req.Os) if err != nil { // 默认只有系统初始化时 return true } logger.Info("curVersion:%s, remoteVersion:%s", curVersion, req.Version) return CompareVersion(req.Version, curVersion.Version) == 1 } // 对外的最大version func (this *THotUpdateVerManager) findDBMaxPubVersion(proj, os string) (*TGetVersionRsp, error) { return this.findMaxVersionByStatus(proj, os, hotupdate.DEF_VER_STATUS_PUBLIC) } // 查找数据库中最大的version func (this *THotUpdateVerManager) findDBMaxVersion(proj, os string) (*TGetVersionRsp, error) { if os == "" { return nil, errors.New("os can't be empty") } return this.findMaxVersionByStatus(proj, os, hotupdate.DEF_VER_STATUS_INVALID) } func (this *THotUpdateVerManager) findMaxVersionByStatus(proj, os string, status int16) (*TGetVersionRsp, error) { filte := bson.M{} if proj != "" { filte["project"] = proj } if os != "" { filte["os"] = os } if status == hotupdate.DEF_VER_STATUS_PUBLIC || status == hotupdate.DEF_VER_STATUS_NONE { filte["status"] = status } opts := options.Find(). SetSort(bson.M{"created_at": -1}) logger.Info("filte:%v", filte) versionArr, err := this.versionDB.Find(filte, opts) if err != nil { return nil, err } if len(versionArr) == 0 { return nil, fmt.Errorf("version not found proj:%s, os:%s, status:%d", proj, os, status) } mVersion := versionArr[0] versionRsp := &TGetVersionRsp{} versionRsp.FromMVersion(&mVersion) return versionRsp, nil } // CompareVersion 比较两个版本号 // 返回: -1(v1v2) func CompareVersion(v1, v2 string) int { // 将版本号按点号分割 parts1 := strings.Split(v1, ".") parts2 := strings.Split(v2, ".") // 获取最大长度 maxLen := len(parts1) if len(parts2) > maxLen { maxLen = len(parts2) } // 逐级比较 for i := 0; i < maxLen; i++ { // 获取当前级别的版本号,如果不存在则为0 num1 := 0 if i < len(parts1) { num1, _ = strconv.Atoi(parts1[i]) } num2 := 0 if i < len(parts2) { num2, _ = strconv.Atoi(parts2[i]) } // 比较当前级别 if num1 < num2 { return -1 } else if num1 > num2 { return 1 } // 如果相等,继续比较下一级 } return 0 }