package appuser import ( "database/sql" "errors" "fmt" "strconv" "strings" "hyapp-admin-server/internal/config" "hyapp-admin-server/internal/integration/activityclient" "hyapp-admin-server/internal/integration/userclient" "hyapp-admin-server/internal/middleware" "hyapp-admin-server/internal/modules/shared" "hyapp-admin-server/internal/response" "github.com/gin-gonic/gin" ) type Handler struct { service *Service cfg config.Config audit shared.OperationLogger } func New(userClient userclient.Client, activityClient activityclient.Client, adminDB *sql.DB, userDB *sql.DB, walletDB *sql.DB, cfg config.Config, audit shared.OperationLogger) *Handler { return &Handler{ service: NewService(userClient, activityClient, adminDB, userDB, walletDB), cfg: cfg, audit: audit, } } func (h *Handler) ListUsers(c *gin.Context) { query := parseListQuery(c) items, total, err := h.service.ListUsers(c.Request.Context(), query) if err != nil { response.ServerError(c, "获取 App 用户列表失败") return } response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total}) } func (h *Handler) ListLoginLogs(c *gin.Context) { query := parseLoginLogQuery(c) items, total, err := h.service.ListLoginLogs(c.Request.Context(), query) if err != nil { response.ServerError(c, "获取登录日志失败") return } response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total}) } func (h *Handler) ListBannedUsers(c *gin.Context) { query := parseBanListQuery(c) items, total, err := h.service.ListBannedUsers(c.Request.Context(), query) if err != nil { response.ServerError(c, "获取封禁列表失败") return } response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total}) } func (h *Handler) ListUserLoginLogs(c *gin.Context) { userID, ok := parseInt64ID(c, "id") if !ok { return } query := parseLoginLogQuery(c) query.UserID = userID items, total, err := h.service.ListLoginLogs(c.Request.Context(), query) if err != nil { response.ServerError(c, "获取用户登录日志失败") return } response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total}) } func (h *Handler) GetUser(c *gin.Context) { userID, ok := parseInt64ID(c, "id") if !ok { return } user, err := h.service.GetUser(c.Request.Context(), userID) if err != nil { writeReadError(c, err, "获取 App 用户失败") return } response.OK(c, user) } func (h *Handler) UpdateUser(c *gin.Context) { userID, ok := parseInt64ID(c, "id") if !ok { return } var req updateUserRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "用户参数不正确") return } user, err := h.service.UpdateUser(c.Request.Context(), userID, middleware.CurrentRequestID(c), req) if err != nil { writeMutationError(c, err, "更新 App 用户失败") return } detail := "update profile" if req.Country != nil { detail = fmt.Sprintf("update country=%s region_id=%d", user.Country, user.RegionID) } writeAppUserAuditLog(c, h.audit, "update-app-user", "app_users", userID, "success", detail) response.OK(c, user) } func (h *Handler) BanUser(c *gin.Context) { h.setUserStatus(c, "banned", "ban-app-user", "用户已封禁") } func (h *Handler) UnbanUser(c *gin.Context) { h.setUserStatus(c, "active", "unban-app-user", "用户已解封") } func (h *Handler) SetPassword(c *gin.Context) { userID, ok := parseInt64ID(c, "id") if !ok { return } var req setPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "密码参数不正确") return } if err := h.service.SetPassword(c.Request.Context(), userID, req.Password); err != nil { writeMutationError(c, err, "设置 App 用户密码失败") return } writeAppUserAuditLog(c, h.audit, "set-app-user-password", "app_users", userID, "success", "set password") response.OK(c, gin.H{"passwordSet": true}) } func (h *Handler) setUserStatus(c *gin.Context, status string, action string, successMessage string) { userID, ok := parseInt64ID(c, "id") if !ok { return } result, err := h.service.SetUserStatus(c.Request.Context(), userID, status, int64(middleware.CurrentUserID(c)), middleware.CurrentRequestID(c)) if err != nil { writeMutationError(c, err, successMessage+"失败") return } writeAppUserAuditLog(c, h.audit, action, "app_users", userID, "success", fmt.Sprintf("status=%s", status)) response.OK(c, result) } func parseListQuery(c *gin.Context) listQuery { options := shared.ListOptions(c) return normalizeListQuery(listQuery{ Page: options.Page, PageSize: options.PageSize, Keyword: options.Keyword, Status: options.Status, }) } func parseLoginLogQuery(c *gin.Context) loginLogQuery { options := shared.ListOptions(c) regionID, regionIDSet := queryOptionalInt64(c, "region_id", "regionId") return normalizeLoginLogQuery(loginLogQuery{ Page: options.Page, PageSize: options.PageSize, Keyword: options.Keyword, UserID: queryInt64(c, "user_id", "userId"), DisplayUserID: firstQuery(c, "display_user_id", "displayUserId"), IP: firstQuery(c, "ip"), IPCountryCode: firstQuery(c, "ip_country_code", "ipCountryCode"), Channel: firstQuery(c, "channel"), Platform: firstQuery(c, "platform"), Result: firstQuery(c, "result"), Blocked: firstQuery(c, "blocked"), RegionID: regionID, RegionIDSet: regionIDSet, StartMs: queryInt64(c, "start_ms", "startMs"), EndMs: queryInt64(c, "end_ms", "endMs"), }) } func parseBanListQuery(c *gin.Context) banListQuery { options := shared.ListOptions(c) return normalizeBanListQuery(banListQuery{ Page: options.Page, PageSize: options.PageSize, TargetKeyword: firstQuery(c, "target_keyword", "targetKeyword", "keyword"), OperatorKeyword: firstQuery(c, "operator_keyword", "operatorKeyword"), StartMs: queryInt64(c, "start_ms", "startMs"), EndMs: queryInt64(c, "end_ms", "endMs"), }) } func firstQuery(c *gin.Context, names ...string) string { for _, name := range names { if value := strings.TrimSpace(c.Query(name)); value != "" { return value } } return "" } func queryInt64(c *gin.Context, names ...string) int64 { value := firstQuery(c, names...) if value == "" { return 0 } parsed, err := strconv.ParseInt(value, 10, 64) if err != nil || parsed < 0 { return 0 } return parsed } func queryOptionalInt64(c *gin.Context, names ...string) (int64, bool) { value := firstQuery(c, names...) if value == "" { return 0, false } parsed, err := strconv.ParseInt(value, 10, 64) if err != nil || parsed < 0 { return 0, false } return parsed, true } func parseInt64ID(c *gin.Context, name string) (int64, bool) { raw := strings.TrimSpace(c.Param(name)) id, err := strconv.ParseInt(raw, 10, 64) if err != nil || id <= 0 { response.BadRequest(c, "ID 参数不正确") return 0, false } return id, true } func writeReadError(c *gin.Context, err error, fallback string) { if errors.Is(err, ErrNotFound) { response.NotFound(c, "App 用户不存在") return } response.ServerError(c, fallback) } func writeMutationError(c *gin.Context, err error, fallback string) { if errors.Is(err, ErrNotFound) { response.NotFound(c, "App 用户不存在") return } if errors.Is(err, ErrInvalidArgument) { response.BadRequest(c, err.Error()) return } response.ServerError(c, fallback) } func writeAppUserAuditLog(c *gin.Context, logger shared.OperationLogger, action string, resource string, resourceID int64, status string, detail string) { shared.OperationLogWithResourceID(c, logger, action, resource, fmt.Sprintf("%d", resourceID), status, detail) }