Skip to content

Commit

Permalink
Merge pull request #2706 from actiontech/issue-2675-3
Browse files Browse the repository at this point in the history
feat: implement of system module red dots
  • Loading branch information
iwanghc authored Oct 24, 2024
2 parents 84b3352 + 9d45d75 commit 5107abd
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 56 deletions.
1 change: 1 addition & 0 deletions sqle/api/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ func StartApi(net *gracenet.Net, exitChan chan struct{}, config *config.SqleOpti
v1Router.GET("/company_notice", v1.GetCompanyNotice)
// 系统功能开关
v1Router.GET("/system/module_status", v1.GetSystemModuleStatus)
v1Router.GET("/system/module_red_dots",v1.GetSystemModuleRedDots)
}

// enterprise customized apis
Expand Down
85 changes: 85 additions & 0 deletions sqle/api/controller/v1/red_dot_notification.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package v1

import (
"context"

"github.com/actiontech/sqle/sqle/model"
"github.com/labstack/echo/v4"
)

type RedDotModule interface {
Name() string
HasRedDot(ctx echo.Context) (bool, error)
}

type RedDot struct {
ModuleName string
HasRedDot bool
}

var redDotList []RedDotModule

func RegisterRedDotModules(redDotModule ...RedDotModule) {
redDotList = append(redDotList, redDotModule...)
}

func GetSystemModuleRedDotsList(ctx echo.Context) ([]*RedDot, error) {
redDots := make([]*RedDot, len(redDotList))
for i, rd := range redDotList {
hasRedDot, err := rd.HasRedDot(ctx)
if err != nil {
return nil, err
}
redDots[i] = &RedDot{
ModuleName: rd.Name(),
HasRedDot: hasRedDot,
}
}
return redDots, nil
}

var statusOfGlobalWorkflowToBeFiltered []string = []string{
model.WorkflowStatusWaitForExecution,
model.WorkflowStatusExecFailed,
model.WorkflowStatusWaitForAudit,
model.WorkflowStatusReject,
}

// 查询待关注工单,是否有未处理的工单
func HasOthersWorkflowToHandle(ctx context.Context, user *model.User, userVisibility GlobalDashBoardVisibility) (has bool, err error) {
filter, err := constructGlobalWorkflowBasicFilter(ctx, user, userVisibility, &globalWorkflowBasicFilter{
FilterStatusList: statusOfGlobalWorkflowToBeFiltered,
})
if err != nil {
return false, err
}
s := model.GetStorage()
total, err := s.GetGlobalWorkflowTotalNum(filter)
if err != nil {
return false, err
}
if total > 0 {
return true, nil
}
return false, nil
}

// 查询创建的工单,是否有未处理的工单
func HasMyWorkflowToHandle(ctx context.Context, user *model.User, userVisibility GlobalDashBoardVisibility) (has bool, err error) {
filter, err := constructGlobalWorkflowBasicFilter(ctx, user, userVisibility, &globalWorkflowBasicFilter{
FilterStatusList: statusOfGlobalWorkflowToBeFiltered,
FilterCreateUserId: user.GetIDStr(),
})
if err != nil {
return false, err
}
s := model.GetStorage()
total, err := s.GetGlobalWorkflowTotalNum(filter)
if err != nil {
return false, err
}
if total > 0 {
return true, nil
}
return false, nil
}
47 changes: 47 additions & 0 deletions sqle/api/controller/v1/red_dot_notification_ce.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//go:build !enterprise
// +build !enterprise

package v1

import (
"github.com/actiontech/dms/pkg/dms-common/dmsobject"
"github.com/actiontech/sqle/sqle/api/controller"
dms "github.com/actiontech/sqle/sqle/dms"
"github.com/labstack/echo/v4"
)

func init() {
RegisterRedDotModules(GlobalDashBoardModule{})
}

type GlobalDashBoardModule struct{}

func (m GlobalDashBoardModule) Name() string {
return "global_dashboard"
}

func (m GlobalDashBoardModule) HasRedDot(ctx echo.Context) (bool, error) {
user, err := controller.GetCurrentUser(ctx, dms.GetUser)
if err != nil {
return false, err
}
permissions, isAdmin, err := dmsobject.GetUserOpPermission(ctx.Request().Context(), "", user.GetIDStr(), dms.GetDMSServerAddress())
if err != nil {
return false, err
}
// 将用户权限信息,转化为全局待处理清单统一的用户可见性
userVisibility := getGlobalDashBoardVisibilityOfUser(isAdmin, permissions)
has, err := HasOthersWorkflowToHandle(ctx.Request().Context(), user, userVisibility)
if err != nil {
return false, err
}
if has {
return true, nil
}

has, err = HasMyWorkflowToHandle(ctx.Request().Context(), user, userVisibility)
if err != nil {
return false, err
}
return has, nil
}
22 changes: 20 additions & 2 deletions sqle/api/controller/v1/system_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type GetSystemModuleRedDotsRes struct {
type ModuleRedDots []ModuleRedDot

type ModuleRedDot struct {
ModuleName string `json:"module_name"`
ModuleName string `json:"module_name" enums:"global_dashboard"`
HasRedDot bool `json:"has_red_dot"`
}

Expand All @@ -71,5 +71,23 @@ type ModuleRedDot struct {
// @Success 200 {object} v1.GetSystemModuleRedDotsRes
// @router /v1/system/module_red_dots [get]
func GetSystemModuleRedDots(c echo.Context) error {
return nil
redDots, err := GetSystemModuleRedDotsList(c)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
return c.JSON(http.StatusOK, &GetSystemModuleRedDotsRes{
BaseRes: controller.NewBaseReq(nil),
Data: toModuleRedDots(redDots),
})
}

func toModuleRedDots(redDots []*RedDot) ModuleRedDots {
moduleRedDots := make(ModuleRedDots, 0, len(redDots))
for _, redDot := range redDots {
moduleRedDots = append(moduleRedDots, ModuleRedDot{
ModuleName: redDot.ModuleName,
HasRedDot: redDot.HasRedDot,
})
}
return moduleRedDots
}
114 changes: 63 additions & 51 deletions sqle/api/controller/v1/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,14 +571,11 @@ func GetGlobalWorkflowsV1(c echo.Context) error {
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
// 2. 将用户权限信息,转化为全局待处理清单统一的用户可见性
// 2. 将用户权限信息,转化为全局待处理清单统一的用户可视范围
userVisibility := getGlobalDashBoardVisibilityOfUser(isAdmin, permissions)
if req.FilterCurrentStepAssigneeUserId != "" {
// 如果根据当前用户筛选,则筛选出用户在所有项目中的工单
userVisibility = GlobalDashBoardVisibilityGlobal
}
// 3. 将用户可见性、接口请求以及用户的权限范围,构造为全局工单的基础的过滤器,满足全局工单统一的过滤逻辑
filter, err := constructGlobalWorkflowBasicFilter(c.Request().Context(), user, userVisibility, permissions,

// 3. 将用户可视范围、接口请求以及用户的权限范围,构造为全局工单的基础的过滤器,满足全局工单统一的过滤逻辑
filter, err := constructGlobalWorkflowBasicFilter(c.Request().Context(), user, userVisibility,
&globalWorkflowBasicFilter{
FilterCreateUserId: req.FilterCreateUserId,
FilterStatusList: req.FilterStatusList,
Expand Down Expand Up @@ -731,14 +728,11 @@ func GetGlobalWorkflowStatistics(c echo.Context) error {
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
// 2. 将用户权限信息,转化为全局待处理清单统一的用户可见性
// 2. 将用户权限信息,转化为全局待处理清单统一的用户可视范围
userVisibility := getGlobalDashBoardVisibilityOfUser(isAdmin, permissions)
if req.FilterCreateUserId != "" {
// 如果根据当前用户筛选,则筛选出用户在所有项目中的工单,将用户可见性扩大至全局
userVisibility = GlobalDashBoardVisibilityGlobal
}
// 3. 将用户可见性、接口请求以及用户的权限范围,构造为全局工单的基础的过滤器,满足全局工单统一的过滤逻辑
filter, err := constructGlobalWorkflowBasicFilter(c.Request().Context(), user, userVisibility, permissions,

// 3. 将用户可视范围、接口请求以及用户的权限范围,构造为全局工单的基础的过滤器,满足全局工单统一的过滤逻辑
filter, err := constructGlobalWorkflowBasicFilter(c.Request().Context(), user, userVisibility,
&globalWorkflowBasicFilter{
FilterCreateUserId: req.FilterCreateUserId,
FilterStatusList: req.FilterStatusList,
Expand All @@ -751,7 +745,7 @@ func GetGlobalWorkflowStatistics(c echo.Context) error {
}
// 4. 根据筛选项获取工单数量
s := model.GetStorage()
count, err := s.GetGlobalWorkflowTotalNumByReq(filter)
count, err := s.GetGlobalWorkflowTotalNum(filter)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
Expand All @@ -770,80 +764,98 @@ type globalWorkflowBasicFilter struct {
FilterProjectPriority dmsV1.ProjectPriority `json:"filter_project_priority" query:"filter_project_priority" valid:"omitempty,oneof=high medium low"`
}

// 将用户可见性、接口请求以及用户的权限范围,构造为全局工单的基础的过滤器,满足全局工单统一的过滤逻辑
func constructGlobalWorkflowBasicFilter(ctx context.Context, user *model.User, userVisibility GlobalDashBoardVisibility, permissions []dmsV1.OpPermissionItem, req *globalWorkflowBasicFilter) (map[string]interface{}, error) {
// 将用户可视范围、接口请求以及用户的权限范围,构造为全局工单的基础的过滤器,满足全局工单统一的过滤逻辑
func constructGlobalWorkflowBasicFilter(ctx context.Context, user *model.User, userVisibility GlobalDashBoardVisibility, req *globalWorkflowBasicFilter) (map[string]interface{}, error) {
// 1. 基本筛选项
data := map[string]interface{}{
"filter_create_user_id": req.FilterCreateUserId, // 根据创建人ID筛选用户自己创建的工单
"filter_status_list": req.FilterStatusList, // 根据SQL工单的状态筛选多个状态的工单
"filter_project_id": req.FilterProjectUid, // 根据项目id筛选某些一个项目下的多个工单
"filter_instance_id": req.FilterInstanceId, // 根据工单记录的数据源id,筛选包含该数据源的工单,多数据源情况下,一旦包含该数据源,则被选中
}
// 2 页面筛选项:如果根据项目优先级筛选,则先筛选出对应优先级下的项目
// 1.1 页面筛选项:如果根据项目优先级筛选,则先筛选出对应优先级下的项目
var projectIdsByPriority []string
var err error
if req.FilterProjectPriority != "" {
projectIdsByPriority, _, err = loadProjectsByPriority(ctx, req.FilterProjectPriority)
if err != nil {
return nil, err
}

}
if req.FilterProjectPriority != "" {
// 2.1 若根据项目优先级筛选,则根据优先级对应的项目筛选
data["filter_project_id_list"] = projectIdsByPriority
}

var projectIdsOfProjectAdmin []string
if userVisibility == GlobalDashBoardVisibilityProjects {
for _, permission := range permissions {
if permission.OpPermissionType == dmsV1.OpPermissionTypeProjectAdmin {
projectIdsOfProjectAdmin = append(projectIdsOfProjectAdmin, permission.RangeUids...)
}
// 2. 发起的工单页面,根据当前用户筛选在所有项目下该用户创建的工单,因此将用户可视范围调整为全局
if req.FilterCreateUserId != "" {
userVisibility.VisibilityType = GlobalDashBoardVisibilityGlobal
}
// 3. 待处理工单页面,根据当前用户的可视范围筛选
switch userVisibility.ViewType() {
case GlobalDashBoardVisibilityProjects:
// 3.1 当用户的可视范围为多项目,则根据项目id筛选
if req.FilterProjectPriority != "" {
// 若根据项目优先级筛选,则将可查看的项目和项目优先级筛选后的项目的集合取交集
data["filter_project_id_list"] = utils.IntersectionStringSlice(projectIdsByPriority, userVisibility.ViewRange())
} else {
// 若不根据项目优先级筛选,则通过用户的有权限的项目进行筛选
data["filter_project_id_list"] = userVisibility.ViewRange()
}
}

if req.FilterProjectPriority != "" && userVisibility == GlobalDashBoardVisibilityProjects {
// 2.2 若根据项目优先级筛选,且可以查看多项目待关注SQL,则将可查看的项目和项目优先级筛选后的项目的集合取交集
data["filter_project_id_list"] = utils.IntersectionStringSlice(projectIdsByPriority, projectIdsOfProjectAdmin)
}
// 3 若不根据项目优先级筛选
if req.FilterProjectPriority == "" && userVisibility == GlobalDashBoardVisibilityProjects {
// 3.1 若可以查看多项目待关注SQL,则通过用户的有权限的项目进行筛选
data["filter_project_id_list"] = projectIdsOfProjectAdmin
}
// 4. 若用户可见性为受让人,则可以查看在SQL管控中分配给他的SQL
if userVisibility == GlobalDashBoardVisibilityAssignee {
case GlobalDashBoardVisibilityAssignee:
// 3.2 若用户可视范围为受让人,则查看分配给他的工单
data["filter_current_step_assignee_user_id"] = user.GetIDStr()
}
return data, nil
}

type GlobalDashBoardVisibility string
type VisibilityType string

const GlobalDashBoardVisibilityGlobal GlobalDashBoardVisibility = "global" // 全局可见
const GlobalDashBoardVisibilityProjects GlobalDashBoardVisibility = "projects" // 多项目可见
const GlobalDashBoardVisibilityAssignee GlobalDashBoardVisibility = "assignee" // 仅可见授予自己的
const GlobalDashBoardVisibilityGlobal VisibilityType = "global" // 全局可见
const GlobalDashBoardVisibilityProjects VisibilityType = "projects" // 多项目可见
const GlobalDashBoardVisibilityAssignee VisibilityType = "assignee" // 仅可见授予自己的

type GlobalDashBoardVisibility struct {
VisibilityType VisibilityType
VisibilityRange []string // 对于项目是项目id
}

// 将用户权限信息,转化为全局待处理清单统一的用户可见性
func (v GlobalDashBoardVisibility) ViewType() VisibilityType {
return v.VisibilityType
}

func (v GlobalDashBoardVisibility) ViewRange() []string {
return v.VisibilityRange
}

// 将用户权限信息,转化为全局待处理清单统一的用户可视范围
func getGlobalDashBoardVisibilityOfUser(isAdmin bool, permissions []dmsV1.OpPermissionItem) GlobalDashBoardVisibility {
// 角色:全局管理员,全局可查看者
if isAdmin {
return GlobalDashBoardVisibilityGlobal
return GlobalDashBoardVisibility{
VisibilityType: GlobalDashBoardVisibilityGlobal,
}
}
for _, permission := range permissions {
if permission.OpPermissionType == dmsV1.OpPermissionTypeGlobalView || permission.OpPermissionType == dmsV1.OpPermissionTypeGlobalManagement {
return GlobalDashBoardVisibilityGlobal
return GlobalDashBoardVisibility{
VisibilityType: GlobalDashBoardVisibilityGlobal,
}
}
}
// 角色:多项目管理者
var projectRange []string
for _, permission := range permissions {
if permission.OpPermissionType == dmsV1.OpPermissionTypeProjectAdmin {
return GlobalDashBoardVisibilityProjects
projectRange = append(projectRange, permission.RangeUids...)
}
}
if len(projectRange) > 0 {
return GlobalDashBoardVisibility{
VisibilityType: GlobalDashBoardVisibilityProjects,
VisibilityRange: projectRange,
}
}
// 角色:受让人,事件处理者
return GlobalDashBoardVisibilityAssignee
return GlobalDashBoardVisibility{
VisibilityType: GlobalDashBoardVisibilityAssignee,
}
}

// 根据项目优先级从 dms 系统中获取相应的项目列表,并返回项目ID列表和项目映射
Expand Down
5 changes: 4 additions & 1 deletion sqle/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14840,7 +14840,10 @@ var doc = `{
"type": "boolean"
},
"module_name": {
"type": "string"
"type": "string",
"enum": [
"global_dashboard"
]
}
}
},
Expand Down
5 changes: 4 additions & 1 deletion sqle/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -14824,7 +14824,10 @@
"type": "boolean"
},
"module_name": {
"type": "string"
"type": "string",
"enum": [
"global_dashboard"
]
}
}
},
Expand Down
2 changes: 2 additions & 0 deletions sqle/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2883,6 +2883,8 @@ definitions:
has_red_dot:
type: boolean
module_name:
enum:
- global_dashboard
type: string
type: object
v1.ModuleRedDots:
Expand Down
2 changes: 1 addition & 1 deletion sqle/model/workflow_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (s *Storage) GetWorkflowsByReq(data map[string]interface{}) (
return result, count, err
}

func (s *Storage) GetGlobalWorkflowTotalNumByReq(data map[string]interface{}) (count uint64, err error) {
func (s *Storage) GetGlobalWorkflowTotalNum(data map[string]interface{}) (count uint64, err error) {
return s.getCountResult(workflowsQueryBodyTpl, workflowsCountTpl, data)
}

Expand Down

0 comments on commit 5107abd

Please sign in to comment.