Skip to content

Commit fc90a6f

Browse files
committed
fix dto
1 parent 1d8ad6f commit fc90a6f

17 files changed

Lines changed: 570 additions & 69 deletions

backend/modules/evaluation/application/convertor/experiment/expt_template.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,13 +642,34 @@ func exptSourceDTO2DO(dto *domain_expt.ExptSource) *entity.ExptSource {
642642
if dto == nil {
643643
return nil
644644
}
645+
// 注意:SpanFilterFields / Sampler 由 service 层的 enrichExptSourceFromPipeline
646+
// 从 Pipeline.Flow 反填,不应从用户请求 body 接受;此处仅承载 SourceType/SourceID/Scheduler/TimeRange。
645647
return &entity.ExptSource{
646648
SourceType: entity.SourceType(gptr.Indirect(dto.SourceType)),
647649
SourceID: gptr.Indirect(dto.SourceID),
650+
Scheduler: exptSchedulerDTO2DO(dto.Scheduler),
648651
TimeRange: taskTimeRangeDTO2DO(dto.TimeRange),
649652
}
650653
}
651654

655+
// exptSchedulerDTO2DO 将 domain_expt.Scheduler 转为 entity.ExptSchedulerDO
656+
func exptSchedulerDTO2DO(dto *domain_expt.Scheduler) *entity.ExptSchedulerDO {
657+
if dto == nil {
658+
return nil
659+
}
660+
do := &entity.ExptSchedulerDO{
661+
Enabled: dto.Enabled,
662+
TriggerAt: dto.TriggerAt,
663+
StartTime: dto.StartTime,
664+
EndTime: dto.EndTime,
665+
}
666+
if dto.Frequency != nil {
667+
f := *dto.Frequency
668+
do.Frequency = &f
669+
}
670+
return do
671+
}
672+
652673
// ConvertUpdateExptTemplateMetaReq 转换更新实验模板 Meta 请求为实体参数
653674
func ConvertUpdateExptTemplateMetaReq(req *expt.UpdateExperimentTemplateMetaRequest) (*entity.UpdateExptTemplateMetaParam, error) {
654675
param := &entity.UpdateExptTemplateMetaParam{
@@ -1261,6 +1282,10 @@ func ConvertUpdateExptTemplateReq(req *expt.UpdateExperimentTemplateRequest) (*e
12611282
if req.GetExptInfo() != nil && req.GetExptInfo().IsSetCronActivate() {
12621283
param.CronActivate = gptr.Of(req.GetExptInfo().GetCronActivate())
12631284
}
1285+
// 显式提供 expt_source 时才覆盖;nil 由 service 层保留 DB 中已有值(包括 Scheduler)
1286+
if req.IsSetExptSource() {
1287+
param.ExptSource = exptSourceDTO2DO(req.GetExptSource())
1288+
}
12641289

12651290
// 从 triple_config 中提取三元组配置(注意:eval_set_id / target_id 不允许修改,仅允许调整版本与配置)
12661291
if req.GetTripleConfig() != nil {

backend/modules/evaluation/application/convertor/experiment/expt_template_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,48 @@ func TestConvertCreateExptTemplateReq_ExptSourceInTemplateConf(t *testing.T) {
5252
}
5353
}
5454

55+
// 回归:exptSourceDTO2DO 必须保留 Scheduler 字段,否则创建/更新接口响应里 Scheduler 会丢失。
56+
func TestExptSourceDTO2DO_PreservesScheduler(t *testing.T) {
57+
t.Parallel()
58+
59+
dto := &domain_expt.ExptSource{
60+
SourceType: gptr.Of(domain_expt.SourceType_Evaluation),
61+
SourceID: gptr.Of("pipe-9"),
62+
Scheduler: &domain_expt.Scheduler{
63+
Enabled: gptr.Of(true),
64+
Frequency: gptr.Of("every_day"),
65+
TriggerAt: gptr.Of(int64(1777392000)),
66+
StartTime: gptr.Of(int64(1777452450)),
67+
EndTime: gptr.Of(int64(1777538850)),
68+
},
69+
}
70+
71+
do := exptSourceDTO2DO(dto)
72+
if assert.NotNil(t, do) && assert.NotNil(t, do.Scheduler) {
73+
assert.Equal(t, true, gptr.Indirect(do.Scheduler.Enabled))
74+
if assert.NotNil(t, do.Scheduler.Frequency) {
75+
assert.Equal(t, "every_day", *do.Scheduler.Frequency)
76+
}
77+
assert.Equal(t, int64(1777392000), gptr.Indirect(do.Scheduler.TriggerAt))
78+
assert.Equal(t, int64(1777452450), gptr.Indirect(do.Scheduler.StartTime))
79+
assert.Equal(t, int64(1777538850), gptr.Indirect(do.Scheduler.EndTime))
80+
}
81+
}
82+
83+
// 回归:exptSourceDTO2DO 在 Scheduler 为 nil 时不能 panic 且不写入伪空对象。
84+
func TestExptSourceDTO2DO_NilScheduler(t *testing.T) {
85+
t.Parallel()
86+
87+
dto := &domain_expt.ExptSource{
88+
SourceType: gptr.Of(domain_expt.SourceType_Evaluation),
89+
SourceID: gptr.Of("pipe-9"),
90+
}
91+
do := exptSourceDTO2DO(dto)
92+
if assert.NotNil(t, do) {
93+
assert.Nil(t, do.Scheduler)
94+
}
95+
}
96+
5597
func TestBuildTemplateConfForCreate_WithExptSourceOnly(t *testing.T) {
5698
t.Parallel()
5799

backend/modules/evaluation/application/experiment_app.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -814,23 +814,32 @@ func (e *experimentApplication) CheckExperimentTemplateName(ctx context.Context,
814814

815815
session := entity.NewSession(ctx)
816816

817-
// 如果传了 template_id,且名称与当前模板名称相同,则直接返回可用
817+
// expt_type 用于在线/离线模板隔离判重,优先级:请求显式 -> 已有模板 -> 0(兼容)
818+
exptType := entity.ExptType(req.GetExptType())
819+
820+
// 如果传了 template_id,且名称与当前模板名称相同,则直接返回可用;
821+
// 同时用现有模板的 expt_type 兜底未显式传 expt_type 的场景
818822
if req.IsSetTemplateID() && req.GetTemplateID() > 0 {
819823
tpl, err := e.templateManager.Get(ctx, req.GetTemplateID(), req.GetWorkspaceID(), session)
820824
if err != nil {
821825
return nil, err
822826
}
823-
if tpl != nil && tpl.Meta != nil && tpl.Meta.Name == req.GetName() {
824-
isAvailable := true
825-
return &expt.CheckExperimentTemplateNameResponse{
826-
IsAvailable: &isAvailable,
827-
BaseResp: base.NewBaseResp(),
828-
}, nil
827+
if tpl != nil && tpl.Meta != nil {
828+
if exptType == 0 {
829+
exptType = tpl.Meta.ExptType
830+
}
831+
if tpl.Meta.Name == req.GetName() {
832+
isAvailable := true
833+
return &expt.CheckExperimentTemplateNameResponse{
834+
IsAvailable: &isAvailable,
835+
BaseResp: base.NewBaseResp(),
836+
}, nil
837+
}
829838
}
830839
}
831840

832-
// 否则走正常的重名校验:模板名在该空间下是否已存在
833-
pass, err := e.templateManager.CheckName(ctx, req.GetName(), req.GetWorkspaceID(), session)
841+
// 否则走正常的重名校验:模板名在该空间 + 同 expt_type 下是否已存在
842+
pass, err := e.templateManager.CheckName(ctx, req.GetName(), req.GetWorkspaceID(), exptType, session)
834843
if err != nil {
835844
return nil, err
836845
}

backend/modules/evaluation/application/experiment_app_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ func TestExperimentApplication_CheckExperimentTemplateName(t *testing.T) {
11611161
},
11621162
mockSetup: func() {
11631163
mockTemplateManager.EXPECT().
1164-
CheckName(gomock.Any(), templateName, workspaceID, &entity.Session{}).
1164+
CheckName(gomock.Any(), templateName, workspaceID, gomock.Any(), &entity.Session{}).
11651165
Return(true, nil)
11661166
mockAuth.EXPECT().
11671167
Authorization(
@@ -1189,7 +1189,7 @@ func TestExperimentApplication_CheckExperimentTemplateName(t *testing.T) {
11891189
},
11901190
mockSetup: func() {
11911191
mockTemplateManager.EXPECT().
1192-
CheckName(gomock.Any(), templateName, workspaceID, &entity.Session{}).
1192+
CheckName(gomock.Any(), templateName, workspaceID, gomock.Any(), &entity.Session{}).
11931193
Return(false, nil)
11941194
mockAuth.EXPECT().
11951195
Authorization(
@@ -1262,7 +1262,7 @@ func TestExperimentApplication_CheckExperimentTemplateName(t *testing.T) {
12621262
},
12631263
}, nil)
12641264
mockTemplateManager.EXPECT().
1265-
CheckName(gomock.Any(), "other_name", workspaceID, &entity.Session{}).
1265+
CheckName(gomock.Any(), "other_name", workspaceID, gomock.Any(), &entity.Session{}).
12661266
Return(true, nil)
12671267
mockAuth.EXPECT().
12681268
Authorization(

backend/modules/evaluation/domain/entity/expt_template.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ type UpdateExptTemplateParam struct {
439439
ExptType ExptType
440440
CronActivate *bool // nil 表示不修改
441441
CreateEvalTargetParam *CreateEvalTargetParam
442+
// ExptSource 实验来源信息;nil 表示不修改,由 service 层保留 DB 已有值
443+
ExptSource *ExptSource
442444
}
443445

444446
// UpdateExptTemplateMetaParam 更新实验模板 Meta 参数

backend/modules/evaluation/domain/repo/expt_template.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ type IExptTemplateRepo interface {
1414
Create(ctx context.Context, template *entity.ExptTemplate, refs []*entity.ExptTemplateEvaluatorRef) error
1515
// GetByID 按模板ID获取模板,如果 spaceID 非空则校验空间ID;spaceID 为空时不校验空间ID
1616
GetByID(ctx context.Context, id int64, spaceID *int64) (*entity.ExptTemplate, error)
17-
GetByName(ctx context.Context, name string, spaceID int64) (*entity.ExptTemplate, bool, error)
17+
// GetByName 按空间 + 名称查询模板;exptType > 0 时按 expt_type 隔离判重,
18+
// 在线/离线模板互不影响;exptType = 0 时跨类型查询,保留旧调用兼容性。
19+
GetByName(ctx context.Context, name string, spaceID int64, exptType entity.ExptType) (*entity.ExptTemplate, bool, error)
1820
MGetByID(ctx context.Context, ids []int64, spaceID int64) ([]*entity.ExptTemplate, error)
1921
Update(ctx context.Context, template *entity.ExptTemplate) error
2022
UpdateFields(ctx context.Context, templateID int64, ufields map[string]any) error

backend/modules/evaluation/domain/repo/mocks/expt_template.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/modules/evaluation/domain/service/expt_template.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import (
1111

1212
//go:generate mockgen -destination ./mocks/expt_template.go --package mocks . IExptTemplateManager
1313
type IExptTemplateManager interface {
14-
CheckName(ctx context.Context, name string, spaceID int64, session *entity.Session) (bool, error)
14+
// CheckName 校验模板名是否可用;exptType 用于在线/离线模板隔离判重,
15+
// 传 0 时跨类型查询(兼容旧调用),传具体类型时仅在同类型范围内判重。
16+
CheckName(ctx context.Context, name string, spaceID int64, exptType entity.ExptType, session *entity.Session) (bool, error)
1517
Create(ctx context.Context, param *entity.CreateExptTemplateParam, session *entity.Session) (*entity.ExptTemplate, error)
1618
Get(ctx context.Context, templateID, spaceID int64, session *entity.Session) (*entity.ExptTemplate, error)
1719
MGet(ctx context.Context, templateIDs []int64, spaceID int64, session *entity.Session) ([]*entity.ExptTemplate, error)

backend/modules/evaluation/domain/service/expt_template_impl.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,17 @@ type ExptTemplateManagerImpl struct {
7575
scheduleAdapter rpc.IExptScheduleAdapter
7676
}
7777

78-
func (e *ExptTemplateManagerImpl) CheckName(ctx context.Context, name string, spaceID int64, session *entity.Session) (bool, error) {
79-
_, exists, err := e.templateRepo.GetByName(ctx, name, spaceID)
78+
func (e *ExptTemplateManagerImpl) CheckName(ctx context.Context, name string, spaceID int64, exptType entity.ExptType, session *entity.Session) (bool, error) {
79+
_, exists, err := e.templateRepo.GetByName(ctx, name, spaceID, exptType)
8080
if err != nil {
8181
return false, err
8282
}
8383
return !exists, nil
8484
}
8585

8686
func (e *ExptTemplateManagerImpl) Create(ctx context.Context, param *entity.CreateExptTemplateParam, session *entity.Session) (*entity.ExptTemplate, error) {
87-
// 验证名称
88-
pass, err := e.CheckName(ctx, param.Name, param.SpaceID, session)
87+
// 验证名称:按 expt_type 隔离,避免在线/离线模板互相判重
88+
pass, err := e.CheckName(ctx, param.Name, param.SpaceID, param.ExptType, session)
8989
if !pass {
9090
return nil, errorx.NewByCode(errno.ExperimentNameExistedCode, errorx.WithExtraMsg(fmt.Sprintf("template name %s already exists", param.Name)))
9191
}
@@ -265,9 +265,9 @@ func (e *ExptTemplateManagerImpl) Update(ctx context.Context, param *entity.Upda
265265
return nil, errorx.NewByCode(errno.ResourceNotFoundCode, errorx.WithExtraMsg(fmt.Sprintf("template %d not found", param.TemplateID)))
266266
}
267267

268-
// 如果名称改变,检查新名称是否可用(允许和当前名称重复)
268+
// 如果名称改变,检查新名称是否可用(允许和当前名称重复);按现有模板的 expt_type 隔离判重
269269
if param.Name != "" && param.Name != existingTemplate.GetName() {
270-
pass, err := e.CheckName(ctx, param.Name, param.SpaceID, session)
270+
pass, err := e.CheckName(ctx, param.Name, param.SpaceID, existingTemplate.Meta.ExptType, session)
271271
if !pass {
272272
return nil, errorx.NewByCode(errno.ExperimentNameExistedCode, errorx.WithExtraMsg(fmt.Sprintf("template name %s already exists", param.Name)))
273273
}
@@ -436,6 +436,15 @@ func (e *ExptTemplateManagerImpl) Update(ctx context.Context, param *entity.Upda
436436
}
437437
}
438438

439+
// 显式传入 expt_source(含 Scheduler 等)时覆盖到 TemplateConf;
440+
// 当 templateConf 还未初始化(请求未带字段映射等),克隆现有再写入,避免污染其他字段。
441+
if param.ExptSource != nil {
442+
if updatedTemplate.TemplateConf == nil {
443+
updatedTemplate.TemplateConf = &entity.ExptTemplateConfiguration{}
444+
}
445+
updatedTemplate.TemplateConf.ExptSource = param.ExptSource
446+
}
447+
439448
// 从 TemplateConf 构建 FieldMappingConfig,并根据 EvaluatorConf.ScoreWeight 设置是否启用分数权重
440449
e.buildFieldMappingConfigAndEnableScoreWeight(updatedTemplate, updatedTemplate.TemplateConf)
441450

@@ -489,9 +498,9 @@ func (e *ExptTemplateManagerImpl) UpdateMeta(ctx context.Context, param *entity.
489498
return nil, errorx.NewByCode(errno.ResourceNotFoundCode, errorx.WithExtraMsg(fmt.Sprintf("template %d not found", param.TemplateID)))
490499
}
491500

492-
// 如果名称改变,检查新名称是否可用(允许和当前名称重复)
501+
// 如果名称改变,检查新名称是否可用(允许和当前名称重复);按现有模板的 expt_type 隔离判重
493502
if param.Name != "" && param.Name != existingTemplate.GetName() {
494-
pass, err := e.CheckName(ctx, param.Name, param.SpaceID, session)
503+
pass, err := e.CheckName(ctx, param.Name, param.SpaceID, existingTemplate.Meta.ExptType, session)
495504
if !pass {
496505
return nil, errorx.NewByCode(errno.ExperimentNameExistedCode, errorx.WithExtraMsg(fmt.Sprintf("template name %s already exists", param.Name)))
497506
}

0 commit comments

Comments
 (0)