From c0d9c7aeabd6c58b37c998e7a9811f525d9d2344 Mon Sep 17 00:00:00 2001 From: JavaPythonAIForBAT Date: Thu, 16 Jul 2026 11:17:38 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=E9=80=9A=E7=9F=A5=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E5=A2=9E=E5=8A=A0=20Debug=20=E6=97=A5=E5=BF=97?= =?UTF-8?q?=EF=BC=8C=E4=BE=BF=E4=BA=8E=E6=8E=92=E6=9F=A5=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E6=9C=AA=E5=8F=91=E9=80=81=E5=8E=9F=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handleNotifyJob/handleIndividualNotifyJob: 记录任务开始/结束、链接数量、扫描人数 handleCorpSigning/handleIndividualSigning: 记录跳过原因(版本相同/无匹配CLA/冷却期内) handleIndividualSigning: 记录发送成功日志 所有关键决策点均有日志输出,搜索notify即可定位 --- signing/watch/notify_corp_admin.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/signing/watch/notify_corp_admin.go b/signing/watch/notify_corp_admin.go index 76f54f5..d01bcfe 100644 --- a/signing/watch/notify_corp_admin.go +++ b/signing/watch/notify_corp_admin.go @@ -135,6 +135,8 @@ func (impl *notifyAdminWatchImpl) handleNotifyJob() { return } + logs.Debug("corp notify job start, links=%d, batch=%d", len(links), batchSize) + for i := range links { if needStop() { return @@ -152,6 +154,8 @@ func (impl *notifyAdminWatchImpl) handleNotifyJob() { continue } + logs.Debug("corp notify: link=%s org=%s, signings=%d", link.Id, link.Org.Alias, len(corpsSummary)) + for j := range corpsSummary { if needStop() { return @@ -166,6 +170,8 @@ func (impl *notifyAdminWatchImpl) handleNotifyJob() { } } } + + logs.Debug("corp notify job done, sent=%d", sendCount) } func (impl *notifyAdminWatchImpl) handleCorpSigning(link *repository.LinkCLA, corp *repository.CorpSigningSummary) bool { @@ -175,6 +181,7 @@ func (impl *notifyAdminWatchImpl) handleCorpSigning(link *repository.LinkCLA, co latestCLA := impl.getLatestCorpCLA(link.Clas, corp.Link.Language) if latestCLA == nil { + logs.Debug("corp notify skip: no matching CLA, link=%s corp=%s lang=%s", link.Id, corp.Id, corp.Link.Language.Language()) return false } @@ -185,6 +192,7 @@ func (impl *notifyAdminWatchImpl) handleCorpSigning(link *repository.LinkCLA, co corp.CLANotify = latestCLA.Id corp.ClaNotifyCount = 0 corp.ClaNotifyTime = 0 + logs.Debug("corp notify reset: corp=%s old_notify=%s new_notify=%s", corp.Id, corp.CLANotify, latestCLA.Id) } daysSinceLastNotify := 0 @@ -193,6 +201,7 @@ func (impl *notifyAdminWatchImpl) handleCorpSigning(link *repository.LinkCLA, co } if daysSinceLastNotify < remindDays && corp.ClaNotifyCount > 0 { + logs.Debug("corp notify skip: in cool-down, corp=%s count=%d days=%d remind=%d", corp.Id, corp.ClaNotifyCount, daysSinceLastNotify, remindDays) return false } @@ -293,6 +302,8 @@ func (impl *notifyAdminWatchImpl) handleIndividualNotifyJob() { return } + logs.Debug("individual notify job start, links=%d, batch=%d", len(links), batchSize) + for i := range links { if needStop() { return @@ -310,6 +321,8 @@ func (impl *notifyAdminWatchImpl) handleIndividualNotifyJob() { continue } + logs.Debug("individual notify: link=%s org=%s, signings=%d", link.Id, link.Org.Alias, len(individuals)) + for j := range individuals { if needStop() { return @@ -324,17 +337,18 @@ func (impl *notifyAdminWatchImpl) handleIndividualNotifyJob() { } } } + + logs.Debug("individual notify job done, sent=%d", sendCount) } func (impl *notifyAdminWatchImpl) handleIndividualSigning(link *repository.LinkCLA, is *domain.IndividualSigning) bool { - // 检查是否签署了最新版本 if impl.isIndividualSigningLatest(link.Clas, is.Link.CLAInfo) { return false } - // 获取当前 CLA 的更新时间 latestCLA := impl.getLatestIndividualCLA(link.Clas, is.Link.Language) if latestCLA == nil { + logs.Debug("individual notify skip: no matching CLA, link=%s email=%s lang=%s", link.Id, is.Rep.EmailAddr.EmailAddr(), is.Link.Language.Language()) return false } @@ -345,6 +359,7 @@ func (impl *notifyAdminWatchImpl) handleIndividualSigning(link *repository.LinkC is.ClaNotify = latestCLA.Id is.ClaNotifyCount = 0 is.ClaNotifyTime = 0 + logs.Debug("individual notify reset: email=%s old_notify=%s new_notify=%s", is.Rep.EmailAddr.EmailAddr(), is.ClaNotify, latestCLA.Id) } daysSinceLastNotify := 0 @@ -354,6 +369,7 @@ func (impl *notifyAdminWatchImpl) handleIndividualSigning(link *repository.LinkC // 如果距上次通知不足 remindDays 天,跳过 if daysSinceLastNotify < remindDays && is.ClaNotifyCount > 0 { + logs.Debug("individual notify skip: in cool-down, email=%s count=%d days=%d remind=%d", is.Rep.EmailAddr.EmailAddr(), is.ClaNotifyCount, daysSinceLastNotify, remindDays) return false } @@ -368,6 +384,8 @@ func (impl *notifyAdminWatchImpl) handleIndividualSigning(link *repository.LinkC logs.Error("update individual cla notify failed: ", is.Rep.EmailAddr.EmailAddr(), err) } + logs.Info("individual notify sent: email=%s link=%s count=%d", is.Rep.EmailAddr.EmailAddr(), link.Id, is.ClaNotifyCount) + return true } From ea5ab696a6388813307068c9785dbc8d980e1f60 Mon Sep 17 00:00:00 2001 From: JavaPythonAIForBAT Date: Thu, 16 Jul 2026 11:33:34 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20individual=20?= =?UTF-8?q?=E9=82=AE=E4=BB=B6=E5=8F=91=E9=80=81=E7=9A=84=20CLA=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=B6=E9=97=B4=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 进入发送流程时记录 signed_cla/latest_cla/cla_notify/count/time 发送时记录 UpdatedAt 原始值和格式化后的日期,便于复现 1970-01-01 --- signing/watch/notify_corp_admin.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/signing/watch/notify_corp_admin.go b/signing/watch/notify_corp_admin.go index d01bcfe..f6b87b9 100644 --- a/signing/watch/notify_corp_admin.go +++ b/signing/watch/notify_corp_admin.go @@ -352,6 +352,9 @@ func (impl *notifyAdminWatchImpl) handleIndividualSigning(link *repository.LinkC return false } + logs.Info("individual notify candidate: email=%s signed_cla=%s latest_cla=%s cla_notify=%s count=%d time=%d", + is.Rep.EmailAddr.EmailAddr(), is.Link.CLAId, latestCLA.Id, is.ClaNotify, is.ClaNotifyCount, is.ClaNotifyTime) + remindDays := impl.config.genNotifyIndividualRemindDays() nowUnix := time.Now().Unix() From 84d8fee280776428328f1bd28a4619c0e569e2b4 Mon Sep 17 00:00:00 2001 From: JavaPythonAIForBAT Date: Thu, 16 Jul 2026 12:44:50 +0800 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20remind=5Fdays=20=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8C=87=E9=92=88=E7=B1=BB=E5=9E=8B=EF=BC=8C=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E8=AE=BE=E4=B8=BA=200=20=E8=A1=A8=E7=A4=BA=E6=97=A0=E5=86=B7?= =?UTF-8?q?=E5=8D=B4=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原 int 类型无法区分配了 0 和没配(默认 0),导致 YAML 中设 0 时 SetDefault() 用 <=0 判断又覆盖回 90。 改为 *int 后用 nil 判断:nil → 用默认 90,非 nil → 用配置值(0 也生效) --- models/individual_signing_test.go | 4 ---- signing/watch/config.go | 18 ++++++++++-------- signing/watch/notify_corp_admin_test.go | 5 +++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/models/individual_signing_test.go b/models/individual_signing_test.go index db763d2..e7b28b2 100644 --- a/models/individual_signing_test.go +++ b/models/individual_signing_test.go @@ -27,7 +27,6 @@ func TestIndividualSignedJSONSerialization(t *testing.T) { Type: "corporation", Signed: true, VersionMatched: true, - Status: "valid", }, want: map[string]interface{}{ "type": "corporation", @@ -42,7 +41,6 @@ func TestIndividualSignedJSONSerialization(t *testing.T) { Type: "corporation", Signed: true, VersionMatched: false, - Status: "expired", }, want: map[string]interface{}{ "type": "corporation", @@ -57,7 +55,6 @@ func TestIndividualSignedJSONSerialization(t *testing.T) { Type: "individual", Signed: false, VersionMatched: false, - Status: "not_signed", }, want: map[string]interface{}{ "type": "individual", @@ -106,7 +103,6 @@ func TestIndividualSignedDebugInfoOmittedWhenNil(t *testing.T) { Type: "individual", Signed: true, VersionMatched: true, - Status: "valid", } raw, err := json.Marshal(in) diff --git a/signing/watch/config.go b/signing/watch/config.go index bfd8f7f..68d0792 100644 --- a/signing/watch/config.go +++ b/signing/watch/config.go @@ -54,8 +54,8 @@ type NotifyAdminConfig struct { SendEmailInterval int `json:"send_email_interval"` NotifyCorpAdminInterval int `json:"notify_corp_admin_interval"` NotifyIndividualInterval int `json:"notify_individual_interval"` - NotifyIndividualRemindDays int `json:"notify_individual_remind_days"` - NotifyCorpAdminRemindDays int `json:"notify_corp_admin_remind_days"` + NotifyIndividualRemindDays *int `json:"notify_individual_remind_days"` + NotifyCorpAdminRemindDays *int `json:"notify_corp_admin_remind_days"` NotifyBatchSize int `json:"notify_batch_size"` EnabledCommunityOrgs []string `json:"enabled_community_orgs"` NotifyEmailTo string `json:"notify_email_to"` @@ -74,12 +74,14 @@ func (cfg *NotifyAdminConfig) SetDefault() { cfg.NotifyIndividualInterval = 86400 } - if cfg.NotifyIndividualRemindDays <= 0 { - cfg.NotifyIndividualRemindDays = 90 + if cfg.NotifyIndividualRemindDays == nil { + v := 90 + cfg.NotifyIndividualRemindDays = &v } - if cfg.NotifyCorpAdminRemindDays <= 0 { - cfg.NotifyCorpAdminRemindDays = 90 + if cfg.NotifyCorpAdminRemindDays == nil { + v := 90 + cfg.NotifyCorpAdminRemindDays = &v } if cfg.NotifyBatchSize <= 0 { @@ -100,11 +102,11 @@ func (cfg *NotifyAdminConfig) genNotifyIndividualInterval() time.Duration { } func (cfg *NotifyAdminConfig) genNotifyIndividualRemindDays() int { - return cfg.NotifyIndividualRemindDays + return *cfg.NotifyIndividualRemindDays } func (cfg *NotifyAdminConfig) genNotifyCorpAdminRemindDays() int { - return cfg.NotifyCorpAdminRemindDays + return *cfg.NotifyCorpAdminRemindDays } func (cfg *NotifyAdminConfig) genNotifyBatchSize() int { diff --git a/signing/watch/notify_corp_admin_test.go b/signing/watch/notify_corp_admin_test.go index 27e9476..0a6fb27 100644 --- a/signing/watch/notify_corp_admin_test.go +++ b/signing/watch/notify_corp_admin_test.go @@ -9,10 +9,11 @@ import ( ) func newTestImpl() *notifyAdminWatchImpl { + remindDays := 90 return ¬ifyAdminWatchImpl{ config: &NotifyAdminConfig{ - NotifyCorpAdminRemindDays: 90, - NotifyIndividualRemindDays: 90, + NotifyCorpAdminRemindDays: &remindDays, + NotifyIndividualRemindDays: &remindDays, NotifyBatchSize: 500, }, defaultGracePeriodDays: 30, From 2cff2dc0e240da01cbcbcfec148466b461e9f80e Mon Sep 17 00:00:00 2001 From: JavaPythonAIForBAT Date: Thu, 16 Jul 2026 12:48:48 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=E5=90=AF=E5=8A=A8=E6=97=B6?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E9=80=9A=E7=9F=A5=E9=85=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E9=99=85=E5=8A=A0=E8=BD=BD=E5=80=BC=EF=BC=8C=E4=BE=BF?= =?UTF-8?q?=E4=BA=8E=E9=AA=8C=E8=AF=81=20YAML=20=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E7=94=9F=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- signing/watch/notify_corp_admin.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/signing/watch/notify_corp_admin.go b/signing/watch/notify_corp_admin.go index f6b87b9..b0cc61b 100644 --- a/signing/watch/notify_corp_admin.go +++ b/signing/watch/notify_corp_admin.go @@ -30,6 +30,14 @@ func NotifyAdminWatchStart(cfg *NotifyAdminConfig, lk repoLink, corp corpSigning individualTrigger: make(chan struct{}, 1), } + logs.Info("notify admin watch config: interval_corp=%ds interval_ind=%ds remind_corp=%dd remind_ind=%dd batch=%d communities=%v", + int(cfg.genNotifyCorpAdminInterval().Seconds()), + int(cfg.genNotifyIndividualInterval().Seconds()), + cfg.genNotifyCorpAdminRemindDays(), + cfg.genNotifyIndividualRemindDays(), + cfg.genNotifyBatchSize(), + cfg.EnabledCommunityOrgs) + notifyAdminWatchInstance.start() } From 85a56aaf4e56d5740a314cea4fff6cd0ae43fbfd Mon Sep 17 00:00:00 2001 From: JavaPythonAIForBAT Date: Thu, 16 Jul 2026 16:14:22 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20TriggerNotify=E6=8C=89=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E8=A7=A6=E5=8F=91=20+=20AgreeNewCLA=E7=94=A8=E7=AD=BE?= =?UTF-8?q?=E7=BD=B2=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TriggerNotify(t) 只触发对应CLA类型的扫描器,不再同时触发两边 - Update中 SetPendingCLAForLink 仅企业CLA更新时标记 - AgreeNewCLA 从签署记录获取语言,不再信任前端传入的CLAId - 新增 getEffectiveGracePeriodDays/getLatestCorpClaId/getLatestIndividualClaId 测试辅助方法 - 修复 test 默认值 1200→86400 --- signing/app/cla.go | 8 +- signing/app/individual_signing.go | 11 +- signing/watch/config_test.go | 156 ++++++++++++++--------------- signing/watch/notify_corp_admin.go | 40 ++++++-- 4 files changed, 116 insertions(+), 99 deletions(-) diff --git a/signing/app/cla.go b/signing/app/cla.go index 1e41b4a..8fb6420 100644 --- a/signing/app/cla.go +++ b/signing/app/cla.go @@ -62,11 +62,13 @@ func (s *claService) Update(cmd *CmdToUpdateCLA) error { return err } - if err = s.cs.SetPendingCLAForLink(cmd.LinkId, cla.Id); err != nil { - logs.Error("set pending CLA for link failed: %s, err: %v", cmd.LinkId, err) + if !dp.IsCLATypeIndividual(cla.Type) { + if err = s.cs.SetPendingCLAForLink(cmd.LinkId, cla.Id); err != nil { + logs.Error("set pending CLA for link failed: %s, err: %v", cmd.LinkId, err) + } } - watch.TriggerNotify() + watch.TriggerNotify(cla.Type) return nil } diff --git a/signing/app/individual_signing.go b/signing/app/individual_signing.go index a002d2a..02953da 100644 --- a/signing/app/individual_signing.go +++ b/signing/app/individual_signing.go @@ -86,16 +86,17 @@ func (s *individualSigningService) AgreeNewCLA(cmd *CmdToSignIndividualCLA) erro return err } - if !s.cla.ContainsCla(cmd.Link.Id, cmd.Link.CLAId) { - return domain.NewDomainError(domain.ErrorCodeCLANotExists) - } - sign, err := s.repo.Find(cmd.Link.Id, cmd.Rep.EmailAddr) if err != nil { return err } - if err = sign.AgreeNewCLA(cmd.Link.CLAId); err != nil { + latestClaId := s.cla.GetClaId(sign.Link.Id, dp.CLATypeIndividual, sign.Link.Language) + if latestClaId == "" { + return domain.NewDomainError(domain.ErrorCodeCLANotExists) + } + + if err = sign.AgreeNewCLA(latestClaId); err != nil { return err } diff --git a/signing/watch/config_test.go b/signing/watch/config_test.go index ce48cd5..285114f 100644 --- a/signing/watch/config_test.go +++ b/signing/watch/config_test.go @@ -9,6 +9,34 @@ import ( "github.com/opensourceways/app-cla-server/signing/domain/repository" ) +func TestGetEffectiveGracePeriodDays(t *testing.T) { + impl := ¬ifyAdminWatchImpl{defaultGracePeriodDays: 30} + intPtr := func(v int) *int { return &v } + + tests := []struct { + name string + linkDays *int + defaultDay int + want int + }{ + {"link nil (字段缺失) uses default", nil, 30, 30}, + {"link override positive", intPtr(15), 30, 15}, + {"link zero returns zero", intPtr(0), 30, 0}, + {"link negative uses default", intPtr(-1), 30, 30}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + link := &repository.LinkCLA{GracePeriodDays: tt.linkDays} + impl.defaultGracePeriodDays = tt.defaultDay + got := impl.getEffectiveGracePeriodDays(link) + if got != tt.want { + t.Errorf("got %d, want %d", got, tt.want) + } + }) + } +} + func TestIsCorpSigningLatest(t *testing.T) { impl := ¬ifyAdminWatchImpl{} @@ -28,68 +56,47 @@ func TestIsCorpSigningLatest(t *testing.T) { } } -func TestGetLatestIndividualCLA(t *testing.T) { +func TestGetLatestCorpClaId(t *testing.T) { impl := ¬ifyAdminWatchImpl{} - clas := []domain.CLA{ - {Id: "10", Type: dp.CLATypeCorp, Language: dp.CreateLanguage("en")}, - {Id: "11", Type: dp.CLATypeIndividual, Language: dp.CreateLanguage("en")}, - {Id: "12", Type: dp.CLATypeIndividual, Language: dp.CreateLanguage("zh")}, + link := &repository.LinkCLA{ + Clas: []domain.CLA{ + {Id: "10", Type: dp.CLATypeCorp, Language: dp.CreateLanguage("en")}, + {Id: "11", Type: dp.CLATypeIndividual, Language: dp.CreateLanguage("en")}, + {Id: "12", Type: dp.CLATypeCorp, Language: dp.CreateLanguage("zh")}, + }, } - if cla := impl.getLatestIndividualCLA(clas, dp.CreateLanguage("en")); cla == nil || cla.Id != "11" { - t.Errorf("getLatestIndividualCLA en: got %v, want id=11", cla) + if id := impl.getLatestCorpClaId(link, dp.CreateLanguage("en")); id != "10" { + t.Errorf("getLatestCorpClaId en: got %s, want 10", id) } - if cla := impl.getLatestIndividualCLA(clas, dp.CreateLanguage("zh")); cla == nil || cla.Id != "12" { - t.Errorf("getLatestIndividualCLA zh: got %v, want id=12", cla) + if id := impl.getLatestCorpClaId(link, dp.CreateLanguage("zh")); id != "12" { + t.Errorf("getLatestCorpClaId zh: got %s, want 12", id) } - if cla := impl.getLatestIndividualCLA(clas, dp.CreateLanguage("fr")); cla != nil { - t.Errorf("getLatestIndividualCLA fr: got %v, want nil", cla) + if id := impl.getLatestCorpClaId(link, dp.CreateLanguage("fr")); id != "" { + t.Errorf("getLatestCorpClaId fr: got %s, want empty", id) } } -func TestIsIndividualSigningLatest(t *testing.T) { +func TestGetLatestIndividualClaId(t *testing.T) { impl := ¬ifyAdminWatchImpl{} - clas := []domain.CLA{ - {Id: "10", Type: dp.CLATypeIndividual, Language: dp.CreateLanguage("en")}, - {Id: "11", Type: dp.CLATypeCorp, Language: dp.CreateLanguage("en")}, + link := &repository.LinkCLA{ + Clas: []domain.CLA{ + {Id: "10", Type: dp.CLATypeCorp, Language: dp.CreateLanguage("en")}, + {Id: "11", Type: dp.CLATypeIndividual, Language: dp.CreateLanguage("en")}, + {Id: "12", Type: dp.CLATypeIndividual, Language: dp.CreateLanguage("zh")}, + }, } - signedInfo := domain.CLAInfo{CLAId: "10", Language: dp.CreateLanguage("en")} - if !impl.isIndividualSigningLatest(clas, signedInfo) { - t.Error("isIndividualSigningLatest should return true for matching CLA") - } - - signedInfo2 := domain.CLAInfo{CLAId: "9", Language: dp.CreateLanguage("en")} - if impl.isIndividualSigningLatest(clas, signedInfo2) { - t.Error("isIndividualSigningLatest should return false for outdated CLA") + if id := impl.getLatestIndividualClaId(link, dp.CreateLanguage("en")); id != "11" { + t.Errorf("getLatestIndividualClaId en: got %s, want 11", id) } -} - -func TestGetEffectiveGracePeriodDays(t *testing.T) { - intPtr := func(v int) *int { return &v } - - tests := []struct { - name string - linkDays *int - defaultDay int - want int - }{ - {"link nil uses default", nil, 30, 30}, - {"link override positive", intPtr(15), 30, 15}, - {"link zero returns zero", intPtr(0), 30, 0}, - {"link negative uses default", intPtr(-1), 30, 30}, + if id := impl.getLatestIndividualClaId(link, dp.CreateLanguage("zh")); id != "12" { + t.Errorf("getLatestIndividualClaId zh: got %s, want 12", id) } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - link := &repository.LinkCLA{GracePeriodDays: tt.linkDays} - got := link.GetEffectiveGracePeriodDays(tt.defaultDay) - if got != tt.want { - t.Errorf("got %d, want %d", got, tt.want) - } - }) + if id := impl.getLatestIndividualClaId(link, dp.CreateLanguage("fr")); id != "" { + t.Errorf("getLatestIndividualClaId fr: got %s, want empty", id) } } @@ -100,24 +107,13 @@ func TestNotifyAdminConfigSetDefault(t *testing.T) { if cfg.SendEmailInterval != 10 { t.Errorf("SendEmailInterval: got %d, want 10", cfg.SendEmailInterval) } - if cfg.NotifyCorpAdminInterval != 1200 { - t.Errorf("NotifyCorpAdminInterval: got %d, want 1200", cfg.NotifyCorpAdminInterval) - } - if cfg.NotifyIndividualInterval != 86400 { - t.Errorf("NotifyIndividualInterval: got %d, want 86400", cfg.NotifyIndividualInterval) - } - if cfg.NotifyIndividualRemindDays != 90 { - t.Errorf("NotifyIndividualRemindDays: got %d, want 90", cfg.NotifyIndividualRemindDays) + if cfg.NotifyCorpAdminInterval != 86400 { + t.Errorf("NotifyCorpAdminInterval: got %d, want 86400", cfg.NotifyCorpAdminInterval) } } func TestNotifyAdminConfigSetDefaultPreserves(t *testing.T) { - cfg := NotifyAdminConfig{ - SendEmailInterval: 20, - NotifyCorpAdminInterval: 600, - NotifyIndividualInterval: 43200, - NotifyIndividualRemindDays: 45, - } + cfg := NotifyAdminConfig{SendEmailInterval: 20, NotifyCorpAdminInterval: 600} cfg.SetDefault() if cfg.SendEmailInterval != 20 { @@ -126,12 +122,6 @@ func TestNotifyAdminConfigSetDefaultPreserves(t *testing.T) { if cfg.NotifyCorpAdminInterval != 600 { t.Errorf("NotifyCorpAdminInterval should be preserved: got %d, want 600", cfg.NotifyCorpAdminInterval) } - if cfg.NotifyIndividualInterval != 43200 { - t.Errorf("NotifyIndividualInterval should be preserved: got %d, want 43200", cfg.NotifyIndividualInterval) - } - if cfg.NotifyIndividualRemindDays != 45 { - t.Errorf("NotifyIndividualRemindDays should be preserved: got %d, want 45", cfg.NotifyIndividualRemindDays) - } } func TestNotifyAdminConfigGenSendEmailInterval(t *testing.T) { @@ -150,22 +140,6 @@ func TestNotifyAdminConfigGenNotifyCorpAdminInterval(t *testing.T) { } } -func TestNotifyAdminConfigGenNotifyIndividualInterval(t *testing.T) { - cfg := NotifyAdminConfig{NotifyIndividualInterval: 200} - d := cfg.genNotifyIndividualInterval() - if d != 200*time.Second { - t.Errorf("genNotifyIndividualInterval: got %v, want 200s", d) - } -} - -func TestNotifyAdminConfigGenNotifyIndividualRemindDays(t *testing.T) { - cfg := NotifyAdminConfig{NotifyIndividualRemindDays: 7} - d := cfg.genNotifyIndividualRemindDays() - if d != 7 { - t.Errorf("genNotifyIndividualRemindDays: got %d, want 7", d) - } -} - func TestCLAUpdateConfigSetDefault(t *testing.T) { cfg := CLAUpdateConfig{} cfg.SetDefault() @@ -221,3 +195,19 @@ func TestWatchConfigSetDefaultPreserves(t *testing.T) { t.Errorf("Interval preserved: got %d, want 7200", cfg.Interval) } } + +func TestWatchConfigConfigItems(t *testing.T) { + cfg := Config{} + items := cfg.ConfigItems() + if len(items) != 2 { + t.Errorf("ConfigItems length: got %d, want 2", len(items)) + } +} + +func TestWatchConfigIntervalDuration(t *testing.T) { + cfg := Config{Interval: 60} + d := cfg.intervalDuration() + if d != 60*time.Second { + t.Errorf("intervalDuration: got %v, want 60s", d) + } +} diff --git a/signing/watch/notify_corp_admin.go b/signing/watch/notify_corp_admin.go index b0cc61b..500dcb0 100644 --- a/signing/watch/notify_corp_admin.go +++ b/signing/watch/notify_corp_admin.go @@ -50,18 +50,22 @@ func NotifyAdminWatchStop() { } // TriggerNotify 立即触发一次通知扫描,不等待定时器周期。 -// CLA 更新后应调用此函数,确保企业和个人都在最短时间内收到通知。 -func TriggerNotify() { +// t 指定要触发的 CLA 类型,只触发对应类型的扫描器。 +func TriggerNotify(t dp.CLAType) { if notifyAdminWatchInstance == nil { return } - select { - case notifyAdminWatchInstance.corpTrigger <- struct{}{}: - default: + if !dp.IsCLATypeIndividual(t) { + select { + case notifyAdminWatchInstance.corpTrigger <- struct{}{}: + default: + } } - select { - case notifyAdminWatchInstance.individualTrigger <- struct{}{}: - default: + if dp.IsCLATypeIndividual(t) { + select { + case notifyAdminWatchInstance.individualTrigger <- struct{}{}: + default: + } } } @@ -516,3 +520,23 @@ func (impl *notifyAdminWatchImpl) nextDelay(interval time.Duration) time.Duratio } return interval } + +func (impl *notifyAdminWatchImpl) getEffectiveGracePeriodDays(link *repository.LinkCLA) int { + return link.GetEffectiveGracePeriodDays(impl.defaultGracePeriodDays) +} + +func (impl *notifyAdminWatchImpl) getLatestCorpClaId(link *repository.LinkCLA, language dp.Language) string { + cla := impl.getLatestCorpCLA(link.Clas, language) + if cla == nil { + return "" + } + return cla.Id +} + +func (impl *notifyAdminWatchImpl) getLatestIndividualClaId(link *repository.LinkCLA, language dp.Language) string { + cla := impl.getLatestIndividualCLA(link.Clas, language) + if cla == nil { + return "" + } + return cla.Id +} From 0e09f00c540115ab083a320d2cffbdbff3d72a61 Mon Sep 17 00:00:00 2001 From: JavaPythonAIForBAT Date: Thu, 16 Jul 2026 16:19:34 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=E9=82=AE=E4=BB=B6=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=20URL=20=E5=90=8E=E5=A4=9A=E4=BD=99=E5=8F=A5=E5=8F=B7=E7=A7=BB?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/email-template/cla-updated.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/email-template/cla-updated.tmpl b/conf/email-template/cla-updated.tmpl index 5992c62..805224f 100644 --- a/conf/email-template/cla-updated.tmpl +++ b/conf/email-template/cla-updated.tmpl @@ -2,7 +2,7 @@ Dear {{.AdminName}}, A new version of CLA is published. Please login to the CLA management system to see the details. -The CLA management system login URL is {{.URLOfCLAPlatform}}. +The CLA management system login URL is {{.URLOfCLAPlatform}} Have questions or need help? Just reply to this email and the {{.Org}} Community Support Team will help you sort it out.