1022 lines
35 KiB
C#
1022 lines
35 KiB
C#
using HoneyBox.Core.Interfaces;
|
||
using HoneyBox.Model.Data;
|
||
using HoneyBox.Model.Entities;
|
||
using HoneyBox.Model.Models.Welfare;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace HoneyBox.Core.Services;
|
||
|
||
/// <summary>
|
||
/// 福利屋服务实现
|
||
/// </summary>
|
||
public class WelfareService : IWelfareService
|
||
{
|
||
private readonly HoneyBoxDbContext _dbContext;
|
||
private readonly ILogger<WelfareService> _logger;
|
||
private const int WelfareType = 15; // 福利屋商品类型
|
||
|
||
public WelfareService(HoneyBoxDbContext dbContext, ILogger<WelfareService> logger)
|
||
{
|
||
_dbContext = dbContext;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<WelfareListResponse> GetWelfareListAsync(int userId, int type, int page, int limit = 15)
|
||
{
|
||
// 验证type参数
|
||
if (type != 1 && type != 3)
|
||
{
|
||
throw new ArgumentException("参数错误");
|
||
}
|
||
|
||
// 计算用户消费总额(用于解锁金额判断)
|
||
decimal userTotalConsumption = 0;
|
||
|
||
if (userId > 0)
|
||
{
|
||
var userInfo = await _dbContext.Users
|
||
.Where(u => u.Id == userId)
|
||
.Select(u => new { u.IsTest })
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (userInfo != null && userInfo.IsTest > 0)
|
||
{
|
||
// 推广账号,门槛计算是全部的order_zhe_total
|
||
userTotalConsumption = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.OrderZheTotal);
|
||
}
|
||
else
|
||
{
|
||
// 普通用户,计算price + use_money
|
||
var orderPrice = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.Price);
|
||
var orderMoney = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.UseMoney);
|
||
userTotalConsumption = orderPrice + orderMoney;
|
||
}
|
||
}
|
||
|
||
// 构建查询
|
||
var now = DateTime.Now;
|
||
IQueryable<Good> query;
|
||
|
||
if (type == 1)
|
||
{
|
||
// type=1 进行中:Status=1, IsOpen=0, 且开奖时间未到
|
||
query = _dbContext.Goods
|
||
.Where(g => g.Status == 1
|
||
&& g.Type == WelfareType
|
||
&& g.IsOpen == 0
|
||
&& g.UnlockAmount <= userTotalConsumption
|
||
&& (g.OpenTime == null || g.OpenTime > now))
|
||
.OrderByDescending(g => g.Sort)
|
||
.ThenByDescending(g => g.Id);
|
||
}
|
||
else
|
||
{
|
||
// type=3 已结束:已开奖的 或 开奖时间已过但未开奖的
|
||
query = _dbContext.Goods
|
||
.Where(g => g.Type == WelfareType
|
||
&& g.UnlockAmount <= userTotalConsumption
|
||
&& (g.IsOpen == 1 || (g.OpenTime != null && g.OpenTime <= now)))
|
||
.OrderByDescending(g => g.OpenTime);
|
||
}
|
||
|
||
// 获取总数
|
||
var total = await query.CountAsync();
|
||
var lastPage = (int)Math.Ceiling((double)total / limit);
|
||
|
||
// 分页查询
|
||
var goods = await query
|
||
.Skip((page - 1) * limit)
|
||
.Take(limit)
|
||
.Select(g => new
|
||
{
|
||
g.Id,
|
||
g.Title,
|
||
g.ImgUrl,
|
||
g.Price,
|
||
g.Type,
|
||
g.NewIs,
|
||
g.QuanjuXiangou,
|
||
g.ChoujiangXianzhi,
|
||
g.FlwStartTime,
|
||
g.FlwEndTime,
|
||
g.OpenTime,
|
||
g.GoodsDescribe,
|
||
g.IsOpen
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取商品ID列表
|
||
var goodsIds = goods.Select(g => g.Id).ToList();
|
||
|
||
// 获取每个商品的奖品列表
|
||
var goodsListItems = await _dbContext.GoodsItems
|
||
.Where(gi => goodsIds.Contains(gi.GoodsId) && gi.Num == 0)
|
||
.Select(gi => new
|
||
{
|
||
gi.GoodsId,
|
||
gi.Title,
|
||
gi.ImgUrl,
|
||
gi.Stock,
|
||
gi.Price,
|
||
gi.ScMoney
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取参与次数
|
||
var joinCounts = await _dbContext.OrderItems
|
||
.Where(oi => goodsIds.Contains(oi.GoodsId ?? 0))
|
||
.GroupBy(oi => oi.GoodsId)
|
||
.Select(g => new { GoodsId = g.Key, Count = g.Count() })
|
||
.ToDictionaryAsync(x => x.GoodsId ?? 0, x => x.Count);
|
||
|
||
// 构建返回数据
|
||
var list = goods.Select(g => new WelfareItemDto
|
||
{
|
||
Id = g.Id,
|
||
Title = g.Title,
|
||
Imgurl = g.ImgUrl,
|
||
Price = g.Price,
|
||
Type = g.Type,
|
||
NewIs = g.NewIs,
|
||
QuanjuXiangou = g.QuanjuXiangou,
|
||
ChoujiangXianzhi = g.ChoujiangXianzhi,
|
||
FlwStartTime = g.FlwStartTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||
FlwEndTime = g.FlwEndTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||
OpenTime = g.OpenTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||
GoodsDescribe = g.GoodsDescribe ?? "",
|
||
IsOpen = g.IsOpen,
|
||
JoinCount = joinCounts.GetValueOrDefault(g.Id, 0),
|
||
Goodslist = goodsListItems
|
||
.Where(gi => gi.GoodsId == g.Id)
|
||
.Select(gi => new WelfareGoodsListItemDto
|
||
{
|
||
Title = gi.Title,
|
||
Imgurl = gi.ImgUrl,
|
||
Stock = gi.Stock,
|
||
Price = gi.Price,
|
||
ScMoney = gi.ScMoney
|
||
})
|
||
.ToList()
|
||
}).ToList();
|
||
|
||
return new WelfareListResponse
|
||
{
|
||
List = list,
|
||
LastPage = lastPage,
|
||
Total = total
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<WelfareDetailResponse> GetWelfareDetailAsync(int userId, int goodsId)
|
||
{
|
||
// 获取商品信息
|
||
var goods = await _dbContext.Goods
|
||
.Where(g => g.Id == goodsId)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (goods == null)
|
||
{
|
||
throw new InvalidOperationException("盒子不存在");
|
||
}
|
||
|
||
if (goods.Status != 1 && goods.Status != 3)
|
||
{
|
||
throw new InvalidOperationException("盒子已下架");
|
||
}
|
||
|
||
if (goods.Type != WelfareType)
|
||
{
|
||
throw new InvalidOperationException("该盒子不是福利屋类型");
|
||
}
|
||
|
||
// 检查用户是否可以查看该福利屋(解锁金额限制)
|
||
decimal userTotalConsumption = 0;
|
||
if (userId > 0)
|
||
{
|
||
var userInfo = await _dbContext.Users
|
||
.Where(u => u.Id == userId)
|
||
.Select(u => new { u.IsTest })
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (userInfo != null && userInfo.IsTest > 0)
|
||
{
|
||
// 推广账号
|
||
userTotalConsumption = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.OrderZheTotal);
|
||
}
|
||
else
|
||
{
|
||
var orderPrice = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.Price);
|
||
var orderMoney = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.UseMoney);
|
||
userTotalConsumption = orderPrice + orderMoney;
|
||
}
|
||
|
||
if (goods.UnlockAmount > userTotalConsumption)
|
||
{
|
||
throw new InvalidOperationException($"您需要消费满{goods.UnlockAmount}元才能查看此福利屋");
|
||
}
|
||
}
|
||
else if (goods.UnlockAmount > 0)
|
||
{
|
||
throw new InvalidOperationException($"您需要登录并充值满{goods.UnlockAmount}元才能查看此福利屋");
|
||
}
|
||
|
||
// 获取奖品列表
|
||
var goodsListItems = await _dbContext.GoodsItems
|
||
.Where(gi => gi.GoodsId == goodsId && gi.Num == 0)
|
||
.OrderByDescending(gi => gi.ShangId)
|
||
.ThenBy(gi => gi.Sort)
|
||
.Select(gi => new
|
||
{
|
||
gi.Id,
|
||
gi.Title,
|
||
gi.ImgUrl,
|
||
gi.ImgUrlDetail,
|
||
gi.Stock,
|
||
gi.Price,
|
||
gi.ScMoney,
|
||
gi.ShangId,
|
||
gi.SurplusStock,
|
||
gi.Sort
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取奖品等级信息
|
||
var shangIds = goodsListItems.Where(gi => gi.ShangId.HasValue).Select(gi => gi.ShangId!.Value).Distinct().ToList();
|
||
var shangInfos = await _dbContext.PrizeLevels
|
||
.Where(p => shangIds.Contains(p.Id))
|
||
.Select(p => new { p.Id, p.Title, p.Color })
|
||
.ToDictionaryAsync(p => p.Id);
|
||
|
||
// 统计参与人数
|
||
var joinCount = await _dbContext.OrderItems
|
||
.Where(oi => oi.GoodsId == goodsId && oi.OrderType == WelfareType)
|
||
.CountAsync();
|
||
|
||
// 统计用户参与次数
|
||
var userCount = userId > 0
|
||
? await _dbContext.OrderItems
|
||
.Where(oi => oi.GoodsId == goodsId && oi.UserId == userId && oi.OrderType == WelfareType)
|
||
.CountAsync()
|
||
: 0;
|
||
|
||
// 获取用户在活动期间的消费数据
|
||
var userConsumption = new UserConsumptionDto();
|
||
if (userId > 0 && goods.FlwStartTime.HasValue && goods.FlwEndTime.HasValue)
|
||
{
|
||
var startTimestamp = ToUnixTimestamp(goods.FlwStartTime.Value);
|
||
var endTimestamp = ToUnixTimestamp(goods.FlwEndTime.Value);
|
||
|
||
var consumptionData = await _dbContext.Orders
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 1
|
||
&& o.Addtime >= startTimestamp
|
||
&& o.Addtime <= endTimestamp)
|
||
.GroupBy(o => 1)
|
||
.Select(g => new
|
||
{
|
||
TotalAmount = g.Sum(o => o.OrderTotal),
|
||
OrderCount = g.Count()
|
||
})
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (consumptionData != null)
|
||
{
|
||
userConsumption.TotalAmount = consumptionData.TotalAmount;
|
||
userConsumption.OrderCount = consumptionData.OrderCount;
|
||
}
|
||
}
|
||
|
||
// 构建商品DTO
|
||
var goodsDto = new WelfareGoodsDto
|
||
{
|
||
Id = goods.Id,
|
||
Title = goods.Title,
|
||
Imgurl = goods.ImgUrl,
|
||
ImgurlDetail = !string.IsNullOrEmpty(goods.ImgUrlDetail) ? goods.ImgUrlDetail : goods.ImgUrl,
|
||
Price = goods.Price,
|
||
Type = goods.Type,
|
||
NewIs = goods.NewIs,
|
||
QuanjuXiangou = goods.QuanjuXiangou,
|
||
ChoujiangXianzhi = goods.ChoujiangXianzhi,
|
||
GoodsDescribe = goods.GoodsDescribe ?? "",
|
||
IsOpen = goods.IsOpen,
|
||
UnlockAmount = goods.UnlockAmount,
|
||
Sort = goods.Sort,
|
||
FlwStartTime = goods.FlwStartTime?.ToString("yyyy-MM-dd HH:mm") ?? "",
|
||
FlwEndTime = goods.FlwEndTime?.ToString("yyyy-MM-dd HH:mm") ?? "",
|
||
OpenTime = goods.OpenTime?.ToString("yyyy-MM-dd HH:mm") ?? ""
|
||
};
|
||
|
||
// 构建奖品列表
|
||
var prizeList = goodsListItems.Select(gi =>
|
||
{
|
||
var shangInfo = gi.ShangId.HasValue ? shangInfos.GetValueOrDefault(gi.ShangId.Value) : null;
|
||
return new WelfarePrizeDto
|
||
{
|
||
Id = gi.Id,
|
||
Title = gi.Title,
|
||
Imgurl = gi.ImgUrl,
|
||
ImgurlDetail = !string.IsNullOrEmpty(gi.ImgUrlDetail) ? gi.ImgUrlDetail : gi.ImgUrl,
|
||
Stock = gi.Stock,
|
||
Price = gi.Price,
|
||
ScMoney = gi.ScMoney,
|
||
ShangId = gi.ShangId ?? 0,
|
||
SurplusStock = gi.SurplusStock,
|
||
Sort = gi.Sort,
|
||
ShangTitle = shangInfo?.Title ?? "",
|
||
ShangColor = shangInfo?.Color ?? ""
|
||
};
|
||
}).ToList();
|
||
|
||
// 判断福利屋状态
|
||
var now = DateTime.Now;
|
||
var startTime = goods.FlwStartTime ?? DateTime.MinValue;
|
||
var endTime = goods.FlwEndTime ?? DateTime.MinValue;
|
||
var openTime = goods.OpenTime ?? DateTime.MinValue;
|
||
|
||
string status;
|
||
string statusText;
|
||
|
||
if (now < startTime)
|
||
{
|
||
status = "waiting";
|
||
statusText = "即将开始";
|
||
}
|
||
else if (now >= startTime && now < endTime)
|
||
{
|
||
status = "ongoing";
|
||
statusText = "进行中";
|
||
}
|
||
else if (now >= endTime && now < openTime)
|
||
{
|
||
status = "ended";
|
||
statusText = "已结束,等待开奖";
|
||
}
|
||
else if (now >= openTime)
|
||
{
|
||
if (goods.IsOpen == 1)
|
||
{
|
||
status = "opened";
|
||
statusText = "已开奖";
|
||
}
|
||
else
|
||
{
|
||
status = "to_open";
|
||
statusText = "待开奖";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = "unknown";
|
||
statusText = "未知状态";
|
||
}
|
||
|
||
return new WelfareDetailResponse
|
||
{
|
||
Goods = goodsDto,
|
||
Goodslist = prizeList,
|
||
JoinCount = joinCount,
|
||
CurrentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
UserCount = userCount,
|
||
UserConsumption = userConsumption,
|
||
Status = status,
|
||
StatusText = statusText
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<List<ParticipantDto>> GetParticipantsAsync(int goodsId, int page = 1, int limit = 15)
|
||
{
|
||
var participants = await _dbContext.OrderItems
|
||
.Where(oi => oi.GoodsId == goodsId && oi.OrderType == WelfareType)
|
||
.OrderByDescending(oi => oi.Addtime)
|
||
.Take(1000)
|
||
.Select(oi => new
|
||
{
|
||
oi.UserId,
|
||
oi.Addtime
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取用户信息
|
||
var userIds = participants.Select(p => p.UserId).Distinct().ToList();
|
||
var users = await _dbContext.Users
|
||
.Where(u => userIds.Contains(u.Id))
|
||
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
|
||
.ToDictionaryAsync(u => u.Id);
|
||
|
||
return participants.Select(p =>
|
||
{
|
||
var user = users.GetValueOrDefault(p.UserId);
|
||
return new ParticipantDto
|
||
{
|
||
Nickname = user?.Nickname ?? "",
|
||
Avatar = user?.HeadImg ?? "",
|
||
CreateTime = DateTimeOffset.FromUnixTimeSeconds(p.Addtime).LocalDateTime.ToString("yyyy-MM-dd HH:mm")
|
||
};
|
||
}).ToList();
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<List<WinningRecordDto>> GetWinningRecordsAsync(int goodsId, int page = 1, int limit = 15)
|
||
{
|
||
var records = await _dbContext.OrderItems
|
||
.Where(oi => oi.GoodsId == goodsId && oi.OrderType == WelfareType && oi.ShangId > 0)
|
||
.OrderByDescending(oi => oi.ShangId)
|
||
.ThenByDescending(oi => oi.Addtime)
|
||
.Take(1000)
|
||
.Select(oi => new
|
||
{
|
||
oi.UserId,
|
||
oi.Addtime,
|
||
oi.GoodslistTitle,
|
||
oi.ShangId
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取用户信息
|
||
var userIds = records.Select(r => r.UserId).Distinct().ToList();
|
||
var users = await _dbContext.Users
|
||
.Where(u => userIds.Contains(u.Id))
|
||
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
|
||
.ToDictionaryAsync(u => u.Id);
|
||
|
||
return records.Select(r =>
|
||
{
|
||
var user = users.GetValueOrDefault(r.UserId);
|
||
return new WinningRecordDto
|
||
{
|
||
Nickname = user?.Nickname ?? "",
|
||
Avatar = user?.HeadImg ?? "",
|
||
GoodslistTitle = r.GoodslistTitle ?? "",
|
||
ShangId = r.ShangId,
|
||
CreateTime = DateTimeOffset.FromUnixTimeSeconds(r.Addtime).LocalDateTime.ToString("yyyy-MM-dd HH:mm")
|
||
};
|
||
}).ToList();
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<List<UserParticipationDto>> GetUserParticipationRecordsAsync(int userId, int page = 1, int limit = 15)
|
||
{
|
||
var records = await _dbContext.OrderItems
|
||
.Where(oi => oi.UserId == userId && oi.OrderType == WelfareType)
|
||
.OrderByDescending(oi => oi.Addtime)
|
||
.Take(1000)
|
||
.Select(oi => new
|
||
{
|
||
oi.GoodsId,
|
||
oi.Addtime,
|
||
oi.GoodslistTitle,
|
||
oi.ShangId
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取商品信息
|
||
var goodsIds = records.Where(r => r.GoodsId.HasValue).Select(r => r.GoodsId!.Value).Distinct().ToList();
|
||
var goodsInfos = await _dbContext.Goods
|
||
.Where(g => goodsIds.Contains(g.Id))
|
||
.Select(g => new { g.Id, g.Title })
|
||
.ToDictionaryAsync(g => g.Id);
|
||
|
||
return records.Select(r =>
|
||
{
|
||
var goodsInfo = r.GoodsId.HasValue ? goodsInfos.GetValueOrDefault(r.GoodsId.Value) : null;
|
||
return new UserParticipationDto
|
||
{
|
||
GoodsId = r.GoodsId ?? 0,
|
||
GoodsTitle = goodsInfo?.Title ?? "",
|
||
GoodslistTitle = r.GoodslistTitle ?? "",
|
||
ShangId = r.ShangId,
|
||
CreateTime = DateTimeOffset.FromUnixTimeSeconds(r.Addtime).LocalDateTime.ToString("yyyy-MM-dd HH:mm")
|
||
};
|
||
}).ToList();
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<List<UserWinningDto>> GetUserWinningRecordsAsync(int userId, int page = 1, int limit = 15)
|
||
{
|
||
var records = await _dbContext.OrderItems
|
||
.Where(oi => oi.UserId == userId && oi.OrderType == WelfareType && oi.ShangId > 0)
|
||
.OrderByDescending(oi => oi.Addtime)
|
||
.Take(1000)
|
||
.Select(oi => new
|
||
{
|
||
oi.GoodsId,
|
||
oi.Addtime,
|
||
oi.GoodslistTitle,
|
||
oi.ShangId
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取商品信息(包括开奖时间)
|
||
var goodsIds = records.Where(r => r.GoodsId.HasValue).Select(r => r.GoodsId!.Value).Distinct().ToList();
|
||
var goodsInfos = await _dbContext.Goods
|
||
.Where(g => goodsIds.Contains(g.Id))
|
||
.Select(g => new { g.Id, g.Title, g.OpenTime })
|
||
.ToDictionaryAsync(g => g.Id);
|
||
|
||
return records.Select(r =>
|
||
{
|
||
var goodsInfo = r.GoodsId.HasValue ? goodsInfos.GetValueOrDefault(r.GoodsId.Value) : null;
|
||
return new UserWinningDto
|
||
{
|
||
GoodsId = r.GoodsId ?? 0,
|
||
GoodsTitle = goodsInfo?.Title ?? "",
|
||
GoodslistTitle = r.GoodslistTitle ?? "",
|
||
ShangId = r.ShangId,
|
||
CreateTime = goodsInfo?.OpenTime?.ToString("yyyy-MM-dd HH:mm") ?? ""
|
||
};
|
||
}).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将DateTime转换为Unix时间戳
|
||
/// </summary>
|
||
private static int ToUnixTimestamp(DateTime dateTime)
|
||
{
|
||
return (int)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<FuliwuListResponse> GetFuliwuListAsync(int userId, int type, int page)
|
||
{
|
||
// 验证type参数
|
||
if (type != 1 && type != 3)
|
||
{
|
||
throw new ArgumentException("参数错误");
|
||
}
|
||
|
||
const int paginate = 15;
|
||
|
||
// 计算用户消费总额(用于解锁金额判断)
|
||
decimal userTotalConsumption = 0;
|
||
|
||
if (userId > 0)
|
||
{
|
||
var userInfo = await _dbContext.Users
|
||
.Where(u => u.Id == userId)
|
||
.Select(u => new { u.IsTest })
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (userInfo != null && userInfo.IsTest > 0)
|
||
{
|
||
// 推广账号,门槛计算是全部的order_zhe_total
|
||
userTotalConsumption = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.OrderZheTotal);
|
||
}
|
||
else
|
||
{
|
||
// 普通用户,计算price + use_money
|
||
var orderPrice = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.Price);
|
||
var orderMoney = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.UseMoney);
|
||
userTotalConsumption = orderPrice + orderMoney;
|
||
}
|
||
}
|
||
|
||
// 构建查询
|
||
var now = DateTime.Now;
|
||
IQueryable<Good> query;
|
||
|
||
_logger.LogInformation("GetFuliwuListAsync: userId={UserId}, type={Type}, page={Page}, userTotalConsumption={Consumption}, now={Now}",
|
||
userId, type, page, userTotalConsumption, now);
|
||
|
||
if (type == 1)
|
||
{
|
||
// type=1 进行中:Status=1, IsOpen=0, 且开奖时间未到
|
||
query = _dbContext.Goods
|
||
.Where(g => g.Status == 1
|
||
&& g.Type == WelfareType
|
||
&& g.IsOpen == 0
|
||
&& g.UnlockAmount <= userTotalConsumption
|
||
&& (g.OpenTime == null || g.OpenTime > now))
|
||
.OrderByDescending(g => g.Sort)
|
||
.ThenByDescending(g => g.Id);
|
||
}
|
||
else
|
||
{
|
||
// type=3 已结束:已开奖的 或 开奖时间已过但未开奖的
|
||
query = _dbContext.Goods
|
||
.Where(g => g.Type == WelfareType
|
||
&& g.UnlockAmount <= userTotalConsumption
|
||
&& (g.IsOpen == 1 || (g.OpenTime != null && g.OpenTime <= now)))
|
||
.OrderByDescending(g => g.OpenTime);
|
||
}
|
||
|
||
// 获取总数计算最后一页
|
||
var total = await query.CountAsync();
|
||
var lastPage = (int)Math.Ceiling((double)total / paginate);
|
||
|
||
_logger.LogInformation("GetFuliwuListAsync: total={Total}, lastPage={LastPage}", total, lastPage);
|
||
|
||
// 分页查询
|
||
var goods = await query
|
||
.Skip((page - 1) * paginate)
|
||
.Take(paginate)
|
||
.Select(g => new
|
||
{
|
||
g.Id,
|
||
g.Title,
|
||
g.ImgUrl,
|
||
g.Price,
|
||
g.Type,
|
||
g.NewIs,
|
||
g.QuanjuXiangou,
|
||
g.ChoujiangXianzhi,
|
||
g.FlwStartTime,
|
||
g.FlwEndTime,
|
||
g.OpenTime,
|
||
g.GoodsDescribe,
|
||
g.IsOpen
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取商品ID列表
|
||
var goodsIds = goods.Select(g => g.Id).ToList();
|
||
|
||
// 获取每个商品的奖品列表
|
||
var goodsListItems = await _dbContext.GoodsItems
|
||
.Where(gi => goodsIds.Contains(gi.GoodsId) && gi.Num == 0)
|
||
.Select(gi => new
|
||
{
|
||
gi.GoodsId,
|
||
gi.Title,
|
||
gi.ImgUrl,
|
||
gi.Stock,
|
||
gi.Price,
|
||
gi.ScMoney
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取参与次数
|
||
var joinCounts = await _dbContext.OrderItems
|
||
.Where(oi => goodsIds.Contains(oi.GoodsId ?? 0))
|
||
.GroupBy(oi => oi.GoodsId)
|
||
.Select(g => new { GoodsId = g.Key, Count = g.Count() })
|
||
.ToDictionaryAsync(x => x.GoodsId ?? 0, x => x.Count);
|
||
|
||
// 构建返回数据
|
||
var list = goods.Select(g => new WelfareItemDto
|
||
{
|
||
Id = g.Id,
|
||
Title = g.Title,
|
||
Imgurl = g.ImgUrl,
|
||
Price = g.Price,
|
||
Type = g.Type,
|
||
NewIs = g.NewIs,
|
||
QuanjuXiangou = g.QuanjuXiangou,
|
||
ChoujiangXianzhi = g.ChoujiangXianzhi,
|
||
FlwStartTime = g.FlwStartTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||
FlwEndTime = g.FlwEndTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||
OpenTime = g.OpenTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||
GoodsDescribe = g.GoodsDescribe ?? "",
|
||
IsOpen = g.IsOpen,
|
||
JoinCount = joinCounts.GetValueOrDefault(g.Id, 0),
|
||
Goodslist = goodsListItems
|
||
.Where(gi => gi.GoodsId == g.Id)
|
||
.Select(gi => new WelfareGoodsListItemDto
|
||
{
|
||
Title = gi.Title,
|
||
Imgurl = gi.ImgUrl,
|
||
Stock = gi.Stock,
|
||
Price = gi.Price,
|
||
ScMoney = gi.ScMoney
|
||
})
|
||
.ToList()
|
||
}).ToList();
|
||
|
||
return new FuliwuListResponse
|
||
{
|
||
Data = list,
|
||
LastPage = lastPage
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<WelfareBuyResponse> BuyWelfareAsync(int userId, WelfareBuyRequest request)
|
||
{
|
||
// 1. 获取用户信息
|
||
var user = await _dbContext.Users
|
||
.Where(u => u.Id == userId)
|
||
.Select(u => new
|
||
{
|
||
u.Id,
|
||
u.Mobile,
|
||
u.Money,
|
||
u.Integral,
|
||
u.Money2,
|
||
u.IsTest
|
||
})
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (user == null)
|
||
{
|
||
throw new InvalidOperationException("用户不存在");
|
||
}
|
||
|
||
// 2. 验证手机号绑定
|
||
if (string.IsNullOrEmpty(user.Mobile))
|
||
{
|
||
throw new InvalidOperationException("请先绑定手机号");
|
||
}
|
||
|
||
// 3. 获取商品信息
|
||
var goods = await _dbContext.Goods
|
||
.Where(g => g.Id == request.GoodsId)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (goods == null)
|
||
{
|
||
throw new InvalidOperationException("盒子不存在");
|
||
}
|
||
|
||
if (goods.Status != 1)
|
||
{
|
||
throw new InvalidOperationException("盒子已下架");
|
||
}
|
||
|
||
// 4. 验证是否为福利屋类型
|
||
if (goods.Type != WelfareType)
|
||
{
|
||
throw new InvalidOperationException("该盒子不是福利屋类型");
|
||
}
|
||
|
||
// 5. 验证福利屋活动时间
|
||
var now = DateTime.Now;
|
||
if (goods.FlwStartTime.HasValue && now < goods.FlwStartTime.Value)
|
||
{
|
||
throw new InvalidOperationException("福利屋活动尚未开始");
|
||
}
|
||
if (goods.FlwEndTime.HasValue && now > goods.FlwEndTime.Value)
|
||
{
|
||
throw new InvalidOperationException("福利屋活动已结束");
|
||
}
|
||
|
||
// 6. 验证解锁金额
|
||
decimal userTotalConsumption = 0;
|
||
if (user.IsTest > 0)
|
||
{
|
||
userTotalConsumption = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.OrderZheTotal);
|
||
}
|
||
else
|
||
{
|
||
var orderPrice = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.Price);
|
||
var orderMoney = await _dbContext.Orders
|
||
.Where(o => o.Status == 1 && o.UserId == userId)
|
||
.SumAsync(o => o.UseMoney);
|
||
userTotalConsumption = orderPrice + orderMoney;
|
||
}
|
||
|
||
if (goods.UnlockAmount > userTotalConsumption)
|
||
{
|
||
throw new InvalidOperationException($"您需要消费满{goods.UnlockAmount}元才能参与此福利屋");
|
||
}
|
||
|
||
// 7. 验证抽奖门槛 (福利屋活动期间消费)
|
||
if (goods.ChoujiangXianzhi > 0 && goods.FlwStartTime.HasValue && goods.FlwEndTime.HasValue)
|
||
{
|
||
var startTimestamp = ToUnixTimestamp(goods.FlwStartTime.Value);
|
||
var endTimestamp = ToUnixTimestamp(goods.FlwEndTime.Value);
|
||
|
||
var consumptionInPeriod = await _dbContext.Orders
|
||
.Where(o => o.Status == 1
|
||
&& o.UserId == userId
|
||
&& o.Addtime >= startTimestamp
|
||
&& o.Addtime <= endTimestamp)
|
||
.SumAsync(o => o.UseMoney);
|
||
|
||
if (consumptionInPeriod < goods.ChoujiangXianzhi)
|
||
{
|
||
var remaining = Math.Round(goods.ChoujiangXianzhi - consumptionInPeriod, 2);
|
||
throw new InvalidOperationException(
|
||
$"需在活动期间消耗达到{goods.ChoujiangXianzhi}钻石,还需{remaining}钻石");
|
||
}
|
||
}
|
||
|
||
// 8. 验证全局限购
|
||
if (goods.QuanjuXiangou > 0)
|
||
{
|
||
var userQuanjuCount = await _dbContext.OrderItems
|
||
.Where(oi => oi.GoodsId == request.GoodsId
|
||
&& oi.UserId == userId
|
||
&& oi.ParentGoodsListId == 0)
|
||
.CountAsync();
|
||
|
||
if (userQuanjuCount >= goods.QuanjuXiangou)
|
||
{
|
||
throw new InvalidOperationException($"当前限购{goods.QuanjuXiangou}次");
|
||
}
|
||
|
||
var nowPrizeNum = request.PrizeNum + userQuanjuCount;
|
||
if (nowPrizeNum > goods.QuanjuXiangou)
|
||
{
|
||
throw new InvalidOperationException($"购买超出限制,还允许购买{goods.QuanjuXiangou - userQuanjuCount}次");
|
||
}
|
||
}
|
||
|
||
// 9. 计算订单金额
|
||
var prizeNum = request.PrizeNum > 0 ? request.PrizeNum : 1;
|
||
var orderTotal = goods.Price * prizeNum;
|
||
var price = orderTotal;
|
||
|
||
// 余额抵扣
|
||
decimal useMoney = 0;
|
||
if (request.UseMoneyIs == 1)
|
||
{
|
||
if (user.Money >= price)
|
||
{
|
||
useMoney = price;
|
||
price = 0;
|
||
}
|
||
else
|
||
{
|
||
useMoney = user.Money;
|
||
price -= user.Money;
|
||
}
|
||
}
|
||
|
||
// 积分抵扣 (1:100比例)
|
||
decimal useIntegral = 0;
|
||
if (request.UseIntegralIs == 1 && price > 0)
|
||
{
|
||
var priceInIntegral = price * 100;
|
||
if (user.Integral >= priceInIntegral)
|
||
{
|
||
useIntegral = priceInIntegral;
|
||
price = 0;
|
||
}
|
||
else
|
||
{
|
||
useIntegral = user.Integral;
|
||
price -= user.Integral / 100;
|
||
}
|
||
}
|
||
|
||
// 货币2抵扣 (1:100比例)
|
||
decimal useMoney2 = 0;
|
||
var userMoney2 = user.Money2 ?? 0;
|
||
if (request.UseMoney2Is == 1 && price > 0)
|
||
{
|
||
var priceInMoney2 = price * 100;
|
||
if (userMoney2 >= priceInMoney2)
|
||
{
|
||
useMoney2 = priceInMoney2;
|
||
price = 0;
|
||
}
|
||
else
|
||
{
|
||
useMoney2 = userMoney2;
|
||
price -= userMoney2 / 100;
|
||
}
|
||
}
|
||
|
||
price = Math.Round(price, 2);
|
||
|
||
// 10. 生成订单号
|
||
var orderNum = $"FLW_{DateTime.Now:yyyyMMddHHmmss}_{userId}_{new Random().Next(1000, 9999)}";
|
||
|
||
// 11. 使用事务创建订单
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 扣除用户余额/积分
|
||
if (useMoney > 0 || useIntegral > 0 || useMoney2 > 0)
|
||
{
|
||
var userEntity = await _dbContext.Users.FindAsync(userId);
|
||
if (userEntity != null)
|
||
{
|
||
if (useMoney > 0)
|
||
{
|
||
userEntity.Money -= useMoney;
|
||
}
|
||
if (useIntegral > 0)
|
||
{
|
||
userEntity.Integral -= useIntegral;
|
||
}
|
||
if (useMoney2 > 0)
|
||
{
|
||
userEntity.Money2 = (userEntity.Money2 ?? 0) - useMoney2;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建订单
|
||
var order = new Order
|
||
{
|
||
UserId = userId,
|
||
OrderNum = orderNum,
|
||
OrderTotal = orderTotal,
|
||
OrderZheTotal = orderTotal,
|
||
Price = price,
|
||
UseMoney = useMoney,
|
||
UseIntegral = useIntegral,
|
||
UseMoney2 = useMoney2,
|
||
UseScore = 0,
|
||
Zhe = 1,
|
||
GoodsId = request.GoodsId,
|
||
Num = 0, // 福利屋固定为0
|
||
GoodsPrice = goods.Price,
|
||
GoodsTitle = goods.Title,
|
||
GoodsImgurl = goods.ImgUrlDetail,
|
||
PrizeNum = prizeNum,
|
||
Status = price > 0 ? (byte)0 : (byte)1, // 需要微信支付则待支付,否则已支付
|
||
PayType = 1,
|
||
OrderType = WelfareType,
|
||
Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||
CouponId = request.CouponId > 0 ? request.CouponId : null,
|
||
UseCoupon = 0,
|
||
IsShouZhe = 0,
|
||
IsFlw = 1,
|
||
CreatedAt = DateTime.Now,
|
||
UpdatedAt = DateTime.Now
|
||
};
|
||
|
||
_dbContext.Orders.Add(order);
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
// 如果不需要微信支付,直接创建参与记录
|
||
if (price <= 0)
|
||
{
|
||
order.Status = 1;
|
||
order.PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
|
||
// 创建订单项(参与记录)
|
||
for (int i = 0; i < prizeNum; i++)
|
||
{
|
||
var orderItem = new OrderItem
|
||
{
|
||
UserId = userId,
|
||
OrderId = order.Id,
|
||
GoodsId = request.GoodsId,
|
||
Num = 0,
|
||
OrderType = WelfareType,
|
||
ShangId = 0, // 未开奖
|
||
GoodslistTitle = "",
|
||
GoodslistImgurl = "",
|
||
GoodslistMoney = 0,
|
||
Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||
ParentGoodsListId = 0,
|
||
CreatedAt = DateTime.Now,
|
||
UpdatedAt = DateTime.Now
|
||
};
|
||
_dbContext.OrderItems.Add(orderItem);
|
||
}
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
}
|
||
|
||
await transaction.CommitAsync();
|
||
|
||
// 12. 返回结果
|
||
if (price > 0)
|
||
{
|
||
// 需要微信支付
|
||
return new WelfareBuyResponse
|
||
{
|
||
Status = 1,
|
||
OrderNum = orderNum,
|
||
PayParams = new { message = "请使用微信支付完成订单" }
|
||
};
|
||
}
|
||
else
|
||
{
|
||
// 余额支付成功
|
||
return new WelfareBuyResponse
|
||
{
|
||
Status = 0,
|
||
OrderNum = orderNum
|
||
};
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "福利屋购买失败: UserId={UserId}, GoodsId={GoodsId}", userId, request.GoodsId);
|
||
throw new InvalidOperationException("购买失败,请刷新重试");
|
||
}
|
||
}
|
||
}
|