ASP.NET MVC多流程统一审批前端泛型实现方案咨询及POC需求
统一审批流程的ASP.NET MVC实现方案
一、现有泛型实现的合理性分析
当前的泛型思路方向是合理的,但代码存在编译错误:RecruitmentDoc和LoanDoc继承的是未指定泛型参数的Document,正确的做法应该是让它们继承带具体业务数据类型的Document<T>。
用泛型类Document<T>封装不同流程的业务数据,同时统一承载审批通用属性(如流程ID、当前步骤、审批人等),既符合开闭原则(新增流程只需添加对应业务数据类,无需修改审批核心逻辑),也能为前端统一审批界面提供一致的数据模型支撑。
二、POC示例实现
1. 后端核心代码调整
泛型文档基类与流程业务数据类
// 泛型文档基类:封装通用审批属性+动态业务数据 public class Document<T> { public T Data { get; set; } public int WorkFlowId { get; set; } public string CurrentStep { get; set; } public int CurrentStepOrder { get; set; } public bool IsRejected { get; set; } public string ApproverId { get; set; } public Document(T data) { Data = data; } } // 招聘流程专属业务数据 public class RecruitmentData { public string PositionName { get; set; } public decimal SalaryRange { get; set; } public int RequiredExperience { get; set; } } // 招聘文档:绑定专属业务数据的泛型实例 public class RecruitmentDoc : Document<RecruitmentData> { public RecruitmentDoc(RecruitmentData data) : base(data) { } } // 贷款流程专属业务数据 public class LoanData { public decimal LoanAmount { get; set; } public int LoanTerm { get; set; } public string ApplicantId { get; set; } } // 贷款文档:绑定专属业务数据的泛型实例 public class LoanDoc : Document<LoanData> { public LoanDoc(LoanData data) : base(data) { } }
统一审批服务与控制器
// ProcessDetails表对应的实体类 public class ProcessDetail { public int WorkFlowId { get; set; } public string ApproverId { get; set; } public string StepName { get; set; } public int StepOrder { get; set; } public string ActionType { get; set; } } // 统一审批逻辑服务 public class ApprovalService { // 模拟从ProcessDetails表获取当前审批步骤(实际替换为数据库查询) public ProcessDetail GetCurrentApprovalStep(int workFlowId, int currentStepOrder) { return new ProcessDetail { WorkFlowId = workFlowId, ApproverId = User.Identity.Name, StepName = workFlowId == 1 ? "部门经理审批" : "风控专员审批", StepOrder = currentStepOrder, ActionType = "Approval" }; } // 统一处理审批/驳回操作 public bool PerformApproval(Document<object> document, bool isApproved, string comment) { // 1. 校验当前审批人权限(结合ProcessDetails表) // 2. 更新流程状态:通过则推进步骤,驳回则标记流程 // 3. 记录审批日志 return true; // 模拟操作成功 } } // 统一审批控制器 public class ApprovalController : Controller { private readonly ApprovalService _approvalService = new ApprovalService(); // 统一审批页面入口 public ActionResult Index(int workFlowId, int documentId) { var document = GetDocumentByWorkFlow(workFlowId, documentId); if (document == null) return HttpNotFound(); ViewBag.CurrentStep = _approvalService.GetCurrentApprovalStep(document.WorkFlowId, document.CurrentStepOrder); ViewBag.WorkFlowId = workFlowId; return View(document); } // 模拟从数据库获取对应流程的文档数据 private Document<object> GetDocumentByWorkFlow(int workFlowId, int documentId) { return workFlowId switch { 1 => new RecruitmentDoc(new RecruitmentData { PositionName = "高级后端开发", SalaryRange = 25000, RequiredExperience = 5 }) { WorkFlowId = 1, CurrentStep = "部门经理审批", CurrentStepOrder = 1, ApproverId = User.Identity.Name }, 2 => new LoanDoc(new LoanData { LoanAmount = 500000, LoanTerm = 360, ApplicantId = "cust007" }) { WorkFlowId = 2, CurrentStep = "风控专员审批", CurrentStepOrder = 2, ApproverId = User.Identity.Name }, _ => null }; } // 审批提交接口 [HttpPost] public ActionResult SubmitApproval(int workFlowId, bool isApproved, string comment, FormCollection form) { var document = GetDocumentByWorkFlow(workFlowId, int.Parse(form["DocumentId"])); bool operationResult = _approvalService.PerformApproval(document, isApproved, comment); if (operationResult) { return isApproved ? RedirectToAction("Index", new { workFlowId, documentId = form["DocumentId"] }) : RedirectToAction("Rejected", new { workFlowId, documentId = form["DocumentId"] }); } else { ModelState.AddModelError("", "审批操作失败,请重试"); return View("Index", document); } } }
2. Razor统一审批视图(Views/Approval/Index.cshtml)
@model Document<object> @{ ViewBag.Title = "统一审批页面"; var currentStep = ViewBag.CurrentStep as ProcessDetail; } <div class="approval-container"> <h2>当前审批环节:@currentStep.StepName</h2> <!-- 动态渲染不同流程的业务数据 --> <div class="document-content"> <h3>流程详情</h3> @if (Model.Data is RecruitmentData recruitment) { <div class="recruitment-card"> <p><strong>招聘岗位:</strong>@recruitment.PositionName</p> <p><strong>薪资范围:</strong>@recruitment.SalaryRange.ToString("C")</p> <p><strong>要求经验:</strong>@recruitment.RequiredExperience年</p> </div> } else if (Model.Data is LoanData loan) { <div class="loan-card"> <p><strong>贷款金额:</strong>@loan.LoanAmount.ToString("C")</p> <p><strong>贷款期限:</strong>@loan.LoanTerm个月</p> <p><strong>申请人ID:</strong>@loan.ApplicantId</p> </div> } </div> <!-- 统一审批操作区域 --> <div class="approval-actions"> @using (Html.BeginForm("SubmitApproval", "Approval", FormMethod.Post)) { @Html.Hidden("WorkFlowId", ViewBag.WorkFlowId) @Html.Hidden("DocumentId", 1) <!-- 实际从路由/模型获取文档ID --> <div class="form-group"> <label for="Comment">审批意见:</label> <textarea id="Comment" name="Comment" class="form-control" rows="3"></textarea> </div> <button type="submit" name="isApproved" value="true" class="btn btn-success mr-3">同意审批</button> <button type="submit" name="isApproved" value="false" class="btn btn-danger">驳回申请</button> } </div> </div> <style> .approval-container { max-width: 800px; margin: 2rem auto; padding: 2rem; border: 1px solid #eee; border-radius: 8px; } .document-content { margin: 1.5rem 0; padding: 1.2rem; background-color: #f8f9fa; border-radius: 4px; } .approval-actions { margin-top: 2rem; } </style>
三、关键优化建议
- 将动态渲染业务数据的逻辑拆分为Razor部分视图(如
_RecruitmentData.cshtml),通过Html.Partial动态加载,降低主视图复杂度 - 用AutoMapper简化不同文档类型的转换,避免控制器中硬编码类型判断
- 审批权限校验整合到ActionFilter中,统一处理权限拦截
- 流程状态更新使用数据库事务,确保数据一致性
内容的提问来源于stack exchange,提问作者user8512043
相关产品推荐
相关产品推荐

