chore: seed migration objects batch 21
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// 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/imauditlog"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var ImAuditLog = cImAuditLog{}
|
||||
|
||||
type cImAuditLog struct{}
|
||||
|
||||
// List 查看IM审计日志列表
|
||||
func (c *cImAuditLog) List(ctx context.Context, req *imauditlog.ListReq) (res *imauditlog.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImAuditLog().List(ctx, &req.ImAuditLogListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImAuditLogListModel{}
|
||||
}
|
||||
res = new(imauditlog.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定IM审计日志信息
|
||||
func (c *cImAuditLog) View(ctx context.Context, req *imauditlog.ViewReq) (res *imauditlog.ViewRes, err error) {
|
||||
data, err := service.SysImAuditLog().View(ctx, &req.ImAuditLogViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(imauditlog.ViewRes)
|
||||
res.ImAuditLogViewModel = data
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// 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/imgroup"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ImGroup = cImGroup{}
|
||||
)
|
||||
|
||||
type cImGroup struct{}
|
||||
|
||||
// List 查看IM群组列表
|
||||
func (c *cImGroup) List(ctx context.Context, req *imgroup.ListReq) (res *imgroup.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().List(ctx, &req.ImGroupListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupListModel{}
|
||||
}
|
||||
|
||||
res = new(imgroup.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定IM群组信息
|
||||
func (c *cImGroup) View(ctx context.Context, req *imgroup.ViewReq) (res *imgroup.ViewRes, err error) {
|
||||
data, err := service.SysImGroup().View(ctx, &req.ImGroupViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = new(imgroup.ViewRes)
|
||||
res.ImGroupViewModel = data
|
||||
return
|
||||
}
|
||||
|
||||
// MemberList 查看IM群成员列表
|
||||
func (c *cImGroup) MemberList(ctx context.Context, req *imgroup.MemberListReq) (res *imgroup.MemberListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().MemberList(ctx, &req.ImGroupMemberListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupMemberListModel{}
|
||||
}
|
||||
|
||||
res = new(imgroup.MemberListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// Governance 获取IM群治理配置
|
||||
func (c *cImGroup) Governance(ctx context.Context, req *imgroup.GovernanceReq) (res *imgroup.GovernanceRes, err error) {
|
||||
data, err := service.SysImGroup().Governance(ctx, &req.ImGroupTopicInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(imgroup.GovernanceRes)
|
||||
res.ImGroupGovernanceModel = data
|
||||
return
|
||||
}
|
||||
|
||||
// JoinRequestList 查看IM入群申请列表
|
||||
func (c *cImGroup) JoinRequestList(ctx context.Context, req *imgroup.JoinRequestListReq) (res *imgroup.JoinRequestListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().JoinRequestList(ctx, &req.ImGroupJoinRequestListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupJoinRequestListModel{}
|
||||
}
|
||||
res = new(imgroup.JoinRequestListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// InvitationList 查看IM群邀请列表
|
||||
func (c *cImGroup) InvitationList(ctx context.Context, req *imgroup.InvitationListReq) (res *imgroup.InvitationListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().InvitationList(ctx, &req.ImGroupInvitationListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupInvitationListModel{}
|
||||
}
|
||||
res = new(imgroup.InvitationListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// AnnouncementList 查看IM群公告列表
|
||||
func (c *cImGroup) AnnouncementList(ctx context.Context, req *imgroup.AnnouncementListReq) (res *imgroup.AnnouncementListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().AnnouncementList(ctx, &req.ImGroupAnnouncementListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupAnnouncementListModel{}
|
||||
}
|
||||
res = new(imgroup.AnnouncementListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// MuteList 查看IM群禁言列表
|
||||
func (c *cImGroup) MuteList(ctx context.Context, req *imgroup.MuteListReq) (res *imgroup.MuteListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().MuteList(ctx, &req.ImGroupMuteListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupMuteListModel{}
|
||||
}
|
||||
res = new(imgroup.MuteListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// AdminLogList 查看IM群管理日志列表
|
||||
func (c *cImGroup) AdminLogList(ctx context.Context, req *imgroup.AdminLogListReq) (res *imgroup.AdminLogListRes, err error) {
|
||||
list, totalCount, err := service.SysImGroup().AdminLogList(ctx, &req.ImGroupAdminLogListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImGroupAdminLogListModel{}
|
||||
}
|
||||
res = new(imgroup.AdminLogListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/api/admin/iminvite"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var ImInvite = cImInvite{}
|
||||
|
||||
type cImInvite struct{}
|
||||
|
||||
func (c *cImInvite) List(ctx context.Context, req *iminvite.ListReq) (res *iminvite.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImInvite().List(ctx, &req.ImInviteListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImInviteCodeModel{}
|
||||
}
|
||||
res = new(iminvite.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) Create(ctx context.Context, req *iminvite.CreateReq) (res *iminvite.CreateRes, err error) {
|
||||
data, err := service.SysImInvite().Create(ctx, &req.ImInviteCreateInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(iminvite.CreateRes)
|
||||
res.ImInviteCodeModel = data
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) BatchCreate(ctx context.Context, req *iminvite.BatchCreateReq) (res *iminvite.BatchCreateRes, err error) {
|
||||
data, err := service.SysImInvite().BatchCreate(ctx, &req.ImInviteBatchCreateInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(iminvite.BatchCreateRes)
|
||||
res.ImInviteBatchModel = data
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) BatchView(ctx context.Context, req *iminvite.BatchViewReq) (res *iminvite.BatchViewRes, err error) {
|
||||
data, err := service.SysImInvite().BatchView(ctx, &req.ImInviteBatchViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(iminvite.BatchViewRes)
|
||||
res.ImInviteBatchDetailModel = data
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) Revoke(ctx context.Context, req *iminvite.RevokeReq) (res *iminvite.RevokeRes, err error) {
|
||||
err = service.SysImInvite().Revoke(ctx, &req.ImInviteRevokeInp)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) BatchRevoke(ctx context.Context, req *iminvite.BatchRevokeReq) (res *iminvite.BatchRevokeRes, err error) {
|
||||
data, err := service.SysImInvite().BatchRevoke(ctx, &req.ImInviteBatchRevokeInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(iminvite.BatchRevokeRes)
|
||||
res.ImInviteBatchModel = data
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImInvite) Export(ctx context.Context, req *iminvite.ExportReq) (res *iminvite.ExportRes, err error) {
|
||||
err = service.SysImInvite().Export(ctx, &req.ImInviteListInp)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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/imrealname"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ImRealname = cImRealname{}
|
||||
)
|
||||
|
||||
type cImRealname struct{}
|
||||
|
||||
// List 查看IM实名审核列表
|
||||
func (c *cImRealname) List(ctx context.Context, req *imrealname.ListReq) (res *imrealname.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImRealname().List(ctx, &req.ImRealnameListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImRealnameListModel{}
|
||||
}
|
||||
|
||||
res = new(imrealname.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定IM实名审核信息
|
||||
func (c *cImRealname) View(ctx context.Context, req *imrealname.ViewReq) (res *imrealname.ViewRes, err error) {
|
||||
data, err := service.SysImRealname().View(ctx, &req.ImRealnameViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = new(imrealname.ViewRes)
|
||||
res.ImRealnameViewModel = data
|
||||
return
|
||||
}
|
||||
|
||||
// Review 审核IM实名
|
||||
func (c *cImRealname) Review(ctx context.Context, req *imrealname.ReviewReq) (res *imrealname.ReviewRes, err error) {
|
||||
err = service.SysImRealname().Review(ctx, &req.ImRealnameReviewInp)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/api/admin/imregistration"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var ImRegistration = cImRegistration{}
|
||||
|
||||
type cImRegistration struct{}
|
||||
|
||||
func (c *cImRegistration) Get(ctx context.Context, req *imregistration.GetReq) (res *imregistration.GetRes, err error) {
|
||||
data, err := service.SysImRegistration().GetConfig(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(imregistration.GetRes)
|
||||
res.ImRegistrationConfigModel = data
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cImRegistration) Update(ctx context.Context, req *imregistration.UpdateReq) (res *imregistration.UpdateRes, err error) {
|
||||
err = service.SysImRegistration().UpdateConfig(ctx, &req.ImRegistrationConfigUpdateInp)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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/imtenant"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ImTenant = cImTenant{}
|
||||
)
|
||||
|
||||
type cImTenant struct{}
|
||||
|
||||
// List 查看IM租户列表
|
||||
func (c *cImTenant) List(ctx context.Context, req *imtenant.ListReq) (res *imtenant.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImTenant().List(ctx, &req.ImTenantListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
list = []*sysin.ImTenantListModel{}
|
||||
}
|
||||
|
||||
res = new(imtenant.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 更新IM租户
|
||||
func (c *cImTenant) Edit(ctx context.Context, req *imtenant.EditReq) (res *imtenant.EditRes, err error) {
|
||||
err = service.SysImTenant().Edit(ctx, &req.ImTenantEditInp)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定IM租户信息
|
||||
func (c *cImTenant) View(ctx context.Context, req *imtenant.ViewReq) (res *imtenant.ViewRes, err error) {
|
||||
data, err := service.SysImTenant().View(ctx, &req.ImTenantViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = new(imtenant.ViewRes)
|
||||
res.ImTenantViewModel = data
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新IM租户状态
|
||||
func (c *cImTenant) Status(ctx context.Context, req *imtenant.StatusReq) (res *imtenant.StatusRes, err error) {
|
||||
err = service.SysImTenant().Status(ctx, &req.ImTenantStatusInp)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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/imuser"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ImUser = cImUser{}
|
||||
)
|
||||
|
||||
type cImUser struct{}
|
||||
|
||||
// List 查看IM用户列表
|
||||
func (c *cImUser) List(ctx context.Context, req *imuser.ListReq) (res *imuser.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImUser().List(ctx, &req.ImUserListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
list = []*sysin.ImUserListModel{}
|
||||
}
|
||||
|
||||
res = new(imuser.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定IM用户信息
|
||||
func (c *cImUser) View(ctx context.Context, req *imuser.ViewReq) (res *imuser.ViewRes, err error) {
|
||||
data, err := service.SysImUser().View(ctx, &req.ImUserViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = new(imuser.ViewRes)
|
||||
res.ImUserViewModel = data
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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/imworkorder"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ImWorkOrder = cImWorkOrder{}
|
||||
)
|
||||
|
||||
type cImWorkOrder struct{}
|
||||
|
||||
// List 查看IM举报反馈工单列表
|
||||
func (c *cImWorkOrder) List(ctx context.Context, req *imworkorder.ListReq) (res *imworkorder.ListRes, err error) {
|
||||
list, totalCount, err := service.SysImWorkOrder().List(ctx, &req.ImWorkOrderListInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []*sysin.ImWorkOrderListModel{}
|
||||
}
|
||||
|
||||
res = new(imworkorder.ListRes)
|
||||
res.List = list
|
||||
res.PageRes.Pack(req, totalCount)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定IM举报反馈工单信息
|
||||
func (c *cImWorkOrder) View(ctx context.Context, req *imworkorder.ViewReq) (res *imworkorder.ViewRes, err error) {
|
||||
data, err := service.SysImWorkOrder().View(ctx, &req.ImWorkOrderViewInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = new(imworkorder.ViewRes)
|
||||
res.ImWorkOrderViewModel = data
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新IM举报反馈工单状态
|
||||
func (c *cImWorkOrder) Status(ctx context.Context, req *imworkorder.StatusReq) (res *imworkorder.StatusRes, err error) {
|
||||
err = service.SysImWorkOrder().Status(ctx, &req.ImWorkOrderStatusInp)
|
||||
return
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
// imTenantDao is the data access object for the table im_tenant.
|
||||
// You can define custom methods on it to extend its functionality as needed.
|
||||
type imTenantDao struct {
|
||||
*internal.ImTenantDao
|
||||
}
|
||||
|
||||
var (
|
||||
// ImTenant is a globally accessible object for table im_tenant operations.
|
||||
ImTenant = imTenantDao{internal.NewImTenantDao()}
|
||||
)
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
// imTenantConfigDao is the data access object for the table im_tenant_config.
|
||||
// You can define custom methods on it to extend its functionality as needed.
|
||||
type imTenantConfigDao struct {
|
||||
*internal.ImTenantConfigDao
|
||||
}
|
||||
|
||||
var (
|
||||
// ImTenantConfig is a globally accessible object for table im_tenant_config operations.
|
||||
ImTenantConfig = imTenantConfigDao{internal.NewImTenantConfigDao()}
|
||||
)
|
||||
|
||||
// Add your custom methods and functionality below.
|
||||
@@ -22,6 +22,7 @@ type AdminMemberDao struct {
|
||||
// AdminMemberColumns defines and stores column names for the table hg_admin_member.
|
||||
type AdminMemberColumns struct {
|
||||
Id string // 管理员ID
|
||||
TenantId string // IM租户ID
|
||||
DeptId string // 部门ID
|
||||
RoleId string // 角色ID
|
||||
RealName string // 真实姓名
|
||||
@@ -54,6 +55,7 @@ type AdminMemberColumns struct {
|
||||
// adminMemberColumns holds the columns for the table hg_admin_member.
|
||||
var adminMemberColumns = AdminMemberColumns{
|
||||
Id: "id",
|
||||
TenantId: "tenant_id",
|
||||
DeptId: "dept_id",
|
||||
RoleId: "role_id",
|
||||
RealName: "real_name",
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// ==========================================================================
|
||||
// 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"
|
||||
)
|
||||
|
||||
// ImTenantDao is the data access object for the table im_tenant.
|
||||
type ImTenantDao 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 ImTenantColumns // columns contains all the column names of Table for convenient usage.
|
||||
handlers []gdb.ModelHandler // handlers for customized model modification.
|
||||
}
|
||||
|
||||
// ImTenantColumns defines and stores column names for the table im_tenant.
|
||||
type ImTenantColumns struct {
|
||||
Id string //
|
||||
Code string //
|
||||
Name string //
|
||||
TenantDesc string //
|
||||
DbName string //
|
||||
DbDsn string //
|
||||
State string //
|
||||
CreatedAt string //
|
||||
CreatedBy string //
|
||||
UpdatedAt string //
|
||||
UpdatedBy string //
|
||||
}
|
||||
|
||||
// imTenantColumns holds the columns for the table im_tenant.
|
||||
var imTenantColumns = ImTenantColumns{
|
||||
Id: "id",
|
||||
Code: "code",
|
||||
Name: "name",
|
||||
TenantDesc: "tenant_desc",
|
||||
DbName: "db_name",
|
||||
DbDsn: "db_dsn",
|
||||
State: "state",
|
||||
CreatedAt: "created_at",
|
||||
CreatedBy: "created_by",
|
||||
UpdatedAt: "updated_at",
|
||||
UpdatedBy: "updated_by",
|
||||
}
|
||||
|
||||
// NewImTenantDao creates and returns a new DAO object for table data access.
|
||||
func NewImTenantDao(handlers ...gdb.ModelHandler) *ImTenantDao {
|
||||
return &ImTenantDao{
|
||||
group: "default",
|
||||
table: "im_tenant",
|
||||
columns: imTenantColumns,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of the current DAO.
|
||||
func (dao *ImTenantDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of the current DAO.
|
||||
func (dao *ImTenantDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of the current DAO.
|
||||
func (dao *ImTenantDao) Columns() ImTenantColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the database configuration group name of the current DAO.
|
||||
func (dao *ImTenantDao) 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 *ImTenantDao) 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 *ImTenantDao) 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,95 @@
|
||||
// ==========================================================================
|
||||
// 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"
|
||||
)
|
||||
|
||||
// ImTenantConfigDao is the data access object for the table im_tenant_config.
|
||||
type ImTenantConfigDao 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 ImTenantConfigColumns // columns contains all the column names of Table for convenient usage.
|
||||
handlers []gdb.ModelHandler // handlers for customized model modification.
|
||||
}
|
||||
|
||||
// ImTenantConfigColumns defines and stores column names for the table im_tenant_config.
|
||||
type ImTenantConfigColumns struct {
|
||||
Id string //
|
||||
TenantId string //
|
||||
MaxUsers string // 0 means unlimited
|
||||
MaxGroups string // 0 means unlimited
|
||||
MaxGroupMembers string // 0 means unlimited
|
||||
CreatedAt string //
|
||||
CreatedBy string //
|
||||
UpdatedAt string //
|
||||
UpdatedBy string //
|
||||
}
|
||||
|
||||
// imTenantConfigColumns holds the columns for the table im_tenant_config.
|
||||
var imTenantConfigColumns = ImTenantConfigColumns{
|
||||
Id: "id",
|
||||
TenantId: "tenant_id",
|
||||
MaxUsers: "max_users",
|
||||
MaxGroups: "max_groups",
|
||||
MaxGroupMembers: "max_group_members",
|
||||
CreatedAt: "created_at",
|
||||
CreatedBy: "created_by",
|
||||
UpdatedAt: "updated_at",
|
||||
UpdatedBy: "updated_by",
|
||||
}
|
||||
|
||||
// NewImTenantConfigDao creates and returns a new DAO object for table data access.
|
||||
func NewImTenantConfigDao(handlers ...gdb.ModelHandler) *ImTenantConfigDao {
|
||||
return &ImTenantConfigDao{
|
||||
group: "default",
|
||||
table: "im_tenant_config",
|
||||
columns: imTenantConfigColumns,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of the current DAO.
|
||||
func (dao *ImTenantConfigDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of the current DAO.
|
||||
func (dao *ImTenantConfigDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of the current DAO.
|
||||
func (dao *ImTenantConfigDao) Columns() ImTenantConfigColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the database configuration group name of the current DAO.
|
||||
func (dao *ImTenantConfigDao) 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 *ImTenantConfigDao) 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 *ImTenantConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
@@ -78,6 +78,15 @@ func GetUserId(ctx context.Context) int64 {
|
||||
return user.Id
|
||||
}
|
||||
|
||||
// GetTenantId 获取当前身份关联的统一租户ID(im_tenant.id)。
|
||||
func GetTenantId(ctx context.Context) int64 {
|
||||
user := GetUser(ctx)
|
||||
if user == nil {
|
||||
return 0
|
||||
}
|
||||
return user.TenantId
|
||||
}
|
||||
|
||||
// GetRoleId 获取用户角色ID
|
||||
func GetRoleId(ctx context.Context) int64 {
|
||||
user := GetUser(ctx)
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
package imadmin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
const maxResponseBody = 10 << 20
|
||||
|
||||
type AdminScope struct {
|
||||
TenantId int64
|
||||
OperatorId int64
|
||||
RoleKey string
|
||||
RequestId string
|
||||
}
|
||||
|
||||
type Page[T any] struct {
|
||||
Items []T `json:"items"`
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"per_page"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
conf Config
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
func NewClient(conf Config) *Client {
|
||||
return &Client{
|
||||
conf: conf,
|
||||
http: &http.Client{Timeout: conf.Timeout},
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultClient(ctx context.Context) *Client {
|
||||
conf := LoadConfig(ctx)
|
||||
return NewClient(conf)
|
||||
}
|
||||
|
||||
func (c *Client) CreateCode(ctx context.Context, scope AdminScope, in any, out any) error {
|
||||
return c.doJSON(ctx, http.MethodPost, "/admin/v1/im/registration-invite-codes", scope, in, out)
|
||||
}
|
||||
|
||||
func (c *Client) CreateBatch(ctx context.Context, scope AdminScope, in any, out any) error {
|
||||
return c.doJSON(ctx, http.MethodPost, "/admin/v1/im/registration-invite-code-batches", scope, in, out)
|
||||
}
|
||||
|
||||
func (c *Client) ListCodes(ctx context.Context, scope AdminScope, in any, out any) error {
|
||||
if hasInviteCode(in) {
|
||||
return c.doJSON(ctx, http.MethodPost, "/admin/v1/im/registration-invite-codes/search", scope, in, out)
|
||||
}
|
||||
path := "/admin/v1/im/registration-invite-codes"
|
||||
if query := toQuery(in); query != "" {
|
||||
path += "?" + query
|
||||
}
|
||||
return c.doJSON(ctx, http.MethodGet, path, scope, nil, out)
|
||||
}
|
||||
|
||||
func (c *Client) GetBatch(ctx context.Context, scope AdminScope, batchId int64, out any) error {
|
||||
return c.doJSON(ctx, http.MethodGet, fmt.Sprintf("/admin/v1/im/registration-invite-code-batches/%d", batchId), scope, nil, out)
|
||||
}
|
||||
|
||||
func (c *Client) RevokeCode(ctx context.Context, scope AdminScope, codeId int64) error {
|
||||
return c.doJSON(ctx, http.MethodPost, fmt.Sprintf("/admin/v1/im/registration-invite-codes/%d/revoke", codeId), scope, nil, nil)
|
||||
}
|
||||
|
||||
func (c *Client) RevokeBatch(ctx context.Context, scope AdminScope, batchId int64, out any) error {
|
||||
return c.doJSON(ctx, http.MethodPost, fmt.Sprintf("/admin/v1/im/registration-invite-code-batches/%d/revoke", batchId), scope, nil, out)
|
||||
}
|
||||
|
||||
func (c *Client) ExportCodes(ctx context.Context, scope AdminScope, in any) (*http.Response, error) {
|
||||
req, err := c.newRequest(ctx, http.MethodPost, "/admin/v1/im/registration-invite-codes/export", scope, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return nil, gerror.New("IM服务暂不可用")
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
defer resp.Body.Close()
|
||||
return nil, c.parseError(resp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) doJSON(ctx context.Context, method string, path string, scope AdminScope, in any, out any) error {
|
||||
req, err := c.newRequest(ctx, method, path, scope, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return gerror.New("IM服务暂不可用")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return c.parseError(resp)
|
||||
}
|
||||
if out == nil {
|
||||
return nil
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBody))
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "读取IM响应失败")
|
||||
}
|
||||
var envelope struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Reason string `json:"reason"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
if err = json.Unmarshal(body, &envelope); err == nil && len(envelope.Data) > 0 {
|
||||
if envelope.Code != 0 {
|
||||
return stableError(envelope.Message, envelope.Reason)
|
||||
}
|
||||
return json.Unmarshal(envelope.Data, out)
|
||||
}
|
||||
return json.Unmarshal(body, out)
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(ctx context.Context, method string, path string, scope AdminScope, in any) (*http.Request, error) {
|
||||
if c.conf.BaseUrl == "" {
|
||||
return nil, gerror.New("IM管理API未配置")
|
||||
}
|
||||
if c.conf.ServiceSecret == "" {
|
||||
return nil, gerror.New("IM管理API密钥未配置")
|
||||
}
|
||||
var body io.Reader
|
||||
bodyBytes := []byte{}
|
||||
if in != nil {
|
||||
b, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "序列化IM请求失败")
|
||||
}
|
||||
bodyBytes = b
|
||||
body = bytes.NewReader(bodyBytes)
|
||||
}
|
||||
baseUrl := strings.TrimRight(c.conf.BaseUrl, "/")
|
||||
req, err := http.NewRequestWithContext(ctx, method, baseUrl+path, body)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "创建IM请求失败")
|
||||
}
|
||||
if scope.RequestId == "" {
|
||||
scope.RequestId = gctx.CtxId(ctx)
|
||||
}
|
||||
timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10)
|
||||
nonce, err := randomNonce()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyHash := sha256.Sum256(bodyBytes)
|
||||
canonical := strings.Join([]string{
|
||||
method,
|
||||
requestTarget(req),
|
||||
c.conf.ServiceId,
|
||||
fmt.Sprintf("%d", scope.TenantId),
|
||||
fmt.Sprintf("%d", scope.OperatorId),
|
||||
timestamp,
|
||||
nonce,
|
||||
scope.RequestId,
|
||||
hex.EncodeToString(bodyHash[:]),
|
||||
}, "\n")
|
||||
mac := hmac.New(sha256.New, []byte(c.conf.ServiceSecret))
|
||||
_, _ = mac.Write([]byte(canonical))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "IM-HMAC "+hex.EncodeToString(mac.Sum(nil)))
|
||||
req.Header.Set("X-IM-Service-ID", c.conf.ServiceId)
|
||||
req.Header.Set("X-IM-Tenant-ID", fmt.Sprintf("%d", scope.TenantId))
|
||||
req.Header.Set("X-IM-Operator-ID", fmt.Sprintf("%d", scope.OperatorId))
|
||||
req.Header.Set("X-IM-Timestamp", timestamp)
|
||||
req.Header.Set("X-IM-Nonce", nonce)
|
||||
req.Header.Set("X-HotGo-Role-Key", scope.RoleKey)
|
||||
req.Header.Set("X-Request-ID", scope.RequestId)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) parseError(resp *http.Response) error {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 64<<10))
|
||||
var envelope struct {
|
||||
Message string `json:"message"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
_ = json.Unmarshal(body, &envelope)
|
||||
return stableError(envelope.Message, envelope.Reason)
|
||||
}
|
||||
|
||||
func stableError(message string, reason string) error {
|
||||
if message != "" {
|
||||
return gerror.New(message)
|
||||
}
|
||||
if reason != "" {
|
||||
return gerror.New(reason)
|
||||
}
|
||||
return gerror.New("IM服务暂不可用")
|
||||
}
|
||||
|
||||
func hasInviteCode(in any) bool {
|
||||
b, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var m map[string]any
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return false
|
||||
}
|
||||
v, ok := m["inviteCode"]
|
||||
if ok && strings.TrimSpace(fmt.Sprint(v)) != "" {
|
||||
return true
|
||||
}
|
||||
v, ok = m["invite_code"]
|
||||
return ok && strings.TrimSpace(fmt.Sprint(v)) != ""
|
||||
}
|
||||
|
||||
func requestTarget(req *http.Request) string {
|
||||
target := req.URL.EscapedPath()
|
||||
if req.URL.RawQuery != "" {
|
||||
target += "?" + req.URL.RawQuery
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
func randomNonce() (string, error) {
|
||||
buf := make([]byte, 16)
|
||||
if _, err := cryptorand.Read(buf); err != nil {
|
||||
return "", gerror.Wrap(err, "生成IM请求随机数失败")
|
||||
}
|
||||
return hex.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
func toQuery(in any) string {
|
||||
b, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var m map[string]any
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return ""
|
||||
}
|
||||
values := url.Values{}
|
||||
for key, value := range m {
|
||||
if value == nil {
|
||||
continue
|
||||
}
|
||||
text := strings.TrimSpace(fmt.Sprint(value))
|
||||
if text == "" || text == "0" || text == "<nil>" {
|
||||
continue
|
||||
}
|
||||
values.Set(key, text)
|
||||
}
|
||||
return values.Encode()
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package imadmin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testInviteListRequest struct {
|
||||
Page int `json:"page,omitempty"`
|
||||
PerPage int `json:"per_page,omitempty"`
|
||||
InviteCode string `json:"invite_code,omitempty"`
|
||||
BatchID int64 `json:"batch_id,omitempty"`
|
||||
}
|
||||
|
||||
func TestListCodesUsesSignedGetWithSnakeQuery(t *testing.T) {
|
||||
server := signedTestServer(t, http.MethodGet, "/admin/v1/im/registration-invite-codes", func(t *testing.T, r *http.Request, body []byte) {
|
||||
if len(body) != 0 {
|
||||
t.Fatalf("GET list should not send body, got %q", string(body))
|
||||
}
|
||||
query := r.URL.Query()
|
||||
if query.Get("page") != "2" || query.Get("per_page") != "20" || query.Get("batch_id") != "123" {
|
||||
t.Fatalf("unexpected query: %s", r.URL.RawQuery)
|
||||
}
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(Config{BaseUrl: server.URL, ServiceId: "hotgo-admin", ServiceSecret: "secret", Timeout: time.Second})
|
||||
var out Page[struct{}]
|
||||
err := client.ListCodes(context.Background(), AdminScope{TenantId: 9, OperatorId: 7, RequestId: "req-1"}, testInviteListRequest{Page: 2, PerPage: 20, BatchID: 123}, &out)
|
||||
if err != nil {
|
||||
t.Fatalf("ListCodes returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListCodesWithInviteCodeUsesSignedSearchPost(t *testing.T) {
|
||||
server := signedTestServer(t, http.MethodPost, "/admin/v1/im/registration-invite-codes/search", func(t *testing.T, r *http.Request, body []byte) {
|
||||
if !strings.Contains(string(body), `"invite_code":"Ab123"`) {
|
||||
t.Fatalf("POST search should send snake_case invite code, got %s", string(body))
|
||||
}
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient(Config{BaseUrl: server.URL, ServiceId: "hotgo-admin", ServiceSecret: "secret", Timeout: time.Second})
|
||||
var out Page[struct{}]
|
||||
err := client.ListCodes(context.Background(), AdminScope{TenantId: 9, OperatorId: 7, RequestId: "req-2"}, testInviteListRequest{Page: 1, PerPage: 20, InviteCode: "Ab123"}, &out)
|
||||
if err != nil {
|
||||
t.Fatalf("ListCodes returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func signedTestServer(t *testing.T, method string, path string, inspect func(t *testing.T, r *http.Request, body []byte)) *httptest.Server {
|
||||
t.Helper()
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != method {
|
||||
t.Fatalf("method = %s, want %s", r.Method, method)
|
||||
}
|
||||
if r.URL.Path != path {
|
||||
t.Fatalf("path = %s, want %s", r.URL.Path, path)
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("read body: %v", err)
|
||||
}
|
||||
inspect(t, r, body)
|
||||
assertSignedRequest(t, r, body, "secret")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"data":{"items":[],"page":1,"per_page":20,"total":0}}`))
|
||||
}))
|
||||
}
|
||||
|
||||
func assertSignedRequest(t *testing.T, r *http.Request, body []byte, secret string) {
|
||||
t.Helper()
|
||||
for _, name := range []string{"Authorization", "X-IM-Service-ID", "X-IM-Tenant-ID", "X-IM-Operator-ID", "X-IM-Timestamp", "X-IM-Nonce", "X-Request-ID"} {
|
||||
if r.Header.Get(name) == "" {
|
||||
t.Fatalf("missing header %s", name)
|
||||
}
|
||||
}
|
||||
if _, err := strconv.ParseInt(r.Header.Get("X-IM-Timestamp"), 10, 64); err != nil {
|
||||
t.Fatalf("timestamp is not unix seconds: %v", err)
|
||||
}
|
||||
bodyHash := sha256.Sum256(body)
|
||||
canonical := strings.Join([]string{
|
||||
r.Method,
|
||||
r.URL.RequestURI(),
|
||||
r.Header.Get("X-IM-Service-ID"),
|
||||
r.Header.Get("X-IM-Tenant-ID"),
|
||||
r.Header.Get("X-IM-Operator-ID"),
|
||||
r.Header.Get("X-IM-Timestamp"),
|
||||
r.Header.Get("X-IM-Nonce"),
|
||||
r.Header.Get("X-Request-ID"),
|
||||
hex.EncodeToString(bodyHash[:]),
|
||||
}, "\n")
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
_, _ = mac.Write([]byte(canonical))
|
||||
expected := fmt.Sprintf("IM-HMAC %s", hex.EncodeToString(mac.Sum(nil)))
|
||||
if r.Header.Get("Authorization") != expected {
|
||||
t.Fatalf("bad signature\nwant: %s\n got: %s", expected, r.Header.Get("Authorization"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package imadmin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ServiceId string `json:"serviceId"`
|
||||
ServiceSecret string `json:"serviceSecret"`
|
||||
Timeout time.Duration `json:"timeout"`
|
||||
}
|
||||
|
||||
func LoadConfig(ctx context.Context) Config {
|
||||
timeout := g.Cfg().MustGet(ctx, "imAdminApi.timeout", "5s").Duration()
|
||||
if timeout <= 0 {
|
||||
timeout = 5 * time.Second
|
||||
}
|
||||
serviceSecret := g.Cfg().MustGet(ctx, "imAdminApi.serviceSecret").String()
|
||||
if strings.HasPrefix(serviceSecret, "${") && strings.HasSuffix(serviceSecret, "}") {
|
||||
serviceSecret = os.Getenv(strings.TrimSuffix(strings.TrimPrefix(serviceSecret, "${"), "}"))
|
||||
}
|
||||
if serviceSecret == "" {
|
||||
serviceSecret = os.Getenv("IM_ADMIN_API_SECRET")
|
||||
}
|
||||
return Config{
|
||||
BaseUrl: g.Cfg().MustGet(ctx, "imAdminApi.baseUrl").String(),
|
||||
ServiceId: g.Cfg().MustGet(ctx, "imAdminApi.serviceId", "hotgo-admin").String(),
|
||||
ServiceSecret: serviceSecret,
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Package imtenant provides helpers for accessing tenant business databases.
|
||||
package imtenant
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/model/entity"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
var tenantDbGroups sync.Map
|
||||
|
||||
const registryDbGroup = "imRegistry"
|
||||
|
||||
// RegistryDB returns the tenant registry DB. It falls back to the default DB for
|
||||
// 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.
|
||||
func DB(ctx context.Context, tenantId int64) (db gdb.DB, tenant *entity.ImTenant, err error) {
|
||||
if tenantId <= 0 {
|
||||
return nil, nil, gerror.New("租户ID不能为空")
|
||||
}
|
||||
|
||||
if err = RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx).WherePri(tenantId).Scan(&tenant); err != nil {
|
||||
return nil, nil, gerror.Wrap(err, "获取租户信息失败")
|
||||
}
|
||||
if tenant == nil || tenant.Id <= 0 {
|
||||
return nil, nil, gerror.New("租户不存在")
|
||||
}
|
||||
|
||||
link, err := tenantLink(ctx, tenant)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
group := fmt.Sprintf("im_tenant_%d_%x", tenant.Id, hash(link))
|
||||
if cached, ok := tenantDbGroups.Load(group); ok && cached == link {
|
||||
db, err = gdb.Instance(group)
|
||||
return db, tenant, err
|
||||
}
|
||||
|
||||
if err = gdb.SetConfigGroup(group, gdb.ConfigGroup{{Link: link}}); err != nil {
|
||||
return nil, nil, gerror.Wrap(err, "初始化租户业务库连接失败")
|
||||
}
|
||||
tenantDbGroups.Store(group, link)
|
||||
|
||||
db, err = gdb.Instance(group)
|
||||
if err != nil {
|
||||
return nil, nil, gerror.Wrap(err, "获取租户业务库连接失败")
|
||||
}
|
||||
return db, tenant, nil
|
||||
}
|
||||
|
||||
func tenantLink(ctx context.Context, tenant *entity.ImTenant) (string, error) {
|
||||
if tenant.DbDsn != "" {
|
||||
return tenant.DbDsn, nil
|
||||
}
|
||||
if tenant.DbName == "" {
|
||||
return "", gerror.New("租户业务库未配置")
|
||||
}
|
||||
|
||||
defaultLink := defaultDbLink(ctx)
|
||||
if defaultLink == "" {
|
||||
return "", gerror.New("默认数据库连接未配置,无法推导租户业务库连接")
|
||||
}
|
||||
|
||||
link, err := replaceMySQLDatabase(defaultLink, tenant.DbName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return link, nil
|
||||
}
|
||||
|
||||
func defaultDbLink(ctx context.Context) string {
|
||||
if configs, err := gdb.GetConfigGroup("default"); err == nil && len(configs) > 0 {
|
||||
if configs[0].Link != "" {
|
||||
return configs[0].Link
|
||||
}
|
||||
}
|
||||
|
||||
cfg := g.Cfg()
|
||||
if !cfg.Available(ctx) {
|
||||
return ""
|
||||
}
|
||||
defaultLink := cfg.MustGet(ctx, "database.default.0.link").String()
|
||||
if defaultLink != "" {
|
||||
return defaultLink
|
||||
}
|
||||
return cfg.MustGet(ctx, "database.default.link").String()
|
||||
}
|
||||
|
||||
func replaceMySQLDatabase(link string, dbName string) (string, error) {
|
||||
if !strings.HasPrefix(link, "mysql:") {
|
||||
return "", gerror.New("仅支持从 MySQL 默认连接推导租户业务库连接")
|
||||
}
|
||||
|
||||
dbStart := strings.LastIndex(link, ")/")
|
||||
if dbStart < 0 {
|
||||
return "", gerror.New("默认数据库连接格式不正确")
|
||||
}
|
||||
dbStart += 2
|
||||
queryStart := strings.Index(link[dbStart:], "?")
|
||||
if queryStart < 0 {
|
||||
return link[:dbStart] + dbName, nil
|
||||
}
|
||||
queryStart += dbStart
|
||||
return link[:dbStart] + dbName + link[queryStart:], nil
|
||||
}
|
||||
|
||||
func hash(s string) uint64 {
|
||||
h := fnv.New64a()
|
||||
_, _ = h.Write([]byte(s))
|
||||
return h.Sum64()
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/library/imtenant"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
@@ -467,6 +468,30 @@ func (s *sAdminMember) Edit(ctx context.Context, in *adminin.MemberEditInp) (err
|
||||
return
|
||||
}
|
||||
|
||||
if service.AdminMember().VerifySuperId(ctx, opMemberId) {
|
||||
if in.TenantId > 0 {
|
||||
count, queryErr := imtenant.RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx).
|
||||
Where(dao.ImTenant.Columns().Id, in.TenantId).
|
||||
Where(dao.ImTenant.Columns().State, consts.ImTenantStateActive).
|
||||
Count()
|
||||
if queryErr != nil {
|
||||
return gerror.Wrap(queryErr, "校验IM租户失败")
|
||||
}
|
||||
if count == 0 {
|
||||
return gerror.New("IM租户不存在或未启用")
|
||||
}
|
||||
}
|
||||
} else if deptType := contexts.GetDeptType(ctx); deptType == consts.DeptTypeTenant ||
|
||||
deptType == consts.DeptTypeMerchant || deptType == consts.DeptTypeUser {
|
||||
in.TenantId = contexts.GetTenantId(ctx)
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("当前管理员未关联租户")
|
||||
}
|
||||
} else {
|
||||
// 非租户域管理员不能通过浏览器为账号指定租户。
|
||||
in.TenantId = 0
|
||||
}
|
||||
|
||||
cols := dao.AdminMember.Columns()
|
||||
err = s.VerifyUnique(ctx, &adminin.VerifyUniqueInp{
|
||||
Id: in.Id,
|
||||
|
||||
@@ -228,6 +228,7 @@ func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (r
|
||||
|
||||
user := &model.Identity{
|
||||
Id: mb.Id,
|
||||
TenantId: mb.TenantId,
|
||||
Pid: mb.Pid,
|
||||
DeptId: dept.Id,
|
||||
DeptType: dept.Type,
|
||||
@@ -319,6 +320,7 @@ func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity
|
||||
|
||||
user := &model.Identity{
|
||||
Id: mb.Id,
|
||||
TenantId: mb.TenantId,
|
||||
Pid: mb.Pid,
|
||||
DeptId: dept.Id,
|
||||
DeptType: dept.Type,
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/imadmin"
|
||||
"hotgo/internal/library/imtenant"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/simple"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
const (
|
||||
imAdminOperationAuditTable = "im_admin_operation_audit"
|
||||
)
|
||||
|
||||
func resolveImAdminScope(ctx context.Context, selectedTenantId int64) (scope imadmin.AdminScope, err error) {
|
||||
operatorId := contexts.GetUserId(ctx)
|
||||
scope.OperatorId = operatorId
|
||||
scope.RoleKey = contexts.GetRoleKey(ctx)
|
||||
scope.RequestId = gctx.CtxId(ctx)
|
||||
if service.AdminMember().VerifySuperId(ctx, operatorId) {
|
||||
if selectedTenantId <= 0 {
|
||||
return scope, gerror.New("请选择IM租户")
|
||||
}
|
||||
if err = ensureImTenantActive(ctx, selectedTenantId); err != nil {
|
||||
return scope, err
|
||||
}
|
||||
scope.TenantId = selectedTenantId
|
||||
return scope, nil
|
||||
}
|
||||
|
||||
if contexts.GetDeptType(ctx) != consts.DeptTypeTenant {
|
||||
return scope, gerror.New("当前账号不是租户管理员,不能管理邀请码")
|
||||
}
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
if tenantId <= 0 {
|
||||
return scope, gerror.New("当前管理员未关联租户")
|
||||
}
|
||||
if err = ensureImTenantActive(ctx, tenantId); err != nil {
|
||||
return scope, err
|
||||
}
|
||||
scope.TenantId = tenantId
|
||||
return scope, nil
|
||||
}
|
||||
|
||||
func ensureImTenantActive(ctx context.Context, tenantId int64) (err error) {
|
||||
var tenant *entity.ImTenant
|
||||
if err = imtenant.RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx).WherePri(tenantId).Scan(&tenant); err != nil {
|
||||
return gerror.Wrap(err, "获取IM租户失败")
|
||||
}
|
||||
if tenant == nil || tenant.Id <= 0 {
|
||||
return gerror.New("IM租户不存在")
|
||||
}
|
||||
if tenant.State != consts.ImTenantStateActive {
|
||||
return gerror.New("IM租户未启用")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeImAdminAudit(ctx context.Context, tenantId int64, action string, targetType string, targetId int64, result string, detail g.Map) error {
|
||||
operatorId := contexts.GetUserId(ctx)
|
||||
request := ghttp.RequestFromCtx(ctx)
|
||||
ip := ""
|
||||
if request != nil {
|
||||
ip = request.GetClientIp()
|
||||
}
|
||||
detailJson := gjson.New(detail)
|
||||
_, err := g.DB().Exec(ctx,
|
||||
"INSERT INTO "+imAdminOperationAuditTable+" (request_id, tenant_id, operator_id, action, target_type, target_id, result, ip, detail_json, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, CAST(? AS JSON), ?)",
|
||||
gctx.CtxId(ctx),
|
||||
tenantId,
|
||||
operatorId,
|
||||
action,
|
||||
targetType,
|
||||
targetId,
|
||||
result,
|
||||
ip,
|
||||
detailJson.String(),
|
||||
gtime.Now(),
|
||||
)
|
||||
if err != nil && !simple.IsDemo(ctx) {
|
||||
g.Log().Warningf(ctx, "写入IM后台审计失败 action:%s err:%+v", action, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// 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/internal/library/imtenant"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
type sSysImAuditLog struct{}
|
||||
|
||||
func NewSysImAuditLog() *sSysImAuditLog {
|
||||
return &sSysImAuditLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImAuditLog(NewSysImAuditLog())
|
||||
}
|
||||
|
||||
// List 获取IM审计日志列表
|
||||
func (s *sSysImAuditLog) List(ctx context.Context, in *sysin.ImAuditLogListInp) (list []*sysin.ImAuditLogListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mod := s.model(ctx, db)
|
||||
mod = s.applyWhere(mod, in)
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("id")
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM审计日志列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
for _, item := range list {
|
||||
item.TenantId = tenant.Id
|
||||
item.TenantName = tenant.Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImAuditLog) model(ctx context.Context, db gdb.DB) *gdb.Model {
|
||||
return db.Model("im_audit_logs").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"id",
|
||||
"actor_uid",
|
||||
"actor_role",
|
||||
"action",
|
||||
"target_type",
|
||||
"target_id",
|
||||
"reason",
|
||||
"request_id",
|
||||
"ip",
|
||||
"user_agent",
|
||||
"CAST(before_snapshot AS CHAR) before_snapshot",
|
||||
"CAST(after_snapshot AS CHAR) after_snapshot",
|
||||
"CAST(metadata AS CHAR) metadata",
|
||||
"created_at",
|
||||
)
|
||||
}
|
||||
|
||||
func (s *sSysImAuditLog) applyWhere(mod *gdb.Model, in *sysin.ImAuditLogListInp) *gdb.Model {
|
||||
if in.ActorUid > 0 {
|
||||
mod = mod.Where("actor_uid", in.ActorUid)
|
||||
}
|
||||
if in.ActorRole != "" {
|
||||
mod = mod.WhereLike("actor_role", "%"+in.ActorRole+"%")
|
||||
}
|
||||
if in.Action != "" {
|
||||
mod = mod.WhereLike("action", "%"+in.Action+"%")
|
||||
}
|
||||
if in.TargetType != "" {
|
||||
mod = mod.Where("target_type", in.TargetType)
|
||||
}
|
||||
if in.TargetId != "" {
|
||||
mod = mod.WhereLike("target_id", "%"+in.TargetId+"%")
|
||||
}
|
||||
if in.RequestId != "" {
|
||||
mod = mod.WhereLike("request_id", "%"+in.RequestId+"%")
|
||||
}
|
||||
if in.Ip != "" {
|
||||
mod = mod.WhereLike("ip", "%"+in.Ip+"%")
|
||||
}
|
||||
if in.Keyword != "" {
|
||||
like := "%" + in.Keyword + "%"
|
||||
mod = mod.Where("(action LIKE ? OR target_type LIKE ? OR target_id LIKE ? OR reason LIKE ? OR request_id LIKE ?)", like, like, like, like, like)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween("created_at", in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
// View 获取IM审计日志指定信息
|
||||
func (s *sSysImAuditLog) View(ctx context.Context, in *sysin.ImAuditLogViewInp) (res *sysin.ImAuditLogViewModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.model(ctx, db).Where("id", in.Id).Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM审计日志信息失败,请稍后重试!")
|
||||
}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res.TenantId = tenant.Id
|
||||
res.TenantName = tenant.Name
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
// 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/internal/library/imtenant"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
type sSysImGroup struct{}
|
||||
|
||||
func NewSysImGroup() *sSysImGroup {
|
||||
return &sSysImGroup{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImGroup(NewSysImGroup())
|
||||
}
|
||||
|
||||
// List 获取IM群组列表
|
||||
func (s *sSysImGroup) List(ctx context.Context, in *sysin.ImGroupListInp) (list []*sysin.ImGroupListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mod := s.groupModel(ctx, db)
|
||||
mod = s.applyGroupWhere(mod, in)
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("t.touchedat").OrderDesc("t.id")
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群组列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
for _, item := range list {
|
||||
item.TenantId = tenant.Id
|
||||
item.TenantName = tenant.Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImGroup) groupModel(ctx context.Context, db gdb.DB) *gdb.Model {
|
||||
return db.Model("im_topics t").Safe().Ctx(ctx).
|
||||
LeftJoin("im_group_settings gs", "gs.topic = t.name").
|
||||
Fields(
|
||||
"t.name topic",
|
||||
"t.state",
|
||||
"COALESCE(gs.owner_user_id, t.owner) owner_id",
|
||||
"t.seqid seq_id",
|
||||
"t.delid del_id",
|
||||
"t.subcnt sub_cnt",
|
||||
"t.touchedat touched_at",
|
||||
"t.createdat created_at",
|
||||
"t.updatedat updated_at",
|
||||
"t.public public_json",
|
||||
"t.trusted trusted_json",
|
||||
"t.tags tags_json",
|
||||
"t.aux aux_json",
|
||||
"COALESCE(gs.name, '') group_name",
|
||||
"COALESCE(gs.avatar_file_id, '') avatar_file_id",
|
||||
"COALESCE(gs.description, '') description",
|
||||
"COALESCE(gs.group_status, '') group_status",
|
||||
"COALESCE(gs.join_approval, 0) join_approval",
|
||||
"COALESCE(gs.mute_all, 0) mute_all",
|
||||
"COALESCE(gs.member_upload_files, 0) member_upload_files",
|
||||
"COALESCE(gs.member_private_chat, 0) member_private_chat",
|
||||
"COALESCE(gs.member_invite, 0) member_invite",
|
||||
"COALESCE(gs.max_member_count, 0) max_member_count",
|
||||
"COALESCE(gs.max_admin_count, 0) max_admin_count",
|
||||
"COALESCE(gs.member_count, 0) business_member_count",
|
||||
"COALESCE(gs.version, 0) settings_version",
|
||||
"gs.updated_at settings_updated_at",
|
||||
).
|
||||
WhereLike("t.name", "grp%")
|
||||
}
|
||||
|
||||
func (s *sSysImGroup) applyGroupWhere(mod *gdb.Model, in *sysin.ImGroupListInp) *gdb.Model {
|
||||
if in.Topic != "" {
|
||||
mod = mod.WhereLike("t.name", "%"+in.Topic+"%")
|
||||
}
|
||||
if in.OwnerId > 0 {
|
||||
mod = mod.Where("COALESCE(gs.owner_user_id, t.owner)", in.OwnerId)
|
||||
}
|
||||
if in.State != nil {
|
||||
mod = mod.Where("t.state", *in.State)
|
||||
}
|
||||
if in.GroupStatus != "" {
|
||||
mod = mod.Where("gs.group_status", in.GroupStatus)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween("t.createdat", in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
if len(in.TouchedAt) == 2 {
|
||||
mod = mod.WhereBetween("t.touchedat", in.TouchedAt[0], in.TouchedAt[1])
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
// View 获取IM群组指定信息
|
||||
func (s *sSysImGroup) View(ctx context.Context, in *sysin.ImGroupViewInp) (res *sysin.ImGroupViewModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = db.Model("im_topics t").Safe().Ctx(ctx).
|
||||
LeftJoin("im_group_settings gs", "gs.topic = t.name").
|
||||
Fields(
|
||||
"t.name topic",
|
||||
"t.state",
|
||||
"COALESCE(gs.owner_user_id, t.owner) owner_id",
|
||||
"t.seqid seq_id",
|
||||
"t.delid del_id",
|
||||
"t.subcnt sub_cnt",
|
||||
"t.touchedat touched_at",
|
||||
"t.createdat created_at",
|
||||
"t.updatedat updated_at",
|
||||
"t.public public_json",
|
||||
"t.trusted trusted_json",
|
||||
"t.tags tags_json",
|
||||
"t.aux aux_json",
|
||||
"t.access access_json",
|
||||
"t.usebt use_bt",
|
||||
"COALESCE(gs.name, '') group_name",
|
||||
"COALESCE(gs.avatar_file_id, '') avatar_file_id",
|
||||
"COALESCE(gs.description, '') description",
|
||||
"COALESCE(gs.group_status, '') group_status",
|
||||
"COALESCE(gs.join_approval, 0) join_approval",
|
||||
"COALESCE(gs.mute_all, 0) mute_all",
|
||||
"COALESCE(gs.member_upload_files, 0) member_upload_files",
|
||||
"COALESCE(gs.member_private_chat, 0) member_private_chat",
|
||||
"COALESCE(gs.member_invite, 0) member_invite",
|
||||
"COALESCE(gs.max_member_count, 0) max_member_count",
|
||||
"COALESCE(gs.max_admin_count, 0) max_admin_count",
|
||||
"COALESCE(gs.member_count, 0) business_member_count",
|
||||
"COALESCE(gs.version, 0) settings_version",
|
||||
"gs.updated_at settings_updated_at",
|
||||
).
|
||||
Where("t.name", in.Topic).
|
||||
WhereLike("t.name", "grp%").
|
||||
Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM群组信息失败,请稍后重试!")
|
||||
}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res.TenantId = tenant.Id
|
||||
res.TenantName = tenant.Name
|
||||
return
|
||||
}
|
||||
|
||||
// MemberList 获取IM群成员列表
|
||||
func (s *sSysImGroup) MemberList(ctx context.Context, in *sysin.ImGroupMemberListInp) (list []*sysin.ImGroupMemberListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.hasGroupMemberTruth(ctx, db, in.Topic) {
|
||||
return s.memberListFromBusiness(ctx, db, in)
|
||||
}
|
||||
return s.memberListFromProjection(ctx, db, in)
|
||||
}
|
||||
|
||||
func (s *sSysImGroup) hasGroupMemberTruth(ctx context.Context, db gdb.DB, topic string) bool {
|
||||
count, err := db.Model("im_group_members").Safe().Ctx(ctx).Where("topic", topic).Count()
|
||||
return err == nil && count > 0
|
||||
}
|
||||
|
||||
func (s *sSysImGroup) memberListFromBusiness(ctx context.Context, db gdb.DB, in *sysin.ImGroupMemberListInp) (list []*sysin.ImGroupMemberListModel, totalCount int, err error) {
|
||||
mod := db.Model("im_group_members gm").Safe().Ctx(ctx).
|
||||
LeftJoin("im_users u", "u.id = gm.user_id").
|
||||
LeftJoin("im_user_profiles up", "up.user_id = gm.user_id").
|
||||
LeftJoin("im_subscriptions s", "s.topic = gm.topic AND s.userid = gm.user_id").
|
||||
Fields(
|
||||
"gm.user_id",
|
||||
"up.nickname",
|
||||
"u.state",
|
||||
"s.modewant mode_want",
|
||||
"s.modegiven mode_given",
|
||||
"s.recvseqid recv_seq_id",
|
||||
"s.readseqid read_seq_id",
|
||||
"s.delid del_id",
|
||||
"s.deletedat deleted_at",
|
||||
"s.createdat created_at",
|
||||
"s.updatedat updated_at",
|
||||
"s.private private_json",
|
||||
"gm.role",
|
||||
"gm.member_status",
|
||||
"gm.nickname group_nickname",
|
||||
"gm.pinned",
|
||||
"gm.mute_notifications",
|
||||
"gm.join_source",
|
||||
"gm.joined_by",
|
||||
"gm.joined_at",
|
||||
"gm.left_at",
|
||||
"gm.removed_by",
|
||||
"gm.version",
|
||||
).
|
||||
Where("gm.topic", in.Topic)
|
||||
if in.UserId > 0 {
|
||||
mod = mod.Where("gm.user_id", in.UserId)
|
||||
}
|
||||
if in.Role != "" {
|
||||
mod = mod.Where("gm.role", in.Role)
|
||||
}
|
||||
if in.MemberStatus != "" {
|
||||
mod = mod.Where("gm.member_status", in.MemberStatus)
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderAsc("gm.member_status").OrderAsc("gm.role").OrderAsc("gm.id")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群成员列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImGroup) memberListFromProjection(ctx context.Context, db gdb.DB, in *sysin.ImGroupMemberListInp) (list []*sysin.ImGroupMemberListModel, totalCount int, err error) {
|
||||
mod := db.Model("im_subscriptions s").Safe().Ctx(ctx).
|
||||
LeftJoin("im_users u", "u.id = s.userid").
|
||||
LeftJoin("im_user_profiles up", "up.user_id = s.userid").
|
||||
Fields(
|
||||
"s.userid user_id",
|
||||
"up.nickname",
|
||||
"u.state",
|
||||
"s.modewant mode_want",
|
||||
"s.modegiven mode_given",
|
||||
"s.recvseqid recv_seq_id",
|
||||
"s.readseqid read_seq_id",
|
||||
"s.delid del_id",
|
||||
"s.deletedat deleted_at",
|
||||
"s.createdat created_at",
|
||||
"s.updatedat updated_at",
|
||||
"s.private private_json",
|
||||
"'member' role",
|
||||
"IF(s.deletedat IS NULL, 'active', 'left') member_status",
|
||||
"'' group_nickname",
|
||||
"0 pinned",
|
||||
"0 mute_notifications",
|
||||
"'projection' join_source",
|
||||
"0 joined_by",
|
||||
"s.createdat joined_at",
|
||||
"s.deletedat left_at",
|
||||
"0 removed_by",
|
||||
"0 version",
|
||||
).
|
||||
Where("s.topic", in.Topic)
|
||||
|
||||
if in.UserId > 0 {
|
||||
mod = mod.Where("s.userid", in.UserId)
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderAsc("s.deletedat").OrderAsc("s.userid")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群成员列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Governance 获取IM群治理配置
|
||||
func (s *sSysImGroup) Governance(ctx context.Context, in *sysin.ImGroupTopicInp) (res *sysin.ImGroupGovernanceModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = db.Model("im_group_settings").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"topic",
|
||||
"owner_user_id",
|
||||
"name",
|
||||
"avatar_file_id",
|
||||
"description",
|
||||
"group_status",
|
||||
"join_approval",
|
||||
"mute_all",
|
||||
"member_upload_files",
|
||||
"member_private_chat",
|
||||
"member_invite",
|
||||
"max_member_count",
|
||||
"max_admin_count",
|
||||
"member_count",
|
||||
"version",
|
||||
"created_by",
|
||||
"dissolved_by",
|
||||
"dissolved_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).
|
||||
Where("topic", in.Topic).
|
||||
Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM群治理配置失败,请稍后重试!")
|
||||
}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res.TenantId = tenant.Id
|
||||
res.TenantName = tenant.Name
|
||||
return
|
||||
}
|
||||
|
||||
// JoinRequestList 获取IM入群申请列表
|
||||
func (s *sSysImGroup) JoinRequestList(ctx context.Context, in *sysin.ImGroupJoinRequestListInp) (list []*sysin.ImGroupJoinRequestListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mod := db.Model("im_group_join_requests").Safe().Ctx(ctx).Where("topic", in.Topic)
|
||||
if in.Status != "" {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
if in.ApplicantUserId > 0 {
|
||||
mod = mod.Where("applicant_user_id", in.ApplicantUserId)
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("id")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM入群申请列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// InvitationList 获取IM群邀请列表
|
||||
func (s *sSysImGroup) InvitationList(ctx context.Context, in *sysin.ImGroupInvitationListInp) (list []*sysin.ImGroupInvitationListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mod := db.Model("im_group_invitations").Safe().Ctx(ctx).Where("topic", in.Topic)
|
||||
if in.Status != "" {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
if in.TargetUserId > 0 {
|
||||
mod = mod.Where("target_user_id", in.TargetUserId)
|
||||
}
|
||||
if in.InviterUserId > 0 {
|
||||
mod = mod.Where("inviter_user_id", in.InviterUserId)
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("id")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群邀请列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AnnouncementList 获取IM群公告列表
|
||||
func (s *sSysImGroup) AnnouncementList(ctx context.Context, in *sysin.ImGroupAnnouncementListInp) (list []*sysin.ImGroupAnnouncementListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mod := db.Model("im_group_announcements").Safe().Ctx(ctx).Where("topic", in.Topic)
|
||||
if in.Status != "" {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("published_at").OrderDesc("id")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群公告列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MuteList 获取IM群禁言列表
|
||||
func (s *sSysImGroup) MuteList(ctx context.Context, in *sysin.ImGroupMuteListInp) (list []*sysin.ImGroupMuteListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mod := db.Model("im_group_mutes").Safe().Ctx(ctx).Where("topic", in.Topic)
|
||||
if in.Status != "" {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
if in.UserId > 0 {
|
||||
mod = mod.Where("user_id", in.UserId)
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("muted_at").OrderDesc("user_id")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群禁言列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AdminLogList 获取IM群管理日志列表
|
||||
func (s *sSysImGroup) AdminLogList(ctx context.Context, in *sysin.ImGroupAdminLogListInp) (list []*sysin.ImGroupAdminLogListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mod := db.Model("im_group_admin_logs").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"id",
|
||||
"log_id",
|
||||
"topic",
|
||||
"operator_id",
|
||||
"target_user_id",
|
||||
"action",
|
||||
"object_type",
|
||||
"object_id",
|
||||
"CAST(before_data AS CHAR) before_data",
|
||||
"CAST(after_data AS CHAR) after_data",
|
||||
"request_id",
|
||||
"idempotency_key",
|
||||
"created_at",
|
||||
).
|
||||
Where("topic", in.Topic)
|
||||
if in.Action != "" {
|
||||
mod = mod.WhereLike("action", "%"+in.Action+"%")
|
||||
}
|
||||
if in.OperatorId > 0 {
|
||||
mod = mod.Where("operator_id", in.OperatorId)
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("id")
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM群管理日志列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/library/imadmin"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sSysImInvite struct{}
|
||||
|
||||
func NewSysImInvite() *sSysImInvite {
|
||||
return &sSysImInvite{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImInvite(NewSysImInvite())
|
||||
}
|
||||
|
||||
type imInviteListRequest struct {
|
||||
Page int `json:"page,omitempty"`
|
||||
PerPage int `json:"per_page,omitempty"`
|
||||
InviteCode string `json:"invite_code,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
BatchID int64 `json:"batch_id,omitempty"`
|
||||
CreatedBy int64 `json:"created_by,omitempty"`
|
||||
CreatedAtStart string `json:"created_at_start,omitempty"`
|
||||
CreatedAtEnd string `json:"created_at_end,omitempty"`
|
||||
ExpiresAtStart string `json:"expires_at_start,omitempty"`
|
||||
ExpiresAtEnd string `json:"expires_at_end,omitempty"`
|
||||
}
|
||||
|
||||
type imInviteCreateRequest struct {
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
Remark string `json:"remark,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key,omitempty"`
|
||||
}
|
||||
|
||||
type imInviteBatchCreateRequest struct {
|
||||
Quantity int `json:"quantity"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
Remark string `json:"remark,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key,omitempty"`
|
||||
}
|
||||
|
||||
type imInviteCodeResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
BatchID *int64 `json:"batch_id"`
|
||||
Code string `json:"code"`
|
||||
Status string `json:"status"`
|
||||
ExpiresAt *time.Time `json:"expires_at"`
|
||||
UsedBy *int64 `json:"used_by"`
|
||||
UsedAt *time.Time `json:"used_at"`
|
||||
RequestID string `json:"request_id"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type imInviteBatchResponse struct {
|
||||
BatchID int64 `json:"batch_id"`
|
||||
Quantity int `json:"quantity"`
|
||||
ActiveCount int `json:"active_count"`
|
||||
UsedCount int `json:"used_count"`
|
||||
RevokedCount int `json:"revoked_count"`
|
||||
ExpiredCount int `json:"expired_count"`
|
||||
ExpiresAt *time.Time `json:"expires_at"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) List(ctx context.Context, in *sysin.ImInviteListInp) (list []*sysin.ImInviteCodeModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req, err := buildInviteListRequest(in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var page imadmin.Page[imInviteCodeResponse]
|
||||
if err = imadmin.DefaultClient(ctx).ListCodes(ctx, scope, req, &page); err != nil {
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_list", "invite_code", 0, "failed", g.Map{"hasInviteCode": in.InviteCode != ""})
|
||||
return
|
||||
}
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_list", "invite_code", 0, "success", g.Map{"hasInviteCode": in.InviteCode != ""})
|
||||
list = make([]*sysin.ImInviteCodeModel, 0, len(page.Items))
|
||||
for i := range page.Items {
|
||||
list = append(list, convertInviteCode(page.Items[i]))
|
||||
}
|
||||
totalCount = page.Total
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) Create(ctx context.Context, in *sysin.ImInviteCreateInp) (res *sysin.ImInviteCodeModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req := imInviteCreateRequest{ExpiresAt: formatRFC3339(in.ExpiresAt), Remark: in.Remark, IdempotencyKey: in.IdempotencyKey}
|
||||
var upstream imInviteCodeResponse
|
||||
err = imadmin.DefaultClient(ctx).CreateCode(ctx, scope, req, &upstream)
|
||||
res = convertInviteCode(upstream)
|
||||
result := "success"
|
||||
if err != nil {
|
||||
result = "failed"
|
||||
}
|
||||
targetId, _ := strconv.ParseInt(res.Id, 10, 64)
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_create", "invite_code", targetId, result, g.Map{"expiresAt": in.ExpiresAt, "remark": in.Remark})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) BatchCreate(ctx context.Context, in *sysin.ImInviteBatchCreateInp) (res *sysin.ImInviteBatchModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req := imInviteBatchCreateRequest{Quantity: in.Quantity, ExpiresAt: formatRFC3339(in.ExpiresAt), Remark: in.Remark, IdempotencyKey: in.IdempotencyKey}
|
||||
var upstream imInviteBatchResponse
|
||||
err = imadmin.DefaultClient(ctx).CreateBatch(ctx, scope, req, &upstream)
|
||||
res = convertInviteBatch(upstream)
|
||||
result := "success"
|
||||
if err != nil {
|
||||
result = "failed"
|
||||
}
|
||||
targetId, _ := strconv.ParseInt(res.BatchId, 10, 64)
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_batch_create", "invite_batch", targetId, result, g.Map{"quantity": in.Quantity, "expiresAt": in.ExpiresAt, "remark": in.Remark})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) BatchView(ctx context.Context, in *sysin.ImInviteBatchViewInp) (res *sysin.ImInviteBatchDetailModel, err error) {
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
batchId, err := sysin.ParseOptionalInt64(in.BatchId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var upstream imInviteBatchResponse
|
||||
err = imadmin.DefaultClient(ctx).GetBatch(ctx, scope, batchId, &upstream)
|
||||
res = &sysin.ImInviteBatchDetailModel{ImInviteBatchModel: *convertInviteBatch(upstream)}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) Revoke(ctx context.Context, in *sysin.ImInviteRevokeInp) (err error) {
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
id, err := sysin.ParseOptionalInt64(in.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = imadmin.DefaultClient(ctx).RevokeCode(ctx, scope, id)
|
||||
result := "success"
|
||||
if err != nil {
|
||||
result = "failed"
|
||||
}
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_revoke", "invite_code", id, result, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) BatchRevoke(ctx context.Context, in *sysin.ImInviteBatchRevokeInp) (res *sysin.ImInviteBatchModel, err error) {
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
batchId, err := sysin.ParseOptionalInt64(in.BatchId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var upstream struct {
|
||||
Batch *imInviteBatchResponse `json:"batch"`
|
||||
RevokedCount int `json:"revoked_count"`
|
||||
}
|
||||
err = imadmin.DefaultClient(ctx).RevokeBatch(ctx, scope, batchId, &upstream)
|
||||
if upstream.Batch != nil {
|
||||
res = convertInviteBatch(*upstream.Batch)
|
||||
} else {
|
||||
res = &sysin.ImInviteBatchModel{BatchId: strconv.FormatInt(batchId, 10), RevokedCount: upstream.RevokedCount}
|
||||
}
|
||||
result := "success"
|
||||
if err != nil {
|
||||
result = "failed"
|
||||
}
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_batch_revoke", "invite_batch", batchId, result, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImInvite) Export(ctx context.Context, in *sysin.ImInviteListInp) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req, err := buildInviteListRequest(in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := imadmin.DefaultClient(ctx).ExportCodes(ctx, scope, req)
|
||||
if err != nil {
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_export", "invite_code", 0, "failed", g.Map{"hasInviteCode": in.InviteCode != ""})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
request := ghttp.RequestFromCtx(ctx)
|
||||
if request == nil {
|
||||
return gerror.New("请求上下文不存在")
|
||||
}
|
||||
filename := resp.Header.Get("Content-Disposition")
|
||||
if filename == "" {
|
||||
filename = `attachment; filename="registration-invite-codes.csv"`
|
||||
}
|
||||
request.Response.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
|
||||
request.Response.Header().Set("Content-Disposition", filename)
|
||||
if _, err = io.Copy(request.Response.Writer, resp.Body); err != nil {
|
||||
return gerror.Wrap(err, "转发导出文件失败")
|
||||
}
|
||||
_ = writeImAdminAudit(ctx, scope.TenantId, "invite_export", "invite_code", 0, "success", g.Map{"hasInviteCode": in.InviteCode != ""})
|
||||
fmt.Fprint(request.Response.Writer, "")
|
||||
return
|
||||
}
|
||||
|
||||
func buildInviteListRequest(in *sysin.ImInviteListInp) (imInviteListRequest, error) {
|
||||
batchId, err := sysin.ParseOptionalInt64(in.BatchId)
|
||||
if err != nil {
|
||||
return imInviteListRequest{}, err
|
||||
}
|
||||
createdBy, err := sysin.ParseOptionalInt64(in.CreatedBy)
|
||||
if err != nil {
|
||||
return imInviteListRequest{}, err
|
||||
}
|
||||
return imInviteListRequest{
|
||||
Page: in.Page,
|
||||
PerPage: in.PerPage,
|
||||
InviteCode: in.InviteCode,
|
||||
Status: in.Status,
|
||||
BatchID: batchId,
|
||||
CreatedBy: createdBy,
|
||||
CreatedAtStart: formatRFC3339(in.CreatedAtStart),
|
||||
CreatedAtEnd: formatRFC3339(in.CreatedAtEnd),
|
||||
ExpiresAtStart: formatRFC3339(in.ExpiresAtStart),
|
||||
ExpiresAtEnd: formatRFC3339(in.ExpiresAtEnd),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func formatRFC3339(t *gtime.Time) string {
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
return t.Time.UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func convertTime(t *time.Time) *gtime.Time {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return gtime.New(t)
|
||||
}
|
||||
|
||||
func formatOptionalInt64(value *int64) string {
|
||||
if value == nil || *value == 0 {
|
||||
return ""
|
||||
}
|
||||
return strconv.FormatInt(*value, 10)
|
||||
}
|
||||
|
||||
func convertInviteCode(item imInviteCodeResponse) *sysin.ImInviteCodeModel {
|
||||
return &sysin.ImInviteCodeModel{
|
||||
Id: strconv.FormatInt(item.ID, 10),
|
||||
BatchId: formatOptionalInt64(item.BatchID),
|
||||
Code: item.Code,
|
||||
Status: item.Status,
|
||||
ExpiresAt: convertTime(item.ExpiresAt),
|
||||
UsedBy: formatOptionalInt64(item.UsedBy),
|
||||
UsedAt: convertTime(item.UsedAt),
|
||||
RequestId: item.RequestID,
|
||||
CreatedBy: strconv.FormatInt(item.CreatedBy, 10),
|
||||
Remark: item.Remark,
|
||||
CreatedAt: convertTime(item.CreatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func convertInviteBatch(item imInviteBatchResponse) *sysin.ImInviteBatchModel {
|
||||
return &sysin.ImInviteBatchModel{
|
||||
BatchId: strconv.FormatInt(item.BatchID, 10),
|
||||
Quantity: item.Quantity,
|
||||
ActiveCount: item.ActiveCount,
|
||||
UsedCount: item.UsedCount,
|
||||
RevokedCount: item.RevokedCount,
|
||||
ExpiredCount: item.ExpiredCount,
|
||||
ExpiresAt: convertTime(item.ExpiresAt),
|
||||
CreatedAt: convertTime(item.CreatedAt),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// 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/internal/consts"
|
||||
"hotgo/internal/library/imtenant"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sSysImRealname struct{}
|
||||
|
||||
func NewSysImRealname() *sSysImRealname {
|
||||
return &sSysImRealname{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImRealname(NewSysImRealname())
|
||||
}
|
||||
|
||||
// List 获取IM实名审核列表
|
||||
func (s *sSysImRealname) List(ctx context.Context, in *sysin.ImRealnameListInp) (list []*sysin.ImRealnameListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mod := s.model(ctx, db)
|
||||
mod = s.applyWhere(mod, in)
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("id")
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM实名审核列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
for _, item := range list {
|
||||
item.TenantId = tenant.Id
|
||||
item.TenantName = tenant.Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImRealname) model(ctx context.Context, db gdb.DB) *gdb.Model {
|
||||
return db.Model("im_realname_verifications").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"id",
|
||||
"user_id",
|
||||
"submission_id",
|
||||
"status",
|
||||
"id_type",
|
||||
"masked_name",
|
||||
"masked_id_number",
|
||||
"review_reason",
|
||||
"verified_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
}
|
||||
|
||||
func (s *sSysImRealname) applyWhere(mod *gdb.Model, in *sysin.ImRealnameListInp) *gdb.Model {
|
||||
if in.UserId > 0 {
|
||||
mod = mod.Where("user_id", in.UserId)
|
||||
}
|
||||
if in.SubmissionId != "" {
|
||||
mod = mod.WhereLike("submission_id", "%"+in.SubmissionId+"%")
|
||||
}
|
||||
if in.Status != "" {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween("created_at", in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
// View 获取IM实名审核指定信息
|
||||
func (s *sSysImRealname) View(ctx context.Context, in *sysin.ImRealnameViewInp) (res *sysin.ImRealnameViewModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Model("im_realname_verifications").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"id",
|
||||
"user_id",
|
||||
"submission_id",
|
||||
"status",
|
||||
"id_type",
|
||||
"masked_name",
|
||||
"masked_id_number",
|
||||
"id_number_hash",
|
||||
"review_reason",
|
||||
"verified_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).
|
||||
Where("id", in.Id).
|
||||
Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM实名审核信息失败,请稍后重试!")
|
||||
}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res.TenantId = tenant.Id
|
||||
res.TenantName = tenant.Name
|
||||
return
|
||||
}
|
||||
|
||||
// Review 审核IM实名
|
||||
func (s *sSysImRealname) Review(ctx context.Context, in *sysin.ImRealnameReviewInp) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := g.Map{
|
||||
"status": in.Status,
|
||||
"review_reason": in.ReviewReason,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
if in.Status == consts.ImRealnameStatusVerified {
|
||||
data["verified_at"] = gtime.Now()
|
||||
} else {
|
||||
data["verified_at"] = nil
|
||||
}
|
||||
|
||||
result, err := db.Model("im_realname_verifications").Safe().Ctx(ctx).
|
||||
Where("id", in.Id).
|
||||
Where("status", consts.ImRealnameStatusPending).
|
||||
Data(data).
|
||||
Update()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "审核IM实名失败,请稍后重试!")
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "读取审核结果失败,请稍后重试!")
|
||||
}
|
||||
if affected == 0 {
|
||||
return gerror.New("当前记录不是待审核状态,不能重复审核")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
const (
|
||||
imRegistrationConfigGroup = "im"
|
||||
imRegistrationSmsKey = "register_sms_enabled"
|
||||
)
|
||||
|
||||
type sSysImRegistration struct{}
|
||||
|
||||
func NewSysImRegistration() *sSysImRegistration {
|
||||
return &sSysImRegistration{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImRegistration(NewSysImRegistration())
|
||||
}
|
||||
|
||||
func (s *sSysImRegistration) GetConfig(ctx context.Context) (res *sysin.ImRegistrationConfigModel, err error) {
|
||||
if err = s.verifySuper(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return s.getConfig(ctx)
|
||||
}
|
||||
|
||||
func (s *sSysImRegistration) UpdateConfig(ctx context.Context, in *sysin.ImRegistrationConfigUpdateInp) (err error) {
|
||||
if err = s.verifySuper(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
old, err := s.getConfig(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
value := "0"
|
||||
if in.RegisterSmsEnabled {
|
||||
value = "1"
|
||||
}
|
||||
_, err = dao.SysConfig.Ctx(ctx).
|
||||
Where(dao.SysConfig.Columns().Group, imRegistrationConfigGroup).
|
||||
Where(dao.SysConfig.Columns().Key, imRegistrationSmsKey).
|
||||
Data(g.Map{
|
||||
dao.SysConfig.Columns().Value: value,
|
||||
dao.SysConfig.Columns().UpdatedAt: gtime.Now(),
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "更新IM注册短信开关失败")
|
||||
}
|
||||
_ = writeImAdminAudit(ctx, 0, "registration_sms_config_update", "config", 0, "success", g.Map{
|
||||
"old": old.RegisterSmsEnabled,
|
||||
"new": in.RegisterSmsEnabled,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImRegistration) getConfig(ctx context.Context) (res *sysin.ImRegistrationConfigModel, err error) {
|
||||
var record gdb.Record
|
||||
record, err = dao.SysConfig.Ctx(ctx).
|
||||
Where(dao.SysConfig.Columns().Group, imRegistrationConfigGroup).
|
||||
Where(dao.SysConfig.Columns().Key, imRegistrationSmsKey).
|
||||
Where(dao.SysConfig.Columns().Status, 1).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM注册短信开关失败")
|
||||
}
|
||||
if record.IsEmpty() {
|
||||
if err = s.seedConfig(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.getConfig(ctx)
|
||||
}
|
||||
return &sysin.ImRegistrationConfigModel{
|
||||
RegisterSmsEnabled: record[dao.SysConfig.Columns().Value].String() == "1",
|
||||
UpdatedAt: record[dao.SysConfig.Columns().UpdatedAt].GTime(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *sSysImRegistration) seedConfig(ctx context.Context) (err error) {
|
||||
_, err = dao.SysConfig.Ctx(ctx).Data(g.Map{
|
||||
dao.SysConfig.Columns().Group: imRegistrationConfigGroup,
|
||||
dao.SysConfig.Columns().Name: "注册短信发送",
|
||||
dao.SysConfig.Columns().Type: "bool",
|
||||
dao.SysConfig.Columns().Key: imRegistrationSmsKey,
|
||||
dao.SysConfig.Columns().Value: "1",
|
||||
dao.SysConfig.Columns().DefaultValue: "1",
|
||||
dao.SysConfig.Columns().Sort: 10,
|
||||
dao.SysConfig.Columns().Tip: "控制 IM 注册验证码是否真实发送",
|
||||
dao.SysConfig.Columns().IsDefault: 1,
|
||||
dao.SysConfig.Columns().Status: 1,
|
||||
dao.SysConfig.Columns().CreatedAt: gtime.Now(),
|
||||
dao.SysConfig.Columns().UpdatedAt: gtime.Now(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "初始化IM注册短信开关失败")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImRegistration) verifySuper(ctx context.Context) error {
|
||||
if !service.AdminMember().VerifySuperId(ctx, contexts.GetUserId(ctx)) {
|
||||
return gerror.New("仅平台超级管理员可操作IM注册配置")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
// 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/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/library/imtenant"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type sSysImTenant struct{}
|
||||
|
||||
func NewSysImTenant() *sSysImTenant {
|
||||
return &sSysImTenant{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImTenant(NewSysImTenant())
|
||||
}
|
||||
|
||||
// Model IM租户ORM模型
|
||||
func (s *sSysImTenant) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(imtenant.RegistryDB(ctx).Model(dao.ImTenant.Table()).Safe().Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sSysImTenant) configModel(ctx context.Context) *gdb.Model {
|
||||
return imtenant.RegistryDB(ctx).Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// List 获取IM租户列表
|
||||
func (s *sSysImTenant) List(ctx context.Context, in *sysin.ImTenantListInp) (list []*sysin.ImTenantListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mod := s.Model(ctx).Fields(
|
||||
dao.ImTenant.Columns().Id,
|
||||
dao.ImTenant.Columns().Code,
|
||||
dao.ImTenant.Columns().Name,
|
||||
dao.ImTenant.Columns().TenantDesc,
|
||||
dao.ImTenant.Columns().DbName,
|
||||
dao.ImTenant.Columns().State,
|
||||
dao.ImTenant.Columns().CreatedBy,
|
||||
dao.ImTenant.Columns().UpdatedBy,
|
||||
dao.ImTenant.Columns().CreatedAt,
|
||||
dao.ImTenant.Columns().UpdatedAt,
|
||||
)
|
||||
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.ImTenant.Columns().Id, in.Id)
|
||||
}
|
||||
if in.Code != "" {
|
||||
mod = mod.WhereLike(dao.ImTenant.Columns().Code, "%"+in.Code+"%")
|
||||
}
|
||||
if in.Name != "" {
|
||||
mod = mod.WhereLike(dao.ImTenant.Columns().Name, "%"+in.Name+"%")
|
||||
}
|
||||
if in.DbName != "" {
|
||||
mod = mod.WhereLike(dao.ImTenant.Columns().DbName, "%"+in.DbName+"%")
|
||||
}
|
||||
if in.State != nil {
|
||||
mod = mod.Where(dao.ImTenant.Columns().State, *in.State)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.ImTenant.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PerPage).
|
||||
OrderDesc(dao.ImTenant.Columns().Id).
|
||||
Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM租户列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.fillListConfig(ctx, list); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImTenant) fillListConfig(ctx context.Context, list []*sysin.ImTenantListModel) (err error) {
|
||||
ids := make([]int64, 0, len(list))
|
||||
for _, item := range list {
|
||||
ids = append(ids, item.Id)
|
||||
}
|
||||
|
||||
var configs []*entity.ImTenantConfig
|
||||
if err = s.configModel(ctx).
|
||||
WhereIn(dao.ImTenantConfig.Columns().TenantId, ids).
|
||||
Scan(&configs); err != nil {
|
||||
return gerror.Wrap(err, "获取IM租户配置失败,请稍后重试!")
|
||||
}
|
||||
configMap := make(map[int64]*entity.ImTenantConfig, len(configs))
|
||||
for _, item := range configs {
|
||||
configMap[item.TenantId] = item
|
||||
}
|
||||
|
||||
var tenants []*entity.ImTenant
|
||||
if err = s.Model(ctx).
|
||||
Fields(dao.ImTenant.Columns().Id, dao.ImTenant.Columns().DbDsn).
|
||||
WhereIn(dao.ImTenant.Columns().Id, ids).
|
||||
Scan(&tenants); err != nil {
|
||||
return gerror.Wrap(err, "获取IM租户连接配置失败,请稍后重试!")
|
||||
}
|
||||
dsnMap := make(map[int64]string, len(tenants))
|
||||
for _, item := range tenants {
|
||||
dsnMap[item.Id] = item.DbDsn
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
if conf, ok := configMap[item.Id]; ok {
|
||||
item.MaxUsers = conf.MaxUsers
|
||||
item.MaxGroups = conf.MaxGroups
|
||||
item.MaxGroupMembers = conf.MaxGroupMembers
|
||||
}
|
||||
item.DbDsnConfigured = dsnMap[item.Id] != ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增IM租户
|
||||
func (s *sSysImTenant) Edit(ctx context.Context, in *sysin.ImTenantEditInp) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return imtenant.RegistryDB(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
if err = s.checkCodeUnique(ctx, in.Id, in.Code); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
userId := contexts.GetUserId(ctx)
|
||||
if in.Id > 0 {
|
||||
in.UpdatedBy = userId
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(sysin.ImTenantUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
return gerror.Wrap(err, "修改IM租户失败,请稍后重试!")
|
||||
}
|
||||
return s.saveConfig(ctx, in.Id, in.MaxUsers, in.MaxGroups, in.MaxGroupMembers, userId)
|
||||
}
|
||||
|
||||
in.CreatedBy = userId
|
||||
id, err := s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(sysin.ImTenantInsertFields{}).
|
||||
Data(in).InsertAndGetId()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "新增IM租户失败,请稍后重试!")
|
||||
}
|
||||
return s.saveConfig(ctx, id, in.MaxUsers, in.MaxGroups, in.MaxGroupMembers, userId)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sSysImTenant) checkCodeUnique(ctx context.Context, id int64, code string) (err error) {
|
||||
mod := s.Model(ctx).Where(dao.ImTenant.Columns().Code, code)
|
||||
if id > 0 {
|
||||
mod = mod.Where(dao.ImTenant.Columns().Id+"<>?", id)
|
||||
}
|
||||
count, err := mod.Count()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "检查IM租户编码失败,请稍后重试!")
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("租户编码已存在")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImTenant) saveConfig(ctx context.Context, tenantId int64, maxUsers uint64, maxGroups uint64, maxGroupMembers uint, userId int64) (err error) {
|
||||
var conf *entity.ImTenantConfig
|
||||
if err = s.configModel(ctx).
|
||||
Where(dao.ImTenantConfig.Columns().TenantId, tenantId).
|
||||
Scan(&conf); err != nil {
|
||||
return gerror.Wrap(err, "获取IM租户配置失败,请稍后重试!")
|
||||
}
|
||||
|
||||
data := g.Map{
|
||||
dao.ImTenantConfig.Columns().MaxUsers: maxUsers,
|
||||
dao.ImTenantConfig.Columns().MaxGroups: maxGroups,
|
||||
dao.ImTenantConfig.Columns().MaxGroupMembers: maxGroupMembers,
|
||||
}
|
||||
if conf != nil && conf.Id > 0 {
|
||||
data[dao.ImTenantConfig.Columns().UpdatedBy] = userId
|
||||
if _, err = s.configModel(ctx).
|
||||
Fields(
|
||||
dao.ImTenantConfig.Columns().MaxUsers,
|
||||
dao.ImTenantConfig.Columns().MaxGroups,
|
||||
dao.ImTenantConfig.Columns().MaxGroupMembers,
|
||||
dao.ImTenantConfig.Columns().UpdatedBy,
|
||||
).
|
||||
WherePri(conf.Id).Data(data).Update(); err != nil {
|
||||
return gerror.Wrap(err, "修改IM租户配置失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data[dao.ImTenantConfig.Columns().TenantId] = tenantId
|
||||
data[dao.ImTenantConfig.Columns().CreatedBy] = userId
|
||||
if _, err = s.configModel(ctx).
|
||||
Fields(
|
||||
dao.ImTenantConfig.Columns().TenantId,
|
||||
dao.ImTenantConfig.Columns().MaxUsers,
|
||||
dao.ImTenantConfig.Columns().MaxGroups,
|
||||
dao.ImTenantConfig.Columns().MaxGroupMembers,
|
||||
dao.ImTenantConfig.Columns().CreatedBy,
|
||||
).
|
||||
Data(data).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增IM租户配置失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取IM租户指定信息
|
||||
func (s *sSysImTenant) View(ctx context.Context, in *sysin.ImTenantViewInp) (res *sysin.ImTenantViewModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM租户信息失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if res == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var conf *entity.ImTenantConfig
|
||||
if err = s.configModel(ctx).
|
||||
Where(dao.ImTenantConfig.Columns().TenantId, in.Id).
|
||||
Scan(&conf); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM租户配置失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if conf != nil {
|
||||
res.MaxUsers = conf.MaxUsers
|
||||
res.MaxGroups = conf.MaxGroups
|
||||
res.MaxGroupMembers = conf.MaxGroupMembers
|
||||
}
|
||||
res.DbDsnConfigured = res.DbDsn != ""
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新IM租户状态
|
||||
func (s *sSysImTenant) Status(ctx context.Context, in *sysin.ImTenantStatusInp) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.ImTenant.Columns().State: in.State,
|
||||
dao.ImTenant.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
}).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新IM租户状态失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// 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/internal/library/imtenant"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
type sSysImUser struct{}
|
||||
|
||||
func NewSysImUser() *sSysImUser {
|
||||
return &sSysImUser{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImUser(NewSysImUser())
|
||||
}
|
||||
|
||||
// List 获取IM用户列表
|
||||
func (s *sSysImUser) List(ctx context.Context, in *sysin.ImUserListInp) (list []*sysin.ImUserListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mod := s.listModel(ctx, db)
|
||||
mod = s.applyListWhere(mod, in)
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("u.id")
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM用户列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
item.TenantId = tenant.Id
|
||||
item.TenantName = tenant.Name
|
||||
}
|
||||
if err = s.fillAuthAndCredentials(ctx, db, list); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImUser) listModel(ctx context.Context, db gdb.DB) *gdb.Model {
|
||||
return db.Model("im_users u").Safe().Ctx(ctx).
|
||||
LeftJoin("im_user_profiles up", "up.user_id = u.id").
|
||||
LeftJoin("im_user_security us", "us.user_id = u.id").
|
||||
Fields(
|
||||
"u.id user_id",
|
||||
"u.state",
|
||||
"u.lastseen last_seen",
|
||||
"u.useragent user_agent",
|
||||
"u.createdat created_at",
|
||||
"u.updatedat updated_at",
|
||||
"up.nickname",
|
||||
"up.public_account",
|
||||
"up.gender",
|
||||
"up.region",
|
||||
"us.auth_version",
|
||||
"us.password_updated_at",
|
||||
)
|
||||
}
|
||||
|
||||
func (s *sSysImUser) applyListWhere(mod *gdb.Model, in *sysin.ImUserListInp) *gdb.Model {
|
||||
if in.UserId > 0 {
|
||||
mod = mod.Where("u.id", in.UserId)
|
||||
}
|
||||
if in.Nickname != "" {
|
||||
mod = mod.WhereLike("up.nickname", "%"+in.Nickname+"%")
|
||||
}
|
||||
if in.AuthName != "" {
|
||||
mod = mod.Where("EXISTS (SELECT 1 FROM im_auth a WHERE a.userid = u.id AND a.uname LIKE ?)", "%"+in.AuthName+"%")
|
||||
}
|
||||
if in.State != nil {
|
||||
mod = mod.Where("u.state", *in.State)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween("u.createdat", in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
if len(in.LastSeen) == 2 {
|
||||
mod = mod.WhereBetween("u.lastseen", in.LastSeen[0], in.LastSeen[1])
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
func (s *sSysImUser) fillAuthAndCredentials(ctx context.Context, db gdb.DB, list []*sysin.ImUserListModel) (err error) {
|
||||
ids := make([]int64, 0, len(list))
|
||||
userMap := make(map[int64]*sysin.ImUserListModel, len(list))
|
||||
for _, item := range list {
|
||||
ids = append(ids, item.UserId)
|
||||
userMap[item.UserId] = item
|
||||
}
|
||||
|
||||
authRows, err := db.Model("im_auth").Safe().Ctx(ctx).
|
||||
Fields("userid", "scheme", "uname").
|
||||
WhereIn("userid", ids).
|
||||
OrderAsc("scheme").OrderAsc("uname").
|
||||
All()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "获取IM用户认证标识失败,请稍后重试!")
|
||||
}
|
||||
authMap := make(map[int64][]string)
|
||||
for _, row := range authRows {
|
||||
userId := row["userid"].Int64()
|
||||
scheme := row["scheme"].String()
|
||||
uname := row["uname"].String()
|
||||
authMap[userId] = append(authMap[userId], scheme+":"+uname)
|
||||
}
|
||||
|
||||
credentialRows, err := db.Model("im_credentials").Safe().Ctx(ctx).
|
||||
Fields("userid", "method", "value", "done").
|
||||
WhereIn("userid", ids).
|
||||
WhereNull("deletedat").
|
||||
OrderAsc("method").OrderAsc("value").
|
||||
All()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "获取IM用户凭证失败,请稍后重试!")
|
||||
}
|
||||
credentialMap := make(map[int64][]string)
|
||||
for _, row := range credentialRows {
|
||||
userId := row["userid"].Int64()
|
||||
method := row["method"].String()
|
||||
value := row["value"].String()
|
||||
done := row["done"].Bool()
|
||||
if !done {
|
||||
value += "(未验证)"
|
||||
}
|
||||
credentialMap[userId] = append(credentialMap[userId], method+":"+value)
|
||||
}
|
||||
|
||||
for userId, item := range userMap {
|
||||
item.AuthNames = strings.Join(authMap[userId], ", ")
|
||||
item.Credentials = strings.Join(credentialMap[userId], ", ")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取IM用户指定信息
|
||||
func (s *sSysImUser) View(ctx context.Context, in *sysin.ImUserViewInp) (res *sysin.ImUserViewModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fields := append(s.viewFields(), "u.access access_json", "u.public profile_json", "u.trusted trusted_json", "u.tags tags_json")
|
||||
err = db.Model("im_users u").Safe().Ctx(ctx).
|
||||
LeftJoin("im_user_profiles up", "up.user_id = u.id").
|
||||
LeftJoin("im_user_security us", "us.user_id = u.id").
|
||||
Fields(fields...).
|
||||
Where("u.id", in.UserId).
|
||||
Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM用户信息失败,请稍后重试!")
|
||||
}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res.TenantId = tenant.Id
|
||||
res.TenantName = tenant.Name
|
||||
list := []*sysin.ImUserListModel{&res.ImUserListModel}
|
||||
if err = s.fillAuthAndCredentials(ctx, db, list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImUser) viewFields() []interface{} {
|
||||
return []interface{}{
|
||||
"u.id user_id",
|
||||
"u.state",
|
||||
"u.lastseen last_seen",
|
||||
"u.useragent user_agent",
|
||||
"u.createdat created_at",
|
||||
"u.updatedat updated_at",
|
||||
"up.nickname",
|
||||
"up.public_account",
|
||||
"up.gender",
|
||||
"up.region",
|
||||
"up.signature",
|
||||
"up.avatar_file_id",
|
||||
"CAST(up.birth_date AS CHAR) birth_date",
|
||||
"up.version profile_version",
|
||||
"us.auth_version",
|
||||
"us.password_updated_at",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
// 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/internal/library/imtenant"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sSysImWorkOrder struct{}
|
||||
|
||||
func NewSysImWorkOrder() *sSysImWorkOrder {
|
||||
return &sSysImWorkOrder{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysImWorkOrder(NewSysImWorkOrder())
|
||||
}
|
||||
|
||||
// List 获取IM举报反馈工单列表
|
||||
func (s *sSysImWorkOrder) List(ctx context.Context, in *sysin.ImWorkOrderListInp) (list []*sysin.ImWorkOrderListModel, totalCount int, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mod := s.listModel(ctx, db, in.Type)
|
||||
mod = s.applyWhere(mod, in)
|
||||
mod = mod.Page(in.Page, in.PerPage).OrderDesc("created_at").OrderDesc("id")
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取IM举报反馈工单列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
for _, item := range list {
|
||||
item.TenantId = tenant.Id
|
||||
item.TenantName = tenant.Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImWorkOrder) listModel(ctx context.Context, db gdb.DB, typ string) *gdb.Model {
|
||||
if typ == sysin.ImWorkOrderTypeAbuse {
|
||||
return db.Model("im_abuse_reports").Safe().Ctx(ctx).Fields(s.abuseListFields()...)
|
||||
}
|
||||
return db.Model("im_feedback_submissions").Safe().Ctx(ctx).Fields(s.feedbackListFields()...)
|
||||
}
|
||||
|
||||
func (s *sSysImWorkOrder) abuseListFields() []interface{} {
|
||||
return []interface{}{
|
||||
"'abuse' type",
|
||||
"id",
|
||||
"report_id order_id",
|
||||
"reporter_id user_id",
|
||||
"target_type",
|
||||
"CONCAT(target_topic, IF(target_seq>0, CONCAT('#', target_seq), ''), IF(target_user_id>0, CONCAT(' user:', target_user_id), '')) target_summary",
|
||||
"reason_code category",
|
||||
"description content",
|
||||
"status",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sSysImWorkOrder) feedbackListFields() []interface{} {
|
||||
return []interface{}{
|
||||
"'feedback' type",
|
||||
"id",
|
||||
"feedback_id order_id",
|
||||
"user_id",
|
||||
"'' target_type",
|
||||
"'' target_summary",
|
||||
"category",
|
||||
"content",
|
||||
"status",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sSysImWorkOrder) applyWhere(mod *gdb.Model, in *sysin.ImWorkOrderListInp) *gdb.Model {
|
||||
if in.UserId > 0 {
|
||||
mod = mod.Where("user_id", in.UserId)
|
||||
}
|
||||
if in.Status != "" {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
if in.Keyword != "" {
|
||||
mod = mod.WhereLike("content", "%"+in.Keyword+"%")
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween("created_at", in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
// View 获取IM举报反馈工单指定信息
|
||||
func (s *sSysImWorkOrder) View(ctx context.Context, in *sysin.ImWorkOrderViewInp) (res *sysin.ImWorkOrderViewModel, err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, tenant, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in.Type == sysin.ImWorkOrderTypeAbuse {
|
||||
res, err = s.viewAbuse(ctx, db, in.Id)
|
||||
} else {
|
||||
res, err = s.viewFeedback(ctx, db, in.Id)
|
||||
}
|
||||
if err != nil || res == nil {
|
||||
return
|
||||
}
|
||||
res.TenantId = tenant.Id
|
||||
res.TenantName = tenant.Name
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImWorkOrder) viewAbuse(ctx context.Context, db gdb.DB, id int64) (res *sysin.ImWorkOrderViewModel, err error) {
|
||||
err = db.Model("im_abuse_reports").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"'abuse' type",
|
||||
"id",
|
||||
"report_id order_id",
|
||||
"reporter_id user_id",
|
||||
"target_type",
|
||||
"CONCAT(target_topic, IF(target_seq>0, CONCAT('#', target_seq), ''), IF(target_user_id>0, CONCAT(' user:', target_user_id), '')) target_summary",
|
||||
"reason_code category",
|
||||
"description content",
|
||||
"description",
|
||||
"evidence_file_ids evidence_json",
|
||||
"snapshot snapshot_json",
|
||||
"idempotency_key",
|
||||
"status",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).
|
||||
Where("id", id).
|
||||
Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM举报工单信息失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImWorkOrder) viewFeedback(ctx context.Context, db gdb.DB, id int64) (res *sysin.ImWorkOrderViewModel, err error) {
|
||||
err = db.Model("im_feedback_submissions").Safe().Ctx(ctx).
|
||||
Fields(
|
||||
"'feedback' type",
|
||||
"id",
|
||||
"feedback_id order_id",
|
||||
"user_id",
|
||||
"'' target_type",
|
||||
"'' target_summary",
|
||||
"category",
|
||||
"content",
|
||||
"contact",
|
||||
"attachment_file_ids evidence_json",
|
||||
"client_context client_context_json",
|
||||
"include_diagnostics",
|
||||
"idempotency_key",
|
||||
"status",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).
|
||||
Where("id", id).
|
||||
Scan(&res)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "获取IM反馈工单信息失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新IM举报反馈工单状态
|
||||
func (s *sSysImWorkOrder) Status(ctx context.Context, in *sysin.ImWorkOrderStatusInp) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
db, _, err := imtenant.DB(ctx, in.TenantId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
table := "im_abuse_reports"
|
||||
if in.Type == sysin.ImWorkOrderTypeFeedback {
|
||||
table = "im_feedback_submissions"
|
||||
}
|
||||
if _, err = db.Model(table).Safe().Ctx(ctx).
|
||||
Where("id", in.Id).
|
||||
Data(g.Map{
|
||||
"status": in.Status,
|
||||
"updated_at": gtime.Now(),
|
||||
}).
|
||||
Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新IM举报反馈工单状态失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
|
||||
module = mctx.Module
|
||||
clientIp = location.GetClientIp(request)
|
||||
postData = gjson.New(consts.NilJsonToString)
|
||||
getData = gjson.New(request.URL.Query())
|
||||
getData = s.MaskSensitiveJson(gjson.New(request.URL.Query()))
|
||||
headerData = gjson.New(consts.NilJsonToString)
|
||||
errorData = gjson.New(consts.NilJsonToString)
|
||||
data entity.SysLog
|
||||
@@ -194,7 +194,7 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
|
||||
|
||||
// post参数
|
||||
if body, ok := mctx.Data["request.body"].(*gjson.Json); ok {
|
||||
postData = body
|
||||
postData = s.MaskSensitiveJson(body)
|
||||
}
|
||||
|
||||
// post表单
|
||||
@@ -208,12 +208,7 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
|
||||
if postData.IsNil() || len(postData.Map()) == 0 {
|
||||
postData = gjson.New(consts.NilJsonToString)
|
||||
} else {
|
||||
// 隐藏明文显示的密码
|
||||
for k := range postData.Map() {
|
||||
if gstr.ContainsI(k, "password") {
|
||||
postData.MustSet(k, "******")
|
||||
}
|
||||
}
|
||||
postData = s.MaskSensitiveJson(postData)
|
||||
}
|
||||
|
||||
// 当前登录用户
|
||||
@@ -247,7 +242,7 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
|
||||
Url: request.URL.Path,
|
||||
GetData: getData,
|
||||
PostData: postData,
|
||||
HeaderData: s.SimplifyHeaderParams(headerData),
|
||||
HeaderData: s.SimplifyHeaderParams(s.MaskSensitiveJson(headerData)),
|
||||
Ip: clientIp,
|
||||
ProvinceId: ipData.ProvinceCode,
|
||||
CityId: ipData.CityCode,
|
||||
@@ -273,6 +268,10 @@ func (s *sSysLog) SimplifyHeaderParams(data *gjson.Json) *gjson.Json {
|
||||
var filters = []string{"Accept", "Authorization", "Cookie"}
|
||||
for _, filter := range filters {
|
||||
v := data.Get(filter)
|
||||
if isSensitiveLogField(filter) {
|
||||
data.MustSet(filter, "******")
|
||||
continue
|
||||
}
|
||||
if len(v.String()) > 128 {
|
||||
data.MustRemove(filter)
|
||||
}
|
||||
@@ -280,6 +279,58 @@ func (s *sSysLog) SimplifyHeaderParams(data *gjson.Json) *gjson.Json {
|
||||
return data
|
||||
}
|
||||
|
||||
// MaskSensitiveJson masks sensitive request fields before access logs are stored.
|
||||
func (s *sSysLog) MaskSensitiveJson(data *gjson.Json) *gjson.Json {
|
||||
if data == nil || data.IsNil() {
|
||||
return data
|
||||
}
|
||||
return gjson.New(maskSensitiveValue(data.Map()))
|
||||
}
|
||||
|
||||
func maskSensitiveValue(v interface{}) interface{} {
|
||||
switch value := v.(type) {
|
||||
case map[string]interface{}:
|
||||
res := make(map[string]interface{}, len(value))
|
||||
for k, item := range value {
|
||||
if isSensitiveLogField(k) {
|
||||
res[k] = "******"
|
||||
continue
|
||||
}
|
||||
res[k] = maskSensitiveValue(item)
|
||||
}
|
||||
return res
|
||||
case map[string][]string:
|
||||
res := make(map[string]interface{}, len(value))
|
||||
for k, item := range value {
|
||||
if isSensitiveLogField(k) {
|
||||
res[k] = "******"
|
||||
continue
|
||||
}
|
||||
res[k] = item
|
||||
}
|
||||
return res
|
||||
case []interface{}:
|
||||
for i, item := range value {
|
||||
value[i] = maskSensitiveValue(item)
|
||||
}
|
||||
return value
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func isSensitiveLogField(field string) bool {
|
||||
sensitive := []string{"password", "invitecode", "invite_code", "servicesecret", "service_secret", "authorization", "x-service-secret"}
|
||||
normalized := gstr.Replace(gstr.Replace(gstr.ToLower(field), "-", ""), "_", "")
|
||||
for _, item := range sensitive {
|
||||
item = gstr.Replace(gstr.Replace(item, "-", ""), "_", "")
|
||||
if normalized == item || gstr.Contains(normalized, item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// View 获取指定请求日志信息
|
||||
func (s *sSysLog) View(ctx context.Context, in *sysin.LogViewInp) (res *sysin.LogViewModel, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
@@ -22,6 +22,7 @@ type Context struct {
|
||||
// Identity 通用身份模型
|
||||
type Identity struct {
|
||||
Id int64 `json:"id" description:"用户ID"`
|
||||
TenantId int64 `json:"tenantId" description:"IM租户ID"`
|
||||
Pid int64 `json:"pid" description:"上级ID"`
|
||||
DeptId int64 `json:"deptId" description:"部门ID"`
|
||||
DeptType string `json:"deptType" description:"部门类型"`
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
type AdminMember struct {
|
||||
g.Meta `orm:"table:hg_admin_member, do:true"`
|
||||
Id any // 管理员ID
|
||||
TenantId any // IM租户ID
|
||||
DeptId any // 部门ID
|
||||
RoleId any // 角色ID
|
||||
RealName any // 真实姓名
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImTenant is the golang structure of table im_tenant for DAO operations like Where/Data.
|
||||
type ImTenant struct {
|
||||
g.Meta `orm:"table:im_tenant, do:true"`
|
||||
Id any //
|
||||
Code any //
|
||||
Name any //
|
||||
TenantDesc any //
|
||||
DbName any //
|
||||
DbDsn any //
|
||||
State any //
|
||||
CreatedAt *gtime.Time //
|
||||
CreatedBy any //
|
||||
UpdatedAt *gtime.Time //
|
||||
UpdatedBy any //
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImTenantConfig is the golang structure of table im_tenant_config for DAO operations like Where/Data.
|
||||
type ImTenantConfig struct {
|
||||
g.Meta `orm:"table:im_tenant_config, do:true"`
|
||||
Id any //
|
||||
TenantId any //
|
||||
MaxUsers any // 0 means unlimited
|
||||
MaxGroups any // 0 means unlimited
|
||||
MaxGroupMembers any // 0 means unlimited
|
||||
CreatedAt *gtime.Time //
|
||||
CreatedBy any //
|
||||
UpdatedAt *gtime.Time //
|
||||
UpdatedBy any //
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
// AdminMember is the golang structure for table admin_member.
|
||||
type AdminMember struct {
|
||||
Id int64 `json:"id" orm:"id" description:"管理员ID"`
|
||||
TenantId int64 `json:"tenantId" orm:"tenant_id" description:"IM租户ID"`
|
||||
DeptId int64 `json:"deptId" orm:"dept_id" description:"部门ID"`
|
||||
RoleId int64 `json:"roleId" orm:"role_id" description:"角色ID"`
|
||||
RealName string `json:"realName" orm:"real_name" description:"真实姓名"`
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImTenant is the golang structure for table im_tenant.
|
||||
type ImTenant struct {
|
||||
Id int64 `json:"id" orm:"id" description:""`
|
||||
Code string `json:"code" orm:"code" description:""`
|
||||
Name string `json:"name" orm:"name" description:""`
|
||||
TenantDesc string `json:"tenantDesc" orm:"tenant_desc" description:""`
|
||||
DbName string `json:"dbName" orm:"db_name" description:""`
|
||||
DbDsn string `json:"dbDsn" orm:"db_dsn" description:""`
|
||||
State int `json:"state" orm:"state" description:""`
|
||||
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
|
||||
CreatedBy int64 `json:"createdBy" orm:"created_by" description:""`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
|
||||
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:""`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImTenantConfig is the golang structure for table im_tenant_config.
|
||||
type ImTenantConfig struct {
|
||||
Id int64 `json:"id" orm:"id" description:""`
|
||||
TenantId int64 `json:"tenantId" orm:"tenant_id" description:""`
|
||||
MaxUsers uint64 `json:"maxUsers" orm:"max_users" description:"0 means unlimited"`
|
||||
MaxGroups uint64 `json:"maxGroups" orm:"max_groups" description:"0 means unlimited"`
|
||||
MaxGroupMembers uint `json:"maxGroupMembers" orm:"max_group_members" description:"0 means unlimited"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
|
||||
CreatedBy int64 `json:"createdBy" orm:"created_by" description:""`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
|
||||
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:""`
|
||||
}
|
||||
@@ -115,6 +115,7 @@ type LoginMemberInfoModel struct {
|
||||
// MemberEditInp 修改用户
|
||||
type MemberEditInp struct {
|
||||
Id int64 `json:"id" dc:"管理员ID"`
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
RoleId int64 `json:"roleId" v:"required#角色不能为空" dc:"角色ID"`
|
||||
PostIds []int64 `json:"postIds" dc:"岗位ID"`
|
||||
DeptId int64 `json:"deptId" v:"required#部门不能为空" dc:"部门ID"`
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Package sysin
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2026 HotGo
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImAuditLogListInp 获取IM审计日志列表
|
||||
type ImAuditLogListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
ActorUid int64 `json:"actorUid" dc:"操作人UID"`
|
||||
ActorRole string `json:"actorRole" dc:"操作角色"`
|
||||
Action string `json:"action" dc:"操作类型"`
|
||||
TargetType string `json:"targetType" dc:"目标类型"`
|
||||
TargetId string `json:"targetId" dc:"目标ID"`
|
||||
RequestId string `json:"requestId" dc:"请求ID"`
|
||||
Ip string `json:"ip" dc:"IP"`
|
||||
Keyword string `json:"keyword" dc:"关键词"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
func (in *ImAuditLogListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImAuditLogListModel struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
ActorUid int64 `json:"actorUid" dc:"操作人UID"`
|
||||
ActorRole string `json:"actorRole" dc:"操作角色"`
|
||||
Action string `json:"action" dc:"操作类型"`
|
||||
TargetType string `json:"targetType" dc:"目标类型"`
|
||||
TargetId string `json:"targetId" dc:"目标ID"`
|
||||
Reason string `json:"reason" dc:"操作原因"`
|
||||
RequestId string `json:"requestId" dc:"请求ID"`
|
||||
Ip string `json:"ip" dc:"IP"`
|
||||
UserAgent string `json:"userAgent" dc:"客户端"`
|
||||
BeforeSnapshot string `json:"beforeSnapshot" dc:"变更前快照"`
|
||||
AfterSnapshot string `json:"afterSnapshot" dc:"变更后快照"`
|
||||
Metadata string `json:"metadata" dc:"元数据"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
// ImAuditLogViewInp 获取指定IM审计日志信息
|
||||
type ImAuditLogViewInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
}
|
||||
|
||||
func (in *ImAuditLogViewInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Id <= 0 {
|
||||
return gerror.New("ID不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImAuditLogViewModel struct {
|
||||
ImAuditLogListModel
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
// Package sysin
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2026 HotGo
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImGroupListInp 获取IM群组列表
|
||||
type ImGroupListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
OwnerId int64 `json:"ownerId" dc:"群主ID"`
|
||||
State *int `json:"state" dc:"通信状态"`
|
||||
GroupStatus string `json:"groupStatus" dc:"业务状态"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
TouchedAt []*gtime.Time `json:"touchedAt" dc:"活跃时间"`
|
||||
}
|
||||
|
||||
func (in *ImGroupListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImGroupListModel struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
State int `json:"state" dc:"状态"`
|
||||
OwnerId int64 `json:"ownerId" dc:"群主ID"`
|
||||
SeqId int `json:"seqId" dc:"最新消息序号"`
|
||||
DelId int `json:"delId" dc:"删除序号"`
|
||||
SubCnt int `json:"subCnt" dc:"成员数"`
|
||||
TouchedAt *gtime.Time `json:"touchedAt" dc:"活跃时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
PublicJson string `json:"publicJson" dc:"公开资料"`
|
||||
TrustedJson string `json:"trustedJson" dc:"可信资料"`
|
||||
TagsJson string `json:"tagsJson" dc:"标签"`
|
||||
AuxJson string `json:"auxJson" dc:"辅助资料"`
|
||||
|
||||
GroupName string `json:"groupName" dc:"群名称"`
|
||||
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
|
||||
Description string `json:"description" dc:"简介"`
|
||||
GroupStatus string `json:"groupStatus" dc:"业务状态"`
|
||||
JoinApproval int `json:"joinApproval" dc:"入群审批"`
|
||||
MuteAll int `json:"muteAll" dc:"全员禁言"`
|
||||
MemberUploadFiles int `json:"memberUploadFiles" dc:"成员上传文件"`
|
||||
MemberPrivateChat int `json:"memberPrivateChat" dc:"成员私聊"`
|
||||
MemberInvite int `json:"memberInvite" dc:"成员邀请"`
|
||||
MaxMemberCount int `json:"maxMemberCount" dc:"最大成员数"`
|
||||
MaxAdminCount int `json:"maxAdminCount" dc:"最大管理员数"`
|
||||
BusinessMemberCount int `json:"businessMemberCount" dc:"业务成员数"`
|
||||
SettingsVersion int `json:"settingsVersion" dc:"治理配置版本"`
|
||||
SettingsUpdatedAt *gtime.Time `json:"settingsUpdatedAt" dc:"治理配置更新时间"`
|
||||
}
|
||||
|
||||
// ImGroupViewInp 获取指定IM群组信息
|
||||
type ImGroupViewInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Topic string `json:"topic" v:"required#Topic不能为空" dc:"Topic"`
|
||||
}
|
||||
|
||||
func (in *ImGroupViewInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Topic == "" {
|
||||
return gerror.New("Topic不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImGroupViewModel struct {
|
||||
ImGroupListModel
|
||||
AccessJson string `json:"accessJson" dc:"访问权限"`
|
||||
UseBt int `json:"useBt" dc:"是否开启BT"`
|
||||
}
|
||||
|
||||
// ImGroupMemberListInp 获取IM群成员列表
|
||||
type ImGroupMemberListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Topic string `json:"topic" v:"required#Topic不能为空" dc:"Topic"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
Role string `json:"role" dc:"角色"`
|
||||
MemberStatus string `json:"memberStatus" dc:"成员状态"`
|
||||
}
|
||||
|
||||
func (in *ImGroupMemberListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Topic == "" {
|
||||
return gerror.New("Topic不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImGroupMemberListModel struct {
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
Nickname string `json:"nickname" dc:"昵称"`
|
||||
State int `json:"state" dc:"用户状态"`
|
||||
ModeWant string `json:"modeWant" dc:"期望权限"`
|
||||
ModeGiven string `json:"modeGiven" dc:"授予权限"`
|
||||
RecvSeqId int `json:"recvSeqId" dc:"接收序号"`
|
||||
ReadSeqId int `json:"readSeqId" dc:"已读序号"`
|
||||
DelId int `json:"delId" dc:"删除序号"`
|
||||
DeletedAt *gtime.Time `json:"deletedAt" dc:"删除时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"加入时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
PrivateJson string `json:"privateJson" dc:"私有资料"`
|
||||
Role string `json:"role" dc:"业务角色"`
|
||||
MemberStatus string `json:"memberStatus" dc:"业务状态"`
|
||||
GroupNickname string `json:"groupNickname" dc:"群昵称"`
|
||||
Pinned int `json:"pinned" dc:"置顶"`
|
||||
MuteNotifications int `json:"muteNotifications" dc:"免打扰"`
|
||||
JoinSource string `json:"joinSource" dc:"加入来源"`
|
||||
JoinedBy int64 `json:"joinedBy" dc:"邀请/处理人"`
|
||||
JoinedAt *gtime.Time `json:"joinedAt" dc:"业务加入时间"`
|
||||
LeftAt *gtime.Time `json:"leftAt" dc:"离开时间"`
|
||||
RemovedBy int64 `json:"removedBy" dc:"移除人"`
|
||||
Version int `json:"version" dc:"版本"`
|
||||
}
|
||||
|
||||
// ImGroupTopicInp 群Topic通用入参
|
||||
type ImGroupTopicInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Topic string `json:"topic" v:"required#Topic不能为空" dc:"Topic"`
|
||||
}
|
||||
|
||||
func (in *ImGroupTopicInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Topic == "" {
|
||||
return gerror.New("Topic不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImGroupGovernanceModel struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
OwnerUserId int64 `json:"ownerUserId" dc:"群主ID"`
|
||||
Name string `json:"name" dc:"群名称"`
|
||||
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
|
||||
Description string `json:"description" dc:"简介"`
|
||||
GroupStatus string `json:"groupStatus" dc:"业务状态"`
|
||||
JoinApproval int `json:"joinApproval" dc:"入群审批"`
|
||||
MuteAll int `json:"muteAll" dc:"全员禁言"`
|
||||
MemberUploadFiles int `json:"memberUploadFiles" dc:"成员上传文件"`
|
||||
MemberPrivateChat int `json:"memberPrivateChat" dc:"成员私聊"`
|
||||
MemberInvite int `json:"memberInvite" dc:"成员邀请"`
|
||||
MaxMemberCount int `json:"maxMemberCount" dc:"最大成员数"`
|
||||
MaxAdminCount int `json:"maxAdminCount" dc:"最大管理员数"`
|
||||
MemberCount int `json:"memberCount" dc:"成员数"`
|
||||
Version int `json:"version" dc:"版本"`
|
||||
CreatedBy int64 `json:"createdBy" dc:"创建人"`
|
||||
DissolvedBy int64 `json:"dissolvedBy" dc:"解散人"`
|
||||
DissolvedAt *gtime.Time `json:"dissolvedAt" dc:"解散时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
type ImGroupJoinRequestListInp struct {
|
||||
form.PageReq
|
||||
ImGroupTopicInp
|
||||
Status string `json:"status" dc:"状态"`
|
||||
ApplicantUserId int64 `json:"applicantUserId" dc:"申请人ID"`
|
||||
}
|
||||
|
||||
type ImGroupJoinRequestListModel struct {
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
RequestId string `json:"requestId" dc:"申请ID"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
ApplicantUserId int64 `json:"applicantUserId" dc:"申请人ID"`
|
||||
InviterUserId int64 `json:"inviterUserId" dc:"邀请人ID"`
|
||||
Source string `json:"source" dc:"来源"`
|
||||
Message string `json:"message" dc:"申请消息"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
RejectReason string `json:"rejectReason" dc:"拒绝原因"`
|
||||
HandledBy int64 `json:"handledBy" dc:"处理人"`
|
||||
HandledAt *gtime.Time `json:"handledAt" dc:"处理时间"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
|
||||
Version int `json:"version" dc:"版本"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
type ImGroupInvitationListInp struct {
|
||||
form.PageReq
|
||||
ImGroupTopicInp
|
||||
Status string `json:"status" dc:"状态"`
|
||||
TargetUserId int64 `json:"targetUserId" dc:"目标用户ID"`
|
||||
InviterUserId int64 `json:"inviterUserId" dc:"邀请人ID"`
|
||||
}
|
||||
|
||||
type ImGroupInvitationListModel struct {
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
InvitationId string `json:"invitationId" dc:"邀请ID"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
InviterUserId int64 `json:"inviterUserId" dc:"邀请人ID"`
|
||||
TargetUserId int64 `json:"targetUserId" dc:"目标用户ID"`
|
||||
Message string `json:"message" dc:"邀请消息"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
Result string `json:"result" dc:"结果"`
|
||||
JoinRequestId string `json:"joinRequestId" dc:"入群申请ID"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
|
||||
HandledAt *gtime.Time `json:"handledAt" dc:"处理时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
type ImGroupAnnouncementListInp struct {
|
||||
form.PageReq
|
||||
ImGroupTopicInp
|
||||
Status string `json:"status" dc:"状态"`
|
||||
}
|
||||
|
||||
type ImGroupAnnouncementListModel struct {
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
AnnouncementId string `json:"announcementId" dc:"公告ID"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
AuthorUserId int64 `json:"authorUserId" dc:"发布人ID"`
|
||||
Content string `json:"content" dc:"内容"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
Version int `json:"version" dc:"版本"`
|
||||
PublishedAt *gtime.Time `json:"publishedAt" dc:"发布时间"`
|
||||
DeletedBy int64 `json:"deletedBy" dc:"删除人"`
|
||||
DeletedAt *gtime.Time `json:"deletedAt" dc:"删除时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
type ImGroupMuteListInp struct {
|
||||
form.PageReq
|
||||
ImGroupTopicInp
|
||||
Status string `json:"status" dc:"状态"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
}
|
||||
|
||||
type ImGroupMuteListModel struct {
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
MutedBy int64 `json:"mutedBy" dc:"禁言人"`
|
||||
MutedAt *gtime.Time `json:"mutedAt" dc:"禁言时间"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
|
||||
Reason string `json:"reason" dc:"原因"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
RevokedBy int64 `json:"revokedBy" dc:"解除人"`
|
||||
RevokedAt *gtime.Time `json:"revokedAt" dc:"解除时间"`
|
||||
Version int `json:"version" dc:"版本"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
type ImGroupAdminLogListInp struct {
|
||||
form.PageReq
|
||||
ImGroupTopicInp
|
||||
Action string `json:"action" dc:"动作"`
|
||||
OperatorId int64 `json:"operatorId" dc:"操作人ID"`
|
||||
}
|
||||
|
||||
type ImGroupAdminLogListModel struct {
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
LogId string `json:"logId" dc:"日志ID"`
|
||||
Topic string `json:"topic" dc:"Topic"`
|
||||
OperatorId int64 `json:"operatorId" dc:"操作人"`
|
||||
TargetUserId int64 `json:"targetUserId" dc:"目标用户"`
|
||||
Action string `json:"action" dc:"动作"`
|
||||
ObjectType string `json:"objectType" dc:"对象类型"`
|
||||
ObjectId string `json:"objectId" dc:"对象ID"`
|
||||
BeforeData string `json:"beforeData" dc:"变更前"`
|
||||
AfterData string `json:"afterData" dc:"变更后"`
|
||||
RequestId string `json:"requestId" dc:"请求ID"`
|
||||
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/model/input/form"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type ImInviteListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
InviteCode string `json:"inviteCode" dc:"完整邀请码"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
BatchId string `json:"batchId" dc:"批次ID"`
|
||||
CreatedBy string `json:"createdBy" dc:"创建人"`
|
||||
CreatedAtStart *gtime.Time `json:"createdAtStart" dc:"创建开始时间"`
|
||||
CreatedAtEnd *gtime.Time `json:"createdAtEnd" dc:"创建结束时间"`
|
||||
ExpiresAtStart *gtime.Time `json:"expiresAtStart" dc:"过期开始时间"`
|
||||
ExpiresAtEnd *gtime.Time `json:"expiresAtEnd" dc:"过期结束时间"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
ExpiresAt []*gtime.Time `json:"expiresAt" dc:"到期时间"`
|
||||
}
|
||||
|
||||
func (in *ImInviteListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.PerPage <= 0 {
|
||||
in.PerPage = 20
|
||||
}
|
||||
if in.PerPage > 100 {
|
||||
return gerror.New("每页数量最大值不能大于100")
|
||||
}
|
||||
if in.InviteCode != "" && len(in.InviteCode) != 5 {
|
||||
return gerror.New("邀请码必须为完整5位")
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAtStart = in.CreatedAt[0]
|
||||
in.CreatedAtEnd = in.CreatedAt[1]
|
||||
}
|
||||
if len(in.ExpiresAt) == 2 {
|
||||
in.ExpiresAtStart = in.ExpiresAt[0]
|
||||
in.ExpiresAtEnd = in.ExpiresAt[1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImInviteCreateInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" v:"required#到期时间不能为空" dc:"到期时间"`
|
||||
Remark string `json:"remark" dc:"备注"`
|
||||
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
|
||||
}
|
||||
|
||||
func (in *ImInviteCreateInp) Filter(ctx context.Context) (err error) {
|
||||
if in.ExpiresAt == nil || in.ExpiresAt.Before(gtime.Now()) {
|
||||
return gerror.New("到期时间必须晚于当前时间")
|
||||
}
|
||||
if len(in.Remark) > 255 {
|
||||
return gerror.New("备注不能超过255个字符")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImInviteBatchCreateInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
Quantity int `json:"quantity" dc:"数量"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" v:"required#到期时间不能为空" dc:"到期时间"`
|
||||
Remark string `json:"remark" dc:"备注"`
|
||||
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
|
||||
}
|
||||
|
||||
func (in *ImInviteBatchCreateInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Quantity < 1 || in.Quantity > 1000 {
|
||||
return gerror.New("批量生成数量必须在1到1000之间")
|
||||
}
|
||||
if in.ExpiresAt == nil || in.ExpiresAt.Before(gtime.Now()) {
|
||||
return gerror.New("到期时间必须晚于当前时间")
|
||||
}
|
||||
if len(in.Remark) > 255 {
|
||||
return gerror.New("备注不能超过255个字符")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImInviteBatchViewInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
BatchId string `json:"batchId" v:"required#批次ID不能为空" dc:"批次ID"`
|
||||
}
|
||||
|
||||
type ImInviteRevokeInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
Id string `json:"id" v:"required#邀请码ID不能为空" dc:"邀请码ID"`
|
||||
}
|
||||
|
||||
type ImInviteBatchRevokeInp struct {
|
||||
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
|
||||
BatchId string `json:"batchId" v:"required#批次ID不能为空" dc:"批次ID"`
|
||||
}
|
||||
|
||||
type ImInviteCodeModel struct {
|
||||
Id string `json:"id" dc:"ID"`
|
||||
BatchId string `json:"batchId" dc:"批次ID"`
|
||||
Code string `json:"code" dc:"邀请码"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
|
||||
UsedBy string `json:"usedBy" dc:"使用用户"`
|
||||
UsedAt *gtime.Time `json:"usedAt" dc:"使用时间"`
|
||||
RequestId string `json:"requestId" dc:"请求ID"`
|
||||
CreatedBy string `json:"createdBy" dc:"创建人"`
|
||||
Remark string `json:"remark" dc:"备注"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
type ImInviteBatchModel struct {
|
||||
BatchId string `json:"batchId" dc:"批次ID"`
|
||||
Quantity int `json:"quantity" dc:"数量"`
|
||||
ActiveCount int `json:"activeCount" dc:"有效数量"`
|
||||
UsedCount int `json:"usedCount" dc:"已使用数量"`
|
||||
RevokedCount int `json:"revokedCount" dc:"已撤销数量"`
|
||||
ExpiredCount int `json:"expiredCount" dc:"已过期数量"`
|
||||
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
type ImInviteBatchDetailModel struct {
|
||||
ImInviteBatchModel
|
||||
List []*ImInviteCodeModel `json:"list" dc:"批次邀请码"`
|
||||
}
|
||||
|
||||
func ParseOptionalInt64(value string) (int64, error) {
|
||||
if value == "" {
|
||||
return 0, nil
|
||||
}
|
||||
parsed, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil || parsed <= 0 {
|
||||
return 0, gerror.New("ID格式不正确")
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Package sysin
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2026 HotGo
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImRealnameListInp 获取IM实名审核列表
|
||||
type ImRealnameListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
SubmissionId string `json:"submissionId" dc:"提交ID"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
func (in *ImRealnameListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImRealnameListModel struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
SubmissionId string `json:"submissionId" dc:"提交ID"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
MaskedName string `json:"maskedName" dc:"脱敏姓名"`
|
||||
MaskedIdNumber string `json:"maskedIdNumber" dc:"脱敏证件号"`
|
||||
IdType string `json:"idType" dc:"证件类型"`
|
||||
ReviewReason string `json:"reviewReason" dc:"审核原因"`
|
||||
VerifiedAt *gtime.Time `json:"verifiedAt" dc:"通过时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
// ImRealnameViewInp 获取指定IM实名审核信息
|
||||
type ImRealnameViewInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
}
|
||||
|
||||
func (in *ImRealnameViewInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Id <= 0 {
|
||||
return gerror.New("ID不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImRealnameViewModel struct {
|
||||
ImRealnameListModel
|
||||
IdNumberHash string `json:"idNumberHash" dc:"证件号哈希"`
|
||||
}
|
||||
|
||||
// ImRealnameReviewInp 审核IM实名
|
||||
type ImRealnameReviewInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
Status string `json:"status" dc:"审核状态"`
|
||||
ReviewReason string `json:"reviewReason" dc:"审核原因"`
|
||||
}
|
||||
|
||||
func (in *ImRealnameReviewInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Id <= 0 {
|
||||
return gerror.New("ID不能为空")
|
||||
}
|
||||
if !dict.HasOptionKey(consts.ImRealnameStatusOptions, in.Status) {
|
||||
return gerror.New("审核状态不正确")
|
||||
}
|
||||
if in.Status == consts.ImRealnameStatusRejected && in.ReviewReason == "" {
|
||||
return gerror.New("拒绝原因不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImRealnameReviewModel struct{}
|
||||
@@ -0,0 +1,12 @@
|
||||
package sysin
|
||||
|
||||
import "github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
type ImRegistrationConfigModel struct {
|
||||
RegisterSmsEnabled bool `json:"registerSmsEnabled" dc:"注册短信是否真实发送"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
|
||||
type ImRegistrationConfigUpdateInp struct {
|
||||
RegisterSmsEnabled bool `json:"registerSmsEnabled" dc:"注册短信是否真实发送"`
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Package sysin
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2026 HotGo
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImTenantUpdateFields 修改IM租户字段过滤
|
||||
type ImTenantUpdateFields struct {
|
||||
Code string `json:"code" dc:"租户编码"`
|
||||
Name string `json:"name" dc:"租户名称"`
|
||||
TenantDesc string `json:"tenantDesc" dc:"租户描述"`
|
||||
DbName string `json:"dbName" dc:"业务库名"`
|
||||
DbDsn string `json:"dbDsn" dc:"独立连接串"`
|
||||
State int `json:"state" dc:"状态"`
|
||||
UpdatedBy int64 `json:"updatedBy" dc:"更新者"`
|
||||
}
|
||||
|
||||
// ImTenantInsertFields 新增IM租户字段过滤
|
||||
type ImTenantInsertFields struct {
|
||||
Code string `json:"code" dc:"租户编码"`
|
||||
Name string `json:"name" dc:"租户名称"`
|
||||
TenantDesc string `json:"tenantDesc" dc:"租户描述"`
|
||||
DbName string `json:"dbName" dc:"业务库名"`
|
||||
DbDsn string `json:"dbDsn" dc:"独立连接串"`
|
||||
State int `json:"state" dc:"状态"`
|
||||
CreatedBy int64 `json:"createdBy" dc:"创建者"`
|
||||
}
|
||||
|
||||
// ImTenantConfigUpdateFields 修改IM租户配置字段过滤
|
||||
type ImTenantConfigUpdateFields struct {
|
||||
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
|
||||
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
|
||||
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
|
||||
UpdatedBy int64 `json:"updatedBy" dc:"更新者"`
|
||||
}
|
||||
|
||||
// ImTenantConfigInsertFields 新增IM租户配置字段过滤
|
||||
type ImTenantConfigInsertFields struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
|
||||
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
|
||||
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
|
||||
CreatedBy int64 `json:"createdBy" dc:"创建者"`
|
||||
}
|
||||
|
||||
// ImTenantEditInp 修改/新增IM租户
|
||||
type ImTenantEditInp struct {
|
||||
entity.ImTenant
|
||||
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
|
||||
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
|
||||
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
|
||||
}
|
||||
|
||||
func (in *ImTenantEditInp) Filter(ctx context.Context) (err error) {
|
||||
if e := g.Validator().Rules("required|length:2,64").Data(in.Code).Messages("租户编码不能为空|租户编码长度应为2到64个字符").Run(ctx); e != nil {
|
||||
return e.Current()
|
||||
}
|
||||
if e := g.Validator().Rules("required|max-length:128").Data(in.Name).Messages("租户名称不能为空|租户名称不能超过128个字符").Run(ctx); e != nil {
|
||||
return e.Current()
|
||||
}
|
||||
if in.DbName == "" && in.DbDsn == "" {
|
||||
return gerror.New("业务库名和独立连接串至少填写一个")
|
||||
}
|
||||
if !dict.HasOptionKey(consts.ImTenantStateOptions, in.State) {
|
||||
return gerror.New("租户状态不正确")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImTenantEditModel struct{}
|
||||
|
||||
// ImTenantViewInp 获取指定IM租户信息
|
||||
type ImTenantViewInp struct {
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
}
|
||||
|
||||
func (in *ImTenantViewInp) Filter(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type ImTenantViewModel struct {
|
||||
entity.ImTenant
|
||||
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
|
||||
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
|
||||
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
|
||||
CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"`
|
||||
UpdatedBySumma *hook.MemberSumma `json:"updatedBySumma" dc:"更新者摘要信息"`
|
||||
DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"`
|
||||
TenantDbReachable bool `json:"tenantDbReachable" dc:"业务库是否可达"`
|
||||
TenantDbCheckError string `json:"tenantDbCheckError" dc:"业务库检查错误"`
|
||||
}
|
||||
|
||||
// ImTenantListInp 获取IM租户列表
|
||||
type ImTenantListInp struct {
|
||||
form.PageReq
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
Code string `json:"code" dc:"租户编码"`
|
||||
Name string `json:"name" dc:"租户名称"`
|
||||
DbName string `json:"dbName" dc:"业务库名"`
|
||||
State *int `json:"state" dc:"状态"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
func (in *ImTenantListInp) Filter(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type ImTenantListModel struct {
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
Code string `json:"code" dc:"租户编码"`
|
||||
Name string `json:"name" dc:"租户名称"`
|
||||
TenantDesc string `json:"tenantDesc" dc:"租户描述"`
|
||||
DbName string `json:"dbName" dc:"业务库名"`
|
||||
State int `json:"state" dc:"状态"`
|
||||
MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"`
|
||||
MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"`
|
||||
MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"`
|
||||
DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"`
|
||||
CreatedBy int64 `json:"createdBy" dc:"创建者"`
|
||||
CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"`
|
||||
UpdatedBy int64 `json:"updatedBy" dc:"更新者"`
|
||||
UpdatedBySumma *hook.MemberSumma `json:"updatedBySumma" dc:"更新者摘要信息"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
// ImTenantStatusInp 更新IM租户状态
|
||||
type ImTenantStatusInp struct {
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
State int `json:"state" dc:"状态"`
|
||||
}
|
||||
|
||||
func (in *ImTenantStatusInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Id <= 0 {
|
||||
return gerror.New("ID不能为空")
|
||||
}
|
||||
if !dict.HasOptionKey(consts.ImTenantStateOptions, in.State) {
|
||||
return gerror.New("租户状态不正确")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImTenantStatusModel struct{}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Package sysin
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2026 HotGo
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ImUserListInp 获取IM用户列表
|
||||
type ImUserListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
Nickname string `json:"nickname" dc:"昵称"`
|
||||
AuthName string `json:"authName" dc:"登录标识"`
|
||||
State *int `json:"state" dc:"账号状态"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
LastSeen []*gtime.Time `json:"lastSeen" dc:"最后在线时间"`
|
||||
}
|
||||
|
||||
func (in *ImUserListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImUserListModel struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
Nickname string `json:"nickname" dc:"昵称"`
|
||||
PublicAccount string `json:"publicAccount" dc:"公开账号"`
|
||||
Gender string `json:"gender" dc:"性别"`
|
||||
Region string `json:"region" dc:"地区"`
|
||||
State int `json:"state" dc:"账号状态"`
|
||||
AuthVersion int `json:"authVersion" dc:"认证版本"`
|
||||
PasswordUpdatedAt *gtime.Time `json:"passwordUpdatedAt" dc:"密码更新时间"`
|
||||
AuthNames string `json:"authNames" dc:"登录标识"`
|
||||
Credentials string `json:"credentials" dc:"凭证"`
|
||||
LastSeen *gtime.Time `json:"lastSeen" dc:"最后在线时间"`
|
||||
UserAgent string `json:"userAgent" dc:"客户端"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
// ImUserViewInp 获取指定IM用户信息
|
||||
type ImUserViewInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
|
||||
}
|
||||
|
||||
func (in *ImUserViewInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.UserId <= 0 {
|
||||
return gerror.New("用户ID不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImUserViewModel struct {
|
||||
ImUserListModel
|
||||
Signature string `json:"signature" dc:"个性签名"`
|
||||
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
|
||||
BirthDate string `json:"birthDate" dc:"出生日期"`
|
||||
ProfileJson string `json:"profileJson" dc:"Tinode Public资料"`
|
||||
TrustedJson string `json:"trustedJson" dc:"Tinode Trusted资料"`
|
||||
TagsJson string `json:"tagsJson" dc:"Tinode标签"`
|
||||
AccessJson string `json:"accessJson" dc:"访问权限"`
|
||||
ProfileVersion int `json:"profileVersion" dc:"资料版本"`
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Package sysin
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2026 HotGo
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
const (
|
||||
ImWorkOrderTypeAbuse = "abuse"
|
||||
ImWorkOrderTypeFeedback = "feedback"
|
||||
)
|
||||
|
||||
// ImWorkOrderListInp 获取IM举报反馈工单列表
|
||||
type ImWorkOrderListInp struct {
|
||||
form.PageReq
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
Type string `json:"type" dc:"工单类型"`
|
||||
UserId int64 `json:"userId" dc:"提交人ID"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
Keyword string `json:"keyword" dc:"关键词"`
|
||||
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
func (in *ImWorkOrderListInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Type == "" {
|
||||
return gerror.New("请选择工单类型")
|
||||
}
|
||||
if in.Type != ImWorkOrderTypeAbuse && in.Type != ImWorkOrderTypeFeedback {
|
||||
return gerror.New("工单类型不正确")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImWorkOrderListModel struct {
|
||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||
Type string `json:"type" dc:"工单类型"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
OrderId string `json:"orderId" dc:"工单ID"`
|
||||
UserId int64 `json:"userId" dc:"提交人ID"`
|
||||
TargetType string `json:"targetType" dc:"目标类型"`
|
||||
TargetSummary string `json:"targetSummary" dc:"目标摘要"`
|
||||
Category string `json:"category" dc:"分类"`
|
||||
Content string `json:"content" dc:"内容"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
// ImWorkOrderViewInp 获取指定IM举报反馈工单信息
|
||||
type ImWorkOrderViewInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Type string `json:"type" v:"required#工单类型不能为空" dc:"工单类型"`
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
}
|
||||
|
||||
func (in *ImWorkOrderViewInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Type != ImWorkOrderTypeAbuse && in.Type != ImWorkOrderTypeFeedback {
|
||||
return gerror.New("工单类型不正确")
|
||||
}
|
||||
if in.Id <= 0 {
|
||||
return gerror.New("ID不能为空")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImWorkOrderViewModel struct {
|
||||
ImWorkOrderListModel
|
||||
Description string `json:"description" dc:"描述"`
|
||||
Contact string `json:"contact" dc:"联系方式"`
|
||||
EvidenceJson string `json:"evidenceJson" dc:"证据/附件"`
|
||||
SnapshotJson string `json:"snapshotJson" dc:"快照"`
|
||||
ClientContextJson string `json:"clientContextJson" dc:"客户端上下文"`
|
||||
IncludeDiagnostics int `json:"includeDiagnostics" dc:"是否包含诊断"`
|
||||
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
|
||||
}
|
||||
|
||||
// ImWorkOrderStatusInp 更新IM举报反馈工单状态
|
||||
type ImWorkOrderStatusInp struct {
|
||||
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
|
||||
Type string `json:"type" v:"required#工单类型不能为空" dc:"工单类型"`
|
||||
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
}
|
||||
|
||||
func (in *ImWorkOrderStatusInp) Filter(ctx context.Context) (err error) {
|
||||
if in.TenantId <= 0 {
|
||||
return gerror.New("请选择租户")
|
||||
}
|
||||
if in.Type != ImWorkOrderTypeAbuse && in.Type != ImWorkOrderTypeFeedback {
|
||||
return gerror.New("工单类型不正确")
|
||||
}
|
||||
if in.Id <= 0 {
|
||||
return gerror.New("ID不能为空")
|
||||
}
|
||||
if !dict.HasOptionKey(consts.ImWorkOrderStatusOptions, in.Status) {
|
||||
return gerror.New("工单状态不正确")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ImWorkOrderStatusModel struct{}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Package genrouter
|
||||
// @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 genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImAuditLog) // IM审计日志
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImGroup) // IM群组
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImInvite) // IM注册邀请码
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImRealname) // IM实名审核
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImRegistration) // IM注册配置
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImTenant) // IM租户
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImUser) // IM用户
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package genrouter
|
||||
|
||||
import "hotgo/internal/controller/admin/sys"
|
||||
|
||||
func init() {
|
||||
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImWorkOrder) // IM举报反馈
|
||||
}
|
||||
@@ -171,6 +171,88 @@ type (
|
||||
// Switch 更新CURD列表开关
|
||||
Switch(ctx context.Context, in *sysin.CurdDemoSwitchInp) (err error)
|
||||
}
|
||||
ISysImTenant interface {
|
||||
// Model IM租户ORM模型
|
||||
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
|
||||
// List 获取IM租户列表
|
||||
List(ctx context.Context, in *sysin.ImTenantListInp) (list []*sysin.ImTenantListModel, totalCount int, err error)
|
||||
// Edit 修改/新增IM租户
|
||||
Edit(ctx context.Context, in *sysin.ImTenantEditInp) (err error)
|
||||
// View 获取IM租户指定信息
|
||||
View(ctx context.Context, in *sysin.ImTenantViewInp) (res *sysin.ImTenantViewModel, err error)
|
||||
// Status 更新IM租户状态
|
||||
Status(ctx context.Context, in *sysin.ImTenantStatusInp) (err error)
|
||||
}
|
||||
ISysImRegistration interface {
|
||||
// GetConfig 获取IM注册配置
|
||||
GetConfig(ctx context.Context) (res *sysin.ImRegistrationConfigModel, err error)
|
||||
// UpdateConfig 更新IM注册配置
|
||||
UpdateConfig(ctx context.Context, in *sysin.ImRegistrationConfigUpdateInp) (err error)
|
||||
}
|
||||
ISysImInvite interface {
|
||||
// List 查询IM注册邀请码
|
||||
List(ctx context.Context, in *sysin.ImInviteListInp) (list []*sysin.ImInviteCodeModel, totalCount int, err error)
|
||||
// Create 单个生成IM注册邀请码
|
||||
Create(ctx context.Context, in *sysin.ImInviteCreateInp) (res *sysin.ImInviteCodeModel, err error)
|
||||
// BatchCreate 批量生成IM注册邀请码
|
||||
BatchCreate(ctx context.Context, in *sysin.ImInviteBatchCreateInp) (res *sysin.ImInviteBatchModel, err error)
|
||||
// BatchView 查看批次详情
|
||||
BatchView(ctx context.Context, in *sysin.ImInviteBatchViewInp) (res *sysin.ImInviteBatchDetailModel, err error)
|
||||
// Revoke 撤销邀请码
|
||||
Revoke(ctx context.Context, in *sysin.ImInviteRevokeInp) (err error)
|
||||
// BatchRevoke 批量撤销邀请码
|
||||
BatchRevoke(ctx context.Context, in *sysin.ImInviteBatchRevokeInp) (res *sysin.ImInviteBatchModel, err error)
|
||||
// Export 导出邀请码
|
||||
Export(ctx context.Context, in *sysin.ImInviteListInp) (err error)
|
||||
}
|
||||
ISysImUser interface {
|
||||
// List 获取IM用户列表
|
||||
List(ctx context.Context, in *sysin.ImUserListInp) (list []*sysin.ImUserListModel, totalCount int, err error)
|
||||
// View 获取IM用户指定信息
|
||||
View(ctx context.Context, in *sysin.ImUserViewInp) (res *sysin.ImUserViewModel, err error)
|
||||
}
|
||||
ISysImGroup interface {
|
||||
// List 获取IM群组列表
|
||||
List(ctx context.Context, in *sysin.ImGroupListInp) (list []*sysin.ImGroupListModel, totalCount int, err error)
|
||||
// View 获取IM群组指定信息
|
||||
View(ctx context.Context, in *sysin.ImGroupViewInp) (res *sysin.ImGroupViewModel, err error)
|
||||
// MemberList 获取IM群成员列表
|
||||
MemberList(ctx context.Context, in *sysin.ImGroupMemberListInp) (list []*sysin.ImGroupMemberListModel, totalCount int, err error)
|
||||
// Governance 获取IM群治理配置
|
||||
Governance(ctx context.Context, in *sysin.ImGroupTopicInp) (res *sysin.ImGroupGovernanceModel, err error)
|
||||
// JoinRequestList 获取IM入群申请列表
|
||||
JoinRequestList(ctx context.Context, in *sysin.ImGroupJoinRequestListInp) (list []*sysin.ImGroupJoinRequestListModel, totalCount int, err error)
|
||||
// InvitationList 获取IM群邀请列表
|
||||
InvitationList(ctx context.Context, in *sysin.ImGroupInvitationListInp) (list []*sysin.ImGroupInvitationListModel, totalCount int, err error)
|
||||
// AnnouncementList 获取IM群公告列表
|
||||
AnnouncementList(ctx context.Context, in *sysin.ImGroupAnnouncementListInp) (list []*sysin.ImGroupAnnouncementListModel, totalCount int, err error)
|
||||
// MuteList 获取IM群禁言列表
|
||||
MuteList(ctx context.Context, in *sysin.ImGroupMuteListInp) (list []*sysin.ImGroupMuteListModel, totalCount int, err error)
|
||||
// AdminLogList 获取IM群管理日志列表
|
||||
AdminLogList(ctx context.Context, in *sysin.ImGroupAdminLogListInp) (list []*sysin.ImGroupAdminLogListModel, totalCount int, err error)
|
||||
}
|
||||
ISysImRealname interface {
|
||||
// List 获取IM实名审核列表
|
||||
List(ctx context.Context, in *sysin.ImRealnameListInp) (list []*sysin.ImRealnameListModel, totalCount int, err error)
|
||||
// View 获取IM实名审核指定信息
|
||||
View(ctx context.Context, in *sysin.ImRealnameViewInp) (res *sysin.ImRealnameViewModel, err error)
|
||||
// Review 审核IM实名
|
||||
Review(ctx context.Context, in *sysin.ImRealnameReviewInp) (err error)
|
||||
}
|
||||
ISysImWorkOrder interface {
|
||||
// List 获取IM举报反馈工单列表
|
||||
List(ctx context.Context, in *sysin.ImWorkOrderListInp) (list []*sysin.ImWorkOrderListModel, totalCount int, err error)
|
||||
// View 获取IM举报反馈工单指定信息
|
||||
View(ctx context.Context, in *sysin.ImWorkOrderViewInp) (res *sysin.ImWorkOrderViewModel, err error)
|
||||
// Status 更新IM举报反馈工单状态
|
||||
Status(ctx context.Context, in *sysin.ImWorkOrderStatusInp) (err error)
|
||||
}
|
||||
ISysImAuditLog interface {
|
||||
// List 获取IM审计日志列表
|
||||
List(ctx context.Context, in *sysin.ImAuditLogListInp) (list []*sysin.ImAuditLogListModel, totalCount int, err error)
|
||||
// View 获取IM审计日志指定信息
|
||||
View(ctx context.Context, in *sysin.ImAuditLogViewInp) (res *sysin.ImAuditLogViewModel, err error)
|
||||
}
|
||||
ISysDictData interface {
|
||||
// Delete 删除
|
||||
Delete(ctx context.Context, in *sysin.DictDataDeleteInp) error
|
||||
@@ -414,6 +496,14 @@ var (
|
||||
localSysCron ISysCron
|
||||
localSysCronGroup ISysCronGroup
|
||||
localSysCurdDemo ISysCurdDemo
|
||||
localSysImTenant ISysImTenant
|
||||
localSysImRegistration ISysImRegistration
|
||||
localSysImInvite ISysImInvite
|
||||
localSysImUser ISysImUser
|
||||
localSysImGroup ISysImGroup
|
||||
localSysImRealname ISysImRealname
|
||||
localSysImWorkOrder ISysImWorkOrder
|
||||
localSysImAuditLog ISysImAuditLog
|
||||
localSysDictData ISysDictData
|
||||
localSysDictType ISysDictType
|
||||
localSysEmsLog ISysEmsLog
|
||||
@@ -517,6 +607,94 @@ func RegisterSysCurdDemo(i ISysCurdDemo) {
|
||||
localSysCurdDemo = i
|
||||
}
|
||||
|
||||
func SysImTenant() ISysImTenant {
|
||||
if localSysImTenant == nil {
|
||||
panic("implement not found for interface ISysImTenant, forgot register?")
|
||||
}
|
||||
return localSysImTenant
|
||||
}
|
||||
|
||||
func RegisterSysImTenant(i ISysImTenant) {
|
||||
localSysImTenant = i
|
||||
}
|
||||
|
||||
func SysImRegistration() ISysImRegistration {
|
||||
if localSysImRegistration == nil {
|
||||
panic("implement not found for interface ISysImRegistration, forgot register?")
|
||||
}
|
||||
return localSysImRegistration
|
||||
}
|
||||
|
||||
func RegisterSysImRegistration(i ISysImRegistration) {
|
||||
localSysImRegistration = i
|
||||
}
|
||||
|
||||
func SysImInvite() ISysImInvite {
|
||||
if localSysImInvite == nil {
|
||||
panic("implement not found for interface ISysImInvite, forgot register?")
|
||||
}
|
||||
return localSysImInvite
|
||||
}
|
||||
|
||||
func RegisterSysImInvite(i ISysImInvite) {
|
||||
localSysImInvite = i
|
||||
}
|
||||
|
||||
func SysImUser() ISysImUser {
|
||||
if localSysImUser == nil {
|
||||
panic("implement not found for interface ISysImUser, forgot register?")
|
||||
}
|
||||
return localSysImUser
|
||||
}
|
||||
|
||||
func RegisterSysImUser(i ISysImUser) {
|
||||
localSysImUser = i
|
||||
}
|
||||
|
||||
func SysImGroup() ISysImGroup {
|
||||
if localSysImGroup == nil {
|
||||
panic("implement not found for interface ISysImGroup, forgot register?")
|
||||
}
|
||||
return localSysImGroup
|
||||
}
|
||||
|
||||
func RegisterSysImGroup(i ISysImGroup) {
|
||||
localSysImGroup = i
|
||||
}
|
||||
|
||||
func SysImRealname() ISysImRealname {
|
||||
if localSysImRealname == nil {
|
||||
panic("implement not found for interface ISysImRealname, forgot register?")
|
||||
}
|
||||
return localSysImRealname
|
||||
}
|
||||
|
||||
func RegisterSysImRealname(i ISysImRealname) {
|
||||
localSysImRealname = i
|
||||
}
|
||||
|
||||
func SysImWorkOrder() ISysImWorkOrder {
|
||||
if localSysImWorkOrder == nil {
|
||||
panic("implement not found for interface ISysImWorkOrder, forgot register?")
|
||||
}
|
||||
return localSysImWorkOrder
|
||||
}
|
||||
|
||||
func RegisterSysImWorkOrder(i ISysImWorkOrder) {
|
||||
localSysImWorkOrder = i
|
||||
}
|
||||
|
||||
func SysImAuditLog() ISysImAuditLog {
|
||||
if localSysImAuditLog == nil {
|
||||
panic("implement not found for interface ISysImAuditLog, forgot register?")
|
||||
}
|
||||
return localSysImAuditLog
|
||||
}
|
||||
|
||||
func RegisterSysImAuditLog(i ISysImAuditLog) {
|
||||
localSysImAuditLog = i
|
||||
}
|
||||
|
||||
func SysDictData() ISysDictData {
|
||||
if localSysDictData == nil {
|
||||
panic("implement not found for interface ISysDictData, forgot register?")
|
||||
|
||||
@@ -7,6 +7,7 @@ package main
|
||||
|
||||
import (
|
||||
_ "hotgo/internal/packed"
|
||||
_ "time/tzdata"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
|
||||
@@ -237,6 +237,9 @@ database:
|
||||
link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
|
||||
debug: true
|
||||
Prefix: "hg_"
|
||||
imRegistry:
|
||||
link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
|
||||
debug: true
|
||||
|
||||
|
||||
# 链路追踪
|
||||
@@ -300,4 +303,10 @@ hggen:
|
||||
addon:
|
||||
srcPath: "./resource/generate/default/addon" # 生成模板路径
|
||||
webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
|
||||
webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
|
||||
webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
|
||||
|
||||
imAdminApi:
|
||||
baseUrl: "http://127.0.0.1:6060"
|
||||
serviceId: "hotgo-admin"
|
||||
serviceSecret: "${IM_ADMIN_API_SECRET}"
|
||||
timeout: "5s"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
FROM alpine:3.20
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
COPY temp/linux_arm64/hotgo /app/hotgo
|
||||
COPY hack /app/hack
|
||||
COPY manifest/config /app/manifest/config
|
||||
COPY resource /app/resource
|
||||
COPY storage /app/storage
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["/app/hotgo"]
|
||||
@@ -0,0 +1,31 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @auditDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imAuditLog' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '审计日志', 'imAuditLog', 'audit-log', '', '1', '/im/audit-log/index', '', '', 'ParentLayout', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '60', '', '1', @now, @now
|
||||
WHERE @auditDirId IS NULL;
|
||||
|
||||
SELECT @auditDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imAuditLog' LIMIT 1;
|
||||
|
||||
SELECT @auditListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imAuditLogIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @auditDirId, '审计列表', 'imAuditLogIndex', 'index', '', '2', '', '/imAuditLog/list', '', '/imAuditLog/index', '1', 'imAuditLog', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @auditDirId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @auditListId IS NULL;
|
||||
|
||||
SELECT @auditListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imAuditLogIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @auditListId, '审计详情', 'imAuditLogView', '', '', '3', '', '/imAuditLog/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @auditDirId, ' tr_', @auditListId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imAuditLogView');
|
||||
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE IF NOT EXISTS im_audit_logs (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
actor_uid BIGINT NOT NULL DEFAULT 0,
|
||||
actor_role VARCHAR(64) NOT NULL DEFAULT '',
|
||||
action VARCHAR(128) NOT NULL,
|
||||
target_type VARCHAR(64) NOT NULL DEFAULT '',
|
||||
target_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
before_snapshot JSON,
|
||||
after_snapshot JSON,
|
||||
reason VARCHAR(512) NOT NULL DEFAULT '',
|
||||
request_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||||
user_agent VARCHAR(512) NOT NULL DEFAULT '',
|
||||
metadata JSON,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
INDEX idx_im_audit_logs_actor (actor_uid, id DESC),
|
||||
INDEX idx_im_audit_logs_action (action, id DESC),
|
||||
INDEX idx_im_audit_logs_target (target_type, target_id, id DESC),
|
||||
INDEX idx_im_audit_logs_request (request_id),
|
||||
INDEX idx_im_audit_logs_created (created_at, id DESC)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='IM租户审计日志';
|
||||
@@ -0,0 +1,59 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @groupDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imGroup' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '群组管理', 'imGroup', 'group', '', '1', '/im/group/index', '', '', 'ParentLayout', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '30', '', '1', @now, @now
|
||||
WHERE @groupDirId IS NULL;
|
||||
|
||||
SELECT @groupDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imGroup' LIMIT 1;
|
||||
|
||||
SELECT @groupListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imGroupIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupDirId, '群组列表', 'imGroupIndex', 'index', '', '2', '', '/imGroup/list', '', '/imGroup/index', '1', 'imGroup', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @groupListId IS NULL;
|
||||
|
||||
SELECT @groupListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imGroupIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群组详情', 'imGroupView', '', '', '3', '', '/imGroup/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupView');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群成员列表', 'imGroupMemberList', '', '', '3', '', '/imGroup/memberList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupMemberList');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群治理配置', 'imGroupGovernance', '', '', '3', '', '/imGroup/governance', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '30', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupGovernance');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '入群申请列表', 'imGroupJoinRequestList', '', '', '3', '', '/imGroup/joinRequestList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '40', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupJoinRequestList');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群邀请列表', 'imGroupInvitationList', '', '', '3', '', '/imGroup/invitationList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '50', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupInvitationList');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群公告列表', 'imGroupAnnouncementList', '', '', '3', '', '/imGroup/announcementList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '60', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupAnnouncementList');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群禁言列表', 'imGroupMuteList', '', '', '3', '', '/imGroup/muteList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '70', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupMuteList');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @groupListId, '群管理日志', 'imGroupAdminLogList', '', '', '3', '', '/imGroup/adminLogList', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @groupDirId, ' tr_', @groupListId, ' '), '80', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imGroupAdminLogList');
|
||||
@@ -0,0 +1,35 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @realnameDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imRealname' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '实名审核', 'imRealname', 'realname', '', '1', '/im/realname/index', '', '', 'ParentLayout', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '40', '', '1', @now, @now
|
||||
WHERE @realnameDirId IS NULL;
|
||||
|
||||
SELECT @realnameDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imRealname' LIMIT 1;
|
||||
|
||||
SELECT @realnameListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imRealnameIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @realnameDirId, '审核列表', 'imRealnameIndex', 'index', '', '2', '', '/imRealname/list', '', '/imRealname/index', '1', 'imRealname', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @realnameDirId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @realnameListId IS NULL;
|
||||
|
||||
SELECT @realnameListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imRealnameIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @realnameListId, '实名详情', 'imRealnameView', '', '', '3', '', '/imRealname/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @realnameDirId, ' tr_', @realnameListId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imRealnameView');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @realnameListId, '实名审核操作', 'imRealnameReview', '', '', '3', '', '/imRealname/review', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @realnameDirId, ' tr_', @realnameListId, ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imRealnameReview');
|
||||
@@ -0,0 +1,43 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @invitePageId := `id` FROM `hg_admin_menu` WHERE `name` = 'imRegistrationInviteIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '注册邀请码', 'imRegistrationInviteIndex', 'registrationInvite', '', '2', '', '/imInvite/list', '', '/im/registrationInvite/index', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '70', '', '1', @now, @now
|
||||
WHERE @invitePageId IS NULL;
|
||||
|
||||
SELECT @invitePageId := `id` FROM `hg_admin_menu` WHERE `name` = 'imRegistrationInviteIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @invitePageId, '生成邀请码', 'imInviteCreate', '', '', '3', '', '/imInvite/create', '', '', '1', '', '0', '0', '', '0', '1', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @invitePageId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imInviteCreate');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @invitePageId, '批量生成邀请码', 'imInviteBatchCreate', '', '', '3', '', '/imInvite/batchCreate', '', '', '1', '', '0', '0', '', '0', '1', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @invitePageId, ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imInviteBatchCreate');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @invitePageId, '批次详情', 'imInviteBatchView', '', '', '3', '', '/imInvite/batchView', '', '', '1', '', '0', '0', '', '0', '1', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @invitePageId, ' '), '30', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imInviteBatchView');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @invitePageId, '撤销邀请码', 'imInviteRevoke', '', '', '3', '', '/imInvite/revoke', '', '', '1', '', '0', '0', '', '0', '1', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @invitePageId, ' '), '40', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imInviteRevoke');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @invitePageId, '批量撤销邀请码', 'imInviteBatchRevoke', '', '', '3', '', '/imInvite/batchRevoke', '', '', '1', '', '0', '0', '', '0', '1', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @invitePageId, ' '), '50', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imInviteBatchRevoke');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @invitePageId, '导出邀请码', 'imInviteExport', '', '', '3', '', '/imInvite/export', '', '', '1', '', '0', '0', '', '0', '1', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @invitePageId, ' '), '60', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imInviteExport');
|
||||
@@ -0,0 +1,39 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @tenantDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imTenant' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '租户管理', 'imTenant', 'tenant', '', '1', '/im/tenant/index', '', '', 'ParentLayout', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @tenantDirId IS NULL;
|
||||
|
||||
SELECT @tenantDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imTenant' LIMIT 1;
|
||||
|
||||
SELECT @tenantListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imTenantIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @tenantDirId, '租户列表', 'imTenantIndex', 'index', '', '2', '', '/imTenant/list', '', '/imTenant/index', '1', 'imTenant', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @tenantDirId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @tenantListId IS NULL;
|
||||
|
||||
SELECT @tenantListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imTenantIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @tenantListId, '租户详情', 'imTenantView', '', '', '3', '', '/imTenant/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @tenantDirId, ' tr_', @tenantListId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imTenantView');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @tenantListId, '编辑/新增租户', 'imTenantEdit', '', '', '3', '', '/imTenant/edit', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @tenantDirId, ' tr_', @tenantListId, ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imTenantEdit');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @tenantListId, '修改租户状态', 'imTenantStatus', '', '', '3', '', '/imTenant/status', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @tenantDirId, ' tr_', @tenantListId, ' '), '30', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imTenantStatus');
|
||||
@@ -0,0 +1,31 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @userDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imUser' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '用户管理', 'imUser', 'user', '', '1', '/im/user/index', '', '', 'ParentLayout', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '20', '', '1', @now, @now
|
||||
WHERE @userDirId IS NULL;
|
||||
|
||||
SELECT @userDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imUser' LIMIT 1;
|
||||
|
||||
SELECT @userListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imUserIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @userDirId, '用户列表', 'imUserIndex', 'index', '', '2', '', '/imUser/list', '', '/imUser/index', '1', 'imUser', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @userDirId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @userListId IS NULL;
|
||||
|
||||
SELECT @userListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imUserIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @userListId, '用户详情', 'imUserView', '', '', '3', '', '/imUser/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @userDirId, ' tr_', @userListId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imUserView');
|
||||
@@ -0,0 +1,35 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, '0', 'IM管理', 'imManager', '/im', '', '1', '/im/tenant/index', '', '', 'LAYOUT', '1', '', '1', '0', '', '0', '0', '0', '1', '', '200', '', '1', @now, @now
|
||||
WHERE @imRootId IS NULL;
|
||||
|
||||
SELECT @imRootId := `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1;
|
||||
|
||||
SELECT @workOrderDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imWorkOrder' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @imRootId, '举报反馈', 'imWorkOrder', 'work-order', '', '1', '/im/work-order/index', '', '', 'ParentLayout', '1', '', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '50', '', '1', @now, @now
|
||||
WHERE @workOrderDirId IS NULL;
|
||||
|
||||
SELECT @workOrderDirId := `id` FROM `hg_admin_menu` WHERE `name` = 'imWorkOrder' LIMIT 1;
|
||||
|
||||
SELECT @workOrderListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imWorkOrderIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @workOrderDirId, '工单列表', 'imWorkOrderIndex', 'index', '', '2', '', '/imWorkOrder/list', '', '/imWorkOrder/index', '1', 'imWorkOrder', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @workOrderDirId, ' '), '10', '', '1', @now, @now
|
||||
WHERE @workOrderListId IS NULL;
|
||||
|
||||
SELECT @workOrderListId := `id` FROM `hg_admin_menu` WHERE `name` = 'imWorkOrderIndex' LIMIT 1;
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @workOrderListId, '工单详情', 'imWorkOrderView', '', '', '3', '', '/imWorkOrder/view', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @workOrderDirId, ' tr_', @workOrderListId, ' '), '10', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imWorkOrderView');
|
||||
|
||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
|
||||
SELECT NULL, @workOrderListId, '更新工单状态', 'imWorkOrderStatus', '', '', '3', '', '/imWorkOrder/status', '', '', '1', '', '0', '0', '', '0', '1', '0', '4', CONCAT('tr_', @imRootId, ' tr_', @workOrderDirId, ' tr_', @workOrderListId, ' '), '20', '', '1', @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imWorkOrderStatus');
|
||||
@@ -223,6 +223,7 @@ COMMENT ON COLUMN hg_admin_dept.updated_at IS '更新时间';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hg_admin_member (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT,
|
||||
dept_id BIGINT DEFAULT 0,
|
||||
role_id BIGINT DEFAULT 10,
|
||||
real_name VARCHAR(32) DEFAULT '',
|
||||
|
||||
Reference in New Issue
Block a user