diff --git a/internal/api/router.go b/internal/api/router.go index 9f622be9e..ce16cd535 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -173,6 +173,8 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co userRouterGroup.POST("/set_group_invite_setting", u.SetGroupInviteSetting) // 设置用户全局阅后即焚时长(秒),0 表示关闭 userRouterGroup.POST("/set_user_msg_burn_duration", u.SetUserMsgBurnDuration) + // 设置删除账号等待间隔(秒),0 表示使用系统默认(18个月) + userRouterGroup.POST("/set_delete_account_interval", u.SetDeleteAccountInterval) // 批量查询阅后即焚、手机号可见性、音视频接收、全局/会话消息接收、群邀请等设置 userRouterGroup.POST("/get_user_privacy_settings", u.GetUserPrivacySettings) // 根据手机号精确查找用户(phoneSearchVisibility=true 时遵守 phone_visibility 设置) diff --git a/internal/api/user.go b/internal/api/user.go index ad4e23fee..90f84fcbd 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -359,6 +359,10 @@ func (u *UserApi) SetUserMsgBurnDuration(c *gin.Context) { a2r.Call(c, user.UserClient.SetUserMsgBurnDuration, u.Client) } +func (u *UserApi) SetDeleteAccountInterval(c *gin.Context) { + a2r.Call(c, user.UserClient.SetDeleteAccountInterval, u.Client) +} + func (u *UserApi) GetUserPrivacySettings(c *gin.Context) { a2r.Call(c, user.UserClient.GetUserPrivacySettings, u.Client) } diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index bb263e4db..abd64eaf0 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -455,6 +455,36 @@ func (s *userServer) SetUserMsgBurnDuration(ctx context.Context, req *pbuser.Set return &pbuser.SetUserMsgBurnDurationResp{}, nil } +// SetDeleteAccountInterval 设置用户删除账号等待间隔(秒); +// 0 表示重置为系统默认(18个月),要求 >= 0。只允许本人或管理员操作。 +func (s *userServer) SetDeleteAccountInterval(ctx context.Context, req *pbuser.SetDeleteAccountIntervalReq) (*pbuser.SetDeleteAccountIntervalResp, error) { + if req.UserID == "" { + return nil, errs.ErrArgs.WrapMsg("userID is required") + } + if req.DeleteAccountInterval < 0 { + return nil, errs.ErrArgs.WrapMsg("deleteAccountInterval must be >= 0 (seconds)") + } + if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil { + log.ZWarn(ctx, "SetDeleteAccountInterval: access denied", err, + "opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID) + return nil, err + } + if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil { + log.ZError(ctx, "SetDeleteAccountInterval: user not found or db error", err, + "opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID) + return nil, err + } + if err := s.db.UpdateByMap(ctx, req.UserID, map[string]any{ + "delete_account_interval": req.DeleteAccountInterval, + }); err != nil { + log.ZError(ctx, "SetDeleteAccountInterval: UpdateByMap failed", err, + "opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID, + "deleteAccountInterval", req.DeleteAccountInterval) + return nil, err + } + return &pbuser.SetDeleteAccountIntervalResp{}, nil +} + // GetUserPrivacySettings 返回当前登录用户(ctx opUserID)的隐私与接收相关设置。 func (s *userServer) GetUserPrivacySettings(ctx context.Context, req *pbuser.GetUserPrivacySettingsReq) (*pbuser.GetUserPrivacySettingsResp, error) { userID := mcontext.GetOpUserID(ctx) @@ -468,13 +498,15 @@ func (s *userServer) GetUserPrivacySettings(ctx context.Context, req *pbuser.Get return nil, err } u := users[0] + return &pbuser.GetUserPrivacySettingsResp{ - MsgBurnDuration: u.MsgBurnDuration, - PhoneVisibility: u.PhoneVisibility, - CallAcceptSetting: u.CallAcceptSetting, - GlobalRecvMsgOpt: u.GlobalRecvMsgOpt, - MsgReceiveSetting: u.MsgReceiveSetting, - GroupInviteSetting: u.GroupInviteSetting, + MsgBurnDuration: u.MsgBurnDuration, + PhoneVisibility: u.PhoneVisibility, + CallAcceptSetting: u.CallAcceptSetting, + GlobalRecvMsgOpt: u.GlobalRecvMsgOpt, + MsgReceiveSetting: u.MsgReceiveSetting, + GroupInviteSetting: u.GroupInviteSetting, + DeleteAccountInterval: u.DeleteAccountInterval, }, nil } diff --git a/pkg/common/storage/database/mgo/user.go b/pkg/common/storage/database/mgo/user.go index 9ba0a0514..25044cb1d 100644 --- a/pkg/common/storage/database/mgo/user.go +++ b/pkg/common/storage/database/mgo/user.go @@ -57,6 +57,11 @@ type UserMgo struct { } func (u *UserMgo) Create(ctx context.Context, users []*model.User) error { + for _, user := range users { + if user.DeleteAccountInterval == 0 { + user.DeleteAccountInterval = model.DefaultDeleteAccountIntervalSec + } + } return mongoutil.InsertMany(ctx, u.coll, users) } diff --git a/pkg/common/storage/model/user.go b/pkg/common/storage/model/user.go index 503ccfb57..1eac5c35a 100644 --- a/pkg/common/storage/model/user.go +++ b/pkg/common/storage/model/user.go @@ -58,6 +58,10 @@ const ( UserStatusBlacklist int32 = 2 ) +// DefaultDeleteAccountIntervalSec 删除账号等待间隔的系统默认值(18 个月,按 30 天/月折算)。 +// 注册时写入 MongoDB 的初始值;用户未显式修改时即为此值。 +const DefaultDeleteAccountIntervalSec int32 = 18 * 30 * 24 * 3600 + type User struct { UserID string `bson:"user_id"` Nickname string `bson:"nickname"` @@ -81,6 +85,8 @@ type User struct { Status int32 `bson:"status"` // MsgBurnDuration 用户全局消息阅后即焚时长(秒);0 表示关闭 MsgBurnDuration int32 `bson:"msg_burn_duration"` + // DeleteAccountInterval 删除账号间隔(秒);0 表示关闭 + DeleteAccountInterval int32 `bson:"delete_account_interval"` } func (u *User) GetNickname() string { diff --git a/protocol b/protocol index f7bfce630..24c722593 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit f7bfce630638a3dc3fd776842d1c3e8078559269 +Subproject commit 24c72259373e9080ab005bb10385524753d4dfc3