赛事编排
This commit is contained in:
parent
f7aedcce91
commit
565ca9cc36
@ -117,12 +117,6 @@ public class AuthService : IDynamicApiController
|
|||||||
throw Oops.Bah("验证码错误或已过期");
|
throw Oops.Bah("验证码错误或已过期");
|
||||||
}
|
}
|
||||||
|
|
||||||
var existAccount = await _sysUseRepository.IsAnyAsync(x => x.Account == dto.Account);
|
|
||||||
if (existAccount)
|
|
||||||
{
|
|
||||||
throw Oops.Bah("该账号已被注册");
|
|
||||||
}
|
|
||||||
|
|
||||||
var existPhone = await _sysUseRepository.IsAnyAsync(x => x.Mobile == dto.Phone);
|
var existPhone = await _sysUseRepository.IsAnyAsync(x => x.Mobile == dto.Phone);
|
||||||
if (existPhone)
|
if (existPhone)
|
||||||
{
|
{
|
||||||
@ -132,8 +126,8 @@ public class AuthService : IDynamicApiController
|
|||||||
var user = new SysUser
|
var user = new SysUser
|
||||||
{
|
{
|
||||||
Id = _idGenerator.NewLong(),
|
Id = _idGenerator.NewLong(),
|
||||||
Account = dto.Account,
|
Account = dto.Phone,
|
||||||
Name = dto.Account,
|
Name = dto.Phone,
|
||||||
Mobile = dto.Phone,
|
Mobile = dto.Phone,
|
||||||
Gender = Gender.Unknown,
|
Gender = Gender.Unknown,
|
||||||
OrgId = 0,
|
OrgId = 0,
|
||||||
|
|||||||
@ -15,14 +15,6 @@ public class RegisterInput
|
|||||||
[RegularExpression(@"^1[3456789]\d{9}$", ErrorMessage = "手机号格式不正确")]
|
[RegularExpression(@"^1[3456789]\d{9}$", ErrorMessage = "手机号格式不正确")]
|
||||||
public string Phone { get; set; } = null!;
|
public string Phone { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 账号
|
|
||||||
/// </summary>
|
|
||||||
[Required(ErrorMessage = "账号不能为空")]
|
|
||||||
[MinLength(3, ErrorMessage = "账号至少3个字符")]
|
|
||||||
[MaxLength(32, ErrorMessage = "账号最多32个字符")]
|
|
||||||
public string Account { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密码
|
/// 密码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -433,6 +433,20 @@ public class CertificateTemplateService : BaseService<CertificateTemplate>, ITra
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取证书模板列表(用于选择)
|
||||||
|
/// </summary>
|
||||||
|
[DisplayName("获取证书模板列表")]
|
||||||
|
public async Task<List<CertificateTemplateSimpleOutput>> GetTemplateList()
|
||||||
|
{
|
||||||
|
var templates = await _templateRepository.AsQueryable()
|
||||||
|
.Where(x => x.Status == Core.Enum.AvailabilityStatus.Enable && !x.DeleteMark)
|
||||||
|
.OrderByDescending(x => x.CreatedTime)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return templates.Adapt<List<CertificateTemplateSimpleOutput>>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前用户ID
|
/// 获取当前用户ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -354,4 +354,9 @@ public class CertificateTemplateSimpleOutput
|
|||||||
/// 创建时间
|
/// 创建时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime CreatedTime { get; set; }
|
public DateTime CreatedTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示名称
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayName => $"模板 {Id} (v{Version}) - {TemplateType}";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using Furion.DatabaseAccessor;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Furion.FriendlyException;
|
using Furion.FriendlyException;
|
||||||
|
using EasyCaching.Core;
|
||||||
using ProjectEntity = YD_Event.Core.Entities.Project;
|
using ProjectEntity = YD_Event.Core.Entities.Project;
|
||||||
|
|
||||||
namespace YD_Event.Application.Client;
|
namespace YD_Event.Application.Client;
|
||||||
@ -29,6 +30,7 @@ public class MyController : IDynamicApiController
|
|||||||
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
|
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
|
||||||
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly IEasyCachingProvider _cachingProvider;
|
||||||
|
|
||||||
public MyController(
|
public MyController(
|
||||||
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
|
||||||
@ -41,7 +43,8 @@ public class MyController : IDynamicApiController
|
|||||||
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
|
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
|
||||||
ISqlSugarRepository<EventInfo> eventRepository,
|
ISqlSugarRepository<EventInfo> eventRepository,
|
||||||
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
||||||
IHttpContextAccessor httpContextAccessor)
|
IHttpContextAccessor httpContextAccessor,
|
||||||
|
IEasyCachingProvider cachingProvider)
|
||||||
{
|
{
|
||||||
_appUserRepository = appUserRepository;
|
_appUserRepository = appUserRepository;
|
||||||
_teamRepository = teamRepository;
|
_teamRepository = teamRepository;
|
||||||
@ -54,6 +57,7 @@ public class MyController : IDynamicApiController
|
|||||||
_eventRepository = eventRepository;
|
_eventRepository = eventRepository;
|
||||||
_merchantCertificationRepository = merchantCertificationRepository;
|
_merchantCertificationRepository = merchantCertificationRepository;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
_cachingProvider = cachingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetCurrentUserId()
|
private long GetCurrentUserId()
|
||||||
@ -178,6 +182,31 @@ public class MyController : IDynamicApiController
|
|||||||
|
|
||||||
if (string.IsNullOrEmpty(input.SmsCode)) throw Oops.Bah("手机号验证码不能为空");
|
if (string.IsNullOrEmpty(input.SmsCode)) throw Oops.Bah("手机号验证码不能为空");
|
||||||
|
|
||||||
|
// 1. 验证短信验证码格式(6位数字)
|
||||||
|
if (input.SmsCode.Length != 6 || !input.SmsCode.All(char.IsDigit))
|
||||||
|
{
|
||||||
|
throw Oops.Bah("验证码格式错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 从缓存中获取验证码,验证是否为系统最新生成且在有效期内
|
||||||
|
var codeCacheKey = $"sms:code:{input.Mobile}";
|
||||||
|
var cachedCode = await _cachingProvider.GetAsync<string>(codeCacheKey);
|
||||||
|
|
||||||
|
// 3. 验证验证码是否在有效期内
|
||||||
|
if (!cachedCode.HasValue)
|
||||||
|
{
|
||||||
|
throw Oops.Bah("验证码已过期");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 验证验证码是否一致
|
||||||
|
if (cachedCode.Value != input.SmsCode)
|
||||||
|
{
|
||||||
|
throw Oops.Bah("验证码不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证成功后,移除验证码缓存
|
||||||
|
await _cachingProvider.RemoveAsync(codeCacheKey);
|
||||||
|
|
||||||
user.Name = input.Name;
|
user.Name = input.Name;
|
||||||
user.Mobile = input.Mobile;
|
user.Mobile = input.Mobile;
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,12 @@ public class ArrangementProjectListOutput
|
|||||||
public int SortIndex { get; set; }
|
public int SortIndex { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 总时长(分钟)
|
/// 总时长(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int TotalDurationSeconds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总时长(分钟,向上取整)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int TotalDurationMinutes { get; set; }
|
public int TotalDurationMinutes { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,7 +155,8 @@ public class EventArrangementService : BaseService<YD_Event.Core.Entities.Projec
|
|||||||
totalDurationSeconds = rounds * proj.DurationSeconds;
|
totalDurationSeconds = rounds * proj.DurationSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 向上取整到最近的分钟数
|
// 设置总时长秒数和分钟数
|
||||||
|
dto.TotalDurationSeconds = totalDurationSeconds;
|
||||||
dto.TotalDurationMinutes = (int)Math.Ceiling((double)totalDurationSeconds / 60);
|
dto.TotalDurationMinutes = (int)Math.Ceiling((double)totalDurationSeconds / 60);
|
||||||
|
|
||||||
result.Add(dto);
|
result.Add(dto);
|
||||||
|
|||||||
@ -15,6 +15,8 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
|
|||||||
private readonly ISqlSugarRepository<EventRegistrationPerson> _personRepository;
|
private readonly ISqlSugarRepository<EventRegistrationPerson> _personRepository;
|
||||||
private readonly ISqlSugarRepository<EventRegistrationPayment> _paymentRepository;
|
private readonly ISqlSugarRepository<EventRegistrationPayment> _paymentRepository;
|
||||||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
||||||
|
private readonly ISqlSugarRepository<TeamMember> _teamMemberRepository;
|
||||||
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _appUserRepository;
|
||||||
private readonly AuthManager _authManager;
|
private readonly AuthManager _authManager;
|
||||||
|
|
||||||
public EventRegistrationService(
|
public EventRegistrationService(
|
||||||
@ -23,6 +25,8 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
|
|||||||
ISqlSugarRepository<EventRegistrationPerson> personRepository,
|
ISqlSugarRepository<EventRegistrationPerson> personRepository,
|
||||||
ISqlSugarRepository<EventRegistrationPayment> paymentRepository,
|
ISqlSugarRepository<EventRegistrationPayment> paymentRepository,
|
||||||
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
||||||
|
ISqlSugarRepository<TeamMember> teamMemberRepository,
|
||||||
|
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
|
||||||
AuthManager authManager) : base(repository)
|
AuthManager authManager) : base(repository)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
@ -30,6 +34,8 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
|
|||||||
_personRepository = personRepository;
|
_personRepository = personRepository;
|
||||||
_paymentRepository = paymentRepository;
|
_paymentRepository = paymentRepository;
|
||||||
_orderRepository = orderRepository;
|
_orderRepository = orderRepository;
|
||||||
|
_teamMemberRepository = teamMemberRepository;
|
||||||
|
_appUserRepository = appUserRepository;
|
||||||
_authManager = authManager;
|
_authManager = authManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +88,75 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
|
|||||||
.Where(x => regIds.Contains(x.RegistrationId))
|
.Where(x => regIds.Contains(x.RegistrationId))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// 获取所有报名人员的 UserId
|
||||||
|
var allAthleteUserIds = persons
|
||||||
|
.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Athlete && p.UserId > 0)
|
||||||
|
.Select(p => p.UserId)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 步骤1:通过运动员的 UserId 找到对应的 TeamId
|
||||||
|
var teamIds = new List<long>();
|
||||||
|
if (allAthleteUserIds.Any())
|
||||||
|
{
|
||||||
|
teamIds = await _teamMemberRepository.AsQueryable()
|
||||||
|
.Where(tm => allAthleteUserIds.Contains(tm.UserId) && !tm.DeleteMark)
|
||||||
|
.Select(tm => tm.TeamId)
|
||||||
|
.Distinct()
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤2:通过 TeamId 获取同团队的教练/领队的 UserId,并获取用户详情
|
||||||
|
Dictionary<long, List<YD_Event.Core.Entities.AppUser>> teamLeadersDict = new();
|
||||||
|
Dictionary<long, List<YD_Event.Core.Entities.AppUser>> teamCoachesDict = new();
|
||||||
|
|
||||||
|
if (teamIds.Any())
|
||||||
|
{
|
||||||
|
var teamMembers = await _teamMemberRepository.AsQueryable()
|
||||||
|
.Where(tm => teamIds.Contains(tm.TeamId) && !tm.DeleteMark)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var coachLeaderUserIds = teamMembers
|
||||||
|
.Where(tm => tm.PersonType == YD_Event.Core.Enum.PersonType.Leader ||
|
||||||
|
tm.PersonType == YD_Event.Core.Enum.PersonType.Coach)
|
||||||
|
.Select(tm => tm.UserId)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (coachLeaderUserIds.Any())
|
||||||
|
{
|
||||||
|
var coachLeaderUsers = await _appUserRepository.AsQueryable()
|
||||||
|
.Where(u => coachLeaderUserIds.Contains(u.Id) && !u.DeleteMark)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var userDict = coachLeaderUsers.ToDictionary(u => u.Id);
|
||||||
|
|
||||||
|
// 按团队分组
|
||||||
|
foreach (var teamId in teamIds)
|
||||||
|
{
|
||||||
|
var teamLeaders = teamMembers
|
||||||
|
.Where(tm => tm.TeamId == teamId && tm.PersonType == YD_Event.Core.Enum.PersonType.Leader)
|
||||||
|
.Select(tm => userDict.TryGetValue(tm.UserId, out var user) ? user : null)
|
||||||
|
.Where(u => u != null)
|
||||||
|
.Cast<YD_Event.Core.Entities.AppUser>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var teamCoaches = teamMembers
|
||||||
|
.Where(tm => tm.TeamId == teamId && tm.PersonType == YD_Event.Core.Enum.PersonType.Coach)
|
||||||
|
.Select(tm => userDict.TryGetValue(tm.UserId, out var user) ? user : null)
|
||||||
|
.Where(u => u != null)
|
||||||
|
.Cast<YD_Event.Core.Entities.AppUser>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (teamLeaders.Any())
|
||||||
|
teamLeadersDict[teamId] = teamLeaders;
|
||||||
|
|
||||||
|
if (teamCoaches.Any())
|
||||||
|
teamCoachesDict[teamId] = teamCoaches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var dto in result.Rows)
|
foreach (var dto in result.Rows)
|
||||||
{
|
{
|
||||||
var myItems = items.Where(x => x.RegistrationId == dto.Id).ToList();
|
var myItems = items.Where(x => x.RegistrationId == dto.Id).ToList();
|
||||||
@ -120,17 +195,81 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
|
|||||||
dto.Items.Add(detail);
|
dto.Items.Add(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置领队信息和教练信息
|
// 设置领队信息和教练信息 - 从团队中获取
|
||||||
var regPersons = persons.Where(p => p.RegistrationId == dto.Id).ToList();
|
var regPersons = persons.Where(p => p.RegistrationId == dto.Id).ToList();
|
||||||
var leaders = regPersons.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Leader).ToList();
|
var regAthleteUserIds = regPersons
|
||||||
var coaches = regPersons.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Coach).ToList();
|
.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Athlete && p.UserId > 0)
|
||||||
|
.Select(p => p.UserId)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
dto.TeamLeaderInfo = leaders.Any()
|
// 获取这些运动员所属的团队
|
||||||
? string.Join(";", leaders.Select(l => $"{l.Name}({l.Phone})"))
|
var regTeamIds = new List<long>();
|
||||||
|
if (regAthleteUserIds.Any())
|
||||||
|
{
|
||||||
|
regTeamIds = await _teamMemberRepository.AsQueryable()
|
||||||
|
.Where(tm => regAthleteUserIds.Contains(tm.UserId) && !tm.DeleteMark)
|
||||||
|
.Select(tm => tm.TeamId)
|
||||||
|
.Distinct()
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集所有相关团队的领队和教练
|
||||||
|
var allLeaders = new List<YD_Event.Core.Entities.AppUser>();
|
||||||
|
var allCoaches = new List<YD_Event.Core.Entities.AppUser>();
|
||||||
|
|
||||||
|
foreach (var teamId in regTeamIds)
|
||||||
|
{
|
||||||
|
if (teamLeadersDict.TryGetValue(teamId, out var leaders))
|
||||||
|
allLeaders.AddRange(leaders);
|
||||||
|
if (teamCoachesDict.TryGetValue(teamId, out var coaches))
|
||||||
|
allCoaches.AddRange(coaches);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去重
|
||||||
|
allLeaders = allLeaders.GroupBy(l => l.Id).Select(g => g.First()).ToList();
|
||||||
|
allCoaches = allCoaches.GroupBy(c => c.Id).Select(g => g.First()).ToList();
|
||||||
|
|
||||||
|
// 如果从团队中没有找到,再回退到从报名人员中查找
|
||||||
|
if (!allLeaders.Any())
|
||||||
|
{
|
||||||
|
var regLeaders = regPersons.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Leader).ToList();
|
||||||
|
allLeaders = regLeaders.Select(l => new YD_Event.Core.Entities.AppUser
|
||||||
|
{
|
||||||
|
Name = l.Name,
|
||||||
|
Mobile = l.Phone,
|
||||||
|
Gender = l.Gender
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allCoaches.Any())
|
||||||
|
{
|
||||||
|
var regCoaches = regPersons.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Coach).ToList();
|
||||||
|
allCoaches = regCoaches.Select(c => new YD_Event.Core.Entities.AppUser
|
||||||
|
{
|
||||||
|
Name = c.Name,
|
||||||
|
Mobile = c.Phone,
|
||||||
|
Gender = c.Gender
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:获取性别文本
|
||||||
|
Func<YD_Event.Core.Enum.Gender, string> getGenderText = (gender) =>
|
||||||
|
{
|
||||||
|
return gender switch
|
||||||
|
{
|
||||||
|
YD_Event.Core.Enum.Gender.Male => "男",
|
||||||
|
YD_Event.Core.Enum.Gender.Female => "女",
|
||||||
|
_ => "未知"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dto.TeamLeaderInfo = allLeaders.Any()
|
||||||
|
? string.Join(",", allLeaders.Select(l => $"{l.Name}({l.Mobile})/{getGenderText(l.Gender)}"))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
dto.CoachInfo = coaches.Any()
|
dto.CoachInfo = allCoaches.Any()
|
||||||
? string.Join(";", coaches.Select(c => $"{c.Name}({c.Phone})"))
|
? string.Join(",", allCoaches.Select(c => $"{c.Name}({c.Mobile})/{getGenderText(c.Gender)}"))
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,6 +54,8 @@ public class PaymentExpirationBackgroundService : BackgroundService
|
|||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var registrationRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<EventRegistration>>();
|
var registrationRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<EventRegistration>>();
|
||||||
var orderRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<YD_Event.Core.Entities.Order>>();
|
var orderRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<YD_Event.Core.Entities.Order>>();
|
||||||
|
var userCouponRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<YD_Event.Core.Entities.UserCoupon>>();
|
||||||
|
var couponRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<YD_Event.Core.Entities.Coupon>>();
|
||||||
|
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
var expirationTime = now.AddMinutes(-30);
|
var expirationTime = now.AddMinutes(-30);
|
||||||
@ -80,6 +82,18 @@ public class PaymentExpirationBackgroundService : BackgroundService
|
|||||||
|
|
||||||
if (orderNos.Any())
|
if (orderNos.Any())
|
||||||
{
|
{
|
||||||
|
// 获取这些订单并释放优惠券
|
||||||
|
var orders = await orderRepository.AsQueryable()
|
||||||
|
.Where(x => orderNos.Contains(x.OrderNo) && x.CouponId.HasValue)
|
||||||
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
await ReleaseCouponAsync(order.CouponId.Value, order.CreatedUserId, order.Id,
|
||||||
|
userCouponRepository, couponRepository, stoppingToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新订单状态
|
||||||
await orderRepository.UpdateAsync(x => new YD_Event.Core.Entities.Order
|
await orderRepository.UpdateAsync(x => new YD_Event.Core.Entities.Order
|
||||||
{
|
{
|
||||||
OrderStatus = OrderStatus.Cancelled,
|
OrderStatus = OrderStatus.Cancelled,
|
||||||
@ -96,6 +110,46 @@ public class PaymentExpirationBackgroundService : BackgroundService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放优惠券,恢复为未使用状态
|
||||||
|
/// </summary>
|
||||||
|
private async Task ReleaseCouponAsync(long couponId, long userId, long orderId,
|
||||||
|
ISqlSugarRepository<YD_Event.Core.Entities.UserCoupon> userCouponRepository,
|
||||||
|
ISqlSugarRepository<YD_Event.Core.Entities.Coupon> couponRepository,
|
||||||
|
CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
var userCoupon = await userCouponRepository.GetFirstAsync(uc =>
|
||||||
|
uc.CouponId == couponId &&
|
||||||
|
uc.UserId == userId &&
|
||||||
|
uc.OrderId == orderId &&
|
||||||
|
!uc.DeleteMark, stoppingToken);
|
||||||
|
|
||||||
|
if (userCoupon != null && userCoupon.Status == 1)
|
||||||
|
{
|
||||||
|
userCoupon.Status = 0;
|
||||||
|
userCoupon.UseTime = null;
|
||||||
|
userCoupon.OrderId = null;
|
||||||
|
await userCouponRepository.UpdateAsync(userCoupon, stoppingToken);
|
||||||
|
|
||||||
|
// 减少优惠券的已使用数量
|
||||||
|
var coupon = await couponRepository.GetByIdAsync(couponId, stoppingToken);
|
||||||
|
if (coupon != null && coupon.UsedQuantity > 0)
|
||||||
|
{
|
||||||
|
coupon.UsedQuantity--;
|
||||||
|
await couponRepository.UpdateAsync(coupon, stoppingToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation($"优惠券已释放,用户优惠券ID={userCoupon.Id},订单ID={orderId}");
|
||||||
|
}
|
||||||
|
else if (userCoupon != null && userCoupon.Status == 0)
|
||||||
|
{
|
||||||
|
// 如果优惠券还没有被标记为已使用,只需要清空OrderId
|
||||||
|
userCoupon.OrderId = null;
|
||||||
|
await userCouponRepository.UpdateAsync(userCoupon, stoppingToken);
|
||||||
|
_logger.LogInformation($"优惠券OrderId已清空,用户优惠券ID={userCoupon.Id},订单ID={orderId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理微信未支付订单:定期检查并更新订单状态
|
/// 处理微信未支付订单:定期检查并更新订单状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -182,12 +236,16 @@ public class PaymentExpirationBackgroundService : BackgroundService
|
|||||||
}
|
}
|
||||||
else if (queryResult.TradeState == "CLOSED" || queryResult.TradeState == "REVOKED")
|
else if (queryResult.TradeState == "CLOSED" || queryResult.TradeState == "REVOKED")
|
||||||
{
|
{
|
||||||
|
var userCouponRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<YD_Event.Core.Entities.UserCoupon>>();
|
||||||
|
var couponRepository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<YD_Event.Core.Entities.Coupon>>();
|
||||||
await ProcessWeChatPaymentClosedAsync(
|
await ProcessWeChatPaymentClosedAsync(
|
||||||
registration,
|
registration,
|
||||||
order,
|
order,
|
||||||
now,
|
now,
|
||||||
registrationRepository,
|
registrationRepository,
|
||||||
orderRepository,
|
orderRepository,
|
||||||
|
userCouponRepository,
|
||||||
|
couponRepository,
|
||||||
stoppingToken);
|
stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,6 +335,8 @@ public class PaymentExpirationBackgroundService : BackgroundService
|
|||||||
DateTime now,
|
DateTime now,
|
||||||
ISqlSugarRepository<EventRegistration> registrationRepository,
|
ISqlSugarRepository<EventRegistration> registrationRepository,
|
||||||
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
||||||
|
ISqlSugarRepository<YD_Event.Core.Entities.UserCoupon> userCouponRepository,
|
||||||
|
ISqlSugarRepository<YD_Event.Core.Entities.Coupon> couponRepository,
|
||||||
CancellationToken stoppingToken)
|
CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("订单 {OrderNo} 支付已关闭,更新订单状态为已取消", registration.OrderNo);
|
_logger.LogInformation("订单 {OrderNo} 支付已关闭,更新订单状态为已取消", registration.OrderNo);
|
||||||
@ -289,6 +349,13 @@ public class PaymentExpirationBackgroundService : BackgroundService
|
|||||||
order.UpdatedTime = now;
|
order.UpdatedTime = now;
|
||||||
await orderRepository.UpdateAsync(order);
|
await orderRepository.UpdateAsync(order);
|
||||||
|
|
||||||
|
// 释放优惠券
|
||||||
|
if (order.CouponId.HasValue)
|
||||||
|
{
|
||||||
|
await ReleaseCouponAsync(order.CouponId.Value, order.CreatedUserId, order.Id,
|
||||||
|
userCouponRepository, couponRepository, stoppingToken);
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation("订单 {OrderNo} 状态更新为已取消成功", registration.OrderNo);
|
_logger.LogInformation("订单 {OrderNo} 状态更新为已取消成功", registration.OrderNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1728,6 +1728,34 @@ public class UnifiedRegistrationService : ITransient
|
|||||||
order.UpdatedTime = DateTime.Now;
|
order.UpdatedTime = DateTime.Now;
|
||||||
await _orderRepository.UpdateAsync(order);
|
await _orderRepository.UpdateAsync(order);
|
||||||
_logger.LogInformation($"Order 记录 {order.OrderNo} 状态已更新为已支付");
|
_logger.LogInformation($"Order 记录 {order.OrderNo} 状态已更新为已支付");
|
||||||
|
|
||||||
|
// 支付成功后,标记优惠券为已使用
|
||||||
|
if (order.CouponId.HasValue)
|
||||||
|
{
|
||||||
|
var userCoupon = await _userCouponRepository.GetFirstAsync(uc =>
|
||||||
|
uc.CouponId == order.CouponId.Value &&
|
||||||
|
uc.UserId == order.CreatedUserId &&
|
||||||
|
uc.Status == 0 &&
|
||||||
|
!uc.DeleteMark);
|
||||||
|
|
||||||
|
if (userCoupon != null)
|
||||||
|
{
|
||||||
|
userCoupon.Status = 1;
|
||||||
|
userCoupon.UseTime = successTime ?? DateTime.Now;
|
||||||
|
userCoupon.OrderId = order.Id;
|
||||||
|
await _userCouponRepository.UpdateAsync(userCoupon);
|
||||||
|
|
||||||
|
// 更新优惠券的已使用数量
|
||||||
|
var coupon = await _couponRepository.GetByIdAsync(order.CouponId.Value);
|
||||||
|
if (coupon != null)
|
||||||
|
{
|
||||||
|
coupon.UsedQuantity++;
|
||||||
|
await _couponRepository.UpdateAsync(coupon);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation($"优惠券已标记为已使用,用户优惠券ID={userCoupon.Id},订单ID={order.Id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -508,11 +508,6 @@
|
|||||||
手机号
|
手机号
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:YD_Event.Application.Auth.Dtos.RegisterInput.Account">
|
|
||||||
<summary>
|
|
||||||
账号
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:YD_Event.Application.Auth.Dtos.RegisterInput.Password">
|
<member name="P:YD_Event.Application.Auth.Dtos.RegisterInput.Password">
|
||||||
<summary>
|
<summary>
|
||||||
密码
|
密码
|
||||||
@ -1007,6 +1002,11 @@
|
|||||||
生成显示名称
|
生成显示名称
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:YD_Event.Application.Certificate.CertificateTemplateService.GetTemplateList">
|
||||||
|
<summary>
|
||||||
|
获取证书模板列表(用于选择)
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:YD_Event.Application.Certificate.CertificateTemplateService.GetCurrentUserUserId">
|
<member name="M:YD_Event.Application.Certificate.CertificateTemplateService.GetCurrentUserUserId">
|
||||||
<summary>
|
<summary>
|
||||||
获取当前用户ID
|
获取当前用户ID
|
||||||
@ -1452,6 +1452,11 @@
|
|||||||
创建时间
|
创建时间
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Certificate.Dtos.CertificateTemplateSimpleOutput.DisplayName">
|
||||||
|
<summary>
|
||||||
|
显示名称
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="T:YD_Event.Application.Certificate.Dtos.DeleteCertificateInput">
|
<member name="T:YD_Event.Application.Certificate.Dtos.DeleteCertificateInput">
|
||||||
<summary>
|
<summary>
|
||||||
删除证书输入参数
|
删除证书输入参数
|
||||||
@ -4052,9 +4057,14 @@
|
|||||||
排序索引
|
排序索引
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Event.Dtos.ArrangementProjectListOutput.TotalDurationSeconds">
|
||||||
|
<summary>
|
||||||
|
总时长(秒)
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="P:YD_Event.Application.Event.Dtos.ArrangementProjectListOutput.TotalDurationMinutes">
|
<member name="P:YD_Event.Application.Event.Dtos.ArrangementProjectListOutput.TotalDurationMinutes">
|
||||||
<summary>
|
<summary>
|
||||||
总时长(分钟)
|
总时长(分钟,向上取整)
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:YD_Event.Application.Event.Dtos.ArrangementGroupDto">
|
<member name="T:YD_Event.Application.Event.Dtos.ArrangementGroupDto">
|
||||||
@ -5752,6 +5762,11 @@
|
|||||||
性别
|
性别
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Event.Dtos.CoachLeaderListOutput.BirthDate">
|
||||||
|
<summary>
|
||||||
|
出生日期
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="P:YD_Event.Application.Event.Dtos.CoachLeaderListOutput.IdCardNumber">
|
<member name="P:YD_Event.Application.Event.Dtos.CoachLeaderListOutput.IdCardNumber">
|
||||||
<summary>
|
<summary>
|
||||||
身份证号
|
身份证号
|
||||||
@ -7687,6 +7702,11 @@
|
|||||||
支付超时处理后台服务
|
支付超时处理后台服务
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:YD_Event.Application.Event.PaymentExpirationBackgroundService.ReleaseCouponAsync(System.Int64,System.Int64,System.Int64,SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.UserCoupon},SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.Coupon},System.Threading.CancellationToken)">
|
||||||
|
<summary>
|
||||||
|
释放优惠券,恢复为未使用状态
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:YD_Event.Application.Event.PaymentExpirationBackgroundService.ProcessUnpaidWeChatOrdersAsync(System.Threading.CancellationToken)">
|
<member name="M:YD_Event.Application.Event.PaymentExpirationBackgroundService.ProcessUnpaidWeChatOrdersAsync(System.Threading.CancellationToken)">
|
||||||
<summary>
|
<summary>
|
||||||
处理微信未支付订单:定期检查并更新订单状态
|
处理微信未支付订单:定期检查并更新订单状态
|
||||||
@ -7697,7 +7717,7 @@
|
|||||||
处理微信支付成功的订单
|
处理微信支付成功的订单
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:YD_Event.Application.Event.PaymentExpirationBackgroundService.ProcessWeChatPaymentClosedAsync(YD_Event.Core.Entities.EventRegistration,YD_Event.Core.Entities.Order,System.DateTime,SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.EventRegistration},SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.Order},System.Threading.CancellationToken)">
|
<member name="M:YD_Event.Application.Event.PaymentExpirationBackgroundService.ProcessWeChatPaymentClosedAsync(YD_Event.Core.Entities.EventRegistration,YD_Event.Core.Entities.Order,System.DateTime,SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.EventRegistration},SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.Order},SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.UserCoupon},SqlSugar.ISqlSugarRepository{YD_Event.Core.Entities.Coupon},System.Threading.CancellationToken)">
|
||||||
<summary>
|
<summary>
|
||||||
处理微信支付关闭的订单
|
处理微信支付关闭的订单
|
||||||
</summary>
|
</summary>
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
"DbType": "SqlServer", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access、OpenGauss、QuestDB、HG、ClickHouse、GBase、Odbc、Custom
|
"DbType": "SqlServer", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access、OpenGauss、QuestDB、HG、ClickHouse、GBase、Odbc、Custom
|
||||||
"ConnectionString": "Data Source=rm-uf64b8oxdt19pfyxfao.sqlserver.rds.aliyuncs.com;Database=EventServerDB_QA;User ID=qa;Password=Admin@123;Connect Timeout=500;Encrypt=True;TrustServerCertificate=True;",
|
"ConnectionString": "Data Source=rm-uf64b8oxdt19pfyxfao.sqlserver.rds.aliyuncs.com;Database=EventServerDB_QA;User ID=qa;Password=Admin@123;Connect Timeout=500;Encrypt=True;TrustServerCertificate=True;",
|
||||||
"IsAutoCloseConnection": true,
|
"IsAutoCloseConnection": true,
|
||||||
"EnableInitDb": true // 启用数据库以及数据表初始化(首次运行项目时开启可以自动创建数据库和初始化项目运行的基础数据)
|
"EnableInitDb": false // 启用数据库以及数据表初始化(首次运行项目时开启可以自动创建数据库和初始化项目运行的基础数据)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user