195 lines
8.5 KiB
C#
195 lines
8.5 KiB
C#
using EasyCaching.Core;
|
|
using YD_Event.Application.Auth;
|
|
using YD_Event.Application.Referee.Dtos;
|
|
using YD_Event.Core.Entities;
|
|
using YD_Event.Core.Enum;
|
|
|
|
namespace YD_Event.Application.Referee;
|
|
|
|
/// <summary>裁判库管理。</summary>
|
|
[ApiDescriptionSettings("打分系统")]
|
|
[Route("api/referee")]
|
|
public class RefereeService : BaseService<YD_Event.Core.Entities.Referee>, ITransient
|
|
{
|
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Referee> _repository;
|
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _userRepository;
|
|
private readonly IEasyCachingProvider _cache;
|
|
private readonly IIdGenerator _idGenerator;
|
|
private readonly AuthManager _authManager;
|
|
|
|
public RefereeService(ISqlSugarRepository<YD_Event.Core.Entities.Referee> repository,
|
|
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> userRepository, IEasyCachingProvider cache,
|
|
IIdGenerator idGenerator, AuthManager authManager) : base(repository)
|
|
{
|
|
_repository = repository;
|
|
_userRepository = userRepository;
|
|
_cache = cache;
|
|
_idGenerator = idGenerator;
|
|
_authManager = authManager;
|
|
}
|
|
|
|
[DisplayName("裁判分页查询")]
|
|
[HttpGet("page")]
|
|
public async Task<PageResult<RefereeOutput>> Page([FromQuery] RefereeQueryInput input)
|
|
{
|
|
var query = _repository.AsQueryable()
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name!))
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.Mobile), x => x.Mobile.Contains(input.Mobile!))
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.RegionCode), x => x.ProvinceCode == input.RegionCode || x.CityCode == input.RegionCode || x.DistrictCode == input.RegionCode)
|
|
.WhereIF(input.Level.HasValue, x => x.Level == input.Level)
|
|
.WhereIF(input.Role.HasValue, x => (x.Roles & input.Role!.Value) == input.Role.Value)
|
|
.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!.Value.Date.AddDays(1))
|
|
.OrderByDescending(x => x.CreatedTime);
|
|
var page = await query.ToPagedListAsync(input);
|
|
return new PageResult<RefereeOutput>
|
|
{
|
|
PageNo = page.PageNo, PageSize = page.PageSize, Total = page.Total,
|
|
Rows = page.Rows.Select(ToOutput).ToList()
|
|
};
|
|
}
|
|
|
|
[DisplayName("新增裁判")]
|
|
[HttpPost("add")]
|
|
public async Task Add([FromBody] SaveRefereeInput input)
|
|
{
|
|
await EnsureMobileAvailable(input.Mobile, null);
|
|
await VerifyCode(input.Mobile, input.VerificationCode);
|
|
var appUser = await FindAppUserAsync(input.Mobile);
|
|
var entity = input.Adapt<YD_Event.Core.Entities.Referee>();
|
|
entity.Id = _idGenerator.NextId();
|
|
entity.AppUserId = appUser?.Id;
|
|
// 后台管理员直接维护的裁判档案无需再次审批。
|
|
var now = DateTime.Now;
|
|
entity.AuditStatus = AuditStatus.Pass;
|
|
entity.AuditUserId = _authManager.UserId;
|
|
entity.AuditTime = now;
|
|
entity.Status = AvailabilityStatus.Enable;
|
|
entity.CreatedUserId = _authManager.UserId;
|
|
entity.CreatedTime = now;
|
|
await _repository.InsertAsync(entity);
|
|
await _cache.RemoveAsync($"sms:code:{input.Mobile}");
|
|
}
|
|
|
|
[DisplayName("修改裁判")]
|
|
[HttpPut("update")]
|
|
public async Task Update([FromBody] UpdateRefereeInput input)
|
|
{
|
|
var entity = await _repository.GetByIdAsync(input.Id);
|
|
if (entity == null || entity.DeleteMark) throw Oops.Bah("裁判不存在");
|
|
await EnsureMobileAvailable(input.Mobile, input.Id);
|
|
if (!string.Equals(entity.Mobile, input.Mobile, StringComparison.Ordinal))
|
|
{
|
|
await VerifyCode(input.Mobile, input.VerificationCode);
|
|
await _cache.RemoveAsync($"sms:code:{input.Mobile}");
|
|
}
|
|
var appUser = await FindAppUserAsync(input.Mobile);
|
|
input.Adapt(entity);
|
|
entity.AppUserId = appUser?.Id;
|
|
entity.UpdatedUserId = _authManager.UserId;
|
|
entity.UpdatedTime = DateTime.Now;
|
|
await _repository.UpdateAsync(entity);
|
|
}
|
|
|
|
[DisplayName("审核裁判"), HttpPost("audit")]
|
|
public async Task Audit([FromBody] AuditRefereeInput input)
|
|
{
|
|
if (input.AuditStatus != AuditStatus.Pass && input.AuditStatus != AuditStatus.Reject)
|
|
throw Oops.Bah("审核状态不正确");
|
|
var entity = await _repository.GetByIdAsync(input.Id);
|
|
if (entity == null || entity.DeleteMark) throw Oops.Bah("裁判不存在");
|
|
var now = DateTime.Now;
|
|
entity.AuditStatus = input.AuditStatus;
|
|
entity.AuditRemark = input.Remark;
|
|
entity.AuditUserId = _authManager.UserId;
|
|
entity.AuditTime = now;
|
|
entity.UpdatedUserId = _authManager.UserId;
|
|
entity.UpdatedTime = now;
|
|
await _repository.UpdateAsync(entity);
|
|
}
|
|
|
|
private async Task EnsureMobileAvailable(string mobile, long? exceptId)
|
|
{
|
|
// 不要把 Nullable.HasValue 分支写进 SqlSugar 表达式;空值时会生成 “<>” 附近的非法 SQL。
|
|
var refereeQuery = _repository.AsQueryable().Where(x => x.Mobile == mobile);
|
|
if (exceptId.HasValue)
|
|
refereeQuery = refereeQuery.Where(x => x.Id != exceptId.Value);
|
|
|
|
if (await refereeQuery.AnyAsync())
|
|
throw Oops.Bah("该手机号已存在于裁判库");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 手机号已注册小程序账号时自动建立关联,未注册时允许裁判档案独立存在。
|
|
/// </summary>
|
|
private Task<YD_Event.Core.Entities.AppUser?> FindAppUserAsync(string mobile)
|
|
{
|
|
return _userRepository.AsQueryable().Where(x => x.Mobile == mobile).FirstAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 批量回填已存在裁判的AppUserId。按手机号匹配小程序用户,建立关联。
|
|
/// </summary>
|
|
[DisplayName("批量回填AppUserId")]
|
|
[HttpPost("backfill-appuser")]
|
|
public async Task<object> BackfillAppUserId()
|
|
{
|
|
var refereesWithoutAppUser = await _repository.AsQueryable()
|
|
.Where(x => x.AppUserId == null && !x.DeleteMark && !string.IsNullOrEmpty(x.Mobile))
|
|
.ToListAsync();
|
|
|
|
if (refereesWithoutAppUser.Count == 0)
|
|
{
|
|
return new { success = true, message = "无需回填", count = 0 };
|
|
}
|
|
|
|
var mobiles = refereesWithoutAppUser.Select(x => x.Mobile).Distinct().ToList();
|
|
var appUsers = await _userRepository.AsQueryable()
|
|
.Where(x => mobiles.Contains(x.Mobile))
|
|
.ToListAsync();
|
|
|
|
var mobileUserMap = appUsers.ToDictionary(x => x.Mobile, x => x.Id);
|
|
var updatedCount = 0;
|
|
|
|
foreach (var referee in refereesWithoutAppUser)
|
|
{
|
|
if (mobileUserMap.TryGetValue(referee.Mobile, out var appUserId))
|
|
{
|
|
referee.AppUserId = appUserId;
|
|
referee.UpdatedUserId = _authManager.UserId;
|
|
referee.UpdatedTime = DateTime.Now;
|
|
updatedCount++;
|
|
}
|
|
}
|
|
|
|
if (updatedCount > 0)
|
|
{
|
|
await _repository.UpdateRangeAsync(refereesWithoutAppUser.Where(x => x.AppUserId != null).ToList());
|
|
}
|
|
|
|
return new { success = true, message = $"成功回填{updatedCount}条记录", count = updatedCount };
|
|
}
|
|
|
|
private async Task VerifyCode(string mobile, string? code)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(code)) throw Oops.Bah("请输入验证码");
|
|
var cached = await _cache.GetAsync<string>($"sms:code:{mobile}");
|
|
if (!cached.HasValue || cached.Value != code) throw Oops.Bah("验证码错误或已过期");
|
|
}
|
|
|
|
private static RefereeOutput ToOutput(YD_Event.Core.Entities.Referee x) => new()
|
|
{
|
|
Id = x.Id, AppUserId = x.AppUserId, Name = x.Name, Mobile = x.Mobile,
|
|
ProvinceCode = x.ProvinceCode, ProvinceName = x.ProvinceName,
|
|
CityCode = x.CityCode, CityName = x.CityName,
|
|
DistrictCode = x.DistrictCode, DistrictName = x.DistrictName,
|
|
RegionName = $"{x.ProvinceName}/{x.CityName}/{x.DistrictName}",
|
|
Level = x.Level, LevelName = x.Level.Description(), CertificateUrl = x.CertificateUrl,
|
|
Roles = x.Roles,
|
|
RoleNames = Enum.GetValues<RefereeRole>().Where(r => r != RefereeRole.None && x.Roles.HasFlag(r)).Select(r => r.Description()).ToList(),
|
|
AuditStatus = x.AuditStatus, AuditRemark = x.AuditRemark,
|
|
AuditUserId = x.AuditUserId, AuditTime = x.AuditTime, CreatedTime = x.CreatedTime
|
|
};
|
|
}
|