- 修改 ConfigService.GetPlatformConfigAsync 返回 isWebPay=false,关闭 Web 支付 - 重写 WechatService.CreatePayOrderAsync 实现原生微信支付: - 调用微信统一下单 API 获取 prepay_id - 生成 MD5 签名 - 返回前端 uni.requestPayment 所需的参数(appId, timeStamp, nonceStr, package, signType, paySign) - 更新 IWechatService 接口,添加 NativePayParams 类型 - 更新 WarehouseModels.cs,SendResultDto 使用 NativePayResultDto - 更新 WarehouseService.SendPrizesAsync 使用新的支付参数格式 - 更新 appsettings.json 配置微信支付商户信息
1757 lines
60 KiB
C#
1757 lines
60 KiB
C#
using HoneyBox.Core.Interfaces;
|
||
using HoneyBox.Model.Data;
|
||
using HoneyBox.Model.Entities;
|
||
using HoneyBox.Model.Models;
|
||
using HoneyBox.Model.Models.Order;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace HoneyBox.Core.Services;
|
||
|
||
/// <summary>
|
||
/// 仓库服务实现
|
||
/// </summary>
|
||
public class WarehouseService : IWarehouseService
|
||
{
|
||
private readonly HoneyBoxDbContext _dbContext;
|
||
private readonly ILogger<WarehouseService> _logger;
|
||
private readonly ILogisticsService _logisticsService;
|
||
private readonly IWechatService _wechatService;
|
||
|
||
public WarehouseService(
|
||
HoneyBoxDbContext dbContext,
|
||
ILogger<WarehouseService> logger,
|
||
ILogisticsService logisticsService,
|
||
IWechatService wechatService)
|
||
{
|
||
_dbContext = dbContext;
|
||
_logger = logger;
|
||
_logisticsService = logisticsService;
|
||
_wechatService = wechatService;
|
||
}
|
||
|
||
#region 仓库查询
|
||
|
||
/// <inheritdoc />
|
||
public async Task<WarehouseIndexResponseDto> GetWarehouseIndexAsync(int userId, WarehouseIndexRequest request)
|
||
{
|
||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
var showDadajuan = true;
|
||
|
||
// 自动将预售商品转为现货(当sale_time <= 当前时间)
|
||
await AutoConvertPresaleToAvailableAsync(userId, currentTime);
|
||
|
||
var response = new WarehouseIndexResponseDto();
|
||
|
||
switch (request.Type)
|
||
{
|
||
case 1: // 赏品
|
||
response = await GetPrizesWarehouseAsync(userId, request, currentTime);
|
||
showDadajuan = await CheckShowDadajuanAsync(userId, response.Data.Any());
|
||
break;
|
||
case 2: // 预售
|
||
response = await GetPresaleWarehouseAsync(userId, request);
|
||
break;
|
||
case 3: // 卡册
|
||
response = await GetCardAlbumWarehouseAsync(userId, request);
|
||
break;
|
||
case 4: // 保险柜
|
||
response = await GetSafeWarehouseAsync(userId, request);
|
||
break;
|
||
case 5: // 无限赏
|
||
response = await GetInfiniteWarehouseAsync(userId, request);
|
||
break;
|
||
default:
|
||
throw new InvalidOperationException("请求错误");
|
||
}
|
||
|
||
// 获取运费设置
|
||
response.Yufei = await GetShippingFeeConfigAsync(request.Type);
|
||
response.ShowDadajuan = showDadajuan;
|
||
|
||
return response;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动将预售商品转为现货
|
||
/// </summary>
|
||
private async Task AutoConvertPresaleToAvailableAsync(int userId, long currentTime)
|
||
{
|
||
var presaleItems = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId && o.Status == 0 && o.GoodslistType == 2)
|
||
.Select(o => new { o.Id, o.GoodslistId, o.UserId, o.GoodslistSaleTime })
|
||
.ToListAsync();
|
||
|
||
var currentDateTime = DateTimeOffset.FromUnixTimeSeconds(currentTime).DateTime;
|
||
|
||
foreach (var item in presaleItems)
|
||
{
|
||
var goodsItem = await _dbContext.GoodsItems
|
||
.Where(g => g.Id == item.GoodslistId)
|
||
.Select(g => new { g.SaleTime })
|
||
.FirstOrDefaultAsync();
|
||
|
||
bool shouldConvert = false;
|
||
if (goodsItem?.SaleTime != null)
|
||
{
|
||
shouldConvert = goodsItem.SaleTime <= currentDateTime;
|
||
}
|
||
else if (item.GoodslistSaleTime > 0 && item.GoodslistSaleTime <= currentTime)
|
||
{
|
||
shouldConvert = true;
|
||
}
|
||
|
||
if (shouldConvert)
|
||
{
|
||
await _dbContext.OrderItems
|
||
.Where(o => o.Id == item.Id && o.Status == 0 && o.UserId == userId && o.GoodslistType == 2)
|
||
.ExecuteUpdateAsync(s => s.SetProperty(o => o.GoodslistType, (byte)1));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取赏品仓库(type=1)
|
||
/// </summary>
|
||
private async Task<WarehouseIndexResponseDto> GetPrizesWarehouseAsync(int userId, WarehouseIndexRequest request, long currentTime)
|
||
{
|
||
var response = new WarehouseIndexResponseDto();
|
||
var total = 0;
|
||
|
||
var goodsGroups = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType != 4)
|
||
.GroupBy(o => o.GoodsId)
|
||
.Select(g => g.Key)
|
||
.OrderBy(g => g)
|
||
.ToListAsync();
|
||
|
||
foreach (var goodsId in goodsGroups)
|
||
{
|
||
var goodsTitle = await GetGoodsTitleAsync(goodsId);
|
||
|
||
var query = _dbContext.OrderItems
|
||
.Where(o => o.GoodsId == goodsId
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType != 4);
|
||
|
||
if (!string.IsNullOrEmpty(request.Keyword))
|
||
{
|
||
query = query.Where(o => o.GoodslistTitle != null && o.GoodslistTitle.Contains(request.Keyword));
|
||
}
|
||
|
||
var orderListItems = await query
|
||
.GroupBy(o => o.PrizeCode)
|
||
.Select(g => new
|
||
{
|
||
Id = g.First().Id,
|
||
GoodslistTitle = g.First().GoodslistTitle,
|
||
GoodslistImgurl = g.First().GoodslistImgurl,
|
||
GoodslistMoney = g.First().GoodslistMoney,
|
||
GoodsId = g.First().GoodsId,
|
||
ShangId = g.First().ShangId,
|
||
PrizeCode = g.Key,
|
||
PrizeNum = g.Count()
|
||
})
|
||
.OrderBy(o => o.ShangId)
|
||
.ToListAsync();
|
||
|
||
var orderListDtos = new List<WarehouseItemDto>();
|
||
foreach (var item in orderListItems)
|
||
{
|
||
var orderListIds = await _dbContext.OrderItems
|
||
.Where(o => o.GoodsId == item.GoodsId
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType != 4 && o.OrderType != 9
|
||
&& o.PrizeCode == item.PrizeCode)
|
||
.OrderBy(o => o.ShangId)
|
||
.Select(o => o.Id)
|
||
.ToListAsync();
|
||
|
||
var shangTitle = await GetShangTitleAsync(item.ShangId);
|
||
|
||
orderListDtos.Add(new WarehouseItemDto
|
||
{
|
||
Id = item.Id,
|
||
GoodsListTitle = item.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(item.GoodslistImgurl),
|
||
GoodsListMoney = ((decimal)item.GoodslistMoney * 100).ToString("0"),
|
||
GoodsId = item.GoodsId ?? 0,
|
||
ShangId = item.ShangId,
|
||
PrizeCode = item.PrizeCode,
|
||
PrizeNum = item.PrizeNum,
|
||
OrderListIds = orderListIds,
|
||
ShangTitle = shangTitle
|
||
});
|
||
}
|
||
|
||
var orderListTotal = orderListDtos.Sum(o => o.PrizeNum);
|
||
total += orderListTotal;
|
||
|
||
response.Data.Add(new WarehouseGoodsGroupDto
|
||
{
|
||
GoodsId = goodsId ?? 0,
|
||
GoodsTitle = goodsTitle,
|
||
OrderList = orderListDtos,
|
||
OrderListTotal = orderListTotal,
|
||
OrderListLength = orderListDtos.Count
|
||
});
|
||
}
|
||
|
||
response.Total = total;
|
||
return response;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取预售仓库(type=2)
|
||
/// </summary>
|
||
private async Task<WarehouseIndexResponseDto> GetPresaleWarehouseAsync(int userId, WarehouseIndexRequest request)
|
||
{
|
||
var response = new WarehouseIndexResponseDto();
|
||
var total = 0;
|
||
|
||
var goodsGroups = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 2
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType != 4)
|
||
.GroupBy(o => o.GoodsId)
|
||
.Select(g => g.Key)
|
||
.OrderBy(g => g)
|
||
.ToListAsync();
|
||
|
||
foreach (var goodsId in goodsGroups)
|
||
{
|
||
var goodsTitle = await GetGoodsTitleAsync(goodsId);
|
||
|
||
var query = _dbContext.OrderItems
|
||
.Where(o => o.GoodsId == goodsId
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 2
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType != 4);
|
||
|
||
if (!string.IsNullOrEmpty(request.Keyword))
|
||
{
|
||
query = query.Where(o => o.GoodslistTitle != null && o.GoodslistTitle.Contains(request.Keyword));
|
||
}
|
||
|
||
var orderListItems = await query
|
||
.GroupBy(o => o.PrizeCode)
|
||
.Select(g => new
|
||
{
|
||
GoodslistTitle = g.First().GoodslistTitle,
|
||
GoodslistImgurl = g.First().GoodslistImgurl,
|
||
GoodslistMoney = g.First().GoodslistMoney,
|
||
GoodsId = g.First().GoodsId,
|
||
ShangId = g.First().ShangId,
|
||
PrizeCode = g.Key,
|
||
GoodslistSaleTime = g.First().GoodslistSaleTime,
|
||
PrizeNum = g.Count()
|
||
})
|
||
.OrderBy(o => o.ShangId)
|
||
.ToListAsync();
|
||
|
||
var orderListDtos = new List<WarehouseItemDto>();
|
||
foreach (var item in orderListItems)
|
||
{
|
||
var shangTitle = await GetShangTitleAsync(item.ShangId);
|
||
|
||
orderListDtos.Add(new WarehouseItemDto
|
||
{
|
||
GoodsListTitle = item.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(item.GoodslistImgurl),
|
||
GoodsListMoney = ((decimal)item.GoodslistMoney * 100).ToString("0"),
|
||
GoodsId = item.GoodsId ?? 0,
|
||
ShangId = item.ShangId,
|
||
PrizeCode = item.PrizeCode,
|
||
PrizeNum = item.PrizeNum,
|
||
ShangTitle = shangTitle,
|
||
GoodsListSaleTime = item.GoodslistSaleTime > 0
|
||
? DateTimeOffset.FromUnixTimeSeconds(item.GoodslistSaleTime).ToString("yyyy-MM-dd")
|
||
: null
|
||
});
|
||
}
|
||
|
||
var orderListTotal = orderListDtos.Sum(o => o.PrizeNum);
|
||
total += orderListTotal;
|
||
|
||
response.Data.Add(new WarehouseGoodsGroupDto
|
||
{
|
||
GoodsId = goodsId ?? 0,
|
||
GoodsTitle = goodsTitle,
|
||
OrderList = orderListDtos,
|
||
OrderListTotal = orderListTotal,
|
||
OrderListLength = orderListDtos.Count
|
||
});
|
||
}
|
||
|
||
response.Total = total;
|
||
return response;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取卡册仓库(type=3)
|
||
/// </summary>
|
||
private async Task<WarehouseIndexResponseDto> GetCardAlbumWarehouseAsync(int userId, WarehouseIndexRequest request)
|
||
{
|
||
var response = new WarehouseIndexResponseDto();
|
||
var pageSize = 30;
|
||
|
||
var query = _dbContext.Orders
|
||
.Where(o => o.UserId == userId && o.OrderType == 4);
|
||
|
||
if (!string.IsNullOrEmpty(request.Keyword))
|
||
{
|
||
query = query.Where(o => o.GoodsTitle != null && o.GoodsTitle.Contains(request.Keyword));
|
||
}
|
||
|
||
if (request.CategoryId > 0)
|
||
{
|
||
var goodsIdsInCategory = await _dbContext.Goods
|
||
.Where(g => g.CategoryId == request.CategoryId)
|
||
.Select(g => g.Id)
|
||
.ToListAsync();
|
||
query = query.Where(o => goodsIdsInCategory.Contains(o.GoodsId));
|
||
}
|
||
|
||
var groupedOrders = await query
|
||
.GroupBy(o => o.GoodsId)
|
||
.Select(g => new { GoodsId = g.Key, OrderId = g.Max(o => o.Id) })
|
||
.OrderByDescending(g => g.OrderId)
|
||
.Skip((request.Page - 1) * pageSize)
|
||
.Take(pageSize)
|
||
.ToListAsync();
|
||
|
||
var totalGroups = await query.GroupBy(o => o.GoodsId).CountAsync();
|
||
response.LastPage = (int)Math.Ceiling((double)totalGroups / pageSize);
|
||
|
||
foreach (var group in groupedOrders)
|
||
{
|
||
var order = await _dbContext.Orders
|
||
.Where(o => o.Id == group.OrderId)
|
||
.Select(o => new { o.Id, o.GoodsId, o.GoodsTitle, o.GoodsImgurl })
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (order == null) continue;
|
||
|
||
var goods = await _dbContext.Goods
|
||
.Where(g => g.Id == group.GoodsId)
|
||
.Select(g => new { g.Title, g.CardBanner })
|
||
.FirstOrDefaultAsync();
|
||
|
||
var goodsTitle = goods?.Title ?? order.GoodsTitle ?? string.Empty;
|
||
var goodsImgUrl = FormatImageUrl(goods?.CardBanner ?? order.GoodsImgurl);
|
||
|
||
var allCount = await _dbContext.GoodsItems
|
||
.Where(g => g.GoodsId == group.GoodsId)
|
||
.CountAsync();
|
||
|
||
var buyCount = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.GoodsId == group.GoodsId
|
||
&& o.Status == 0)
|
||
.GroupBy(o => o.GoodslistId)
|
||
.CountAsync();
|
||
|
||
decimal gailv = 0;
|
||
if (buyCount > 0 && allCount > 0)
|
||
{
|
||
gailv = Math.Round((decimal)buyCount / allCount * 100, 2);
|
||
}
|
||
|
||
response.Data.Add(new WarehouseGoodsGroupDto
|
||
{
|
||
GoodsId = group.GoodsId,
|
||
GoodsTitle = goodsTitle,
|
||
OrderList = new List<WarehouseItemDto>
|
||
{
|
||
new WarehouseItemDto
|
||
{
|
||
Id = order.Id,
|
||
GoodsId = group.GoodsId,
|
||
GoodsTitle = goodsTitle,
|
||
GoodsListImgUrl = goodsImgUrl,
|
||
PrizeNum = buyCount
|
||
}
|
||
},
|
||
OrderListTotal = allCount,
|
||
OrderListLength = buyCount
|
||
});
|
||
}
|
||
|
||
return response;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取保险柜仓库(type=4)
|
||
/// </summary>
|
||
private async Task<WarehouseIndexResponseDto> GetSafeWarehouseAsync(int userId, WarehouseIndexRequest request)
|
||
{
|
||
var response = new WarehouseIndexResponseDto();
|
||
var total = 0;
|
||
|
||
var goodsGroups = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 1
|
||
&& o.OrderType != 4)
|
||
.GroupBy(o => o.GoodsId)
|
||
.Select(g => g.Key)
|
||
.OrderBy(g => g)
|
||
.ToListAsync();
|
||
|
||
foreach (var goodsId in goodsGroups)
|
||
{
|
||
var goodsTitle = await GetGoodsTitleAsync(goodsId);
|
||
|
||
var query = _dbContext.OrderItems
|
||
.Where(o => o.GoodsId == goodsId
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 1
|
||
&& o.OrderType != 4);
|
||
|
||
if (!string.IsNullOrEmpty(request.Keyword))
|
||
{
|
||
query = query.Where(o => o.GoodslistTitle != null && o.GoodslistTitle.Contains(request.Keyword));
|
||
}
|
||
|
||
var orderListItems = await query
|
||
.GroupBy(o => o.PrizeCode)
|
||
.Select(g => new
|
||
{
|
||
GoodslistTitle = g.First().GoodslistTitle,
|
||
GoodslistImgurl = g.First().GoodslistImgurl,
|
||
GoodslistMoney = g.First().GoodslistMoney,
|
||
GoodsId = g.First().GoodsId,
|
||
ShangId = g.First().ShangId,
|
||
PrizeCode = g.Key,
|
||
PrizeNum = g.Count()
|
||
})
|
||
.OrderBy(o => o.ShangId)
|
||
.ToListAsync();
|
||
|
||
var orderListDtos = new List<WarehouseItemDto>();
|
||
foreach (var item in orderListItems)
|
||
{
|
||
var shangTitle = await GetShangTitleAsync(item.ShangId);
|
||
|
||
orderListDtos.Add(new WarehouseItemDto
|
||
{
|
||
GoodsListTitle = item.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(item.GoodslistImgurl),
|
||
GoodsListMoney = ((decimal)item.GoodslistMoney * 100).ToString("0"),
|
||
GoodsId = item.GoodsId ?? 0,
|
||
ShangId = item.ShangId,
|
||
PrizeCode = item.PrizeCode,
|
||
PrizeNum = item.PrizeNum,
|
||
ShangTitle = shangTitle
|
||
});
|
||
}
|
||
|
||
var orderListTotal = orderListDtos.Sum(o => o.PrizeNum);
|
||
total += orderListTotal;
|
||
|
||
response.Data.Add(new WarehouseGoodsGroupDto
|
||
{
|
||
GoodsId = goodsId ?? 0,
|
||
GoodsTitle = goodsTitle,
|
||
OrderList = orderListDtos,
|
||
OrderListTotal = orderListTotal,
|
||
OrderListLength = orderListDtos.Count
|
||
});
|
||
}
|
||
|
||
response.Total = total;
|
||
return response;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取无限赏仓库(type=5)
|
||
/// </summary>
|
||
private async Task<WarehouseIndexResponseDto> GetInfiniteWarehouseAsync(int userId, WarehouseIndexRequest request)
|
||
{
|
||
var response = new WarehouseIndexResponseDto();
|
||
var total = 0;
|
||
|
||
var goodsGroups = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType == 2)
|
||
.GroupBy(o => o.GoodsId)
|
||
.Select(g => g.Key)
|
||
.OrderBy(g => g)
|
||
.ToListAsync();
|
||
|
||
foreach (var goodsId in goodsGroups)
|
||
{
|
||
var goodsTitle = await GetGoodsTitleAsync(goodsId);
|
||
|
||
var query = _dbContext.OrderItems
|
||
.Where(o => o.GoodsId == goodsId
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType == 2);
|
||
|
||
if (!string.IsNullOrEmpty(request.Keyword))
|
||
{
|
||
query = query.Where(o => o.GoodslistTitle != null && o.GoodslistTitle.Contains(request.Keyword));
|
||
}
|
||
|
||
var orderListItems = await query
|
||
.GroupBy(o => o.PrizeCode)
|
||
.Select(g => new
|
||
{
|
||
Id = g.First().Id,
|
||
GoodslistTitle = g.First().GoodslistTitle,
|
||
GoodslistImgurl = g.First().GoodslistImgurl,
|
||
GoodslistMoney = g.First().GoodslistMoney,
|
||
GoodsId = g.First().GoodsId,
|
||
ShangId = g.First().ShangId,
|
||
PrizeCode = g.Key,
|
||
PrizeNum = g.Count()
|
||
})
|
||
.OrderBy(o => o.ShangId)
|
||
.ToListAsync();
|
||
|
||
var orderListDtos = new List<WarehouseItemDto>();
|
||
foreach (var item in orderListItems)
|
||
{
|
||
var orderListIds = await _dbContext.OrderItems
|
||
.Where(o => o.GoodsId == item.GoodsId
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.InsuranceIs == 0
|
||
&& o.OrderType == 2
|
||
&& o.PrizeCode == item.PrizeCode)
|
||
.OrderBy(o => o.ShangId)
|
||
.Select(o => o.Id)
|
||
.ToListAsync();
|
||
|
||
var shangTitle = await GetShangTitleAsync(item.ShangId);
|
||
|
||
orderListDtos.Add(new WarehouseItemDto
|
||
{
|
||
Id = item.Id,
|
||
GoodsListTitle = item.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(item.GoodslistImgurl),
|
||
GoodsListMoney = ((decimal)item.GoodslistMoney * 100).ToString("0"),
|
||
GoodsId = item.GoodsId ?? 0,
|
||
ShangId = item.ShangId,
|
||
PrizeCode = item.PrizeCode,
|
||
PrizeNum = item.PrizeNum,
|
||
OrderListIds = orderListIds,
|
||
ShangTitle = shangTitle
|
||
});
|
||
}
|
||
|
||
var orderListTotal = orderListDtos.Sum(o => o.PrizeNum);
|
||
total += orderListTotal;
|
||
|
||
response.Data.Add(new WarehouseGoodsGroupDto
|
||
{
|
||
GoodsId = goodsId ?? 0,
|
||
GoodsTitle = goodsTitle,
|
||
OrderList = orderListDtos,
|
||
OrderListTotal = orderListTotal,
|
||
OrderListLength = orderListDtos.Count
|
||
});
|
||
}
|
||
|
||
response.Total = total;
|
||
return response;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Helper Methods
|
||
|
||
/// <summary>
|
||
/// 获取商品标题
|
||
/// </summary>
|
||
private async Task<string> GetGoodsTitleAsync(int? goodsId)
|
||
{
|
||
if (goodsId == null) return "其他";
|
||
if (goodsId == 0) return "无限令奖品";
|
||
if (goodsId == -1) return "周榜奖品";
|
||
if (goodsId == -2) return "月榜奖品";
|
||
|
||
var title = await _dbContext.Goods
|
||
.Where(g => g.Id == goodsId)
|
||
.Select(g => g.Title)
|
||
.FirstOrDefaultAsync();
|
||
|
||
return title ?? "其他";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取赏品等级标题
|
||
/// </summary>
|
||
private async Task<string> GetShangTitleAsync(int shangId)
|
||
{
|
||
if (shangId <= 0) return string.Empty;
|
||
|
||
var title = await _dbContext.PrizeLevels
|
||
.Where(p => p.Id == shangId)
|
||
.Select(p => p.Title)
|
||
.FirstOrDefaultAsync();
|
||
|
||
return title ?? string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否显示达达卷
|
||
/// </summary>
|
||
private async Task<bool> CheckShowDadajuanAsync(int userId, bool hasData)
|
||
{
|
||
if (!hasData) return false;
|
||
|
||
var config = await _dbContext.Configs
|
||
.Where(c => c.ConfigKey == "app_setting")
|
||
.Select(c => c.ConfigValue)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (string.IsNullOrEmpty(config)) return true;
|
||
|
||
try
|
||
{
|
||
var settings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(config);
|
||
if (settings != null && settings.TryGetValue("show_dadajuan_limit", out var limitObj))
|
||
{
|
||
var limitStr = limitObj?.ToString();
|
||
if (!string.IsNullOrEmpty(limitStr) && limitStr != "0" && int.TryParse(limitStr, out var limit))
|
||
{
|
||
var totalConsumption = await _dbContext.Orders
|
||
.Where(o => o.UserId == userId && o.Status == 1)
|
||
.SumAsync(o => (decimal?)o.OrderTotal) ?? 0;
|
||
|
||
return totalConsumption >= limit;
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 配置解析失败,默认显示
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取运费配置
|
||
/// </summary>
|
||
private async Task<ShippingFeeDto> GetShippingFeeConfigAsync(int type)
|
||
{
|
||
var config = await _dbContext.Configs
|
||
.Where(c => c.ConfigKey == "base")
|
||
.Select(c => c.ConfigValue)
|
||
.FirstOrDefaultAsync();
|
||
|
||
var result = new ShippingFeeDto();
|
||
|
||
if (string.IsNullOrEmpty(config)) return result;
|
||
|
||
try
|
||
{
|
||
var settings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(config);
|
||
if (settings != null)
|
||
{
|
||
if (type == 3) // 卡册
|
||
{
|
||
result.FreePost = settings.TryGetValue("card_free_post", out var fp) && int.TryParse(fp?.ToString(), out var fpVal) ? fpVal : 0;
|
||
result.PostMoney = settings.TryGetValue("card_post_money", out var pm) && int.TryParse(pm?.ToString(), out var pmVal) ? pmVal : 0;
|
||
}
|
||
else
|
||
{
|
||
result.FreePost = settings.TryGetValue("free_post", out var fp) && int.TryParse(fp?.ToString(), out var fpVal) ? fpVal : 0;
|
||
result.PostMoney = settings.TryGetValue("post_money", out var pm) && int.TryParse(pm?.ToString(), out var pmVal) ? pmVal : 0;
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 配置解析失败,返回默认值
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化图片URL
|
||
/// </summary>
|
||
private static string FormatImageUrl(string? imgUrl)
|
||
{
|
||
if (string.IsNullOrEmpty(imgUrl))
|
||
return string.Empty;
|
||
|
||
if (imgUrl.StartsWith("http://") || imgUrl.StartsWith("https://"))
|
||
return imgUrl;
|
||
|
||
return imgUrl;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 奖品回收
|
||
|
||
/// <inheritdoc />
|
||
public async Task<RecoveryResultDto> RecoveryPrizesAsync(int userId, RecoveryRequest request)
|
||
{
|
||
// 1. 验证回收信息
|
||
if (string.IsNullOrEmpty(request.RecoveryInfo))
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 2. 检查达达卷消费限制
|
||
await CheckDadajuanLimitAsync(userId);
|
||
|
||
// 3. 检查每日兑换次数限制
|
||
await CheckDailyExchangeLimitAsync(userId);
|
||
|
||
// 4. 解析回收信息
|
||
List<RecoveryInfoItem> recoveryItems;
|
||
try
|
||
{
|
||
recoveryItems = System.Text.Json.JsonSerializer.Deserialize<List<RecoveryInfoItem>>(request.RecoveryInfo)
|
||
?? new List<RecoveryInfoItem>();
|
||
}
|
||
catch
|
||
{
|
||
throw new InvalidOperationException("回收信息格式错误");
|
||
}
|
||
|
||
if (!recoveryItems.Any())
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 5. 根据prize_code和number获取订单详情ID列表
|
||
var orderListIds = new List<int>();
|
||
foreach (var item in recoveryItems)
|
||
{
|
||
var ids = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0
|
||
&& o.PrizeCode == item.PrizeCode)
|
||
.OrderBy(o => o.Id)
|
||
.Take(item.Number)
|
||
.Select(o => o.Id)
|
||
.ToListAsync();
|
||
|
||
orderListIds.AddRange(ids);
|
||
}
|
||
|
||
if (!orderListIds.Any())
|
||
{
|
||
throw new InvalidOperationException("请刷新重新选择奖品");
|
||
}
|
||
|
||
// 6. 计算回收总金额
|
||
var totalMoney = await _dbContext.OrderItems
|
||
.Where(o => orderListIds.Contains(o.Id)
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0)
|
||
.SumAsync(o => o.GoodslistMoney);
|
||
|
||
// 7. 生成回收单号
|
||
var recoveryNum = GenerateRecoveryNum();
|
||
|
||
// 8. 使用事务处理
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
var currentTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
|
||
// 8.1 创建回收记录
|
||
var recoveryRecord = new OrderItemsRecovery
|
||
{
|
||
UserId = userId,
|
||
RecoveryNum = recoveryNum,
|
||
Money = totalMoney,
|
||
Count = orderListIds.Count,
|
||
Addtime = currentTime,
|
||
CreatedAt = DateTime.UtcNow,
|
||
UpdatedAt = DateTime.UtcNow
|
||
};
|
||
_dbContext.OrderItemsRecoveries.Add(recoveryRecord);
|
||
|
||
// 8.2 更新订单详情状态
|
||
await _dbContext.OrderItems
|
||
.Where(o => orderListIds.Contains(o.Id)
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.RecoveryNum, recoveryNum)
|
||
.SetProperty(o => o.Status, (byte)1)
|
||
.SetProperty(o => o.ChoiceTime, currentTime)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
// 8.3 增加用户哈尼券(money2),金额乘以100
|
||
var money2Amount = totalMoney * 100;
|
||
if (money2Amount > 0)
|
||
{
|
||
await ChangeMoney2Async(userId, money2Amount, 4, "兑换获得");
|
||
}
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
await transaction.CommitAsync();
|
||
|
||
// 9. 获取用户当前余额
|
||
var userMoney2 = await _dbContext.Users
|
||
.Where(u => u.Id == userId)
|
||
.Select(u => u.Money2 ?? 0)
|
||
.FirstOrDefaultAsync();
|
||
|
||
return new RecoveryResultDto
|
||
{
|
||
TotalMoney = (money2Amount).ToString("0.00"),
|
||
Count = orderListIds.Count,
|
||
RecoveryNum = recoveryNum,
|
||
UserMoney = userMoney2.ToString("0.00")
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "奖品回收失败: UserId={UserId}", userId);
|
||
throw new InvalidOperationException("打包失败");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查达达卷消费限制
|
||
/// </summary>
|
||
private async Task CheckDadajuanLimitAsync(int userId)
|
||
{
|
||
var config = await _dbContext.Configs
|
||
.Where(c => c.ConfigKey == "app_setting")
|
||
.Select(c => c.ConfigValue)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (string.IsNullOrEmpty(config)) return;
|
||
|
||
try
|
||
{
|
||
var settings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(config);
|
||
if (settings != null && settings.TryGetValue("show_dadajuan_limit", out var limitObj))
|
||
{
|
||
var limitStr = limitObj?.ToString();
|
||
if (!string.IsNullOrEmpty(limitStr) && limitStr != "0" && int.TryParse(limitStr, out var limit))
|
||
{
|
||
var totalConsumption = await _dbContext.Orders
|
||
.Where(o => o.UserId == userId && o.Status == 1)
|
||
.SumAsync(o => (decimal?)o.OrderTotal) ?? 0;
|
||
|
||
if (totalConsumption < limit)
|
||
{
|
||
throw new InvalidOperationException($"消费满{limit}才能兑换达达卷");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (InvalidOperationException)
|
||
{
|
||
throw;
|
||
}
|
||
catch
|
||
{
|
||
// 配置解析失败,忽略限制
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查每日兑换次数限制
|
||
/// </summary>
|
||
private async Task CheckDailyExchangeLimitAsync(int userId)
|
||
{
|
||
var config = await _dbContext.Configs
|
||
.Where(c => c.ConfigKey == "app_setting")
|
||
.Select(c => c.ConfigValue)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (string.IsNullOrEmpty(config)) return;
|
||
|
||
try
|
||
{
|
||
var settings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(config);
|
||
if (settings != null && settings.TryGetValue("cabinet_exchange_limit", out var limitObj))
|
||
{
|
||
var limitStr = limitObj?.ToString();
|
||
if (!string.IsNullOrEmpty(limitStr) && int.TryParse(limitStr, out var limit) && limit > 0)
|
||
{
|
||
var todayStart = new DateTimeOffset(DateTime.UtcNow.Date).ToUnixTimeSeconds();
|
||
var todayEnd = todayStart + 86400 - 1;
|
||
|
||
var todayCount = await _dbContext.OrderItemsRecoveries
|
||
.Where(r => r.UserId == userId
|
||
&& r.Addtime >= todayStart
|
||
&& r.Addtime <= todayEnd)
|
||
.CountAsync();
|
||
|
||
if (todayCount >= limit)
|
||
{
|
||
throw new InvalidOperationException("今日兑换次数已达上限");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (InvalidOperationException)
|
||
{
|
||
throw;
|
||
}
|
||
catch
|
||
{
|
||
// 配置解析失败,忽略限制
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成回收单号
|
||
/// </summary>
|
||
private static string GenerateRecoveryNum()
|
||
{
|
||
return $"HS_{DateTime.UtcNow:yyyyMMddHHmmss}{new Random().Next(1000, 9999)}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 变更用户哈尼券(money2)
|
||
/// </summary>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <param name="changeMoney">变动金额(正数增加,负数减少)</param>
|
||
/// <param name="type">变动类型:1后台充值 2在线充值 3抽赏消费 4背包兑换 5推荐奖励</param>
|
||
/// <param name="content">变动说明</param>
|
||
private async Task ChangeMoney2Async(int userId, decimal changeMoney, byte type, string content)
|
||
{
|
||
if (changeMoney == 0) return;
|
||
|
||
var user = await _dbContext.Users.FindAsync(userId);
|
||
if (user == null) return;
|
||
|
||
var currentMoney2 = user.Money2 ?? 0;
|
||
var newMoney2 = currentMoney2 + changeMoney;
|
||
|
||
// 更新用户余额
|
||
user.Money2 = newMoney2;
|
||
user.UpdatedAt = DateTime.UtcNow;
|
||
|
||
// 记录变动明细
|
||
var profitRecord = new ProfitMoney2
|
||
{
|
||
UserId = userId,
|
||
ChangeMoney = changeMoney,
|
||
Money = newMoney2,
|
||
Type = type,
|
||
Content = content,
|
||
ShareUid = 0,
|
||
Other = null,
|
||
CreatedAt = DateTime.UtcNow
|
||
};
|
||
_dbContext.ProfitMoney2s.Add(profitRecord);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 奖品发货
|
||
|
||
/// <inheritdoc />
|
||
public async Task<SendResultDto> SendPrizesAsync(int userId, SendRequest request)
|
||
{
|
||
// 1. 验证类型参数
|
||
if (request.Type != 1 && request.Type != 2)
|
||
{
|
||
throw new InvalidOperationException("请求参数错误");
|
||
}
|
||
|
||
// 2. 验证发货信息
|
||
if (string.IsNullOrEmpty(request.RecoveryInfo))
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 3. 验证收货信息
|
||
if (string.IsNullOrEmpty(request.Name) || string.IsNullOrEmpty(request.Mobile) || string.IsNullOrEmpty(request.Address))
|
||
{
|
||
throw new InvalidOperationException("缺少收货信息");
|
||
}
|
||
|
||
// 4. 解析发货信息
|
||
List<RecoveryInfoItem> sendItems;
|
||
try
|
||
{
|
||
sendItems = System.Text.Json.JsonSerializer.Deserialize<List<RecoveryInfoItem>>(request.RecoveryInfo)
|
||
?? new List<RecoveryInfoItem>();
|
||
}
|
||
catch
|
||
{
|
||
throw new InvalidOperationException("发货信息格式错误");
|
||
}
|
||
|
||
if (!sendItems.Any())
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 5. 根据prize_code和number获取订单详情ID列表
|
||
var orderListIds = new List<int>();
|
||
foreach (var item in sendItems)
|
||
{
|
||
var lim = item.Number;
|
||
var ids = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0
|
||
&& o.GoodslistType == 1
|
||
&& o.PrizeCode == item.PrizeCode.Trim())
|
||
.OrderBy(o => o.Id)
|
||
.Take(lim)
|
||
.Select(o => o.Id)
|
||
.ToListAsync();
|
||
|
||
orderListIds.AddRange(ids);
|
||
}
|
||
|
||
if (!orderListIds.Any())
|
||
{
|
||
throw new InvalidOperationException("请刷新重新选择奖品");
|
||
}
|
||
|
||
// 6. 获取发货数量
|
||
var count = orderListIds.Count;
|
||
|
||
// 7. 获取运费配置
|
||
var shippingConfig = await GetShippingFeeConfigAsync(request.Type);
|
||
var freePost = shippingConfig.FreePost <= 0 ? 0 : shippingConfig.FreePost;
|
||
var postMoney = shippingConfig.PostMoney <= 0 ? 0 : shippingConfig.PostMoney;
|
||
|
||
// 8. 生成发货单号
|
||
var sendNum = GenerateSendNum();
|
||
var currentTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
|
||
// 9. 计算实际运费
|
||
var actualFreight = postMoney;
|
||
if (freePost <= count || postMoney == 0)
|
||
{
|
||
actualFreight = 0;
|
||
}
|
||
|
||
// 10. 使用事务处理
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 10.1 更新订单详情的发货单号
|
||
await _dbContext.OrderItems
|
||
.Where(o => orderListIds.Contains(o.Id)
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0
|
||
&& o.GoodslistType == 1)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.SendNum, sendNum)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
// 10.2 创建发货记录
|
||
var sendRecord = new OrderItemsSend
|
||
{
|
||
UserId = userId,
|
||
SendNum = sendNum,
|
||
Freight = actualFreight,
|
||
Status = 0, // 0=待支付
|
||
Count = count,
|
||
Name = request.Name,
|
||
Mobile = request.Mobile,
|
||
Address = request.Address,
|
||
Message = request.Message,
|
||
Addtime = currentTime,
|
||
PayTime = 0,
|
||
SendTime = 0,
|
||
ShouTime = 0,
|
||
CancelTime = 0,
|
||
DeliveryStatus = -1, // -1 表示未查询物流
|
||
DeliveryTime = 0,
|
||
AdminId = 0,
|
||
CreatedAt = DateTime.UtcNow,
|
||
UpdatedAt = DateTime.UtcNow
|
||
};
|
||
_dbContext.OrderItemsSends.Add(sendRecord);
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
var sendRecordId = sendRecord.Id;
|
||
|
||
// 10.3 判断是否需要支付运费
|
||
SendResultDto result;
|
||
if (freePost > count && postMoney > 0)
|
||
{
|
||
// 需要支付运费,调用微信支付服务生成支付参数
|
||
// 获取用户信息
|
||
var user = await _dbContext.Users.FindAsync(userId);
|
||
if (user == null)
|
||
{
|
||
throw new InvalidOperationException("用户不存在");
|
||
}
|
||
|
||
var payRequest = new CreatePayRequest
|
||
{
|
||
UserId = userId,
|
||
OpenId = user.OpenId,
|
||
Price = actualFreight,
|
||
Title = $"背包发货{count}件",
|
||
Attach = "order_list_send",
|
||
Prefix = "FH_"
|
||
};
|
||
|
||
var payResult = await _wechatService.CreatePayOrderAsync(payRequest);
|
||
|
||
if (payResult.Status != 1)
|
||
{
|
||
throw new InvalidOperationException(payResult.Message ?? "创建支付订单失败");
|
||
}
|
||
|
||
// 更新发货记录的订单号为支付订单号
|
||
sendRecord.SendNum = payResult.OrderNo;
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
// 同时更新 OrderItems 的 SendNum
|
||
await _dbContext.OrderItems
|
||
.Where(o => o.SendNum == sendNum && o.UserId == userId)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.SendNum, payResult.OrderNo)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
result = new SendResultDto
|
||
{
|
||
Status = 1, // 需要支付
|
||
OrderNo = payResult.OrderNo,
|
||
Res = payResult.Res != null ? new NativePayResultDto
|
||
{
|
||
AppId = payResult.Res.AppId,
|
||
TimeStamp = payResult.Res.TimeStamp,
|
||
NonceStr = payResult.Res.NonceStr,
|
||
Package = payResult.Res.Package,
|
||
SignType = payResult.Res.SignType,
|
||
PaySign = payResult.Res.PaySign
|
||
} : null
|
||
};
|
||
}
|
||
else
|
||
{
|
||
// 免运费,直接处理发货
|
||
sendRecord.Freight = 0;
|
||
sendRecord.Status = 1; // 1=待发货
|
||
sendRecord.PayTime = currentTime;
|
||
|
||
// 更新订单详情状态为已发货
|
||
await _dbContext.OrderItems
|
||
.Where(o => o.SendNum == sendNum && o.UserId == userId)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.Status, (byte)2)
|
||
.SetProperty(o => o.ChoiceTime, currentTime)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
result = new SendResultDto
|
||
{
|
||
Status = 0, // 免运费
|
||
OrderNo = sendNum
|
||
};
|
||
}
|
||
|
||
await transaction.CommitAsync();
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "奖品发货失败: UserId={UserId}", userId);
|
||
throw new InvalidOperationException("发货失败");
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ConfirmSendAsync(int userId, int id)
|
||
{
|
||
// 1. 验证参数
|
||
if (id <= 0)
|
||
{
|
||
throw new InvalidOperationException("非法请求");
|
||
}
|
||
|
||
// 2. 查找发货记录
|
||
var sendRecord = await _dbContext.OrderItemsSends
|
||
.Where(s => s.Id == id && s.UserId == userId)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (sendRecord == null)
|
||
{
|
||
throw new InvalidOperationException("请求参数错误");
|
||
}
|
||
|
||
// 3. 检查状态
|
||
if (sendRecord.Status == 3)
|
||
{
|
||
throw new InvalidOperationException("请勿重复操作");
|
||
}
|
||
|
||
if (sendRecord.Status != 2)
|
||
{
|
||
throw new InvalidOperationException("该订单暂不能确认收货");
|
||
}
|
||
|
||
// 4. 更新状态为已签收
|
||
var currentTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
sendRecord.Status = 3; // 3=已签收
|
||
sendRecord.ShouTime = currentTime;
|
||
sendRecord.UpdatedAt = DateTime.UtcNow;
|
||
|
||
var result = await _dbContext.SaveChangesAsync();
|
||
return result > 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成发货单号
|
||
/// </summary>
|
||
private static string GenerateSendNum()
|
||
{
|
||
return $"FH_{DateTime.UtcNow:yyyyMMddHHmmss}{new Random().Next(1000, 9999)}";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 记录查询
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PageResponse<SendRecordDto>> GetSendRecordsAsync(int userId, int page, int status = 1)
|
||
{
|
||
// 验证状态参数
|
||
if (status != 1 && status != 2 && status != 3)
|
||
{
|
||
throw new InvalidOperationException("非法请求");
|
||
}
|
||
|
||
var pageSize = 10;
|
||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
|
||
// 自动收货:7天后自动签收
|
||
var expireTime = currentTime - (7 * 86400);
|
||
await _dbContext.OrderItemsSends
|
||
.Where(s => s.Status == 2 && s.SendTime <= expireTime && s.SendTime > 0)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.Status, (byte)3)
|
||
.SetProperty(o => o.ShouTime, (int)currentTime)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
// 查询发货记录
|
||
var query = _dbContext.OrderItemsSends
|
||
.Where(s => s.UserId == userId && s.Status == status)
|
||
.OrderByDescending(s => s.Id);
|
||
|
||
var totalCount = await query.CountAsync();
|
||
var lastPage = (int)Math.Ceiling((double)totalCount / pageSize);
|
||
|
||
var records = await query
|
||
.Skip((page - 1) * pageSize)
|
||
.Take(pageSize)
|
||
.ToListAsync();
|
||
|
||
var result = new List<SendRecordDto>();
|
||
foreach (var record in records)
|
||
{
|
||
// 获取奖品列表
|
||
var orderList = await _dbContext.OrderItems
|
||
.Where(o => o.SendNum == record.SendNum)
|
||
.Select(o => new SendRecordItemDto
|
||
{
|
||
Id = o.Id,
|
||
GoodsListTitle = o.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(o.GoodslistImgurl),
|
||
GoodsListMoney = o.GoodslistMoney.ToString("0.00"),
|
||
ShangId = o.ShangId,
|
||
PrizeNum = 1,
|
||
FhStatus = o.FhStatus
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取赏品等级标题
|
||
foreach (var item in orderList)
|
||
{
|
||
item.ShangTitle = await GetShangTitleAsync(item.ShangId);
|
||
}
|
||
|
||
result.Add(new SendRecordDto
|
||
{
|
||
Id = record.Id,
|
||
SendNum = record.SendNum ?? string.Empty,
|
||
Name = record.Name ?? string.Empty,
|
||
Mobile = MaskMobile(record.Mobile),
|
||
Address = record.Address ?? string.Empty,
|
||
Status = record.Status,
|
||
StatusName = GetSendStatusName(record.Status),
|
||
Count = record.Count,
|
||
Freight = record.Freight.ToString("0.00"),
|
||
AddTime = FormatTimestamp(record.Addtime),
|
||
CourierNumber = record.CourierNumber,
|
||
CourierName = record.CourierName,
|
||
UserId = record.UserId,
|
||
OrderList = orderList
|
||
});
|
||
}
|
||
|
||
return new PageResponse<SendRecordDto>
|
||
{
|
||
Data = result,
|
||
LastPage = lastPage > 0 ? lastPage : 1
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<SendRecordDetailDto> GetSendRecordDetailAsync(int userId, int id)
|
||
{
|
||
// 验证参数
|
||
if (id <= 0)
|
||
{
|
||
throw new InvalidOperationException("非法请求");
|
||
}
|
||
|
||
// 查询发货记录
|
||
var record = await _dbContext.OrderItemsSends
|
||
.Where(s => s.Id == id && s.UserId == userId)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (record == null)
|
||
{
|
||
throw new InvalidOperationException("请求参数错误");
|
||
}
|
||
|
||
// 验证状态
|
||
if (record.Status != 1 && record.Status != 2 && record.Status != 3)
|
||
{
|
||
throw new InvalidOperationException("请求参数错误");
|
||
}
|
||
|
||
// 获取奖品列表
|
||
var goods = await _dbContext.OrderItems
|
||
.Where(o => o.SendNum == record.SendNum)
|
||
.OrderByDescending(o => o.GoodslistMoney)
|
||
.Select(o => new SendRecordItemDto
|
||
{
|
||
Id = o.Id,
|
||
GoodsListTitle = o.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(o.GoodslistImgurl),
|
||
GoodsListMoney = o.GoodslistMoney.ToString("0.00"),
|
||
ShangId = o.ShangId,
|
||
PrizeNum = 1,
|
||
FhStatus = o.FhStatus
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 获取赏品等级标题
|
||
foreach (var item in goods)
|
||
{
|
||
item.ShangTitle = await GetShangTitleAsync(item.ShangId);
|
||
}
|
||
|
||
return new SendRecordDetailDto
|
||
{
|
||
Id = record.Id,
|
||
SendNum = record.SendNum ?? string.Empty,
|
||
Name = record.Name ?? string.Empty,
|
||
Mobile = record.Mobile ?? string.Empty,
|
||
Address = record.Address ?? string.Empty,
|
||
Message = record.Message,
|
||
Status = record.Status,
|
||
Count = record.Count,
|
||
Freight = record.Freight.ToString("0.00"),
|
||
AddTime = FormatTimestamp(record.Addtime),
|
||
PayTime = FormatTimestamp(record.PayTime),
|
||
SendTime = record.SendTime > 0 ? FormatTimestamp(record.SendTime) : "待发货",
|
||
ShouTime = record.ShouTime > 0 ? FormatTimestamp(record.ShouTime) : "待收货",
|
||
CourierNumber = record.CourierNumber,
|
||
CourierName = record.CourierName,
|
||
Goods = goods
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PageResponse<RecoveryRecordDto>> GetRecoveryRecordsAsync(int userId, int page)
|
||
{
|
||
var pageSize = 10;
|
||
|
||
var query = _dbContext.OrderItemsRecoveries
|
||
.Where(r => r.UserId == userId)
|
||
.OrderByDescending(r => r.Id);
|
||
|
||
var totalCount = await query.CountAsync();
|
||
var lastPage = (int)Math.Ceiling((double)totalCount / pageSize);
|
||
|
||
var records = await query
|
||
.Skip((page - 1) * pageSize)
|
||
.Take(pageSize)
|
||
.ToListAsync();
|
||
|
||
var result = new List<RecoveryRecordDto>();
|
||
foreach (var record in records)
|
||
{
|
||
// 获取奖品列表
|
||
var orderList = await _dbContext.OrderItems
|
||
.Where(o => o.RecoveryNum == record.RecoveryNum)
|
||
.Select(o => new RecoveryRecordItemDto
|
||
{
|
||
Id = o.Id,
|
||
GoodsListTitle = o.GoodslistTitle ?? string.Empty,
|
||
GoodsListImgUrl = FormatImageUrl(o.GoodslistImgurl),
|
||
GoodsListMoney = o.GoodslistMoney.ToString("0.00")
|
||
})
|
||
.ToListAsync();
|
||
|
||
result.Add(new RecoveryRecordDto
|
||
{
|
||
Id = record.Id,
|
||
RecoveryNum = record.RecoveryNum ?? string.Empty,
|
||
Money = record.Money.ToString("0.00"),
|
||
Count = record.Count,
|
||
AddTime = FormatTimestamp(record.Addtime),
|
||
OrderList = orderList
|
||
});
|
||
}
|
||
|
||
return new PageResponse<RecoveryRecordDto>
|
||
{
|
||
Data = result,
|
||
LastPage = lastPage > 0 ? lastPage : 1
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取发货状态名称
|
||
/// </summary>
|
||
private static string GetSendStatusName(int status)
|
||
{
|
||
return status switch
|
||
{
|
||
0 => "待支付",
|
||
1 => "待发货",
|
||
2 => "已发货",
|
||
3 => "已签收",
|
||
4 => "已取消",
|
||
_ => "未知"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手机号脱敏
|
||
/// </summary>
|
||
private static string MaskMobile(string? mobile)
|
||
{
|
||
if (string.IsNullOrEmpty(mobile) || mobile.Length < 7)
|
||
return mobile ?? string.Empty;
|
||
|
||
return mobile.Substring(0, 3) + "****" + mobile.Substring(mobile.Length - 4);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化时间戳为日期时间字符串
|
||
/// </summary>
|
||
private static string FormatTimestamp(int timestamp)
|
||
{
|
||
if (timestamp <= 0) return string.Empty;
|
||
return DateTimeOffset.FromUnixTimeSeconds(timestamp).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 物流查询
|
||
|
||
/// <summary>
|
||
/// 物流状态名称映射
|
||
/// </summary>
|
||
private static readonly string[] DeliveryStatusNames =
|
||
{
|
||
"快递收件(揽件)", // 0
|
||
"在途中", // 1
|
||
"正在派件", // 2
|
||
"已签收", // 3
|
||
"派送失败", // 4
|
||
"疑难件", // 5
|
||
"退件签收", // 6
|
||
"其他" // 7
|
||
};
|
||
|
||
/// <inheritdoc />
|
||
public async Task<LogisticsDto> GetLogisticsAsync(int userId, int id)
|
||
{
|
||
// 1. 验证参数
|
||
if (id <= 0)
|
||
{
|
||
throw new InvalidOperationException("非法请求");
|
||
}
|
||
|
||
// 2. 查询发货记录
|
||
var sendRecord = await _dbContext.OrderItemsSends
|
||
.Where(s => s.Id == id && s.UserId == userId)
|
||
.Select(s => new
|
||
{
|
||
s.Id,
|
||
s.SendNum,
|
||
s.CourierNumber,
|
||
s.CourierName,
|
||
s.CourierCode,
|
||
s.DeliveryList,
|
||
s.DeliveryStatus,
|
||
s.DeliveryTime,
|
||
s.Count,
|
||
s.Mobile
|
||
})
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (sendRecord == null)
|
||
{
|
||
throw new InvalidOperationException("请求参数错误");
|
||
}
|
||
|
||
// 3. 获取奖品图片
|
||
var goodsListImgUrl = await _dbContext.OrderItems
|
||
.Where(o => o.SendNum == sendRecord.SendNum)
|
||
.Select(o => o.GoodslistImgurl)
|
||
.FirstOrDefaultAsync();
|
||
|
||
// 4. 初始化响应
|
||
var result = new LogisticsDto
|
||
{
|
||
CourierNumber = sendRecord.CourierNumber,
|
||
CourierName = sendRecord.CourierName,
|
||
CourierCode = sendRecord.CourierCode,
|
||
Count = sendRecord.Count,
|
||
SendNum = sendRecord.SendNum,
|
||
GoodsListImgUrl = FormatImageUrl(goodsListImgUrl),
|
||
DeliveryStatus = GetDeliveryStatusName(sendRecord.DeliveryStatus),
|
||
DeliveryList = ParseDeliveryList(sendRecord.DeliveryList)
|
||
};
|
||
|
||
// 5. 检查是否需要刷新物流信息(每10分钟刷新一次)
|
||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
var shouldRefresh = !string.IsNullOrEmpty(sendRecord.CourierNumber)
|
||
&& (currentTime > sendRecord.DeliveryTime + 600);
|
||
|
||
if (shouldRefresh)
|
||
{
|
||
// 6. 处理顺丰快递特殊逻辑(需要手机号后4位)
|
||
var trackingNo = sendRecord.CourierNumber!;
|
||
if (sendRecord.CourierCode?.ToUpper() == "SFEXPRESS" && !string.IsNullOrEmpty(sendRecord.Mobile))
|
||
{
|
||
var mobileSuffix = sendRecord.Mobile.Length >= 4
|
||
? sendRecord.Mobile.Substring(sendRecord.Mobile.Length - 4)
|
||
: sendRecord.Mobile;
|
||
trackingNo = $"{trackingNo}:{mobileSuffix}";
|
||
}
|
||
|
||
// 7. 调用物流查询API
|
||
var queryResult = await _logisticsService.QueryLogisticsAsync(
|
||
trackingNo,
|
||
sendRecord.CourierCode ?? string.Empty);
|
||
|
||
if (queryResult.Success)
|
||
{
|
||
// 8. 更新物流信息到数据库
|
||
var deliveryListJson = queryResult.TraceList != null
|
||
? System.Text.Json.JsonSerializer.Serialize(queryResult.TraceList)
|
||
: null;
|
||
|
||
await _dbContext.OrderItemsSends
|
||
.Where(s => s.Id == id)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.DeliveryList, deliveryListJson)
|
||
.SetProperty(o => o.DeliveryStatus, (byte)queryResult.DeliveryStatus)
|
||
.SetProperty(o => o.DeliveryTime, (int)currentTime)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
// 9. 更新响应
|
||
result.DeliveryStatus = GetDeliveryStatusName(queryResult.DeliveryStatus);
|
||
result.DeliveryList = queryResult.TraceList;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取物流状态名称
|
||
/// </summary>
|
||
private static string GetDeliveryStatusName(int status)
|
||
{
|
||
if (status >= 0 && status < DeliveryStatusNames.Length)
|
||
{
|
||
return DeliveryStatusNames[status];
|
||
}
|
||
return "暂无物流轨迹";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析物流轨迹JSON
|
||
/// </summary>
|
||
private static List<LogisticsTraceDto>? ParseDeliveryList(string? deliveryListJson)
|
||
{
|
||
if (string.IsNullOrEmpty(deliveryListJson))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
return System.Text.Json.JsonSerializer.Deserialize<List<LogisticsTraceDto>>(deliveryListJson);
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 保险柜操作
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> MoveIntoSafeAsync(int userId, SafeOperationRequest request)
|
||
{
|
||
// 1. 验证操作信息
|
||
if (string.IsNullOrEmpty(request.RecoveryInfo))
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 2. 解析操作信息
|
||
List<RecoveryInfoItem> operationItems;
|
||
try
|
||
{
|
||
operationItems = System.Text.Json.JsonSerializer.Deserialize<List<RecoveryInfoItem>>(request.RecoveryInfo)
|
||
?? new List<RecoveryInfoItem>();
|
||
}
|
||
catch
|
||
{
|
||
throw new InvalidOperationException("操作信息格式错误");
|
||
}
|
||
|
||
if (!operationItems.Any())
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 3. 根据prize_code和number获取订单详情ID列表
|
||
var orderListIds = new List<int>();
|
||
foreach (var item in operationItems)
|
||
{
|
||
var ids = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0
|
||
&& o.PrizeCode == item.PrizeCode)
|
||
.OrderBy(o => o.Id)
|
||
.Take(item.Number)
|
||
.Select(o => o.Id)
|
||
.ToListAsync();
|
||
|
||
orderListIds.AddRange(ids);
|
||
}
|
||
|
||
if (!orderListIds.Any())
|
||
{
|
||
throw new InvalidOperationException("请刷新重新选择奖品");
|
||
}
|
||
|
||
// 4. 更新订单详情的保险柜状态
|
||
var result = await _dbContext.OrderItems
|
||
.Where(o => orderListIds.Contains(o.Id)
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 0)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.InsuranceIs, (byte)1)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
return result > 0;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> RemoveFromSafeAsync(int userId, SafeOperationRequest request)
|
||
{
|
||
// 1. 验证操作信息
|
||
if (string.IsNullOrEmpty(request.RecoveryInfo))
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 2. 解析操作信息
|
||
List<RecoveryInfoItem> operationItems;
|
||
try
|
||
{
|
||
operationItems = System.Text.Json.JsonSerializer.Deserialize<List<RecoveryInfoItem>>(request.RecoveryInfo)
|
||
?? new List<RecoveryInfoItem>();
|
||
}
|
||
catch
|
||
{
|
||
throw new InvalidOperationException("操作信息格式错误");
|
||
}
|
||
|
||
if (!operationItems.Any())
|
||
{
|
||
throw new InvalidOperationException("请选择兑换的赏品");
|
||
}
|
||
|
||
// 3. 根据prize_code和number获取订单详情ID列表(从保险柜中)
|
||
var orderListIds = new List<int>();
|
||
foreach (var item in operationItems)
|
||
{
|
||
var ids = await _dbContext.OrderItems
|
||
.Where(o => o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 1
|
||
&& o.PrizeCode == item.PrizeCode)
|
||
.OrderBy(o => o.Id)
|
||
.Take(item.Number)
|
||
.Select(o => o.Id)
|
||
.ToListAsync();
|
||
|
||
orderListIds.AddRange(ids);
|
||
}
|
||
|
||
if (!orderListIds.Any())
|
||
{
|
||
throw new InvalidOperationException("请刷新重新选择奖品");
|
||
}
|
||
|
||
// 4. 更新订单详情的保险柜状态
|
||
var result = await _dbContext.OrderItems
|
||
.Where(o => orderListIds.Contains(o.Id)
|
||
&& o.UserId == userId
|
||
&& o.Status == 0
|
||
&& o.InsuranceIs == 1)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(o => o.InsuranceIs, (byte)0)
|
||
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));
|
||
|
||
return result > 0;
|
||
}
|
||
|
||
#endregion
|
||
}
|