255 lines
8.3 KiB
Go

package redpacket
import (
"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}
}
type configRequest struct {
Enabled bool `json:"enabled"`
CountTiers []int32 `json:"count_tiers"`
AmountTiers []int64 `json:"amount_tiers"`
DelayedOpenSeconds int32 `json:"delayed_open_seconds"`
ExpireSeconds int32 `json:"expire_seconds"`
DailySendLimit int32 `json:"daily_send_limit"`
}
type configDTO struct {
AppCode string `json:"app_code"`
Enabled bool `json:"enabled"`
CountTiers []int32 `json:"count_tiers"`
AmountTiers []int64 `json:"amount_tiers"`
DelayedOpenSeconds int32 `json:"delayed_open_seconds"`
ExpireSeconds int32 `json:"expire_seconds"`
DailySendLimit int32 `json:"daily_send_limit"`
UpdatedByAdminID int64 `json:"updated_by_admin_id"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type packetDTO struct {
AppCode string `json:"app_code"`
PacketID string `json:"packet_id"`
CommandID string `json:"command_id"`
SenderUserID int64 `json:"sender_user_id"`
RoomID string `json:"room_id"`
RegionID int64 `json:"region_id"`
PacketType string `json:"packet_type"`
TotalAmount int64 `json:"total_amount"`
PacketCount int32 `json:"packet_count"`
RemainingAmount int64 `json:"remaining_amount"`
RemainingCount int32 `json:"remaining_count"`
Status string `json:"status"`
OpenAtMS int64 `json:"open_at_ms"`
ExpiresAtMS int64 `json:"expires_at_ms"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
Claims []claimDTO `json:"claims,omitempty"`
}
type claimDTO struct {
AppCode string `json:"app_code"`
ClaimID string `json:"claim_id"`
CommandID string `json:"command_id"`
PacketID string `json:"packet_id"`
UserID int64 `json:"user_id"`
Amount int64 `json:"amount"`
WalletTransactionID string `json:"wallet_transaction_id"`
Status string `json:"status"`
FailureReason string `json:"failure_reason"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
func (h *Handler) GetConfig(c *gin.Context) {
if h.wallet == nil {
response.ServerError(c, "钱包服务未配置")
return
}
resp, err := h.wallet.GetRedPacketConfig(c.Request.Context(), &walletv1.GetRedPacketConfigRequest{
AppCode: appctx.FromContext(c.Request.Context()),
})
if err != nil {
response.ServerError(c, "获取红包配置失败")
return
}
response.OK(c, configFromProto(resp.GetConfig()))
}
func (h *Handler) UpdateConfig(c *gin.Context) {
if h.wallet == nil {
response.ServerError(c, "钱包服务未配置")
return
}
var req configRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "红包配置参数不正确")
return
}
resp, err := h.wallet.UpdateRedPacketConfig(c.Request.Context(), &walletv1.UpdateRedPacketConfigRequest{
AppCode: appctx.FromContext(c.Request.Context()),
Enabled: req.Enabled,
CountTiers: req.CountTiers,
AmountTiers: req.AmountTiers,
DelayedOpenSeconds: req.DelayedOpenSeconds,
ExpireSeconds: req.ExpireSeconds,
DailySendLimit: req.DailySendLimit,
OperatorUserId: int64(middleware.CurrentUserID(c)),
})
if err != nil {
response.BadRequest(c, err.Error())
return
}
item := configFromProto(resp.GetConfig())
shared.OperationLogWithResourceID(c, h.audit, "update-red-packet-config", "red_packet_configs", item.AppCode, "success", "")
response.OK(c, item)
}
func (h *Handler) ListPackets(c *gin.Context) {
if h.wallet == nil {
response.ServerError(c, "钱包服务未配置")
return
}
options := shared.ListOptions(c)
req := &walletv1.ListRedPacketsRequest{
AppCode: appctx.FromContext(c.Request.Context()),
RoomId: strings.TrimSpace(c.Query("room_id")),
PacketType: strings.TrimSpace(c.Query("packet_type")),
Status: strings.TrimSpace(options.Status),
Page: int32(options.Page),
PageSize: int32(options.PageSize),
SenderUserId: parseInt64Query(c, "sender_user_id"),
RegionId: parseInt64Query(c, "region_id"),
StartAtMs: parseInt64Query(c, "start_at_ms"),
EndAtMs: parseInt64Query(c, "end_at_ms"),
}
resp, err := h.wallet.ListRedPackets(c.Request.Context(), req)
if err != nil {
response.ServerError(c, "获取红包列表失败")
return
}
items := make([]packetDTO, 0, len(resp.GetPackets()))
for _, packet := range resp.GetPackets() {
items = append(items, packetFromProto(packet))
}
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}
func (h *Handler) GetPacket(c *gin.Context) {
if h.wallet == nil {
response.ServerError(c, "钱包服务未配置")
return
}
packetID := strings.TrimSpace(c.Param("packet_id"))
if packetID == "" {
response.BadRequest(c, "红包 ID 不正确")
return
}
resp, err := h.wallet.GetRedPacket(c.Request.Context(), &walletv1.GetRedPacketRequest{
AppCode: appctx.FromContext(c.Request.Context()),
PacketId: packetID,
IncludeClaims: true,
})
if err != nil {
response.ServerError(c, "获取红包详情失败")
return
}
response.OK(c, packetFromProto(resp.GetPacket()))
}
func configFromProto(config *walletv1.RedPacketConfig) configDTO {
if config == nil {
return configDTO{}
}
return configDTO{
AppCode: config.GetAppCode(),
Enabled: config.GetEnabled(),
CountTiers: append([]int32(nil), config.GetCountTiers()...),
AmountTiers: append([]int64(nil), config.GetAmountTiers()...),
DelayedOpenSeconds: config.GetDelayedOpenSeconds(),
ExpireSeconds: config.GetExpireSeconds(),
DailySendLimit: config.GetDailySendLimit(),
UpdatedByAdminID: config.GetUpdatedByAdminId(),
CreatedAtMS: config.GetCreatedAtMs(),
UpdatedAtMS: config.GetUpdatedAtMs(),
}
}
func packetFromProto(packet *walletv1.RedPacket) packetDTO {
if packet == nil {
return packetDTO{}
}
claims := make([]claimDTO, 0, len(packet.GetClaims()))
for _, claim := range packet.GetClaims() {
claims = append(claims, claimFromProto(claim))
}
return packetDTO{
AppCode: packet.GetAppCode(),
PacketID: packet.GetPacketId(),
CommandID: packet.GetCommandId(),
SenderUserID: packet.GetSenderUserId(),
RoomID: packet.GetRoomId(),
RegionID: packet.GetRegionId(),
PacketType: packet.GetPacketType(),
TotalAmount: packet.GetTotalAmount(),
PacketCount: packet.GetPacketCount(),
RemainingAmount: packet.GetRemainingAmount(),
RemainingCount: packet.GetRemainingCount(),
Status: packet.GetStatus(),
OpenAtMS: packet.GetOpenAtMs(),
ExpiresAtMS: packet.GetExpiresAtMs(),
CreatedAtMS: packet.GetCreatedAtMs(),
UpdatedAtMS: packet.GetUpdatedAtMs(),
Claims: claims,
}
}
func claimFromProto(claim *walletv1.RedPacketClaim) claimDTO {
if claim == nil {
return claimDTO{}
}
return claimDTO{
AppCode: claim.GetAppCode(),
ClaimID: claim.GetClaimId(),
CommandID: claim.GetCommandId(),
PacketID: claim.GetPacketId(),
UserID: claim.GetUserId(),
Amount: claim.GetAmount(),
WalletTransactionID: claim.GetWalletTransactionId(),
Status: claim.GetStatus(),
FailureReason: claim.GetFailureReason(),
CreatedAtMS: claim.GetCreatedAtMs(),
UpdatedAtMS: claim.GetUpdatedAtMs(),
}
}
func parseInt64Query(c *gin.Context, key string) int64 {
value := strings.TrimSpace(c.Query(key))
if value == "" {
return 0
}
parsed, _ := strconv.ParseInt(value, 10, 64)
if parsed < 0 {
return 0
}
return parsed
}