using AutoMapper;
using GraphQL;
using K4os.Compression.LZ4.Internal;
using MassTransit;
using MassTransit.Internals.Extensions;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Logging;
using MySqlX.XDevAPI.Common;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using OfficeOpenXml;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using OfficeOpenXml.Style;
using Org.BouncyCastle.Utilities;
using Performance.DtoModels;
using Performance.DtoModels.Request;
using Performance.DtoModels.Response;
using Performance.DtoModels.Second;
using Performance.EntityModels;
using Performance.EntityModels.Entity;
using Performance.EntityModels.Other;
using Performance.Infrastructure;
using Performance.Repository;
using Performance.Repository.Repository;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using Z.EntityFramework.Plus;
using static Performance.Services.ExtractExcelService.WriteDataHelper;

namespace Performance.Services
{
    public class AttendanceService : IAutoInjection
    {
        private readonly IMapper mapper;
        private readonly ILogger<AttendanceService> logger;
        private readonly DapperService _service;
        private readonly PerforUserRepository _userRepository;
        private readonly PerforPerallotRepository perforPerallotRepository;
        private readonly PerforPerAttendanceRepository perforPerAttendanceRepository;
        private readonly PerforPerAttendanceIssueRepository perforPerAttendanceIssueRepository;
        private readonly PerfoPperAttendanceTypeRepository perfoPperAttendanceTypeRepository;
        private readonly PerfoPperAttendanceVacationeRepository perfoPperAttendanceVacationeRepository;
        private readonly PerforPerdeptdicRepository perdeptdicRepository;
        private readonly PerforPeremployeeRepository perforPeremployeeRepository;
        private readonly PerforCofaccountingRepository cofaccountingRepository;
        private readonly PerforPerAttendanceDeptRepository _attendanceDeptRepository;
        private readonly PerforPerAttendanceDeptReportRepository _perforPerAttendanceDeptReportRepository;
        private readonly RoleService roleService;
        private readonly UserService userService;
        public AttendanceService(
            IMapper mapper,
            ILogger<AttendanceService> logger,
            DapperService service,
            PerforUserRepository userRepository,
            PerforPerallotRepository perforPerallotRepository,
            PerforPerAttendanceRepository perforPerAttendanceRepository,
            PerforPerAttendanceIssueRepository perforPerAttendanceIssueRepository,
            PerfoPperAttendanceTypeRepository perfoPperAttendanceTypeRepository,
            PerfoPperAttendanceVacationeRepository perfoPperAttendanceVacationeRepository,
            PerforPerdeptdicRepository perdeptdicRepository,
            PerforPeremployeeRepository perforPeremployeeRepository,
            PerforCofaccountingRepository cofaccountingRepository,
            PerforPerAttendanceDeptRepository attendanceDeptRepository,
            PerforPerAttendanceDeptReportRepository perforPerAttendanceDeptReportRepository,
            RoleService roleService,
            UserService userService
            )
        {
            this.mapper = mapper;
            this.logger = logger;
            _service = service;
            _userRepository = userRepository;
            this.perforPerallotRepository = perforPerallotRepository;
            this.perforPerAttendanceRepository = perforPerAttendanceRepository;
            this.perforPerAttendanceIssueRepository = perforPerAttendanceIssueRepository;
            this.perfoPperAttendanceTypeRepository = perfoPperAttendanceTypeRepository;
            this.perfoPperAttendanceVacationeRepository = perfoPperAttendanceVacationeRepository;
            this.perdeptdicRepository = perdeptdicRepository;
            this.perforPeremployeeRepository = perforPeremployeeRepository;
            this.cofaccountingRepository = cofaccountingRepository;
            _attendanceDeptRepository = attendanceDeptRepository;
            _perforPerAttendanceDeptReportRepository = perforPerAttendanceDeptReportRepository;
            this.roleService = roleService;
            this.userService = userService;
        }

        #region 初始考勤页面

        //public ApiResponse<List<AttendanceStatistics>> GetAttendance(int allotId, int userid)
        //{
        //    var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
        //    if (allot == null)
        //        throw new PerformanceException("当前绩效记录不存在");

        //    var attendanceData = perforPerallotRepository.GetAttendance(allotId);
        //    var roleType = new[] { (int)Role.护士长, (int)Role.科主任, (int)Role.特殊科室, (int)Role.行政科室, };
        //    var user = userService.GetUser(userid);
        //    var role = roleService.GetUserRole(user.UserID);
        //    if (role.Any(t => roleType.Contains(t.Type.Value)))
        //        attendanceData = attendanceData.Where(w => w.AccountingUnit == user.Department && w.UnitType == user.UnitType).ToList();


        //    List<AttendanceStatistics> statistics = new List<AttendanceStatistics>();
        //    // 交叉补全科室结束时间
        //    foreach (var personnelNumber in attendanceData.Select(w => w.PersonnelNumber).Distinct())
        //    {
        //        var attendances = attendanceData.Where(w => w.PersonnelNumber == personnelNumber).OrderBy(w => w.AttendanceDate);
        //        for (int i = 0; i < attendances.Count() - 1; i++)
        //        {
        //            var begDate = attendances.ElementAt(i).AttendanceDate.Date;
        //            var endDate = attendances.ElementAt(i + 1).AttendanceDate.Date;
        //            // 调入科室需要额外减去1天作为上一个科室结束时间
        //            var days = attendances.ElementAt(i + 1).Source == Attendance.Type.调入.ToString() ? -1 : 0;
        //            if (endDate > begDate)
        //            {
        //                var stat = new AttendanceStatistics
        //                {
        //                    AllotID = attendances.ElementAt(i).ALLOTID,
        //                    UnitType = attendances.ElementAt(i).UnitType,
        //                    AccountingUnit = attendances.ElementAt(i).AccountingUnit,
        //                    Department = attendances.ElementAt(i).Department,
        //                    PersonnelNumber = attendances.ElementAt(i).PersonnelNumber,
        //                    PersonnelName = attendances.ElementAt(i).PersonnelName,
        //                    BeginDate = begDate,
        //                    EndDate = endDate.AddDays(days),
        //                    //Detial = new List<AttendanceStatisticsDetial>()
        //                };
        //                statistics.Add(stat);
        //            }
        //        }
        //    }
        //    if (statistics != null)
        //        return new ApiResponse<List<AttendanceStatistics>>(ResponseType.OK, statistics);
        //    else
        //    {
        //        return new ApiResponse<List<AttendanceStatistics>>(ResponseType.Fail);
        //    }
        //}


        #endregion

        #region 调入记录
        public ApiResponse<List<view_attendance>> GetCallIn(int allotId)
        {
            var view_attendance = perforPerallotRepository.GetAttendance(allotId).Where(t => t.Source.Contains("调入")).ToList();

            if (view_attendance != null)
                return new ApiResponse<List<view_attendance>>(ResponseType.OK, view_attendance);
            else
            {
                return new ApiResponse<List<view_attendance>>(ResponseType.Fail);
            }

        }
        public HandsonTableBase GetBatchCallInHandsonTable(int allotId)
        {
            HandsonTableBase handson = new HandsonTableBase()
            {
                Columns = Person.Select(t => new HandsonColumn(t.Item2)).ToList(),
                ColHeaders = Person.Select(c => c.Item2).ToList(),
            };
            if (handson.Columns != null && handson.Columns.Any())
            {
                var cofaccounting = cofaccountingRepository.GetEntities(w => w.UnitType != "" && w.AllotId == allotId);
                foreach (var column in handson.Columns)
                {
                    column.Type = "text";
                    if (column.Data == "调入组别")
                    {
                        column.Type = "autocomplete";
                        column.Source = cofaccounting.Select(w => w.UnitType).Distinct().ToArray();
                        column.Strict = true;
                    }

                    if (column.Data == "调入核算单元")
                    {
                        column.Type = "autocomplete";
                        column.Source = cofaccounting.Select(w => w.AccountingUnit).Distinct().ToArray();
                        column.Strict = true;
                    }
                }
            }
            List<view_attendance> data = new List<view_attendance>();
            data = GetCallIn(allotId).Data;

            var convertDate = data.Select(t => new { t.PersonnelName, t.PersonnelNumber, t.AccountingUnit, t.UnitType, t.PermanentStaff, AttendanceDate = t.AttendanceDate.ToString("d") });
            if (convertDate == null)
                return handson;

            List<HandsonRowData> rowDatas = new List<HandsonRowData>();
            int i = 1;

            var dict = new Dictionary<string, string>();
            Person2.ForEach(t => dict.Add(t.Item1, t.Item2));

            foreach (var item in convertDate)
            {
                var json = JsonHelper.Serialize(item);
                var firstDic = JsonHelper.Deserialize<Dictionary<string, string>>(json);

                var cells = (from conf in dict join fst in firstDic on conf.Key.ToUpper() equals fst.Key.ToUpper() select new HandsonCellData(conf.Value, fst.Value)).ToList();

                rowDatas.Add(new HandsonRowData(i, cells));
                i++;
            }
            handson.SetRowData(rowDatas);
            foreach (var item in handson.Data)
            {
                item.Remove("编号");
            }
            return handson;
        }

        public ApiResponse BatchCallIn(int allotId, int hospitalId, SaveCollectData request)
        {
            var dict = new Dictionary<string, string>();
            Person.ForEach(t => dict.Add(t.Item1, t.Item2));

            var dicData = CreateDataRow(request, dict);
            if (dicData == null || dicData.Count == 0)
                return new ApiResponse(ResponseType.Error, "空数据，无效操作");

            var convertDicData = dicData.Select(w => new per_attendance
            {
                PersonnelNumber = w["PersonnelNumber"],
                PersonnelName = w["PersonnelName"],
                CallInAccountingUnit = w["CallInAccountingUnit"],
                CallInUnitType = w["CallInUnitType"],
                CallInDate = ConvertHelper.To<DateTime>(w["CallInDate"]),
                PermanentStaff = ConvertHelper.To<decimal?>(w["PermanentStaff"]),
            }).ToList();

            var jsons = JsonHelper.Serialize(convertDicData);
            var newAttendanceVacatione = JsonHelper.Deserialize<List<per_attendance>>(jsons);
            var oldCallinAttendance = perforPerAttendanceRepository.GetEntities(t => t.AllotId == allotId && t.HospitalId == hospitalId);

            var allot = perforPerallotRepository.GetEntities(t => t.ID == allotId && t.HospitalId == hospitalId).FirstOrDefault();

            if (allot == null) return new ApiResponse(ResponseType.Error, "无绩效数据");

            var cofaccounting = cofaccountingRepository.GetEntities(ca => ca.AllotId == allotId);

            var employees = perforPeremployeeRepository.GetEntities(t => t.AllotId == allotId && t.HospitalId == hospitalId);
            List<Dictionary<string, string>> error = new List<Dictionary<string, string>>();

            for (int i = 0; i < newAttendanceVacatione.Count; i++)
            {
                if (string.IsNullOrEmpty(newAttendanceVacatione[i].PersonnelName?.Trim())
                    || string.IsNullOrEmpty(newAttendanceVacatione[i].PersonnelNumber?.Trim())
                    || string.IsNullOrEmpty(newAttendanceVacatione[i].CallInAccountingUnit?.Trim())
                    || string.IsNullOrEmpty(newAttendanceVacatione[i].CallInUnitType?.Trim())
                    || newAttendanceVacatione[i].CallInDate == null)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "调入核算单元", newAttendanceVacatione[i].CallInAccountingUnit??"" },
                        { "调入组别", newAttendanceVacatione[i].CallInUnitType??"" },
                        { "调入时间", newAttendanceVacatione[i].CallInDate?.ToString("d")??"" },
                        { "人员系数", newAttendanceVacatione[i].PermanentStaff?.ToString()??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "“关键信息缺失”请补全或删除" },
                    });
                }
                if (allot.Month >= 1 && allot.Month <= 12)
                {
                    DateTime dt = new DateTime(allot.Year, allot.Month, 1).AddMonths(1);
                    if (newAttendanceVacatione[i].CallInDate > dt)
                    {
                        error.Add(new Dictionary<string, string>
                        {
                            { "行号", $"第{i+1}行" },
                            { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                            { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                            { "调入核算单元", newAttendanceVacatione[i].CallInAccountingUnit??"" },
                            { "调入组别", newAttendanceVacatione[i].CallInUnitType??"" },
                            { "调入时间", newAttendanceVacatione[i].CallInDate?.ToString("d")??"" },
                            { "人员系数", newAttendanceVacatione[i].PermanentStaff?.ToString()??"" },
                            { "来源", "粘贴数据" },
                            { "错误原因", $"调入时间不在当前绩效月份范围内,已超出{SplitEveryDay(dt.AddDays(-1),newAttendanceVacatione[i].CallInDate.Value).Count-1}天"},
                        });
                    }
                }

                if (!cofaccounting.Any(t => t.AccountingUnit == newAttendanceVacatione[i].CallInAccountingUnit && t.UnitType == newAttendanceVacatione[i].CallInUnitType))
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "调入核算单元", newAttendanceVacatione[i].CallInAccountingUnit??"" },
                        { "调入组别", newAttendanceVacatione[i].CallInUnitType??"" },
                        { "调入时间", newAttendanceVacatione[i].CallInDate?.ToString("d")??"" },
                        { "人员系数", newAttendanceVacatione[i].PermanentStaff?.ToString()??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "该核算单元的调入组别不一致或不存在" },
                    });
                }

                if (newAttendanceVacatione[i].PersonnelName != employees.FirstOrDefault(t => t.PersonnelNumber == newAttendanceVacatione[i].PersonnelNumber && t.AllotId == allotId && t.HospitalId == hospitalId)?.DoctorName)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "调入核算单元", newAttendanceVacatione[i].CallInAccountingUnit??"" },
                        { "调入组别", newAttendanceVacatione[i].CallInUnitType??"" },
                        { "调入时间", newAttendanceVacatione[i].CallInDate?.ToString("d")??"" },
                        { "人员系数", newAttendanceVacatione[i].PermanentStaff?.ToString()??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "该人员与人员字典不一致或不存在" },
                    });
                }
                var oldEmp = oldCallinAttendance.FirstOrDefault(w => w.PersonnelNumber == newAttendanceVacatione[i].PersonnelNumber);
                if (!string.IsNullOrEmpty(newAttendanceVacatione[i].PersonnelName) && oldEmp != null && oldEmp.PersonnelName != newAttendanceVacatione[i].PersonnelName)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "调入核算单元", newAttendanceVacatione[i].CallInAccountingUnit??"" },
                        { "调入组别", newAttendanceVacatione[i].CallInUnitType??"" },
                        { "调入时间", newAttendanceVacatione[i].CallInDate?.ToString("d")??"" },
                        { "人员系数", newAttendanceVacatione[i].PermanentStaff?.ToString()??"" },
                        { "来源", "“粘贴数据”与“历史数据”比对" },
                        { "错误原因", $"原名“{oldEmp.PersonnelName}”,工号相同但姓名不同,请删除“历史数据”中该员工" },
                    });
                }
            }
            if (error.Count > 0)
                return new ApiResponse(ResponseType.WarningTable, "验证不通过,当前操作已拒绝", error);

            List<per_attendance> addPer_Attendances = new List<per_attendance>();

            foreach (var data in newAttendanceVacatione)
            {
                data.AllotId = allotId;
                data.HospitalId = hospitalId;
                data.PermanentStaff = data.PermanentStaff.HasValue
                    ? data.PermanentStaff
                    : employees.FirstOrDefault(w => w.PersonnelNumber == data.PersonnelNumber)?.PermanentStaff;

                if (string.IsNullOrEmpty(data.CallInUnitType) || string.IsNullOrEmpty(data.CallInAccountingUnit))
                {
                    var emp = employees.FirstOrDefault(w => w.PersonnelNumber == data.PersonnelNumber);
                    data.CallInUnitType = emp?.UnitType;
                    data.CallInAccountingUnit = emp?.AccountingUnit;
                }
                if (!data.CallInDate.HasValue || data.CallInDate.Value < new DateTime(1970, 1, 1))
                {
                    data.CallInDate = new DateTime(allot.Year, allot.Month, 1);
                }

                addPer_Attendances.Add(data);
                var any = oldCallinAttendance.FirstOrDefault(w => w.AllotId == allotId && w.HospitalId == hospitalId && w.CallInDate == data.CallInDate && w.CallInAccountingUnit?.Trim() == data.CallInAccountingUnit?.Trim() && w.CallInUnitType?.Trim() == data.CallInUnitType?.Trim() && w.PersonnelName?.Trim() == data.PersonnelName?.Trim() && w.PersonnelNumber?.Trim() == data.PersonnelNumber?.Trim());
            }

            perforPerAttendanceRepository.RemoveRange(oldCallinAttendance.ToArray());
            if (addPer_Attendances != null && addPer_Attendances.Any())
                perforPerAttendanceRepository.AddRange(addPer_Attendances.ToArray());

            return new ApiResponse(ResponseType.OK, "");

        }


        public ApiResponse AttendanceAdd(int allotId, AttendanceData request)
        {
            if (string.IsNullOrEmpty(request.PersonnelNumber))
                throw new PerformanceException("人员工号必须填写");

            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");
            var cofaccounting = cofaccountingRepository.GetEntities(ca => ca.AllotId == allotId);
            if (!cofaccounting.Any(t => t.AccountingUnit == request.CallInAccountingUnit && t.UnitType == request.CallInUnitType))
                throw new PerformanceException("当前“核算组别与核算单元”无法对应，请检查后重试!");

            var data = mapper.Map<per_attendance>(request);
            data.AllotId = allotId;
            data.HospitalId = allot.HospitalId;
            if (string.IsNullOrEmpty(request.CallInUnitType) || string.IsNullOrEmpty(request.CallInAccountingUnit))
            {
                var emp = perforPeremployeeRepository.GetEntity(w => w.AllotId == allotId && w.PersonnelNumber == request.PersonnelNumber);
                data.CallInUnitType = emp?.UnitType;
                data.CallInAccountingUnit = emp?.AccountingUnit;
            }
            if (!request.CallInDate.HasValue || request.CallInDate.Value < new DateTime(1970, 1, 1))
            {
                data.CallInDate = new DateTime(allot.Year, allot.Month, 1);
            }
            if (!request.CallInDate.HasValue || request.CallInDate.Value < new DateTime(1970, 1, 1))
            {
                data.CallInDate = new DateTime(allot.Year, allot.Month, 1);
            }
            perforPerAttendanceRepository.Add(data);

            return new ApiResponse(ResponseType.OK);
        }

        public ApiResponse AttendanceUpdate(int allotId, AttendanceUpdateData request)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");
            var data = perforPerAttendanceRepository.GetEntity(w => w.Id == request.AttendanceId && w.AllotId == allotId);
            if (data == null) throw new PerformanceException("当前调动记录不存在");

            var cofaccounting = cofaccountingRepository.GetEntities(ca => ca.AllotId == allotId);
            if (!cofaccounting.Any(t => t.AccountingUnit == request.CallInAccountingUnit && t.UnitType == request.CallInUnitType))
                throw new PerformanceException("当前“核算组别与核算单元”无法对应，请检查后重试!");

            data.PermanentStaff = request.PermanentStaff;
            data.CallInUnitType = request.CallInUnitType;
            data.CallInAccountingUnit = request.CallInAccountingUnit;
            data.CallInDate = request.CallInDate;
            if (string.IsNullOrEmpty(request.CallInUnitType) || string.IsNullOrEmpty(request.CallInAccountingUnit))
            {
                var emp = perforPeremployeeRepository.GetEntity(w => w.AllotId == allotId && w.PersonnelNumber == data.PersonnelNumber);
                data.CallInUnitType = emp?.UnitType;
                data.CallInAccountingUnit = emp?.AccountingUnit;
            }
            if (!request.CallInDate.HasValue || request.CallInDate.Value < new DateTime(1970, 1, 1))
            {
                data.CallInDate = new DateTime(allot.Year, allot.Month, 1);
            }
            perforPerAttendanceRepository.Update(data);
            return new ApiResponse(ResponseType.OK);
        }

        public ApiResponse AttendanceDelete(int attendanceId)
        {
            var data = perforPerAttendanceRepository.GetEntity(w => w.Id == attendanceId);
            perforPerAttendanceRepository.Remove(data);
            return new ApiResponse(ResponseType.OK);
        }

        public ApiResponse AttendanceClearAll(int allotId)
        {
            var data = perforPerAttendanceRepository.GetEntities(w => w.AllotId == allotId);
            perforPerAttendanceRepository.RemoveRange(data.ToArray());
            return new ApiResponse(ResponseType.OK);
        }

        private List<Dictionary<string, string>> CreateDataRow(SaveCollectData request, Dictionary<string, string> config)
        {
            List<Dictionary<string, string>> allData = new List<Dictionary<string, string>>();

            for (int r = 0; r < request.Data.Length; r++)
            {
                // 创建固定数据列
                Dictionary<string, string> baseData = CreateBaseData(request, config, r);
                allData.Add(baseData);

            }
            return allData;
        }

        private Dictionary<string, string> CreateBaseData(SaveCollectData request, Dictionary<string, string> config, int rownumber)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            for (int c = 0; c < request.ColHeaders.Length; c++)
            {
                var header = request.ColHeaders[c];
                var first = config.FirstOrDefault(w => w.Value == header);
                if (!default(KeyValuePair<string, string>).Equals(first)
                    && !result.ContainsKey(header)
                    && request.Data[rownumber].Length > c)
                {
                    result.Add(first.Key, request.Data[rownumber][c]);
                }
            }

            return result;
        }

        public static List<(string, string, Func<per_attendance, object>)> Person { get; } = new List<(string, string, Func<per_attendance, object>)>
        {
            (nameof(per_attendance.PersonnelName), "人员姓名", t => t.PersonnelName),
            (nameof(per_attendance.PersonnelNumber), "员工工号", t => t.PersonnelNumber),
            (nameof(per_attendance.CallInAccountingUnit), "调入核算单元" ,t => t.CallInAccountingUnit),
            (nameof(per_attendance.CallInUnitType), "调入组别", t => t.CallInUnitType),
            (nameof(per_attendance.CallInDate), "调入时间", t => t.CallInDate),
            (nameof(per_attendance.PermanentStaff), "人员系数", t => t.PermanentStaff),
        };
        public static List<(string, string, Func<view_attendance, object>)> Person2 { get; } = new List<(string, string, Func<view_attendance, object>)>
        {
            (nameof(view_attendance.PersonnelName), "人员姓名", t => t.PersonnelName),
            (nameof(view_attendance.PersonnelNumber), "员工工号", t => t.PersonnelNumber),
            (nameof(view_attendance.AccountingUnit), "调入核算单元" ,t => t.AccountingUnit),
            (nameof(view_attendance.UnitType), "调入组别", t => t.UnitType),
            (nameof(view_attendance.AttendanceDate), "调入时间", t => t.AttendanceDate),
            (nameof(per_attendance.PermanentStaff), "人员系数", t => t.PermanentStaff),
        };

        public static List<(string, string, Func<RecordAttendcance, object>)> Vacation { get; } = new List<(string, string, Func<RecordAttendcance, object>)>
        {
            (nameof(RecordAttendcance.PersonnelName), "人员姓名", t => t.PersonnelName),
            (nameof(RecordAttendcance.PersonnelNumber), "员工工号", t => t.PersonnelNumber),
            (nameof(RecordAttendcance.AttendanceName), "考勤类型" ,t => t.AttendanceName),
            (nameof(RecordAttendcance.BegDate), "开始时间", t => t.BegDate),
            (nameof(RecordAttendcance.EndDate), "结束时间", t => t.EndDate),
        };
        #endregion

        #region 考勤类型

        public ApiResponse<List<per_attendance_type>> GetAttendanceType(int allotId)
        {
            var result = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId).ToList();
            if (result != null)
                return new ApiResponse<List<per_attendance_type>>(ResponseType.OK, result);
            else
            {
                return new ApiResponse<List<per_attendance_type>>(ResponseType.Fail);

            }
        }
        public ApiResponse<AttendanceType> InsertAttendanceType(int allotId, AttendanceType attendanceType)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var any = perfoPperAttendanceTypeRepository.GetEntities().FirstOrDefault(t => t.Id == attendanceType.Id);
            if (any != null)
            {
                any.AttendanceName = attendanceType.AttendanceName;
                any.IsDeduction = Convert.ToInt32(attendanceType.IsDeduction);
                if (perfoPperAttendanceTypeRepository.Update(any)) return new ApiResponse<AttendanceType>(ResponseType.OK, "修改成功");
                else return new ApiResponse<AttendanceType>(ResponseType.Fail, "修改失败");

            }
            else
            {
                per_attendance_type per_Attendance_Type = new per_attendance_type()
                {
                    AllotId = allotId,
                    HospitalId = allot.HospitalId,
                    AttendanceName = attendanceType.AttendanceName,
                    IsDeduction = Convert.ToInt32(attendanceType.IsDeduction)
                };
                if (perfoPperAttendanceTypeRepository.Add(per_Attendance_Type)) return new ApiResponse<AttendanceType>(ResponseType.OK, "添加成功");
                else return new ApiResponse<AttendanceType>(ResponseType.Fail, "添加失败");
            }

        }
        public ApiResponse DeleteAttendanceType(int id)
        {
            var any = perfoPperAttendanceTypeRepository.GetEntity(t => t.Id == id);

            if (any == null) return new ApiResponse(ResponseType.Fail, "没有该数据");

            var use = perfoPperAttendanceVacationeRepository.GetEntity(t => t.TypeId == any.Id);
            if (use != null) return new ApiResponse(ResponseType.Fail, "该类型正在使用！");

            if (any != null && use == null)
            {
                if (perfoPperAttendanceTypeRepository.DeleteFromQuery(t => t.Id == id) > 0) return new ApiResponse(ResponseType.OK, "删除成功");
                else return new ApiResponse(ResponseType.Fail, "删除失败");
            }
            else return new ApiResponse(ResponseType.Fail, "删除失败");
        }
        #endregion

        #region 考勤记录
        public HandsonTableBase GetAttendanceVacationHandsonTable(int allotId)
        {
            HandsonTableBase handson = new HandsonTableBase()
            {
                Columns = Vacation.Select(t => new HandsonColumn(t.Item2)).ToList(),
                ColHeaders = Vacation.Select(c => c.Item2).ToList(),
            };
            if (handson.Columns != null && handson.Columns.Any())
            {
                var type = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId);
                foreach (var column in handson.Columns)
                {
                    column.Type = "text";
                    if (column.Data == "考勤类型")
                    {
                        column.Type = "autocomplete";
                        column.Source = type.Select(t => t.AttendanceName).ToArray();
                    }
                }
            }

            List<RecordAttendcance> data = new List<RecordAttendcance>();
            data = GetAttendanceVacation(allotId).Data;

            var convertDate = data.Select(t =>
            new
            {
                t.PersonnelName,
                t.PersonnelNumber,
                t.AttendanceName,
                BegDate = t.BegDate > DateTime.MinValue ? t.BegDate?.ToString("d") : t.BegDate.ToString(),
                EndDate = t.EndDate > DateTime.MinValue ? t.EndDate?.ToString("d") : t.EndDate.ToString()
            });
            if (convertDate == null)
                return handson;

            List<HandsonRowData> rowDatas = new List<HandsonRowData>();
            int i = 1;

            var dict = new Dictionary<string, string>();
            Vacation.ForEach(t => dict.Add(t.Item1, t.Item2));

            foreach (var item in convertDate)
            {
                var json = JsonHelper.Serialize(item);
                var firstDic = JsonHelper.Deserialize<Dictionary<string, string>>(json);

                var cells = (from conf in dict join fst in firstDic on conf.Key.ToUpper() equals fst.Key.ToUpper() select new HandsonCellData(conf.Value, fst.Value)).ToList();

                rowDatas.Add(new HandsonRowData(i, cells));
                i++;
            }
            handson.SetRowData(rowDatas);
            foreach (var item in handson.Data)
            {
                item.Remove("编号");
            }

            return handson;
        }

        public ApiResponse<List<RecordAttendcance>> GetAttendanceVacation(int allotId)
        {
            var vacatione = perfoPperAttendanceVacationeRepository.GetEntities(t => t.AllotId == allotId);
            var type = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId);
            if (type.Count == 0)
                return new ApiResponse<List<RecordAttendcance>>(ResponseType.Fail, new List<RecordAttendcance>());

            var data = from a in vacatione
                       join b in type on a.TypeId equals b.Id into temp
                       from tt in temp.DefaultIfEmpty()
                       select new RecordAttendcance
                       {
                           Id = a.Id,
                           AllotId = a.AllotId,
                           HospitalId = a.HospitalId,
                           PersonnelName = a.PersonnelName,
                           PersonnelNumber = a.PersonnelNumber,
                           AttendanceName = tt?.AttendanceName ?? "考勤类型缺失",
                           TypeId = a.TypeId,
                           BegDate = a.BegDate,
                           EndDate = a.EndDate,
                           Days = SplitEveryDay(ConvertHelper.To<DateTime>(a.BegDate), ConvertHelper.To<DateTime>(a.EndDate)).Count()
                       };
            if (data != null)
                return new ApiResponse<List<RecordAttendcance>>(ResponseType.OK, data.ToList());
            else
            {
                return new ApiResponse<List<RecordAttendcance>>(ResponseType.Fail);
            }

        }

        public ApiResponse AttendanceBatch(int allotId, int hospitalId, SaveCollectData request)
        {
            var dict = new Dictionary<string, string>();
            Vacation.ForEach(t => dict.Add(t.Item1, t.Item2));

            var dicData = CreateDataRow(request, dict);
            if (dicData == null || dicData.Count == 0)
                return new ApiResponse(ResponseType.Error, "空数据，无效操作");

            var convertDicData = dicData.Select(w => new RecordAttendcance
            {
                PersonnelNumber = w["PersonnelNumber"],
                PersonnelName = w["PersonnelName"],
                AttendanceName = w["AttendanceName"],
                BegDate = ConvertHelper.To<DateTime?>(w["BegDate"]),
                EndDate = ConvertHelper.To<DateTime?>(w["EndDate"]),
            });

            var jsons = JsonHelper.Serialize(convertDicData);
            var newAttendanceVacatione = JsonHelper.Deserialize<List<RecordAttendcance>>(jsons);
            var oldAttendanceVacatione = perfoPperAttendanceVacationeRepository.GetEntities(t => t.AllotId == allotId && t.HospitalId == hospitalId);

            var attendanceType = perfoPperAttendanceTypeRepository.GetEntities();
            var per_employee = perforPeremployeeRepository.GetEntities(t => t.AllotId == allotId && t.HospitalId == hospitalId);

            List<Dictionary<string, string>> error = new List<Dictionary<string, string>>();
            var allot = perforPerallotRepository.GetEntities(t => t.ID == allotId && t.HospitalId == hospitalId).FirstOrDefault();

            for (int i = 0; i < newAttendanceVacatione.Count; i++)
            {

                if (string.IsNullOrEmpty(newAttendanceVacatione[i].PersonnelName?.Trim())
                    || string.IsNullOrEmpty(newAttendanceVacatione[i].PersonnelNumber?.Trim())
                    || string.IsNullOrEmpty(newAttendanceVacatione[i].AttendanceName?.Trim())
                    || newAttendanceVacatione[i].BegDate == DateTime.MinValue
                    || newAttendanceVacatione[i].EndDate == DateTime.MinValue)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                        { "开始日期", newAttendanceVacatione[i].BegDate?.ToString("d")??"" },
                        { "结束日期", newAttendanceVacatione[i].EndDate?.ToString("d")??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "“关键信息缺失”请补全或删除" },
                    });
                }

                var typeAny = attendanceType.FirstOrDefault(t => t.AllotId == allotId && t.HospitalId == hospitalId && t.AttendanceName == newAttendanceVacatione[i].AttendanceName);
                if (typeAny == null)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                        { "开始日期", newAttendanceVacatione[i].BegDate?.ToString("d")??"" },
                        { "结束日期", newAttendanceVacatione[i].EndDate?.ToString("d")??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "没有该考勤类型" },
                    });
                }
                if (newAttendanceVacatione[i].PersonnelName != per_employee.FirstOrDefault(t => t.PersonnelNumber == newAttendanceVacatione[i].PersonnelNumber && t.AllotId == allotId && t.HospitalId == hospitalId)?.DoctorName)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                        { "开始日期", newAttendanceVacatione[i].BegDate?.ToString("d")??"" },
                        { "结束日期", newAttendanceVacatione[i].EndDate?.ToString("d")??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "该人员与人员字典不一致或不存在" },
                    });
                }
                var oldEmp = oldAttendanceVacatione.FirstOrDefault(w => w.PersonnelNumber == newAttendanceVacatione[i].PersonnelNumber);
                if (!string.IsNullOrEmpty(newAttendanceVacatione[i].PersonnelName) && oldEmp != null && oldEmp.PersonnelName != newAttendanceVacatione[i].PersonnelName)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                        { "开始日期", newAttendanceVacatione[i].BegDate?.ToString("d")??"" },
                        { "结束日期", newAttendanceVacatione[i].EndDate?.ToString("d")??"" },
                        { "来源", "“粘贴数据”与“历史数据”比对" },
                        { "错误原因", $"原名“{oldEmp.PersonnelName}”,工号相同但姓名不同,请删除“历史数据”中该员工" },
                    });
                }

                if (allot.Month >= 1 && allot.Month <= 12)
                {
                    DateTime dt = new DateTime(allot.Year, allot.Month, 1).AddMonths(1);
                    if (newAttendanceVacatione[i].BegDate >= dt && newAttendanceVacatione[i].EndDate > dt)
                    {
                        error.Add(new Dictionary<string, string>
                        {
                            { "行号", $"第{i+1}行" },
                            { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                            { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                            { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                            { "开始日期", newAttendanceVacatione[i].BegDate?.ToString("d")??"" },
                            { "结束日期", newAttendanceVacatione[i].EndDate?.ToString("d")??"" },
                            { "来源", "粘贴数据" },
                            { "错误原因", $"考勤时间不在该绩效月份内,已超出{SplitEveryDay(dt.AddDays(-1),newAttendanceVacatione[i].EndDate.Value).Count-1}天" },
                        });
                    }
                }
                if (newAttendanceVacatione[i].BegDate > newAttendanceVacatione[i].EndDate)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{i+1}行" },
                        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                        { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                        { "开始日期", newAttendanceVacatione[i].BegDate?.ToString("d")??"" },
                        { "结束日期", newAttendanceVacatione[i].EndDate?.ToString("d")??"" },
                        { "来源", "粘贴数据" },
                        { "错误原因", "开始时间不能大于结束时间" },
                    });
                }

                var newDate = SplitEveryDay(Convert.ToDateTime(newAttendanceVacatione[i].BegDate), Convert.ToDateTime(newAttendanceVacatione[i].EndDate));

                //foreach (var item in oldAttendanceVacatione.Where(w => w.PersonnelNumber == newAttendanceVacatione[i].PersonnelNumber))
                //{
                //    var oldDate = SplitEveryDay(Convert.ToDateTime(item.BegDate), Convert.ToDateTime(item.EndDate));

                //    bool any = false;
                //    foreach (var old in oldDate)
                //    {
                //        if (newDate.Contains(old))
                //            any = true;
                //    }
                //    if (any)
                //        error.Add(new Dictionary<string, string>
                //        {
                //        { "行号", $"第{i+1}行" },
                //        { "人员工号", newAttendanceVacatione[i].PersonnelNumber??"" },
                //        { "人员姓名", newAttendanceVacatione[i].PersonnelName??"" },
                //        { "考勤类型", newAttendanceVacatione[i].AttendanceName??"" },
                //        { "开始日期", newAttendanceVacatione[i].BegDate.ToString()??"" },
                //        { "结束日期", newAttendanceVacatione[i].EndDate.ToString()??"" },
                //        { "来源", "粘贴数据" },
                //        { "错误原因", "该考勤时间范围与历史考勤时间冲突" },
                //        });
                //}
            }
            if (error.Count > 0)
                return new ApiResponse(ResponseType.WarningTable, "验证不通过,当前操作已拒绝", error);

            List<per_attendance_vacation> addAttendanceVacatione = new List<per_attendance_vacation>();

            var type = GetAttendanceType(allotId);
            foreach (var data in newAttendanceVacatione)
            {
                data.AllotId = allotId;
                data.HospitalId = hospitalId;
                data.TypeId = type.Data.FirstOrDefault(t => t.AttendanceName == data.AttendanceName).Id;
                addAttendanceVacatione.Add(data);
                var any = oldAttendanceVacatione.FirstOrDefault(w => w.AllotId == allotId && w.HospitalId == hospitalId && w.BegDate == data.BegDate && w.EndDate == data.EndDate && w.PersonnelName?.Trim() == data.PersonnelName?.Trim() && w.PersonnelNumber?.Trim() == data.PersonnelNumber?.Trim() && w.TypeId == data.TypeId);
            }

            perfoPperAttendanceVacationeRepository.RemoveRange(oldAttendanceVacatione.ToArray());
            if (addAttendanceVacatione != null && addAttendanceVacatione.Any())
                perfoPperAttendanceVacationeRepository.AddRange(addAttendanceVacatione.ToArray());

            return new ApiResponse(ResponseType.OK, "");

        }


        public ApiResponse AttendanceVacationAdd(int allotId, AttendanceVacationData request)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            //DateTime dt = new DateTime(allot.Year, allot.Month, 1);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");
            //if (request.EndDate < request.BegDate)
            //    throw new PerformanceException("开始时间不能大于结束时间");

            var data = mapper.Map<per_attendance_vacation>(request);
            data.AllotId = allotId;
            data.HospitalId = allot.HospitalId;
            perfoPperAttendanceVacationeRepository.Add(data);
            return new ApiResponse(ResponseType.OK);
        }

        public ApiResponse AttendanceVacationUpdate(int allotId, AttendanceVacationUpdateData request)
        {
            var data = perfoPperAttendanceVacationeRepository.GetEntity(w => w.Id == request.AttendanceVacationId && w.AllotId == allotId);
            if (data == null) throw new PerformanceException("当前调动记录不存在");
            data.TypeId = request.TypeId;
            data.BegDate = request.BegDate;
            data.EndDate = request.EndDate;

            perfoPperAttendanceVacationeRepository.Update(data);
            return new ApiResponse(ResponseType.OK);
        }

        public ApiResponse AttendanceVacationDelete(int AttendanceVacationId)
        {
            var data = perfoPperAttendanceVacationeRepository.GetEntity(w => w.Id == AttendanceVacationId);
            perfoPperAttendanceVacationeRepository.Remove(data);
            return new ApiResponse(ResponseType.OK);
        }

        public ApiResponse AttendanceVacationClearAll(int allotId)
        {
            var data = perfoPperAttendanceVacationeRepository.GetEntities(w => w.AllotId == allotId);
            perfoPperAttendanceVacationeRepository.RemoveRange(data.ToArray());
            return new ApiResponse(ResponseType.OK);
        }
        #endregion

        public ApiResponse<List<AttendanceStatistics>> GetAttendanceStatistics(int allotId, int userid)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            var vacationeData = perfoPperAttendanceVacationeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_vacation>();
            //// 只关注请假的人
            //var numbers = vacationeData.Select(w => w.PersonnelNumber).ToList();
            //var attendanceData = perforPerallotRepository.GetAttendance(allotId)
            //    .Where(w => numbers.Contains(w.PersonnelNumber))
            //    .ToList();
            //查询全部数据
            var attendanceData = perforPerallotRepository.GetAttendance(allotId);
            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var unitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            if (unitTypes?.Any() == true)
                attendanceData = attendanceData.Where(w => w.AccountingUnit == userInfo.User.Department && UnitTypeUtil.Is(w.UnitType, unitTypes)).ToList();

            List<AttendanceStatistics> statistics = new List<AttendanceStatistics>();
            // 交叉补全科室结束时间
            foreach (var personnelNumber in attendanceData.Select(w => w.PersonnelNumber).Distinct())
            {
                var attendances = attendanceData.Where(w => w.PersonnelNumber == personnelNumber).OrderBy(w => w.AttendanceDate);
                for (int i = 0; i < attendances.Count() - 1; i++)
                {
                    var begDate = attendances.ElementAt(i).AttendanceDate.Date;
                    var endDate = attendances.ElementAt(i + 1).AttendanceDate.Date;
                    var lastDayOfMonth = new DateTime(begDate.Year, begDate.Month, 1).AddMonths(1).AddDays(-1).Date;
                    // 调入科室需要额外减去1天作为上一个科室结束时间
                    var days = attendances.ElementAt(i + 1).Source == Attendance.Type.调入.ToString() ? -1 : 0;
                    if (endDate > begDate || begDate == lastDayOfMonth)
                    {
                        var stat = new AttendanceStatistics
                        {
                            AllotID = attendances.ElementAt(i).ALLOTID,
                            UnitType = attendances.ElementAt(i).UnitType,
                            AccountingUnit = attendances.ElementAt(i).AccountingUnit,
                            Department = attendances.ElementAt(i).Department,
                            PersonnelNumber = attendances.ElementAt(i).PersonnelNumber,
                            PersonnelName = attendances.ElementAt(i).PersonnelName,
                            PermanentStaff = attendances.ElementAt(i).PermanentStaff,
                            BeginDate = begDate,
                            EndDate = endDate.AddDays(days),
                            //Detial = new List<AttendanceStatisticsDetial>()
                            Code = "",
                        };
                        statistics.Add(stat);
                    }
                }
            }

            var vacationes = vacationeData
                .Where(w => w.BegDate > DateTime.MinValue && w.EndDate > DateTime.MinValue)
                .Select(w => new
                {
                    w.PersonnelNumber,
                    Type = types.FirstOrDefault(p => p.Id == w.TypeId)?.AttendanceName ?? "考勤类型缺失",
                    Dates = SplitEveryDay(w.BegDate.Value.Date, w.EndDate.Value.Date),
                    Remark = types.FirstOrDefault(p => p.Id == w.TypeId)?.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                });


            foreach (var stat in statistics)
            {
                stat.Detial = vacationes
                    .Where(w => w.PersonnelNumber == stat.PersonnelNumber)
                    .Select(w => new AttendanceStatisticsDetial
                    {
                        Title = w.Type,
                        Value = w.Dates.Where(date => date >= stat.BeginDate && date <= stat.EndDate).Count(),
                        Remark = w.Remark,
                    })
                    .ToList();

                foreach (var item in types)
                {
                    if (!stat.Detial.Any(w => w.Title == item.AttendanceName))
                    {
                        stat.Detial.Add(new AttendanceStatisticsDetial
                        {
                            Title = item.AttendanceName,
                            Value = 0,
                            Remark = item.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                        });
                    }
                }
                int vacationesDays = stat.Detial.Where(w => !w.Remark.Contains("不核减")).Sum(w => w.Value);
                stat.AttendanceDays = SplitEveryDay(stat.BeginDate, stat.EndDate).Where(date => date >= stat.BeginDate && date <= stat.EndDate).Count() - vacationesDays;
            }
            var cofaccounting = cofaccountingRepository.GetEntities(g => g.AllotId == allotId);


            foreach (var item in statistics)
            {
                var code = cofaccounting.FirstOrDefault(w => w.UnitType == item.UnitType && w.AccountingUnit == item.AccountingUnit);
                item.Code = code == null ? "" : code.Code;
            }
            return new ApiResponse<List<AttendanceStatistics>>(ResponseType.OK, "", statistics);
        }

        #region 考勤下发
        // 科室确认列表
        public ApiResponse<List<AttendanceAuditList>> AuditList(int allotId, string unitType, string accountingUnit, Attendance.State state)
        {
            var issues = perforPerAttendanceIssueRepository.GetEntities(w => w.AllotId == allotId);
            if (!string.IsNullOrEmpty(unitType))
                issues = issues.Where(w => w.UnitType.Contains(unitType)).ToList();
            if (!string.IsNullOrEmpty(accountingUnit))
                issues = issues.Where(w => w.AccountingUnit.Contains(accountingUnit)).ToList();
            var stateOrders = new int[] { (int)Attendance.State.未确认, (int)Attendance.State.驳回, (int)Attendance.State.确认 };
            var items = issues.GroupBy(w => new { w.Code, w.UnitType, w.AccountingUnit })
                .Select(w => new AttendanceAuditList
                {
                    Code = w.Key.Code,
                    UnitType = w.Key.UnitType,
                    AccountingUnit = w.Key.AccountingUnit,
                    Count = w.Count(),
                    ConfirmCount = w.Count(p => p.State == (int)Attendance.State.确认),
                    RejectCount = w.Count(p => p.State == (int)Attendance.State.驳回),
                    OperationUser = w.OrderByDescending(o => o.ConfirmTime).Select(s => s.ConfirmUser).FirstOrDefault(),
                    OperationTime = w.OrderByDescending(o => o.ConfirmTime).Select(s => s.ConfirmTime).FirstOrDefault(),
                    Remarks = string.Join("；", w.Select(p => p.Remarks).Where(s => !string.IsNullOrEmpty(s)).Distinct()),
                }).ToList();

            foreach (var item in items)
            {
                item.State = (int)Attendance.State.未确认;
                if (item.RejectCount > 0)
                    item.State = (int)Attendance.State.驳回;
                else if (item.ConfirmCount == item.Count)
                    item.State = (int)Attendance.State.确认;
            }
            items = items.OrderBy(w => Array.IndexOf(stateOrders, w.State)).ToList();

            if ((int)state == (int)Attendance.State.确认 || (int)state == (int)Attendance.State.未确认 || (int)state == (int)Attendance.State.驳回)
                items = items.Where(w => w.State == (int)state).ToList();
            return new ApiResponse<List<AttendanceAuditList>>(ResponseType.OK, items);
        }
        // 科室确认详情(已下发结果展示列表)
        public ApiResponse<List<AttendanceAuditDetail>> AuditDetail(int allotId, string unitType, string accountingUnit, string searchTxet, int userid)
        {
            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var queryUnitTypes = new List<string>();
            string queryAccountingUnit = accountingUnit;
            string querySearchTxet = searchTxet;

            var permissionUnitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            if (permissionUnitTypes?.Any() == true)
            {
                queryAccountingUnit = userInfo.User.Department;
                queryUnitTypes.AddRange(permissionUnitTypes);
            }
            else if (!string.IsNullOrEmpty(unitType))
            {
                queryUnitTypes.Add(unitType);
            }

            var names = typeof(per_attendance_issue).GetProperties().Select(w => w.Name);
            var stateOrders = new int[] { (int)Attendance.State.未确认, (int)Attendance.State.驳回, (int)Attendance.State.确认 };
            var issues = _service.QueryAttendanceIssue(allotId, queryUnitTypes, queryAccountingUnit, querySearchTxet);
            List<AttendanceAuditDetail> items = new List<AttendanceAuditDetail>();
            foreach (var isu in issues)
            {
                AttendanceAuditDetail item = JsonHelper.Deserialize<AttendanceAuditDetail>(JsonHelper.Serialize(isu));
                item.Detial = new List<AttendanceAuditDetails>();
                items.Add(item);

                var dic = new RouteValueDictionary(isu);
                dic.TryGetValue(nameof(per_attendance_issue.Id), out object attendanceIsueId);
                item.AttendanceIsueId = ConvertHelper.To(attendanceIsueId, 0);

                foreach (var dicItem in dic.Where(w => !names.Any(name => name.Equals(w.Key, StringComparison.OrdinalIgnoreCase))))
                {
                    item.Detial.Add(new AttendanceAuditDetails { Title = dicItem.Key, Remark = dicItem.Key, Value = dicItem.Value });
                }
            }
            items = items.OrderBy(w => Array.IndexOf(stateOrders, w.State)).ToList();
            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            var vacationeData = perfoPperAttendanceVacationeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_vacation>();

            var vacationes = vacationeData
              .Where(w => w.BegDate > DateTime.MinValue && w.EndDate > DateTime.MinValue)
              .Select(w => new
              {
                  w.PersonnelNumber,
                  Type = types.FirstOrDefault(p => p.Id == w.TypeId)?.AttendanceName ?? "考勤类型缺失",
                  Dates = SplitEveryDay(w.BegDate.Value.Date, w.EndDate.Value.Date),
                  Remark = types.FirstOrDefault(p => p.Id == w.TypeId)?.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
              });
            foreach (var stat in items)
            {
                var detail = vacationes
                    .Where(w => w.PersonnelNumber == stat.PersonnelNumber)
                    .Select(w => new AttendanceAuditDetails
                    {
                        Title = w.Type,
                        Value = w.Dates.Where(date => date >= stat.BeginDate && date <= stat.EndDate).Count(),
                        Remark = w.Remark,
                    })
                    .ToList();
                foreach (var item in types)
                {
                    if (!detail.Any(w => w.Title == item.AttendanceName))
                    {
                        detail.Add(new AttendanceAuditDetails
                        {
                            Title = item.AttendanceName,
                            Value = 0,
                            Remark = item.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                        });
                    }
                }
                stat.Detial.InsertRange(0, detail);
            }
            items = items.OrderBy(w => w.PersonnelNumber).ThenBy(w => w.BeginDate).ToList();
            return new ApiResponse<List<AttendanceAuditDetail>>(ResponseType.OK, items);
        }
        // 最终考勤结果下发(预确认显示)
        public ApiResponse<List<PerAttendanceIssueDto>> PreConfirmationDisplay(int allotId, int userid, string unitType, string accountingUnit, Attendance.OperationType state)
        {
            var statistics = GetAttendanceStatistics(allotId, userid).Data;
            var issues = perforPerAttendanceIssueRepository.GetEntities(w => w.AllotId == allotId);
            //删除（删除页面没有的数据）
            var statisticPersonnelNumber = statistics.Select(s => s.PersonnelNumber);
            var scStatistics = issues.Where(w => !statisticPersonnelNumber.Contains(w.PersonnelNumber)).ToList();
            var issuedates = new List<PerAttendanceIssueDto>();
            Func<per_attendance_issue, AttendanceStatistics, bool> func = (issue, statistic) =>
            {
                var result = true;
                result =
                issue.UnitType == statistic.UnitType &&
                issue.AccountingUnit == statistic.AccountingUnit &&
                issue.PermanentStaff == statistic.PermanentStaff &&
                issue.BeginDate == statistic.BeginDate &&
                issue.EndDate == statistic.EndDate &&
                issue.AttendanceDays == statistic.AttendanceDays &&
                issue.DeductionDays == statistic.Detial.Where(w => !w.Remark.Contains("不核减")).Sum(w => w.Value) &&
                issue.NoDeductionDays == statistic.Detial.Where(w => !w.Remark.Equals("核减")).Sum(w => w.Value);
                return result;
            };
            foreach (var personnelNumber in statistics.Select(w => w.PersonnelNumber).Distinct())
            {
                // 按在科时间排序，重要
                var staticPersonnels = statistics.Where(w => w.PersonnelNumber == personnelNumber).OrderBy(w => w.BeginDate).ToList();
                var issuePersonnels = issues.Where(w => w.PersonnelNumber == personnelNumber).OrderBy(w => w.BeginDate).ToList();

                foreach (var item in staticPersonnels.Select((sp, index) => (sp, index)))
                {
                    // 按索引下标来判断 额外多出的都是要删除的
                    var deleteIndex = issuePersonnels.Count() - staticPersonnels.Count();
                    if (deleteIndex > 0)
                    {
                        var deleteIssuePersonnels = issuePersonnels.Skip(staticPersonnels.Count()).Take(deleteIndex).ToList();
                        scStatistics.AddRange(deleteIssuePersonnels);
                    }

                    // 条数满足修改条件
                    if (issuePersonnels.Count() > item.index)
                    {
                        var issuePersonnel = issuePersonnels.ElementAt(item.index);
                        if (func(issuePersonnel, item.sp)) continue;

                        issuedates.Add(new PerAttendanceIssueDto(item.sp)
                        {
                            Id = issuePersonnel.Id,
                            PersonnelName = issuePersonnel.PersonnelName,
                            PersonnelNumber = issuePersonnel.PersonnelNumber,
                            OperationType = (int)Attendance.OperationType.修改,
                            Remarks = "修改数据",
                        });
                    }
                    // 否则新增
                    else
                    {
                        issuedates.Add(new PerAttendanceIssueDto(item.sp));
                    }
                }
            }
            if (scStatistics.Any())
            {
                foreach (var deleteItem in scStatistics)
                {
                    if (!issuedates.Any(w => w.Id == deleteItem.Id))
                        issuedates.Add(new PerAttendanceIssueDto(deleteItem));
                }
            }
            if (!string.IsNullOrEmpty(unitType))
                issuedates = issuedates.Where(w => w.UnitType.Contains(unitType)).ToList();
            if (!string.IsNullOrEmpty(accountingUnit))
                issuedates = issuedates.Where(w => w.AccountingUnit.Contains(accountingUnit)).ToList();
            if ((int)state == (int)Attendance.OperationType.新增 || (int)state == (int)Attendance.OperationType.修改 || (int)state == (int)Attendance.OperationType.删除)
                issuedates = issuedates.Where(w => w.OperationType == (int)state).ToList();
            return new ApiResponse<List<PerAttendanceIssueDto>>(ResponseType.OK, issuedates);
        }

        // 最终考勤结果下发
        public ApiResponse IssueStatistics(int allotId, int userid, string realName, string unitType = "", string accountingUnit = "", Attendance.OperationType state = 0)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var preConfirmationDisplay = PreConfirmationDisplay(allotId, userid, unitType, accountingUnit, state).Data;
            if (preConfirmationDisplay == null)
                return new ApiResponse(ResponseType.NotFound, "暂无下发数据");
            var issues = perforPerAttendanceIssueRepository.GetEntities(w => w.AllotId == allotId);

            var insertIssues = preConfirmationDisplay.Where(w => w.OperationType == (int)Attendance.OperationType.新增).ToList();
            var updateIssues = preConfirmationDisplay.Where(w => w.OperationType == (int)Attendance.OperationType.修改).ToList();
            var deleteIssues = preConfirmationDisplay.Where(w => w.OperationType == (int)Attendance.OperationType.删除).Select(w => w.Id).ToList();
            if (insertIssues.Any())
            {
                var inserts = insertIssues.Select(item =>
                {
                    return new per_attendance_issue()
                    {
                        AllotId = allotId,
                        HospitalId = allot.HospitalId,
                        Code = item.Code,
                        UnitType = item.UnitType,
                        AccountingUnit = item.AccountingUnit,
                        PersonnelNumber = item.PersonnelNumber,
                        PersonnelName = item.PersonnelName,
                        PermanentStaff = item.PermanentStaff,
                        BeginDate = item.BeginDate,
                        EndDate = item.EndDate,
                        DeductionDays = item.DeductionDays,
                        NoDeductionDays = item.NoDeductionDays,
                        AttendanceDays = item.AttendanceDays,
                        WorkFullDays = item.AttendanceDays + item.DeductionDays,
                        CreateUser = realName,
                        CreateTime = DateTime.Now,
                        State = (int)Attendance.State.未确认,
                    };
                });
                perforPerAttendanceIssueRepository.AddRange(inserts.ToArray());
            }
            if (updateIssues.Any())
            {
                List<per_attendance_issue> updateIssuedates = new List<per_attendance_issue>();
                foreach (var item in updateIssues)
                {
                    var issuePersonnel = issues.FirstOrDefault(w => w.Id == item.Id);
                    if (issuePersonnel != null)
                    {
                        issuePersonnel.UnitType = item.UnitType;
                        issuePersonnel.AccountingUnit = item.AccountingUnit;
                        issuePersonnel.PermanentStaff = item.PermanentStaff;
                        issuePersonnel.Code = item.Code;
                        issuePersonnel.BeginDate = item.BeginDate;
                        issuePersonnel.EndDate = item.EndDate;
                        issuePersonnel.DeductionDays = item.DeductionDays;
                        issuePersonnel.NoDeductionDays = item.NoDeductionDays;
                        issuePersonnel.AttendanceDays = item.AttendanceDays;
                        issuePersonnel.WorkFullDays = item.AttendanceDays + item.DeductionDays;
                        issuePersonnel.State = (int)Attendance.State.未确认;
                        issuePersonnel.CreateUser = realName;
                        issuePersonnel.CreateTime = DateTime.Now;
                        issuePersonnel.ConfirmUser = "";
                        issuePersonnel.ConfirmTime = null;
                        issuePersonnel.Remarks = "";
                        updateIssuedates.Add(issuePersonnel);
                    }
                }
                perforPerAttendanceIssueRepository.UpdateRange(updateIssuedates.ToArray());
            }
            if (deleteIssues.Any())
            {
                var deletePersonnel = issues.Where(w => deleteIssues.Contains(w.Id)).ToList();
                perforPerAttendanceIssueRepository.RemoveRange(deletePersonnel.ToArray());
            }

            perforPerallotRepository.PerAttendanceIssueBackup(allotId);
            return new ApiResponse(ResponseType.OK, "下发成功");
        }
        // 最终考勤结果驳回或确认无误((批量操作)
        public ApiResponse ChooseStatistics(AttendanceIssueChoose attendanceIssueChoose, int userid, string realName)
        {
            var issuedates = perforPerAttendanceIssueRepository.GetEntities(w => w.AllotId == attendanceIssueChoose.AllotId).ToList();
            if (attendanceIssueChoose.Issueid?.Any() == true)
                issuedates = issuedates.Where(w => attendanceIssueChoose.Issueid.Contains(w.Id)).ToList();

            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var unitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            if (unitTypes?.Any() == true)
                issuedates = issuedates.Where(w => w.AccountingUnit == userInfo.User.Department && UnitTypeUtil.Is(w.UnitType, unitTypes)).ToList();

            foreach (var item in issuedates)
            {
                item.State = (int)attendanceIssueChoose.state;
                item.ConfirmUser = realName;
                item.ConfirmTime = DateTime.Now;
                item.Remarks = attendanceIssueChoose.Remarks;
            }
            perforPerAttendanceIssueRepository.UpdateRange(issuedates.ToArray());
            return new ApiResponse(ResponseType.OK);
        }
        #endregion

        #region 考勤上报
        /// <summary>
        /// 考勤上报审核列表
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="accountingUnit"></param>
        /// <returns></returns>
        public ApiResponse<List<AttendanceDeptAuditList>> DeptAuditList(int allotId, string unitType, string accountingUnit, int? state = -1)
        {
            var attendanceDepts = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allotId);
            var stateOrders = new int[] { (int)Attendance.Report.提交, (int)Attendance.Report.驳回, (int)Attendance.Report.通过, (int)Attendance.Report.未提交 };
            var items = attendanceDepts.GroupBy(w => new { w.Code, w.UnitType, w.AccountingUnit })
                .Select(w => new AttendanceDeptAuditList
                {
                    Code = w.Key.Code,
                    UnitType = w.Key.UnitType,
                    AccountingUnit = w.Key.AccountingUnit,
                    Count = w.Count(),
                    ConfirmCount = w.Count(p => p.State == (int)Attendance.Report.提交),
                    FinishCount = w.Count(p => p.State == (int)Attendance.Report.通过),
                    RejectCount = w.Count(p => p.State == (int)Attendance.Report.驳回),
                    SubmitUser = w.OrderByDescending(o => o.SubmitUser).Select(s => s.SubmitUser).FirstOrDefault(),
                    SubmitTime = w.OrderByDescending(o => o.SubmitTime).Select(s => s.SubmitTime).FirstOrDefault(),
                    AuditUser = w.OrderByDescending(o => o.AuditUser).Select(s => s.AuditUser).FirstOrDefault(),
                    AuditTime = w.OrderByDescending(o => o.AuditTime).Select(s => s.AuditTime).FirstOrDefault(),
                    Remarks = string.Join("；", w.Select(p => p.Remarks).Where(s => !string.IsNullOrEmpty(s)).Distinct()),
                }).ToList();

            var cofaccounting = cofaccountingRepository.GetEntities(g => g.AllotId == allotId);
            var unitTyps = perforPeremployeeRepository.GetEmployeeUnit(w => w.AllotId == allotId);
            foreach (var unit in unitTyps)
            {
                if (!items.Any(w => w.UnitType == unit.UnitType && w.AccountingUnit == unit.AccountingUnit))
                {
                    items.Add(new AttendanceDeptAuditList
                    {
                        Code = cofaccounting.FirstOrDefault(w => w.UnitType == unit.UnitType && w.AccountingUnit == unit.AccountingUnit)?.Code ?? "",
                        UnitType = unit.UnitType,
                        AccountingUnit = unit.AccountingUnit,
                    });
                }
            }

            foreach (var item in items)
            {
                item.State = (int)Attendance.Report.未提交;
                if (item.RejectCount > 0)
                    item.State = (int)Attendance.Report.驳回;
                else if (item.Count > 0 && item.FinishCount == item.Count)
                    item.State = (int)Attendance.Report.通过;
                else if (item.Count > 0 && item.ConfirmCount == item.Count)
                    item.State = (int)Attendance.Report.提交;
            }

            Expression<Func<AttendanceDeptAuditList, bool>> deptExpression = (w) => true;
            if (!string.IsNullOrEmpty(unitType))
                items = items.Where(w => w.UnitType.Contains(unitType)).ToList();
            if (!string.IsNullOrEmpty(accountingUnit))
                items = items.Where(w => w.AccountingUnit.Contains(accountingUnit)).ToList();

            items = items
                .OrderBy(w => Array.IndexOf(stateOrders, w.State))
                .ThenBy(w => w.Code)
                .ThenBy(w => w.UnitType)
                .ToList();

            if (Enum.IsDefined(typeof(Attendance.Report), state))
                items = items.Where(w => w.State == (int)state).ToList();

            return new ApiResponse<List<AttendanceDeptAuditList>>(ResponseType.OK, items);
        }

        public ApiResponse<AttendanceDeptState> DeptState(int allotId, int userid, string unitType)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var queryUnitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            var queryAccountingUnit = userInfo.User.Department;

            var cofaccounting = cofaccountingRepository.GetEntities(g => g.AllotId == allotId);
            var attendanceDepts = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allotId && queryUnitTypes.Contains(w.UnitType) && queryAccountingUnit.Equals(w.AccountingUnit))
                ?? new List<per_attendance_dept>();

            var item = new AttendanceDeptState
            {
                Title = $"{allot.Name ?? $"{allot.Year}年{allot.Month}月"} {queryAccountingUnit} {unitType} 考勤上报",
                Code = cofaccounting.FirstOrDefault(w => unitType.Equals(w.UnitType) && queryAccountingUnit.Equals(w.AccountingUnit))?.Code ?? "",
                UnitType = unitType,
                AccountingUnit = queryAccountingUnit,
                Count = attendanceDepts.Count(),
                ConfirmCount = attendanceDepts.Count(p => p.State == (int)Attendance.Report.提交),
                FinishCount = attendanceDepts.Count(p => p.State == (int)Attendance.Report.通过),
                RejectCount = attendanceDepts.Count(p => p.State == (int)Attendance.Report.驳回),
                SubmitUser = attendanceDepts.OrderByDescending(o => o.SubmitUser).Select(s => s.SubmitUser).FirstOrDefault(),
                SubmitTime = attendanceDepts.OrderByDescending(o => o.SubmitTime).Select(s => s.SubmitTime).FirstOrDefault(),
                AuditUser = attendanceDepts.OrderByDescending(o => o.AuditUser).Select(s => s.AuditUser).FirstOrDefault(),
                AuditTime = attendanceDepts.OrderByDescending(o => o.AuditTime).Select(s => s.AuditTime).FirstOrDefault(),
                Remarks = string.Join("；", attendanceDepts.Select(p => p.Remarks).Where(s => !string.IsNullOrEmpty(s)).Distinct()),
            };

            item.State = (int)Attendance.Report.未提交;
            if (item.RejectCount > 0)
                item.State = (int)Attendance.Report.驳回;
            else if (item.Count > 0 && item.FinishCount == item.Count)
                item.State = (int)Attendance.Report.通过;
            else if (item.Count > 0 && item.ConfirmCount == item.Count)
                item.State = (int)Attendance.Report.提交;

            return new ApiResponse<AttendanceDeptState>(ResponseType.OK, item);
        }
        /// <summary>
        /// 科室考勤上报录入加载
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="userid"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        /// <exception cref="PerformanceException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public ApiResponse DeptLoad(int allotId, int userid, string userName)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var queryUnitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            var queryAccountingUnit = userInfo.User.Department;

            Expression<Func<per_attendance_dept, bool>> deptExpression = (w) => w.AllotId == allotId;
            if (queryUnitTypes?.Any() == true)
                deptExpression = deptExpression.And(w => queryUnitTypes.Contains(w.UnitType));
            if (!string.IsNullOrEmpty(queryAccountingUnit))
                deptExpression = deptExpression.And(w => queryAccountingUnit.Equals(w.AccountingUnit));

            var attendances = _attendanceDeptRepository.GetEntities(deptExpression) ?? new List<per_attendance_dept>();

            HandsonTableBase handson = new HandsonTableBase();
            handson.ColHeaders.AddRange(new string[] { "工号", "姓名", "人员系数" });
            handson.Columns.AddRange(new HandsonColumn[]
            {
                new HandsonColumn(nameof(per_attendance_dept.PersonnelNumber).ToLower()),
                new HandsonColumn(nameof(per_attendance_dept.PersonnelName).ToLower()),
                new HandsonColumn(nameof(per_attendance_dept.PermanentStaff).ToLower(), format: DataFormat.小数),
            });
            List<string> allDays = new List<string>();
            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            for (DateTime i = begMonthDate; i <= endMonthDate; i = i.AddDays(1))
            {
                var title = CultureInfo.CurrentCulture.DateTimeFormat.GetDayName(i.DayOfWeek).Replace("星期", "");
                var beaDay = i.Day.ToString().PadLeft(2, '0');
                handson.ColHeaders.Add($"{beaDay}({title})");
                var source = types.Select(w => w.AttendanceName).ToArray();
                handson.Columns.Add(new HandsonColumn($"Day{beaDay}".ToLower()) { Type = "autocomplete", Strict = true, Source = source });
                allDays.Add($"Day{beaDay}".ToLower());
            }
            Func<object, string> getAattendanceType = (value) =>
            {
                _ = int.TryParse(value == null ? "" : value.ToString(), out int typeId);
                return typeId > 0 ? types.FirstOrDefault(w => w.Id == typeId)?.AttendanceName ?? "考勤类型缺失" : "";
            };
            List<Dictionary<string, object>> datas = new List<Dictionary<string, object>>();
            if (attendances.Any())
            {
                foreach (var item in attendances)
                {
                    Dictionary<string, object> row = new Dictionary<string, object>();
                    var dict = JsonHelper.Deserialize<Dictionary<string, object>>(JsonHelper.Serialize(item));
                    foreach (var column in handson.Columns)
                    {
                        dict.TryGetValue(column.Data, out object value);
                        row.AddOrUpdate(column.Data, allDays.Contains(column.Data) ? getAattendanceType(value) : value);
                    }
                    datas.Add(row);
                }
            }
            else
            {
                Expression<Func<per_employee, bool>> empExpression = (w) => w.AllotId == allotId;
                if (queryUnitTypes?.Any() == true)
                    empExpression = empExpression.And(w => queryUnitTypes.Contains(w.UnitType));
                if (!string.IsNullOrEmpty(queryAccountingUnit))
                    empExpression = empExpression.And(w => queryAccountingUnit.Equals(w.AccountingUnit));

                var employees = perforPeremployeeRepository.GetEntities(empExpression);
                foreach (var item in employees)
                {
                    Dictionary<string, object> row = new Dictionary<string, object>
                    {
                        { nameof(per_attendance_dept.PersonnelNumber).ToLower(), item.PersonnelNumber },
                        { nameof(per_attendance_dept.PersonnelName).ToLower(), item.DoctorName },
                        { nameof(per_attendance_dept.PermanentStaff).ToLower(), item.PermanentStaff }
                    };
                    foreach (var column in handson.Columns)
                    {
                        if (!row.ContainsKey(column.Data))
                        {
                            row.AddOrUpdate(column.Data, null);
                        }
                    }
                    datas.Add(row);
                }
            }

            handson.Data = datas;

            return new ApiResponse(ResponseType.OK, "", handson);
            //return new SecondDetailDto { Head = head, Body = handson, Dic = dic };
        }
        /// <summary>
        /// 科室考勤上报录入保存
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="datas"></param>
        /// <param name="unitType"></param>
        /// <param name="userid"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        /// <exception cref="PerformanceException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public ApiResponse DeptSave(int allotId, List<AttendanceDept> datas, string unitType, int userid, string userName)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var unitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            string accountingUnit = userInfo.User.Department;

            if (!unitTypes.Contains(unitType)) throw new PerformanceException("当前用户角色与“核算组别”不匹配");

            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            var cofaccounting = cofaccountingRepository.GetEntities(g => g.AllotId == allotId);
            var employees = perforPeremployeeRepository.GetEntities(g => g.AllotId == allotId);

            var attendances = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allotId && unitTypes.Contains(w.UnitType) && accountingUnit.Equals(w.AccountingUnit))
                ?? new List<per_attendance_dept>();

            var stateCount = attendances.GroupBy(w => w.State).Select(w => new { w.Key, StateCount = w.Count() }).ToList();
            if (attendances.Count() > 0 && attendances.Count() == attendances.Count(w => w.State == (int)Attendance.Report.通过))
                throw new PerformanceException("当前考勤已审核通过，无法提交！");

            List<per_attendance_dept> newAttendanceDepts = new List<per_attendance_dept>();
            List<per_attendance_dept> updAttendanceDepts = new List<per_attendance_dept>();
            List<int> validAttendanceId = new List<int>();

            List<Dictionary<string, string>> error = new List<Dictionary<string, string>>();
            foreach (var item in datas.Select((row, ind) => (row, ind)))
            {
                var row = item.row;
                if (string.IsNullOrEmpty(row.PersonnelNumber) || string.IsNullOrEmpty(row.PersonnelNumber))
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“员工号/姓名”尚未填写" },
                    });
                    continue;
                }
                if (datas.Count(w => w.PersonnelNumber.Trim().Equals(row.PersonnelNumber.Trim(), StringComparison.OrdinalIgnoreCase)) > 1)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“员工号”多次填写请删除其他保留一条" },
                    });
                    continue;
                }
                var emp = employees.FirstOrDefault(w => w.PersonnelNumber == row.PersonnelNumber);
                if (emp == null || emp.DoctorName != row.PersonnelName)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“员工号/姓名”无法与“人员字典”对应" },
                    });
                    continue;
                }
                if (row.PermanentStaff == null)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“人员系数”尚未填写" },
                    });
                    continue;
                }
                Func<string, int?> getAattendanceType = (name) => types.FirstOrDefault(w => w.AttendanceName == name)?.Id;
                var days = new[]
                {
                    new { Date = begMonthDate.AddDays(00), TypeId = getAattendanceType(row.Day01), OrgTypeName = row.Day01 },
                    new { Date = begMonthDate.AddDays(01), TypeId = getAattendanceType(row.Day02), OrgTypeName = row.Day02 },
                    new { Date = begMonthDate.AddDays(02), TypeId = getAattendanceType(row.Day03), OrgTypeName = row.Day03 },
                    new { Date = begMonthDate.AddDays(03), TypeId = getAattendanceType(row.Day04), OrgTypeName = row.Day04 },
                    new { Date = begMonthDate.AddDays(04), TypeId = getAattendanceType(row.Day05), OrgTypeName = row.Day05 },
                    new { Date = begMonthDate.AddDays(05), TypeId = getAattendanceType(row.Day06), OrgTypeName = row.Day06 },
                    new { Date = begMonthDate.AddDays(06), TypeId = getAattendanceType(row.Day07), OrgTypeName = row.Day07 },
                    new { Date = begMonthDate.AddDays(07), TypeId = getAattendanceType(row.Day08), OrgTypeName = row.Day08 },
                    new { Date = begMonthDate.AddDays(08), TypeId = getAattendanceType(row.Day09), OrgTypeName = row.Day09 },
                    new { Date = begMonthDate.AddDays(09), TypeId = getAattendanceType(row.Day10), OrgTypeName = row.Day10 },
                    new { Date = begMonthDate.AddDays(10), TypeId = getAattendanceType(row.Day11), OrgTypeName = row.Day11 },
                    new { Date = begMonthDate.AddDays(11), TypeId = getAattendanceType(row.Day12), OrgTypeName = row.Day12 },
                    new { Date = begMonthDate.AddDays(12), TypeId = getAattendanceType(row.Day13), OrgTypeName = row.Day13 },
                    new { Date = begMonthDate.AddDays(13), TypeId = getAattendanceType(row.Day14), OrgTypeName = row.Day14 },
                    new { Date = begMonthDate.AddDays(14), TypeId = getAattendanceType(row.Day15), OrgTypeName = row.Day15 },
                    new { Date = begMonthDate.AddDays(15), TypeId = getAattendanceType(row.Day16), OrgTypeName = row.Day16 },
                    new { Date = begMonthDate.AddDays(16), TypeId = getAattendanceType(row.Day17), OrgTypeName = row.Day17 },
                    new { Date = begMonthDate.AddDays(17), TypeId = getAattendanceType(row.Day18), OrgTypeName = row.Day18 },
                    new { Date = begMonthDate.AddDays(18), TypeId = getAattendanceType(row.Day19), OrgTypeName = row.Day19 },
                    new { Date = begMonthDate.AddDays(19), TypeId = getAattendanceType(row.Day20), OrgTypeName = row.Day20 },
                    new { Date = begMonthDate.AddDays(20), TypeId = getAattendanceType(row.Day21), OrgTypeName = row.Day21 },
                    new { Date = begMonthDate.AddDays(21), TypeId = getAattendanceType(row.Day22), OrgTypeName = row.Day22 },
                    new { Date = begMonthDate.AddDays(22), TypeId = getAattendanceType(row.Day23), OrgTypeName = row.Day23 },
                    new { Date = begMonthDate.AddDays(23), TypeId = getAattendanceType(row.Day24), OrgTypeName = row.Day24 },
                    new { Date = begMonthDate.AddDays(24), TypeId = getAattendanceType(row.Day25), OrgTypeName = row.Day25 },
                    new { Date = begMonthDate.AddDays(25), TypeId = getAattendanceType(row.Day26), OrgTypeName = row.Day26 },
                    new { Date = begMonthDate.AddDays(26), TypeId = getAattendanceType(row.Day27), OrgTypeName = row.Day27 },
                    new { Date = begMonthDate.AddDays(27), TypeId = getAattendanceType(row.Day28), OrgTypeName = row.Day28 },
                    new { Date = begMonthDate.AddDays(28), TypeId = getAattendanceType(row.Day29), OrgTypeName = row.Day29 },
                    new { Date = begMonthDate.AddDays(29), TypeId = getAattendanceType(row.Day30), OrgTypeName = row.Day30 },
                    new { Date = begMonthDate.AddDays(30), TypeId = getAattendanceType(row.Day31), OrgTypeName = row.Day31 },
                };

                foreach (var d in days)
                {
                    if (!string.IsNullOrEmpty(d.OrgTypeName) && !d.TypeId.HasValue)
                    {
                        var title = CultureInfo.CurrentCulture.DateTimeFormat.GetDayName(d.Date.DayOfWeek).Replace("星期", "");
                        var beaDay = d.Date.Day.ToString().PadLeft(2, '0');

                        error.Add(new Dictionary<string, string>
                        {
                            { "行号", $"第{item.ind + 1}行" },
                            { "工号", row.PersonnelNumber ?? "" },
                            { "姓名", row.PersonnelName ?? "" },
                            { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                            { "考勤类型", d.OrgTypeName },
                            { "错误原因", $"“{beaDay}({title})”考勤类型“{d.OrgTypeName}”不存在"},
                        });
                    }
                }


                var accounting = cofaccounting.FirstOrDefault(p => p.UnitType == unitType && p.AccountingUnit == accountingUnit);
                var attendance = attendances.FirstOrDefault(w => w.PersonnelNumber == row.PersonnelNumber && w.UnitType == unitType.ToString() && w.AccountingUnit == accountingUnit);
                if (attendance == null)
                {
                    attendance = new per_attendance_dept
                    {
                        HospitalId = allot.HospitalId,
                        AllotId = allotId,
                        Code = accounting?.Code ?? "",
                        UnitType = unitType,
                        AccountingUnit = accountingUnit,
                    };
                    attendance.PersonnelNumber = row.PersonnelNumber;
                    attendance.PersonnelName = row.PersonnelName;
                    attendance.PermanentStaff = row.PermanentStaff ?? emp?.PermanentStaff;
                    attendance.Day01 = getAattendanceType(row.Day01);
                    attendance.Day02 = getAattendanceType(row.Day02);
                    attendance.Day03 = getAattendanceType(row.Day03);
                    attendance.Day04 = getAattendanceType(row.Day04);
                    attendance.Day05 = getAattendanceType(row.Day05);
                    attendance.Day06 = getAattendanceType(row.Day06);
                    attendance.Day07 = getAattendanceType(row.Day07);
                    attendance.Day08 = getAattendanceType(row.Day08);
                    attendance.Day09 = getAattendanceType(row.Day09);
                    attendance.Day10 = getAattendanceType(row.Day10);
                    attendance.Day11 = getAattendanceType(row.Day11);
                    attendance.Day12 = getAattendanceType(row.Day12);
                    attendance.Day13 = getAattendanceType(row.Day13);
                    attendance.Day14 = getAattendanceType(row.Day14);
                    attendance.Day15 = getAattendanceType(row.Day15);
                    attendance.Day16 = getAattendanceType(row.Day16);
                    attendance.Day17 = getAattendanceType(row.Day17);
                    attendance.Day18 = getAattendanceType(row.Day18);
                    attendance.Day19 = getAattendanceType(row.Day19);
                    attendance.Day20 = getAattendanceType(row.Day20);
                    attendance.Day21 = getAattendanceType(row.Day21);
                    attendance.Day22 = getAattendanceType(row.Day22);
                    attendance.Day23 = getAattendanceType(row.Day23);
                    attendance.Day24 = getAattendanceType(row.Day24);
                    attendance.Day25 = getAattendanceType(row.Day25);
                    attendance.Day26 = getAattendanceType(row.Day26);
                    attendance.Day27 = getAattendanceType(row.Day27);
                    attendance.Day28 = getAattendanceType(row.Day28);
                    attendance.Day29 = getAattendanceType(row.Day29);
                    attendance.Day30 = getAattendanceType(row.Day30);
                    attendance.Day31 = getAattendanceType(row.Day31);

                    newAttendanceDepts.Add(attendance);
                }
                else
                {
                    attendance.PersonnelNumber = row.PersonnelNumber;
                    attendance.PersonnelName = row.PersonnelName;
                    attendance.PermanentStaff = row.PermanentStaff ?? emp?.PermanentStaff;
                    attendance.Day01 = getAattendanceType(row.Day01);
                    attendance.Day02 = getAattendanceType(row.Day02);
                    attendance.Day03 = getAattendanceType(row.Day03);
                    attendance.Day04 = getAattendanceType(row.Day04);
                    attendance.Day05 = getAattendanceType(row.Day05);
                    attendance.Day06 = getAattendanceType(row.Day06);
                    attendance.Day07 = getAattendanceType(row.Day07);
                    attendance.Day08 = getAattendanceType(row.Day08);
                    attendance.Day09 = getAattendanceType(row.Day09);
                    attendance.Day10 = getAattendanceType(row.Day10);
                    attendance.Day11 = getAattendanceType(row.Day11);
                    attendance.Day12 = getAattendanceType(row.Day12);
                    attendance.Day13 = getAattendanceType(row.Day13);
                    attendance.Day14 = getAattendanceType(row.Day14);
                    attendance.Day15 = getAattendanceType(row.Day15);
                    attendance.Day16 = getAattendanceType(row.Day16);
                    attendance.Day17 = getAattendanceType(row.Day17);
                    attendance.Day18 = getAattendanceType(row.Day18);
                    attendance.Day19 = getAattendanceType(row.Day19);
                    attendance.Day20 = getAattendanceType(row.Day20);
                    attendance.Day21 = getAattendanceType(row.Day21);
                    attendance.Day22 = getAattendanceType(row.Day22);
                    attendance.Day23 = getAattendanceType(row.Day23);
                    attendance.Day24 = getAattendanceType(row.Day24);
                    attendance.Day25 = getAattendanceType(row.Day25);
                    attendance.Day26 = getAattendanceType(row.Day26);
                    attendance.Day27 = getAattendanceType(row.Day27);
                    attendance.Day28 = getAattendanceType(row.Day28);
                    attendance.Day29 = getAattendanceType(row.Day29);
                    attendance.Day30 = getAattendanceType(row.Day30);
                    attendance.Day31 = getAattendanceType(row.Day31);
                    updAttendanceDepts.Add(attendance);
                    // 记录不需要删除的历史数据
                    validAttendanceId.Add(attendance.Id);
                }
            }
            if (error.Count > 0)
                return new ApiResponse(ResponseType.WarningTable, "验证不通过,当前操作已拒绝", error);

            if (newAttendanceDepts.Any())
                _attendanceDeptRepository.AddRange(newAttendanceDepts.ToArray());
            if (updAttendanceDepts.Any())
                _attendanceDeptRepository.UpdateRange(updAttendanceDepts.ToArray());

            var delAttendanceDepts = attendances.Where(w => !validAttendanceId.Contains(w.Id));
            if (delAttendanceDepts.Any())
                _attendanceDeptRepository.RemoveRange(delAttendanceDepts.ToArray());


            return new ApiResponse(ResponseType.OK, "保存成功");
        }
        /// <summary>
        /// 考勤上报结果统计
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="accountingUnit"></param>
        /// <param name="datas"></param>
        /// <returns></returns>
        /// <exception cref="PerformanceException"></exception>
        public ApiResponse<List<AttendanceStatistics>> DeptCompute(int allotId, List<AttendanceDeptMore> datas)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            var statistics = new List<AttendanceStatistics>();
            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();

            foreach (var item in datas)
            {
                var days = new[]
                {
                    new { Date = begMonthDate.AddDays(00), OrgTypeName = item.Day01 },
                    new { Date = begMonthDate.AddDays(01), OrgTypeName = item.Day02 },
                    new { Date = begMonthDate.AddDays(02), OrgTypeName = item.Day03 },
                    new { Date = begMonthDate.AddDays(03), OrgTypeName = item.Day04 },
                    new { Date = begMonthDate.AddDays(04), OrgTypeName = item.Day05 },
                    new { Date = begMonthDate.AddDays(05), OrgTypeName = item.Day06 },
                    new { Date = begMonthDate.AddDays(06), OrgTypeName = item.Day07 },
                    new { Date = begMonthDate.AddDays(07), OrgTypeName = item.Day08 },
                    new { Date = begMonthDate.AddDays(08), OrgTypeName = item.Day09 },
                    new { Date = begMonthDate.AddDays(09), OrgTypeName = item.Day10 },
                    new { Date = begMonthDate.AddDays(10), OrgTypeName = item.Day11 },
                    new { Date = begMonthDate.AddDays(11), OrgTypeName = item.Day12 },
                    new { Date = begMonthDate.AddDays(12), OrgTypeName = item.Day13 },
                    new { Date = begMonthDate.AddDays(13), OrgTypeName = item.Day14 },
                    new { Date = begMonthDate.AddDays(14), OrgTypeName = item.Day15 },
                    new { Date = begMonthDate.AddDays(15), OrgTypeName = item.Day16 },
                    new { Date = begMonthDate.AddDays(16), OrgTypeName = item.Day17 },
                    new { Date = begMonthDate.AddDays(17), OrgTypeName = item.Day18 },
                    new { Date = begMonthDate.AddDays(18), OrgTypeName = item.Day19 },
                    new { Date = begMonthDate.AddDays(19), OrgTypeName = item.Day20 },
                    new { Date = begMonthDate.AddDays(20), OrgTypeName = item.Day21 },
                    new { Date = begMonthDate.AddDays(21), OrgTypeName = item.Day22 },
                    new { Date = begMonthDate.AddDays(22), OrgTypeName = item.Day23 },
                    new { Date = begMonthDate.AddDays(23), OrgTypeName = item.Day24 },
                    new { Date = begMonthDate.AddDays(24), OrgTypeName = item.Day25 },
                    new { Date = begMonthDate.AddDays(25), OrgTypeName = item.Day26 },
                    new { Date = begMonthDate.AddDays(26), OrgTypeName = item.Day27 },
                    new { Date = begMonthDate.AddDays(27), OrgTypeName = item.Day28 },
                    new { Date = begMonthDate.AddDays(28), OrgTypeName = item.Day29 },
                    new { Date = begMonthDate.AddDays(29), OrgTypeName = item.Day30 },
                    new { Date = begMonthDate.AddDays(30), OrgTypeName = item.Day31 },
                };
                var vaildDays = days.Where(w => !string.IsNullOrEmpty(w.OrgTypeName));
                if (vaildDays?.Any() == true)
                {
                    var vaildBegDate = vaildDays.Min(w => w.Date);
                    for (int i = 0; i < vaildDays.Count(); i++)
                    {
                        var currDate = vaildDays.ElementAt(i).Date;
                        var nextDate = (i + 1 < vaildDays.Count()) ? vaildDays.ElementAt(i + 1).Date : vaildDays.Max(w => w.Date);

                        if (nextDate.Day - currDate.Day > 1 || currDate == vaildDays.Max(w => w.Date))
                        {
                            var stat = new AttendanceStatistics
                            {
                                AllotID = allotId,
                                Code = item.Code ?? "",
                                UnitType = item.UnitType,
                                AccountingUnit = item.AccountingUnit,

                                PersonnelNumber = item.PersonnelNumber,
                                PersonnelName = item.PersonnelName,
                                PermanentStaff = item.PermanentStaff,
                                BeginDate = vaildBegDate,
                                EndDate = currDate,
                                Detial = new List<AttendanceStatisticsDetial>()
                            };
                            stat.Detial = days.Where(w => w.Date >= vaildBegDate && w.Date <= currDate)
                                .GroupBy(w => w.OrgTypeName)
                                .Select(w => new AttendanceStatisticsDetial
                                {
                                    Title = w.Key,
                                    Value = w.Count(),
                                    Remark = types.FirstOrDefault(p => p.AttendanceName == w.Key)?.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                                }).ToList();

                            foreach (var tp in types)
                            {
                                if (!stat.Detial.Any(w => w.Title == tp.AttendanceName))
                                {
                                    stat.Detial.Add(new AttendanceStatisticsDetial
                                    {
                                        Title = tp.AttendanceName,
                                        Value = 0,
                                        Remark = tp.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                                    });
                                }
                            }
                            int vacationesDays = stat.Detial.Where(w => !w.Remark.Contains("不核减")).Sum(w => w.Value);
                            stat.AttendanceDays = SplitEveryDay(stat.BeginDate, stat.EndDate).Where(date => date >= stat.BeginDate && date <= stat.EndDate).Count() - vacationesDays;

                            statistics.Add(stat);
                            vaildBegDate = nextDate;
                        }
                    }
                }
            }

            return new ApiResponse<List<AttendanceStatistics>>(ResponseType.OK, "", statistics);
        }
        /// <summary>
        /// 科室考勤上报录入提交
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="userid"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        /// <exception cref="PerformanceException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public ApiResponse DeptSubmit(int allotId, string unitType, int userid, string userName)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var queryUnitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            string queryAccountingUnit = userInfo.User.Department;

            if (!queryUnitTypes.Contains(unitType)) throw new PerformanceException("当前用户角色与“核算组别”不匹配");
            var attendances = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allotId && queryUnitTypes.Contains(w.UnitType) && queryAccountingUnit.Equals(w.AccountingUnit)) ?? new List<per_attendance_dept>();
            if (!attendances.Any()) return new ApiResponse(ResponseType.OK, "暂无数据，无需提交！");

            var cofaccounting = cofaccountingRepository.GetEntities(g => g.AllotId == allotId);
            var employees = perforPeremployeeRepository.GetEntities(g => g.AllotId == allotId);
            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();

            List<Dictionary<string, string>> error = new List<Dictionary<string, string>>();
            foreach (var item in attendances.Select((row, ind) => (row, ind)))
            {
                var row = item.row;
                if (string.IsNullOrEmpty(row.PersonnelNumber) || string.IsNullOrEmpty(row.PersonnelNumber))
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“员工号/姓名”尚未填写" },
                    });
                    continue;
                }
                if (attendances.Count(w => w.PersonnelNumber.Trim().Equals(row.PersonnelNumber.Trim(), StringComparison.OrdinalIgnoreCase)) > 1)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“员工号”多次填写请删除其他保留一条" },
                    });
                    continue;
                }
                var emp = employees.FirstOrDefault(w => w.PersonnelNumber == row.PersonnelNumber);
                if (emp == null || emp.DoctorName != row.PersonnelName)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“员工号/姓名”无法与“人员字典”对应" },
                    });
                    continue;
                }
                if (row.PermanentStaff == null)
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"“人员系数”尚未填写" },
                    });
                    continue;
                }
                Func<int?, string> getAattendanceType = (typeId) => typeId > 0 ? types.FirstOrDefault(w => w.Id == typeId)?.AttendanceName ?? "考勤类型缺失" : "";
                var dicDays = new[]
                {
                    new { Date = begMonthDate.AddDays(00), TypeName = getAattendanceType(row.Day01), TypeId = row.Day01 },
                    new { Date = begMonthDate.AddDays(01), TypeName = getAattendanceType(row.Day02), TypeId = row.Day02 },
                    new { Date = begMonthDate.AddDays(02), TypeName = getAattendanceType(row.Day03), TypeId = row.Day03 },
                    new { Date = begMonthDate.AddDays(03), TypeName = getAattendanceType(row.Day04), TypeId = row.Day04 },
                    new { Date = begMonthDate.AddDays(04), TypeName = getAattendanceType(row.Day05), TypeId = row.Day05 },
                    new { Date = begMonthDate.AddDays(05), TypeName = getAattendanceType(row.Day06), TypeId = row.Day06 },
                    new { Date = begMonthDate.AddDays(06), TypeName = getAattendanceType(row.Day07), TypeId = row.Day07 },
                    new { Date = begMonthDate.AddDays(07), TypeName = getAattendanceType(row.Day08), TypeId = row.Day08 },
                    new { Date = begMonthDate.AddDays(08), TypeName = getAattendanceType(row.Day09), TypeId = row.Day09 },
                    new { Date = begMonthDate.AddDays(09), TypeName = getAattendanceType(row.Day10), TypeId = row.Day10 },
                    new { Date = begMonthDate.AddDays(10), TypeName = getAattendanceType(row.Day11), TypeId = row.Day11 },
                    new { Date = begMonthDate.AddDays(11), TypeName = getAattendanceType(row.Day12), TypeId = row.Day12 },
                    new { Date = begMonthDate.AddDays(12), TypeName = getAattendanceType(row.Day13), TypeId = row.Day13 },
                    new { Date = begMonthDate.AddDays(13), TypeName = getAattendanceType(row.Day14), TypeId = row.Day14 },
                    new { Date = begMonthDate.AddDays(14), TypeName = getAattendanceType(row.Day15), TypeId = row.Day15 },
                    new { Date = begMonthDate.AddDays(15), TypeName = getAattendanceType(row.Day16), TypeId = row.Day16 },
                    new { Date = begMonthDate.AddDays(16), TypeName = getAattendanceType(row.Day17), TypeId = row.Day17 },
                    new { Date = begMonthDate.AddDays(17), TypeName = getAattendanceType(row.Day18), TypeId = row.Day18 },
                    new { Date = begMonthDate.AddDays(18), TypeName = getAattendanceType(row.Day19), TypeId = row.Day19 },
                    new { Date = begMonthDate.AddDays(19), TypeName = getAattendanceType(row.Day20), TypeId = row.Day20 },
                    new { Date = begMonthDate.AddDays(20), TypeName = getAattendanceType(row.Day21), TypeId = row.Day21 },
                    new { Date = begMonthDate.AddDays(21), TypeName = getAattendanceType(row.Day22), TypeId = row.Day22 },
                    new { Date = begMonthDate.AddDays(22), TypeName = getAattendanceType(row.Day23), TypeId = row.Day23 },
                    new { Date = begMonthDate.AddDays(23), TypeName = getAattendanceType(row.Day24), TypeId = row.Day24 },
                    new { Date = begMonthDate.AddDays(24), TypeName = getAattendanceType(row.Day25), TypeId = row.Day25 },
                    new { Date = begMonthDate.AddDays(25), TypeName = getAattendanceType(row.Day26), TypeId = row.Day26 },
                    new { Date = begMonthDate.AddDays(26), TypeName = getAattendanceType(row.Day27), TypeId = row.Day27 },
                    new { Date = begMonthDate.AddDays(27), TypeName = getAattendanceType(row.Day28), TypeId = row.Day28 },
                    new { Date = begMonthDate.AddDays(28), TypeName = getAattendanceType(row.Day29), TypeId = row.Day29 },
                    new { Date = begMonthDate.AddDays(29), TypeName = getAattendanceType(row.Day30), TypeId = row.Day30 },
                    new { Date = begMonthDate.AddDays(30), TypeName = getAattendanceType(row.Day31), TypeId = row.Day31 },
                };
                var days = dicDays.Where(w => w.Date.Date >= begMonthDate && w.Date.Date <= endMonthDate);
                foreach (var d in days)
                {
                    if (!string.IsNullOrEmpty(d.TypeName) && !d.TypeId.HasValue)
                    {
                        var title = CultureInfo.CurrentCulture.DateTimeFormat.GetDayName(d.Date.DayOfWeek).Replace("星期", "");
                        var beaDay = d.Date.Day.ToString().PadLeft(2, '0');

                        error.Add(new Dictionary<string, string>
                        {
                            { "行号", $"第{item.ind + 1}行" },
                            { "工号", row.PersonnelNumber ?? "" },
                            { "姓名", row.PersonnelName ?? "" },
                            { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                            { "考勤类型", d.TypeName },
                            { "错误原因", $"“{beaDay}({title})”考勤类型“{d.TypeName}”不存在"},
                        });
                    }
                }

                if (days.Count(w => string.IsNullOrEmpty(w.TypeName)) == days.Count())
                {
                    error.Add(new Dictionary<string, string>
                    {
                        { "行号", $"第{item.ind + 1}行" },
                        { "工号", row.PersonnelNumber ?? "" },
                        { "姓名", row.PersonnelName ?? "" },
                        { "人员系数", row.PermanentStaff?.ToString() ?? "" },
                        { "考勤类型", "" },
                        { "错误原因", $"当前人员没有任何考勤记录请删除" },
                    });
                    continue;
                }

            }
            if (error.Count > 0)
                return new ApiResponse(ResponseType.WarningTable, "验证不通过,当前操作已拒绝", error);

            var stateCount = attendances.GroupBy(w => w.State).Select(w => new { w.Key, StateCount = w.Count() }).ToList();

            if (attendances.Count() > 0 && attendances.Count() == attendances.Count(w => w.State == (int)Attendance.Report.通过))
                throw new PerformanceException("当前考勤已审核通过，无法提交！");

            var submitTime = DateTime.Now;
            foreach (var item in attendances)
            {
                item.State = (int)Attendance.Report.提交;
                item.SubmitTime = submitTime;
                item.SubmitUser = userName;
                item.Remarks = null;
            }
            if (attendances.Any())
                _attendanceDeptRepository.UpdateRange(attendances.ToArray());

            return new ApiResponse(ResponseType.OK, "提交成功");
        }
        /// <summary>
        /// 科室考勤上报录入撤回
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="userid"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        /// <exception cref="PerformanceException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public ApiResponse DeptRollback(int allotId, string unitType, int userid, string userName)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            //当角色对应时过滤
            var userInfo = _userRepository.GetUser(userid);
            if (userInfo?.User == null) throw new NotImplementedException("当前用户不存在");
            if (userInfo?.URole == null) throw new NotImplementedException("当前用户暂未分配角色");

            var queryUnitTypes = UnitTypeUtil.GetMaps(userInfo?.URole.Type ?? 0);
            string queryAccountingUnit = userInfo.User.Department;

            if (!queryUnitTypes.Contains(unitType)) throw new PerformanceException("当前用户角色与“核算组别”不匹配");
            var attendances = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allotId && queryUnitTypes.Contains(w.UnitType) && queryAccountingUnit.Equals(w.AccountingUnit))
                ?? new List<per_attendance_dept>();

            if (!attendances.Any()) return new ApiResponse(ResponseType.OK, "暂无数据，无需撤回！");

            var stateCount = attendances.GroupBy(w => w.State).Select(w => new { w.Key, StateCount = w.Count() }).ToList();

            if (attendances.Count() > 0 && attendances.Count() == attendances.Count(w => w.State == (int)Attendance.Report.通过))
                throw new PerformanceException("当前考勤已审核通过，无法撤回！");

            if (attendances.Count() > 0 && attendances.Count() == attendances.Count(w => w.State == (int)Attendance.Report.未提交))
                throw new PerformanceException("当前考勤尚未提交，无需撤回！");

            if (attendances.Count() > 0 && attendances.Count() == attendances.Count(w => w.State == (int)Attendance.Report.驳回))
                throw new PerformanceException("当前考勤尚已驳回，无需撤回！");

            var submitTime = DateTime.Now;
            foreach (var item in attendances)
            {
                item.State = (int)Attendance.Report.未提交;
                item.SubmitTime = null;
                item.SubmitUser = null;
                item.AuditTime = null;
                item.AuditUser = null;
                item.Remarks = null;
            }
            if (attendances.Any())
                _attendanceDeptRepository.UpdateRange(attendances.ToArray());

            return new ApiResponse(ResponseType.OK, "撤回成功");
        }
        /// <summary>
        /// 科室考勤上报结果审核
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="state"></param>
        /// <param name="remark"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        public ApiResponse DeptAudit(int allotId, string unitType, string accountingUnit, int state, string remark, string userName)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var attendances = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allotId && unitType.Equals(w.UnitType) && accountingUnit.Equals(w.AccountingUnit))
                ?? new List<per_attendance_dept>();

            if (attendances.Count() == 0)
                throw new PerformanceException("当前考勤暂无数据，无法审核！");

            //if (attendances.Count() == attendances.Count(w => w.State == (int)Attendance.Report.通过))
            //    throw new PerformanceException("当前考勤已审核通过，无需审核！");

            //if (attendances.Count() != attendances.Count(w => w.State == (int)Attendance.Report.提交))
            //    throw new PerformanceException("当前考勤尚未提交，请勿审核！");

            var optTime = DateTime.Now;
            foreach (var item in attendances)
            {
                item.State = state;
                item.AuditTime = optTime;
                item.AuditUser = userName;
                item.Remarks = remark;
            }
            if (attendances.Any())
            {
                _attendanceDeptRepository.UpdateRange(attendances.ToArray());
                DeptReportRefresh(allot, state, attendances);
            }

            return new ApiResponse(ResponseType.OK, "审核成功");
        }
        /// <summary>
        /// 通过或驳回后存储删除数据
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="state"></param>
        public void DeptReportRefresh(int allotId, int state)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");
            var attendances = _attendanceDeptRepository.GetEntities((w) => w.AllotId == allot.ID) ?? new List<per_attendance_dept>();
            DeptReportRefresh(allot, state, attendances);
        }

        /// <summary>
        /// 通过或驳回后存储删除数据
        /// </summary>
        /// <param name="allot"></param>
        /// <param name="state"></param>
        /// <param name="attendances"></param>
        public void DeptReportRefresh(per_allot allot, int state, List<per_attendance_dept> attendances)
        {
            attendances ??= new List<per_attendance_dept>();
            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allot.ID) ?? new List<per_attendance_type>();
            Func<int?, string> getAattendanceType = (typeId) => typeId > 0 ? types.FirstOrDefault(w => w.Id == typeId)?.AttendanceName ?? "考勤类型缺失" : "";
            List<AttendanceDeptMore> datas = attendances.Select(w => new AttendanceDeptMore
            {
                AllotId = allot.ID,
                Code = w.Code,
                UnitType = w.UnitType,
                AccountingUnit = w.AccountingUnit,
                PersonnelNumber = w.PersonnelNumber,
                PersonnelName = w.PersonnelName,
                PermanentStaff = w.PermanentStaff,
                AuditTime = DateTime.Now,
                AuditUser = "",
                Day01 = getAattendanceType(w.Day01),
                Day02 = getAattendanceType(w.Day02),
                Day03 = getAattendanceType(w.Day03),
                Day04 = getAattendanceType(w.Day04),
                Day05 = getAattendanceType(w.Day05),
                Day06 = getAattendanceType(w.Day06),
                Day07 = getAattendanceType(w.Day07),
                Day08 = getAattendanceType(w.Day08),
                Day09 = getAattendanceType(w.Day09),
                Day10 = getAattendanceType(w.Day10),
                Day11 = getAattendanceType(w.Day11),
                Day12 = getAattendanceType(w.Day12),
                Day13 = getAattendanceType(w.Day13),
                Day14 = getAattendanceType(w.Day14),
                Day15 = getAattendanceType(w.Day15),
                Day16 = getAattendanceType(w.Day16),
                Day17 = getAattendanceType(w.Day17),
                Day18 = getAattendanceType(w.Day18),
                Day19 = getAattendanceType(w.Day19),
                Day20 = getAattendanceType(w.Day20),
                Day21 = getAattendanceType(w.Day21),
                Day22 = getAattendanceType(w.Day22),
                Day23 = getAattendanceType(w.Day23),
                Day24 = getAattendanceType(w.Day24),
                Day25 = getAattendanceType(w.Day25),
                Day26 = getAattendanceType(w.Day26),
                Day27 = getAattendanceType(w.Day27),
                Day28 = getAattendanceType(w.Day28),
                Day29 = getAattendanceType(w.Day29),
                Day30 = getAattendanceType(w.Day30),
                Day31 = getAattendanceType(w.Day31),
            }).ToList();

            var computeResult = DeptCompute(allot.ID, datas)?.Data ?? new List<AttendanceStatistics>();
            var newEntities = computeResult
                .Select(item =>
                {
                    var deductionDays = item.Detial.Where(w => w.Remark.Equals(Attendance.Deduction.核减.ToString())).Sum(w => w.Value);
                    var noDeductionDays = item.Detial.Where(w => w.Remark.Equals(Attendance.Deduction.不核减.ToString())).Sum(w => w.Value);
                    return new per_attendance_dept_report
                    {
                        AllotId = allot.ID,
                        HospitalId = allot.HospitalId,
                        Code = item.Code,
                        UnitType = item.UnitType,
                        AccountingUnit = item.AccountingUnit,
                        PersonnelNumber = item.PersonnelNumber,
                        PersonnelName = item.PersonnelName,
                        PermanentStaff = item.PermanentStaff,
                        BeginDate = item.BeginDate,
                        EndDate = item.EndDate,
                        DeductionDays = deductionDays,
                        NoDeductionDays = noDeductionDays,
                        AttendanceDays = item.AttendanceDays,
                        WorkFullDays = item.AttendanceDays + deductionDays,
                        CreateTime = DateTime.Now,
                    };
                });
            var unitTypes = attendances.Select(w => w.UnitType).Distinct().ToList();
            var accountingUnits = attendances.Select(w => w.AccountingUnit).Distinct().ToList();
            var oldEntities = _perforPerAttendanceDeptReportRepository.GetEntities(w => w.AllotId == allot.ID && unitTypes.Contains(w.UnitType) && accountingUnits.Contains(w.AccountingUnit));
            if (oldEntities?.Any() == true)
            {
                _perforPerAttendanceDeptReportRepository.RemoveRange(oldEntities.ToArray());
            }
            if (state == (int)Attendance.Report.通过 && newEntities?.Any() == true)
            {
                _perforPerAttendanceDeptReportRepository.AddRange(newEntities.ToArray());
            }
        }

        /// <summary>
        /// 科室考勤上报结果详情
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="accountingUnit"></param>
        /// <returns></returns>
        public ApiResponse<AttendanceDeptDetail> DeptDetail(int allotId, string unitType, string accountingUnit, string searchTxet)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            Expression<Func<per_attendance_dept, bool>> deptExpression = (w) => w.AllotId == allotId;
            if (!string.IsNullOrEmpty(unitType))
                deptExpression = deptExpression.And(w => w.UnitType.Equals(unitType));
            if (!string.IsNullOrEmpty(accountingUnit))
                deptExpression = deptExpression.And(w => w.AccountingUnit.Equals(accountingUnit));
            if (!string.IsNullOrEmpty(searchTxet))
                deptExpression = deptExpression.And(w => w.PersonnelNumber.Contains(searchTxet) || w.PersonnelName.Contains(searchTxet));

            var attendances = _attendanceDeptRepository.GetEntities(deptExpression) ?? new List<per_attendance_dept>();

            AttendanceDeptDetail detail = new AttendanceDeptDetail()
            {
                Columns = new List<TColumn>(),
                Data = new List<AttendanceDeptMore>(),
            };
            detail.Columns.Add(new TColumn(nameof(AttendanceDeptMore.PersonnelNumber).ToLower(), "工号", 80, "left", true));
            detail.Columns.Add(new TColumn(nameof(AttendanceDeptMore.PersonnelName).ToLower(), "姓名", 80, "left", true));
            detail.Columns.Add(new TColumn(nameof(AttendanceDeptMore.PermanentStaff).ToLower(), "人员系数", 80, "left", true));
            for (DateTime i = begMonthDate; i <= endMonthDate; i = i.AddDays(1))
            {
                var title = CultureInfo.CurrentCulture.DateTimeFormat.GetDayName(i.DayOfWeek).Replace("星期", "");
                var beaDay = i.Day.ToString().PadLeft(2, '0');
                detail.Columns.Add(new TColumn($"Day{beaDay}".ToLower(), $"{beaDay}({title})", width: 60));
            }

            if (!attendances.Any()) return new ApiResponse<AttendanceDeptDetail>(ResponseType.OK, "操作成功", detail);

            var cofaccounting = cofaccountingRepository.GetEntities(g => g.AllotId == allotId);
            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            Func<int?, string> getAattendanceType = (typeId) => typeId > 0 ? types.FirstOrDefault(w => w.Id == typeId)?.AttendanceName ?? "考勤类型缺失" : "";

            foreach (var row in attendances)
            {
                var accounting = cofaccounting.FirstOrDefault(p => p.UnitType == unitType && p.AccountingUnit == accountingUnit);
                var attendance = new AttendanceDeptMore
                {
                    AllotId = allotId,
                    Code = accounting?.Code ?? "",
                    UnitType = row.UnitType,
                    AccountingUnit = row.AccountingUnit,
                };
                attendance.PersonnelNumber = row.PersonnelNumber;
                attendance.PersonnelName = row.PersonnelName;
                attendance.PermanentStaff = row.PermanentStaff;
                attendance.Day01 = getAattendanceType(row.Day01);
                attendance.Day02 = getAattendanceType(row.Day02);
                attendance.Day03 = getAattendanceType(row.Day03);
                attendance.Day04 = getAattendanceType(row.Day04);
                attendance.Day05 = getAattendanceType(row.Day05);
                attendance.Day06 = getAattendanceType(row.Day06);
                attendance.Day07 = getAattendanceType(row.Day07);
                attendance.Day08 = getAattendanceType(row.Day08);
                attendance.Day09 = getAattendanceType(row.Day09);
                attendance.Day10 = getAattendanceType(row.Day10);
                attendance.Day11 = getAattendanceType(row.Day11);
                attendance.Day12 = getAattendanceType(row.Day12);
                attendance.Day13 = getAattendanceType(row.Day13);
                attendance.Day14 = getAattendanceType(row.Day14);
                attendance.Day15 = getAattendanceType(row.Day15);
                attendance.Day16 = getAattendanceType(row.Day16);
                attendance.Day17 = getAattendanceType(row.Day17);
                attendance.Day18 = getAattendanceType(row.Day18);
                attendance.Day19 = getAattendanceType(row.Day19);
                attendance.Day20 = getAattendanceType(row.Day20);
                attendance.Day21 = getAattendanceType(row.Day21);
                attendance.Day22 = getAattendanceType(row.Day22);
                attendance.Day23 = getAattendanceType(row.Day23);
                attendance.Day24 = getAattendanceType(row.Day24);
                attendance.Day25 = getAattendanceType(row.Day25);
                attendance.Day26 = getAattendanceType(row.Day26);
                attendance.Day27 = getAattendanceType(row.Day27);
                attendance.Day28 = getAattendanceType(row.Day28);
                attendance.Day29 = getAattendanceType(row.Day29);
                attendance.Day30 = getAattendanceType(row.Day30);
                attendance.Day31 = getAattendanceType(row.Day31);

                attendance.State = row.State;
                attendance.SubmitUser = row.SubmitUser;
                attendance.SubmitTime = row.SubmitTime;
                attendance.AuditUser = row.AuditUser;
                attendance.AuditTime = row.AuditTime;
                attendance.Remarks = row.Remarks;

                detail.Data.Add(attendance);
            }
            return new ApiResponse<AttendanceDeptDetail>(ResponseType.OK, "操作成功", detail);
        }
        /// <summary>
        /// 科室考勤上报汇总统计
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="unitType"></param>
        /// <param name="accountingUnit"></param>
        /// <param name="searchTxet"></param>
        /// <returns></returns>
        /// <exception cref="PerformanceException"></exception>
        public ApiResponse<List<AttendanceDeptReport>> DeptReport(int allotId, string unitType, string accountingUnit, string searchTxet)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            Expression<Func<per_attendance_dept, bool>> deptExpression = (w) => w.AllotId == allotId;
            if (!string.IsNullOrEmpty(unitType))
                deptExpression = deptExpression.And(w => w.UnitType.Equals(unitType));
            if (!string.IsNullOrEmpty(accountingUnit))
                deptExpression = deptExpression.And(w => w.AccountingUnit.Equals(accountingUnit));
            if (!string.IsNullOrEmpty(searchTxet))
                deptExpression = deptExpression.And(w => w.PersonnelNumber.Contains(searchTxet) || w.PersonnelName.Contains(searchTxet));

            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            var attendances = _attendanceDeptRepository.GetEntities(deptExpression) ?? new List<per_attendance_dept>();
            var names = typeof(per_attendance_dept_report).GetProperties().Select(w => w.Name);
            var rows = _service.QueryAttendanceDeptReport(allotId, unitType, accountingUnit, searchTxet);
            Func<int?, string> getAattendanceType = (typeId) => typeId > 0 ? types.FirstOrDefault(w => w.Id == typeId)?.AttendanceName ?? "考勤类型缺失" : "";
            var temp1 = new List<int?>();
            foreach (var item in attendances)
            {
                var Day = new[] {
                    new { TypeId = item.Day01 },
                    new {  TypeId = item.Day02 },
                    new {  TypeId = item.Day03 },
                    new {  TypeId = item.Day04 },
                    new {  TypeId = item.Day05 },
                    new {  TypeId = item.Day06 },
                    new {  TypeId = item.Day07 },
                    new {  TypeId = item.Day08 },
                    new {  TypeId = item.Day09 },
                    new {  TypeId = item.Day10 },
                    new { TypeId = item.Day11 },
                    new {  TypeId = item.Day12 },
                    new { TypeId = item.Day13 },
                    new {  TypeId = item.Day14 },
                    new { TypeId = item.Day15 },
                    new {  TypeId = item.Day16 },
                    new {  TypeId = item.Day17 },
                    new {  TypeId = item.Day18 },
                    new { TypeId = item.Day19 },
                    new {  TypeId = item.Day20 },
                    new {  TypeId = item.Day21 },
                    new {  TypeId = item.Day22 },
                    new { TypeId = item.Day23 },
                    new {  TypeId = item.Day24 },
                    new {  TypeId = item.Day25 },
                    new { TypeId = item.Day26 },
                    new {  TypeId = item.Day27 },
                    new {  TypeId = item.Day28 },
                    new {  TypeId = item.Day29 },
                    new {  TypeId = item.Day30 },
                    new {  TypeId = item.Day31 },
                };
                List<int?> temp2 = Day.Where(w => w.TypeId != null).Distinct().Select(w => w.TypeId).ToList();
                temp1.AddRange(temp2);
            }
            var temp3 = temp1.Distinct();
            types = types.Where(w => temp3.Contains(w.Id)).ToList();
            List<AttendanceDeptReport> items = new List<AttendanceDeptReport>();
            foreach (var row in rows)
            {
                AttendanceDeptReport item = JsonHelper.Deserialize<AttendanceDeptReport>(JsonHelper.Serialize(row));
                item.Detial = new List<AttendanceDeptReportItem>();
                var atte = attendances.FirstOrDefault(w => w.UnitType == item.UnitType && w.AccountingUnit == item.AccountingUnit && w.PersonnelNumber == item.PersonnelNumber) ?? new per_attendance_dept();
                var days = new[]
                {
                    new { Date = begMonthDate.AddDays(00), TypeId = atte.Day01 },
                    new { Date = begMonthDate.AddDays(01), TypeId = atte.Day02 },
                    new { Date = begMonthDate.AddDays(02), TypeId = atte.Day03 },
                    new { Date = begMonthDate.AddDays(03), TypeId = atte.Day04 },
                    new { Date = begMonthDate.AddDays(04), TypeId = atte.Day05 },
                    new { Date = begMonthDate.AddDays(05), TypeId = atte.Day06 },
                    new { Date = begMonthDate.AddDays(06), TypeId = atte.Day07 },
                    new { Date = begMonthDate.AddDays(07), TypeId = atte.Day08 },
                    new { Date = begMonthDate.AddDays(08), TypeId = atte.Day09 },
                    new { Date = begMonthDate.AddDays(09), TypeId = atte.Day10 },
                    new { Date = begMonthDate.AddDays(10), TypeId = atte.Day11 },
                    new { Date = begMonthDate.AddDays(11), TypeId = atte.Day12 },
                    new { Date = begMonthDate.AddDays(12), TypeId = atte.Day13 },
                    new { Date = begMonthDate.AddDays(13), TypeId = atte.Day14 },
                    new { Date = begMonthDate.AddDays(14), TypeId = atte.Day15 },
                    new { Date = begMonthDate.AddDays(15), TypeId = atte.Day16 },
                    new { Date = begMonthDate.AddDays(16), TypeId = atte.Day17 },
                    new { Date = begMonthDate.AddDays(17), TypeId = atte.Day18 },
                    new { Date = begMonthDate.AddDays(18), TypeId = atte.Day19 },
                    new { Date = begMonthDate.AddDays(19), TypeId = atte.Day20 },
                    new { Date = begMonthDate.AddDays(20), TypeId = atte.Day21 },
                    new { Date = begMonthDate.AddDays(21), TypeId = atte.Day22 },
                    new { Date = begMonthDate.AddDays(22), TypeId = atte.Day23 },
                    new { Date = begMonthDate.AddDays(23), TypeId = atte.Day24 },
                    new { Date = begMonthDate.AddDays(24), TypeId = atte.Day25 },
                    new { Date = begMonthDate.AddDays(25), TypeId = atte.Day26 },
                    new { Date = begMonthDate.AddDays(26), TypeId = atte.Day27 },
                    new { Date = begMonthDate.AddDays(27), TypeId = atte.Day28 },
                    new { Date = begMonthDate.AddDays(28), TypeId = atte.Day29 },
                    new { Date = begMonthDate.AddDays(29), TypeId = atte.Day30 },
                    new { Date = begMonthDate.AddDays(30), TypeId = atte.Day31 },
                };
                var arr = days.Where(w => w.Date.Date >= item.BeginDate.Date && w.Date.Date <= item.EndDate.Date)
                    .GroupBy(w => w.TypeId)
                    .Select(w => new { TypeId = w.Key, TypeName = getAattendanceType(w.Key), Value = w.Count() })
                    .ToList();
                foreach (var d in arr)
                {
                    item.Detial.Add(new AttendanceDeptReportItem
                    {
                        Title = d.TypeName,
                        Value = d.Value,
                        Remark = types.FirstOrDefault(w => w.Id == d.TypeId)?.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                    });
                }
                foreach (var t in types)
                {
                    if (!item.Detial.Any(w => w.Title == t.AttendanceName))
                    {
                        item.Detial.Add(new AttendanceDeptReportItem
                        {
                            Title = t.AttendanceName,
                            Value = 0,
                            Remark = t.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                        });
                    }
                }
                var dic = new RouteValueDictionary(row);
                foreach (var dicItem in dic.Where(w => !names.Any(name => name.Equals(w.Key, StringComparison.OrdinalIgnoreCase))))
                {
                    item.Detial.Add(new AttendanceDeptReportItem { Title = dicItem.Key, Remark = "", Value = dicItem.Value ?? "" });
                }
                items.Add(item);
            }
            items = items.OrderBy(w => w.PersonnelNumber).ThenBy(w => w.BeginDate).ToList();
            return new ApiResponse<List<AttendanceDeptReport>>(ResponseType.OK, items);
        }

        public ApiResponse<List<AttendanceDeptReport>> DeptComputeReport(int allotId, string unitType, string accountingUnit, string searchTxet)
        {
            var allot = perforPerallotRepository.GetEntity(w => w.ID == allotId);
            if (allot == null)
                throw new PerformanceException("当前绩效记录不存在");

            var begMonthDate = allot.Month >= 1 && allot.Month <= 12 ? new DateTime(allot.Year, allot.Month, 1) : new DateTime(allot.Year, 12, 1);
            var endMonthDate = begMonthDate.AddMonths(1).AddDays(-1);

            Expression<Func<per_attendance_dept, bool>> deptExpression = (w) => w.AllotId == allotId;
            if (!string.IsNullOrEmpty(unitType))
                deptExpression = deptExpression.And(w => w.UnitType.Equals(unitType));
            if (!string.IsNullOrEmpty(accountingUnit))
                deptExpression = deptExpression.And(w => w.AccountingUnit.Equals(accountingUnit));
            if (!string.IsNullOrEmpty(searchTxet))
                deptExpression = deptExpression.And(w => w.PersonnelNumber.Contains(searchTxet) || w.PersonnelName.Contains(searchTxet));

            var types = perfoPperAttendanceTypeRepository.GetEntities(t => t.AllotId == allotId) ?? new List<per_attendance_type>();
            var attendances = _attendanceDeptRepository.GetEntities(deptExpression) ?? new List<per_attendance_dept>();
            var names = typeof(per_attendance_dept_report).GetProperties().Select(w => w.Name);
            var rows = _service.QueryAttendanceDeptReportCompute(allotId, unitType, accountingUnit, searchTxet);
            Func<int?, string> getAattendanceType = (typeId) => typeId > 0 ? types.FirstOrDefault(w => w.Id == typeId)?.AttendanceName ?? "考勤类型缺失" : "";
            var temp1 = new List<int?>();
            foreach (var item in attendances)
            {
                var Day = new[] {
                    new { TypeId = item.Day01 },
                    new {  TypeId = item.Day02 },
                    new {  TypeId = item.Day03 },
                    new {  TypeId = item.Day04 },
                    new {  TypeId = item.Day05 },
                    new {  TypeId = item.Day06 },
                    new {  TypeId = item.Day07 },
                    new {  TypeId = item.Day08 },
                    new {  TypeId = item.Day09 },
                    new {  TypeId = item.Day10 },
                    new { TypeId = item.Day11 },
                    new {  TypeId = item.Day12 },
                    new { TypeId = item.Day13 },
                    new {  TypeId = item.Day14 },
                    new { TypeId = item.Day15 },
                    new {  TypeId = item.Day16 },
                    new {  TypeId = item.Day17 },
                    new {  TypeId = item.Day18 },
                    new { TypeId = item.Day19 },
                    new {  TypeId = item.Day20 },
                    new {  TypeId = item.Day21 },
                    new {  TypeId = item.Day22 },
                    new { TypeId = item.Day23 },
                    new {  TypeId = item.Day24 },
                    new {  TypeId = item.Day25 },
                    new { TypeId = item.Day26 },
                    new {  TypeId = item.Day27 },
                    new {  TypeId = item.Day28 },
                    new {  TypeId = item.Day29 },
                    new {  TypeId = item.Day30 },
                    new {  TypeId = item.Day31 },
                };
                List<int?> temp2 = Day.Where(w => w.TypeId != null).Distinct().Select(w => w.TypeId).ToList();
                temp1.AddRange(temp2);
            }
            var temp3 = temp1.Distinct();
            types = types.Where(w => temp3.Contains(w.Id)).ToList();
            List<AttendanceDeptReport> items = new List<AttendanceDeptReport>();
            foreach (var row in rows)
            {
                AttendanceDeptReport item = JsonHelper.Deserialize<AttendanceDeptReport>(JsonHelper.Serialize(row));
                item.Detial = new List<AttendanceDeptReportItem>();
                var atte = attendances.FirstOrDefault(w => w.UnitType == item.UnitType && w.AccountingUnit == item.AccountingUnit && w.PersonnelNumber == item.PersonnelNumber) ?? new per_attendance_dept();
                var days = new[] {
                    new { TypeId = atte.Day01 },
                    new {  TypeId = atte.Day02 },
                    new {  TypeId = atte.Day03 },
                    new {  TypeId = atte.Day04 },
                    new {  TypeId = atte.Day05 },
                    new {  TypeId = atte.Day06 },
                    new {  TypeId = atte.Day07 },
                    new {  TypeId = atte.Day08 },
                    new {  TypeId = atte.Day09 },
                    new {  TypeId = atte.Day10 },
                    new { TypeId = atte.Day11 },
                    new {  TypeId = atte.Day12 },
                    new { TypeId = atte.Day13 },
                    new {  TypeId = atte.Day14 },
                    new { TypeId = atte.Day15 },
                    new {  TypeId = atte.Day16 },
                    new {  TypeId = atte.Day17 },
                    new {  TypeId = atte.Day18 },
                    new { TypeId = atte.Day19 },
                    new {  TypeId = atte.Day20 },
                    new {  TypeId = atte.Day21 },
                    new {  TypeId = atte.Day22 },
                    new { TypeId = atte.Day23 },
                    new {  TypeId = atte.Day24 },
                    new {  TypeId = atte.Day25 },
                    new { TypeId = atte.Day26 },
                    new {  TypeId = atte.Day27 },
                    new {  TypeId = atte.Day28 },
                    new {  TypeId = atte.Day29 },
                    new {  TypeId = atte.Day30 },
                    new {  TypeId = atte.Day31 },
                };
                var arr = days.Where(w => w.TypeId.HasValue && w.TypeId > 0)
                    .GroupBy(w => w.TypeId)
                    .Select(w => new { TypeId = w.Key, TypeName = getAattendanceType(w.Key), Value = w.Count() })
                    .ToList();
                foreach (var d in arr)
                {
                    item.Detial.Add(new AttendanceDeptReportItem
                    {
                        Title = d.TypeName,
                        Value = d.Value,
                        Remark = types.FirstOrDefault(w => w.Id == d.TypeId)?.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                    });
                }
                foreach (var t in types)
                {
                    if (!item.Detial.Any(w => w.Title == t.AttendanceName))
                    {
                        item.Detial.Add(new AttendanceDeptReportItem
                        {
                            Title = t.AttendanceName,
                            Value = 0,
                            Remark = t.IsDeduction == (int)Attendance.Deduction.核减 ? "核减" : "不核减",
                        });
                    }
                }
                var dic = new RouteValueDictionary(row);
                foreach (var dicItem in dic.Where(w => !names.Any(name => name.Equals(w.Key, StringComparison.OrdinalIgnoreCase))))
                {
                    item.Detial.Add(new AttendanceDeptReportItem { Title = dicItem.Key, Remark = "", Value = dicItem.Value ?? 0 });
                }
                items.Add(item);
            }
            items = items.OrderBy(w => w.PersonnelNumber).ThenBy(w => w.BeginDate).ToList();
            return new ApiResponse<List<AttendanceDeptReport>>(ResponseType.OK, items);
        }


        #endregion

        #region 拆分请假时间段为每个日期
        // 拆分请假时间段为每个日期
        private List<DateTime> SplitEveryDay(DateTime begin, DateTime end)
        {
            List<DateTime> dates = new List<DateTime>();
            for (int i = 0; i <= (end - begin).TotalDays; i++)
            {
                dates.Add(begin.AddDays(i));
            }
            return dates;
        }
        #endregion

        #region 下载
        public string ExcelDownload(List<Dictionary<string, object>> rows, string name, int allotId, List<ExcelDownloadHeads> headList)
        {
            var perAllot = perforPerallotRepository.GetEntity(t => t.ID == allotId);
            string title = $"{perAllot.Year}年{perAllot.Month}月{name}";

            var data = new List<Dictionary<string, object>>();
            foreach (var obj in rows)
            {
                Dictionary<string, object> nobj = new Dictionary<string, object>();

                foreach (var item in obj)
                {
                    var lower = item.Key.ToLower();

                    if (lower.Contains("date"))
                        nobj[lower] = Convert.ToDateTime(item.Value).ToString("d");
                    else if (lower.Contains("detial"))
                    {
                        var detRows = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(item.Value.ToString());
                        foreach (var detlist in detRows)
                        {
                            object value = null;
                            foreach (var detitem in detlist)
                            {
                                if (detitem.Key.Equals("Value", StringComparison.OrdinalIgnoreCase))
                                    value = detitem.Value;
                                if (detitem.Key.Equals("Title", StringComparison.OrdinalIgnoreCase))
                                    nobj[detitem.Value.ToString()] = value;
                            }

                        }
                    }
                    else
                        nobj[lower] = item.Value;
                }
                data.Add(nobj);
            }

            var dpath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Files");
            if (!Directory.Exists(dpath)) Directory.CreateDirectory(dpath);

            string filepath = Path.Combine(dpath, $"{name}{DateTime.Now:yyyy年MM月dd日}");
            if (File.Exists(filepath)) File.Delete(filepath);

            using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate))
            using (ExcelPackage package = new ExcelPackage(fs))
            {
                var worksheet = package.Workbook.Worksheets.Add(name);

                if (rows != null && rows.Count() > 0)
                {
                    worksheet.SetValue(1, 1, title);

                    for (int col = 0; col < headList.Count; col++)
                    {
                        worksheet.SetValue(2, col + 1, headList[col].Alias);
                    }
                    for (int col = 0; col < headList.Count; col++)
                    {
                        for (int row = 0; row < data.Count(); row++)
                        {
                            var temp = data.ElementAt(row);

                            var key = headList[col].Name;
                            if (!temp.ContainsKey(key))
                                key = key.ToLower();
                            temp.TryGetValue(key, out object value);
                            worksheet.Cells[row + 3, col + 1].Value = value ?? "";

                        }
                    }

                    #region 样式设置
                    for (int row = worksheet.Dimension.Start.Row; row <= worksheet.Dimension.End.Row; row++)
                    {
                        worksheet.Row(row).Height = 20;
                        for (int col = worksheet.Dimension.Start.Column; col <= worksheet.Dimension.End.Column; col++)
                        {
                            worksheet.Cells[row, col].Style.Border.BorderAround(ExcelBorderStyle.Thin);
                            worksheet.Cells[row, col].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
                            worksheet.Cells[row, col].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                        }
                    }
                    worksheet.Cells[1, 1, 1, headList.Count].Merge = true;
                    worksheet.Cells[1, 1, 1, headList.Count].Style.Font.Bold = true;
                    worksheet.Cells[1, 1, 1, headList.Count].Style.Font.Size = 16;
                    worksheet.Row(1).Height = 24;
                    worksheet.Cells[2, 1, 2, headList.Count].Style.Font.Bold = true;
                    worksheet.View.FreezePanes(3, 1);
                    worksheet.Cells.AutoFitColumns();
                    for (int col = worksheet.Dimension.Start.Column; col <= worksheet.Dimension.End.Column; col++)
                    {
                        worksheet.Column(col).Width = worksheet.Column(col).Width > 20 ? 20 : worksheet.Column(col).Width;
                    }
                    #endregion
                }
                package.Save();
            }
            return filepath;
        }
        #endregion
    }
}
