Explorar el Código

task任务部分完善

yuanrf hace 1 día
padre
commit
708eb5512e

+ 5 - 1
.gitignore

@@ -360,4 +360,8 @@ MigrationBackup/
 .ionide/
 
 # Fody - auto-generated XML schema
-FodyWeavers.xsd
+FodyWeavers.xsd
+
+# VS Code
+.vscode/
+*.code-workspace

+ 6 - 6
OASystem/OASystem.Api/Controllers/GroupsController.cs

@@ -7666,7 +7666,7 @@ FROM
                             if (_EnterExitCosts.Visa > 0)
                             {
                                 //insidePayTotal += _EnterExitCosts.Visa;
-                                row1_1 = $"签证费:{_EnterExitCosts.Visa:#0.00} 人民币/人";
+                                row1_1 = $"签证费:{_EnterExitCosts.Visa:#0.00} /人";
                                 //if (!string.IsNullOrEmpty(_EnterExitCosts.VisaRemark))
                                 //{
                                 //    row1_1 += $"\t签证费用描述: {_EnterExitCosts.VisaRemark} 人民币/人";
@@ -7677,29 +7677,29 @@ FROM
                             if (_EnterExitCosts.YiMiao > 0)
                             {
                                 //insidePayTotal += _EnterExitCosts.YiMiao;
-                                row1_2 += $"疫苗费:{_EnterExitCosts.YiMiao:#0.00} 人民币/人";
+                                row1_2 += $"疫苗费:{_EnterExitCosts.YiMiao:#0.00} /人";
                             }
                             if (_EnterExitCosts.HeSuan > 0)
                             {
                                 //insidePayTotal += _EnterExitCosts.HeSuan;
-                                row1_2 += $"核酸检测费:{_EnterExitCosts.HeSuan:#0.00} 人民币/人";
+                                row1_2 += $"核酸检测费:{_EnterExitCosts.HeSuan:#0.00} /人";
                             }
                             if (_EnterExitCosts.Service > 0)
                             {
                                 //insidePayTotal += _EnterExitCosts.Service;
-                                row1_2 += $"服务费:{_EnterExitCosts.Service:#0.00} 人民币/人";
+                                row1_2 += $"服务费:{_EnterExitCosts.Service:#0.00} /人";
                             }
 
                             string row1_3 = "";
                             if (_EnterExitCosts.Safe > 0)
                             {
                                 //insidePayTotal += _EnterExitCosts.Safe;
-                                row1_3 += $"保险费:{_EnterExitCosts.Safe:#0.00} 人民币/人";
+                                row1_3 += $"保险费:{_EnterExitCosts.Safe:#0.00} /人";
                             }
                             if (_EnterExitCosts.Ticket > 0)
                             {
                                 //insidePayTotal += _EnterExitCosts.Ticket;
-                                row1_3 += $"参展门票:{_EnterExitCosts.Ticket:#0.00} 人民币/人";
+                                row1_3 += $"参展门票:{_EnterExitCosts.Ticket:#0.00} /人";
                             }
 
                             string row1 = "";

+ 783 - 0
OASystem/OASystem.Api/Controllers/TaskController.cs

@@ -0,0 +1,783 @@
+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;
+
+namespace OASystem.API.Controllers
+{
+    /// <summary>
+    /// 部门任务相关
+    /// </summary>
+    //[Authorize]
+    [Route("api/[controller]/[action]")]
+    public class TaskController : ControllerBase
+    {
+
+        readonly SqlSugarClient _sqlsugar;
+        public TaskController(SqlSugarClient sqlsugar)
+        {
+            _sqlsugar = sqlsugar;
+        }
+
+        [HttpPost]
+        public async Task<IActionResult> 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<Task_WorkOrder>().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<Task_WorkOrder>()
+                     .LeftJoin<Grp_DelegationInfo>((x, y) => x.GroupId == y.Id && y.IsDel == 0)
+                     .LeftJoin<Sys_SetData>((x, y, z) => x.ForeignOptionId == z.Id && z.IsDel == 0)
+                     .LeftJoin<Sys_Users>((x, y, z, w) => x.AssignedUserId == w.Id && w.IsDel == 0)
+                     .Where(expr.ToExpression());
+            var count = query.Count();
+
+            var list = 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
+            }).ToPageList(dto.PageIndex, dto.PageSize);
+
+
+            var data = list.Select(x =>
+            {
+                var taskArr = _sqlsugar.Queryable<Task_WorkTask>()
+                                        .Where(y => y.IsDel == 0 && y.WorkOrderId == x.Id)
+                                        .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).Select(x => new Steps
+                        {
+                            Id = x.Id,
+                            Name = x.Name,
+                        }).ToList(),
+                        ExtraStep = taskArr.Where(x => x.IsExtraTask == true).Select(x => new Steps
+                        {
+                            Id = x.Id,
+                            Name = x.Name,
+                        }).ToList()
+                    },
+                    isReview = x.CreateUserid == dto.UserId
+                };
+
+            }).ToList();
+
+            jw.Data = data;
+            jw.Count = count;
+            jw.Msg = "查询成功!";
+            jw.Code = 200;
+            return Ok(jw);
+        }
+
+        /// <summary>
+        /// 计算两个时间之间的工作小时数
+        /// 工作时间:周一至周五,上午9:00-12:00,下午13:30-18:00
+        /// </summary>
+        /// <param name="startStr">开始时间 "yyyy-MM-dd HH:mm"</param>
+        /// <param name="endStr">结束时间 "yyyy-MM-dd HH:mm"</param>
+        /// <returns>工作小时数</returns>
+        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; // 转为小时
+        }
+
+        /// <summary>
+        /// 根据开始时间和工作小时数,计算结束日期时间
+        /// </summary>
+        /// <param name="start">开始时间</param>
+        /// <param name="workHours">工作小时数</param>
+        /// <returns>结束时间</returns>
+        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");
+        }
+
+        /// <summary>
+        /// 新增/编辑操作基本数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public async Task<IActionResult> TaskInit(TaskInitDto dto)
+        {
+            if (dto.TypeId < 1)
+            {
+                return Ok(JsonView(false, "参数有误!"));
+            }
+
+            var jw = JsonView(false);
+            var groupList = _sqlsugar.Queryable<Grp_DelegationInfo>().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<int>() { 122, 123 };
+            var setData = _sqlsugar.Queryable<Sys_SetData>().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<Sys_Users>().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<Task_WorkOrderDefault>()
+                    .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<IActionResult> TaskDetail(TaskDetailDto dto)
+        {
+            var jw = JsonView(false);
+            var groupList = _sqlsugar.Queryable<Grp_DelegationInfo>().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<int>() { 122, 123 };
+            var setData = _sqlsugar.Queryable<Sys_SetData>().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<Sys_Users>().Where(x => x.IsDel == 0).Select(x => new { x.Id, x.CnName }).ToList();
+            users.Insert(0, new { Id = 0, CnName = "未选择" });
+
+            var workOrder = _sqlsugar.Queryable<Task_WorkOrder>().First(x => x.Id == dto.Id && x.IsDel == 0);
+
+            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
+                },
+                workTask = _sqlsugar.Queryable<Task_WorkTask>()
+                                    .Where(x => x.IsDel == 0 && x.WorkOrderId == workOrder.Id)
+                                    .OrderBy(x => x.Sort)
+                                    .Select<WorkTaskView>()
+                                    .ToList()
+            };
+            jw.Data = data;
+            jw.Code = 200;
+            jw.Msg = "SUCCESS!";
+
+            return Ok(jw);
+        }
+
+        /// <summary>
+        /// 新增/编辑操作
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public async Task<IActionResult> 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<Task_WorkOrder>(workOrder).ExecuteReturnIdentity();
+                    List<Task_WorkTask> 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<Task_WorkOrder>(workOrder).ExecuteCommand();
+                    List<Task_WorkTask> 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<Task_WorkTask>().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())
+                    {
+                        _sqlsugar.Insertable(insertArr).ExecuteCommand();
+                    }
+                    if (updateArr.Any())
+                    {
+                        _sqlsugar.Updateable(updateArr).ExecuteCommand();
+                    }
+
+                    _sqlsugar.CommitTran();
+                    jw.Code = 200;
+                    jw.Msg = "SUCCESS!";
+                }
+                catch (Exception ex)
+                {
+                    _sqlsugar.RollbackTran();
+                    jw.Code = 500;
+                    jw.Msg = ex.Message;
+                }
+            }
+
+            return Ok(jw);
+        }
+
+
+        [HttpPost]
+        public async Task<IActionResult> GetWorkTaskReceipt(GetWorkTaskReceiptDto dto)
+        {
+            var jw = JsonView(false);
+
+            if (dto.OrderId < 1 || dto.TaskId < 1)
+            {
+                jw.Msg = "参数有误!";
+                return Ok(jw);
+            }
+
+            var data = _sqlsugar.Queryable<Task_WorkTaskReceipt>()
+                .Where(x => x.WorkOrderId == dto.OrderId && x.WorkTaskId == dto.TaskId && x.IsDel == 0)
+                .Select<WorkTaskReceiptView>()
+                .ToList()
+                .Select(x =>
+                {
+                    var Files = JsonConvert.DeserializeObject<List<string>>(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<IActionResult> SubmitWorkTaskReceipt([FromForm] SubmitWorkTaskReceiptDto dto)
+        {
+            var jw = JsonView(false);
+            jw.Data = "";
+
+            var orderInfo = _sqlsugar.Queryable<Task_WorkOrder>().First(x => x.Id == dto.WorkOrderId);
+
+            if (orderInfo == null)
+            {
+                jw.Msg = "参数有误!";
+                return Ok(jw);
+            }
+
+            var filePathArr = new List<string>();
+            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);
+                }
+            }
+
+
+            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();
+            jw.Code = 200;
+            jw.Msg = $"{count}条回执单、{dto.Files}个文件保存成功!";
+
+            return Ok(jw);
+        }
+
+        /// <summary>
+        /// 软删除工作订单
+        /// </summary>
+        /// <param name="dto">删除参数</param>
+        /// <returns></returns>
+        [HttpPost]
+        public async Task<IActionResult> SoftDeleteWorkOrder(SoftDeleteWorkOrderDto dto)
+        {
+            var jw = JsonView(false);
+
+            if (dto.WorkOrderId < 1 || dto.UserId < 1)
+            {
+                jw.Msg = "参数有误!";
+                return Ok(jw);
+            }
+
+            // 检查工作订单是否存在且未被删除
+            var workOrder = _sqlsugar.Queryable<Task_WorkOrder>()
+                .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<Task_WorkOrder>()
+                    .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<Task_WorkTask>()
+                    .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<Task_WorkTaskReceipt>()
+                    .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);
+        }
+
+        /// <summary>
+        /// 回执审核
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public async Task<IActionResult> 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<Task_WorkTaskReceipt>()
+                .First(x => x.Id == dto.ReceiptId && x.IsDel == 0);
+
+            if (receipt == null)
+            {
+                jw.Msg = "回执单不存在或已删除!";
+                return Ok(jw);
+            }
+
+            try
+            {
+
+                _sqlsugar.BeginTran();
+
+                var affected = _sqlsugar.Updateable<Task_WorkTaskReceipt>()
+                    .SetColumns(x => new Task_WorkTaskReceipt
+                    {
+                        IsApproved = dto.Approve,
+                        RejectReason = dto.Approve == -1 ? dto.RejectReason : ""
+                    })
+                    .Where(x => x.Id == dto.ReceiptId)
+                    .ExecuteCommand();
+
+                _sqlsugar.Updateable<Task_WorkOrder>()
+                .SetColumns(x => new Task_WorkOrder
+                {
+                    ProgressId = x.ProgressId + 1
+                })
+                .Where(x => x.Id == receipt.WorkOrderId)
+                .ExecuteCommand();
+
+                _sqlsugar.CommitTran();
+
+                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);
+        }
+
+    }
+}

+ 4 - 0
OASystem/OASystem.Api/appsettings.json

@@ -154,6 +154,10 @@
   "ShareFileBasePath": "D:/FTP/File/OA2023/Office/ShareFile/",
   "ShareFileFtpPath": "Office/ShareFile/",
 
+  "TaskFileBaseUrl": "http://132.232.92.186:24/",
+  "TaskFileBasePath": "D:/FTP/File/OA2023/Office/TaskFile/",
+  "TaskFileFtpPath": "Office/TaskFile/",
+
   "ExcelTempPath": "D:/FTP/File/OA2023/Office/Excel/Template/",
   "GrpListFileBasePath": "D:/FTP/File/OA2023/Office/GrpFile/GroupList/",
   "GrpListFileFtpPath": "Office/GrpFile/GroupList/",

+ 282 - 0
OASystem/OASystem.Domain/Dtos/Task/GetTaskDefaultDto.cs

@@ -0,0 +1,282 @@
+using Microsoft.AspNetCore.Http;
+
+namespace OASystem.Domain.Dtos.Task
+{
+    public class GetTaskDefaultDto
+    {
+        public int TypeId { get; set; }
+    }
+
+    public class GetTaskListDto
+    {
+        public int UserId { get; set; }
+
+        public int PageSize { get; set; }
+
+        public int PageIndex { get; set; }
+
+        public int TypeId { get; set; }
+        public string Search_Name { get; set; }
+
+        //public int Search_UserId { get; set; }
+
+        public string Seach_StartTime { get; set; }
+
+        public string Seach_EndTime { get; set; }
+    }
+
+    public class TaskOperationDto
+    {
+        public int Id { get; set; }
+
+        public int CreateUserId { get; set; }
+        /// <summary>
+        /// 工单名称
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 任务开始时间
+        /// </summary>
+        public string StartTime { get; set; }
+
+        /// <summary>
+        /// 所属团组ID
+        /// </summary>
+        public int GroupId { get; set; }
+
+        /// <summary>
+        /// 指派用户ID
+        /// </summary>
+        public int AssignedUserId { get; set; }
+
+        /// <summary>
+        /// 外办选项ID
+        /// </summary>
+        public int ForeignOptionId { get; set; }
+
+        /// <summary>
+        /// 部门类型
+        /// </summary>
+        public int TypeId { get; set; }
+
+        /// <summary>
+        /// 步骤索引
+        /// </summary>
+        public int Action { get; set; }
+
+        public List<WorkTaskView> Tasks { get; set; }
+
+    }
+
+    public class TaskInitDto
+    {
+        public int TypeId { get; set; }
+    }
+
+    public class TaskDetailDto
+    {
+        public int Id { get; set; }
+    }
+
+
+    public class WorkTaskView
+    {
+        public int Id { get; set; }
+        /// <summary>
+        /// 任务名称
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 优先级ID
+        /// </summary>
+        public int PriorityId { get; set; }
+
+        /// <summary>
+        /// 是否加急
+        /// </summary>
+        public bool IsUrgent { get; set; }
+
+        /// <summary>
+        /// 指派用户ID
+        /// </summary>
+        public int AssignedUserId { get; set; }
+
+        /// <summary>
+        /// 任务开始时间
+        /// </summary>
+        public DateTime StartTime { get; set; }
+
+        /// <summary>
+        /// 任务结束时间
+        /// </summary>
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 任务耗时(小时数)
+        /// </summary>
+        public double? DurationHours { get; set; }
+
+        /// <summary>
+        /// 是否为额外任务
+        /// </summary>
+        public bool IsExtraTask { get; set; }
+
+        /// <summary>
+        /// 工单主表ID
+        /// </summary>
+        public int WorkOrderId { get; set; }
+
+        public int Sort { get; set; }
+    }
+
+    public class WorkOrdeView
+    {
+        public int Id { get; set; }
+        public string TaskName { get; set; }
+        public string StartTime { get; set; }
+        public string Team { get; set; }
+        public string Assignee { get; set; }
+        public string ExternalOption { get; set; }
+        public StepInfo StepInfo { get; set; }
+
+        public bool isReview { get; set; }
+    }
+
+    public class StepInfo
+    {
+        public int Active { get; set; }
+        public List<Steps> Steps { get; set; }
+
+        public int ExtraActive { get; set; }
+        public List<Steps> ExtraStep { get; set; }
+    }
+
+    public class Steps
+    {
+        public int Id { get; set; }
+
+        public string Name { get; set; }
+    }
+
+
+    public class GetWorkTaskReceiptDto
+    {
+        public int OrderId { get; set; }
+
+        public int TaskId { get; set; }
+
+        /// <summary>
+        /// 审核状态 0未审核 1通过 -1未通过
+        /// </summary>
+        //public int IsApproved { get; set; }
+    }
+
+    public class WorkTaskReceiptView
+    {
+        public int Id { get; set; }
+        /// <summary>
+        /// 工单主表ID
+        /// </summary>
+        public int WorkOrderId { get; set; }
+
+        /// <summary>
+        /// 任务ID
+        /// </summary>
+        public int WorkTaskId { get; set; }
+
+        /// <summary>
+        /// 任务是否完成
+        /// </summary>
+        public int IsCompleted { get; set; }
+
+        /// <summary>
+        /// 相关内容
+        /// </summary>
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 文件路径
+        /// </summary>
+        public string FilePath { get; set; }
+
+        /// <summary>
+        /// 是否通过审核
+        /// </summary>
+        public int IsApproved { get; set; }
+
+        /// <summary>
+        /// 未通过审核原因
+        /// </summary>
+        public string RejectReason { get; set; }
+
+        public string Remark { get; set; }
+    }
+
+
+    public class SubmitWorkTaskReceiptDto
+    {
+        /// <summary>
+        /// 工单主表ID
+        /// </summary>
+        public int WorkOrderId { get; set; }
+
+        /// <summary>
+        /// 任务ID
+        /// </summary>
+        public int WorkTaskId { get; set; }
+
+        /// <summary>
+        /// 任务是否完成
+        /// </summary>
+        public int IsCompleted { get; set; }
+
+        /// <summary>
+        /// 相关内容
+        /// </summary>
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 文件
+        /// </summary>
+        public List<IFormFile> Files { get; set; }
+
+        public int UserId { get; set; }
+    }
+
+    /// <summary>
+    /// 软删除工作订单DTO
+    /// </summary>
+    public class SoftDeleteWorkOrderDto
+    {
+        /// <summary>
+        /// 工作订单ID
+        /// </summary>
+        public int WorkOrderId { get; set; }
+
+        /// <summary>
+        /// 操作用户ID
+        /// </summary>
+        public int UserId { get; set; }
+    }
+
+    public class AuditWorkTaskReceiptDto
+    {
+        /// <summary>
+        /// 回执单Id
+        /// </summary>
+        public int ReceiptId { get; set; }
+
+        /// <summary>
+        /// 审核人Id
+        /// </summary>
+        public int ApproverId { get; set; }
+
+        // 1通过 -1驳回
+        public int Approve { get; set; }
+
+        // 驳回原因(驳回时必填)
+        public string? RejectReason { get; set; }
+    }
+}

+ 240 - 0
OASystem/OASystem.Domain/Entities/Task/WorkOrder.cs

@@ -0,0 +1,240 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+
+namespace OASystem.Domain.Entities.WorkOrder
+{
+    /// <summary>
+    /// 主表:工单
+    /// </summary>
+    [SugarTable("Task_WorkOrder", TableDescription = "工单主表")]
+    public class Task_WorkOrder : EntityBase
+    {
+        /// <summary>
+        /// 工单名称
+        /// </summary>
+        [SugarColumn(Length = 200, IsNullable = false, ColumnDescription = "工单名称")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 任务开始时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务开始时间")]
+        public DateTime StartTime { get; set; }
+
+        /// <summary>
+        /// 所属团组ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "所属团组ID")]
+        public int GroupId { get; set; }
+
+        /// <summary>
+        /// 指派用户ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "指派用户ID")]
+        public int AssignedUserId { get; set; }
+
+        /// <summary>
+        /// 外办选项ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "外办选项ID")]
+        public int ForeignOptionId { get; set; }
+
+        /// <summary>
+        /// 部门类型
+        /// </summary>
+        public int TypeId { get; set; }
+
+        /// <summary>
+        /// 进度ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "进度ID")]
+        public int ProgressId { get; set; }
+
+        /// <summary>
+        /// 子任务集合(导航属性,不映射数据库)
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public List<Task_WorkTask> Tasks { get; set; }
+    }
+
+    /// <summary>
+    /// 从表:任务
+    /// </summary>
+    [SugarTable("Task_WorkTask", TableDescription = "工单任务表")]
+    public class Task_WorkTask : EntityBase
+    {
+        /// <summary>
+        /// 任务名称
+        /// </summary>
+        [SugarColumn(Length = 200, IsNullable = false, ColumnDescription = "任务名称")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 优先级ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "优先级ID")]
+        public int PriorityId { get; set; }
+
+        /// <summary>
+        /// 是否加急
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否加急")]
+        public bool IsUrgent { get; set; }
+
+        /// <summary>
+        /// 指派用户ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "指派用户ID")]
+        public int AssignedUserId { get; set; }
+
+        /// <summary>
+        /// 任务开始时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务开始时间")]
+        public DateTime StartTime { get; set; }
+
+        /// <summary>
+        /// 任务结束时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务结束时间", IsNullable = true)]
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 任务耗时(小时数)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务耗时(小时)", IsNullable = true)]
+        public double? DurationHours { get; set; }
+
+        /// <summary>
+        /// 是否为额外任务
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否为额外任务")]
+        public bool IsExtraTask { get; set; }
+
+        /// <summary>
+        /// 工单主表ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "工单主表ID")]
+        public int WorkOrderId { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        public int Sort { get; set; }
+
+        /// <summary>
+        /// 工单导航属性(不映射数据库)
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public Task_WorkOrder WorkOrder { get; set; }
+
+        /// <summary>
+        /// 任务回执集合(不映射数据库)
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public List<Task_WorkTaskReceipt> Receipts { get; set; }
+    }
+
+    /// <summary>
+    /// 从表:任务回执
+    /// </summary>
+    [SugarTable("Task_WorkTaskReceipt", TableDescription = "工单任务回执表")]
+    public class Task_WorkTaskReceipt : EntityBase
+    {
+        /// <summary>
+        /// 工单主表ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "工单主表ID")]
+        public int WorkOrderId { get; set; }
+
+        /// <summary>
+        /// 任务ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务ID")]
+        public int WorkTaskId { get; set; }
+
+        /// <summary>
+        /// 任务是否完成
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务是否完成")]
+        public int IsCompleted { get; set; }
+
+        /// <summary>
+        /// 相关内容
+        /// </summary>
+        [SugarColumn(Length = 2000, IsNullable = true, ColumnDescription = "相关内容")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 文件路径
+        /// </summary>
+        [SugarColumn(Length = 500, IsNullable = true, ColumnDescription = "文件路径")]
+        public string FilePath { get; set; }
+
+        /// <summary>
+        /// 是否通过审核 0未审核 1通过 2驳回
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否通过审核")]
+        public int IsApproved { get; set; }
+
+        /// <summary>
+        /// 未通过审核原因
+        /// </summary>
+        [SugarColumn(Length = 500, IsNullable = true, ColumnDescription = "未通过审核原因")]
+        public string RejectReason { get; set; }
+
+        /// <summary>
+        /// 任务导航属性(不映射数据库)
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public Task_WorkTask WorkTask { get; set; }
+
+        /// <summary>
+        /// 工单导航属性(不映射数据库)
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public Task_WorkOrder WorkOrder { get; set; }
+    }
+
+    /// <summary>
+    /// 默认任务表
+    /// </summary>
+    public class Task_WorkOrderDefault : EntityBase
+    {
+        /// <summary>
+        /// 任务名称
+        /// </summary>
+        [SugarColumn(Length = 200, IsNullable = false, ColumnDescription = "任务名称")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 优先级ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "优先级ID")]
+        public int PriorityId { get; set; }
+
+        /// <summary>
+        /// 是否加急
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否加急")]
+        public bool IsUrgent { get; set; }
+
+        /// <summary>
+        /// 任务耗时(小时数)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "任务耗时(小时)", IsNullable = true)]
+        public double DurationHours { get; set; } = 0;
+
+        /// <summary>
+        /// 部门类型
+        /// </summary>
+        public int TypeId { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        public int Sort { get; set; }
+    }
+
+}