feat: add invite user binding
This commit is contained in:
@@ -51,6 +51,13 @@ type RevokeReq struct {
|
||||
|
||||
type RevokeRes struct{}
|
||||
|
||||
type BindUserReq struct {
|
||||
g.Meta `path:"/imInvite/bindUser" method:"post" tags:"IM邀请码" summary:"绑定邀请码用户"`
|
||||
sysin.ImInviteBindUserInp
|
||||
}
|
||||
|
||||
type BindUserRes struct{}
|
||||
|
||||
type BatchRevokeReq struct {
|
||||
g.Meta `path:"/imInvite/batchRevoke" method:"post" tags:"IM邀请码" summary:"批量撤销IM注册邀请码"`
|
||||
sysin.ImInviteBatchRevokeInp
|
||||
|
||||
@@ -60,6 +60,11 @@ func (c *cImInvite) Revoke(ctx context.Context, req *iminvite.RevokeReq) (res *i
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) BindUser(ctx context.Context, req *iminvite.BindUserReq) (res *iminvite.BindUserRes, err error) {
|
||||
err = service.SysImInvite().BindUser(ctx, &req.ImInviteBindUserInp)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) BatchRevoke(ctx context.Context, req *iminvite.BatchRevokeReq) (res *iminvite.BatchRevokeRes, err error) {
|
||||
data, err := service.SysImInvite().BatchRevoke(ctx, &req.ImInviteBatchRevokeInp)
|
||||
if err != nil {
|
||||
|
||||
@@ -7,8 +7,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hotgo/internal/library/imtenant"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -44,18 +46,21 @@ type imInviteListRequest struct {
|
||||
}
|
||||
|
||||
type imInviteCodeResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
TenantName string `json:"tenant_name"`
|
||||
BatchID *int64 `json:"batch_id"`
|
||||
Code string `json:"code"`
|
||||
Status string `json:"status"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
UsageCount int `json:"usage_count"`
|
||||
LastUsedAt *time.Time `json:"last_used_at"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
TenantName string `json:"tenant_name"`
|
||||
BatchID *int64 `json:"batch_id"`
|
||||
Code string `json:"code"`
|
||||
Status string `json:"status"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
MaxUses int `json:"max_uses"`
|
||||
UsageCount int `json:"usage_count"`
|
||||
BoundUserID *int64 `json:"bound_user_id"`
|
||||
BoundUserLabel string `json:"bound_user_label"`
|
||||
LastUsedAt *time.Time `json:"last_used_at"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type imInviteBatchResponse struct {
|
||||
@@ -202,6 +207,47 @@ func (s *sSysImInvite) Revoke(ctx context.Context, in *sysin.ImInviteRevokeInp)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) BindUser(ctx context.Context, in *sysin.ImInviteBindUserInp) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
scope, db, err := inviteDB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := sysin.ParseOptionalInt64(in.Id)
|
||||
if err != nil || id <= 0 {
|
||||
return gerror.New("邀请码ID不能为空")
|
||||
}
|
||||
tenant, err := resolveImAdminTenant(ctx, scope.TenantId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
label, err := inviteBoundUserLabel(ctx, tenant, in.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now().UTC().Round(time.Millisecond)
|
||||
result, err := db.Exec(ctx, `UPDATE im_registration_invite_codes
|
||||
SET bound_user_id=?,bound_user_label=?,updated_at=?
|
||||
WHERE tenant_id=? AND id=? AND status='active' AND usage_count=0 AND (bound_user_id IS NULL OR bound_user_id=0)`,
|
||||
in.UserId, label, now, scope.TenantId, id)
|
||||
if err == nil {
|
||||
if affected, _ := result.RowsAffected(); affected == 0 {
|
||||
err = gerror.New("邀请码不存在、已使用、已绑定或不可绑定")
|
||||
}
|
||||
}
|
||||
auditResult := "success"
|
||||
if err != nil {
|
||||
auditResult = "failed"
|
||||
}
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_bind_user", "invite_code", id, auditResult, g.Map{"user_id": in.UserId})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) BatchRevoke(ctx context.Context, in *sysin.ImInviteBatchRevokeInp) (res *sysin.ImInviteBatchModel, err error) {
|
||||
scope, db, err := inviteDB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
@@ -251,14 +297,14 @@ func (s *sSysImInvite) Export(ctx context.Context, in *sysin.ImInviteListInp) (e
|
||||
request.Response.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=registration-invite-codes-%s.csv", time.Now().Format("20060102-150405")))
|
||||
_, _ = request.Response.Writer.Write([]byte{0xef, 0xbb, 0xbf})
|
||||
writer := csv.NewWriter(request.Response.Writer)
|
||||
_ = writer.Write([]string{"tenant_id", "tenant_name", "code", "status", "batch_id", "expires_at", "usage_count", "last_used_at", "created_by", "created_at", "remark"})
|
||||
_ = writer.Write([]string{"tenant_id", "tenant_name", "code", "status", "expires_at", "usage_count", "available_uses", "last_used_at", "created_by", "created_at", "remark"})
|
||||
for offset := 0; ; offset += 100 {
|
||||
items, total, queryErr := queryInviteCodes(ctx, db, req, 100, offset)
|
||||
if queryErr != nil {
|
||||
return gerror.Wrap(queryErr, "导出邀请码失败")
|
||||
}
|
||||
for _, item := range items {
|
||||
_ = writer.Write([]string{strconv.FormatInt(item.TenantID, 10), item.TenantName, item.Code, item.Status, formatOptionalInt64(item.BatchID), item.ExpiresAt.Format(time.RFC3339), strconv.Itoa(item.UsageCount), formatTime(item.LastUsedAt), strconv.FormatInt(item.CreatedBy, 10), item.CreatedAt.Format(time.RFC3339), item.Remark})
|
||||
_ = writer.Write([]string{strconv.FormatInt(item.TenantID, 10), item.TenantName, item.Code, item.Status, item.ExpiresAt.Format(time.RFC3339), strconv.Itoa(item.UsageCount), formatInviteAvailableUses(item.MaxUses, item.UsageCount), formatTime(item.LastUsedAt), strconv.FormatInt(item.CreatedBy, 10), item.CreatedAt.Format(time.RFC3339), item.Remark})
|
||||
}
|
||||
writer.Flush()
|
||||
if writer.Error() != nil {
|
||||
@@ -300,10 +346,10 @@ func createSingleInvite(ctx context.Context, db gdb.DB, tenantID, operatorID int
|
||||
}
|
||||
now := time.Now().UTC().Round(time.Millisecond)
|
||||
_, err = db.Exec(ctx, `INSERT INTO im_registration_invite_codes
|
||||
(id,tenant_id,batch_id,code,status,expires_at,usage_count,last_used_at,created_by,remark,created_at,updated_at)
|
||||
VALUES(?,?,NULL,?,'active',?,0,NULL,?,?,?,?)`, id, tenantID, code, expiresAt, operatorID, remark, now, now)
|
||||
(id,tenant_id,batch_id,code,status,expires_at,max_uses,usage_count,last_used_at,created_by,remark,created_at,updated_at)
|
||||
VALUES(?,?,NULL,?,'active',?,1,0,NULL,?,?,?,?)`, id, tenantID, code, expiresAt, operatorID, remark, now, now)
|
||||
if err == nil {
|
||||
return imInviteCodeResponse{ID: id, TenantID: tenantID, Code: code, Status: "active", ExpiresAt: expiresAt, CreatedBy: operatorID, Remark: remark, CreatedAt: now}, nil
|
||||
return imInviteCodeResponse{ID: id, TenantID: tenantID, Code: code, Status: "active", ExpiresAt: expiresAt, MaxUses: 1, CreatedBy: operatorID, Remark: remark, CreatedAt: now}, nil
|
||||
}
|
||||
if !isDuplicateError(err) {
|
||||
return imInviteCodeResponse{}, err
|
||||
@@ -356,8 +402,8 @@ func createInviteBatch(ctx context.Context, db gdb.DB, tenantID, operatorID int6
|
||||
}
|
||||
for i := range codes {
|
||||
if _, txErr := tx.Exec(`INSERT INTO im_registration_invite_codes
|
||||
(id,tenant_id,batch_id,code,status,expires_at,usage_count,last_used_at,created_by,remark,created_at,updated_at)
|
||||
VALUES(?,?,?,?,'active',?,0,NULL,?,?,?,?)`, codes[i].ID, tenantID, batchID, codes[i].Code, expiresAt, operatorID, remark, now, now); txErr != nil {
|
||||
(id,tenant_id,batch_id,code,status,expires_at,max_uses,usage_count,last_used_at,created_by,remark,created_at,updated_at)
|
||||
VALUES(?,?,?,?,'active',?,0,0,NULL,?,?,?,?)`, codes[i].ID, tenantID, batchID, codes[i].Code, expiresAt, operatorID, remark, now, now); txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
codes[i].TenantID, codes[i].Status, codes[i].ExpiresAt, codes[i].CreatedBy, codes[i].Remark, codes[i].CreatedAt = tenantID, "active", expiresAt, operatorID, remark, now
|
||||
@@ -400,7 +446,7 @@ func queryInviteCodes(ctx context.Context, db gdb.DB, req imInviteListRequest, l
|
||||
}
|
||||
queryArgs := append(append([]any{}, args...), limit, offset)
|
||||
items := make([]imInviteCodeResponse, 0)
|
||||
err = db.Raw(`SELECT c.id,c.tenant_id,t.name tenant_name,c.batch_id,c.code,c.status,c.expires_at,c.created_by,c.remark,c.created_at,
|
||||
err = db.Raw(`SELECT c.id,c.tenant_id,t.name tenant_name,c.batch_id,c.code,c.status,c.expires_at,c.max_uses,c.bound_user_id,c.bound_user_label,c.created_by,c.remark,c.created_at,
|
||||
c.usage_count,c.last_used_at
|
||||
FROM im_registration_invite_codes c
|
||||
LEFT JOIN im_tenant t ON t.id=c.tenant_id
|
||||
@@ -521,10 +567,45 @@ func convertInviteCodes(items []imInviteCodeResponse) []*sysin.ImInviteCodeModel
|
||||
func convertInviteCode(item imInviteCodeResponse) *sysin.ImInviteCodeModel {
|
||||
return &sysin.ImInviteCodeModel{Id: strconv.FormatInt(item.ID, 10), BatchId: formatOptionalInt64(item.BatchID), Code: item.Code,
|
||||
TenantId: strconv.FormatInt(item.TenantID, 10), TenantName: item.TenantName,
|
||||
Status: item.Status, ExpiresAt: gtime.New(item.ExpiresAt), UsageCount: item.UsageCount, LastUsedAt: convertTime(item.LastUsedAt),
|
||||
Status: item.Status, ExpiresAt: gtime.New(item.ExpiresAt), MaxUses: item.MaxUses, UsageCount: item.UsageCount,
|
||||
BoundUserId: formatOptionalInt64(item.BoundUserID), BoundUserLabel: item.BoundUserLabel, LastUsedAt: convertTime(item.LastUsedAt),
|
||||
CreatedBy: strconv.FormatInt(item.CreatedBy, 10), Remark: item.Remark, CreatedAt: gtime.New(item.CreatedAt)}
|
||||
}
|
||||
|
||||
func inviteBoundUserLabel(ctx context.Context, tenant *entity.ImTenant, userID int64) (string, error) {
|
||||
if tenant == nil || tenant.Code == "" || userID <= 0 {
|
||||
return "", gerror.New("绑定用户不合法")
|
||||
}
|
||||
values := addPageQuery(url.Values{}, 1, 1)
|
||||
values.Set("user_ids", strconv.FormatInt(userID, 10))
|
||||
users, _, err := imAdminQueryList[sysin.ImUserListModel](ctx, tenant,
|
||||
"/internal/admin-query/tenants/"+url.PathEscape(tenant.Code)+"/users",
|
||||
values,
|
||||
map[string]string{
|
||||
"public": "profileJson",
|
||||
"trusted": "trustedJson",
|
||||
"access": "accessJson",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return "", gerror.New("绑定用户不存在")
|
||||
}
|
||||
user := users[0]
|
||||
if user.Nickname != "" {
|
||||
return fmt.Sprintf("%s(%s)", user.Nickname, user.UserId), nil
|
||||
}
|
||||
if user.Username != "" {
|
||||
return fmt.Sprintf("%s(%s)", user.Username, user.UserId), nil
|
||||
}
|
||||
if user.Phone != "" {
|
||||
return fmt.Sprintf("%s(%s)", user.Phone, user.UserId), nil
|
||||
}
|
||||
return user.UserId, nil
|
||||
}
|
||||
|
||||
func convertInviteBatch(item *imInviteBatchResponse) *sysin.ImInviteBatchModel {
|
||||
if item == nil {
|
||||
return nil
|
||||
@@ -564,6 +645,17 @@ func formatTime(value *time.Time) string {
|
||||
return value.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func formatInviteAvailableUses(maxUses, usageCount int) string {
|
||||
if maxUses <= 0 {
|
||||
return "不限制"
|
||||
}
|
||||
available := maxUses - usageCount
|
||||
if available < 0 {
|
||||
available = 0
|
||||
}
|
||||
return strconv.Itoa(available)
|
||||
}
|
||||
|
||||
func batchID(batch *imInviteBatchResponse) int64 {
|
||||
if batch == nil {
|
||||
return 0
|
||||
|
||||
@@ -105,24 +105,43 @@ type ImInviteRevokeInp struct {
|
||||
Id string `json:"id" v:"required#邀请码ID不能为空" dc:"邀请码ID"`
|
||||
}
|
||||
|
||||
type ImInviteBindUserInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
Id string `json:"id" v:"required#邀请码ID不能为空" dc:"邀请码ID"`
|
||||
UserId int64 `json:"userId" v:"required|min:1#绑定用户不能为空|绑定用户不合法" dc:"绑定C端用户ID"`
|
||||
}
|
||||
|
||||
func (in *ImInviteBindUserInp) Filter(ctx context.Context) (err error) {
|
||||
if _, err = ParseOptionalInt64(in.Id); err != nil {
|
||||
return gerror.New("邀请码ID不能为空")
|
||||
}
|
||||
if in.UserId <= 0 {
|
||||
return gerror.New("绑定用户不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ImInviteBatchRevokeInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
BatchId string `json:"batchId" v:"required#批次ID不能为空" dc:"批次ID"`
|
||||
}
|
||||
|
||||
type ImInviteCodeModel struct {
|
||||
Id string `json:"id" dc:"ID"`
|
||||
TenantId string `json:"tenantId" dc:"IM租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"IM租户名称"`
|
||||
BatchId string `json:"batchId" dc:"批次ID"`
|
||||
Code string `json:"code" dc:"邀请码"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
|
||||
UsageCount int `json:"usageCount" dc:"累计使用次数"`
|
||||
LastUsedAt *gtime.Time `json:"lastUsedAt" dc:"最后使用时间"`
|
||||
CreatedBy string `json:"createdBy" dc:"创建人"`
|
||||
Remark string `json:"remark" dc:"备注"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
Id string `json:"id" dc:"ID"`
|
||||
TenantId string `json:"tenantId" dc:"IM租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"IM租户名称"`
|
||||
BatchId string `json:"batchId" dc:"批次ID"`
|
||||
Code string `json:"code" dc:"邀请码"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
|
||||
MaxUses int `json:"maxUses" dc:"最大可用次数,0表示不限制"`
|
||||
UsageCount int `json:"usageCount" dc:"累计使用次数"`
|
||||
BoundUserId string `json:"boundUserId" dc:"绑定用户ID"`
|
||||
BoundUserLabel string `json:"boundUserLabel" dc:"绑定用户展示名"`
|
||||
LastUsedAt *gtime.Time `json:"lastUsedAt" dc:"最后使用时间"`
|
||||
CreatedBy string `json:"createdBy" dc:"创建人"`
|
||||
Remark string `json:"remark" dc:"备注"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
type ImInviteBatchModel struct {
|
||||
|
||||
@@ -209,6 +209,8 @@ type (
|
||||
BatchView(ctx context.Context, in *sysin.ImInviteBatchViewInp) (res *sysin.ImInviteBatchDetailModel, err error)
|
||||
// Revoke 撤销邀请码
|
||||
Revoke(ctx context.Context, in *sysin.ImInviteRevokeInp) (err error)
|
||||
// BindUser 绑定邀请码注册后自动添加的C端用户
|
||||
BindUser(ctx context.Context, in *sysin.ImInviteBindUserInp) (err error)
|
||||
// BatchRevoke 批量撤销邀请码
|
||||
BatchRevoke(ctx context.Context, in *sysin.ImInviteBatchRevokeInp) (res *sysin.ImInviteBatchModel, err error)
|
||||
// Export 导出邀请码
|
||||
|
||||
@@ -8,14 +8,14 @@ WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'workbenchManager
|
||||
SET @workbenchRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'workbenchManager' LIMIT 1);
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT @workbenchRootId, '意见反馈配置', 'imFeedbackConfigIndex', 'feedback-config/index', '', 2, '', '/imFeedbackConfig/get', '', '/imFeedbackConfig/index', 1, 'workbenchManager', 0, 0, '', 0, 0, 0, 2, CONCAT('tr_', @workbenchRootId, ' '), 30, '', 1, @now, @now
|
||||
SELECT @workbenchRootId, '意见反馈配置', 'imFeedbackConfigIndex', 'feedback-config/index', '', 2, '', '/imFeedbackConfig/get', '', '/imFeedbackConfig/index', 1, 'workbenchManager', 0, 0, '', 0, 0, 0, 2, CONCAT('tr_', @workbenchRootId, ' '), 40, '', 1, @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imFeedbackConfigIndex');
|
||||
|
||||
SET @imFeedbackConfigId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imFeedbackConfigIndex' LIMIT 1);
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid`=@workbenchRootId, `path`='feedback-config/index', `active_menu`='workbenchManager',
|
||||
`level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=30,
|
||||
`level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=40,
|
||||
`status`=1, `hidden`=0, `updated_at`=@now
|
||||
WHERE `id`=@imFeedbackConfigId;
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'workbenchManager
|
||||
SET @workbenchRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'workbenchManager' LIMIT 1);
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT @workbenchRootId, '总经理信箱配置', 'imMailboxConfigIndex', 'mailbox-config/index', '', 2, '', '/imMailboxConfig/get', '', '/imMailboxConfig/index', 1, 'workbenchManager', 0, 0, '', 0, 0, 0, 2, CONCAT('tr_', @workbenchRootId, ' '), 20, '', 1, @now, @now
|
||||
SELECT @workbenchRootId, '总经理信箱配置', 'imMailboxConfigIndex', 'mailbox-config/index', '', 2, '', '/imMailboxConfig/get', '', '/imMailboxConfig/index', 1, 'workbenchManager', 0, 0, '', 0, 0, 0, 2, CONCAT('tr_', @workbenchRootId, ' '), 30, '', 1, @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imMailboxConfigIndex');
|
||||
|
||||
SET @imMailboxConfigId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imMailboxConfigIndex' LIMIT 1);
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid`=@workbenchRootId, `path`='mailbox-config/index', `active_menu`='workbenchManager',
|
||||
`level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=20,
|
||||
`level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=30,
|
||||
`status`=1, `hidden`=0, `updated_at`=@now
|
||||
WHERE `id`=@imMailboxConfigId;
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ UPDATE `hg_admin_menu` SET `redirect`='/im/dashboard/index', `updated_at`=@now W
|
||||
UPDATE `hg_admin_menu` SET `pid`=@peopleId, `title`='用户管理', `path`='user/index', `level`=3, `tree`=CONCAT('tr_', @imRootId, ' tr_', @peopleId, ' '), `sort`=10, `updated_at`=@now WHERE `name`='imUserIndex';
|
||||
UPDATE `hg_admin_menu` SET `pid`=@peopleId, `path`='user-policy-group/index', `level`=3, `tree`=CONCAT('tr_', @imRootId, ' tr_', @peopleId, ' '), `sort`=20, `updated_at`=@now WHERE `name`='imUserPolicyGroupIndex';
|
||||
UPDATE `hg_admin_menu` SET `pid`=@peopleId, `title`='群组管理', `path`='group/index', `level`=3, `tree`=CONCAT('tr_', @imRootId, ' tr_', @peopleId, ' '), `sort`=30, `updated_at`=@now WHERE `name`='imGroupIndex';
|
||||
UPDATE `hg_admin_menu` SET `pid`=@peopleId, `path`='org/index', `level`=3, `tree`=CONCAT('tr_', @imRootId, ' tr_', @peopleId, ' '), `sort`=40, `updated_at`=@now WHERE `name`='imOrgIndex';
|
||||
UPDATE `hg_admin_menu` SET `pid`=@peopleId, `title`='邀请码管理', `path`='registration-invite/index', `level`=3, `tree`=CONCAT('tr_', @imRootId, ' tr_', @peopleId, ' '), `sort`=50, `updated_at`=@now WHERE `name`='imRegistrationInviteIndex';
|
||||
|
||||
-- Content review.
|
||||
@@ -97,15 +96,17 @@ WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'workbenchManager
|
||||
|
||||
SET @workbenchRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'workbenchManager' LIMIT 1);
|
||||
SET @workbenchId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imWorkbenchIndex' LIMIT 1);
|
||||
SET @orgId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imOrgIndex' LIMIT 1);
|
||||
SET @mailboxConfigId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imMailboxConfigIndex' LIMIT 1);
|
||||
SET @feedbackConfigId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imFeedbackConfigIndex' LIMIT 1);
|
||||
|
||||
UPDATE `hg_admin_menu` SET `pid`=0, `title`='工作台管理', `path`='/workbench', `icon`='AppstoreOutlined', `type`=1, `redirect`='/workbench/config/index', `component`='LAYOUT', `always_show`=1, `active_menu`='', `is_root`=1, `hidden`=0, `level`=1, `tree`=CONCAT('tr_', `id`, ' '), `sort`=126, `status`=1, `updated_at`=@now WHERE `id`=@workbenchRootId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `title`='工作台配置', `path`='config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=10, `status`=1, `hidden`=0, `updated_at`=@now WHERE `id`=@workbenchId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='mailbox-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=20, `status`=1, `hidden`=0, `updated_at`=@now WHERE `id`=@mailboxConfigId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='feedback-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=30, `status`=1, `hidden`=0, `updated_at`=@now WHERE `id`=@feedbackConfigId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `title`='组织架构', `path`='org/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=20, `status`=1, `hidden`=0, `updated_at`=@now WHERE `id`=@orgId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='mailbox-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=30, `status`=1, `hidden`=0, `updated_at`=@now WHERE `id`=@mailboxConfigId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='feedback-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=40, `status`=1, `hidden`=0, `updated_at`=@now WHERE `id`=@feedbackConfigId;
|
||||
UPDATE `hg_admin_menu` SET `level`=3, `tree`=CONCAT('tr_', @workbenchRootId, ' tr_', @workbenchId, ' '), `updated_at`=@now WHERE `pid`=@workbenchId AND `type`=3;
|
||||
UPDATE `hg_admin_menu` child JOIN `hg_admin_menu` page ON page.`id`=child.`pid` SET child.`level`=3, child.`tree`=CONCAT('tr_', @workbenchRootId, ' tr_', page.`id`, ' '), child.`updated_at`=@now WHERE page.`id` IN (@mailboxConfigId, @feedbackConfigId) AND child.`type`=3;
|
||||
UPDATE `hg_admin_menu` child JOIN `hg_admin_menu` page ON page.`id`=child.`pid` SET child.`level`=3, child.`tree`=CONCAT('tr_', @workbenchRootId, ' tr_', page.`id`, ' '), child.`updated_at`=@now WHERE page.`id` IN (@orgId, @mailboxConfigId, @feedbackConfigId) AND child.`type`=3;
|
||||
|
||||
INSERT IGNORE INTO `hg_admin_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT `role_id`, @workbenchRootId FROM `hg_admin_role_menu` WHERE `menu_id` IN (@workbenchId, @mailboxConfigId, @feedbackConfigId);
|
||||
SELECT `role_id`, @workbenchRootId FROM `hg_admin_role_menu` WHERE `menu_id` IN (@workbenchId, @orgId, @mailboxConfigId, @feedbackConfigId);
|
||||
|
||||
@@ -48,6 +48,7 @@ UPDATE `hg_admin_menu` SET `title` = '创建邀请码' WHERE `name` = 'imInviteC
|
||||
UPDATE `hg_admin_menu` SET `title` = '批量创建邀请码' WHERE `name` = 'imInviteBatchCreate';
|
||||
UPDATE `hg_admin_menu` SET `title` = '批次详情' WHERE `name` = 'imInviteBatchView';
|
||||
UPDATE `hg_admin_menu` SET `title` = '撤销邀请码' WHERE `name` = 'imInviteRevoke';
|
||||
UPDATE `hg_admin_menu` SET `title` = '绑定用户' WHERE `name` = 'imInviteBindUser';
|
||||
UPDATE `hg_admin_menu` SET `title` = '撤销批次邀请码' WHERE `name` = 'imInviteBatchRevoke';
|
||||
UPDATE `hg_admin_menu` SET `title` = '导出邀请码' WHERE `name` = 'imInviteExport';
|
||||
|
||||
|
||||
@@ -87,9 +87,10 @@ WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name`='workbenchManager')
|
||||
|
||||
SET @workbenchRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name`='workbenchManager' LIMIT 1);
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=10, `updated_at`=@now WHERE `id`=@workbenchId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='mailbox-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=20, `updated_at`=@now WHERE `id`=@mailboxConfigId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='feedback-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=30, `updated_at`=@now WHERE `id`=@feedbackConfigId;
|
||||
UPDATE `hg_admin_menu` child JOIN `hg_admin_menu` page ON page.`id`=child.`pid` SET child.`level`=3, child.`tree`=CONCAT('tr_', @workbenchRootId, ' tr_', page.`id`, ' '), child.`updated_at`=@now WHERE page.`id` IN (@workbenchId, @mailboxConfigId, @feedbackConfigId) AND child.`type`=3;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `title`='组织架构', `path`='org/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=20, `updated_at`=@now WHERE `id`=@imOrgId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='mailbox-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=30, `updated_at`=@now WHERE `id`=@mailboxConfigId;
|
||||
UPDATE `hg_admin_menu` SET `pid`=@workbenchRootId, `path`='feedback-config/index', `active_menu`='workbenchManager', `level`=2, `tree`=CONCAT('tr_', @workbenchRootId, ' '), `sort`=40, `updated_at`=@now WHERE `id`=@feedbackConfigId;
|
||||
UPDATE `hg_admin_menu` child JOIN `hg_admin_menu` page ON page.`id`=child.`pid` SET child.`level`=3, child.`tree`=CONCAT('tr_', @workbenchRootId, ' tr_', page.`id`, ' '), child.`updated_at`=@now WHERE page.`id` IN (@workbenchId, @imOrgId, @mailboxConfigId, @feedbackConfigId) AND child.`type`=3;
|
||||
|
||||
INSERT IGNORE INTO `hg_admin_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT `role_id`, @workbenchRootId FROM `hg_admin_role_menu` WHERE `menu_id` IN (@workbenchId, @mailboxConfigId, @feedbackConfigId);
|
||||
SELECT `role_id`, @workbenchRootId FROM `hg_admin_role_menu` WHERE `menu_id` IN (@workbenchId, @imOrgId, @mailboxConfigId, @feedbackConfigId);
|
||||
|
||||
@@ -1,40 +1,60 @@
|
||||
SET @now := NOW();
|
||||
SET @imRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1);
|
||||
SET @workbenchRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'workbenchManager' LIMIT 1);
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '组织架构', 'imOrgIndex', 'org/index', '', '2', '', '/imOrg/departmentList', '', '/imOrg/index', '1', 'imManager', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '65', '', '1', @now, @now
|
||||
SELECT NULL, COALESCE(@workbenchRootId, @imRootId), '组织架构', 'imOrgIndex', 'org/index', '', '2', '', '/imOrg/departmentList', '', '/imOrg/index', '1', IF(@workbenchRootId IS NULL, 'imManager', 'workbenchManager'), '0', '0', '', '0', '0', '0', IF(@workbenchRootId IS NULL, '2', '2'), CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgIndex');
|
||||
|
||||
SET @imOrgId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imOrgIndex' LIMIT 1);
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid` = COALESCE(@workbenchRootId, @imRootId),
|
||||
`title` = '组织架构',
|
||||
`path` = 'org/index',
|
||||
`active_menu` = IF(@workbenchRootId IS NULL, 'imManager', 'workbenchManager'),
|
||||
`level` = 2,
|
||||
`tree` = CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' '),
|
||||
`sort` = 20,
|
||||
`status` = 1,
|
||||
`hidden` = 0,
|
||||
`updated_at` = @now
|
||||
WHERE `id` = @imOrgId;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '部门编辑', 'imOrgDepartmentEdit', '', '', '3', '', '/imOrg/departmentEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '10', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '部门编辑', 'imOrgDepartmentEdit', '', '', '3', '', '/imOrg/departmentEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentEdit');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '部门导出', 'imOrgDepartmentExport', '', '', '3', '', '/imOrg/departmentExport', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '15', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '部门导出', 'imOrgDepartmentExport', '', '', '3', '', '/imOrg/departmentExport', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '15', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentExport');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '部门删除', 'imOrgDepartmentDelete', '', '', '3', '', '/imOrg/departmentDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '20', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '部门删除', 'imOrgDepartmentDelete', '', '', '3', '', '/imOrg/departmentDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentDelete');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '成员列表', 'imOrgMemberList', '', '', '3', '', '/imOrg/memberList', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '30', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '成员列表', 'imOrgMemberList', '', '', '3', '', '/imOrg/memberList', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '30', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberList');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '成员编辑', 'imOrgMemberEdit', '', '', '3', '', '/imOrg/memberEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '40', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '成员编辑', 'imOrgMemberEdit', '', '', '3', '', '/imOrg/memberEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '40', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberEdit');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '成员导出', 'imOrgMemberExport', '', '', '3', '', '/imOrg/memberExport', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '45', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '成员导出', 'imOrgMemberExport', '', '', '3', '', '/imOrg/memberExport', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '45', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberExport');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '成员移除', 'imOrgMemberDelete', '', '', '3', '', '/imOrg/memberDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '50', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '成员移除', 'imOrgMemberDelete', '', '', '3', '', '/imOrg/memberDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '50', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberDelete');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imOrgId, '组织搜索', 'imOrgSearch', '', '', '3', '', '/imOrg/search', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '60', '', '1', @now, @now
|
||||
SELECT NULL, @imOrgId, '组织搜索', 'imOrgSearch', '', '', '3', '', '/imOrg/search', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '), '60', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgSearch');
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `level` = 3,
|
||||
`tree` = CONCAT('tr_', COALESCE(@workbenchRootId, @imRootId), ' tr_', @imOrgId, ' '),
|
||||
`updated_at` = @now
|
||||
WHERE `pid` = @imOrgId AND `type` = 3;
|
||||
|
||||
@@ -22,6 +22,7 @@ WHERE @workbenchRootId IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imWorkbenchIndex');
|
||||
|
||||
SET @imWorkbenchId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imWorkbenchIndex' LIMIT 1);
|
||||
SET @imOrgId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imOrgIndex' LIMIT 1);
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid` = @workbenchRootId, `title` = '工作台配置', `path` = 'config/index',
|
||||
@@ -31,6 +32,14 @@ SET `pid` = @workbenchRootId, `title` = '工作台配置', `path` = 'config/inde
|
||||
`updated_at` = @now
|
||||
WHERE `id` = @imWorkbenchId;
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid` = @workbenchRootId, `title` = '组织架构', `path` = 'org/index',
|
||||
`type` = 2, `permissions` = '/imOrg/departmentList', `component` = '/imOrg/index',
|
||||
`always_show` = 1, `active_menu` = 'workbenchManager', `is_root` = 0, `hidden` = 0,
|
||||
`level` = 2, `tree` = CONCAT('tr_', @workbenchRootId, ' '), `sort` = 20, `status` = 1,
|
||||
`updated_at` = @now
|
||||
WHERE `id` = @imOrgId;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT @imWorkbenchId, item.title, item.name, '', '', 3, '', item.permissions, '', '', 1, '', 0, 0, '', 0, 1, 0, 3, CONCAT('tr_', @workbenchRootId, ' tr_', @imWorkbenchId, ' '), item.sort, '', 1, @now, @now
|
||||
FROM (
|
||||
@@ -51,13 +60,13 @@ SET @feedbackConfigId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imFee
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid` = @workbenchRootId, `path` = 'mailbox-config/index', `active_menu` = 'workbenchManager',
|
||||
`level` = 2, `tree` = CONCAT('tr_', @workbenchRootId, ' '), `sort` = 20,
|
||||
`level` = 2, `tree` = CONCAT('tr_', @workbenchRootId, ' '), `sort` = 30,
|
||||
`status` = 1, `hidden` = 0, `updated_at` = @now
|
||||
WHERE `id` = @mailboxConfigId;
|
||||
|
||||
UPDATE `hg_admin_menu`
|
||||
SET `pid` = @workbenchRootId, `path` = 'feedback-config/index', `active_menu` = 'workbenchManager',
|
||||
`level` = 2, `tree` = CONCAT('tr_', @workbenchRootId, ' '), `sort` = 30,
|
||||
`level` = 2, `tree` = CONCAT('tr_', @workbenchRootId, ' '), `sort` = 40,
|
||||
`status` = 1, `hidden` = 0, `updated_at` = @now
|
||||
WHERE `id` = @feedbackConfigId;
|
||||
|
||||
@@ -66,11 +75,11 @@ JOIN `hg_admin_menu` page ON page.`id` = child.`pid`
|
||||
SET child.`level` = 3,
|
||||
child.`tree` = CONCAT('tr_', @workbenchRootId, ' tr_', page.`id`, ' '),
|
||||
child.`updated_at` = @now
|
||||
WHERE page.`id` IN (@mailboxConfigId, @feedbackConfigId)
|
||||
WHERE page.`id` IN (@imOrgId, @mailboxConfigId, @feedbackConfigId)
|
||||
AND child.`type` = 3;
|
||||
|
||||
-- Preserve access for every role which could already open a workbench feature.
|
||||
INSERT IGNORE INTO `hg_admin_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT `role_id`, @workbenchRootId
|
||||
FROM `hg_admin_role_menu`
|
||||
WHERE `menu_id` IN (@imWorkbenchId, @mailboxConfigId, @feedbackConfigId);
|
||||
WHERE `menu_id` IN (@imWorkbenchId, @imOrgId, @mailboxConfigId, @feedbackConfigId);
|
||||
|
||||
@@ -81,6 +81,7 @@ CREATE TABLE IF NOT EXISTS im_registration_invite_batches (
|
||||
id BIGINT NOT NULL,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
quantity INT UNSIGNED NOT NULL,
|
||||
max_uses_per_code INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
expires_at DATETIME(3) NOT NULL,
|
||||
created_by BIGINT NOT NULL,
|
||||
@@ -99,6 +100,9 @@ CREATE TABLE IF NOT EXISTS im_registration_invite_codes (
|
||||
code CHAR(5) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
status VARCHAR(16) NOT NULL,
|
||||
expires_at DATETIME(3) NOT NULL,
|
||||
max_uses INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
bound_user_id BIGINT NULL,
|
||||
bound_user_label VARCHAR(128) NOT NULL DEFAULT '',
|
||||
usage_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
last_used_at DATETIME(3) NULL,
|
||||
created_by BIGINT NOT NULL,
|
||||
@@ -107,6 +111,7 @@ CREATE TABLE IF NOT EXISTS im_registration_invite_codes (
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_im_registration_invite_code_tenant (tenant_id, code),
|
||||
KEY idx_im_registration_invite_tenant_bound_user (tenant_id, bound_user_id),
|
||||
KEY idx_im_registration_invite_tenant_status_expires (tenant_id, status, expires_at),
|
||||
KEY idx_im_registration_invite_tenant_batch (tenant_id, batch_id),
|
||||
KEY idx_im_registration_invite_tenant_created (tenant_id, created_at)
|
||||
@@ -166,8 +171,9 @@ FROM (
|
||||
SELECT '批量生成邀请码', 'imInviteBatchCreate', '/imInvite/batchCreate', 20 UNION ALL
|
||||
SELECT '批次详情', 'imInviteBatchView', '/imInvite/batchView', 30 UNION ALL
|
||||
SELECT '撤销邀请码', 'imInviteRevoke', '/imInvite/revoke', 40 UNION ALL
|
||||
SELECT '批量撤销邀请码', 'imInviteBatchRevoke', '/imInvite/batchRevoke', 50 UNION ALL
|
||||
SELECT '导出邀请码', 'imInviteExport', '/imInvite/export', 60
|
||||
SELECT '绑定用户', 'imInviteBindUser', '/imInvite/bindUser', 50 UNION ALL
|
||||
SELECT '批量撤销邀请码', 'imInviteBatchRevoke', '/imInvite/batchRevoke', 60 UNION ALL
|
||||
SELECT '导出邀请码', 'imInviteExport', '/imInvite/export', 70
|
||||
) p
|
||||
WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu m WHERE m.name = p.name);
|
||||
|
||||
|
||||
@@ -40,6 +40,14 @@ export function Revoke(params) {
|
||||
});
|
||||
}
|
||||
|
||||
export function BindUser(params) {
|
||||
return http.request({
|
||||
url: '/imInvite/bindUser',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchRevoke(params) {
|
||||
return http.request({
|
||||
url: '/imInvite/batchRevoke',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<n-modal v-model:show="showModal" preset="card" title="批次详情" class="batch-view-modal">
|
||||
<n-descriptions :column="4" bordered size="small">
|
||||
<n-descriptions-item label="批次ID">{{ detail.batchId || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="总数">{{ detail.quantity || 0 }}</n-descriptions-item>
|
||||
<n-descriptions-item label="有效">{{ detail.activeCount || 0 }}</n-descriptions-item>
|
||||
<n-descriptions-item label="累计注册次数">{{ detail.usageCount || 0 }}</n-descriptions-item>
|
||||
@@ -27,6 +26,16 @@
|
||||
{ title: '邀请码', key: 'code' },
|
||||
{ title: '状态', key: 'status' },
|
||||
{ title: '累计使用次数', key: 'usageCount' },
|
||||
{
|
||||
title: '可用次数',
|
||||
key: 'availableUses',
|
||||
render(row: any) {
|
||||
if (!row.maxUses || Number(row.maxUses) <= 0) {
|
||||
return '不限制';
|
||||
}
|
||||
return `${Math.max(Number(row.maxUses) - Number(row.usageCount || 0), 0)}`;
|
||||
},
|
||||
},
|
||||
{ title: '最后使用时间', key: 'lastUsedAt' },
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<n-modal v-model:show="showModal" preset="card" title="绑定用户" class="bind-user-modal">
|
||||
<n-form :model="searchForm" label-placement="left" label-width="90">
|
||||
<n-grid :cols="2" :x-gap="12">
|
||||
<n-form-item-gi label="用户ID">
|
||||
<n-input v-model:value="searchForm.userId" clearable placeholder="用户ID/编号" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi label="关键词">
|
||||
<n-input v-model:value="searchForm.keyword" clearable placeholder="用户名/昵称/手机号" />
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
<n-space>
|
||||
<n-button type="primary" :loading="loading" @click="handleSearch">查询</n-button>
|
||||
<n-button @click="handleReset">重置</n-button>
|
||||
</n-space>
|
||||
</n-form>
|
||||
<n-data-table
|
||||
class="user-table"
|
||||
:columns="columns"
|
||||
:data="users"
|
||||
:loading="loading"
|
||||
:row-key="(row) => row.userId"
|
||||
:checked-row-keys="checkedRowKeys"
|
||||
:pagination="pagination"
|
||||
@update:checked-row-keys="handleChecked"
|
||||
@update:page="handlePageChange"
|
||||
/>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showModal = false">取消</n-button>
|
||||
<n-button type="primary" :disabled="!selectedUserId" :loading="submitting" @click="handleSubmit">
|
||||
确认绑定
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, h, reactive, ref } from 'vue';
|
||||
import { NAvatar, useMessage } from 'naive-ui';
|
||||
import { List as UserList } from '@/api/imUser';
|
||||
import { BindUser } from '@/api/im/registrationInvite';
|
||||
import defaultAvatar from '@/assets/images/schoolboy.png';
|
||||
|
||||
const emit = defineEmits(['reload']);
|
||||
const message = useMessage();
|
||||
const showModal = ref(false);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const tenantId = ref(0);
|
||||
const inviteId = ref('');
|
||||
const users = ref<any[]>([]);
|
||||
const checkedRowKeys = ref<string[]>([]);
|
||||
const searchForm = reactive({ userId: '', keyword: '' });
|
||||
const pagination = reactive({ page: 1, pageSize: 10, itemCount: 0, showSizePicker: false });
|
||||
const selectedUserId = computed(() => checkedRowKeys.value[0] || '');
|
||||
|
||||
const columns = [
|
||||
{ type: 'selection', multiple: false },
|
||||
{ title: '编号', key: 'displayNo', width: 90 },
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 70,
|
||||
render(row: any) {
|
||||
return h(NAvatar, { size: 36, src: row.avatar || defaultAvatar });
|
||||
},
|
||||
},
|
||||
{ title: '用户ID', key: 'userId', width: 130 },
|
||||
{ title: '用户名', key: 'username', width: 140 },
|
||||
{
|
||||
title: '昵称',
|
||||
key: 'nickname',
|
||||
width: 140,
|
||||
render(row: any) {
|
||||
return row.nickname || '--';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
key: 'phone',
|
||||
width: 150,
|
||||
render(row: any) {
|
||||
return row.phone || '--';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
async function loadUsers() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await UserList({
|
||||
tenantId: tenantId.value,
|
||||
userId: searchForm.userId,
|
||||
keyword: searchForm.keyword,
|
||||
page: pagination.page,
|
||||
perPage: pagination.pageSize,
|
||||
});
|
||||
users.value = res?.list || [];
|
||||
pagination.itemCount = Number(res?.total || 0);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleChecked(keys: string[]) {
|
||||
checkedRowKeys.value = keys.slice(-1);
|
||||
}
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
pagination.page = page;
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1;
|
||||
checkedRowKeys.value = [];
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.userId = '';
|
||||
searchForm.keyword = '';
|
||||
handleSearch();
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!selectedUserId.value) {
|
||||
message.warning('请选择要绑定的用户');
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
await BindUser({ tenantId: tenantId.value, id: inviteId.value, userId: Number(selectedUserId.value) });
|
||||
message.success('绑定成功');
|
||||
showModal.value = false;
|
||||
emit('reload');
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openModal(rowTenantId: number, rowInviteId: string) {
|
||||
tenantId.value = rowTenantId;
|
||||
inviteId.value = rowInviteId;
|
||||
searchForm.userId = '';
|
||||
searchForm.keyword = '';
|
||||
users.value = [];
|
||||
checkedRowKeys.value = [];
|
||||
pagination.page = 1;
|
||||
pagination.itemCount = 0;
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
defineExpose({ openModal });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.bind-user-modal {
|
||||
width: min(980px, 94vw);
|
||||
}
|
||||
|
||||
.user-table {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -45,6 +45,7 @@
|
||||
</n-card>
|
||||
<CreateModal ref="createRef" @reload="reloadTable" />
|
||||
<BatchViewModal ref="batchViewRef" />
|
||||
<BindUserModal ref="bindUserRef" @reload="reloadTable" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -69,6 +70,7 @@
|
||||
import { adaTableScrollX } from '@/utils/hotgo';
|
||||
import CreateModal from './CreateModal.vue';
|
||||
import BatchViewModal from './BatchViewModal.vue';
|
||||
import BindUserModal from './BindUserModal.vue';
|
||||
|
||||
const message = useMessage();
|
||||
const dialog = useDialog();
|
||||
@@ -78,6 +80,7 @@
|
||||
const searchFormRef = ref<any>({});
|
||||
const createRef = ref();
|
||||
const batchViewRef = ref();
|
||||
const bindUserRef = ref();
|
||||
const isPlatformAdmin = computed(() => userStore.isCompanyDept);
|
||||
|
||||
const tableColumns = computed(() =>
|
||||
@@ -98,7 +101,7 @@
|
||||
);
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 300,
|
||||
width: 380,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
@@ -106,6 +109,12 @@
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: '绑定用户',
|
||||
onClick: handleBindUser.bind(null, record),
|
||||
auth: ['/imInvite/bindUser'],
|
||||
ifShow: () => record.status === 'active' && !record.boundUserId && Number(record.usageCount || 0) === 0,
|
||||
},
|
||||
{
|
||||
label: '批次',
|
||||
onClick: handleBatchView.bind(null, record),
|
||||
@@ -168,6 +177,10 @@
|
||||
batchViewRef.value.openModal(Number(record.tenantId || selectedTenantId()), record.batchId);
|
||||
}
|
||||
|
||||
function handleBindUser(record: State) {
|
||||
bindUserRef.value.openModal(Number(record.tenantId || selectedTenantId()), record.id);
|
||||
}
|
||||
|
||||
function handleRevoke(record: State) {
|
||||
dialog.warning({
|
||||
title: '确认撤销',
|
||||
@@ -185,7 +198,7 @@
|
||||
function handleBatchRevoke(record: State) {
|
||||
dialog.warning({
|
||||
title: '确认撤销批次',
|
||||
content: `将撤销批次 ${record.batchId} 下仍有效的邀请码,已过期或已撤销的邀请码不受影响,是否确认?`,
|
||||
content: '将撤销该批次下仍有效的邀请码,已过期或已撤销的邀请码不受影响,是否确认?',
|
||||
positiveText: '确认',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
|
||||
@@ -16,7 +16,10 @@ export class State {
|
||||
public code = '';
|
||||
public status = '';
|
||||
public expiresAt = '';
|
||||
public maxUses = 0;
|
||||
public usageCount = 0;
|
||||
public boundUserId = '';
|
||||
public boundUserLabel = '';
|
||||
public lastUsedAt = '';
|
||||
public createdBy = '';
|
||||
public remark = '';
|
||||
@@ -59,14 +62,6 @@ export const schemas = ref<FormSchema[]>([
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'batchId',
|
||||
component: 'NInput',
|
||||
label: '批次ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入批次ID',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createdBy',
|
||||
component: 'NInput',
|
||||
@@ -102,9 +97,26 @@ export const columns = [
|
||||
{ title: '商户', key: 'tenantName', align: 'left', width: 160 },
|
||||
{ title: '邀请码', key: 'code', align: 'left', width: 120 },
|
||||
{ title: '状态', key: 'status', align: 'left', width: 110 },
|
||||
{ title: '批次ID', key: 'batchId', align: 'left', width: 150 },
|
||||
{ title: '到期时间', key: 'expiresAt', align: 'left', width: 190 },
|
||||
{ title: '累计使用次数', key: 'usageCount', align: 'left', width: 130 },
|
||||
{
|
||||
title: '可用次数',
|
||||
key: 'availableUses',
|
||||
align: 'left',
|
||||
width: 110,
|
||||
render(row: State) {
|
||||
return inviteAvailableUsesLabel(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '绑定用户',
|
||||
key: 'boundUserLabel',
|
||||
align: 'left',
|
||||
width: 180,
|
||||
render(row: State) {
|
||||
return row.boundUserLabel || row.boundUserId || '--';
|
||||
},
|
||||
},
|
||||
{ title: '最后使用时间', key: 'lastUsedAt', align: 'left', width: 190 },
|
||||
{ title: '创建人', key: 'createdBy', align: 'left', width: 110 },
|
||||
{ title: '创建时间', key: 'createdAt', align: 'left', width: 190 },
|
||||
@@ -128,6 +140,13 @@ export function statusLabel(status: string) {
|
||||
return statusOptions.find((item) => item.value === status)?.label || status;
|
||||
}
|
||||
|
||||
export function inviteAvailableUsesLabel(row: State) {
|
||||
if (!row.maxUses || Number(row.maxUses) <= 0) {
|
||||
return '不限制';
|
||||
}
|
||||
return `${Math.max(Number(row.maxUses) - Number(row.usageCount || 0), 0)}`;
|
||||
}
|
||||
|
||||
export function localRfc3339(value: number | null) {
|
||||
if (!value) {
|
||||
return '';
|
||||
|
||||
Reference in New Issue
Block a user