2026-05-11 01:40:29 +08:00

243 lines
6.8 KiB
Go

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, userDB *sql.DB, walletDB *sql.DB, cfg config.Config, audit shared.OperationLogger) *Handler {
return &Handler{
service: NewService(userClient, activityClient, 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) 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
}
user, err := h.service.SetUserStatus(c.Request.Context(), userID, status)
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, user)
}
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 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)
}