refactor,feat: 批量代码重构与新增业务模块

1. 重命名枚举项与实体字段,修正类型引用
2. 新增IsNull扩展方法与多项字符串处理扩展
3. 新增大量业务DTO、服务接口与枚举定义
4. 重构RabbitMQ服务实现,替换旧版消息队列组件
5. 优化签到服务的宠物喂养事务逻辑
6. 移除冗余的项目引用与旧版消息队列代码
7. 新增Excel导出、导入模板相关工具方法
This commit is contained in:
glz
2026-06-11 17:01:24 +08:00
parent ae4cd627df
commit e493c85d08
57 changed files with 4477 additions and 399 deletions

View File

@ -1,8 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using QYZH.InteractiveMagazine.Infrastructure.Context;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Enum;
using System.Security.Claims;
using System.Web;
namespace QYZH.InteractiveMagazine.WebApi.Controllers;
@ -75,4 +79,57 @@ public abstract class BaseController : ControllerBase
{
return Ok(BaseResponse.Fail(msg));
}
/// <summary>
/// 导出Excel
/// </summary>
/// <param name="path">完整文件路径</param>
/// <param name="fileName">带扩展文件名</param>
/// <returns></returns>
protected IActionResult ExportExcel(string path, string fileName)
{
//var webHostEnvironment = App.WebHostEnvironment;
if (!Path.Exists(path))
{
throw new BusinessException(fileName + "文件不存在");
}
var stream = System.IO.File.OpenRead(path); //创建文件流
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName));
}
protected (string, string) DownloadImportTemplate<T>(List<T> list, string fileName)
{
IWebHostEnvironment webHostEnvironment = ServiceContext.GetService<IWebHostEnvironment>();
string sFileName = $"{fileName}.xlsx";
string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName);
//不存在模板创建模板
if (!Directory.Exists(fullPath))
{
var directoryPath = Path.GetDirectoryName(fullPath);
if (directoryPath != null)
{
Directory.CreateDirectory(directoryPath);
}
}
if (!Path.Exists(fullPath))
{
MiniExcel.SaveAs(fullPath, list, overwriteFile: true);
}
return (sFileName, fullPath);
}
protected (string, string) ExportExcelMini<T>(List<T> list, string sheetName, string fileName)
{
IWebHostEnvironment webHostEnvironment = ServiceContext.GetService<IWebHostEnvironment>();
string sFileName = $"{fileName}_{DateTime.Now:MMdd_HHmmss}.xlsx";
string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "export", sFileName);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
MiniExcel.SaveAs(fullPath, list, sheetName: sheetName);
return (sFileName, fullPath);
}
}

View File

@ -0,0 +1,559 @@

using QYZH.InteractiveMagazine.Models.Base;
using QYZH.InteractiveMagazine.Models.Dto.Journal;
using QYZH.InteractiveMagazine.Models.Dto.DotMatrix;
using QYZH.InteractiveMagazine.Models.Enum;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using QYZH.InteractiveMagazine.Infrastructure.OSS;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.WebApi.Controllers;
using System.ComponentModel.DataAnnotations;
using System.Web;
namespace QYZH.InteractiveMagazine.WebApi.Controllers
{
/// <summary>
/// 期刊管理
/// </summary>
[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Platform))]
[Route("api/icr")]
public class JournalController(
OssService ossService,
IJournalCatalogService JournalCatalogService,
IJournalService JournalService,
IJournalPageService JournalPageService,
IJournalPageTaskService JournalPageTaskService,
IHttpClientFactory httpClientFactory,
Microsoft.Extensions.Configuration.IConfiguration configuration) : BaseController
{
#region
/// <summary>
/// 根据ID查询书籍数据
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet, Route("Journal/detail/{id:long}")]
public async Task<BaseResponse<JournalDto>> GetByIdAsync([Required] long id)
{
var result = await JournalService.GetByExpressionAsync<JournalDto>(c => c.Id == id);
return BaseResponse<JournalDto>.Success(result);
}
/// <summary>
/// 分页查询书籍列表
/// </summary>
/// <param name="search">查询条件</param>
/// <returns></returns>
[HttpPost, Route("Journal/pagelist")]
public async Task<BaseResponse<PageListModel<JournalDto>>> GetPageListAsync([FromBody] PageQueryModel<JournalQueryDto> search)
{
var result = await JournalService.GetPageListAsync(search);
return BaseResponse<PageListModel<JournalDto>>.Success(result);
}
/// <summary>
/// 查询书籍列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost, Route("Journal/list")]
public async Task<BaseResponse<List<JournalDto>>> GetListAsync([FromQuery] JournalQueryDto dto)
{
var result = await JournalService.GetListAsync(dto);
return BaseResponse<List<JournalDto>>.Success(result);
}
///// <summary>
///// 创建书籍
///// </summary>
///// <param name="dto">书籍对象</param>
///// <returns></returns>
//[HttpPost, Route("Journal/add")]
//public async Task<BaseResponse<long>> AddAsync([FromBody] JournalEditDto dto)
//{
// var result = await JournalService.AddAsync(dto);
// return result;
//}
/// <summary>
/// 编辑书籍
/// </summary>
/// <param name="dto">书籍对象</param>
/// <returns></returns>
[HttpPost, Route("Journal/update")]
public async Task<BaseResponse> Edit([FromBody] JournalEditDto dto)
{
var result = await JournalService.EditAsync(dto);
return result;
}
/// <summary>
/// 批量删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[HttpPost, Route("Journal/delete")]
public async Task<BaseResponse<bool>> DeleteAsync([Required][FromBody] List<long> ids)
{
var result = await JournalService.DeleteAsync(ids);
return BaseResponse<bool>.Success(result);
}
/// <summary>
/// 书籍起始页
/// </summary>
/// <returns></returns>
[HttpPost, Route("Journal/startpage")]
public async Task<BaseResponse<bool>> StartPageAsync([FromQuery]long id, [FromQuery] int index)
{
var data = await JournalService.StartPageAsync(id, index);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书籍归档
/// </summary>
/// <returns></returns>
[HttpPost, Route("Journal/rchive/{id:long}")]
public async Task<BaseResponse<bool>> Archive(long id)
{
var data = await JournalService.StatusAsync(id, JournalStatusEnum.Archive);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书籍废弃
/// </summary>
/// <returns></returns>
[HttpPost]
[HttpPost, Route("Journal/abandon/{id:long}")]
public async Task<BaseResponse<bool>> Abandon(long id)
{
var data = await JournalService.StatusAsync(id, JournalStatusEnum.Abandoned);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书籍铺码
/// </summary>
/// <returns></returns>
[HttpPost, Route("Journal/printcode/{id:long}")]
public async Task<BaseResponse<DotMatrixOutput>> PrintCodeAsync(long id)
{
var data = await JournalService.PrintCodeAsync(id);
return BaseResponse<DotMatrixOutput>.Success(data);
}
/// <summary>
/// 上报铺码结果
/// </summary>
/// <param name="input"></param>
/// <param name="file"></param>
/// <returns></returns>
[HttpPost, Route("Journal/resultreport")]
public async Task<BaseResponse<bool>> GenerateAsync([FromBody] DotMatrixNoteJournalReportInput input, [FromForm] IFormFile? file)
{
if (file != null)
{
input.FileKey = $"Journal/{input.Id}/dotMatrixJournal.pdf";
input.FileUrl = ossService.PutObject(input.FileKey, file.OpenReadStream());
}
var data = await JournalService.ResultReportAsync(input);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书籍导入
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost, Route("Journal/import")]
public async Task<BaseResponse<long>> JournalImport(JournalImportDto input)
{
var data = await JournalCatalogService.ImportAsync(input);
return BaseResponse<long>.Success(data);
}
///// <summary>
///// 工单分页列表
///// </summary>
///// <param name="search"></param>
///// <returns></returns>
//[HttpPost, Route("tasklibrary/workorder/pagelist")]
//public async Task<BaseResponse<PageListModel<IcrJournalWorkOrderDto>>> WorkOrderPageListAsync([FromBody] PageQueryModel<IcrWorkOrderQueryDto> search)
//{
// var data = await JournalWorkOrderService.WorkOrderPageListAsync(search);
// return BaseResponse<PageListModel<IcrJournalWorkOrderDto>>.Success(data);
//}
/// <summary>
/// 书籍发起工单
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
//[HttpPost, Route("tasklibrary/workorder/create")]
//public async Task<BaseResponse<bool>> CreateWorkOrderAsync([FromBody] IcrWorkOrderCreateDto dto)
//{
// var result = await JournalWorkOrderService.CreateWorkOrderAsync(dto);
// return result;
//}
///// <summary>
///// 更新工单状态
///// </summary>
///// <param name="JournalId"></param>
///// <returns></returns>
//[HttpPost, Route("tasklibrary/workorder/setstatus/{JournalId:long}")]
//public async Task<BaseResponse<int>> UpdateWorkOrderAsync(long JournalId)
//{
// var result = await JournalWorkOrderService.UpdateWorkOrderAsync(JournalId);
// return BaseResponse<int>.Success(result);
//}
/// <summary>
/// 题库代理请求接口
/// </summary>
/// <returns></returns>
[HttpGet, Route("tasklibrary/proxyget")]
public async Task<IActionResult> GetTaskLibraryData(string apiUrl = "")
{
var client = httpClientFactory.CreateClient();
var response = await client.GetAsync(configuration["TaskLibraryApi"] + apiUrl);
// 确保请求成功(状态码 200
response.EnsureSuccessStatusCode();
string result = await response.Content.ReadAsStringAsync();
return Content(result, "application/json");
}
#endregion
#region
///// <summary>
///// 书籍目录详情
///// </summary>
///// <returns></returns>
//[HttpGet, Route("catalog/tree/{JournalId:long}")]
//public async Task<BaseResponse<List<IcrJournalCatalog>>> CatalogDetailAsync(long JournalId)
//{
// var data = await _JournalCatalogService.DetailAsync(JournalId);
// return BaseResponse<List<IcrJournalCatalog>>.Success(data);
//}
/// <summary>
/// 书籍目录详情
/// </summary>
/// <returns></returns>
[HttpGet, Route("catalog/tree/{JournalId:long}")]
public async Task<BaseResponse<List<IcrJournalCatalogTreeDto>>> CatalogDetailAsync(long JournalId)
{
var data = await JournalCatalogService.GetJournalCataloTreeAsync(JournalId);
return BaseResponse<List<IcrJournalCatalogTreeDto>>.Success(data);
}
/// <summary>
/// 获取书分类及页码
/// </summary>
/// <param name="JournalId"></param>
/// <returns></returns>
[HttpGet, Route("catalog/list")]
public async Task<BaseResponse<List<JournalCatalogTreeListDto>>> GetJournalCatelogListAsync(long JournalId)
{
var data = await JournalCatalogService.GetJournalCatalogListAsync(JournalId);
return BaseResponse<List<JournalCatalogTreeListDto>>.Success(data);
}
/// <summary>
/// 下载书籍目录导入模板
/// </summary>
/// <returns></returns>
[HttpGet, Route("catalog/template")]
[AllowAnonymous]
public IActionResult ImportCatalogTemplateExcel()
{
var result = DownloadImportTemplate(new List<JournalCatalogTreeListDto>() { }, "书籍目录导入模板");
return ExportExcel(result.Item2, result.Item1);
}
/// <summary>
/// 书籍目录导出
/// </summary>
/// <param name="JournalId"></param>
/// <returns></returns>
[HttpGet, Route("catalog/export/{JournalId}")]
public async Task<IActionResult> CatalogExport(long JournalId)
{
var list = await JournalCatalogService.GetJournalCatalogListAsync(JournalId);
var result = ExportExcelMini(list, "sheet1", "书籍目录导出");
return ExportExcel(result.Item2, result.Item1);
}
/// <summary>
/// 书籍目录导入
/// </summary>
/// <param name="JournalId">file</param>
/// <param name="file">file</param>
/// <returns></returns>
[HttpPost, Route("catalog/import/{JournalId}")]
public async Task<BaseResponse<bool>> ImportAsync(long JournalId, [FromForm(Name = "file")] IFormFile file)
{
List<JournalCatalogTreeListDto> dto = new();
using (var stream = file.OpenReadStream())
{
dto = stream.Query<JournalCatalogTreeListDto>().ToList();
}
var result = await JournalCatalogService.ImportCatalogAsync(JournalId, dto);
return BaseResponse<bool>.Success(result);
}
/// <summary>
/// 书籍目录新增
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost, Route("catalog/add")]
public async Task<BaseResponse<long>> CatalogAdd(JournalCatalogInput input)
{
var data = await JournalCatalogService.InsertAsync(input);
return BaseResponse<long>.Success(data);
}
/// <summary>
/// 书籍目录修改
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost, Route("catalog/update")]
public async Task<BaseResponse<bool>> CatalogUpdate(JournalCatalogUpdateInput input)
{
var data = await JournalCatalogService.UpdateAsync(input);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书籍目录删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpPost, Route("catalog/delete/{id:long}")]
public async Task<BaseResponse<bool>> CatalogDelete(long id)
{
var data = await JournalCatalogService.DeleteAsync(id);
return BaseResponse<bool>.Success(data);
}
///// <summary>
///// 书籍目录复制
///// </summary>
///// <param name="input"></param>
///// <returns></returns>
//[HttpPost, Route("catalog/copy")]
//[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
//public async Task<BaseResponse> CatalogCopy(CopyInput input)
//{
// var data = await _JournalCatalogServices.CopyAsync(input);
// return ApiResult(data, "书籍目录删除失败!");
//}
/// <summary>
/// 书籍目录移动
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost, Route("catalog/move")]
public async Task<BaseResponse<bool>> CatalogMove(MoveInput input)
{
var data = await JournalCatalogService.MoveAsync(input);
return BaseResponse<bool>.Success(data);
}
#endregion
#region
/// <summary>
/// 书页新增
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/add")]
public async Task<BaseResponse<long>> PageAdd(JournalAddV2Input input)
{
var data = await JournalPageService.InsertAsync(input);
return BaseResponse<long>.Success(data);
}
/// <summary>
/// 书页修改
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/update")]
public async Task<BaseResponse<bool>> PageUpdate(PageLayoutInput input)
{
var data = await JournalPageService.UpdateAsync(input);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 自动铺码
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/updatepageno/{JournalId:long}")]
public async Task<BaseResponse<bool>> PageUpdatePageNo([Required(ErrorMessage = "书籍编号不允许为空")] long JournalId)
{
var data = await JournalPageService.UpdatePageNoAsync(JournalId);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 下载书籍页码点阵码PDF文件名称
/// </summary>
/// <returns></returns>
[HttpGet, Route("page/downloadJournalpagePdf/{JournalId:long}")]
public async Task<IActionResult> DownloadJournalPagePdf([Required(ErrorMessage = "书籍编号不允许为空")] long JournalId)
{
var entity = await JournalService.GetFirstAsync(x => x.Id == JournalId);
if (entity == null)
throw new BusinessException("书籍不存在");
var downloadUrl = entity.DownloadJournalPagePdfName;
if (string.IsNullOrWhiteSpace(downloadUrl))
throw new BusinessException("下载点阵码PDF文件为空,请先铺码");
var stream = await httpClientFactory.CreateClient().GetStreamAsync(downloadUrl); //创建文件流
if (stream == null)
throw new BusinessException("下载点阵码PDF文件失败");
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
return File(stream, "application/pdf", HttpUtility.UrlEncode(Path.GetFileName(downloadUrl)));
}
///// <summary>
///// 打印书页(全部) 暂时废弃这个方,直接使用 Adobe Reader X 软件打印就可以了
///// </summary>
///// <returns></returns>
//[HttpPost, Route("page/printJournalPage/{JournalId:long}")]
//public async Task<BaseResponse<bool>> PrintJournalPage([Required(ErrorMessage = "书籍编号不允许为空")] long JournalId)
//{
// var data = await JournalPageService.PrintJournalPageAsync(JournalId);
// return BaseResponse<bool>.Success(data);
//}
///// <summary>
///// 书页删除
///// </summary>
///// <returns></returns>
//[HttpPost, Route("page/delete/{id:long}")]
//[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
//public async Task<IActionResult> PageDelete(long id)
//{
// var data = await _JournalPageServices.DeleteAsync(id);
// return ApiResult(data, "更新书页失败!");
//}
/// <summary>
/// 书页详情
/// </summary>
/// <returns></returns>
[HttpGet, Route("page/detail/{id:long}")]
public async Task<BaseResponse<JournalPageV2Output>> PageDetail(long id)
{
var data = await JournalPageService.DetailAsync(id);
return BaseResponse<JournalPageV2Output>.Success(data);
}
///// <summary>
///// 不包含有文章的书页
///// </summary>
///// <returns></returns>
//[HttpGet, Route("page/no-article/{JournalId:long}")]
//[ProducesResponseType(typeof(BaseResponse<List<JournalPageNoArticleOutput>>), 200)]
//public async Task<IActionResult> PageNoArticleAsync(long JournalId)
//{
// var data = await _JournalPageService.PageNoArticleAsync(JournalId);
// return BaseResponse<List<JournalPageNoArticleOutput>>(data, "查询书页失败!");
//}
#endregion
#region
/// <summary>
/// 书页问题新增
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/task/add")]
[ProducesResponseType(typeof(BaseResponse<long>), 200)]
public async Task<BaseResponse<long>> PageTaskAdd(JournalPageTaskAddInput input)
{
var data = await JournalPageTaskService.InsertAsync(input);
return BaseResponse<long>.Success(data.Value);
}
/// <summary>
/// 书页问题单项解析
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/task/keyword-analysis")]
[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
public async Task<BaseResponse<bool>> KeywordAnalysisAsync(JournalPageTaskKeywordAnalysis input)
{
var data = await JournalPageTaskService.KeywordAnalysisAsync(input);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书页问题更新
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/task/update")]
[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
public async Task<BaseResponse<bool>> PageTaskUpdate(JournalPageTaskUpdateInput input)
{
var data = await JournalPageTaskService.UpdateAsync(input);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书页问题详情 + 题库那边需要用
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet, Route("page/task/detail/{id:long}")]
public async Task<BaseResponse<JournalPageTaskOutput>> PageTaskDetail(long id)
{
var data = await JournalPageTaskService.DetailAsync(id);
return BaseResponse<JournalPageTaskOutput>.Success(data);
}
/// <summary>
/// 书页问题删除
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/task/delete/{id:long}")]
public async Task<BaseResponse<bool>> PageTaskDelete(long id)
{
var data = await JournalPageTaskService.DeleteAsync(id);
return BaseResponse<bool>.Success(data);
}
/// <summary>
/// 书页问题补充
/// </summary>
/// <returns></returns>
[HttpPost, Route("page/task/complement")]
public async Task<BaseResponse<bool>> ComplementAnsync(JournalPageTaskComplementInput input)
{
var data = await JournalPageTaskService.ComplementAsync(input);
return BaseResponse<bool>.Success(data);
}
#endregion
}
}