using System.ComponentModel.DataAnnotations;
using MAX.Framework.Commons;
using MAX.Framework.Validation;
using Store.Shops.Dtos;
using Store.Shops.Entities;
using Store.Shops.Mappers;
using YourNamespace;
namespace Store.Shops.Services;
public sealed partial class ShopService
{
/// 选品列表跨月时重置300条额度,并同步店铺表;同店多条登录记录只处理一次。
private async Task RefreshQuotas(List shops, CancellationToken ct)
{
using var work = db.CreateUnitOfWork();
work.IsolationLevel = System.Data.IsolationLevel.Serializable;
work.GetOrBeginTransaction();
var orm = work.Orm;
// shortcut: 旧表仅保存月份;需要区分跨年同月额度时升级为年月字段。
var month = DateTime.Now.Month;
foreach (var group in shops.Where(s => !string.IsNullOrEmpty(s.ShopId)).GroupBy(s => s.ShopId).OrderBy(g => g.Key, StringComparer.Ordinal))
{
var quota = await orm.Select().Where(q => q.ShopId == group.Key).ForUpdate().FirstAsync(ct);
if (quota == null)
{
quota = new() { ShopId = group.Key, ShopAccount = group.First().ShopAccount, AddProductMonth = month, AddProductRemaining = 300 };
await orm.Insert(quota).ExecuteAffrowsAsync(ct);
}
else if (quota.AddProductMonth != month)
{
quota.AddProductMonth = month;
quota.AddProductRemaining = 300;
await orm.Update().Where(q => q.ShopId == group.Key)
.Set(q => q.AddProductMonth, month).Set(q => q.AddProductRemaining, 300).ExecuteAffrowsAsync(ct);
}
await orm.Update().Where(s => s.ShopId == group.Key)
.Set(s => s.AddProductMonth, (byte)quota.AddProductMonth)
.Set(s => s.AddProductRemaining, quota.AddProductRemaining).ExecuteAffrowsAsync(ct);
foreach (var item in group)
{
item.AddProductMonth = (byte)quota.AddProductMonth;
item.AddProductRemaining = quota.AddProductRemaining;
}
}
ct.ThrowIfCancellationRequested();
work.Commit();
}
/// 管理员设置明确指定店铺的共享额度,同步列表读取的店铺字段。
public async Task UpdateQuota(QuotaRequest request, CancellationToken ct)
{
RequestValidator.Validate(request);
Ids(request.ShopIds);
if (request.RemainCount < 0) throw new ValidationException("剩余额度不能小于0");
RequireAdmin(await Actor());
using var work = db.CreateUnitOfWork();
work.IsolationLevel = System.Data.IsolationLevel.Serializable;
work.GetOrBeginTransaction();
var orm = work.Orm;
var existing = await orm.Select().Where(q => request.ShopIds.Contains(q.ShopId)).ForUpdate().ToListAsync(ct);
if (request.ShopIds.Except(existing.Select(q => q.ShopId)).Any()) throw new AppException("部分店铺尚未建立额度记录");
await orm.Update().Where(q => request.ShopIds.Contains(q.ShopId))
.Set(q => q.AddProductRemaining, request.RemainCount).ExecuteAffrowsAsync(ct);
await orm.Update().Where(s => request.ShopIds.Contains(s.ShopId))
.Set(s => s.AddProductRemaining, request.RemainCount).ExecuteAffrowsAsync(ct);
ct.ThrowIfCancellationRequested();
work.Commit();
}
/// 管理员按记录ID或登录名分配选品操作员,空账号表示解除分配。
public async Task Assign(AssignRequest request, CancellationToken ct)
{
RequestValidator.Validate(request);
if (request.DbIds is not { Length: > 0 } && request.ShopLoginNames is not { Length: > 0 })
throw new ValidationException("请选择店铺记录或登录名");
if (request.DbIds?.Any(id => id <= 0) == true) throw new ValidationException("记录ID必须大于0");
if (request.ShopLoginNames != null) Ids(request.ShopLoginNames);
RequireAdmin(await Actor());
if (request.OperateSubSaName != "") await users.GetUser(request.OperateSubSaName);
var update = db.Update();
if (request.DbIds is { Length: > 0 }) update.Where(s => request.DbIds.Contains(s.Id));
else update.Where(s => request.ShopLoginNames!.Contains(s.LoginName));
if (await update.Set(s => s.OperateSubSaName, request.OperateSubSaName)
.Set(s => s.AddSubSaTime, DateTime.Now).ExecuteAffrowsAsync(ct) == 0)
throw new AppException("未找到指定店铺");
}
/// 查询授权店铺对应的ERP商户余额,不能仅凭外部商户ID读取他人余额。
public async Task> ErpMerchants(ErpRequest request, CancellationToken ct)
{
RequestValidator.Validate(request);
Page(request.Page, request.PageSize);
if (request.CorporateAccountUserId == null && request.ManageUserId == null)
throw new ValidationException("必须指定公司账号或管理账号");
if (request.CorporateAccountUserId <= 0 || request.ManageUserId <= 0)
throw new ValidationException("ERP账号ID必须大于0");
var actor = await Actor();
var ids = await VisibleIds(actor, ct);
var linked = await db.Select().Where(s => ids.Contains(s.AppKey))
.WhereIf(!string.IsNullOrEmpty(request.ShopId), s => s.AppKey == request.ShopId).ToListAsync(ct);
var managers = new List();
if (request.ManageUserId.HasValue)
{
if (!linked.Any(s => s.ManageUserId == request.ManageUserId)) throw new AppException("无权查询该ERP管理账号");
managers.Add(request.ManageUserId.Value);
}
if (request.CorporateAccountUserId.HasValue)
{
if (!linked.Any(s => s.CorporateAccountUserId == request.CorporateAccountUserId))
throw new AppException("无权查询该ERP公司账号");
managers.AddRange(await db.Select()
.Where(g => g.CorporateAccountUserId == request.CorporateAccountUserId)
.ToListAsync(g => g.ManageUserId, ct));
}
var query = db.Select().Where(a => managers.Contains(a.UserId));
var count = request.NotCount ? -1 : await query.CountAsync(ct);
var rows = await query.OrderByDescending(a => a.Balance).OrderBy(a => a.Id).Page(request.Page, request.PageSize).ToListAsync(ct);
return new(rows.Select(new ShopDetailsMapper().Map).ToList(), count);
}
}