package resource import ( "fmt" "strconv" "strings" "hyapp-admin-server/internal/appctx" "hyapp-admin-server/internal/integration/walletclient" "hyapp-admin-server/internal/middleware" "hyapp-admin-server/internal/modules/shared" "hyapp-admin-server/internal/response" walletv1 "hyapp.local/api/proto/wallet/v1" "github.com/gin-gonic/gin" ) type Handler struct { wallet walletclient.Client audit shared.OperationLogger } func New(wallet walletclient.Client, audit shared.OperationLogger) *Handler { return &Handler{wallet: wallet, audit: audit} } func (h *Handler) ListResources(c *gin.Context) { options := shared.ListOptions(c) resp, err := h.wallet.ListResources(c.Request.Context(), &walletv1.ListResourcesRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), ResourceType: strings.TrimSpace(c.Query("resource_type")), Status: options.Status, Keyword: options.Keyword, Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取资源列表失败") return } items := make([]resourceDTO, 0, len(resp.GetResources())) for _, item := range resp.GetResources() { items = append(items, resourceFromProto(item)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) GetResource(c *gin.Context) { resourceID, ok := parseID(c, "resource_id") if !ok { return } resp, err := h.wallet.GetResource(c.Request.Context(), &walletv1.GetResourceRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), ResourceId: resourceID, }) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, resourceFromProto(resp.GetResource())) } func (h *Handler) CreateResource(c *gin.Context) { var req resourceRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "资源参数不正确") return } resp, err := h.wallet.CreateResource(c.Request.Context(), req.createProto(c)) if err != nil { response.BadRequest(c, err.Error()) return } resource := resourceFromProto(resp.GetResource()) h.auditLog(c, "create-resource", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", resource.ResourceCode) response.Created(c, resource) } func (h *Handler) UpdateResource(c *gin.Context) { resourceID, ok := parseID(c, "resource_id") if !ok { return } var req resourceRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "资源参数不正确") return } protoReq := req.updateProto(c, resourceID) resp, err := h.wallet.UpdateResource(c.Request.Context(), protoReq) if err != nil { response.BadRequest(c, err.Error()) return } resource := resourceFromProto(resp.GetResource()) h.auditLog(c, "update-resource", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", resource.ResourceCode) response.OK(c, resource) } func (h *Handler) EnableResource(c *gin.Context) { h.setResourceStatus(c, "active") } func (h *Handler) DisableResource(c *gin.Context) { h.setResourceStatus(c, "disabled") } func (h *Handler) ListResourceGroups(c *gin.Context) { options := shared.ListOptions(c) resp, err := h.wallet.ListResourceGroups(c.Request.Context(), &walletv1.ListResourceGroupsRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), Status: options.Status, Keyword: options.Keyword, Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取资源组列表失败") return } items := make([]resourceGroupDTO, 0, len(resp.GetGroups())) for _, item := range resp.GetGroups() { items = append(items, resourceGroupFromProto(item)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) GetResourceGroup(c *gin.Context) { groupID, ok := parseID(c, "group_id") if !ok { return } resp, err := h.wallet.GetResourceGroup(c.Request.Context(), &walletv1.GetResourceGroupRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GroupId: groupID, }) if err != nil { response.BadRequest(c, err.Error()) return } response.OK(c, resourceGroupFromProto(resp.GetGroup())) } func (h *Handler) CreateResourceGroup(c *gin.Context) { var req resourceGroupRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "资源组参数不正确") return } resp, err := h.wallet.CreateResourceGroup(c.Request.Context(), req.createProto(c)) if err != nil { response.BadRequest(c, err.Error()) return } group := resourceGroupFromProto(resp.GetGroup()) h.auditLog(c, "create-resource-group", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", group.GroupCode) response.Created(c, group) } func (h *Handler) UpdateResourceGroup(c *gin.Context) { groupID, ok := parseID(c, "group_id") if !ok { return } var req resourceGroupRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "资源组参数不正确") return } resp, err := h.wallet.UpdateResourceGroup(c.Request.Context(), req.updateProto(c, groupID)) if err != nil { response.BadRequest(c, err.Error()) return } group := resourceGroupFromProto(resp.GetGroup()) h.auditLog(c, "update-resource-group", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", group.GroupCode) response.OK(c, group) } func (h *Handler) EnableResourceGroup(c *gin.Context) { h.setResourceGroupStatus(c, "active") } func (h *Handler) DisableResourceGroup(c *gin.Context) { h.setResourceGroupStatus(c, "disabled") } func (h *Handler) ListGifts(c *gin.Context) { options := shared.ListOptions(c) regionID, filterRegion, ok := optionalInt64QueryWithPresence(c, "regionId", "region_id") if !ok { return } resp, err := h.wallet.ListGiftConfigs(c.Request.Context(), &walletv1.ListGiftConfigsRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), Status: options.Status, Keyword: options.Keyword, Page: int32(options.Page), PageSize: int32(options.PageSize), RegionId: regionID, FilterRegion: filterRegion, }) if err != nil { response.ServerError(c, "获取礼物列表失败") return } items := make([]giftDTO, 0, len(resp.GetGifts())) for _, item := range resp.GetGifts() { items = append(items, giftFromProto(item)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) CreateGift(c *gin.Context) { var req giftRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "礼物参数不正确") return } resp, err := h.wallet.CreateGiftConfig(c.Request.Context(), req.createProto(c)) if err != nil { response.BadRequest(c, err.Error()) return } gift := giftFromProto(resp.GetGift()) h.auditLog(c, "create-gift", "gift_configs", gift.GiftID, "success", gift.Name) response.Created(c, gift) } func (h *Handler) UpdateGift(c *gin.Context) { giftID := strings.TrimSpace(c.Param("gift_id")) if giftID == "" { response.BadRequest(c, "ID 参数不正确") return } var req giftRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "礼物参数不正确") return } resp, err := h.wallet.UpdateGiftConfig(c.Request.Context(), req.updateProto(c, giftID)) if err != nil { response.BadRequest(c, err.Error()) return } gift := giftFromProto(resp.GetGift()) h.auditLog(c, "update-gift", "gift_configs", gift.GiftID, "success", gift.Name) response.OK(c, gift) } func (h *Handler) EnableGift(c *gin.Context) { h.setGiftStatus(c, "active") } func (h *Handler) DisableGift(c *gin.Context) { h.setGiftStatus(c, "disabled") } func (h *Handler) GrantResource(c *gin.Context) { var req grantResourceRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "资源赠送参数不正确") return } resp, err := h.wallet.GrantResource(c.Request.Context(), &walletv1.GrantResourceRequest{ CommandId: strings.TrimSpace(req.CommandID), AppCode: appctx.FromContext(c.Request.Context()), TargetUserId: req.TargetUserID, ResourceId: req.ResourceID, Quantity: req.Quantity, DurationMs: req.DurationMS, Reason: strings.TrimSpace(req.Reason), OperatorUserId: actorID(c), GrantSource: "admin", }) if err != nil { response.BadRequest(c, err.Error()) return } grant := grantFromProto(resp.GetGrant()) h.auditLog(c, "grant-resource", "resource_grants", grant.GrantID, "success", fmt.Sprintf("target_user_id=%d", grant.TargetUserID)) response.Created(c, grant) } func (h *Handler) GrantResourceGroup(c *gin.Context) { var req grantGroupRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "资源组赠送参数不正确") return } resp, err := h.wallet.GrantResourceGroup(c.Request.Context(), &walletv1.GrantResourceGroupRequest{ CommandId: strings.TrimSpace(req.CommandID), AppCode: appctx.FromContext(c.Request.Context()), TargetUserId: req.TargetUserID, GroupId: req.GroupID, Reason: strings.TrimSpace(req.Reason), OperatorUserId: actorID(c), GrantSource: "admin", }) if err != nil { response.BadRequest(c, err.Error()) return } grant := grantFromProto(resp.GetGrant()) h.auditLog(c, "grant-resource-group", "resource_grants", grant.GrantID, "success", fmt.Sprintf("target_user_id=%d", grant.TargetUserID)) response.Created(c, grant) } func (h *Handler) ListResourceGrants(c *gin.Context) { options := shared.ListOptions(c) targetUserID, ok := optionalInt64Query(c, "target_user_id", "targetUserId") if !ok { return } resp, err := h.wallet.ListResourceGrants(c.Request.Context(), &walletv1.ListResourceGrantsRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), TargetUserId: targetUserID, Status: options.Status, Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取资源赠送记录失败") return } items := make([]grantDTO, 0, len(resp.GetGrants())) for _, item := range resp.GetGrants() { items = append(items, grantFromProto(item)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) setResourceStatus(c *gin.Context, status string) { resourceID, ok := parseID(c, "resource_id") if !ok { return } resp, err := h.wallet.SetResourceStatus(c.Request.Context(), &walletv1.SetResourceStatusRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), ResourceId: resourceID, Status: status, OperatorUserId: actorID(c), }) if err != nil { response.BadRequest(c, err.Error()) return } resource := resourceFromProto(resp.GetResource()) h.auditLog(c, "set-resource-status", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", status) response.OK(c, resource) } func (h *Handler) setResourceGroupStatus(c *gin.Context, status string) { groupID, ok := parseID(c, "group_id") if !ok { return } resp, err := h.wallet.SetResourceGroupStatus(c.Request.Context(), &walletv1.SetResourceGroupStatusRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GroupId: groupID, Status: status, OperatorUserId: actorID(c), }) if err != nil { response.BadRequest(c, err.Error()) return } group := resourceGroupFromProto(resp.GetGroup()) h.auditLog(c, "set-resource-group-status", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", status) response.OK(c, group) } func (h *Handler) setGiftStatus(c *gin.Context, status string) { giftID := strings.TrimSpace(c.Param("gift_id")) if giftID == "" { response.BadRequest(c, "ID 参数不正确") return } resp, err := h.wallet.SetGiftConfigStatus(c.Request.Context(), &walletv1.SetGiftConfigStatusRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GiftId: giftID, Status: status, OperatorUserId: actorID(c), }) if err != nil { response.BadRequest(c, err.Error()) return } gift := giftFromProto(resp.GetGift()) h.auditLog(c, "set-gift-status", "gift_configs", gift.GiftID, "success", status) response.OK(c, gift) } func (h *Handler) auditLog(c *gin.Context, action string, resource string, resourceID string, status string, detail string) { shared.OperationLogWithResourceID(c, h.audit, action, resource, resourceID, status, detail) } func actorID(c *gin.Context) int64 { return int64(shared.ActorFromContext(c).UserID) } func parseID(c *gin.Context, name string) (int64, bool) { raw := strings.TrimSpace(c.Param(name)) value, err := strconv.ParseInt(raw, 10, 64) if err != nil || value <= 0 { response.BadRequest(c, "ID 参数不正确") return 0, false } return value, true } func optionalInt64Query(c *gin.Context, primary string, fallback string) (int64, bool) { value, _, ok := optionalInt64QueryWithPresence(c, primary, fallback) return value, ok } func optionalInt64QueryWithPresence(c *gin.Context, primary string, fallback string) (int64, bool, bool) { raw := strings.TrimSpace(c.Query(primary)) if raw == "" { raw = strings.TrimSpace(c.Query(fallback)) } if raw == "" { return 0, false, true } value, err := strconv.ParseInt(raw, 10, 64) if err != nil || value < 0 { response.BadRequest(c, primary+" 参数不正确") return 0, false, false } return value, true, true }