2026-04-10 22:39:32 +08:00
|
|
|
|
using Furion.FriendlyException;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2026-03-06 12:10:09 +08:00
|
|
|
|
using YD_Event.Application.Auth;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
using YD_Event.Application.Merchant.Dtos;
|
|
|
|
|
|
using YD_Event.Core.Entities;
|
2026-03-06 12:10:09 +08:00
|
|
|
|
using YD_Event.Core.Enum;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
|
|
|
|
|
namespace YD_Event.Application.Merchant;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 商户管理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class MerchantService : BaseService<YD_Event.Core.Entities.Merchant>, ITransient
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Merchant> _repository;
|
2026-03-06 12:10:09 +08:00
|
|
|
|
private readonly ISqlSugarRepository<UserCoupon> _userCouponRepository;
|
2026-03-26 00:09:20 +08:00
|
|
|
|
private readonly ISqlSugarRepository<WeChatPayConfig> _weChatPayConfigRepository;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
|
|
|
|
|
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
2026-03-06 12:10:09 +08:00
|
|
|
|
private readonly AuthManager _authManager;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
private readonly IIdGenerator _idGenerator;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
private readonly Microsoft.Extensions.Logging.ILogger<MerchantService> _logger;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
|
|
|
|
|
public MerchantService(ISqlSugarRepository<YD_Event.Core.Entities.Merchant> repository,
|
2026-03-06 12:10:09 +08:00
|
|
|
|
ISqlSugarRepository<UserCoupon> userCouponRepository,
|
2026-03-26 00:09:20 +08:00
|
|
|
|
ISqlSugarRepository<WeChatPayConfig> weChatPayConfigRepository,
|
2026-04-10 22:39:32 +08:00
|
|
|
|
ISqlSugarRepository<EventInfo> eventRepository,
|
|
|
|
|
|
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
2026-03-06 12:10:09 +08:00
|
|
|
|
AuthManager authManager,
|
2026-04-10 22:39:32 +08:00
|
|
|
|
IIdGenerator idGenerator,
|
|
|
|
|
|
Microsoft.Extensions.Logging.ILogger<MerchantService> logger) : base(repository)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
_repository = repository;
|
2026-03-06 12:10:09 +08:00
|
|
|
|
_userCouponRepository = userCouponRepository;
|
2026-03-26 00:09:20 +08:00
|
|
|
|
_weChatPayConfigRepository = weChatPayConfigRepository;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
_eventRepository = eventRepository;
|
|
|
|
|
|
_orderRepository = orderRepository;
|
2026-03-06 12:10:09 +08:00
|
|
|
|
_authManager = authManager;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
_idGenerator = idGenerator;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
_logger = logger;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 商户分页查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("商户分页查询")]
|
2026-03-06 12:10:09 +08:00
|
|
|
|
[HttpGet("page")]
|
2026-03-25 15:51:30 +08:00
|
|
|
|
public async Task<PageResult<MerchantOutput>> Page([FromQuery] MerchantQueryInput input)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-03-25 15:51:30 +08:00
|
|
|
|
var pageResult = await _repository.AsQueryable()
|
2026-01-17 12:52:58 +08:00
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.MerchantName), x => x.MerchantName.Contains(input.MerchantName))
|
2026-03-06 12:10:09 +08:00
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.ContactPhone), x => x.ContactPhone.Contains(input.ContactPhone))
|
|
|
|
|
|
.WhereIF(input.AuditStatus.HasValue, x => x.AuditStatus == input.AuditStatus)
|
|
|
|
|
|
.WhereIF(input.StartTime.HasValue, x => x.CreatedTime >= input.StartTime)
|
|
|
|
|
|
.WhereIF(input.EndTime.HasValue, x => x.CreatedTime <= input.EndTime)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
.OrderByDescending(x => x.CreatedTime)
|
2026-03-25 15:51:30 +08:00
|
|
|
|
.Select(x => new MerchantOutput
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
|
MerchantName = x.MerchantName,
|
|
|
|
|
|
CreditCode = x.CreditCode,
|
|
|
|
|
|
BankName = x.BankName,
|
|
|
|
|
|
AccountNumber = x.AccountNumber,
|
|
|
|
|
|
BusinessLicenseUrl = x.BusinessLicenseUrl,
|
|
|
|
|
|
ContactPhone = x.ContactPhone,
|
|
|
|
|
|
Address = x.Address,
|
|
|
|
|
|
AuditStatus = x.AuditStatus,
|
|
|
|
|
|
AuditRemark = x.AuditRemark,
|
|
|
|
|
|
Status = x.Status,
|
|
|
|
|
|
CreatedTime = x.CreatedTime,
|
|
|
|
|
|
UpdatedTime = x.UpdatedTime
|
|
|
|
|
|
})
|
2026-01-17 12:52:58 +08:00
|
|
|
|
.ToPagedListAsync(input);
|
2026-03-25 15:51:30 +08:00
|
|
|
|
|
|
|
|
|
|
return pageResult;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 添加商户
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("添加商户")]
|
|
|
|
|
|
[HttpPost("add")]
|
2026-03-26 00:09:20 +08:00
|
|
|
|
[UnitOfWork]
|
2026-01-17 12:52:58 +08:00
|
|
|
|
public async Task Add(AddMerchantInput input)
|
|
|
|
|
|
{
|
|
|
|
|
|
var entity = input.Adapt<YD_Event.Core.Entities.Merchant>();
|
|
|
|
|
|
entity.Id = _idGenerator.NextId();
|
|
|
|
|
|
await _repository.InsertAsync(entity);
|
2026-03-26 00:09:20 +08:00
|
|
|
|
|
|
|
|
|
|
if (input.WeChatPayConfig != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var weChatPayConfig = input.WeChatPayConfig.Adapt<WeChatPayConfig>();
|
|
|
|
|
|
weChatPayConfig.Id = _idGenerator.NextId();
|
|
|
|
|
|
weChatPayConfig.MerchantId = entity.Id;
|
|
|
|
|
|
await _weChatPayConfigRepository.InsertAsync(weChatPayConfig);
|
|
|
|
|
|
}
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新商户
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("更新商户")]
|
|
|
|
|
|
[HttpPut("edit")]
|
2026-03-26 00:09:20 +08:00
|
|
|
|
[UnitOfWork]
|
2026-01-17 12:52:58 +08:00
|
|
|
|
public async Task Update(UpdateMerchantInput input)
|
|
|
|
|
|
{
|
|
|
|
|
|
var entity = await _repository.GetByIdAsync(input.Id);
|
|
|
|
|
|
if (entity == null) throw Oops.Bah("商户不存在");
|
|
|
|
|
|
|
|
|
|
|
|
input.Adapt(entity);
|
|
|
|
|
|
await _repository.UpdateAsync(entity);
|
2026-03-26 00:09:20 +08:00
|
|
|
|
|
|
|
|
|
|
var existingWeChatPayConfig = await _weChatPayConfigRepository.GetFirstAsync(c => c.MerchantId == input.Id);
|
|
|
|
|
|
|
|
|
|
|
|
if (input.WeChatPayConfig != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (existingWeChatPayConfig != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
input.WeChatPayConfig.Adapt(existingWeChatPayConfig);
|
|
|
|
|
|
await _weChatPayConfigRepository.UpdateAsync(existingWeChatPayConfig);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var weChatPayConfig = input.WeChatPayConfig.Adapt<WeChatPayConfig>();
|
|
|
|
|
|
weChatPayConfig.Id = _idGenerator.NextId();
|
|
|
|
|
|
weChatPayConfig.MerchantId = input.Id;
|
|
|
|
|
|
await _weChatPayConfigRepository.InsertAsync(weChatPayConfig);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (existingWeChatPayConfig != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _weChatPayConfigRepository.DeleteAsync(existingWeChatPayConfig);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 删除商户
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("删除商户")]
|
|
|
|
|
|
[HttpPost("delete")]
|
2026-04-10 22:39:32 +08:00
|
|
|
|
[UnitOfWork]
|
2026-01-17 12:52:58 +08:00
|
|
|
|
public async Task Delete([FromBody] List<long> ids)
|
|
|
|
|
|
{
|
2026-04-10 22:39:32 +08:00
|
|
|
|
_logger.LogInformation("开始删除商户:商户ID列表={Ids}", string.Join(",", ids));
|
|
|
|
|
|
|
|
|
|
|
|
if (ids == null || !ids.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("删除商户失败:未选择商户");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查商户是否被赛事使用
|
|
|
|
|
|
var hasEvents = await _eventRepository.IsAnyAsync(x =>
|
|
|
|
|
|
x.MerchantId.HasValue && ids.Contains(x.MerchantId.Value) && !x.DeleteMark);
|
|
|
|
|
|
|
|
|
|
|
|
if (hasEvents)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("删除商户失败:部分商户正在被赛事使用");
|
|
|
|
|
|
throw Oops.Bah("部分商户正在被赛事使用,无法删除。请先从赛事中移除这些商户。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查商户是否有订单记录
|
|
|
|
|
|
var hasOrders = await _orderRepository.IsAnyAsync(x =>
|
|
|
|
|
|
x.MerchantId.HasValue && ids.Contains(x.MerchantId.Value) && !x.DeleteMark);
|
|
|
|
|
|
|
|
|
|
|
|
if (hasOrders)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("删除商户失败:部分商户已有订单记录");
|
|
|
|
|
|
throw Oops.Bah("部分商户已有订单记录,为保证数据完整性,无法删除。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行删除
|
2026-01-17 12:52:58 +08:00
|
|
|
|
await _repository.DeleteAsync(x => ids.Contains(x.Id));
|
2026-04-10 22:39:32 +08:00
|
|
|
|
|
|
|
|
|
|
// 同时删除关联的微信支付配置
|
|
|
|
|
|
await _weChatPayConfigRepository.DeleteAsync(x => ids.Contains(x.MerchantId));
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("删除商户成功:商户ID列表={Ids}", string.Join(",", ids));
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取商户详情
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("获取商户详情")]
|
|
|
|
|
|
[HttpGet("detail")]
|
2026-03-25 15:51:30 +08:00
|
|
|
|
public async Task<MerchantOutput> Detail([FromQuery] long id)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
var entity = await _repository.GetByIdAsync(id);
|
|
|
|
|
|
if (entity == null) throw Oops.Bah("商户不存在");
|
2026-03-25 15:51:30 +08:00
|
|
|
|
|
2026-03-26 00:09:20 +08:00
|
|
|
|
var weChatPayConfig = await _weChatPayConfigRepository.GetFirstAsync(c => c.MerchantId == id);
|
|
|
|
|
|
|
2026-03-25 15:51:30 +08:00
|
|
|
|
return new MerchantOutput
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = entity.Id,
|
|
|
|
|
|
MerchantName = entity.MerchantName,
|
|
|
|
|
|
CreditCode = entity.CreditCode,
|
|
|
|
|
|
BankName = entity.BankName,
|
|
|
|
|
|
AccountNumber = entity.AccountNumber,
|
|
|
|
|
|
BusinessLicenseUrl = entity.BusinessLicenseUrl,
|
|
|
|
|
|
ContactPhone = entity.ContactPhone,
|
|
|
|
|
|
Address = entity.Address,
|
|
|
|
|
|
AuditStatus = entity.AuditStatus,
|
|
|
|
|
|
AuditRemark = entity.AuditRemark,
|
|
|
|
|
|
Status = entity.Status,
|
|
|
|
|
|
CreatedTime = entity.CreatedTime,
|
2026-03-26 00:09:20 +08:00
|
|
|
|
UpdatedTime = entity.UpdatedTime,
|
|
|
|
|
|
WeChatPayConfig = weChatPayConfig != null ? weChatPayConfig.Adapt<WeChatPayConfigDto>() : null
|
2026-03-25 15:51:30 +08:00
|
|
|
|
};
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
2026-03-06 12:10:09 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 发放优惠券
|
|
|
|
|
|
/// </summary>
|
2026-03-17 15:32:01 +08:00
|
|
|
|
[DisplayName("发放优惠券")]
|
2026-03-06 12:10:09 +08:00
|
|
|
|
[HttpPost("issueCoupon")]
|
|
|
|
|
|
[UnitOfWork]
|
|
|
|
|
|
public async Task IssueCoupon([FromBody] IssueCouponInput input)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (input.MerchantIds == null || !input.MerchantIds.Any()) return;
|
|
|
|
|
|
if (input.Count <= 0) throw Oops.Bah("发放数量必须大于0");
|
|
|
|
|
|
|
|
|
|
|
|
var merchants = await _repository.GetListAsync(m => input.MerchantIds.Contains(m.Id));
|
|
|
|
|
|
if (!merchants.Any()) return;
|
|
|
|
|
|
|
|
|
|
|
|
var coupons = new List<UserCoupon>();
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var merchant in merchants)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < input.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
coupons.Add(new UserCoupon
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = _idGenerator.NextId(),
|
|
|
|
|
|
UserId = merchant.CreatedUserId, // Issue to the creator of the merchant
|
|
|
|
|
|
CouponId = input.CouponId,
|
|
|
|
|
|
Status = 0, // Unused
|
|
|
|
|
|
ClaimTime = now,
|
|
|
|
|
|
CreatedTime = now,
|
|
|
|
|
|
CreatedUserId = _authManager.UserId
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await _userCouponRepository.InsertRangeAsync(coupons);
|
|
|
|
|
|
}
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|