Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions sqle/api/controller/v1/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,13 +598,18 @@ type CheckedWorkflowInfo struct {
GetOpExecUser func([]*model.Task) (canAuditUsers [][]*model.User, canExecUsers [][]*model.User)
}

func CheckWorkflowCreationPrerequisites(c echo.Context, projectName string, taskIdsToBindWithWorkflow []uint, workflowTemplateId *uint) (*CheckedWorkflowInfo, error) {
func CheckWorkflowCreationPrerequisites(c echo.Context, projectName string, taskIdsToBindWithWorkflow []uint, workflowTemplateId *uint, opsTypeUID string) (*CheckedWorkflowInfo, error) {
// check project
projectUid, err := dms.GetProjectUIDByName(context.TODO(), projectName, true)
if err != nil {
return nil, err
}

// 运维类型归属:非空须属于本项目字典;空=未设置(允许)
if err := dms.ValidateOpsTypeBelongToProject(c.Request().Context(), projectUid, opsTypeUID); err != nil {
return nil, err
}

s := model.GetStorage()
// check user
user, err := controller.GetCurrentUser(c, dms.GetUser)
Expand Down Expand Up @@ -781,6 +786,7 @@ type GetWorkflowsReqV1 struct {
FilterProjectUid string `json:"filter_project_uid" query:"filter_project_uid"`
FilterInstanceId string `json:"filter_instance_id" query:"filter_instance_id"`
FilterProjectPriority dmsV1.ProjectPriority `json:"filter_project_priority" query:"filter_project_priority" valid:"omitempty,oneof=high medium low"`
FilterByOpsTypeUID string `json:"filter_by_ops_type_uid" query:"filter_by_ops_type_uid"`
PageIndex uint32 `json:"page_index" query:"page_index" valid:"required"`
PageSize uint32 `json:"page_size" query:"page_size" valid:"required"`
FuzzyKeyword string `json:"fuzzy_keyword" query:"fuzzy_keyword"`
Expand Down Expand Up @@ -808,6 +814,8 @@ type WorkflowDetailResV1 struct {
InstanceInfo []InstanceInfo `json:"instance_info,omitempty"`
WorkflowTemplateId *uint `json:"workflow_template_id,omitempty"`
WorkflowTemplateName string `json:"workflow_template_name,omitempty"`
// OpsType 运维类型(项目字典批量解析);未设置或字典项已删时省略
OpsType *dms.OpsType `json:"ops_type,omitempty"`
}

type InstanceInfo struct {
Expand Down Expand Up @@ -1305,6 +1313,7 @@ func loadInstanceByInstanceIds(ctx context.Context, instanceIds []string) (insta
// @Param filter_task_instance_id query string false "filter instance id"
// @Param filter_sql_version_id query string false "filter sql version id"
// @Param filter_workflow_template_id query uint false "filter workflow template id"
// @Param filter_by_ops_type_uid query string false "filter by ops type dictionary item uid; empty means no filter"
// @Param page_index query uint32 true "page index"
// @Param page_size query uint32 true "size of per page"
// @Param project_name path string true "project name"
Expand Down Expand Up @@ -1345,6 +1354,7 @@ func GetWorkflowsV1(c echo.Context) error {
"filter_status": req.FilterStatus,
"filter_current_step_assignee_user_id": req.FilterCurrentStepAssigneeUserId,
"filter_task_instance_id": req.FilterTaskInstanceId,
"filter_by_ops_type_uid": req.FilterByOpsTypeUID,
"filter_project_id": projectUid,
"current_user_id": user.ID,
"check_user_can_access": !up.CanViewProject(),
Expand All @@ -1364,6 +1374,11 @@ func GetWorkflowsV1(c echo.Context) error {
return controller.JSONBaseErrorReq(c, err)
}

opsTypeNameByUID := map[string]string{}
if len(workflows) > 0 {
opsTypeNameByUID = dms.BuildOpsTypeNameMap(c.Request().Context(), projectUid)
}

workflowsResV1 := make([]*WorkflowDetailResV1, 0, len(workflows))
for _, workflow := range workflows {
// TODO DMS提供根据ID批量查询用户接口,demo阶段使用GetUser实现
Expand All @@ -1389,6 +1404,7 @@ func GetWorkflowsV1(c echo.Context) error {
SqlVersionName: workflow.SqlVersionNames,
WorkflowTemplateId: templateId,
WorkflowTemplateName: workflow.WorkflowTemplateName.String,
OpsType: dms.ResolveOpsTypeFromMap(workflow.OpsTypeUID, opsTypeNameByUID),
}
workflowsResV1 = append(workflowsResV1, workflowRes)
}
Expand Down Expand Up @@ -1683,6 +1699,7 @@ type ExportWorkflowReqV1 struct {
FilterTaskInstanceId string `json:"filter_task_instance_id" query:"filter_task_instance_id"`
FilterTaskExecuteStartTimeFrom string `json:"filter_task_execute_start_time_from" query:"filter_task_execute_start_time_from"`
FilterTaskExecuteStartTimeTo string `json:"filter_task_execute_start_time_to" query:"filter_task_execute_start_time_to"`
FilterByOpsTypeUID string `json:"filter_by_ops_type_uid" query:"filter_by_ops_type_uid"`
FuzzyKeyword string `json:"fuzzy_keyword" query:"fuzzy_keyword"`
ExportFormat string `json:"export_format" query:"export_format" enums:"csv,excel" example:"excel"` // 导出格式:csv 或 excel,默认为 excel
}
Expand All @@ -1703,6 +1720,7 @@ type ExportWorkflowReqV1 struct {
// @Param filter_status query string false "filter workflow status" Enums(wait_for_audit,wait_for_execution,rejected,executing,canceled,exec_failed,finished)
// @Param filter_current_step_assignee_user_id query string false "filter current step assignee user id"
// @Param filter_task_instance_id query string false "filter instance id"
// @Param filter_by_ops_type_uid query string false "filter by ops type dictionary item uid; empty means no filter"
// @Param project_name path string true "project name"
// @Param fuzzy_keyword query string false "fuzzy matching subject/workflow_id/desc"
// @Param export_format query string false "export format" Enums(csv,excel) "export format: csv or excel, default is excel"
Expand Down Expand Up @@ -2092,7 +2110,7 @@ func AutoCreateAndExecuteWorkflowV1(c echo.Context) error {
taskIds = append(taskIds, task.ID)
}

w, err := CheckWorkflowCreationPrerequisites(c, projectName, taskIds, req.WorkflowTemplateId)
w, err := CheckWorkflowCreationPrerequisites(c, projectName, taskIds, req.WorkflowTemplateId, "")
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
Expand All @@ -2108,7 +2126,7 @@ func AutoCreateAndExecuteWorkflowV1(c echo.Context) error {
}

// 9. 创建工单
if err := s.CreateWorkflowV2(req.Subject, w.WorkflowId, req.Desc, w.User, w.Tasks, w.StepTemplates, w.ProjectId, nil, nil, nil, w.WorkflowTemplateId, w.GetOpExecUser); err != nil {
if err := s.CreateWorkflowV2(req.Subject, w.WorkflowId, req.Desc, "", w.User, w.Tasks, w.StepTemplates, w.ProjectId, nil, nil, nil, w.WorkflowTemplateId, w.GetOpExecUser); err != nil {
return controller.JSONBaseErrorReq(c, err)
}

Expand Down
3 changes: 3 additions & 0 deletions sqle/api/controller/v2/dashboard/workflow_manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type GetGlobalWorkflowListReqV2 struct {
FilterCreateTimeTo string `json:"filter_create_time_to" query:"filter_create_time_to"`
FilterUpdateTimeFrom string `json:"filter_update_time_from" query:"filter_update_time_from"`
FilterUpdateTimeTo string `json:"filter_update_time_to" query:"filter_update_time_to"`
// FilterByOpsTypeUID 按运维类型单选筛选(空则不筛;不提供「未设置」;对 SQL 上线与数据导出均生效)
FilterByOpsTypeUID string `json:"filter_by_ops_type_uid" query:"filter_by_ops_type_uid"`

// 卡片过滤类型
FilterCard dashboard_svc.GlobalWorkflowFilterCard `json:"filter_card" query:"filter_card" valid:"omitempty,oneof=archived pending_for_me initiated_by_me view_all" enums:"archived,pending_for_me,initiated_by_me,view_all"`
Expand Down Expand Up @@ -101,6 +103,7 @@ func GetGlobalWorkflowStatisticsV2(c echo.Context) error {
// @Param filter_create_user_id query string false "filter by create user id"
// @Param filter_create_time_from query string false "filter create time from"
// @Param filter_create_time_to query string false "filter create time to"
// @Param filter_by_ops_type_uid query string false "filter by ops type dictionary item uid; empty means no filter; applies to sql_release and data_export"
// @Success 200 {object} GlobalWorkflowListResV2
// @Router /v2/dashboard/workflows [get]
func GetGlobalWorkflowListV2(c echo.Context) error {
Expand Down
9 changes: 7 additions & 2 deletions sqle/api/controller/v2/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,8 @@ type CreateWorkflowReqV2 struct {
SqlVersionID *uint `json:"sql_version_id" form:"sql_version_id"`
WorkflowTemplateId *uint `json:"workflow_template_id" form:"workflow_template_id"`
TaskIds []uint `json:"task_ids" form:"task_ids" valid:"required"`
// OpsTypeUID 运维类型字典项标识;可选;空表示未设置;创建后不可改
OpsTypeUID string `json:"ops_type_uid" form:"ops_type_uid"`
}

type CreateWorkflowResV2 struct {
Expand Down Expand Up @@ -645,7 +647,7 @@ func CreateWorkflowV2(c echo.Context) error {
return controller.JSONBaseErrorReq(c, err)
}

w, err := v1.CheckWorkflowCreationPrerequisites(c, c.Param("project_name"), req.TaskIds, req.WorkflowTemplateId)
w, err := v1.CheckWorkflowCreationPrerequisites(c, c.Param("project_name"), req.TaskIds, req.WorkflowTemplateId, req.OpsTypeUID)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
Expand All @@ -658,7 +660,7 @@ func CreateWorkflowV2(c echo.Context) error {
}
s := model.GetStorage()
// create workflow with checking op permission in task
err = s.CreateWorkflowV2(req.Subject, w.WorkflowId, req.Desc, w.User, w.Tasks, w.StepTemplates, w.ProjectId, req.SqlVersionID, nil, nil, w.WorkflowTemplateId, w.GetOpExecUser)
err = s.CreateWorkflowV2(req.Subject, w.WorkflowId, req.Desc, req.OpsTypeUID, w.User, w.Tasks, w.StepTemplates, w.ProjectId, req.SqlVersionID, nil, nil, w.WorkflowTemplateId, w.GetOpExecUser)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
Expand Down Expand Up @@ -1035,6 +1037,8 @@ type WorkflowResV2 struct {
CreateTime *time.Time `json:"create_time"`
WorkflowTemplateId *uint `json:"workflow_template_id,omitempty"`
WorkflowTemplateName string `json:"workflow_template_name,omitempty"`
// OpsType 运维类型(项目字典解析);未设置或字典项已删时省略,供前端「-」约定
OpsType *dms.OpsType `json:"ops_type,omitempty"`
SqlVersion *SqlVersion `json:"sql_version,omitempty"`
Record *WorkflowRecordResV2 `json:"record"`
RecordHistory []*WorkflowRecordResV2 `json:"record_history_list,omitempty"`
Expand Down Expand Up @@ -1133,6 +1137,7 @@ func convertWorkflowToRes(ctx context.Context, workflow *model.Workflow, sqlVers
CreateUser: dms.GetUserNameWithDelTag(workflow.CreateUserId),
CreateTime: &workflow.CreatedAt,
WorkflowTemplateId: workflow.WorkflowTemplateId,
OpsType: dms.ResolveOpsTypeDisplay(ctx, string(workflow.ProjectId), workflow.OpsTypeUID),
AssociatedStageWorkflows: convertAssociatedWorkflowToRes(associatedWorkflows),
AssociatedRollbackWorkflows: convertAssociatedRollbackWorkflowToRes(associatedRollbackWorkflows),
}
Expand Down
110 changes: 110 additions & 0 deletions sqle/dms/ops_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package dms

import (
"context"
"fmt"
"net/url"
"strings"

base "github.com/actiontech/dms/pkg/dms-common/api/base/v1"
pkgHttp "github.com/actiontech/dms/pkg/dms-common/pkg/http"
"github.com/actiontech/sqle/sqle/errors"
)

// OpsType 与 AC-B06 / dms-common OpsType 契约对齐(uid + name)。
type OpsType struct {
UID string `json:"uid,omitempty"`
Name string `json:"name"`
}

type listOpsTypesReply struct {
Data []*OpsType `json:"data"`
Total int64 `json:"total_nums"`
base.GenericResp
}

// ListOpsTypes 调用 DMS 项目运维类型字典 list(与 dmsobject.ListOpsTypes / AC-B06 同一 HTTP 契约)。
// 本仓 vendor 尚未同步 dms-common 新符号时,在此复用既有 pkgHttp + DefaultDMSToken 封装。
func ListOpsTypes(ctx context.Context, projectUID string, pageIndex, pageSize uint32) ([]*OpsType, int64, error) {
header := map[string]string{
"Authorization": pkgHttp.DefaultDMSToken,
}

baseURL, err := url.Parse(fmt.Sprintf("%s/v1/dms/projects/%s/ops_types", GetDMSServerAddress(), projectUID))
if err != nil {
return nil, 0, fmt.Errorf("failed to parse ops types URL: %v", err)
}
query := url.Values{}
query.Set("page_size", fmt.Sprintf("%d", pageSize))
query.Set("page_index", fmt.Sprintf("%d", pageIndex))
baseURL.RawQuery = query.Encode()

reply := &listOpsTypesReply{}
if err := pkgHttp.Get(ctx, baseURL.String(), header, nil, reply); err != nil {
return nil, 0, fmt.Errorf("failed to list ops types from %v: %v", baseURL.String(), err)
}
if reply.Code != 0 {
return nil, 0, fmt.Errorf("http reply code(%v) error: %v", reply.Code, reply.Message)
}
return reply.Data, reply.Total, nil
}

// ErrOpsTypeNotBelongToProject 所选运维类型不属于本项目字典(用户可读)。
var ErrOpsTypeNotBelongToProject = errors.New(errors.DataInvalid, fmt.Errorf("所选运维类型不属于本项目字典"))

// ValidateOpsTypeBelongToProject 空串视为未设置(通过);非空须属于本项目字典。
func ValidateOpsTypeBelongToProject(ctx context.Context, projectUID, opsTypeUID string) error {
opsTypeUID = strings.TrimSpace(opsTypeUID)
if opsTypeUID == "" {
return nil
}
items, _, err := ListOpsTypes(ctx, projectUID, 1, 1000)
if err != nil {
return err
}
for _, item := range items {
if item != nil && item.UID == opsTypeUID {
return nil
}
}
return ErrOpsTypeNotBelongToProject
}

// BuildOpsTypeNameMap 按项目一次 ListOpsTypes,构建 uid→name 内存 map(AC-B17 / D8)。
// 拉取失败或空项目 → 空 map(展示侧 omitempty,不向上抛错)。
func BuildOpsTypeNameMap(ctx context.Context, projectUID string) map[string]string {
nameByUID := map[string]string{}
if strings.TrimSpace(projectUID) == "" {
return nameByUID
}
items, _, err := ListOpsTypes(ctx, projectUID, 1, 1000)
if err != nil {
return nameByUID
}
for _, item := range items {
if item != nil && item.UID != "" {
nameByUID[item.UID] = item.Name
}
}
return nameByUID
}

// ResolveOpsTypeFromMap 从已构建的 uid→name map 回填展示对象。
// 未设置或字典已删/不命中 → nil(omitempty)。
func ResolveOpsTypeFromMap(opsTypeUID string, nameByUID map[string]string) *OpsType {
opsTypeUID = strings.TrimSpace(opsTypeUID)
if opsTypeUID == "" || nameByUID == nil {
return nil
}
name, ok := nameByUID[opsTypeUID]
if !ok {
return nil
}
return &OpsType{UID: opsTypeUID, Name: name}
}

// ResolveOpsTypeDisplay 详情单条解析:内部走 BuildOpsTypeNameMap + ResolveOpsTypeFromMap(与列表共用)。
// 未设置、字典拉取失败、或字典项已删/不命中 → nil(前端「-」/省略)。
func ResolveOpsTypeDisplay(ctx context.Context, projectUID, opsTypeUID string) *OpsType {
return ResolveOpsTypeFromMap(opsTypeUID, BuildOpsTypeNameMap(ctx, projectUID))
}
48 changes: 48 additions & 0 deletions sqle/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7972,6 +7972,12 @@ var doc = `{
"name": "filter_workflow_template_id",
"in": "query"
},
{
"type": "string",
"description": "filter by ops type dictionary item uid; empty means no filter",
"name": "filter_by_ops_type_uid",
"in": "query"
},
{
"type": "integer",
"description": "page index",
Expand Down Expand Up @@ -8321,6 +8327,12 @@ var doc = `{
"name": "filter_task_instance_id",
"in": "query"
},
{
"type": "string",
"description": "filter by ops type dictionary item uid; empty means no filter",
"name": "filter_by_ops_type_uid",
"in": "query"
},
{
"type": "string",
"description": "project name",
Expand Down Expand Up @@ -11546,6 +11558,12 @@ var doc = `{
"description": "filter create time to",
"name": "filter_create_time_to",
"in": "query"
},
{
"type": "string",
"description": "filter by ops type dictionary item uid; empty means no filter; applies to sql_release and data_export",
"name": "filter_by_ops_type_uid",
"in": "query"
}
],
"responses": {
Expand Down Expand Up @@ -14334,6 +14352,11 @@ var doc = `{
"description": "实例名称",
"type": "string"
},
"ops_type": {
"description": "OpsType 运维类型(跨项目按工单所属项目字典批量解析);未设置或字典项已删时省略",
"type": "object",
"$ref": "#/definitions/dms.OpsType"
},
"priority": {
"description": "High, Medium, Low",
"type": "string"
Expand Down Expand Up @@ -14442,6 +14465,17 @@ var doc = `{
}
}
},
"dms.OpsType": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"uid": {
"type": "string"
}
}
},
"model.AuditResultInfo": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -23240,6 +23274,11 @@ var doc = `{
"$ref": "#/definitions/v1.InstanceInfo"
}
},
"ops_type": {
"description": "OpsType 运维类型(项目字典批量解析);未设置或字典项已删时省略",
"type": "object",
"$ref": "#/definitions/dms.OpsType"
},
"project_name": {
"type": "string"
},
Expand Down Expand Up @@ -24355,6 +24394,10 @@ var doc = `{
"desc": {
"type": "string"
},
"ops_type_uid": {
"description": "OpsTypeUID 运维类型字典项标识;可选;空表示未设置;创建后不可改",
"type": "string"
},
"sql_version_id": {
"type": "integer"
},
Expand Down Expand Up @@ -25668,6 +25711,11 @@ var doc = `{
"different_sqls"
]
},
"ops_type": {
"description": "OpsType 运维类型(项目字典解析);未设置或字典项已删时省略,供前端「-」约定",
"type": "object",
"$ref": "#/definitions/dms.OpsType"
},
"record": {
"type": "object",
"$ref": "#/definitions/v2.WorkflowRecordResV2"
Expand Down
Loading
Loading