/// <summary> /// 更新申请单工作流 /// </summary> /// <param name="dbContext"></param> /// <param name="dbOrders"></param> /// <param name="order"></param> /// <param name="process"></param> /// <param name="currentUser"></param> private void UpdateOrderProcess(MissionskyOAEntities dbContext, IEnumerable <Order> dbOrders, OrderModel order, WorkflowProcessModel process, UserModel currentUser) { //流程结束 && 批量申请,向流程申请人发推送消息 if (process.IsCompletedApproved() && order.IsBatchApply) { this._orderService.AddNotification(dbContext, order, currentUser.Id, process); //默认发送到申请人 } dbOrders.ToList().ForEach(entity => { entity.WorkflowId = process.FlowId; entity.Status = (int)process.NextStatus; if (process.IsCompletedApproved()) //申请通过 || 过[行政审阅]到[财务审阅] { this._orderService.AddNotification(dbContext, order, currentUser.Id, process, entity.UserId); } else //需要下一步审批 { if (process.NextApprover == null) { Log.Error("流程下一步审批人异常。"); throw new InvalidOperationException("流程下一步审批人异常。"); } entity.NextStep = process.NextStep.Id; entity.NextAudit = process.NextApprover.Id; this._orderService.AddNotification(dbContext, order, currentUser.Id, process, process.NextApprover.Id); } }); }
/// <summary> /// 流程撤销 /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="order">申请单</param> /// <param name="owner">申请人</param> /// <param name="operation">流程操作</param> /// <returns>下一个审批步骤</returns> public WorkflowProcessModel Revoke(MissionskyOAEntities dbContext, OrderModel order, UserModel owner, OperateOrderModel operation) { if (order == null) { Log.Error("参数无效。"); throw new InvalidOperationException("参数无效。"); } // 流程审批 WorkflowProcessModel process = _workflowProcessService.Process(dbContext, order, owner, operation); return(process); }
/// <summary> /// 添加流程处理记录 /// </summary> /// <param name="dbContext"></param> /// <param name="curUser"></param> /// <param name="operation"></param> public void AddWorkflowProcess(MissionskyOAEntities dbContext, UserModel curUser, OperateOrderModel operation) { var process = new WorkflowProcessModel() { OrderNo = operation.OrderNo, FlowId = 0, StepId = 0, Operator = curUser.Id, OperationDesc = string.Format("{0}取消了申请单。", curUser.EnglishName), Operation = operation.Operation, CreatedTime = DateTime.Now }; dbContext.WorkflowProcesses.Add(process.ToEntity()); }
public static WorkflowProcessModel ToModel(this WorkflowProcess entity) { var model = new WorkflowProcessModel() { Id = entity.Id, OrderNo = entity.OrderNo, FlowId = entity.FlowId, StepId = entity.StepId, Operator = entity.Operator, Operation = (WorkflowOperation)entity.Operation, OperationDesc = entity.OperationDesc, CreatedTime = entity.CreatedTime }; return(model); }
public static WorkflowProcess ToEntity(this WorkflowProcessModel model) { var entity = new WorkflowProcess() { Id = model.Id, OrderNo = model.OrderNo, FlowId = model.FlowId, StepId = model.StepId, Operator = model.Operator, Operation = model.Operation == WorkflowOperation.None ? (int)WorkflowOperation.Apply : (int)model.Operation, OperationDesc = model.OperationDesc, CreatedTime = model.CreatedTime }; return(entity); }
/// <summary> /// 工作流处理消息推送 /// </summary> /// <param name="dbContext"></param> /// <param name="order">申请单</param> /// <param name="operaterId">消息推送人Id</param> /// <param name="process">流程处理</param> /// <param name="receiverId">消息接收人Id</param> /// <remarks>receiverId = 0: 表示默认发送到代申请人</remarks> public void AddNotification(MissionskyOAEntities dbContext, OrderModel order, int operaterId, WorkflowProcessModel process, int receiverId = 0) { #region 1. 获取用户信息 //代理申请人 var agentUser = dbContext.Users.FirstOrDefault(it => it.Id == order.ApplyUserId); if (agentUser == null) { Log.Error(string.Format("找不到代申请人, Id: {0}", order.ApplyUserId)); throw new KeyNotFoundException("找不到代申请人。"); } //消息接收人 var receiver = dbContext.Users.FirstOrDefault(it => it.Id == receiverId); receiver = receiver ?? agentUser; //消息推送人 var operater = (operaterId == receiverId ? receiver : dbContext.Users.FirstOrDefault(it => it.Id == operaterId)); if (operater == null) { Log.Error(string.Format("找不到流程操作人, Id: {0}", receiverId)); throw new KeyNotFoundException("找不到流程操作人。"); } #endregion #region 2. 验证申请单 if (order == null || order.OrderDets == null || order.OrderDets.Count < 1) { Log.Error("无效的流程申请单。"); throw new InvalidOperationException("无效的流程申请单。"); } var detail = order.OrderDets.FirstOrDefault(); if (detail == null || !detail.IOHours.HasValue) { Log.Error("无效的流程申请单详细。"); throw new InvalidOperationException("无效的流程申请单详细。"); } // 委托人 var recipient = dbContext.Users.FirstOrDefault(it => it.Id == detail.Recipient); var checkType = order.OrderType; if (checkType == OrderType.DaysOff) { checkType = OrderType.Overtime; } #endregion #region 3. 批量申请,向代申请推送消息 var model = new NotificationModel() { Target = receiver.Email, CreatedUserId = operater.Id, //MessageType = NotificationType.PushMessage, MessageType = NotificationType.Email, BusinessType = BusinessType.Approving, Title = "Missionsky OA Notification", MessagePrams = "申请单流程处理", Scope = NotificationScope.User, CreatedTime = DateTime.Now, TargetUserIds = new List <int> { receiver.Id } }; //流程拒绝时,推送拒绝消息 if (process.Operation == WorkflowOperation.Rejecte) { model.MessageContent = string.Format("您的{0}单被拒绝。", order.IsOvertime() ? "加班" : "请假"); } //审批流程结束,或者到财务审批时(需要更新用户假期信息) else if (process.Operation == WorkflowOperation.Approve && (process.NextStep == null || process.NextStep.IsFinanceReviewing())) { if (receiverId == 0) //默认发送到代申请人 { model.MessageContent = string.Format("您的{0}单已批准。", order.IsOvertime() ? "加班" : "请假"); } else if (receiver.Id == receiverId) //发送到请假或加班实际申请人 { //假期汇总信息 var summary = dbContext.AttendanceSummaries.FirstOrDefault( it => it.UserId == receiverId && it.Type == (int)checkType && it.Year == DateTime.Now.Year); if (summary == null) { Log.Error("未找用户假期或加班信息。"); throw new KeyNotFoundException("未找用户假期或加班信息。"); } //计算剩余天数 var days = Math.Round(detail.IOHours.Value / 8, 1); //申请天数 var remainDays = summary.RemainValue.HasValue ? Math.Round(summary.RemainValue.Value / 8, 1) + days : 0.0; //剩余天数 days = Math.Abs(days); //取绝对值 if (order.OrderType == OrderType.AnnualLeave) //年假 { model.MessageContent = order.RefOrderId.HasValue ? string.Format("{0}, 您的年假增加{1}天,剩余{2}天。", receiver.EnglishName, days, remainDays) : string.Format("{0}, 您的年假被扣除{1}天,剩余{2}天。", receiver.EnglishName, days, remainDays); } else if (order.OrderType == OrderType.DaysOff) //调休 { model.MessageContent = order.RefOrderId.HasValue ? string.Format("{0}, 您的调休假增加{1}天,剩余{2}天。", receiver.EnglishName, days, remainDays) : string.Format("{0}, 您的调休假被扣除{1}天,剩余{2}天。", receiver.EnglishName, days, remainDays); } else if (order.OrderType == OrderType.Overtime) //加班 { model.MessageContent = order.RefOrderId.HasValue ? string.Format("{0}, 您的调休假被扣除{1}天,剩余{2}天。", receiver.EnglishName, days, remainDays) : string.Format("{0}, 您的调休假增加{1}天,剩余{2}天。", receiver.EnglishName, days, remainDays); } else //其它假期 { model.MessageContent = string.Format("您的{0}单已批准。", order.IsOvertime() ? "加班" : "请假"); } } else { Log.Error("推送消息接收人不匹配。"); throw new KeyNotFoundException("推送消息接收人不匹配。"); } } //申请或审批时,发送到下一步审批人 else { if (receiverId == 0) //默认发送到代申请人 { model.MessageContent = string.Format("请您及时审批{0}的{1}单, 时间从{2} {3}到{4} {5}, 总时长为{6}, {7}。", agentUser.EnglishName, order.IsOvertime() ? "加班" : "请假", detail.StartDate.ToString("yyyy-MM-dd"), OrderExtentions.FormatTimeSpan(detail.StartTime), detail.EndDate.ToString("yyyy-MM-dd"), OrderExtentions.FormatTimeSpan(detail.EndTime), Math.Abs((sbyte)detail.IOHours), recipient == null ? "没有工作交接人。" : "把工作交接给了" + recipient.EnglishName ); model.MessageContent = string.Format("您的{0}单已批准。", order.IsOvertime() ? "加班" : "请假"); } else if (receiver.Id == receiverId) //发送到请假或加班实际申请人 { model.MessageContent = string.Format("请您及时审批{0}的{1}单,时间从{2} {3}到{4} {5}, 总时长为{6}, {7}。", agentUser.EnglishName, order.IsOvertime() ? "加班" : "请假", detail.StartDate.ToString("yyyy-MM-dd"), OrderExtentions.FormatTimeSpan(detail.StartTime), detail.EndDate.ToString("yyyy-MM-dd"), OrderExtentions.FormatTimeSpan(detail.EndTime), Math.Abs((sbyte)detail.IOHours), recipient == null ? "没有工作交接人。" : "把工作交接给了" + recipient.EnglishName ); } else { Log.Error("推送消息接收人不匹配。"); throw new KeyNotFoundException("推送消息接收人不匹配。"); } } this._notificationService.Add(model, Global.IsProduction); //消息推送 #endregion }
/// <summary> /// 拒绝请假加班申请(撤销)单 /// </summary> /// <param name="model">申请单</param> /// <param name="approver">审批人</param> /// <returns>是否拒绝成功</returns> public bool Rejecte(OperateOrderModel model, UserModel approver) { if (model == null || model.OrderNo < 1 || approver == null) { Log.Error("无效的参数。"); throw new InvalidOperationException("无效的参数。"); } if (string.IsNullOrEmpty(model.Opinion)) { Log.Error("拒拒理由为空。"); throw new InvalidOperationException("拒拒理由为空。"); } using (var dbContext = new MissionskyOAEntities()) { #region 申请单详细 //申请单详细 var orderEntities = dbContext.Orders.Where(it => it.OrderNo == model.OrderNo); #region 验证申请是否有效 var validateEntity = orderEntities.FirstOrDefault(); //流程及步骤是否有效 if (validateEntity == null || validateEntity.WorkflowId < 1 || validateEntity.NextStep < 1) { Log.Error("申请单流程无效。"); throw new InvalidOperationException("申请单流程无效."); } var validateOrder = validateEntity.ToModel(); //申请单为撤销单 var isRevokedOrder = validateOrder.IsRevokedOrder(); //领导审批中 var isApproveStatus = validateOrder.IsApproveStatus(); //行政财务审批中 var isReviewStatus = validateOrder.IsReviewStatus(); //申请状态不是审批状态 if (!isApproveStatus && !isReviewStatus) { Log.Error("申请单状态无效。"); throw new InvalidOperationException("申请单状态无效."); } //用户不是申请当前的审批人 if (approver.Id != validateEntity.NextAudit) { Log.Error("当前用户不能审批申请。"); throw new InvalidOperationException("当前用户不能审批申请."); } //申请人详细 var applicant = dbContext.Users.FirstOrDefault(it => it.Id == validateEntity.ApplyUserId); if (applicant == null) { Log.Error("无效的申请人。"); throw new KeyNotFoundException("无效的申请人。"); } #endregion var hasSentToAapplicant = false; //已经向申请人发送过拒绝申请的推送消息 var operation = new OperateOrderModel() { Operation = WorkflowOperation.Rejecte, Opinion = model.Opinion }; WorkflowProcessModel process = _workflowProcessService.Process(dbContext, validateOrder, approver, operation); //拒绝申请 orderEntities.ToList().ForEach(o => { if (isRevokedOrder && !isApproveStatus) //拒绝撤销申请单,则更新原始申请单为通过状态 { var oldOrder = dbContext.Orders.FirstOrDefault(it => it.Id == o.RefOrderId.Value); if (oldOrder != null) { oldOrder.Status = (int)OrderStatus.Approved; } } //更新申请单状态[拒绝] o.Status = (int)OrderStatus.Rejected; o.NextAudit = null; //不再需要审批 o.NextStep = null; // //更新假期 //_askLeaveService.RecoverOrder(o.ToModel()); hasSentToAapplicant = (hasSentToAapplicant || o.UserId == applicant.Id); AddNotification(dbContext, validateOrder, approver.Id, process, o.Id); //向用户推送拒绝消息 }); if (!hasSentToAapplicant) { AddNotification(dbContext, validateOrder, approver.Id, process, applicant.Id); //向申请人推送拒绝消息 } #endregion #region 添加审计信息 //审计信息 var message = new AuditMessageModel() { Type = AuditMessageType.None, Status = AuditMessageStatus.Unread, CreatedTime = DateTime.Now, Message = "你拒绝{0}的{1}申请" }; //同意加班申请/撤销消息 if (validateOrder.IsOvertime()) { message.Type = AuditMessageType.Recject_OT_Application_Message; message.Message = string.Format(message.Message, applicant.EnglishName, (validateOrder.IsRevokedOrder() ? "加班撤销" : "加班")); } //同意请假申请/撤销消息 if (validateOrder.IsAskLeave()) { message.Type = AuditMessageType.Recject_Leave_Application_Message; message.Message = string.Format(message.Message, applicant.EnglishName, (validateOrder.IsRevokedOrder() ? "请假撤销" : "请假")); } dbContext.AuditMessages.Add(message.ToEntity()); #endregion //更新数据库 dbContext.SaveChanges(); return(true); } }
/// <summary> /// 流程处理 /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="order">申请单</param> /// <param name="operation">流程操作</param> /// <param name="curUser">当前用户</param> /// <returns>流程处理对象</returns> /// <remarks> /// 流程逻辑: /// 1, 提交申请时,查找申请人的直接领导, /// 2, 如果申请人的直接领导可以审批通过,则转到行政审批 /// 3, 如果申请人的直接领导没有权限审批, 则转到审批人的直接领导,重复2的验证 /// 4, 如果申请人无直接领导,则转到行政审批 /// </remarks> public WorkflowProcessModel Process(MissionskyOAEntities dbContext, OrderModel order, UserModel curUser, OperateOrderModel operation) { try { #region 1, 验证申请单数据及状态 if (order == null || order.OrderDets == null || order.OrderDets.Count < 1) { Log.Error("申请单数据无效。"); throw new InvalidOperationException("申请单数据无效。"); } if (!order.IsApproveStatus() && !order.IsReviewStatus()) //无效的[审批或审阅]状态 { Log.Error("申请单状态无效。"); throw new KeyNotFoundException("申请单状态无效。"); } if (operation.Operation == WorkflowOperation.Approve && (order.WorkflowId == null || order.WorkflowId.Value < 1 || order.NextStep == null || order.NextStep.Value < 1 || order.NextAudit == null || order.NextAudit.Value < 1)) { Log.Error("申请单流程无效。"); throw new KeyNotFoundException("申请单流程无效。"); } var detail = order.OrderDets.FirstOrDefault(); //申请单详细 if (detail == null || !detail.IOHours.HasValue) { throw new KeyNotFoundException("申请单详细数据无效。"); } #endregion //流程处理 var workflow = GetWorkflow(dbContext, order, curUser, operation); //2, 选择流程 var nextStep = GotoNextStep(order, curUser, workflow, operation); //3, 选择流程下一步 var nextApprover = GotoNextApprover(dbContext, nextStep); //4, 转到流程下一步审批人 var nextStatus = SwitchStatus(order, nextStep, operation); //5, 转换申请单流程状态 UpdateAttendanceSummary(dbContext, order, workflow, nextStep, nextStatus); //6, 更新假期表 var process = new WorkflowProcessModel() { OrderNo = order.OrderNo, FlowId = workflow.Id, StepId = operation.Operation == WorkflowOperation.Apply || !order.NextStep.HasValue? 0 : order.NextStep.Value, NextStep = nextStep, NextApprover = nextApprover, NextStatus = nextStatus, Operator = curUser.Id, OperationDesc = operation.Operation == WorkflowOperation.Apply ? detail.Description : operation.Opinion, Operation = operation.Operation == WorkflowOperation.Apply ? WorkflowOperation.Apply : operation.Operation, CreatedTime = DateTime.Now }; dbContext.WorkflowProcesses.Add(process.ToEntity()); return(process); } catch (Exception ex) { throw ex; } }
/// <summary> /// 销请假单或者加班单 /// </summary> /// <returns>取消</returns> public bool RevokeOrder(int orderNo, RevokeOrderModel model, UserModel applicant) { using (var dbContext = new MissionskyOAEntities()) { #region 申请单信息 var revokeOrder = new OrderModel() { Status = OrderStatus.Apply, ApplyUserId = applicant.Id, IsBatchApply = (model.UserIds.Count() > 1), CreatedTime = DateTime.Now }; var orderDet = new OrderDetModel() { StartDate = model.StartDate, EndDate = model.EndDate, StartTime = model.StartDate.TimeOfDay, EndTime = model.EndDate.TimeOfDay, IOHours = model.IOHours, Description = model.RevokeReason, InformLeader = model.InformLeader, WorkTransfer = model.WorkTransfer, Recipient = model.Recipient }; revokeOrder.OrderNo = _orderService.GenerateOrderNo(dbContext); //生成申请单号 revokeOrder.OrderUsers = _userService.GetOrderUsers(dbContext, model.UserIds); //相关申请单用户 revokeOrder.OrderDets = new List <OrderDetModel>() { orderDet }; //验证申请单 Order oldEntity = null; revokeOrder.OrderUsers.ToList().ForEach(ou => { //申请单是否存在 oldEntity = dbContext.Orders.FirstOrDefault(o => o.UserId == ou.Id && o.OrderNo == orderNo); if (oldEntity == null) { Log.Error("此订单不存在。"); throw new KeyNotFoundException("此订单不存在。"); } ou.OrderId = oldEntity.Id; //撤销单Id //是否已经撤销过 var revokedEntity = dbContext.Orders.FirstOrDefault(o => o.RefOrderId.HasValue && o.RefOrderId == ou.OrderId); if (revokedEntity != null && !revokedEntity.ToModel().CanRevokeOrder()) { Log.Error("已经撤销一次,不能再撤销。"); throw new InvalidOperationException("已经撤销一次,不能再撤销。"); } }); revokeOrder.OrderType = (OrderType)oldEntity.OrderType; //申请单类型 var oldDetailEntity = dbContext.OrderDets.FirstOrDefault(it => it.OrderId == oldEntity.Id); if (oldDetailEntity != null && oldDetailEntity.IOHours.HasValue) { if (Math.Abs(oldDetailEntity.IOHours.Value) < Math.Abs(model.IOHours)) { Log.Error("撤销时长不正确。"); throw new InvalidOperationException("撤销时长不正确。"); } } //验证申请单是否有效,并转换IOHours正负值 ValidOrder(revokeOrder, WorkflowOperation.Revoke); #endregion //添加初始审批信息 AddAuditMessages(dbContext, revokeOrder, applicant); //对于同意的请假单或者加班单,自动填写新的撤销请假单 if (oldEntity.Status == (int)OrderStatus.Approved) { //添加申请单到数据库 var dbOrders = AddOrderToDb(dbContext, revokeOrder, WorkflowOperation.Revoke); //流程申请 WorkflowProcessModel process = _orderService.Apply(dbContext, revokeOrder); if (process == null) { Log.Error("找不到流程。"); throw new InvalidOperationException("找不到流程。"); } //更新申请单工作流 UpdateOrderProcess(dbContext, dbOrders, revokeOrder, process, applicant); try { dbContext.SaveChanges(); } catch (DbEntityValidationException dbEx) { LogDatabaseError(dbEx); throw dbEx; } return(true); } else { Log.Error("流程处理异常。"); throw new InvalidOperationException("流程处理异常。"); } } }
/// <summary> /// 添加请假信息 /// /// 处理请假申请单逻辑 /// /// </summary> /// <param name="model">申请单详细: 对应APP中的申请单详细</param> /// <param name="applicant">流程申请人Id</param> /// <returns>请假单信息</returns> public OrderModel AddOrder(ApplyOrderModel model, UserModel applicant) { /* * * * * **/ #region 申请单详细 // 1、新建申请单变量 var order = new OrderModel() { OrderType = model.OrderType, Status = OrderStatus.Apply, ApplyUserId = applicant.Id, IsBatchApply = (model.UserIds.Count() > 1), CreatedTime = DateTime.Now, }; // 2、新建申请单详情 var orderDet = new OrderDetModel() { StartDate = model.StartDate, EndDate = model.EndDate, StartTime = model.StartDate.TimeOfDay, EndTime = model.EndDate.TimeOfDay, Description = model.Description, InformLeader = model.InformLeader ?? false, WorkTransfer = model.WorkTransfer ?? false, Recipient = model.Recipient ?? 0, IOHours = model.IOHours }; // 3、将申请单详情变量 放入到 申请单变量中 order.OrderDets = new List <OrderDetModel>() { orderDet }; #endregion // 4、逻辑处理 using (var dbContext = new MissionskyOAEntities()) { // 1、生成申请单号 、申请单用户 // OrderNo --> int order.OrderNo = _orderService.GenerateOrderNo(dbContext); //生成申请单号 /* * OrderUsers --> IList<OrderUserModel> * model.UserIds --> 定义为 int[], 表示可以或者可能有多个申请用户 * * **/ order.OrderUsers = _userService.GetOrderUsers(dbContext, model.UserIds); // 2、验证申请单是否有效,并转换IOHours正负值 ValidOrder(order, WorkflowOperation.Apply); // 3、添加初始审批信息 AddAuditMessages(dbContext, order, applicant); // 4、添加申请单到数据库 var dbOrders = AddOrderToDb(dbContext, order, WorkflowOperation.Apply); // 5、流程申请 WorkflowProcessModel process = _orderService.Apply(dbContext, order); if (process == null) { Log.Error("找不到流程。"); throw new InvalidOperationException("找不到流程。"); } // 6、更新申请单工作流 UpdateOrderProcess(dbContext, dbOrders, order, process, applicant); // 7、发送工作交接通知 if (model.WorkTransfer.HasValue && model.WorkTransfer.Value && model.Recipient.HasValue && model.OrderType != OrderType.Overtime) { _orderService.AddNotification(dbContext, order); } // 8、更新申请单其它信息 dbContext.SaveChanges(); // 9、 return(order); } }
/// <summary> /// 流程需要继续审批 /// </summary> /// <param name="process"></param> /// <returns></returns> public static bool NeedContinueApprove(this WorkflowProcessModel process) { return(process.NextStep != null && process.NextApprover != null); }
/// <summary> /// 流程审批结束 /// </summary> /// <param name="process"></param> /// <returns></returns> public static bool IsCompletedApproved(this WorkflowProcessModel process) { return(process.NextStep == null || process.NextStep.IsFinanceReviewing()); }