增加Shop API
This commit is contained in:
1 parent
3769193696
commit
5d017740c6
83 files changed
+4451
-696
No files matched your search
@@ -0,0 +1,146 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using MAX.Framework.Commons;
|
||||
using MAX.Framework.Validation;
|
||||
using Store.Shops.Dtos;
|
||||
using Store.Shops.Entities;
|
||||
using Store.Shops.Mappers;
|
||||
|
||||
namespace Store.Shops.Services;
|
||||
|
||||
public sealed partial class ShopService
|
||||
{
|
||||
/// <summary>查询本人运单;跨账号按订单查询仅供管理员使用且必须指定订单。</summary>
|
||||
public async Task<ShopPage<WaybillInfo>> Waybills(WaybillSearchRequest request, bool allUsers, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
Page(request.Page, request.PageSize);
|
||||
if (request.Status?.Any(s => s is < 0 or > 2) == true) throw new ValidationException("运单状态只能为0、1、2");
|
||||
var actor = await Actor();
|
||||
if (allUsers)
|
||||
{
|
||||
RequireAdmin(actor);
|
||||
if (request.OrderIds == null) throw new ValidationException("订单编号不能为空");
|
||||
Ids(request.OrderIds);
|
||||
}
|
||||
var query = db.Select<WaybillEntity>().WhereIf(!allUsers, w => w.UserName == actor.UserName)
|
||||
.WhereIf(request.Status != null, w => request.Status!.Contains(w.Status))
|
||||
.WhereIf(request.OrderIds != null, w => request.OrderIds!.Contains(w.OrderId));
|
||||
var count = request.NotCount ? -1 : await query.CountAsync(ct);
|
||||
var rows = await query.OrderByDescending(w => w.Id).Page(request.Page, request.PageSize).ToListAsync(ct);
|
||||
return new(rows.Select(new ShopDetailsMapper().Map).ToList(), count);
|
||||
}
|
||||
|
||||
/// <summary>导入运单;揽收时间或绑定订单至少提供一个,运单号全库去重。</summary>
|
||||
public async Task AddWaybill(WaybillAddRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
if (request.WaybillPickupTime == null && string.IsNullOrWhiteSpace(request.BindOrderId))
|
||||
throw new ValidationException("揽收时间和绑定订单不能同时为空");
|
||||
var actor = await Actor();
|
||||
using var work = db.CreateUnitOfWork();
|
||||
work.IsolationLevel = System.Data.IsolationLevel.Serializable;
|
||||
work.GetOrBeginTransaction();
|
||||
var orm = work.Orm;
|
||||
if (await orm.Select<WaybillEntity>().Where(w => w.WaybillId == request.WaybillId).ForUpdate().AnyAsync(ct))
|
||||
throw new AppException("运单号已存在");
|
||||
await orm.Insert(new WaybillEntity
|
||||
{
|
||||
UserName = actor.UserName, DeliveryId = request.DeliveryId, DeliveryName = request.DeliveryName,
|
||||
WaybillId = request.WaybillId, BindOrderId = request.BindOrderId ?? "", WaybillPickupTime = request.WaybillPickupTime,
|
||||
Status = 0, AddTime = DateTime.Now
|
||||
}).ExecuteAffrowsAsync(ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
work.Commit();
|
||||
}
|
||||
|
||||
/// <summary>只删除本人尚未使用的运单,已预占或使用的运单保持不变。</summary>
|
||||
public async Task DeleteWaybills(WaybillDeleteRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
Ids(request.WaybillIds);
|
||||
var actor = await Actor();
|
||||
await db.Delete<WaybillEntity>().Where(w => w.UserName == actor.UserName && w.Status == 0
|
||||
&& request.WaybillIds.Contains(w.WaybillId)).ExecuteAffrowsAsync(ct);
|
||||
}
|
||||
|
||||
/// <summary>预占或直接使用运单;先复用同订单记录,再按绑定订单及次日揽收条件选择。</summary>
|
||||
public async Task<WaybillInfo> SelectWaybill(WaybillSelectRequest request, bool useImmediately, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
if (request.OrderTime?.Date >= DateTime.MaxValue.Date.AddDays(-2))
|
||||
throw new ValidationException("订单时间超出可匹配范围");
|
||||
var actor = await Actor();
|
||||
using var work = db.CreateUnitOfWork();
|
||||
work.IsolationLevel = System.Data.IsolationLevel.Serializable;
|
||||
work.GetOrBeginTransaction();
|
||||
var orm = work.Orm;
|
||||
await RequireShop(orm, actor, request.ShopId, ct);
|
||||
var row = await orm.Select<WaybillEntity>()
|
||||
.Where(w => w.UserName == actor.UserName && w.OrderId == request.OrderId && (w.Status == 1 || w.Status == 2))
|
||||
.OrderBy(w => w.Id).ForUpdate().FirstAsync(ct);
|
||||
if (row != null && !string.IsNullOrEmpty(row.ShopId) && row.ShopId != request.ShopId)
|
||||
throw new AppException("订单已绑定其他店铺的运单");
|
||||
if (row == null)
|
||||
{
|
||||
row = await orm.Select<WaybillEntity>().Where(w => w.UserName == actor.UserName && w.Status == 0
|
||||
&& w.BindOrderId == request.OrderId && (w.OrderId == "" || w.OrderId == null))
|
||||
.OrderBy("RAND()").ForUpdate().FirstAsync(ct);
|
||||
if (row == null && (request.OrderTime.HasValue || useImmediately))
|
||||
{
|
||||
var available = orm.Select<WaybillEntity>().Where(w => w.UserName == actor.UserName && w.Status == 0
|
||||
&& (w.OrderId == "" || w.OrderId == null) && (w.BindOrderId == "" || w.BindOrderId == null));
|
||||
if (!useImmediately)
|
||||
{
|
||||
var start = request.OrderTime!.Value.Date.AddDays(1);
|
||||
var end = start.AddDays(1);
|
||||
available.Where(w => w.WaybillPickupTime >= start && w.WaybillPickupTime < end);
|
||||
}
|
||||
row = await available.OrderBy("RAND()").ForUpdate().FirstAsync(ct);
|
||||
}
|
||||
if (row == null) throw new AppException("没有可用的运单号");
|
||||
}
|
||||
if (row.Status != 2)
|
||||
{
|
||||
var status = useImmediately ? 2 : 1;
|
||||
var useTime = useImmediately ? DateTime.Now : (DateTime?)null;
|
||||
// 行锁与状态条件一起保护分配;请求重试不会重复占用第二张运单。
|
||||
var affected = await orm.Update<WaybillEntity>().Where(w => w.Id == row.Id && w.Status == row.Status)
|
||||
.Set(w => w.OrderId, request.OrderId).Set(w => w.ShopId, request.ShopId)
|
||||
.Set(w => w.Status, status).Set(w => w.UseTime, useTime).ExecuteAffrowsAsync(ct);
|
||||
if (affected == 0 && row.Status == 0) throw new AppException("运单已被占用,请重试");
|
||||
row.OrderId = request.OrderId;
|
||||
row.ShopId = request.ShopId;
|
||||
row.Status = status;
|
||||
row.UseTime = useTime;
|
||||
}
|
||||
ct.ThrowIfCancellationRequested();
|
||||
work.Commit();
|
||||
return new ShopDetailsMapper().Map(row);
|
||||
}
|
||||
|
||||
/// <summary>仅对本人的预占运单确认使用或释放;不能释放已正式使用的运单。</summary>
|
||||
public async Task UseWaybill(WaybillUseRequest request, CancellationToken ct)
|
||||
{
|
||||
RequestValidator.Validate(request);
|
||||
var actor = await Actor();
|
||||
using var work = db.CreateUnitOfWork();
|
||||
work.GetOrBeginTransaction();
|
||||
var orm = work.Orm;
|
||||
await RequireShop(orm, actor, request.ShopId, ct);
|
||||
var row = await orm.Select<WaybillEntity>().Where(w => w.UserName == actor.UserName
|
||||
&& w.OrderId == request.OrderId && w.WaybillId == request.WaybillId)
|
||||
.ForUpdate().FirstAsync(ct);
|
||||
if (row == null || (row.ShopId != "" && row.ShopId != null && row.ShopId != request.ShopId))
|
||||
throw new AppException("运单不存在或不属于指定店铺订单");
|
||||
if (!request.IsRollback && row.Status == 2) return;
|
||||
if (row.Status != 1) throw new AppException("只有预占运单可以确认或释放");
|
||||
var changed = await orm.Update<WaybillEntity>().Where(w => w.Id == row.Id && w.Status == 1)
|
||||
.Set(w => w.ShopId, request.IsRollback ? "" : request.ShopId)
|
||||
.Set(w => w.OrderId, request.IsRollback ? "" : request.OrderId)
|
||||
.Set(w => w.Status, request.IsRollback ? 0 : 2)
|
||||
.Set(w => w.UseTime, request.IsRollback ? null : (DateTime?)DateTime.Now).ExecuteAffrowsAsync(ct);
|
||||
if (changed != 1) throw new AppException("运单状态已变化,请重新查询");
|
||||
ct.ThrowIfCancellationRequested();
|
||||
work.Commit();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user