feat: publish password reset via kafka

This commit is contained in:
richjohnrambo
2026-08-28 14:18:27 +08:00
parent 6e08013fd1
commit cde4480b51
5 changed files with 65 additions and 15 deletions
+3 -1
View File
@@ -38,7 +38,9 @@ type ResetPasswordReq struct {
sysin.ImUserPasswordResetInp
}
type ResetPasswordRes struct{}
type ResetPasswordRes struct {
*sysin.ImUserCommandModel
}
type RevokeSessionsReq struct {
g.Meta `path:"/imUser/revokeSessions" method:"post" tags:"IM用户" summary:"撤销IM用户会话"`
+1
View File
@@ -46,6 +46,7 @@ const (
ImAdminCommandUserLogoutAll = "user.logout_all"
ImAdminCommandUserCancel = "user.account.cancel"
ImAdminCommandUserDelete = "user.account.delete"
ImAdminCommandUserPasswordReset = "user.password.reset"
ImAdminCommandKafkaDeadReplay = "kafka.dead_letter.replay"
ImAdminCommandKafkaDeadIgnore = "kafka.dead_letter.ignore"
ImAdminCommandKafkaDeadResolve = "kafka.dead_letter.resolve"
@@ -48,7 +48,12 @@ func (c *cImUser) View(ctx context.Context, req *imuser.ViewReq) (res *imuser.Vi
}
func (c *cImUser) ResetPassword(ctx context.Context, req *imuser.ResetPasswordReq) (res *imuser.ResetPasswordRes, err error) {
err = service.SysImUser().ResetPassword(ctx, &req.ImUserPasswordResetInp)
data, err := service.SysImUser().ResetPassword(ctx, &req.ImUserPasswordResetInp)
if err != nil {
return
}
res = new(imuser.ResetPasswordRes)
res.ImUserCommandModel = data
return
}
+54 -12
View File
@@ -8,6 +8,7 @@ package sys
import (
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
@@ -15,6 +16,7 @@ import (
"hotgo/internal/library/contexts"
"hotgo/internal/library/imkafka"
"hotgo/internal/library/imtenant"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"net/url"
@@ -209,29 +211,25 @@ func (s *sSysImUser) viewFields() []interface{} {
}
}
func (s *sSysImUser) ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (err error) {
func (s *sSysImUser) ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (res *sysin.ImUserCommandModel, err error) {
if err = in.Filter(ctx); err != nil {
return
return nil, err
}
scope, err := resolveImAdminScope(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return err
return nil, err
}
userId, err := sysin.ParseOptionalInt64(in.UserId)
if err != nil {
return err
return nil, err
}
db, _, err := imtenant.DB(ctx, scope.TenantId)
if err != nil {
return err
}
err = resetIMUserPassword(ctx, db, scope, userId, in.NewPassword)
command, err := s.publishPasswordResetCommand(ctx, tenant, userId, in.NewPassword)
result := "success"
if err != nil {
result = "failed"
}
_ = writeImAdminAudit(ctx, scope.TenantId, "user_password_reset", "user", userId, result, nil)
return err
_ = writeImAdminAudit(ctx, tenant.Id, "user_password_reset", "user", userId, result, nil)
return command, err
}
func (s *sSysImUser) RevokeSessions(ctx context.Context, in *sysin.ImUserRevokeSessionsInp) (err error) {
@@ -391,6 +389,50 @@ func (s *sSysImUser) publishGovernanceCommand(ctx context.Context, in *sysin.ImU
}, nil
}
func (s *sSysImUser) publishPasswordResetCommand(ctx context.Context, tenant *entity.ImTenant, userId int64, newPassword string) (*sysin.ImUserCommandModel, error) {
passwordHash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
if err != nil {
return nil, gerror.Wrap(err, "生成密码摘要失败")
}
user := contexts.GetUser(ctx)
actorName := ""
if user != nil {
actorName = user.RealName
}
payload := map[string]any{
"operator_id": strconv.FormatInt(contexts.GetUserId(ctx), 10),
"user_id": strconv.FormatInt(userId, 10),
"target_user_id": strconv.FormatInt(userId, 10),
"password_hash": base64.StdEncoding.EncodeToString(passwordHash),
}
command, err := imkafka.PublishAdminCommand(ctx, imkafka.AdminCommandInput{
TenantID: tenant.Id,
TenantCode: tenant.Code,
EventType: consts.ImAdminCommandUserPasswordReset,
BizType: "user",
BizID: strconv.FormatInt(userId, 10),
Actor: &imkafka.Actor{
Type: "admin",
ID: strconv.FormatInt(contexts.GetUserId(ctx), 10),
DisplayName: actorName,
},
Payload: payload,
CreatedBy: contexts.GetUserId(ctx),
})
if err != nil {
return nil, err
}
return &sysin.ImUserCommandModel{
CommandID: command.CommandID,
EventID: command.EventID,
Status: command.Status,
TenantID: command.TenantID,
TenantCode: command.TenantCode,
EventType: command.EventType,
IdempotencyKey: command.IdempotencyKey,
}, nil
}
func updateIMUserStatus(ctx context.Context, db gdb.DB, scope imAdminScope, userId int64, state int) error {
securityId, err := imtenant.NextSnowflakeID(ctx)
if err != nil {
+1 -1
View File
@@ -219,7 +219,7 @@ type (
// View 获取IM用户指定信息
View(ctx context.Context, in *sysin.ImUserViewInp) (res *sysin.ImUserViewModel, err error)
// ResetPassword 管理员重置IM用户密码
ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (err error)
ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (res *sysin.ImUserCommandModel, err error)
// RevokeSessions 撤销IM用户会话
RevokeSessions(ctx context.Context, in *sysin.ImUserRevokeSessionsInp) (err error)
// Status 更新IM用户状态