package service import ( "context" "errors" "fmt" "sort" "strconv" "strings" "time" "dsbqj-admin/app/task" "dsbqj-admin/model/mongo/abver" "dsbqj-admin/model/mongo/md5" "dsbqj-admin/model/mongo/version" "dsbqj-admin/tool" "github.com/kamva/mgm/v3" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) const versionExternalReqTimeoutSec = 30 * 60 type VersionReloadService struct { } func (service *VersionReloadService) Reload() error { task.VersionTask.Exec() return nil } type VersionServerService struct { Platform string `json:"platform"` LegacyChannel string `json:"channel"` } func (service *VersionServerService) Servers() ([]*version.Version, error) { platform := resolvePlatform(service.Platform, service.LegacyChannel) if platform == "" { return nil, errors.New("平台不能为空") } items := task.VersionTask.Servers(platform) result := make([]*version.Version, 0, len(items)) for _, item := range items { result = append(result, versionWithConfig(item, platform, task.NormalizeEnvVersion(item.EnvVersion))) } return result, nil } type VersionCheckService struct { Version string `json:"version"` Platform string `json:"platform"` LegacyChannel string `json:"channel"` EnvVersion string `json:"envVersion"` } func (service *VersionCheckService) Check() (*version.Version, error) { platform := resolvePlatform(service.Platform, service.LegacyChannel) if platform == "" { return nil, errors.New("平台不能为空") } result := task.VersionTask.Check(service.Version, platform, service.EnvVersion) if result == nil { return nil, nil } return versionWithConfig(result, platform, task.NormalizeEnvVersion(result.EnvVersion)), nil } func versionWithConfig(item *version.Version, platform, env string) *version.Version { result := *item abVer := task.VersionConfigTask.GetAbVer(platform, env) md5 := task.VersionConfigTask.GetMD5(platform, env) result.AbVer, result.AbVerVersion = abVer.Value, abVer.Version result.MD5, result.MD5Version = md5.Value, md5.Version return &result } func resolvePlatform(platform, legacyChannel string) string { if value := strings.TrimSpace(platform); value != "" { return value } return strings.TrimSpace(legacyChannel) } type VersionShowService struct { CondPage Platform string `form:"platform" json:"platform"` EnvVersion string `form:"envVersion" json:"envVersion"` } func (service *VersionShowService) List() ([]version.Version, error) { versions := make([]version.Version, 0) var queryDoc = bson.M{} clauses := make([]bson.M, 0, 2) if service.Platform != "" { clauses = append(clauses, bson.M{"$or": []bson.M{{"platform": service.Platform}, {"platform": ""}, {"platform": bson.M{"$exists": false}}}}) } if service.EnvVersion != "" { envVersion := task.NormalizeEnvVersion(service.EnvVersion) if envVersion == "release" { clauses = append(clauses, bson.M{"$or": []bson.M{ {"envVersion": envVersion}, {"envVersion": ""}, {"envVersion": bson.M{"$exists": false}}, }}) } else { clauses = append(clauses, bson.M{"envVersion": envVersion}) } } if len(clauses) > 0 { queryDoc["$and"] = clauses } err := mgm.Coll(&version.Version{}).SimpleFind(&versions, queryDoc) return versions, err } type VersionCreateService struct { Platform string `json:"platform" binding:"required"` EnvVersion string `json:"envVersion"` Version string `json:"version" binding:"required"` Name string `json:"name" binding:"required"` CDN string `json:"cdn" binding:"required"` Url string `json:"url" binding:"required"` } func (service *VersionCreateService) Create() error { envVersion := task.NormalizeEnvVersion(service.EnvVersion) var exists version.Version err := mgm.Coll(&version.Version{}).First(versionIdentityFilter(service.Platform, service.Version, envVersion), &exists) if err == nil { return errors.New("该平台、环境和版本号已存在") } if !errors.Is(err, mongo.ErrNoDocuments) { return err } version := new(version.Version) version.Platform = service.Platform version.EnvVersion = envVersion version.Name = service.Name version.Version = service.Version version.CDN = service.CDN version.Url = service.Url if err := mgm.Coll(version).Create(version); err != nil { return err } task.VersionTask.Exec() return nil } type VersionEditService struct { Id string `json:"id" binding:"required"` Platform *string `json:"platform"` EnvVersion *string `json:"envVersion"` Version *string `json:"version"` Name *string `json:"name"` CDN *string `json:"cdn"` Url *string `json:"url"` Default *bool `json:"default"` } func (service *VersionEditService) Edit() error { objectId, _ := primitive.ObjectIDFromHex(service.Id) coll := mgm.Coll(&version.Version{}) var current version.Version if err := coll.FindByID(objectId, ¤t); err != nil { return err } platform, envVersion, versionNumber := current.Platform, task.NormalizeEnvVersion(current.EnvVersion), current.Version if service.Platform != nil { platform = strings.TrimSpace(*service.Platform) } if service.EnvVersion != nil { envVersion = task.NormalizeEnvVersion(*service.EnvVersion) } if service.Version != nil { versionNumber = *service.Version } var duplicate version.Version duplicateFilter := versionIdentityFilter(platform, versionNumber, envVersion) duplicateFilter["_id"] = bson.M{"$ne": objectId} if err := coll.First(duplicateFilter, &duplicate); err == nil { return errors.New("该平台、环境和版本号已存在") } else if !errors.Is(err, mongo.ErrNoDocuments) { return err } filterDoc := bson.M{"_id": objectId} updateDoc := bson.M{} if service.Platform != nil { updateDoc["platform"] = platform } if service.EnvVersion != nil { updateDoc["envVersion"] = task.NormalizeEnvVersion(*service.EnvVersion) } if service.Name != nil { updateDoc["name"] = *service.Name } if service.Version != nil { updateDoc["version"] = *service.Version } if service.CDN != nil { updateDoc["cdn"] = *service.CDN } if service.Url != nil { updateDoc["url"] = *service.Url } if service.Default != nil { updateDoc["default"] = *service.Default } _, err := coll.UpdateOne(context.Background(), filterDoc, bson.M{"$set": updateDoc}) if err != nil { return err } task.VersionTask.Exec() return nil } func versionIdentityFilter(platform, versionNumber, envVersion string) bson.M { filter := bson.M{"platform": platform, "version": versionNumber} if task.NormalizeEnvVersion(envVersion) == "release" { filter["$or"] = []bson.M{{"envVersion": "release"}, {"envVersion": ""}, {"envVersion": bson.M{"$exists": false}}} } else { filter["envVersion"] = "trial" } return filter } type VersionProdService struct { Id string `json:"id" binding:"required"` IsProd bool `json:"isProd"` } func (service *VersionProdService) Update() error { objectId, err := primitive.ObjectIDFromHex(service.Id) if err != nil { return err } coll := mgm.Coll(&version.Version{}) var current version.Version if err := coll.FindByID(objectId, ¤t); err != nil { return err } if service.IsProd { scope := versionIdentityFilter(current.Platform, "", current.EnvVersion) delete(scope, "version") scope["_id"] = bson.M{"$ne": objectId} if _, err := coll.UpdateMany( context.Background(), scope, bson.M{"$set": bson.M{"isProd": false}}, ); err != nil { return err } } _, err = coll.UpdateOne( context.Background(), bson.M{"_id": objectId}, bson.M{"$set": bson.M{"isProd": service.IsProd}}, ) return err } type VersionExternalConfigService struct { Platform string `form:"platform" json:"platform" binding:"required"` Version string `form:"version" json:"version" binding:"required"` AbVer *string `form:"abVer" json:"abVer"` MD5 *string `form:"md5" json:"md5"` TimeSec int64 `form:"timesec" json:"timesec" binding:"required"` Sign string `form:"sign" json:"sign" binding:"required"` } type VersionConfigUploadService struct { Platform string `form:"platform" json:"platform" binding:"required"` Version string `form:"version" json:"version" binding:"required"` Value string `form:"value" json:"value" binding:"required"` TimeSec int64 `form:"timesec" json:"timesec" binding:"required"` Sign string `form:"sign" json:"sign" binding:"required"` } type VersionConfigManageService struct { Type string `json:"type" form:"type" binding:"required"` ID string `json:"id"` PlatformFilter string `json:"-"` Version string `json:"version"` Value *string `json:"value"` EnvVersions *[]string `json:"envVersions"` External *bool `json:"external"` } func configCollection(configType string) interface{} { if configType == "md5" { return &md5.MD5{} } return &abver.AbVer{} } func (service *VersionConfigManageService) List() (interface{}, error) { items := make([]interface{}, 0) filter := bson.M{} if service.PlatformFilter != "" { filter["$or"] = []bson.M{{"platform": service.PlatformFilter}, {"platform": ""}, {"platform": bson.M{"$exists": false}}} } if service.Type == "md5" { result := make([]md5.MD5, 0) err := mgm.Coll(&md5.MD5{}).SimpleFind(&result, filter) sort.SliceStable(result, func(i, j int) bool { return task.CompareConfigVersion(result[i].Version, result[j].Version) > 0 }) for i := range result { items = append(items, result[i]) } return items, err } result := make([]abver.AbVer, 0) err := mgm.Coll(&abver.AbVer{}).SimpleFind(&result, filter) sort.SliceStable(result, func(i, j int) bool { return task.CompareConfigVersion(result[i].Version, result[j].Version) > 0 }) for i := range result { items = append(items, result[i]) } return items, err } func (service *VersionConfigManageService) Save() error { if service.ID == "" { return errors.New("配置记录编号不能为空") } id, err := primitive.ObjectIDFromHex(service.ID) if err != nil { return err } update := bson.M{} if service.Value != nil { update["value"] = *service.Value } if service.EnvVersions != nil { update["envVersions"] = *service.EnvVersions } if service.External != nil { update["external"] = *service.External } if len(update) == 0 { return errors.New("没有需要更新的内容") } if service.Type == "md5" { _, err = mgm.Coll(&md5.MD5{}).UpdateOne(context.Background(), bson.M{"_id": id}, bson.M{"$set": update}) if err == nil { task.VersionConfigTask.Reload() } return err } _, err = mgm.Coll(&abver.AbVer{}).UpdateOne(context.Background(), bson.M{"_id": id}, bson.M{"$set": update}) if err == nil { task.VersionConfigTask.Reload() } return err } func (service *VersionConfigUploadService) ValidateAuth() error { deltaTime := time.Now().Unix() - service.TimeSec if deltaTime < 0 { deltaTime = -deltaTime } if deltaTime > versionExternalReqTimeoutSec { return errors.New("时间戳已过期") } params := map[string]any{"platform": service.Platform, "version": service.Version, "value": service.Value, "timesec": strconv.FormatInt(service.TimeSec, 10)} if tool.GenSign(params) != service.Sign { return errors.New("签名错误") } return nil } func (service *VersionConfigUploadService) SaveAbVer() error { var exists abver.AbVer filter := bson.M{"platform": service.Platform, "version": service.Version} if err := mgm.Coll(&abver.AbVer{}).First(filter, &exists); err == nil { return errors.New("abVer 配置版本已存在,不能重复上报") } item := &abver.AbVer{Platform: service.Platform, Version: service.Version, Value: service.Value, External: false} _, err := mgm.Coll(item).UpdateOne(context.Background(), filter, bson.M{"$set": item}, options.Update().SetUpsert(true)) if err == nil { task.VersionConfigTask.Reload() } return err } func (service *VersionConfigUploadService) SaveMD5() error { var exists md5.MD5 filter := bson.M{"platform": service.Platform, "version": service.Version} if err := mgm.Coll(&md5.MD5{}).First(filter, &exists); err == nil { return errors.New("md5 配置版本已存在,不能重复上报") } item := &md5.MD5{Platform: service.Platform, Version: service.Version, Value: service.Value, External: false} _, err := mgm.Coll(item).UpdateOne(context.Background(), filter, bson.M{"$set": item}, options.Update().SetUpsert(true)) if err == nil { task.VersionConfigTask.Reload() } return err } func (service *VersionExternalConfigService) Save() error { if service.AbVer == nil && service.MD5 == nil { return errors.New("abVer or md5 is required") } filterDoc := bson.M{ "platform": service.Platform, "version": service.Version, } coll := mgm.Coll(&version.Version{}) var current version.Version if err := coll.First(filterDoc, ¤t); err != nil { return errors.New("version not found") } updateDoc := bson.M{} if service.AbVer != nil { updateDoc["abVer"] = *service.AbVer } if service.MD5 != nil { updateDoc["md5"] = *service.MD5 } _, err := coll.UpdateOne( context.Background(), bson.M{"_id": current.ID}, bson.M{"$set": updateDoc}, ) return err } func (service *VersionExternalConfigService) ValidateAuth() error { deltaTime := time.Now().Unix() - service.TimeSec if deltaTime < 0 { deltaTime = -deltaTime } if deltaTime > versionExternalReqTimeoutSec { return fmt.Errorf("timesec timeout") } params := map[string]any{ "platform": service.Platform, "version": service.Version, "timesec": strconv.FormatInt(service.TimeSec, 10), } if service.AbVer != nil { params["abVer"] = *service.AbVer } if service.MD5 != nil { params["md5"] = *service.MD5 } sign := tool.GenSign(params) if sign != service.Sign { return fmt.Errorf("sign error") } return nil } type VersionDeleteService struct { Id string `json:"id" binding:"required"` } func (service *VersionDeleteService) Delete() error { version := new(version.Version) objectId, _ := primitive.ObjectIDFromHex(service.Id) filterDoc := bson.M{"_id": objectId} _, err := mgm.Coll(version).DeleteOne(context.Background(), filterDoc) if err != nil { return err } task.VersionTask.Exec() return nil }