From 88862b2e2fb3031802b92dab6865b1c4cce56230 Mon Sep 17 00:00:00 2001 From: wangjingyu Date: Wed, 31 Jan 2024 15:38:55 +0800 Subject: [PATCH] refactor: delete iterations and their sub items together feat: initialize issueService feat: add option to delete only iterations or all feat: add BatchUpdateIssueIterationID feat: batch deletion of functions and addition of transactions feat: add BatchDeleteIssueByIterationID function feat: expose interfaces refactor: Transaction validation and change the parameter transfer method --- .../batchOperationTipModal/model.go | 4 +- .../batchOperationTipModal/render.go | 3 +- internal/apps/dop/endpoints/endpoints.go | 9 +++ internal/apps/dop/endpoints/iteration.go | 21 +++++++ internal/apps/dop/initialize.go | 1 + .../apps/dop/providers/issue/core/service.go | 56 +++++++++++++------ internal/apps/dop/providers/issue/dao/core.go | 26 +++++++++ .../apps/dop/services/iteration/iteration.go | 22 -------- 8 files changed, 101 insertions(+), 41 deletions(-) diff --git a/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/model.go b/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/model.go index 9bfb2b84941..2eaffde7db2 100644 --- a/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/model.go +++ b/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/model.go @@ -18,8 +18,8 @@ import ( "context" "github.com/erda-project/erda-infra/providers/component-protocol/cptype" - "github.com/erda-project/erda-proto-go/dop/issue/core/pb" "github.com/erda-project/erda/bundle" + "github.com/erda-project/erda/internal/apps/dop/providers/issue/core" ) type BatchOperationTipModal struct { @@ -30,7 +30,7 @@ type BatchOperationTipModal struct { SDK *cptype.SDK CtxBdl *bundle.Bundle ctx context.Context - issueSvc pb.IssueCoreServiceServer + issueSvc core.IssueService } type Props struct { diff --git a/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/render.go b/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/render.go index 733ea3af18d..12cd17a74e8 100644 --- a/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/render.go +++ b/internal/apps/dop/component-protocol/components/issue-manage/batchOperationTipModal/render.go @@ -26,6 +26,7 @@ import ( "github.com/erda-project/erda-infra/providers/component-protocol/utils/cputil" "github.com/erda-project/erda-proto-go/dop/issue/core/pb" "github.com/erda-project/erda/internal/apps/dop/component-protocol/types" + "github.com/erda-project/erda/internal/apps/dop/providers/issue/core" ) func init() { @@ -90,7 +91,7 @@ func (bot *BatchOperationTipModal) Render(ctx context.Context, c *cptype.Compone bot.ctx = ctx cputil.MustObjJSONTransfer(&c.State, &bot.State) bot.State.Visible = false - issueSvc := ctx.Value(types.IssueService).(pb.IssueCoreServiceServer) + issueSvc := ctx.Value(types.IssueService).(core.IssueService) bot.issueSvc = issueSvc _, err = bot.DeleteItems(bot.State.SelectedRowKeys, projectid) if err != nil { diff --git a/internal/apps/dop/endpoints/endpoints.go b/internal/apps/dop/endpoints/endpoints.go index fd29ef9eef4..606f6393039 100644 --- a/internal/apps/dop/endpoints/endpoints.go +++ b/internal/apps/dop/endpoints/endpoints.go @@ -33,6 +33,7 @@ import ( "github.com/erda-project/erda/bundle" "github.com/erda-project/erda/internal/apps/dop/dao" "github.com/erda-project/erda/internal/apps/dop/event" + "github.com/erda-project/erda/internal/apps/dop/providers/issue/core" issuequery "github.com/erda-project/erda/internal/apps/dop/providers/issue/core/query" issuedao "github.com/erda-project/erda/internal/apps/dop/providers/issue/dao" "github.com/erda-project/erda/internal/apps/dop/providers/projectpipeline" @@ -630,6 +631,7 @@ type Endpoints struct { namespace *namespace.Namespace envConfig *environment.EnvConfig issue *issue.Issue + issueService core.IssueService issueState *issuestate.IssueState workBench *workbench.Workbench iteration *iteration.Iteration @@ -895,6 +897,13 @@ func WithIssue(issue *issue.Issue) Option { } } +// WithIssueService Configure issueService +func WithIssueService(issueService *core.IssueService) Option { + return func(e *Endpoints) { + e.issueService = *issueService + } +} + func WithIssueState(state *issuestate.IssueState) Option { return func(e *Endpoints) { e.issueState = state diff --git a/internal/apps/dop/endpoints/iteration.go b/internal/apps/dop/endpoints/iteration.go index eb43c2067a4..af04c3f7e4e 100644 --- a/internal/apps/dop/endpoints/iteration.go +++ b/internal/apps/dop/endpoints/iteration.go @@ -29,6 +29,7 @@ import ( "github.com/erda-project/erda/internal/apps/dop/dao" "github.com/erda-project/erda/internal/apps/dop/services/apierrors" "github.com/erda-project/erda/internal/pkg/user" + "github.com/erda-project/erda/pkg/common/apis" "github.com/erda-project/erda/pkg/http/httpserver" "github.com/erda-project/erda/pkg/http/httpserver/errorresp" "github.com/erda-project/erda/pkg/strutil" @@ -205,6 +206,7 @@ func (e *Endpoints) DeleteIteration(ctx context.Context, r *http.Request, vars m } iterationID, err := strconv.ParseUint(vars["id"], 10, 64) + onlyIteration, err := strconv.ParseBool(r.URL.Query().Get("onlyIteration")) if err != nil { return apierrors.ErrDeleteIteration.InvalidParameter("id").ToResp(), nil } @@ -235,6 +237,25 @@ func (e *Endpoints) DeleteIteration(ctx context.Context, r *http.Request, vars m return errorresp.ErrResp(err) } + itemIDs, err := e.issueService.DBClient().GetIssuesIDByIterationID(iterationID) + if err != nil { + return apierrors.ErrDeleteIteration.InternalError(err).ToResp(), nil + } + + if len(itemIDs) > 0 { + ctx = apis.WithUserIDContext(ctx, identityInfo.UserID) + if onlyIteration { + err = e.issueService.BatchUpdateIssueIterationIDByIterationID(ctx, iterationID, -1) + if err != nil { + return apierrors.ErrDeleteIteration.InternalError(err).ToResp(), nil + } + } else { + err = e.issueService.BatchDeleteIssueByIterationID(ctx, iterationID) + if err != nil { + return apierrors.ErrDeleteIteration.InternalError(err).ToResp(), nil + } + } + } // delete relation labels if err = e.db.DeleteLabelRelations(apistructs.LabelTypeIteration, strconv.FormatUint(iterationID, 10), nil); err != nil { return apierrors.ErrDeleteIteration.InternalError(err).ToResp(), nil diff --git a/internal/apps/dop/initialize.go b/internal/apps/dop/initialize.go index e13c3d7cd4c..07a6bf655bb 100644 --- a/internal/apps/dop/initialize.go +++ b/internal/apps/dop/initialize.go @@ -674,6 +674,7 @@ func (p *provider) initEndpoints(db *dao.DBClient) (*endpoints.Endpoints, error) endpoints.WithNamespace(ns), endpoints.WithEnvConfig(env), endpoints.WithIssue(issue), + endpoints.WithIssueService(p.IssueCoreSvc), endpoints.WithIssueState(issueState), endpoints.WithIteration(itr), endpoints.WithPublisher(pub), diff --git a/internal/apps/dop/providers/issue/core/service.go b/internal/apps/dop/providers/issue/core/service.go index 97c4eeb6d09..8f0675ad2fd 100644 --- a/internal/apps/dop/providers/issue/core/service.go +++ b/internal/apps/dop/providers/issue/core/service.go @@ -42,6 +42,7 @@ import ( "github.com/erda-project/erda/internal/apps/dop/services/testcase" mttestplan "github.com/erda-project/erda/internal/apps/dop/services/testplan" "github.com/erda-project/erda/pkg/common/apis" + "github.com/erda-project/erda/pkg/database/dbengine" "github.com/erda-project/erda/pkg/strutil" ) @@ -554,16 +555,10 @@ func (i *IssueService) DeleteIssue(ctx context.Context, req *pb.DeleteIssueReque // BatchDeleteIssues 批量删除 func (i *IssueService) BatchDeleteIssues(ctx context.Context, req *pb.BatchDeleteIssueRequest) (*pb.BatchDeleteIssueResponse, error) { numSlice := make([]uint64, len(req.Ids)) - for i, str := range req.Ids { - num, err := strconv.ParseUint(str, 10, 64) - if err != nil { - return nil, apierrors.ErrBatchDeleteIssue.InvalidParameter(err) - } - numSlice[i] = num - } ids := make([]int64, len(req.Ids)) - for k, v := range numSlice { - ids[k] = int64(v) + for i, str := range req.Ids { + numSlice[i], _ = strconv.ParseUint(str, 10, 64) + ids[i] = int64(numSlice[i]) } identityInfo := apis.GetIdentityInfo(ctx) @@ -572,11 +567,15 @@ func (i *IssueService) BatchDeleteIssues(ctx context.Context, req *pb.BatchDelet } issues, _, err := i.query.Paging(pb.PagingIssueRequest{IDs: ids, ProjectID: req.ProjectID}) - if err != nil { return nil, err } + tx := i.db.Begin() + client := &dao.DBClient{ + DBEngine: &dbengine.DBEngine{DB: tx}, + } + for k, issue := range issues { rels, err := i.GetTestPlanCaseRels(uint64(issue.Id)) if err != nil { @@ -615,26 +614,31 @@ func (i *IssueService) BatchDeleteIssues(ctx context.Context, req *pb.BatchDelet // 删除测试计划用例关联 if issue.Type == pb.IssueTypeEnum_BUG { - if err := i.db.DeleteIssueTestCaseRelationsByIssueIDs([]uint64{numSlice[k]}); err != nil { + if err := client.DeleteIssueTestCaseRelationsByIssueIDs([]uint64{numSlice[k]}); err != nil { + tx.Rollback() return nil, apierrors.ErrBatchDeleteIssue.InternalError(err) } } } - if err := i.db.BatchCleanIssueRelation(numSlice); err != nil { + if err := client.BatchCleanIssueRelation(numSlice); err != nil { + tx.Rollback() return nil, apierrors.ErrBatchDeleteIssue.InternalError(err) } - if err := i.db.BatchDeletePropertyRelationByIssueID(ids); err != nil { + if err := client.BatchDeletePropertyRelationByIssueID(ids); err != nil { + tx.Rollback() return nil, apierrors.ErrBatchDeleteIssue.InternalError(err) } // delete issue state transition - if err = i.db.BatchDeleteIssuesStateTransition(numSlice); err != nil { + if err = client.BatchDeleteIssuesStateTransition(numSlice); err != nil { + tx.Rollback() return nil, apierrors.ErrBatchDeleteIssue.InternalError(err) } - if err = i.db.BatchDeleteIssues(numSlice); err != nil { + if err = client.BatchDeleteIssues(numSlice); err != nil { + tx.Rollback() return nil, err } @@ -646,10 +650,30 @@ func (i *IssueService) BatchDeleteIssues(ctx context.Context, req *pb.BatchDelet logrus.Errorf("update project active time err: %v", err) } } - + tx.Commit() return &pb.BatchDeleteIssueResponse{Data: issues}, nil } +// BatchDeleteIssueByIterationID 根据迭代 id 批量删除 issue +func (i *IssueService) BatchDeleteIssueByIterationID(ctx context.Context, iterationID uint64) (err error) { + identityInfo := apis.GetIdentityInfo(ctx) + if identityInfo == nil { + return apierrors.ErrUpdateIssue.NotLogin() + } + err = i.db.BatchDeleteIssueByIterationID(iterationID) + return +} + +// BatchUpdateIssueIterationIDByIterationID 根据迭代 id 批量更新 issue 的 iteration_id +func (i *IssueService) BatchUpdateIssueIterationIDByIterationID(ctx context.Context, iterationID uint64, ID int64) (err error) { + identityInfo := apis.GetIdentityInfo(ctx) + if identityInfo == nil { + return apierrors.ErrUpdateIssue.NotLogin() + } + err = i.db.BatchUpdateIssueIterationIDByIterationID(iterationID, ID) + return +} + func (i *IssueService) GetTestPlanCaseRels(issueID uint64) ([]*pb.TestPlanCaseRel, error) { // 查询关联的测试计划用例 testPlanCaseRels := make([]*pb.TestPlanCaseRel, 0) diff --git a/internal/apps/dop/providers/issue/dao/core.go b/internal/apps/dop/providers/issue/dao/core.go index 8d9cd6c3584..bf522ab54bc 100644 --- a/internal/apps/dop/providers/issue/dao/core.go +++ b/internal/apps/dop/providers/issue/dao/core.go @@ -161,6 +161,10 @@ func (client *DBClient) UpdateIssues(requirementID uint64, fields map[string]int Updates(fields).Error } +func (client *DBClient) BatchUpdateIssueIterationIDByIterationID(iterationID uint64, ID int64) error { + return client.Model(Issue{}).Where("deleted = 0").Where("iteration_id = ?", iterationID).Updates(map[string]interface{}{"iteration_id": ID}).Error +} + // UpdateIssueType 转换issueType func (client *DBClient) UpdateIssueType(issue *Issue) error { return client.Model(Issue{}).Save(issue).Error @@ -226,6 +230,19 @@ func (client *DBClient) BatchDeleteIssues(ids []uint64) error { return client.Model(Issue{}).Where("id IN (?)", ids).Update("deleted", 1).Error } +func (client *DBClient) BatchDeleteIssueByIterationID(iterationID uint64) error { + sql := fmt.Sprintf(` + UPDATE dice_issues AS dis + LEFT JOIN dice_issue_testcase_relations AS ditr ON ditr.issue_id = dis.id + LEFT JOIN dice_issue_relation AS dir ON dir.issue_id = dis.id + LEFT JOIN dice_issue_property_relation AS dipr ON dipr.issue_id = dis.id + LEFT JOIN erda_issue_state_transition AS eist ON eist.issue_id = dis.id + SET dis.deleted = 1 + WHERE dis.iteration_id = ?; + `) + return client.Exec(sql, iterationID).Error +} + // DeleteIssuesByRequirement . func (client *DBClient) DeleteIssuesByRequirement(requirementID uint64) error { return client.Model(Issue{}).Where("requirement_id = ?", requirementID).Update("deleted", 1).Error @@ -405,6 +422,15 @@ func (client *DBClient) GetIssueWithOutDelete(id int64) (Issue, error) { return issue, err } +func (client *DBClient) GetIssuesIDByIterationID(iterationID uint64) (ids []uint64, err error) { + var issues []Issue + err = client.Model(Issue{}).Where("deleted = 0").Where("iteration_id = ?", iterationID).Select("id").Find(&issues).Error + for _, v := range issues { + ids = append(ids, v.ID) + } + return ids, err +} + func (client *DBClient) ListIssueByIDs(issueIDs []int64) ([]Issue, error) { var issues []Issue if err := client.Where("`id` IN (?)", issueIDs).Find(&issues).Error; err != nil { diff --git a/internal/apps/dop/services/iteration/iteration.go b/internal/apps/dop/services/iteration/iteration.go index e06a5fc108c..15253970504 100644 --- a/internal/apps/dop/services/iteration/iteration.go +++ b/internal/apps/dop/services/iteration/iteration.go @@ -18,7 +18,6 @@ package iteration import ( "github.com/jinzhu/gorm" - "github.com/erda-project/erda-proto-go/dop/issue/core/pb" "github.com/erda-project/erda/apistructs" "github.com/erda-project/erda/internal/apps/dop/dao" "github.com/erda-project/erda/internal/apps/dop/providers/issue/core/query" @@ -159,27 +158,6 @@ func (itr *Iteration) GetByTitle(projectID uint64, title string) (*dao.Iteration // Delete 删除 iteration func (itr *Iteration) Delete(id uint64) error { - iteration, err := itr.db.GetIteration(id) - if err != nil { - return err - } - - // 检查 iteration 下是否有需求/任务/bug; 若有,不可删除 - issueReq := pb.PagingIssueRequest{ - ProjectID: iteration.ProjectID, - IterationID: int64(id), - External: true, - PageNo: 1, - PageSize: 1, - } - issues, _, err := itr.issue.Paging(issueReq) - if err != nil { - return err - } - if len(issues) > 0 { - return apierrors.ErrDeleteIteration.InvalidParameter("该迭代下存在事件,请先删除事件后再删除迭代") - } - if err := itr.db.DeleteIteration(id); err != nil { return apierrors.ErrDeleteIteration.InternalError(err) }