package resource import ( "context" "strings" "hyapp-admin-server/internal/appctx" "hyapp-admin-server/internal/model" ) const ( grantSourceAdmin = "admin" grantSourceManagerCenter = "manager_center" ) type adminOperator struct { UserID uint Username string Name string } type managerOperator struct { UserID int64 DisplayUserID string Username string Avatar string } func (h *Handler) enrichGrantTargets(ctx context.Context, grants []grantDTO) error { ids := collectGrantTargetIDs(grants) if len(ids) == 0 { return nil } users := make(map[int64]grantUserDTO, len(ids)) if h.userDB != nil { var err error users, err = h.queryGrantTargetUsers(ctx, ids) if err != nil { return err } } applyGrantTargetUsers(grants, users) return nil } func (h *Handler) queryGrantTargetUsers(ctx context.Context, ids []int64) (map[int64]grantUserDTO, error) { out := make(map[int64]grantUserDTO, len(ids)) if len(ids) == 0 || h.userDB == nil { return out, nil } args := append([]any{appctx.FromContext(ctx)}, int64Args(ids)...) rows, err := h.userDB.QueryContext(ctx, ` SELECT user_id, current_display_user_id, COALESCE(username, ''), COALESCE(avatar, '') FROM users WHERE app_code = ? AND user_id IN (`+placeholders(len(ids))+`) `, args...) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var user grantUserDTO if err := rows.Scan(&user.UserID, &user.DisplayUserID, &user.Username, &user.Avatar); err != nil { return nil, err } out[user.UserID] = user } return out, rows.Err() } func applyGrantTargetUsers(grants []grantDTO, users map[int64]grantUserDTO) { for index := range grants { targetUserID := grants[index].TargetUserID if targetUserID <= 0 { continue } if user, ok := users[targetUserID]; ok { grants[index].TargetUser = &user continue } grants[index].TargetUser = &grantUserDTO{UserID: targetUserID} } } func (h *Handler) enrichGrantOperators(ctx context.Context, grants []grantDTO) error { // operator_user_id 在 wallet 发放事实里只保存稳定 ID;后台列表按来源补展示资料,避免把 admin 用户和 app 用户混成同一张表。 adminIDs, managerIDs := collectGrantOperatorIDs(grants) admins, err := h.queryAdminOperators(adminIDs) if err != nil { return err } managers, err := h.queryManagerOperators(ctx, managerIDs) if err != nil { return err } applyGrantOperators(grants, admins, managers) return nil } func (h *Handler) queryAdminOperators(ids []uint) (map[uint]adminOperator, error) { out := make(map[uint]adminOperator, len(ids)) if len(ids) == 0 || h.store == nil { return out, nil } users, err := h.store.UsersByIDs(ids) if err != nil { return nil, err } for id, user := range users { out[id] = adminOperatorFromModel(user) } return out, nil } func (h *Handler) queryManagerOperators(ctx context.Context, ids []int64) (map[int64]managerOperator, error) { out := make(map[int64]managerOperator, len(ids)) if len(ids) == 0 || h.userDB == nil { return out, nil } // 经理中心发放人是 app 用户,只读 user-service 的用户库投影;资源发放事实仍然以 wallet-service 返回为准。 args := append([]any{appctx.FromContext(ctx)}, int64Args(ids)...) rows, err := h.userDB.QueryContext(ctx, ` SELECT user_id, current_display_user_id, COALESCE(username, ''), COALESCE(avatar, '') FROM users WHERE app_code = ? AND user_id IN (`+placeholders(len(ids))+`) `, args...) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var operator managerOperator if err := rows.Scan(&operator.UserID, &operator.DisplayUserID, &operator.Username, &operator.Avatar); err != nil { return nil, err } out[operator.UserID] = operator } return out, rows.Err() } func applyGrantOperators(grants []grantDTO, admins map[uint]adminOperator, managers map[int64]managerOperator) { for index := range grants { source := normalizeGrantSource(grants[index].GrantSource) operatorID := grants[index].OperatorUserID if operatorID <= 0 { continue } // 即使资料缺失也返回 source + user_id,前端仍能展示可追溯的操作人线索。 operator := &operatorDTO{ Source: source, UserID: operatorID, } switch source { case grantSourceAdmin: if admin, ok := admins[uint(operatorID)]; ok { operator.Username = admin.Username operator.Name = admin.Name } case grantSourceManagerCenter: if manager, ok := managers[operatorID]; ok { operator.DisplayUserID = manager.DisplayUserID operator.Username = manager.Username operator.Avatar = manager.Avatar } } grants[index].Operator = operator } } func collectGrantOperatorIDs(grants []grantDTO) ([]uint, []int64) { adminSet := make(map[uint]struct{}) managerSet := make(map[int64]struct{}) for _, grant := range grants { if grant.OperatorUserID <= 0 { continue } switch normalizeGrantSource(grant.GrantSource) { case grantSourceAdmin: adminSet[uint(grant.OperatorUserID)] = struct{}{} case grantSourceManagerCenter: managerSet[grant.OperatorUserID] = struct{}{} } } adminIDs := make([]uint, 0, len(adminSet)) for id := range adminSet { adminIDs = append(adminIDs, id) } managerIDs := make([]int64, 0, len(managerSet)) for id := range managerSet { managerIDs = append(managerIDs, id) } return adminIDs, managerIDs } func collectGrantTargetIDs(grants []grantDTO) []int64 { targetSet := make(map[int64]struct{}) for _, grant := range grants { if grant.TargetUserID <= 0 { continue } targetSet[grant.TargetUserID] = struct{}{} } targetIDs := make([]int64, 0, len(targetSet)) for id := range targetSet { targetIDs = append(targetIDs, id) } return targetIDs } func adminOperatorFromModel(user model.User) adminOperator { return adminOperator{ UserID: user.ID, Username: user.Username, Name: user.Name, } } func normalizeGrantSource(source string) string { return strings.ToLower(strings.TrimSpace(source)) } func placeholders(count int) string { if count <= 0 { return "" } items := make([]string, count) for index := range items { items[index] = "?" } return strings.Join(items, ",") } func int64Args(values []int64) []any { args := make([]any, 0, len(values)) for _, value := range values { args = append(args, value) } return args }