2026-05-08 18:55:47 +08:00

231 lines
7.0 KiB
Go

package voiceroomredpacket
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"time"
"chatapp3-golang/internal/model"
"gorm.io/gorm"
)
// Detail 返回红包详情和当前用户领取状态。
func (s *Service) Detail(ctx context.Context, user AuthUser, packetNo string) (*DetailResponse, error) {
packetNo = strings.TrimSpace(packetNo)
if packetNo == "" {
return nil, NewAppError(http.StatusBadRequest, "missing_packet_no", "packetNo is required")
}
packet, err := s.loadPacketByNo(ctx, packetNo)
if err != nil {
return nil, err
}
if packet == nil {
return nil, NewAppError(http.StatusNotFound, "voice_room_red_packet_not_found", "red packet not found")
}
claims, err := s.loadClaims(ctx, packet.ID)
if err != nil {
return nil, err
}
resp := &DetailResponse{
Packet: packetPayload(*packet),
Claims: make([]ClaimPayload, 0, len(claims)),
NowMillis: time.Now().UnixMilli(),
}
for _, claim := range claims {
payload := ClaimPayload{
ID: claim.ID,
UserID: claim.UserID,
UserRegionCode: claim.UserRegionCode,
Amount: claim.Amount,
WalletStatus: claim.WalletStatus,
ClaimTime: formatTime(claim.ClaimTime),
}
resp.Claims = append(resp.Claims, payload)
if claim.UserID == user.UserID {
resp.Grabbed = true
resp.MyAmount = claim.Amount
}
}
region, _ := s.resolveCachedUserRegion(ctx, normalizeSysOrigin(user.SysOrigin), user.UserID)
resp.Claimable = validateClaimable(*packet, region) == nil
return resp, nil
}
// ListClaimRecords 返回某个红包的成功领取列表,包含领取人资料、领取金额和领取时间。
func (s *Service) ListClaimRecords(ctx context.Context, _ AuthUser, packetNo string) (*ClaimRecordListResponse, error) {
packetNo = strings.TrimSpace(packetNo)
if packetNo == "" {
return nil, NewAppError(http.StatusBadRequest, "missing_packet_no", "packetNo is required")
}
packet, err := s.loadPacketByNo(ctx, packetNo)
if err != nil {
return nil, err
}
if packet == nil {
return nil, NewAppError(http.StatusNotFound, "voice_room_red_packet_not_found", "red packet not found")
}
claims, err := s.loadSuccessfulClaims(ctx, packet.ID)
if err != nil {
return nil, err
}
profiles, err := s.mapClaimUserProfiles(ctx, claims)
if err != nil {
return nil, err
}
resp := &ClaimRecordListResponse{
PacketNo: packet.PacketNo,
TotalCount: packet.TotalCount,
ClaimedCount: len(claims),
Records: make([]ClaimRecordPayload, 0, len(claims)),
}
for _, claim := range claims {
profile := profiles[claim.UserID]
resp.ClaimedAmount += claim.Amount
resp.Records = append(resp.Records, ClaimRecordPayload{
ID: claim.ID,
UserID: claim.UserID,
Account: defaultIfBlank(profile.Account, fmt.Sprint(claim.UserID)),
UserNickname: defaultIfBlank(profile.UserNickname, defaultIfBlank(profile.Account, fmt.Sprint(claim.UserID))),
UserAvatar: profile.UserAvatar,
CountryCode: profile.CountryCode,
CountryName: profile.CountryName,
UserRegionCode: claim.UserRegionCode,
Amount: claim.Amount,
WalletStatus: claim.WalletStatus,
ClaimTime: formatTime(claim.ClaimTime),
})
}
return resp, nil
}
// PageSentRecords 返回当前用户发出的红包记录。
func (s *Service) PageSentRecords(ctx context.Context, user AuthUser, cursor, limit int) (*SentRecordPageResponse, error) {
cursor, limit = normalizePage(cursor, limit)
var rows []model.VoiceRoomRedPacket
query := s.db.WithContext(ctx).
Where("sys_origin = ? AND sender_user_id = ?", normalizeSysOrigin(user.SysOrigin), user.UserID).
Order("create_time desc").
Order("id desc")
var total int64
if err := query.Model(&model.VoiceRoomRedPacket{}).Count(&total).Error; err != nil {
return nil, err
}
if err := query.Offset((cursor - 1) * limit).Limit(limit).Find(&rows).Error; err != nil {
return nil, err
}
return &SentRecordPageResponse{Records: packetPayloads(rows), Total: total}, nil
}
// PageAdminRecords 返回后台红包记录。
func (s *Service) PageAdminRecords(ctx context.Context, sysOrigin, status string, userID int64, cursor, limit int) (*SentRecordPageResponse, error) {
cursor, limit = normalizePage(cursor, limit)
query := s.db.WithContext(ctx).Model(&model.VoiceRoomRedPacket{})
if strings.TrimSpace(sysOrigin) != "" {
query = query.Where("sys_origin = ?", normalizeSysOrigin(sysOrigin))
}
if strings.TrimSpace(status) != "" {
query = query.Where("status = ?", strings.ToUpper(strings.TrimSpace(status)))
}
if userID > 0 {
query = query.Where("sender_user_id = ?", userID)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, err
}
var rows []model.VoiceRoomRedPacket
if err := query.Order("create_time desc").Order("id desc").Offset((cursor - 1) * limit).Limit(limit).Find(&rows).Error; err != nil {
return nil, err
}
return &SentRecordPageResponse{Records: packetPayloads(rows), Total: total}, nil
}
func (s *Service) loadClaims(ctx context.Context, packetID int64) ([]model.VoiceRoomRedPacketClaim, error) {
var rows []model.VoiceRoomRedPacketClaim
if err := s.db.WithContext(ctx).
Where("packet_id = ?", packetID).
Order("claim_time asc").
Find(&rows).Error; err != nil {
return nil, err
}
return rows, nil
}
func (s *Service) loadSuccessfulClaims(ctx context.Context, packetID int64) ([]model.VoiceRoomRedPacketClaim, error) {
var rows []model.VoiceRoomRedPacketClaim
if err := s.db.WithContext(ctx).
Where("packet_id = ? AND wallet_status = ?", packetID, walletStatusSuccess).
Order("claim_time asc").
Order("id asc").
Find(&rows).Error; err != nil {
return nil, err
}
return rows, nil
}
func (s *Service) mapClaimUserProfiles(ctx context.Context, claims []model.VoiceRoomRedPacketClaim) (map[int64]model.UserBaseInfo, error) {
userIDs := make([]int64, 0, len(claims))
seen := map[int64]struct{}{}
for _, claim := range claims {
if claim.UserID <= 0 {
continue
}
if _, exists := seen[claim.UserID]; exists {
continue
}
seen[claim.UserID] = struct{}{}
userIDs = append(userIDs, claim.UserID)
}
if len(userIDs) == 0 {
return map[int64]model.UserBaseInfo{}, nil
}
var users []model.UserBaseInfo
if err := s.db.WithContext(ctx).Where("id IN ?", userIDs).Find(&users).Error; err != nil {
return nil, err
}
result := make(map[int64]model.UserBaseInfo, len(users))
for _, user := range users {
result[user.ID] = user
}
return result, nil
}
func packetPayloads(rows []model.VoiceRoomRedPacket) []PacketPayload {
result := make([]PacketPayload, 0, len(rows))
for _, row := range rows {
result = append(result, packetPayload(row))
}
return result
}
func defaultIfBlank(value string, fallback string) string {
if strings.TrimSpace(value) == "" {
return fallback
}
return value
}
func ParseUserID(raw string) int64 {
raw = strings.TrimSpace(raw)
if raw == "" {
return 0
}
var userID int64
_, _ = fmt.Sscan(raw, &userID)
return userID
}
func ignoreRecordNotFound(err error) error {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
}