119 lines
5.7 KiB
C#
119 lines
5.7 KiB
C#
using FreeSql;
|
|
using MAX.Core.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Store.Modules.CloudProduct.Entities;
|
|
using Store.Modules.CloudProduct.Dtos;
|
|
|
|
namespace Store.Modules.CloudProduct.Repositorys;
|
|
|
|
public class GoodsViolationRepository(
|
|
[FromKeyedServices("CloudProductConnection")] IFreeSql fsql)
|
|
: BaseRepository<GoodsViolationEntity>(fsql), IScopedDependency
|
|
{
|
|
public Task<List<ViolationReasonDto>> GetReasonsAsync() => Orm.Select<ViolationReasonEntity>()
|
|
.OrderBy(x => x.Id).ToListAsync(x => new ViolationReasonDto
|
|
{
|
|
Name = x.Name,
|
|
IsViolation = x.IsViolation
|
|
});
|
|
|
|
public Task<List<long>> GetViolationIdsAsync(long[] ids) => Select
|
|
.Where(x => ids.Contains(x.GoodsId)).Distinct().ToListAsync(x => x.GoodsId);
|
|
|
|
public async Task<List<long>> GetOffShelfGoodsIdsAsync(long[] ids)
|
|
{
|
|
var records = await Select.Where(x => ids.Contains(x.GoodsId)).ToListAsync();
|
|
return records.GroupBy(x => x.GoodsId).Where(x => x.All(row => row.IsIndexDeleted))
|
|
.Select(x => x.Key).ToList();
|
|
}
|
|
|
|
// 解除违规和设置待同步必须一起提交,进程中断后后台索引任务仍可恢复。
|
|
public async Task<int> RemoveAndScheduleRestoreAsync(long[] ids)
|
|
{
|
|
using var unit = Orm.CreateUnitOfWork();
|
|
var violations = unit.GetRepository<GoodsViolationEntity>();
|
|
var goods = unit.GetRepository<GoodsEntity>();
|
|
var removedIds = await violations.Select.Where(x => ids.Contains(x.GoodsId)).Distinct().ToListAsync(x => x.GoodsId);
|
|
await violations.DeleteAsync(x => ids.Contains(x.GoodsId));
|
|
await goods.UpdateDiy.Set(x => x.IsSegmented, (byte)0)
|
|
.Where(x => ids.Contains(x.GoodsId) && x.Status == 0).ExecuteAffrowsAsync();
|
|
unit.Commit();
|
|
return removedIds.Count;
|
|
}
|
|
|
|
public Task<List<GoodsEntity>> GetRestorableGoodsAsync(long[] ids) => Orm.Select<GoodsEntity>()
|
|
.Where(x => ids.Contains(x.GoodsId) && x.Status == 0).ToListAsync();
|
|
|
|
public Task<int> MarkRestoredAsync(long[] ids) => Orm.Update<GoodsEntity>()
|
|
.Set(x => x.IsSegmented, (byte)1)
|
|
.Where(x => ids.Contains(x.GoodsId) && x.Status == 0).ExecuteAffrowsAsync();
|
|
|
|
// 调用方持有 GoodsIndexLock,事务内合并原因,避免并发覆盖。
|
|
public async Task<long[]> MergeAsync(Dictionary<long, string[]> items)
|
|
{
|
|
using var unit = Orm.CreateUnitOfWork();
|
|
var repository = unit.GetRepository<GoodsViolationEntity>();
|
|
var reasonRepository = unit.GetRepository<ViolationReasonEntity>();
|
|
var reasonIds = new Dictionary<string, uint>(StringComparer.Ordinal);
|
|
foreach (var name in items.Values.SelectMany(x => x).Distinct(StringComparer.Ordinal))
|
|
{
|
|
// 让数据库按字典表的排序规则匹配名称,与唯一索引保持一致。
|
|
var reason = await reasonRepository.Select.Where(x => x.Name == name).FirstAsync();
|
|
// 新类型只收录到字典,绝不因首次上报自动启用商品封禁。
|
|
reason ??= await reasonRepository.InsertAsync(new ViolationReasonEntity { Name = name, IsViolation = false });
|
|
// 数据库排序规则可能忽略部分字符差异;判定时仍要求名称完整相等。
|
|
if (reason.IsViolation && string.Equals(reason.Name, name, StringComparison.Ordinal))
|
|
reasonIds.Add(name, reason.Id);
|
|
}
|
|
var ids = items.Keys.ToArray();
|
|
var existing = (await repository.Select.Where(x => ids.Contains(x.GoodsId))
|
|
.ToListAsync()).ToLookup(x => x.GoodsId);
|
|
var now = DateTime.UtcNow;
|
|
var recordedIds = new List<long>();
|
|
foreach (var (goodsId, reasons) in items)
|
|
{
|
|
var accepted = reasons.Where(reasonIds.ContainsKey).Select(x => reasonIds[x]).Distinct().ToArray();
|
|
// 忽略原因仅记录字典,不新增或改变该商品已有的违规状态。
|
|
if (accepted.Length == 0) continue;
|
|
var rows = existing[goodsId].ToArray();
|
|
var previous = rows.Select(x => x.ReasonId).ToHashSet();
|
|
var added = accepted.Where(x => !previous.Contains(x)).ToArray();
|
|
if (added.Length == 0 && rows.All(x => x.IsIndexDeleted)) continue;
|
|
|
|
// 下架是商品级操作:新增原因或存在失败记录时,整件商品重新标记待下架。
|
|
foreach (var row in rows)
|
|
{
|
|
row.IsIndexDeleted = false;
|
|
row.UpdatedTime = now;
|
|
await repository.UpdateAsync(row);
|
|
}
|
|
foreach (var reasonId in added)
|
|
{
|
|
await repository.InsertAsync(new GoodsViolationEntity
|
|
{
|
|
GoodsId = goodsId, ReasonId = reasonId, IsIndexDeleted = false,
|
|
CreatedTime = now, UpdatedTime = now
|
|
});
|
|
}
|
|
recordedIds.Add(goodsId);
|
|
}
|
|
unit.Commit();
|
|
return recordedIds.ToArray();
|
|
}
|
|
|
|
public Task<int> MarkDeletedAsync(long[] ids) => Orm.Update<GoodsViolationEntity>()
|
|
.Set(x => x.IsIndexDeleted, true)
|
|
.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
|
.Where(x => ids.Contains(x.GoodsId)).ExecuteAffrowsAsync();
|
|
|
|
public Task<List<long>> GetBatchAsync(long afterId, int size, bool pendingOnly) => Select
|
|
.Where(x => x.GoodsId > afterId)
|
|
.WhereIf(pendingOnly, x => !x.IsIndexDeleted)
|
|
.Distinct().OrderBy(x => x.GoodsId).Limit(size).ToListAsync(x => x.GoodsId);
|
|
|
|
public Task<List<long>> GetByReasonAsync(uint reasonId, long afterId, int size) => Select
|
|
.Where(x => x.ReasonId == reasonId)
|
|
.Where(x => x.GoodsId > afterId).OrderBy(x => x.GoodsId)
|
|
.Limit(size).ToListAsync(x => x.GoodsId);
|
|
}
|