diff --git a/YD_Event.Application/Auth/AuthService.cs b/YD_Event.Application/Auth/AuthService.cs index fe19d5b..1cc5825 100644 --- a/YD_Event.Application/Auth/AuthService.cs +++ b/YD_Event.Application/Auth/AuthService.cs @@ -1,5 +1,7 @@ using YD_Event.Application.Auth.Dtos; +using YD_Event.Core.Options; using YD_Event.Core.Enum; +using Microsoft.Extensions.Options; namespace YD_Event.Application.Auth; /// @@ -14,13 +16,15 @@ public class AuthService : IDynamicApiController private readonly IIdGenerator _idGenerator; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IOptionsMonitor _roleOptionsMonitor; public AuthService(ISqlSugarRepository sysUseRepository, ISqlSugarRepository sysUserRoleRepository, ICaptcha captcha, IIdGenerator idGenerator, IEasyCachingProvider easyCachingProvider, - IHttpContextAccessor httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + IOptionsMonitor roleOptionsMonitor) { _sysUseRepository = sysUseRepository; _sysUserRoleRepository = sysUserRoleRepository; @@ -28,6 +32,7 @@ public class AuthService : IDynamicApiController _idGenerator = idGenerator; _easyCachingProvider = easyCachingProvider; _httpContextAccessor = httpContextAccessor; + _roleOptionsMonitor = roleOptionsMonitor; } /// @@ -141,12 +146,12 @@ public class AuthService : IDynamicApiController await _sysUseRepository.InsertAsync(user); - // 为新用户分配普通用户角色 (RoleId = 1001) + // 为新用户分配单位用户角色 var userRole = new SysUserRole { Id = _idGenerator.NewLong(), UserId = user.Id, - RoleId = 1001, + RoleId = _roleOptionsMonitor.CurrentValue.UnitUserRoleId, CreatedTime = DateTime.Now, CreatedUserId = 0, DeleteMark = false diff --git a/YD_Event.Application/Event/PaymentExpirationBackgroundService.cs b/YD_Event.Application/Event/PaymentExpirationBackgroundService.cs index 25131e0..6fe2f85 100644 --- a/YD_Event.Application/Event/PaymentExpirationBackgroundService.cs +++ b/YD_Event.Application/Event/PaymentExpirationBackgroundService.cs @@ -23,7 +23,7 @@ public class PaymentExpirationBackgroundService : BackgroundService { _serviceProvider = serviceProvider; _logger = logger; - _timer = new PeriodicTimer(TimeSpan.FromMinutes(0.2)); + _timer = new PeriodicTimer(TimeSpan.FromMinutes(1)); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs index 4637858..82fd67a 100644 --- a/YD_Event.Application/Event/UnifiedRegistrationService.cs +++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs @@ -4085,15 +4085,14 @@ public class UnifiedRegistrationService : ITransient _logger.LogInformation("确认微信订单已支付成功,报名ID={RegistrationId}, 订单号={OrderNo}", registrationId, registration.OrderNo); - // 1. 更新报名状态为"已退款"(状态3) - registration.PaymentStatus = 3; + // 1. 保持原订单状态不变,仅添加备注 + registration.RegistrationRemark = "报名失败,已全额退款"; registration.UpdatedTime = now; registration.UpdatedUserId = operatorUserId; - registration.RegistrationRemark = "报名失败,已全额退款"; await _registrationRepository.UpdateAsync(registration); - // 2. 更新订单状态为"报名失败" - order.OrderStatus = OrderStatus.RegistrationFailed; + // 2. 更新原订单状态为"报名失败" + order.Remark = $"{eventInfo.EventName} - 报名失败"; order.UpdatedTime = now; order.UpdatedUserId = operatorUserId; await _orderRepository.UpdateAsync(order); @@ -4118,7 +4117,7 @@ public class UnifiedRegistrationService : ITransient OriginalAmount = order.ActualAmount, ActualAmount = order.ActualAmount, DiscountAmount = 0, - OrderStatus = OrderStatus.Refunded, + OrderStatus = OrderStatus.RegistrationFailed, PaymentTime = now, Remark = refundReason, CreatedTime = now, diff --git a/YD_Event.Application/Order/Dtos/OrderListOutput.cs b/YD_Event.Application/Order/Dtos/OrderListOutput.cs index 026d0bb..765d9c4 100644 --- a/YD_Event.Application/Order/Dtos/OrderListOutput.cs +++ b/YD_Event.Application/Order/Dtos/OrderListOutput.cs @@ -141,6 +141,11 @@ public class OrderListOutput /// 收款人银行卡号(线下支付退款时使用,仅导出时显示) /// public string? PayeeBankAccount { get; set; } + + /// + /// 备注信息 + /// + public string Remark { get; set; } } /// @@ -277,6 +282,11 @@ public class OrderDetailOutput /// 是否为退款订单 /// public bool IsRefundOrder { get; set; } + + /// + /// 备注信息 + /// + public string Remark { get; set; } } /// diff --git a/YD_Event.Application/Order/OrderService.cs b/YD_Event.Application/Order/OrderService.cs index 00c6545..42103dc 100644 --- a/YD_Event.Application/Order/OrderService.cs +++ b/YD_Event.Application/Order/OrderService.cs @@ -176,12 +176,13 @@ public class OrderService : BaseService, ITransien .LeftJoin((o, e, m) => o.MerchantId == m.Id) .LeftJoin((o, e, m, r) => o.RegistrationId == r.Id) .LeftJoin((o, e, m, r, ro) => o.RelatedOrderId == ro.Id) // 自连接获取关联订单编号 - // 默认只显示已完成支付流程的订单(已支付、已发货、已完成、已退款) + // 默认只显示已完成支付流程的订单(已支付、已发货、已完成、已退款、报名失败) .WhereIF(!input.OrderStatus.HasValue, (o, e, m, r, ro) => o.OrderStatus == OrderStatus.Paid || o.OrderStatus == OrderStatus.Shipped || o.OrderStatus == OrderStatus.Completed || - o.OrderStatus == OrderStatus.Refunded) + o.OrderStatus == OrderStatus.Refunded || + o.OrderStatus == OrderStatus.RegistrationFailed) .WhereIF(!string.IsNullOrWhiteSpace(input.OrderNo), (o, e, m, r, ro) => o.OrderNo.Contains(input.OrderNo)) .WhereIF(!string.IsNullOrWhiteSpace(input.EventName), (o, e, m, r, ro) => e.EventName.Contains(input.EventName)) .WhereIF(input.OrderType.HasValue, (o, e, m, r, ro) => o.OrderType == input.OrderType) @@ -227,7 +228,8 @@ public class OrderService : BaseService, ITransien o.OrderStatus == OrderStatus.Cancelled ? "已取消" : o.OrderStatus == OrderStatus.Refunded ? "已退款" : o.OrderStatus == OrderStatus.Shipped ? "已发货" : - o.OrderStatus == OrderStatus.Completed ? "已完成" : "", + o.OrderStatus == OrderStatus.Completed ? "已完成" : + o.OrderStatus == OrderStatus.RegistrationFailed ? "报名失败" : "", CreatedTime = o.CreatedTime, PaymentTime = o.PaymentTime, RefundAmount = o.OrderType == 2 ? o.ActualAmount : 0, // 如果是退款订单,ActualAmount即为退款金额 @@ -236,7 +238,8 @@ public class OrderService : BaseService, ITransien IsRefundOrder = o.OrderType == 2, PayeeName = o.PayeeName, PayeeBankName = o.PayeeBankName, - PayeeBankAccount = o.PayeeBankAccount + PayeeBankAccount = o.PayeeBankAccount, + Remark = o.Remark }) .ToPagedListAsync(input); @@ -369,7 +372,8 @@ public class OrderService : BaseService, ITransien orderInfo.Order.OrderStatus == OrderStatus.Cancelled ? "已取消" : orderInfo.Order.OrderStatus == OrderStatus.Refunded ? "已退款" : orderInfo.Order.OrderStatus == OrderStatus.Shipped ? "已发货" : - orderInfo.Order.OrderStatus == OrderStatus.Completed ? "已完成" : "", + orderInfo.Order.OrderStatus == OrderStatus.Completed ? "已完成" : + orderInfo.Order.OrderStatus == OrderStatus.RegistrationFailed ? "取消报名" : "", EventName = orderInfo.EventName, ChargeMode = orderInfo.ChargeMode, ProjectNames = projectNames, @@ -392,7 +396,8 @@ public class OrderService : BaseService, ITransien ContactName = orderInfo.Registration?.ContactName, ContactPhone = orderInfo.Registration?.ContactPhone, RelatedOrderActualAmount = orderInfo.Order.OrderType == 2 ? orderInfo.RelatedOrderActualAmount : null, - IsRefundOrder = orderInfo.Order.OrderType == 2 + IsRefundOrder = orderInfo.Order.OrderType == 2, + Remark = orderInfo.Order.Remark }; } @@ -612,7 +617,8 @@ public class OrderService : BaseService, ITransien o.OrderStatus == OrderStatus.Cancelled ? "已取消" : o.OrderStatus == OrderStatus.Refunded ? "已退款" : o.OrderStatus == OrderStatus.Shipped ? "已发货" : - o.OrderStatus == OrderStatus.Completed ? "已完成" : "", + o.OrderStatus == OrderStatus.Completed ? "已完成" : + o.OrderStatus == OrderStatus.RegistrationFailed ? "报名失败" : "", CreatedTime = o.CreatedTime, RelatedOrderActualAmount = o.OrderType == 2 ? ro.ActualAmount : null, IsRefundOrder = o.OrderType == 2, diff --git a/YD_Event.Application/Role/SysRoleService.cs b/YD_Event.Application/Role/SysRoleService.cs index 24f1a08..bd69eed 100644 --- a/YD_Event.Application/Role/SysRoleService.cs +++ b/YD_Event.Application/Role/SysRoleService.cs @@ -1,5 +1,8 @@ +using YD_Event.Application.Auth; using YD_Event.Application.Role.Dtos; +using YD_Event.Core.Options; using YD_Event.Core.Shared; +using Microsoft.Extensions.Options; namespace YD_Event.Application.Role; @@ -10,18 +13,27 @@ public class SysRoleService : BaseService, ITransient { private readonly ISqlSugarRepository _sysRoleRepository; private readonly ISqlSugarRepository _sysRoleMenuRepository; + private readonly ISqlSugarRepository _sysUserRoleRepository; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IIdGenerator _idGenerator; + private readonly AuthManager _authManager; + private readonly IOptionsMonitor _roleOptionsMonitor; public SysRoleService(ISqlSugarRepository sysRoleRepository, ISqlSugarRepository sysRoleMenuRepository, + ISqlSugarRepository sysUserRoleRepository, IEasyCachingProvider easyCachingProvider, - IIdGenerator idGenerator) : base(sysRoleRepository) + IIdGenerator idGenerator, + AuthManager authManager, + IOptionsMonitor roleOptionsMonitor) : base(sysRoleRepository) { _sysRoleRepository = sysRoleRepository; _sysRoleMenuRepository = sysRoleMenuRepository; + _sysUserRoleRepository = sysUserRoleRepository; _easyCachingProvider = easyCachingProvider; _idGenerator = idGenerator; + _authManager = authManager; + _roleOptionsMonitor = roleOptionsMonitor; } /// @@ -33,8 +45,17 @@ public class SysRoleService : BaseService, ITransient [HttpGet("page")] public async Task> Page([FromQuery] SysRoleQueryInput dto) { - return await _sysRoleRepository.AsQueryable() - .WhereIF(!string.IsNullOrWhiteSpace(dto.Name), x => x.Name.Contains(dto.Name)) + var isAdmin = await IsCurrentUserAdminAsync(); + var query = _sysRoleRepository.AsQueryable() + .WhereIF(!string.IsNullOrWhiteSpace(dto.Name), x => x.Name.Contains(dto.Name)); + + if (!isAdmin) + { + // 非管理员用户,过滤掉管理员和单位用户角色 + query = query.Where(x => x.Id != _roleOptionsMonitor.CurrentValue.AdminRoleId && x.Id != _roleOptionsMonitor.CurrentValue.UnitUserRoleId); + } + + return await query .OrderBy(x => x.Sort) .OrderByDescending(x => x.Id) .Select(x => new SysRolePageOutput @@ -49,6 +70,22 @@ public class SysRoleService : BaseService, ITransient }).ToPagedListAsync(dto); } + /// + /// 检查当前登录用户是否是管理员 + /// + /// + private async Task IsCurrentUserAdminAsync() + { + var userId = _authManager.UserId; + // 首先检查是否是超级管理员(用户ID为1000) + if (_authManager.IsSuperAdmin) + { + return true; + } + // 检查用户是否拥有管理员角色 + return await _sysUserRoleRepository.IsAnyAsync(x => x.UserId == userId && x.RoleId == _roleOptionsMonitor.CurrentValue.AdminRoleId); + } + /// /// 添加角色 /// @@ -205,7 +242,16 @@ public class SysRoleService : BaseService, ITransient [HttpGet("select")] public async Task> RoleSelect() { - return await _sysRoleRepository.AsQueryable().Where(x => x.Status == AvailabilityStatus.Enable) + var isAdmin = await IsCurrentUserAdminAsync(); + var query = _sysRoleRepository.AsQueryable().Where(x => x.Status == AvailabilityStatus.Enable); + + if (!isAdmin) + { + // 非管理员用户,过滤掉管理员和单位用户角色 + query = query.Where(x => x.Id != _roleOptionsMonitor.CurrentValue.AdminRoleId && x.Id != _roleOptionsMonitor.CurrentValue.UnitUserRoleId); + } + + return await query .OrderBy(x => x.Sort) .OrderBy(x => x.Id) .Select(x => new SelectOutput() diff --git a/YD_Event.Application/User/SysUserService.cs b/YD_Event.Application/User/SysUserService.cs index d251316..883f314 100644 --- a/YD_Event.Application/User/SysUserService.cs +++ b/YD_Event.Application/User/SysUserService.cs @@ -1,7 +1,9 @@ using YD_Event.Application.Auth; using YD_Event.Application.Menu; using YD_Event.Application.User.Dtos; +using YD_Event.Core.Options; using YD_Event.Core.Shared; +using Microsoft.Extensions.Options; namespace YD_Event.Application.User; @@ -18,6 +20,7 @@ public class SysUserService : BaseService, ITransient private readonly AuthManager _authManager; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IIdGenerator _idGenerator; + private readonly IOptionsMonitor _roleOptionsMonitor; public SysUserService(ISqlSugarRepository repository, ISqlSugarRepository userRoleRepository, @@ -26,7 +29,8 @@ public class SysUserService : BaseService, ITransient SysMenuService sysMenuService, AuthManager authManager, IEasyCachingProvider easyCachingProvider, - IIdGenerator idGenerator) : base(repository) + IIdGenerator idGenerator, + IOptionsMonitor roleOptionsMonitor) : base(repository) { _repository = repository; _userRoleRepository = userRoleRepository; @@ -36,6 +40,23 @@ public class SysUserService : BaseService, ITransient _authManager = authManager; _easyCachingProvider = easyCachingProvider; _idGenerator = idGenerator; + _roleOptionsMonitor = roleOptionsMonitor; + } + + /// + /// 检查当前登录用户是否是管理员 + /// + /// + private async Task IsCurrentUserAdminAsync() + { + var userId = _authManager.UserId; + // 首先检查是否是超级管理员(用户ID为1) + if (_authManager.IsSuperAdmin) + { + return true; + } + // 检查用户是否拥有管理员角色 + return await _userRoleRepository.IsAnyAsync(x => x.UserId == userId && x.RoleId == _roleOptionsMonitor.CurrentValue.AdminRoleId); } /// @@ -47,6 +68,8 @@ public class SysUserService : BaseService, ITransient [HttpGet("page")] public async Task> Page([FromQuery] QuerySysUserInput dto) { + var isAdmin = await IsCurrentUserAdminAsync(); + // 构造基本查询 var query = _repository.AsQueryable() .Where(x => x.Id > 1) // 排除超级管理员或特定ID @@ -60,6 +83,21 @@ public class SysUserService : BaseService, ITransient { query = query.InnerJoin((u, ur) => u.Id == ur.UserId && ur.RoleId == dto.RoleId.Value); } + else if (!isAdmin) + { + // 非管理员用户,排除掉拥有管理员和单位用户角色的用户 + var excludedRoleIds = new List { _roleOptionsMonitor.CurrentValue.AdminRoleId, _roleOptionsMonitor.CurrentValue.UnitUserRoleId }; + + // 使用子查询来获取那些拥有被排除角色的用户ID + var excludedUserIds = await _userRoleRepository.AsQueryable() + .Where(x => excludedRoleIds.Contains(x.RoleId)) + .Select(x => x.UserId) + .Distinct() + .ToListAsync(); + + // 排除这些用户 + query = query.Where(x => !excludedUserIds.Contains(x.Id)); + } var entityList = await query.ToPagedListAsync(dto); diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml index ed5268f..b69bf59 100644 --- a/YD_Event.Application/YD_Event.Application.xml +++ b/YD_Event.Application/YD_Event.Application.xml @@ -3452,6 +3452,11 @@ 支付方式:1-线上,2-线下 + + + 收费模式:0-免费,1-定价(赛事),2-单价(赛事),3-单价(项目) + + 项目列表 @@ -7203,6 +7208,11 @@ 赛事状态 + + + 赛事状态枚举 + + 赛事类型 (int: 0-线上, 1-线下) @@ -7218,6 +7228,16 @@ 积分规则名称 + + + 积分规则详情 + + + + + 退款规则详情 + + 报名开始时间 @@ -8785,6 +8805,11 @@ 处理微信退款失败的订单 + + + 处理已取消但实际支付成功的报名(报名失败退款) + + 统一报名处理服务 @@ -9121,6 +9146,14 @@ 退款订单输入 操作人用户ID + + + 处理已取消但实际支付成功的退款(报名失败退款) + + 报名ID + 操作人用户ID + 是否成功 + 用户端参赛记录管理 @@ -10734,6 +10767,11 @@ 收款人银行卡号(线下支付退款时使用,仅导出时显示) + + + 备注信息 + + 订单详情输出 @@ -10869,6 +10907,11 @@ 是否为退款订单 + + + 备注信息 + + 订单项目DTO @@ -12181,6 +12224,12 @@ + + + 检查当前登录用户是否是管理员 + + + 添加角色 @@ -12742,6 +12791,12 @@ 系统用户管理 + + + 检查当前登录用户是否是管理员 + + + 系统用户分页查询 diff --git a/YD_Event.Application/applicationsettings.json b/YD_Event.Application/applicationsettings.json index 8c3cdc6..50d414e 100644 --- a/YD_Event.Application/applicationsettings.json +++ b/YD_Event.Application/applicationsettings.json @@ -163,5 +163,10 @@ "AccessKeyId": "LTAI5tLfbdZwBoA9bCZVjZkF", "AccessKeySecret": "4DmFIY77zwdp1gaRUTmBMf7WfdsNsC", "Endpoint": "ocr-api.cn-hangzhou.aliyuncs.com" + }, + // 角色配置 + "RoleSettings": { + "AdminRoleId": 1000, + "UnitUserRoleId": 1001 } } \ No newline at end of file diff --git a/YD_Event.Core/Extensions/ServiceCollectionExtension.cs b/YD_Event.Core/Extensions/ServiceCollectionExtension.cs index 1d360a8..29dda0a 100644 --- a/YD_Event.Core/Extensions/ServiceCollectionExtension.cs +++ b/YD_Event.Core/Extensions/ServiceCollectionExtension.cs @@ -1,4 +1,4 @@ -using YD_Event.Core.Options; +using YD_Event.Core.Options; namespace Easy.Core; @@ -18,6 +18,7 @@ public static class ServiceCollectionExtension service.AddConfigurableOptions(); service.AddConfigurableOptions(); service.AddConfigurableOptions(); + service.AddConfigurableOptions(); return service; } } \ No newline at end of file diff --git a/YD_Event.Core/InitData/sysrole.txt b/YD_Event.Core/InitData/sysrole.txt index ecfc5a6..2518985 100644 --- a/YD_Event.Core/InitData/sysrole.txt +++ b/YD_Event.Core/InitData/sysrole.txt @@ -1,7 +1,7 @@ [ { "Id": 1000, - "Name": "管理员", + "Name": "系统管理员", "Code": "admin", "Status": 0, "Sort": 1, @@ -12,11 +12,22 @@ }, { "Id": 1001, - "Name": "普通用户", + "Name": "单位用户", "Code": "user", "Status": 0, "Sort": 2, - "Remark": "普通用户", + "Remark": "单位用户", + "DeleteMark": false, + "CreatedUserId": 1000, + "CreatedTime": "2023-06-01" + }, + { + "Id": 1002, + "Name": "管理员", + "Code": "eventuser", + "Status": 0, + "Sort": 3, + "Remark": "赛事管理员", "DeleteMark": false, "CreatedUserId": 1000, "CreatedTime": "2023-06-01" diff --git a/YD_Event.Core/Options/RoleOptions.cs b/YD_Event.Core/Options/RoleOptions.cs new file mode 100644 index 0000000..dc64991 --- /dev/null +++ b/YD_Event.Core/Options/RoleOptions.cs @@ -0,0 +1,20 @@ +using Furion.ConfigurableOptions; + +namespace YD_Event.Core.Options; + +/// +/// 角色配置选项 +/// +[OptionsSettings("RoleSettings")] +public class RoleOptions : IConfigurableOptions +{ + /// + /// 系统管理员角色ID + /// + public long AdminRoleId { get; set; } + + /// + /// 单位用户角色ID + /// + public long UnitUserRoleId { get; set; } +} diff --git a/YD_Event.Core/YD_Event.Core.xml b/YD_Event.Core/YD_Event.Core.xml index 459b913..0de2e5b 100644 --- a/YD_Event.Core/YD_Event.Core.xml +++ b/YD_Event.Core/YD_Event.Core.xml @@ -4630,6 +4630,11 @@ 已完成 + + + 报名失败 + + 参赛状态枚举 @@ -5841,6 +5846,21 @@ 外网访问域名或IP + + + 角色配置选项 + + + + + 系统管理员角色ID + + + + + 单位用户角色ID + + 短信配置选项