Skip to content

Commit

Permalink
Merge pull request #1185 from actiontech/issue_1177
Browse files Browse the repository at this point in the history
对接钉钉
  • Loading branch information
sjjian authored Dec 23, 2022
2 parents 6a45d40 + ed8efdb commit 6eb88a5
Show file tree
Hide file tree
Showing 24 changed files with 16,028 additions and 57 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ require (
github.com/agiledragon/gomonkey v2.0.2+incompatible
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/alibabacloud-go/darabonba-openapi v0.1.18
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2
github.com/alibabacloud-go/dingtalk v1.4.88
github.com/alibabacloud-go/rds-20140815/v2 v2.1.0
github.com/alibabacloud-go/tea v1.1.19
github.com/alibabacloud-go/tea-utils v1.4.3
github.com/alibabacloud-go/tea-utils/v2 v2.0.1
github.com/clbanning/mxj/v2 v2.5.6 // indirect
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548
github.com/cznic/parser v0.0.0-20181122101858-d773202d5b1f
Expand Down
35 changes: 18 additions & 17 deletions go.sum

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions spelling_dict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -352,5 +352,15 @@ dbeaver
logon
alipay
oltp
dinghkwuua1gf099mfhl
tug9edq1baq9jilkcvsmtb6vr_a-fhb9zhtcwzdjyptf1ftouphm_5xpckjk0hgn
dingding
oapi
topapi
dingtalk
orgapp
textarea
approvers
actioner
errcode
errmsg
userid
getbymobile
2 changes: 2 additions & 0 deletions sqle/api/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ func StartApi(net *gracenet.Net, exitChan chan struct{}, config config.SqleConfi
v1Router.GET("/configurations/wechat", v1.GetWeChatConfiguration, AdminUserAllowed())
v1Router.PATCH("/configurations/wechat", v1.UpdateWeChatConfigurationV1, AdminUserAllowed())
v1Router.POST("/configurations/wechat/test", v1.TestWeChatConfigurationV1, AdminUserAllowed())
v1Router.GET("/configurations/ding_talk", v1.GetDingTalkConfigurationV1, AdminUserAllowed())
v1Router.PATCH("/configurations/ding_talk", v1.UpdateDingTalkConfigurationV1, AdminUserAllowed())
v1Router.GET("/configurations/system_variables", v1.GetSystemVariables, AdminUserAllowed())
v1Router.PATCH("/configurations/system_variables", v1.UpdateSystemVariables, AdminUserAllowed())
v1Router.GET("/configurations/license", v1.GetLicense, AdminUserAllowed())
Expand Down
54 changes: 52 additions & 2 deletions sqle/api/controller/v1/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"net/http"

"github.com/actiontech/sqle/sqle/pkg/im"

"github.com/actiontech/sqle/sqle/api/cloudbeaver_wrapper/service"
"github.com/actiontech/sqle/sqle/api/controller"
"github.com/actiontech/sqle/sqle/config"
Expand Down Expand Up @@ -224,7 +226,26 @@ type DingTalkConfigurationV1 struct {
// @Success 200 {object} v1.GetDingTalkConfigurationResV1
// @router /v1/configurations/ding_talk [get]
func GetDingTalkConfigurationV1(c echo.Context) error {
return nil
s := model.GetStorage()
dingTalk, exist, err := s.GetImConfigByType(model.ImTypeDingTalk)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}
if !exist {
return c.JSON(http.StatusOK, &GetDingTalkConfigurationResV1{
BaseRes: controller.NewBaseReq(nil),
Data: DingTalkConfigurationV1{},
})
}

return c.JSON(http.StatusOK, &GetDingTalkConfigurationResV1{
BaseRes: controller.NewBaseReq(nil),
Data: DingTalkConfigurationV1{
AppKey: dingTalk.AppKey,
AppSecret: dingTalk.AppSecret,
IsEnableDingTalkNotify: dingTalk.IsEnable,
},
})
}

type UpdateDingTalkConfigurationReqV1 struct {
Expand All @@ -244,7 +265,36 @@ type UpdateDingTalkConfigurationReqV1 struct {
// @Success 200 {object} controller.BaseRes
// @router /v1/configurations/ding_talk [patch]
func UpdateDingTalkConfigurationV1(c echo.Context) error {
return nil
req := new(UpdateDingTalkConfigurationReqV1)
if err := controller.BindAndValidateReq(c, req); err != nil {
return controller.JSONBaseErrorReq(c, err)
}

s := model.GetStorage()
dingTalk, _, err := s.GetImConfigByType(model.ImTypeDingTalk)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}

if req.AppKey != nil {
dingTalk.AppKey = *req.AppKey
}
if req.AppSecret != nil {
dingTalk.AppSecret = *req.AppSecret
}
if req.IsEnableDingTalkNotify != nil {
dingTalk.IsEnable = *req.IsEnableDingTalkNotify
}

dingTalk.Type = model.ImTypeDingTalk

if err := s.Save(dingTalk); err != nil {
return controller.JSONBaseErrorReq(c, err)
}

go im.CreateApprovalTemplate(model.ImTypeDingTalk)

return c.JSON(http.StatusOK, controller.NewBaseReq(nil))
}

type UpdateWeChatConfigurationReqV1 struct {
Expand Down
11 changes: 11 additions & 0 deletions sqle/api/controller/v1/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func CreateUser(c echo.Context) error {
Name: req.Name,
Password: req.Password,
Email: req.Email,
Phone: req.Phone,
WeChatID: req.WeChatID,
}

Expand Down Expand Up @@ -109,6 +110,11 @@ func UpdateUser(c echo.Context) error {
user.Email = *req.Email
}

// Phone
if req.Phone != nil {
user.Phone = *req.Phone
}

// WeChatID
if req.WeChatID != nil {
user.WeChatID = *req.WeChatID
Expand Down Expand Up @@ -279,6 +285,7 @@ func convertUserToRes(user *model.User, managementPermissionCodes []uint, projec
}
userResp := UserDetailResV1{
Name: user.Name,
Phone: user.Phone,
Email: user.Email,
WeChatID: user.WeChatID,
LoginType: string(user.UserAuthenticationType),
Expand Down Expand Up @@ -405,6 +412,9 @@ func UpdateCurrentUser(c echo.Context) error {
if req.WeChatID != nil {
user.WeChatID = *req.WeChatID
}
if req.Phone != nil {
user.Phone = *req.Phone
}
err = s.Save(user)
if err != nil {
return controller.JSONBaseErrorReq(c, err)
Expand Down Expand Up @@ -526,6 +536,7 @@ func GetUsers(c echo.Context) error {
userReq := UserResV1{
Name: user.Name,
Email: user.Email,
Phone: user.Phone.String,
WeChatID: user.WeChatID.String,
LoginType: user.LoginType,
IsDisabled: user.IsDisabled(),
Expand Down
57 changes: 24 additions & 33 deletions sqle/api/controller/v1/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"time"

"github.com/actiontech/sqle/sqle/pkg/im"

"github.com/actiontech/sqle/sqle/api/controller"
"github.com/actiontech/sqle/sqle/driver"
"github.com/actiontech/sqle/sqle/errors"
Expand Down Expand Up @@ -393,33 +395,22 @@ func ApproveWorkflow(c echo.Context) error {
return controller.JSONBaseErrorReq(c, ErrWorkflowNoAccess)
}

nextStep := workflow.NextStep()

err = CheckUserCanOperateStep(user, workflow, stepId)
if err != nil {
return controller.JSONBaseErrorReq(c, errors.New(errors.DataInvalid, err))
}

currentStep := workflow.CurrentStep()

if workflow.Record.Status == model.WorkflowStatusWaitForExecution {
return controller.JSONBaseErrorReq(c, errors.New(errors.DataInvalid,
fmt.Errorf("workflow has been approved, you should to execute it")))
if err := server.ApproveWorkflowProcess(workflow, user, s); err != nil {
return controller.JSONBaseErrorReq(c, err)
}

currentStep.State = model.WorkflowStepStateApprove
now := time.Now()
currentStep.OperateAt = &now
currentStep.OperationUserId = user.ID
nextStep := workflow.NextStep()
workflow.Record.CurrentWorkflowStepId = nextStep.ID
if nextStep.Template.Typ == model.WorkflowStepTypeSQLExecute {
workflow.Record.Status = model.WorkflowStatusWaitForExecution
}
go im.UpdateApprove(workflow.ID, workflow.CurrentStep().ID, user.Phone, model.ApproveStatusAgree, "")

err = s.UpdateWorkflowStatus(workflow, currentStep, nil)
if err != nil {
return c.JSON(http.StatusOK, controller.NewBaseReq(err))
if nextStep.Template.Typ != model.WorkflowStepTypeSQLExecute {
go im.CreateApprove(strconv.Itoa(int(workflow.ID)))
}
go notification.NotifyWorkflow(workflowIdStr, notification.WorkflowNotifyTypeApprove)

return c.JSON(http.StatusOK, controller.NewBaseReq(nil))
}
Expand Down Expand Up @@ -506,21 +497,11 @@ func RejectWorkflow(c echo.Context) error {
}
}

currentStep := workflow.CurrentStep()
currentStep.State = model.WorkflowStepStateReject
currentStep.Reason = req.Reason
now := time.Now()
currentStep.OperateAt = &now
currentStep.OperationUserId = user.ID

workflow.Record.Status = model.WorkflowStatusReject
workflow.Record.CurrentWorkflowStepId = 0

err = s.UpdateWorkflowStatus(workflow, currentStep, nil)
if err != nil {
return c.JSON(http.StatusOK, controller.NewBaseReq(err))
if err := server.RejectWorkflowProcess(workflow, req.Reason, user, s); err != nil {
return controller.JSONBaseErrorReq(c, err)
}
go notification.NotifyWorkflow(fmt.Sprintf("%v", workflow.ID), notification.WorkflowNotifyTypeReject)

go im.UpdateApprove(workflow.ID, workflow.CurrentStep().ID, user.Phone, model.ApproveStatusRefuse, req.Reason)

return c.JSON(http.StatusOK, controller.NewBaseReq(nil))
}
Expand Down Expand Up @@ -587,6 +568,9 @@ func CancelWorkflow(c echo.Context) error {
if err != nil {
return controller.JSONBaseErrorReq(c, err)
}

im.CancelApprove(workflow.ID, workflow.CurrentStep().ID)

return controller.JSONBaseErrorReq(c, nil)
}

Expand Down Expand Up @@ -1076,7 +1060,11 @@ func CreateWorkflowV1(c echo.Context) error {
if !exist {
return controller.JSONBaseErrorReq(c, errors.New(errors.DataNotExist, fmt.Errorf("should exist at least one workflow after create workflow")))
}
go notification.NotifyWorkflow(fmt.Sprintf("%v", workflow.ID), notification.WorkflowNotifyTypeCreate)

workFlowId := strconv.Itoa(int(workflow.ID))
go notification.NotifyWorkflow(workFlowId, notification.WorkflowNotifyTypeCreate)

go im.CreateApprove(workFlowId)

return c.JSON(http.StatusOK, controller.NewBaseReq(nil))
}
Expand Down Expand Up @@ -1436,6 +1424,9 @@ func UpdateWorkflowV1(c echo.Context) error {
}
go notification.NotifyWorkflow(workflowIdStr, notification.WorkflowNotifyTypeCreate)

workFlowId := strconv.Itoa(int(workflow.ID))
go im.CreateApprove(workFlowId)

return c.JSON(http.StatusOK, controller.NewBaseReq(nil))
}

Expand Down
78 changes: 78 additions & 0 deletions sqle/model/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,81 @@ func (s *Storage) GetWorkflowExpiredHoursOrDefault() (int64, error) {

return 30 * 24, nil
}

const (
ImTypeDingTalk = "dingTalk"
)

type IM struct {
Model
AppKey string `json:"app_key" gorm:"column:app_key"`
AppSecret string `json:"app_secret" gorm:"column:app_secret"`
IsEnable bool `json:"is_enable" gorm:"column:is_enable"`
ProcessCode string `json:"process_code" gorm:"column:process_code"`
// 类型唯一
Type string `json:"type" gorm:"unique"`
}

func (i *IM) TableName() string {
return fmt.Sprintf("%v_im", globalConfigurationTablePrefix)
}

func (s *Storage) GetImConfigByType(imType string) (*IM, bool, error) {
im := new(IM)
err := s.db.Where("type = ?", imType).First(&im).Error
if err == gorm.ErrRecordNotFound {
return im, false, nil
}
return im, true, errors.New(errors.ConnectStorageError, err)
}

func (s *Storage) GetAllIMConfig() ([]IM, error) {
var ims []IM
err := s.db.Find(&ims).Error
if err != nil {
return nil, errors.New(errors.ConnectStorageError, err)
}
return ims, nil
}

func (s *Storage) UpdateImConfigById(id uint, m map[string]interface{}) error {
err := s.db.Model(&IM{}).Where("id = ?", id).Updates(m).Error
if err != nil {
return errors.New(errors.ConnectStorageError, err)
}
return nil
}

const (
ApproveStatusInitialized = "initialized"
ApproveStatusAgree = "agree"
ApproveStatusRefuse = "refuse"
)

type DingTalkInstance struct {
Model
ApproveInstanceCode string `json:"approve_instance" gorm:"column:approve_instance"`
WorkflowId uint `json:"workflow_id" gorm:"column:workflow_id"`
WorkflowStepID uint `json:"workflow_step_id" gorm:"column:workflow_step_id"`
// 审批实例 taskID
TaskID int64 `json:"task_id" gorm:"column:task_id"`
Status string `json:"status" gorm:"default:\"initialized\""`
}

func (s *Storage) GetDingTalkInstanceByWorkflowStepID(workflowId, workflowStepID uint) (*DingTalkInstance, bool, error) {
dti := new(DingTalkInstance)
err := s.db.Where("workflow_step_id = ? and workflow_id = ?", workflowStepID, workflowId).First(&dti).Error
if err == gorm.ErrRecordNotFound {
return dti, false, nil
}
return dti, true, errors.New(errors.ConnectStorageError, err)
}

func (s *Storage) GetDingTalkInstByStatus(status string) ([]DingTalkInstance, error) {
var dingTalkInstances []DingTalkInstance
err := s.db.Where("status = ?", status).Find(&dingTalkInstances).Error
if err != nil {
return nil, err
}
return dingTalkInstances, nil
}
1 change: 1 addition & 0 deletions sqle/model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type User struct {
// has created composite index: [id, login_name] by gorm#AddIndex
Name string `gorm:"index;column:login_name"`
Email string
Phone string `json:"phone" gorm:"column:phone"`
WeChatID string `json:"wechat_id" gorm:"column:wechat_id"`
Password string `json:"-" gorm:"-"`
SecretPassword string `json:"secret_password" gorm:"not null;column:password"`
Expand Down
3 changes: 2 additions & 1 deletion sqle/model/user_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type UserDetail struct {
Id int
Name string `json:"login_name"`
Email string
Phone sql.NullString `json:"phone"`
WeChatID sql.NullString `json:"wechat_id"`
LoginType string `json:"user_authentication_type"`
Stat int `json:"stat"`
Expand All @@ -18,7 +19,7 @@ func (u *UserDetail) IsDisabled() bool {
}

var usersQueryTpl = `SELECT
users.id, users.login_name, users.email, users.wechat_id,
users.id, users.login_name, users.email, users.phone, users.wechat_id,
users.user_authentication_type, users.stat,
GROUP_CONCAT(DISTINCT COALESCE(user_groups.name,'')) AS user_group_names
FROM users
Expand Down
2 changes: 2 additions & 0 deletions sqle/model/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ var autoMigrateList = []interface{}{
&ProjectMemberRole{},
&ProjectMemberGroupRole{},
&ManagementPermission{},
&IM{},
&DingTalkInstance{},
}

func (s *Storage) AutoMigrate() error {
Expand Down
Loading

0 comments on commit 6eb88a5

Please sign in to comment.