package sys import ( "context" "net" "hotgo/internal/consts" "hotgo/internal/dao" "hotgo/internal/library/contexts" "hotgo/internal/library/imtenant" "hotgo/internal/model/input/sysin" "hotgo/internal/service" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) type sSysImSecurityPolicy struct{} func NewSysImSecurityPolicy() *sSysImSecurityPolicy { return &sSysImSecurityPolicy{} } func init() { service.RegisterSysImSecurityPolicy(NewSysImSecurityPolicy()) } func securityPolicyFields(alias string) []any { c := dao.ImSecurityPolicy.Columns() p := alias if p != "" { p += "." } return []any{p + c.Id, p + c.TenantId, p + c.PolicyVersion, p + c.PolicyName, p + c.PublishStatus, "CAST(" + p + c.PasswordPolicy + " AS CHAR) password_policy", "CAST(" + p + c.LoginRateLimit + " AS CHAR) login_rate_limit", "CAST(" + p + c.SensitiveWordPolicy + " AS CHAR) sensitive_word_policy", "CAST(" + p + c.FileReviewPolicy + " AS CHAR) file_review_policy", "CAST(" + p + c.RealnamePolicy + " AS CHAR) realname_policy", "CAST(" + p + c.RiskChallengePolicy + " AS CHAR) risk_challenge_policy", p + c.GlobalFriendEnabled, "CAST(" + p + c.FriendDisabledPolicy + " AS CHAR) friend_disabled_policy", p + c.MessageCurfewEnabled, p + c.Remark, p + c.PublishedAt, p + c.PublishedBy, p + c.CreatedAt, p + c.UpdatedAt} } func (s *sSysImSecurityPolicy) List(ctx context.Context, in *sysin.ImSecurityPolicyListInp) (list []*sysin.ImSecurityPolicyModel, total int, err error) { if err = in.Filter(ctx); err != nil { return } tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return nil, 0, err } c := dao.ImSecurityPolicy.Columns() model := imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Fields(securityPolicyFields("")...).Where(c.TenantId, tenantID).WhereNull(c.DeletedAt) if in.PublishStatus != "" { model = model.Where(c.PublishStatus, in.PublishStatus) } err = model.Page(in.Page, in.PerPage).OrderDesc(c.PolicyVersion).ScanAndCount(&list, &total, false) return } func (s *sSysImSecurityPolicy) View(ctx context.Context, in *sysin.ImSecurityPolicyViewInp) (res *sysin.ImSecurityPolicyModel, err error) { if err = in.Filter(ctx); err != nil { return } c := dao.ImSecurityPolicy.Columns() baseModel := imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Fields(securityPolicyFields("")...).WhereNull(c.DeletedAt) if in.Id > 0 { err = baseModel.Where(c.Id, in.Id).Scan(&res) } else { tenantID, _, resolveErr := resolveImPolicyTenant(ctx, in.TenantId) if resolveErr != nil { return nil, resolveErr } err = baseModel.Where(c.TenantId, tenantID). Where(c.PublishStatus, consts.ImSecurityPublishPublished). OrderDesc(c.PolicyVersion).Scan(&res) if err != nil { return nil, gerror.Wrap(err, "获取安全策略失败") } if res == nil { // 无自建策略时回落默认模板展示,保存后成为本商户自己的策略 tpl, tplErr := loadPolicyTemplateRow(imtenant.RegistryDB().Model(imPolicyTemplateTable).Safe().Ctx(ctx)) if tplErr != nil { return nil, gerror.Wrap(tplErr, "读取安全策略模板失败") } if tpl != nil { res = &sysin.ImSecurityPolicyModel{ TenantId: tenantID, PolicyName: tpl["policy_name"].String(), PasswordPolicy: tpl["password_policy"].String(), LoginRateLimit: tpl["login_rate_limit"].String(), SensitiveWordPolicy: tpl["sensitive_word_policy"].String(), FileReviewPolicy: tpl["file_review_policy"].String(), RealnamePolicy: tpl["realname_policy"].String(), RiskChallengePolicy: tpl["risk_challenge_policy"].String(), GlobalFriendEnabled: tpl["global_friend_enabled"].Int(), FriendDisabledPolicy: tpl["friend_disabled_policy"].String(), MessageCurfewEnabled: tpl["message_curfew_enabled"].Int(), } for _, rule := range templateCurfewRules(tpl) { res.CurfewRules = append(res.CurfewRules, &sysin.ImMessageCurfewRuleModel{RuleCode: rule.RuleCode, RuleName: rule.RuleName, DateType: rule.DateType, CustomDates: rule.CustomDates.String(), Weekdays: rule.Weekdays.String(), Timezone: rule.Timezone, StartTime: rule.StartTime, EndTime: rule.EndTime, Scope: rule.Scope, WhitelistUserIds: rule.WhitelistUserIds.String(), WhitelistGroupIds: rule.WhitelistGroupIds.String(), Message: rule.Message, Status: rule.Status, Sort: rule.Sort}) } } return } } if err != nil { return nil, gerror.Wrap(err, "获取安全策略失败") } if res == nil { return nil, gerror.New("安全策略不存在") } if _, _, err = resolveImPolicyTenant(ctx, res.TenantId); err != nil { return nil, err } res.CurfewRules, err = s.curfewRules(ctx, res.Id) return } func (s *sSysImSecurityPolicy) curfewRules(ctx context.Context, policyID int64) (list []*sysin.ImMessageCurfewRuleModel, err error) { c := dao.ImMessageCurfewRules.Columns() err = imtenant.RegistryDB().Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).Fields(c.Id, c.RuleCode, c.RuleName, c.DateType, "CAST("+c.CustomDates+" AS CHAR) custom_dates", "CAST("+c.Weekdays+" AS CHAR) weekdays", c.Timezone, "TIME_FORMAT("+c.StartTime+",'%H:%i') start_time", "TIME_FORMAT("+c.EndTime+",'%H:%i') end_time", c.Scope, "CAST("+c.WhitelistUserIds+" AS CHAR) whitelist_user_ids", "CAST("+c.WhitelistGroupIds+" AS CHAR) whitelist_group_ids", c.Message, c.Status, c.Sort).Where(c.PolicyId, policyID).WhereNull(c.DeletedAt).OrderAsc(c.Sort).OrderAsc(c.Id).Scan(&list) return } // Save 保存安全策略并立即生效:每租户仅一条当前生效记录(单记录模型,无草稿/发布工作流)。 // 已有记录则原地更新(版本号自增仅作审计),无则创建;同步 im_tenant 冗余开关并投递策略变更事件。 func (s *sSysImSecurityPolicy) Save(ctx context.Context, in *sysin.ImSecurityPolicySaveInp) (id int64, err error) { if err = in.Filter(ctx); err != nil { return } tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return 0, err } if tenantID == 0 { // 默认模板不可通过商户策略保存入口修改 return 0, gerror.New("默认模板不可修改,请选择具体商户") } db := imtenant.RegistryDB() c := dao.ImSecurityPolicy.Columns() rc := dao.ImMessageCurfewRules.Columns() tc := dao.ImTenant.Columns() operator := contexts.GetUserId(ctx) now := gtime.Now() version := uint(1) err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { current, readErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx). Where(c.TenantId, tenantID).Where(c.PublishStatus, consts.ImSecurityPublishPublished). WhereNull(c.DeletedAt).OrderDesc(c.PolicyVersion).One() if readErr != nil { return readErr } data := g.Map{c.PolicyName: in.PolicyName, c.PublishStatus: consts.ImSecurityPublishPublished, c.PasswordPolicy: policyJSON(in.PasswordPolicy, "{}"), c.LoginRateLimit: policyJSON(in.LoginRateLimit, "{}"), c.SensitiveWordPolicy: normalizedModerationPolicy(in.SensitiveWordPolicy, tenantCode), c.FileReviewPolicy: policyJSON(in.FileReviewPolicy, "{}"), c.RealnamePolicy: policyJSON(in.RealnamePolicy, "{}"), c.RiskChallengePolicy: policyJSON(in.RiskChallengePolicy, "{}"), c.GlobalFriendEnabled: in.GlobalFriendEnabled, c.FriendDisabledPolicy: policyJSON(in.FriendDisabledPolicy, `{"hide_profile":true,"block_qrcode":true,"block_group_member_entry":true,"blocked_entries":["search","profile_card","group_member","qrcode","invite_auto_add","recommendation"],"message":"当前时段暂不允许添加好友"}`), c.MessageCurfewEnabled: in.MessageCurfewEnabled, c.Remark: in.Remark, c.UpdatedAt: now, c.UpdatedBy: operator} if in.LoginRateLimit == nil { // 未提交登录失败策略时保留原值(登录失败锁定由独立配置入口管理,避免保存时清空配置) delete(data, c.LoginRateLimit) } if current.IsEmpty() { version = 1 data[c.TenantId] = tenantID data[c.PolicyVersion] = version data[c.CreatedAt] = now data[c.CreatedBy] = operator result, insertErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Data(data).Insert() if insertErr != nil { return insertErr } if id, insertErr = result.LastInsertId(); insertErr != nil { return insertErr } } else { id = current[c.Id].Int64() version = current[c.PolicyVersion].Uint() + 1 data[c.PolicyVersion] = version if _, updateErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, id).Data(data).Update(); updateErr != nil { return updateErr } } // 规则整体重建:uk_im_message_curfew_rule 唯一键含 tenant_id+rule_code,软删行仍占键会冲突,必须硬删 if _, deleteErr := tx.Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).Where(rc.PolicyId, id).Unscoped().Delete(); deleteErr != nil { return deleteErr } for _, rule := range in.CurfewRules { _, insertErr := tx.Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).Data(g.Map{rc.TenantId: tenantID, rc.PolicyId: id, rc.RuleCode: rule.RuleCode, rc.RuleName: rule.RuleName, rc.DateType: rule.DateType, rc.CustomDates: policyJSON(rule.CustomDates, "[]"), rc.Weekdays: policyJSON(rule.Weekdays, "[]"), rc.Timezone: rule.Timezone, rc.StartTime: rule.StartTime, rc.EndTime: rule.EndTime, rc.Scope: rule.Scope, rc.WhitelistUserIds: policyJSON(rule.WhitelistUserIds, "[]"), rc.WhitelistGroupIds: policyJSON(rule.WhitelistGroupIds, "[]"), rc.Message: rule.Message, rc.Status: rule.Status, rc.Sort: rule.Sort, rc.CreatedAt: now, rc.CreatedBy: operator, rc.UpdatedAt: now, rc.UpdatedBy: operator}).Insert() if insertErr != nil { return insertErr } } if tenantID > 0 { // 上游已将商户配置并入 im_tenant 表,此处同步冗余开关与策略版本。 // updated_at 显式自赋值:抑制 gf 自动填充与 ON UPDATE CURRENT_TIMESTAMP,策略保存不 touch 商户行的更新人/时间 if _, cfgErr := tx.Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Where(tc.Id, tenantID). Data(g.Map{tc.SecurityPolicyVersion: version, tc.GlobalFriendEnabled: in.GlobalFriendEnabled, tc.MessageCurfewEnabled: in.MessageCurfewEnabled, tc.UpdatedAt: gdb.Raw("updated_at")}).Update(); cfgErr != nil { return cfgErr } } return nil }) if err != nil { return 0, gerror.Wrap(err, "保存安全策略失败") } _ = writeImAdminAudit(ctx, tenantID, "im_security_policy_save", "im_security_policy", id, "success", g.Map{"version": version, "global_friend_enabled": in.GlobalFriendEnabled, "message_curfew_enabled": in.MessageCurfewEnabled}) // 保存即生效:投递策略变更事件,IM 端消费后失效策略缓存并回源拉取新配置。 // 事件已持久化到 im_admin_outbox,立即投递失败仅告警+审计、由 outbox 重试续投,不阻塞已生效的保存结果 if pubErr := publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", version); pubErr != nil { g.Log().Warningf(ctx, "IM策略事件投递失败(outbox将重试): tenant=%d version=%d err=%v", tenantID, version, pubErr) _ = writeImAdminAudit(ctx, tenantID, "im_policy_event_publish", "im_security_policy", id, "failed", g.Map{"error": pubErr.Error(), "version": version}) } return id, nil } func normalizedModerationPolicy(value *gjson.Json, _ string) string { policy := map[string]any{ "scenes": map[string]bool{"user_nickname": true, "user_signature": true, "group_name": true, "group_nickname": true, "private_message": true, "group_message": true, "group_announcement": true}, "risk_actions": map[string]string{"high": "block", "medium": "block", "low": "audit_only", "none": "allow"}, "failure_mode": "allow", "block_message": "内容不符合平台规范,请修改后重试", } if value != nil && value.String() != "" && value.String() != "null" { for key, item := range value.Map() { policy[key] = item } } return gjson.New(policy).String() } func (s *sSysImSecurityPolicy) RestoreDefault(ctx context.Context, in *sysin.ImSecurityPolicyRestoreInp) (int64, error) { if err := in.Filter(ctx); err != nil { return 0, err } tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return 0, err } // 读取安全策略模板,将模板内容覆盖为当前商户的策略(复制后商户可自行修改) tpl, err := loadPolicyTemplateRow(imtenant.RegistryDB().Model(imPolicyTemplateTable).Safe().Ctx(ctx)) if err != nil { return 0, gerror.Wrap(err, "读取安全策略模板失败") } if tpl == nil { return 0, gerror.New("安全策略模板未初始化") } id, err := s.Save(ctx, &sysin.ImSecurityPolicySaveInp{TenantId: tenantID, PolicyName: tpl["policy_name"].String(), PasswordPolicy: gjson.New(tpl["password_policy"].String()), LoginRateLimit: gjson.New(tpl["login_rate_limit"].String()), SensitiveWordPolicy: gjson.New(tpl["sensitive_word_policy"].String()), FileReviewPolicy: gjson.New(tpl["file_review_policy"].String()), RealnamePolicy: gjson.New(tpl["realname_policy"].String()), RiskChallengePolicy: gjson.New(tpl["risk_challenge_policy"].String()), GlobalFriendEnabled: tpl["global_friend_enabled"].Int(), FriendDisabledPolicy: gjson.New(tpl["friend_disabled_policy"].String()), MessageCurfewEnabled: tpl["message_curfew_enabled"].Int(), Remark: in.Reason, CurfewRules: templateCurfewRules(tpl)}) if err == nil { _ = writeImAdminAudit(ctx, tenantID, "im_security_policy_restore_default", "im_security_policy", id, "success", g.Map{"reason": in.Reason}) } return id, err } // GetLoginLockoutConfig 读取租户已发布策略中的登录失败锁定配置,无配置时返回默认值。 func (s *sSysImSecurityPolicy) GetLoginLockoutConfig(ctx context.Context, in *sysin.ImLoginLockoutConfigGetInp) (res *sysin.ImLoginLockoutConfigModel, err error) { if err = in.Filter(ctx); err != nil { return nil, err } tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return nil, err } res = &sysin.ImLoginLockoutConfigModel{Enabled: false, PasswordMaxAttempts: 5, LockMinutes: 1440, WindowMinutes: 60} c := dao.ImSecurityPolicy.Columns() var row gdb.Record if row, err = imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx). Fields(c.Id, c.PolicyVersion, "CAST("+c.LoginRateLimit+" AS CHAR) login_rate_limit"). Where(c.TenantId, tenantID).Where(c.PublishStatus, consts.ImSecurityPublishPublished). WhereNull(c.DeletedAt).OrderDesc(c.PolicyVersion).One(); err != nil { return nil, gerror.Wrap(err, "获取登录失败策略配置失败") } if row.IsEmpty() { return res, nil } res.PolicyId = row[c.Id].Int64() res.PolicyVersion = row[c.PolicyVersion].Uint() policy := gjson.New(row["login_rate_limit"].String()) res.Enabled = policy.Get("enabled").Bool() if v := policy.Get("password_max_attempts").Int(); v >= 1 { res.PasswordMaxAttempts = int(v) } if v := policy.Get("lock_seconds").Int(); v >= 60 { res.LockMinutes = int(v / 60) } if v := policy.Get("window_seconds").Int(); v >= 60 { res.WindowMinutes = int(v / 60) } return res, nil } // SaveLoginLockoutConfig 保存租户登录失败锁定配置:直接更新已发布策略,保存后通过策略变更事件即时生效。 func (s *sSysImSecurityPolicy) SaveLoginLockoutConfig(ctx context.Context, in *sysin.ImLoginLockoutConfigSaveInp) error { if err := in.Filter(ctx); err != nil { return err } tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return err } value := map[string]any{"enabled": false} if in.Enabled { value = map[string]any{"enabled": true, "password_max_attempts": in.PasswordMaxAttempts, "lock_seconds": in.LockMinutes * 60, "window_seconds": in.WindowMinutes * 60} } loginRateLimit := gjson.New(value).String() c := dao.ImSecurityPolicy.Columns() db := imtenant.RegistryDB() var row gdb.Record if row, err = db.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx). Fields(c.Id, c.PolicyVersion). Where(c.TenantId, tenantID).Where(c.PublishStatus, consts.ImSecurityPublishPublished). WhereNull(c.DeletedAt).OrderDesc(c.PolicyVersion).One(); err != nil { return gerror.Wrap(err, "获取登录失败策略配置失败") } operator := contexts.GetUserId(ctx) now := gtime.Now() if !row.IsEmpty() { policyID := row[c.Id].Int64() version := row[c.PolicyVersion].Uint() if _, err = db.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, policyID). Data(g.Map{c.LoginRateLimit: loginRateLimit, c.UpdatedAt: now, c.UpdatedBy: operator}).Update(); err != nil { return gerror.Wrap(err, "保存登录失败策略配置失败") } _ = writeImAdminAudit(ctx, tenantID, "im_security_policy_login_lockout_save", "im_security_policy", policyID, "success", g.Map{"enabled": in.Enabled, "password_max_attempts": in.PasswordMaxAttempts, "lock_minutes": in.LockMinutes, "window_minutes": in.WindowMinutes}) if pubErr := publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", version); pubErr != nil { g.Log().Warningf(ctx, "IM策略事件投递失败(outbox将重试): tenant=%d version=%d err=%v", tenantID, version, pubErr) _ = writeImAdminAudit(ctx, tenantID, "im_policy_event_publish", "im_security_policy", policyID, "failed", g.Map{"error": pubErr.Error(), "version": version}) } return nil } // 租户尚无策略:按模板复制初始策略(模板未初始化时退回内置默认值),仅覆盖登录失败配置,保存即生效并投递策略变更事件 saveInp := &sysin.ImSecurityPolicySaveInp{TenantId: tenantID, PolicyName: "默认安全策略", LoginRateLimit: gjson.New(value), GlobalFriendEnabled: 1, MessageCurfewEnabled: 0} tpl, tplErr := loadPolicyTemplateRow(imtenant.RegistryDB().Model(imPolicyTemplateTable).Safe().Ctx(ctx)) if tplErr != nil { return gerror.Wrap(tplErr, "读取安全策略模板失败") } if tpl != nil { saveInp.PolicyName = tpl["policy_name"].String() saveInp.PasswordPolicy = gjson.New(tpl["password_policy"].String()) saveInp.SensitiveWordPolicy = gjson.New(tpl["sensitive_word_policy"].String()) saveInp.FileReviewPolicy = gjson.New(tpl["file_review_policy"].String()) saveInp.RealnamePolicy = gjson.New(tpl["realname_policy"].String()) saveInp.RiskChallengePolicy = gjson.New(tpl["risk_challenge_policy"].String()) saveInp.GlobalFriendEnabled = tpl["global_friend_enabled"].Int() saveInp.FriendDisabledPolicy = gjson.New(tpl["friend_disabled_policy"].String()) saveInp.MessageCurfewEnabled = tpl["message_curfew_enabled"].Int() saveInp.CurfewRules = templateCurfewRules(tpl) } id, saveErr := s.Save(ctx, saveInp) if saveErr != nil { return saveErr } _ = writeImAdminAudit(ctx, tenantID, "im_security_policy_login_lockout_save", "im_security_policy", id, "success", g.Map{"enabled": in.Enabled, "password_max_attempts": in.PasswordMaxAttempts, "lock_minutes": in.LockMinutes, "window_minutes": in.WindowMinutes}) return nil } func (s *sSysImSecurityPolicy) IPRuleList(ctx context.Context, in *sysin.ImSecurityIPRuleListInp) (list []*sysin.ImSecurityIPRuleModel, total int, err error) { if err = in.Filter(ctx); err != nil { return } tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return nil, 0, err } c := dao.ImSecurityIpRules.Columns() model := imtenant.RegistryDB().Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Fields(c.Id, c.TenantId, c.RuleType, c.IpCidr, c.Action, "CAST("+c.SceneJson+" AS CHAR) scene_json", c.RiskLevel, c.Status, c.ExpiresAt, c.Remark, c.CreatedAt, c.UpdatedAt).Where(c.TenantId, tenantID).WhereNull(c.DeletedAt) if in.RuleType != "" { model = model.Where(c.RuleType, in.RuleType) } if in.IpCidr != "" { model = model.WhereLike(c.IpCidr, "%"+in.IpCidr+"%") } if in.Status != 0 { model = model.Where(c.Status, in.Status) } err = model.Page(in.Page, in.PerPage).OrderDesc(c.Id).ScanAndCount(&list, &total, false) return } func (s *sSysImSecurityPolicy) IPRuleEdit(ctx context.Context, in *sysin.ImSecurityIPRuleEditInp) error { if err := in.Filter(ctx); err != nil { return err } if net.ParseIP(in.IpCidr) == nil { if _, _, err := net.ParseCIDR(in.IpCidr); err != nil { return gerror.New("IP或CIDR格式不合法") } } tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId) if err != nil { return err } c := dao.ImSecurityIpRules.Columns() db := imtenant.RegistryDB() now := gtime.Now() data := g.Map{c.TenantId: tenantID, c.RuleType: in.RuleType, c.IpCidr: in.IpCidr, c.Action: in.Action, c.SceneJson: policyJSON(in.SceneJson, "[]"), c.RiskLevel: in.RiskLevel, c.Status: in.Status, c.ExpiresAt: in.ExpiresAt, c.Remark: in.Remark, c.UpdatedAt: now, c.UpdatedBy: contexts.GetUserId(ctx)} targetID := in.Id if in.Id > 0 { var row gdb.Record if row, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil { return err } if row.IsEmpty() || row[c.TenantId].Int64() != tenantID { return gerror.New("IP规则不存在") } _, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(data).Update() } else { data[c.CreatedAt] = now data[c.CreatedBy] = contexts.GetUserId(ctx) result, insertErr := db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Data(data).Insert() err = insertErr if err == nil { targetID, err = result.LastInsertId() } } if err != nil { return gerror.Wrap(err, "保存IP规则失败") } _ = writeImAdminAudit(ctx, tenantID, "im_security_ip_rule_edit", "im_security_ip_rule", targetID, "success", g.Map{"ip_cidr": in.IpCidr, "rule_type": in.RuleType}) return publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", uint(targetID)) } func (s *sSysImSecurityPolicy) IPRuleDelete(ctx context.Context, in *sysin.ImSecurityIPRuleDeleteInp) error { if err := in.Filter(ctx); err != nil { return err } c := dao.ImSecurityIpRules.Columns() db := imtenant.RegistryDB() var row gdb.Record var err error if row, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil { return err } if row.IsEmpty() { return gerror.New("IP规则不存在") } tenantID, tenantCode, err := resolveImPolicyTenant(ctx, row[c.TenantId].Int64()) if err != nil { return err } _, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(g.Map{c.Status: consts.StatusDisable, c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx), c.UpdatedAt: gtime.Now(), c.UpdatedBy: contexts.GetUserId(ctx)}).Update() if err != nil { return gerror.Wrap(err, "删除IP规则失败") } _ = writeImAdminAudit(ctx, tenantID, "im_security_ip_rule_delete", "im_security_ip_rule", in.Id, "success", nil) return publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", uint(in.Id)) }