feat: add friend add and curfew controls

This commit is contained in:
richjohnrambo
2026-09-18 10:55:33 +08:00
parent 348cbcaf96
commit 7c8b4d9b7c
12 changed files with 270 additions and 1 deletions
@@ -108,6 +108,11 @@ func (c *cImUser) AdjustGroup(ctx context.Context, req *imuser.AdjustGroupReq) (
return
}
func (c *cImUser) FriendAdd(ctx context.Context, req *imuser.FriendAddReq) (res *imuser.FriendAddRes, err error) {
err = service.SysImUser().FriendAdd(ctx, &req.ImUserFriendAddInp)
return
}
func (c *cImUser) IPWhitelist(ctx context.Context, req *imuser.IPWhitelistReq) (res *imuser.IPWhitelistRes, err error) {
data, err := service.SysImUser().IPWhitelist(ctx, &req.ImUserIPWhitelistInp)
if err != nil {
@@ -211,6 +211,99 @@ func (c *cConfig) ModerationPolicy(ctx context.Context, req *api.ModerationPolic
return res, nil
}
func (c *cConfig) SecurityPolicy(ctx context.Context, req *api.SecurityPolicyReq) (res *api.SecurityPolicyRes, err error) {
tenant, err := getTenant(ctx, dao.ImTenant.Columns().Code, strings.TrimSpace(req.Code))
if err != nil {
return nil, err
}
res = &api.SecurityPolicyRes{
TenantID: tenant.Id,
TenantCode: tenant.Code,
GlobalFriendEnabled: true,
FriendDisabledPolicy: map[string]interface{}{},
MessageCurfewEnabled: false,
CurfewRules: []*api.SecurityCurfewRuleRes{},
}
columns := dao.ImSecurityPolicy.Columns()
record, err := imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).
Fields(columns.Id, columns.PolicyVersion, columns.GlobalFriendEnabled, "CAST("+columns.FriendDisabledPolicy+" AS CHAR) friend_disabled_policy", columns.MessageCurfewEnabled).
Where(columns.TenantId, tenant.Id).Where(columns.PublishStatus, consts.ImSecurityPublishPublished).
WhereNull(columns.DeletedAt).OrderDesc(columns.PolicyVersion).One()
if err != nil {
return nil, gerror.Wrap(err, "获取商户安全策略失败")
}
if record.IsEmpty() {
return res, nil
}
policyID := record[columns.Id].Int64()
res.PolicyVersion = record[columns.PolicyVersion].Uint()
res.GlobalFriendEnabled = record[columns.GlobalFriendEnabled].Int() == 1
res.MessageCurfewEnabled = record[columns.MessageCurfewEnabled].Int() == 1
if raw := strings.TrimSpace(record["friend_disabled_policy"].String()); raw != "" && raw != "null" {
_ = json.Unmarshal([]byte(raw), &res.FriendDisabledPolicy)
}
ruleColumns := dao.ImMessageCurfewRules.Columns()
var rows []struct {
RuleCode string `json:"rule_code"`
RuleName string `json:"rule_name"`
DateType string `json:"date_type"`
CustomDates string `json:"custom_dates"`
Weekdays string `json:"weekdays"`
Timezone string `json:"timezone"`
StartTime string `json:"start_time"`
EndTime string `json:"end_time"`
Scope string `json:"scope"`
WhitelistUserIds string `json:"whitelist_user_ids"`
WhitelistGroupIds string `json:"whitelist_group_ids"`
Message string `json:"message"`
Status int `json:"status"`
Sort int `json:"sort"`
}
if err = imtenant.RegistryDB().Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).
Fields(ruleColumns.RuleCode, ruleColumns.RuleName, ruleColumns.DateType, "CAST("+ruleColumns.CustomDates+" AS CHAR) custom_dates", "CAST("+ruleColumns.Weekdays+" AS CHAR) weekdays", ruleColumns.Timezone, "TIME_FORMAT("+ruleColumns.StartTime+",'%H:%i') start_time", "TIME_FORMAT("+ruleColumns.EndTime+",'%H:%i') end_time", ruleColumns.Scope, "CAST("+ruleColumns.WhitelistUserIds+" AS CHAR) whitelist_user_ids", "CAST("+ruleColumns.WhitelistGroupIds+" AS CHAR) whitelist_group_ids", ruleColumns.Message, ruleColumns.Status, ruleColumns.Sort).
Where(ruleColumns.PolicyId, policyID).Where(ruleColumns.Status, consts.StatusEnabled).
WhereNull(ruleColumns.DeletedAt).OrderAsc(ruleColumns.Sort).OrderAsc(ruleColumns.Id).Scan(&rows); err != nil {
return nil, gerror.Wrap(err, "获取商户宵禁规则失败")
}
for _, row := range rows {
res.CurfewRules = append(res.CurfewRules, &api.SecurityCurfewRuleRes{
RuleCode: row.RuleCode,
RuleName: row.RuleName,
DateType: row.DateType,
CustomDates: parseStringArray(row.CustomDates),
Weekdays: parseIntArray(row.Weekdays),
Timezone: row.Timezone,
StartTime: row.StartTime,
EndTime: row.EndTime,
Scope: row.Scope,
WhitelistUserIds: parseStringArray(row.WhitelistUserIds),
WhitelistGroupIds: parseStringArray(row.WhitelistGroupIds),
Message: row.Message,
Status: row.Status,
Sort: row.Sort,
})
}
return res, nil
}
func parseStringArray(raw string) []string {
values := []string{}
if strings.TrimSpace(raw) == "" {
return values
}
_ = json.Unmarshal([]byte(raw), &values)
return values
}
func parseIntArray(raw string) []int {
values := []int{}
if strings.TrimSpace(raw) == "" {
return values
}
_ = json.Unmarshal([]byte(raw), &values)
return values
}
func (c *cConfig) SensitiveWords(ctx context.Context, req *api.SensitiveWordsReq) (res *api.SensitiveWordsRes, err error) {
tenant, err := getTenant(ctx, dao.ImTenant.Columns().Code, strings.TrimSpace(req.Code))
if err != nil {
+49
View File
@@ -360,6 +360,7 @@ func (s *sSysImUser) listModel(ctx context.Context, db gdb.DB) *gdb.Model {
"up.public_account",
"up.gender",
"up.region",
"COALESCE(up.friend_add_enabled, 1) friend_add_enabled",
"us.auth_version",
"us.password_updated_at",
)
@@ -589,6 +590,54 @@ func (s *sSysImUser) AdjustGroup(ctx context.Context, in *sysin.ImUserAdjustGrou
return nil
}
func (s *sSysImUser) FriendAdd(ctx context.Context, in *sysin.ImUserFriendAddInp) error {
if err := in.Filter(ctx); err != nil {
return err
}
scope, err := resolveImAdminScope(ctx, in.TenantId)
if err != nil {
return err
}
db, _, err := imtenant.DB(ctx, scope.TenantId)
if err != nil {
return err
}
now := gtime.Now()
result, err := db.Model("im_user_profiles").Safe().Ctx(ctx).
Where("user_id", in.UserId).
Data(g.Map{
"friend_add_enabled": in.Enabled,
"friend_add_disabled_reason": in.Reason,
"friend_add_policy_version": gdb.Raw("friend_add_policy_version+1"),
"updated_at": now,
}).Update()
if err != nil {
return gerror.Wrap(err, "设置用户加好友开关失败")
}
affected, _ := result.RowsAffected()
if affected == 0 {
profileID, idErr := imtenant.NextSnowflakeID(ctx)
if idErr != nil {
return gerror.Wrap(idErr, "生成用户资料ID失败")
}
_, err = db.Model("im_user_profiles").Safe().Ctx(ctx).Data(g.Map{
"id": profileID,
"user_id": in.UserId,
"public_account": fmt.Sprintf("usr%d", in.UserId),
"friend_add_enabled": in.Enabled,
"friend_add_disabled_reason": in.Reason,
"friend_add_policy_version": 1,
"created_at": now,
"updated_at": now,
}).Insert()
if err != nil {
return gerror.Wrap(err, "补全用户资料并设置加好友开关失败")
}
}
_ = writeImAdminAudit(ctx, scope.TenantId, "im_user_friend_add_update", "user", in.UserId, "success", g.Map{"enabled": in.Enabled, "reason": in.Reason})
return nil
}
func (s *sSysImUser) IPWhitelist(ctx context.Context, in *sysin.ImUserIPWhitelistInp) (*sysin.ImUserIPWhitelistModel, error) {
if err := in.Filter(ctx); err != nil {
return nil, err
@@ -63,6 +63,7 @@ type ImUserListModel struct {
PolicyGroupId int64 `json:"policyGroupId" dc:"用户策略分组ID"`
PolicyGroupName string `json:"policyGroupName" dc:"用户策略分组名称"`
RegisterType string `json:"registerType" dc:"注册方式"`
FriendAddEnabled int `json:"friendAddEnabled" dc:"是否允许发起加好友"`
}
type ImUserCreateInp struct {
@@ -263,6 +264,27 @@ func (in *ImUserAdjustGroupInp) Filter(ctx context.Context) error {
return nil
}
type ImUserFriendAddInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" v:"required|min:1#用户ID不能为空|用户ID不合法" dc:"用户ID"`
Enabled int `json:"enabled" dc:"是否允许发起加好友:1允许 0禁止"`
Reason string `json:"reason" dc:"禁用原因"`
}
func (in *ImUserFriendAddInp) Filter(ctx context.Context) error {
in.Reason = strings.TrimSpace(in.Reason)
if in.UserId <= 0 {
return gerror.New("用户ID不能为空")
}
if in.Enabled != 0 && in.Enabled != 1 {
return gerror.New("加好友开关不合法")
}
if in.Enabled == 0 && in.Reason == "" {
in.Reason = "后台禁止该用户发起加好友"
}
return nil
}
type ImUserIPWhitelistInp struct {
TenantId int64 `json:"tenantId" dc:"租户ID"`
UserId int64 `json:"userId" v:"required|min:1#用户ID不能为空|用户ID不合法" dc:"用户ID"`
+1
View File
@@ -233,6 +233,7 @@ type (
// Status 更新IM用户状态
Status(ctx context.Context, in *sysin.ImUserStatusInp) (err error)
AdjustGroup(ctx context.Context, in *sysin.ImUserAdjustGroupInp) (err error)
FriendAdd(ctx context.Context, in *sysin.ImUserFriendAddInp) (err error)
IPWhitelist(ctx context.Context, in *sysin.ImUserIPWhitelistInp) (res *sysin.ImUserIPWhitelistModel, err error)
IPWhitelistSave(ctx context.Context, in *sysin.ImUserIPWhitelistInp) (err error)
// Devices 获取IM用户设备列表