From b2dafc2ad239703372e88aacc640d7a16846f218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E6=A2=93=E5=BD=AD?= Date: Tue, 7 Apr 2026 13:10:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E7=99=BB=E5=BD=95=E6=B3=A8?= =?UTF-8?q?=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YD_Event.Application/Auth/AuthService.cs | 148 ++++++++++++++++- YD_Event.Application/Auth/Dtos/AuthDtos.cs | 108 +++++++++++++ .../Client/Dtos/EventClientDtos.cs | 26 +++ .../Client/EventClientController.cs | 97 +++++++---- .../Points/Dtos/AddPointsRuleInput.cs | 2 +- .../User/Dtos/UpdateSysUserInput.cs | 39 ++++- YD_Event.Application/User/SysUserService.cs | 12 +- YD_Event.Application/YD_Event.Application.xml | 153 +++++++++++++++++- YD_Event.Core/Entities/PointsRule.cs | 2 +- 9 files changed, 542 insertions(+), 45 deletions(-) create mode 100644 YD_Event.Application/Auth/Dtos/AuthDtos.cs diff --git a/YD_Event.Application/Auth/AuthService.cs b/YD_Event.Application/Auth/AuthService.cs index 281a47d..f8db809 100644 --- a/YD_Event.Application/Auth/AuthService.cs +++ b/YD_Event.Application/Auth/AuthService.cs @@ -1,4 +1,5 @@ using YD_Event.Application.Auth.Dtos; +using YD_Event.Core.Enum; namespace YD_Event.Application.Auth; /// @@ -8,18 +9,21 @@ namespace YD_Event.Application.Auth; public class AuthService : IDynamicApiController { private readonly ISqlSugarRepository _sysUseRepository; + private readonly ISqlSugarRepository _sysUserRoleRepository; private readonly ICaptcha _captcha; private readonly IIdGenerator _idGenerator; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IHttpContextAccessor _httpContextAccessor; public AuthService(ISqlSugarRepository sysUseRepository, + ISqlSugarRepository sysUserRoleRepository, ICaptcha captcha, IIdGenerator idGenerator, IEasyCachingProvider easyCachingProvider, IHttpContextAccessor httpContextAccessor) { _sysUseRepository = sysUseRepository; + _sysUserRoleRepository = sysUserRoleRepository; _captcha = captcha; _idGenerator = idGenerator; _easyCachingProvider = easyCachingProvider; @@ -95,5 +99,147 @@ public class AuthService : IDynamicApiController return new FileStreamResult(stream, "image/gif"); } + /// + /// 用户注册 + /// + /// + /// + [DisplayName("用户注册")] + [HttpPost] + [UnitOfWork] + public async Task Register(RegisterInput dto) + { + var codeCacheKey = $"sms:code:{dto.Phone}"; + var cachedCode = await _easyCachingProvider.GetAsync(codeCacheKey); + + if (!cachedCode.HasValue || cachedCode.Value != dto.Code) + { + throw Oops.Bah("验证码错误或已过期"); + } -} \ No newline at end of file + 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); + if (existPhone) + { + throw Oops.Bah("该手机号已被注册"); + } + + var user = new SysUser + { + Id = _idGenerator.NewLong(), + Account = dto.Account, + Name = dto.Account, + Mobile = dto.Phone, + Gender = Gender.Unknown, + OrgId = 0, + Status = AvailabilityStatus.Enable, + DeleteMark = false, + CreatedTime = DateTime.Now, + CreatedUserId = 0 + }; + + user.Password = MD5Encryption.Encrypt($"{_idGenerator.Encode(user.Id)}{dto.Password}"); + + await _sysUseRepository.InsertAsync(user); + + // 为新用户分配普通用户角色 (RoleId = 1001) + var userRole = new SysUserRole + { + Id = _idGenerator.NewLong(), + UserId = user.Id, + RoleId = 1001, + CreatedTime = DateTime.Now, + CreatedUserId = 0, + DeleteMark = false + }; + await _sysUserRoleRepository.InsertAsync(userRole); + + await _easyCachingProvider.RemoveAsync(codeCacheKey); + } + + /// + /// 短信验证码登录 + /// + /// + /// + [DisplayName("短信验证码登录")] + [HttpPost] + public async Task SmsSignIn(SmsLoginInput dto) + { + var codeCacheKey = $"sms:code:{dto.Phone}"; + var cachedCode = await _easyCachingProvider.GetAsync(codeCacheKey); + + if (!cachedCode.HasValue || cachedCode.Value != dto.Code) + { + throw Oops.Bah("验证码错误或已过期"); + } + + SysUser user = await _sysUseRepository.GetFirstAsync(x => x.Mobile == dto.Phone); + if (user == null) + { + throw Oops.Bah("该手机号尚未注册"); + } + + var context = _httpContextAccessor.HttpContext; + if (user.Status == AvailabilityStatus.Disable || (user.LockExpired.HasValue && DateTime.Now < user.LockExpired)) + { + throw Oops.Bah("您的账号被锁定"); + } + + long uniqueId = _idGenerator.NewLong(); + string token = JWTEncryption.Encrypt(new Dictionary() + { + [AuthClaimsConst.AuthIdKey] = user.Id, + [AuthClaimsConst.AccountKey] = user.Account, + [AuthClaimsConst.UuidKey] = uniqueId, + [AuthClaimsConst.AuthPlatformTypeKey] = AuthPlatformType.Manager + }); + var refreshToken = JWTEncryption.GenerateRefreshToken(token); + context.SigninToSwagger(token); + context!.Response.Headers["access-token"] = token; + context.Response.Headers["x-access-token"] = refreshToken; + + await _easyCachingProvider.RemoveAsync(codeCacheKey); + } + + /// + /// 重置密码 + /// + /// + /// + [DisplayName("重置密码")] + [HttpPost] + public async Task ResetPassword(ResetPasswordInput dto) + { + var codeCacheKey = $"sms:code:{dto.Phone}"; + var cachedCode = await _easyCachingProvider.GetAsync(codeCacheKey); + + if (!cachedCode.HasValue || cachedCode.Value != dto.Code) + { + throw Oops.Bah("验证码错误或已过期"); + } + + SysUser user = await _sysUseRepository.GetFirstAsync(x => x.Mobile == dto.Phone); + if (user == null) + { + throw Oops.Bah("该手机号尚未注册"); + } + + if (user.Status == AvailabilityStatus.Disable || (user.LockExpired.HasValue && DateTime.Now < user.LockExpired)) + { + throw Oops.Bah("您的账号被锁定"); + } + + user.Password = MD5Encryption.Encrypt($"{_idGenerator.Encode(user.Id)}{dto.NewPassword}"); + user.UpdatedTime = DateTime.Now; + + await _sysUseRepository.UpdateAsync(user); + + await _easyCachingProvider.RemoveAsync(codeCacheKey); + } +} diff --git a/YD_Event.Application/Auth/Dtos/AuthDtos.cs b/YD_Event.Application/Auth/Dtos/AuthDtos.cs new file mode 100644 index 0000000..9183f72 --- /dev/null +++ b/YD_Event.Application/Auth/Dtos/AuthDtos.cs @@ -0,0 +1,108 @@ +#nullable enable +using System.ComponentModel.DataAnnotations; + +namespace YD_Event.Application.Auth.Dtos; + +/// +/// 用户注册输入参数 +/// +public class RegisterInput +{ + /// + /// 手机号 + /// + [Required(ErrorMessage = "手机号不能为空")] + [RegularExpression(@"^1[3456789]\d{9}$", ErrorMessage = "手机号格式不正确")] + public string Phone { get; set; } = null!; + + /// + /// 账号 + /// + [Required(ErrorMessage = "账号不能为空")] + [MinLength(3, ErrorMessage = "账号至少3个字符")] + [MaxLength(32, ErrorMessage = "账号最多32个字符")] + public string Account { get; set; } = null!; + + /// + /// 密码 + /// + [Required(ErrorMessage = "密码不能为空")] + [MinLength(6, ErrorMessage = "密码至少6个字符")] + [MaxLength(18, ErrorMessage = "密码最多18个字符")] + public string Password { get; set; } = null!; + + /// + /// 确认密码 + /// + [Required(ErrorMessage = "请再次输入密码")] + [Compare(nameof(Password), ErrorMessage = "两次输入的密码不一致")] + public string ConfirmPassword { get; set; } = null!; + + /// + /// 短信验证码 + /// + [Required(ErrorMessage = "验证码不能为空")] + [StringLength(6, MinimumLength = 4, ErrorMessage = "验证码格式不正确")] + public string Code { get; set; } = null!; + + /// + /// 同意用户协议 + /// + [Required(ErrorMessage = "请同意用户协议")] + public bool AgreeTerms { get; set; } +} + +/// +/// 短信登录输入参数 +/// +public class SmsLoginInput +{ + /// + /// 手机号 + /// + [Required(ErrorMessage = "手机号不能为空")] + [RegularExpression(@"^1[3456789]\d{9}$", ErrorMessage = "手机号格式不正确")] + public string Phone { get; set; } = null!; + + /// + /// 验证码 + /// + [Required(ErrorMessage = "验证码不能为空")] + [StringLength(6, MinimumLength = 4, ErrorMessage = "验证码格式不正确")] + public string Code { get; set; } = null!; +} + +/// +/// 重置密码输入参数 +/// +public class ResetPasswordInput +{ + /// + /// 手机号 + /// + [Required(ErrorMessage = "手机号不能为空")] + [RegularExpression(@"^1[3456789]\d{9}$", ErrorMessage = "手机号格式不正确")] + public string Phone { get; set; } = null!; + + /// + /// 验证码 + /// + [Required(ErrorMessage = "验证码不能为空")] + [StringLength(6, MinimumLength = 4, ErrorMessage = "验证码格式不正确")] + public string Code { get; set; } = null!; + + /// + /// 新密码 + /// + [Required(ErrorMessage = "密码不能为空")] + [MinLength(6, ErrorMessage = "密码至少6个字符")] + [MaxLength(18, ErrorMessage = "密码最多18个字符")] + public string NewPassword { get; set; } = null!; + + /// + /// 确认新密码 + /// + [Required(ErrorMessage = "请再次输入密码")] + [Compare(nameof(NewPassword), ErrorMessage = "两次输入的密码不一致")] + public string ConfirmNewPassword { get; set; } = null!; +} diff --git a/YD_Event.Application/Client/Dtos/EventClientDtos.cs b/YD_Event.Application/Client/Dtos/EventClientDtos.cs index 8e14415..0c886f8 100644 --- a/YD_Event.Application/Client/Dtos/EventClientDtos.cs +++ b/YD_Event.Application/Client/Dtos/EventClientDtos.cs @@ -3,6 +3,32 @@ using YD_Event.Core.Enum; namespace YD_Event.Application.Client.Dtos; +/// +/// 客户端赛事查询输入 +/// +public class EventClientQueryInput : Pagination +{ + /// + /// 搜索关键字 + /// + public string? Keyword { get; set; } + + /// + /// 状态筛选:0-全部 1-报名中 2-比赛中 3-已结束 + /// + public int Status { get; set; } = 0; + + /// + /// 赛事类型 + /// + public EventFormat? EventType { get; set; } + + /// + /// 收费模式 + /// + public ChargeMode? ChargeMode { get; set; } +} + /// /// 赛事列表项DTO /// diff --git a/YD_Event.Application/Client/EventClientController.cs b/YD_Event.Application/Client/EventClientController.cs index 50ef850..db23657 100644 --- a/YD_Event.Application/Client/EventClientController.cs +++ b/YD_Event.Application/Client/EventClientController.cs @@ -2,10 +2,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; using YD_Event.Application.Client.Dtos; -using YD_Event.Application.Event; using YD_Event.Core.Entities; using YD_Event.Core.Enum; -using YD_Event.Core.Domain.Pricing; using YD_Event.Core.Infrastructure.WeChat; using YD_Event.Core.Infrastructure.WeChat.Dto; using YD_Event.Core.Options; @@ -41,6 +39,7 @@ public class EventClientController : IDynamicApiController private readonly ISqlSugarRepository _merchantRepository; private readonly ISqlSugarRepository _pointsRuleRepository; private readonly ISqlSugarRepository _appUserRepository; + private readonly ISqlSugarRepository _projectTypeRepository; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IIdGenerator _idGenerator; private readonly WeChatPayService _weChatPayService; @@ -64,6 +63,7 @@ public class EventClientController : IDynamicApiController ISqlSugarRepository merchantRepository, ISqlSugarRepository pointsRuleRepository, ISqlSugarRepository appUserRepository, + ISqlSugarRepository projectTypeRepository, IHttpContextAccessor httpContextAccessor, IIdGenerator idGenerator, WeChatPayService weChatPayService, @@ -86,6 +86,7 @@ public class EventClientController : IDynamicApiController _merchantRepository = merchantRepository; _pointsRuleRepository = pointsRuleRepository; _appUserRepository = appUserRepository; + _projectTypeRepository = projectTypeRepository; _httpContextAccessor = httpContextAccessor; _idGenerator = idGenerator; _weChatPayService = weChatPayService; @@ -109,6 +110,7 @@ public class EventClientController : IDynamicApiController /// [HttpGet("banners")] [AllowAnonymous] + [DisplayName("获取轮播图列表")] public async Task> GetBannerList() { var list = await _bannerRepository.AsQueryable() @@ -129,43 +131,36 @@ public class EventClientController : IDynamicApiController /// /// 获取赛事列表 /// - /// 页码 - /// 页大小 - /// 状态筛选:0-全部 1-报名中 2-比赛中 3-已结束 - /// 搜索关键字 + /// 查询参数 /// [HttpGet("list")] - [AllowAnonymous] // 允许未登录查看列表?根据需求,原型中有“我的”,可能需要登录。但通常列表可公开。这里先设为允许匿名,获取详情再校验。 - // 修正:Class上有[Authorize],如果需要匿名需显式[AllowAnonymous]。 - // 假设小程序进入即登录,保持Authorize - public async Task> GetEventList([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] int status = 0, [FromQuery] string? keyword = null) + [AllowAnonymous] + [DisplayName("获取赛事列表")] + public async Task> GetEventList([FromQuery] EventClientQueryInput input) { var query = _eventRepository.AsQueryable() .Where(e => !e.DeleteMark && e.Status == AvailabilityStatus.Enable) - .WhereIF(!string.IsNullOrEmpty(keyword), e => e.EventName.Contains(keyword)); + .WhereIF(!string.IsNullOrEmpty(input.Keyword), e => e.EventName.Contains(input.Keyword)) + .WhereIF(input.EventType.HasValue, e => e.EventType == input.EventType) + .WhereIF(input.ChargeMode.HasValue, e => e.ChargeMode == input.ChargeMode); - // 状态筛选逻辑 - // 1-报名中: Now >= RegStart && Now <= RegEnd - // 2-比赛中: Now >= MatchStart && Now <= MatchEnd - // 3-已结束: Now > MatchEnd var now = DateTime.Now; - if (status == 1) + if (input.Status == 1) { query = query.Where(e => now >= e.RegistrationStartTime && now <= e.RegistrationEndTime); } - else if (status == 2) + else if (input.Status == 2) { query = query.Where(e => now >= e.MatchStartTime && now <= e.MatchEndTime); } - else if (status == 3) + else if (input.Status == 3) { query = query.Where(e => now > e.MatchEndTime); } - // 排序:优先按报名开始时间倒序 query = query.OrderByDescending(e => e.RegistrationStartTime); - var list = await query.ToPagedListAsync(new Pagination { PageNo = page, PageSize = pageSize }); + var list = await query.ToPagedListAsync(input); var result = new PageResult { @@ -178,7 +173,7 @@ public class EventClientController : IDynamicApiController Id = e.Id, EventName = e.EventName, CoverImage = e.CoverImage, - EventStatus = CalculateEventStatus(e), // 动态计算状态 + EventStatus = CalculateEventStatus(e), RegistrationStartTime = e.RegistrationStartTime, RegistrationEndTime = e.RegistrationEndTime, MatchStartTime = e.MatchStartTime, @@ -197,6 +192,7 @@ public class EventClientController : IDynamicApiController /// [HttpGet("{id}")] [AllowAnonymous] + [DisplayName("获取赛事详情")] public async Task GetEventDetail(long id) { var evt = await _eventRepository.GetFirstAsync(e => e.Id == id && !e.DeleteMark); @@ -338,6 +334,7 @@ public class EventClientController : IDynamicApiController /// /// [HttpPost("calculate-amount")] + [DisplayName("计算报名金额")] public async Task CalculateRegistrationAmount([FromBody] CalculateAmountInput input) { var evt = await _eventRepository.GetFirstAsync(e => e.Id == input.EventId && !e.DeleteMark); @@ -431,6 +428,7 @@ public class EventClientController : IDynamicApiController /// [HttpPost("apply")] [UnitOfWork] + [DisplayName("提交赛事报名")] public async Task SubmitRegistration([FromBody] SubmitRegistrationInput input) { var userId = GetCurrentUserId(); @@ -457,10 +455,32 @@ public class EventClientController : IDynamicApiController var limit = await _limitRepository.GetFirstAsync(l => l.EventId == input.EventId && !l.DeleteMark); if (limit != null) { - // 简单校验项目总数,更严谨需按项目类型统计 if (input.Items.Count > limit.IndividualProjectLimit) throw Oops.Bah($"每人最多可报{limit.IndividualProjectLimit}个项目"); } + + var projectLimits = await _projectLimitRepository.AsQueryable() + .Where(pl => pl.EventId == input.EventId && !pl.DeleteMark) + .ToListAsync(); + + foreach (var item in input.Items) + { + var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == item.ProjectId); + if (projectLimit != null) + { + var currentCount = await _registrationItemRepository.AsQueryable() + .InnerJoin((ri, r) => ri.RegistrationId == r.Id) + .Where((ri, r) => ri.ProjectId == item.ProjectId && r.RegistrationStatus != 4) + .CountAsync(); + + if (currentCount >= projectLimit.LimitCount) + { + var eventProject = await _eventProjectRepository.GetFirstAsync(ep => ep.Id == item.ProjectId); + var project = await _projectRepository.GetFirstAsync(p => p.Id == eventProject.ProjectId); + throw Oops.Bah($"项目[{project?.Name}]已达到报名上限,无法继续报名"); + } + } + } // 4. 创建报名主表 var registrationId = _idGenerator.NewLong(); @@ -563,6 +583,7 @@ public class EventClientController : IDynamicApiController /// 支付信息查询输入参数,包含报名记录ID和OpenId /// 支付信息,包括微信支付参数(如果是微信支付) [HttpPost("payment-info")] + [DisplayName("获取支付信息")] public async Task GetPaymentInfo([FromBody] GetPaymentInfoInput input) { var userId = GetCurrentUserId(); @@ -573,14 +594,15 @@ public class EventClientController : IDynamicApiController if (evt == null) throw Oops.Bah("赛事不存在"); var configOptions = _wechatTenpayClientOptionsMonitor.CurrentValue; + var merchant = await _merchantRepository.GetFirstAsync(m => m.Id == evt.MerchantId && !m.DeleteMark); var paymentInfo = new PaymentInfoDto { RegistrationId = reg.Id, Amount = reg.TotalAmount, PaymentMethod = reg.PaymentMethod, - BankAccountName = "上海跃动文化科技有限公司", - BankAccountNo = "456498465198456261", - BankName = "招商银行" + BankAccountName = merchant?.MerchantName ?? "上海跃动文化科技有限公司", + BankAccountNo = merchant?.AccountNumber ?? "456498465198456261", + BankName = merchant?.BankName ?? "招商银行" }; if (reg.PaymentMethod == 2 && reg.TotalAmount > 0) @@ -649,6 +671,7 @@ public class EventClientController : IDynamicApiController /// 上传支付凭证(线下支付) /// [HttpPost("upload-voucher/{registrationId}")] + [DisplayName("上传支付凭证")] public async Task UploadPaymentVoucher(long registrationId, [FromBody] UploadVoucherInput input) { var userId = GetCurrentUserId(); @@ -668,6 +691,7 @@ public class EventClientController : IDynamicApiController /// 更新参赛人信息 /// [HttpPost("update-player")] + [DisplayName("更新参赛人信息")] public async Task UpdateRegistrationPlayer([FromBody] UpdatePlayerInput input) { var userId = GetCurrentUserId(); @@ -693,6 +717,7 @@ public class EventClientController : IDynamicApiController /// /// [HttpGet("my-events")] + [DisplayName("获取我的已报赛事列表")] public async Task> GetMyEventList([FromQuery] int page = 1, [FromQuery] int pageSize = 10) { var userId = GetCurrentUserId(); @@ -720,6 +745,7 @@ public class EventClientController : IDynamicApiController /// 获取报名详情(包含参赛人) /// [HttpGet("my-registration/{registrationId}")] + [DisplayName("获取报名详情")] public async Task GetRegistrationDetail(long registrationId) { var userId = GetCurrentUserId(); @@ -749,15 +775,11 @@ public class EventClientController : IDynamicApiController if (reg == null) throw Oops.Bah("报名记录不存在"); // 计算 EventStatus + EventStatus status = EventStatus.Enrolling; var now = DateTime.Now; - var tempEvent = new EventInfo - { - RegistrationStartTime = reg.RegStart, - RegistrationEndTime = reg.RegEnd, - MatchStartTime = reg.MatchStart, - MatchEndTime = reg.MatchEnd - }; - EventStatus status = EventStatusUpdateService.CalculateEventStatus(tempEvent, now); + if (now >= reg.RegStart && now <= reg.RegEnd) status = EventStatus.Enrolling; + else if (now >= reg.MatchStart && now <= reg.MatchEnd) status = EventStatus.Ongoing; + else if (now > reg.MatchEnd) status = EventStatus.Ended; // 获取 Items 和 Persons var items = await _registrationItemRepository.AsQueryable() @@ -805,6 +827,7 @@ public class EventClientController : IDynamicApiController /// /// [HttpGet("status/{registrationId}")] + [DisplayName("查询报名状态")] public async Task GetRegistrationStatus(long registrationId) { var userId = GetCurrentUserId(); @@ -826,6 +849,7 @@ public class EventClientController : IDynamicApiController /// /// [HttpPost("cancel/{registrationId}")] + [DisplayName("取消报名")] public async Task CancelRegistration(long registrationId) { var userId = GetCurrentUserId(); @@ -856,6 +880,7 @@ public class EventClientController : IDynamicApiController /// 操作是否成功 [HttpPost("select-payment-method")] [UnitOfWork] + [DisplayName("选择支付方式")] public async Task SelectPaymentMethod([FromBody] SelectPaymentMethodInput input) { var userId = GetCurrentUserId(); @@ -890,6 +915,7 @@ public class EventClientController : IDynamicApiController /// [HttpPost("sync-payment/{registrationId}")] [UnitOfWork] + [DisplayName("同步支付状态")] public async Task SyncPaymentStatus(long registrationId) { var userId = GetCurrentUserId(); @@ -1138,6 +1164,9 @@ public class EventClientController : IDynamicApiController private EventStatus CalculateEventStatus(EventInfo e) { var now = DateTime.Now; - return EventStatusUpdateService.CalculateEventStatus(e, now); + if (now >= e.RegistrationStartTime && now <= e.RegistrationEndTime) return EventStatus.Enrolling; // 报名中 + if (now >= e.MatchStartTime && now <= e.MatchEndTime) return EventStatus.Ongoing; // 比赛中 + if (now > e.MatchEndTime) return EventStatus.Ended; // 已结束 + return EventStatus.Enrolling; // 默认? 或者新增未开始状态 } } diff --git a/YD_Event.Application/Points/Dtos/AddPointsRuleInput.cs b/YD_Event.Application/Points/Dtos/AddPointsRuleInput.cs index 9cdca3a..70938e1 100644 --- a/YD_Event.Application/Points/Dtos/AddPointsRuleInput.cs +++ b/YD_Event.Application/Points/Dtos/AddPointsRuleInput.cs @@ -38,7 +38,7 @@ public class AddPointsRuleInput /// /// 参与积分(发放积分个数) /// - public int ParticipationPoints { get; set; } + public int? ParticipationPoints { get; set; } /// /// 备注 diff --git a/YD_Event.Application/User/Dtos/UpdateSysUserInput.cs b/YD_Event.Application/User/Dtos/UpdateSysUserInput.cs index b6f62b9..03824f5 100644 --- a/YD_Event.Application/User/Dtos/UpdateSysUserInput.cs +++ b/YD_Event.Application/User/Dtos/UpdateSysUserInput.cs @@ -1,12 +1,47 @@ -namespace YD_Event.Application.User.Dtos; +using System.ComponentModel.DataAnnotations; + +namespace YD_Event.Application.User.Dtos; /// /// 更新系统用户输入 /// -public class UpdateSysUserInput : AddSysUserInput +public class UpdateSysUserInput { /// /// 用户Id /// + [Required(ErrorMessage = "用户Id为必填项")] public long Id { get; set; } + + /// + /// 姓名 + /// + [Required(ErrorMessage = "姓名为必填项")] + [MaxLength(16, ErrorMessage = "姓名限制16个字符内")] + public string Name { get; set; } + + /// + /// 账号 + /// + [Required(ErrorMessage = "账号为必填项")] + [MaxLength(32, ErrorMessage = "账号限制32个字符内")] + public string Account { get; set; } + + /// + /// 密码(编辑时可选,不填则不修改) + /// + [MinLength(6, ErrorMessage = "密码长度至少6位")] + [MaxLength(32, ErrorMessage = "密码长度最多32位")] + public string? Password { get; set; } + + /// + /// 关联角色ID集合 + /// + [Required(ErrorMessage = "请选择关联角色")] + public List Roles { get; set; } + + /// + /// 状态(默认启用) + /// + public AvailabilityStatus Status { get; set; } = AvailabilityStatus.Enable; } \ No newline at end of file diff --git a/YD_Event.Application/User/SysUserService.cs b/YD_Event.Application/User/SysUserService.cs index 0d3f677..8b257ba 100644 --- a/YD_Event.Application/User/SysUserService.cs +++ b/YD_Event.Application/User/SysUserService.cs @@ -147,7 +147,17 @@ public class SysUserService : BaseService, ITransient if (await _repository.IsAnyAsync(x => x.Account == dto.Account && x.Id != dto.Id)) throw new InvalidOperationException($"账号 {dto.Account} 已被使用"); - dto.Adapt(user); + // 先更新除密码外的其他字段 + user.Name = dto.Name; + user.Account = dto.Account; + user.Status = dto.Status; + + // 只有密码不为空时才更新密码 + if (!string.IsNullOrWhiteSpace(dto.Password)) + { + string encode = _idGenerator.Encode(user.Id); + user.Password = MD5Encryption.Encrypt(encode + dto.Password); + } // 处理角色关联:先删后加 var roles = new List(); diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml index 3e3e2f7..bd97233 100644 --- a/YD_Event.Application/YD_Event.Application.xml +++ b/YD_Event.Application/YD_Event.Application.xml @@ -431,6 +431,27 @@ 验证码唯一id + + + 用户注册 + + + + + + + 短信验证码登录 + + + + + + + 重置密码 + + + + 管理员登录输入参数 @@ -456,6 +477,81 @@ 验证码 + + + 用户注册输入参数 + + + + + 手机号 + + + + + 账号 + + + + + 密码 + + + + + 确认密码 + + + + + 短信验证码 + + + + + 同意用户协议 + + + + + 短信登录输入参数 + + + + + 手机号 + + + + + 验证码 + + + + + 重置密码输入参数 + + + + + 手机号 + + + + + 验证码 + + + + + 新密码 + + + + + 确认新密码 + + 轮播图服务 @@ -1495,6 +1591,31 @@ 主键ID + + + 客户端赛事查询输入 + + + + + 搜索关键字 + + + + + 状态筛选:0-全部 1-报名中 2-比赛中 3-已结束 + + + + + 赛事类型 + + + + + 收费模式 + + 赛事列表项DTO @@ -2919,14 +3040,11 @@ - + 获取赛事列表 - 页码 - 页大小 - 状态筛选:0-全部 1-报名中 2-比赛中 3-已结束 - 搜索关键字 + 查询参数 @@ -9868,6 +9986,31 @@ 用户Id + + + 姓名 + + + + + 账号 + + + + + 密码(编辑时可选,不填则不修改) + + + + + 关联角色ID集合 + + + + + 状态(默认启用) + + 系统用户管理 diff --git a/YD_Event.Core/Entities/PointsRule.cs b/YD_Event.Core/Entities/PointsRule.cs index 5f49b29..4aa137b 100644 --- a/YD_Event.Core/Entities/PointsRule.cs +++ b/YD_Event.Core/Entities/PointsRule.cs @@ -16,7 +16,7 @@ public class PointsRule : Entity, ISoftDelete, ICreatedUserId, ICreatedTim /// /// 参与积分(发放积分个数) /// - public int ParticipationPoints { get; set; } + public int? ParticipationPoints { get; set; } /// /// 备注