基于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
相关产品推荐
相关产品推荐

