chore: seed migration objects batch 5

This commit is contained in:
dev
2026-10-18 13:37:24 +08:00
parent 53c9c0afd8
commit c8c40b1dd2
239 changed files with 16941 additions and 1544 deletions
@@ -0,0 +1,32 @@
package imabusereason
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imAbuseReason/list" method:"get" tags:"IM举报原因" summary:"获取IM举报原因列表"`
sysin.ImAbuseReasonListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImAbuseReasonModel `json:"list" dc:"数据列表"`
}
type EditReq struct {
g.Meta `path:"/imAbuseReason/edit" method:"post" tags:"IM举报原因" summary:"新增/编辑IM举报原因"`
sysin.ImAbuseReasonEditInp
}
type EditRes struct{}
type DeleteReq struct {
g.Meta `path:"/imAbuseReason/delete" method:"post" tags:"IM举报原因" summary:"禁用IM举报原因"`
sysin.ImAbuseReasonDeleteInp
}
type DeleteRes struct{}
@@ -0,0 +1,25 @@
package imapprelease
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imAppRelease/list" method:"get" tags:"IM版本更新" summary:"获取IM客户端版本策略列表"`
sysin.ImAppReleaseListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImAppReleaseModel `json:"list" dc:"数据列表"`
}
type EditReq struct {
g.Meta `path:"/imAppRelease/edit" method:"post" tags:"IM版本更新" summary:"新增/编辑IM客户端版本策略"`
sysin.ImAppReleaseEditInp
}
type EditRes struct{}
@@ -22,6 +22,14 @@ type ListRes struct {
List []*sysin.ImAuditLogListModel `json:"list" dc:"数据列表"` List []*sysin.ImAuditLogListModel `json:"list" dc:"数据列表"`
} }
// ExportReq 导出IM审计日志列表
type ExportReq struct {
g.Meta `path:"/imAuditLog/export" method:"get" tags:"IM审计日志" summary:"导出IM审计日志列表"`
sysin.ImAuditLogListInp
}
type ExportRes struct{}
// ViewReq 获取IM审计日志指定信息 // ViewReq 获取IM审计日志指定信息
type ViewReq struct { type ViewReq struct {
g.Meta `path:"/imAuditLog/view" method:"get" tags:"IM审计日志" summary:"获取IM审计日志指定信息"` g.Meta `path:"/imAuditLog/view" method:"get" tags:"IM审计日志" summary:"获取IM审计日志指定信息"`
@@ -57,3 +57,37 @@ type ThirdPartyReq struct {
type ThirdPartyRes struct { type ThirdPartyRes struct {
Values map[string]string `json:"values" dc:"配置值"` Values map[string]string `json:"values" dc:"配置值"`
} }
type TenantLoginIPWhitelistReq struct {
g.Meta `path:"/internal/im-config/tenants/by-id/{id}/login-ip-whitelist" method:"get" tags:"IM内部配置" summary:"获取租户渠道登录IP白名单"`
Id int64 `json:"id" in:"path" dc:"租户ID"`
}
type TenantLoginIPWhitelistRes struct {
TenantId int64 `json:"tenant_id" dc:"租户ID"`
AppList []string `json:"app_list" dc:"APP登录IP白名单"`
WebList []string `json:"web_list" dc:"Web登录IP白名单"`
}
type AppReleaseLatestReq struct {
g.Meta `path:"/internal/im-config/app/releases/latest" method:"get" tags:"IM内部配置" summary:"获取客户端最新版本策略"`
Platform string `json:"platform" v:"required#平台不能为空" dc:"平台"`
Channel string `json:"channel" dc:"渠道"`
}
type AppReleaseLatestRes struct {
ID int64 `json:"id"`
Platform string `json:"platform"`
Channel string `json:"channel"`
LatestVersion string `json:"latest_version"`
MinSupportedVersion string `json:"min_supported_version"`
UpdateType string `json:"update_type"`
ReleaseNotes string `json:"release_notes"`
StoreURL string `json:"store_url"`
PackageURL string `json:"package_url"`
Checksum string `json:"checksum"`
RemindAfterSeconds int `json:"remind_after_seconds"`
PublishedAt string `json:"published_at"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
@@ -0,0 +1,25 @@
package imdashboard
import (
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type OverviewReq struct {
g.Meta `path:"/imDashboard/overview" method:"get" tags:"IM数据概览" summary:"获取IM数据概览"`
sysin.ImDashboardOverviewInp
}
type OverviewRes struct {
*sysin.ImDashboardOverviewModel
}
type TrendsReq struct {
g.Meta `path:"/imDashboard/trends" method:"get" tags:"IM数据概览" summary:"获取IM增长趋势"`
sysin.ImDashboardTrendsInp
}
type TrendsRes struct {
List []*sysin.ImDashboardTrendModel `json:"list" dc:"趋势数据"`
}
@@ -0,0 +1,41 @@
package imexternalconfig
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imExternalConfig/list" method:"get" tags:"IM外部服务配置" summary:"获取IM外部服务配置列表"`
sysin.ImExternalConfigListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImExternalConfigListModel `json:"list" dc:"数据列表"`
}
type ViewReq struct {
g.Meta `path:"/imExternalConfig/view" method:"get" tags:"IM外部服务配置" summary:"获取IM外部服务配置详情"`
sysin.ImExternalConfigViewInp
}
type ViewRes struct {
*sysin.ImExternalConfigViewModel
}
type UpdateReq struct {
g.Meta `path:"/imExternalConfig/update" method:"post" tags:"IM外部服务配置" summary:"更新IM外部服务配置"`
sysin.ImExternalConfigUpdateInp
}
type UpdateRes struct{}
type CheckReq struct {
g.Meta `path:"/imExternalConfig/check" method:"post" tags:"IM外部服务配置" summary:"检查IM外部服务配置"`
sysin.ImExternalConfigCheckInp
}
type CheckRes struct{}
@@ -0,0 +1,23 @@
package imfeedbackconfig
import (
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type GetReq struct {
g.Meta `path:"/imFeedbackConfig/get" method:"get" tags:"IM意见反馈配置" summary:"获取IM意见反馈配置"`
sysin.ImFeedbackConfigGetInp
}
type GetRes struct {
*sysin.ImFeedbackConfigModel
}
type UpdateReq struct {
g.Meta `path:"/imFeedbackConfig/update" method:"post" tags:"IM意见反馈配置" summary:"更新IM意见反馈配置"`
sysin.ImFeedbackConfigUpdateInp
}
type UpdateRes struct{}
+60
View File
@@ -0,0 +1,60 @@
// Package imfile
// @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 imfile
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
// ListReq 查询IM文件列表
type ListReq struct {
g.Meta `path:"/imFile/list" method:"get" tags:"IM文件治理" summary:"获取IM文件列表"`
sysin.ImFileListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImFileListModel `json:"list" dc:"数据列表"`
}
// ViewReq 获取IM文件详情
type ViewReq struct {
g.Meta `path:"/imFile/view" method:"get" tags:"IM文件治理" summary:"获取IM文件详情"`
sysin.ImFileViewInp
}
type ViewRes struct {
*sysin.ImFileViewModel
}
// QuarantineReq 隔离IM文件
type QuarantineReq struct {
g.Meta `path:"/imFile/quarantine" method:"post" tags:"IM文件治理" summary:"隔离IM文件"`
sysin.ImFileActionInp
}
type QuarantineRes struct{}
// DeleteReq 删除IM文件
type DeleteReq struct {
g.Meta `path:"/imFile/delete" method:"post" tags:"IM文件治理" summary:"删除IM文件"`
sysin.ImFileActionInp
}
type DeleteRes struct{}
// CleanupOrphanReq 清理IM孤儿文件
type CleanupOrphanReq struct {
g.Meta `path:"/imFile/cleanupOrphan" method:"post" tags:"IM文件治理" summary:"清理IM孤儿文件"`
sysin.ImFileCleanupOrphanInp
}
type CleanupOrphanRes struct {
*sysin.ImFileCleanupOrphanModel
}
@@ -0,0 +1,25 @@
package imfilepolicy
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imFilePolicy/list" method:"get" tags:"IM文件策略" summary:"获取IM文件策略列表"`
sysin.ImFilePolicyListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImFilePolicyListModel `json:"list" dc:"数据列表"`
}
type UpdateReq struct {
g.Meta `path:"/imFilePolicy/update" method:"post" tags:"IM文件策略" summary:"更新IM文件策略"`
sysin.ImFilePolicyUpdateInp
}
type UpdateRes struct{}
+155
View File
@@ -54,6 +54,142 @@ type GovernanceRes struct {
*sysin.ImGroupGovernanceModel *sysin.ImGroupGovernanceModel
} }
// GovernanceUpdateReq 更新IM群治理配置
type GovernanceUpdateReq struct {
g.Meta `path:"/imGroup/governanceUpdate" method:"post" tags:"IM群组" summary:"更新IM群治理配置"`
sysin.ImGroupGovernanceUpdateInp
}
type GovernanceUpdateRes struct {
*sysin.ImGroupCommandModel
}
// ProfileUpdateReq 修改IM群资料
type ProfileUpdateReq struct {
g.Meta `path:"/imGroup/profileUpdate" method:"post" tags:"IM群组" summary:"修改IM群资料"`
sysin.ImGroupProfileUpdateInp
}
type ProfileUpdateRes struct {
*sysin.ImGroupCommandModel
}
// DissolveReq 解散IM群
type DissolveReq struct {
g.Meta `path:"/imGroup/dissolve" method:"post" tags:"IM群组" summary:"解散IM群"`
sysin.ImGroupDissolveInp
}
type DissolveRes struct {
*sysin.ImGroupCommandModel
}
// MuteReq 禁言IM群成员
type MuteReq struct {
g.Meta `path:"/imGroup/mute" method:"post" tags:"IM群组" summary:"禁言IM群成员"`
sysin.ImGroupMuteInp
}
type MuteRes struct {
*sysin.ImGroupCommandModel
}
// MemberMuteReq Kafka禁言IM群成员
type MemberMuteReq struct {
g.Meta `path:"/imGroup/memberMute" method:"post" tags:"IM群组" summary:"Kafka禁言IM群成员"`
sysin.ImGroupMuteInp
}
type MemberMuteRes struct {
*sysin.ImGroupCommandModel
}
// UnmuteReq 解除IM群成员禁言
type UnmuteReq struct {
g.Meta `path:"/imGroup/unmute" method:"post" tags:"IM群组" summary:"解除IM群成员禁言"`
sysin.ImGroupUnmuteInp
}
type UnmuteRes struct {
*sysin.ImGroupCommandModel
}
// MemberUnmuteReq Kafka解除IM群成员禁言
type MemberUnmuteReq struct {
g.Meta `path:"/imGroup/memberUnmute" method:"post" tags:"IM群组" summary:"Kafka解除IM群成员禁言"`
sysin.ImGroupUnmuteInp
}
type MemberUnmuteRes struct {
*sysin.ImGroupCommandModel
}
// MemberRemoveReq 移出IM群成员
type MemberRemoveReq struct {
g.Meta `path:"/imGroup/memberRemove" method:"post" tags:"IM群组" summary:"移出IM群成员"`
sysin.ImGroupMemberRemoveInp
}
type MemberRemoveRes struct {
*sysin.ImGroupCommandModel
}
// 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 {
*sysin.ImGroupCommandModel
}
// AdminSetReq 设置IM群管理员
type AdminSetReq struct {
g.Meta `path:"/imGroup/adminSet" method:"post" tags:"IM群组" summary:"设置IM群管理员"`
sysin.ImGroupRoleUpdateInp
}
type AdminSetRes struct {
*sysin.ImGroupCommandModel
}
// AdminRemoveReq 删除IM群管理员
type AdminRemoveReq struct {
g.Meta `path:"/imGroup/adminRemove" method:"post" tags:"IM群组" summary:"删除IM群管理员"`
sysin.ImGroupRoleUpdateInp
}
type AdminRemoveRes struct {
*sysin.ImGroupCommandModel
}
// OwnerTransferReq 转让IM群主
type OwnerTransferReq struct {
g.Meta `path:"/imGroup/ownerTransfer" method:"post" tags:"IM群组" summary:"转让IM群主"`
sysin.ImGroupOwnerTransferInp
}
type OwnerTransferRes struct {
*sysin.ImGroupCommandModel
}
// 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入群申请列表"`
@@ -65,6 +201,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群邀请列表"`
@@ -0,0 +1,37 @@
package imgroupmessage
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imGroupMessage/list" method:"get" tags:"IM群聊消息" summary:"获取群聊消息列表"`
sysin.ImGroupMessageListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImMessageListModel `json:"list" dc:"数据列表"`
}
type ViewReq struct {
g.Meta `path:"/imGroupMessage/view" method:"get" tags:"IM群聊消息" summary:"获取群聊消息详情"`
sysin.ImMessageViewInp
}
type ViewRes struct{ *sysin.ImMessageViewModel }
type HideReq struct {
g.Meta `path:"/imGroupMessage/hide" method:"post" tags:"IM群聊消息" summary:"隐藏群聊消息"`
sysin.ImMessageActionInp
}
type HideRes struct{ *sysin.ImMessageCommandModel }
type DeleteReq struct {
g.Meta `path:"/imGroupMessage/delete" method:"post" tags:"IM群聊消息" summary:"删除群聊消息"`
sysin.ImMessageActionInp
}
type DeleteRes struct{ *sysin.ImMessageCommandModel }
type ExportReq struct {
g.Meta `path:"/imGroupMessage/export" method:"get" tags:"IM群聊消息" summary:"导出群聊消息"`
sysin.ImGroupMessageListInp
}
type ExportRes struct{}
+7
View File
@@ -51,6 +51,13 @@ type RevokeReq struct {
type RevokeRes struct{} type RevokeRes struct{}
type BindUserReq struct {
g.Meta `path:"/imInvite/bindUser" method:"post" tags:"IM邀请码" summary:"绑定邀请码用户"`
sysin.ImInviteBindUserInp
}
type BindUserRes struct{}
type BatchRevokeReq struct { type BatchRevokeReq struct {
g.Meta `path:"/imInvite/batchRevoke" method:"post" tags:"IM邀请码" summary:"批量撤销IM注册邀请码"` g.Meta `path:"/imInvite/batchRevoke" method:"post" tags:"IM邀请码" summary:"批量撤销IM注册邀请码"`
sysin.ImInviteBatchRevokeInp sysin.ImInviteBatchRevokeInp
@@ -0,0 +1,37 @@
package imloginfailurelog
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imLoginFailureLog/list" method:"get" tags:"IM登录失败日志" summary:"获取登录失败日志"`
sysin.ImLoginFailureLogListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImLoginFailureLogModel `json:"list" dc:"数据列表"`
}
type ViewReq struct {
g.Meta `path:"/imLoginFailureLog/view" method:"get" tags:"IM登录失败日志" summary:"获取登录失败详情"`
sysin.ImLoginFailureViewInp
}
type ViewRes struct{ *sysin.ImLoginFailureLogModel }
type ExportReq struct {
g.Meta `path:"/imLoginFailureLog/export" method:"get" tags:"IM登录失败日志" summary:"导出登录失败日志"`
sysin.ImLoginFailureLogListInp
}
type ExportRes struct{}
type AddBlacklistReq struct {
g.Meta `path:"/imLoginFailureLog/addBlacklist" method:"post" tags:"IM登录失败日志" summary:"将IP加入黑名单"`
sysin.ImLoginFailureAddBlacklistInp
}
type AddBlacklistRes struct{}
type BanUserReq struct {
g.Meta `path:"/imLoginFailureLog/banUser" method:"post" tags:"IM登录失败日志" summary:"封禁关联用户"`
sysin.ImLoginFailureBanUserInp
}
type BanUserRes struct{ *sysin.ImUserCommandModel }
@@ -0,0 +1,23 @@
package immailboxconfig
import (
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type GetReq struct {
g.Meta `path:"/imMailboxConfig/get" method:"get" tags:"IM总经理信箱配置" summary:"获取IM总经理信箱配置"`
sysin.ImMailboxConfigGetInp
}
type GetRes struct {
*sysin.ImMailboxConfigModel
}
type UpdateReq struct {
g.Meta `path:"/imMailboxConfig/update" method:"post" tags:"IM总经理信箱配置" summary:"更新IM总经理信箱配置"`
sysin.ImMailboxConfigUpdateInp
}
type UpdateRes struct{}
@@ -0,0 +1,25 @@
package imnotification
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type TemplateListReq struct {
g.Meta `path:"/imNotification/templateList" method:"get" tags:"IM通知模板" summary:"获取IM通知模板列表"`
sysin.ImNotificationTemplateListInp
}
type TemplateListRes struct {
form.PageRes
List []*sysin.ImNotificationTemplateListModel `json:"list" dc:"数据列表"`
}
type TemplateUpdateReq struct {
g.Meta `path:"/imNotification/templateUpdate" method:"post" tags:"IM通知模板" summary:"更新IM通知模板"`
sysin.ImNotificationTemplateUpdateInp
}
type TemplateUpdateRes struct{}
+79
View File
@@ -0,0 +1,79 @@
package imorg
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type DepartmentListReq struct {
g.Meta `path:"/imOrg/departmentList" method:"get" tags:"IM组织架构" summary:"获取IM组织部门列表"`
sysin.ImOrgDepartmentListInp
}
type DepartmentListRes struct {
form.PageRes
List []*sysin.ImOrgDepartmentModel `json:"list" dc:"数据列表"`
}
type DepartmentExportReq struct {
g.Meta `path:"/imOrg/departmentExport" method:"get" tags:"IM组织架构" summary:"导出IM组织部门列表"`
sysin.ImOrgDepartmentListInp
}
type DepartmentExportRes struct{}
type DepartmentEditReq struct {
g.Meta `path:"/imOrg/departmentEdit" method:"post" tags:"IM组织架构" summary:"新增/编辑IM组织部门"`
sysin.ImOrgDepartmentEditInp
}
type DepartmentEditRes struct{}
type DepartmentDeleteReq struct {
g.Meta `path:"/imOrg/departmentDelete" method:"post" tags:"IM组织架构" summary:"删除IM组织部门"`
sysin.ImOrgDepartmentDeleteInp
}
type DepartmentDeleteRes struct{}
type MemberListReq struct {
g.Meta `path:"/imOrg/memberList" method:"get" tags:"IM组织架构" summary:"获取IM组织成员列表"`
sysin.ImOrgMemberListInp
}
type MemberListRes struct {
form.PageRes
List []*sysin.ImOrgMemberModel `json:"list" dc:"数据列表"`
}
type MemberExportReq struct {
g.Meta `path:"/imOrg/memberExport" method:"get" tags:"IM组织架构" summary:"导出IM组织成员列表"`
sysin.ImOrgMemberListInp
}
type MemberExportRes struct{}
type MemberEditReq struct {
g.Meta `path:"/imOrg/memberEdit" method:"post" tags:"IM组织架构" summary:"新增/编辑IM组织成员"`
sysin.ImOrgMemberEditInp
}
type MemberEditRes struct{}
type MemberDeleteReq struct {
g.Meta `path:"/imOrg/memberDelete" method:"post" tags:"IM组织架构" summary:"删除IM组织成员"`
sysin.ImOrgMemberDeleteInp
}
type MemberDeleteRes struct{}
type SearchReq struct {
g.Meta `path:"/imOrg/search" method:"get" tags:"IM组织架构" summary:"搜索IM组织"`
sysin.ImOrgSearchInp
}
type SearchRes struct {
*sysin.ImOrgSearchModel
}
+46
View File
@@ -0,0 +1,46 @@
// Package imoutbox
// @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 imoutbox
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imOutbox/list" method:"get" tags:"IM Outbox" summary:"获取IM Outbox列表"`
sysin.ImOutboxListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImOutboxListModel `json:"list" dc:"数据列表"`
}
type ViewReq struct {
g.Meta `path:"/imOutbox/view" method:"get" tags:"IM Outbox" summary:"获取IM Outbox详情"`
sysin.ImOutboxViewInp
}
type ViewRes struct {
*sysin.ImOutboxViewModel
}
type RetryReq struct {
g.Meta `path:"/imOutbox/retry" method:"post" tags:"IM Outbox" summary:"重试IM Outbox事件"`
sysin.ImOutboxActionInp
}
type RetryRes struct{}
type DeadReq struct {
g.Meta `path:"/imOutbox/dead" method:"post" tags:"IM Outbox" summary:"标记IM Outbox死信"`
sysin.ImOutboxActionInp
}
type DeadRes struct{}
@@ -0,0 +1,37 @@
package imprivatemessage
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imPrivateMessage/list" method:"get" tags:"IM单聊消息" summary:"获取单聊消息列表"`
sysin.ImPrivateMessageListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImMessageListModel `json:"list" dc:"数据列表"`
}
type ViewReq struct {
g.Meta `path:"/imPrivateMessage/view" method:"get" tags:"IM单聊消息" summary:"获取单聊消息详情"`
sysin.ImMessageViewInp
}
type ViewRes struct{ *sysin.ImMessageViewModel }
type HideReq struct {
g.Meta `path:"/imPrivateMessage/hide" method:"post" tags:"IM单聊消息" summary:"隐藏单聊消息"`
sysin.ImMessageActionInp
}
type HideRes struct{ *sysin.ImMessageCommandModel }
type DeleteReq struct {
g.Meta `path:"/imPrivateMessage/delete" method:"post" tags:"IM单聊消息" summary:"删除单聊消息"`
sysin.ImMessageActionInp
}
type DeleteRes struct{ *sysin.ImMessageCommandModel }
type ExportReq struct {
g.Meta `path:"/imPrivateMessage/export" method:"get" tags:"IM单聊消息" summary:"导出单聊消息"`
sysin.ImPrivateMessageListInp
}
type ExportRes struct{}
@@ -0,0 +1,59 @@
package imsecuritypolicy
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imSecurityPolicy/list" method:"get" tags:"IM安全策略" summary:"获取安全策略历史"`
sysin.ImSecurityPolicyListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImSecurityPolicyModel `json:"list"`
}
type ViewReq struct {
g.Meta `path:"/imSecurityPolicy/view" method:"get" tags:"IM安全策略" summary:"获取安全策略详情"`
sysin.ImSecurityPolicyViewInp
}
type ViewRes struct{ *sysin.ImSecurityPolicyModel }
type SaveReq struct {
g.Meta `path:"/imSecurityPolicy/save" method:"post" tags:"IM安全策略" summary:"保存安全策略草稿"`
sysin.ImSecurityPolicySaveInp
}
type SaveRes struct {
Id int64 `json:"id"`
}
type PublishReq struct {
g.Meta `path:"/imSecurityPolicy/publish" method:"post" tags:"IM安全策略" summary:"发布安全策略"`
sysin.ImSecurityPolicyPublishInp
}
type PublishRes struct{}
type RestoreDefaultReq struct {
g.Meta `path:"/imSecurityPolicy/restoreDefault" method:"post" tags:"IM安全策略" summary:"恢复默认安全策略"`
sysin.ImSecurityPolicyRestoreInp
}
type RestoreDefaultRes struct {
Id int64 `json:"id"`
}
type IPRuleListReq struct {
g.Meta `path:"/imSecurityPolicy/ipRuleList" method:"get" tags:"IM安全策略" summary:"获取IP黑白名单"`
sysin.ImSecurityIPRuleListInp
}
type IPRuleListRes struct {
form.PageRes
List []*sysin.ImSecurityIPRuleModel `json:"list"`
}
type IPRuleEditReq struct {
g.Meta `path:"/imSecurityPolicy/ipRuleEdit" method:"post" tags:"IM安全策略" summary:"新增或编辑IP规则"`
sysin.ImSecurityIPRuleEditInp
}
type IPRuleEditRes struct{}
type IPRuleDeleteReq struct {
g.Meta `path:"/imSecurityPolicy/ipRuleDelete" method:"post" tags:"IM安全策略" summary:"删除IP规则"`
sysin.ImSecurityIPRuleDeleteInp
}
type IPRuleDeleteRes struct{}
@@ -0,0 +1,51 @@
package imsensitiveword
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imSensitiveWord/list" method:"get" tags:"IM敏感词" summary:"获取敏感词列表"`
sysin.ImSensitiveWordListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImSensitiveWordListModel `json:"list"`
}
type EditReq struct {
g.Meta `path:"/imSensitiveWord/edit" method:"post" tags:"IM敏感词" summary:"新增或编辑敏感词"`
sysin.ImSensitiveWordEditInp
}
type EditRes struct{}
type DeleteReq struct {
g.Meta `path:"/imSensitiveWord/delete" method:"post" tags:"IM敏感词" summary:"删除敏感词"`
sysin.ImSensitiveWordDeleteInp
}
type DeleteRes struct{}
type StatusReq struct {
g.Meta `path:"/imSensitiveWord/status" method:"post" tags:"IM敏感词" summary:"更新敏感词状态"`
sysin.ImSensitiveWordStatusInp
}
type StatusRes struct{}
type BatchImportReq struct {
g.Meta `path:"/imSensitiveWord/batchImport" method:"post" tags:"IM敏感词" summary:"批量导入敏感词"`
sysin.ImSensitiveWordBatchImportInp
}
type BatchImportRes struct {
*sysin.ImSensitiveWordBatchImportModel
}
type GroupListReq struct {
g.Meta `path:"/imSensitiveWord/groupList" method:"get" tags:"IM敏感词" summary:"获取敏感词分组"`
sysin.ImSensitiveWordGroupListInp
}
type GroupListRes struct {
List []*sysin.ImSensitiveWordGroupModel `json:"list"`
}
type GroupEditReq struct {
g.Meta `path:"/imSensitiveWord/groupEdit" method:"post" tags:"IM敏感词" summary:"新增或编辑敏感词分组"`
sysin.ImSensitiveWordGroupEditInp
}
type GroupEditRes struct{}
@@ -0,0 +1,18 @@
package imtaskdashboard
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imTaskDashboard/list" method:"get" tags:"IM任务看板" summary:"获取IM任务看板列表"`
sysin.ImTaskDashboardListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImTaskDashboardListModel `json:"list" dc:"数据列表"`
}
+32
View File
@@ -48,3 +48,35 @@ type StatusReq struct {
} }
type StatusRes struct{} type StatusRes struct{}
type UsageReq struct {
g.Meta `path:"/imTenant/usage" method:"get" tags:"IM租户" summary:"获取IM租户用量和健康状态"`
sysin.ImTenantUsageInp
}
type UsageRes struct {
*sysin.ImTenantUsageModel
}
type DeletionCheckReq struct {
g.Meta `path:"/imTenant/deletionCheck" method:"get" tags:"IM租户" summary:"IM租户删除前检查"`
sysin.ImTenantDeletionCheckInp
}
type DeletionCheckRes struct {
*sysin.ImTenantDeletionCheckModel
}
type EnterDeletingReq struct {
g.Meta `path:"/imTenant/enterDeleting" method:"post" tags:"IM租户" summary:"IM租户进入删除流程"`
sysin.ImTenantLifecycleInp
}
type EnterDeletingRes struct{}
type RestoreReq struct {
g.Meta `path:"/imTenant/restore" method:"post" tags:"IM租户" summary:"恢复IM租户"`
sysin.ImTenantLifecycleInp
}
type RestoreRes struct{}
+100 -8
View File
@@ -10,6 +10,7 @@ import (
"hotgo/internal/model/input/sysin" "hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
) )
// ListReq 查询IM用户列表 // ListReq 查询IM用户列表
@@ -23,6 +24,41 @@ type ListRes struct {
List []*sysin.ImUserListModel `json:"list" dc:"数据列表"` List []*sysin.ImUserListModel `json:"list" dc:"数据列表"`
} }
type CreateReq struct {
g.Meta `path:"/imUser/create" method:"post" tags:"IM用户" summary:"后台创建IM用户"`
sysin.ImUserCreateInp
}
type CreateRes struct {
*sysin.ImUserCreateModel
}
type BatchCreateReq struct {
g.Meta `path:"/imUser/batchCreate" method:"post" tags:"IM用户" summary:"批量创建IM用户"`
sysin.ImUserBatchCreateInp
}
type BatchCreateRes struct {
*sysin.ImUserBatchCreateModel
}
type ImportReq struct {
g.Meta `path:"/imUser/import" method:"post" tags:"IM用户" summary:"Excel导入IM用户"`
TenantId int64 `json:"tenantId" dc:"租户ID"`
GroupId int64 `json:"groupId" dc:"默认用户策略分组ID"`
File *ghttp.UploadFile `json:"file" type:"file" v:"required#请选择Excel文件" dc:"Excel文件"`
}
type ImportRes struct {
*sysin.ImUserBatchCreateModel
}
type DownloadTemplateReq struct {
g.Meta `path:"/imUser/downloadTemplate" method:"get" tags:"IM用户" summary:"下载IM用户导入模板"`
}
type DownloadTemplateRes struct{}
// ViewReq 获取IM用户指定信息 // ViewReq 获取IM用户指定信息
type ViewReq struct { type ViewReq struct {
g.Meta `path:"/imUser/view" method:"get" tags:"IM用户" summary:"获取IM用户指定信息"` g.Meta `path:"/imUser/view" method:"get" tags:"IM用户" summary:"获取IM用户指定信息"`
@@ -33,8 +69,64 @@ type ViewRes struct {
*sysin.ImUserViewModel *sysin.ImUserViewModel
} }
type ResetPasswordReq struct {
g.Meta `path:"/imUser/resetPassword" method:"post" tags:"IM用户" summary:"重置IM用户密码"`
sysin.ImUserPasswordResetInp
}
type ResetPasswordRes struct {
*sysin.ImUserCommandModel
}
type RevokeSessionsReq struct {
g.Meta `path:"/imUser/revokeSessions" method:"post" tags:"IM用户" summary:"撤销IM用户会话"`
sysin.ImUserRevokeSessionsInp
}
type RevokeSessionsRes struct{}
type StatusReq struct {
g.Meta `path:"/imUser/status" method:"post" tags:"IM用户" summary:"更新IM用户状态"`
sysin.ImUserStatusInp
}
type StatusRes struct{}
type AdjustGroupReq struct {
g.Meta `path:"/imUser/adjustGroup" method:"post" tags:"IM用户" summary:"调整IM用户分组"`
sysin.ImUserAdjustGroupInp
}
type AdjustGroupRes struct{}
type IPWhitelistReq struct {
g.Meta `path:"/imUser/ipWhitelist" method:"get" tags:"IM用户" summary:"获取IM用户IP白名单"`
sysin.ImUserIPWhitelistInp
}
type IPWhitelistRes struct {
*sysin.ImUserIPWhitelistModel
}
type IPWhitelistSaveReq struct {
g.Meta `path:"/imUser/ipWhitelistSave" method:"post" tags:"IM用户" summary:"保存IM用户IP白名单"`
sysin.ImUserIPWhitelistInp
}
type IPWhitelistSaveRes struct{}
type DevicesReq struct {
g.Meta `path:"/imUser/devices" method:"get" tags:"IM用户" summary:"获取IM用户设备列表"`
sysin.ImUserDeviceListInp
}
type DevicesRes struct {
form.PageRes
List []*sysin.ImUserDeviceListModel `json:"list" dc:"设备列表"`
}
type BanReq struct { type BanReq struct {
g.Meta `path:"/imUser/ban" method:"post" tags:"IM用户管理" summary:"封禁IM用户"` g.Meta `path:"/imUser/ban" method:"post" tags:"IM用户" summary:"封禁IM用户"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -43,7 +135,7 @@ type BanRes struct {
} }
type UnbanReq struct { type UnbanReq struct {
g.Meta `path:"/imUser/unban" method:"post" tags:"IM用户管理" summary:"解封IM用户"` g.Meta `path:"/imUser/unban" method:"post" tags:"IM用户" summary:"解封IM用户"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -52,7 +144,7 @@ type UnbanRes struct {
} }
type FreezeReq struct { type FreezeReq struct {
g.Meta `path:"/imUser/freeze" method:"post" tags:"IM用户管理" summary:"冻结IM用户"` g.Meta `path:"/imUser/freeze" method:"post" tags:"IM用户" summary:"冻结IM用户"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -61,7 +153,7 @@ type FreezeRes struct {
} }
type UnfreezeReq struct { type UnfreezeReq struct {
g.Meta `path:"/imUser/unfreeze" method:"post" tags:"IM用户管理" summary:"解冻IM用户"` g.Meta `path:"/imUser/unfreeze" method:"post" tags:"IM用户" summary:"解冻IM用户"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -70,7 +162,7 @@ type UnfreezeRes struct {
} }
type DeviceKickReq struct { type DeviceKickReq struct {
g.Meta `path:"/imUser/deviceKick" method:"post" tags:"IM用户管理" summary:"踢出IM用户设备"` g.Meta `path:"/imUser/deviceKick" method:"post" tags:"IM用户" summary:"踢出IM用户设备"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -79,7 +171,7 @@ type DeviceKickRes struct {
} }
type LogoutAllReq struct { type LogoutAllReq struct {
g.Meta `path:"/imUser/logoutAll" method:"post" tags:"IM用户管理" summary:"踢出IM用户全部在线会话"` g.Meta `path:"/imUser/logoutAll" method:"post" tags:"IM用户" summary:"踢出IM用户全部会话"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -88,7 +180,7 @@ type LogoutAllRes struct {
} }
type AccountCancelReq struct { type AccountCancelReq struct {
g.Meta `path:"/imUser/accountCancel" method:"post" tags:"IM用户管理" summary:"注销IM用户账号"` g.Meta `path:"/imUser/accountCancel" method:"post" tags:"IM用户" summary:"注销IM用户账号"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -97,7 +189,7 @@ type AccountCancelRes struct {
} }
type AccountDeleteReq struct { type AccountDeleteReq struct {
g.Meta `path:"/imUser/accountDelete" method:"post" tags:"IM用户管理" summary:"删除IM用户账号"` g.Meta `path:"/imUser/accountDelete" method:"post" tags:"IM用户" summary:"删除IM用户账号"`
sysin.ImUserGovernanceInp sysin.ImUserGovernanceInp
} }
@@ -0,0 +1,22 @@
package imuserloginhistory
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imUserLoginHistory/list" method:"get" tags:"IM用户登录历史" summary:"获取用户登录历史"`
sysin.ImUserLoginHistoryListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImUserLoginHistoryModel `json:"list" dc:"数据列表"`
}
type ExportReq struct {
g.Meta `path:"/imUserLoginHistory/export" method:"get" tags:"IM用户登录历史" summary:"导出用户登录历史"`
sysin.ImUserLoginHistoryListInp
}
type ExportRes struct{}
@@ -0,0 +1,70 @@
package imuserpolicygroup
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imUserPolicyGroup/list" method:"get" tags:"IM用户策略分组" summary:"获取用户策略分组列表"`
sysin.ImUserPolicyGroupListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImUserPolicyGroupListModel `json:"list"`
}
type ViewReq struct {
g.Meta `path:"/imUserPolicyGroup/view" method:"get" tags:"IM用户策略分组" summary:"获取用户策略分组详情"`
sysin.ImUserPolicyGroupViewInp
}
type ViewRes struct {
*sysin.ImUserPolicyGroupViewModel
}
type EditReq struct {
g.Meta `path:"/imUserPolicyGroup/edit" method:"post" tags:"IM用户策略分组" summary:"新增或编辑用户策略分组"`
sysin.ImUserPolicyGroupEditInp
}
type EditRes struct{}
type DeleteReq struct {
g.Meta `path:"/imUserPolicyGroup/delete" method:"post" tags:"IM用户策略分组" summary:"删除用户策略分组"`
sysin.ImUserPolicyGroupDeleteInp
}
type DeleteRes struct{}
type StatusReq struct {
g.Meta `path:"/imUserPolicyGroup/status" method:"post" tags:"IM用户策略分组" summary:"更新用户策略分组状态"`
sysin.ImUserPolicyGroupStatusInp
}
type StatusRes struct{}
type MemberListReq struct {
g.Meta `path:"/imUserPolicyGroup/memberList" method:"get" tags:"IM用户策略分组" summary:"获取策略分组成员"`
sysin.ImUserPolicyGroupMemberListInp
}
type MemberListRes struct {
form.PageRes
List []*sysin.ImUserPolicyGroupMemberModel `json:"list"`
}
type MemberAddReq struct {
g.Meta `path:"/imUserPolicyGroup/memberAdd" method:"post" tags:"IM用户策略分组" summary:"添加策略分组成员"`
sysin.ImUserPolicyGroupMemberAddInp
}
type MemberAddRes struct{}
type MemberRemoveReq struct {
g.Meta `path:"/imUserPolicyGroup/memberRemove" method:"post" tags:"IM用户策略分组" summary:"移除策略分组成员"`
sysin.ImUserPolicyGroupMemberRemoveInp
}
type MemberRemoveRes struct{}
type LoginIPWhitelistReq struct {
g.Meta `path:"/imUserPolicyGroup/loginIPWhitelist" method:"get" tags:"IM用户策略分组" summary:"获取租户登录IP白名单"`
sysin.ImTenantLoginIPWhitelistInp
}
type LoginIPWhitelistRes struct {
*sysin.ImTenantLoginIPWhitelistModel
}
type LoginIPWhitelistSaveReq struct {
g.Meta `path:"/imUserPolicyGroup/loginIPWhitelistSave" method:"post" tags:"IM用户策略分组" summary:"保存租户登录IP白名单"`
sysin.ImTenantLoginIPWhitelistSaveInp
}
type LoginIPWhitelistSaveRes struct{}
@@ -0,0 +1,39 @@
package imworkbench
import (
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"github.com/gogf/gf/v2/frame/g"
)
type ListReq struct {
g.Meta `path:"/imWorkbench/list" method:"get" tags:"IM工作台" summary:"获取IM工作台模块列表"`
sysin.ImWorkbenchAppListInp
}
type ListRes struct {
form.PageRes
List []*sysin.ImWorkbenchAppModel `json:"list" dc:"数据列表"`
}
type EditReq struct {
g.Meta `path:"/imWorkbench/edit" method:"post" tags:"IM工作台" summary:"新增/编辑IM工作台模块"`
sysin.ImWorkbenchAppEditInp
}
type EditRes struct{}
type DeleteReq struct {
g.Meta `path:"/imWorkbench/delete" method:"post" tags:"IM工作台" summary:"禁用IM工作台模块"`
sysin.ImWorkbenchAppDeleteInp
}
type DeleteRes struct{}
type ReorderReq struct {
g.Meta `path:"/imWorkbench/reorder" method:"post" tags:"IM工作台" summary:"调整IM工作台模块排序"`
sysin.ImWorkbenchAppReorderInp
}
type ReorderRes struct{}
+2
View File
@@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/util/gmode" "github.com/gogf/gf/v2/util/gmode"
"hotgo/internal/controller/health"
"hotgo/internal/library/addons" "hotgo/internal/library/addons"
"hotgo/internal/library/casbin" "hotgo/internal/library/casbin"
"hotgo/internal/library/hggen" "hotgo/internal/library/hggen"
@@ -33,6 +34,7 @@ var (
// 请求响应结束后回调 // 请求响应结束后回调
s.BindHookHandler("/*any", ghttp.HookAfterOutput, service.Hook().AfterOutput) s.BindHookHandler("/*any", ghttp.HookAfterOutput, service.Hook().AfterOutput)
s.BindHandler("/healthz", health.Serve)
// 注册全局中间件 // 注册全局中间件
s.BindMiddleware("/*any", []ghttp.HandlerFunc{ s.BindMiddleware("/*any", []ghttp.HandlerFunc{
@@ -0,0 +1,84 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
const (
ImMessageContentText = "text"
ImMessageContentImage = "image"
ImMessageContentVideo = "video"
ImMessageContentAudio = "audio"
ImMessageContentVoice = "voice"
ImMessageContentFile = "file"
ImMessageContentCard = "card"
ImMessageContentSystem = "system"
ImMessageStatusNormal = "normal"
ImMessageStatusDeleted = "deleted"
ImMessageStatusRecalled = "recalled"
ImMessageStatusBlocked = "blocked"
ImMessageStatusHidden = "hidden"
)
var ImMessageContentTypeOptions = []*model.Option{
dict.GenInfoOption(ImMessageContentText, "文本"),
dict.GenPrimaryOption(ImMessageContentImage, "图片"),
dict.GenPrimaryOption(ImMessageContentVideo, "视频"),
dict.GenPrimaryOption(ImMessageContentAudio, "语音"),
dict.GenPrimaryOption(ImMessageContentVoice, "语音"),
dict.GenDefaultOption(ImMessageContentFile, "文件"),
dict.GenDefaultOption(ImMessageContentCard, "名片"),
dict.GenWarningOption(ImMessageContentSystem, "系统"),
}
var ImMessageStatusOptions = []*model.Option{
dict.GenSuccessOption(ImMessageStatusNormal, "正常"),
dict.GenErrorOption(ImMessageStatusDeleted, "后台删除"),
dict.GenWarningOption(ImMessageStatusRecalled, "用户撤回"),
dict.GenErrorOption(ImMessageStatusBlocked, "风控拦截"),
dict.GenWarningOption(ImMessageStatusHidden, "已隐藏"),
}
var ImLoginTypeOptions = []*model.Option{
dict.GenPrimaryOption("password", "密码"),
dict.GenPrimaryOption("sms_code", "短信验证码"),
dict.GenInfoOption("sso", "单点登录"),
dict.GenInfoOption("qr_code", "二维码"),
dict.GenDefaultOption("token_refresh", "令牌刷新"),
}
var ImLoginEventTypeOptions = []*model.Option{
dict.GenSuccessOption("login", "登录"),
dict.GenInfoOption("refresh", "刷新会话"),
dict.GenDefaultOption("logout", "主动退出"),
dict.GenWarningOption("kicked", "被踢下线"),
dict.GenWarningOption("session_expired", "会话过期"),
}
var ImClientPlatformOptions = []*model.Option{
dict.GenPrimaryOption("ios", "iOS"),
dict.GenSuccessOption("android", "Android"),
dict.GenInfoOption("web", "Web"),
dict.GenDefaultOption("desktop", "桌面端"),
dict.GenDefaultOption("unknown", "未知"),
}
var ImLoginFailureTypeOptions = []*model.Option{
dict.GenErrorOption("password_error", "密码错误"),
dict.GenErrorOption("code_error", "验证码错误"),
dict.GenWarningOption("account_not_found", "账号不存在"),
dict.GenErrorOption("account_disabled", "账号已禁用"),
dict.GenWarningOption("rate_limited", "访问频率受限"),
dict.GenErrorOption("risk_blocked", "风控拦截"),
}
func init() {
dict.RegisterEnums("ImMessageContentTypeOptions", "IM消息内容类型选项", ImMessageContentTypeOptions)
dict.RegisterEnums("ImMessageStatusOptions", "IM消息状态选项", ImMessageStatusOptions)
dict.RegisterEnums("ImLoginTypeOptions", "IM登录方式选项", ImLoginTypeOptions)
dict.RegisterEnums("ImLoginEventTypeOptions", "IM登录事件选项", ImLoginEventTypeOptions)
dict.RegisterEnums("ImClientPlatformOptions", "IM客户端平台选项", ImClientPlatformOptions)
dict.RegisterEnums("ImLoginFailureTypeOptions", "IM登录失败类型选项", ImLoginFailureTypeOptions)
}
@@ -0,0 +1,35 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImExternalServiceOptions", "IM外部服务选项", ImExternalServiceOptions)
dict.RegisterEnums("ImExternalConfigStatusOptions", "IM外部配置状态选项", ImExternalConfigStatusOptions)
}
const (
ImExternalServiceSms = "sms"
ImExternalServicePush = "push"
ImExternalServiceTurn = "turn"
ImExternalServiceStorage = "storage"
ImExternalServiceModeration = "moderation"
ImExternalConfigStatusConfigured = "configured"
ImExternalConfigStatusUnconfigured = "unconfigured"
)
var ImExternalServiceOptions = []*model.Option{
dict.GenSuccessOption(ImExternalServiceSms, "SMS"),
dict.GenPrimaryOption(ImExternalServicePush, "Push"),
dict.GenInfoOption(ImExternalServiceTurn, "TURN/STUN"),
dict.GenWarningOption(ImExternalServiceStorage, "对象存储"),
dict.GenDefaultOption(ImExternalServiceModeration, "审核插件"),
}
var ImExternalConfigStatusOptions = []*model.Option{
dict.GenSuccessOption(ImExternalConfigStatusConfigured, "已配置"),
dict.GenWarningOption(ImExternalConfigStatusUnconfigured, "未配置"),
}
+52
View File
@@ -0,0 +1,52 @@
// Package consts
// @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 consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImFileStatusOptions", "IM文件状态选项", ImFileStatusOptions)
dict.RegisterEnums("ImFilePurposeOptions", "IM文件用途选项", ImFilePurposeOptions)
}
const (
ImFileStatusUploading = 0
ImFileStatusCompleted = 1
ImFileStatusFailed = 2
ImFileStatusDeleted = 3
ImFileStatusQuarantined = -1
ImFilePurposeUnknown = "unknown"
ImFilePurposeChat = "chat"
ImFilePurposeUserAvatar = "user_avatar"
ImFilePurposeGroupAvatar = "group_avatar"
ImFilePurposeGroupFile = "group_file"
ImFilePurposeAbuseEvidence = "abuse_evidence"
ImFilePurposeFeedback = "feedback"
ImFilePurposeOrphan = "orphan"
)
var ImFileStatusOptions = []*model.Option{
dict.GenWarningOption(ImFileStatusUploading, "上传中"),
dict.GenSuccessOption(ImFileStatusCompleted, "已完成"),
dict.GenErrorOption(ImFileStatusFailed, "失败"),
dict.GenDefaultOption(ImFileStatusDeleted, "已删除"),
dict.GenErrorOption(ImFileStatusQuarantined, "已隔离"),
}
var ImFilePurposeOptions = []*model.Option{
dict.GenDefaultOption(ImFilePurposeUnknown, "未知"),
dict.GenSuccessOption(ImFilePurposeChat, "聊天附件"),
dict.GenInfoOption(ImFilePurposeUserAvatar, "用户头像"),
dict.GenInfoOption(ImFilePurposeGroupAvatar, "群头像"),
dict.GenPrimaryOption(ImFilePurposeGroupFile, "群文件"),
dict.GenWarningOption(ImFilePurposeAbuseEvidence, "举报证据"),
dict.GenWarningOption(ImFilePurposeFeedback, "反馈附件"),
dict.GenDefaultOption(ImFilePurposeOrphan, "孤儿文件"),
}
+20
View File
@@ -0,0 +1,20 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImFilePolicySwitchOptions", "IM文件策略开关选项", ImFilePolicySwitchOptions)
}
const (
ImFilePolicySwitchOff = 0
ImFilePolicySwitchOn = 1
)
var ImFilePolicySwitchOptions = []*model.Option{
dict.GenWarningOption(ImFilePolicySwitchOff, "关闭"),
dict.GenSuccessOption(ImFilePolicySwitchOn, "开启"),
}
+22
View File
@@ -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, "音频"),
}
+4
View File
@@ -38,6 +38,7 @@ const (
ImAdminCommandGroupMemberUnmute = "group.member.unmute" ImAdminCommandGroupMemberUnmute = "group.member.unmute"
ImAdminCommandGroupMemberRemove = "group.member.remove" ImAdminCommandGroupMemberRemove = "group.member.remove"
ImAdminCommandGroupOwnerTransfer = "group.owner.transfer" ImAdminCommandGroupOwnerTransfer = "group.owner.transfer"
ImAdminCommandGroupDissolve = "group.dissolve"
ImAdminCommandUserBan = "user.ban" ImAdminCommandUserBan = "user.ban"
ImAdminCommandUserUnban = "user.unban" ImAdminCommandUserUnban = "user.unban"
ImAdminCommandUserFreeze = "user.freeze" ImAdminCommandUserFreeze = "user.freeze"
@@ -46,6 +47,9 @@ const (
ImAdminCommandUserLogoutAll = "user.logout_all" ImAdminCommandUserLogoutAll = "user.logout_all"
ImAdminCommandUserCancel = "user.account.cancel" ImAdminCommandUserCancel = "user.account.cancel"
ImAdminCommandUserDelete = "user.account.delete" ImAdminCommandUserDelete = "user.account.delete"
ImAdminCommandUserPasswordReset = "user.password.reset"
ImAdminCommandMessageHide = "message.hide"
ImAdminCommandMessageDelete = "message.delete"
ImAdminCommandKafkaDeadReplay = "kafka.dead_letter.replay" ImAdminCommandKafkaDeadReplay = "kafka.dead_letter.replay"
ImAdminCommandKafkaDeadIgnore = "kafka.dead_letter.ignore" ImAdminCommandKafkaDeadIgnore = "kafka.dead_letter.ignore"
ImAdminCommandKafkaDeadResolve = "kafka.dead_letter.resolve" ImAdminCommandKafkaDeadResolve = "kafka.dead_letter.resolve"
+39
View File
@@ -0,0 +1,39 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImNotificationSceneOptions", "IM通知场景选项", ImNotificationSceneOptions)
dict.RegisterEnums("ImNotificationTemplateStatusOptions", "IM通知模板状态选项", ImNotificationTemplateStatusOptions)
}
const (
ImNotificationSceneAccountSecurity = "account_security"
ImNotificationSceneFriendRequest = "friend_request"
ImNotificationSceneGroupEvent = "group_event"
ImNotificationSceneFileEvent = "file_event"
ImNotificationSceneMeetingReminder = "meeting_reminder"
ImNotificationSceneReportReminder = "report_reminder"
ImNotificationSceneWorkOrder = "work_order"
ImNotificationTemplateEnabled = 1
ImNotificationTemplateDisabled = 0
)
var ImNotificationSceneOptions = []*model.Option{
dict.GenPrimaryOption(ImNotificationSceneAccountSecurity, "账号安全"),
dict.GenInfoOption(ImNotificationSceneFriendRequest, "好友申请"),
dict.GenInfoOption(ImNotificationSceneGroupEvent, "群事件"),
dict.GenDefaultOption(ImNotificationSceneFileEvent, "文件事件"),
dict.GenWarningOption(ImNotificationSceneMeetingReminder, "会议提醒"),
dict.GenWarningOption(ImNotificationSceneReportReminder, "日报月报提醒"),
dict.GenErrorOption(ImNotificationSceneWorkOrder, "工单处理"),
}
var ImNotificationTemplateStatusOptions = []*model.Option{
dict.GenSuccessOption(ImNotificationTemplateEnabled, "启用"),
dict.GenWarningOption(ImNotificationTemplateDisabled, "禁用"),
}
+38
View File
@@ -0,0 +1,38 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImOrgDepartmentStateOptions", "IM组织部门状态选项", ImOrgDepartmentStateOptions)
dict.RegisterEnums("ImOrgEmploymentStateOptions", "IM组织成员在职状态选项", ImOrgEmploymentStateOptions)
dict.RegisterEnums("ImOrgContactableOptions", "IM组织成员可联系选项", ImOrgContactableOptions)
}
const (
ImOrgDepartmentStateActive = "active"
ImOrgDepartmentStateDisabled = "disabled"
ImOrgEmploymentStateActive = "active"
ImOrgEmploymentStateLeft = "left"
ImOrgContactableYes = 1
ImOrgContactableNo = 0
)
var ImOrgDepartmentStateOptions = []*model.Option{
dict.GenSuccessOption(ImOrgDepartmentStateActive, "启用"),
dict.GenWarningOption(ImOrgDepartmentStateDisabled, "停用"),
}
var ImOrgEmploymentStateOptions = []*model.Option{
dict.GenSuccessOption(ImOrgEmploymentStateActive, "在职"),
dict.GenDefaultOption(ImOrgEmploymentStateLeft, "离职"),
}
var ImOrgContactableOptions = []*model.Option{
dict.GenSuccessOption(ImOrgContactableYes, "可联系"),
dict.GenWarningOption(ImOrgContactableNo, "不可联系"),
}
+38
View File
@@ -0,0 +1,38 @@
// Package consts
// @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 consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImOutboxSourceOptions", "IM Outbox来源选项", ImOutboxSourceOptions)
dict.RegisterEnums("ImOutboxStatusOptions", "IM Outbox状态选项", ImOutboxStatusOptions)
}
const (
ImOutboxSourceBusiness = "business"
ImOutboxSourceGroup = "group"
ImOutboxStatusPending = "pending"
ImOutboxStatusDelivered = "delivered"
ImOutboxStatusFailed = "failed"
ImOutboxStatusDead = "dead"
)
var ImOutboxSourceOptions = []*model.Option{
dict.GenPrimaryOption(ImOutboxSourceBusiness, "业务事件"),
dict.GenInfoOption(ImOutboxSourceGroup, "群事件"),
}
var ImOutboxStatusOptions = []*model.Option{
dict.GenWarningOption(ImOutboxStatusPending, "待投递"),
dict.GenSuccessOption(ImOutboxStatusDelivered, "已投递"),
dict.GenErrorOption(ImOutboxStatusFailed, "失败"),
dict.GenDefaultOption(ImOutboxStatusDead, "死信"),
}
+118
View File
@@ -0,0 +1,118 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
const (
ImPolicyActionAllow = "allow"
ImPolicyActionDeny = "deny"
ImPolicyActionChallenge = "challenge"
ImPolicyActionAuditOnly = "audit_only"
ImSecurityActionBlock = "block"
ImSensitiveMatchExact = "exact"
ImSensitiveMatchContains = "contains"
ImSensitiveMatchRegex = "regex"
ImSensitiveMatchPinyin = "pinyin"
ImSensitiveActionReplace = "replace"
ImSensitiveActionBlock = "block"
ImSensitiveActionAuditOnly = "audit_only"
ImSensitiveActionManualReview = "manual_review"
ImRiskLevelLow = "low"
ImRiskLevelMedium = "medium"
ImRiskLevelHigh = "high"
ImSecurityPublishDraft = "draft"
ImSecurityPublishPublished = "published"
ImSecurityPublishArchived = "archived"
ImSecurityPublishFailed = "failed"
ImCurfewDateDaily = "daily"
ImCurfewDateWeekday = "weekday"
ImCurfewDateWeekend = "weekend"
ImCurfewDateCustom = "custom"
ImCurfewScopePrivate = "private_message"
ImCurfewScopeGroup = "group_message"
)
var (
ImPolicyActionOptions = []*model.Option{
dict.GenSuccessOption(ImPolicyActionAllow, "放行"),
dict.GenErrorOption(ImPolicyActionDeny, "拦截"),
dict.GenWarningOption(ImPolicyActionChallenge, "二次验证"),
dict.GenInfoOption(ImPolicyActionAuditOnly, "仅审计"),
}
ImSecurityActionOptions = []*model.Option{
dict.GenSuccessOption(ImPolicyActionAllow, "放行"),
dict.GenWarningOption(ImPolicyActionChallenge, "二次验证"),
dict.GenErrorOption(ImSecurityActionBlock, "阻断"),
dict.GenInfoOption(ImPolicyActionAuditOnly, "仅审计"),
}
ImSensitiveMatchTypeOptions = []*model.Option{
dict.GenPrimaryOption(ImSensitiveMatchExact, "精确匹配"),
dict.GenDefaultOption(ImSensitiveMatchContains, "包含匹配"),
dict.GenWarningOption(ImSensitiveMatchRegex, "正则匹配"),
dict.GenInfoOption(ImSensitiveMatchPinyin, "拼音匹配"),
}
ImSensitiveActionOptions = []*model.Option{
dict.GenWarningOption(ImSensitiveActionReplace, "替换"),
dict.GenErrorOption(ImSensitiveActionBlock, "拦截"),
dict.GenInfoOption(ImSensitiveActionAuditOnly, "仅审计"),
dict.GenPrimaryOption(ImSensitiveActionManualReview, "人工审核"),
}
ImRiskLevelOptions = []*model.Option{
dict.GenSuccessOption(ImRiskLevelLow, "低风险"),
dict.GenWarningOption(ImRiskLevelMedium, "中风险"),
dict.GenErrorOption(ImRiskLevelHigh, "高风险"),
}
ImSecurityPublishStatusOptions = []*model.Option{
dict.GenWarningOption(ImSecurityPublishDraft, "草稿"),
dict.GenSuccessOption(ImSecurityPublishPublished, "已发布"),
dict.GenDefaultOption(ImSecurityPublishArchived, "已归档"),
dict.GenErrorOption(ImSecurityPublishFailed, "发布失败"),
}
ImCurfewDateTypeOptions = []*model.Option{
dict.GenDefaultOption(ImCurfewDateDaily, "每天"),
dict.GenPrimaryOption(ImCurfewDateWeekday, "工作日"),
dict.GenInfoOption(ImCurfewDateWeekend, "周末"),
dict.GenWarningOption(ImCurfewDateCustom, "自定义日期"),
}
ImCurfewScopeOptions = []*model.Option{
dict.GenErrorOption(ImCurfewScopePrivate, "单聊/私聊"),
dict.GenWarningOption(ImCurfewScopeGroup, "群聊"),
}
ImSensitiveSceneOptions = []*model.Option{
dict.GenPrimaryOption("private_message", "单聊消息"),
dict.GenPrimaryOption("group_message", "群聊消息"),
dict.GenInfoOption("user_profile", "用户资料"),
dict.GenInfoOption("group_profile", "群资料"),
dict.GenWarningOption("feedback", "用户反馈"),
dict.GenWarningOption("report", "举报内容"),
}
ImFriendEntryOptions = []*model.Option{
dict.GenDefaultOption("search", "用户搜索"),
dict.GenDefaultOption("profile_card", "个人名片"),
dict.GenDefaultOption("group_member", "群成员"),
dict.GenDefaultOption("qrcode", "二维码"),
dict.GenDefaultOption("invite_auto_add", "邀请注册自动添加"),
dict.GenDefaultOption("recommendation", "推荐联系人"),
}
)
func init() {
dict.RegisterEnums("ImPolicyActionOptions", "IM策略动作", ImPolicyActionOptions)
dict.RegisterEnums("ImSecurityActionOptions", "IM安全策略动作", ImSecurityActionOptions)
dict.RegisterEnums("ImSensitiveMatchTypeOptions", "IM敏感词匹配类型", ImSensitiveMatchTypeOptions)
dict.RegisterEnums("ImSensitiveActionOptions", "IM敏感词动作", ImSensitiveActionOptions)
dict.RegisterEnums("ImRiskLevelOptions", "IM风险等级", ImRiskLevelOptions)
dict.RegisterEnums("ImSecurityPublishStatusOptions", "IM安全策略发布状态", ImSecurityPublishStatusOptions)
dict.RegisterEnums("ImCurfewDateTypeOptions", "IM宵禁日期类型", ImCurfewDateTypeOptions)
dict.RegisterEnums("ImCurfewScopeOptions", "IM宵禁范围", ImCurfewScopeOptions)
dict.RegisterEnums("ImSensitiveSceneOptions", "IM敏感词场景", ImSensitiveSceneOptions)
dict.RegisterEnums("ImFriendEntryOptions", "IM好友入口", ImFriendEntryOptions)
}
@@ -0,0 +1,52 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImAppPlatformOptions", "IM客户端平台选项", ImAppPlatformOptions)
dict.RegisterEnums("ImAppUpdateTypeOptions", "IM客户端更新类型选项", ImAppUpdateTypeOptions)
dict.RegisterEnums("ImAbuseTargetTypeOptions", "IM举报对象类型选项", ImAbuseTargetTypeOptions)
dict.RegisterEnums("ImAbuseReasonEnabledOptions", "IM举报原因启用状态", ImAbuseReasonEnabledOptions)
}
const (
ImAppPlatformIos = "ios"
ImAppPlatformAndroid = "android"
)
const (
ImAppUpdateTypeNone = "none"
ImAppUpdateTypeRecommended = "recommended"
ImAppUpdateTypeForce = "force"
)
const (
ImAbuseTargetTypeUser = "user"
ImAbuseTargetTypeGroup = "group"
ImAbuseTargetTypeMessage = "message"
)
var ImAppPlatformOptions = []*model.Option{
dict.GenPrimaryOption(ImAppPlatformIos, "iOS"),
dict.GenSuccessOption(ImAppPlatformAndroid, "Android"),
}
var ImAppUpdateTypeOptions = []*model.Option{
dict.GenDefaultOption(ImAppUpdateTypeNone, "无需更新"),
dict.GenWarningOption(ImAppUpdateTypeRecommended, "建议更新"),
dict.GenErrorOption(ImAppUpdateTypeForce, "强制更新"),
}
var ImAbuseTargetTypeOptions = []*model.Option{
dict.GenPrimaryOption(ImAbuseTargetTypeUser, "用户"),
dict.GenSuccessOption(ImAbuseTargetTypeGroup, "群组"),
dict.GenWarningOption(ImAbuseTargetTypeMessage, "消息"),
}
var ImAbuseReasonEnabledOptions = []*model.Option{
dict.GenWarningOption(0, "禁用"),
dict.GenSuccessOption(1, "启用"),
}
@@ -0,0 +1,22 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImTaskDashboardStatusOptions", "IM任务看板状态选项", ImTaskDashboardStatusOptions)
}
const (
ImTaskDashboardStatusHealthy = "healthy"
ImTaskDashboardStatusWarning = "warning"
ImTaskDashboardStatusError = "error"
)
var ImTaskDashboardStatusOptions = []*model.Option{
dict.GenSuccessOption(ImTaskDashboardStatusHealthy, "正常"),
dict.GenWarningOption(ImTaskDashboardStatusWarning, "待处理"),
dict.GenErrorOption(ImTaskDashboardStatusError, "异常"),
}
+25 -3
View File
@@ -12,6 +12,22 @@ import (
func init() { func init() {
dict.RegisterEnums("ImUserStateOptions", "IM用户状态选项", ImUserStateOptions) dict.RegisterEnums("ImUserStateOptions", "IM用户状态选项", ImUserStateOptions)
dict.RegisterEnums("ImUserListStateOptions", "IM用户列表状态选项", ImUserListStateOptions)
dict.RegisterEnums("ImUserRegisterTypeOptions", "IM用户注册方式选项", ImUserRegisterTypeOptions)
}
const (
ImUserRegisterTypeAdmin = "admin"
ImUserRegisterTypeMobile = "mobile"
ImUserRegisterTypeInviteCode = "invite_code"
ImLoginChannelApp = "app"
ImLoginChannelWeb = "web"
)
var ImUserRegisterTypeOptions = []*model.Option{
dict.GenPrimaryOption(ImUserRegisterTypeAdmin, "后台创建"),
dict.GenSuccessOption(ImUserRegisterTypeMobile, "手机号注册"),
dict.GenInfoOption(ImUserRegisterTypeInviteCode, "邀请码注册"),
} }
const ( const (
@@ -22,8 +38,14 @@ const (
) )
var ImUserStateOptions = []*model.Option{ var ImUserStateOptions = []*model.Option{
dict.GenSuccessOption(ImUserStateOk, "正常"), dict.GenSuccessOption(ImUserStateOk, "启用"),
dict.GenErrorOption(ImUserStateSuspended, "已停用"), dict.GenErrorOption(ImUserStateSuspended, "封禁"),
dict.GenDefaultOption(ImUserStateDeleted, "已删除"), dict.GenDefaultOption(ImUserStateDeleted, "已注销"),
dict.GenWarningOption(ImUserStateUndefined, "未定义"), dict.GenWarningOption(ImUserStateUndefined, "未定义"),
} }
var ImUserListStateOptions = []*model.Option{
dict.GenSuccessOption(ImUserStateOk, "启用"),
dict.GenErrorOption(ImUserStateSuspended, "封禁"),
dict.GenDefaultOption(ImUserStateDeleted, "已注销"),
}
+50
View File
@@ -0,0 +1,50 @@
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("ImWorkbenchAppEnabledOptions", "IM工作台模块启用状态", ImWorkbenchAppEnabledOptions)
dict.RegisterEnums("ImWorkbenchPermissionOptions", "IM工作台模块权限策略", ImWorkbenchPermissionOptions)
dict.RegisterEnums("ImWorkbenchRouteOptions", "IM工作台模块路由选项", ImWorkbenchRouteOptions)
}
const (
ImWorkbenchAppDisabled = 0
ImWorkbenchAppEnabled = 1
)
const (
ImWorkbenchPermissionEnabled = "enabled"
ImWorkbenchPermissionRoleRequired = "role_required"
ImWorkbenchPermissionDisabled = "disabled"
)
const (
ImWorkbenchRouteOrganization = "/organization"
ImWorkbenchRouteMeetings = "/meetings"
ImWorkbenchRouteDailyReports = "/reports/daily"
ImWorkbenchRouteMonthlyReports = "/reports/monthly"
ImWorkbenchRouteExecutiveMailbox = "/executive-mailbox"
)
var ImWorkbenchAppEnabledOptions = []*model.Option{
dict.GenWarningOption(ImWorkbenchAppDisabled, "禁用"),
dict.GenSuccessOption(ImWorkbenchAppEnabled, "启用"),
}
var ImWorkbenchPermissionOptions = []*model.Option{
dict.GenSuccessOption(ImWorkbenchPermissionEnabled, "全部可见"),
dict.GenPrimaryOption(ImWorkbenchPermissionRoleRequired, "按角色权限"),
dict.GenWarningOption(ImWorkbenchPermissionDisabled, "不可见"),
}
var ImWorkbenchRouteOptions = []*model.Option{
dict.GenPrimaryOption(ImWorkbenchRouteOrganization, "组织架构"),
dict.GenPrimaryOption(ImWorkbenchRouteMeetings, "会议通知"),
dict.GenPrimaryOption(ImWorkbenchRouteDailyReports, "日报"),
dict.GenPrimaryOption(ImWorkbenchRouteMonthlyReports, "月报"),
dict.GenPrimaryOption(ImWorkbenchRouteExecutiveMailbox, "总经理信箱"),
}
@@ -0,0 +1,36 @@
package sys
import (
"context"
"hotgo/api/admin/imabusereason"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImAbuseReason = cImAbuseReason{}
type cImAbuseReason struct{}
func (c *cImAbuseReason) List(ctx context.Context, req *imabusereason.ListReq) (res *imabusereason.ListRes, err error) {
list, totalCount, err := service.SysImAbuseReason().List(ctx, &req.ImAbuseReasonListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImAbuseReasonModel{}
}
res = new(imabusereason.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImAbuseReason) Edit(ctx context.Context, req *imabusereason.EditReq) (res *imabusereason.EditRes, err error) {
err = service.SysImAbuseReason().Edit(ctx, &req.ImAbuseReasonEditInp)
return
}
func (c *cImAbuseReason) Delete(ctx context.Context, req *imabusereason.DeleteReq) (res *imabusereason.DeleteRes, err error) {
err = service.SysImAbuseReason().Delete(ctx, &req.ImAbuseReasonDeleteInp)
return
}
@@ -0,0 +1,31 @@
package sys
import (
"context"
"hotgo/api/admin/imapprelease"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImAppRelease = cImAppRelease{}
type cImAppRelease struct{}
func (c *cImAppRelease) List(ctx context.Context, req *imapprelease.ListReq) (res *imapprelease.ListRes, err error) {
list, totalCount, err := service.SysImAppRelease().List(ctx, &req.ImAppReleaseListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImAppReleaseModel{}
}
res = new(imapprelease.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImAppRelease) Edit(ctx context.Context, req *imapprelease.EditReq) (res *imapprelease.EditRes, err error) {
err = service.SysImAppRelease().Edit(ctx, &req.ImAppReleaseEditInp)
return
}
@@ -31,6 +31,12 @@ func (c *cImAuditLog) List(ctx context.Context, req *imauditlog.ListReq) (res *i
return return
} }
// Export 导出IM审计日志列表
func (c *cImAuditLog) Export(ctx context.Context, req *imauditlog.ExportReq) (res *imauditlog.ExportRes, err error) {
err = service.SysImAuditLog().Export(ctx, &req.ImAuditLogListInp)
return
}
// View 获取指定IM审计日志信息 // View 获取指定IM审计日志信息
func (c *cImAuditLog) View(ctx context.Context, req *imauditlog.ViewReq) (res *imauditlog.ViewRes, err error) { func (c *cImAuditLog) View(ctx context.Context, req *imauditlog.ViewReq) (res *imauditlog.ViewRes, err error) {
data, err := service.SysImAuditLog().View(ctx, &req.ImAuditLogViewInp) data, err := service.SysImAuditLog().View(ctx, &req.ImAuditLogViewInp)
@@ -0,0 +1,31 @@
package sys
import (
"context"
"hotgo/api/admin/imdashboard"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImDashboard = cImDashboard{}
type cImDashboard struct{}
func (c *cImDashboard) Overview(ctx context.Context, req *imdashboard.OverviewReq) (res *imdashboard.OverviewRes, err error) {
data, err := service.SysImDashboard().Overview(ctx, &req.ImDashboardOverviewInp)
if err != nil {
return nil, err
}
return &imdashboard.OverviewRes{ImDashboardOverviewModel: data}, nil
}
func (c *cImDashboard) Trends(ctx context.Context, req *imdashboard.TrendsReq) (res *imdashboard.TrendsRes, err error) {
list, err := service.SysImDashboard().Trends(ctx, &req.ImDashboardTrendsInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImDashboardTrendModel{}
}
return &imdashboard.TrendsRes{List: list}, nil
}
@@ -0,0 +1,46 @@
package sys
import (
"context"
"hotgo/api/admin/imexternalconfig"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImExternalConfig = cImExternalConfig{}
type cImExternalConfig struct{}
func (c *cImExternalConfig) List(ctx context.Context, req *imexternalconfig.ListReq) (res *imexternalconfig.ListRes, err error) {
list, totalCount, err := service.SysImExternalConfig().List(ctx, &req.ImExternalConfigListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImExternalConfigListModel{}
}
res = new(imexternalconfig.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImExternalConfig) View(ctx context.Context, req *imexternalconfig.ViewReq) (res *imexternalconfig.ViewRes, err error) {
data, err := service.SysImExternalConfig().View(ctx, &req.ImExternalConfigViewInp)
if err != nil {
return
}
res = new(imexternalconfig.ViewRes)
res.ImExternalConfigViewModel = data
return
}
func (c *cImExternalConfig) Update(ctx context.Context, req *imexternalconfig.UpdateReq) (res *imexternalconfig.UpdateRes, err error) {
err = service.SysImExternalConfig().Update(ctx, &req.ImExternalConfigUpdateInp)
return
}
func (c *cImExternalConfig) Check(ctx context.Context, req *imexternalconfig.CheckReq) (res *imexternalconfig.CheckRes, err error) {
err = service.SysImExternalConfig().Check(ctx, &req.ImExternalConfigCheckInp)
return
}
@@ -0,0 +1,25 @@
package sys
import (
"context"
"hotgo/api/admin/imfeedbackconfig"
"hotgo/internal/service"
)
var ImFeedbackConfig = cImFeedbackConfig{}
type cImFeedbackConfig struct{}
func (c *cImFeedbackConfig) Get(ctx context.Context, req *imfeedbackconfig.GetReq) (res *imfeedbackconfig.GetRes, err error) {
data, err := service.SysImFeedbackConfig().Get(ctx, &req.ImFeedbackConfigGetInp)
if err != nil {
return
}
res = &imfeedbackconfig.GetRes{ImFeedbackConfigModel: data}
return
}
func (c *cImFeedbackConfig) Update(ctx context.Context, req *imfeedbackconfig.UpdateReq) (res *imfeedbackconfig.UpdateRes, err error) {
err = service.SysImFeedbackConfig().Update(ctx, &req.ImFeedbackConfigUpdateInp)
return
}
@@ -0,0 +1,68 @@
// Package sys
// @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 sys
import (
"context"
"hotgo/api/admin/imfile"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var (
ImFile = cImFile{}
)
type cImFile struct{}
// List 查看IM文件列表
func (c *cImFile) List(ctx context.Context, req *imfile.ListReq) (res *imfile.ListRes, err error) {
list, totalCount, err := service.SysImFile().List(ctx, &req.ImFileListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImFileListModel{}
}
res = new(imfile.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
// View 获取IM文件详情
func (c *cImFile) View(ctx context.Context, req *imfile.ViewReq) (res *imfile.ViewRes, err error) {
data, err := service.SysImFile().View(ctx, &req.ImFileViewInp)
if err != nil {
return
}
res = new(imfile.ViewRes)
res.ImFileViewModel = data
return
}
// Quarantine 隔离IM文件
func (c *cImFile) Quarantine(ctx context.Context, req *imfile.QuarantineReq) (res *imfile.QuarantineRes, err error) {
err = service.SysImFile().Quarantine(ctx, &req.ImFileActionInp)
return
}
// Delete 删除IM文件
func (c *cImFile) Delete(ctx context.Context, req *imfile.DeleteReq) (res *imfile.DeleteRes, err error) {
err = service.SysImFile().Delete(ctx, &req.ImFileActionInp)
return
}
// CleanupOrphan 清理IM孤儿文件
func (c *cImFile) CleanupOrphan(ctx context.Context, req *imfile.CleanupOrphanReq) (res *imfile.CleanupOrphanRes, err error) {
data, err := service.SysImFile().CleanupOrphan(ctx, &req.ImFileCleanupOrphanInp)
if err != nil {
return
}
res = new(imfile.CleanupOrphanRes)
res.ImFileCleanupOrphanModel = data
return
}
@@ -0,0 +1,31 @@
package sys
import (
"context"
"hotgo/api/admin/imfilepolicy"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImFilePolicy = cImFilePolicy{}
type cImFilePolicy struct{}
func (c *cImFilePolicy) List(ctx context.Context, req *imfilepolicy.ListReq) (res *imfilepolicy.ListRes, err error) {
list, totalCount, err := service.SysImFilePolicy().List(ctx, &req.ImFilePolicyListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImFilePolicyListModel{}
}
res = new(imfilepolicy.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImFilePolicy) Update(ctx context.Context, req *imfilepolicy.UpdateReq) (res *imfilepolicy.UpdateRes, err error) {
err = service.SysImFilePolicy().Update(ctx, &req.ImFilePolicyUpdateInp)
return
}
@@ -75,6 +75,150 @@ func (c *cImGroup) Governance(ctx context.Context, req *imgroup.GovernanceReq) (
return return
} }
// GovernanceUpdate 更新IM群治理配置
func (c *cImGroup) GovernanceUpdate(ctx context.Context, req *imgroup.GovernanceUpdateReq) (res *imgroup.GovernanceUpdateRes, err error) {
data, err := service.SysImGroup().GovernanceUpdate(ctx, &req.ImGroupGovernanceUpdateInp)
if err != nil {
return
}
res = new(imgroup.GovernanceUpdateRes)
res.ImGroupCommandModel = data
return
}
// ProfileUpdate 修改IM群资料
func (c *cImGroup) ProfileUpdate(ctx context.Context, req *imgroup.ProfileUpdateReq) (res *imgroup.ProfileUpdateRes, err error) {
data, err := service.SysImGroup().ProfileUpdate(ctx, &req.ImGroupProfileUpdateInp)
if err != nil {
return
}
res = new(imgroup.ProfileUpdateRes)
res.ImGroupCommandModel = data
return
}
// Dissolve 解散IM群
func (c *cImGroup) Dissolve(ctx context.Context, req *imgroup.DissolveReq) (res *imgroup.DissolveRes, err error) {
data, err := service.SysImGroup().Dissolve(ctx, &req.ImGroupDissolveInp)
if err != nil {
return
}
res = new(imgroup.DissolveRes)
res.ImGroupCommandModel = data
return
}
// Mute 禁言IM群成员
func (c *cImGroup) Mute(ctx context.Context, req *imgroup.MuteReq) (res *imgroup.MuteRes, err error) {
data, err := service.SysImGroup().Mute(ctx, &req.ImGroupMuteInp)
if err != nil {
return
}
res = new(imgroup.MuteRes)
res.ImGroupCommandModel = data
return
}
// MemberMute Kafka禁言IM群成员
func (c *cImGroup) MemberMute(ctx context.Context, req *imgroup.MemberMuteReq) (res *imgroup.MemberMuteRes, err error) {
data, err := service.SysImGroup().MemberMute(ctx, &req.ImGroupMuteInp)
if err != nil {
return
}
res = new(imgroup.MemberMuteRes)
res.ImGroupCommandModel = data
return
}
// Unmute 解除IM群成员禁言
func (c *cImGroup) Unmute(ctx context.Context, req *imgroup.UnmuteReq) (res *imgroup.UnmuteRes, err error) {
data, err := service.SysImGroup().Unmute(ctx, &req.ImGroupUnmuteInp)
if err != nil {
return
}
res = new(imgroup.UnmuteRes)
res.ImGroupCommandModel = data
return
}
// MemberUnmute Kafka解除IM群成员禁言
func (c *cImGroup) MemberUnmute(ctx context.Context, req *imgroup.MemberUnmuteReq) (res *imgroup.MemberUnmuteRes, err error) {
data, err := service.SysImGroup().MemberUnmute(ctx, &req.ImGroupUnmuteInp)
if err != nil {
return
}
res = new(imgroup.MemberUnmuteRes)
res.ImGroupCommandModel = data
return
}
// MemberRemove 移出IM群成员
func (c *cImGroup) MemberRemove(ctx context.Context, req *imgroup.MemberRemoveReq) (res *imgroup.MemberRemoveRes, err error) {
data, err := service.SysImGroup().MemberRemove(ctx, &req.ImGroupMemberRemoveInp)
if err != nil {
return
}
res = new(imgroup.MemberRemoveRes)
res.ImGroupCommandModel = data
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) {
data, err := service.SysImGroup().RoleUpdate(ctx, &req.ImGroupRoleUpdateInp)
if err != nil {
return
}
res = new(imgroup.RoleUpdateRes)
res.ImGroupCommandModel = data
return
}
// AdminSet 设置IM群管理员
func (c *cImGroup) AdminSet(ctx context.Context, req *imgroup.AdminSetReq) (res *imgroup.AdminSetRes, err error) {
data, err := service.SysImGroup().AdminSet(ctx, &req.ImGroupRoleUpdateInp)
if err != nil {
return
}
res = new(imgroup.AdminSetRes)
res.ImGroupCommandModel = data
return
}
// AdminRemove 删除IM群管理员
func (c *cImGroup) AdminRemove(ctx context.Context, req *imgroup.AdminRemoveReq) (res *imgroup.AdminRemoveRes, err error) {
data, err := service.SysImGroup().AdminRemove(ctx, &req.ImGroupRoleUpdateInp)
if err != nil {
return
}
res = new(imgroup.AdminRemoveRes)
res.ImGroupCommandModel = data
return
}
// OwnerTransfer 转让IM群主
func (c *cImGroup) OwnerTransfer(ctx context.Context, req *imgroup.OwnerTransferReq) (res *imgroup.OwnerTransferRes, err error) {
data, err := service.SysImGroup().OwnerTransfer(ctx, &req.ImGroupOwnerTransferInp)
if err != nil {
return
}
res = new(imgroup.OwnerTransferRes)
res.ImGroupCommandModel = data
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)
@@ -90,6 +234,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)
@@ -0,0 +1,40 @@
package sys
import (
"context"
"hotgo/api/admin/imgroupmessage"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImGroupMessage = cImGroupMessage{}
type cImGroupMessage struct{}
func (c *cImGroupMessage) List(ctx context.Context, req *imgroupmessage.ListReq) (res *imgroupmessage.ListRes, err error) {
list, total, err := service.SysImGroupMessage().List(ctx, &req.ImGroupMessageListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImMessageListModel{}
}
res = &imgroupmessage.ListRes{List: list}
res.PageRes.Pack(req, total)
return
}
func (c *cImGroupMessage) View(ctx context.Context, req *imgroupmessage.ViewReq) (*imgroupmessage.ViewRes, error) {
data, err := service.SysImGroupMessage().View(ctx, &req.ImMessageViewInp)
return &imgroupmessage.ViewRes{ImMessageViewModel: data}, err
}
func (c *cImGroupMessage) Hide(ctx context.Context, req *imgroupmessage.HideReq) (*imgroupmessage.HideRes, error) {
data, err := service.SysImGroupMessage().Hide(ctx, &req.ImMessageActionInp)
return &imgroupmessage.HideRes{ImMessageCommandModel: data}, err
}
func (c *cImGroupMessage) Delete(ctx context.Context, req *imgroupmessage.DeleteReq) (*imgroupmessage.DeleteRes, error) {
data, err := service.SysImGroupMessage().Delete(ctx, &req.ImMessageActionInp)
return &imgroupmessage.DeleteRes{ImMessageCommandModel: data}, err
}
func (c *cImGroupMessage) Export(ctx context.Context, req *imgroupmessage.ExportReq) (*imgroupmessage.ExportRes, error) {
return &imgroupmessage.ExportRes{}, service.SysImGroupMessage().Export(ctx, &req.ImGroupMessageListInp)
}
@@ -60,6 +60,11 @@ func (c *cImInvite) Revoke(ctx context.Context, req *iminvite.RevokeReq) (res *i
return return
} }
func (c *cImInvite) BindUser(ctx context.Context, req *iminvite.BindUserReq) (res *iminvite.BindUserRes, err error) {
err = service.SysImInvite().BindUser(ctx, &req.ImInviteBindUserInp)
return
}
func (c *cImInvite) BatchRevoke(ctx context.Context, req *iminvite.BatchRevokeReq) (res *iminvite.BatchRevokeRes, err error) { func (c *cImInvite) BatchRevoke(ctx context.Context, req *iminvite.BatchRevokeReq) (res *iminvite.BatchRevokeRes, err error) {
data, err := service.SysImInvite().BatchRevoke(ctx, &req.ImInviteBatchRevokeInp) data, err := service.SysImInvite().BatchRevoke(ctx, &req.ImInviteBatchRevokeInp)
if err != nil { if err != nil {
@@ -0,0 +1,39 @@
package sys
import (
"context"
"hotgo/api/admin/imloginfailurelog"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImLoginFailureLog = cImLoginFailureLog{}
type cImLoginFailureLog struct{}
func (c *cImLoginFailureLog) List(ctx context.Context, req *imloginfailurelog.ListReq) (res *imloginfailurelog.ListRes, err error) {
list, total, err := service.SysImLoginFailureLog().List(ctx, &req.ImLoginFailureLogListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImLoginFailureLogModel{}
}
res = &imloginfailurelog.ListRes{List: list}
res.PageRes.Pack(req, total)
return
}
func (c *cImLoginFailureLog) View(ctx context.Context, req *imloginfailurelog.ViewReq) (*imloginfailurelog.ViewRes, error) {
data, err := service.SysImLoginFailureLog().View(ctx, &req.ImLoginFailureViewInp)
return &imloginfailurelog.ViewRes{ImLoginFailureLogModel: data}, err
}
func (c *cImLoginFailureLog) Export(ctx context.Context, req *imloginfailurelog.ExportReq) (*imloginfailurelog.ExportRes, error) {
return &imloginfailurelog.ExportRes{}, service.SysImLoginFailureLog().Export(ctx, &req.ImLoginFailureLogListInp)
}
func (c *cImLoginFailureLog) AddBlacklist(ctx context.Context, req *imloginfailurelog.AddBlacklistReq) (*imloginfailurelog.AddBlacklistRes, error) {
return &imloginfailurelog.AddBlacklistRes{}, service.SysImLoginFailureLog().AddBlacklist(ctx, &req.ImLoginFailureAddBlacklistInp)
}
func (c *cImLoginFailureLog) BanUser(ctx context.Context, req *imloginfailurelog.BanUserReq) (*imloginfailurelog.BanUserRes, error) {
data, err := service.SysImLoginFailureLog().BanUser(ctx, &req.ImLoginFailureBanUserInp)
return &imloginfailurelog.BanUserRes{ImUserCommandModel: data}, err
}
@@ -0,0 +1,25 @@
package sys
import (
"context"
"hotgo/api/admin/immailboxconfig"
"hotgo/internal/service"
)
var ImMailboxConfig = cImMailboxConfig{}
type cImMailboxConfig struct{}
func (c *cImMailboxConfig) Get(ctx context.Context, req *immailboxconfig.GetReq) (res *immailboxconfig.GetRes, err error) {
data, err := service.SysImMailboxConfig().Get(ctx, &req.ImMailboxConfigGetInp)
if err != nil {
return
}
res = &immailboxconfig.GetRes{ImMailboxConfigModel: data}
return
}
func (c *cImMailboxConfig) Update(ctx context.Context, req *immailboxconfig.UpdateReq) (res *immailboxconfig.UpdateRes, err error) {
err = service.SysImMailboxConfig().Update(ctx, &req.ImMailboxConfigUpdateInp)
return
}
@@ -0,0 +1,31 @@
package sys
import (
"context"
"hotgo/api/admin/imnotification"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImNotification = cImNotification{}
type cImNotification struct{}
func (c *cImNotification) TemplateList(ctx context.Context, req *imnotification.TemplateListReq) (res *imnotification.TemplateListRes, err error) {
list, totalCount, err := service.SysImNotification().TemplateList(ctx, &req.ImNotificationTemplateListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImNotificationTemplateListModel{}
}
res = new(imnotification.TemplateListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImNotification) TemplateUpdate(ctx context.Context, req *imnotification.TemplateUpdateReq) (res *imnotification.TemplateUpdateRes, err error) {
err = service.SysImNotification().TemplateUpdate(ctx, &req.ImNotificationTemplateUpdateInp)
return
}
@@ -0,0 +1,79 @@
package sys
import (
"context"
"hotgo/api/admin/imorg"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImOrg = cImOrg{}
type cImOrg struct{}
func (c *cImOrg) DepartmentList(ctx context.Context, req *imorg.DepartmentListReq) (res *imorg.DepartmentListRes, err error) {
list, totalCount, err := service.SysImOrg().DepartmentList(ctx, &req.ImOrgDepartmentListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImOrgDepartmentModel{}
}
res = new(imorg.DepartmentListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImOrg) DepartmentEdit(ctx context.Context, req *imorg.DepartmentEditReq) (res *imorg.DepartmentEditRes, err error) {
err = service.SysImOrg().DepartmentEdit(ctx, &req.ImOrgDepartmentEditInp)
return
}
func (c *cImOrg) DepartmentExport(ctx context.Context, req *imorg.DepartmentExportReq) (res *imorg.DepartmentExportRes, err error) {
err = service.SysImOrg().DepartmentExport(ctx, &req.ImOrgDepartmentListInp)
return
}
func (c *cImOrg) DepartmentDelete(ctx context.Context, req *imorg.DepartmentDeleteReq) (res *imorg.DepartmentDeleteRes, err error) {
err = service.SysImOrg().DepartmentDelete(ctx, &req.ImOrgDepartmentDeleteInp)
return
}
func (c *cImOrg) MemberList(ctx context.Context, req *imorg.MemberListReq) (res *imorg.MemberListRes, err error) {
list, totalCount, err := service.SysImOrg().MemberList(ctx, &req.ImOrgMemberListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImOrgMemberModel{}
}
res = new(imorg.MemberListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImOrg) MemberEdit(ctx context.Context, req *imorg.MemberEditReq) (res *imorg.MemberEditRes, err error) {
err = service.SysImOrg().MemberEdit(ctx, &req.ImOrgMemberEditInp)
return
}
func (c *cImOrg) MemberExport(ctx context.Context, req *imorg.MemberExportReq) (res *imorg.MemberExportRes, err error) {
err = service.SysImOrg().MemberExport(ctx, &req.ImOrgMemberListInp)
return
}
func (c *cImOrg) MemberDelete(ctx context.Context, req *imorg.MemberDeleteReq) (res *imorg.MemberDeleteRes, err error) {
err = service.SysImOrg().MemberDelete(ctx, &req.ImOrgMemberDeleteInp)
return
}
func (c *cImOrg) Search(ctx context.Context, req *imorg.SearchReq) (res *imorg.SearchRes, err error) {
data, err := service.SysImOrg().Search(ctx, &req.ImOrgSearchInp)
if err != nil {
return
}
res = &imorg.SearchRes{ImOrgSearchModel: data}
return
}
@@ -0,0 +1,55 @@
// Package sys
// @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 sys
import (
"context"
"hotgo/api/admin/imoutbox"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var (
ImOutbox = cImOutbox{}
)
type cImOutbox struct{}
func (c *cImOutbox) List(ctx context.Context, req *imoutbox.ListReq) (res *imoutbox.ListRes, err error) {
list, totalCount, err := service.SysImOutbox().List(ctx, &req.ImOutboxListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImOutboxListModel{}
}
res = new(imoutbox.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImOutbox) View(ctx context.Context, req *imoutbox.ViewReq) (res *imoutbox.ViewRes, err error) {
data, err := service.SysImOutbox().View(ctx, &req.ImOutboxViewInp)
if err != nil {
return
}
res = new(imoutbox.ViewRes)
res.ImOutboxViewModel = data
return
}
func (c *cImOutbox) Retry(ctx context.Context, req *imoutbox.RetryReq) (res *imoutbox.RetryRes, err error) {
err = service.SysImOutbox().Retry(ctx, &req.ImOutboxActionInp)
return
}
func (c *cImOutbox) Dead(ctx context.Context, req *imoutbox.DeadReq) (res *imoutbox.DeadRes, err error) {
err = service.SysImOutbox().Dead(ctx, &req.ImOutboxActionInp)
return
}
@@ -0,0 +1,40 @@
package sys
import (
"context"
"hotgo/api/admin/imprivatemessage"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImPrivateMessage = cImPrivateMessage{}
type cImPrivateMessage struct{}
func (c *cImPrivateMessage) List(ctx context.Context, req *imprivatemessage.ListReq) (res *imprivatemessage.ListRes, err error) {
list, total, err := service.SysImPrivateMessage().List(ctx, &req.ImPrivateMessageListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImMessageListModel{}
}
res = &imprivatemessage.ListRes{List: list}
res.PageRes.Pack(req, total)
return
}
func (c *cImPrivateMessage) View(ctx context.Context, req *imprivatemessage.ViewReq) (*imprivatemessage.ViewRes, error) {
data, err := service.SysImPrivateMessage().View(ctx, &req.ImMessageViewInp)
return &imprivatemessage.ViewRes{ImMessageViewModel: data}, err
}
func (c *cImPrivateMessage) Hide(ctx context.Context, req *imprivatemessage.HideReq) (*imprivatemessage.HideRes, error) {
data, err := service.SysImPrivateMessage().Hide(ctx, &req.ImMessageActionInp)
return &imprivatemessage.HideRes{ImMessageCommandModel: data}, err
}
func (c *cImPrivateMessage) Delete(ctx context.Context, req *imprivatemessage.DeleteReq) (*imprivatemessage.DeleteRes, error) {
data, err := service.SysImPrivateMessage().Delete(ctx, &req.ImMessageActionInp)
return &imprivatemessage.DeleteRes{ImMessageCommandModel: data}, err
}
func (c *cImPrivateMessage) Export(ctx context.Context, req *imprivatemessage.ExportReq) (*imprivatemessage.ExportRes, error) {
return &imprivatemessage.ExportRes{}, service.SysImPrivateMessage().Export(ctx, &req.ImPrivateMessageListInp)
}
@@ -0,0 +1,72 @@
package sys
import (
"context"
"hotgo/api/admin/imsecuritypolicy"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImSecurityPolicy = cImSecurityPolicy{}
type cImSecurityPolicy struct{}
func (c *cImSecurityPolicy) List(ctx context.Context, req *imsecuritypolicy.ListReq) (res *imsecuritypolicy.ListRes, err error) {
list, total, err := service.SysImSecurityPolicy().List(ctx, &req.ImSecurityPolicyListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImSecurityPolicyModel{}
}
res = new(imsecuritypolicy.ListRes)
res.List = list
res.PageRes.Pack(req, total)
return
}
func (c *cImSecurityPolicy) View(ctx context.Context, req *imsecuritypolicy.ViewReq) (res *imsecuritypolicy.ViewRes, err error) {
data, err := service.SysImSecurityPolicy().View(ctx, &req.ImSecurityPolicyViewInp)
if err != nil {
return nil, err
}
return &imsecuritypolicy.ViewRes{ImSecurityPolicyModel: data}, nil
}
func (c *cImSecurityPolicy) Save(ctx context.Context, req *imsecuritypolicy.SaveReq) (res *imsecuritypolicy.SaveRes, err error) {
id, err := service.SysImSecurityPolicy().Save(ctx, &req.ImSecurityPolicySaveInp)
if err != nil {
return nil, err
}
return &imsecuritypolicy.SaveRes{Id: id}, nil
}
func (c *cImSecurityPolicy) Publish(ctx context.Context, req *imsecuritypolicy.PublishReq) (res *imsecuritypolicy.PublishRes, err error) {
err = service.SysImSecurityPolicy().Publish(ctx, &req.ImSecurityPolicyPublishInp)
return
}
func (c *cImSecurityPolicy) RestoreDefault(ctx context.Context, req *imsecuritypolicy.RestoreDefaultReq) (res *imsecuritypolicy.RestoreDefaultRes, err error) {
id, err := service.SysImSecurityPolicy().RestoreDefault(ctx, &req.ImSecurityPolicyRestoreInp)
if err != nil {
return nil, err
}
return &imsecuritypolicy.RestoreDefaultRes{Id: id}, nil
}
func (c *cImSecurityPolicy) IPRuleList(ctx context.Context, req *imsecuritypolicy.IPRuleListReq) (res *imsecuritypolicy.IPRuleListRes, err error) {
list, total, err := service.SysImSecurityPolicy().IPRuleList(ctx, &req.ImSecurityIPRuleListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImSecurityIPRuleModel{}
}
res = new(imsecuritypolicy.IPRuleListRes)
res.List = list
res.PageRes.Pack(req, total)
return
}
func (c *cImSecurityPolicy) IPRuleEdit(ctx context.Context, req *imsecuritypolicy.IPRuleEditReq) (res *imsecuritypolicy.IPRuleEditRes, err error) {
err = service.SysImSecurityPolicy().IPRuleEdit(ctx, &req.ImSecurityIPRuleEditInp)
return
}
func (c *cImSecurityPolicy) IPRuleDelete(ctx context.Context, req *imsecuritypolicy.IPRuleDeleteReq) (res *imsecuritypolicy.IPRuleDeleteRes, err error) {
err = service.SysImSecurityPolicy().IPRuleDelete(ctx, &req.ImSecurityIPRuleDeleteInp)
return
}
@@ -0,0 +1,59 @@
package sys
import (
"context"
"hotgo/api/admin/imsensitiveword"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImSensitiveWord = cImSensitiveWord{}
type cImSensitiveWord struct{}
func (c *cImSensitiveWord) List(ctx context.Context, req *imsensitiveword.ListReq) (res *imsensitiveword.ListRes, err error) {
list, total, err := service.SysImSensitiveWord().List(ctx, &req.ImSensitiveWordListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImSensitiveWordListModel{}
}
res = new(imsensitiveword.ListRes)
res.List = list
res.PageRes.Pack(req, total)
return
}
func (c *cImSensitiveWord) Edit(ctx context.Context, req *imsensitiveword.EditReq) (res *imsensitiveword.EditRes, err error) {
err = service.SysImSensitiveWord().Edit(ctx, &req.ImSensitiveWordEditInp)
return
}
func (c *cImSensitiveWord) Delete(ctx context.Context, req *imsensitiveword.DeleteReq) (res *imsensitiveword.DeleteRes, err error) {
err = service.SysImSensitiveWord().Delete(ctx, &req.ImSensitiveWordDeleteInp)
return
}
func (c *cImSensitiveWord) Status(ctx context.Context, req *imsensitiveword.StatusReq) (res *imsensitiveword.StatusRes, err error) {
err = service.SysImSensitiveWord().Status(ctx, &req.ImSensitiveWordStatusInp)
return
}
func (c *cImSensitiveWord) BatchImport(ctx context.Context, req *imsensitiveword.BatchImportReq) (res *imsensitiveword.BatchImportRes, err error) {
data, err := service.SysImSensitiveWord().BatchImport(ctx, &req.ImSensitiveWordBatchImportInp)
if err != nil {
return nil, err
}
return &imsensitiveword.BatchImportRes{ImSensitiveWordBatchImportModel: data}, nil
}
func (c *cImSensitiveWord) GroupList(ctx context.Context, req *imsensitiveword.GroupListReq) (res *imsensitiveword.GroupListRes, err error) {
list, err := service.SysImSensitiveWord().GroupList(ctx, &req.ImSensitiveWordGroupListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImSensitiveWordGroupModel{}
}
return &imsensitiveword.GroupListRes{List: list}, nil
}
func (c *cImSensitiveWord) GroupEdit(ctx context.Context, req *imsensitiveword.GroupEditReq) (res *imsensitiveword.GroupEditRes, err error) {
err = service.SysImSensitiveWord().GroupEdit(ctx, &req.ImSensitiveWordGroupEditInp)
return
}
@@ -0,0 +1,26 @@
package sys
import (
"context"
"hotgo/api/admin/imtaskdashboard"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImTaskDashboard = cImTaskDashboard{}
type cImTaskDashboard struct{}
func (c *cImTaskDashboard) List(ctx context.Context, req *imtaskdashboard.ListReq) (res *imtaskdashboard.ListRes, err error) {
list, totalCount, err := service.SysImTaskDashboard().List(ctx, &req.ImTaskDashboardListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImTaskDashboardListModel{}
}
res = new(imtaskdashboard.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
@@ -58,3 +58,35 @@ func (c *cImTenant) Status(ctx context.Context, req *imtenant.StatusReq) (res *i
err = service.SysImTenant().Status(ctx, &req.ImTenantStatusInp) err = service.SysImTenant().Status(ctx, &req.ImTenantStatusInp)
return return
} }
func (c *cImTenant) Usage(ctx context.Context, req *imtenant.UsageReq) (res *imtenant.UsageRes, err error) {
data, err := service.SysImTenant().Usage(ctx, &req.ImTenantUsageInp)
if err != nil {
return
}
res = new(imtenant.UsageRes)
res.ImTenantUsageModel = data
return
}
func (c *cImTenant) DeletionCheck(ctx context.Context, req *imtenant.DeletionCheckReq) (res *imtenant.DeletionCheckRes, err error) {
data, err := service.SysImTenant().DeletionCheck(ctx, &req.ImTenantDeletionCheckInp)
if err != nil {
return
}
res = new(imtenant.DeletionCheckRes)
res.ImTenantDeletionCheckModel = data
return
}
func (c *cImTenant) EnterDeleting(ctx context.Context, req *imtenant.EnterDeletingReq) (res *imtenant.EnterDeletingRes, err error) {
err = service.SysImTenant().EnterDeleting(ctx, &req.ImTenantLifecycleInp)
return
}
func (c *cImTenant) Restore(ctx context.Context, req *imtenant.RestoreReq) (res *imtenant.RestoreRes, err error) {
err = service.SysImTenant().Restore(ctx, &req.ImTenantLifecycleInp)
return
}
@@ -10,6 +10,8 @@ import (
"hotgo/api/admin/imuser" "hotgo/api/admin/imuser"
"hotgo/internal/model/input/sysin" "hotgo/internal/model/input/sysin"
"hotgo/internal/service" "hotgo/internal/service"
"github.com/gogf/gf/v2/errors/gerror"
) )
var ( var (
@@ -18,6 +20,40 @@ var (
type cImUser struct{} type cImUser struct{}
func (c *cImUser) Create(ctx context.Context, req *imuser.CreateReq) (res *imuser.CreateRes, err error) {
data, err := service.SysImUser().Create(ctx, &req.ImUserCreateInp)
if err != nil {
return nil, err
}
return &imuser.CreateRes{ImUserCreateModel: data}, nil
}
func (c *cImUser) BatchCreate(ctx context.Context, req *imuser.BatchCreateReq) (res *imuser.BatchCreateRes, err error) {
data, err := service.SysImUser().BatchCreate(ctx, &req.ImUserBatchCreateInp)
if err != nil {
return nil, err
}
return &imuser.BatchCreateRes{ImUserBatchCreateModel: data}, nil
}
func (c *cImUser) Import(ctx context.Context, req *imuser.ImportReq) (res *imuser.ImportRes, err error) {
file, err := req.File.Open()
if err != nil {
return nil, gerror.Wrap(err, "打开Excel文件失败")
}
defer file.Close()
data, err := service.SysImUser().ImportExcel(ctx, req.TenantId, req.GroupId, file)
if err != nil {
return nil, err
}
return &imuser.ImportRes{ImUserBatchCreateModel: data}, nil
}
func (c *cImUser) DownloadTemplate(ctx context.Context, req *imuser.DownloadTemplateReq) (res *imuser.DownloadTemplateRes, err error) {
err = service.SysImUser().DownloadTemplate(ctx)
return
}
// List 查看IM用户列表 // List 查看IM用户列表
func (c *cImUser) List(ctx context.Context, req *imuser.ListReq) (res *imuser.ListRes, err error) { func (c *cImUser) List(ctx context.Context, req *imuser.ListReq) (res *imuser.ListRes, err error) {
list, totalCount, err := service.SysImUser().List(ctx, &req.ImUserListInp) list, totalCount, err := service.SysImUser().List(ctx, &req.ImUserListInp)
@@ -47,6 +83,60 @@ func (c *cImUser) View(ctx context.Context, req *imuser.ViewReq) (res *imuser.Vi
return return
} }
func (c *cImUser) ResetPassword(ctx context.Context, req *imuser.ResetPasswordReq) (res *imuser.ResetPasswordRes, err error) {
data, err := service.SysImUser().ResetPassword(ctx, &req.ImUserPasswordResetInp)
if err != nil {
return
}
res = new(imuser.ResetPasswordRes)
res.ImUserCommandModel = data
return
}
func (c *cImUser) RevokeSessions(ctx context.Context, req *imuser.RevokeSessionsReq) (res *imuser.RevokeSessionsRes, err error) {
err = service.SysImUser().RevokeSessions(ctx, &req.ImUserRevokeSessionsInp)
return
}
func (c *cImUser) Status(ctx context.Context, req *imuser.StatusReq) (res *imuser.StatusRes, err error) {
err = service.SysImUser().Status(ctx, &req.ImUserStatusInp)
return
}
func (c *cImUser) AdjustGroup(ctx context.Context, req *imuser.AdjustGroupReq) (res *imuser.AdjustGroupRes, err error) {
err = service.SysImUser().AdjustGroup(ctx, &req.ImUserAdjustGroupInp)
return
}
func (c *cImUser) IPWhitelist(ctx context.Context, req *imuser.IPWhitelistReq) (res *imuser.IPWhitelistRes, err error) {
data, err := service.SysImUser().IPWhitelist(ctx, &req.ImUserIPWhitelistInp)
if err != nil {
return nil, err
}
return &imuser.IPWhitelistRes{ImUserIPWhitelistModel: data}, nil
}
func (c *cImUser) IPWhitelistSave(ctx context.Context, req *imuser.IPWhitelistSaveReq) (res *imuser.IPWhitelistSaveRes, err error) {
err = service.SysImUser().IPWhitelistSave(ctx, &req.ImUserIPWhitelistInp)
return
}
func (c *cImUser) Devices(ctx context.Context, req *imuser.DevicesReq) (res *imuser.DevicesRes, err error) {
list, totalCount, err := service.SysImUser().Devices(ctx, &req.ImUserDeviceListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImUserDeviceListModel{}
}
res = new(imuser.DevicesRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImUser) Ban(ctx context.Context, req *imuser.BanReq) (res *imuser.BanRes, err error) { func (c *cImUser) Ban(ctx context.Context, req *imuser.BanReq) (res *imuser.BanRes, err error) {
data, err := service.SysImUser().Ban(ctx, &req.ImUserGovernanceInp) data, err := service.SysImUser().Ban(ctx, &req.ImUserGovernanceInp)
if err != nil { if err != nil {
@@ -0,0 +1,28 @@
package sys
import (
"context"
"hotgo/api/admin/imuserloginhistory"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImUserLoginHistory = cImUserLoginHistory{}
type cImUserLoginHistory struct{}
func (c *cImUserLoginHistory) List(ctx context.Context, req *imuserloginhistory.ListReq) (res *imuserloginhistory.ListRes, err error) {
list, total, err := service.SysImUserLoginHistory().List(ctx, &req.ImUserLoginHistoryListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImUserLoginHistoryModel{}
}
res = &imuserloginhistory.ListRes{List: list}
res.PageRes.Pack(req, total)
return
}
func (c *cImUserLoginHistory) Export(ctx context.Context, req *imuserloginhistory.ExportReq) (*imuserloginhistory.ExportRes, error) {
return &imuserloginhistory.ExportRes{}, service.SysImUserLoginHistory().Export(ctx, &req.ImUserLoginHistoryListInp)
}
@@ -0,0 +1,79 @@
package sys
import (
"context"
"hotgo/api/admin/imuserpolicygroup"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImUserPolicyGroup = cImUserPolicyGroup{}
type cImUserPolicyGroup struct{}
func (c *cImUserPolicyGroup) List(ctx context.Context, req *imuserpolicygroup.ListReq) (res *imuserpolicygroup.ListRes, err error) {
list, total, err := service.SysImUserPolicyGroup().List(ctx, &req.ImUserPolicyGroupListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImUserPolicyGroupListModel{}
}
res = new(imuserpolicygroup.ListRes)
res.List = list
res.PageRes.Pack(req, total)
return
}
func (c *cImUserPolicyGroup) View(ctx context.Context, req *imuserpolicygroup.ViewReq) (res *imuserpolicygroup.ViewRes, err error) {
data, err := service.SysImUserPolicyGroup().View(ctx, &req.ImUserPolicyGroupViewInp)
if err != nil {
return nil, err
}
return &imuserpolicygroup.ViewRes{ImUserPolicyGroupViewModel: data}, nil
}
func (c *cImUserPolicyGroup) Edit(ctx context.Context, req *imuserpolicygroup.EditReq) (res *imuserpolicygroup.EditRes, err error) {
err = service.SysImUserPolicyGroup().Edit(ctx, &req.ImUserPolicyGroupEditInp)
return
}
func (c *cImUserPolicyGroup) Delete(ctx context.Context, req *imuserpolicygroup.DeleteReq) (res *imuserpolicygroup.DeleteRes, err error) {
err = service.SysImUserPolicyGroup().Delete(ctx, &req.ImUserPolicyGroupDeleteInp)
return
}
func (c *cImUserPolicyGroup) Status(ctx context.Context, req *imuserpolicygroup.StatusReq) (res *imuserpolicygroup.StatusRes, err error) {
err = service.SysImUserPolicyGroup().Status(ctx, &req.ImUserPolicyGroupStatusInp)
return
}
func (c *cImUserPolicyGroup) MemberList(ctx context.Context, req *imuserpolicygroup.MemberListReq) (res *imuserpolicygroup.MemberListRes, err error) {
list, total, err := service.SysImUserPolicyGroup().MemberList(ctx, &req.ImUserPolicyGroupMemberListInp)
if err != nil {
return nil, err
}
if list == nil {
list = []*sysin.ImUserPolicyGroupMemberModel{}
}
res = new(imuserpolicygroup.MemberListRes)
res.List = list
res.PageRes.Pack(req, total)
return
}
func (c *cImUserPolicyGroup) MemberAdd(ctx context.Context, req *imuserpolicygroup.MemberAddReq) (res *imuserpolicygroup.MemberAddRes, err error) {
err = service.SysImUserPolicyGroup().MemberAdd(ctx, &req.ImUserPolicyGroupMemberAddInp)
return
}
func (c *cImUserPolicyGroup) MemberRemove(ctx context.Context, req *imuserpolicygroup.MemberRemoveReq) (res *imuserpolicygroup.MemberRemoveRes, err error) {
err = service.SysImUserPolicyGroup().MemberRemove(ctx, &req.ImUserPolicyGroupMemberRemoveInp)
return
}
func (c *cImUserPolicyGroup) LoginIPWhitelist(ctx context.Context, req *imuserpolicygroup.LoginIPWhitelistReq) (res *imuserpolicygroup.LoginIPWhitelistRes, err error) {
data, err := service.SysImUserPolicyGroup().LoginIPWhitelist(ctx, &req.ImTenantLoginIPWhitelistInp)
if err != nil {
return nil, err
}
return &imuserpolicygroup.LoginIPWhitelistRes{ImTenantLoginIPWhitelistModel: data}, nil
}
func (c *cImUserPolicyGroup) LoginIPWhitelistSave(ctx context.Context, req *imuserpolicygroup.LoginIPWhitelistSaveReq) (res *imuserpolicygroup.LoginIPWhitelistSaveRes, err error) {
err = service.SysImUserPolicyGroup().LoginIPWhitelistSave(ctx, &req.ImTenantLoginIPWhitelistSaveInp)
return
}
@@ -0,0 +1,41 @@
package sys
import (
"context"
"hotgo/api/admin/imworkbench"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var ImWorkbench = cImWorkbench{}
type cImWorkbench struct{}
func (c *cImWorkbench) List(ctx context.Context, req *imworkbench.ListReq) (res *imworkbench.ListRes, err error) {
list, totalCount, err := service.SysImWorkbench().List(ctx, &req.ImWorkbenchAppListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImWorkbenchAppModel{}
}
res = new(imworkbench.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cImWorkbench) Edit(ctx context.Context, req *imworkbench.EditReq) (res *imworkbench.EditRes, err error) {
err = service.SysImWorkbench().Edit(ctx, &req.ImWorkbenchAppEditInp)
return
}
func (c *cImWorkbench) Delete(ctx context.Context, req *imworkbench.DeleteReq) (res *imworkbench.DeleteRes, err error) {
err = service.SysImWorkbench().Delete(ctx, &req.ImWorkbenchAppDeleteInp)
return
}
func (c *cImWorkbench) Reorder(ctx context.Context, req *imworkbench.ReorderReq) (res *imworkbench.ReorderRes, err error) {
err = service.SysImWorkbench().Reorder(ctx, &req.ImWorkbenchAppReorderInp)
return
}
@@ -0,0 +1,69 @@
package health
import (
"context"
"net/http"
"strings"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/internal/library/queue"
)
type response struct {
Status string `json:"status"`
Checks map[string]string `json:"checks"`
}
// Serve exposes an unauthenticated dependency health endpoint for container probes.
func Serve(r *ghttp.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
r.Response.Header().Set("Allow", http.MethodGet+", "+http.MethodHead)
r.Response.WriteStatusExit(http.StatusMethodNotAllowed, "method not allowed")
return
}
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
checks := map[string]string{}
statusCode := http.StatusOK
if err := g.DB().Ctx(ctx).Raw("SELECT 1").Scan(new(int)); err != nil {
checks["mysql"] = err.Error()
statusCode = http.StatusServiceUnavailable
} else {
checks["mysql"] = "ok"
}
if _, err := g.Redis().Do(ctx, "PING"); err != nil {
checks["redis"] = err.Error()
statusCode = http.StatusServiceUnavailable
} else {
checks["redis"] = "ok"
}
if strings.EqualFold(strings.TrimSpace(g.Cfg().MustGet(ctx, "queue.driver").String()), "kafka") {
if err := queue.KafkaHealthCheck(ctx); err != nil {
checks["kafka"] = err.Error()
statusCode = http.StatusServiceUnavailable
} else {
checks["kafka"] = "ok"
}
} else {
checks["kafka"] = "disabled"
}
body := response{Status: "ok", Checks: checks}
if statusCode != http.StatusOK {
body.Status = "error"
}
r.Response.Header().Set("Content-Type", "application/json; charset=utf-8")
r.Response.WriteHeader(statusCode)
if r.Method != http.MethodHead {
r.Response.WriteJsonExit(body)
return
}
r.Exit()
}
@@ -6,10 +6,10 @@ import (
"hotgo/internal/dao" "hotgo/internal/dao"
"hotgo/internal/library/imtenant" "hotgo/internal/library/imtenant"
"hotgo/internal/model/entity" "hotgo/internal/model/entity"
"hotgo/internal/service"
"strings" "strings"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
) )
@@ -39,7 +39,7 @@ func (c *cConfig) TenantList(ctx context.Context, req *api.TenantListReq) (res *
if limit <= 0 || limit > 1000 { if limit <= 0 || limit > 1000 {
limit = 1000 limit = 1000
} }
mod := imtenant.RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Limit(limit) mod := imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Limit(limit)
if req.State != "" { if req.State != "" {
if req.State == "active" { if req.State == "active" {
mod = mod.Where(dao.ImTenant.Columns().State, 1) mod = mod.Where(dao.ImTenant.Columns().State, 1)
@@ -59,7 +59,7 @@ func (c *cConfig) TenantList(ctx context.Context, req *api.TenantListReq) (res *
} }
func (c *cConfig) RegistrationSms(ctx context.Context, req *api.RegistrationSmsReq) (res *api.RegistrationSmsRes, err error) { func (c *cConfig) RegistrationSms(ctx context.Context, req *api.RegistrationSmsReq) (res *api.RegistrationSmsRes, err error) {
var record g.Map var record *entity.SysConfig
if err = dao.SysConfig.Ctx(ctx). if err = dao.SysConfig.Ctx(ctx).
Where(dao.SysConfig.Columns().Group, "im"). Where(dao.SysConfig.Columns().Group, "im").
Where(dao.SysConfig.Columns().Key, "register_sms_enabled"). Where(dao.SysConfig.Columns().Key, "register_sms_enabled").
@@ -69,7 +69,7 @@ func (c *cConfig) RegistrationSms(ctx context.Context, req *api.RegistrationSmsR
} }
enabled := true enabled := true
if record != nil { if record != nil {
enabled = gconv.String(record[dao.SysConfig.Columns().Value]) != "0" enabled = gconv.String(record.Value) != "0"
} }
return &api.RegistrationSmsRes{Enabled: enabled}, nil return &api.RegistrationSmsRes{Enabled: enabled}, nil
} }
@@ -91,9 +91,65 @@ func (c *cConfig) ThirdParty(ctx context.Context, req *api.ThirdPartyReq) (res *
return &api.ThirdPartyRes{Values: values}, nil return &api.ThirdPartyRes{Values: values}, nil
} }
func (c *cConfig) TenantLoginIPWhitelist(ctx context.Context, req *api.TenantLoginIPWhitelistReq) (res *api.TenantLoginIPWhitelistRes, err error) {
if _, err = getTenant(ctx, dao.ImTenant.Columns().Id, req.Id); err != nil {
return nil, err
}
data, err := service.SysImUserPolicyGroup().LoginIPWhitelistByTenant(ctx, req.Id)
if err != nil {
return nil, err
}
return &api.TenantLoginIPWhitelistRes{TenantId: data.TenantId, AppList: data.AppList, WebList: data.WebList}, nil
}
func (c *cConfig) AppReleaseLatest(ctx context.Context, req *api.AppReleaseLatestReq) (res *api.AppReleaseLatestRes, err error) {
platform := strings.ToLower(strings.TrimSpace(req.Platform))
channel := strings.ToLower(strings.TrimSpace(req.Channel))
if platform != "ios" && platform != "android" {
return nil, gerror.New("客户端平台不合法")
}
if channel == "" {
channel = "stable"
}
if strings.ContainsAny(channel, " \t\r\n/") || len(channel) > 32 {
return nil, gerror.New("客户端渠道不合法")
}
var row struct {
ID int64 `json:"id"`
Platform string `json:"platform"`
Channel string `json:"channel"`
LatestVersion string `json:"latest_version"`
MinSupportedVersion string `json:"min_supported_version"`
UpdateType string `json:"update_type"`
ReleaseNotes string `json:"release_notes"`
StoreURL string `json:"store_url"`
PackageURL string `json:"package_url"`
Checksum string `json:"checksum"`
RemindAfterSeconds int `json:"remind_after_seconds"`
PublishedAt *gtime.Time `json:"published_at"`
CreatedAt *gtime.Time `json:"created_at"`
UpdatedAt *gtime.Time `json:"updated_at"`
}
err = imtenant.RegistryDB().Model("im_app_releases").Safe().Ctx(ctx).
Where("platform", platform).Where("channel", channel).Limit(1).Scan(&row)
if err != nil {
return nil, gerror.Wrap(err, "获取IM客户端版本策略失败")
}
if row.ID <= 0 {
return nil, gerror.New("IM客户端版本策略不存在")
}
return &api.AppReleaseLatestRes{
ID: row.ID, Platform: row.Platform, Channel: row.Channel,
LatestVersion: row.LatestVersion, MinSupportedVersion: row.MinSupportedVersion,
UpdateType: row.UpdateType, ReleaseNotes: row.ReleaseNotes, StoreURL: row.StoreURL,
PackageURL: row.PackageURL, Checksum: row.Checksum, RemindAfterSeconds: row.RemindAfterSeconds,
PublishedAt: gtimeToRFC3339(row.PublishedAt), CreatedAt: gtimeToRFC3339(row.CreatedAt), UpdatedAt: gtimeToRFC3339(row.UpdatedAt),
}, nil
}
func getTenant(ctx context.Context, field string, value any) (*entity.ImTenant, error) { func getTenant(ctx context.Context, field string, value any) (*entity.ImTenant, error) {
var tenant *entity.ImTenant var tenant *entity.ImTenant
if err := imtenant.RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Where(field, value).Scan(&tenant); err != nil { if err := imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Where(field, value).Scan(&tenant); err != nil {
return nil, gerror.Wrap(err, "获取IM租户配置失败") return nil, gerror.Wrap(err, "获取IM租户配置失败")
} }
if tenant == nil || tenant.Id <= 0 { if tenant == nil || tenant.Id <= 0 {
@@ -0,0 +1,98 @@
// Package crons
// @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 crons
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/cron"
"hotgo/internal/library/imtenant"
"hotgo/internal/model/entity"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
func init() {
cron.Register(ImFileCleanupOrphan)
}
// ImFileCleanupOrphan 清理IM孤儿文件,参数:保留天数、单租户处理上限。
var ImFileCleanupOrphan = &cImFileCleanupOrphan{name: "imFileCleanupOrphan"}
type cImFileCleanupOrphan struct {
name string
}
func (c *cImFileCleanupOrphan) GetName() string {
return c.name
}
func (c *cImFileCleanupOrphan) Execute(ctx context.Context, parser *cron.Parser) (err error) {
days := 7
limit := 100
if len(parser.Args) > 0 {
days = gconv.Int(parser.Args[0])
}
if len(parser.Args) > 1 {
limit = gconv.Int(parser.Args[1])
}
if days <= 0 {
days = 7
}
if limit <= 0 || limit > 1000 {
limit = 100
}
var tenants []*entity.ImTenant
if err = imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx).
Where(dao.ImTenant.Columns().State, consts.ImTenantStateActive).
Scan(&tenants); err != nil {
parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan load tenants err:%+v", err)
return err
}
cutoff := gtime.Now().AddDate(0, 0, -days)
for _, tenant := range tenants {
db, _, dbErr := imtenant.DB(ctx, tenant.Id)
if dbErr != nil {
parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan tenant:%d db err:%+v", tenant.Id, dbErr)
continue
}
rows, dbErr := db.Model("im_fileuploads fu").Safe().Ctx(ctx).
LeftJoin("im_filemsglinks fml", "fml.fileid=fu.id").
Fields("fu.id").
Where("fml.id IS NULL").
WhereLT("fu.updatedat", cutoff).
WhereNotIn("fu.status", []int{consts.ImFileStatusDeleted, consts.ImFileStatusQuarantined}).
Group("fu.id").
OrderAsc("fu.updatedat").
Limit(limit).
All()
if dbErr != nil {
parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan tenant:%d scan err:%+v", tenant.Id, dbErr)
continue
}
if len(rows) == 0 {
continue
}
ids := make([]int64, 0, len(rows))
for _, row := range rows {
ids = append(ids, row["id"].Int64())
}
if _, dbErr = db.Model("im_fileuploads").Safe().Ctx(ctx).
WhereIn("id", ids).
Data(g.Map{"status": consts.ImFileStatusDeleted, "updatedat": gtime.Now()}).
Update(); dbErr != nil {
parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan tenant:%d update err:%+v", tenant.Id, dbErr)
continue
}
parser.Logger.Infof(ctx, "cron ImFileCleanupOrphan tenant:%d count:%d", tenant.Id, len(ids))
}
return nil
}
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imMessageCurfewRulesDao is the data access object for the table im_message_curfew_rules.
// You can define custom methods on it to extend its functionality as needed.
type imMessageCurfewRulesDao struct {
*internal.ImMessageCurfewRulesDao
}
var (
// ImMessageCurfewRules is a globally accessible object for table im_message_curfew_rules operations.
ImMessageCurfewRules = imMessageCurfewRulesDao{internal.NewImMessageCurfewRulesDao()}
)
// Add your custom methods and functionality below.
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imSecurityIpRulesDao is the data access object for the table im_security_ip_rules.
// You can define custom methods on it to extend its functionality as needed.
type imSecurityIpRulesDao struct {
*internal.ImSecurityIpRulesDao
}
var (
// ImSecurityIpRules is a globally accessible object for table im_security_ip_rules operations.
ImSecurityIpRules = imSecurityIpRulesDao{internal.NewImSecurityIpRulesDao()}
)
// Add your custom methods and functionality below.
+22
View File
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imSecurityPolicyDao is the data access object for the table im_security_policy.
// You can define custom methods on it to extend its functionality as needed.
type imSecurityPolicyDao struct {
*internal.ImSecurityPolicyDao
}
var (
// ImSecurityPolicy is a globally accessible object for table im_security_policy operations.
ImSecurityPolicy = imSecurityPolicyDao{internal.NewImSecurityPolicyDao()}
)
// Add your custom methods and functionality below.
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imSensitiveWordGroupsDao is the data access object for the table im_sensitive_word_groups.
// You can define custom methods on it to extend its functionality as needed.
type imSensitiveWordGroupsDao struct {
*internal.ImSensitiveWordGroupsDao
}
var (
// ImSensitiveWordGroups is a globally accessible object for table im_sensitive_word_groups operations.
ImSensitiveWordGroups = imSensitiveWordGroupsDao{internal.NewImSensitiveWordGroupsDao()}
)
// Add your custom methods and functionality below.
+22
View File
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imSensitiveWordsDao is the data access object for the table im_sensitive_words.
// You can define custom methods on it to extend its functionality as needed.
type imSensitiveWordsDao struct {
*internal.ImSensitiveWordsDao
}
var (
// ImSensitiveWords is a globally accessible object for table im_sensitive_words operations.
ImSensitiveWords = imSensitiveWordsDao{internal.NewImSensitiveWordsDao()}
)
// Add your custom methods and functionality below.
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imUserPolicyGroupMembersDao is the data access object for the table im_user_policy_group_members.
// You can define custom methods on it to extend its functionality as needed.
type imUserPolicyGroupMembersDao struct {
*internal.ImUserPolicyGroupMembersDao
}
var (
// ImUserPolicyGroupMembers is a globally accessible object for table im_user_policy_group_members operations.
ImUserPolicyGroupMembers = imUserPolicyGroupMembersDao{internal.NewImUserPolicyGroupMembersDao()}
)
// Add your custom methods and functionality below.
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"hotgo/internal/dao/internal"
)
// imUserPolicyGroupsDao is the data access object for the table im_user_policy_groups.
// You can define custom methods on it to extend its functionality as needed.
type imUserPolicyGroupsDao struct {
*internal.ImUserPolicyGroupsDao
}
var (
// ImUserPolicyGroups is a globally accessible object for table im_user_policy_groups operations.
ImUserPolicyGroups = imUserPolicyGroupsDao{internal.NewImUserPolicyGroupsDao()}
)
// Add your custom methods and functionality below.
@@ -0,0 +1,123 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImMessageCurfewRulesDao is the data access object for the table im_message_curfew_rules.
type ImMessageCurfewRulesDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImMessageCurfewRulesColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImMessageCurfewRulesColumns defines and stores column names for the table im_message_curfew_rules.
type ImMessageCurfewRulesColumns struct {
Id string //
TenantId string //
PolicyId string //
RuleCode string //
RuleName string //
DateType string // daily, weekday, weekend, custom
CustomDates string // YYYY-MM-DD values for custom date type
Weekdays string // ISO weekdays 1 through 7
Timezone string //
StartTime string //
EndTime string // Less than start_time means crossing midnight
Scope string // private_message or group_message
WhitelistUserIds string //
WhitelistGroupIds string //
Message string //
Status string // 1 enabled, 2 disabled
Sort string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imMessageCurfewRulesColumns holds the columns for the table im_message_curfew_rules.
var imMessageCurfewRulesColumns = ImMessageCurfewRulesColumns{
Id: "id",
TenantId: "tenant_id",
PolicyId: "policy_id",
RuleCode: "rule_code",
RuleName: "rule_name",
DateType: "date_type",
CustomDates: "custom_dates",
Weekdays: "weekdays",
Timezone: "timezone",
StartTime: "start_time",
EndTime: "end_time",
Scope: "scope",
WhitelistUserIds: "whitelist_user_ids",
WhitelistGroupIds: "whitelist_group_ids",
Message: "message",
Status: "status",
Sort: "sort",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImMessageCurfewRulesDao creates and returns a new DAO object for table data access.
func NewImMessageCurfewRulesDao(handlers ...gdb.ModelHandler) *ImMessageCurfewRulesDao {
return &ImMessageCurfewRulesDao{
group: "default",
table: "im_message_curfew_rules",
columns: imMessageCurfewRulesColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImMessageCurfewRulesDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImMessageCurfewRulesDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImMessageCurfewRulesDao) Columns() ImMessageCurfewRulesColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImMessageCurfewRulesDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImMessageCurfewRulesDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImMessageCurfewRulesDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -0,0 +1,109 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImSecurityIpRulesDao is the data access object for the table im_security_ip_rules.
type ImSecurityIpRulesDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImSecurityIpRulesColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImSecurityIpRulesColumns defines and stores column names for the table im_security_ip_rules.
type ImSecurityIpRulesColumns struct {
Id string //
TenantId string //
RuleType string // whitelist or blacklist
IpCidr string // IPv4, IPv6 or CIDR
Action string // allow, challenge, block, audit_only
SceneJson string // login, register, message, upload and other scenes
RiskLevel string //
Status string // 1 enabled, 2 disabled
ExpiresAt string //
Remark string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imSecurityIpRulesColumns holds the columns for the table im_security_ip_rules.
var imSecurityIpRulesColumns = ImSecurityIpRulesColumns{
Id: "id",
TenantId: "tenant_id",
RuleType: "rule_type",
IpCidr: "ip_cidr",
Action: "action",
SceneJson: "scene_json",
RiskLevel: "risk_level",
Status: "status",
ExpiresAt: "expires_at",
Remark: "remark",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImSecurityIpRulesDao creates and returns a new DAO object for table data access.
func NewImSecurityIpRulesDao(handlers ...gdb.ModelHandler) *ImSecurityIpRulesDao {
return &ImSecurityIpRulesDao{
group: "default",
table: "im_security_ip_rules",
columns: imSecurityIpRulesColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImSecurityIpRulesDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImSecurityIpRulesDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImSecurityIpRulesDao) Columns() ImSecurityIpRulesColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImSecurityIpRulesDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImSecurityIpRulesDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImSecurityIpRulesDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -0,0 +1,123 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImSecurityPolicyDao is the data access object for the table im_security_policy.
type ImSecurityPolicyDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImSecurityPolicyColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImSecurityPolicyColumns defines and stores column names for the table im_security_policy.
type ImSecurityPolicyColumns struct {
Id string //
TenantId string // 0 means global fallback policy
PolicyVersion string //
PolicyName string //
PublishStatus string //
PasswordPolicy string //
LoginRateLimit string //
SensitiveWordPolicy string //
FileReviewPolicy string //
RealnamePolicy string //
RiskChallengePolicy string //
GlobalFriendEnabled string // Backend master switch for every add-friend entry
FriendDisabledPolicy string // Profile masking, QR and group-member restrictions
MessageCurfewEnabled string // Backend master switch for message curfew
Remark string //
PublishedAt string //
PublishedBy string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imSecurityPolicyColumns holds the columns for the table im_security_policy.
var imSecurityPolicyColumns = ImSecurityPolicyColumns{
Id: "id",
TenantId: "tenant_id",
PolicyVersion: "policy_version",
PolicyName: "policy_name",
PublishStatus: "publish_status",
PasswordPolicy: "password_policy",
LoginRateLimit: "login_rate_limit",
SensitiveWordPolicy: "sensitive_word_policy",
FileReviewPolicy: "file_review_policy",
RealnamePolicy: "realname_policy",
RiskChallengePolicy: "risk_challenge_policy",
GlobalFriendEnabled: "global_friend_enabled",
FriendDisabledPolicy: "friend_disabled_policy",
MessageCurfewEnabled: "message_curfew_enabled",
Remark: "remark",
PublishedAt: "published_at",
PublishedBy: "published_by",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImSecurityPolicyDao creates and returns a new DAO object for table data access.
func NewImSecurityPolicyDao(handlers ...gdb.ModelHandler) *ImSecurityPolicyDao {
return &ImSecurityPolicyDao{
group: "default",
table: "im_security_policy",
columns: imSecurityPolicyColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImSecurityPolicyDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImSecurityPolicyDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImSecurityPolicyDao) Columns() ImSecurityPolicyColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImSecurityPolicyDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImSecurityPolicyDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImSecurityPolicyDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -0,0 +1,103 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImSensitiveWordGroupsDao is the data access object for the table im_sensitive_word_groups.
type ImSensitiveWordGroupsDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImSensitiveWordGroupsColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImSensitiveWordGroupsColumns defines and stores column names for the table im_sensitive_word_groups.
type ImSensitiveWordGroupsColumns struct {
Id string //
TenantId string // 0 means global dictionary
GroupCode string //
GroupName string //
Priority string //
Status string // 1 enabled, 2 disabled
Remark string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imSensitiveWordGroupsColumns holds the columns for the table im_sensitive_word_groups.
var imSensitiveWordGroupsColumns = ImSensitiveWordGroupsColumns{
Id: "id",
TenantId: "tenant_id",
GroupCode: "group_code",
GroupName: "group_name",
Priority: "priority",
Status: "status",
Remark: "remark",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImSensitiveWordGroupsDao creates and returns a new DAO object for table data access.
func NewImSensitiveWordGroupsDao(handlers ...gdb.ModelHandler) *ImSensitiveWordGroupsDao {
return &ImSensitiveWordGroupsDao{
group: "default",
table: "im_sensitive_word_groups",
columns: imSensitiveWordGroupsColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImSensitiveWordGroupsDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImSensitiveWordGroupsDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImSensitiveWordGroupsDao) Columns() ImSensitiveWordGroupsColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImSensitiveWordGroupsDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImSensitiveWordGroupsDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImSensitiveWordGroupsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -0,0 +1,115 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImSensitiveWordsDao is the data access object for the table im_sensitive_words.
type ImSensitiveWordsDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImSensitiveWordsColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImSensitiveWordsColumns defines and stores column names for the table im_sensitive_words.
type ImSensitiveWordsColumns struct {
Id string //
TenantId string // 0 means global dictionary
GroupId string //
Word string //
WordHash string // SHA-256 of normalized word
MatchType string //
SceneJson string // Enabled scenes
Action string //
Replacement string //
RiskLevel string //
Priority string //
Status string // 1 enabled, 2 disabled
Remark string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imSensitiveWordsColumns holds the columns for the table im_sensitive_words.
var imSensitiveWordsColumns = ImSensitiveWordsColumns{
Id: "id",
TenantId: "tenant_id",
GroupId: "group_id",
Word: "word",
WordHash: "word_hash",
MatchType: "match_type",
SceneJson: "scene_json",
Action: "action",
Replacement: "replacement",
RiskLevel: "risk_level",
Priority: "priority",
Status: "status",
Remark: "remark",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImSensitiveWordsDao creates and returns a new DAO object for table data access.
func NewImSensitiveWordsDao(handlers ...gdb.ModelHandler) *ImSensitiveWordsDao {
return &ImSensitiveWordsDao{
group: "default",
table: "im_sensitive_words",
columns: imSensitiveWordsColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImSensitiveWordsDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImSensitiveWordsDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImSensitiveWordsDao) Columns() ImSensitiveWordsColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImSensitiveWordsDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImSensitiveWordsDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImSensitiveWordsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -21,28 +21,36 @@ type ImTenantConfigDao struct {
// ImTenantConfigColumns defines and stores column names for the table im_tenant_config. // ImTenantConfigColumns defines and stores column names for the table im_tenant_config.
type ImTenantConfigColumns struct { type ImTenantConfigColumns struct {
Id string // Id string //
TenantId string // TenantId string //
MaxUsers string // 0 means unlimited MaxUsers string // 0 means unlimited
MaxGroups string // 0 means unlimited MaxGroups string // 0 means unlimited
MaxGroupMembers string // 0 means unlimited MaxGroupMembers string // 0 means unlimited
CreatedAt string // DefaultPolicyGroupId string // Default user policy group
CreatedBy string // SecurityPolicyVersion string // Published security policy version
UpdatedAt string // GlobalFriendEnabled string // Published global friend switch
UpdatedBy string // MessageCurfewEnabled string // Published message curfew switch
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
} }
// imTenantConfigColumns holds the columns for the table im_tenant_config. // imTenantConfigColumns holds the columns for the table im_tenant_config.
var imTenantConfigColumns = ImTenantConfigColumns{ var imTenantConfigColumns = ImTenantConfigColumns{
Id: "id", Id: "id",
TenantId: "tenant_id", TenantId: "tenant_id",
MaxUsers: "max_users", MaxUsers: "max_users",
MaxGroups: "max_groups", MaxGroups: "max_groups",
MaxGroupMembers: "max_group_members", MaxGroupMembers: "max_group_members",
CreatedAt: "created_at", DefaultPolicyGroupId: "default_policy_group_id",
CreatedBy: "created_by", SecurityPolicyVersion: "security_policy_version",
UpdatedAt: "updated_at", GlobalFriendEnabled: "global_friend_enabled",
UpdatedBy: "updated_by", MessageCurfewEnabled: "message_curfew_enabled",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
} }
// NewImTenantConfigDao creates and returns a new DAO object for table data access. // NewImTenantConfigDao creates and returns a new DAO object for table data access.
@@ -0,0 +1,105 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImUserPolicyGroupMembersDao is the data access object for the table im_user_policy_group_members.
type ImUserPolicyGroupMembersDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImUserPolicyGroupMembersColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImUserPolicyGroupMembersColumns defines and stores column names for the table im_user_policy_group_members.
type ImUserPolicyGroupMembersColumns struct {
Id string //
TenantId string //
GroupId string //
UserId string // User ID in the tenant database
Status string // 1 enabled, 2 disabled
ValidFrom string //
ValidUntil string //
Remark string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imUserPolicyGroupMembersColumns holds the columns for the table im_user_policy_group_members.
var imUserPolicyGroupMembersColumns = ImUserPolicyGroupMembersColumns{
Id: "id",
TenantId: "tenant_id",
GroupId: "group_id",
UserId: "user_id",
Status: "status",
ValidFrom: "valid_from",
ValidUntil: "valid_until",
Remark: "remark",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImUserPolicyGroupMembersDao creates and returns a new DAO object for table data access.
func NewImUserPolicyGroupMembersDao(handlers ...gdb.ModelHandler) *ImUserPolicyGroupMembersDao {
return &ImUserPolicyGroupMembersDao{
group: "default",
table: "im_user_policy_group_members",
columns: imUserPolicyGroupMembersColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImUserPolicyGroupMembersDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImUserPolicyGroupMembersDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImUserPolicyGroupMembersDao) Columns() ImUserPolicyGroupMembersColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImUserPolicyGroupMembersDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImUserPolicyGroupMembersDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImUserPolicyGroupMembersDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -0,0 +1,111 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ImUserPolicyGroupsDao is the data access object for the table im_user_policy_groups.
type ImUserPolicyGroupsDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns ImUserPolicyGroupsColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// ImUserPolicyGroupsColumns defines and stores column names for the table im_user_policy_groups.
type ImUserPolicyGroupsColumns struct {
Id string //
TenantId string // 0 means global default
GroupCode string // Stable policy group code
GroupName string // Policy group name
Priority string // Higher value has higher priority
Status string // 1 enabled, 2 disabled
PolicyJson string // Login, friend, message, group and file policies
IpWhitelist string // IPv4/IPv6 or CIDR list
IpBlacklist string // IPv4/IPv6 or CIDR list
Version string // Optimistic-lock/config version
Remark string //
CreatedAt string //
CreatedBy string //
UpdatedAt string //
UpdatedBy string //
DeletedAt string //
DeletedBy string //
}
// imUserPolicyGroupsColumns holds the columns for the table im_user_policy_groups.
var imUserPolicyGroupsColumns = ImUserPolicyGroupsColumns{
Id: "id",
TenantId: "tenant_id",
GroupCode: "group_code",
GroupName: "group_name",
Priority: "priority",
Status: "status",
PolicyJson: "policy_json",
IpWhitelist: "ip_whitelist",
IpBlacklist: "ip_blacklist",
Version: "version",
Remark: "remark",
CreatedAt: "created_at",
CreatedBy: "created_by",
UpdatedAt: "updated_at",
UpdatedBy: "updated_by",
DeletedAt: "deleted_at",
DeletedBy: "deleted_by",
}
// NewImUserPolicyGroupsDao creates and returns a new DAO object for table data access.
func NewImUserPolicyGroupsDao(handlers ...gdb.ModelHandler) *ImUserPolicyGroupsDao {
return &ImUserPolicyGroupsDao{
group: "default",
table: "im_user_policy_groups",
columns: imUserPolicyGroupsColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *ImUserPolicyGroupsDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *ImUserPolicyGroupsDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *ImUserPolicyGroupsDao) Columns() ImUserPolicyGroupsColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *ImUserPolicyGroupsDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *ImUserPolicyGroupsDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *ImUserPolicyGroupsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
+46 -47
View File
@@ -1,67 +1,66 @@
// Package casbin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package casbin package casbin
import ( import (
"context"
"net/http" "net/http"
"testing" "testing"
casbinv2 "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
) )
// TestNew description const testModel = `
func TestNew(t *testing.T) { [request_definition]
r = sub, obj, act
InitEnforcer(context.TODO()) [policy_definition]
p = sub, obj, act
user := "admin" [role_definition]
path := "/" g = _, _
method := http.MethodGet
t.Logf("\nuser:%v\npath:%v\nmethod:%v", user, path, method)
ok, err := Enforcer.DeletePermissionsForUser(user) [policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
`
func TestEnforcerPermissions(t *testing.T) {
m, err := model.NewModelFromString(testModel)
if err != nil { if err != nil {
t.Error(err) t.Fatalf("创建Casbin测试模型失败: %v", err)
} }
t.Logf("delete user premission:%v", ok) enforcer, err := casbinv2.NewEnforcer(m)
CheckPremission(t, user, path, method)
AddPremission(t, user, "*", ActionAll)
CheckPremission(t, user, path, method)
user1 := "user1"
path1 := "/api/v1/*"
checkPathTrue := "/api/v1/user/list"
checkPathFalse := "/api/v2/user/list"
AddPremission(t, user1, path1, ActionAll)
CheckPremission(t, user1, checkPathTrue, ActionPost)
CheckPremission(t, user1, checkPathFalse, http.MethodGet)
CheckPremission(t, user1, checkPathTrue, http.MethodGet)
CheckPremission(t, user1, "/api/v1/user/list2", http.MethodGet)
ok, err = Enforcer.DeletePermissionsForUser(user1)
if err != nil { if err != nil {
t.Error(err) t.Fatalf("创建Casbin测试实例失败: %v", err)
} }
t.Logf("delete user premission:%v", ok)
CheckPremission(t, user1, "/api/v1/user1/list", http.MethodGet) assertPermission(t, enforcer, "admin", "/", http.MethodGet, false)
if ok, addErr := enforcer.AddPolicy("admin", "*", ActionAll); addErr != nil || !ok {
t.Fatalf("添加管理员策略失败: ok=%v err=%v", ok, addErr)
}
assertPermission(t, enforcer, "admin", "/", http.MethodGet, true)
if ok, addErr := enforcer.AddPolicy("user1", "/api/v1/*", ActionAll); addErr != nil || !ok {
t.Fatalf("添加用户策略失败: ok=%v err=%v", ok, addErr)
}
assertPermission(t, enforcer, "user1", "/api/v1/user/list", http.MethodPost, true)
assertPermission(t, enforcer, "user1", "/api/v1/user/list", http.MethodGet, true)
assertPermission(t, enforcer, "user1", "/api/v2/user/list", http.MethodGet, false)
if ok, deleteErr := enforcer.DeletePermissionsForUser("user1"); deleteErr != nil || !ok {
t.Fatalf("删除用户策略失败: ok=%v err=%v", ok, deleteErr)
}
assertPermission(t, enforcer, "user1", "/api/v1/user/list", http.MethodGet, false)
} }
// CheckPremission description func assertPermission(t *testing.T, enforcer *casbinv2.Enforcer, user, path, method string, expected bool) {
func CheckPremission(t *testing.T, user string, path string, method string) { t.Helper()
ok, err := Enforcer.Enforce(user, path, method) actual, err := enforcer.Enforce(user, path, method)
if err != nil { if err != nil {
t.Error(err) t.Fatalf("检查权限失败: %v", err)
} }
t.Logf("check \tuser[%s] \tpremission[%s] \tpath[%s] \tallow[%v]", user, method, path, ok) if actual != expected {
} t.Fatalf("权限结果不符 user=%s path=%s method=%s expected=%v actual=%v", user, path, method, expected, actual)
// Add description
func AddPremission(t *testing.T, user string, path string, method string) {
ok, err := Enforcer.AddPolicy(user, path, method)
if err != nil {
t.Error(err)
} }
t.Logf("add \tuser[%s] \tpremission[%s] \tpath[%s] \tresult[%v]", user, method, path, ok)
} }
@@ -17,6 +17,7 @@ import (
"time" "time"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gctx"
) )
@@ -198,6 +199,9 @@ func (c *Client) parseError(resp *http.Response) error {
Reason string `json:"reason"` Reason string `json:"reason"`
} }
_ = json.Unmarshal(body, &envelope) _ = json.Unmarshal(body, &envelope)
if envelope.Message == "" && envelope.Reason == "" {
g.Log().Warningf(context.Background(), "IM API returned non-success status:%d url:%s body:%s", resp.StatusCode, resp.Request.URL.String(), string(body))
}
return stableError(envelope.Message, envelope.Reason) return stableError(envelope.Message, envelope.Reason)
} }
@@ -58,6 +58,39 @@ func TestListCodesWithInviteCodeUsesSignedSearchPost(t *testing.T) {
} }
} }
func TestInternalPostUsesInternalSignatureAndBody(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || r.URL.Path != "/internal/admin-query/tenants/default/users" {
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
}
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("read body: %v", err)
}
if string(body) != `{"username":"alice"}` {
t.Fatalf("unexpected body %s", body)
}
if r.Header.Get("X-Service-Name") != "hotgo-admin" || r.Header.Get("X-Signature") == "" {
t.Fatalf("missing internal authentication headers")
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"code":0,"data":{"user_id":123,"username":"alice"}}`))
}))
defer server.Close()
client := NewClient(Config{InternalBaseUrl: server.URL, InternalServiceName: "hotgo-admin", InternalServiceSecret: "secret", Timeout: time.Second})
var out struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
}
if err := client.InternalPost(context.Background(), "/internal/admin-query/tenants/default/users", map[string]string{"username": "alice"}, &out); err != nil {
t.Fatalf("InternalPost returned error: %v", err)
}
if out.UserID != 123 || out.Username != "alice" {
t.Fatalf("unexpected response: %+v", out)
}
}
func signedTestServer(t *testing.T, method string, path string, inspect func(t *testing.T, r *http.Request, body []byte)) *httptest.Server { func signedTestServer(t *testing.T, method string, path string, inspect func(t *testing.T, r *http.Request, body []byte)) *httptest.Server {
t.Helper() t.Helper()
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+18 -4
View File
@@ -14,15 +14,29 @@ import (
"time" "time"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
) )
func (c *Client) InternalGet(ctx context.Context, path string, out any) error { func (c *Client) InternalGet(ctx context.Context, path string, out any) error {
req, err := c.newInternalRequest(ctx, http.MethodGet, path, nil) return c.internalJSON(ctx, http.MethodGet, path, nil, out)
}
func (c *Client) InternalPost(ctx context.Context, path string, in any, out any) error {
body, err := json.Marshal(in)
if err != nil {
return gerror.Wrap(err, "序列化IM内部请求失败")
}
return c.internalJSON(ctx, http.MethodPost, path, body, out)
}
func (c *Client) internalJSON(ctx context.Context, method, path string, body []byte, out any) error {
req, err := c.newInternalRequest(ctx, method, path, body)
if err != nil { if err != nil {
return err return err
} }
resp, err := c.http.Do(req) resp, err := c.http.Do(req)
if err != nil { if err != nil {
g.Log().Warningf(ctx, "IM internal query request failed url:%s err:%+v", req.URL.String(), err)
return gerror.New("IM内部查询服务暂不可用") return gerror.New("IM内部查询服务暂不可用")
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -32,7 +46,7 @@ func (c *Client) InternalGet(ctx context.Context, path string, out any) error {
if out == nil { if out == nil {
return nil return nil
} }
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBody)) responseBody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBody))
if err != nil { if err != nil {
return gerror.Wrap(err, "读取IM内部查询响应失败") return gerror.Wrap(err, "读取IM内部查询响应失败")
} }
@@ -42,13 +56,13 @@ func (c *Client) InternalGet(ctx context.Context, path string, out any) error {
Reason string `json:"reason"` Reason string `json:"reason"`
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
if err = json.Unmarshal(body, &envelope); err == nil && len(envelope.Data) > 0 { if err = json.Unmarshal(responseBody, &envelope); err == nil && len(envelope.Data) > 0 {
if envelope.Code != 0 { if envelope.Code != 0 {
return stableError(envelope.Message, envelope.Reason) return stableError(envelope.Message, envelope.Reason)
} }
return json.Unmarshal(envelope.Data, out) return json.Unmarshal(envelope.Data, out)
} }
return json.Unmarshal(body, out) return json.Unmarshal(responseBody, out)
} }
func (c *Client) newInternalRequest(ctx context.Context, method string, path string, bodyBytes []byte) (*http.Request, error) { func (c *Client) newInternalRequest(ctx context.Context, method string, path string, bodyBytes []byte) (*http.Request, error) {
+58 -107
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"hotgo/internal/consts" "hotgo/internal/consts"
"hotgo/internal/library/imtenant"
"hotgo/internal/library/queue" "hotgo/internal/library/queue"
"time" "time"
@@ -16,11 +17,6 @@ import (
"github.com/gogf/gf/v2/util/guid" "github.com/gogf/gf/v2/util/guid"
) )
const (
adminCommandTable = "im_admin_commands"
adminOutboxTable = "im_admin_outbox"
)
func PublishAdminCommand(ctx context.Context, in AdminCommandInput) (res *AdminCommandModel, err error) { func PublishAdminCommand(ctx context.Context, in AdminCommandInput) (res *AdminCommandModel, err error) {
if in.TenantID <= 0 || in.TenantCode == "" { if in.TenantID <= 0 || in.TenantCode == "" {
return nil, gerror.New("IM租户信息不完整") return nil, gerror.New("IM租户信息不完整")
@@ -65,41 +61,22 @@ func PublishAdminCommand(ctx context.Context, in AdminCommandInput) (res *AdminC
return nil, gerror.Wrap(err, "序列化IM命令Envelope失败") return nil, gerror.Wrap(err, "序列化IM命令Envelope失败")
} }
db := g.DB() db := adminDB()
err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
data := g.Map{ nowTime := gtime.Now()
"command_id": commandID, if _, err := tx.Exec(`INSERT INTO im_admin_commands
"tenant_id": in.TenantID, (command_id,tenant_id,tenant_code,event_id,event_type,biz_type,biz_id,idempotency_key,status,payload,envelope,created_by,created_at,updated_at)
"tenant_code": in.TenantCode, VALUES(?,?,?,?,?,?,?,?,?,CAST(? AS JSON),CAST(? AS JSON),?,?,?)`,
"event_id": eventID, commandID, in.TenantID, in.TenantCode, eventID, in.EventType, in.BizType, in.BizID,
"event_type": in.EventType, in.IdempotencyKey, consts.ImAdminCommandStatusPending, string(payloadBytes), string(envelopeBytes),
"biz_type": in.BizType, in.CreatedBy, nowTime, nowTime); err != nil {
"biz_id": in.BizID,
"idempotency_key": in.IdempotencyKey,
"status": consts.ImAdminCommandStatusPending,
"payload": string(payloadBytes),
"envelope": string(envelopeBytes),
"created_by": in.CreatedBy,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}
if _, err := tx.Model(adminCommandTable).Safe().Ctx(ctx).Data(data).Insert(); err != nil {
return gerror.Wrap(err, "创建IM后台命令失败") return gerror.Wrap(err, "创建IM后台命令失败")
} }
outbox := g.Map{ if _, err := tx.Exec(`INSERT INTO im_admin_outbox
"event_id": eventID, (event_id,command_id,topic,event_type,biz_type,biz_id,key_value,envelope,status,created_at,updated_at)
"command_id": commandID, VALUES(?,?,?,?,?,?,?,CAST(? AS JSON),?,?,?)`,
"topic": consts.QueueImAdminCommandTopic, eventID, commandID, consts.QueueImAdminCommandTopic, in.EventType, in.BizType, in.BizID,
"event_type": in.EventType, in.BizID, string(envelopeBytes), consts.ImAdminOutboxStatusPending, nowTime, nowTime); err != nil {
"biz_type": in.BizType,
"biz_id": in.BizID,
"key_value": in.BizID,
"envelope": string(envelopeBytes),
"status": consts.ImAdminOutboxStatusPending,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}
if _, err := tx.Model(adminOutboxTable).Safe().Ctx(ctx).Data(outbox).Insert(); err != nil {
return gerror.Wrap(err, "创建IM后台命令Outbox失败") return gerror.Wrap(err, "创建IM后台命令Outbox失败")
} }
return nil return nil
@@ -125,11 +102,9 @@ func PublishAdminCommand(ctx context.Context, in AdminCommandInput) (res *AdminC
} }
func DispatchOutboxByEvent(ctx context.Context, eventID string) error { func DispatchOutboxByEvent(ctx context.Context, eventID string) error {
var row gdb.Record row, err := adminDB().GetOne(ctx, `SELECT * FROM im_admin_outbox WHERE event_id=? AND status=? LIMIT 1`,
if err := g.DB().Model(adminOutboxTable).Safe().Ctx(ctx). eventID, consts.ImAdminOutboxStatusPending)
Where("event_id", eventID). if err != nil {
Where("status", consts.ImAdminOutboxStatusPending).
Scan(&row); err != nil {
return gerror.Wrap(err, "读取IM命令Outbox失败") return gerror.Wrap(err, "读取IM命令Outbox失败")
} }
if row.IsEmpty() { if row.IsEmpty() {
@@ -171,19 +146,12 @@ func PublishConfigEvent(ctx context.Context, in ConfigEventInput) error {
if err != nil { if err != nil {
return gerror.Wrap(err, "序列化IM配置事件Envelope失败") return gerror.Wrap(err, "序列化IM配置事件Envelope失败")
} }
_, err = g.DB().Model(adminOutboxTable).Safe().Ctx(ctx).Data(g.Map{ nowTime := gtime.Now()
"event_id": eventID, _, err = adminDB().Exec(ctx, `INSERT INTO im_admin_outbox
"command_id": "", (event_id,command_id,topic,event_type,biz_type,biz_id,key_value,envelope,status,created_at,updated_at)
"topic": consts.QueueImConfigEventTopic, VALUES(?,?,?,?,?,?,?,CAST(? AS JSON),?,?,?)`,
"event_type": in.EventType, eventID, "", consts.QueueImConfigEventTopic, in.EventType, "config", in.BizID, in.TenantCode,
"biz_type": "config", string(envelopeBytes), consts.ImAdminOutboxStatusPending, nowTime, nowTime)
"biz_id": in.BizID,
"key_value": in.TenantCode,
"envelope": string(envelopeBytes),
"status": consts.ImAdminOutboxStatusPending,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil { if err != nil {
return gerror.Wrap(err, "创建IM配置事件Outbox失败") return gerror.Wrap(err, "创建IM配置事件Outbox失败")
} }
@@ -230,19 +198,12 @@ func PublishEvent(ctx context.Context, in EventInput) (eventID string, err error
if err != nil { if err != nil {
return "", gerror.Wrap(err, "序列化IM Kafka事件Envelope失败") return "", gerror.Wrap(err, "序列化IM Kafka事件Envelope失败")
} }
_, err = g.DB().Model(adminOutboxTable).Safe().Ctx(ctx).Data(g.Map{ nowTime := gtime.Now()
"event_id": eventID, _, err = adminDB().Exec(ctx, `INSERT INTO im_admin_outbox
"command_id": "", (event_id,command_id,topic,event_type,biz_type,biz_id,key_value,envelope,status,created_at,updated_at)
"topic": in.Topic, VALUES(?,?,?,?,?,?,?,CAST(? AS JSON),?,?,?)`,
"event_type": in.EventType, eventID, "", in.Topic, in.EventType, in.BizType, in.BizID, in.BizID,
"biz_type": in.BizType, string(envelopeBytes), consts.ImAdminOutboxStatusPending, nowTime, nowTime)
"biz_id": in.BizID,
"key_value": in.BizID,
"envelope": string(envelopeBytes),
"status": consts.ImAdminOutboxStatusPending,
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil { if err != nil {
return "", gerror.Wrap(err, "创建IM Kafka事件Outbox失败") return "", gerror.Wrap(err, "创建IM Kafka事件Outbox失败")
} }
@@ -256,13 +217,9 @@ func DispatchPendingOutbox(ctx context.Context, limit int) (sent int, err error)
if limit <= 0 { if limit <= 0 {
limit = 100 limit = 100
} }
var rows []gdb.Record rows, err := adminDB().GetAll(ctx, `SELECT * FROM im_admin_outbox
err = g.DB().Model(adminOutboxTable).Safe().Ctx(ctx). WHERE status IN (?,?) AND (next_retry_at IS NULL OR next_retry_at <= NOW())
WhereIn("status", []string{consts.ImAdminOutboxStatusPending, consts.ImAdminOutboxStatusFailed}). ORDER BY id ASC LIMIT ?`, consts.ImAdminOutboxStatusPending, consts.ImAdminOutboxStatusFailed, limit)
Where("next_retry_at IS NULL OR next_retry_at <= NOW()").
OrderAsc("id").
Limit(limit).
Scan(&rows)
if err != nil { if err != nil {
return 0, gerror.Wrap(err, "读取待投递IM命令Outbox失败") return 0, gerror.Wrap(err, "读取待投递IM命令Outbox失败")
} }
@@ -294,27 +251,16 @@ func dispatchOutboxRecord(ctx context.Context, row gdb.Record) error {
return err return err
} }
now := gtime.Now() now := gtime.Now()
_, err := g.DB().Model(adminOutboxTable).Safe().Ctx(ctx). _, err := adminDB().Exec(ctx, `UPDATE im_admin_outbox
Where("event_id", eventID). SET status=?,attempts=attempts+1,sent_at=?,last_reason='',last_error='',updated_at=?
Data(g.Map{ WHERE event_id=?`, consts.ImAdminOutboxStatusSent, now, now, eventID)
"status": consts.ImAdminOutboxStatusSent,
"attempts": gdb.Raw("attempts + 1"),
"sent_at": now,
"last_reason": "",
"last_error": "",
"updated_at": now,
}).Update()
if err != nil { if err != nil {
return gerror.Wrap(err, "更新IM命令Outbox投递状态失败") return gerror.Wrap(err, "更新IM命令Outbox投递状态失败")
} }
if commandID != "" { if commandID != "" {
_, err = g.DB().Model(adminCommandTable).Safe().Ctx(ctx). _, err = adminDB().Exec(ctx, `UPDATE im_admin_commands
Where("command_id", commandID). SET status=?,sent_at=?,updated_at=? WHERE command_id=?`,
Data(g.Map{ consts.ImAdminCommandStatusRunning, now, now, commandID)
"status": consts.ImAdminCommandStatusRunning,
"sent_at": now,
"updated_at": now,
}).Update()
if err != nil { if err != nil {
return gerror.Wrap(err, "更新IM后台命令投递状态失败") return gerror.Wrap(err, "更新IM后台命令投递状态失败")
} }
@@ -324,16 +270,9 @@ func dispatchOutboxRecord(ctx context.Context, row gdb.Record) error {
func markOutboxFailed(ctx context.Context, eventID string, reason string, message string) error { func markOutboxFailed(ctx context.Context, eventID string, reason string, message string) error {
now := gtime.Now() now := gtime.Now()
_, err := g.DB().Model(adminOutboxTable).Safe().Ctx(ctx). _, err := adminDB().Exec(ctx, `UPDATE im_admin_outbox
Where("event_id", eventID). SET status=?,attempts=attempts+1,next_retry_at=?,last_reason=?,last_error=?,updated_at=?
Data(g.Map{ WHERE event_id=?`, consts.ImAdminOutboxStatusFailed, now.Add(30*time.Second), reason, message, now, eventID)
"status": consts.ImAdminOutboxStatusFailed,
"attempts": gdb.Raw("attempts + 1"),
"next_retry_at": now.Add(30 * time.Second),
"last_reason": reason,
"last_error": message,
"updated_at": now,
}).Update()
if err != nil { if err != nil {
return gerror.Wrap(err, "更新IM命令Outbox失败状态失败") return gerror.Wrap(err, "更新IM命令Outbox失败状态失败")
} }
@@ -364,15 +303,27 @@ func ApplyCommandResult(ctx context.Context, result CommandResult) error {
if len(result.Result) > 0 { if len(result.Result) > 0 {
data["result"] = string(result.Result) data["result"] = string(result.Result)
} }
mod := g.DB().Model(adminCommandTable).Safe().Ctx(ctx) whereColumn := "event_id"
whereValue := result.CommandEventID
if result.CommandID != "" { if result.CommandID != "" {
mod = mod.Where("command_id", result.CommandID) whereColumn = "command_id"
} else { whereValue = result.CommandID
mod = mod.Where("event_id", result.CommandEventID)
} }
_, err := mod.Data(data).Update() sql := `UPDATE im_admin_commands SET status=?,reason=?,message=?,completed_at=?,updated_at=?`
args := []any{data["status"], data["reason"], data["message"], data["completed_at"], data["updated_at"]}
if resultValue, ok := data["result"]; ok {
sql += `,result=CAST(? AS JSON)`
args = append(args, resultValue)
}
sql += ` WHERE ` + whereColumn + `=?`
args = append(args, whereValue)
_, err := adminDB().Exec(ctx, sql, args...)
if err != nil { if err != nil {
return gerror.Wrap(err, "更新IM后台命令结果失败") return gerror.Wrap(err, "更新IM后台命令结果失败")
} }
return nil return nil
} }
func adminDB() gdb.DB {
return imtenant.RegistryDB()
}
+58 -12
View File
@@ -7,6 +7,7 @@ import (
"hash/fnv" "hash/fnv"
"hotgo/internal/dao" "hotgo/internal/dao"
"hotgo/internal/model/entity" "hotgo/internal/model/entity"
"net/url"
"strings" "strings"
"sync" "sync"
@@ -20,14 +21,7 @@ var tenantDbGroups sync.Map
const registryDbGroup = "imRegistry" const registryDbGroup = "imRegistry"
// RegistryDB returns the tenant registry DB. It falls back to the default DB for var registryDbGroupOnce sync.Once
// single-database deployments where database.imRegistry is not configured.
func RegistryDB(ctx context.Context) gdb.DB {
if configs, err := gdb.GetConfigGroup(registryDbGroup); err == nil && len(configs) > 0 {
return g.DB(registryDbGroup)
}
return g.DB()
}
// DB resolves a tenant registry record and returns the corresponding business DB. // DB resolves a tenant registry record and returns the corresponding business DB.
func DB(ctx context.Context, tenantId int64) (db gdb.DB, tenant *entity.ImTenant, err error) { func DB(ctx context.Context, tenantId int64) (db gdb.DB, tenant *entity.ImTenant, err error) {
@@ -35,7 +29,7 @@ func DB(ctx context.Context, tenantId int64) (db gdb.DB, tenant *entity.ImTenant
return nil, nil, gerror.New("租户ID不能为空") return nil, nil, gerror.New("租户ID不能为空")
} }
if err = RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx).WherePri(tenantId).Scan(&tenant); err != nil { if err = RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx).WherePri(tenantId).Scan(&tenant); err != nil {
return nil, nil, gerror.Wrap(err, "获取租户信息失败") return nil, nil, gerror.Wrap(err, "获取租户信息失败")
} }
if tenant == nil || tenant.Id <= 0 { if tenant == nil || tenant.Id <= 0 {
@@ -65,9 +59,40 @@ func DB(ctx context.Context, tenantId int64) (db gdb.DB, tenant *entity.ImTenant
return db, tenant, nil return db, tenant, nil
} }
// RegistryDB returns the dedicated tenant registry connection, or the default
// HotGo database when a separate registry group is not configured.
func RegistryDB() gdb.DB {
registryDbGroupOnce.Do(func() {
ensureRegistryDBGroup()
})
if configs, err := gdb.GetConfigGroup(registryDbGroup); err == nil && len(configs) > 0 {
return g.DB(registryDbGroup)
}
return g.DB()
}
func ensureRegistryDBGroup() {
if configs, err := gdb.GetConfigGroup(registryDbGroup); err == nil && len(configs) > 0 {
configs[0].Prefix = ""
_ = gdb.SetConfigGroup(registryDbGroup, configs)
return
}
configs, err := gdb.GetConfigGroup("default")
if err != nil || len(configs) == 0 {
return
}
cloned := make(gdb.ConfigGroup, len(configs))
copy(cloned, configs)
for i := range cloned {
cloned[i].Prefix = ""
}
_ = gdb.SetConfigGroup(registryDbGroup, cloned)
}
func tenantLink(ctx context.Context, tenant *entity.ImTenant) (string, error) { func tenantLink(ctx context.Context, tenant *entity.ImTenant) (string, error) {
if tenant.DbDsn != "" { if tenant.DbDsn != "" {
return tenant.DbDsn, nil return normalizeMySQLTimeOptions(tenant.DbDsn)
} }
if tenant.DbName == "" { if tenant.DbName == "" {
return "", gerror.New("租户业务库未配置") return "", gerror.New("租户业务库未配置")
@@ -115,10 +140,31 @@ func replaceMySQLDatabase(link string, dbName string) (string, error) {
dbStart += 2 dbStart += 2
queryStart := strings.Index(link[dbStart:], "?") queryStart := strings.Index(link[dbStart:], "?")
if queryStart < 0 { if queryStart < 0 {
return link[:dbStart] + dbName, nil return normalizeMySQLTimeOptions(link[:dbStart] + dbName)
} }
queryStart += dbStart queryStart += dbStart
return link[:dbStart] + dbName + link[queryStart:], nil return normalizeMySQLTimeOptions(link[:dbStart] + dbName + link[queryStart:])
}
func normalizeMySQLTimeOptions(link string) (string, error) {
if !strings.HasPrefix(link, "mysql:") {
if !strings.Contains(link, "@tcp(") {
return "", gerror.New("租户业务库仅支持 MySQL")
}
link = "mysql:" + link
}
parts := strings.SplitN(link, "?", 2)
values := make(url.Values)
if len(parts) == 2 {
var err error
values, err = url.ParseQuery(parts[1])
if err != nil {
return "", gerror.Wrap(err, "租户业务库连接参数格式不正确")
}
}
values.Set("loc", "UTC")
values.Set("parseTime", "true")
return parts[0] + "?" + values.Encode(), nil
} }
func hash(s string) uint64 { func hash(s string) uint64 {
@@ -0,0 +1,52 @@
package imtenant
import (
"context"
"strings"
"testing"
)
func TestNormalizeMySQLTimeOptions(t *testing.T) {
link, err := normalizeMySQLTimeOptions("user:pass@tcp(127.0.0.1:3306)/tinode?charset=utf8mb4")
if err != nil {
t.Fatalf("normalize raw mysql dsn: %v", err)
}
if !strings.HasPrefix(link, "mysql:user:pass@tcp(127.0.0.1:3306)/tinode?") {
t.Fatalf("unexpected normalized link: %s", link)
}
if !strings.Contains(link, "loc=UTC") || !strings.Contains(link, "parseTime=true") {
t.Fatalf("missing required time options: %s", link)
}
}
func TestNormalizeMySQLTimeOptionsRejectsUnsupportedFormat(t *testing.T) {
if _, err := normalizeMySQLTimeOptions("postgres://user:pass@127.0.0.1:5432/tinode"); err == nil {
t.Fatal("expected unsupported format error")
}
}
func TestReplaceMySQLDatabaseUsesUTC(t *testing.T) {
link, err := replaceMySQLDatabase("mysql:user:pass@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=false&charset=utf8mb4", "tinode")
if err != nil {
t.Fatal(err)
}
for _, expected := range []string{"/tinode?", "loc=UTC", "parseTime=true", "charset=utf8mb4"} {
if !strings.Contains(link, expected) {
t.Fatalf("link %q does not contain %q", link, expected)
}
}
}
func TestNextSnowflakeID(t *testing.T) {
first, err := NextSnowflakeID(context.Background())
if err != nil {
t.Fatal(err)
}
second, err := NextSnowflakeID(context.Background())
if err != nil {
t.Fatal(err)
}
if first <= 0 || second <= first {
t.Fatalf("Snowflake IDs are not increasing: %d, %d", first, second)
}
}
@@ -0,0 +1,56 @@
package imtenant
import (
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
)
func TestNoDirectDefaultDBModelForIMTables(t *testing.T) {
_, file, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("runtime caller unavailable")
}
serverRoot := filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..", ".."))
internalRoot := filepath.Join(serverRoot, "internal")
patterns := []*regexp.Regexp{
regexp.MustCompile(`g\.DB\(\)\.Model\(\s*"im_`),
regexp.MustCompile(`g\.DB\(\)\.Model\(\s*` + "`" + `im_`),
regexp.MustCompile(`g\.DB\(\)\.(Insert|InsertIgnore|Replace|Save|Update|Delete)\([^,\n]+,\s*"im_`),
regexp.MustCompile(`g\.DB\(\)\.(Insert|InsertIgnore|Replace|Save|Update|Delete)\([^,\n]+,\s*` + "`" + `im_`),
}
var violations []string
err := filepath.WalkDir(internalRoot, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
return nil
}
content, err := os.ReadFile(path)
if err != nil {
return err
}
for _, pattern := range patterns {
if pattern.Match(content) {
rel, _ := filepath.Rel(serverRoot, path)
violations = append(violations, rel)
break
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
if len(violations) > 0 {
t.Fatalf("IM public tables must not use default g.DB() model helpers because the default DB has table prefix: %s", strings.Join(violations, ", "))
}
}
@@ -281,6 +281,8 @@ func (client *Client) handleRoutineTask(ctx context.Context, task func()) {
// Conn 获取当前连接 // Conn 获取当前连接
func (client *Client) Conn() *Conn { func (client *Client) Conn() *Conn {
client.mutex.Lock()
defer client.mutex.Unlock()
return client.conn return client.conn
} }
+6 -2
View File
@@ -43,7 +43,6 @@ func (r *RPC) Request(ctx context.Context, msgId string, send func()) (res inter
defer func() { defer func() {
isClose.Set(true) isClose.Set(true)
close(resCh)
r.popCallback(msgId) r.popCallback(msgId)
}() }()
@@ -57,8 +56,13 @@ func (r *RPC) Request(ctx context.Context, msgId string, send func()) (res inter
r.task(ctx, send) r.task(ctx, send)
timer := time.NewTimer(time.Second * RPCTimeout)
defer timer.Stop()
select { select {
case <-time.After(time.Second * RPCTimeout): case <-ctx.Done():
err = gerror.Wrap(ctx.Err(), "RPC request canceled")
return
case <-timer.C:
err = gerror.New("RPC response timeout") err = gerror.New("RPC response timeout")
return return
case got := <-resCh: case got := <-resCh:
@@ -1,21 +1,14 @@
// Package tcp_test
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcp_test package tcp_test
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/gogf/gf/v2/os/gctx" "net"
"github.com/gogf/gf/v2/test/gtest"
"hotgo/internal/library/network/tcp"
"testing" "testing"
"time" "time"
)
var T *testing.T // 声明一个全局的 *testing.T 变量 "hotgo/internal/library/network/tcp"
)
type TestMsgReq struct { type TestMsgReq struct {
Name string `json:"name"` Name string `json:"name"`
@@ -33,101 +26,95 @@ type TestRPCMsgRes struct {
tcp.ServerRes tcp.ServerRes
} }
func onTestMsg(ctx context.Context, req *TestMsgReq) {
fmt.Printf("服务器收到消息 ==> onTestMsg:%+v\n", req)
conn := tcp.ConnFromCtx(ctx)
gtest.C(T, func(t *gtest.T) {
t.AssertNE(conn, nil)
})
res := new(TestMsgRes)
res.Message = fmt.Sprintf("你的名字:%v", req.Name)
conn.Send(ctx, res)
}
func onResponseTestMsg(ctx context.Context, req *TestMsgRes) {
fmt.Printf("客户端收到响应消息 ==> TestMsgRes:%+v\n", req)
err := req.GetError()
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}
func onTestRPCMsg(ctx context.Context, req *TestRPCMsgReq) (res *TestRPCMsgRes, err error) {
fmt.Printf("服务器收到消息 ==> onTestRPCMsg:%+v\n", req)
res = new(TestRPCMsgRes)
res.Message = fmt.Sprintf("你的名字:%v", req.Name)
return
}
func startTCPServer() {
serv := tcp.NewServer(&tcp.ServerConfig{
Name: "hotgo",
Addr: ":8002",
})
// 注册路由
serv.RegisterRouter(
onTestMsg,
)
// 注册RPC路由
serv.RegisterRPCRouter(
onTestRPCMsg,
)
// 服务监听
err := serv.Listen()
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}
// 一个基本的消息收发
func TestSendMsg(t *testing.T) { func TestSendMsg(t *testing.T) {
T = t address := freeTCPAddress(t)
go startTCPServer() messageResponse := make(chan string, 1)
server := tcp.NewServer(&tcp.ServerConfig{Name: "hotgo-test", Addr: address})
ctx := gctx.New() server.RegisterRouter(func(ctx context.Context, req *TestMsgReq) {
client := tcp.NewClient(&tcp.ClientConfig{ res := &TestMsgRes{}
Addr: "127.0.0.1:8002", res.SetMessage(fmt.Sprintf("你的名字:%s", req.Name))
if err := tcp.ConnFromCtx(ctx).Send(ctx, res); err != nil {
messageResponse <- "send-error:" + err.Error()
}
}) })
server.RegisterRPCRouter(func(ctx context.Context, req *TestRPCMsgReq) (*TestRPCMsgRes, error) {
// 注册路由 res := &TestRPCMsgRes{}
client.RegisterRouter( res.SetMessage(fmt.Sprintf("你的名字:%s", req.Name))
onResponseTestMsg, return res, nil
) })
serverErrors := make(chan error, 1)
go func() { go func() {
err := client.Start() serverErrors <- server.Listen()
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}() }()
defer server.Close()
// 确保服务都启动完成 client := tcp.NewClient(&tcp.ClientConfig{
time.Sleep(time.Second * 1) Addr: address,
Timeout: time.Second,
// 拿到客户端的连接 ConnectInterval: 10 * time.Millisecond,
conn := client.Conn() MaxConnectCount: 100,
gtest.C(T, func(t *gtest.T) {
t.AssertNE(conn, nil)
}) })
client.RegisterRouter(func(ctx context.Context, res *TestMsgRes) {
// 向服务器发送tcp消息,不会阻塞程序执行 messageResponse <- res.Message
err := conn.Send(ctx, &TestMsgReq{Name: "Tom"})
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
}) })
if err := client.Start(); err != nil {
t.Fatalf("启动TCP客户端失败: %v", err)
}
defer client.Stop()
// 向服务器发送rpc消息,会等待服务器响应结果,直到拿到结果或响应超时才会继续 conn := waitForConnection(t, client, serverErrors)
var res TestRPCMsgRes ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
if err = conn.RequestScan(ctx, &TestRPCMsgReq{Name: "Tony"}, &res); err != nil { defer cancel()
gtest.C(T, func(t *gtest.T) { if err := conn.Send(ctx, &TestMsgReq{Name: "Tom"}); err != nil {
t.AssertNil(err) t.Fatalf("发送TCP消息失败: %v", err)
}) }
select {
case message := <-messageResponse:
if message != "你的名字:Tom" {
t.Fatalf("TCP响应不符: %s", message)
}
case <-ctx.Done():
t.Fatal("等待TCP响应超时")
} }
fmt.Printf("客户端收到RPC消息响应 ==> TestRPCMsgRes:%+v\n", res) var rpcResponse TestRPCMsgRes
time.Sleep(time.Second * 1) if err := conn.RequestScan(ctx, &TestRPCMsgReq{Name: "Tony"}, &rpcResponse); err != nil {
t.Fatalf("发送RPC消息失败: %v", err)
}
if rpcResponse.Message != "你的名字:Tony" {
t.Fatalf("RPC响应不符: %s", rpcResponse.Message)
}
}
func freeTCPAddress(t *testing.T) string {
t.Helper()
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("申请测试端口失败: %v", err)
}
address := listener.Addr().String()
if err = listener.Close(); err != nil {
t.Fatalf("释放测试端口失败: %v", err)
}
return address
}
func waitForConnection(t *testing.T, client *tcp.Client, serverErrors <-chan error) *tcp.Conn {
t.Helper()
deadline := time.NewTimer(2 * time.Second)
defer deadline.Stop()
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
for {
select {
case err := <-serverErrors:
t.Fatalf("TCP服务启动失败: %v", err)
case <-deadline.C:
t.Fatal("等待TCP连接超时")
case <-ticker.C:
if conn := client.Conn(); conn != nil {
return conn
}
}
}
} }
+2 -2
View File
@@ -55,7 +55,7 @@ func consumerListen(ctx context.Context, job Consumer) {
) )
if err != nil { if err != nil {
Logger().Fatalf(ctx, "InstanceConsumer %s err:%+v", topic, err) Logger().Errorf(ctx, "InstanceConsumer %s err:%+v", topic, err)
return return
} }
@@ -70,6 +70,6 @@ func consumerListen(ctx context.Context, job Consumer) {
// 记录消费队列日志 // 记录消费队列日志
ConsumerLog(ctx, topic, mqMsg, err) ConsumerLog(ctx, topic, mqMsg, err)
}); listenErr != nil { }); listenErr != nil {
Logger().Fatalf(ctx, "消费队列:%s 监听失败, err:%+v", topic, listenErr) Logger().Errorf(ctx, "消费队列:%s 监听失败, err:%+v", topic, listenErr)
} }
} }
+112 -65
View File
@@ -7,7 +7,10 @@ package queue
import ( import (
"context" "context"
"crypto/tls" "crypto/sha256"
"crypto/sha512"
"errors"
"hash"
"strings" "strings"
"time" "time"
@@ -15,6 +18,7 @@ import (
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/xdg-go/scram" "github.com/xdg-go/scram"
"hotgo/internal/consts" "hotgo/internal/consts"
"hotgo/utility/simple" "hotgo/utility/simple"
) )
@@ -26,17 +30,37 @@ type KafkaMq struct {
} }
type KafkaConfig struct { type KafkaConfig struct {
ClientId string ClientId string
Brokers []string Brokers []string
GroupID string GroupID string
Partitions int32 Partitions int32
Replication int16 Replication int16
Version string Version string
UserName string UserName string
Password string Password string
SASLEnabled bool Mechanism string
SASLMechanism string }
TLSEnabled bool
type xdgSCRAMClient struct {
*scram.ClientConversation
HashGeneratorFcn scram.HashGeneratorFcn
}
func (x *xdgSCRAMClient) Begin(userName, password, authzID string) error {
client, err := x.HashGeneratorFcn.NewClient(userName, password, authzID)
if err != nil {
return err
}
x.ClientConversation = client.NewConversation()
return nil
}
func (x *xdgSCRAMClient) Step(challenge string) (string, error) {
return x.ClientConversation.Step(challenge)
}
func (x *xdgSCRAMClient) Done() bool {
return x.ClientConversation.Done()
} }
// SendMsg 按字符串类型生产数据 // SendMsg 按字符串类型生产数据
@@ -100,11 +124,16 @@ func (r *KafkaMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg))
go func(consumerCtx context.Context) { go func(consumerCtx context.Context) {
for { for {
if err = r.consumerIns.Consume(consumerCtx, []string{topic}, &consumer); err != nil { if err = r.consumerIns.Consume(consumerCtx, []string{topic}, &consumer); err != nil {
Logger().Fatalf(ctx, "kafka Error from consumer, err%+v", err) Logger().Errorf(ctx, "kafka error from consumer, topic:%s err:%+v", topic, err)
select {
case <-consumerCtx.Done():
return
case <-time.After(3 * time.Second):
}
} }
if consumerCtx.Err() != nil { if consumerCtx.Err() != nil {
Logger().Debugf(ctx, "kafka consoumer stop : %v", consumerCtx.Err()) Logger().Debugf(ctx, "kafka consumer stop : %v", consumerCtx.Err())
return return
} }
consumer.ready = make(chan bool) consumer.ready = make(chan bool)
@@ -112,14 +141,18 @@ func (r *KafkaMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg))
}(consumerCtx) }(consumerCtx)
// await till the consumer has been set up // await till the consumer has been set up
<-consumer.ready select {
case <-consumer.ready:
case <-time.After(10 * time.Second):
Logger().Warningf(ctx, "kafka consumer setup timeout, topic:%s", topic)
}
Logger().Debug(ctx, "kafka consumer up and running!...") Logger().Debug(ctx, "kafka consumer up and running!...")
simple.Event().Register(consts.EventServerClose, func(ctx context.Context, args ...interface{}) { simple.Event().Register(consts.EventServerClose, func(ctx context.Context, args ...interface{}) {
Logger().Debug(ctx, "kafka consumer close...") Logger().Debug(ctx, "kafka consumer close...")
cancel() cancel()
if err = r.consumerIns.Close(); err != nil { if err = r.consumerIns.Close(); err != nil {
Logger().Fatalf(ctx, "kafka Error closing client, err:%+v", err) Logger().Errorf(ctx, "kafka error closing client, err:%+v", err)
} }
}) })
return return
@@ -128,7 +161,7 @@ func (r *KafkaMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg))
// RegisterKafkaMqConsumer 注册消费者 // RegisterKafkaMqConsumer 注册消费者
func RegisterKafkaMqConsumer(connOpt KafkaConfig) (client MqConsumer, err error) { func RegisterKafkaMqConsumer(connOpt KafkaConfig) (client MqConsumer, err error) {
mqIns := &KafkaMq{} mqIns := &KafkaMq{}
kfkVersion, err := sarama.ParseKafkaVersion(connOpt.Version) kfkVersion, err := parseKafkaVersion(connOpt.Version)
if err != nil { if err != nil {
return return
} }
@@ -140,9 +173,7 @@ func RegisterKafkaMqConsumer(connOpt KafkaConfig) (client MqConsumer, err error)
conf := sarama.NewConfig() conf := sarama.NewConfig()
conf.Consumer.Return.Errors = true conf.Consumer.Return.Errors = true
conf.Version = kfkVersion conf.Version = kfkVersion
if err = configureKafkaSecurity(conf, connOpt); err != nil { configureKafkaSASL(conf, connOpt)
return
}
// 默认按随机方式消费 // 默认按随机方式消费
conf.Consumer.Group.Rebalance.Strategy = sarama.BalanceStrategyRange conf.Consumer.Group.Rebalance.Strategy = sarama.BalanceStrategyRange
@@ -161,9 +192,7 @@ func RegisterKafkaMqConsumer(connOpt KafkaConfig) (client MqConsumer, err error)
// RegisterKafkaProducer 注册并启动生产者接口实现 // RegisterKafkaProducer 注册并启动生产者接口实现
func RegisterKafkaProducer(connOpt KafkaConfig) (client MqProducer, err error) { func RegisterKafkaProducer(connOpt KafkaConfig) (client MqProducer, err error) {
mqIns := &KafkaMq{} mqIns := &KafkaMq{}
if connOpt.ClientId == "" { connOpt.ClientId = "HOTGO-Producer"
connOpt.ClientId = "hotgo-producer"
}
// 这里如果使用go程需要处理chan同步问题 // 这里如果使用go程需要处理chan同步问题
if err = doRegisterKafkaProducer(connOpt, mqIns); err != nil { if err = doRegisterKafkaProducer(connOpt, mqIns); err != nil {
@@ -175,7 +204,7 @@ func RegisterKafkaProducer(connOpt KafkaConfig) (client MqProducer, err error) {
// doRegisterKafkaProducer 注册同步类型实例 // doRegisterKafkaProducer 注册同步类型实例
func doRegisterKafkaProducer(connOpt KafkaConfig, mqIns *KafkaMq) (err error) { func doRegisterKafkaProducer(connOpt KafkaConfig, mqIns *KafkaMq) (err error) {
kfkVersion, err := sarama.ParseKafkaVersion(connOpt.Version) kfkVersion, err := parseKafkaVersion(connOpt.Version)
if err != nil { if err != nil {
return return
} }
@@ -197,9 +226,7 @@ func doRegisterKafkaProducer(connOpt KafkaConfig, mqIns *KafkaMq) (err error) {
conf.ClientID = connOpt.ClientId conf.ClientID = connOpt.ClientId
conf.Version = kfkVersion conf.Version = kfkVersion
if err = configureKafkaSecurity(conf, connOpt); err != nil { configureKafkaSASL(conf, connOpt)
return
}
mqIns.producerIns, err = sarama.NewAsyncProducer(brokers, conf) mqIns.producerIns, err = sarama.NewAsyncProducer(brokers, conf)
if err != nil { if err != nil {
@@ -213,61 +240,81 @@ func doRegisterKafkaProducer(connOpt KafkaConfig, mqIns *KafkaMq) (err error) {
return return
} }
func configureKafkaSecurity(conf *sarama.Config, connOpt KafkaConfig) error { func parseKafkaVersion(version string) (sarama.KafkaVersion, error) {
if connOpt.TLSEnabled { parts := strings.Split(version, ".")
conf.Net.TLS.Enable = true if len(parts) == 4 && parts[0] != "0" && parts[3] == "0" {
conf.Net.TLS.Config = &tls.Config{MinVersion: tls.VersionTLS12} version = strings.Join(parts[:3], ".")
} }
if !connOpt.SASLEnabled && connOpt.UserName == "" { return sarama.ParseKafkaVersion(version)
return nil }
}
if connOpt.UserName == "" || connOpt.Password == "" { func configureKafkaSASL(conf *sarama.Config, connOpt KafkaConfig) {
return gerror.New("queue kafka sasl username or password is empty") if connOpt.UserName == "" {
return
} }
conf.Net.SASL.Enable = true conf.Net.SASL.Enable = true
conf.Net.SASL.User = connOpt.UserName conf.Net.SASL.User = connOpt.UserName
conf.Net.SASL.Password = connOpt.Password conf.Net.SASL.Password = connOpt.Password
mechanism := strings.ToUpper(strings.ReplaceAll(connOpt.SASLMechanism, "_", "-")) mechanism := strings.ToUpper(strings.ReplaceAll(connOpt.Mechanism, "_", "-"))
if mechanism == "" {
mechanism = sarama.SASLTypeSCRAMSHA512
}
switch mechanism { switch mechanism {
case "", "PLAIN": case sarama.SASLTypeSCRAMSHA512:
conf.Net.SASL.Mechanism = sarama.SASLTypePlaintext conf.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA512
case "SCRAM-SHA-256": conf.Net.SASL.SCRAMClientGeneratorFunc = scramClientGenerator(sha512.New)
case sarama.SASLTypeSCRAMSHA256:
conf.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA256 conf.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA256
conf.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { conf.Net.SASL.SCRAMClientGeneratorFunc = scramClientGenerator(sha256.New)
return &xdgSCRAMClient{HashGeneratorFcn: scram.SHA256}
}
case "SCRAM-SHA-512":
conf.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA512
conf.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient {
return &xdgSCRAMClient{HashGeneratorFcn: scram.SHA512}
}
default: default:
return gerror.New("queue kafka sasl mechanism is not support") conf.Net.SASL.Mechanism = sarama.SASLMechanism(mechanism)
} }
return nil
} }
type xdgSCRAMClient struct { // KafkaHealthCheck verifies broker connectivity by creating a short-lived Sarama client.
*scram.Client func KafkaHealthCheck(ctx context.Context) error {
*scram.ClientConversation if len(config.Kafka.Address) == 0 {
scram.HashGeneratorFcn return errors.New("queue kafka address is empty")
} }
kfkVersion, err := parseKafkaVersion(config.Kafka.Version)
func (x *xdgSCRAMClient) Begin(userName, password, authzID string) (err error) {
x.Client, err = x.HashGeneratorFcn.NewClient(userName, password, authzID)
if err != nil { if err != nil {
return err return err
} }
x.ClientConversation = x.Client.NewConversation() if !validateVersion(kfkVersion) {
kfkVersion = sarama.V2_4_0_0
}
conf := sarama.NewConfig()
conf.Version = kfkVersion
conf.ClientID = "hotgo-healthz"
conf.Net.DialTimeout = 2 * time.Second
conf.Net.ReadTimeout = 2 * time.Second
conf.Net.WriteTimeout = 2 * time.Second
configureKafkaSASL(conf, KafkaConfig{
UserName: config.Kafka.Username,
Password: config.Kafka.Password,
Mechanism: config.Kafka.Mechanism,
})
select {
case <-ctx.Done():
return ctx.Err()
default:
}
client, err := sarama.NewClient(config.Kafka.Address, conf)
if err != nil {
return err
}
defer func() { _ = client.Close() }()
if len(client.Brokers()) == 0 {
return errors.New("no kafka brokers available")
}
return nil return nil
} }
func (x *xdgSCRAMClient) Step(challenge string) (response string, err error) { func scramClientGenerator(hashFunc func() hash.Hash) func() sarama.SCRAMClient {
return x.ClientConversation.Step(challenge) return func() sarama.SCRAMClient {
} return &xdgSCRAMClient{HashGeneratorFcn: scram.HashGeneratorFcn(hashFunc)}
}
func (x *xdgSCRAMClient) Done() bool {
return x.ClientConversation.Done()
} }
// validateVersion 验证版本是否有效 // validateVersion 验证版本是否有效
+16 -51
View File
@@ -11,8 +11,6 @@ import (
"github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gctx"
"hotgo/internal/library/queue/disk" "hotgo/internal/library/queue/disk"
"hotgo/utility/charset" "hotgo/utility/charset"
"os"
"strings"
"sync" "sync"
"time" "time"
) )
@@ -59,14 +57,11 @@ type RocketmqConf struct {
type KafkaConf struct { type KafkaConf struct {
Address []string `json:"address"` Address []string `json:"address"`
Version string `json:"version"` Version string `json:"version"`
Username string `json:"username"`
Password string `json:"password"`
Mechanism string `json:"mechanism"`
RandClient bool `json:"randClient"` RandClient bool `json:"randClient"`
MultiConsumer bool `json:"multiConsumer"` MultiConsumer bool `json:"multiConsumer"`
ClientId string `json:"clientId"`
UserName string `json:"userName"`
Password string `json:"password"`
SASLEnabled bool `json:"saslEnabled"`
SASLMechanism string `json:"saslMechanism"`
TLSEnabled bool `json:"tlsEnabled"`
} }
type MqMsg struct { type MqMsg struct {
@@ -93,7 +88,6 @@ func init() {
if err := g.Cfg().MustGet(ctx, "queue").Scan(&config); err != nil { if err := g.Cfg().MustGet(ctx, "queue").Scan(&config); err != nil {
Logger().Warningf(ctx, "queue init err:%+v", err) Logger().Warningf(ctx, "queue init err:%+v", err)
} }
normalizeKafkaConf()
} }
// InstanceConsumer 实例化消费者 // InstanceConsumer 实例化消费者
@@ -130,15 +124,12 @@ func NewProducer(groupName string) (mqClient MqProducer, err error) {
return return
} }
mqClient, err = RegisterKafkaProducer(KafkaConfig{ mqClient, err = RegisterKafkaProducer(KafkaConfig{
Brokers: config.Kafka.Address, Brokers: config.Kafka.Address,
GroupID: groupName, GroupID: groupName,
Version: config.Kafka.Version, Version: config.Kafka.Version,
ClientId: firstKafkaClientId(config.Kafka.ClientId, "hotgo-producer"), UserName: config.Kafka.Username,
UserName: config.Kafka.UserName, Password: config.Kafka.Password,
Password: config.Kafka.Password, Mechanism: config.Kafka.Mechanism,
SASLEnabled: config.Kafka.SASLEnabled,
SASLMechanism: config.Kafka.SASLMechanism,
TLSEnabled: config.Kafka.TLSEnabled,
}) })
case "redis": case "redis":
if _, err = g.Redis().Do(ctx, "ping"); err == nil { if _, err = g.Redis().Do(ctx, "ping"); err == nil {
@@ -200,15 +191,13 @@ func NewConsumer(groupName string) (mqClient MqConsumer, err error) {
} }
mqClient, err = RegisterKafkaMqConsumer(KafkaConfig{ mqClient, err = RegisterKafkaMqConsumer(KafkaConfig{
Brokers: config.Kafka.Address, Brokers: config.Kafka.Address,
GroupID: groupName, GroupID: groupName,
Version: config.Kafka.Version, Version: config.Kafka.Version,
ClientId: clientId, ClientId: clientId,
UserName: config.Kafka.UserName, UserName: config.Kafka.Username,
Password: config.Kafka.Password, Password: config.Kafka.Password,
SASLEnabled: config.Kafka.SASLEnabled, Mechanism: config.Kafka.Mechanism,
SASLMechanism: config.Kafka.SASLMechanism,
TLSEnabled: config.Kafka.TLSEnabled,
}) })
case "redis": case "redis":
if _, err = g.Redis().Do(ctx, "ping"); err == nil { if _, err = g.Redis().Do(ctx, "ping"); err == nil {
@@ -233,30 +222,6 @@ func NewConsumer(groupName string) (mqClient MqConsumer, err error) {
return return
} }
func normalizeKafkaConf() {
config.Kafka.UserName = resolveEnvPlaceholder(config.Kafka.UserName)
config.Kafka.Password = resolveEnvPlaceholder(config.Kafka.Password)
if config.Kafka.SASLMechanism == "" {
config.Kafka.SASLMechanism = "SCRAM-SHA-512"
}
}
func resolveEnvPlaceholder(value string) string {
value = strings.TrimSpace(value)
if strings.HasPrefix(value, "${") && strings.HasSuffix(value, "}") {
return os.Getenv(strings.TrimSuffix(strings.TrimPrefix(value, "${"), "}"))
}
return value
}
func firstKafkaClientId(value string, fallback string) string {
value = strings.TrimSpace(value)
if value != "" {
return value
}
return fallback
}
// BodyString 返回消息体 // BodyString 返回消息体
func (m *MqMsg) BodyString() string { func (m *MqMsg) BodyString() string {
return string(m.Body) return string(m.Body)
@@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
"io"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@@ -137,20 +138,53 @@ func (d *LocalDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res
// MergePartFile 合并分片文件 // MergePartFile 合并分片文件
func MergePartFile(srcPath, dstPath string) (err error) { func MergePartFile(srcPath, dstPath string) (err error) {
dir, err := os.ReadDir(srcPath) entries, err := os.ReadDir(srcPath)
if err != nil { if err != nil {
return err return err
} }
sort.Slice(dir, func(i, j int) bool { type filePart struct {
fiIndex, _ := strconv.Atoi(dir[i].Name()) index int
fjIndex, _ := strconv.Atoi(dir[j].Name()) path string
return fiIndex < fjIndex }
parts := make([]filePart, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() {
continue
}
index, parseErr := strconv.Atoi(entry.Name())
if parseErr != nil || index <= 0 {
return gerror.Newf("无效的分片文件名:%s", entry.Name())
}
parts = append(parts, filePart{index: index, path: filepath.Join(srcPath, entry.Name())})
}
if len(parts) == 0 {
return gerror.New("没有可合并的分片文件")
}
sort.Slice(parts, func(i, j int) bool {
return parts[i].index < parts[j].index
}) })
for _, file := range dir { if err = os.MkdirAll(filepath.Dir(dstPath), 0o755); err != nil {
filePath := filepath.Join(srcPath, file.Name()) return gerror.Wrap(err, "创建目标目录失败")
if err = gfile.PutBytesAppend(dstPath, gfile.GetBytes(filePath)); err != nil { }
return err dstFile, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
if err != nil {
return gerror.Wrap(err, "创建合并文件失败")
}
defer dstFile.Close()
for _, part := range parts {
srcFile, openErr := os.Open(part.path)
if openErr != nil {
return gerror.Wrapf(openErr, "打开分片%d失败", part.index)
}
_, copyErr := io.Copy(dstFile, srcFile)
closeErr := srcFile.Close()
if copyErr != nil {
return gerror.Wrapf(copyErr, "合并分片%d失败", part.index)
}
if closeErr != nil {
return gerror.Wrapf(closeErr, "关闭分片%d失败", part.index)
} }
} }
return return dstFile.Sync()
} }
@@ -1,61 +1,52 @@
package storager package storager
import ( import (
"crypto/md5" "bytes"
"errors"
"fmt"
"io"
"os" "os"
"path/filepath"
"testing" "testing"
) )
func TestMergePartFile(t *testing.T) { func TestMergePartFile(t *testing.T) {
srcFile, _ := os.Open("1.zip") tempDir := t.TempDir()
defer srcFile.Close() partDir := filepath.Join(tempDir, "parts")
fileInfo, err := srcFile.Stat() if err := os.MkdirAll(partDir, 0o755); err != nil {
if err != nil { t.Fatal(err)
t.Error(err)
} }
hash := md5.New()
_, err = io.Copy(hash, srcFile)
sum := hash.Sum(nil)
fileHash := fmt.Sprintf("%x", sum)
var singleSize = int64(1024 * 1024 * 100) parts := map[string][]byte{
chunks := int(fileInfo.Size() / singleSize) "1": []byte("first-"),
if fileInfo.Size()%singleSize != 0 { "2": []byte("second-"),
chunks += 1 "10": []byte("tenth"),
} }
srcFile.Seek(0, io.SeekStart) for name, content := range parts {
for j := 0; j < chunks; j++ { if err := os.WriteFile(filepath.Join(partDir, name), content, 0o644); err != nil {
partSize := singleSize t.Fatal(err)
if j == chunks-1 {
partSize = fileInfo.Size() - int64(j)*singleSize
} }
partData := make([]byte, partSize)
_, err = io.ReadFull(srcFile, partData)
pf, _ := os.Create(fmt.Sprintf("tmp/%d", j+1))
_, err = pf.Write(partData)
if err != nil {
t.Error(err)
return
}
pf.Close()
} }
err = MergePartFile("tmp/", "2.zip") destination := filepath.Join(tempDir, "merged.bin")
if err := os.WriteFile(destination, []byte("stale-data"), 0o644); err != nil {
t.Fatal(err)
}
if err := MergePartFile(partDir, destination); err != nil {
t.Fatalf("合并分片失败: %v", err)
}
actual, err := os.ReadFile(destination)
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
return
} }
expected := []byte("first-second-tenth")
if !bytes.Equal(actual, expected) {
t.Fatalf("合并内容不符 expected=%q actual=%q", expected, actual)
}
}
f2, _ := os.Open("2.zip") func TestMergePartFileRejectsEmptyDirectory(t *testing.T) {
hash2 := md5.New() partDir := t.TempDir()
_, err = io.Copy(hash2, f2) err := MergePartFile(partDir, filepath.Join(t.TempDir(), "merged.bin"))
sum2 := hash.Sum(nil) if err == nil {
fileHash2 := fmt.Sprintf("%x", sum2) t.Fatal("空分片目录应返回错误")
if fileHash != fileHash2 {
t.Error(errors.New("hash mismatch"))
} }
} }

Some files were not shown because too many files have changed in this diff Show More