更新
This commit is contained in:
2026-02-23 18:52:32 +08:00
parent d429560511
commit 123fa6a7aa
81 changed files with 5952 additions and 407 deletions
@@ -3,6 +3,8 @@ using IM_API.Domain.Events;
using IM_API.Dtos;
using IM_API.Dtos.Message;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using IM_API.VOs.Message;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -19,12 +21,12 @@ namespace IM_API.Controllers
{
private readonly IMessageSevice _messageService;
private readonly ILogger<MessageController> _logger;
private readonly IEventBus _eventBus;
public MessageController(IMessageSevice messageService, ILogger<MessageController> logger, IEventBus eventBus)
public MessageController(IMessageSevice messageService,
ILogger<MessageController> logger)
{
_messageService = messageService;
_logger = logger;
_eventBus = eventBus;
}
[HttpPost]
[ProducesResponseType(typeof(BaseResponse<MessageBaseVo>), StatusCodes.Status200OK)]
@@ -32,15 +34,23 @@ namespace IM_API.Controllers
{
var userIdstr = User.FindFirstValue(ClaimTypes.NameIdentifier);
MessageBaseVo messageBaseVo = new MessageBaseVo();
var handledMessage = await _messageService.HandleFileMessageContentAsync(dto);
if(dto.ChatType == Models.ChatType.PRIVATE)
{
messageBaseVo = await _messageService.SendPrivateMessageAsync(int.Parse(userIdstr), dto.ReceiverId, dto);
messageBaseVo = await _messageService.SendPrivateMessageAsync(int.Parse(userIdstr), dto.ReceiverId, handledMessage);
}
else
{
messageBaseVo = await _messageService.SendGroupMessageAsync(int.Parse(userIdstr), dto.ReceiverId, dto);
messageBaseVo = await _messageService.SendGroupMessageAsync(int.Parse(userIdstr), dto.ReceiverId, handledMessage);
}
return Ok(new BaseResponse<MessageBaseVo>(messageBaseVo));
if (messageBaseVo.Type != MessageMsgType.Text)
{
var request = HttpContext?.Request;
var baseUrl = $"{request.Scheme}://{request.Host}";
messageBaseVo.Content = UrlTools.ProcessMessageUrl(messageBaseVo.Content, baseUrl);
}
return Ok(new BaseResponse<MessageBaseVo>(messageBaseVo));
}
[HttpGet]
[ProducesResponseType(typeof(BaseResponse<List<MessageBaseVo>>), StatusCodes.Status200OK)]
@@ -0,0 +1,124 @@
using IM_API.Dtos;
using IM_API.Interface.Services;
using IM_API.Models.Upload;
using IM_API.Tools;
using IM_API.VOs;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Storage;
using StackExchange.Redis;
using System.Text;
using System.Text.Json;
using IDatabase = StackExchange.Redis.IDatabase;
namespace IM_API.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UploadController : ControllerBase
{
private readonly IWebHostEnvironment _env;
private readonly IStorageService _storage;
private readonly IDatabase _redis;
public UploadController(IWebHostEnvironment env, IStorageService storage, IConnectionMultiplexer connectionMultiplexer)
{
_env = env;
_storage = storage;
_redis = connectionMultiplexer.GetDatabase();
}
[HttpPost("local/{taskId}/parts/{partNumber}")]
[ProducesResponseType(typeof(BaseResponse<object?>), StatusCodes.Status200OK)]
public async Task<IActionResult> LocalUpload(Guid taskId, int partNumber, IFormFile file)
{
var baseDir = Path.Combine(_env.ContentRootPath, "uploads"); // 项目根目录下 uploads
Directory.CreateDirectory(baseDir);
var path = Path.Combine(baseDir, "temp", taskId.ToString(), $"{partNumber}.part.tmp");
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
using var stream = System.IO.File.Create(path);
await file.CopyToAsync(stream);
await _redis.SetAddAsync(RedisKeys.GetUploadPartKey(taskId), partNumber);
return Ok(new BaseResponse<object?>());
}
[HttpPost("CreateTask")]
[ProducesResponseType(typeof(BaseResponse<CreateUploadTaskVo>), StatusCodes.Status200OK)]
public async Task<IActionResult> CreateUpload(CreateUploadTaskDto dto)
{
var vo = await _storage.InitTaskAsync(dto);
return Ok(new BaseResponse<CreateUploadTaskVo>(vo));
}
[HttpPost("CreatePart")]
public async Task<IActionResult> CreatePart(Guid taskId, int partNum)
{
var vo = await _storage.CreatePartInstructionAsync(taskId, partNum);
return Ok(new BaseResponse<UploadPartInstructionVo>(vo));
}
[HttpPost("CompleteTask")]
public async Task<IActionResult> CompleteTask([FromQuery]Guid taskId, [FromBody]List<UploadPartDto> dtos)
{
var taskIdRes = await _storage.CompleteAsync(taskId, dtos);
return Ok(new BaseResponse<string>(data: taskIdRes.ToString()));
}
[HttpGet("events/{taskId}")]
[AllowAnonymous]
public async Task Events(Guid taskId)
{
Response.Headers.Add("Content-Type", "text/event-stream");
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
var lastProgress = -1;
while (!HttpContext.RequestAborted.IsCancellationRequested)
{
var hash = await _redis.HashGetAllAsync(RedisKeys.MergeStatus(taskId));
if (hash.Length == 0)
{
await Task.Delay(1000);
continue;
}
var status = hash.FirstOrDefault(x => x.Name == "status").Value;
var progress = hash.FirstOrDefault(x => x.Name == "progress").Value;
var url = hash.FirstOrDefault(x => x.Name == "url").Value;
// 避免重复发送
if (progress != lastProgress)
{
var data = new
{
status = status.ToString(),
progress = progress.ToString(),
url = (string)url
};
await Response.WriteAsync($"data: {JsonSerializer.Serialize(data)}\n\n");
await Response.Body.FlushAsync();
// 完成后关闭 SSE
if (status == "Completed")
break;
await Task.Delay(1000); // 每秒检查一次
}
}
}
[HttpPost("upload/{hash}")]
public async Task<IActionResult> UploadSmallFile(IFormFile file,string hash)
{
using var stream = file.OpenReadStream();
var res = await _storage.UploadSmallFileAsync(stream, file.FileName, file.ContentType, file.Length, hash);
return Ok(new BaseResponse<UploadTask>(res));
}
}
}