修复经理状态更新持久化

This commit is contained in:
zhx 2026-07-08 18:47:49 +08:00
parent dd8dc489b7
commit 779d864e80
4 changed files with 115 additions and 3 deletions

View File

@ -107,8 +107,12 @@ func (h *Handler) UpdateManager(c *gin.Context) {
response.BadRequest(c, err.Error())
return
}
statusDetail := ""
if req.Status != nil && strings.TrimSpace(*req.Status) != "" {
statusDetail = " status=" + strings.ToLower(strings.TrimSpace(*req.Status))
}
writeHostOrgAuditLog(c, h.audit, "update-manager", "manager_profiles", item.UserID,
fmt.Sprintf("user_id=%d permissions_updated=true", item.UserID))
fmt.Sprintf("user_id=%d permissions_updated=true%s", item.UserID, statusDetail))
response.OK(c, item)
}

View File

@ -652,8 +652,8 @@ func (r *Reader) CreateManagerProfile(ctx context.Context, userID int64, actorID
return items[0], nil
}
// UpdateManagerProfile 局部更新经理运营资料和经理中心权限。
// 未传入的布尔权限不参与 SET避免后台只编辑联系方式时把现有权限误恢复成默认值。
// UpdateManagerProfile 局部更新经理运营资料、身份状态和经理中心权限。
// 未传入的状态/布尔权限不参与 SET避免后台只编辑联系方式时把现有开关误恢复成默认值。
func (r *Reader) UpdateManagerProfile(ctx context.Context, userID int64, actorID int64, req updateManagerRequest) (*ManagerListItem, error) {
if r == nil || r.db == nil {
return nil, errUserDBNotConfigured()
@ -671,6 +671,9 @@ func (r *Reader) UpdateManagerProfile(ctx context.Context, userID int64, actorID
assignments = append(assignments, "contact_info = ?")
args = append(args, contact)
}
if err := req.appendStatusAssignment(&assignments, &args); err != nil {
return nil, err
}
req.appendPermissionAssignments(&assignments, &args)
args = append(args, appctx.FromContext(ctx), userID)
// 权限更新只作用于已存在经理RowsAffected=0 时返回 not found避免 PUT 顺手创建新经理绕过创建审计。

View File

@ -0,0 +1,87 @@
package hostorg
import (
"context"
"testing"
"hyapp-admin-server/internal/appctx"
"github.com/DATA-DOG/go-sqlmock"
)
func TestUpdateManagerProfilePersistsStatus(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("new sqlmock: %v", err)
}
defer db.Close()
status := "disabled"
reader := NewReader(db, nil, nil)
ctx := appctx.WithContext(context.Background(), "lalu")
// 经理状态是 manager_profiles 的身份开关PUT 传 status 时必须落到 UPDATE否则前端刷新会重新读到旧状态。
mock.ExpectExec(`(?s)UPDATE manager_profiles\s+SET updated_at_ms = \?, status = \?\s+WHERE app_code = \? AND user_id = \?`).
WithArgs(sqlmock.AnyArg(), "disabled", "lalu", int64(1003)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectQuery(`(?s)SELECT COUNT\(DISTINCT mp\.user_id\).*FROM manager_profiles mp`).
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(int64(1)))
mock.ExpectQuery(`(?s)SELECT mp\.user_id, COALESCE\(manager\.current_display_user_id, ''\).*FROM manager_profiles mp`).
WillReturnRows(managerListRows().
AddRow(int64(1003), "1003", "manager", "avatar.png", int64(7), "西亚区", "disabled", "+63",
true, true, true, true, true, true, true, true, true, true,
int64(0), int64(0), int64(1710000000000), int64(1710000001000)))
item, err := reader.UpdateManagerProfile(ctx, 1003, 7, updateManagerRequest{Status: &status})
if err != nil {
t.Fatalf("update manager profile: %v", err)
}
if item.Status != "disabled" {
t.Fatalf("manager status = %q, want disabled", item.Status)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatalf("sql expectations: %v", err)
}
}
func TestUpdateManagerProfileRejectsInvalidStatus(t *testing.T) {
db, _, err := sqlmock.New()
if err != nil {
t.Fatalf("new sqlmock: %v", err)
}
defer db.Close()
status := "off"
reader := NewReader(db, nil, nil)
_, err = reader.UpdateManagerProfile(appctx.WithContext(context.Background(), "lalu"), 1003, 7, updateManagerRequest{Status: &status})
if err == nil || err.Error() != "manager status is invalid" {
t.Fatalf("invalid status error = %v", err)
}
}
func managerListRows() *sqlmock.Rows {
return sqlmock.NewRows([]string{
"user_id",
"current_display_user_id",
"username",
"avatar",
"region_id",
"region_name",
"status",
"contact_info",
"can_grant_avatar_frame",
"can_grant_vehicle",
"can_grant_badge",
"can_grant_vip",
"can_update_user_level",
"can_add_bd_leader",
"can_add_admin",
"can_add_superadmin",
"can_block_user",
"can_transfer_user_country",
"bd_leader_count",
"last_invited_at_ms",
"created_at_ms",
"updated_at_ms",
})
}

View File

@ -83,6 +83,7 @@ type createManagerRequest struct {
type updateManagerRequest struct {
Contact *string `json:"contact"`
Status *string `json:"status"`
CanGrantAvatarFrame *bool `json:"canGrantAvatarFrame"`
CanGrantVehicle *bool `json:"canGrantVehicle"`
CanGrantBadge *bool `json:"canGrantBadge"`
@ -95,6 +96,23 @@ type updateManagerRequest struct {
CanTransferCountry *bool `json:"canTransferUserCountry"`
}
func (r updateManagerRequest) appendStatusAssignment(assignments *[]string, args *[]any) error {
if r.Status == nil {
return nil
}
status := strings.ToLower(strings.TrimSpace(*r.Status))
if status == "" {
return nil
}
if status != "active" && status != "disabled" {
return fmt.Errorf("manager status is invalid")
}
// 经理状态属于 manager_profiles 自身身份开关;停用经理只关闭经理中心身份,不改用户主状态或其他 Host/BD 关系。
*assignments = append(*assignments, "status = ?")
*args = append(*args, status)
return nil
}
type managerPermissions struct {
CanGrantAvatarFrame bool
CanGrantVehicle bool