231 lines
7.0 KiB
Go
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
|
|
}
|