订单管理和权限管理

This commit is contained in:
马梓彭 2026-05-14 15:27:16 +08:00
parent 6e576690d2
commit 2b262abda1
13 changed files with 242 additions and 26 deletions

View File

@ -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;
/// <summary>
@ -14,13 +16,15 @@ public class AuthService : IDynamicApiController
private readonly IIdGenerator _idGenerator;
private readonly IEasyCachingProvider _easyCachingProvider;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IOptionsMonitor<RoleOptions> _roleOptionsMonitor;
public AuthService(ISqlSugarRepository<SysUser> sysUseRepository,
ISqlSugarRepository<SysUserRole> sysUserRoleRepository,
ICaptcha captcha,
IIdGenerator idGenerator,
IEasyCachingProvider easyCachingProvider,
IHttpContextAccessor httpContextAccessor)
IHttpContextAccessor httpContextAccessor,
IOptionsMonitor<RoleOptions> roleOptionsMonitor)
{
_sysUseRepository = sysUseRepository;
_sysUserRoleRepository = sysUserRoleRepository;
@ -28,6 +32,7 @@ public class AuthService : IDynamicApiController
_idGenerator = idGenerator;
_easyCachingProvider = easyCachingProvider;
_httpContextAccessor = httpContextAccessor;
_roleOptionsMonitor = roleOptionsMonitor;
}
/// <summary>
@ -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

View File

@ -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)

View File

@ -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,

View File

@ -141,6 +141,11 @@ public class OrderListOutput
/// 收款人银行卡号(线下支付退款时使用,仅导出时显示)
/// </summary>
public string? PayeeBankAccount { get; set; }
/// <summary>
/// 备注信息
/// </summary>
public string Remark { get; set; }
}
/// <summary>
@ -277,6 +282,11 @@ public class OrderDetailOutput
/// 是否为退款订单
/// </summary>
public bool IsRefundOrder { get; set; }
/// <summary>
/// 备注信息
/// </summary>
public string Remark { get; set; }
}
/// <summary>

View File

@ -176,12 +176,13 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
.LeftJoin<YD_Event.Core.Entities.Merchant>((o, e, m) => o.MerchantId == m.Id)
.LeftJoin<EventRegistration>((o, e, m, r) => o.RegistrationId == r.Id)
.LeftJoin<YD_Event.Core.Entities.Order>((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<YD_Event.Core.Entities.Order>, 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<YD_Event.Core.Entities.Order>, 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<YD_Event.Core.Entities.Order>, 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<YD_Event.Core.Entities.Order>, 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<YD_Event.Core.Entities.Order>, 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,

View File

@ -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<SysRole>, ITransient
{
private readonly ISqlSugarRepository<SysRole> _sysRoleRepository;
private readonly ISqlSugarRepository<SysRoleMenu> _sysRoleMenuRepository;
private readonly ISqlSugarRepository<SysUserRole> _sysUserRoleRepository;
private readonly IEasyCachingProvider _easyCachingProvider;
private readonly IIdGenerator _idGenerator;
private readonly AuthManager _authManager;
private readonly IOptionsMonitor<RoleOptions> _roleOptionsMonitor;
public SysRoleService(ISqlSugarRepository<SysRole> sysRoleRepository,
ISqlSugarRepository<SysRoleMenu> sysRoleMenuRepository,
ISqlSugarRepository<SysUserRole> sysUserRoleRepository,
IEasyCachingProvider easyCachingProvider,
IIdGenerator idGenerator) : base(sysRoleRepository)
IIdGenerator idGenerator,
AuthManager authManager,
IOptionsMonitor<RoleOptions> roleOptionsMonitor) : base(sysRoleRepository)
{
_sysRoleRepository = sysRoleRepository;
_sysRoleMenuRepository = sysRoleMenuRepository;
_sysUserRoleRepository = sysUserRoleRepository;
_easyCachingProvider = easyCachingProvider;
_idGenerator = idGenerator;
_authManager = authManager;
_roleOptionsMonitor = roleOptionsMonitor;
}
/// <summary>
@ -33,8 +45,17 @@ public class SysRoleService : BaseService<SysRole>, ITransient
[HttpGet("page")]
public async Task<PageResult<SysRolePageOutput>> 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<SysRole>, ITransient
}).ToPagedListAsync(dto);
}
/// <summary>
/// 检查当前登录用户是否是管理员
/// </summary>
/// <returns></returns>
private async Task<bool> 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);
}
/// <summary>
/// 添加角色
/// </summary>
@ -205,7 +242,16 @@ public class SysRoleService : BaseService<SysRole>, ITransient
[HttpGet("select")]
public async Task<List<SelectOutput>> 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()

View File

@ -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<SysUser>, ITransient
private readonly AuthManager _authManager;
private readonly IEasyCachingProvider _easyCachingProvider;
private readonly IIdGenerator _idGenerator;
private readonly IOptionsMonitor<RoleOptions> _roleOptionsMonitor;
public SysUserService(ISqlSugarRepository<SysUser> repository,
ISqlSugarRepository<SysUserRole> userRoleRepository,
@ -26,7 +29,8 @@ public class SysUserService : BaseService<SysUser>, ITransient
SysMenuService sysMenuService,
AuthManager authManager,
IEasyCachingProvider easyCachingProvider,
IIdGenerator idGenerator) : base(repository)
IIdGenerator idGenerator,
IOptionsMonitor<RoleOptions> roleOptionsMonitor) : base(repository)
{
_repository = repository;
_userRoleRepository = userRoleRepository;
@ -36,6 +40,23 @@ public class SysUserService : BaseService<SysUser>, ITransient
_authManager = authManager;
_easyCachingProvider = easyCachingProvider;
_idGenerator = idGenerator;
_roleOptionsMonitor = roleOptionsMonitor;
}
/// <summary>
/// 检查当前登录用户是否是管理员
/// </summary>
/// <returns></returns>
private async Task<bool> 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);
}
/// <summary>
@ -47,6 +68,8 @@ public class SysUserService : BaseService<SysUser>, ITransient
[HttpGet("page")]
public async Task<PageResult<SysUserPageOutput>> 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<SysUser>, ITransient
{
query = query.InnerJoin<SysUserRole>((u, ur) => u.Id == ur.UserId && ur.RoleId == dto.RoleId.Value);
}
else if (!isAdmin)
{
// 非管理员用户,排除掉拥有管理员和单位用户角色的用户
var excludedRoleIds = new List<long> { _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);

View File

@ -3452,6 +3452,11 @@
支付方式:1-线上,2-线下
</summary>
</member>
<member name="P:YD_Event.Application.Client.Dtos.MyOrderDetailDto.ChargeMode">
<summary>
收费模式:0-免费,1-定价(赛事),2-单价(赛事),3-单价(项目)
</summary>
</member>
<member name="P:YD_Event.Application.Client.Dtos.MyOrderDetailDto.Items">
<summary>
项目列表
@ -7203,6 +7208,11 @@
赛事状态
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserEventDetailOutput.EventStatus">
<summary>
赛事状态枚举
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserEventDetailOutput.Type">
<summary>
赛事类型 (int: 0-线上, 1-线下)
@ -7218,6 +7228,16 @@
积分规则名称
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserEventDetailOutput.PointsRuleDetail">
<summary>
积分规则详情
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserEventDetailOutput.RefundRules">
<summary>
退款规则详情
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserEventDetailOutput.RegistrationStartTime">
<summary>
报名开始时间
@ -8785,6 +8805,11 @@
处理微信退款失败的订单
</summary>
</member>
<member name="M:YD_Event.Application.Event.PaymentExpirationBackgroundService.ProcessFailedRegistrationsAsync(System.Threading.CancellationToken)">
<summary>
处理已取消但实际支付成功的报名(报名失败退款)
</summary>
</member>
<member name="T:YD_Event.Application.Event.UnifiedRegistrationService">
<summary>
统一报名处理服务
@ -9121,6 +9146,14 @@
<param name="input">退款订单输入</param>
<param name="operatorUserId">操作人用户ID</param>
</member>
<member name="M:YD_Event.Application.Event.UnifiedRegistrationService.ProcessFailedRegistrationRefundAsync(System.Int64,System.Int64)">
<summary>
处理已取消但实际支付成功的退款(报名失败退款)
</summary>
<param name="registrationId">报名ID</param>
<param name="operatorUserId">操作人用户ID</param>
<returns>是否成功</returns>
</member>
<member name="T:YD_Event.Application.Event.UserEventRecordService">
<summary>
用户端参赛记录管理
@ -10734,6 +10767,11 @@
收款人银行卡号(线下支付退款时使用,仅导出时显示)
</summary>
</member>
<member name="P:YD_Event.Application.Order.Dtos.OrderListOutput.Remark">
<summary>
备注信息
</summary>
</member>
<member name="T:YD_Event.Application.Order.Dtos.OrderDetailOutput">
<summary>
订单详情输出
@ -10869,6 +10907,11 @@
是否为退款订单
</summary>
</member>
<member name="P:YD_Event.Application.Order.Dtos.OrderDetailOutput.Remark">
<summary>
备注信息
</summary>
</member>
<member name="T:YD_Event.Application.Order.Dtos.OrderItemDto">
<summary>
订单项目DTO
@ -12181,6 +12224,12 @@
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Role.SysRoleService.IsCurrentUserAdminAsync">
<summary>
检查当前登录用户是否是管理员
</summary>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Role.SysRoleService.AddRole(YD_Event.Application.Role.Dtos.AddSysRoleInput)">
<summary>
添加角色
@ -12742,6 +12791,12 @@
系统用户管理
</summary>
</member>
<member name="M:YD_Event.Application.User.SysUserService.IsCurrentUserAdminAsync">
<summary>
检查当前登录用户是否是管理员
</summary>
<returns></returns>
</member>
<member name="M:YD_Event.Application.User.SysUserService.Page(YD_Event.Application.User.Dtos.QuerySysUserInput)">
<summary>
系统用户分页查询

View File

@ -163,5 +163,10 @@
"AccessKeyId": "LTAI5tLfbdZwBoA9bCZVjZkF",
"AccessKeySecret": "4DmFIY77zwdp1gaRUTmBMf7WfdsNsC",
"Endpoint": "ocr-api.cn-hangzhou.aliyuncs.com"
},
// 角色配置
"RoleSettings": {
"AdminRoleId": 1000,
"UnitUserRoleId": 1001
}
}

View File

@ -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<AliyunOcrOptions>();
service.AddConfigurableOptions<WeChatOptions>();
service.AddConfigurableOptions<WechatTenpayClientOptions>();
service.AddConfigurableOptions<RoleOptions>();
return service;
}
}

View File

@ -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"

View File

@ -0,0 +1,20 @@
using Furion.ConfigurableOptions;
namespace YD_Event.Core.Options;
/// <summary>
/// 角色配置选项
/// </summary>
[OptionsSettings("RoleSettings")]
public class RoleOptions : IConfigurableOptions
{
/// <summary>
/// 系统管理员角色ID
/// </summary>
public long AdminRoleId { get; set; }
/// <summary>
/// 单位用户角色ID
/// </summary>
public long UnitUserRoleId { get; set; }
}

View File

@ -4630,6 +4630,11 @@
已完成
</summary>
</member>
<member name="F:YD_Event.Core.Enum.OrderStatus.RegistrationFailed">
<summary>
报名失败
</summary>
</member>
<member name="T:YD_Event.Core.Enum.ParticipationStatus">
<summary>
参赛状态枚举
@ -5841,6 +5846,21 @@
外网访问域名或IP
</summary>
</member>
<member name="T:YD_Event.Core.Options.RoleOptions">
<summary>
角色配置选项
</summary>
</member>
<member name="P:YD_Event.Core.Options.RoleOptions.AdminRoleId">
<summary>
系统管理员角色ID
</summary>
</member>
<member name="P:YD_Event.Core.Options.RoleOptions.UnitUserRoleId">
<summary>
单位用户角色ID
</summary>
</member>
<member name="T:YD_Event.Core.Options.SmsOptions">
<summary>
短信配置选项