You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Dotnet Core与SignalR实现单表单多用户编辑限制及实时通知

基于Dotnet Core + SignalR实现单表单多用户编辑限制与实时通知

这个需求完全可以实现,下面是具体的实现思路和关键步骤:

1. 核心设计逻辑

  • 为每个表单分配唯一标识(比如表单ID),用户打开表单时通过SignalR加入对应表单ID的专属分组
  • 用户触发编辑操作时,向SignalR服务发送锁定请求,服务端记录当前锁定该表单的用户信息
  • 其他用户尝试编辑同一表单时,服务端检查锁定状态,返回已锁定提示并实时推送通知
  • 锁定用户完成编辑、关闭页面或超时后,服务端释放锁定,同时通知所有关注该表单的用户

2. 关键代码实现

SignalR Hub 核心逻辑

public class FormEditHub : Hub
{
    // 生产环境建议替换为Redis等分布式缓存,避免多实例状态不一致
    private static readonly Dictionary<string, string> _formLocks = new();

    // 用户加入对应表单分组
    public async Task JoinFormGroup(string formId)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, formId);
        // 告知当前用户表单的锁定状态
        var lockedBy = _formLocks.TryGetValue(formId, out var user) ? user : null;
        await Clients.Caller.SendAsync("FormLockStatusUpdated", formId, lockedBy);
    }

    // 请求锁定表单
    public async Task LockForm(string formId, string userName)
    {
        if (!_formLocks.ContainsKey(formId))
        {
            _formLocks.Add(formId, userName);
            // 通知分组内所有用户表单已锁定
            await Clients.Group(formId).SendAsync("FormLocked", formId, userName);
        }
        else
        {
            // 告知当前用户表单已被他人锁定
            await Clients.Caller.SendAsync("FormAlreadyLocked", formId, _formLocks[formId]);
        }
    }

    // 释放表单锁定
    public async Task UnlockForm(string formId)
    {
        if (_formLocks.Remove(formId))
        {
            // 通知分组内所有用户表单已解锁
            await Clients.Group(formId).SendAsync("FormUnlocked", formId);
        }
    }

    // 用户断开连接时自动释放其锁定的表单
    public override async Task OnDisconnectedAsync(Exception exception)
    {
        var lockedForm = _formLocks.FirstOrDefault(kv => kv.Value == Context.User?.Identity?.Name);
        if (!string.IsNullOrEmpty(lockedForm.Key))
        {
            _formLocks.Remove(lockedForm.Key);
            await Clients.Group(lockedForm.Key).SendAsync("FormUnlocked", lockedForm.Key);
        }
        await base.OnDisconnectedAsync(exception);
    }
}

前端关键交互逻辑(JavaScript)

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/formEditHub")
    .build();

// 监听表单锁定通知
connection.on("FormLocked", (formId, userName) => {
    document.querySelector(`#${formId} .lock-notice`).textContent = `表单已被${userName}锁定,无法编辑`;
    // 禁用所有编辑控件
    document.querySelectorAll(`#${formId} input, #${formId} textarea, #${formId} .edit-btn`).forEach(el => el.disabled = true);
});

// 监听表单解锁通知
connection.on("FormUnlocked", (formId) => {
    document.querySelector(`#${formId} .lock-notice`).textContent = "";
    // 启用编辑控件
    document.querySelectorAll(`#${formId} input, #${formId} textarea, #${formId} .edit-btn`).forEach(el => el.disabled = false);
});

// 监听表单已锁定提示
connection.on("FormAlreadyLocked", (formId, userName) => {
    alert(`表单${formId}已被${userName}锁定`);
});

// 页面加载初始化
document.addEventListener("DOMContentLoaded", async () => {
    const formId = "order-form-1001"; // 实际从页面获取表单ID
    const userName = "zhangsan"; // 实际从登录态获取

    await connection.start();
    await connection.invoke("JoinFormGroup", formId);
    // 用户点击编辑按钮时调用LockForm,此处简化为页面加载时请求锁定
    await connection.invoke("LockForm", formId, userName);
});

// 提交表单后释放锁定
document.querySelector("#submit-form-btn").addEventListener("click", async () => {
    const formId = "order-form-1001";
    await connection.invoke("UnlockForm", formId);
    // 执行表单提交逻辑
});

3. 生产环境优化建议

  • 用Redis等分布式缓存替换静态字典,适配多服务器部署场景
  • 增加锁定超时机制,比如用户10分钟无操作自动释放锁定
  • 优化前端交互,用内联提示替代alert,显示锁定用户的详细信息
  • 增加权限校验,确保只有具备编辑权限的用户才能发起锁定请求

内容的提问来源于stack exchange,提问作者kalaivanan govindaraj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:25:02