using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using OASystem.Domain.Dtos.Task; using OASystem.Domain.Entities.Groups; using OASystem.Domain.Entities.WorkOrder; using OASystem.Domain.ViewModels.JuHeExchangeRate; using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice; using Dm.util; namespace OASystem.API.Controllers { /// /// 部门任务相关 /// //[Authorize] [Route("api/[controller]/[action]")] public class TaskController : ControllerBase { readonly SqlSugarClient _sqlsugar; public TaskController(SqlSugarClient sqlsugar) { _sqlsugar = sqlsugar; } /// /// 数据列表 /// /// /// [HttpPost] public async Task GetTaskList(GetTaskListDto dto) { var jw = JsonView(false); if (dto.TypeId < 1 || dto.UserId < 1) { jw.Msg = "参数有误!"; return Ok(jw); } DateTime startTime; DateTime endTime = DateTime.Now; var expr = Expressionable.Create().And(x => x.IsDel == 0) .And(x => x.TypeId == dto.TypeId) .And(x => (x.CreateUserId == dto.UserId || x.AssignedUserId == dto.UserId)) .AndIF(!string.IsNullOrEmpty(dto.Search_Name), x => x.Name.Contains(dto.Search_Name)) // .AndIF(dto.Search_UserId > 0, x => (x.CreateUserId == dto.Search_UserId || x.AssignedUserId == dto.Search_UserId)) .AndIF(DateTime.TryParse(dto.Seach_StartTime, out startTime) && DateTime.TryParse(dto.Seach_EndTime, out endTime), x => x.StartTime >= startTime && x.StartTime <= endTime); var query = _sqlsugar.Queryable() .LeftJoin((x, y) => x.GroupId == y.Id && y.IsDel == 0) .LeftJoin((x, y, z) => x.ForeignOptionId == z.Id && z.IsDel == 0) .LeftJoin((x, y, z, w) => x.AssignedUserId == w.Id && w.IsDel == 0) .Where(expr.ToExpression()); var count = query.Count(); var temp = query.Select((x, y, z, w) => new { Id = x.Id, TaskName = x.Name, StartTime = x.StartTime.ToString("yyyy-MM-dd HH:mm"), Assignee = w.CnName, ExternalOption = z.Name, Team = y.TeamName, Active = x.ProgressId, CreateUserid = x.CreateUserId }); var list = temp .OrderByIF(dto.isLv == 1, x => SqlFunc.IsNull( SqlFunc.Subqueryable() .Where(twt => twt.IsDel == 0 && twt.IsUrgent && twt.WorkOrderId == x.Id) .Min(twt => twt.StartTime), DateTime.MaxValue // 把 null 替换成极大值 ) ) .OrderBy(x => x.StartTime) .ToPageList(dto.PageIndex, dto.PageSize); var data = list.Select(x => { var taskArr = _sqlsugar.Queryable() .Where(y => y.IsDel == 0 && y.WorkOrderId == x.Id) .ToList(); var ExtraStep = taskArr.Where(x => x.IsExtraTask == true); var historyStep = _sqlsugar.Queryable() .Where(y => y.IsDel == 0 && y.WorkOrderId == x.Id && ExtraStep.Select(z => z.Id).Contains(y.WorkTaskId) && y.IsApproved == 1) .Select(x => x.WorkTaskId) .ToList(); return new WorkOrdeView { Id = x.Id, TaskName = x.TaskName, StartTime = x.StartTime, Assignee = x.Assignee, ExternalOption = x.ExternalOption, Team = string.IsNullOrWhiteSpace(x.Team) ? "未选择" : x.Team, StepInfo = new StepInfo { Active = x.Active, ExtraActive = 1, Steps = taskArr.Where(x => x.IsExtraTask == false) .OrderBy(x => x.Sort) .Select(x => new Steps { Id = x.Id, Name = x.Name, }).ToList(), ExtraStep = ExtraStep .OrderBy(x => x.Sort) .Select(x => new ExtraSteps { Id = x.Id, Name = x.Name, Approved = historyStep.Contains(x.Id) ? 1 : 0, }).ToList() }, isReview = x.CreateUserid == dto.UserId, UrgentCount = taskArr.Where(x => x.IsUrgent).Count() }; }).ToList(); jw.Data = data; jw.Count = count; jw.Msg = "查询成功!"; jw.Code = 200; return Ok(jw); } /// /// 计算两个时间之间的工作小时数 /// 工作时间:周一至周五,上午9:00-12:00,下午13:30-18:00 /// /// 开始时间 "yyyy-MM-dd HH:mm" /// 结束时间 "yyyy-MM-dd HH:mm" /// 工作小时数 private double CalcWorkHoursExact(string startStr, string endStr) { DateTime start = DateTime.ParseExact(startStr, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture); DateTime end = DateTime.ParseExact(endStr, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture); if (end <= start) return 0; // 每天工作时间段(分钟) (int Start, int End)[] workPeriods = { (9 * 60, 12 * 60), // 上午 9:00-12:00 (13 * 60 + 30, 18 * 60) // 下午 13:30-18:00 }; double totalMinutes = 0; DateTime current = start; while (current < end) { DayOfWeek day = current.DayOfWeek; if (day >= DayOfWeek.Monday && day <= DayOfWeek.Friday) // 工作日 { foreach (var period in workPeriods) { DateTime periodStart = current.Date.AddMinutes(period.Start); DateTime periodEnd = current.Date.AddMinutes(period.End); DateTime effectiveStart = periodStart > current ? periodStart : current; DateTime effectiveEnd = periodEnd < end ? periodEnd : end; if (effectiveEnd > effectiveStart) { totalMinutes += (effectiveEnd - effectiveStart).TotalMinutes; } } } // 下一天 0 点 current = current.Date.AddDays(1); } return totalMinutes / 60.0; // 转为小时 } /// /// 根据开始时间和工作小时数,计算结束日期时间 /// /// 开始时间 /// 工作小时数 /// 结束时间 private DateTime CalcEndDate(DateTime start, double workHours) { // 工作时间段(分钟) (int Start, int End)[] workPeriods = { (9 * 60, 12 * 60), // 上午 9:00-12:00 (13 * 60 + 30, 18 * 60) // 下午 13:30-18:00 }; double remainingMinutes = workHours * 60; DateTime current = start; while (remainingMinutes > 0) { DayOfWeek day = current.DayOfWeek; if (day >= DayOfWeek.Monday && day <= DayOfWeek.Friday) // 工作日 { foreach (var period in workPeriods) { DateTime periodStart = current.Date.AddMinutes(period.Start); DateTime periodEnd = current.Date.AddMinutes(period.End); // 如果当前时间超过本段结束,则跳过 if (current >= periodEnd) continue; // 当前段开始时间 DateTime effectiveStart = current > periodStart ? current : periodStart; double availableMinutes = (periodEnd - effectiveStart).TotalMinutes; if (availableMinutes >= remainingMinutes) { // 剩余工作时间在本段可以完成 return effectiveStart.AddMinutes(remainingMinutes); } else { remainingMinutes -= availableMinutes; } } } // 下一天 0点 current = current.Date.AddDays(1); current = current.Date.AddHours(0); } return current; // 理论上不会到这里 } private string GetAdjustedStartTime() { // 获取当前时间 DateTime now = DateTime.Now; // 获取年月日、小时和分钟 int year = now.Year; int month = now.Month; int day = now.Day; int hours = now.Hour; int minutes = now.Minute; // 调整分钟数,使其能被10整除 if (minutes % 10 != 0) { minutes = (int)Math.Ceiling(minutes / 10.0) * 10; // 如果分钟超过59,则进位到下一小时 if (minutes > 59) { minutes = 0; hours++; // 如果小时进位到24,则日期需要增加一天 if (hours >= 24) { hours = 0; // 如果小时为24,日期需要增加一天 DateTime nextDay = now.AddDays(1); year = nextDay.Year; month = nextDay.Month; day = nextDay.Day; } } } // 格式化时间为 "yyyy-MM-dd HH:mm" return new DateTime(year, month, day, hours, minutes, 0).ToString("yyyy-MM-dd HH:mm"); } /// /// 新增/编辑操作基本数据 /// /// /// [HttpPost] public async Task TaskInit(TaskInitDto dto) { if (dto.TypeId < 1) { return Ok(JsonView(false, "参数有误!")); } var jw = JsonView(false); var groupList = _sqlsugar.Queryable().Where(x => x.IsDel == 0).OrderByDescending(x => x.VisitStartDate).Select(x => new { x.Id, x.TeamName }).ToList(); groupList.Insert(0, new { Id = -1, TeamName = "未选择" }); var setdataids = new List() { 122, 123 }; var setData = _sqlsugar.Queryable().Where(x => x.IsDel == 0 && setdataids.Contains(x.STid)).Select(x => new { x.Id, x.STid, x.Name }).ToList(); var taskLv = setData.Where(x => x.STid == 122); var foreignLv = setData.Where(x => x.STid == 123); var users = _sqlsugar.Queryable().Where(x => x.IsDel == 0).Select(x => new { x.Id, x.CnName }).ToList(); users.Insert(0, new { Id = 0, CnName = "未选择" }); var defaultTask = new ArrayList(); var startTime = GetAdjustedStartTime(); if (DateTime.TryParse(startTime, out DateTime thisTime)) { var taskArr = _sqlsugar.Queryable() .Where(x => x.IsDel == 0 && x.TypeId == dto.TypeId) .OrderBy(x => x.Sort).ToList(); var i = 1; foreach (var item in taskArr) { var endTime = CalcEndDate(thisTime, item.DurationHours); defaultTask.Add(new WorkTaskView { Name = item.Name, PriorityId = item.PriorityId, IsUrgent = item.IsUrgent, StartTime = thisTime, EndTime = endTime, DurationHours = item.DurationHours, IsExtraTask = false, Sort = i }); i++; thisTime = endTime; } } var data = new { GroupList = groupList, TaskLv = taskLv, ForeignLv = foreignLv, Users = users, DefaultTask = defaultTask, startTime }; jw.Data = data; jw.Code = 200; jw.Msg = "SUCCESS!"; return Ok(jw); } /// /// 工单详情 /// /// /// [HttpPost] public async Task TaskDetail(TaskDetailDto dto) { var jw = JsonView(false); var groupList = _sqlsugar.Queryable().Where(x => x.IsDel == 0).OrderByDescending(x => x.VisitStartDate).Select(x => new { x.Id, x.TeamName }).ToList(); groupList.Insert(0, new { Id = -1, TeamName = "未选择" }); var setdataids = new List() { 122, 123 }; var setData = _sqlsugar.Queryable().Where(x => x.IsDel == 0 && setdataids.Contains(x.STid)).Select(x => new { x.Id, x.STid, x.Name }).ToList(); var taskLv = setData.Where(x => x.STid == 122); var foreignLv = setData.Where(x => x.STid == 123); var users = _sqlsugar.Queryable().Where(x => x.IsDel == 0).Select(x => new { x.Id, x.CnName }).ToList(); users.Insert(0, new { Id = 0, CnName = "未选择" }); var workOrder = _sqlsugar.Queryable().First(x => x.Id == dto.Id && x.IsDel == 0); var tasks = _sqlsugar.Queryable() .Where(x => x.IsDel == 0 && x.WorkOrderId == workOrder.Id) .OrderBy(x => x.Sort) .Select() .ToList(); var data = new { GroupList = groupList, TaskLv = taskLv, ForeignLv = foreignLv, Users = users, workOrder = new { workOrder.Id, workOrder.Name, workOrder.Remark, workOrder.AssignedUserId, workOrder.ForeignOptionId, workOrder.GroupId, workOrder.StartTime, Action = workOrder.ProgressId }, mainTask = tasks.Where(x => x.IsExtraTask == false).ToList(), extraTask = tasks.Where(x => x.IsExtraTask == true).ToList() }; jw.Data = data; jw.Code = 200; jw.Msg = "SUCCESS!"; return Ok(jw); } /// /// 新增/编辑操作 /// /// /// [HttpPost] public async Task TaskOperation(TaskOperationDto dto) { var jw = JsonView(false); jw.Data = ""; if (dto.TypeId < 1 || dto.AssignedUserId < 1) { return Ok(JsonView(false, "参数有误!")); } if (!DateTime.TryParse(dto.StartTime, out DateTime startTime)) { return Ok(JsonView(false, "时间格式不正确!")); } Task_WorkOrder workOrder = new Task_WorkOrder { Id = dto.Id, AssignedUserId = dto.AssignedUserId, CreateTime = DateTime.Now, CreateUserId = dto.CreateUserId, ForeignOptionId = dto.ForeignOptionId, GroupId = dto.GroupId, ProgressId = dto.Action, TypeId = dto.TypeId, IsDel = 0, Name = dto.Name, StartTime = startTime, }; if (dto.Id == 0) { _sqlsugar.BeginTran(); try { var workOrderId = _sqlsugar.Insertable(workOrder).ExecuteReturnIdentity(); workOrder.Id = workOrderId; List workTasks = dto.Tasks.Select(x => new Task_WorkTask { AssignedUserId = x.AssignedUserId, CreateTime = DateTime.Now, CreateUserId = dto.CreateUserId, DurationHours = x.DurationHours, IsUrgent = x.IsUrgent, PriorityId = x.PriorityId, WorkOrderId = workOrderId, IsDel = 0, StartTime = x.StartTime, EndTime = x.EndTime, IsExtraTask = x.IsExtraTask, Name = x.Name, Sort = x.Sort }).ToList(); _sqlsugar.Insertable(workTasks).ExecuteCommand(); _sqlsugar.CommitTran(); jw.Code = 200; jw.Msg = "SUCCESS!"; } catch (Exception ex) { _sqlsugar.RollbackTran(); jw.Code = 500; jw.Msg = ex.Message; } } else { _sqlsugar.BeginTran(); try { var workOrderId = workOrder.Id; _sqlsugar.Updateable(workOrder) .IgnoreColumns(x => new { x.CreateTime, x.CreateUserId }) .ExecuteCommand(); List workTasks = dto.Tasks.Select(x => new Task_WorkTask { Id = x.Id, AssignedUserId = x.AssignedUserId, DurationHours = x.DurationHours, IsUrgent = x.IsUrgent, PriorityId = x.PriorityId, WorkOrderId = workOrderId, IsDel = 0, StartTime = x.StartTime, EndTime = x.EndTime, IsExtraTask = x.IsExtraTask, Name = x.Name, Sort = x.Sort }).ToList(); var insertArr = workTasks.Where(x => x.Id == 0).ToList(); var updateArr = workTasks.Where(x => x.Id > 0).ToList(); var notdeleteArr = updateArr.Select(x => x.Id); _sqlsugar.Updateable().Where(x => x.WorkOrderId == workOrderId && !notdeleteArr.Contains(x.Id)) .SetColumns(x => new Task_WorkTask { IsDel = 1, DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm"), DeleteUserId = dto.CreateUserId }) .ExecuteCommand(); if (insertArr.Any()) { insertArr.ForEach(x => { x.CreateUserId = dto.CreateUserId; x.CreateTime = DateTime.Now; }); _sqlsugar.Insertable(insertArr).ExecuteCommand(); } if (updateArr.Any()) { _sqlsugar.Updateable(updateArr) .IgnoreColumns(x => new { x.CreateTime, x.CreateUserId }) .ExecuteCommand(); } _sqlsugar.CommitTran(); jw.Code = 200; jw.Msg = "SUCCESS!"; } catch (Exception ex) { _sqlsugar.RollbackTran(); jw.Code = 500; jw.Msg = ex.Message; } } _ = AppNoticeLibrary.SendUserMsg_Task_ToUser(workOrder.Id, new List() { dto.AssignedUserId.toString(), "235" } , dto.Id == 0); return Ok(jw); } /// /// 获取历史回执数据 /// /// /// [HttpPost] public async Task GetWorkTaskReceipt(GetWorkTaskReceiptDto dto) { var jw = JsonView(false); if (dto.OrderId < 1 || dto.TaskId < 1) { jw.Msg = "参数有误!"; return Ok(jw); } var data = _sqlsugar.Queryable() .Where(x => x.WorkOrderId == dto.OrderId && x.WorkTaskId == dto.TaskId && x.IsDel == 0) .Select() .ToList() .Select(x => { var Files = JsonConvert.DeserializeObject>(x.FilePath); return new { x.WorkTaskId, x.WorkOrderId, x.Remark, x.Id, x.RejectReason, x.Content, x.IsApproved, x.IsCompleted, Files = Files.Select(y => { var sp = y.Split('/'); string filename = sp[sp.Length - 1]; string ftpUrl = AppSettingsHelper.Get("TaskFileBaseUrl") + y.Replace(AppSettingsHelper.Get("TaskFileBasePath"), AppSettingsHelper.Get("TaskFileFtpPath")); return new { FileName = filename, Url = ftpUrl, }; }) }; }); jw.Data = data; jw.Code = 200; jw.Msg = "SUCCESS"; return Ok(jw); } /// /// 任务回执提交 /// /// /// [HttpPost] public async Task SubmitWorkTaskReceipt([FromForm] SubmitWorkTaskReceiptDto dto) { var jw = JsonView(false); jw.Data = ""; var orderInfo = _sqlsugar.Queryable().First(x => x.Id == dto.WorkOrderId); if (orderInfo == null) { jw.Msg = "参数有误!"; return Ok(jw); } var filePathArr = new List(); if (dto.Files != null && dto.Files.Count > 0) { string winPath = AppSettingsHelper.Get("TaskFileBasePath") + $"{orderInfo.Name}/"; if (!Directory.Exists(winPath)) { Directory.CreateDirectory(winPath); } //保存文件 foreach (var item in dto.Files) { var fileName = item.FileName; var saveFilePath = winPath + fileName; using (FileStream fs = System.IO.File.Create(saveFilePath)) { item.CopyTo(fs); fs.Flush(); } filePathArr.Add(saveFilePath); } } var task = _sqlsugar.Queryable(). First(x => x.Id == dto.WorkTaskId && x.IsDel == 0); if (task == null) { jw.Msg = "任务不存在或已删除!"; return Ok(jw); } Task_WorkTaskReceipt data = new Task_WorkTaskReceipt { Content = dto.Content, CreateTime = DateTime.Now, CreateUserId = dto.UserId, IsApproved = 0, IsCompleted = dto.IsCompleted, WorkTaskId = dto.WorkTaskId, WorkOrderId = dto.WorkOrderId, FilePath = JsonConvert.SerializeObject(filePathArr) }; var count = _sqlsugar.Insertable(data).ExecuteCommand(); if (count > 0) { _ = AppNoticeLibrary.SendUserMsg_TaskSubMit_ToUser(task.Id, new List() { task.CreateUserId.toString() }); } jw.Code = 200; jw.Msg = $"{count}条回执单、{dto.Files}个文件保存成功!"; return Ok(jw); } /// /// 软删除工作订单 /// /// 删除参数 /// [HttpPost] public async Task SoftDeleteWorkOrder(SoftDeleteWorkOrderDto dto) { var jw = JsonView(false); if (dto.WorkOrderId < 1 || dto.UserId < 1) { jw.Msg = "参数有误!"; return Ok(jw); } // 检查工作订单是否存在且未被删除 var workOrder = _sqlsugar.Queryable() .First(x => x.Id == dto.WorkOrderId && x.IsDel == 0); if (workOrder == null) { jw.Msg = "工作订单不存在或已被删除!"; return Ok(jw); } // 检查权限:只有创建者或管理员可以删除 if (workOrder.CreateUserId != dto.UserId) { jw.Msg = "权限不足,无法删除此工作订单!"; return Ok(jw); } _sqlsugar.BeginTran(); try { // 软删除工作订单 var updateWorkOrderResult = _sqlsugar.Updateable() .SetColumns(x => new Task_WorkOrder { IsDel = 1, DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), DeleteUserId = dto.UserId }) .Where(x => x.Id == dto.WorkOrderId) .ExecuteCommand(); // 软删除相关的工作任务 var updateWorkTaskResult = _sqlsugar.Updateable() .SetColumns(x => new Task_WorkTask { IsDel = 1, DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), DeleteUserId = dto.UserId }) .Where(x => x.WorkOrderId == dto.WorkOrderId) .ExecuteCommand(); // 软删除相关的回执单 var updateReceiptResult = _sqlsugar.Updateable() .SetColumns(x => new Task_WorkTaskReceipt { IsDel = 1, DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), DeleteUserId = dto.UserId }) .Where(x => x.WorkOrderId == dto.WorkOrderId) .ExecuteCommand(); _sqlsugar.CommitTran(); jw.Code = 200; jw.Msg = "工作订单删除成功!"; jw.Data = new { WorkOrderId = dto.WorkOrderId, DeletedWorkTasks = updateWorkTaskResult, DeletedReceipts = updateReceiptResult, DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), }; } catch (Exception ex) { _sqlsugar.RollbackTran(); jw.Code = 500; jw.Msg = $"删除失败:{ex.Message}"; } return Ok(jw); } /// /// 回执审核 /// /// /// [HttpPost] public async Task AuditWorkTaskReceipt(AuditWorkTaskReceiptDto dto) { var jw = JsonView(false); if (dto.ReceiptId < 1 || dto.ApproverId < 1) { jw.Msg = "参数有误!"; return Ok(jw); } if (dto.Approve == -1 && string.IsNullOrWhiteSpace(dto.RejectReason)) { jw.Msg = "驳回必须填写原因!"; return Ok(jw); } var receipt = _sqlsugar.Queryable() .Includes(o => o.WorkTask) .First(x => x.Id == dto.ReceiptId && x.IsDel == 0); if (receipt == null) { jw.Msg = "回执单不存在或已删除!"; return Ok(jw); } try { _sqlsugar.BeginTran(); var existsApproved = _sqlsugar.Queryable() .First(x => x.WorkOrderId == receipt.WorkOrderId && x.WorkTaskId == receipt.WorkTaskId && x.IsDel == 0 && x.IsApproved == 1); var affected = _sqlsugar.Updateable() .SetColumns(x => new Task_WorkTaskReceipt { IsApproved = dto.Approve, RejectReason = dto.Approve == -1 ? dto.RejectReason : "" }) .Where(x => x.Id == dto.ReceiptId) .ExecuteCommand(); if (existsApproved == null && dto.Approve == 1 && !receipt.WorkTask.IsExtraTask) { _sqlsugar.Updateable() .SetColumns(x => new Task_WorkOrder { ProgressId = x.ProgressId + 1 }) .Where(x => x.Id == receipt.WorkOrderId) .ExecuteCommand(); } _sqlsugar.CommitTran(); //企微通知 _ = AppNoticeLibrary.SendUserMsg_TaskAudit_ToUser (receipt.WorkTaskId, dto.Approve == 1, dto.RejectReason, new List() { receipt.WorkTask.AssignedUserId.toString() }); jw.Code = 200; jw.Msg = "审核完成!"; jw.Data = new { dto.ReceiptId, Status = dto.Approve == 1 ? "审核通过" : "审核驳回", dto.RejectReason, receipt.WorkOrderId, Affected = affected }; } catch (Exception ex) { _sqlsugar.RollbackTran(); jw.Code = 500; jw.Msg = $"审核失败:{ex.Message}"; } return Ok(jw); } } }