Files
F9Web/server/src/F9MES.Api/Controllers/ImController.cs
T
fanhongcai 1bec470647 feat: 新增IM即时通讯(浮窗)、工作流、打印模块及工作台增强
- IM: 新增浮窗聊天(ImFloatWindow)、管理页(monitor/config/service/message)、SSE推送
- 工作流: 新增待办/我的流程页面及后端服务
- 打印: 新增打印模板、出库单打印(PrintPage)、模板种子脚本
- 工作台: 增强快捷入口与工作台数据
- 修复: TagsView页签关闭、CrudPage通用表格增强
- 移除导航菜单中的即时通讯入口,改为右下角浮窗
2026-08-16 00:19:24 +08:00

96 lines
3.1 KiB
C#

using System.Threading.Tasks;
using F9MES.Application.Im;
using F9MES.Common.Auth;
using F9MES.Common.Result;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace F9MES.Api.Controllers;
/// <summary>轻量 IM:单聊 + 系统通知(SSE 长连接实时推送)</summary>
[ApiController]
[Route("api/im")]
[Authorize]
public class ImController : ControllerBase
{
private readonly ImService _im;
private readonly ImEventHub _hub;
private readonly CurrentUserService _currentUser;
public ImController(ImService im, ImEventHub hub, CurrentUserService currentUser)
{
_im = im;
_hub = hub;
_currentUser = currentUser;
}
/// <summary>
/// SSE 长连接:订阅新消息/通知的实时推送。
/// 客户端用 fetch 流式读取(EventSource 无法携带 Authorization header),
/// 收到 message 事件后刷新会话/消息列表即可,无需再轮询。
/// </summary>
[HttpGet("events")]
public async Task Events(CancellationToken ct)
{
var me = _currentUser.UserId;
if (me <= 0)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
_im.TouchActive();
Response.ContentType = "text/event-stream";
Response.Headers.CacheControl = "no-cache";
Response.Headers.Append("Connection", "keep-alive");
await _hub.SubscribeAsync(me, Response, ct);
}
/// <summary>会话列表</summary>
[HttpGet("sessions")]
public async Task<ActionResult> Sessions()
{
var list = await _im.GetSessionsAsync();
return Ok(ApiResult.Ok(list));
}
/// <summary>与某人的聊天记录</summary>
[HttpGet("messages")]
public async Task<ActionResult> Messages([FromQuery] long peerId, [FromQuery] int page = 1, [FromQuery] int size = 20)
{
var (items, total) = await _im.GetMessagesAsync(peerId, page, size);
return Ok(ApiResult.Ok(new { items, total, page, size }));
}
/// <summary>发送单聊消息</summary>
[HttpPost("send")]
public async Task<ActionResult> Send([FromBody] ImSendInput input)
{
await _im.SendAsync(input.PeerId, input.Content);
return Ok(ApiResult.Ok(true, "发送成功"));
}
/// <summary>标记与某人的会话已读</summary>
[HttpPost("read")]
public async Task<ActionResult> Read([FromBody] ImReadInput input)
{
await _im.ReadAsync(input.PeerId);
return Ok(ApiResult.Ok(true));
}
/// <summary>未读消息总数(顶栏角标)</summary>
[HttpGet("unread-count")]
public async Task<ActionResult> UnreadCount()
{
var count = await _im.GetUnreadCountAsync();
return Ok(ApiResult.Ok(count));
}
/// <summary>系统/业务通知群发(工单/工艺单变更等)</summary>
[HttpPost("notify")]
public async Task<ActionResult> Notify([FromBody] ImNotifyInput input)
{
var count = await _im.NotifyAsync(input);
return Ok(ApiResult.Ok(count, $"已发送 {count} 条通知"));
}
}