feat: complete IM group governance actions
This commit is contained in:
+13
-14
@@ -13,7 +13,7 @@
|
|||||||
4. 审计日志:列表、详情、导出。
|
4. 审计日志:列表、详情、导出。
|
||||||
5. Outbox 运维:列表、详情、重试、标记死信,覆盖 `im_business_events_outbox` 和 `im_group_events_outbox`。
|
5. Outbox 运维:列表、详情、重试、标记死信,覆盖 `im_business_events_outbox` 和 `im_group_events_outbox`。
|
||||||
6. 租户边界:用户、审计、群、实名、工单、Outbox 等后台接口已按登录态解析租户范围。
|
6. 租户边界:用户、审计、群、实名、工单、Outbox 等后台接口已按登录态解析租户范围。
|
||||||
7. 群治理处置:治理配置更新、解散群、成员禁言、解除禁言。
|
7. 群治理处置:治理配置更新、解散群、成员禁言、解除禁言、入群审批、管理员设置/取消、群主转让、群文件删除。
|
||||||
|
|
||||||
## 剩余 P0
|
## 剩余 P0
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
|
|
||||||
### P1-1 群治理处置动作
|
### P1-1 群治理处置动作
|
||||||
|
|
||||||
状态:部分实现。
|
状态:已完成基础后台闭环。
|
||||||
|
|
||||||
已实现:
|
已实现:
|
||||||
|
|
||||||
@@ -131,20 +131,19 @@
|
|||||||
2. 成员列表。
|
2. 成员列表。
|
||||||
3. 治理配置查看。
|
3. 治理配置查看。
|
||||||
4. 入群申请、邀请、公告、禁言、管理日志列表。
|
4. 入群申请、邀请、公告、禁言、管理日志列表。
|
||||||
|
5. 治理配置更新。
|
||||||
|
6. 全员禁言开关。
|
||||||
|
7. 单人禁言/解禁。
|
||||||
|
8. 解散群。
|
||||||
|
9. 入群审批通过/拒绝。
|
||||||
|
10. 设置/取消管理员。
|
||||||
|
11. 转让群主。
|
||||||
|
12. 群文件列表和删除处置。
|
||||||
|
|
||||||
未实现:
|
待 IM 服务端配合确认:
|
||||||
|
|
||||||
1. 入群审批通过/拒绝。
|
1. 后台写入 `im_group_events_outbox` 后,IM 服务端消费者需同步在线会话、群资料缓存和成员 ACL。
|
||||||
2. 设置/取消管理员。
|
2. 群文件删除当前为业务索引软删除,底层对象文件物理清理归入 P1-2 文件治理。
|
||||||
3. 转让群主。
|
|
||||||
4. 群文件处置。
|
|
||||||
|
|
||||||
已增强:
|
|
||||||
|
|
||||||
1. 治理配置更新。
|
|
||||||
2. 全员禁言开关。
|
|
||||||
3. 单人禁言/解禁。
|
|
||||||
4. 解散群。
|
|
||||||
|
|
||||||
### P1-2 文件治理
|
### P1-2 文件治理
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,38 @@ type UnmuteReq struct {
|
|||||||
|
|
||||||
type UnmuteRes struct{}
|
type UnmuteRes struct{}
|
||||||
|
|
||||||
|
// JoinRequestApproveReq 通过IM入群申请
|
||||||
|
type JoinRequestApproveReq struct {
|
||||||
|
g.Meta `path:"/imGroup/joinRequestApprove" method:"post" tags:"IM群组" summary:"通过IM入群申请"`
|
||||||
|
sysin.ImGroupJoinRequestResolveInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type JoinRequestApproveRes struct{}
|
||||||
|
|
||||||
|
// JoinRequestRejectReq 拒绝IM入群申请
|
||||||
|
type JoinRequestRejectReq struct {
|
||||||
|
g.Meta `path:"/imGroup/joinRequestReject" method:"post" tags:"IM群组" summary:"拒绝IM入群申请"`
|
||||||
|
sysin.ImGroupJoinRequestResolveInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type JoinRequestRejectRes struct{}
|
||||||
|
|
||||||
|
// RoleUpdateReq 更新IM群成员角色
|
||||||
|
type RoleUpdateReq struct {
|
||||||
|
g.Meta `path:"/imGroup/roleUpdate" method:"post" tags:"IM群组" summary:"更新IM群成员角色"`
|
||||||
|
sysin.ImGroupRoleUpdateInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleUpdateRes struct{}
|
||||||
|
|
||||||
|
// OwnerTransferReq 转让IM群主
|
||||||
|
type OwnerTransferReq struct {
|
||||||
|
g.Meta `path:"/imGroup/ownerTransfer" method:"post" tags:"IM群组" summary:"转让IM群主"`
|
||||||
|
sysin.ImGroupOwnerTransferInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type OwnerTransferRes struct{}
|
||||||
|
|
||||||
// JoinRequestListReq 查询IM入群申请列表
|
// JoinRequestListReq 查询IM入群申请列表
|
||||||
type JoinRequestListReq struct {
|
type JoinRequestListReq struct {
|
||||||
g.Meta `path:"/imGroup/joinRequestList" method:"get" tags:"IM群组" summary:"获取IM入群申请列表"`
|
g.Meta `path:"/imGroup/joinRequestList" method:"get" tags:"IM群组" summary:"获取IM入群申请列表"`
|
||||||
@@ -97,6 +129,25 @@ type JoinRequestListRes struct {
|
|||||||
List []*sysin.ImGroupJoinRequestListModel `json:"list" dc:"数据列表"`
|
List []*sysin.ImGroupJoinRequestListModel `json:"list" dc:"数据列表"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileListReq 查询IM群文件列表
|
||||||
|
type FileListReq struct {
|
||||||
|
g.Meta `path:"/imGroup/fileList" method:"get" tags:"IM群组" summary:"获取IM群文件列表"`
|
||||||
|
sysin.ImGroupFileListInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileListRes struct {
|
||||||
|
form.PageRes
|
||||||
|
List []*sysin.ImGroupFileListModel `json:"list" dc:"数据列表"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileDeleteReq 删除IM群文件
|
||||||
|
type FileDeleteReq struct {
|
||||||
|
g.Meta `path:"/imGroup/fileDelete" method:"post" tags:"IM群组" summary:"删除IM群文件"`
|
||||||
|
sysin.ImGroupFileDeleteInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileDeleteRes struct{}
|
||||||
|
|
||||||
// InvitationListReq 查询IM群邀请列表
|
// InvitationListReq 查询IM群邀请列表
|
||||||
type InvitationListReq struct {
|
type InvitationListReq struct {
|
||||||
g.Meta `path:"/imGroup/invitationList" method:"get" tags:"IM群组" summary:"获取IM群邀请列表"`
|
g.Meta `path:"/imGroup/invitationList" method:"get" tags:"IM群组" summary:"获取IM群邀请列表"`
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ func init() {
|
|||||||
dict.RegisterEnums("ImGroupMuteStatusOptions", "IM禁言状态选项", ImGroupMuteStatusOptions)
|
dict.RegisterEnums("ImGroupMuteStatusOptions", "IM禁言状态选项", ImGroupMuteStatusOptions)
|
||||||
dict.RegisterEnums("ImGroupAnnouncementStatusOptions", "IM群公告状态选项", ImGroupAnnouncementStatusOptions)
|
dict.RegisterEnums("ImGroupAnnouncementStatusOptions", "IM群公告状态选项", ImGroupAnnouncementStatusOptions)
|
||||||
dict.RegisterEnums("ImGroupOutboxStatusOptions", "IM群事件状态选项", ImGroupOutboxStatusOptions)
|
dict.RegisterEnums("ImGroupOutboxStatusOptions", "IM群事件状态选项", ImGroupOutboxStatusOptions)
|
||||||
|
dict.RegisterEnums("ImGroupFileStatusOptions", "IM群文件状态选项", ImGroupFileStatusOptions)
|
||||||
|
dict.RegisterEnums("ImGroupFileTypeOptions", "IM群文件类型选项", ImGroupFileTypeOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ImGroupStateOptions = []*model.Option{
|
var ImGroupStateOptions = []*model.Option{
|
||||||
@@ -58,6 +60,14 @@ const (
|
|||||||
ImGroupOutboxStatusPending = "pending"
|
ImGroupOutboxStatusPending = "pending"
|
||||||
ImGroupOutboxStatusDelivered = "delivered"
|
ImGroupOutboxStatusDelivered = "delivered"
|
||||||
ImGroupOutboxStatusFailed = "failed"
|
ImGroupOutboxStatusFailed = "failed"
|
||||||
|
|
||||||
|
ImGroupFileStatusActive = "active"
|
||||||
|
ImGroupFileStatusDeleted = "deleted"
|
||||||
|
|
||||||
|
ImGroupFileTypeFile = "file"
|
||||||
|
ImGroupFileTypeImage = "image"
|
||||||
|
ImGroupFileTypeVideo = "video"
|
||||||
|
ImGroupFileTypeAudio = "audio"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ImGroupStatusOptions = []*model.Option{
|
var ImGroupStatusOptions = []*model.Option{
|
||||||
@@ -103,3 +113,15 @@ var ImGroupOutboxStatusOptions = []*model.Option{
|
|||||||
dict.GenSuccessOption(ImGroupOutboxStatusDelivered, "已投递"),
|
dict.GenSuccessOption(ImGroupOutboxStatusDelivered, "已投递"),
|
||||||
dict.GenErrorOption(ImGroupOutboxStatusFailed, "投递失败"),
|
dict.GenErrorOption(ImGroupOutboxStatusFailed, "投递失败"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ImGroupFileStatusOptions = []*model.Option{
|
||||||
|
dict.GenSuccessOption(ImGroupFileStatusActive, "正常"),
|
||||||
|
dict.GenDefaultOption(ImGroupFileStatusDeleted, "已删除"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var ImGroupFileTypeOptions = []*model.Option{
|
||||||
|
dict.GenDefaultOption(ImGroupFileTypeFile, "文件"),
|
||||||
|
dict.GenInfoOption(ImGroupFileTypeImage, "图片"),
|
||||||
|
dict.GenPrimaryOption(ImGroupFileTypeVideo, "视频"),
|
||||||
|
dict.GenWarningOption(ImGroupFileTypeAudio, "音频"),
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,6 +99,30 @@ func (c *cImGroup) Unmute(ctx context.Context, req *imgroup.UnmuteReq) (res *img
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinRequestApprove 通过IM入群申请
|
||||||
|
func (c *cImGroup) JoinRequestApprove(ctx context.Context, req *imgroup.JoinRequestApproveReq) (res *imgroup.JoinRequestApproveRes, err error) {
|
||||||
|
err = service.SysImGroup().JoinRequestApprove(ctx, &req.ImGroupJoinRequestResolveInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinRequestReject 拒绝IM入群申请
|
||||||
|
func (c *cImGroup) JoinRequestReject(ctx context.Context, req *imgroup.JoinRequestRejectReq) (res *imgroup.JoinRequestRejectRes, err error) {
|
||||||
|
err = service.SysImGroup().JoinRequestReject(ctx, &req.ImGroupJoinRequestResolveInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleUpdate 更新IM群成员角色
|
||||||
|
func (c *cImGroup) RoleUpdate(ctx context.Context, req *imgroup.RoleUpdateReq) (res *imgroup.RoleUpdateRes, err error) {
|
||||||
|
err = service.SysImGroup().RoleUpdate(ctx, &req.ImGroupRoleUpdateInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnerTransfer 转让IM群主
|
||||||
|
func (c *cImGroup) OwnerTransfer(ctx context.Context, req *imgroup.OwnerTransferReq) (res *imgroup.OwnerTransferRes, err error) {
|
||||||
|
err = service.SysImGroup().OwnerTransfer(ctx, &req.ImGroupOwnerTransferInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// JoinRequestList 查看IM入群申请列表
|
// JoinRequestList 查看IM入群申请列表
|
||||||
func (c *cImGroup) JoinRequestList(ctx context.Context, req *imgroup.JoinRequestListReq) (res *imgroup.JoinRequestListRes, err error) {
|
func (c *cImGroup) JoinRequestList(ctx context.Context, req *imgroup.JoinRequestListReq) (res *imgroup.JoinRequestListRes, err error) {
|
||||||
list, totalCount, err := service.SysImGroup().JoinRequestList(ctx, &req.ImGroupJoinRequestListInp)
|
list, totalCount, err := service.SysImGroup().JoinRequestList(ctx, &req.ImGroupJoinRequestListInp)
|
||||||
@@ -114,6 +138,27 @@ func (c *cImGroup) JoinRequestList(ctx context.Context, req *imgroup.JoinRequest
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileList 查看IM群文件列表
|
||||||
|
func (c *cImGroup) FileList(ctx context.Context, req *imgroup.FileListReq) (res *imgroup.FileListRes, err error) {
|
||||||
|
list, totalCount, err := service.SysImGroup().FileList(ctx, &req.ImGroupFileListInp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if list == nil {
|
||||||
|
list = []*sysin.ImGroupFileListModel{}
|
||||||
|
}
|
||||||
|
res = new(imgroup.FileListRes)
|
||||||
|
res.List = list
|
||||||
|
res.PageRes.Pack(req, totalCount)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileDelete 删除IM群文件
|
||||||
|
func (c *cImGroup) FileDelete(ctx context.Context, req *imgroup.FileDeleteReq) (res *imgroup.FileDeleteRes, err error) {
|
||||||
|
err = service.SysImGroup().FileDelete(ctx, &req.ImGroupFileDeleteInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// InvitationList 查看IM群邀请列表
|
// InvitationList 查看IM群邀请列表
|
||||||
func (c *cImGroup) InvitationList(ctx context.Context, req *imgroup.InvitationListReq) (res *imgroup.InvitationListRes, err error) {
|
func (c *cImGroup) InvitationList(ctx context.Context, req *imgroup.InvitationListReq) (res *imgroup.InvitationListRes, err error) {
|
||||||
list, totalCount, err := service.SysImGroup().InvitationList(ctx, &req.ImGroupInvitationListInp)
|
list, totalCount, err := service.SysImGroup().InvitationList(ctx, &req.ImGroupInvitationListInp)
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ import (
|
|||||||
|
|
||||||
type sSysImGroup struct{}
|
type sSysImGroup struct{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
imGroupModeMember = "JRWPS"
|
||||||
|
imGroupModeAdmin = "JRWPASO"
|
||||||
|
imGroupModeOwner = "JRWPASDO"
|
||||||
|
)
|
||||||
|
|
||||||
func NewSysImGroup() *sSysImGroup {
|
func NewSysImGroup() *sSysImGroup {
|
||||||
return &sSysImGroup{}
|
return &sSysImGroup{}
|
||||||
}
|
}
|
||||||
@@ -523,6 +529,262 @@ func (s *sSysImGroup) Unmute(ctx context.Context, in *sysin.ImGroupUnmuteInp) (e
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinRequestApprove 通过IM入群申请
|
||||||
|
func (s *sSysImGroup) JoinRequestApprove(ctx context.Context, in *sysin.ImGroupJoinRequestResolveInp) (err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return s.resolveJoinRequest(ctx, in, consts.ImGroupRequestStatusAccepted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinRequestReject 拒绝IM入群申请
|
||||||
|
func (s *sSysImGroup) JoinRequestReject(ctx context.Context, in *sysin.ImGroupJoinRequestResolveInp) (err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return s.resolveJoinRequest(ctx, in, consts.ImGroupRequestStatusRejected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) resolveJoinRequest(ctx context.Context, in *sysin.ImGroupJoinRequestResolveInp, status string) (err error) {
|
||||||
|
db, _, err := resolveImTenantDB(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
actor := contexts.GetUserId(ctx)
|
||||||
|
requestId := gctx.CtxId(ctx)
|
||||||
|
if requestId == "" {
|
||||||
|
requestId = guid.S()
|
||||||
|
}
|
||||||
|
now := gtime.Now()
|
||||||
|
return db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
if err := s.ensureActiveGroup(ctx, tx, in.Topic); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
before, txErr := s.groupJoinRequestSnapshot(ctx, tx, in.Topic, in.RequestId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if before == nil {
|
||||||
|
return gerror.New("入群申请不存在")
|
||||||
|
}
|
||||||
|
if before["status"].String() != consts.ImGroupRequestStatusPending {
|
||||||
|
return gerror.New("入群申请已处理")
|
||||||
|
}
|
||||||
|
applicantUserId := before["applicant_user_id"].Int64()
|
||||||
|
if status == consts.ImGroupRequestStatusAccepted {
|
||||||
|
if txErr = s.upsertGroupSubscription(ctx, tx, in.Topic, applicantUserId, imGroupModeMember, now); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if txErr = s.upsertGroupMember(ctx, tx, in.Topic, applicantUserId, consts.ImGroupRoleMember, "approval", actor, now); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, txErr := tx.Exec(`UPDATE im_group_join_requests
|
||||||
|
SET status=?,reject_reason=?,handled_by=?,handled_at=?,updated_at=?,version=version+1
|
||||||
|
WHERE topic=? AND request_id=? AND status=?`,
|
||||||
|
status, in.RejectReason, actor, now, now, in.Topic, in.RequestId, consts.ImGroupRequestStatusPending)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if affected, _ := result.RowsAffected(); affected == 0 {
|
||||||
|
return gerror.New("入群申请已处理")
|
||||||
|
}
|
||||||
|
if txErr = s.recountGroupMembers(ctx, tx, in.Topic); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
after, txErr := s.groupJoinRequestSnapshot(ctx, tx, in.Topic, in.RequestId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
action := "join_request_reject"
|
||||||
|
if status == consts.ImGroupRequestStatusAccepted {
|
||||||
|
action = "join_request_accept"
|
||||||
|
}
|
||||||
|
if txErr = s.insertGroupAdminLog(ctx, tx, in.Topic, actor, applicantUserId, action, "join_request", in.RequestId, recordMap(before), recordMap(after), requestId); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return s.insertGroupEvent(ctx, tx, in.Topic, actor, "group.join_request."+status, g.Map{
|
||||||
|
"topic": in.Topic, "request_id": in.RequestId, "applicant_user_id": applicantUserId, "status": status, "request_id_admin": requestId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleUpdate 更新IM群成员角色
|
||||||
|
func (s *sSysImGroup) RoleUpdate(ctx context.Context, in *sysin.ImGroupRoleUpdateInp) (err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.Role != consts.ImGroupRoleAdmin && in.Role != consts.ImGroupRoleMember {
|
||||||
|
return gerror.New("只能设置管理员或普通成员")
|
||||||
|
}
|
||||||
|
db, _, err := resolveImTenantDB(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
actor := contexts.GetUserId(ctx)
|
||||||
|
requestId := gctx.CtxId(ctx)
|
||||||
|
if requestId == "" {
|
||||||
|
requestId = guid.S()
|
||||||
|
}
|
||||||
|
modeGiven := imGroupModeMember
|
||||||
|
action := "member_role_member"
|
||||||
|
if in.Role == consts.ImGroupRoleAdmin {
|
||||||
|
modeGiven = imGroupModeAdmin
|
||||||
|
action = "member_role_admin"
|
||||||
|
}
|
||||||
|
now := gtime.Now()
|
||||||
|
return db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
if err := s.ensureActiveGroup(ctx, tx, in.Topic); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
before, txErr := s.groupMemberSnapshot(ctx, tx, in.Topic, in.UserId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if before == nil || before["member_status"].String() != consts.ImGroupMemberStatusActive {
|
||||||
|
return gerror.New("成员不在群内或不是正常状态")
|
||||||
|
}
|
||||||
|
if before["role"].String() == consts.ImGroupRoleOwner {
|
||||||
|
return gerror.New("群主角色请通过群主转让处理")
|
||||||
|
}
|
||||||
|
if in.Role == consts.ImGroupRoleAdmin {
|
||||||
|
if txErr = s.ensureAdminLimit(ctx, tx, in.Topic, in.UserId); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subResult, txErr := tx.Exec(`UPDATE im_subscriptions SET modegiven=?,updatedat=?
|
||||||
|
WHERE topic=? AND userid=? AND deletedat IS NULL`, modeGiven, now, in.Topic, in.UserId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if affected, _ := subResult.RowsAffected(); affected == 0 {
|
||||||
|
return gerror.New("成员通信订阅不存在")
|
||||||
|
}
|
||||||
|
result, txErr := tx.Exec(`UPDATE im_group_members
|
||||||
|
SET role=?,version=version+1,updated_at=?
|
||||||
|
WHERE topic=? AND user_id=? AND member_status=?`,
|
||||||
|
in.Role, now, in.Topic, in.UserId, consts.ImGroupMemberStatusActive)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if affected, _ := result.RowsAffected(); affected == 0 {
|
||||||
|
return gerror.New("成员不在群内或不是正常状态")
|
||||||
|
}
|
||||||
|
after, txErr := s.groupMemberSnapshot(ctx, tx, in.Topic, in.UserId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if txErr = s.insertGroupAdminLog(ctx, tx, in.Topic, actor, in.UserId, action, "member", fmt.Sprintf("%d", in.UserId), recordMap(before), recordMap(after), requestId); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return s.insertGroupEvent(ctx, tx, in.Topic, actor, "group.role.updated", g.Map{
|
||||||
|
"topic": in.Topic, "user_id": in.UserId, "role": in.Role, "request_id": requestId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnerTransfer 转让IM群主
|
||||||
|
func (s *sSysImGroup) OwnerTransfer(ctx context.Context, in *sysin.ImGroupOwnerTransferInp) (err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
db, _, err := resolveImTenantDB(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
actor := contexts.GetUserId(ctx)
|
||||||
|
requestId := gctx.CtxId(ctx)
|
||||||
|
if requestId == "" {
|
||||||
|
requestId = guid.S()
|
||||||
|
}
|
||||||
|
now := gtime.Now()
|
||||||
|
return db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
beforeGroup, txErr := s.groupGovernanceSnapshot(ctx, tx, in.Topic)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if beforeGroup == nil {
|
||||||
|
return gerror.New("群治理配置不存在")
|
||||||
|
}
|
||||||
|
if beforeGroup["group_status"].String() == consts.ImGroupStatusDissolved {
|
||||||
|
return gerror.New("已解散群不能转让群主")
|
||||||
|
}
|
||||||
|
previousOwner := beforeGroup["owner_user_id"].Int64()
|
||||||
|
if previousOwner <= 0 {
|
||||||
|
return gerror.New("原群主不存在")
|
||||||
|
}
|
||||||
|
if previousOwner == in.UserId {
|
||||||
|
return gerror.New("新群主不能与原群主相同")
|
||||||
|
}
|
||||||
|
if txErr = s.ensureActiveGroupMember(ctx, tx, in.Topic, previousOwner); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if txErr = s.ensureActiveGroupMember(ctx, tx, in.Topic, in.UserId); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
beforeOld, txErr := s.groupMemberSnapshot(ctx, tx, in.Topic, previousOwner)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
beforeNew, txErr := s.groupMemberSnapshot(ctx, tx, in.Topic, in.UserId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
oldSubResult, txErr := tx.Exec(`UPDATE im_subscriptions SET modegiven=?,updatedat=?
|
||||||
|
WHERE topic=? AND userid=? AND deletedat IS NULL`, imGroupModeAdmin, now, in.Topic, previousOwner)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if affected, _ := oldSubResult.RowsAffected(); affected == 0 {
|
||||||
|
return gerror.New("原群主通信订阅不存在")
|
||||||
|
}
|
||||||
|
if _, txErr = tx.Exec(`UPDATE im_group_members SET role=?,version=version+1,updated_at=?
|
||||||
|
WHERE topic=? AND user_id=? AND member_status=?`, consts.ImGroupRoleAdmin, now, in.Topic, previousOwner, consts.ImGroupMemberStatusActive); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
newSubResult, txErr := tx.Exec(`UPDATE im_subscriptions SET modegiven=?,updatedat=?
|
||||||
|
WHERE topic=? AND userid=? AND deletedat IS NULL`, imGroupModeOwner, now, in.Topic, in.UserId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if affected, _ := newSubResult.RowsAffected(); affected == 0 {
|
||||||
|
return gerror.New("新群主通信订阅不存在")
|
||||||
|
}
|
||||||
|
if _, txErr = tx.Exec(`UPDATE im_group_members SET role=?,version=version+1,updated_at=?
|
||||||
|
WHERE topic=? AND user_id=? AND member_status=?`, consts.ImGroupRoleOwner, now, in.Topic, in.UserId, consts.ImGroupMemberStatusActive); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if _, txErr = tx.Exec(`UPDATE im_topics SET owner=?,updatedat=? WHERE name=?`, in.UserId, now, in.Topic); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if _, txErr = tx.Exec(`UPDATE im_group_settings SET owner_user_id=?,version=version+1,updated_at=? WHERE topic=?`, in.UserId, now, in.Topic); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
afterGroup, txErr := s.groupGovernanceSnapshot(ctx, tx, in.Topic)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
afterOld, txErr := s.groupMemberSnapshot(ctx, tx, in.Topic, previousOwner)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
afterNew, txErr := s.groupMemberSnapshot(ctx, tx, in.Topic, in.UserId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if txErr = s.insertGroupAdminLog(ctx, tx, in.Topic, actor, in.UserId, "owner_transfer", "group", in.Topic, g.Map{
|
||||||
|
"group": recordMap(beforeGroup), "previous_owner": recordMap(beforeOld), "new_owner": recordMap(beforeNew),
|
||||||
|
}, g.Map{
|
||||||
|
"group": recordMap(afterGroup), "previous_owner": recordMap(afterOld), "new_owner": recordMap(afterNew),
|
||||||
|
}, requestId); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return s.insertGroupEvent(ctx, tx, in.Topic, actor, "group.owner.transferred", g.Map{
|
||||||
|
"topic": in.Topic, "previous_owner": previousOwner, "owner": in.UserId, "request_id": requestId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sSysImGroup) groupGovernanceSnapshot(ctx context.Context, tx gdb.TX, topic string) (res gdb.Record, err error) {
|
func (s *sSysImGroup) groupGovernanceSnapshot(ctx context.Context, tx gdb.TX, topic string) (res gdb.Record, err error) {
|
||||||
return tx.Model("im_group_settings").Safe().Ctx(ctx).
|
return tx.Model("im_group_settings").Safe().Ctx(ctx).
|
||||||
Fields(
|
Fields(
|
||||||
@@ -535,6 +797,41 @@ func (s *sSysImGroup) groupGovernanceSnapshot(ctx context.Context, tx gdb.TX, to
|
|||||||
One()
|
One()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) groupJoinRequestSnapshot(ctx context.Context, tx gdb.TX, topic string, requestId string) (res gdb.Record, err error) {
|
||||||
|
return tx.Model("im_group_join_requests").Safe().Ctx(ctx).
|
||||||
|
Where("topic", topic).
|
||||||
|
Where("request_id", requestId).
|
||||||
|
One()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) groupMemberSnapshot(ctx context.Context, tx gdb.TX, topic string, userId int64) (res gdb.Record, err error) {
|
||||||
|
return tx.Model("im_group_members").Safe().Ctx(ctx).
|
||||||
|
Where("topic", topic).
|
||||||
|
Where("user_id", userId).
|
||||||
|
One()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) groupFileSnapshot(ctx context.Context, tx gdb.TX, topic string, groupFileId string) (res gdb.Record, err error) {
|
||||||
|
return tx.Model("im_group_files").Safe().Ctx(ctx).
|
||||||
|
Where("topic", topic).
|
||||||
|
Where("group_file_id", groupFileId).
|
||||||
|
One()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) ensureActiveGroup(ctx context.Context, tx gdb.TX, topic string) error {
|
||||||
|
status, err := tx.Model("im_group_settings").Safe().Ctx(ctx).Where("topic", topic).Value("group_status")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status.IsNil() {
|
||||||
|
return gerror.New("群治理配置不存在")
|
||||||
|
}
|
||||||
|
if status.String() == consts.ImGroupStatusDissolved {
|
||||||
|
return gerror.New("已解散群不能执行该操作")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sSysImGroup) groupMuteSnapshot(ctx context.Context, tx gdb.TX, topic string, userId int64) (res gdb.Record, err error) {
|
func (s *sSysImGroup) groupMuteSnapshot(ctx context.Context, tx gdb.TX, topic string, userId int64) (res gdb.Record, err error) {
|
||||||
return tx.Model("im_group_mutes").Safe().Ctx(ctx).
|
return tx.Model("im_group_mutes").Safe().Ctx(ctx).
|
||||||
Fields(
|
Fields(
|
||||||
@@ -561,6 +858,69 @@ func (s *sSysImGroup) ensureActiveGroupMember(ctx context.Context, tx gdb.TX, to
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) ensureAdminLimit(ctx context.Context, tx gdb.TX, topic string, userId int64) error {
|
||||||
|
currentRole, err := tx.Model("im_group_members").Safe().Ctx(ctx).
|
||||||
|
Where("topic", topic).
|
||||||
|
Where("user_id", userId).
|
||||||
|
Where("member_status", consts.ImGroupMemberStatusActive).
|
||||||
|
Value("role")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if currentRole.String() == consts.ImGroupRoleAdmin {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
maxAdminCount, err := tx.Model("im_group_settings").Safe().Ctx(ctx).Where("topic", topic).Value("max_admin_count")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maxAdminCount.Int() <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
adminCount, err := tx.Model("im_group_members").Safe().Ctx(ctx).
|
||||||
|
Where("topic", topic).
|
||||||
|
Where("role", consts.ImGroupRoleAdmin).
|
||||||
|
Where("member_status", consts.ImGroupMemberStatusActive).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if adminCount >= maxAdminCount.Int() {
|
||||||
|
return gerror.New("群管理员数量已达上限")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) upsertGroupSubscription(ctx context.Context, tx gdb.TX, topic string, userId int64, modeGiven string, now *gtime.Time) error {
|
||||||
|
_, err := tx.Exec(`INSERT INTO im_subscriptions(createdat,updatedat,deletedat,userid,topic,modewant,modegiven,private)
|
||||||
|
VALUES(?,?,NULL,?,?,?,?,CAST('{}' AS JSON))
|
||||||
|
ON DUPLICATE KEY UPDATE createdat=VALUES(createdat),updatedat=VALUES(updatedat),deletedat=NULL,
|
||||||
|
modewant=VALUES(modewant),modegiven=VALUES(modegiven),delid=0,recvseqid=0,readseqid=0`,
|
||||||
|
now, now, userId, topic, imGroupModeMember, modeGiven)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) upsertGroupMember(ctx context.Context, tx gdb.TX, topic string, userId int64, role string, joinSource string, joinedBy int64, now *gtime.Time) error {
|
||||||
|
_, err := tx.Exec(`INSERT INTO im_group_members(topic,user_id,role,member_status,join_source,joined_by,joined_at,version,created_at,updated_at)
|
||||||
|
VALUES(?,?,?,?,?,?,?,?,?,?)
|
||||||
|
ON DUPLICATE KEY UPDATE role=VALUES(role),member_status=VALUES(member_status),join_source=VALUES(join_source),
|
||||||
|
joined_by=VALUES(joined_by),joined_at=VALUES(joined_at),left_at=NULL,removed_by=NULL,version=version+1,updated_at=VALUES(updated_at)`,
|
||||||
|
topic, userId, role, consts.ImGroupMemberStatusActive, joinSource, joinedBy, now, 1, now, now)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImGroup) recountGroupMembers(ctx context.Context, tx gdb.TX, topic string) error {
|
||||||
|
if _, err := tx.Exec(`UPDATE im_group_settings
|
||||||
|
SET member_count=(SELECT COUNT(*) FROM im_group_members WHERE topic=? AND member_status=?),updated_at=CURRENT_TIMESTAMP(3)
|
||||||
|
WHERE topic=?`, topic, consts.ImGroupMemberStatusActive, topic); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := tx.Exec(`UPDATE im_topics
|
||||||
|
SET subcnt=(SELECT COUNT(*) FROM im_subscriptions WHERE topic=? AND deletedat IS NULL),updatedat=CURRENT_TIMESTAMP(3)
|
||||||
|
WHERE name=?`, topic, topic)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func recordMap(record gdb.Record) any {
|
func recordMap(record gdb.Record) any {
|
||||||
if record == nil {
|
if record == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -619,6 +979,87 @@ func (s *sSysImGroup) JoinRequestList(ctx context.Context, in *sysin.ImGroupJoin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileList 获取IM群文件列表
|
||||||
|
func (s *sSysImGroup) FileList(ctx context.Context, in *sysin.ImGroupFileListInp) (list []*sysin.ImGroupFileListModel, totalCount int, err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
db, _, err := resolveImTenantDB(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mod := db.Model("im_group_files").Safe().Ctx(ctx).Where("topic", in.Topic)
|
||||||
|
if in.Status != "" {
|
||||||
|
mod = mod.Where("status", in.Status)
|
||||||
|
}
|
||||||
|
if in.FileType != "" {
|
||||||
|
mod = mod.Where("file_type", in.FileType)
|
||||||
|
}
|
||||||
|
if in.FileName != "" {
|
||||||
|
mod = mod.WhereLike("file_name", "%"+in.FileName+"%")
|
||||||
|
}
|
||||||
|
if in.UploaderId > 0 {
|
||||||
|
mod = mod.Where("uploader_id", in.UploaderId)
|
||||||
|
}
|
||||||
|
mod = mod.Page(in.Page, in.PerPage).OrderDesc("id")
|
||||||
|
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||||
|
err = gerror.Wrap(err, "获取IM群文件列表失败,请稍后重试!")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileDelete 删除IM群文件
|
||||||
|
func (s *sSysImGroup) FileDelete(ctx context.Context, in *sysin.ImGroupFileDeleteInp) (err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
db, _, err := resolveImTenantDB(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
actor := contexts.GetUserId(ctx)
|
||||||
|
requestId := gctx.CtxId(ctx)
|
||||||
|
if requestId == "" {
|
||||||
|
requestId = guid.S()
|
||||||
|
}
|
||||||
|
now := gtime.Now()
|
||||||
|
return db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
if err := s.ensureActiveGroup(ctx, tx, in.Topic); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
before, txErr := s.groupFileSnapshot(ctx, tx, in.Topic, in.GroupFileId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if before == nil {
|
||||||
|
return gerror.New("群文件不存在")
|
||||||
|
}
|
||||||
|
if before["status"].String() == consts.ImGroupFileStatusDeleted {
|
||||||
|
return gerror.New("群文件已删除")
|
||||||
|
}
|
||||||
|
result, txErr := tx.Exec(`UPDATE im_group_files
|
||||||
|
SET status=?,deleted_by=?,deleted_at=?,updated_at=?
|
||||||
|
WHERE topic=? AND group_file_id=? AND status<>?`,
|
||||||
|
consts.ImGroupFileStatusDeleted, actor, now, now, in.Topic, in.GroupFileId, consts.ImGroupFileStatusDeleted)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if affected, _ := result.RowsAffected(); affected == 0 {
|
||||||
|
return gerror.New("群文件已删除")
|
||||||
|
}
|
||||||
|
after, txErr := s.groupFileSnapshot(ctx, tx, in.Topic, in.GroupFileId)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if txErr = s.insertGroupAdminLog(ctx, tx, in.Topic, actor, before["uploader_id"].Int64(), "group_file_delete", "group_file", in.GroupFileId, recordMap(before), recordMap(after), requestId); txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return s.insertGroupEvent(ctx, tx, in.Topic, actor, "group.file.deleted", g.Map{
|
||||||
|
"topic": in.Topic, "group_file_id": in.GroupFileId, "file_id": before["file_id"].String(), "request_id": requestId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// InvitationList 获取IM群邀请列表
|
// InvitationList 获取IM群邀请列表
|
||||||
func (s *sSysImGroup) InvitationList(ctx context.Context, in *sysin.ImGroupInvitationListInp) (list []*sysin.ImGroupInvitationListModel, totalCount int, err error) {
|
func (s *sSysImGroup) InvitationList(ctx context.Context, in *sysin.ImGroupInvitationListInp) (list []*sysin.ImGroupInvitationListModel, totalCount int, err error) {
|
||||||
if err = in.Filter(ctx); err != nil {
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
|||||||
@@ -248,6 +248,59 @@ func (in *ImGroupUnmuteInp) Filter(ctx context.Context) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImGroupJoinRequestResolveInp struct {
|
||||||
|
ImGroupTopicInp
|
||||||
|
RequestId string `json:"requestId" v:"required#申请ID不能为空" dc:"申请ID"`
|
||||||
|
RejectReason string `json:"rejectReason" dc:"拒绝原因"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *ImGroupJoinRequestResolveInp) Filter(ctx context.Context) (err error) {
|
||||||
|
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.RequestId == "" {
|
||||||
|
return gerror.New("申请ID不能为空")
|
||||||
|
}
|
||||||
|
if len([]rune(in.RejectReason)) > 200 {
|
||||||
|
return gerror.New("拒绝原因不能超过200个字符")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImGroupRoleUpdateInp struct {
|
||||||
|
ImGroupTopicInp
|
||||||
|
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
|
||||||
|
Role string `json:"role" v:"required#角色不能为空" dc:"角色"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *ImGroupRoleUpdateInp) Filter(ctx context.Context) (err error) {
|
||||||
|
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.UserId <= 0 {
|
||||||
|
return gerror.New("用户ID不能为空")
|
||||||
|
}
|
||||||
|
if in.Role == "" {
|
||||||
|
return gerror.New("角色不能为空")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImGroupOwnerTransferInp struct {
|
||||||
|
ImGroupTopicInp
|
||||||
|
UserId int64 `json:"userId" v:"required#新群主ID不能为空" dc:"新群主ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *ImGroupOwnerTransferInp) Filter(ctx context.Context) (err error) {
|
||||||
|
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.UserId <= 0 {
|
||||||
|
return gerror.New("新群主ID不能为空")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type ImGroupJoinRequestListInp struct {
|
type ImGroupJoinRequestListInp struct {
|
||||||
form.PageReq
|
form.PageReq
|
||||||
ImGroupTopicInp
|
ImGroupTopicInp
|
||||||
@@ -273,6 +326,49 @@ type ImGroupJoinRequestListModel struct {
|
|||||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImGroupFileListInp struct {
|
||||||
|
form.PageReq
|
||||||
|
ImGroupTopicInp
|
||||||
|
Status string `json:"status" dc:"状态"`
|
||||||
|
FileType string `json:"fileType" dc:"文件类型"`
|
||||||
|
FileName string `json:"fileName" dc:"文件名"`
|
||||||
|
UploaderId int64 `json:"uploaderId" dc:"上传人ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImGroupFileListModel struct {
|
||||||
|
Id int64 `json:"id" dc:"ID"`
|
||||||
|
GroupFileId string `json:"groupFileId" dc:"群文件ID"`
|
||||||
|
Topic string `json:"topic" dc:"Topic"`
|
||||||
|
FileId string `json:"fileId" dc:"文件ID"`
|
||||||
|
SourceTopic string `json:"sourceTopic" dc:"来源Topic"`
|
||||||
|
SourceSeq int `json:"sourceSeq" dc:"来源消息序号"`
|
||||||
|
UploaderId int64 `json:"uploaderId" dc:"上传人ID"`
|
||||||
|
FileName string `json:"fileName" dc:"文件名"`
|
||||||
|
MimeType string `json:"mimeType" dc:"MIME类型"`
|
||||||
|
FileType string `json:"fileType" dc:"文件类型"`
|
||||||
|
FileSize int64 `json:"fileSize" dc:"文件大小"`
|
||||||
|
Status string `json:"status" dc:"状态"`
|
||||||
|
DeletedBy int64 `json:"deletedBy" dc:"删除人"`
|
||||||
|
DeletedAt *gtime.Time `json:"deletedAt" dc:"删除时间"`
|
||||||
|
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||||
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImGroupFileDeleteInp struct {
|
||||||
|
ImGroupTopicInp
|
||||||
|
GroupFileId string `json:"groupFileId" v:"required#群文件ID不能为空" dc:"群文件ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *ImGroupFileDeleteInp) Filter(ctx context.Context) (err error) {
|
||||||
|
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.GroupFileId == "" {
|
||||||
|
return gerror.New("群文件ID不能为空")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type ImGroupInvitationListInp struct {
|
type ImGroupInvitationListInp struct {
|
||||||
form.PageReq
|
form.PageReq
|
||||||
ImGroupTopicInp
|
ImGroupTopicInp
|
||||||
|
|||||||
@@ -244,8 +244,20 @@ type (
|
|||||||
Mute(ctx context.Context, in *sysin.ImGroupMuteInp) (err error)
|
Mute(ctx context.Context, in *sysin.ImGroupMuteInp) (err error)
|
||||||
// Unmute 解除IM群成员禁言
|
// Unmute 解除IM群成员禁言
|
||||||
Unmute(ctx context.Context, in *sysin.ImGroupUnmuteInp) (err error)
|
Unmute(ctx context.Context, in *sysin.ImGroupUnmuteInp) (err error)
|
||||||
|
// JoinRequestApprove 通过IM入群申请
|
||||||
|
JoinRequestApprove(ctx context.Context, in *sysin.ImGroupJoinRequestResolveInp) (err error)
|
||||||
|
// JoinRequestReject 拒绝IM入群申请
|
||||||
|
JoinRequestReject(ctx context.Context, in *sysin.ImGroupJoinRequestResolveInp) (err error)
|
||||||
|
// RoleUpdate 更新IM群成员角色
|
||||||
|
RoleUpdate(ctx context.Context, in *sysin.ImGroupRoleUpdateInp) (err error)
|
||||||
|
// OwnerTransfer 转让IM群主
|
||||||
|
OwnerTransfer(ctx context.Context, in *sysin.ImGroupOwnerTransferInp) (err error)
|
||||||
// JoinRequestList 获取IM入群申请列表
|
// JoinRequestList 获取IM入群申请列表
|
||||||
JoinRequestList(ctx context.Context, in *sysin.ImGroupJoinRequestListInp) (list []*sysin.ImGroupJoinRequestListModel, totalCount int, err error)
|
JoinRequestList(ctx context.Context, in *sysin.ImGroupJoinRequestListInp) (list []*sysin.ImGroupJoinRequestListModel, totalCount int, err error)
|
||||||
|
// FileList 获取IM群文件列表
|
||||||
|
FileList(ctx context.Context, in *sysin.ImGroupFileListInp) (list []*sysin.ImGroupFileListModel, totalCount int, err error)
|
||||||
|
// FileDelete 删除IM群文件
|
||||||
|
FileDelete(ctx context.Context, in *sysin.ImGroupFileDeleteInp) (err error)
|
||||||
// InvitationList 获取IM群邀请列表
|
// InvitationList 获取IM群邀请列表
|
||||||
InvitationList(ctx context.Context, in *sysin.ImGroupInvitationListInp) (list []*sysin.ImGroupInvitationListModel, totalCount int, err error)
|
InvitationList(ctx context.Context, in *sysin.ImGroupInvitationListInp) (list []*sysin.ImGroupInvitationListModel, totalCount int, err error)
|
||||||
// AnnouncementList 获取IM群公告列表
|
// AnnouncementList 获取IM群公告列表
|
||||||
|
|||||||
@@ -52,10 +52,34 @@ INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type
|
|||||||
SELECT NULL, @groupListId, '解除成员禁言', 'imGroupUnmute', '', '', '3', '', '/imGroup/unmute', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '70', '', '1', @now, @now
|
SELECT NULL, @groupListId, '解除成员禁言', 'imGroupUnmute', '', '', '3', '', '/imGroup/unmute', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '70', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupUnmute');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupUnmute');
|
||||||
|
|
||||||
|
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, @groupListId, '通过入群申请', 'imGroupJoinRequestApprove', '', '', '3', '', '/imGroup/joinRequestApprove', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '80', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupJoinRequestApprove');
|
||||||
|
|
||||||
|
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, @groupListId, '拒绝入群申请', 'imGroupJoinRequestReject', '', '', '3', '', '/imGroup/joinRequestReject', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '90', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupJoinRequestReject');
|
||||||
|
|
||||||
|
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, @groupListId, '更新群成员角色', 'imGroupRoleUpdate', '', '', '3', '', '/imGroup/roleUpdate', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '100', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupRoleUpdate');
|
||||||
|
|
||||||
|
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, @groupListId, '转让群主', 'imGroupOwnerTransfer', '', '', '3', '', '/imGroup/ownerTransfer', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '110', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupOwnerTransfer');
|
||||||
|
|
||||||
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`)
|
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, @groupListId, '入群申请列表', 'imGroupJoinRequestList', '', '', '3', '', '/imGroup/joinRequestList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '40', '', '1', @now, @now
|
SELECT NULL, @groupListId, '入群申请列表', 'imGroupJoinRequestList', '', '', '3', '', '/imGroup/joinRequestList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '40', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupJoinRequestList');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupJoinRequestList');
|
||||||
|
|
||||||
|
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, @groupListId, '群文件列表', 'imGroupFileList', '', '', '3', '', '/imGroup/fileList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '120', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupFileList');
|
||||||
|
|
||||||
|
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`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||||
|
SELECT NULL, @groupListId, '删除群文件', 'imGroupFileDelete', '', '', '3', '', '/imGroup/fileDelete', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '130', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupFileDelete');
|
||||||
|
|
||||||
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`)
|
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, @groupListId, '群邀请列表', 'imGroupInvitationList', '', '', '3', '', '/imGroup/invitationList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '50', '', '1', @now, @now
|
SELECT NULL, @groupListId, '群邀请列表', 'imGroupInvitationList', '', '', '3', '', '/imGroup/invitationList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '50', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupInvitationList');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupInvitationList');
|
||||||
|
|||||||
@@ -72,6 +72,42 @@ export function Unmute(params) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通过IM入群申请
|
||||||
|
export function JoinRequestApprove(params) {
|
||||||
|
return http.request({
|
||||||
|
url: '/imGroup/joinRequestApprove',
|
||||||
|
method: 'POST',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拒绝IM入群申请
|
||||||
|
export function JoinRequestReject(params) {
|
||||||
|
return http.request({
|
||||||
|
url: '/imGroup/joinRequestReject',
|
||||||
|
method: 'POST',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新IM群成员角色
|
||||||
|
export function RoleUpdate(params) {
|
||||||
|
return http.request({
|
||||||
|
url: '/imGroup/roleUpdate',
|
||||||
|
method: 'POST',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转让IM群主
|
||||||
|
export function OwnerTransfer(params) {
|
||||||
|
return http.request({
|
||||||
|
url: '/imGroup/ownerTransfer',
|
||||||
|
method: 'POST',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 获取IM入群申请列表
|
// 获取IM入群申请列表
|
||||||
export function JoinRequestList(params) {
|
export function JoinRequestList(params) {
|
||||||
return http.request({
|
return http.request({
|
||||||
@@ -81,6 +117,24 @@ export function JoinRequestList(params) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取IM群文件列表
|
||||||
|
export function FileList(params) {
|
||||||
|
return http.request({
|
||||||
|
url: '/imGroup/fileList',
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除IM群文件
|
||||||
|
export function FileDelete(params) {
|
||||||
|
return http.request({
|
||||||
|
url: '/imGroup/fileDelete',
|
||||||
|
method: 'POST',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 获取IM群邀请列表
|
// 获取IM群邀请列表
|
||||||
export function InvitationList(params) {
|
export function InvitationList(params) {
|
||||||
return http.request({
|
return http.request({
|
||||||
|
|||||||
@@ -102,10 +102,22 @@
|
|||||||
:columns="joinRequestColumns"
|
:columns="joinRequestColumns"
|
||||||
:request="joinRequestData"
|
:request="joinRequestData"
|
||||||
:row-key="(row) => row.requestId"
|
:row-key="(row) => row.requestId"
|
||||||
|
:actionColumn="joinRequestActionColumn"
|
||||||
:scroll-x="joinRequestScrollX"
|
:scroll-x="joinRequestScrollX"
|
||||||
:resizeHeightOffset="-10000"
|
:resizeHeightOffset="-10000"
|
||||||
/>
|
/>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="files" tab="群文件">
|
||||||
|
<BasicTable
|
||||||
|
ref="fileRef"
|
||||||
|
:columns="groupFileColumns"
|
||||||
|
:request="fileData"
|
||||||
|
:row-key="(row) => row.groupFileId"
|
||||||
|
:actionColumn="fileActionColumn"
|
||||||
|
:scroll-x="fileScrollX"
|
||||||
|
:resizeHeightOffset="-10000"
|
||||||
|
/>
|
||||||
|
</n-tab-pane>
|
||||||
<n-tab-pane name="invitations" tab="群邀请">
|
<n-tab-pane name="invitations" tab="群邀请">
|
||||||
<BasicTable
|
<BasicTable
|
||||||
ref="invitationRef"
|
ref="invitationRef"
|
||||||
@@ -183,10 +195,14 @@
|
|||||||
AdminLogList,
|
AdminLogList,
|
||||||
AnnouncementList,
|
AnnouncementList,
|
||||||
Dissolve,
|
Dissolve,
|
||||||
|
FileDelete,
|
||||||
|
FileList,
|
||||||
Governance,
|
Governance,
|
||||||
GovernanceUpdate,
|
GovernanceUpdate,
|
||||||
InvitationList,
|
InvitationList,
|
||||||
|
JoinRequestApprove,
|
||||||
JoinRequestList,
|
JoinRequestList,
|
||||||
|
JoinRequestReject,
|
||||||
MuteList,
|
MuteList,
|
||||||
Unmute,
|
Unmute,
|
||||||
} from '@/api/imGroup';
|
} from '@/api/imGroup';
|
||||||
@@ -194,6 +210,7 @@
|
|||||||
adminLogColumns,
|
adminLogColumns,
|
||||||
announcementColumns,
|
announcementColumns,
|
||||||
GovernanceState,
|
GovernanceState,
|
||||||
|
groupFileColumns,
|
||||||
invitationColumns,
|
invitationColumns,
|
||||||
joinRequestColumns,
|
joinRequestColumns,
|
||||||
muteColumns,
|
muteColumns,
|
||||||
@@ -222,10 +239,58 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
const joinRequestRef = ref();
|
const joinRequestRef = ref();
|
||||||
|
const fileRef = ref();
|
||||||
const invitationRef = ref();
|
const invitationRef = ref();
|
||||||
const announcementRef = ref();
|
const announcementRef = ref();
|
||||||
const muteRef = ref();
|
const muteRef = ref();
|
||||||
const adminLogRef = ref();
|
const adminLogRef = ref();
|
||||||
|
const joinRequestActionColumn = reactive({
|
||||||
|
width: 150,
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction as any, {
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '通过',
|
||||||
|
type: 'primary',
|
||||||
|
ifShow: () => record.status === 'pending',
|
||||||
|
auth: ['/imGroup/joinRequestApprove'],
|
||||||
|
onClick: () => handleJoinRequestApprove(record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '拒绝',
|
||||||
|
type: 'error',
|
||||||
|
ifShow: () => record.status === 'pending',
|
||||||
|
auth: ['/imGroup/joinRequestReject'],
|
||||||
|
onClick: () => handleJoinRequestReject(record),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const fileActionColumn = reactive({
|
||||||
|
width: 100,
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction as any, {
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'error',
|
||||||
|
ifShow: () => record.status === 'active',
|
||||||
|
auth: ['/imGroup/fileDelete'],
|
||||||
|
onClick: () => handleFileDelete(record),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
const muteActionColumn = reactive({
|
const muteActionColumn = reactive({
|
||||||
width: 110,
|
width: 110,
|
||||||
title: '操作',
|
title: '操作',
|
||||||
@@ -247,7 +312,10 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const joinRequestScrollX = computed(() => adaTableScrollX(joinRequestColumns, 0));
|
const joinRequestScrollX = computed(() =>
|
||||||
|
adaTableScrollX(joinRequestColumns, joinRequestActionColumn.width)
|
||||||
|
);
|
||||||
|
const fileScrollX = computed(() => adaTableScrollX(groupFileColumns, fileActionColumn.width));
|
||||||
const invitationScrollX = computed(() => adaTableScrollX(invitationColumns, 0));
|
const invitationScrollX = computed(() => adaTableScrollX(invitationColumns, 0));
|
||||||
const announcementScrollX = computed(() => adaTableScrollX(announcementColumns, 0));
|
const announcementScrollX = computed(() => adaTableScrollX(announcementColumns, 0));
|
||||||
const muteScrollX = computed(() => adaTableScrollX(muteColumns, muteActionColumn.width));
|
const muteScrollX = computed(() => adaTableScrollX(muteColumns, muteActionColumn.width));
|
||||||
@@ -265,6 +333,10 @@
|
|||||||
return await JoinRequestList(baseParams(res));
|
return await JoinRequestList(baseParams(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fileData(res) {
|
||||||
|
return await FileList(baseParams(res));
|
||||||
|
}
|
||||||
|
|
||||||
async function invitationData(res) {
|
async function invitationData(res) {
|
||||||
return await InvitationList(baseParams(res));
|
return await InvitationList(baseParams(res));
|
||||||
}
|
}
|
||||||
@@ -361,9 +433,65 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleJoinRequestApprove(record) {
|
||||||
|
dialog.success({
|
||||||
|
title: '通过入群申请',
|
||||||
|
content: '确定通过该入群申请?',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await JoinRequestApprove({
|
||||||
|
tenantId: current.value.tenantId,
|
||||||
|
topic: current.value.topic,
|
||||||
|
requestId: record.requestId,
|
||||||
|
});
|
||||||
|
message.success('操作成功');
|
||||||
|
joinRequestRef.value?.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleJoinRequestReject(record) {
|
||||||
|
dialog.error({
|
||||||
|
title: '拒绝入群申请',
|
||||||
|
content: '确定拒绝该入群申请?',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await JoinRequestReject({
|
||||||
|
tenantId: current.value.tenantId,
|
||||||
|
topic: current.value.topic,
|
||||||
|
requestId: record.requestId,
|
||||||
|
rejectReason: '后台管理员拒绝',
|
||||||
|
});
|
||||||
|
message.success('操作成功');
|
||||||
|
joinRequestRef.value?.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileDelete(record) {
|
||||||
|
dialog.error({
|
||||||
|
title: '删除群文件',
|
||||||
|
content: '删除后该群文件会被标记为已删除,并写入群事件等待 IM 服务端同步,是否确认?',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await FileDelete({
|
||||||
|
tenantId: current.value.tenantId,
|
||||||
|
topic: current.value.topic,
|
||||||
|
groupFileId: record.groupFileId,
|
||||||
|
});
|
||||||
|
message.success('操作成功');
|
||||||
|
fileRef.value?.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function reloadActiveTable() {
|
function reloadActiveTable() {
|
||||||
const refs = {
|
const refs = {
|
||||||
joinRequests: joinRequestRef,
|
joinRequests: joinRequestRef,
|
||||||
|
files: fileRef,
|
||||||
invitations: invitationRef,
|
invitations: invitationRef,
|
||||||
announcements: announcementRef,
|
announcements: announcementRef,
|
||||||
mutes: muteRef,
|
mutes: muteRef,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
import { useDialog, useMessage } from 'naive-ui';
|
import { useDialog, useMessage } from 'naive-ui';
|
||||||
import { BasicTable, TableAction } from '@/components/Table';
|
import { BasicTable, TableAction } from '@/components/Table';
|
||||||
import { usePermission } from '@/hooks/web/usePermission';
|
import { usePermission } from '@/hooks/web/usePermission';
|
||||||
import { MemberList, Mute } from '@/api/imGroup';
|
import { MemberList, Mute, OwnerTransfer, RoleUpdate } from '@/api/imGroup';
|
||||||
import { memberColumns, newState, State } from './model';
|
import { memberColumns, newState, State } from './model';
|
||||||
import { adaModalWidth, adaTableScrollX } from '@/utils/hotgo';
|
import { adaModalWidth, adaTableScrollX } from '@/utils/hotgo';
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
const actionRef = ref();
|
const actionRef = ref();
|
||||||
const dialogWidth = computed(() => adaModalWidth(980));
|
const dialogWidth = computed(() => adaModalWidth(980));
|
||||||
const actionColumn = reactive({
|
const actionColumn = reactive({
|
||||||
width: 100,
|
width: 210,
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
@@ -59,6 +59,27 @@
|
|||||||
disabled: !hasPermission(['/imGroup/mute']),
|
disabled: !hasPermission(['/imGroup/mute']),
|
||||||
onClick: () => handleMute(record),
|
onClick: () => handleMute(record),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '设管理员',
|
||||||
|
type: 'primary',
|
||||||
|
ifShow: () => record.memberStatus === 'active' && record.role === 'member',
|
||||||
|
auth: ['/imGroup/roleUpdate'],
|
||||||
|
onClick: () => handleRoleUpdate(record, 'admin'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '取消管理员',
|
||||||
|
type: 'default',
|
||||||
|
ifShow: () => record.memberStatus === 'active' && record.role === 'admin',
|
||||||
|
auth: ['/imGroup/roleUpdate'],
|
||||||
|
onClick: () => handleRoleUpdate(record, 'member'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '转群主',
|
||||||
|
type: 'error',
|
||||||
|
ifShow: () => record.memberStatus === 'active' && record.role !== 'owner',
|
||||||
|
auth: ['/imGroup/ownerTransfer'],
|
||||||
|
onClick: () => handleOwnerTransfer(record),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -102,6 +123,43 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleRoleUpdate(record, role: string) {
|
||||||
|
dialog.warning({
|
||||||
|
title: role === 'admin' ? '设置管理员' : '取消管理员',
|
||||||
|
content: role === 'admin' ? '确定将该成员设置为管理员?' : '确定取消该成员管理员身份?',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await RoleUpdate({
|
||||||
|
tenantId: current.value.tenantId,
|
||||||
|
topic: current.value.topic,
|
||||||
|
userId: record.userId,
|
||||||
|
role,
|
||||||
|
});
|
||||||
|
message.success('操作成功');
|
||||||
|
actionRef.value?.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOwnerTransfer(record) {
|
||||||
|
dialog.error({
|
||||||
|
title: '转让群主',
|
||||||
|
content: '转让后原群主会变为管理员,是否确认?',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await OwnerTransfer({
|
||||||
|
tenantId: current.value.tenantId,
|
||||||
|
topic: current.value.topic,
|
||||||
|
userId: record.userId,
|
||||||
|
});
|
||||||
|
message.success('操作成功');
|
||||||
|
actionRef.value?.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 打开弹窗
|
// 打开弹窗
|
||||||
function openModal(state: State) {
|
function openModal(state: State) {
|
||||||
current.value = newState(state);
|
current.value = newState(state);
|
||||||
|
|||||||
@@ -368,6 +368,38 @@ export const joinRequestColumns = [
|
|||||||
{ title: '创建时间', key: 'createdAt', align: 'left', width: 180 },
|
{ title: '创建时间', key: 'createdAt', align: 'left', width: 180 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const groupFileColumns = [
|
||||||
|
{ title: '群文件ID', key: 'groupFileId', align: 'left', width: 180 },
|
||||||
|
{ title: '文件ID', key: 'fileId', align: 'left', width: 160 },
|
||||||
|
{ title: '文件名', key: 'fileName', align: 'left', width: 220 },
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
key: 'fileType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
render(row) {
|
||||||
|
return dict.getLabel('ImGroupFileTypeOptions', row.fileType) || row.fileType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: 'MIME', key: 'mimeType', align: 'left', width: 160 },
|
||||||
|
{ title: '大小', key: 'fileSize', align: 'left', width: 110 },
|
||||||
|
{ title: '上传人', key: 'uploaderId', align: 'left', width: 120 },
|
||||||
|
{ title: '来源Topic', key: 'sourceTopic', align: 'left', width: 160 },
|
||||||
|
{ title: '来源序号', key: 'sourceSeq', align: 'left', width: 100 },
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'status',
|
||||||
|
align: 'left',
|
||||||
|
width: 110,
|
||||||
|
render(row) {
|
||||||
|
return dict.getLabel('ImGroupFileStatusOptions', row.status);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '删除人', key: 'deletedBy', align: 'left', width: 120 },
|
||||||
|
{ title: '删除时间', key: 'deletedAt', align: 'left', width: 180 },
|
||||||
|
{ title: '创建时间', key: 'createdAt', align: 'left', width: 180 },
|
||||||
|
];
|
||||||
|
|
||||||
export const invitationColumns = [
|
export const invitationColumns = [
|
||||||
{ title: '邀请ID', key: 'invitationId', align: 'left', width: 180 },
|
{ title: '邀请ID', key: 'invitationId', align: 'left', width: 180 },
|
||||||
{ title: '邀请人ID', key: 'inviterUserId', align: 'left', width: 120 },
|
{ title: '邀请人ID', key: 'inviterUserId', align: 'left', width: 120 },
|
||||||
@@ -450,6 +482,8 @@ export function loadOptions() {
|
|||||||
'ImGroupMuteStatusOptions',
|
'ImGroupMuteStatusOptions',
|
||||||
'ImGroupAnnouncementStatusOptions',
|
'ImGroupAnnouncementStatusOptions',
|
||||||
'ImGroupOutboxStatusOptions',
|
'ImGroupOutboxStatusOptions',
|
||||||
|
'ImGroupFileStatusOptions',
|
||||||
|
'ImGroupFileTypeOptions',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user