using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using YD_Event.Application.Event.Dtos; using YD_Event.Core.Entities; using YD_Event.Core.Enum; using MiniExcelLibs; using SqlSugar; namespace YD_Event.Application.Event; /// /// 成绩与证书管理 /// public class EventScoreService : BaseService, ITransient { private readonly ISqlSugarRepository _personRepository; private readonly ISqlSugarRepository _resultRepository; private readonly ISqlSugarRepository _certificateRepository; private readonly ISqlSugarRepository _registrationRepository; private readonly IIdGenerator _idGenerator; private readonly ILogger _logger; public EventScoreService( ISqlSugarRepository personRepository, ISqlSugarRepository resultRepository, ISqlSugarRepository certificateRepository, ISqlSugarRepository registrationRepository, IIdGenerator idGenerator, ILogger logger) : base(resultRepository) { _personRepository = personRepository; _resultRepository = resultRepository; _certificateRepository = certificateRepository; _registrationRepository = registrationRepository; _idGenerator = idGenerator; _logger = logger; } /// /// 运动员成绩列表 /// [DisplayName("运动员成绩列表")] public async Task> PageAthlete(ScoreQueryInput input) { return await _personRepository.AsQueryable() .LeftJoin((p, r) => p.RegistrationId == r.Id) .LeftJoin((p, r, res) => p.Id == res.RegistrationPersonId) .LeftJoin((p, r, res, c) => res.Id == c.ResultId) .Where((p, r, res, c) => r.EventId == input.EventId && (p.PersonType == PersonType.Athlete || p.PersonType == PersonType.User)) .WhereIF(input.ProjectId.HasValue, p => p.ProjectId == input.ProjectId) .WhereIF(!string.IsNullOrWhiteSpace(input.GroupName), p => p.GroupName == input.GroupName) .WhereIF(!string.IsNullOrWhiteSpace(input.Name), p => p.Name.Contains(input.Name)) .WhereIF(!string.IsNullOrWhiteSpace(input.UnitName), (p, r) => r.TeamName.Contains(input.UnitName)) .WhereIF(input.Gender.HasValue, p => p.Gender == input.Gender.Value) .Select((p, r, res, c) => new ScoreListOutput { Id = p.Id, Name = p.Name, UnitName = r.TeamName, Gender = p.Gender.ToString(), Score = res.Score, Rank = res.Rank, ResultId = res.Id, CertificateId = c.Id }) .ToPagedListAsync(input); } /// /// 教练/领队列表 /// [DisplayName("教练领队列表")] public async Task> PageCoachLeader(CoachLeaderQueryInput input) { return await _personRepository.AsQueryable() .LeftJoin((p, c) => p.Id == c.ParticipationRecordId) .InnerJoin((p, c, r) => p.RegistrationId == r.Id) .Where((p, c, r) => r.EventId == input.EventId && p.PersonType == input.PersonType) .WhereIF(!string.IsNullOrWhiteSpace(input.Name), p => p.Name.Contains(input.Name)) .WhereIF(!string.IsNullOrWhiteSpace(input.Mobile), p => p.Phone.Contains(input.Mobile)) .WhereIF(input.Gender.HasValue, p => p.Gender == input.Gender.Value) .Select((p, c) => new CoachLeaderListOutput { Id = p.Id, Name = p.Name, Age = p.Age, Gender = p.Gender.ToString(), IdCardNumber = p.IdCard, Mobile = p.Phone, CertificateId = c.Id }) .ToPagedListAsync(input); } /// /// 导入成绩 /// [UnitOfWork] [DisplayName("导入成绩")] public async Task ImportScore(long eventId, IFormFile file) { using var stream = file.OpenReadStream(); var rows = stream.Query().ToList(); foreach(var row in rows) { // 先获取所有匹配的人员ID var personIdList = await _personRepository.AsQueryable() .InnerJoin((p, r) => p.RegistrationId == r.Id) .Where((p, r) => r.EventId == eventId && p.Name == row.Name && p.ProjectName == row.ProjectName && p.GroupName == row.GroupName) .Select((p, r) => p.Id) .ToListAsync(); if (personIdList == null || personIdList.Count == 0) continue; var person = await _personRepository.GetByIdAsync(personIdList[0]); if (person == null) continue; var result = await _resultRepository.GetFirstAsync(x => x.RegistrationPersonId == person.Id); if (result == null) { result = new EventResult { Id = _idGenerator.NewLong(), EventId = eventId, RegistrationPersonId = person.Id, RegistrationId = person.RegistrationId, ProjectId = person.ProjectId, ParticipantName = person.Name, GroupName = person.GroupName, CreatedTime = DateTime.Now }; await _resultRepository.InsertAsync(result); } result.Score = row.Score; result.Rank = row.Rank; result.Remark = row.RankText; result.UpdatedTime = DateTime.Now; await _resultRepository.UpdateAsync(result); } } /// /// 发放证书 /// [UnitOfWork] [DisplayName("发放证书")] public async Task IssueCertificate(IssueCertificateInput input) { foreach (var personId in input.PersonIds) { var person = await _personRepository.GetByIdAsync(personId); if (person == null) continue; if (person.PersonType == PersonType.Athlete || person.PersonType == PersonType.User) { var result = await _resultRepository.GetFirstAsync(x => x.RegistrationPersonId == person.Id); if (result == null) continue; var cert = await _certificateRepository.GetFirstAsync(x => x.ResultId == result.Id); if (cert == null) { cert = new EventCertificate { Id = _idGenerator.NewLong(), EventId = input.EventId, ResultId = result.Id, RegistrationId = person.RegistrationId, ParticipationRecordId = person.Id, ParticipantName = person.Name, GroupName = person.GroupName, CertificateStatus = 1, GeneratedTime = DateTime.Now, CertificateNo = $"C{DateTime.Now:yyyyMMdd}{person.Id}", CreatedTime = DateTime.Now }; await _certificateRepository.InsertAsync(cert); } } else { var cert = await _certificateRepository.GetFirstAsync(x => x.ParticipationRecordId == person.Id); if (cert == null) { cert = new EventCertificate { Id = _idGenerator.NewLong(), EventId = input.EventId, RegistrationId = person.RegistrationId, ParticipationRecordId = person.Id, ParticipantName = person.Name, GroupName = person.GroupName, CertificateStatus = 1, GeneratedTime = DateTime.Now, CertificateNo = $"CC{DateTime.Now:yyyyMMdd}{person.Id}", CreatedTime = DateTime.Now }; await _certificateRepository.InsertAsync(cert); } } } } /// /// 查看证书接口 /// /// 报名人员ID (EventRegistrationPerson的Id) /// 证书详情 [DisplayName("查看证书")] [HttpGet("/api/EventScore/GetCertificateDetail/{id}")] public async Task GetCertificateDetail([FromRoute] long id) { if (id <= 0) { return new BadRequestObjectResult(new { message = "无效的 ID" }); } var personExists = await _personRepository.IsAnyAsync(x => x.Id == id); if (!personExists) { return new NotFoundObjectResult(new { message = "人员记录不存在" }); } var cert = await _certificateRepository.GetFirstAsync(x => x.ParticipationRecordId == id); if (cert == null) { return new NotFoundObjectResult(new { message = "未找到对应的证书信息" }); } if (string.IsNullOrWhiteSpace(cert.CertificateUrl)) { return new NoContentResult(); } return new JsonResult(new { url = cert.CertificateUrl }); } /// /// 下载模板 /// [DisplayName("下载模板")] [HttpGet] public IActionResult DownloadTemplate() { // 动态生成空数据及正确列名、样式的Excel模板 var headers = new string[] { "赛事地址", "项目", "组别", "姓名", "联系方式", "单位", "成绩", "名次", "名次 " }; // 创建一个包含动态表头的列表,MiniExcel可以通过字典或匿名对象集合来生成带指定列名的Excel var value = new List> { // 插入一行空数据以确保生成正确的表头 headers.ToDictionary(h => h, h => (object)null) }; using var memoryStream = new MemoryStream(); // MiniExcel 保存,使用默认样式。如果需要复杂的背景色和边框(如图片中的浅绿色表头), // MiniExcel 目前不支持高级样式导出,因此这里我们使用 OpenXml 直接操作或基础导出。 // 但由于需求要求保持代码清晰并尽量匹配原图,这里先用 MiniExcel 导出带中文表头的文件。 // 为了实现完全一致的浅绿色样式,最好结合 EPPlus 或 ClosedXML。 // 考虑到项目中可能没有引入 EPPlus,使用 MiniExcel 的基础导出,并辅以说明。 memoryStream.SaveAs(value); return new FileContentResult(memoryStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "模板.xlsx" }; } /// /// 导出证书 /// [DisplayName("导出证书")] public async Task ExportCertificate(ScoreQueryInput input) { var list = await _certificateRepository.AsQueryable() .LeftJoin((c, r) => c.ResultId == r.Id) .LeftJoin((c, r, reg) => c.RegistrationId == reg.Id) .LeftJoin((c, r, reg, p) => c.ProjectId == p.Id) .Where((c, r, reg, p) => c.EventId == input.EventId) .WhereIF(input.ProjectId.HasValue, (c, r, reg, p) => c.ProjectId == input.ProjectId) .WhereIF(!string.IsNullOrWhiteSpace(input.GroupName), (c, r, reg, p) => c.GroupName == input.GroupName) .WhereIF(!string.IsNullOrWhiteSpace(input.Name), (c, r, reg, p) => c.ParticipantName.Contains(input.Name)) .Select((c, r, reg, p) => new CertificateExportDto { CertificateNo = c.CertificateNo, ParticipantName = c.ParticipantName, ProjectName = p.Name, GroupName = c.GroupName, UnitName = reg.TeamName, Score = r.Score, Rank = r.Rank, CertificateType = c.CertificateType == 1 ? "冠军证书" : c.CertificateType == 2 ? "亚军证书" : c.CertificateType == 3 ? "季军证书" : c.CertificateType == 4 ? "优胜证书" : "参与证书", IssuedTime = c.IssuedTime }) .ToListAsync(); using var stream = new MemoryStream(); stream.SaveAs(list); return Convert.ToBase64String(stream.ToArray()); } }