Merge branch 'master' of http://192.168.195.14:3000/outsource/HaniBlindBox
This commit is contained in:
commit
370ff0fb5a
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
// 测试环境配置 - .NET 10 后端
|
// 测试环境配置 - .NET 10 后端
|
||||||
const testing = {
|
const testing = {
|
||||||
baseUrl: 'https://app.zpc-xy.com/honey/api',
|
// baseUrl: 'https://app.zpc-xy.com/honey/api',
|
||||||
// baseUrl: 'http://192.168.1.24:5238',
|
// baseUrl: 'http://192.168.1.24:5238',
|
||||||
// baseUrl: 'http://192.168.195.15:2822',
|
baseUrl: 'http://192.168.195.15:2822',
|
||||||
imageUrl: 'https://youdas-1308826010.cos.ap-shanghai.myqcloud.com',
|
imageUrl: 'https://youdas-1308826010.cos.ap-shanghai.myqcloud.com',
|
||||||
loginPage: '',
|
loginPage: '',
|
||||||
wxAppId: ''
|
wxAppId: ''
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<scroll-view scroll-y="true" class="benefits-scroll">
|
<scroll-view scroll-y="true" class="benefits-scroll">
|
||||||
<template v-if="ongoingData.length > 0">
|
<template v-if="ongoingData.length > 0">
|
||||||
<view v-for="(item, index) in ongoingData" :key="index" class="benefit-item relative"
|
<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-title">{{ item.title }}</text>
|
||||||
<text class="benefit-tips">{{ item.tips }}</text>
|
<text class="benefit-tips">{{ item.tips }}</text>
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
<scroll-view scroll-y="true" class="benefits-scroll">
|
<scroll-view scroll-y="true" class="benefits-scroll">
|
||||||
<template v-if="endedData.length > 0">
|
<template v-if="endedData.length > 0">
|
||||||
<view v-for="(item, index) in endedData" :key="index" class="benefit-item relative"
|
<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-title">{{ item.title }}</text>
|
||||||
<text class="benefit-tips">{{ item.tips }}</text>
|
<text class="benefit-tips">{{ item.tips }}</text>
|
||||||
|
|
||||||
|
|
@ -185,20 +185,19 @@ export default {
|
||||||
if (goods.stock > 0) {
|
if (goods.stock > 0) {
|
||||||
goodsList.push({
|
goodsList.push({
|
||||||
imgUrl: goods.imgurl,
|
imgUrl: goods.imgurl,
|
||||||
num: goods.stock//item1.price,
|
num: goods.stock
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
choujiang_xianzhi: item.choujiang_xianzhi,
|
choujiangXianzhi: item.choujiangXianzhi,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
tips: item.goods_describe,
|
tips: item.goodsDescribe,
|
||||||
Popularity: item.join_count,
|
Popularity: item.joinCount || 0,
|
||||||
Time: `${item.flw_start_time} — ${item.flw_end_time}`,
|
Time: `${item.flwStartTime} — ${item.flwEndTime}`,
|
||||||
GoodsList: goodsList
|
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>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getWelfareHouseDetail, getWelfareParticipants, getWelfareRecords } from '@/common/server/welfare.js';
|
import { getWelfareHouseDetail, getWelfareParticipants, getWelfareRecords, buyWelfareHouse } from '@/common/server/welfare.js';
|
||||||
import { calcOrderMoney, createOrder } from '@/common/server/order.js';
|
import { calcOrderMoney } from '@/common/server/order.js';
|
||||||
import OrderConfirmPopupFlw from '@/components/order-confirm-popup/order-confirm-popup-flw.vue';
|
import OrderConfirmPopupFlw from '@/components/order-confirm-popup/order-confirm-popup-flw.vue';
|
||||||
import PageContainer from '@/components/page-container/page-container.vue';
|
import PageContainer from '@/components/page-container/page-container.vue';
|
||||||
|
|
||||||
|
|
@ -271,32 +271,32 @@ export default {
|
||||||
const {
|
const {
|
||||||
goods,
|
goods,
|
||||||
goodslist,
|
goodslist,
|
||||||
join_count,
|
joinCount,
|
||||||
current_time,
|
currentTime,
|
||||||
status,
|
status,
|
||||||
status_text,
|
statusText,
|
||||||
user_consumption,
|
userConsumption,
|
||||||
user_count
|
userCount
|
||||||
} = res.data;
|
} = res.data;
|
||||||
this.orderData.goods = goods;
|
this.orderData.goods = goods;
|
||||||
// 更新福利屋基本信息
|
// 更新福利屋基本信息
|
||||||
this.bonusData = {
|
this.bonusData = {
|
||||||
title: goods.title,
|
title: goods.title,
|
||||||
tips: goods.goods_describe,
|
tips: goods.goodsDescribe,
|
||||||
time: goods.flw_start_time + '-' + goods.flw_end_time,
|
time: goods.flwStartTime + '-' + goods.flwEndTime,
|
||||||
open_time: goods.open_time,
|
open_time: goods.openTime,
|
||||||
start_time: goods.flw_start_time,
|
start_time: goods.flwStartTime,
|
||||||
end_time: goods.flw_end_time,
|
end_time: goods.flwEndTime,
|
||||||
choujiang_xianzhi: goods.choujiang_xianzhi,
|
choujiang_xianzhi: goods.choujiangXianzhi,
|
||||||
popularity: join_count || 0,
|
popularity: joinCount || 0,
|
||||||
quanju_xiangou: goods.quanju_xiangou,
|
quanju_xiangou: goods.quanjuXiangou,
|
||||||
price: goods.price
|
price: goods.price
|
||||||
};
|
};
|
||||||
if (user_consumption != null) {
|
if (userConsumption != null) {
|
||||||
this.user_total_consumption = user_consumption.total_consumed;
|
this.user_total_consumption = userConsumption.totalAmount || 0;
|
||||||
}
|
}
|
||||||
if (user_count != null) {
|
if (userCount != null) {
|
||||||
this.user_count = user_count;
|
this.user_count = userCount;
|
||||||
}
|
}
|
||||||
let index = 0;
|
let index = 0;
|
||||||
this.goodsList.splice(0, this.goodsList.length)
|
this.goodsList.splice(0, this.goodsList.length)
|
||||||
|
|
@ -311,24 +311,24 @@ export default {
|
||||||
stock: item.stock,
|
stock: item.stock,
|
||||||
realPrice: item.price,
|
realPrice: item.price,
|
||||||
sortIndex: item.sort,
|
sortIndex: item.sort,
|
||||||
type: item.shang_title,
|
type: item.shangTitle,
|
||||||
typeColor: item.shang_color,
|
typeColor: item.shangColor,
|
||||||
imgurl_detail: item.imgurl_detail
|
imgurl_detail: item.imgurlDetail
|
||||||
});
|
});
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算倒计时
|
// 计算倒计时
|
||||||
this.calculateRemainingTime(goods.open_time, goods.flw_start_time, current_time);
|
this.calculateRemainingTime(goods.openTime, goods.flwStartTime, currentTime);
|
||||||
this.startCountdownTimer(goods.open_time, goods.flw_start_time, current_time);
|
this.startCountdownTimer(goods.openTime, goods.flwStartTime, currentTime);
|
||||||
|
|
||||||
// 其他状态信息
|
// 其他状态信息
|
||||||
this.activityStatus = status;
|
this.activityStatus = status;
|
||||||
this.activityStatusText = status_text;
|
this.activityStatusText = statusText;
|
||||||
|
|
||||||
// 获取参与人数和赏品记录列表
|
// 获取参与人数和赏品记录列表
|
||||||
if (join_count > 0) {
|
if (joinCount > 0) {
|
||||||
await this.loadParticipants(goods_id);
|
await this.loadParticipants(goods_id);
|
||||||
await this.loadAwardRecords(goods_id);
|
await this.loadAwardRecords(goods_id);
|
||||||
}
|
}
|
||||||
|
|
@ -355,8 +355,8 @@ export default {
|
||||||
|
|
||||||
let res;
|
let res;
|
||||||
if (type == 1) {
|
if (type == 1) {
|
||||||
// API: orderbuy
|
// API: fuliwu_buy
|
||||||
res = await createOrder(data);
|
res = await buyWelfareHouse(data);
|
||||||
} else {
|
} else {
|
||||||
// API: ordermoney
|
// API: ordermoney
|
||||||
res = await calcOrderMoney(data);
|
res = await calcOrderMoney(data);
|
||||||
|
|
@ -535,13 +535,13 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleTabChange(index) {
|
async handleTabChange(index) {
|
||||||
this.currentTab = index;
|
this.currentTab = index;
|
||||||
// 添加延迟加载动画
|
// 切换到参与人数或赏品记录时,重新加载数据
|
||||||
if (index === 1 && this.participantList.length > 0) {
|
if (index === 1) {
|
||||||
this.animateListItems('participant-row');
|
await this.loadParticipants(this.goods_id);
|
||||||
} else if (index === 2 && this.awardRecordList.length > 0) {
|
} else if (index === 2) {
|
||||||
this.animateListItems('award-row');
|
await this.loadAwardRecords(this.goods_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace HoneyBox.Admin.Business.Extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 灵活的日期时间 JSON 转换器
|
||||||
|
/// 支持多种日期格式:ISO 8601、"yyyy-MM-dd HH:mm:ss"、"yyyy-MM-dd HH:mm" 等
|
||||||
|
/// </summary>
|
||||||
|
public class FlexibleDateTimeConverter : JsonConverter<DateTime?>
|
||||||
|
{
|
||||||
|
private static readonly string[] DateFormats = new[]
|
||||||
|
{
|
||||||
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
"yyyy-MM-dd HH:mm",
|
||||||
|
"yyyy-MM-dd",
|
||||||
|
"yyyy/MM/dd HH:mm:ss",
|
||||||
|
"yyyy/MM/dd HH:mm",
|
||||||
|
"yyyy/MM/dd",
|
||||||
|
"MM/dd/yyyy HH:mm:ss",
|
||||||
|
"MM/dd/yyyy",
|
||||||
|
};
|
||||||
|
|
||||||
|
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.Null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.TokenType == JsonTokenType.String)
|
||||||
|
{
|
||||||
|
var dateString = reader.GetString();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(dateString))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试标准 ISO 8601 格式
|
||||||
|
if (DateTime.TryParse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试自定义格式
|
||||||
|
foreach (var format in DateFormats)
|
||||||
|
{
|
||||||
|
if (DateTime.TryParseExact(dateString, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"无法解析日期时间: {dateString}");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"意外的 JSON 令牌类型: {reader.TokenType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value.HasValue)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.Value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteNullValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 非空日期时间转换器
|
||||||
|
/// </summary>
|
||||||
|
public class FlexibleDateTimeNonNullableConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
private static readonly string[] DateFormats = new[]
|
||||||
|
{
|
||||||
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
"yyyy-MM-dd HH:mm",
|
||||||
|
"yyyy-MM-dd",
|
||||||
|
"yyyy/MM/dd HH:mm:ss",
|
||||||
|
"yyyy/MM/dd HH:mm",
|
||||||
|
"yyyy/MM/dd",
|
||||||
|
};
|
||||||
|
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.String)
|
||||||
|
{
|
||||||
|
var dateString = reader.GetString();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(dateString))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.TryParse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var format in DateFormats)
|
||||||
|
{
|
||||||
|
if (DateTime.TryParseExact(dateString, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"无法解析日期时间: {dateString}");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"意外的 JSON 令牌类型: {reader.TokenType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using HoneyBox.Admin.Business.Models;
|
using HoneyBox.Admin.Business.Models;
|
||||||
|
using HoneyBox.Admin.Business.Extensions;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace HoneyBox.Admin.Business.Models.Goods;
|
namespace HoneyBox.Admin.Business.Models.Goods;
|
||||||
|
|
||||||
|
|
@ -103,16 +105,19 @@ public class GoodsCreateRequest
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 福利屋开始时间
|
/// 福利屋开始时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(FlexibleDateTimeConverter))]
|
||||||
public DateTime? FlwStartTime { get; set; }
|
public DateTime? FlwStartTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 福利屋结束时间
|
/// 福利屋结束时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(FlexibleDateTimeConverter))]
|
||||||
public DateTime? FlwEndTime { get; set; }
|
public DateTime? FlwEndTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开放时间
|
/// 开放时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(FlexibleDateTimeConverter))]
|
||||||
public DateTime? OpenTime { get; set; }
|
public DateTime? OpenTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -80,12 +80,13 @@ public class WelfareController : ControllerBase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("fuliwu")]
|
[HttpGet("fuliwu")]
|
||||||
[HttpPost("fuliwu")]
|
[HttpPost("fuliwu")]
|
||||||
public async Task<ApiResponse<FuliwuListResponse>> GetFuliwuList([FromForm] FuliwuListRequest? request)
|
public async Task<ApiResponse<FuliwuListResponse>> GetFuliwuList()
|
||||||
{
|
{
|
||||||
// 尝试从Query获取参数 (GET请求)
|
// 从多种来源获取参数
|
||||||
var type = request?.Type ?? 1;
|
var type = 1;
|
||||||
var page = request?.Page ?? 1;
|
var page = 1;
|
||||||
|
|
||||||
|
// 1. 尝试从 Query 获取 (GET请求)
|
||||||
if (Request.Query.ContainsKey("type"))
|
if (Request.Query.ContainsKey("type"))
|
||||||
{
|
{
|
||||||
int.TryParse(Request.Query["type"], out type);
|
int.TryParse(Request.Query["type"], out type);
|
||||||
|
|
@ -95,6 +96,45 @@ public class WelfareController : ControllerBase
|
||||||
int.TryParse(Request.Query["page"], out page);
|
int.TryParse(Request.Query["page"], out page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 尝试从 Form 获取 (POST form-urlencoded)
|
||||||
|
if (Request.HasFormContentType)
|
||||||
|
{
|
||||||
|
if (Request.Form.ContainsKey("type"))
|
||||||
|
{
|
||||||
|
int.TryParse(Request.Form["type"], out type);
|
||||||
|
}
|
||||||
|
if (Request.Form.ContainsKey("page"))
|
||||||
|
{
|
||||||
|
int.TryParse(Request.Form["page"], out page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. 尝试从 JSON Body 获取 (POST application/json)
|
||||||
|
else if (Request.ContentType?.Contains("application/json") == true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Request.Body.Position = 0;
|
||||||
|
using var reader = new StreamReader(Request.Body);
|
||||||
|
var body = await reader.ReadToEndAsync();
|
||||||
|
if (!string.IsNullOrEmpty(body))
|
||||||
|
{
|
||||||
|
var json = System.Text.Json.JsonDocument.Parse(body);
|
||||||
|
if (json.RootElement.TryGetProperty("type", out var typeElement))
|
||||||
|
{
|
||||||
|
type = typeElement.GetInt32();
|
||||||
|
}
|
||||||
|
if (json.RootElement.TryGetProperty("page", out var pageElement))
|
||||||
|
{
|
||||||
|
page = pageElement.GetInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to parse JSON body: {Error}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取用户ID (可选,未登录用户也可以查看)
|
// 获取用户ID (可选,未登录用户也可以查看)
|
||||||
var userId = GetCurrentUserId() ?? 0;
|
var userId = GetCurrentUserId() ?? 0;
|
||||||
|
|
||||||
|
|
@ -118,11 +158,10 @@ public class WelfareController : ControllerBase
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 福利屋购买/参与
|
/// 福利屋购买/参与
|
||||||
/// POST /api/fuliwu_buy
|
/// POST /api/fuliwu_buy
|
||||||
/// Requirements: 4.2
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("fuliwu_buy")]
|
[HttpPost("fuliwu_buy")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ApiResponse<WelfareBuyResponse>> BuyWelfare([FromForm] WelfareBuyRequest request)
|
public async Task<ApiResponse<WelfareBuyResponse>> BuyWelfare([FromBody] WelfareBuyRequest request)
|
||||||
{
|
{
|
||||||
var userId = GetCurrentUserId();
|
var userId = GetCurrentUserId();
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
|
|
@ -130,6 +169,8 @@ public class WelfareController : ControllerBase
|
||||||
return ApiResponse<WelfareBuyResponse>.Unauthorized();
|
return ApiResponse<WelfareBuyResponse>.Unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("BuyWelfare: UserId={UserId}, GoodsId={GoodsId}", userId, request.GoodsId);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await _welfareService.BuyWelfareAsync(userId.Value, request);
|
var result = await _welfareService.BuyWelfareAsync(userId.Value, request);
|
||||||
|
|
@ -151,13 +192,12 @@ public class WelfareController : ControllerBase
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取福利屋详情
|
/// 获取福利屋详情
|
||||||
/// GET/POST /api/fuliwu_detail
|
/// GET /api/fuliwu_detail
|
||||||
/// Requirements: 13.1-13.4
|
/// Requirements: 13.1-13.4
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("fuliwu_detail")]
|
[HttpGet("fuliwu_detail")]
|
||||||
[HttpPost("fuliwu_detail")]
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ApiResponse<WelfareDetailResponse>> GetWelfareDetail([FromForm] WelfareDetailRequest? request)
|
public async Task<ApiResponse<WelfareDetailResponse>> GetWelfareDetail([FromQuery] WelfareDetailRequest? request)
|
||||||
{
|
{
|
||||||
var userId = GetCurrentUserId();
|
var userId = GetCurrentUserId();
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
|
|
@ -199,61 +239,69 @@ public class WelfareController : ControllerBase
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取福利屋参与者列表
|
/// 获取福利屋参与者列表
|
||||||
/// POST /api/fuliwu_participants
|
/// GET /api/fuliwu_participants
|
||||||
/// Requirements: 14.1
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("fuliwu_participants")]
|
[HttpGet("fuliwu_participants")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ApiResponse<List<ParticipantDto>>> GetParticipants([FromForm] ParticipantsRequest request)
|
public async Task<ApiResponse<WelfareParticipantsResponse>> GetParticipants(
|
||||||
|
[FromQuery(Name = "goods_id")] int goodsId,
|
||||||
|
[FromQuery] int page = 1,
|
||||||
|
[FromQuery] int limit = 15)
|
||||||
{
|
{
|
||||||
var userId = GetCurrentUserId();
|
var userId = GetCurrentUserId();
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
{
|
{
|
||||||
return ApiResponse<List<ParticipantDto>>.Unauthorized();
|
return ApiResponse<WelfareParticipantsResponse>.Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (goodsId <= 0)
|
||||||
|
{
|
||||||
|
return ApiResponse<WelfareParticipantsResponse>.Fail("商品ID不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await _welfareService.GetParticipantsAsync(
|
var result = await _welfareService.GetParticipantsAsync(goodsId, page, limit);
|
||||||
request.GoodsId,
|
return ApiResponse<WelfareParticipantsResponse>.Success(new WelfareParticipantsResponse { List = result });
|
||||||
request.Page,
|
|
||||||
request.Limit);
|
|
||||||
return ApiResponse<List<ParticipantDto>>.Success(result);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Failed to get participants: GoodsId={GoodsId}", request.GoodsId);
|
_logger.LogError(ex, "Failed to get participants: GoodsId={GoodsId}", goodsId);
|
||||||
return ApiResponse<List<ParticipantDto>>.Fail("获取参与者列表失败");
|
return ApiResponse<WelfareParticipantsResponse>.Fail("获取参与者列表失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取福利屋开奖记录
|
/// 获取福利屋开奖记录
|
||||||
/// POST /api/fuliwu_records
|
/// GET /api/fuliwu_records
|
||||||
/// Requirements: 14.2
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("fuliwu_records")]
|
[HttpGet("fuliwu_records")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ApiResponse<List<WinningRecordDto>>> GetWinningRecords([FromForm] WinningRecordsRequest request)
|
public async Task<ApiResponse<WelfareRecordsResponse>> GetWinningRecords(
|
||||||
|
[FromQuery(Name = "goods_id")] int goodsId,
|
||||||
|
[FromQuery] int page = 1,
|
||||||
|
[FromQuery] int limit = 15)
|
||||||
{
|
{
|
||||||
var userId = GetCurrentUserId();
|
var userId = GetCurrentUserId();
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
{
|
{
|
||||||
return ApiResponse<List<WinningRecordDto>>.Unauthorized();
|
return ApiResponse<WelfareRecordsResponse>.Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (goodsId <= 0)
|
||||||
|
{
|
||||||
|
return ApiResponse<WelfareRecordsResponse>.Fail("商品ID不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await _welfareService.GetWinningRecordsAsync(
|
var result = await _welfareService.GetWinningRecordsAsync(goodsId, page, limit);
|
||||||
request.GoodsId,
|
return ApiResponse<WelfareRecordsResponse>.Success(new WelfareRecordsResponse { List = result });
|
||||||
request.Page,
|
|
||||||
request.Limit);
|
|
||||||
return ApiResponse<List<WinningRecordDto>>.Success(result);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Failed to get winning records: GoodsId={GoodsId}", request.GoodsId);
|
_logger.LogError(ex, "Failed to get winning records: GoodsId={GoodsId}", goodsId);
|
||||||
return ApiResponse<List<WinningRecordDto>>.Fail("获取开奖记录失败");
|
return ApiResponse<WelfareRecordsResponse>.Fail("获取开奖记录失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Autofac.Extensions.DependencyInjection;
|
using Autofac.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using HoneyBox.Api.BackgroundServices;
|
||||||
using HoneyBox.Api.Filters;
|
using HoneyBox.Api.Filters;
|
||||||
using HoneyBox.Core.Mappings;
|
using HoneyBox.Core.Mappings;
|
||||||
using HoneyBox.Infrastructure.Cache;
|
using HoneyBox.Infrastructure.Cache;
|
||||||
|
|
@ -102,6 +103,9 @@ try
|
||||||
builder.Services.AddSingleton<ICacheService>(sp =>
|
builder.Services.AddSingleton<ICacheService>(sp =>
|
||||||
new RedisCacheService(builder.Configuration));
|
new RedisCacheService(builder.Configuration));
|
||||||
|
|
||||||
|
// 注册福利屋开奖后台服务
|
||||||
|
builder.Services.AddHostedService<WelfareLotteryService>();
|
||||||
|
|
||||||
// 添加控制器
|
// 添加控制器
|
||||||
builder.Services.AddControllers(options =>
|
builder.Services.AddControllers(options =>
|
||||||
{
|
{
|
||||||
|
|
@ -169,6 +173,13 @@ try
|
||||||
// 使用 Serilog 请求日志
|
// 使用 Serilog 请求日志
|
||||||
app.UseSerilogRequestLogging();
|
app.UseSerilogRequestLogging();
|
||||||
|
|
||||||
|
// 启用请求体缓冲,允许多次读取
|
||||||
|
app.Use(async (context, next) =>
|
||||||
|
{
|
||||||
|
context.Request.EnableBuffering();
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
// 使用路由
|
// 使用路由
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,20 +62,29 @@ public class WelfareService : IWelfareService
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建查询
|
// 构建查询
|
||||||
var query = _dbContext.Goods
|
var now = DateTime.Now;
|
||||||
.Where(g => g.Status == type
|
IQueryable<Good> query;
|
||||||
&& g.Type == WelfareType
|
|
||||||
&& g.IsOpen == (type == 1 ? (byte)0 : (byte)1)
|
|
||||||
&& g.UnlockAmount <= userTotalConsumption);
|
|
||||||
|
|
||||||
// 排序
|
|
||||||
if (type == 1)
|
if (type == 1)
|
||||||
{
|
{
|
||||||
query = query.OrderByDescending(g => g.Sort).ThenByDescending(g => g.Id);
|
// 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
|
else
|
||||||
{
|
{
|
||||||
query = query.OrderByDescending(g => g.OpenTime);
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取总数
|
// 获取总数
|
||||||
|
|
@ -585,26 +594,40 @@ public class WelfareService : IWelfareService
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建查询
|
// 构建查询
|
||||||
var query = _dbContext.Goods
|
var now = DateTime.Now;
|
||||||
.Where(g => g.Status == type
|
IQueryable<Good> query;
|
||||||
&& g.Type == WelfareType
|
|
||||||
&& g.IsOpen == (type == 1 ? (byte)0 : (byte)1)
|
_logger.LogInformation("GetFuliwuListAsync: userId={UserId}, type={Type}, page={Page}, userTotalConsumption={Consumption}, now={Now}",
|
||||||
&& g.UnlockAmount <= userTotalConsumption);
|
userId, type, page, userTotalConsumption, now);
|
||||||
|
|
||||||
// 排序
|
|
||||||
if (type == 1)
|
if (type == 1)
|
||||||
{
|
{
|
||||||
query = query.OrderByDescending(g => g.Sort).ThenByDescending(g => g.Id);
|
// 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
|
else
|
||||||
{
|
{
|
||||||
query = query.OrderByDescending(g => g.OpenTime);
|
// 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 total = await query.CountAsync();
|
||||||
var lastPage = (int)Math.Ceiling((double)total / paginate);
|
var lastPage = (int)Math.Ceiling((double)total / paginate);
|
||||||
|
|
||||||
|
_logger.LogInformation("GetFuliwuListAsync: total={Total}, lastPage={LastPage}", total, lastPage);
|
||||||
|
|
||||||
// 分页查询
|
// 分页查询
|
||||||
var goods = await query
|
var goods = await query
|
||||||
.Skip((page - 1) * paginate)
|
.Skip((page - 1) * paginate)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace HoneyBox.Model.Converters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JSON转换器:将字符串或空值转换为int
|
||||||
|
/// </summary>
|
||||||
|
public class StringToIntConverter : JsonConverter<int>
|
||||||
|
{
|
||||||
|
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.String)
|
||||||
|
{
|
||||||
|
var stringValue = reader.GetString();
|
||||||
|
if (string.IsNullOrEmpty(stringValue))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (int.TryParse(stringValue, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.TokenType == JsonTokenType.Number)
|
||||||
|
{
|
||||||
|
return reader.GetInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.TokenType == JsonTokenType.Null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteNumberValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -549,31 +549,39 @@ public class WelfareBuyRequest
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 商品ID
|
/// 商品ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Text.Json.Serialization.JsonPropertyName("goods_id")]
|
||||||
|
[System.Text.Json.Serialization.JsonConverter(typeof(HoneyBox.Model.Converters.StringToIntConverter))]
|
||||||
public int GoodsId { get; set; }
|
public int GoodsId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 购买数量/抽奖次数
|
/// 购买数量/抽奖次数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Text.Json.Serialization.JsonPropertyName("prize_num")]
|
||||||
public int PrizeNum { get; set; } = 1;
|
public int PrizeNum { get; set; } = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否使用余额抵扣 0=不抵扣 1=抵扣
|
/// 是否使用余额抵扣 0=不抵扣 1=抵扣
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Text.Json.Serialization.JsonPropertyName("use_money_is")]
|
||||||
public int UseMoneyIs { get; set; }
|
public int UseMoneyIs { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否使用积分抵扣 0=不抵扣 1=抵扣
|
/// 是否使用积分抵扣 0=不抵扣 1=抵扣
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Text.Json.Serialization.JsonPropertyName("use_integral_is")]
|
||||||
public int UseIntegralIs { get; set; }
|
public int UseIntegralIs { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否使用货币2抵扣 0=不抵扣 1=抵扣
|
/// 是否使用货币2抵扣 0=不抵扣 1=抵扣
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Text.Json.Serialization.JsonPropertyName("use_money2_is")]
|
||||||
public int UseMoney2Is { get; set; }
|
public int UseMoney2Is { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 优惠券ID
|
/// 优惠券ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Text.Json.Serialization.JsonPropertyName("coupon_id")]
|
||||||
|
[System.Text.Json.Serialization.JsonConverter(typeof(HoneyBox.Model.Converters.StringToIntConverter))]
|
||||||
public int CouponId { get; set; }
|
public int CouponId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -629,3 +637,26 @@ public class FuliwuListResponse
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int LastPage { get; set; }
|
public int LastPage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 福利屋参与者列表响应
|
||||||
|
/// </summary>
|
||||||
|
public class WelfareParticipantsResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 参与者列表
|
||||||
|
/// </summary>
|
||||||
|
public List<ParticipantDto> List { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 福利屋开奖记录响应
|
||||||
|
/// </summary>
|
||||||
|
public class WelfareRecordsResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 开奖记录列表
|
||||||
|
/// </summary>
|
||||||
|
public List<WinningRecordDto> List { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user