using EasyCaching.Core;
using YD_Event.Application.Auth;
using YD_Event.Application.Client.Dtos;
namespace YD_Event.Application.Client;
///
/// 小程序裁判认证与执裁报名接口。
/// 裁判自己注册账号后在小程序提交认证资料,由后台审核;审核通过后才能针对赛事报名执裁。
///
[ApiDescriptionSettings("微信小程序接口")]
[Authorize]
public class RefereeController : IDynamicApiController
{
private const string SmsCodeCachePrefix = "sms:code:";
private readonly ISqlSugarRepository _userRepository;
private readonly ISqlSugarRepository _eventRepository;
private readonly ISqlSugarRepository _applicationRepository;
private readonly ISqlSugarRepository _refereeRepository;
private readonly ISqlSugarRepository _eventRefereeRepository;
private readonly ISqlSugarRepository _limitRepository;
private readonly IEasyCachingProvider _cache;
private readonly AuthManager _authManager;
private readonly IIdGenerator _idGenerator;
public RefereeController(
ISqlSugarRepository userRepository,
ISqlSugarRepository eventRepository,
ISqlSugarRepository applicationRepository,
ISqlSugarRepository refereeRepository,
ISqlSugarRepository eventRefereeRepository,
ISqlSugarRepository limitRepository,
IEasyCachingProvider cache,
AuthManager authManager,
IIdGenerator idGenerator)
{
_userRepository = userRepository;
_eventRepository = eventRepository;
_applicationRepository = applicationRepository;
_refereeRepository = refereeRepository;
_eventRefereeRepository = eventRefereeRepository;
_limitRepository = limitRepository;
_cache = cache;
_authManager = authManager;
_idGenerator = idGenerator;
}
///
/// 获取裁判认证页面的级别、角色下拉数据。
///
[HttpGet("profile/options")]
[DisplayName("获取裁判认证选项")]
public RefereeProfileOptionsDto GetProfileOptions()
{
return new RefereeProfileOptionsDto
{
Levels = System.Enum.GetValues()
.Select(x => new RefereeEnumOptionDto { Value = (int)x, Label = x.Description() }).ToList(),
Roles = System.Enum.GetValues()
.Where(x => x != RefereeRole.None)
.Select(x => new RefereeEnumOptionDto { Value = (int)x, Label = x.Description() }).ToList()
};
}
///
/// 获取我的裁判认证资料。尚未提交时返回 null。
///
[HttpGet("profile")]
[DisplayName("获取我的裁判认证")]
public async Task GetProfile()
{
var userId = await EnsureRefereeAsync();
var referee = await _refereeRepository.GetFirstAsync(x => x.AppUserId == userId && !x.DeleteMark);
return referee == null ? null : ToProfileDto(referee);
}
///
/// 提交或更新裁判认证资料。已通过审核的资料再次提交后重新进入待审核。
///
[HttpPost("profile/submit")]
[UnitOfWork]
[DisplayName("提交裁判认证")]
public async Task SubmitProfile([FromBody] RefereeProfileSubmitInput input)
{
var userId = await EnsureRefereeAsync();
var user = await _userRepository.GetFirstAsync(x => x.Id == userId && !x.DeleteMark);
if (user == null) throw Oops.Bah("用户不存在");
var mobile = string.IsNullOrWhiteSpace(input.Mobile) ? user.Mobile : input.Mobile.Trim();
if (!Regex.IsMatch(mobile, @"^1[3-9]\d{9}$")) throw Oops.Bah("手机号格式不正确");
var referee = await _refereeRepository.GetFirstAsync(x => x.AppUserId == userId && !x.DeleteMark);
// 换绑手机号必须验证码校验,避免占用他人裁判档案。
var needVerify = !string.Equals(mobile, user.Mobile, StringComparison.Ordinal)
|| (referee != null && !string.Equals(mobile, referee.Mobile, StringComparison.Ordinal));
if (needVerify)
{
await VerifyCodeAsync(mobile, input.VerificationCode);
}
// 裁判库中手机号唯一,允许命中自己的档案。
var sameMobile = await _refereeRepository.AsQueryable()
.Where(x => x.Mobile == mobile && !x.DeleteMark).ToListAsync();
if (sameMobile.Any(x => referee == null || x.Id != referee.Id))
throw Oops.Bah("该手机号已存在于裁判库,请联系赛事组委会处理");
var isNew = referee == null;
referee ??= new YD_Event.Core.Entities.Referee
{
Id = _idGenerator.NextId(),
AppUserId = userId,
CreatedUserId = userId,
CreatedTime = DateTime.Now
};
referee.Name = input.Name.Trim();
referee.Mobile = mobile;
referee.ProvinceCode = input.ProvinceCode;
referee.ProvinceName = input.ProvinceName;
referee.CityCode = input.CityCode;
referee.CityName = input.CityName;
referee.DistrictCode = input.DistrictCode;
referee.DistrictName = input.DistrictName;
referee.Level = input.Level;
referee.CertificateUrl = input.CertificateUrl;
referee.Roles = input.Roles;
// 小程序提交的资料统一走后台审核。
referee.AuditStatus = AuditStatus.PendingAudit;
referee.AuditRemark = null;
referee.AuditUserId = null;
referee.AuditTime = null;
referee.Status = AvailabilityStatus.Enable;
if (isNew)
{
await _refereeRepository.InsertAsync(referee);
}
else
{
referee.UpdatedUserId = userId;
referee.UpdatedTime = DateTime.Now;
await _refereeRepository.UpdateAsync(referee);
}
if (needVerify) await _cache.RemoveAsync($"{SmsCodeCachePrefix}{mobile}");
return ToProfileDto(referee);
}
///
/// 查询指定赛事的裁判报名名额状态。
///
[HttpGet("applications/quota")]
[DisplayName("获取赛事裁判名额")]
public async Task GetQuota([FromQuery] long eventId)
{
var userId = await EnsureRefereeAsync();
var evt = await _eventRepository.GetFirstAsync(x =>
x.Id == eventId && x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
if (evt == null) throw Oops.Bah("赛事不存在");
var quota = await BuildQuotaAsync(evt, userId);
return quota;
}
[HttpGet("applications")]
[DisplayName("获取我的执裁报名")]
public async Task GetApplications()
{
var userId = await EnsureRefereeAsync();
var applications = await _applicationRepository.AsQueryable()
.Where(x => x.RefereeUserId == userId && x.Status == AvailabilityStatus.Enable)
.OrderByDescending(x => x.CreatedTime)
.ToListAsync();
var eventIds = applications.Select(x => x.EventId).Distinct().ToList();
var events = eventIds.Count == 0
? new List()
: await _eventRepository.AsQueryable().Where(x => eventIds.Contains(x.Id)).ToListAsync();
var eventMap = events.ToDictionary(x => x.Id);
var rows = applications.Select(x =>
{
eventMap.TryGetValue(x.EventId, out var evt);
return new RefereeApplicationDto
{
Id = x.Id,
ApplicationId = x.Id,
EventId = x.EventId,
EventName = evt?.EventName ?? string.Empty,
CoverImage = evt?.CoverImage,
Address = evt?.DetailAddress,
EventStatus = evt == null ? 0 : (int)evt.EventStatus,
ApplicationStatus = x.ApplicationStatus,
ApplicationStatusText = ApplicationStatusText(x.ApplicationStatus),
AppliedAt = x.CreatedTime,
MatchStartTime = evt?.MatchStartTime ?? DateTime.MinValue,
MatchEndTime = evt?.MatchEndTime ?? DateTime.MinValue
};
}).ToList();
return new RefereeApplicationListOutput { Rows = rows, Total = rows.Count };
}
[HttpPost("applications/apply")]
[UnitOfWork]
[DisplayName("报名执裁")]
public async Task