21
This commit is contained in:
parent
4199a47211
commit
3fc2c8eea5
|
|
@ -23,7 +23,7 @@
|
|||
<scroll-view scroll-y="true" class="benefits-scroll">
|
||||
<template v-if="ongoingData.length > 0">
|
||||
<view v-for="(item, index) in ongoingData" :key="index" class="benefit-item relative"
|
||||
@click="$c.to({ url: '/pages/infinite/bonus_house_details?goods_id=' + item.id })">
|
||||
@click="goToDetail(item.id)">
|
||||
<text class="benefit-title">{{ item.title }}</text>
|
||||
<text class="benefit-tips">{{ item.tips }}</text>
|
||||
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
<scroll-view scroll-y="true" class="benefits-scroll">
|
||||
<template v-if="endedData.length > 0">
|
||||
<view v-for="(item, index) in endedData" :key="index" class="benefit-item relative"
|
||||
@click="$c.to({ url: '/pages/infinite/bonus_house_details?goods_id=' + item.id })">
|
||||
@click="goToDetail(item.id)">
|
||||
<text class="benefit-title">{{ item.title }}</text>
|
||||
<text class="benefit-tips">{{ item.tips }}</text>
|
||||
|
||||
|
|
@ -185,20 +185,19 @@ export default {
|
|||
if (goods.stock > 0) {
|
||||
goodsList.push({
|
||||
imgUrl: goods.imgurl,
|
||||
num: goods.stock//item1.price,
|
||||
num: goods.stock
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
choujiang_xianzhi: item.choujiang_xianzhi,
|
||||
choujiangXianzhi: item.choujiangXianzhi,
|
||||
title: item.title,
|
||||
tips: item.goods_describe,
|
||||
Popularity: item.join_count,
|
||||
Time: `${item.flw_start_time} — ${item.flw_end_time}`,
|
||||
tips: item.goodsDescribe,
|
||||
Popularity: item.joinCount || 0,
|
||||
Time: `${item.flwStartTime} — ${item.flwEndTime}`,
|
||||
GoodsList: goodsList
|
||||
};
|
||||
});
|
||||
|
|
@ -225,6 +224,23 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 跳转到福利屋详情
|
||||
goToDetail(goodsId) {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (!token) {
|
||||
uni.showToast({
|
||||
title: '请先登录',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.$c.to({ url: '/pages/user/login' });
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
this.$c.to({ url: '/pages/infinite/bonus_house_details?goods_id=' + goodsId });
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -271,32 +271,32 @@ export default {
|
|||
const {
|
||||
goods,
|
||||
goodslist,
|
||||
join_count,
|
||||
current_time,
|
||||
joinCount,
|
||||
currentTime,
|
||||
status,
|
||||
status_text,
|
||||
user_consumption,
|
||||
user_count
|
||||
statusText,
|
||||
userConsumption,
|
||||
userCount
|
||||
} = res.data;
|
||||
this.orderData.goods = goods;
|
||||
// 更新福利屋基本信息
|
||||
this.bonusData = {
|
||||
title: goods.title,
|
||||
tips: goods.goods_describe,
|
||||
time: goods.flw_start_time + '-' + goods.flw_end_time,
|
||||
open_time: goods.open_time,
|
||||
start_time: goods.flw_start_time,
|
||||
end_time: goods.flw_end_time,
|
||||
choujiang_xianzhi: goods.choujiang_xianzhi,
|
||||
popularity: join_count || 0,
|
||||
quanju_xiangou: goods.quanju_xiangou,
|
||||
tips: goods.goodsDescribe,
|
||||
time: goods.flwStartTime + '-' + goods.flwEndTime,
|
||||
open_time: goods.openTime,
|
||||
start_time: goods.flwStartTime,
|
||||
end_time: goods.flwEndTime,
|
||||
choujiang_xianzhi: goods.choujiangXianzhi,
|
||||
popularity: joinCount || 0,
|
||||
quanju_xiangou: goods.quanjuXiangou,
|
||||
price: goods.price
|
||||
};
|
||||
if (user_consumption != null) {
|
||||
this.user_total_consumption = user_consumption.total_consumed;
|
||||
if (userConsumption != null) {
|
||||
this.user_total_consumption = userConsumption.totalAmount || 0;
|
||||
}
|
||||
if (user_count != null) {
|
||||
this.user_count = user_count;
|
||||
if (userCount != null) {
|
||||
this.user_count = userCount;
|
||||
}
|
||||
let index = 0;
|
||||
this.goodsList.splice(0, this.goodsList.length)
|
||||
|
|
@ -311,24 +311,24 @@ export default {
|
|||
stock: item.stock,
|
||||
realPrice: item.price,
|
||||
sortIndex: item.sort,
|
||||
type: item.shang_title,
|
||||
typeColor: item.shang_color,
|
||||
imgurl_detail: item.imgurl_detail
|
||||
type: item.shangTitle,
|
||||
typeColor: item.shangColor,
|
||||
imgurl_detail: item.imgurlDetail
|
||||
});
|
||||
index++;
|
||||
}
|
||||
})
|
||||
|
||||
// 计算倒计时
|
||||
this.calculateRemainingTime(goods.open_time, goods.flw_start_time, current_time);
|
||||
this.startCountdownTimer(goods.open_time, goods.flw_start_time, current_time);
|
||||
this.calculateRemainingTime(goods.openTime, goods.flwStartTime, currentTime);
|
||||
this.startCountdownTimer(goods.openTime, goods.flwStartTime, currentTime);
|
||||
|
||||
// 其他状态信息
|
||||
this.activityStatus = status;
|
||||
this.activityStatusText = status_text;
|
||||
this.activityStatusText = statusText;
|
||||
|
||||
// 获取参与人数和赏品记录列表
|
||||
if (join_count > 0) {
|
||||
if (joinCount > 0) {
|
||||
await this.loadParticipants(goods_id);
|
||||
await this.loadAwardRecords(goods_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,276 @@
|
|||
using HoneyBox.Model.Data;
|
||||
using HoneyBox.Model.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace HoneyBox.Api.BackgroundServices;
|
||||
|
||||
/// <summary>
|
||||
/// 福利屋开奖后台服务
|
||||
/// 每分钟检查一次是否有需要开奖的福利屋
|
||||
/// </summary>
|
||||
public class WelfareLotteryService : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<WelfareLotteryService> _logger;
|
||||
private const int WelfareType = 15; // 福利屋商品类型
|
||||
|
||||
public WelfareLotteryService(
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<WelfareLotteryService> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("福利屋开奖服务已启动");
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessWelfareLotteryAsync(stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "福利屋开奖服务执行出错");
|
||||
}
|
||||
|
||||
// 每分钟执行一次
|
||||
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation("福利屋开奖服务已停止");
|
||||
}
|
||||
|
||||
private async Task ProcessWelfareLotteryAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
|
||||
|
||||
var now = DateTime.Now;
|
||||
_logger.LogDebug("福利屋开奖检查: {Time}", now);
|
||||
|
||||
// 查找需要开奖的福利屋
|
||||
// 条件: status=1, is_flw=1, is_open=0, open_time <= 当前时间
|
||||
var welfareGoods = await dbContext.Goods
|
||||
.Where(g => g.Status == 1
|
||||
&& g.Type == WelfareType
|
||||
&& g.IsOpen == 0
|
||||
&& g.OpenTime != null
|
||||
&& g.OpenTime <= now)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
foreach (var goods in welfareGoods)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessSingleWelfareLotteryAsync(dbContext, goods, stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "福利屋开奖失败: GoodsId={GoodsId}, Title={Title}",
|
||||
goods.Id, goods.Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessSingleWelfareLotteryAsync(
|
||||
HoneyBoxDbContext dbContext,
|
||||
Model.Entities.Good goods,
|
||||
CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("开始福利屋开奖: GoodsId={GoodsId}, Title={Title}",
|
||||
goods.Id, goods.Title);
|
||||
|
||||
// 获取所有奖品(num=0 表示第一箱)
|
||||
var prizeList = await dbContext.GoodsItems
|
||||
.Where(gi => gi.GoodsId == goods.Id && gi.Num == 0)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
// 展开奖品列表(根据库存数量)
|
||||
var expandedPrizes = new List<GoodsItem>();
|
||||
foreach (var prize in prizeList)
|
||||
{
|
||||
for (int i = 0; i < prize.Stock; i++)
|
||||
{
|
||||
expandedPrizes.Add(prize);
|
||||
}
|
||||
}
|
||||
|
||||
// 打乱奖品顺序
|
||||
var random = new Random();
|
||||
expandedPrizes = expandedPrizes.OrderBy(_ => random.Next()).ToList();
|
||||
|
||||
// 获取所有参与用户的订单
|
||||
var participants = await dbContext.OrderItems
|
||||
.Where(oi => oi.GoodsId == goods.Id && oi.OrderType == WelfareType)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
if (participants.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("福利屋无人参与,直接结束: GoodsId={GoodsId}", goods.Id);
|
||||
goods.IsOpen = 1;
|
||||
goods.Status = 3; // 已结束
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
return;
|
||||
}
|
||||
|
||||
// 打乱参与者顺序
|
||||
participants = participants.OrderBy(_ => random.Next()).ToList();
|
||||
|
||||
var prizeIndex = 0;
|
||||
var prizeCount = expandedPrizes.Count;
|
||||
|
||||
// 遍历所有参与者分配奖品
|
||||
foreach (var participant in participants)
|
||||
{
|
||||
using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
|
||||
try
|
||||
{
|
||||
if (prizeIndex < prizeCount)
|
||||
{
|
||||
var prize = expandedPrizes[prizeIndex];
|
||||
|
||||
// 检查库存
|
||||
var currentStock = await dbContext.GoodsItems
|
||||
.Where(gi => gi.Id == prize.Id)
|
||||
.Select(gi => gi.SurplusStock)
|
||||
.FirstOrDefaultAsync(stoppingToken);
|
||||
|
||||
if (currentStock <= 0)
|
||||
{
|
||||
_logger.LogWarning("奖品库存不足: PrizeId={PrizeId}", prize.Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 更新订单信息
|
||||
participant.GoodslistId = prize.Id;
|
||||
participant.GoodslistTitle = prize.Title;
|
||||
participant.GoodslistImgurl = prize.ImgUrl;
|
||||
participant.GoodslistMoney = prize.Money;
|
||||
participant.GoodslistType = prize.GoodsType;
|
||||
participant.ShangId = prize.ShangId ?? 0;
|
||||
participant.PrizeCode = prize.RewardId?.ToString() ?? "";
|
||||
|
||||
// 如果是实物奖品,设置状态为待发货
|
||||
if (participant.Status != 0 && participant.GoodslistType == 1)
|
||||
{
|
||||
participant.Status = 0;
|
||||
participant.RecoveryNum = "";
|
||||
}
|
||||
|
||||
// 减少库存
|
||||
var updateResult = await dbContext.GoodsItems
|
||||
.Where(gi => gi.Id == prize.Id && gi.SurplusStock > 0)
|
||||
.ExecuteUpdateAsync(s => s
|
||||
.SetProperty(gi => gi.SurplusStock, gi => gi.SurplusStock - 1),
|
||||
stoppingToken);
|
||||
|
||||
if (updateResult == 0)
|
||||
{
|
||||
_logger.LogWarning("更新库存失败: PrizeId={PrizeId}", prize.Id);
|
||||
await transaction.RollbackAsync(stoppingToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 发放奖励(如果有reward_id)
|
||||
if (!string.IsNullOrEmpty(prize.RewardId) && int.TryParse(prize.RewardId, out var rewardId) && rewardId > 0)
|
||||
{
|
||||
await SendRewardAsync(dbContext, participant.UserId, rewardId,
|
||||
$"{goods.Title}开奖", stoppingToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation("发放奖品成功: GoodsId={GoodsId}, UserId={UserId}, Prize={Prize}",
|
||||
goods.Id, participant.UserId, prize.Title);
|
||||
|
||||
prizeIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 轮空处理
|
||||
participant.GoodslistId = 0;
|
||||
participant.GoodslistTitle = "轮空";
|
||||
participant.GoodslistMoney = 0;
|
||||
participant.ShangId = 0;
|
||||
participant.PrizeCode = "";
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
await transaction.CommitAsync(stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync(stoppingToken);
|
||||
_logger.LogError(ex, "处理参与者奖品失败: GoodsId={GoodsId}, UserId={UserId}",
|
||||
goods.Id, participant.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新福利屋状态为已开奖
|
||||
goods.IsOpen = 1;
|
||||
goods.Status = 3; // 已结束
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
|
||||
_logger.LogInformation("福利屋开奖完成: GoodsId={GoodsId}, Title={Title}, 参与人数={Count}",
|
||||
goods.Id, goods.Title, participants.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发放奖励
|
||||
/// </summary>
|
||||
private async Task SendRewardAsync(
|
||||
HoneyBoxDbContext dbContext,
|
||||
int userId,
|
||||
int rewardId,
|
||||
string remark,
|
||||
CancellationToken stoppingToken)
|
||||
{
|
||||
// 获取奖励配置
|
||||
var reward = await dbContext.Rewards
|
||||
.Where(r => r.Id == rewardId)
|
||||
.FirstOrDefaultAsync(stoppingToken);
|
||||
|
||||
if (reward == null)
|
||||
{
|
||||
_logger.LogWarning("奖励配置不存在: RewardId={RewardId}", rewardId);
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await dbContext.Users
|
||||
.Where(u => u.Id == userId)
|
||||
.FirstOrDefaultAsync(stoppingToken);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("用户不存在: UserId={UserId}", userId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据奖励类型发放
|
||||
switch (reward.RewardType)
|
||||
{
|
||||
case 1: // 钻石/余额
|
||||
user.Money += reward.RewardValue;
|
||||
_logger.LogInformation("发放钻石: UserId={UserId}, Amount={Amount}", userId, reward.RewardValue);
|
||||
break;
|
||||
|
||||
case 2: // 积分/UU币
|
||||
user.Integral += reward.RewardValue;
|
||||
_logger.LogInformation("发放积分: UserId={UserId}, Amount={Amount}", userId, reward.RewardValue);
|
||||
break;
|
||||
|
||||
case 3: // 哈尼券/达达卷
|
||||
user.Money2 = (user.Money2 ?? 0) + reward.RewardValue;
|
||||
_logger.LogInformation("发放哈尼券: UserId={UserId}, Amount={Amount}", userId, reward.RewardValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
_logger.LogWarning("未知奖励类型: Type={Type}", reward.RewardType);
|
||||
break;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
|
||||
using HoneyBox.Api.BackgroundServices;
|
||||
using HoneyBox.Api.Filters;
|
||||
using HoneyBox.Core.Mappings;
|
||||
using HoneyBox.Infrastructure.Cache;
|
||||
|
|
@ -102,6 +103,9 @@ try
|
|||
builder.Services.AddSingleton<ICacheService>(sp =>
|
||||
new RedisCacheService(builder.Configuration));
|
||||
|
||||
// 注册福利屋开奖后台服务
|
||||
builder.Services.AddHostedService<WelfareLotteryService>();
|
||||
|
||||
// 添加控制器
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -62,12 +62,19 @@ public class WelfareService : IWelfareService
|
|||
}
|
||||
|
||||
// 构建查询
|
||||
var now = DateTime.Now;
|
||||
var query = _dbContext.Goods
|
||||
.Where(g => g.Status == type
|
||||
&& g.Type == WelfareType
|
||||
&& g.IsOpen == (type == 1 ? (byte)0 : (byte)1)
|
||||
&& g.UnlockAmount <= userTotalConsumption);
|
||||
|
||||
// type=1 进行中:只显示开奖时间未到的福利屋
|
||||
if (type == 1)
|
||||
{
|
||||
query = query.Where(g => g.OpenTime == null || g.OpenTime > now);
|
||||
}
|
||||
|
||||
// 排序
|
||||
if (type == 1)
|
||||
{
|
||||
|
|
@ -585,12 +592,19 @@ public class WelfareService : IWelfareService
|
|||
}
|
||||
|
||||
// 构建查询
|
||||
var now = DateTime.Now;
|
||||
var query = _dbContext.Goods
|
||||
.Where(g => g.Status == type
|
||||
&& g.Type == WelfareType
|
||||
&& g.IsOpen == (type == 1 ? (byte)0 : (byte)1)
|
||||
&& g.UnlockAmount <= userTotalConsumption);
|
||||
|
||||
// type=1 进行中:只显示开奖时间未到的福利屋
|
||||
if (type == 1)
|
||||
{
|
||||
query = query.Where(g => g.OpenTime == null || g.OpenTime > now);
|
||||
}
|
||||
|
||||
// 排序
|
||||
if (type == 1)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user