chore: seed migration objects batch 5

This commit is contained in:
dev
2026-10-18 13:37:24 +08:00
parent 53c9c0afd8
commit c8c40b1dd2
239 changed files with 16941 additions and 1544 deletions
@@ -0,0 +1,39 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// ImMessageCurfewRules is the golang structure of table im_message_curfew_rules for DAO operations like Where/Data.
type ImMessageCurfewRules struct {
g.Meta `orm:"table:im_message_curfew_rules, do:true"`
Id any //
TenantId any //
PolicyId any //
RuleCode any //
RuleName any //
DateType any // daily, weekday, weekend, custom
CustomDates *gjson.Json // YYYY-MM-DD values for custom date type
Weekdays *gjson.Json // ISO weekdays 1 through 7
Timezone any //
StartTime *gtime.Time //
EndTime *gtime.Time // Less than start_time means crossing midnight
Scope any // private_message or group_message
WhitelistUserIds *gjson.Json //
WhitelistGroupIds *gjson.Json //
Message any //
Status any // 1 enabled, 2 disabled
Sort any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
@@ -0,0 +1,32 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// ImSecurityIpRules is the golang structure of table im_security_ip_rules for DAO operations like Where/Data.
type ImSecurityIpRules struct {
g.Meta `orm:"table:im_security_ip_rules, do:true"`
Id any //
TenantId any //
RuleType any // whitelist or blacklist
IpCidr any // IPv4, IPv6 or CIDR
Action any // allow, challenge, block, audit_only
SceneJson *gjson.Json // login, register, message, upload and other scenes
RiskLevel any //
Status any // 1 enabled, 2 disabled
ExpiresAt *gtime.Time //
Remark any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
@@ -0,0 +1,39 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// ImSecurityPolicy is the golang structure of table im_security_policy for DAO operations like Where/Data.
type ImSecurityPolicy struct {
g.Meta `orm:"table:im_security_policy, do:true"`
Id any //
TenantId any // 0 means global fallback policy
PolicyVersion any //
PolicyName any //
PublishStatus any //
PasswordPolicy *gjson.Json //
LoginRateLimit *gjson.Json //
SensitiveWordPolicy *gjson.Json //
FileReviewPolicy *gjson.Json //
RealnamePolicy *gjson.Json //
RiskChallengePolicy *gjson.Json //
GlobalFriendEnabled any // Backend master switch for every add-friend entry
FriendDisabledPolicy *gjson.Json // Profile masking, QR and group-member restrictions
MessageCurfewEnabled any // Backend master switch for message curfew
Remark any //
PublishedAt *gtime.Time //
PublishedBy any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
@@ -0,0 +1,28 @@
// =================================================================================
// 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"
)
// ImSensitiveWordGroups is the golang structure of table im_sensitive_word_groups for DAO operations like Where/Data.
type ImSensitiveWordGroups struct {
g.Meta `orm:"table:im_sensitive_word_groups, do:true"`
Id any //
TenantId any // 0 means global dictionary
GroupCode any //
GroupName any //
Priority any //
Status any // 1 enabled, 2 disabled
Remark any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
@@ -0,0 +1,35 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// ImSensitiveWords is the golang structure of table im_sensitive_words for DAO operations like Where/Data.
type ImSensitiveWords struct {
g.Meta `orm:"table:im_sensitive_words, do:true"`
Id any //
TenantId any // 0 means global dictionary
GroupId any //
Word any //
WordHash any // SHA-256 of normalized word
MatchType any //
SceneJson *gjson.Json // Enabled scenes
Action any //
Replacement any //
RiskLevel any //
Priority any //
Status any // 1 enabled, 2 disabled
Remark any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
+14 -10
View File
@@ -11,14 +11,18 @@ import (
// 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 //
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
DefaultPolicyGroupId any // Default user policy group
SecurityPolicyVersion any // Published security policy version
GlobalFriendEnabled any // Published global friend switch
MessageCurfewEnabled any // Published message curfew switch
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
}
@@ -0,0 +1,29 @@
// =================================================================================
// 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"
)
// ImUserPolicyGroupMembers is the golang structure of table im_user_policy_group_members for DAO operations like Where/Data.
type ImUserPolicyGroupMembers struct {
g.Meta `orm:"table:im_user_policy_group_members, do:true"`
Id any //
TenantId any //
GroupId any //
UserId any // User ID in the tenant database
Status any // 1 enabled, 2 disabled
ValidFrom *gtime.Time //
ValidUntil *gtime.Time //
Remark any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
@@ -0,0 +1,33 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// ImUserPolicyGroups is the golang structure of table im_user_policy_groups for DAO operations like Where/Data.
type ImUserPolicyGroups struct {
g.Meta `orm:"table:im_user_policy_groups, do:true"`
Id any //
TenantId any // 0 means global default
GroupCode any // Stable policy group code
GroupName any // Policy group name
Priority any // Higher value has higher priority
Status any // 1 enabled, 2 disabled
PolicyJson *gjson.Json // Login, friend, message, group and file policies
IpWhitelist *gjson.Json // IPv4/IPv6 or CIDR list
IpBlacklist *gjson.Json // IPv4/IPv6 or CIDR list
Version any // Optimistic-lock/config version
Remark any //
CreatedAt *gtime.Time //
CreatedBy any //
UpdatedAt *gtime.Time //
UpdatedBy any //
DeletedAt *gtime.Time //
DeletedBy any //
}
@@ -0,0 +1,37 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/os/gtime"
)
// ImMessageCurfewRules is the golang structure for table im_message_curfew_rules.
type ImMessageCurfewRules struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:""`
PolicyId int64 `json:"policyId" orm:"policy_id" description:""`
RuleCode string `json:"ruleCode" orm:"rule_code" description:""`
RuleName string `json:"ruleName" orm:"rule_name" description:""`
DateType string `json:"dateType" orm:"date_type" description:"daily, weekday, weekend, custom"`
CustomDates *gjson.Json `json:"customDates" orm:"custom_dates" description:"YYYY-MM-DD values for custom date type"`
Weekdays *gjson.Json `json:"weekdays" orm:"weekdays" description:"ISO weekdays 1 through 7"`
Timezone string `json:"timezone" orm:"timezone" description:""`
StartTime *gtime.Time `json:"startTime" orm:"start_time" description:""`
EndTime *gtime.Time `json:"endTime" orm:"end_time" description:"Less than start_time means crossing midnight"`
Scope string `json:"scope" orm:"scope" description:"private_message or group_message"`
WhitelistUserIds *gjson.Json `json:"whitelistUserIds" orm:"whitelist_user_ids" description:""`
WhitelistGroupIds *gjson.Json `json:"whitelistGroupIds" orm:"whitelist_group_ids" description:""`
Message string `json:"message" orm:"message" description:""`
Status int `json:"status" orm:"status" description:"1 enabled, 2 disabled"`
Sort int `json:"sort" orm:"sort" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -0,0 +1,30 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/os/gtime"
)
// ImSecurityIpRules is the golang structure for table im_security_ip_rules.
type ImSecurityIpRules struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:""`
RuleType string `json:"ruleType" orm:"rule_type" description:"whitelist or blacklist"`
IpCidr string `json:"ipCidr" orm:"ip_cidr" description:"IPv4, IPv6 or CIDR"`
Action string `json:"action" orm:"action" description:"allow, challenge, block, audit_only"`
SceneJson *gjson.Json `json:"sceneJson" orm:"scene_json" description:"login, register, message, upload and other scenes"`
RiskLevel string `json:"riskLevel" orm:"risk_level" description:""`
Status int `json:"status" orm:"status" description:"1 enabled, 2 disabled"`
ExpiresAt *gtime.Time `json:"expiresAt" orm:"expires_at" description:""`
Remark string `json:"remark" orm:"remark" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -0,0 +1,37 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/os/gtime"
)
// ImSecurityPolicy is the golang structure for table im_security_policy.
type ImSecurityPolicy struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:"0 means global fallback policy"`
PolicyVersion uint `json:"policyVersion" orm:"policy_version" description:""`
PolicyName string `json:"policyName" orm:"policy_name" description:""`
PublishStatus string `json:"publishStatus" orm:"publish_status" description:""`
PasswordPolicy *gjson.Json `json:"passwordPolicy" orm:"password_policy" description:""`
LoginRateLimit *gjson.Json `json:"loginRateLimit" orm:"login_rate_limit" description:""`
SensitiveWordPolicy *gjson.Json `json:"sensitiveWordPolicy" orm:"sensitive_word_policy" description:""`
FileReviewPolicy *gjson.Json `json:"fileReviewPolicy" orm:"file_review_policy" description:""`
RealnamePolicy *gjson.Json `json:"realnamePolicy" orm:"realname_policy" description:""`
RiskChallengePolicy *gjson.Json `json:"riskChallengePolicy" orm:"risk_challenge_policy" description:""`
GlobalFriendEnabled int `json:"globalFriendEnabled" orm:"global_friend_enabled" description:"Backend master switch for every add-friend entry"`
FriendDisabledPolicy *gjson.Json `json:"friendDisabledPolicy" orm:"friend_disabled_policy" description:"Profile masking, QR and group-member restrictions"`
MessageCurfewEnabled int `json:"messageCurfewEnabled" orm:"message_curfew_enabled" description:"Backend master switch for message curfew"`
Remark string `json:"remark" orm:"remark" description:""`
PublishedAt *gtime.Time `json:"publishedAt" orm:"published_at" description:""`
PublishedBy int64 `json:"publishedBy" orm:"published_by" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -0,0 +1,26 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// ImSensitiveWordGroups is the golang structure for table im_sensitive_word_groups.
type ImSensitiveWordGroups struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:"0 means global dictionary"`
GroupCode string `json:"groupCode" orm:"group_code" description:""`
GroupName string `json:"groupName" orm:"group_name" description:""`
Priority int `json:"priority" orm:"priority" description:""`
Status int `json:"status" orm:"status" description:"1 enabled, 2 disabled"`
Remark string `json:"remark" orm:"remark" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -0,0 +1,33 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/os/gtime"
)
// ImSensitiveWords is the golang structure for table im_sensitive_words.
type ImSensitiveWords struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:"0 means global dictionary"`
GroupId int64 `json:"groupId" orm:"group_id" description:""`
Word string `json:"word" orm:"word" description:""`
WordHash string `json:"wordHash" orm:"word_hash" description:"SHA-256 of normalized word"`
MatchType string `json:"matchType" orm:"match_type" description:""`
SceneJson *gjson.Json `json:"sceneJson" orm:"scene_json" description:"Enabled scenes"`
Action string `json:"action" orm:"action" description:""`
Replacement string `json:"replacement" orm:"replacement" description:""`
RiskLevel string `json:"riskLevel" orm:"risk_level" description:""`
Priority int `json:"priority" orm:"priority" description:""`
Status int `json:"status" orm:"status" description:"1 enabled, 2 disabled"`
Remark string `json:"remark" orm:"remark" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -10,13 +10,17 @@ import (
// 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:""`
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"`
DefaultPolicyGroupId int64 `json:"defaultPolicyGroupId" orm:"default_policy_group_id" description:"Default user policy group"`
SecurityPolicyVersion uint `json:"securityPolicyVersion" orm:"security_policy_version" description:"Published security policy version"`
GlobalFriendEnabled int `json:"globalFriendEnabled" orm:"global_friend_enabled" description:"Published global friend switch"`
MessageCurfewEnabled int `json:"messageCurfewEnabled" orm:"message_curfew_enabled" description:"Published message curfew switch"`
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,27 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// ImUserPolicyGroupMembers is the golang structure for table im_user_policy_group_members.
type ImUserPolicyGroupMembers struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:""`
GroupId int64 `json:"groupId" orm:"group_id" description:""`
UserId int64 `json:"userId" orm:"user_id" description:"User ID in the tenant database"`
Status int `json:"status" orm:"status" description:"1 enabled, 2 disabled"`
ValidFrom *gtime.Time `json:"validFrom" orm:"valid_from" description:""`
ValidUntil *gtime.Time `json:"validUntil" orm:"valid_until" description:""`
Remark string `json:"remark" orm:"remark" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -0,0 +1,31 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/os/gtime"
)
// ImUserPolicyGroups is the golang structure for table im_user_policy_groups.
type ImUserPolicyGroups struct {
Id int64 `json:"id" orm:"id" description:""`
TenantId int64 `json:"tenantId" orm:"tenant_id" description:"0 means global default"`
GroupCode string `json:"groupCode" orm:"group_code" description:"Stable policy group code"`
GroupName string `json:"groupName" orm:"group_name" description:"Policy group name"`
Priority int `json:"priority" orm:"priority" description:"Higher value has higher priority"`
Status int `json:"status" orm:"status" description:"1 enabled, 2 disabled"`
PolicyJson *gjson.Json `json:"policyJson" orm:"policy_json" description:"Login, friend, message, group and file policies"`
IpWhitelist *gjson.Json `json:"ipWhitelist" orm:"ip_whitelist" description:"IPv4/IPv6 or CIDR list"`
IpBlacklist *gjson.Json `json:"ipBlacklist" orm:"ip_blacklist" description:"IPv4/IPv6 or CIDR list"`
Version uint `json:"version" orm:"version" description:"Optimistic-lock/config version"`
Remark string `json:"remark" orm:"remark" 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:""`
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
DeletedBy int64 `json:"deletedBy" orm:"deleted_by" description:""`
}
@@ -0,0 +1,96 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImAbuseReasonListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
TargetType string `json:"targetType" dc:"对象类型"`
ReasonCode string `json:"reasonCode" dc:"原因编码"`
Enabled *int `json:"enabled" dc:"启用状态"`
}
func (in *ImAbuseReasonListInp) Filter(ctx context.Context) (err error) {
in.TargetType = strings.TrimSpace(in.TargetType)
in.ReasonCode = strings.TrimSpace(in.ReasonCode)
if in.TargetType != "" && !dict.HasOptionKey(consts.ImAbuseTargetTypeOptions, in.TargetType) {
return gerror.New("举报对象类型不合法")
}
if in.Enabled != nil && !dict.HasOptionKey(consts.ImAbuseReasonEnabledOptions, *in.Enabled) {
return gerror.New("启用状态不合法")
}
return nil
}
type ImAbuseReasonModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
TargetType string `json:"targetType" dc:"对象类型"`
ReasonCode string `json:"reasonCode" dc:"原因编码"`
Label string `json:"label" dc:"展示文案"`
RequiresDescription int `json:"requiresDescription" dc:"要求描述"`
AllowsEvidence int `json:"allowsEvidence" dc:"允许证据"`
SortOrder int `json:"sortOrder" dc:"排序"`
Enabled int `json:"enabled" dc:"启用状态"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImAbuseReasonEditInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" dc:"ID"`
TargetType string `json:"targetType" v:"required#对象类型不能为空" dc:"对象类型"`
ReasonCode string `json:"reasonCode" v:"required#原因编码不能为空" dc:"原因编码"`
Label string `json:"label" v:"required#展示文案不能为空" dc:"展示文案"`
RequiresDescription int `json:"requiresDescription" dc:"要求描述"`
AllowsEvidence int `json:"allowsEvidence" dc:"允许证据"`
SortOrder int `json:"sortOrder" dc:"排序"`
Enabled int `json:"enabled" dc:"启用状态"`
}
func (in *ImAbuseReasonEditInp) Filter(ctx context.Context) (err error) {
in.TargetType = strings.TrimSpace(in.TargetType)
in.ReasonCode = strings.TrimSpace(in.ReasonCode)
in.Label = strings.TrimSpace(in.Label)
if !dict.HasOptionKey(consts.ImAbuseTargetTypeOptions, in.TargetType) {
return gerror.New("举报对象类型不合法")
}
if in.ReasonCode == "" || strings.ContainsAny(in.ReasonCode, " \t\r\n/") || len([]rune(in.ReasonCode)) > 64 {
return gerror.New("原因编码不能为空,且不能包含空格或斜杠")
}
if in.Label == "" || len([]rune(in.Label)) > 128 {
return gerror.New("展示文案不能为空且不能超过128个字符")
}
if !dict.HasOptionKey(consts.ImAbuseReasonEnabledOptions, in.RequiresDescription) {
return gerror.New("要求描述开关不合法")
}
if !dict.HasOptionKey(consts.ImAbuseReasonEnabledOptions, in.AllowsEvidence) {
return gerror.New("允许证据开关不合法")
}
if !dict.HasOptionKey(consts.ImAbuseReasonEnabledOptions, in.Enabled) {
return gerror.New("启用状态不合法")
}
return nil
}
type ImAbuseReasonDeleteInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" v:"required|min:1#请选择要禁用的举报原因|请选择要禁用的举报原因" dc:"ID"`
}
func (in *ImAbuseReasonDeleteInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("请选择要禁用的举报原因")
}
return nil
}
@@ -0,0 +1,153 @@
package sysin
import (
"context"
"net/url"
"strconv"
"strings"
"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"
)
type ImAppReleaseListInp struct {
form.PageReq
Platform string `json:"platform" dc:"平台"`
Channel string `json:"channel" dc:"渠道"`
}
func (in *ImAppReleaseListInp) Filter(ctx context.Context) (err error) {
in.Platform = strings.TrimSpace(in.Platform)
in.Channel = strings.TrimSpace(in.Channel)
if in.Platform != "" && !dict.HasOptionKey(consts.ImAppPlatformOptions, in.Platform) {
return gerror.New("客户端平台不合法")
}
return nil
}
type ImAppReleaseModel struct {
Id int64 `json:"id" dc:"ID"`
Platform string `json:"platform" dc:"平台"`
Channel string `json:"channel" dc:"渠道"`
LatestVersion string `json:"latestVersion" dc:"最新版本"`
MinSupportedVersion string `json:"minSupportedVersion" dc:"最低支持版本"`
UpdateType string `json:"updateType" dc:"更新类型"`
ReleaseNotes string `json:"releaseNotes" dc:"更新说明"`
StoreUrl string `json:"storeUrl" dc:"商店地址"`
PackageUrl string `json:"packageUrl" dc:"安装包地址"`
Checksum string `json:"checksum" dc:"校验值"`
RemindAfterSeconds int `json:"remindAfterSeconds" dc:"再次提醒秒数"`
PublishedAt *gtime.Time `json:"publishedAt" dc:"发布时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImAppReleaseEditInp struct {
Id int64 `json:"id" dc:"ID"`
Platform string `json:"platform" v:"required#平台不能为空" dc:"平台"`
Channel string `json:"channel" v:"required#渠道不能为空" dc:"渠道"`
LatestVersion string `json:"latestVersion" v:"required#最新版本不能为空" dc:"最新版本"`
MinSupportedVersion string `json:"minSupportedVersion" v:"required#最低支持版本不能为空" dc:"最低支持版本"`
UpdateType string `json:"updateType" v:"required#更新类型不能为空" dc:"更新类型"`
ReleaseNotes string `json:"releaseNotes" dc:"更新说明"`
StoreUrl string `json:"storeUrl" dc:"商店地址"`
PackageUrl string `json:"packageUrl" dc:"安装包地址"`
Checksum string `json:"checksum" dc:"校验值"`
RemindAfterSeconds int `json:"remindAfterSeconds" dc:"再次提醒秒数"`
}
func (in *ImAppReleaseEditInp) Filter(ctx context.Context) (err error) {
in.Platform = strings.TrimSpace(in.Platform)
in.Channel = strings.TrimSpace(in.Channel)
in.LatestVersion = strings.TrimSpace(in.LatestVersion)
in.MinSupportedVersion = strings.TrimSpace(in.MinSupportedVersion)
in.UpdateType = strings.TrimSpace(in.UpdateType)
in.StoreUrl = strings.TrimSpace(in.StoreUrl)
in.PackageUrl = strings.TrimSpace(in.PackageUrl)
in.Checksum = strings.TrimSpace(in.Checksum)
if !dict.HasOptionKey(consts.ImAppPlatformOptions, in.Platform) {
return gerror.New("客户端平台不合法")
}
if in.Channel == "" || strings.ContainsAny(in.Channel, " \t\r\n/") || len([]rune(in.Channel)) > 32 {
return gerror.New("渠道不能为空,且不能包含空格或斜杠")
}
if in.LatestVersion == "" || len([]rune(in.LatestVersion)) > 64 {
return gerror.New("最新版本不能为空且不能超过64个字符")
}
if in.MinSupportedVersion == "" || len([]rune(in.MinSupportedVersion)) > 64 {
return gerror.New("最低支持版本不能为空且不能超过64个字符")
}
latest, err := parseAppVersion(in.LatestVersion)
if err != nil {
return gerror.New("最新版本号格式不正确,请使用数字点分版本号")
}
minimum, err := parseAppVersion(in.MinSupportedVersion)
if err != nil {
return gerror.New("最低支持版本号格式不正确,请使用数字点分版本号")
}
if compareAppVersion(latest, minimum) < 0 {
return gerror.New("最新版本不能低于最低支持版本")
}
if !dict.HasOptionKey(consts.ImAppUpdateTypeOptions, in.UpdateType) {
return gerror.New("更新类型不合法")
}
if in.RemindAfterSeconds < 0 {
return gerror.New("再次提醒时间不能小于0")
}
if in.Platform == consts.ImAppPlatformIos && in.UpdateType != consts.ImAppUpdateTypeNone {
storeURL, parseErr := url.Parse(in.StoreUrl)
if parseErr != nil || storeURL.Scheme != "https" || !strings.EqualFold(storeURL.Hostname(), "apps.apple.com") {
return gerror.New("iOS更新策略需要配置 apps.apple.com 的 HTTPS 商店地址")
}
in.PackageUrl = ""
in.Checksum = ""
}
return nil
}
func parseAppVersion(raw string) ([]uint64, error) {
raw = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(raw)), "v")
parts := strings.Split(raw, ".")
if len(parts) == 0 || len(parts) > 8 {
return nil, gerror.New("版本号格式不正确")
}
result := make([]uint64, len(parts))
for i, part := range parts {
if part == "" {
return nil, gerror.New("版本号格式不正确")
}
value, err := strconv.ParseUint(part, 10, 32)
if err != nil {
return nil, err
}
result[i] = value
}
return result, nil
}
func compareAppVersion(left, right []uint64) int {
length := len(left)
if len(right) > length {
length = len(right)
}
for i := 0; i < length; i++ {
var l, r uint64
if i < len(left) {
l = left[i]
}
if i < len(right) {
r = right[i]
}
if l < r {
return -1
}
if l > r {
return 1
}
}
return 0
}
@@ -0,0 +1,39 @@
package sysin
import "testing"
func TestImAppReleaseEditFilter(t *testing.T) {
tests := []struct {
name string
input ImAppReleaseEditInp
wantErr bool
}{
{
name: "valid ios release",
input: ImAppReleaseEditInp{Platform: "ios", Channel: "stable", LatestVersion: "1.10.0", MinSupportedVersion: "1.9.9", UpdateType: "recommended", StoreUrl: "https://apps.apple.com/app/id123"},
},
{
name: "minimum newer than latest",
input: ImAppReleaseEditInp{Platform: "android", Channel: "stable", LatestVersion: "1.2.0", MinSupportedVersion: "2.0.0", UpdateType: "force"},
wantErr: true,
},
{
name: "invalid version",
input: ImAppReleaseEditInp{Platform: "android", Channel: "stable", LatestVersion: "1.2-beta", MinSupportedVersion: "1.0.0", UpdateType: "recommended"},
wantErr: true,
},
{
name: "ios store url required",
input: ImAppReleaseEditInp{Platform: "ios", Channel: "stable", LatestVersion: "2.0.0", MinSupportedVersion: "1.0.0", UpdateType: "force", StoreUrl: "https://example.com/app"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.input.Filter(nil)
if (err != nil) != tt.wantErr {
t.Fatalf("Filter() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
@@ -29,9 +29,6 @@ type ImAuditLogListInp struct {
}
func (in *ImAuditLogListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
return
}
@@ -61,9 +58,6 @@ type ImAuditLogViewInp struct {
}
func (in *ImAuditLogViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
@@ -73,3 +67,18 @@ func (in *ImAuditLogViewInp) Filter(ctx context.Context) (err error) {
type ImAuditLogViewModel struct {
ImAuditLogListModel
}
type ImAuditLogExportModel struct {
Id int64 `json:"id" dc:"ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
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:"客户端"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
}
@@ -0,0 +1,44 @@
package sysin
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImDashboardOverviewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
}
type ImDashboardOverviewModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
TotalUsers int64 `json:"totalUsers" dc:"总用户数"`
ActiveUsers int64 `json:"activeUsers" dc:"活跃用户数"`
TotalGroups int64 `json:"totalGroups" dc:"总群组数"`
TotalConversations int64 `json:"totalConversations" dc:"总会话数"`
TotalMessages int64 `json:"totalMessages" dc:"总消息数"`
TodayMessages int64 `json:"todayMessages" dc:"今日消息数"`
}
type ImDashboardTrendsInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Days int `json:"days" dc:"统计天数"`
}
func (in *ImDashboardTrendsInp) Filter(ctx context.Context) error {
if in.Days == 0 {
in.Days = 7
}
if in.Days < 1 || in.Days > 31 {
return gerror.New("统计天数必须在1到31天之间")
}
return nil
}
type ImDashboardTrendModel struct {
Date *gtime.Time `json:"date" dc:"日期"`
UserCount int64 `json:"userCount" dc:"新增用户数"`
MessageCount int64 `json:"messageCount" dc:"新增消息数"`
}
@@ -0,0 +1,85 @@
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImExternalConfigListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Service string `json:"service" dc:"服务"`
Provider string `json:"provider" dc:"服务商"`
Enabled *int `json:"enabled" dc:"是否启用"`
}
func (in *ImExternalConfigListInp) Filter(ctx context.Context) (err error) { return }
type ImExternalConfigListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
Service string `json:"service" dc:"服务"`
Provider string `json:"provider" dc:"服务商"`
Endpoint string `json:"endpoint" dc:"端点"`
Enabled int `json:"enabled" dc:"是否启用"`
Status string `json:"status" dc:"配置状态"`
ConfigJson string `json:"configJson" dc:"普通配置"`
HasSecret bool `json:"hasSecret" dc:"是否有敏感配置"`
CheckedAt *gtime.Time `json:"checkedAt" dc:"检查时间"`
CheckResult string `json:"checkResult" dc:"检查结果"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImExternalConfigViewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Service string `json:"service" v:"required#服务不能为空" dc:"服务"`
}
func (in *ImExternalConfigViewInp) Filter(ctx context.Context) (err error) {
if in.Service == "" {
return gerror.New("服务不能为空")
}
return
}
type ImExternalConfigViewModel struct {
ImExternalConfigListModel
}
type ImExternalConfigUpdateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Service string `json:"service" v:"required#服务不能为空" dc:"服务"`
Provider string `json:"provider" dc:"服务商"`
Endpoint string `json:"endpoint" dc:"端点"`
Enabled int `json:"enabled" dc:"是否启用"`
ConfigJson *gjson.Json `json:"configJson" dc:"普通配置"`
SecretJson *gjson.Json `json:"secretJson" dc:"敏感配置,空则保持原值"`
}
func (in *ImExternalConfigUpdateInp) Filter(ctx context.Context) (err error) {
if in.Service == "" {
return gerror.New("服务不能为空")
}
if in.Enabled != 0 && in.Enabled != 1 {
return gerror.New("启用状态不正确")
}
return
}
type ImExternalConfigCheckInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Service string `json:"service" v:"required#服务不能为空" dc:"服务"`
}
func (in *ImExternalConfigCheckInp) Filter(ctx context.Context) (err error) {
if in.Service == "" {
return gerror.New("服务不能为空")
}
return
}
@@ -0,0 +1,48 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImFeedbackConfigGetInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
}
func (in *ImFeedbackConfigGetInp) Filter(ctx context.Context) (err error) { return }
type ImFeedbackConfigModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
Categories string `json:"categories" dc:"分类JSON"`
MaxLength int `json:"maxLength" dc:"最大字数"`
AttachmentPolicy string `json:"attachmentPolicy" dc:"附件策略JSON"`
AllowContact int `json:"allowContact" dc:"允许联系方式"`
DiagnosticsNotice string `json:"diagnosticsNotice" dc:"诊断信息提示"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImFeedbackConfigUpdateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Categories *gjson.Json `json:"categories" dc:"分类JSON"`
MaxLength int `json:"maxLength" dc:"最大字数"`
AttachmentPolicy *gjson.Json `json:"attachmentPolicy" dc:"附件策略JSON"`
AllowContact int `json:"allowContact" dc:"允许联系方式"`
DiagnosticsNotice string `json:"diagnosticsNotice" v:"required#诊断提示不能为空" dc:"诊断信息提示"`
}
func (in *ImFeedbackConfigUpdateInp) Filter(ctx context.Context) (err error) {
if in.MaxLength <= 0 {
return gerror.New("最大字数必须大于0")
}
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.AllowContact) {
return gerror.New("联系方式开关不合法")
}
return nil
}
@@ -0,0 +1,116 @@
// 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"
)
type ImFileListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
FileId int64 `json:"fileId" dc:"文件ID"`
UserId int64 `json:"userId" dc:"上传用户ID"`
Status *int `json:"status" dc:"状态"`
Purpose string `json:"purpose" dc:"用途"`
MimeType string `json:"mimeType" dc:"MIME类型"`
Topic string `json:"topic" dc:"关联Topic"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt []*gtime.Time `json:"updatedAt" dc:"更新时间"`
}
func (in *ImFileListInp) Filter(ctx context.Context) (err error) {
return
}
type ImFileListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"文件ID"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
UserId int64 `json:"userId" dc:"上传用户ID"`
Status int `json:"status" dc:"状态"`
MimeType string `json:"mimeType" dc:"MIME类型"`
Size int64 `json:"size" dc:"大小"`
Etag string `json:"etag" dc:"ETag"`
Location string `json:"location" dc:"文件位置"`
Purpose string `json:"purpose" dc:"用途"`
LinkCount int `json:"linkCount" dc:"引用数"`
FirstTopic string `json:"firstTopic" dc:"首个关联Topic"`
FirstMsgId int64 `json:"firstMsgId" dc:"首个关联消息ID"`
FirstLinkAt *gtime.Time `json:"firstLinkAt" dc:"首个关联时间"`
}
type ImFileViewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" v:"required#文件ID不能为空" dc:"文件ID"`
}
func (in *ImFileViewInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("文件ID不能为空")
}
return
}
type ImFileViewModel struct {
ImFileListModel
Links []*ImFileLinkModel `json:"links" dc:"引用列表"`
}
type ImFileLinkModel struct {
Id int64 `json:"id" dc:"ID"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
FileId int64 `json:"fileId" dc:"文件ID"`
MsgId int64 `json:"msgId" dc:"消息ID"`
Topic string `json:"topic" dc:"Topic"`
UserId int64 `json:"userId" dc:"用户ID"`
}
type ImFileActionInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" v:"required#文件ID不能为空" dc:"文件ID"`
Reason string `json:"reason" dc:"原因"`
}
func (in *ImFileActionInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("文件ID不能为空")
}
if len([]rune(in.Reason)) > 200 {
return gerror.New("原因不能超过200个字符")
}
return
}
type ImFileCleanupOrphanInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Days int `json:"days" dc:"保留天数"`
Limit int `json:"limit" dc:"处理上限"`
}
func (in *ImFileCleanupOrphanInp) Filter(ctx context.Context) (err error) {
if in.Days <= 0 {
in.Days = 7
}
if in.Limit <= 0 {
in.Limit = 100
}
if in.Limit > 1000 {
return gerror.New("单次清理上限不能超过1000")
}
return
}
type ImFileCleanupOrphanModel struct {
Count int `json:"count" dc:"标记数量"`
Locations []string `json:"locations" dc:"待物理清理位置"`
}
@@ -0,0 +1,75 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImFilePolicyListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Purpose string `json:"purpose" dc:"用途"`
AutoCleanup *int `json:"autoCleanup" dc:"自动清理"`
ScanEnabled *int `json:"scanEnabled" dc:"病毒扫描"`
}
func (in *ImFilePolicyListInp) Filter(ctx context.Context) (err error) { return }
type ImFilePolicyListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
Purpose string `json:"purpose" dc:"用途"`
MaxSize int64 `json:"maxSize" dc:"单文件大小上限"`
RetentionDays int `json:"retentionDays" dc:"保留天数"`
AllowedMimes string `json:"allowedMimes" dc:"允许MIME JSON"`
AutoCleanup int `json:"autoCleanup" dc:"自动清理"`
ScanEnabled int `json:"scanEnabled" dc:"病毒扫描"`
Remark string `json:"remark" dc:"备注"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImFilePolicyUpdateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Purpose string `json:"purpose" v:"required#用途不能为空" dc:"用途"`
MaxSize int64 `json:"maxSize" dc:"单文件大小上限"`
RetentionDays int `json:"retentionDays" dc:"保留天数"`
AllowedMimes *gjson.Json `json:"allowedMimes" dc:"允许MIME JSON"`
AutoCleanup int `json:"autoCleanup" dc:"自动清理"`
ScanEnabled int `json:"scanEnabled" dc:"病毒扫描"`
Remark string `json:"remark" dc:"备注"`
}
func (in *ImFilePolicyUpdateInp) Filter(ctx context.Context) (err error) {
in.Purpose = strings.TrimSpace(in.Purpose)
in.Remark = strings.TrimSpace(in.Remark)
if !dict.HasOptionKey(consts.ImFilePurposeOptions, in.Purpose) {
return gerror.New("文件用途不合法")
}
if in.Purpose == consts.ImFilePurposeUnknown || in.Purpose == consts.ImFilePurposeOrphan {
return gerror.New("该文件用途不允许配置策略")
}
if in.MaxSize <= 0 {
return gerror.New("单文件大小上限必须大于0")
}
if in.RetentionDays < 0 {
return gerror.New("保留天数不能小于0")
}
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.AutoCleanup) {
return gerror.New("自动清理开关不合法")
}
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.ScanEnabled) {
return gerror.New("病毒扫描开关不合法")
}
if len([]rune(in.Remark)) > 200 {
return gerror.New("备注不能超过200个字符")
}
return nil
}
+260 -104
View File
@@ -26,28 +26,27 @@ type ImGroupListInp struct {
}
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:"辅助资料"`
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,string" dc:"群主ID"`
OwnerNickname string `json:"ownerNickname" dc:"群主昵称"`
OwnerUsername string `json:"ownerUsername" dc:"群主用户名"`
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"`
@@ -72,9 +71,6 @@ type ImGroupViewInp struct {
}
func (in *ImGroupViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Topic == "" {
return gerror.New("Topic不能为空")
}
@@ -98,9 +94,6 @@ type ImGroupMemberListInp struct {
}
func (in *ImGroupMemberListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Topic == "" {
return gerror.New("Topic不能为空")
}
@@ -108,7 +101,7 @@ func (in *ImGroupMemberListInp) Filter(ctx context.Context) (err error) {
}
type ImGroupMemberListModel struct {
UserId int64 `json:"userId" dc:"用户ID"`
UserId int64 `json:"userId,string" dc:"用户ID"`
Nickname string `json:"nickname" dc:"昵称"`
State int `json:"state" dc:"用户状态"`
ModeWant string `json:"modeWant" dc:"期望权限"`
@@ -126,10 +119,10 @@ type ImGroupMemberListModel struct {
Pinned int `json:"pinned" dc:"置顶"`
MuteNotifications int `json:"muteNotifications" dc:"免打扰"`
JoinSource string `json:"joinSource" dc:"加入来源"`
JoinedBy int64 `json:"joinedBy" dc:"邀请/处理人"`
JoinedBy int64 `json:"joinedBy,string" dc:"邀请/处理人"`
JoinedAt *gtime.Time `json:"joinedAt" dc:"业务加入时间"`
LeftAt *gtime.Time `json:"leftAt" dc:"离开时间"`
RemovedBy int64 `json:"removedBy" dc:"移除人"`
RemovedBy int64 `json:"removedBy,string" dc:"移除人"`
Version int `json:"version" dc:"版本"`
}
@@ -140,9 +133,6 @@ type ImGroupTopicInp struct {
}
func (in *ImGroupTopicInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Topic == "" {
return gerror.New("Topic不能为空")
}
@@ -153,7 +143,7 @@ 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"`
OwnerUserId int64 `json:"ownerUserId,string" dc:"群主ID"`
Name string `json:"name" dc:"群名称"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Description string `json:"description" dc:"简介"`
@@ -167,13 +157,176 @@ type ImGroupGovernanceModel struct {
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:"解散人"`
CreatedBy int64 `json:"createdBy,string" dc:"创建人"`
DissolvedBy int64 `json:"dissolvedBy,string" dc:"解散人"`
DissolvedAt *gtime.Time `json:"dissolvedAt" dc:"解散时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupGovernanceUpdateInp struct {
ImGroupTopicInp
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:"最大管理员数"`
}
func (in *ImGroupGovernanceUpdateInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.JoinApproval != 0 && in.JoinApproval != 1 {
return gerror.New("入群审批值不正确")
}
if in.MuteAll != 0 && in.MuteAll != 1 {
return gerror.New("全员禁言值不正确")
}
if in.MemberUploadFiles != 0 && in.MemberUploadFiles != 1 {
return gerror.New("成员上传文件值不正确")
}
if in.MemberPrivateChat != 0 && in.MemberPrivateChat != 1 {
return gerror.New("成员私聊值不正确")
}
if in.MemberInvite != 0 && in.MemberInvite != 1 {
return gerror.New("成员邀请值不正确")
}
if in.MaxMemberCount < 0 || in.MaxAdminCount < 0 {
return gerror.New("人数上限不能小于0")
}
return
}
type ImGroupProfileUpdateInp struct {
ImGroupTopicInp
Name string `json:"name" dc:"群名称"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Description string `json:"description" dc:"简介"`
Version int64 `json:"version" dc:"版本"`
}
func (in *ImGroupProfileUpdateInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.Name == "" && in.AvatarFileId == "" && in.Description == "" {
return gerror.New("请填写需要修改的群资料")
}
if len([]rune(in.Name)) > 80 {
return gerror.New("群名称不能超过80个字符")
}
if len([]rune(in.Description)) > 500 {
return gerror.New("群简介不能超过500个字符")
}
return
}
type ImGroupDissolveInp struct {
ImGroupTopicInp
Reason string `json:"reason" dc:"解散原因"`
}
func (in *ImGroupDissolveInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if len([]rune(in.Reason)) > 200 {
return gerror.New("解散原因不能超过200个字符")
}
return
}
type ImGroupMuteInp struct {
ImGroupTopicInp
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
Reason string `json:"reason" dc:"禁言原因"`
}
func (in *ImGroupMuteInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if len([]rune(in.Reason)) > 200 {
return gerror.New("禁言原因不能超过200个字符")
}
return
}
type ImGroupUnmuteInp struct {
ImGroupTopicInp
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
func (in *ImGroupUnmuteInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
return
}
type ImGroupJoinRequestResolveInp struct {
ImGroupTopicInp
RequestId string `json:"requestId" v:"required#申请ID不能为空" dc:"申请ID"`
RejectReason string `json:"rejectReason" dc:"拒绝原因"`
}
func (in *ImGroupJoinRequestResolveInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.RequestId == "" {
return gerror.New("申请ID不能为空")
}
if len([]rune(in.RejectReason)) > 200 {
return gerror.New("拒绝原因不能超过200个字符")
}
return
}
type ImGroupRoleUpdateInp struct {
ImGroupTopicInp
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
Role string `json:"role" v:"required#角色不能为空" dc:"角色"`
}
func (in *ImGroupRoleUpdateInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if in.Role == "" {
return gerror.New("角色不能为空")
}
return
}
type ImGroupOwnerTransferInp struct {
ImGroupTopicInp
UserId int64 `json:"userId" v:"required#新群主ID不能为空" dc:"新群主ID"`
}
func (in *ImGroupOwnerTransferInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.UserId <= 0 {
return gerror.New("新群主ID不能为空")
}
return
}
type ImGroupJoinRequestListInp struct {
form.PageReq
ImGroupTopicInp
@@ -185,13 +338,13 @@ 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"`
ApplicantUserId int64 `json:"applicantUserId,string" dc:"申请人ID"`
InviterUserId int64 `json:"inviterUserId,string" 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:"处理人"`
HandledBy int64 `json:"handledBy,string" dc:"处理人"`
HandledAt *gtime.Time `json:"handledAt" dc:"处理时间"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
Version int `json:"version" dc:"版本"`
@@ -199,6 +352,49 @@ type ImGroupJoinRequestListModel struct {
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupFileListInp struct {
form.PageReq
ImGroupTopicInp
Status string `json:"status" dc:"状态"`
FileType string `json:"fileType" dc:"文件类型"`
FileName string `json:"fileName" dc:"文件名"`
UploaderId int64 `json:"uploaderId" dc:"上传人ID"`
}
type ImGroupFileListModel struct {
Id int64 `json:"id" dc:"ID"`
GroupFileId string `json:"groupFileId" dc:"群文件ID"`
Topic string `json:"topic" dc:"Topic"`
FileId string `json:"fileId" dc:"文件ID"`
SourceTopic string `json:"sourceTopic" dc:"来源Topic"`
SourceSeq int `json:"sourceSeq" dc:"来源消息序号"`
UploaderId int64 `json:"uploaderId,string" dc:"上传人ID"`
FileName string `json:"fileName" dc:"文件名"`
MimeType string `json:"mimeType" dc:"MIME类型"`
FileType string `json:"fileType" dc:"文件类型"`
FileSize int64 `json:"fileSize" dc:"文件大小"`
Status string `json:"status" dc:"状态"`
DeletedBy int64 `json:"deletedBy,string" dc:"删除人"`
DeletedAt *gtime.Time `json:"deletedAt" dc:"删除时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
}
type ImGroupFileDeleteInp struct {
ImGroupTopicInp
GroupFileId string `json:"groupFileId" v:"required#群文件ID不能为空" dc:"群文件ID"`
}
func (in *ImGroupFileDeleteInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.GroupFileId == "" {
return gerror.New("群文件ID不能为空")
}
return
}
type ImGroupInvitationListInp struct {
form.PageReq
ImGroupTopicInp
@@ -211,8 +407,8 @@ 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"`
InviterUserId int64 `json:"inviterUserId,string" dc:"邀请人ID"`
TargetUserId int64 `json:"targetUserId,string" dc:"目标用户ID"`
Message string `json:"message" dc:"邀请消息"`
Status string `json:"status" dc:"状态"`
Result string `json:"result" dc:"结果"`
@@ -233,12 +429,12 @@ 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"`
AuthorUserId int64 `json:"authorUserId,string" 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:"删除人"`
DeletedBy int64 `json:"deletedBy,string" dc:"删除人"`
DeletedAt *gtime.Time `json:"deletedAt" dc:"删除时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
@@ -253,13 +449,13 @@ type ImGroupMuteListInp struct {
type ImGroupMuteListModel struct {
Topic string `json:"topic" dc:"Topic"`
UserId int64 `json:"userId" dc:"用户ID"`
MutedBy int64 `json:"mutedBy" dc:"禁言人"`
UserId int64 `json:"userId,string" dc:"用户ID"`
MutedBy int64 `json:"mutedBy,string" 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:"解除人"`
RevokedBy int64 `json:"revokedBy,string" dc:"解除人"`
RevokedAt *gtime.Time `json:"revokedAt" dc:"解除时间"`
Version int `json:"version" dc:"版本"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
@@ -277,8 +473,8 @@ 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:"目标用户"`
OperatorId int64 `json:"operatorId,string" dc:"操作人"`
TargetUserId int64 `json:"targetUserId,string" dc:"目标用户"`
Action string `json:"action" dc:"动作"`
ObjectType string `json:"objectType" dc:"对象类型"`
ObjectId string `json:"objectId" dc:"对象ID"`
@@ -289,6 +485,25 @@ type ImGroupAdminLogListModel struct {
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
}
type ImGroupMemberRemoveInp struct {
ImGroupTopicInp
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
Reason string `json:"reason" dc:"原因"`
}
func (in *ImGroupMemberRemoveInp) Filter(ctx context.Context) (err error) {
if err = in.ImGroupTopicInp.Filter(ctx); err != nil {
return
}
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if len([]rune(in.Reason)) > 200 {
return gerror.New("原因不能超过200个字符")
}
return
}
type ImGroupCommandModel struct {
CommandID string `json:"commandId" dc:"命令ID"`
EventID string `json:"eventId" dc:"Kafka事件ID"`
@@ -298,62 +513,3 @@ type ImGroupCommandModel struct {
EventType string `json:"eventType" dc:"事件类型"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
}
type ImGroupProfileUpdateInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
Name string `json:"name" dc:"群名称"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Description string `json:"description" dc:"简介"`
Version int `json:"version" dc:"版本"`
}
type ImGroupGovernanceUpdateInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
JoinApproval *bool `json:"joinApproval" dc:"入群审批"`
MuteAll *bool `json:"muteAll" dc:"全员禁言"`
MemberUploadFiles *bool `json:"memberUploadFiles" dc:"成员上传文件"`
MemberPrivateChat *bool `json:"memberPrivateChat" dc:"成员私聊"`
MemberInvite *bool `json:"memberInvite" dc:"成员邀请"`
Version int `json:"version" dc:"版本"`
}
type ImGroupAdminSetInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
UserIds []int64 `json:"userIds" v:"required#用户ID不能为空" dc:"用户ID列表"`
}
type ImGroupAdminRemoveInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
type ImGroupMemberMuteInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
Reason string `json:"reason" dc:"原因"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"过期时间"`
}
type ImGroupMemberUnmuteInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
type ImGroupMemberRemoveInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
type ImGroupOwnerTransferInp struct {
ImGroupTopicInp
ActorUserId int64 `json:"actorUserId" v:"required#操作IM用户ID不能为空" dc:"操作IM用户ID"`
TargetUserId int64 `json:"targetUserId" v:"required#新群主ID不能为空" dc:"新群主ID"`
Version int `json:"version" dc:"版本"`
}
+45 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"hotgo/internal/model/input/form"
"strconv"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
@@ -25,6 +26,9 @@ type ImInviteListInp struct {
}
func (in *ImInviteListInp) Filter(ctx context.Context) (err error) {
if in.Page <= 0 {
in.Page = 1
}
if in.PerPage <= 0 {
in.PerPage = 20
}
@@ -53,12 +57,16 @@ type ImInviteCreateInp struct {
}
func (in *ImInviteCreateInp) Filter(ctx context.Context) (err error) {
in.IdempotencyKey = strings.TrimSpace(in.IdempotencyKey)
if in.ExpiresAt == nil || in.ExpiresAt.Before(gtime.Now()) {
return gerror.New("到期时间必须晚于当前时间")
}
if len(in.Remark) > 255 {
return gerror.New("备注不能超过255个字符")
}
if len(in.IdempotencyKey) > 128 {
return gerror.New("幂等键不能超过128个字符")
}
return
}
@@ -71,6 +79,7 @@ type ImInviteBatchCreateInp struct {
}
func (in *ImInviteBatchCreateInp) Filter(ctx context.Context) (err error) {
in.IdempotencyKey = strings.TrimSpace(in.IdempotencyKey)
if in.Quantity < 1 || in.Quantity > 1000 {
return gerror.New("批量生成数量必须在1到1000之间")
}
@@ -80,6 +89,9 @@ func (in *ImInviteBatchCreateInp) Filter(ctx context.Context) (err error) {
if len(in.Remark) > 255 {
return gerror.New("备注不能超过255个字符")
}
if len(in.IdempotencyKey) == 0 || len(in.IdempotencyKey) > 128 {
return gerror.New("批量生成必须提供不超过128个字符的幂等键")
}
return
}
@@ -93,30 +105,51 @@ type ImInviteRevokeInp struct {
Id string `json:"id" v:"required#邀请码ID不能为空" dc:"邀请码ID"`
}
type ImInviteBindUserInp struct {
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
Id string `json:"id" v:"required#邀请码ID不能为空" dc:"邀请码ID"`
UserId int64 `json:"userId" v:"required|min:1#绑定用户不能为空|绑定用户不合法" dc:"绑定C端用户ID"`
}
func (in *ImInviteBindUserInp) Filter(ctx context.Context) (err error) {
if _, err = ParseOptionalInt64(in.Id); err != nil {
return gerror.New("邀请码ID不能为空")
}
if in.UserId <= 0 {
return gerror.New("绑定用户不能为空")
}
return nil
}
type ImInviteBatchRevokeInp struct {
TenantId int64 `json:"tenantId" dc:"IM租户ID"`
BatchId string `json:"batchId" v:"required#批次ID不能为空" dc:"批次ID"`
}
type ImInviteCodeModel struct {
Id string `json:"id" dc:"ID"`
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:"创建时间"`
Id string `json:"id" dc:"ID"`
TenantId string `json:"tenantId" dc:"IM租户ID"`
TenantName string `json:"tenantName" dc:"IM租户名称"`
BatchId string `json:"batchId" dc:"批次ID"`
Code string `json:"code" dc:"邀请码"`
Status string `json:"status" dc:"状态"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
MaxUses int `json:"maxUses" dc:"最大可用次数,0表示不限制"`
UsageCount int `json:"usageCount" dc:"累计使用次数"`
BoundUserId string `json:"boundUserId" dc:"绑定用户ID"`
BoundUserLabel string `json:"boundUserLabel" dc:"绑定用户展示名"`
LastUsedAt *gtime.Time `json:"lastUsedAt" dc:"最后使用时间"`
CreatedBy string `json:"createdBy" dc:"创建人"`
Remark string `json:"remark" dc:"备注"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
}
type ImInviteBatchModel struct {
BatchId string `json:"batchId" dc:"批次ID"`
TenantId string `json:"tenantId" dc:"IM租户ID"`
Quantity int `json:"quantity" dc:"数量"`
ActiveCount int `json:"activeCount" dc:"有效数量"`
UsedCount int `json:"usedCount" dc:"已使用数量"`
UsageCount int `json:"usageCount" dc:"累计使用次数"`
RevokedCount int `json:"revokedCount" dc:"已撤销数量"`
ExpiredCount int `json:"expiredCount" dc:"已过期数量"`
ExpiresAt *gtime.Time `json:"expiresAt" dc:"到期时间"`
@@ -0,0 +1,161 @@
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImUserLoginHistoryListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" dc:"用户ID"`
Account string `json:"account" dc:"脱敏账号"`
LoginType string `json:"loginType" dc:"登录方式"`
EventType string `json:"eventType" dc:"事件类型"`
Platform string `json:"platform" dc:"客户端平台"`
Ip string `json:"ip" dc:"IP地址"`
LoginAt []*gtime.Time `json:"loginAt" dc:"登录时间"`
}
func (in *ImUserLoginHistoryListInp) Filter(ctx context.Context) error {
in.Account = strings.TrimSpace(in.Account)
in.Ip = strings.TrimSpace(in.Ip)
return filterImAuditPage(&in.PageReq)
}
type ImUserLoginHistoryModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
UserId string `json:"userId" dc:"用户ID"`
Username string `json:"username" dc:"用户名"`
AccountType string `json:"accountType" dc:"账号类型"`
AccountMask string `json:"accountMask" dc:"脱敏账号"`
LoginType string `json:"loginType" dc:"登录方式"`
EventType string `json:"eventType" dc:"事件类型"`
SessionId string `json:"sessionId" dc:"会话ID"`
DeviceId string `json:"deviceId" dc:"设备ID"`
Platform string `json:"platform" dc:"客户端平台"`
AppVersion string `json:"appVersion" dc:"客户端版本"`
Ip string `json:"ip" dc:"IP地址"`
Region string `json:"region" dc:"地区"`
UserAgent string `json:"userAgent" dc:"客户端标识"`
RiskLevel string `json:"riskLevel" dc:"风险等级"`
RequestId string `json:"requestId" dc:"请求ID"`
LoginAt *gtime.Time `json:"loginAt" dc:"登录时间"`
LogoutAt *gtime.Time `json:"logoutAt" dc:"登出时间"`
Metadata string `json:"metadata" dc:"元数据"`
}
type ImLoginFailureLogListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" dc:"用户ID"`
Account string `json:"account" dc:"脱敏账号"`
FailureType string `json:"failureType" dc:"失败类型"`
Platform string `json:"platform" dc:"客户端平台"`
Ip string `json:"ip" dc:"IP地址"`
AttemptAt []*gtime.Time `json:"attemptAt" dc:"尝试时间"`
}
func (in *ImLoginFailureLogListInp) Filter(ctx context.Context) error {
in.Account = strings.TrimSpace(in.Account)
in.Ip = strings.TrimSpace(in.Ip)
return filterImAuditPage(&in.PageReq)
}
func filterImAuditPage(page *form.PageReq) error {
if page.Page <= 0 {
page.Page = 1
}
if page.PerPage <= 0 {
page.PerPage = 20
}
if page.PerPage > 200 {
return gerror.New("每页数量不能超过200")
}
return nil
}
type ImLoginFailureLogModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
UserId string `json:"userId" dc:"用户ID"`
AccountType string `json:"accountType" dc:"账号类型"`
AccountMask string `json:"accountMask" dc:"脱敏账号"`
FailureType string `json:"failureType" dc:"失败类型"`
Reason string `json:"reason" dc:"原因码"`
Detail string `json:"detail" dc:"失败详情"`
Ip string `json:"ip" dc:"IP地址"`
Region string `json:"region" dc:"地区"`
DeviceId string `json:"deviceId" dc:"设备ID"`
Platform string `json:"platform" dc:"客户端平台"`
AppVersion string `json:"appVersion" dc:"客户端版本"`
UserAgent string `json:"userAgent" dc:"客户端标识"`
RiskLevel string `json:"riskLevel" dc:"风险等级"`
RiskAction string `json:"riskAction" dc:"风控动作"`
RequestId string `json:"requestId" dc:"请求ID"`
AttemptAt *gtime.Time `json:"attemptAt" dc:"尝试时间"`
Metadata string `json:"metadata" dc:"元数据"`
}
type ImLoginFailureViewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" dc:"日志ID"`
}
func (in *ImLoginFailureViewInp) Filter(ctx context.Context) error {
if in.Id <= 0 {
return gerror.New("日志ID不能为空")
}
return nil
}
type ImLoginFailureAddBlacklistInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Ip string `json:"ip" dc:"IP地址"`
Remark string `json:"remark" dc:"备注"`
}
func (in *ImLoginFailureAddBlacklistInp) Filter(ctx context.Context) error {
in.Ip = strings.TrimSpace(in.Ip)
if in.Ip == "" {
return gerror.New("IP地址不能为空")
}
return nil
}
type ImLoginFailureBanUserInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" dc:"用户ID"`
Reason string `json:"reason" dc:"封禁原因"`
}
func (in *ImLoginFailureBanUserInp) Filter(ctx context.Context) error {
in.Reason = strings.TrimSpace(in.Reason)
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if in.Reason == "" {
return gerror.New("封禁原因不能为空")
}
return nil
}
type ImLoginAuditExportModel struct {
Id int64 `json:"id" dc:"ID"`
UserId string `json:"userId" dc:"用户ID"`
AccountMask string `json:"accountMask" dc:"脱敏账号"`
EventType string `json:"eventType" dc:"事件/失败类型"`
Platform string `json:"platform" dc:"客户端平台"`
Ip string `json:"ip" dc:"IP地址"`
Region string `json:"region" dc:"地区"`
RiskLevel string `json:"riskLevel" dc:"风险等级"`
Reason string `json:"reason" dc:"原因"`
OccurredAt *gtime.Time `json:"occurredAt" dc:"发生时间"`
}
@@ -0,0 +1,59 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImMailboxConfigGetInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
}
func (in *ImMailboxConfigGetInp) Filter(ctx context.Context) (err error) { return }
type ImMailboxConfigModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
Enabled int `json:"enabled" dc:"启用状态"`
AllowAnonymous int `json:"allowAnonymous" dc:"允许匿名"`
RequiredFields string `json:"requiredFields" dc:"必填字段JSON"`
MaxLength int `json:"maxLength" dc:"最大字数"`
AttachmentPolicy string `json:"attachmentPolicy" dc:"附件策略JSON"`
NoticeTitle string `json:"noticeTitle" dc:"告知标题"`
NoticeContent string `json:"noticeContent" dc:"告知内容"`
NoticeUpdatedAt *gtime.Time `json:"noticeUpdatedAt" dc:"告知更新时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImMailboxConfigUpdateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Enabled int `json:"enabled" dc:"启用状态"`
AllowAnonymous int `json:"allowAnonymous" dc:"允许匿名"`
RequiredFields *gjson.Json `json:"requiredFields" dc:"必填字段JSON"`
MaxLength int `json:"maxLength" dc:"最大字数"`
AttachmentPolicy *gjson.Json `json:"attachmentPolicy" dc:"附件策略JSON"`
NoticeTitle string `json:"noticeTitle" v:"required#告知标题不能为空" dc:"告知标题"`
NoticeContent string `json:"noticeContent" v:"required#告知内容不能为空" dc:"告知内容"`
}
func (in *ImMailboxConfigUpdateInp) Filter(ctx context.Context) (err error) {
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.Enabled) {
return gerror.New("启用状态不合法")
}
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.AllowAnonymous) {
return gerror.New("匿名开关不合法")
}
if in.MaxLength <= 0 {
return gerror.New("最大字数必须大于0")
}
if len([]rune(in.NoticeTitle)) > 128 {
return gerror.New("告知标题不能超过128个字符")
}
return nil
}
@@ -0,0 +1,154 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImPrivateMessageListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Topic string `json:"topic" dc:"会话Topic"`
FromUserId string `json:"fromUserId" dc:"发送用户ID"`
ToUserId string `json:"toUserId" dc:"接收用户ID"`
ContentType string `json:"contentType" dc:"内容类型"`
Status string `json:"status" dc:"状态"`
Keyword string `json:"keyword" dc:"内容关键词"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"发送时间"`
}
func (in *ImPrivateMessageListInp) Filter(ctx context.Context) error {
return filterImMessageList(&in.PageReq, &in.Topic, &in.Keyword, in.ContentType, in.Status)
}
type ImGroupMessageListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Topic string `json:"topic" dc:"群Topic"`
SeqId int64 `json:"seqId" dc:"消息序号"`
SenderUserId string `json:"senderUserId" dc:"发送用户ID"`
SenderUsername string `json:"senderUsername" dc:"发送人用户名"`
SenderNickname string `json:"senderNickname" dc:"发送人昵称"`
GroupName string `json:"groupName" dc:"群名称"`
ContentType string `json:"contentType" dc:"内容类型"`
Status string `json:"status" dc:"状态"`
Keyword string `json:"keyword" dc:"内容关键词"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"发送时间"`
}
func (in *ImGroupMessageListInp) Filter(ctx context.Context) error {
in.SenderUsername = strings.TrimSpace(in.SenderUsername)
in.SenderNickname = strings.TrimSpace(in.SenderNickname)
in.GroupName = strings.TrimSpace(in.GroupName)
return filterImMessageList(&in.PageReq, &in.Topic, &in.Keyword, in.ContentType, in.Status)
}
func filterImMessageList(page *form.PageReq, topic *string, keyword *string, contentType string, status string) error {
*topic = strings.TrimSpace(*topic)
*keyword = strings.TrimSpace(*keyword)
if page.Page <= 0 {
page.Page = 1
}
if page.PerPage <= 0 {
page.PerPage = 20
}
if page.PerPage > 200 {
return gerror.New("每页数量不能超过200")
}
if contentType != "" && !dict.HasOptionKey(consts.ImMessageContentTypeOptions, contentType) {
return gerror.New("消息类型不合法")
}
if status != "" && !dict.HasOptionKey(consts.ImMessageStatusOptions, status) {
return gerror.New("消息状态不合法")
}
return nil
}
type ImMessageListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Topic string `json:"topic" dc:"会话Topic"`
SeqId int64 `json:"seqId" dc:"消息序号"`
FromUserId string `json:"fromUserId" dc:"发送用户ID"`
SenderUsername string `json:"senderUsername" dc:"发送人用户名"`
SenderNickname string `json:"senderNickname" dc:"发送人昵称"`
ToUserId string `json:"toUserId" dc:"接收用户ID"`
ReceiverUsername string `json:"receiverUsername" dc:"接收人用户名"`
ReceiverNickname string `json:"receiverNickname" dc:"接收人昵称"`
GroupName string `json:"groupName" dc:"群名称"`
GroupNo string `json:"groupNo" dc:"群号"`
ContentType string `json:"contentType" dc:"消息类型"`
ContentExcerpt string `json:"contentExcerpt" dc:"内容摘要"`
Status string `json:"status" dc:"状态"`
ReplyToSeq int64 `json:"replyToSeq" dc:"回复消息序号"`
AtUsers string `json:"atUsers" dc:"@用户"`
CreatedAt *gtime.Time `json:"createdAt" dc:"发送时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImMessageViewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Topic string `json:"topic" dc:"会话Topic"`
SeqId int64 `json:"seqId" dc:"消息序号"`
}
func (in *ImMessageViewInp) Filter(ctx context.Context) error {
in.Topic = strings.TrimSpace(in.Topic)
if in.Topic == "" || in.SeqId <= 0 {
return gerror.New("消息Topic和序号不能为空")
}
return nil
}
type ImMessageViewModel struct {
ImMessageListModel
Head string `json:"head" dc:"消息头"`
Content string `json:"content" dc:"完整消息内容"`
Metadata string `json:"metadata" dc:"扩展数据"`
}
type ImMessageActionInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Topic string `json:"topic" dc:"会话Topic"`
SeqId int64 `json:"seqId" dc:"消息序号"`
Reason string `json:"reason" dc:"处置原因"`
}
func (in *ImMessageActionInp) Filter(ctx context.Context) error {
in.Topic = strings.TrimSpace(in.Topic)
in.Reason = strings.TrimSpace(in.Reason)
if in.Topic == "" || in.SeqId <= 0 {
return gerror.New("消息Topic和序号不能为空")
}
if in.Reason == "" {
return gerror.New("处置原因不能为空")
}
if len([]rune(in.Reason)) > 500 {
return gerror.New("处置原因不能超过500个字符")
}
return nil
}
type ImMessageCommandModel struct {
CommandId string `json:"commandId" dc:"命令ID"`
EventId string `json:"eventId" dc:"事件ID"`
Status string `json:"status" dc:"命令状态"`
}
type ImMessageExportModel struct {
Topic string `json:"topic" dc:"Topic"`
SeqId int64 `json:"seqId" dc:"消息序号"`
FromUserId string `json:"fromUserId" dc:"发送用户ID"`
ToUserId string `json:"toUserId" dc:"接收用户ID"`
GroupName string `json:"groupName" dc:"群名称"`
ContentType string `json:"contentType" dc:"消息类型"`
ContentExcerpt string `json:"contentExcerpt" dc:"内容摘要"`
Status string `json:"status" dc:"状态"`
CreatedAt *gtime.Time `json:"createdAt" dc:"发送时间"`
}
@@ -0,0 +1,22 @@
package sysin
import (
"context"
"testing"
)
func TestImGroupMessageListFilterTrimsTextFilters(t *testing.T) {
in := &ImGroupMessageListInp{
SenderUsername: " alice ",
SenderNickname: " 小明 ",
GroupName: " 测试群 ",
Keyword: " 关键字 ",
Topic: " grpTest ",
}
if err := in.Filter(context.Background()); err != nil {
t.Fatal(err)
}
if in.SenderUsername != "alice" || in.SenderNickname != "小明" || in.GroupName != "测试群" || in.Keyword != "关键字" || in.Topic != "grpTest" {
t.Fatalf("message filters were not normalized: %#v", in)
}
}
@@ -0,0 +1,69 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImNotificationTemplateListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Scene string `json:"scene" dc:"通知场景"`
Status *int `json:"status" dc:"状态"`
}
func (in *ImNotificationTemplateListInp) Filter(ctx context.Context) (err error) { return }
type ImNotificationTemplateListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
Scene string `json:"scene" dc:"通知场景"`
Name string `json:"name" dc:"场景名称"`
InAppEnabled int `json:"inAppEnabled" dc:"站内通知"`
PushEnabled int `json:"pushEnabled" dc:"Push通知"`
SmsEnabled int `json:"smsEnabled" dc:"短信通知"`
Status int `json:"status" dc:"状态"`
TitleTemplate string `json:"titleTemplate" dc:"标题模板"`
ContentTemplate string `json:"contentTemplate" dc:"内容模板"`
PayloadJson string `json:"payloadJson" dc:"扩展JSON"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImNotificationTemplateUpdateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Scene string `json:"scene" v:"required#通知场景不能为空" dc:"通知场景"`
InAppEnabled int `json:"inAppEnabled" dc:"站内通知"`
PushEnabled int `json:"pushEnabled" dc:"Push通知"`
SmsEnabled int `json:"smsEnabled" dc:"短信通知"`
Status int `json:"status" dc:"状态"`
TitleTemplate string `json:"titleTemplate" dc:"标题模板"`
ContentTemplate string `json:"contentTemplate" dc:"内容模板"`
PayloadJson *gjson.Json `json:"payloadJson" dc:"扩展JSON"`
}
func (in *ImNotificationTemplateUpdateInp) Filter(ctx context.Context) (err error) {
in.Scene = strings.TrimSpace(in.Scene)
in.TitleTemplate = strings.TrimSpace(in.TitleTemplate)
in.ContentTemplate = strings.TrimSpace(in.ContentTemplate)
if !dict.HasOptionKey(consts.ImNotificationSceneOptions, in.Scene) {
return gerror.New("通知场景不合法")
}
if !dict.HasOptionKey(consts.ImNotificationTemplateStatusOptions, in.Status) {
return gerror.New("通知模板状态不合法")
}
if in.TitleTemplate == "" {
return gerror.New("标题模板不能为空")
}
if in.ContentTemplate == "" {
return gerror.New("内容模板不能为空")
}
return nil
}
+215
View File
@@ -0,0 +1,215 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImOrgDepartmentListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
ParentId string `json:"parentId" dc:"上级部门ID"`
Name string `json:"name" dc:"部门名称"`
State string `json:"state" dc:"状态"`
}
func (in *ImOrgDepartmentListInp) Filter(ctx context.Context) (err error) { return }
type ImOrgDepartmentModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
Name string `json:"name" dc:"部门名称"`
ParentId string `json:"parentId" dc:"上级部门ID"`
SortOrder int `json:"sortOrder" dc:"排序"`
Version int `json:"version" dc:"版本"`
State string `json:"state" dc:"状态"`
ChildCount int `json:"childCount" dc:"子部门数"`
MemberCount int `json:"memberCount" dc:"成员数"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImOrgDepartmentExportModel struct {
TenantName string `json:"tenantName" dc:"租户名称"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
Name string `json:"name" dc:"部门名称"`
ParentId string `json:"parentId" dc:"上级部门ID"`
SortOrder int `json:"sortOrder" dc:"排序"`
Version int `json:"version" dc:"版本"`
State string `json:"state" dc:"状态"`
ChildCount int `json:"childCount" dc:"子部门数"`
MemberCount int `json:"memberCount" dc:"成员数"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImOrgDepartmentEditInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" dc:"ID"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
Name string `json:"name" dc:"部门名称"`
ParentId string `json:"parentId" dc:"上级部门ID"`
SortOrder int `json:"sortOrder" dc:"排序"`
State string `json:"state" dc:"状态"`
}
func (in *ImOrgDepartmentEditInp) Filter(ctx context.Context) (err error) {
in.Name = strings.TrimSpace(in.Name)
in.DepartmentId = strings.TrimSpace(in.DepartmentId)
in.ParentId = strings.TrimSpace(in.ParentId)
in.State = strings.TrimSpace(in.State)
if in.Name == "" {
return gerror.New("部门名称不能为空")
}
if in.ParentId == "" {
in.ParentId = "root"
}
if in.State == "" {
in.State = consts.ImOrgDepartmentStateActive
}
if !dict.HasOptionKey(consts.ImOrgDepartmentStateOptions, in.State) {
return gerror.New("部门状态不合法")
}
return
}
type ImOrgDepartmentDeleteInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
DepartmentId string `json:"departmentId" v:"required#部门ID不能为空" dc:"部门ID"`
}
func (in *ImOrgDepartmentDeleteInp) Filter(ctx context.Context) (err error) {
in.DepartmentId = strings.TrimSpace(in.DepartmentId)
if in.DepartmentId == "" {
return gerror.New("部门ID不能为空")
}
return
}
type ImOrgMemberListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
UserId int64 `json:"userId" dc:"用户ID"`
Query string `json:"query" dc:"关键词"`
EmploymentState string `json:"employmentState" dc:"在职状态"`
}
func (in *ImOrgMemberListInp) Filter(ctx context.Context) (err error) { return }
type ImOrgMemberModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
DepartmentName string `json:"departmentName" dc:"部门名称"`
UserId int64 `json:"userId" dc:"用户ID"`
DisplayName string `json:"displayName" dc:"展示名称"`
Position string `json:"position" dc:"岗位"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Mobile string `json:"mobile" dc:"手机号"`
Email string `json:"email" dc:"邮箱"`
EmploymentState string `json:"employmentState" dc:"在职状态"`
Contactable int `json:"contactable" dc:"是否可联系"`
SortOrder int `json:"sortOrder" dc:"排序"`
Version int `json:"version" dc:"版本"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImOrgMemberExportModel struct {
TenantName string `json:"tenantName" dc:"租户名称"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
DepartmentName string `json:"departmentName" dc:"部门名称"`
UserId int64 `json:"userId" dc:"用户ID"`
DisplayName string `json:"displayName" dc:"展示名称"`
Position string `json:"position" dc:"岗位"`
Mobile string `json:"mobile" dc:"手机号"`
Email string `json:"email" dc:"邮箱"`
EmploymentState string `json:"employmentState" dc:"在职状态"`
Contactable int `json:"contactable" dc:"是否可联系"`
SortOrder int `json:"sortOrder" dc:"排序"`
Version int `json:"version" dc:"版本"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImOrgMemberEditInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" dc:"ID"`
DepartmentId string `json:"departmentId" dc:"部门ID"`
UserId int64 `json:"userId" dc:"用户ID"`
DisplayName string `json:"displayName" dc:"展示名称"`
Position string `json:"position" dc:"岗位"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Mobile string `json:"mobile" dc:"手机号"`
Email string `json:"email" dc:"邮箱"`
EmploymentState string `json:"employmentState" dc:"在职状态"`
Contactable int `json:"contactable" dc:"是否可联系"`
SortOrder int `json:"sortOrder" dc:"排序"`
}
func (in *ImOrgMemberEditInp) Filter(ctx context.Context) (err error) {
in.DepartmentId = strings.TrimSpace(in.DepartmentId)
in.DisplayName = strings.TrimSpace(in.DisplayName)
in.Position = strings.TrimSpace(in.Position)
in.AvatarFileId = strings.TrimSpace(in.AvatarFileId)
in.Mobile = strings.TrimSpace(in.Mobile)
in.Email = strings.TrimSpace(in.Email)
in.EmploymentState = strings.TrimSpace(in.EmploymentState)
if in.DepartmentId == "" {
return gerror.New("部门ID不能为空")
}
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if in.EmploymentState == "" {
in.EmploymentState = consts.ImOrgEmploymentStateActive
}
if !dict.HasOptionKey(consts.ImOrgEmploymentStateOptions, in.EmploymentState) {
return gerror.New("在职状态不合法")
}
if !dict.HasOptionKey(consts.ImOrgContactableOptions, in.Contactable) {
return gerror.New("可联系状态不合法")
}
return
}
type ImOrgMemberDeleteInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" v:"required#成员关系ID不能为空" dc:"ID"`
}
func (in *ImOrgMemberDeleteInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("成员关系ID不能为空")
}
return
}
type ImOrgSearchInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Query string `json:"query" v:"required#关键词不能为空" dc:"关键词"`
}
func (in *ImOrgSearchInp) Filter(ctx context.Context) (err error) {
in.Query = strings.TrimSpace(in.Query)
if in.Query == "" {
return gerror.New("关键词不能为空")
}
return
}
type ImOrgSearchModel struct {
Departments []*ImOrgDepartmentModel `json:"departments" dc:"部门"`
Members []*ImOrgMemberModel `json:"members" dc:"成员"`
}
@@ -0,0 +1,101 @@
// 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"
)
const (
ImOutboxSourceBusiness = "business"
ImOutboxSourceGroup = "group"
ImOutboxStatusPending = "pending"
ImOutboxStatusDelivered = "delivered"
ImOutboxStatusFailed = "failed"
ImOutboxStatusDead = "dead"
)
type ImOutboxListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Source string `json:"source" dc:"事件来源"`
EventId string `json:"eventId" dc:"事件ID"`
EventType string `json:"eventType" dc:"事件类型"`
TargetType string `json:"targetType" dc:"目标类型"`
Target string `json:"target" dc:"目标"`
Status string `json:"status" dc:"状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
}
func (in *ImOutboxListInp) Filter(ctx context.Context) (err error) {
if in.Source == "" {
in.Source = ImOutboxSourceBusiness
}
if in.Source != ImOutboxSourceBusiness && in.Source != ImOutboxSourceGroup {
return gerror.New("Outbox来源不正确")
}
return
}
type ImOutboxListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Source string `json:"source" dc:"事件来源"`
Id int64 `json:"id" dc:"ID"`
EventId string `json:"eventId" dc:"事件ID"`
TargetType string `json:"targetType" dc:"目标类型"`
Target string `json:"target" dc:"目标"`
ActorUserId int64 `json:"actorUserId" dc:"操作用户ID"`
EventType string `json:"eventType" dc:"事件类型"`
Payload string `json:"payload" dc:"事件负载"`
Status string `json:"status" dc:"状态"`
Attempts int `json:"attempts" dc:"尝试次数"`
NextRetryAt *gtime.Time `json:"nextRetryAt" dc:"下次重试时间"`
DeliveredAt *gtime.Time `json:"deliveredAt" dc:"投递时间"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImOutboxViewInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Source string `json:"source" v:"required#事件来源不能为空" dc:"事件来源"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImOutboxViewInp) Filter(ctx context.Context) (err error) {
if in.Source != ImOutboxSourceBusiness && in.Source != ImOutboxSourceGroup {
return gerror.New("Outbox来源不正确")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
type ImOutboxViewModel struct {
ImOutboxListModel
}
type ImOutboxActionInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Source string `json:"source" v:"required#事件来源不能为空" dc:"事件来源"`
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImOutboxActionInp) Filter(ctx context.Context) (err error) {
if in.Source != ImOutboxSourceBusiness && in.Source != ImOutboxSourceGroup {
return gerror.New("Outbox来源不正确")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
@@ -0,0 +1,163 @@
package sysin
import (
"context"
"testing"
"hotgo/internal/consts"
"github.com/gogf/gf/v2/encoding/gjson"
)
func TestImMessageCurfewRuleFilter(t *testing.T) {
ctx := context.Background()
valid := &ImMessageCurfewRuleInp{
RuleCode: "after_work",
RuleName: "下班时段",
DateType: consts.ImCurfewDateWeekday,
StartTime: "18:00",
EndTime: "09:00",
Scope: consts.ImCurfewScopePrivate,
}
if err := valid.Filter(ctx); err != nil {
t.Fatalf("跨午夜宵禁规则应合法: %v", err)
}
if valid.Timezone != "Asia/Shanghai" || valid.Status != consts.StatusEnabled {
t.Fatalf("默认值未补齐: timezone=%s status=%d", valid.Timezone, valid.Status)
}
invalidTime := *valid
invalidTime.StartTime = "25:00"
if err := invalidTime.Filter(ctx); err == nil {
t.Fatal("非法宵禁时间应被拒绝")
}
}
func TestImSecurityPolicySaveFilterRequiresCurfewRule(t *testing.T) {
in := &ImSecurityPolicySaveInp{
PolicyName: "默认策略",
GlobalFriendEnabled: 1,
MessageCurfewEnabled: 1,
}
if err := in.Filter(context.Background()); err == nil {
t.Fatal("启用宵禁但未配置规则应被拒绝")
}
in.CurfewRules = []*ImMessageCurfewRuleInp{{
RuleCode: "group_only",
RuleName: "仅群聊",
DateType: consts.ImCurfewDateDaily,
StartTime: "18:00",
EndTime: "09:00",
Scope: consts.ImCurfewScopeGroup,
}}
if err := in.Filter(context.Background()); err == nil {
t.Fatal("宵禁仅配置群聊规则时应被拒绝")
}
}
func TestImSecurityPolicySaveFilterForcesEveryFriendEntryOff(t *testing.T) {
in := &ImSecurityPolicySaveInp{
PolicyName: "下班策略",
GlobalFriendEnabled: 0,
MessageCurfewEnabled: 0,
FriendDisabledPolicy: gjson.New(map[string]interface{}{
"hide_profile": false,
"blocked_entries": []string{"search"},
}),
}
if err := in.Filter(context.Background()); err != nil {
t.Fatalf("关闭全局好友功能的策略应合法: %v", err)
}
policy := in.FriendDisabledPolicy.Map()
if hide, ok := policy["hide_profile"].(bool); !ok || !hide {
t.Fatal("关闭全局好友功能时必须强制隐藏个人资料")
}
entries := in.FriendDisabledPolicy.Get("blocked_entries").Array()
if len(entries) != 6 {
t.Fatalf("关闭全局好友功能时必须禁用全部6个入口: %#v", policy["blocked_entries"])
}
}
func TestImSecurityPolicyPublishFilter(t *testing.T) {
ctx := context.Background()
if err := (&ImSecurityPolicyPublishInp{Id: 1, Reason: " "}).Filter(ctx); err == nil {
t.Fatal("空发布原因应被拒绝")
}
if err := (&ImSecurityPolicyPublishInp{Id: 1, Reason: "管理员发布"}).Filter(ctx); err != nil {
t.Fatalf("合法发布参数不应报错: %v", err)
}
}
func TestImSecurityIPRuleEditFilter(t *testing.T) {
ctx := context.Background()
valid := &ImSecurityIPRuleEditInp{
RuleType: "blacklist",
IpCidr: "192.0.2.0/24",
Action: consts.ImSecurityActionBlock,
SceneJson: gjson.New([]string{"login"}),
RiskLevel: consts.ImRiskLevelHigh,
}
if err := valid.Filter(ctx); err != nil {
t.Fatalf("合法IP阻断规则不应报错: %v", err)
}
invalidAction := *valid
invalidAction.Action = consts.ImPolicyActionDeny
if err := invalidAction.Filter(ctx); err == nil {
t.Fatal("数据库枚举不支持的deny动作应被拒绝")
}
}
func TestImSensitiveWordEditFilter(t *testing.T) {
in := &ImSensitiveWordEditInp{
GroupId: 1,
Word: "测试词",
MatchType: consts.ImSensitiveMatchContains,
SceneJson: gjson.New([]string{"private_message"}),
Action: consts.ImSensitiveActionReplace,
RiskLevel: consts.ImRiskLevelMedium,
}
if err := in.Filter(context.Background()); err == nil {
t.Fatal("替换动作未填写替换文本应被拒绝")
}
}
func TestImSensitiveWordGroupEditFilter(t *testing.T) {
in := &ImSensitiveWordGroupEditInp{GroupCode: "default", GroupName: " "}
if err := in.Filter(context.Background()); err == nil {
t.Fatal("空分组名称应被拒绝")
}
}
func TestImUserPolicyGroupInputFilter(t *testing.T) {
ctx := context.Background()
group := &ImUserPolicyGroupEditInp{
GroupCode: "staff",
GroupName: " ",
PolicyJson: gjson.New(map[string]interface{}{}),
}
if err := group.Filter(ctx); err == nil {
t.Fatal("空用户策略分组名称应被拒绝")
}
if err := (&ImUserPolicyGroupMemberAddInp{GroupId: 1, UserId: 0}).Filter(ctx); err == nil {
t.Fatal("无效用户ID应被拒绝")
}
}
func TestImTenantLoginIPWhitelistSaveFilter(t *testing.T) {
in := &ImTenantLoginIPWhitelistSaveInp{
TenantId: 1,
AppList: []string{" 203.0.113.10 ", "2001:db8::/32", "203.0.113.10"},
WebList: []string{"198.51.100.7/24"},
}
if err := in.Filter(context.Background()); err != nil {
t.Fatalf("合法租户登录白名单不应报错: %v", err)
}
if len(in.AppList) != 2 || in.AppList[0] != "203.0.113.10" || in.WebList[0] != "198.51.100.0/24" {
t.Fatalf("IP白名单未正确规范化: app=%v web=%v", in.AppList, in.WebList)
}
in.AppList = []string{"not-an-ip"}
if err := in.Filter(context.Background()); err == nil {
t.Fatal("非法IP地址应被拒绝")
}
}
@@ -26,9 +26,6 @@ type ImRealnameListInp struct {
}
func (in *ImRealnameListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
return
}
@@ -55,9 +52,6 @@ type ImRealnameViewInp struct {
}
func (in *ImRealnameViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
@@ -78,9 +72,6 @@ type ImRealnameReviewInp struct {
}
func (in *ImRealnameReviewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
@@ -0,0 +1,324 @@
package sysin
import (
"context"
"strings"
"time"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImSecurityPolicyListInp struct {
form.PageReq
TenantId int64 `json:"tenantId"`
PublishStatus string `json:"publishStatus"`
}
func (in *ImSecurityPolicyListInp) Filter(ctx context.Context) error {
if in.PublishStatus != "" && !dict.HasOptionKey(consts.ImSecurityPublishStatusOptions, in.PublishStatus) {
return gerror.New("发布状态不合法")
}
return nil
}
type ImMessageCurfewRuleModel struct {
Id int64 `json:"id"`
RuleCode string `json:"ruleCode"`
RuleName string `json:"ruleName"`
DateType string `json:"dateType"`
CustomDates string `json:"customDates"`
Weekdays string `json:"weekdays"`
Timezone string `json:"timezone"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
Scope string `json:"scope"`
WhitelistUserIds string `json:"whitelistUserIds"`
WhitelistGroupIds string `json:"whitelistGroupIds"`
Message string `json:"message"`
Status int `json:"status"`
Sort int `json:"sort"`
}
type ImSecurityPolicyModel struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
PolicyVersion uint `json:"policyVersion"`
PolicyName string `json:"policyName"`
PublishStatus string `json:"publishStatus"`
PasswordPolicy string `json:"passwordPolicy"`
LoginRateLimit string `json:"loginRateLimit"`
SensitiveWordPolicy string `json:"sensitiveWordPolicy"`
FileReviewPolicy string `json:"fileReviewPolicy"`
RealnamePolicy string `json:"realnamePolicy"`
RiskChallengePolicy string `json:"riskChallengePolicy"`
GlobalFriendEnabled int `json:"globalFriendEnabled"`
FriendDisabledPolicy string `json:"friendDisabledPolicy"`
MessageCurfewEnabled int `json:"messageCurfewEnabled"`
Remark string `json:"remark"`
PublishedAt *gtime.Time `json:"publishedAt"`
PublishedBy int64 `json:"publishedBy"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
CurfewRules []*ImMessageCurfewRuleModel `json:"curfewRules"`
}
type ImSecurityPolicyViewInp struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
}
func (in *ImSecurityPolicyViewInp) Filter(ctx context.Context) error {
if in.Id <= 0 && in.TenantId < 0 {
return gerror.New("策略ID或租户ID不合法")
}
return nil
}
type ImMessageCurfewRuleInp struct {
RuleCode string `json:"ruleCode"`
RuleName string `json:"ruleName"`
DateType string `json:"dateType"`
CustomDates *gjson.Json `json:"customDates"`
Weekdays *gjson.Json `json:"weekdays"`
Timezone string `json:"timezone"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
Scope string `json:"scope"`
WhitelistUserIds *gjson.Json `json:"whitelistUserIds"`
WhitelistGroupIds *gjson.Json `json:"whitelistGroupIds"`
Message string `json:"message"`
Status int `json:"status"`
Sort int `json:"sort"`
}
func (in *ImMessageCurfewRuleInp) Filter(ctx context.Context) error {
in.RuleCode = strings.TrimSpace(in.RuleCode)
in.RuleName = strings.TrimSpace(in.RuleName)
in.Timezone = strings.TrimSpace(in.Timezone)
in.Message = strings.TrimSpace(in.Message)
if !imPolicyGroupCodePattern.MatchString(in.RuleCode) {
return gerror.New("宵禁规则编码格式不合法")
}
if in.RuleName == "" {
return gerror.New("宵禁规则名称不能为空")
}
if !dict.HasOptionKey(consts.ImCurfewDateTypeOptions, in.DateType) {
return gerror.New("宵禁日期类型不合法")
}
if !dict.HasOptionKey(consts.ImCurfewScopeOptions, in.Scope) {
return gerror.New("宵禁范围不合法")
}
if len(in.StartTime) != 5 || len(in.EndTime) != 5 {
return gerror.New("宵禁时间必须使用HH:mm格式")
}
if _, err := time.Parse("15:04", in.StartTime); err != nil {
return gerror.New("宵禁开始时间不合法")
}
if _, err := time.Parse("15:04", in.EndTime); err != nil {
return gerror.New("宵禁结束时间不合法")
}
if in.Timezone == "" {
in.Timezone = "Asia/Shanghai"
}
if in.Message == "" {
in.Message = "当前时段暂不能发送消息"
}
if in.Status == 0 {
in.Status = consts.StatusEnabled
}
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("宵禁规则状态不合法")
}
return nil
}
type ImSecurityPolicySaveInp struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
PolicyName string `json:"policyName" v:"required#策略名称不能为空"`
PasswordPolicy *gjson.Json `json:"passwordPolicy"`
LoginRateLimit *gjson.Json `json:"loginRateLimit"`
SensitiveWordPolicy *gjson.Json `json:"sensitiveWordPolicy"`
FileReviewPolicy *gjson.Json `json:"fileReviewPolicy"`
RealnamePolicy *gjson.Json `json:"realnamePolicy"`
RiskChallengePolicy *gjson.Json `json:"riskChallengePolicy"`
GlobalFriendEnabled int `json:"globalFriendEnabled"`
FriendDisabledPolicy *gjson.Json `json:"friendDisabledPolicy"`
MessageCurfewEnabled int `json:"messageCurfewEnabled"`
Remark string `json:"remark"`
CurfewRules []*ImMessageCurfewRuleInp `json:"curfewRules"`
}
func (in *ImSecurityPolicySaveInp) Filter(ctx context.Context) error {
in.PolicyName = strings.TrimSpace(in.PolicyName)
in.Remark = strings.TrimSpace(in.Remark)
if in.PolicyName == "" || len([]rune(in.PolicyName)) > 128 {
return gerror.New("策略名称长度必须为1至128个字符")
}
if in.GlobalFriendEnabled != 0 && in.GlobalFriendEnabled != 1 {
return gerror.New("全局好友开关不合法")
}
if in.MessageCurfewEnabled != 0 && in.MessageCurfewEnabled != 1 {
return gerror.New("宵禁开关不合法")
}
if in.GlobalFriendEnabled == 0 {
policy := make(map[string]interface{})
if in.FriendDisabledPolicy != nil {
policy = in.FriendDisabledPolicy.Map()
}
policy["hide_profile"] = true
policy["block_qrcode"] = true
policy["block_group_member_entry"] = true
policy["blocked_entries"] = []string{
"search", "profile_card", "group_member", "qrcode", "invite_auto_add", "recommendation",
}
message, ok := policy["message"].(string)
if !ok || strings.TrimSpace(message) == "" {
policy["message"] = "当前时段暂不允许添加好友"
}
in.FriendDisabledPolicy = gjson.New(policy)
}
if in.MessageCurfewEnabled == 1 && len(in.CurfewRules) == 0 {
return gerror.New("启用宵禁时至少配置一条宵禁规则")
}
seen := make(map[string]struct{}, len(in.CurfewRules))
hasPrivateMessageRule := false
for _, rule := range in.CurfewRules {
if rule == nil {
return gerror.New("宵禁规则不能为空")
}
if err := rule.Filter(ctx); err != nil {
return err
}
if _, ok := seen[rule.RuleCode]; ok {
return gerror.New("宵禁规则编码不能重复")
}
seen[rule.RuleCode] = struct{}{}
if rule.Scope == consts.ImCurfewScopePrivate && rule.Status == consts.StatusEnabled {
hasPrivateMessageRule = true
}
}
if in.MessageCurfewEnabled == 1 && !hasPrivateMessageRule {
return gerror.New("启用宵禁时至少配置一条启用的单聊/私聊规则")
}
return nil
}
type ImSecurityPolicyPublishInp struct {
Id int64 `json:"id" v:"required|min:1#策略ID不能为空|策略ID不合法"`
Reason string `json:"reason" v:"required#发布原因不能为空"`
}
func (in *ImSecurityPolicyPublishInp) Filter(ctx context.Context) error {
in.Reason = strings.TrimSpace(in.Reason)
if in.Id <= 0 {
return gerror.New("策略ID不合法")
}
if in.Reason == "" {
return gerror.New("发布原因不能为空")
}
if len([]rune(in.Reason)) > 500 {
return gerror.New("发布原因不能超过500个字符")
}
return nil
}
type ImSecurityPolicyRestoreInp struct {
TenantId int64 `json:"tenantId"`
Reason string `json:"reason" v:"required#恢复原因不能为空"`
}
func (in *ImSecurityPolicyRestoreInp) Filter(ctx context.Context) error {
in.Reason = strings.TrimSpace(in.Reason)
if in.TenantId < 0 {
return gerror.New("租户ID不合法")
}
if in.Reason == "" {
return gerror.New("恢复原因不能为空")
}
if len([]rune(in.Reason)) > 500 {
return gerror.New("恢复原因不能超过500个字符")
}
return nil
}
type ImSecurityIPRuleListInp struct {
form.PageReq
TenantId int64 `json:"tenantId"`
RuleType string `json:"ruleType"`
IpCidr string `json:"ipCidr"`
Status int `json:"status"`
}
func (in *ImSecurityIPRuleListInp) Filter(ctx context.Context) error {
in.IpCidr = strings.TrimSpace(in.IpCidr)
if in.RuleType != "" && in.RuleType != "whitelist" && in.RuleType != "blacklist" {
return gerror.New("IP规则类型不合法")
}
return nil
}
type ImSecurityIPRuleModel struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
RuleType string `json:"ruleType"`
IpCidr string `json:"ipCidr"`
Action string `json:"action"`
SceneJson string `json:"sceneJson"`
RiskLevel string `json:"riskLevel"`
Status int `json:"status"`
ExpiresAt *gtime.Time `json:"expiresAt"`
Remark string `json:"remark"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
type ImSecurityIPRuleEditInp struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
RuleType string `json:"ruleType"`
IpCidr string `json:"ipCidr"`
Action string `json:"action"`
SceneJson *gjson.Json `json:"sceneJson"`
RiskLevel string `json:"riskLevel"`
Status int `json:"status"`
ExpiresAt *gtime.Time `json:"expiresAt"`
Remark string `json:"remark"`
}
func (in *ImSecurityIPRuleEditInp) Filter(ctx context.Context) error {
in.IpCidr = strings.TrimSpace(in.IpCidr)
if in.RuleType != "whitelist" && in.RuleType != "blacklist" {
return gerror.New("IP规则类型不合法")
}
if in.IpCidr == "" {
return gerror.New("IP或CIDR不能为空")
}
if !dict.HasOptionKey(consts.ImSecurityActionOptions, in.Action) {
return gerror.New("规则动作不合法")
}
if !dict.HasOptionKey(consts.ImRiskLevelOptions, in.RiskLevel) {
return gerror.New("风险等级不合法")
}
if in.Status == 0 {
in.Status = consts.StatusEnabled
}
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
if in.SceneJson == nil || in.SceneJson.String() == "" || in.SceneJson.String() == "null" {
return gerror.New("生效场景不能为空")
}
return nil
}
type ImSecurityIPRuleDeleteInp struct {
Id int64 `json:"id" v:"required|min:1#IP规则ID不能为空|IP规则ID不合法"`
}
func (in *ImSecurityIPRuleDeleteInp) Filter(ctx context.Context) error { return nil }
@@ -0,0 +1,190 @@
package sysin
import (
"context"
"strings"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImSensitiveWordListInp struct {
form.PageReq
TenantId int64 `json:"tenantId"`
GroupId int64 `json:"groupId"`
Word string `json:"word"`
MatchType string `json:"matchType"`
Action string `json:"action"`
RiskLevel string `json:"riskLevel"`
Status int `json:"status"`
}
func (in *ImSensitiveWordListInp) Filter(ctx context.Context) error {
in.Word = strings.TrimSpace(in.Word)
if in.MatchType != "" && !dict.HasOptionKey(consts.ImSensitiveMatchTypeOptions, in.MatchType) {
return gerror.New("匹配类型不合法")
}
if in.Action != "" && !dict.HasOptionKey(consts.ImSensitiveActionOptions, in.Action) {
return gerror.New("命中动作不合法")
}
if in.RiskLevel != "" && !dict.HasOptionKey(consts.ImRiskLevelOptions, in.RiskLevel) {
return gerror.New("风险等级不合法")
}
return nil
}
type ImSensitiveWordListModel struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
GroupId int64 `json:"groupId"`
GroupName string `json:"groupName"`
Word string `json:"word"`
MatchType string `json:"matchType"`
SceneJson string `json:"sceneJson"`
Action string `json:"action"`
Replacement string `json:"replacement"`
RiskLevel string `json:"riskLevel"`
Priority int `json:"priority"`
Status int `json:"status"`
Remark string `json:"remark"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
type ImSensitiveWordEditInp struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
GroupId int64 `json:"groupId" v:"required|min:1#敏感词分组不能为空|敏感词分组不合法"`
Word string `json:"word" v:"required#敏感词不能为空"`
MatchType string `json:"matchType"`
SceneJson *gjson.Json `json:"sceneJson" v:"required#生效场景不能为空"`
Action string `json:"action"`
Replacement string `json:"replacement"`
RiskLevel string `json:"riskLevel"`
Priority int `json:"priority"`
Status int `json:"status"`
Remark string `json:"remark"`
}
func (in *ImSensitiveWordEditInp) Filter(ctx context.Context) error {
in.Word = strings.TrimSpace(in.Word)
in.Replacement = strings.TrimSpace(in.Replacement)
in.Remark = strings.TrimSpace(in.Remark)
if in.Word == "" || len([]rune(in.Word)) > 255 {
return gerror.New("敏感词长度必须为1至255个字符")
}
if !dict.HasOptionKey(consts.ImSensitiveMatchTypeOptions, in.MatchType) {
return gerror.New("匹配类型不合法")
}
if !dict.HasOptionKey(consts.ImSensitiveActionOptions, in.Action) {
return gerror.New("命中动作不合法")
}
if !dict.HasOptionKey(consts.ImRiskLevelOptions, in.RiskLevel) {
return gerror.New("风险等级不合法")
}
if in.Status == 0 {
in.Status = consts.StatusEnabled
}
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
if in.SceneJson == nil || in.SceneJson.String() == "" || in.SceneJson.String() == "null" {
return gerror.New("生效场景不能为空")
}
if in.Action == consts.ImSensitiveActionReplace && in.Replacement == "" {
return gerror.New("替换动作必须填写替换文本")
}
return nil
}
type ImSensitiveWordDeleteInp struct {
Id int64 `json:"id" v:"required|min:1#敏感词ID不能为空|敏感词ID不合法"`
}
func (in *ImSensitiveWordDeleteInp) Filter(ctx context.Context) error { return nil }
type ImSensitiveWordStatusInp struct {
Id int64 `json:"id" v:"required|min:1#敏感词ID不能为空|敏感词ID不合法"`
Status int `json:"status" v:"required#状态不能为空"`
}
func (in *ImSensitiveWordStatusInp) Filter(ctx context.Context) error {
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
return nil
}
type ImSensitiveWordBatchImportInp struct {
TenantId int64 `json:"tenantId"`
GroupId int64 `json:"groupId" v:"required|min:1#敏感词分组不能为空|敏感词分组不合法"`
Words []string `json:"words" v:"required#敏感词列表不能为空"`
MatchType string `json:"matchType"`
SceneJson *gjson.Json `json:"sceneJson"`
Action string `json:"action"`
Replacement string `json:"replacement"`
RiskLevel string `json:"riskLevel"`
}
func (in *ImSensitiveWordBatchImportInp) Filter(ctx context.Context) error {
if len(in.Words) == 0 || len(in.Words) > 5000 {
return gerror.New("每次导入1至5000个敏感词")
}
base := ImSensitiveWordEditInp{GroupId: in.GroupId, Word: "validate", MatchType: in.MatchType, SceneJson: in.SceneJson, Action: in.Action, Replacement: in.Replacement, RiskLevel: in.RiskLevel, Status: consts.StatusEnabled}
return base.Filter(ctx)
}
type ImSensitiveWordBatchImportModel struct {
Inserted int `json:"inserted"`
Skipped int `json:"skipped"`
}
type ImSensitiveWordGroupListInp struct {
TenantId int64 `json:"tenantId"`
}
func (in *ImSensitiveWordGroupListInp) Filter(ctx context.Context) error { return nil }
type ImSensitiveWordGroupModel struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
GroupCode string `json:"groupCode"`
GroupName string `json:"groupName"`
Priority int `json:"priority"`
Status int `json:"status"`
Remark string `json:"remark"`
CreatedAt *gtime.Time `json:"createdAt"`
}
type ImSensitiveWordGroupEditInp struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
GroupCode string `json:"groupCode" v:"required#分组编码不能为空"`
GroupName string `json:"groupName" v:"required#分组名称不能为空"`
Priority int `json:"priority"`
Status int `json:"status"`
Remark string `json:"remark"`
}
func (in *ImSensitiveWordGroupEditInp) Filter(ctx context.Context) error {
in.GroupCode = strings.TrimSpace(in.GroupCode)
in.GroupName = strings.TrimSpace(in.GroupName)
in.Remark = strings.TrimSpace(in.Remark)
if !imPolicyGroupCodePattern.MatchString(in.GroupCode) {
return gerror.New("分组编码格式不合法")
}
if in.GroupName == "" || len([]rune(in.GroupName)) > 128 {
return gerror.New("分组名称长度必须为1至128个字符")
}
if in.Status == 0 {
in.Status = consts.StatusEnabled
}
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
return nil
}
@@ -0,0 +1,35 @@
package sysin
import (
"context"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/os/gtime"
)
type ImTaskDashboardListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Status string `json:"status" dc:"状态"`
}
func (in *ImTaskDashboardListInp) Filter(ctx context.Context) (err error) { return }
type ImTaskDashboardListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
TenantCode string `json:"tenantCode" dc:"租户编码"`
TenantState int `json:"tenantState" dc:"租户状态"`
DbStatus string `json:"dbStatus" dc:"数据库状态"`
Status string `json:"status" dc:"看板状态"`
OutboxPendingCount int `json:"outboxPendingCount" dc:"业务Outbox待处理"`
OutboxFailedCount int `json:"outboxFailedCount" dc:"业务Outbox失败"`
GroupOutboxPending int `json:"groupOutboxPending" dc:"群Outbox待处理"`
GroupOutboxFailed int `json:"groupOutboxFailed" dc:"群Outbox失败"`
OrphanFileCount int `json:"orphanFileCount" dc:"孤儿文件数"`
DeletedFileCount int `json:"deletedFileCount" dc:"待物理清理文件数"`
LastOutboxCreatedAt *gtime.Time `json:"lastOutboxCreatedAt" dc:"最新Outbox时间"`
LastGroupEventAt *gtime.Time `json:"lastGroupEventAt" dc:"最新群事件时间"`
LastFileUpdatedAt *gtime.Time `json:"lastFileUpdatedAt" dc:"最新文件更新时间"`
RecommendedNextAction string `json:"recommendedNextAction" dc:"建议动作"`
}
@@ -155,3 +155,67 @@ func (in *ImTenantStatusInp) Filter(ctx context.Context) (err error) {
}
type ImTenantStatusModel struct{}
type ImTenantUsageInp struct {
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImTenantUsageInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
type ImTenantUsageModel struct {
Id int64 `json:"id" dc:"租户ID"`
Code string `json:"code" dc:"租户编码"`
Name string `json:"name" dc:"租户名称"`
DbName string `json:"dbName" dc:"业务库名"`
DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"`
DbReachable bool `json:"dbReachable" dc:"业务库是否可达"`
DbCheckError string `json:"dbCheckError" dc:"业务库检查错误"`
SchemaVersion string `json:"schemaVersion" dc:"Schema版本"`
UserCount int64 `json:"userCount" dc:"用户数"`
ActiveUserCount int64 `json:"activeUserCount" dc:"正常用户数"`
GroupCount int64 `json:"groupCount" dc:"群数"`
DeviceCount int64 `json:"deviceCount" dc:"设备数"`
PushTokenCount int64 `json:"pushTokenCount" dc:"Push Token数"`
InviteUseCount int64 `json:"inviteUseCount" dc:"邀请码使用次数"`
OutboxPendingCount int64 `json:"outboxPendingCount" dc:"待处理Outbox数"`
Warnings []string `json:"warnings" dc:"警告信息"`
}
type ImTenantDeletionCheckInp struct {
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImTenantDeletionCheckInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
type ImTenantDeletionCheckModel struct {
Id int64 `json:"id" dc:"租户ID"`
Code string `json:"code" dc:"租户编码"`
Name string `json:"name" dc:"租户名称"`
CanEnterDeleting bool `json:"canEnterDeleting" dc:"是否可进入删除流程"`
Blockers []string `json:"blockers" dc:"阻断原因"`
Warnings []string `json:"warnings" dc:"警告信息"`
UserCount int64 `json:"userCount" dc:"用户数"`
GroupCount int64 `json:"groupCount" dc:"群数"`
PendingOutbox int64 `json:"pendingOutbox" dc:"待处理Outbox"`
}
type ImTenantLifecycleInp struct {
Id int64 `json:"id" v:"required#ID不能为空" dc:"ID"`
}
func (in *ImTenantLifecycleInp) Filter(ctx context.Context) (err error) {
if in.Id <= 0 {
return gerror.New("ID不能为空")
}
return
}
+200 -21
View File
@@ -8,6 +8,7 @@ package sysin
import (
"context"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
@@ -17,7 +18,9 @@ import (
type ImUserListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" dc:"用户ID"`
UserId string `json:"userId" dc:"后台展示编号"`
GroupId int64 `json:"groupId" dc:"用户策略分组ID"`
Keyword string `json:"keyword" dc:"用户名、昵称、手机号或编号"`
Nickname string `json:"nickname" dc:"昵称"`
AuthName string `json:"authName" dc:"登录标识"`
State *int `json:"state" dc:"账号状态"`
@@ -26,8 +29,11 @@ type ImUserListInp struct {
}
func (in *ImUserListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
in.Keyword = strings.TrimSpace(in.Keyword)
if in.UserId != "" {
if _, err = ParseOptionalInt64(in.UserId); err != nil {
return err
}
}
return
}
@@ -35,7 +41,10 @@ func (in *ImUserListInp) Filter(ctx context.Context) (err error) {
type ImUserListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
UserId int64 `json:"userId" dc:"用户ID"`
DisplayNo int `json:"displayNo" dc:"后台展示编号"`
UserId string `json:"userId" dc:"用户ID"`
Username string `json:"username" dc:"登录账号"`
Phone string `json:"phone" dc:"手机号"`
Nickname string `json:"nickname" dc:"昵称"`
PublicAccount string `json:"publicAccount" dc:"公开账号"`
Gender string `json:"gender" dc:"性别"`
@@ -49,24 +58,140 @@ type ImUserListModel struct {
UserAgent string `json:"userAgent" dc:"客户端"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
AvatarFileId string `json:"avatarFileId" dc:"头像文件ID"`
Avatar string `json:"avatar" dc:"头像地址"`
PolicyGroupId int64 `json:"policyGroupId" dc:"用户策略分组ID"`
PolicyGroupName string `json:"policyGroupName" dc:"用户策略分组名称"`
RegisterType string `json:"registerType" dc:"注册方式"`
}
type ImUserCreateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Username string `json:"username" dc:"用户名"`
Nickname string `json:"nickname" v:"required#昵称不能为空" dc:"昵称"`
Phone string `json:"phone" dc:"手机号"`
Password string `json:"password" v:"required#密码不能为空" dc:"密码"`
GroupId int64 `json:"groupId" dc:"用户策略分组ID"`
}
func (in *ImUserCreateInp) Filter(ctx context.Context) error {
in.Username = strings.ToLower(strings.TrimSpace(in.Username))
in.Nickname = strings.TrimSpace(in.Nickname)
in.Phone = strings.TrimSpace(in.Phone)
if in.Nickname == "" {
return gerror.New("昵称不能为空")
}
if len([]rune(in.Password)) < 3 || len([]byte(in.Password)) > 72 {
return gerror.New("密码长度需为3至72字节")
}
return nil
}
type ImUserCreateModel struct {
DisplayNo int `json:"displayNo" dc:"后台展示编号"`
UserId int64 `json:"userId" dc:"用户ID"`
Username string `json:"username" dc:"用户名"`
Nickname string `json:"nickname" dc:"昵称"`
Phone string `json:"phone" dc:"手机号"`
}
type ImUserBatchCreateInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Count int `json:"count" v:"required|min:1|max:100#创建数量不能为空|创建数量最少为1|单次最多创建100个用户" dc:"创建数量"`
Password string `json:"password" v:"required#默认密码不能为空" dc:"默认密码"`
NicknamePrefix string `json:"nicknamePrefix" dc:"昵称前缀"`
GroupId int64 `json:"groupId" dc:"用户策略分组ID"`
}
func (in *ImUserBatchCreateInp) Filter(ctx context.Context) error {
in.NicknamePrefix = strings.TrimSpace(in.NicknamePrefix)
if in.Count < 1 || in.Count > 100 {
return gerror.New("单次创建数量需为1至100")
}
if len([]rune(in.Password)) < 3 || len([]byte(in.Password)) > 72 {
return gerror.New("密码长度需为3至72字节")
}
return nil
}
type ImUserBatchItemModel struct {
Row int `json:"row" dc:"行号"`
DisplayNo int `json:"displayNo" dc:"后台展示编号"`
Username string `json:"username" dc:"用户名"`
UserId int64 `json:"userId" dc:"用户ID"`
Success bool `json:"success" dc:"是否成功"`
Reason string `json:"reason" dc:"失败原因"`
}
type ImUserBatchCreateModel struct {
SuccessCount int `json:"successCount" dc:"成功数量"`
FailedCount int `json:"failedCount" dc:"失败数量"`
Items []*ImUserBatchItemModel `json:"items" dc:"处理明细"`
}
type ImUserImportInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
GroupId int64 `json:"groupId" dc:"默认用户策略分组ID"`
Rows []*ImUserCreateInp `json:"rows" dc:"导入用户"`
}
// ImUserViewInp 获取指定IM用户信息
type ImUserViewInp struct {
TenantId int64 `json:"tenantId" v:"required#租户ID不能为空" dc:"租户ID"`
UserId int64 `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
func (in *ImUserViewInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.UserId <= 0 {
if _, err = ParseOptionalInt64(in.UserId); err != nil {
return gerror.New("用户ID不能为空")
}
return
}
type ImUserPasswordResetInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
NewPassword string `json:"newPassword" v:"required#新密码不能为空" dc:"新密码"`
}
func (in *ImUserPasswordResetInp) Filter(ctx context.Context) error {
if _, err := ParseOptionalInt64(in.UserId); err != nil {
return err
}
if len([]rune(in.NewPassword)) < 3 {
return gerror.New("新密码长度不能少于3位")
}
if len([]byte(in.NewPassword)) > 72 {
return gerror.New("新密码长度不能超过72字节")
}
return nil
}
type ImUserRevokeSessionsInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
func (in *ImUserRevokeSessionsInp) Filter(ctx context.Context) error {
if _, err := ParseOptionalInt64(in.UserId); err != nil {
return err
}
return nil
}
type ImUserStatusInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
State int `json:"state" dc:"账号状态"`
}
func (in *ImUserStatusInp) Filter(ctx context.Context) error {
if _, err := ParseOptionalInt64(in.UserId); err != nil {
return err
}
return nil
}
type ImUserViewModel struct {
ImUserListModel
Signature string `json:"signature" dc:"个性签名"`
@@ -74,19 +199,36 @@ type ImUserViewModel struct {
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:"资料版本"`
}
type ImUserCommandModel struct {
CommandID string `json:"commandId" dc:"命令ID"`
EventID string `json:"eventId" dc:"事件ID"`
Status string `json:"status" dc:"状态"`
TenantID int64 `json:"tenantId" dc:"租户ID"`
TenantCode string `json:"tenantCode" dc:"租户编码"`
EventType string `json:"eventType" dc:"事件类型"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
type ImUserDeviceListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId string `json:"userId" v:"required#用户ID不能为空" dc:"用户ID"`
}
func (in *ImUserDeviceListInp) Filter(ctx context.Context) (err error) {
if _, err = ParseOptionalInt64(in.UserId); err != nil {
return gerror.New("用户ID不能为空")
}
return
}
type ImUserDeviceListModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
UserId int64 `json:"userId" dc:"用户ID"`
DeviceId string `json:"deviceId" dc:"设备ID"`
Platform string `json:"platform" dc:"平台"`
Lang string `json:"lang" dc:"语言"`
LastSeen *gtime.Time `json:"lastSeen" dc:"最后活跃时间"`
PushProvider string `json:"pushProvider" dc:"Push服务商"`
PushPlatform string `json:"pushPlatform" dc:"Push平台"`
PushAppVersion string `json:"pushAppVersion" dc:"App版本"`
PushTokenConfigured bool `json:"pushTokenConfigured" dc:"是否配置Push Token"`
PushUpdatedAt *gtime.Time `json:"pushUpdatedAt" dc:"Push更新时间"`
}
type ImUserGovernanceInp struct {
@@ -105,12 +247,39 @@ func (in *ImUserGovernanceInp) Filter(ctx context.Context) (err error) {
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if in.ActorUserId <= 0 {
return gerror.New("操作IM用户ID不能为空")
return nil
}
type ImUserAdjustGroupInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" v:"required|min:1#用户ID不能为空|用户ID不合法" dc:"用户ID"`
GroupId int64 `json:"groupId" v:"required|min:1#用户分组不能为空|用户分组不合法" dc:"用户策略分组ID"`
}
func (in *ImUserAdjustGroupInp) Filter(ctx context.Context) error {
if in.UserId <= 0 || in.GroupId <= 0 {
return gerror.New("用户和用户分组不能为空")
}
return nil
}
type ImUserIPWhitelistInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" v:"required|min:1#用户ID不能为空|用户ID不合法" dc:"用户ID"`
IPList []string `json:"ipList" dc:"IP或CIDR列表"`
}
func (in *ImUserIPWhitelistInp) Filter(ctx context.Context) error {
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
return nil
}
type ImUserIPWhitelistModel struct {
IPList []string `json:"ipList" dc:"IP或CIDR列表"`
}
func (in *ImUserGovernanceInp) FilterDevice(ctx context.Context) (err error) {
if err = in.Filter(ctx); err != nil {
return err
@@ -120,3 +289,13 @@ func (in *ImUserGovernanceInp) FilterDevice(ctx context.Context) (err error) {
}
return nil
}
type ImUserCommandModel struct {
CommandID string `json:"commandId" dc:"命令ID"`
EventID string `json:"eventId" dc:"Kafka事件ID"`
Status string `json:"status" dc:"状态"`
TenantID int64 `json:"tenantId" dc:"租户ID"`
TenantCode string `json:"tenantCode" dc:"租户编码"`
EventType string `json:"eventType" dc:"事件类型"`
IdempotencyKey string `json:"idempotencyKey" dc:"幂等键"`
}
@@ -0,0 +1,221 @@
package sysin
import (
"context"
"net/netip"
"regexp"
"strings"
"hotgo/internal/consts"
"hotgo/internal/model/input/form"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
var imPolicyGroupCodePattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_-]{1,63}$`)
type ImUserPolicyGroupListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID,0为全局默认"`
Keyword string `json:"keyword" dc:"编码或名称"`
Status int `json:"status" dc:"状态"`
}
func (in *ImUserPolicyGroupListInp) Filter(ctx context.Context) error {
in.Keyword = strings.TrimSpace(in.Keyword)
if in.Status != 0 && in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
return nil
}
type ImUserPolicyGroupListModel struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId"`
GroupCode string `json:"groupCode"`
GroupName string `json:"groupName"`
Priority int `json:"priority"`
Status int `json:"status"`
PolicyJson string `json:"policyJson"`
IpWhitelist string `json:"ipWhitelist"`
IpBlacklist string `json:"ipBlacklist"`
Version uint `json:"version"`
Remark string `json:"remark"`
MemberCount int `json:"memberCount"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
type ImUserPolicyGroupViewInp struct {
Id int64 `json:"id" v:"required|min:1#策略分组ID不能为空|策略分组ID不合法"`
}
func (in *ImUserPolicyGroupViewInp) Filter(ctx context.Context) error { return nil }
type ImUserPolicyGroupViewModel struct{ ImUserPolicyGroupListModel }
type ImUserPolicyGroupEditInp struct {
Id int64 `json:"id"`
TenantId int64 `json:"tenantId" dc:"租户ID,0为全局默认"`
GroupCode string `json:"groupCode" v:"required#分组编码不能为空"`
GroupName string `json:"groupName" v:"required#分组名称不能为空"`
Priority int `json:"priority"`
Status int `json:"status"`
PolicyJson *gjson.Json `json:"policyJson" v:"required#策略配置不能为空"`
IpWhitelist *gjson.Json `json:"ipWhitelist"`
IpBlacklist *gjson.Json `json:"ipBlacklist"`
Remark string `json:"remark"`
}
func (in *ImUserPolicyGroupEditInp) Filter(ctx context.Context) error {
in.GroupCode = strings.TrimSpace(in.GroupCode)
in.GroupName = strings.TrimSpace(in.GroupName)
in.Remark = strings.TrimSpace(in.Remark)
if !imPolicyGroupCodePattern.MatchString(in.GroupCode) {
return gerror.New("分组编码必须以字母开头,只能包含字母、数字、下划线和中划线")
}
if in.GroupName == "" || len([]rune(in.GroupName)) > 128 {
return gerror.New("分组名称长度必须为1至128个字符")
}
if in.Status == 0 {
in.Status = consts.StatusEnabled
}
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
if in.PolicyJson == nil || in.PolicyJson.String() == "null" {
return gerror.New("策略配置不能为空")
}
return nil
}
type ImUserPolicyGroupDeleteInp struct {
Id int64 `json:"id" v:"required|min:1#策略分组ID不能为空|策略分组ID不合法"`
}
func (in *ImUserPolicyGroupDeleteInp) Filter(ctx context.Context) error { return nil }
type ImUserPolicyGroupStatusInp struct {
Id int64 `json:"id" v:"required|min:1#策略分组ID不能为空|策略分组ID不合法"`
Status int `json:"status" v:"required#状态不能为空"`
}
func (in *ImUserPolicyGroupStatusInp) Filter(ctx context.Context) error {
if in.Status != consts.StatusEnabled && in.Status != consts.StatusDisable {
return gerror.New("状态不合法")
}
return nil
}
type ImUserPolicyGroupMemberListInp struct {
form.PageReq
GroupId int64 `json:"groupId" v:"required|min:1#策略分组ID不能为空|策略分组ID不合法"`
UserId int64 `json:"userId"`
}
func (in *ImUserPolicyGroupMemberListInp) Filter(ctx context.Context) error { return nil }
type ImUserPolicyGroupMemberModel struct {
Id int64 `json:"id"`
GroupId int64 `json:"groupId"`
TenantId int64 `json:"tenantId"`
UserId int64 `json:"userId"`
Status int `json:"status"`
ValidFrom *gtime.Time `json:"validFrom"`
ValidUntil *gtime.Time `json:"validUntil"`
Remark string `json:"remark"`
CreatedAt *gtime.Time `json:"createdAt"`
}
type ImUserPolicyGroupMemberAddInp struct {
GroupId int64 `json:"groupId" v:"required|min:1#策略分组ID不能为空|策略分组ID不合法"`
UserId int64 `json:"userId" v:"required|min:1#用户ID不能为空|用户ID不合法"`
ValidFrom *gtime.Time `json:"validFrom"`
ValidUntil *gtime.Time `json:"validUntil"`
Remark string `json:"remark"`
}
func (in *ImUserPolicyGroupMemberAddInp) Filter(ctx context.Context) error {
in.Remark = strings.TrimSpace(in.Remark)
if in.GroupId <= 0 {
return gerror.New("策略分组ID不合法")
}
if in.UserId <= 0 {
return gerror.New("用户ID不合法")
}
if in.ValidFrom != nil && in.ValidUntil != nil && in.ValidUntil.Before(in.ValidFrom) {
return gerror.New("有效结束时间不能早于开始时间")
}
return nil
}
type ImUserPolicyGroupMemberRemoveInp struct {
Id int64 `json:"id" v:"required|min:1#成员关系ID不能为空|成员关系ID不合法"`
}
func (in *ImUserPolicyGroupMemberRemoveInp) Filter(ctx context.Context) error { return nil }
type ImTenantLoginIPWhitelistInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
}
func (in *ImTenantLoginIPWhitelistInp) Filter(ctx context.Context) error {
if in.TenantId < 0 {
return gerror.New("租户ID不合法")
}
return nil
}
type ImTenantLoginIPWhitelistSaveInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
AppList []string `json:"appList" dc:"APP登录IP白名单"`
WebList []string `json:"webList" dc:"Web登录IP白名单"`
}
func (in *ImTenantLoginIPWhitelistSaveInp) Filter(ctx context.Context) (err error) {
if in.TenantId < 0 {
return gerror.New("租户ID不合法")
}
if in.AppList, err = normalizeTenantLoginIPList(in.AppList); err != nil {
return gerror.Wrap(err, "APP登录白名单不合法")
}
if in.WebList, err = normalizeTenantLoginIPList(in.WebList); err != nil {
return gerror.Wrap(err, "Web登录白名单不合法")
}
return nil
}
type ImTenantLoginIPWhitelistModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
AppList []string `json:"appList" dc:"APP登录IP白名单"`
WebList []string `json:"webList" dc:"Web登录IP白名单"`
}
func normalizeTenantLoginIPList(values []string) ([]string, error) {
if len(values) > 500 {
return nil, gerror.New("单个渠道最多配置500条IP或CIDR")
}
result := make([]string, 0, len(values))
seen := make(map[string]struct{}, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if prefix, err := netip.ParsePrefix(value); err == nil {
value = prefix.Masked().String()
} else if address, addressErr := netip.ParseAddr(value); addressErr == nil {
value = address.String()
} else {
return nil, gerror.Newf("%s 不是有效的IPv4、IPv6或CIDR", value)
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result, nil
}
@@ -0,0 +1,27 @@
package sysin
import (
"testing"
"github.com/gogf/gf/v2/os/gctx"
)
func TestImUserGovernanceAllowsAdminActor(t *testing.T) {
in := &ImUserGovernanceInp{TenantId: 1, UserId: 2}
if err := in.Filter(gctx.New()); err != nil {
t.Fatalf("admin governance should not require an IM actor: %v", err)
}
}
func TestImUserActionInputs(t *testing.T) {
ctx := gctx.New()
if err := (&ImUserAdjustGroupInp{UserId: 2, GroupId: 3}).Filter(ctx); err != nil {
t.Fatalf("valid group adjustment rejected: %v", err)
}
if err := (&ImUserAdjustGroupInp{UserId: 2}).Filter(ctx); err == nil {
t.Fatal("missing group must be rejected")
}
if err := (&ImUserIPWhitelistInp{UserId: 2}).Filter(ctx); err != nil {
t.Fatalf("valid whitelist input rejected: %v", err)
}
}
@@ -32,9 +32,6 @@ type ImWorkOrderListInp struct {
}
func (in *ImWorkOrderListInp) Filter(ctx context.Context) (err error) {
if in.TenantId <= 0 {
return gerror.New("请选择租户")
}
if in.Type == "" {
return gerror.New("请选择工单类型")
}
@@ -68,9 +65,6 @@ type ImWorkOrderViewInp struct {
}
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("工单类型不正确")
}
@@ -100,9 +94,6 @@ type ImWorkOrderStatusInp struct {
}
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("工单类型不正确")
}
@@ -0,0 +1,141 @@
package sysin
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type ImWorkbenchAppListInp struct {
form.PageReq
TenantId int64 `json:"tenantId" dc:"租户ID"`
Code string `json:"code" dc:"模块编码"`
Name string `json:"name" dc:"模块名称"`
Enabled *int `json:"enabled" dc:"启用状态"`
}
func (in *ImWorkbenchAppListInp) Filter(ctx context.Context) (err error) {
in.Code = strings.TrimSpace(in.Code)
in.Name = strings.TrimSpace(in.Name)
if in.Enabled != nil && !dict.HasOptionKey(consts.ImWorkbenchAppEnabledOptions, *in.Enabled) {
return gerror.New("启用状态不合法")
}
return nil
}
type ImWorkbenchAppModel struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
TenantName string `json:"tenantName" dc:"租户名称"`
Id int64 `json:"id" dc:"ID"`
Code string `json:"code" dc:"模块编码"`
Name string `json:"name" dc:"模块名称"`
IconFileId string `json:"iconFileId" dc:"图标文件ID"`
Route string `json:"route" dc:"客户端路由"`
SortOrder int `json:"sortOrder" dc:"排序"`
UnreadKey string `json:"unreadKey" dc:"未读角标Key"`
Enabled int `json:"enabled" dc:"启用状态"`
Permission string `json:"permission" dc:"权限策略"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
type ImWorkbenchAppEditInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" dc:"ID"`
Code string `json:"code" v:"required#模块编码不能为空" dc:"模块编码"`
Name string `json:"name" v:"required#模块名称不能为空" dc:"模块名称"`
IconFileId string `json:"iconFileId" dc:"图标文件ID"`
Route string `json:"route" v:"required#客户端路由不能为空" dc:"客户端路由"`
SortOrder int `json:"sortOrder" dc:"排序"`
UnreadKey string `json:"unreadKey" dc:"未读角标Key"`
Enabled int `json:"enabled" dc:"启用状态"`
Permission string `json:"permission" dc:"权限策略"`
}
func (in *ImWorkbenchAppEditInp) Filter(ctx context.Context) (err error) {
in.Code = strings.TrimSpace(in.Code)
in.Name = strings.TrimSpace(in.Name)
in.IconFileId = strings.TrimSpace(in.IconFileId)
in.Route = strings.TrimSpace(in.Route)
in.UnreadKey = strings.TrimSpace(in.UnreadKey)
in.Permission = strings.TrimSpace(in.Permission)
if in.Code == "" {
return gerror.New("模块编码不能为空")
}
if strings.ContainsAny(in.Code, " \t\r\n/") {
return gerror.New("模块编码不能包含空格或斜杠")
}
if len([]rune(in.Code)) > 64 {
return gerror.New("模块编码不能超过64个字符")
}
if in.Name == "" {
return gerror.New("模块名称不能为空")
}
if len([]rune(in.Name)) > 128 {
return gerror.New("模块名称不能超过128个字符")
}
if !dict.HasOptionKey(consts.ImWorkbenchRouteOptions, in.Route) {
return gerror.New("客户端路由不在当前IM服务端白名单内")
}
if !dict.HasOptionKey(consts.ImWorkbenchAppEnabledOptions, in.Enabled) {
return gerror.New("启用状态不合法")
}
if in.Permission == "" {
in.Permission = consts.ImWorkbenchPermissionEnabled
}
if !dict.HasOptionKey(consts.ImWorkbenchPermissionOptions, in.Permission) {
return gerror.New("权限策略不合法")
}
if len([]rune(in.IconFileId)) > 64 {
return gerror.New("图标文件ID不能超过64个字符")
}
if len([]rune(in.UnreadKey)) > 64 {
return gerror.New("未读角标Key不能超过64个字符")
}
return nil
}
type ImWorkbenchAppDeleteInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Id int64 `json:"id" dc:"ID"`
Code string `json:"code" dc:"模块编码"`
}
func (in *ImWorkbenchAppDeleteInp) Filter(ctx context.Context) (err error) {
in.Code = strings.TrimSpace(in.Code)
if in.Id <= 0 && in.Code == "" {
return gerror.New("请选择要禁用的工作台模块")
}
return nil
}
type ImWorkbenchAppReorderItem struct {
Code string `json:"code" dc:"模块编码"`
SortOrder int `json:"sortOrder" dc:"排序"`
}
type ImWorkbenchAppReorderInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
Items []*ImWorkbenchAppReorderItem `json:"items" dc:"排序列表"`
}
func (in *ImWorkbenchAppReorderInp) Filter(ctx context.Context) (err error) {
if len(in.Items) == 0 {
return gerror.New("排序列表不能为空")
}
for _, item := range in.Items {
if item == nil {
return gerror.New("排序项不能为空")
}
item.Code = strings.TrimSpace(item.Code)
if item.Code == "" {
return gerror.New("模块编码不能为空")
}
}
return nil
}