291 lines
9.0 KiB
C#
291 lines
9.0 KiB
C#
using HoneyBox.Core.Interfaces;
|
||
using HoneyBox.Model.Data;
|
||
using HoneyBox.Model.Entities;
|
||
using HoneyBox.Model.Models;
|
||
using HoneyBox.Model.Models.Goods;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace HoneyBox.Core.Services;
|
||
|
||
/// <summary>
|
||
/// 收藏服务实现
|
||
/// </summary>
|
||
public class CollectionService : ICollectionService
|
||
{
|
||
private readonly HoneyBoxDbContext _dbContext;
|
||
private readonly ILogger<CollectionService> _logger;
|
||
private readonly IRedisService _redisService;
|
||
|
||
// 奖品统计ID范围 (与PHP保持一致)
|
||
private static readonly int[] ShangPrizeIdRange = { 10, 33 };
|
||
|
||
public CollectionService(
|
||
HoneyBoxDbContext dbContext,
|
||
ILogger<CollectionService> logger,
|
||
IRedisService redisService)
|
||
{
|
||
_dbContext = dbContext;
|
||
_logger = logger;
|
||
_redisService = redisService;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ToggleCollectionAsync(int userId, int goodsId, int goodsNum)
|
||
{
|
||
// 1. 验证商品是否存在
|
||
var goods = await _dbContext.Goods
|
||
.Where(g => g.Id == goodsId)
|
||
.Select(g => new { g.Id, g.Stock, g.Status, g.Type })
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (goods == null)
|
||
{
|
||
throw new InvalidOperationException("盒子不存在");
|
||
}
|
||
|
||
if (goods.Status != 1 && goods.Status != 3)
|
||
{
|
||
throw new InvalidOperationException("盒子已下架");
|
||
}
|
||
|
||
// 2. 检查是否已收藏
|
||
var existingCollection = await _dbContext.GoodsCollections
|
||
.Where(c => c.UserId == userId && c.GoodsId == goodsId && c.Num == goodsNum)
|
||
.FirstOrDefaultAsync();
|
||
|
||
bool result;
|
||
if (existingCollection != null)
|
||
{
|
||
// 已收藏,则取消收藏
|
||
_dbContext.GoodsCollections.Remove(existingCollection);
|
||
result = await _dbContext.SaveChangesAsync() > 0;
|
||
}
|
||
else
|
||
{
|
||
// 未收藏,则添加收藏
|
||
var collection = new GoodsCollection
|
||
{
|
||
UserId = userId,
|
||
GoodsId = goodsId,
|
||
Num = goodsNum,
|
||
Type = goods.Type,
|
||
CreatedAt = DateTime.Now
|
||
};
|
||
_dbContext.GoodsCollections.Add(collection);
|
||
result = await _dbContext.SaveChangesAsync() > 0;
|
||
}
|
||
|
||
// 3. 清除相关缓存
|
||
if (result)
|
||
{
|
||
try
|
||
{
|
||
await _redisService.DeleteAsync($"goods_detail_{goodsId}_{userId}");
|
||
await _redisService.DeleteAsync($"infinite_goodsdetail_{goodsId}_{userId}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "清除收藏相关缓存失败");
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<CollectionListResponse> GetCollectionListAsync(int userId, int type, int page, int pageSize)
|
||
{
|
||
// 1. 构建查询
|
||
var query = _dbContext.GoodsCollections
|
||
.Where(c => c.UserId == userId);
|
||
|
||
// 按类型过滤
|
||
if (type > 0)
|
||
{
|
||
query = query.Where(c => c.Type == type);
|
||
}
|
||
|
||
// 2. 获取总数和分页数据
|
||
var totalCount = await query.CountAsync();
|
||
var lastPage = pageSize > 0 ? (int)Math.Ceiling((double)totalCount / pageSize) : 1;
|
||
|
||
var collections = await query
|
||
.OrderByDescending(c => c.Id)
|
||
.Skip((page - 1) * pageSize)
|
||
.Take(pageSize)
|
||
.ToListAsync();
|
||
|
||
if (!collections.Any())
|
||
{
|
||
return new CollectionListResponse
|
||
{
|
||
Data = new List<CollectionDto>(),
|
||
LastPage = lastPage
|
||
};
|
||
}
|
||
|
||
// 3. 获取商品信息
|
||
var goodsIds = collections.Select(c => c.GoodsId).Distinct().ToList();
|
||
var goodsDict = await _dbContext.Goods
|
||
.Where(g => goodsIds.Contains(g.Id))
|
||
.Select(g => new { g.Id, g.Title, g.Price, g.ImgUrl })
|
||
.ToDictionaryAsync(g => g.Id);
|
||
|
||
// 4. 获取库存信息 (仅对特定类型的商品)
|
||
// 类型 1, 3, 5, 6, 10, 11 需要查询库存
|
||
var stockTypes = new byte[] { 1, 3, 5, 6, 10, 11 };
|
||
var stockCollections = collections
|
||
.Where(c => stockTypes.Contains(c.Type))
|
||
.Select(c => new { c.GoodsId, c.Num })
|
||
.Distinct()
|
||
.ToList();
|
||
|
||
var stockDict = new Dictionary<string, (int Stock, int SurplusStock)>();
|
||
if (stockCollections.Any())
|
||
{
|
||
foreach (var sc in stockCollections)
|
||
{
|
||
var stockInfo = await _dbContext.GoodsItems
|
||
.Where(gi => gi.GoodsId == sc.GoodsId
|
||
&& gi.Num == sc.Num
|
||
&& gi.ShangId >= ShangPrizeIdRange[0]
|
||
&& gi.ShangId <= ShangPrizeIdRange[1])
|
||
.GroupBy(gi => 1)
|
||
.Select(g => new
|
||
{
|
||
Stock = g.Sum(x => x.Stock),
|
||
SurplusStock = g.Sum(x => x.SurplusStock)
|
||
})
|
||
.FirstOrDefaultAsync();
|
||
|
||
var key = $"{sc.GoodsId}_{sc.Num}";
|
||
stockDict[key] = stockInfo != null
|
||
? (stockInfo.Stock, stockInfo.SurplusStock)
|
||
: (0, 0);
|
||
}
|
||
}
|
||
|
||
// 5. 构建响应
|
||
var result = new List<CollectionDto>();
|
||
foreach (var collection in collections)
|
||
{
|
||
var dto = new CollectionDto
|
||
{
|
||
Id = collection.Id,
|
||
GoodsId = collection.GoodsId,
|
||
Type = collection.Type,
|
||
Num = collection.Num
|
||
};
|
||
|
||
// 填充商品信息
|
||
if (goodsDict.TryGetValue(collection.GoodsId, out var goodsInfo))
|
||
{
|
||
dto.GoodsTitle = goodsInfo.Title;
|
||
dto.GoodsPrice = goodsInfo.Price.ToString("0.##");
|
||
dto.ImgUrl = FormatImageUrl(goodsInfo.ImgUrl);
|
||
}
|
||
|
||
// 填充库存信息
|
||
if (stockTypes.Contains(collection.Type))
|
||
{
|
||
var stockKey = $"{collection.GoodsId}_{collection.Num}";
|
||
if (stockDict.TryGetValue(stockKey, out var stock))
|
||
{
|
||
dto.Stock = stock.Stock;
|
||
dto.SurplusStock = stock.SurplusStock;
|
||
}
|
||
}
|
||
|
||
result.Add(dto);
|
||
}
|
||
|
||
return new CollectionListResponse
|
||
{
|
||
Data = result,
|
||
LastPage = lastPage
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> DeleteCollectionAsync(int userId, int collectionId)
|
||
{
|
||
var collection = await _dbContext.GoodsCollections
|
||
.Where(c => c.UserId == userId && c.Id == collectionId)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (collection == null)
|
||
{
|
||
throw new InvalidOperationException("请求重复操作");
|
||
}
|
||
|
||
_dbContext.GoodsCollections.Remove(collection);
|
||
return await _dbContext.SaveChangesAsync() > 0;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> IsCollectedAsync(int userId, int goodsId, int goodsNum)
|
||
{
|
||
if (userId <= 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return await _dbContext.GoodsCollections
|
||
.AnyAsync(c => c.UserId == userId && c.GoodsId == goodsId && c.Num == goodsNum);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> CancelCollectionByGoodsIdAsync(int userId, int goodsId)
|
||
{
|
||
// 查找该用户对该商品的所有收藏记录
|
||
var collections = await _dbContext.GoodsCollections
|
||
.Where(c => c.UserId == userId && c.GoodsId == goodsId)
|
||
.ToListAsync();
|
||
|
||
if (!collections.Any())
|
||
{
|
||
throw new InvalidOperationException("未找到收藏记录");
|
||
}
|
||
|
||
_dbContext.GoodsCollections.RemoveRange(collections);
|
||
var result = await _dbContext.SaveChangesAsync() > 0;
|
||
|
||
// 清除相关缓存
|
||
if (result)
|
||
{
|
||
try
|
||
{
|
||
await _redisService.DeleteAsync($"goods_detail_{goodsId}_{userId}");
|
||
await _redisService.DeleteAsync($"infinite_goodsdetail_{goodsId}_{userId}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "清除收藏相关缓存失败");
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化图片URL
|
||
/// </summary>
|
||
private static string FormatImageUrl(string? imgUrl)
|
||
{
|
||
if (string.IsNullOrEmpty(imgUrl))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
// 如果已经是完整URL,直接返回
|
||
if (imgUrl.StartsWith("http://") || imgUrl.StartsWith("https://"))
|
||
{
|
||
return imgUrl;
|
||
}
|
||
|
||
// 否则直接返回 (可以从配置读取基础URL)
|
||
return imgUrl;
|
||
}
|
||
}
|