feat: add tenant-aware IM management console

This commit is contained in:
richjohnrambo
2026-08-19 00:31:09 +08:00
parent 642b7ec5da
commit 89ba6bcff8
126 changed files with 11941 additions and 339 deletions
@@ -0,0 +1,75 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2026 HotGo
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
// ImAuditLogListInp 获取IM审计日志列表
type ImAuditLogListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
ActorUid int64 `json:"actorUid" dc:"操作人UID"`
ActorRole string `json:"actorRole" dc:"操作角色"`
Action string `json:"action" dc:"操作类型"`
TargetType string `json:"targetType" dc:"目标类型"`
TargetId string `json:"targetId" dc:"目标ID"`
RequestId string `json:"requestId" dc:"请求ID"`
Ip string `json:"ip" dc:"IP"`
Keyword string `json:"keyword" dc:"关键词"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
}
func (in *ImAuditLogListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
return
}
type ImAuditLogListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
ActorUid int64 `json:"actorUid" dc:"操作人UID"`
ActorRole string `json:"actorRole" dc:"操作角色"`
Action string `json:"action" dc:"操作类型"`
TargetType string `json:"targetType" dc:"目标类型"`
TargetId string `json:"targetId" dc:"目标ID"`
Reason string `json:"reason" dc:"操作原因"`
RequestId string `json:"requestId" dc:"请求ID"`
Ip string `json:"ip" dc:"IP"`
UserAgent string `json:"userAgent" dc:"客户端"`
BeforeSnapshot string `json:"beforeSnapshot" dc:"变更前快照"`
AfterSnapshot string `json:"afterSnapshot" dc:"变更后快照"`
Metadata string `json:"metadata" dc:"元数据"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
}
// ImAuditLogViewInp 获取指定IM审计日志信息
type ImAuditLogViewInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImAuditLogViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
type ImAuditLogViewModel struct {
ImAuditLogListModel
}
@@ -0,0 +1,290 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2026 HotGo
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
// ImGroupListInp 获取IM群组列表
type ImGroupListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Topic string `json:"topic" dc:"Topic"`
OwnerId int64 `json:"ownerId" dc:"群主ID"`
State *int `json:"state" dc:"通信状态"`
GroupStatus string `json:"groupStatus" dc:"业务状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
TouchedAt []*gtime.Time `json:"touchedAt" dc:"活跃时间"`
}
func (in *ImGroupListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
return
}
type ImGroupListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Topic string `json:"topic" dc:"Topic"`
State int `json:"state" dc:"状态"`
OwnerId int64 `json:"ownerId" dc:"群主ID"`
SeqId int `json:"seqId" dc:"最新消息序号"`
DelId int `json:"delId" dc:"删除序号"`
SubCnt int `json:"subCnt" dc:"成员数"`
TouchedAt *gtime.Time `json:"touchedAt" dc:"活跃时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
PublicJson string `json:"publicJson" dc:"公开资料"`
TrustedJson string `json:"trustedJson" dc:"可信资料"`
TagsJson string `json:"tagsJson" dc:"标签"`
AuxJson string `json:"auxJson" dc:"辅助资料"`
GroupName string `json:"groupName" dc:"群名称"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Description string `json:"description" dc:"简介"`
GroupStatus string `json:"groupStatus" dc:"业务状态"`
JoinApproval int `json:"joinApproval" dc:"入群审批"`
MuteAll int `json:"muteAll" dc:"全员禁言"`
MemberUploadFiles int `json:"memberUploadFiles" dc:"成员上传文件"`
MemberPrivateChat int `json:"memberPrivateChat" dc:"成员私聊"`
MemberInvite int `json:"memberInvite" dc:"成员邀请"`
MaxMemberCount int `json:"maxMemberCount" dc:"最大成员数"`
MaxAdminCount int `json:"maxAdminCount" dc:"最大管理员数"`
BusinessMemberCount int `json:"businessMemberCount" dc:"业务成员数"`
SettingsVersion int `json:"settingsVersion" dc:"治理配置版本"`
SettingsUpdatedAt *gtime.Time `json:"settingsUpdatedAt" dc:"治理配置更新时间"`
}
// ImGroupViewInp 获取指定IM群组信息
type ImGroupViewInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Topic string `json:"topic" v:"required#Topic不能为空" dc:"Topic"`
}
func (in *ImGroupViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Topic == "" {
return gerror.New("Topic不能为空")
}
return
}
type ImGroupViewModel struct {
ImGroupListModel
AccessJson string `json:"accessJson" dc:"访问权限"`
UseBt int `json:"useBt" dc:"是否开启BT"`
}
// ImGroupMemberListInp 获取IM群成员列表
type ImGroupMemberListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Topic string `json:"topic" v:"required#Topic不能为空" dc:"Topic"`
UserId int64 `json:"userId" dc:"用户ID"`
Role string `json:"role" dc:"角色"`
MemberStatus string `json:"memberStatus" dc:"成员状态"`
}
func (in *ImGroupMemberListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Topic == "" {
return gerror.New("Topic不能为空")
}
return
}
type ImGroupMemberListModel struct {
UserId int64 `json:"userId" dc:"用户ID"`
Nickname string `json:"nickname" dc:"昵称"`
State int `json:"state" dc:"用户状态"`
ModeWant string `json:"modeWant" dc:"期望权限"`
ModeGiven string `json:"modeGiven" dc:"授予权限"`
RecvSeqId int `json:"recvSeqId" dc:"接收序号"`
ReadSeqId int `json:"readSeqId" dc:"已读序号"`
DelId int `json:"delId" dc:"删除序号"`
DeletedAt *gtime.Time `json:"deletedAt" dc:"删除时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"加入时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
PrivateJson string `json:"privateJson" dc:"私有资料"`
Role string `json:"role" dc:"业务角色"`
MemberStatus string `json:"memberStatus" dc:"业务状态"`
GroupNickname string `json:"groupNickname" dc:"群昵称"`
Pinned int `json:"pinned" dc:"置顶"`
MuteNotifications int `json:"muteNotifications" dc:"免打扰"`
JoinSource string `json:"joinSource" dc:"加入来源"`
JoinedBy int64 `json:"joinedBy" dc:"邀请/处理人"`
JoinedAt *gtime.Time `json:"joinedAt" dc:"业务加入时间"`
LeftAt *gtime.Time `json:"leftAt" dc:"离开时间"`
RemovedBy int64 `json:"removedBy" dc:"移除人"`
Version int `json:"version" dc:"版本"`
}
// ImGroupTopicInp 群Topic通用入参
type ImGroupTopicInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Topic string `json:"topic" v:"required#Topic不能为空" dc:"Topic"`
}
func (in *ImGroupTopicInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Topic == "" {
return gerror.New("Topic不能为空")
}
return
}
type ImGroupGovernanceModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Topic string `json:"topic" dc:"Topic"`
OwnerUserId int64 `json:"ownerUserId" dc:"群主ID"`
Name string `json:"name" dc:"群名称"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Description string `json:"description" dc:"简介"`
GroupStatus string `json:"groupStatus" dc:"业务状态"`
JoinApproval int `json:"joinApproval" dc:"入群审批"`
MuteAll int `json:"muteAll" dc:"全员禁言"`
MemberUploadFiles int `json:"memberUploadFiles" dc:"成员上传文件"`
MemberPrivateChat int `json:"memberPrivateChat" dc:"成员私聊"`
MemberInvite int `json:"memberInvite" dc:"成员邀请"`
MaxMemberCount int `json:"maxMemberCount" dc:"最大成员数"`
MaxAdminCount int `json:"maxAdminCount" dc:"最大管理员数"`
MemberCount int `json:"memberCount" dc:"成员数"`
Version int `json:"version" dc:"版本"`
CreatedBy int64 `json:"createdBy" dc:"创建人"`
DissolvedBy int64 `json:"dissolvedBy" dc:"解散人"`
DissolvedAt *gtime.Time `json:"dissolvedAt" dc:"解散时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupJoinRequestListInp struct {
form.PageReq
ImGroupTopicInp
Status string `json:"status" dc:"状态"`
ApplicantUserId int64 `json:"applicantUserId" dc:"申请人ID"`
}
type ImGroupJoinRequestListModel struct {
Id int64 `json:"id" dc:"ID"`
RequestId string `json:"requestId" dc:"申请ID"`
Topic string `json:"topic" dc:"Topic"`
ApplicantUserId int64 `json:"applicantUserId" dc:"申请人ID"`
InviterUserId int64 `json:"inviterUserId" dc:"邀请人ID"`
Source string `json:"source" dc:"来源"`
Message string `json:"message" dc:"申请消息"`
Status string `json:"status" dc:"状态"`
RejectReason string `json:"rejectReason" dc:"拒绝原因"`
HandledBy int64 `json:"handledBy" dc:"处理人"`
HandledAt *gtime.Time `json:"handledAt" dc:"处理时间"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
Version int `json:"version" dc:"版本"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupInvitationListInp struct {
form.PageReq
ImGroupTopicInp
Status string `json:"status" dc:"状态"`
TargetUserId int64 `json:"targetUserId" dc:"目标用户ID"`
InviterUserId int64 `json:"inviterUserId" dc:"邀请人ID"`
}
type ImGroupInvitationListModel struct {
Id int64 `json:"id" dc:"ID"`
InvitationId string `json:"invitationId" dc:"邀请ID"`
Topic string `json:"topic" dc:"Topic"`
InviterUserId int64 `json:"inviterUserId" dc:"邀请人ID"`
TargetUserId int64 `json:"targetUserId" dc:"目标用户ID"`
Message string `json:"message" dc:"邀请消息"`
Status string `json:"status" dc:"状态"`
Result string `json:"result" dc:"结果"`
JoinRequestId string `json:"joinRequestId" dc:"入群申请ID"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
HandledAt *gtime.Time `json:"handledAt" dc:"处理时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupAnnouncementListInp struct {
form.PageReq
ImGroupTopicInp
Status string `json:"status" dc:"状态"`
}
type ImGroupAnnouncementListModel struct {
Id int64 `json:"id" dc:"ID"`
AnnouncementId string `json:"announcementId" dc:"公告ID"`
Topic string `json:"topic" dc:"Topic"`
AuthorUserId int64 `json:"authorUserId" dc:"发布人ID"`
Content string `json:"content" dc:"内容"`
Status string `json:"status" dc:"状态"`
Version int `json:"version" dc:"版本"`
PublishedAt *gtime.Time `json:"publishedAt" 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 ImGroupMuteListInp struct {
form.PageReq
ImGroupTopicInp
Status string `json:"status" dc:"状态"`
UserId int64 `json:"userId" dc:"用户ID"`
}
type ImGroupMuteListModel struct {
Topic string `json:"topic" dc:"Topic"`
UserId int64 `json:"userId" dc:"用户ID"`
MutedBy int64 `json:"mutedBy" dc:"禁言人"`
MutedAt *gtime.Time `json:"mutedAt" dc:"禁言时间"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
Reason string `json:"reason" dc:"原因"`
Status string `json:"status" dc:"状态"`
RevokedBy int64 `json:"revokedBy" dc:"解除人"`
RevokedAt *gtime.Time `json:"revokedAt" dc:"解除时间"`
Version int `json:"version" dc:"版本"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupAdminLogListInp struct {
form.PageReq
ImGroupTopicInp
Action string `json:"action" dc:"动作"`
OperatorId int64 `json:"operatorId" dc:"操作人ID"`
}
type ImGroupAdminLogListModel struct {
Id int64 `json:"id" dc:"ID"`
LogId string `json:"logId" dc:"日志ID"`
Topic string `json:"topic" dc:"Topic"`
OperatorId int64 `json:"operatorId" dc:"操作人"`
TargetUserId int64 `json:"targetUserId" dc:"目标用户"`
Action string `json:"action" dc:"动作"`
ObjectType string `json:"objectType" dc:"对象类型"`
ObjectId string `json:"objectId" dc:"对象ID"`
BeforeData string `json:"beforeData" dc:"变更前"`
AfterData string `json:"afterData" dc:"变更后"`
RequestId string `json:"requestId" dc:"请求ID"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
}
@@ -0,0 +1,154 @@
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"strconv"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImInviteListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
InviteCode string `json:"inviteCode" dc:"完整邀请码"`
Status string `json:"status" dc:"状态"`
BatchId string `json:"batchId" dc:"批次ID"`
CreatedBy string `json:"createdBy" dc:"创建人"`
CreatedAtStart *gtime.Time `json:"createdAtStart" dc:"创建开始时间"`
CreatedAtEnd *gtime.Time `json:"createdAtEnd" dc:"创建结束时间"`
ExpiresAtStart *gtime.Time `json:"expiresAtStart" dc:"过期开始时间"`
ExpiresAtEnd *gtime.Time `json:"expiresAtEnd" dc:"过期结束时间"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
ExpiresAt []*gtime.Time `json:"expiresAt" dc:"到期时间"`
}
func (in *ImInviteListInp) Filter(ctx context.Context) (err error) {
if in.Page <= 0 {
in.Page = 1
}
if in.PerPage <= 0 {
in.PerPage = 20
}
if in.PerPage > 100 {
return gerror.New("每页数量最大值不能大于100")
}
if in.InviteCode != "" && len(in.InviteCode) != 5 {
return gerror.New("邀请码必须为完整5位")
}
if len(in.CreatedAt) == 2 {
in.CreatedAtStart = in.CreatedAt[0]
in.CreatedAtEnd = in.CreatedAt[1]
}
if len(in.ExpiresAt) == 2 {
in.ExpiresAtStart = in.ExpiresAt[0]
in.ExpiresAtEnd = in.ExpiresAt[1]
}
return
}
type ImInviteCreateInp struct {
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
ExpiresAt *gtime.Time `json:"expiresAt" v:"required#到期时间不能为空" dc:"到期时间"`
Remark string `json:"remark" dc:"备注"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
}
func (in *ImInviteCreateInp) Filter(ctx context.Context) (err error) {
in.IdempotencyKey = strings.TrimSpace(in.IdempotencyKey)
if in.ExpiresAt == nil || in.ExpiresAt.Before(gtime.Now()) {
return gerror.New("到期时间必须晚于当前时间")
}
if len(in.Remark) > 255 {
return gerror.New("备注不能超过255个字符")
}
if len(in.IdempotencyKey) > 128 {
return gerror.New("幂等键不能超过128个字符")
}
return
}
type ImInviteBatchCreateInp struct {
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
Quantity int `json:"quantity" dc:"数量"`
ExpiresAt *gtime.Time `json:"expiresAt" v:"required#到期时间不能为空" dc:"到期时间"`
Remark string `json:"remark" dc:"备注"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
}
func (in *ImInviteBatchCreateInp) Filter(ctx context.Context) (err error) {
in.IdempotencyKey = strings.TrimSpace(in.IdempotencyKey)
if in.Quantity < 1 || in.Quantity > 1000 {
return gerror.New("批量生成数量必须在1到1000之间")
}
if in.ExpiresAt == nil || in.ExpiresAt.Before(gtime.Now()) {
return gerror.New("到期时间必须晚于当前时间")
}
if len(in.Remark) > 255 {
return gerror.New("备注不能超过255个字符")
}
if len(in.IdempotencyKey) == 0 || len(in.IdempotencyKey) > 128 {
return gerror.New("批量生成必须提供不超过128个字符的幂等键")
}
return
}
type ImInviteBatchViewInp struct {
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
BatchId string `json:"batchId" v:"required#批次ID不能为空" dc:"批次ID"`
}
type ImInviteRevokeInp struct {
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
Id string `json:"id" v:"required#邀请码ID不能为空" dc:"邀请码ID"`
}
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:"创建时间"`
}
type ImInviteBatchModel struct {
BatchId string `json:"batchId" dc:"批次ID"`
TenantId string `json:"tenantId" dc:"IM租户ID"`
Quantity int `json:"quantity" dc:"数量"`
ActiveCount int `json:"activeCount" dc:"有效数量"`
UsageCount int `json:"usageCount" dc:"累计使用次数"`
RevokedCount int `json:"revokedCount" dc:"已撤销数量"`
ExpiredCount int `json:"expiredCount" dc:"已过期数量"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
}
type ImInviteBatchDetailModel struct {
ImInviteBatchModel
List []*ImInviteCodeModel `json:"list" dc:"批次邀请码"`
}
func ParseOptionalInt64(value string) (int64, error) {
if value == "" {
return 0, nil
}
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil || parsed <= 0 {
return 0, gerror.New("ID格式不正确")
}
return parsed, nil
}
@@ -0,0 +1,96 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2026 HotGo
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
// ImRealnameListInp 获取IM实名审核列表
type ImRealnameListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" dc:"用户ID"`
SubmissionId string `json:"submissionId" dc:"提交ID"`
Status string `json:"status" dc:"状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
}
func (in *ImRealnameListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
return
}
type ImRealnameListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
UserId int64 `json:"userId" dc:"用户ID"`
SubmissionId string `json:"submissionId" dc:"提交ID"`
Status string `json:"status" dc:"状态"`
MaskedName string `json:"maskedName" dc:"脱敏姓名"`
MaskedIdNumber string `json:"maskedIdNumber" dc:"脱敏证件号"`
IdType string `json:"idType" dc:"证件类型"`
ReviewReason string `json:"reviewReason" dc:"审核原因"`
VerifiedAt *gtime.Time `json:"verifiedAt" dc:"通过时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
// ImRealnameViewInp 获取指定IM实名审核信息
type ImRealnameViewInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImRealnameViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
type ImRealnameViewModel struct {
ImRealnameListModel
IdNumberHash string `json:"idNumberHash" dc:"证件号哈希"`
}
// ImRealnameReviewInp 审核IM实名
type ImRealnameReviewInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
Status string `json:"status" dc:"审核状态"`
ReviewReason string `json:"reviewReason" dc:"审核原因"`
}
func (in *ImRealnameReviewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
if !dict.HasOptionKey(consts.ImRealnameStatusOptions, in.Status) {
return gerror.New("审核状态不正确")
}
if in.Status == consts.ImRealnameStatusRejected && in.ReviewReason == "" {
return gerror.New("拒绝原因不能为空")
}
return
}
type ImRealnameReviewModel struct{}
@@ -0,0 +1,12 @@
package sysin
import "github.com/gogf/gf/v2/os/gtime"
type ImRegistrationConfigModel struct {
RegisterSmsEnabled bool `json:"registerSmsEnabled" dc:"注册短信是否真实发送"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImRegistrationConfigUpdateInp struct {
RegisterSmsEnabled bool `json:"registerSmsEnabled" dc:"注册短信是否真实发送"`
}
@@ -0,0 +1,157 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2026 HotGo
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// ImTenantUpdateFields 修改IM租户字段过滤
type ImTenantUpdateFields struct {
Code string `json:"code" dc:"租户编码"`
Name string `json:"name" dc:"租户名称"`
TenantDesc string `json:"tenantDesc" dc:"租户描述"`
DbName string `json:"dbName" dc:"业务库名"`
DbDsn string `json:"dbDsn" dc:"独立连接串"`
State int `json:"state" dc:"状态"`
UpdatedBy int64 `json:"updatedBy" dc:"更新者"`
}
// ImTenantInsertFields 新增IM租户字段过滤
type ImTenantInsertFields struct {
Code string `json:"code" dc:"租户编码"`
Name string `json:"name" dc:"租户名称"`
TenantDesc string `json:"tenantDesc" dc:"租户描述"`
DbName string `json:"dbName" dc:"业务库名"`
DbDsn string `json:"dbDsn" dc:"独立连接串"`
State int `json:"state" dc:"状态"`
CreatedBy int64 `json:"createdBy" dc:"创建者"`
}
// ImTenantConfigUpdateFields 修改IM租户配置字段过滤
type ImTenantConfigUpdateFields struct {
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
UpdatedBy int64 `json:"updatedBy" dc:"更新者"`
}
// ImTenantConfigInsertFields 新增IM租户配置字段过滤
type ImTenantConfigInsertFields struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
CreatedBy int64 `json:"createdBy" dc:"创建者"`
}
// ImTenantEditInp 修改/新增IM租户
type ImTenantEditInp struct {
entity.ImTenant
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
}
func (in *ImTenantEditInp) Filter(ctx context.Context) (err error) {
if e := g.Validator().Rules("required|length:2,64").Data(in.Code).Messages("租户编码不能为空|租户编码长度应为2到64个字符").Run(ctx); e != nil {
return e.Current()
}
if e := g.Validator().Rules("required|max-length:128").Data(in.Name).Messages("租户名称不能为空|租户名称不能超过128个字符").Run(ctx); e != nil {
return e.Current()
}
if in.DbName == "" && in.DbDsn == "" {
return gerror.New("业务库名和独立连接串至少填写一个")
}
if !dict.HasOptionKey(consts.ImTenantStateOptions, in.State) {
return gerror.New("租户状态不正确")
}
return
}
type ImTenantEditModel struct{}
// ImTenantViewInp 获取指定IM租户信息
type ImTenantViewInp struct {
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImTenantViewInp) Filter(ctx context.Context) (err error) {
return
}
type ImTenantViewModel struct {
entity.ImTenant
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"`
UpdatedBySumma *hook.MemberSumma `json:"updatedBySumma" dc:"更新者摘要信息"`
DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"`
TenantDbReachable bool `json:"tenantDbReachable" dc:"业务库是否可达"`
TenantDbCheckError string `json:"tenantDbCheckError" dc:"业务库检查错误"`
}
// ImTenantListInp 获取IM租户列表
type ImTenantListInp struct {
form.PageReq
Id int64 `json:"id" dc:"ID"`
Code string `json:"code" dc:"租户编码"`
Name string `json:"name" dc:"租户名称"`
DbName string `json:"dbName" dc:"业务库名"`
State *int `json:"state" dc:"状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
}
func (in *ImTenantListInp) Filter(ctx context.Context) (err error) {
return
}
type ImTenantListModel struct {
Id int64 `json:"id" dc:"ID"`
Code string `json:"code" dc:"租户编码"`
Name string `json:"name" dc:"租户名称"`
TenantDesc string `json:"tenantDesc" dc:"租户描述"`
DbName string `json:"dbName" dc:"业务库名"`
State int `json:"state" dc:"状态"`
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"`
CreatedBy int64 `json:"createdBy" dc:"创建者"`
CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"`
UpdatedBy int64 `json:"updatedBy" dc:"更新者"`
UpdatedBySumma *hook.MemberSumma `json:"updatedBySumma" dc:"更新者摘要信息"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
// ImTenantStatusInp 更新IM租户状态
type ImTenantStatusInp struct {
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
State int `json:"state" dc:"状态"`
}
func (in *ImTenantStatusInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
if !dict.HasOptionKey(consts.ImTenantStateOptions, in.State) {
return gerror.New("租户状态不正确")
}
return
}
type ImTenantStatusModel struct{}
@@ -0,0 +1,99 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2026 HotGo
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
// ImUserListInp 获取IM用户列表
type ImUserListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" dc:"用户ID"`
Nickname string `json:"nickname" dc:"昵称"`
AuthName string `json:"authName" dc:"登录标识"`
State *int `json:"state" dc:"账号状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
LastSeen []*gtime.Time `json:"lastSeen" dc:"最后在线时间"`
}
func (in *ImUserListInp) Filter(ctx context.Context) (err error) {
if in.UserId != "" {
if _, err = ParseOptionalInt64(in.UserId); err != nil {
return err
}
}
return
}
type ImUserListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
UserId string `json:"userId" dc:"用户ID"`
Username string `json:"username" dc:"登录账号"`
Phone string `json:"phone" dc:"手机号"`
Nickname string `json:"nickname" dc:"昵称"`
PublicAccount string `json:"publicAccount" dc:"公开账号"`
Gender string `json:"gender" dc:"性别"`
Region string `json:"region" dc:"地区"`
State int `json:"state" dc:"账号状态"`
AuthVersion int `json:"authVersion" dc:"认证版本"`
PasswordUpdatedAt *gtime.Time `json:"passwordUpdatedAt" dc:"密码更新时间"`
AuthNames string `json:"authNames" dc:"登录标识"`
Credentials string `json:"credentials" dc:"凭证"`
LastSeen *gtime.Time `json:"lastSeen" dc:"最后在线时间"`
UserAgent string `json:"userAgent" dc:"客户端"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
// ImUserViewInp 获取指定IM用户信息
type ImUserViewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
func (in *ImUserViewInp) Filter(ctx context.Context) (err error) {
if _, err = ParseOptionalInt64(in.UserId); err != nil {
return gerror.New("用户ID不能为空")
}
return
}
type ImUserPasswordResetInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
NewPassword string `json:"newPassword" v:"required#新密码不能为空" dc:"新密码"`
}
func (in *ImUserPasswordResetInp) Filter(ctx context.Context) error {
if _, err := ParseOptionalInt64(in.UserId); err != nil {
return err
}
if len([]rune(in.NewPassword)) < 3 {
return gerror.New("新密码长度不能少于3位")
}
if len([]byte(in.NewPassword)) > 72 {
return gerror.New("新密码长度不能超过72字节")
}
return nil
}
type ImUserViewModel struct {
ImUserListModel
Signature string `json:"signature" dc:"个性签名"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
BirthDate string `json:"birthDate" dc:"出生日期"`
ProfileJson string `json:"profileJson" dc:"Tinode Public资料"`
TrustedJson string `json:"trustedJson" dc:"Tinode Trusted资料"`
AccessJson string `json:"accessJson" dc:"访问权限"`
ProfileVersion int `json:"profileVersion" dc:"资料版本"`
}
@@ -0,0 +1,118 @@
// Package sysin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2026 HotGo
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
const (
ImWorkOrderTypeAbuse = "abuse"
ImWorkOrderTypeFeedback = "feedback"
)
// ImWorkOrderListInp 获取IM举报反馈工单列表
type ImWorkOrderListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Type string `json:"type" dc:"工单类型"`
UserId int64 `json:"userId" dc:"提交人ID"`
Status string `json:"status" dc:"状态"`
Keyword string `json:"keyword" dc:"关键词"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
}
func (in *ImWorkOrderListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Type == "" {
return gerror.New("请选择工单类型")
}
if in.Type != ImWorkOrderTypeAbuse && in.Type != ImWorkOrderTypeFeedback {
return gerror.New("工单类型不正确")
}
return
}
type ImWorkOrderListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Type string `json:"type" dc:"工单类型"`
Id int64 `json:"id" dc:"ID"`
OrderId string `json:"orderId" dc:"工单ID"`
UserId int64 `json:"userId" dc:"提交人ID"`
TargetType string `json:"targetType" dc:"目标类型"`
TargetSummary string `json:"targetSummary" dc:"目标摘要"`
Category string `json:"category" dc:"分类"`
Content string `json:"content" dc:"内容"`
Status string `json:"status" dc:"状态"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
// ImWorkOrderViewInp 获取指定IM举报反馈工单信息
type ImWorkOrderViewInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Type string `json:"type" v:"required#工单类型不能为空" dc:"工单类型"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImWorkOrderViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Type != ImWorkOrderTypeAbuse && in.Type != ImWorkOrderTypeFeedback {
return gerror.New("工单类型不正确")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
type ImWorkOrderViewModel struct {
ImWorkOrderListModel
Description string `json:"description" dc:"描述"`
Contact string `json:"contact" dc:"联系方式"`
EvidenceJson string `json:"evidenceJson" dc:"证据/附件"`
SnapshotJson string `json:"snapshotJson" dc:"快照"`
ClientContextJson string `json:"clientContextJson" dc:"客户端上下文"`
IncludeDiagnostics int `json:"includeDiagnostics" dc:"是否包含诊断"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
}
// ImWorkOrderStatusInp 更新IM举报反馈工单状态
type ImWorkOrderStatusInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
Type string `json:"type" v:"required#工单类型不能为空" dc:"工单类型"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
Status string `json:"status" dc:"状态"`
}
func (in *ImWorkOrderStatusInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Type != ImWorkOrderTypeAbuse && in.Type != ImWorkOrderTypeFeedback {
return gerror.New("工单类型不正确")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
if !dict.HasOptionKey(consts.ImWorkOrderStatusOptions, in.Status) {
return gerror.New("工单状态不正确")
}
return
}
type ImWorkOrderStatusModel struct{}