From 017424bf7bb12eb6dfe98275821cf250c69e8d98 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:21:08 +0800 Subject: [PATCH] fix: Fixed issue where syncing remote apps causes local app tags to be lost (#9774) --- agent/app/repo/app_tag.go | 17 +++++++++++ agent/app/repo/tag.go | 9 ++++++ agent/app/service/app.go | 37 ++++++++-------------- agent/app/service/app_utils.go | 56 ++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 24 deletions(-) diff --git a/agent/app/repo/app_tag.go b/agent/app/repo/app_tag.go index c2d54522b..6f3adc99d 100644 --- a/agent/app/repo/app_tag.go +++ b/agent/app/repo/app_tag.go @@ -17,8 +17,10 @@ type IAppTagRepo interface { GetByAppId(appId uint) ([]model.AppTag, error) GetByTagIds(tagIds []uint) ([]model.AppTag, error) DeleteBy(ctx context.Context, opts ...DBOption) error + GetFirst(opts ...DBOption) (*model.AppTag, error) WithByTagID(tagID uint) DBOption + WithByAppID(appId uint) DBOption } func NewIAppTagRepo() IAppTagRepo { @@ -31,6 +33,12 @@ func (a AppTagRepo) WithByTagID(tagID uint) DBOption { } } +func (a AppTagRepo) WithByAppID(appId uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("app_id = ?", appId) + } +} + func (a AppTagRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error { return getTx(ctx).Create(&tags).Error } @@ -62,3 +70,12 @@ func (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) { func (a AppTagRepo) DeleteBy(ctx context.Context, opts ...DBOption) error { return getTx(ctx, opts...).Delete(&model.AppTag{}).Error } + +func (a AppTagRepo) GetFirst(opts ...DBOption) (*model.AppTag, error) { + var appTag model.AppTag + if err := getDb(opts...).First(&appTag).Error; err != nil { + return nil, err + } + return &appTag, nil + +} diff --git a/agent/app/repo/tag.go b/agent/app/repo/tag.go index 6b2788e8e..f5342cb75 100644 --- a/agent/app/repo/tag.go +++ b/agent/app/repo/tag.go @@ -19,6 +19,7 @@ type ITagRepo interface { DeleteByID(ctx context.Context, id uint) error Create(ctx context.Context, tag *model.Tag) error Save(ctx context.Context, tag *model.Tag) error + GetByKey(key string) (*model.Tag, error) } func NewITagRepo() ITagRepo { @@ -41,6 +42,14 @@ func (t TagRepo) All() ([]model.Tag, error) { return tags, nil } +func (t TagRepo) GetByKey(key string) (*model.Tag, error) { + var tag model.Tag + if err := getDb().Where("key = ?", key).First(&tag).Error; err != nil { + return nil, err + } + return &tag, nil +} + func (t TagRepo) GetByIds(ids []uint) ([]model.Tag, error) { var tags []model.Tag if err := getDb().Where("id in (?)", ids).Find(&tags).Error; err != nil { diff --git a/agent/app/service/app.go b/agent/app/service/app.go index a04c9d5dd..50d369190 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -928,18 +928,11 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { return err } var ( - tags []*model.Tag appTags []*model.AppTag oldAppIds []uint ) - for _, tag := range list.Extra.Tags { - translations, _ := json.Marshal(tag.Locales) - tags = append(tags, &model.Tag{ - Name: tag.Name, - Translations: string(translations), - Sort: tag.Sort, - Key: tag.Key, - }) + if err = SyncTags(list.Extra); err != nil { + return err } deleteCustomApp() oldApps, err := appRepo.GetBy(appRepo.WithNotLocal()) @@ -1015,6 +1008,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { } t.LogSuccess(i18n.GetMsgByKey("SyncAppDetail")) + tags, _ := tagRepo.All() var ( addAppArray []model.App updateAppArray []model.App @@ -1061,16 +1055,8 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { return } } - if err = tagRepo.DeleteAll(ctx); err != nil { - return - } - if len(tags) > 0 { - if err = tagRepo.BatchCreate(ctx, tags); err != nil { - return - } - for _, tag := range tags { - tagMap[tag.Key] = tag.ID - } + for _, tag := range tags { + tagMap[tag.Key] = tag.ID } for _, update := range updateAppArray { if err = appRepo.Save(ctx, &update); err != nil { @@ -1088,10 +1074,13 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { for _, tag := range app.TagsKey { tagId, ok := tagMap[tag] if ok { - appTags = append(appTags, &model.AppTag{ - AppId: app.ID, - TagId: tagId, - }) + exist, _ := appTagRepo.GetFirst(appTagRepo.WithByTagID(tagId), appTagRepo.WithByAppID(app.ID)) + if exist.ID == 0 { + appTags = append(appTags, &model.AppTag{ + AppId: app.ID, + TagId: tagId, + }) + } } } for _, d := range app.Details { @@ -1134,7 +1123,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { } if len(oldAppIds) > 0 { - if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil { + if err = appTagRepo.DeleteByAppIds(ctx, deleteIds); err != nil { return } } diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index b43ae8c6d..bef2f942e 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -1905,3 +1905,59 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error } return nil } + +func SyncTags(remoteProperties dto.ExtraProperties) error { + tx, ctx := getTxAndContext() + defer tx.Rollback() + localTags, _ := tagRepo.All() + localTagsMap := make(map[string]*model.Tag) + for i := range localTags { + localTagsMap[localTags[i].Key] = &localTags[i] + } + var err error + remoteTagsMap := make(map[string]*dto.Tag) + for i := range remoteProperties.Tags { + remoteTagsMap[remoteProperties.Tags[i].Key] = &remoteProperties.Tags[i] + } + + for key, localTag := range localTagsMap { + if _, exists := remoteTagsMap[key]; !exists { + _ = tagRepo.DeleteByID(ctx, localTag.ID) + } + } + + for _, remoteTag := range remoteProperties.Tags { + translations, _ := json.Marshal(remoteTag.Locales) + + if existTag, exists := localTagsMap[remoteTag.Key]; exists { + if needsUpdate(existTag, remoteTag, string(translations)) { + existTag.Name = remoteTag.Name + existTag.Sort = remoteTag.Sort + existTag.Translations = string(translations) + + if err = tagRepo.Save(ctx, existTag); err != nil { + return err + } + } + } else { + newTag := &model.Tag{ + Key: remoteTag.Key, + Name: remoteTag.Name, + Sort: remoteTag.Sort, + Translations: string(translations), + } + if err = tagRepo.Create(ctx, newTag); err != nil { + return err + } + } + } + + tx.Commit() + return nil +} + +func needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bool { + return localTag.Name != remoteTag.Name || + localTag.Sort != remoteTag.Sort || + localTag.Translations != translations +}