using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using System.IO.Compression;
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using static Tiobon.Core.Model.Consts;

namespace Tiobon.Core.Services;

/// <summary>
/// 个人简历 (服务)
/// </summary>
public class Ghrh_ResumeServices : BaseServices<Ghrh_Resume, Ghrh_ResumeDto, InsertGhrh_ResumeInput, EditGhrh_ResumeInput>, IGhrh_ResumeServices
{
    #region 变量定义

    private readonly IBaseRepository<Ghrh_Resume> _dal;
    private readonly IGhrh_ResumeEduBGServices _ghrh_ResumeEduBGServices;
    private readonly IGhrh_ResumeWorkExpServices _ghrh_ResumeWorkExpServices;

    /// <summary>
    /// 标签
    /// </summary>
    private readonly IGhrh_ResumeTagServices _ghrh_ResumeTagServices;
    /// <summary>
    /// 家庭关系
    /// </summary>
    private readonly IGhrh_ResumeHomeServices _ghrh_ResumeHomeServices;
    private readonly IGhrh_ResumeLicenceServices _ghrh_ResumeLicenceServices;
    private readonly IGhrh_ResumeTrainingServices _ghrh_ResumeTrainingServices;
    private readonly IGhrh_ResumeStatementServices _ghrh_ResumeStatementServices;
    /// <summary>
    /// 面试工单
    /// </summary>
    private readonly IGhrh_InterviewOrderServices _ghrh_InterviewOrderServices;

    /// <summary>
    /// 面试记录
    /// </summary>
    private readonly IGhrh_InterviewRecordServices _ghrh_InterviewRecordServices;

    /// <summary>
    /// 
    /// </summary>
    private readonly IGhrh_InterviewLogServices _ghrh_InterviewLogServices;
    private readonly IGhrh_HumanRequestServices _ghrh_HumanRequestServices;
    private readonly IGhrh_AssessConfigServices _ghrh_AssessConfigServices;
    private readonly IGhrh_InterviewAssessServices _ghrh_InterviewAssessServices;
    private readonly IGhrh_InterviewAssessDetailServices _ghrh_InterviewAssessDetailServices;
    private readonly IGhrh_TemplateServices _ghrh_TemplateServices;
    private readonly IWebHostEnvironment Env;
    private readonly IConverter _converter;
    private readonly IWebHostEnvironment _hostingEnvironment;
    public Ghrh_ResumeServices(ICaching caching,
        IBaseRepository<Ghrh_Resume> dal,
        IGhrh_ResumeEduBGServices ghre_ResumeEduBGServices,
        IGhrh_ResumeTagServices ghre_ResumeTagServices,
        IGhrh_ResumeHomeServices ghre_ResumeHomeServices,
        IGhrh_ResumeLicenceServices ghre_ResumeLicenceServices,
        IGhrh_ResumeTrainingServices ghre_ResumeTrainingServices,
        IGhrh_ResumeStatementServices ghre_ResumeStatementServices,
        IGhrh_InterviewOrderServices ghre_InterviewOrderServices,
        IGhrh_InterviewRecordServices ghre_InterviewRecordServices,
        IGhrh_InterviewLogServices ghrh_InterviewLogServices,
        IGhrh_HumanRequestServices ghrh_HumanRequestServices,
        IGhrh_AssessConfigServices ghrh_AssessConfigServices,
        IGhrh_InterviewAssessServices ghrh_InterviewAssessServices,
        IGhrh_InterviewAssessDetailServices ghrh_InterviewAssessDetailServices,
        IGhrh_TemplateServices ghrh_TemplateServices,
        IConverter converter,
        IWebHostEnvironment hostingEnvironment,
        IGhrh_ResumeWorkExpServices ghre_ResumeWorkExpServices, IWebHostEnvironment env)
    {
        this._dal = dal;
        base.BaseDal = dal;
        base._caching = caching;
        _ghrh_ResumeEduBGServices = ghre_ResumeEduBGServices;
        _ghrh_ResumeTagServices = ghre_ResumeTagServices;
        _ghrh_ResumeHomeServices = ghre_ResumeHomeServices;
        _ghrh_ResumeLicenceServices = ghre_ResumeLicenceServices;
        _ghrh_ResumeTrainingServices = ghre_ResumeTrainingServices;
        _ghrh_ResumeStatementServices = ghre_ResumeStatementServices;
        _ghrh_InterviewOrderServices = ghre_InterviewOrderServices;
        _ghrh_InterviewRecordServices = ghre_InterviewRecordServices;
        _ghrh_ResumeWorkExpServices = ghre_ResumeWorkExpServices;
        _ghrh_InterviewLogServices = ghrh_InterviewLogServices;
        _ghrh_HumanRequestServices = ghrh_HumanRequestServices;
        _ghrh_AssessConfigServices = ghrh_AssessConfigServices;
        _ghrh_InterviewAssessServices = ghrh_InterviewAssessServices;
        _ghrh_InterviewAssessDetailServices = ghrh_InterviewAssessDetailServices;
        _ghrh_TemplateServices = ghrh_TemplateServices;
        Env = env;
        _converter = converter;
        _hostingEnvironment = hostingEnvironment;
    }
    #endregion

    #region 字典映射、全称、单位转换等
    /// <summary>
    /// 字典映射、全称、单位转换等
    /// </summary>
    /// <param name="view"></param>
    private async Task SetLabel(Ghrh_ResumeDto x)
    {
        if (x != null)
        {
            if (x.Education.IsNullOrEmpty())
                x.Education = x.EduDegree;
            x.TitleLabel = x.TitleName;
            x.GenderLabel = await GetParaLabel("Gender", x.Gender);
            x.EducationLabel = await GetParaLabel("EducationalBGLevel", x.Education);
            x.NationLabel = await GetParaLabel("A02", x.Nation);
            x.MaritalStatusLabel = await GetParaLabel("MaritalStatus", x.MaritalStatus);
            x.PoliticStatusLabel = await GetParaLabel("PoliticStatus", x.PoliticStatus);
            x.UrgentRelationLabel = await GetParaLabel("SocialRelationType", x.UrgentRelation);
            x.CertificateTypeLabel = await GetParaLabel("IDCardType", x.CertificateType);
            x.RegisteredTypeLabel = await GetParaLabel("RegisteredType", x.RegisteredType);
            x.ApplyStatusLabel = await GetParaLabel("ResumeApplyStatus", x.ApplyStatus);
            x.ChannelLabel = await GetParaLabel("ResumeChannel", x.Channel);
            x.EduDegreeLabel = await GetParaLabel("EducationalBGLevel", x.EduDegree);
            x.SalaryPeriod = await GetParaLabel("SalaryPeriod", x.SalaryPeriod);

            #region 计算年龄
            if (x.Birthday != null && x.Birthday > DateTime.MinValue)
            {
                DateTime birthdate = (DateTime)x.Birthday;
                DateTime now = DateTime.Now;
                int age = now.Year - birthdate.Year;
                if (now.Month < birthdate.Month || (now.Month == birthdate.Month && now.Day < birthdate.Day))
                    age--;
                x.Age = age;
            }
            #endregion
            x.Birthday1 = DateTimeHelper.ConvertToDayString(x.Birthday);
            x.WorkYears = x.WorkYears ?? 0;
            x.Indate1 = DateTimeHelper.ConvertToDayString(x.Indate);

            if (x.IsPass != null)
                x.IsPassLabel = x.IsPass == true ? "通过" : "不通过";
        }
    }
    #endregion

    #region 查询
    public override async Task<ServicePageResult<Ghrh_ResumeDto>> QueryFilterPage(QueryBody filter, string condition, bool? IsEnable = true)
    {
        var query = Db.Queryable<Ghrh_Resume>();

        #region 处理查询条件
        //Expression<Func<Ghrh_Resume, bool>> whereExpression = new Expression<Func<Ghrh_Resume, bool>>();
        var whereExpression = Expressionable.Create<Ghrh_Resume>();
        foreach (JProperty jProperty in filter.jsonParam.Properties())
        {
            var name = jProperty.Name;
            var value = jProperty.Value.ToString();
            if (name == "page" || name == "pageSize")
                continue;
            if (value.IsNotEmptyOrNull())
            {
                var jsonParam = JsonHelper.JsonToObj<JsonParam>(value);

                switch (name)
                {
                    case "WaitRecommend":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitRecommended);
                        break;
                    case "HasRecommend":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => (x.Status == DIC_INTERVIEW_ORDER_STATUS.HasRecommended || x.Status == DIC_INTERVIEW_ORDER_STATUS.Fail));
                        break;
                    case "WaitAppointment":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitAppointment);
                        break;
                    case "WaitInterview":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview);
                        break;
                    case "HasInterview":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => (x.Status == DIC_INTERVIEW_ORDER_STATUS.HasInterview || x.Status == DIC_INTERVIEW_ORDER_STATUS.InterviewFail));
                        break;
                    case "WaitSendOffer":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer);
                        break;
                    case "HasSendOffer":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);
                        break;
                    case "HasOffer":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.HasOffer);
                        break;
                    case "Cancel":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.Cancel);
                        break;
                    case "HasChangeDate":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            whereExpression.And(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.HasChangeDate);
                        break;

                    case "SalaryPeriod":
                    case "EduDegree":
                    case "Education":
                    case "ApplyStatus":
                    case "Gender":
                        //if (jsonParam.columnValue.IsNotEmptyOrNull())
                        //{
                        //    var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                        //    if (!ids1.Any(x => x == "NoFliter"))
                        //        whereExpression.And(x => ids1.Contains(x.Education));
                        //}

                        if (jsonParam.columnValue != null)
                        {
                            var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                            if (!ids1.Any(x => x == "NoFliter"))
                                condition += $" AND {name} IN ({string.Join(",", ids1.Select(id => "'" + id + "'"))})";
                        }
                        break;

                    case "WorkYears":
                    case "Age":
                        if (jsonParam.columnValue.IsNotEmptyOrNull())
                        {
                            var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                            if (!ids1.Any(x => x == "NoFliter"))
                            {
                                var i = 0;
                                condition += " AND (";
                                ids1.ForEach(x =>
                                {
                                    var arr = x.Split(['-']);
                                    if (i == 0)
                                        condition += $"({name} >= {arr[0]} AND {name} <={arr[1]})";
                                    else
                                        condition += $" OR ({name} >= {arr[0]} AND {name} <={arr[1]})";
                                    i++;
                                });
                                condition += ")";
                            }
                        }
                        break;
                    case "Tags":
                        if (jsonParam.columnValue.IsNotEmptyOrNull())
                        {
                            var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                            if (!ids1.Any(x => x == "NoFliter"))
                            {
                                var i = 0;
                                condition += " AND (";
                                ids1.ForEach(x =>
                                {
                                    var arr = x.Split(['-']);
                                    if (i == 0)
                                        condition += $"({name} like '%{x}%')";
                                    else
                                        condition += $" OR ({name} like '%{x}%')";
                                    i++;
                                });
                                condition += ")";
                            }
                        }
                        break;
                    case "Keywords":
                        if (jsonParam.columnValue.IsNotEmptyOrNull())
                            whereExpression.And(x => (x.StaffName.Contains(jsonParam.columnValue.ToString()) ||
                            x.StaffEname.Contains(jsonParam.columnValue.ToString()) ||
                            x.Tags.Contains(jsonParam.columnValue.ToString()) ||
                            x.RemarkSz.Contains(jsonParam.columnValue.ToString()) ||
                            x.School.Contains(jsonParam.columnValue.ToString())));
                        break;
                    default:
                        break;
                }
            }
        }
        #endregion

        RefAsync<int> total = 0;
        whereExpression.And(x => x.ApplicationStatus == "Submit");
        var entitys = await query
            .WhereIF(condition.IsNotEmptyOrNull(), condition)
            .Where(whereExpression.ToExpression())
            .OrderBy(it => new { UpdateTime = SqlFunc.Desc(it.UpdateTime), CreateTime = SqlFunc.Desc(it.CreateTime) })
            .ToPageListAsync(filter.pageNum, filter.pageSize, total);
        var result = new ServicePageResult<Ghrh_ResumeDto>(filter.pageNum, total, filter.pageSize, Mapper.Map(entitys).ToANew<List<Ghrh_ResumeDto>>());
        var list = result.result.DT_TableDataT1;
        var ids = list.Select(x => x.Id).ToList();
        var titleIds = list.Where(x => x.TitleId != null).Select(x => x.TitleId.Value).Distinct().ToList();

        var eduBGs = await _ghrh_ResumeEduBGServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));
        var workExps = await _ghrh_ResumeWorkExpServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));

        var tagIds = new List<long>();
        list.ForEach(x =>
        {
            if (x.Tags.IsNotEmptyOrNull())
                tagIds.AddRange(JsonHelper.JsonToObj<List<long>>(x.Tags));
        });

        var orders = await _ghrh_InterviewOrderServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));
        tagIds = tagIds.Distinct().ToList();
        var tags = await _ghrh_ResumeTagServices.QueryDto(x => tagIds.Contains(x.Id));

        #region 查询审批流程状态
        //【面试过程 - 已面试 - 发起录用审批】配置按钮
        var offerApplyConfig = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "ESS_Recruit_Custom_Offer_Apply").FirstAsync();
        //【简历库-录用-已发offer】配置按钮
        var sendOfferConfig = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "ESS_Recruit_Custom_Transfer_Staff").FirstAsync();

        var applyOrders = await Db.Queryable<Ghrh_OfferApplyOrder>().Where(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value)).ToListAsync();

        var companyIds = applyOrders.Where(x => x.CompanyId != null).Select(x => x.CompanyId).ToList();
        var deptIds = applyOrders.Where(x => x.DeptId != null).Select(x => x.DeptId).ToList();
        titleIds.AddRange(applyOrders.Where(x => x.TitleId != null).Select(x => x.TitleId.Value).ToList());
        var legalCompanys = await Db.Queryable<Ghra_LegalCompany>().Where(x => companyIds.Contains(x.LegalCompanyID)).ToListAsync();
        var depts = await Db.Queryable<Ghro_Dept>().Where(x => deptIds.Contains(x.DeptID)).ToListAsync();
        var titles = await Db.Queryable<Ghra_Title>().Where(x => x.TitleID != null && titleIds.Contains(x.TitleID.Value)).ToListAsync();

        #endregion

        list.ForEach(async x =>
        {
            x.ResumeId = x.Id;
            if (x.TitleId != null) x.TitleName = titles.Where(o => o.TitleID == x.TitleId).FirstOrDefault()?.TitleName;
            await SetLabel(x);

            x.EduBG = eduBGs.Where(o => x.Id == o.ResumeId).ToList();
            x.WorkExp = workExps.Where(o => x.Id == o.ResumeId).ToList();
            if (x.Tags.IsNotEmptyOrNull())
            {
                var tagIds1 = JsonHelper.JsonToObj<List<long>>(x.Tags);
                x.TagIds = tagIds1;
                x.TagList = tags.Where(o => tagIds1.Contains(o.Id)).Select(o => o.TagName).ToList();
                x.TagString = string.Join(",", x.TagList);
            }

            var order = orders.Where(o => o.ResumeId == x.ResumeId).FirstOrDefault();
            if (order != null)
            {
                x.IsPass = order.IsPass;
                if (order.IsPass != null)
                    x.IsPassLabel = order.IsPass == true ? "通过" : "不通过";
                x.InterviewResult = order.InterviewResult;
                x.OptionalInterviewTime = order.OptionalInterviewTime;
                x.InterviewTime = order.InterviewTime;
                x.InterviewAddress = order.InterviewAddress;
                x.InterviewStepName = order.InterviewStepName;
                if (x.Status != DIC_INTERVIEW_ORDER_STATUS.WaitRecommended)
                {
                    x.HireDeptName = order.HireDeptName;
                    if (order.HireTitleName.IsNotEmptyOrNull())
                        x.HireTitleName = order.HireTitleName;
                    x.Interviewer = order.Interviewer;
                }

                if (x.Status == DIC_INTERVIEW_ORDER_STATUS.Fail)
                {
                    x.InterviewResult = order.InterviewResult;
                    x.InterviewResultRemark = order.FilterFeedback.IsNotEmptyOrNull() ? order.FilterFeedback : "(不合适原因未填写)";
                }
                else if (x.Status == DIC_INTERVIEW_ORDER_STATUS.HasRecommended)
                {
                    x.InterviewResult = "推荐中";
                    x.InterviewResultRemark = order.RecommendRemark.IsNotEmptyOrNull() ? order.RecommendRemark : "(推荐说明未填写)";
                }
                else if (x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitAppointment || x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
                {
                    //x.InterviewResult = "推荐中";
                    x.AppointmentFeedback = order.AppointmentFeedback.IsNotEmptyOrNull() ? order.AppointmentFeedback : "(约面反馈未填写)";
                }
                //x.InterviewResult = "推荐中";

                x.InterviewTime1 = order.InterviewTime;
                x.InterviewContent = order.InterviewContent;

                if (order.WaitInterviewStatus == "Cancel")
                    x.InterviewTime1 = "已取消";
                else if (order.WaitInterviewStatus == "HasChangeInterviewTime")
                    x.InterviewTime1 = "已改期";
            }


            var applyOrder = applyOrders.Where((o => o.ResumeId == x.ResumeId)).FirstOrDefault();
            if (applyOrder != null)
            {
                if (applyOrder.WorkNo.IsNotEmptyOrNull())
                    x.OfferApplyExist = true;
                x.HireCompanyName = legalCompanys.Where(x => x.LegalCompanyID == applyOrder.CompanyId).FirstOrDefault()?.LegalCompanyName;
                x.HireDeptName = depts.Where(x => x.DeptID == applyOrder.DeptId).FirstOrDefault()?.DeptName;
                x.HireTitleName = titles.Where(x => x.TitleID == applyOrder.TitleId).FirstOrDefault()?.TitleName;
            }
            x.OfferApplyType = offerApplyConfig?.ConfigValue ?? "System";

            if (x.Status == DIC_INTERVIEW_ORDER_STATUS.HasSendOffer)
            {
                //x.InterviewResult = "推荐中";
                x.TransferStaffType = sendOfferConfig?.ConfigValue ?? "System";
            }
            x.IsAllowAssess = true;
        });

        return result;

    }

    public override async Task<Ghrh_ResumeDto> QueryById(object objId)
    {
        var result = await base.QueryById(objId);
        List<Ghrh_ResumeDto> list = [result];
        var ids = list.Select(x => x.Id).ToList();
        var titleIds = list.Where(x => x.TitleId != null).Select(x => x.TitleId.Value).Distinct().ToList();

        var titles = await Db.Queryable<Ghra_Title>().Where(x => x.TitleID != null && titleIds.Contains(x.TitleID.Value)).ToListAsync();
        var eduBGs = await _ghrh_ResumeEduBGServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));
        var workExps = await _ghrh_ResumeWorkExpServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));
        eduBGs.ForEach(async x =>
        {
            x.DegreeLevelLabel = await GetParaLabel("EducationalBGLevel", x.DegreeLevel);
            x.BeginDate1 = DateTimeHelper.ConvertToDayString(x.BeginDate);
            x.EndDate1 = DateTimeHelper.ConvertToDayString(x.EndDate);
        });
        workExps.ForEach(x =>
        {
            x.BeginDate1 = DateTimeHelper.ConvertToDayString(x.BeginDate);
            x.EndDate1 = DateTimeHelper.ConvertToDayString(x.EndDate);
        });

        var tagIds = new List<long>();
        list.ForEach(x =>
        {
            if (x.Tags.IsNotEmptyOrNull())
                tagIds.AddRange(JsonHelper.JsonToObj<List<long>>(x.Tags));
        });

        tagIds = tagIds.Distinct().ToList();
        var tags = await _ghrh_ResumeTagServices.QueryDto(x => tagIds.Contains(x.Id));
        list.ForEach(async x =>
        {
            x.ResumeId = x.Id;
            if (x.TitleId != null) x.TitleName = titles.Where(o => o.TitleID == x.TitleId).FirstOrDefault()?.TitleName;
            await SetLabel(x);

            x.EduBG = eduBGs.Where(o => x.Id == o.ResumeId).ToList();
            x.WorkExp = workExps.Where(o => x.Id == o.ResumeId).ToList();
            if (x.Tags.IsNotEmptyOrNull())
            {
                var tagIds1 = JsonHelper.JsonToObj<List<long>>(x.Tags);
                x.TagList = tags.Where(o => tagIds1.Contains(o.Id)).Select(o => o.TagName).ToList();
            }
        });

        return list[0];
    }
    #endregion

    #region 获取简历Tabs接口
    public async Task<ServiceResult<List<ResumeViewTab>>> QueryViewTab(int langId = 1)
    {
        string str = @"[
	{
		""MenuName"": ""F_ResumeMaintenance_All"",
		""TabName"": ""简历库"",
		""QueryUrl"": ""/api/Ghrh_Resume/QueryList/All"",
		""Count"": 0,
		""Items"": [
			{
				""ItemName"": ""待推荐"",
				""Key"": ""WaitRecommend"",
				""Count"": 0
			},
			{
				""ItemName"": ""已推荐"",
				""Key"": ""HasRecommend"",
				""Count"": 0
			}
		]
	},
	{
		""MenuName"": ""F_ResumeMaintenance_Process"",
		""TabName"": ""面试过程"",
		""QueryUrl"": ""/api/Ghrh_Resume/QueryList/Process"",
		""Count"": 0,
		""Items"": [
			{
				""ItemName"": ""待预约"",
				""Key"": ""WaitAppointment"",
				""Count"": 0
			},
			{
                ""ItemName"": ""已改期"",
				""Key"": ""HasChangeDate"",
				""Count"": 0
			},
			{
				""ItemName"": ""待面试"",
				""Key"": ""WaitInterview"",
				""Count"": 0
			},
			{
				""ItemName"": ""已面试"",
				""Key"": ""HasInterview"",
				""Count"": 0
			},
			{
				""ItemName"": ""已取消"",
				""Key"": ""Cancel"",
				""Count"": 0
			}
		]
	},
	{
		""MenuName"": ""F_ResumeMaintenance_Hire"",
		""TabName"": ""录用"",
		""QueryUrl"": ""/api/Ghrh_Resume/QueryList/Hire"",
		""Count"": 0,
		""Items"": [
			{
				""ItemName"": ""待发offer"",
				""Key"": ""WaitSendOffer"",
				""Count"": 0
			},
			{
				""ItemName"": ""已发offer"",
				""Key"": ""HasSendOffer"",
				""Count"": 0
			},
			{
				""ItemName"": ""已报到"",
				""Key"": ""HasOffer"",
				""Count"": 0
			}
		]
	},
	{
		""MenuName"": ""F_ResumeMaintenance_Talent_Pool"",
		""TabName"": ""人才库"",
		""QueryUrl"": ""/api/Ghrh_Resume/QueryList/Talent_Pool"",
		""Count"": 0,
	},
	{
		""MenuName"": ""F_ResumeMaintenance_Recycled"",
		""TabName"": ""回收站"",
		""QueryUrl"": ""/api/Ghrh_Resume/QueryList/Recycled"",
		""Count"": 0,
	},
	{
		""MenuName"": ""F_ResumeMaintenance_Blacklist"",
		""TabName"": ""黑名单"",
		""QueryUrl"": ""/api/Ghrh_Resume/QueryList/Blacklist"",
		""Count"": 0,
	}
]";
        var tabs = JsonHelper.JsonToObj<List<ResumeViewTab>>(str);

        var menus = Db.Ado.SqlQuery<Ghrs_Menu>(@$"SELECT MenuNo, [dbo].[FLangKeyToValue] (MKey, {langId}, MenuName) MenuName
FROM Ghrs_Menu
WHERE     ParentMenuId = (SELECT MenuId
                          FROM Ghrs_Menu
                          WHERE MenuNo = 'F_ResumeMaintenance')
      AND IsEnable = 1");

        var menuNos = menus.Select(x => x.MenuNo).ToList();
        tabs = tabs.Where(x => menuNos.Contains(x.MenuName)).ToList();
        tabs.ForEach(x =>
        {
            x.TabName = menus.Where(o => o.MenuNo == x.MenuName).FirstOrDefault()?.MenuName;
        });

        for (int i = 0; i < tabs.Count; i++)
        {
            tabs[i].TabName = menus.Where(o => o.MenuNo == tabs[i].MenuName).FirstOrDefault()?.MenuName;

            if (tabs[i].Items != null)
                for (int j = 0; j < tabs[i].Items.Count; j++)
                {
                    var key = "Recruit_" + tabs[i].MenuName + "_" + tabs[i].Items[j].Key;
                    tabs[i].Items[j].ItemName = await QueryLangValue(key, langId, tabs[i].Items[j].ItemName);
                }
        }

        return ServiceResult<List<ResumeViewTab>>.OprateSuccess("查询成功！", tabs);
    }
    #endregion

    #region 获取查询条件
    public async Task<ServiceResult<List<ResumeCondition>>> QueryConditions()
    {

        var sql = @"SELECT A.TableName,
       A.ColumnName 'Key',
       A.ResumeInfoColumnNo,
       A.ResumeInfoColumnName 'Name',
       A.DataSource,
       A.DataSourceType,
       A.DataType,
       A.ColumnType,
       B.ParaMasterNo
FROM Ghrh_ResumeInfoColumn A
     LEFT JOIN Ghrs_ParaMaster B ON A.DataSource = B.ParaMasterId
WHERE     ResumeInfoGroupId IN (SELECT Id
                                FROM Ghrh_ResumeInfoGroup
                                WHERE GroupType = 'Base')
      AND A.QueryCondition = 1
ORDER BY A.SortNo ASC";

        var tabs = await Db.Ado.SqlQueryAsync<ResumeCondition>(sql);
        //string str = "[\r\n\t{\r\n\t\t\"Key\": \"Education\",\r\n\t\t\"Name\": \"学历要求\",\r\n\t\t\"ParaMasterNo\": \"EducationalBGLevel\"\r\n\t},\r\n\t{\r\n\t\t\"Key\": \"WorkYears\",\r\n\t\t\"Name\": \"经验要求\",\r\n\t\t\"ParaMasterNo\": \"ResumeWorkYears\"\r\n\t},\r\n\t{\r\n\t\t\"Key\": \"Age\",\r\n\t\t\"Name\": \"年龄要求\",\r\n\t\t\"ParaMasterNo\": \"AgePeriodSetup\"\r\n\t},\r\n\t{\r\n\t\t\"Key\": \"Gender\",\r\n\t\t\"Name\": \"性  别\",\r\n\t\t\"ParaMasterNo\": \"Gender\"\r\n\t},\r\n\t{\r\n\t\t\"Key\": \"SalaryPeriod\",\r\n\t\t\"Name\": \"薪资区间\",\r\n\t\t\"ParaMasterNo\": \"SalaryPeriodSetup\"\r\n\t},\r\n\t{\r\n\t\t\"Key\": \"ApplyStatus\",\r\n\t\t\"Name\": \"求职状态\",\r\n\t\t\"ParaMasterNo\": \"ResumeApplyStatus\"\r\n\t},\r\n\t{\r\n\t\t\"Key\": \"Tags\",\r\n\t\t\"Name\": \"标签\",\r\n\t\t\"ParaMasterNo\": \"ResumeTag\"\r\n\t}\r\n]";
        //var tabs = JsonHelper.JsonToObj<List<ResumeCondition>>(str);
        tabs.Add(new ResumeCondition() { Key = "Tags", Name = "标签" });

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

            if (tabs[i].Key == "WorkYears")
                tabs[i].ParaMasterNo = "ResumeWorkYears";
            var items = await GetParaList(tabs[i].ParaMasterNo);

            if (tabs[i].Key == "Tags")
            {
                var tags = await _ghrh_ResumeTagServices.Query();
                items = tags.Select(x => new Ghrs_ParaDetail()
                {
                    ParaDetailNo = x.Id.ToString(),
                    ParaDetailName = x.TagName
                }).ToList();
                //items.Insert(0, new Ghrs_ParaDetail() { ParaDetailNo = "NoFliter", ParaDetailName = "未打标签" });

            }

            if (items != null)
            {
                if (tabs[i].Key != "Tags")
                {
                    items.Insert(0, new Ghrs_ParaDetail() { ParaDetailNo = "NoFliter", ParaDetailName = "不限" });
                    tabs[i].Items = items.ToDictionary(item => item.ParaDetailNo, item => item.ParaDetailName);

                }
                else
                {
                    tabs[i].Items = new Dictionary<string, string>
                    {
                        { "NoFliter", "未打标签" }
                    };

                    items.ForEach(o =>
                    {
                        tabs[i].Items.Add(o.ParaDetailNo, o.ParaDetailName);
                    });
                }
            }

        }

        return ServiceResult<List<ResumeCondition>>.OprateSuccess("查询成功！", tabs);

    }
    #endregion

    #region 处理是否推荐
    public async Task<ServiceResult> SwitchIsRecommend(List<long> ids, bool isRecommend)
    {
        await Db.Updateable<Ghrh_Resume>()
            .SetColumns(it => new Ghrh_Resume()
            {
                IsRecommend = isRecommend,
                UpdateBy = App.User.ID,
                UpdateTime = DateTime.Now,
            })
        .Where(it => ids.Contains(it.Id)).ExecuteCommandAsync();
        return ServiceResult.OprateSuccess();

    }
    #endregion

    #region 修改状态
    public async Task<bool> UpdateStatus(List<long> ids, string status)
    {
        var result = await Db.Updateable<Ghrh_Resume>()
              .SetColumns(it => new Ghrh_Resume()
              {
                  Status = status,
                  UpdateBy = App.User.ID,
                  UpdateTime = DateTime.Now,
                  RecycledTime = null,
              })
        .Where(it => ids.Contains(it.Id)).ExecuteCommandAsync();

        if (status == "Talent_Pool" || status == "Blacklist")
            await Db.Updateable<Ghrh_InterviewOrder>()
               .SetColumns(it => it.IsEnable == 0)
               .Where(it => it.ResumeId != null && ids.Contains(it.ResumeId.Value))
               .ExecuteCommandAsync();

        return result > 0 ? true : false;

    }
    #endregion

    #region 回收简历
    public async Task<bool> Recovery(List<long> ids)
    {
        var result = await Db.Updateable<Ghrh_Resume>()
              .SetColumns(it => new Ghrh_Resume()
              {
                  Status = "Recycled",
                  UpdateBy = App.User.ID,
                  UpdateTime = DateTime.Now,
                  RecycledTime = DateTime.Now,
              })
        .Where(it => ids.Contains(it.Id)).ExecuteCommandAsync();

        await Db.Updateable<Ghrh_InterviewOrder>()
             .SetColumns(it => it.IsEnable == 0)
             .Where(it => it.ResumeId != null && ids.Contains(it.ResumeId.Value))
             .ExecuteCommandAsync();
        return result > 0 ? true : false;

    }
    #endregion

    #region 添加标签
    public async Task<bool> MarkTags(List<long> ids, List<long> tags)
    {
        var result = 0;
        var tag = JsonHelper.ObjToJson(tags);
        if (ids != null)
        {
            if (ids.Count == 0)
            {
                result = await Db.Updateable<Ghrh_Resume>()
                    .SetColumns(it => new Ghrh_Resume()
                    {
                        Tags = tag,
                        UpdateBy = App.User.ID,
                        UpdateTime = DateTime.Now,
                    })
                    .Where(it => ids.Contains(it.Id))
                    .ExecuteCommandAsync();
            }
            else
            {
                for (int i = 0; i < ids.Count; i++)
                {
                    var resume = await base.QueryById(ids[i]);
                    if (resume != null)
                    {
                        var tagIds = JsonHelper.JsonToObj<List<long>>(resume.Tags ?? "[]");
                        tagIds.AddRange(tags);
                        tagIds = tagIds.Distinct().ToList();
                        tag = JsonHelper.ObjToJson(tagIds);
                        result = await Db.Updateable<Ghrh_Resume>()
                            .SetColumns(it => new Ghrh_Resume()
                            {
                                Tags = tag,
                                UpdateBy = App.User.ID,
                                UpdateTime = DateTime.Now,
                            })
                           .Where(it => it.Id == ids[i])
                           .ExecuteCommandAsync();
                    }
                }
            }
        }

        return result > 0 ? true : false;

    }
    #endregion

    #region 查询简历是否存在
    public async Task<ServiceResult<dynamic>> CheckIsExist(EditGhrh_ResumeInput input)
    {
        if (input.Mobile.IsNullOrEmpty())
            return ServiceResult<dynamic>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0228", "手机号不能为空！"));
        if (input.StaffName.IsNullOrEmpty())
            return ServiceResult<dynamic>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0229", "姓名不能为空！"));

        dynamic obj = new ExpandoObject();
        obj.ApplicationStatus = "Hold";
        long id = 0;
        var resume = await QuerySingle(x => x.Mobile == input.Mobile);

        if (resume != null && resume.Status == DIC_INTERVIEW_ORDER_STATUS.Blacklist)
            return ServiceResult<dynamic>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0146", "无法登录！"));

        if (resume == null)
            id = await base.Add(new InsertGhrh_ResumeInput()
            {
                Mobile = input.Mobile,
                StaffName = input.StaffName,
                ApplicationStatus = "Hold"
            });
        else
        {
            if (input.IdCardNo.IsNullOrEmpty() && resume.IdCardNo.IsNotEmptyOrNull())
                return ServiceResult<dynamic>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0147", "请输入身份证号码后六位！"));

            if (input.IdCardNo.IsNotEmptyOrNull() && resume.IdCardNo.IsNotEmptyOrNull())
                resume = await QuerySingle(x => x.Mobile == input.Mobile && x.IdCardNo.Contains(input.IdCardNo));

            id = resume.Id;

            if (resume.StaffName != input.StaffName && resume.ApplicationStatus != "Submit")
                await base.Update(id, input, ["StaffName"]);

            obj.ApplicationStatus = resume.ApplicationStatus ?? "Hold";

            if (resume.Status == DIC_INTERVIEW_ORDER_STATUS.WaitRecommended ||
           resume.Status == DIC_INTERVIEW_ORDER_STATUS.Recycled ||
           resume.Status == DIC_INTERVIEW_ORDER_STATUS.Talent_Pool)
                obj.ApplicationStatus = "TempSave";

        }
        obj.Id = id;
        return ServiceResult<dynamic>.OprateSuccess("查询成功", obj);

    }
    #endregion

    #region 查询简历公司新增
    public async Task<ServiceResult<Dictionary<string, string>>> QueryCompanyInfo(string companySpecCode, int langId)
    {
        var url = await Db.Ado.GetStringAsync("SELECT LogoUrl FROM Ghrs_LoginPageSetting");
        var companyName = await Db.Ado.GetStringAsync("select top 1 CompanyName from Ghro_Company");

        if (langId != 1)
            companyName = await Db.Ado.GetStringAsync("select top 1 CompanyEname from Ghro_Company");
        Dictionary<string, string> dict = new Dictionary<string, string>()
        {
            { "CompanyName", companyName},
            { "LoginDesc", await QueryLangValue("Recruit_Resume_From_Login_Desc",langId,AppSettings.app(new string[] { "Resume", "LoginDesc" }))  },
            { "LoginTitle",  await QueryLangValue("Recruit_Resume_From_Login_Title",langId,AppSettings.app(new string[] { "Resume", "LoginTitle" }))   },
            { "IdCardNo", await QueryLangValue("Recruit_Resume_From_IdCardNo_Text",langId,"身份证后六位") },
            { "StaffName", await QueryLangValue("Recruit_Resume_From_Staff_Name",langId,"姓名") },
            { "Mobile",await QueryLangValue("Recruit_Resume_From_Mobile",langId,"手机号")   },
            { "AgainLoginText",await QueryLangValue("Recruit_Resume_From_Login_Again_Login_Text",langId,"二次登录")   },
            { "LoginBtnText",await QueryLangValue("Recruit_Resume_From_Login_Btn_Text",langId,"登录")   },
            { "LogoUrl", url },
            { "StartInputText",await QueryLangValue("Recruit_Resume_From_Start_Input_Text",langId,"开始填写")   },
            { "HoldText",await QueryLangValue("Recruit_Resume_From_Login_Hold_Text",langId,"暂存")  },
            { "NextText", await QueryLangValue("Recruit_Resume_From_Login_Next_Text",langId,"下一步")  },
            { "PreviousText", await QueryLangValue("Recruit_Resume_From_Previous_Text",langId,"上一步")  },
            { "QuestionText",await QueryLangValue("Recruit_Resume_From_Question_Text",langId,"道题目")   },
            { "PleaseEnterText",await QueryLangValue("Recruit_Resume_From_Please_Enter_Text",langId,"请输入")   },
            { "PleaseSelectText",await QueryLangValue("Recruit_Resume_From_Please_Select_Text",langId,"请选择")   },
            { "ApplicantPromiseText", await QueryLangValue("Recruit_Resume_From_Applicant_Promise_Text",langId,"应聘者承诺")  },
            { "HasSubmitText",await QueryLangValue("Recruit_Resume_From_Has_Submit_Text",langId,"已提交")   },
            { "SubmitText",await QueryLangValue("Recruit_Resume_From_Submit_Text",langId,"提交")   },
            { "InformationCompletionDegreeText",await QueryLangValue("Recruit_Resume_From_Infor_Completion_Degree_Text",langId,"信息填写完成度")   },
            { "AddText", await QueryLangValue("Recruit_Resume_From_Add_Text",langId,"新增")  },
            { "DeleteText",await QueryLangValue("Recruit_Resume_From_Delete_Text",langId,"删除")   },
            { "DeleteAttachmentConfirmText",await QueryLangValue("Recruit_Resume_From_Delete_Attachment_Confirm_Text",langId,"是否确认删除附件?")   },
            { "Thumbnail",await QueryLangValue("Recruit_Resume_From_Thumbnail",langId,"缩略图")   },
            { "FileName",await QueryLangValue("Recruit_Resume_From_File_Name",langId,"文件名")   },
            { "FileSize",await QueryLangValue("Recruit_Resume_From_File_Size",langId,"文件大小")   },
            { "Remark",await QueryLangValue("Recruit_Resume_From_Remark",langId,"备注")   },
            { "Option",await QueryLangValue("Recruit_Resume_From_Option",langId,"操作")   },
            { "TrueText",await QueryLangValue("Recruit_Resume_From_TrueText",langId,"是")  },
            { "FalseText", await QueryLangValue("Recruit_Resume_From_FalseText",langId,"否")  }
        };
        return ServiceResult<Dictionary<string, string>>.OprateSuccess("查询成功", dict);

    }
    #endregion

    #region 查询招聘表单信息接口
    public async Task<ServiceResult<dynamic>> Query(long id, int langId)
    {
        dynamic obj = new ExpandoObject();
        dynamic Guide = new ExpandoObject();
        dynamic Data = new ExpandoObject();
        var entity = await QueryById(id);
        string statementRemark = string.Empty;
        #region Column
        string sql = @$"DECLARE @ID   BIGINT = 1

BEGIN
   DECLARE
      @MasterTemplateID   INT = 0,
      @TemplateType       INT                                        -- 母版设定ID
   SELECT @TemplateType = TemplateType
   FROM Ghrh_ResumeTemplate
   WHERE ID = @ID
   SELECT @MasterTemplateID = ID
   FROM Ghrh_ResumeTemplate
   WHERE IsEnable = 1 AND TemplateType = 1

   SELECT *
   FROM (SELECT a.ID,
                a.ID ResumeTemplateInfoGroupID,
                b.ID ResumeInfoGroupID,
                [dbo].[FLangKeyToValue](b.MKey,{langId},b.GroupName) tabName,
                b.GroupType tabKey,
                b.TableName,
                b.TablePKIDName,                                   --  是否可挑选栏位
                ISNULL (a.IsDisplay, 0) isDisplay,
                isnull (a.SortNo, b.SortNo) SortNo,
                CASE
                   WHEN b.GroupType = 'Base' THEN 'list'
                   WHEN b.GroupType = 'Attachment' THEN 'Attachment'
                   WHEN b.GroupType = 'Statement' THEN 'Statement'
                   ELSE 'block'
                END AS type,
                -- case when ISNULL(a.IsDisplay,0)=0 then ''
                CASE
                   WHEN 1 = 2
                   THEN
                      ''
                   ELSE
                      CASE
                         WHEN b.GroupType = 'Base'                  -- 读取各模板设定
                         THEN
                            isnull
                            (
                               (SELECT pp.ColumnName field,
                                       pp.ResumeInfoColumnName label,
                                       pp.IsRequired required,
                                       pp.ColumnType elementType,
                                       CASE
                                          WHEN pp.DataSourceType =
                                               'ParaDetailNo'
                                          THEN
                                             (SELECT   'TBParaDetail_'
                                                     + ParaTypeNo
                                                     + '_'
                                                     + ParaMasterNo
                                              FROM Ghrs_ParaMaster
                                              WHERE ParaMasterId =
                                                    pp.DataSource)
                                          WHEN pp.DataSourceType =
                                               'CommonList'
                                          THEN
                                             (SELECT   'CommonList_'
                                                     + ListCommonSqlNo
                                              FROM Ghrs_ListCommonSql
                                              WHERE ListCommonSqlId =
                                                    pp.DataSource)
                                          ELSE
                                             NULL
                                       END dataSource,
                                       pp.DataType dataType,
                                       pp.MultipleSelect multipleSelect,
                                       kk.SortNo sortNo,
                                       pp.Placeholder placeholder,
                                       pp.Editable editable
                                FROM Ghrh_ResumeTemplateInfoGroupColumn kk,
                                     Ghrh_ResumeInfoColumn pp
                                WHERE     kk.ResumeTemplateInfoGroupID = a.ID
                                      -- a.ResumeTemplateInfoGroupID
                                      AND kk.ResumeInfoColumnID = pp.ID
                                      AND kk.IsDisplay = 1
                                      AND pp.IsEnable = 1
                                      AND kk.IsEnable = 1
                                order by kk.SortNo
                                FOR JSON PATH, INCLUDE_NULL_VALUES),
                               '')
                         WHEN b.GroupType = 'Photo'                 -- 读取各模板设定
                         THEN
                            (SELECT PhotoType
                             FROM Ghrh_ResumeTemplate
                             WHERE ResumeTemplateID = @MasterTemplateID
                             FOR JSON PATH, INCLUDE_NULL_VALUES)
                         WHEN b.GroupType NOT IN ('Base', 'Photo')  -- 读取母版的设定
                         THEN
                            isnull
                            (
                               (SELECT pp.ColumnName field,
                                       pp.ResumeInfoColumnName label,
                                       pp.IsRequired required,
                                       pp.ColumnType elementType,
                                       CASE
                                          WHEN pp.DataSourceType =
                                               'ParaDetailNo'
                                          THEN
                                             (SELECT   'TBParaDetail_'
                                                     + ParaTypeNo
                                                     + '_'
                                                     + ParaMasterNo
                                              FROM Ghrs_ParaMaster
                                              WHERE ParaMasterId =
                                                    pp.DataSource)
                                          WHEN pp.DataSourceType =
                                               'CommonList'
                                          THEN
                                             (SELECT   'CommonList_'
                                                     + ListCommonSqlNo
                                              FROM Ghrs_ListCommonSql
                                              WHERE ListCommonSqlId =
                                                    pp.DataSource)
                                          ELSE
                                             NULL
                                       END dataSource,
                                       pp.DataType dataType,
                                       pp.MultipleSelect multipleSelect,
                                       kk.SortNo sortNo,
                                       pp.Placeholder placeholder,
                                       pp.Editable editable
                                FROM Ghrh_ResumeTemplateInfoGroupColumn kk,
                                     Ghrh_ResumeInfoColumn pp
                                WHERE     kk.ResumeTemplateID =
                                          @MasterTemplateID
                                      AND kk.ResumeInfoGroupId = b.Id
                                      -- b.ResumeInfoGroupId
                                      AND pp.Id = kk.ResumeInfoColumnId
                                      AND kk.IsDisplay = 1
                                      AND pp.IsEnable = 1
                                      AND kk.IsEnable = 1
                                order by kk.SortNo
                                FOR JSON PATH, INCLUDE_NULL_VALUES),
                               '')
                      END
                END children1
         FROM Ghrh_ResumeInfoGroup b
              LEFT JOIN Ghrh_ResumeTemplateInfoGroup a
                 ON     a.ResumeInfoGroupId = b.ID
                    AND a.IsEnable = 1
                    AND a.ResumeTemplateID = @ID
         WHERE b.IsEnable = 1) a
   WHERE a.IsDisplay = 1
   -- order by ISNULL(a.IsDisplay,0) desc,isnull(a.SortNo,b.SortNo)  -- 显示的放上面, 不显示的放下面, 再按照序号排序
   ORDER BY a.SortNo ASC
END";
        var columns = await Db.Ado.SqlQueryAsync<ResumeFormColumn>(sql);
        columns.ForEach(x =>
        {
            if (x.children1.IsNotEmptyOrNull())
                x.children = JsonHelper.JsonToObj<List<ResumeFormColumnChildren>>(x.children1);

            x.children1 = null;
        });

        if (columns.Any(x => x.tabKey == "Photo") && columns.Any(x => x.tabKey == "Base"))
        {
            columns.ForEach(x =>
            {
                if (x.tabKey == "Base")
                {
                    x.children.Insert(0, new ResumeFormColumnChildren()
                    {
                        field = "PhotoUrls",
                        elementType = "FileUpload",
                        label = "照片",
                        editable = "1"
                    });
                }
                else if (x.tabKey == "Statement")
                {
                    var child = x.children.Where(o => o.field == "StatementRemark").SingleOrDefault();
                    if (child != null)
                        statementRemark = child.placeholder;
                    x.children = x.children.Where(o => o.field != "StatementRemark").ToList();
                }
            });

        }
        obj.Columns = columns.Where(x => x.tabKey != "Photo").ToList();
        #endregion

        #region Data

        var formColumns = await QueryResumeFormColumn(1);

        #region Base

        var columnNames = formColumns.Where(x => x.GroupType == "Base").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        var dicts = new Dictionary<string, object>
        {
            //{ "PhotoUrl", entity.PhotoUrl }
        };
        columnNames.ForEach(x =>
        {
            var value = entity.GetPropertyValue(x);
            dicts.Add(x, value);
        });
        if (entity.PhotoUrl.IsNotEmptyOrNull())
        {
            dicts.Add("PhotoUrls", new List<ResumePhotoAttachment>()
            {
                new ResumePhotoAttachment() { RelativePath =entity.PhotoUrl }
            });

        }
        else dicts.Add("PhotoUrls", new List<ResumePhotoAttachment>()
        {
        });
        Data.Base = dicts;
        #endregion

        #region 家庭关系 

        columnNames = formColumns.Where(x => x.GroupType == "Family").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        var familys = await _ghrh_ResumeHomeServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        var familyDic = new List<Dictionary<string, object>>();
        familys.ForEach(family =>
        {
            var dicts = new Dictionary<string, object>();
            columnNames.ForEach(x =>
            {
                var value = family.GetPropertyValue(x);
                dicts.Add(x, value);
            });
            if (columnNames.Any(x => x == "AttachmentIDs"))
                dicts["AttachmentIDs"] = family.AttachmentIDs;
            familyDic.Add(dicts);
        });

        Data.Family = familyDic;
        #endregion

        #region 教育背景 

        columnNames = formColumns.Where(x => x.GroupType == "Education").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        var educations = await _ghrh_ResumeEduBGServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        var educationDic = new List<Dictionary<string, object>>();
        educations.ForEach(education =>
        {
            var dicts = new Dictionary<string, object>();
            columnNames.ForEach(x =>
            {
                var value = education.GetPropertyValue(x);
                dicts.Add(x, value);
            });
            if (columnNames.Any(x => x == "AttachmentIDs"))
                dicts["AttachmentIDs"] = education.AttachmentIDs;
            educationDic.Add(dicts);
        });

        Data.Education = educationDic;
        #endregion 

        #region 工作经历
        columnNames = formColumns.Where(x => x.GroupType == "WorkExp").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        var workExps = await _ghrh_ResumeWorkExpServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        var workExpDic = new List<Dictionary<string, object>>();
        workExps.ForEach(workExp =>
        {
            var dicts = new Dictionary<string, object>();
            columnNames.ForEach(x =>
            {
                var value = workExp.GetPropertyValue(x);
                dicts.Add(x, value);
            });
            if (columnNames.Any(x => x == "AttachmentIDs"))
                dicts["AttachmentIDs"] = workExp.AttachmentIDs;
            workExpDic.Add(dicts);
        });

        Data.WorkExp = workExpDic;
        #endregion

        #region 证件
        columnNames = formColumns.Where(x => x.GroupType == "Licence").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        var Licences = await _ghrh_ResumeLicenceServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        var licenceDic = new List<Dictionary<string, object>>();
        Licences.ForEach(licence =>
        {
            var dicts = new Dictionary<string, object>();
            columnNames.ForEach(x =>
            {
                var value = licence.GetPropertyValue(x);
                dicts.Add(x, value);
            });
            if (columnNames.Any(x => x == "AttachmentIDs"))
                dicts["AttachmentIDs"] = licence.AttachmentIDs;
            licenceDic.Add(dicts);
        });

        Data.Licence = licenceDic;
        #endregion

        #region 简历培训记录 
        columnNames = formColumns.Where(x => x.GroupType == "Training").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        var Trainings = await _ghrh_ResumeTrainingServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        var trainingDic = new List<Dictionary<string, object>>();
        Trainings.ForEach(training =>
        {
            var dicts = new Dictionary<string, object>();
            columnNames.ForEach(x =>
            {
                var value = training.GetPropertyValue(x);
                dicts.Add(x, value);
            });
            if (columnNames.Any(x => x == "AttachmentIDs"))
                dicts["AttachmentIDs"] = training.AttachmentIDs;
            trainingDic.Add(dicts);
        });

        Data.Training = trainingDic;
        #endregion

        Data.Attachment = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == id.ObjToString()).ToListAsync();
        var statements = await _ghrh_ResumeStatementServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);//证件
        var StatementDic = new Dictionary<string, bool?>();
        statements.ForEach(statement =>
        {
            if (!StatementDic.ContainsKey(statement.StatementCode))
                StatementDic.Add(statement.StatementCode, statement.TrueOrFalse);
        });
        Data.Statement = StatementDic;
        //Data.Statement = statements.ToDictionary(person => person.StatementCode, person => person.TrueOrFalse);
        obj.Data = Data;
        #endregion

        #region Guide
        var companyName = await Db.Ado.GetStringAsync("select top 1 CompanyName from Ghro_Company");

        if (langId != 1)
            companyName = await Db.Ado.GetStringAsync("select top 1 CompanyEname from Ghro_Company");
        Guide.Title = "应聘信息登记表填写说明";
        Guide.CompanyName = companyName;
        Guide.GuideText = @"<p>亲爱的小伙伴:</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 感谢你对公司的认可。</p>
<p>&nbsp;</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 填写公司岗位应聘表时，请确保信息准确无误。</p>
<p>&nbsp;</p>
<p>请依次填写个人信息(如姓名、联系方式等)、教育背景、工作经验(从最近到最远列出，包括公司名称、职位和主要职责)等。
强调与职位相关的经历和技能，保持内容简洁明了。</p>";
        obj.Guide = Guide;
        #endregion

        #region 承诺
        var promiseList = new List<string>
        {
           "本人诚实告知未思有各类传染性疾病，若经体检后发现有不符合本公司要求的项目，本人愿意放弃此次应征机会!",
           "本人正式入职之前，已不与任何单位存在劳动关系，并承诺在工作中不使用曾经服务过的工作单位的技术和商业秘密，如有违背，由此引起的法律纠纷及经济责任由本人承担。"
        };
        obj.Promise = promiseList;
        #endregion

        dynamic StatementRemark = new ExpandoObject();
        StatementRemark.Top = "员工本人声明(本人填写、提供的资料，是真实的、可靠的。如有虚假，则所签订的劳动合同将自动作废，由此产生的一切后果由本人负责。)";

        if (statementRemark.IsNotEmptyOrNull())
            StatementRemark.Top = statementRemark;
        StatementRemark.CenterPart1 = "根据本人实际情况，在下列";
        StatementRemark.CenterPart2 = "中打“√”选择符合项目:";
        obj.StatementRemark = StatementRemark;

        return ServiceResult<dynamic>.OprateSuccess("查询成功", obj);

    }


    public async Task<List<ResumeFormColumn1>> QueryResumeFormColumn(long resumeTemplateID = 0)
    {
        resumeTemplateID = await Db.Ado.GetLongAsync("select top 1 Id from Ghrh_ResumeTemplate where IsEnable=1 and IsPublish=1");
        var sql = @$"DECLARE @ResumeTemplateID   BIGINT = {resumeTemplateID}

            SELECT A.ColumnName,
       A.ResumeInfoColumnName
          ColumnNameDesc,
       D.GroupType,
       A.MapTableName,
       A.MapColumnName,
       A.DataType,
       A.ColumnType,
       A.IsRequired,
       A.DataSourceType,
       A.DataSource DataSource1,
     dbo.FS_GetdataSourceBySet (NULL, A.DataSourceType, A.DataSource) DataSource,
       A.DataSourceID,
       b.SortNo 
FROM Ghrh_ResumeInfoColumn A
     LEFT JOIN Ghrh_ResumeTemplateInfoGroupColumn B
        ON     B.ResumeInfoColumnID = A.ID
           AND B.IsDisplay = 1
           AND B.IsEnable = 1
           AND A.IsEnable = 1
     JOIN Ghrh_ResumeTemplateInfoGroup C
        ON B.ResumeTemplateInfoGroupID = C.Id
     JOIN Ghrh_ResumeInfoGroup D
        ON C.ResumeInfoGroupId = D.ID AND D.IsEnable = 1
WHERE C.ResumeTemplateID = @ResumeTemplateID
-- AND D.GroupType = 'Base'
ORDER BY D.GroupType, b.SortNo";

        return await Db.Ado.SqlQueryAsync<ResumeFormColumn1>(sql);
    }
    #endregion

    #region  简历提交接口
    public async Task<ServiceResult> Submit(long id, string status, ResumeFormColumnSubmit resume)
    {

        if (resume.Base.IdCardNo.IsNotEmptyOrNull())
            if (await base.AnyAsync(x => x.Id != id && x.IdCardNo == resume.Base.IdCardNo))
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0148", "证件号码【{0}】已在系统中存在，请确认填写是否正确！", resume.Base.IdCardNo));

        resume.Base.ApplicationStatus = status;
        resume.Base.ApplicationTime = DateTime.Now;
        if (status == "Submit")
            resume.Base.Status = DIC_INTERVIEW_ORDER_STATUS.WaitRecommended;
        resume.Base.ApplyStatus = resume.Base.ApplyStatus ?? "Dimission";
        var entity = await base.QueryById(id);
        if (entity != null)
        {
            if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.WaitRecommended)
                resume.Base.Status = entity.Status;
            resume.Base.Tags = entity.Tags;
        }

        if (resume.Base.Status.IsNullOrEmpty())
            resume.Base.Status = DIC_INTERVIEW_ORDER_STATUS.WaitRecommended;
        if (resume.Base.PhotoUrls != null && resume.Base.PhotoUrls.Any())
            resume.Base.PhotoUrl = resume.Base.PhotoUrls[0].RelativePath;


        #region 计算年龄
        if (resume.Base.Birthday != null && resume.Base.Birthday > DateTime.MinValue)
        {
            DateTime birthdate = (DateTime)resume.Base.Birthday;
            DateTime now = DateTime.Now;
            int age = now.Year - birthdate.Year;
            if (now.Month < birthdate.Month || (now.Month == birthdate.Month && now.Day < birthdate.Day))
                age--;
            resume.Base.Age = age;
        }
        #endregion
        resume.Base.Education = resume.Base.EduDegree;

        #region 计算工作年限
        var days = 0;
        if (resume.WorkExp != null && resume.WorkExp.Any())
        {
            resume.WorkExp.ForEach(x =>
            {
                if (x.BeginDate != null && x.EndDate != null && x.EndDate > x.BeginDate)
                {
                    var difference = x.EndDate.Value - x.BeginDate.Value;
                    days += difference.TotalDays.ObjToInt();
                }
            });
        }

        resume.Base.WorkYears = days / 365;
        #endregion

        await base.Update(id, resume.Base);

        await _ghrh_ResumeHomeServices.Delete(x => x.ResumeId != null && x.ResumeId == id);//家庭关系
        await _ghrh_ResumeEduBGServices.Delete(x => x.ResumeId != null && x.ResumeId == id);//教育背景
        await _ghrh_ResumeWorkExpServices.Delete(x => x.ResumeId != null && x.ResumeId == id);//工作经历
        await _ghrh_ResumeLicenceServices.Delete(x => x.ResumeId != null && x.ResumeId == id);//简历培训记录
        await _ghrh_ResumeTrainingServices.Delete(x => x.ResumeId != null && x.ResumeId == id);//证件
        await _ghrh_ResumeStatementServices.Delete(x => x.ResumeId != null && x.ResumeId == id);//证件
        //await Db.Queryable<Ghre_Attachment>().Where(x => x.TableName == id.ObjToString()).ToListAsync();

        if (resume.Family != null && resume.Family.Any())
        {
            resume.Family.ForEach(x => x.ResumeId = id);
            await _ghrh_ResumeHomeServices.Add(resume.Family);
        }
        if (resume.Education != null && resume.Education.Any())
        {
            resume.Education.ForEach(x => x.ResumeId = id);
            await _ghrh_ResumeEduBGServices.Add(resume.Education);
        }
        if (resume.WorkExp != null && resume.WorkExp.Any())
        {
            resume.WorkExp.ForEach(x => x.ResumeId = id);
            await _ghrh_ResumeWorkExpServices.Add(resume.WorkExp);
        }
        if (resume.Licence != null && resume.Licence.Any())
        {
            resume.Licence.ForEach(x => x.ResumeId = id);
            await _ghrh_ResumeLicenceServices.Add(resume.Licence);
        }
        if (resume.Training != null && resume.Training.Any())
        {
            resume.Training.ForEach(x => x.ResumeId = id);
            await _ghrh_ResumeTrainingServices.Add(resume.Training);
        }
        if (resume.Statement != null && resume.Statement.Any())
        {
            var inserts = new List<InsertGhrh_ResumeStatementInput>();
            foreach (var item in resume.Statement)
            {
                inserts.Add(new InsertGhrh_ResumeStatementInput()
                {
                    ResumeId = id,
                    StatementCode = item.Key,
                    TrueOrFalse = item.Value
                });
            }

            await _ghrh_ResumeStatementServices.Add(inserts);
        }

        await Db.Updateable<Ghrs_Attachment>()
             .SetColumns(it => new Ghrs_Attachment() { TableName = null })
             .Where(it => it.TableName == id.ToString())
             .ExecuteCommandAsync();
        if (resume.Attachment != null && resume.Attachment.Any())
        {
            for (int i = 0; i < resume.Attachment.Count; i++)
            {
                await Db.Updateable<Ghrs_Attachment>()
                    .SetColumns(it => new Ghrs_Attachment()
                    {
                        TableName = id.ToString(),
                        RemarkSz = resume.Attachment[i].RemarkSz
                    })
                    .Where(it => it.RelativePath == resume.Attachment[i].RelativePath)
                    .ExecuteCommandAsync();
            }
        }
        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region  查询招聘表单信息提交结果接口
    public async Task<ServiceResult<dynamic>> QueryResult(long id, int langId)
    {
        dynamic Data = new ExpandoObject();
        var entity = await QueryById(id);
        if (entity is null) throw new Exception(await QueryLangValue("F_ResumeMaintenance_0149", langId, "无效的ID！"));
        decimal completionDegree = 0;

        var groups = await Db.Ado.SqlQueryAsync<ResumeFormColumn>("select GroupName tabName, GroupType type from Ghrh_ResumeInfoGroup where IsEnable=1  and GroupType !='Photo' and GroupType !='Attachment'");
        int count = groups.Count;

        #region 基础数据
        var notNUllCount = 0;
        var formColumns = await QueryResumeFormColumn(1);
        var columnNames = formColumns.Where(x => x.GroupType == "Base").Select(x => x.ColumnName).ToList();
        columnNames = columnNames.Distinct().ToList();
        columnNames.ForEach(x =>
        {
            var value = entity.GetPropertyValue(x);
            if (value != null)
                notNUllCount++;
        });
        var basePercent = (decimal)notNUllCount / columnNames.Count * 100;
        completionDegree += basePercent;
        #endregion

        #region 家庭关系
        decimal familyPercent = 0;
        var familys = await _ghrh_ResumeHomeServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        if (familys.Any())
        {
            notNUllCount = 0;
            columnNames = formColumns.Where(x => x.GroupType == "Family").Select(x => x.ColumnName).ToList();
            columnNames = columnNames.Distinct().ToList();

            familys.ForEach(family =>
            {
                columnNames.ForEach(x =>
                {
                    var value = family.GetPropertyValue(x);
                    if (value != null)
                        notNUllCount++;

                });
            });
            familyPercent = (decimal)notNUllCount / (columnNames.Count * familys.Count) * 100;
            completionDegree += familyPercent;
        }
        #endregion

        #region 教育背景
        decimal educationPercent = 0;
        var educations = await _ghrh_ResumeEduBGServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        if (educations.Any())
        {
            notNUllCount = 0;
            columnNames = formColumns.Where(x => x.GroupType == "Education").Select(x => x.ColumnName).ToList();
            columnNames = columnNames.Distinct().ToList();

            educations.ForEach(education =>
            {
                columnNames.ForEach(x =>
                {
                    var value = education.GetPropertyValue(x);
                    if (value != null)
                        notNUllCount++;

                });
            });
            educationPercent = (decimal)notNUllCount / (columnNames.Count * educations.Count) * 100;
            completionDegree += educationPercent;
        }
        #endregion

        #region 工作经历
        decimal workExpPercent = 0;
        var workExps = await _ghrh_ResumeWorkExpServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        if (workExps.Any())
        {
            notNUllCount = 0;
            columnNames = formColumns.Where(x => x.GroupType == "WorkExp").Select(x => x.ColumnName).ToList();
            columnNames = columnNames.Distinct().ToList();
            workExps.ForEach(workExp =>
            {
                columnNames.ForEach(x =>
                {
                    var value = workExp.GetPropertyValue(x);
                    if (value != null)
                        notNUllCount++;

                });
            });
            workExpPercent = (decimal)notNUllCount / (columnNames.Count * workExps.Count) * 100;
            completionDegree += workExpPercent;
        }
        #endregion

        #region 证件
        decimal licencePercent = 0;
        var licences = await _ghrh_ResumeLicenceServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        if (licences.Any())
        {
            notNUllCount = 0;
            columnNames = formColumns.Where(x => x.GroupType == "Licence").Select(x => x.ColumnName).ToList();
            columnNames = columnNames.Distinct().ToList();

            licences.ForEach(licence =>
            {
                columnNames.ForEach(x =>
                {
                    var value = licence.GetPropertyValue(x);
                    if (value != null)
                        notNUllCount++;

                });
            });
            licencePercent = (decimal)notNUllCount / (columnNames.Count * licences.Count) * 100;
            completionDegree += licencePercent;
        }
        #endregion

        #region 培训
        decimal trainingPercent = 0;
        var trainings = await _ghrh_ResumeTrainingServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
        if (trainings.Any())
        {
            notNUllCount = 0;
            columnNames = formColumns.Where(x => x.GroupType == "Training").Select(x => x.ColumnName).ToList();
            columnNames = columnNames.Distinct().ToList();

            trainings.ForEach(training =>
            {
                columnNames.ForEach(x =>
                {
                    var value = training.GetPropertyValue(x);
                    if (value != null)
                        notNUllCount++;

                });
            });
            trainingPercent = (decimal)notNUllCount / (columnNames.Count * trainings.Count) * 100;
            completionDegree += trainingPercent;
        }
        #endregion

        #region 声明
        decimal statementPercent = 0;
        var statements = await _ghrh_ResumeStatementServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);//证件
        if (statements.Any())
        {
            notNUllCount = statements.Count;
            statementPercent = (decimal)notNUllCount / columnNames.Count * 100;
            completionDegree += statementPercent;
        }
        #endregion

        completionDegree = completionDegree / count;

        Dictionary<string, int> dics = new()
        {
            { "Base", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(basePercent, 0))) },
            { "Family", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(familyPercent, 0))) },
            { "Education", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(educationPercent, 0))) },
            { "WorkExp", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(workExpPercent, 0))) },
            { "Licence", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(licencePercent, 0))) },
            { "Training", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(trainingPercent, 0))) },
            { "Statement", Convert.ToInt32(StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(statementPercent, 0))) }
        };
        var seriesData = new List<int>();
        var xAxisData = new List<string>();

        groups.ForEach(x =>
        {
            if (dics.ContainsKey(x.type))
            {
                seriesData.Add(dics[x.type]);
                xAxisData.Add(x.tabName);
            }
        });


        Data.CompletionDegree = StringHelper.TrimDecimalString(StringHelper.TrimDecimalString(completionDegree, 2));
        Data.SeriesData = seriesData;
        Data.XAxisData = xAxisData;
        Data.StaffName = entity.StaffName;
        Data.PhotoUrl = entity.PhotoUrl;
        return ServiceResult<dynamic>.OprateSuccess("查询成功", Data);
    }
    #endregion

    #region 导出Excel
    public async Task<ServiceResult<string>> Export(QueryBody filter, string condition, bool? IsEnable = true)
    {
        filter.pageNum = 1;
        filter.pageSize = 100000;
        var data = await QueryFilterPage(filter, condition, IsEnable);
        var formColumns = await QueryResumeFormColumn(1);

        formColumns.ForEach(x =>
        {
            if (x.GroupType == "Base")
            {
                if (x.ColumnName == "TitleId")
                    x.ColumnName = "TitleName";
                else if (x.ColumnName == "Gender")
                    x.ColumnName = "GenderLabel";
                else if (x.ColumnName == "Education")
                    x.ColumnName = "EducationLabel";
                else if (x.ColumnName == "Nation")
                    x.ColumnName = "NationLabel";
                else if (x.ColumnName == "MaritalStatus")
                    x.ColumnName = "MaritalStatusLabel";
                else if (x.ColumnName == "PoliticStatus")
                    x.ColumnName = "PoliticStatusLabel";
                else if (x.ColumnName == "UrgentRelation")
                    x.ColumnName = "UrgentRelationLabel";
                else if (x.ColumnName == "CertificateType")
                    x.ColumnName = "CertificateTypeLabel";
                else if (x.ColumnName == "RegisteredType")
                    x.ColumnName = "RegisteredTypeLabel";
            }
        });

        var menuName = string.Empty;
        string sql = "select QueryProcedure, EditProcedure, IUDProcedure, MenuName from Ghrs_Menu where MenuNo='{0}'";
        sql = string.Format(sql, filter.menuName);
        var dt = await Db.Ado.GetDataTableAsync(sql);
        if (dt.Rows.Count > 0)
        {
            menuName = dt.Rows[0]["MenuName"].ToString();
        }
        var dic = formColumns.Where(x => x.GroupType == "Base").ToDictionary(item => item.ColumnNameDesc, item => item.ColumnName);

        var name = menuName + "_" + DateTimeHelper.ConvertToSecondString1(DateTime.Now);
        var physicsPath1 = await ReportHelper.SendFile(data.result.DT_TableDataT1.AsQueryable(), name, null, dic, null, null, menuName);
        var result = ServiceResult<string>.OprateSuccess(name + ".xlsx", physicsPath1);
        return result;
    }
    #endregion

    #region 简历推荐
    public async Task<ServiceResult> Recommend(ResumeRecommendForm recommend)
    {
        //if (recommend.RequestId.IsNullOrEmpty())
        //    throw new Exception("请先选择用人申请单！");

        if (recommend.RequestId.IsNullOrEmpty())
        {
            if (recommend.DeptId.IsNullOrEmpty() || recommend.TitleId.IsNullOrEmpty())
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0150", "请先选择部门或岗位！"));
        }

        if (recommend.InterviewStaffs.IsNullOrEmpty())
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0151", "请先选择面试者！"));
        recommend.InterviewStaffs = recommend.InterviewStaffs.Where(x => x.StaffId != null).ToList();

        if (!recommend.InterviewStaffs.Any())
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0152", "至少选择一名面试者！"));

        recommend.InterviewStaffs = recommend.InterviewStaffs.Where(x => x.StaffId != null).Distinct().ToList();

        var recommendCount = 0;
        if (recommend.RequestId.IsNotEmptyOrNull())
        {
            var request = await _ghrh_HumanRequestServices.QueryById(recommend.RequestId);
            recommend.DeptId = request?.DeptId;
            recommend.TitleId = request?.TitleId;
            recommendCount = (request?.RecommendCount ?? 0) + recommend.ids.Count;
        }

        string deptName = string.Empty;
        string titleName = string.Empty;
        if (recommend.DeptId.IsNotEmptyOrNull() && recommend.TitleId.IsNotEmptyOrNull())
        {
            deptName = (await Db.Queryable<Ghro_Dept>().Where(x => x.DeptID == recommend.DeptId).FirstAsync())?.DeptName;
            titleName = (await Db.Queryable<Ghra_Title>().Where(x => x.TitleID == recommend.TitleId).FirstAsync())?.TitleName;
        }

        for (int m = 0; m < recommend.ids.Count; m++)
        {
            var id = recommend.ids[m];
            if (await AnyAsync(x => x.Id == id && (x.Status == DIC_INTERVIEW_ORDER_STATUS.HasRecommended ||
            x.Status == DIC_INTERVIEW_ORDER_STATUS.HasRecommended ||
            x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitAppointment ||
            x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview ||
            x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer ||
            x.Status == DIC_INTERVIEW_ORDER_STATUS.HasSendOffer ||
            x.Status == DIC_INTERVIEW_ORDER_STATUS.HasOffer)))
                continue;

            await Db.Updateable<Ghrh_InterviewOrder>()
                .SetColumns(it => it.IsEnable == 0)
                .Where(it => it.ResumeId == id)
                .ExecuteCommandAsync();

            var interviewIds = recommend.InterviewStaffs
                .Where(o => o.StaffId != null)
                .Select(o => o.StaffId.Value).ToList();

            var interviewer = await Db.Queryable<Ghra_Staff>().Where(o => interviewIds.Contains(o.StaffID)).ToListAsync();

            var orderId = await _ghrh_InterviewOrderServices.Add(new InsertGhrh_InterviewOrderInput()
            {
                ResumeId = id,
                RequestId = recommend.RequestId,
                RecommendTime = DateTime.Now,
                RecommendId = App.User.ID,
                HireDeptId = recommend.DeptId,
                HireTitleId = recommend.TitleId,
                HireDeptName = deptName,
                HireTitleName = titleName,
                Round = 1,
                OrderNo = await GenerateContinuousSequence("Ghrh_InterviewOrder", "OrderNo", "V"),
                Status = DIC_INTERVIEW_ORDER_STATUS.HasRecommended,
                RecommendRemark = recommend.RecommendRemark,
                InterviewIds = JsonHelper.ObjToJson(interviewIds),
                Interviewer = string.Join(",", interviewer.Select(o => o.StaffName))
            });
            for (int i = 0; i < recommend.InterviewStaffs.Count; i++)
                await _ghrh_InterviewRecordServices.Add(new InsertGhrh_InterviewRecordInput()
                {
                    OrderId = orderId,
                    Round = 1,
                    Status = DIC_INTERVIEW_ORDER_STATUS.HasRecommended,
                    StaffId = recommend.InterviewStaffs[i].StaffId,
                    Email = recommend.InterviewStaffs[i].Email,
                    IsPrimary = i == 0 ? true : false,
                });
            await SendMessage(recommend.InterviewStaffs.Select(x => x.StaffId.Value).ToList(), await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), await QueryLangValue("F_ResumeMaintenance_0154", "您有个简历推荐，请及时查看！"), "/M_ESS_Recruit/F_ESS_Candidate");

            var entity = await base.QueryById(id);
            entity.Status = DIC_INTERVIEW_ORDER_STATUS.HasRecommended;
            await Update(entity, ["Status"]);

            #region 日志 
            string content = await QueryLangValue("F_ResumeMaintenance_0155", "向【{0}】推荐了简历，推荐理由：{1}", string.Join("、", interviewer.Select(o => o.StaffName)), recommend.RecommendRemark ?? "无");
            await LogRecord(orderId, content, id, null, "Recommended", true);
            #endregion

            #region 处理附件
            if (recommend.Attachments != null && recommend.Attachments.Any())
                for (int i = 0; i < recommend.Attachments.Count; i++)
                {
                    await Db.Updateable<Ghrs_Attachment>()
                        .SetColumns(it => new Ghrs_Attachment()
                        {
                            TableName = orderId.ObjToString(),
                            UpdateTime = DateTime.Now
                        })
                        .Where(it => it.RelativePath == recommend.Attachments[i].RelativePath)
                        .ExecuteCommandAsync();
                }
            #endregion
        }

        #region 回写需求单推荐人数
        if (recommend.RequestId.IsNotEmptyOrNull())
        {
            await Db.Updateable<Ghrh_HumanRequest>()
                .SetColumns(it => new Ghrh_HumanRequest() { RecommendCount = recommendCount }, true)
                .Where(it => it.Id == recommend.RequestId)
                .ExecuteCommandAsync();
        }
        #endregion

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 已推荐提醒
    public async Task<ServiceResult> RemindHasRecommend(List<long> ids)
    {
        for (int m = 0; m < ids.Count; m++)
        {
            var id = ids[0];
            var entity = await base.QueryById(id);
            if (entity == null)
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

            if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.HasRecommended)
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0156", "非【已推荐】状态下简历不可发送提醒！"));

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
            if (order != null)
            {
                var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);
                //if (record.FirstViewTime.IsNotEmptyOrNull())
                //    return ServiceResult.OprateFailed("面试官已查看简历，暂不可发送提醒！");
                var interviewIds = records.Select(x => x.StaffId).ToList();
                var interviewer = await Db.Queryable<Ghra_Staff>().Where(o => interviewIds.Contains(o.StaffID)).ToListAsync();

                for (int i = 0; i < records.Count; i++)
                {
                    if (records[i].FirstViewTime.IsNullOrEmpty())
                        await SendMessage([records[i].StaffId.Value], await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), await QueryLangValue("F_ResumeMaintenance_0154", "您有个简历推荐，请及时查看！"), "/M_ESS_Recruit/F_ESS_Candidate");
                }
                #region 日志
                string content = await QueryLangValue("F_ResumeMaintenance_0157", "向【{0}】发送简历推荐提醒！", string.Join("、", interviewer.Select(o => o.StaffName)));
                await LogRecord(order.Id, content, id, null, "RemindHasRecommend");
                #endregion
            }
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 面试官约面
    public async Task<ServiceResult> SubscribeInterview(long id, ResumeSubscribeInterviewForm form)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        //if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.HasRecommended)
        //    return ServiceResult.OprateFailed("【已推荐】状态下才能约面！");

        string content = await QueryLangValue("F_ResumeMaintenance_0158", "传入时间【{0}】无效，请检查格式！");
        form.Times.ForEach(x =>
        {
            if (x.IndexOf("~") < 0)
                throw new Exception(string.Format(content, x));
            var array = x.Split('~');
            try
            {
                Convert.ToDateTime(array[0]);
                Convert.ToDateTime(array[1]);
            }
            catch (Exception)
            {
                throw new Exception(string.Format(content, x));
            }
        });

        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        if (order != null)
        {
            bool isChangeInterviewTime = false;

            var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);
            records.ForEach(record =>
            {
                record.Status = DIC_INTERVIEW_ORDER_STATUS.WaitAppointment;
                record.RemarkSz = form.Remark;
                if (record.PlanInterviewTime1.IsNotEmptyOrNull() || record.PlanInterviewTime2.IsNotEmptyOrNull() || record.PlanInterviewTime3.IsNotEmptyOrNull())
                    isChangeInterviewTime = true;
                if (isChangeInterviewTime)
                    record.Status = DIC_INTERVIEW_ORDER_STATUS.HasChangeDate;

            });

            for (int i = 0; i < form.Times.Count; i++)
            {
                var time = form.Times[i];
                if (time.IsNotEmptyOrNull())
                {
                    var time1 = time.Split('~');

                    time = DateTimeHelper.ConvertToMiniuteString(time1[0]) + "~" + DateTimeHelper.ConvertToOnlyHourMinuteString(time1[1]);
                }

                form.Times[i] = time;
                if (i == 0)
                    records.ForEach(record => record.PlanInterviewTime1 = time);
                else if (i == 1)
                    records.ForEach(record => record.PlanInterviewTime2 = time);
                else if (i == 2)
                    records.ForEach(record => record.PlanInterviewTime3 = time);
            }
            order.Status = DIC_INTERVIEW_ORDER_STATUS.WaitAppointment;
            if (isChangeInterviewTime)
                order.Status = DIC_INTERVIEW_ORDER_STATUS.HasChangeDate;

            order.AppointmentFeedback = form.Remark;
            await _ghrh_InterviewOrderServices.Update(order, ["Status", "AppointmentFeedback"]);

            await _ghrh_InterviewRecordServices.Update(records[0], ["PlanInterviewTime1", "PlanInterviewTime2", "PlanInterviewTime3", "Status"]);

            #region 日志
            if (!isChangeInterviewTime)
            {

                string content1 = await QueryLangValue("F_ResumeMaintenance_0159", "预约面试，面试时间：{0}，约面反馈：{1}", string.Join("、", form.Times), order.AppointmentFeedback ?? "无");
                await LogRecord(order.Id, content1, id, null, "SubscribeInterview");
            }
            else
            {
                string content1 = await QueryLangValue("F_ResumeMaintenance_0160", "修改面试时间，面试时间：{0}，备注：{1}", string.Join("、", form.Times), form.Remark ?? "无");
                await LogRecord(order.Id, content1, id, null, "ChangeInterviewDate");

            }
            #endregion

            if (!isChangeInterviewTime)
                await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.WaitAppointment);
            else
                await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.HasChangeDate);
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 安排面试
    public async Task<ServiceResult> ScheduleInterview(long id, ResumeScheduleInterviewForm body)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        //if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.WaitAppointment)
        //    return ServiceResult.OprateFailed("【待预约】状态下才能安排面试！");

        string content = await QueryLangValue("F_ResumeMaintenance_0158", "传入时间【{0}】无效，请检查格式！");
        if (body.Time.IndexOf("~") < 0)
            throw new Exception(string.Format(content, body.Time));
        var array = body.Time.Split('~');
        try
        {
            Convert.ToDateTime(array[0]);
            Convert.ToDateTime(array[1]);
        }
        catch (Exception)
        {
            throw new Exception(string.Format(content, body.Time));
        }
        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        if (order != null)
        {
            var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);

            order.InterviewStepName = body.InterviewStepName;
            order.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
            records.ForEach(record =>
            {
                record.InterviewTime = body.Time;
                record.InterviewBeginTime = Convert.ToDateTime(array[0]);
                record.InterviewEndTime = Convert.ToDateTime(array[1]);
                record.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
                record.InterviewStepName = body.InterviewStepName;
                record.RemarkSz = body.Remark;
                record.InterviewAddress = body.InterviewAddress;

            });
            await _ghrh_InterviewRecordServices.Update(records, ["InterviewTime", "InterviewBeginTime", "InterviewEndTime", "Status", "InterviewStepName", "RemarkSz", "InterviewAddress", "UpdateTime", "UpdateBy"]);
            await _ghrh_InterviewOrderServices.Update(order, ["InterviewStepName", "UpdateTime", "UpdateBy"]);

            string content1 = await QueryLangValue("F_ResumeMaintenance_0161", "安排面试，面试时间：{0}，面试地点：{1}，面试备注：{2}", body.Time, body.InterviewAddress, body.Remark ?? "无");
            await LogRecord(order.Id, content1, id, records[0].Id, "ScheduleInterview", true);

            entity.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
            await Update(entity, ["Status", "UpdateTime", "UpdateBy"]);

            content1 = await QueryLangValue("F_ResumeMaintenance_0163", "您有个简历待面试,面试时间：{0}，请及时查看！", body.Time);
            for (int i = 0; i < records.Count; i++)
            {
                await SendMessage([records[i].StaffId.Value], await QueryLangValue("F_ResumeMaintenance_0162", "面试提醒"), content1, "/M_ESS_Recruit/F_ESS_Interview");
            }

            #region 处理附件
            if (body.Attachments != null && body.Attachments.Any())
                for (int i = 0; i < body.Attachments.Count; i++)
                {
                    await Db.Updateable<Ghrs_Attachment>()
                        .SetColumns(it => new Ghrs_Attachment()
                        {
                            TableName = records[0].Id.ObjToString(),
                            UpdateTime = DateTime.Now
                        })
                        .Where(it => it.RelativePath == body.Attachments[i].RelativePath)
                        .ExecuteCommandAsync();
                }
            #endregion
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 改期
    public async Task<ServiceResult> ChangeSubscribeInterview(long id, ResumeSubscribeInterviewForm form)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.HasRecommended)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0164", "【已推荐】状态下才能约面！"));

        string content = await QueryLangValue("F_ResumeMaintenance_0158", "传入时间【{0}】无效，请检查格式！");
        form.Times.ForEach(x =>
        {
            if (x.IndexOf("~") < 0)
                throw new Exception(string.Format(content, x));
            var array = x.Split('~');
            try
            {
                Convert.ToDateTime(array[0]);
                Convert.ToDateTime(array[1]);
            }
            catch (Exception)
            {
                throw new Exception(string.Format(content, x));
            }
        });

        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        if (order != null)
        {

            bool isChangeInterviewTime = false;

            var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);
            records.ForEach(record =>
            {
                record.Status = DIC_INTERVIEW_ORDER_STATUS.HasChangeDate;
                record.RemarkSz = form.Remark;
                if (record.PlanInterviewTime1.IsNotEmptyOrNull() || record.PlanInterviewTime2.IsNotEmptyOrNull() || record.PlanInterviewTime3.IsNotEmptyOrNull())
                    isChangeInterviewTime = true;
                if (isChangeInterviewTime)
                    record.WaitInterviewStatus = "HasChangeInterviewTime";

            });

            for (int i = 0; i < form.Times.Count; i++)
            {
                var time = form.Times[i];
                if (time.IsNotEmptyOrNull())
                {
                    var time1 = time.Split('~');

                    time = DateTimeHelper.ConvertToMiniuteString(time1[0]) + "~" + DateTimeHelper.ConvertToOnlyHourMinuteString(time1[1]);
                }
                if (i == 0)
                    records.ForEach(record => record.PlanInterviewTime1 = time);
                else if (i == 1)
                    records.ForEach(record => record.PlanInterviewTime2 = time);
                else if (i == 2)
                    records.ForEach(record => record.PlanInterviewTime3 = time);
            }
            if (isChangeInterviewTime)
                order.WaitInterviewStatus = "HasChangeInterviewTime";

            order.Status = DIC_INTERVIEW_ORDER_STATUS.HasChangeDate;
            order.AppointmentFeedback = form.Remark;
            await _ghrh_InterviewOrderServices.Update(order, ["Status", "AppointmentFeedback"]);

            await _ghrh_InterviewRecordServices.Update(records[0], ["PlanInterviewTime1", "PlanInterviewTime2", "PlanInterviewTime3", "Status"]);

            string content1 = await QueryLangValue("F_ResumeMaintenance_0165", "修改面试时间，面试时间：{0},备注：{1}", string.Join("、", form.Times), form.Remark ?? "无");
            await LogRecord(order.Id, content1);

            await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.HasChangeDate);
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 变更面试时间
    public async Task<ServiceResult> ModifyInterviewTime(long id, string time)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
        {
            string content1 = await QueryLangValue("F_ResumeMaintenance_0166", "【待面试】状态下才能变更面试时间！");
            return ServiceResult.OprateFailed("");

        }

        string content = await QueryLangValue("F_ResumeMaintenance_0158", "传入时间【{0}】无效，请检查格式！");
        if (time.IndexOf("~") < 0)
            throw new Exception(string.Format(content, time));
        var array = time.Split('~');
        try
        {
            Convert.ToDateTime(array[0]);
            Convert.ToDateTime(array[1]);
        }
        catch (Exception)
        {
            throw new Exception(string.Format(content, time));
        }
        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        if (order != null)
        {
            var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);

            records.ForEach(record =>
            {
                record.InterviewTime = time;
                record.InterviewBeginTime = Convert.ToDateTime(array[0]);
                record.InterviewEndTime = Convert.ToDateTime(array[1]);

                record.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;

            });

            await _ghrh_InterviewRecordServices.Update(records, ["InterviewTime", "InterviewBeginTime", "InterviewEndTime", "Status"]);

            //if (record.ReceiverIds.IsNotEmptyOrNull())
            //{
            //    var staffs = JsonHelper.JsonToObj<List<ResumeRecommendFormStaff>>(record.ReceiverIds);
            //    await SendMessage(staffs.Select(x => x.StaffId).ToList(), await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), "您有个简历【待面试】，请及时查看面试时间！");
            //} 
            string content1 = await QueryLangValue("F_ResumeMaintenance_0167", "变更面试时间，面试时间：{0}", time);
            await LogRecord(order.Id, content1);

            entity.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
            await Update(entity, ["Status"]);
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 待面试提醒
    public async Task<ServiceResult> RemindWaitInterview(List<long> ids)
    {
        for (int m = 0; m < ids.Count; m++)
        {
            var id = ids[m];
            var entity = await base.QueryById(id);
            if (entity == null)
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

            if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
            {
                string content1 = await QueryLangValue("F_ResumeMaintenance_0168", "非【待面试】状态下简历不可发送提醒！");
                return ServiceResult.OprateFailed(content1);

            }

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
            if (order != null)
            {
                var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);

                string content1 = await QueryLangValue("F_ResumeMaintenance_0169", "您有个简历【待面试】，请及时查看面试时间！");
                for (int i = 0; i < records.Count; i++)
                {
                    if (records[i].FirstViewTime.IsNotEmptyOrNull())
                        await SendMessage([records[i].StaffId.Value], await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), content1, "/M_ESS_Recruit/F_ESS_Interview");
                }
                content1 = await QueryLangValue("F_ResumeMaintenance_0170", "发送待面试提醒");
                await LogRecord(order.Id, $"", id, null, "RemindWaitInterview");
            }
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 重新安排面试
    public async Task<ServiceResult> RescheduleInterview(long id, ResumeRescheduleInterviewForm input, string type)
    {
        if (input.InterviewStaffs is null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0152", "至少选择一名面试者！"));
        input.InterviewStaffs = input.InterviewStaffs.Where(x => x.StaffId != null).Distinct().ToList();

        if (!input.InterviewStaffs.Any())
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0152", "至少选择一名面试者！"));
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        if (type == "Reschedule")
        {
            //if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
            //    return ServiceResult.OprateFailed("【待】状态下才能安排面试！");

            string content = await QueryLangValue("F_ResumeMaintenance_0158", "传入时间【{0}】无效，请检查格式！");
            if (input.Time.IndexOf("~") < 0)
                throw new Exception(string.Format(content, input.Time));
            var array = input.Time.Split('~');
            try
            {
                Convert.ToDateTime(array[0]);
                Convert.ToDateTime(array[1]);

                input.Time = DateTimeHelper.ConvertToMiniuteString(array[0]) + "~" + DateTimeHelper.ConvertToOnlyHourMinuteString(array[1]);
            }
            catch (Exception)
            {
                throw new Exception(string.Format(content, input.Time));
            }

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);

            if (order != null)
            {
                var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id && x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview);
                if (records != null && records.Any())
                {
                    for (int j = 0; j < records.Count; j++)
                    {
                        records[j].Status = DIC_INTERVIEW_ORDER_STATUS.HasInterview;

                        var staff = await Db.Queryable<Ghra_Staff>().Where(x => x.StaffID == records[j].StaffId).FirstAsync();
                        content = await QueryLangValue("F_ResumeMaintenance_0171", "因发起一个新面试，【{0}】未及时提交评估，自动转入【已面试】！", $"{staff?.StaffName}（{staff?.StaffNo}）");
                        await LogRecord(order.Id, content, id, records[j].Id, "TransferWaitSendOffer");
                    }
                    await _ghrh_InterviewRecordServices.Update(records, ["Status", "UpdateTime", "UpdateBy"]);
                }
                await Db.Updateable<Ghrh_InterviewOrder>()
                .SetColumns(it => it.IsEnable == 0)
                .Where(it => it.ResumeId == id)
                .ExecuteCommandAsync();
            }
            var interviewIds = input.InterviewStaffs.Select(o => o.StaffId.Value).ToList();
            var interviewer = await Db.Queryable<Ghra_Staff>().Where(o => interviewIds.Contains(o.StaffID)).ToListAsync();

            var orderId = await _ghrh_InterviewOrderServices.Add(new InsertGhrh_InterviewOrderInput()
            {
                ResumeId = id,
                RecommendTime = DateTime.Now,
                RecommendId = App.User.ID,
                Round = 1,
                OrderNo = await GenerateContinuousSequence("Ghrh_InterviewOrder", "OrderNo", "V"),
                Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview,
                RecommendRemark = null,
                InterviewIds = JsonHelper.ObjToJson(interviewIds),
                Interviewer = string.Join(",", interviewer.Select(o => o.StaffName))
            });

            //await Db.Updateable<Ghrh_InterviewRecord>()
            //    .SetColumns(it => new Ghrh_InterviewRecord()
            //    {
            //        Status = DIC_INTERVIEW_ORDER_STATUS.SystemCancelInterview,
            //        UpdateTime = DateTime.Now
            //    })
            //    .Where(it => it.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview && it.OrderId == order.Id)
            //    .ExecuteCommandAsync();

            for (int i = 0; i < input.InterviewStaffs.Count; i++)
            {
                await _ghrh_InterviewRecordServices.Add(new InsertGhrh_InterviewRecordInput()
                {
                    OrderId = orderId,
                    Round = 1,
                    StaffId = input.InterviewStaffs[i].StaffId,
                    Email = input.InterviewStaffs[i].Email,
                    InterviewTime = input.Time,
                    PlanInterviewTime1 = input.Time,
                    InterviewBeginTime = Convert.ToDateTime(array[0]),
                    InterviewEndTime = Convert.ToDateTime(array[1]),
                    Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview,
                    IsPrimary = i == 0 ? true : false
                });
            }

            content = await QueryLangValue("F_ResumeMaintenance_0172", "向【{0}】发起一个新的面试，面试时间：{0}", string.Join("、", interviewer.Select(o => o.StaffName + $"（{o.StaffNo}）")), input.Time);
            await LogRecord(orderId, content, id, null, "ReRecommended");

            entity.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
            await Update(entity, ["Status", "UpdateTime", "UpdateBy"]);

            if (input.InterviewStaffs.IsNotEmptyOrNull())
                await SendMessage(input.InterviewStaffs.Select(x => x.StaffId.Value).ToList(), await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), await QueryLangValue("F_ResumeMaintenance_0173", "您有个新的邀约面试，请及时查看！"), "/M_ESS_Recruit/F_ESS_Interview");
        }
        else
        {
            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);

            if (order != null)
            {

                var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id && x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview);
                if (records != null && records.Any())
                {
                    for (int j = 0; j < records.Count; j++)
                    {
                        records[j].Status = DIC_INTERVIEW_ORDER_STATUS.HasInterview;

                        var staff = await Db.Queryable<Ghra_Staff>().Where(x => x.StaffID == records[j].StaffId).FirstAsync();
                        var content = await QueryLangValue("F_ResumeMaintenance_0171", "因发起一个新一轮面试，【{staff?.StaffName}（{staff?.StaffNo}）】未及时提交评估，自动转入【已面试】！", $"{staff?.StaffName}（{staff?.StaffNo}）");
                        await LogRecord(order.Id, content, id, records[j].Id, "TransferWaitSendOffer");
                    }
                    await _ghrh_InterviewRecordServices.Update(records, ["Status", "UpdateTime", "UpdateBy"]);
                }

                order.Round++;
                order.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
                await _ghrh_InterviewOrderServices.Update(order, ["Status", "Round"]);


                entity.Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
                await Update(entity, ["Status", "UpdateTime", "UpdateBy"]);

                for (int i = 0; i < input.InterviewStaffs.Count; i++)
                    await _ghrh_InterviewRecordServices.Add(new InsertGhrh_InterviewRecordInput()
                    {
                        OrderId = order.Id,
                        Round = order.Round,
                        Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview,
                        Email = input.InterviewStaffs[i].Email,
                        StaffId = input.InterviewStaffs[i].StaffId,
                        IsPrimary = i == 0 ? true : false
                    });
                var interviewIds = input.InterviewStaffs.Select(o => o.StaffId.Value).ToList();

                var interviewer = await Db.Queryable<Ghra_Staff>().Where(o => interviewIds.Contains(o.StaffID)).ToListAsync();
                var content1 = await QueryLangValue("F_ResumeMaintenance_0174", "向【{0}】发起一个新一轮面试，面试时间：{1}", string.Join("、", interviewer.Select(o => o.StaffName + $"（{o.StaffNo}）")), input.Time);
                await LogRecord(order.Id, content1, id, null, "ReScheduleInterview");

                if (input.InterviewStaffs.IsNotEmptyOrNull())
                    await SendMessage(input.InterviewStaffs.Select(x => x.StaffId.Value).ToList(), await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), await QueryLangValue("F_ResumeMaintenance_0175", "您有个新的推荐简历，请及时查看！"), "/M_ESS_Recruit/F_ESS_Candidate");
            }
        }
        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 变更面试官
    public async Task<ServiceResult> ModifyInterviewer(long id, List<ResumeRecommendFormStaff> InterviewStaffs)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        var record = await _ghrh_InterviewRecordServices.QuerySingle(x => x.Round == order.Round && x.OrderId == order.Id);

        var insertRecord = Mapper.Map(record).ToANew<InsertGhrh_InterviewRecordInput>();

        await _ghrh_InterviewRecordServices.Delete(x => x.Round == order.Round && x.OrderId == order.Id);
        for (int i = 0; i < InterviewStaffs.Count; i++)
        {
            insertRecord.StaffId = InterviewStaffs[i].StaffId;
            await _ghrh_InterviewRecordServices.Add(insertRecord);
        }

        await LogRecord(order.Id, await QueryLangValue("F_ResumeMaintenance_0176", "变更面试官！"));
        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 转入已发Offer
    public async Task<ServiceResult> TransferHasSendOffer(long id, long templateId, Dictionary<string, string> extFields)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        var content = (await _ghrh_TemplateServices.Preview(id, templateId, extFields)).Data;
        var offerFileUrl = await GenerateOffer(entity, content);

        await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);
        var content1 = await QueryLangValue("F_ResumeMaintenance_0177", "变更状态为：已发offer！");
        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        if (order != null)
        {
            await UpdateInterviewOrderStatus(order, DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);
            //await UpdateInterviewRecordStatus(order, DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);

            await _ghrh_InterviewLogServices.Add(new InsertGhrh_InterviewLogInput()
            {
                InterviewOrderId = order.Id,
                ResumeId = id,
                StaffId = App.User.StaffId,
                StaffName = App.User.StaffName + "（" + App.User.StaffNo + "）",
                Source = "TransferHasSendOffer",
                Reverse1 = offerFileUrl,
                UserId = App.User.ID,
                UserName = App.User.Name,
                RemarkSz = content1
            });
        }
        else
            await _ghrh_InterviewLogServices.Add(new InsertGhrh_InterviewLogInput()
            {
                InterviewOrderId = null,
                ResumeId = id,
                StaffId = App.User.StaffId,
                StaffName = App.User.StaffName + "（" + App.User.StaffNo + "）",
                Source = "TransferHasSendOffer",
                Reverse1 = offerFileUrl,
                UserId = App.User.ID,
                UserName = App.User.Name,
                RemarkSz = content1
            });
        return ServiceResult.OprateSuccess();
    }

    public async Task<ServiceResult> SendOffer(long id, long templateId, Dictionary<string, string> extFields)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        if (entity.Email.IsNullOrEmpty())
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0178", "简历中尚未设置邮箱，暂不可发送！"));

        await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);
        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        await UpdateInterviewOrderStatus(order, DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);
        await UpdateInterviewRecordStatus(order, DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);

        //await LogRecord(order.Id, $"给【{entity.StaffName}】发送Offer！", id, null, "SendOffer");


        var content = (await _ghrh_TemplateServices.Preview(id, templateId, extFields)).Data;

        #region 发送邮件
        var mailOutbox = new Ghrs_MailOutbox();
        mailOutbox.TOMail = entity.Email;
        mailOutbox.MailTitle = "录用通知";
        mailOutbox.MailBody = content;
        mailOutbox.MailPriority = 1;
        mailOutbox.ScheduleTime = DateTime.Now;
        mailOutbox.IsEnable = 1;
        mailOutbox.SortNo = 1;
        mailOutbox.CompanyID = 0;
        mailOutbox.IsDefault = 0;
        mailOutbox.CreateProg = "SendOffer";
        var id1 = await Db.Insertable(mailOutbox).ExecuteReturnIdentityAsync();
        #endregion

        #region 生成Offer PDf
        entity.OfferFileUrl = await GenerateOffer(entity, content);
        await Update(entity, ["OfferFileUrl"]);
        var ontent1 = await QueryLangValue("F_ResumeMaintenance_0179", "给【{0}】发送Offer！");
        ontent1 = string.Format(ontent1, entity.StaffName);
        await _ghrh_InterviewLogServices.Add(new InsertGhrh_InterviewLogInput()
        {
            InterviewOrderId = order.Id,
            ResumeId = id,
            StaffId = App.User.StaffId,
            StaffName = App.User.StaffName + "（" + App.User.StaffNo + "）",
            Source = "SendOffer",
            Reverse1 = entity.OfferFileUrl,
            UserId = App.User.ID,
            UserName = App.User.Name,
            RemarkSz = ontent1,
        });

        #endregion

        #region 处理人力需求单 录用人数
        if (order.RequestId.IsNotEmptyOrNull())
        {
            var sql = $@"UPDATE Ghrh_HumanRequest
                            SET OfferCount = ISNULL (OfferCount, 0) + 1
                            WHERE Id = {order.RequestId}";
            await Db.Ado.ExecuteCommandAsync(sql);
        }
        #endregion

        return ServiceResult.OprateSuccess();
    }

    /// <summary>
    /// 生成Offer
    /// </summary>
    /// <param name="id"></param>
    /// <param name="templateId"></param>
    /// <param name="extFields"></param>
    /// <returns></returns>
    public async Task<string> GenerateOffer(Ghrh_ResumeDto entity, string content)
    {

        #region 生成Offer PDf
        var globalSettings = new GlobalSettings
        {
            ColorMode = ColorMode.Color,
            Orientation = Orientation.Portrait,
            PaperSize = PaperKind.A4,
            DocumentTitle = entity.StaffName,
        };

        var objectSettings = new ObjectSettings
        {
            PagesCount = true,
            HtmlContent = content,
            WebSettings = { DefaultEncoding = "utf-8" },
        };

        var pdf = new HtmlToPdfDocument()
        {
            GlobalSettings = globalSettings,
            Objects = { objectSettings }
        };

        var fileBytes = _converter.Convert(pdf);
        string pathHeader = "wwwroot/files/pdf_files";
        if (!Directory.Exists(pathHeader))
            Directory.CreateDirectory(pathHeader);
        var ms = new MemoryStream(fileBytes);
        var file = new FormFile(ms, 0, ms.Length, Path.GetFileNameWithoutExtension(pathHeader), Path.GetFileName(pathHeader));

        var fileName = entity.StaffName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";
        await using (var fs = System.IO.File.Create(pathHeader + "/" + fileName))
        {
            await file.CopyToAsync(fs);
            fs.Flush();
        }
        return "/Advanced/files/pdf_files/" + fileName;


        #endregion
    }
    #endregion

    #region 已发offer提醒
    public async Task<ServiceResult> RemindHasOffer(List<long> ids)
    {
        for (int i = 0; i < ids.Count; i++)
        {
            var id = ids[i];
            var entity = await base.QueryById(id);
            if (entity == null)
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

            if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.HasSendOffer)
            {
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0180", "非【已发offer】状态下简历不可发送提醒！"));
            }

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
            if (order != null)
            {
                var record = await _ghrh_InterviewRecordServices.QuerySingle(x => x.Round == order.Round && x.OrderId == order.Id);

                //if (record.ReceiverIds.IsNotEmptyOrNull())
                //{
                //    var staffs = JsonHelper.JsonToObj<List<ResumeRecommendFormStaff>>(record.ReceiverIds);
                //} 
                await SendMessage([record.StaffId.Value], await QueryLangValue("F_ResumeMaintenance_0153", "简历提醒"), await QueryLangValue("F_ResumeMaintenance_0181", "您有个简历【待面试】，请及时查看面试时间！"));

                await LogRecord(order.Id, await QueryLangValue("F_ResumeMaintenance_0182", "发送待已发offer提醒！"));

            }
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 变更是否报道
    public async Task<ServiceResult> ModifyIsOffer(List<long> ids, bool isOffer)
    {
        for (int i = 0; i < ids.Count; i++)
        {
            var id = ids[i];
            var entity = await base.QueryById(id);
            if (entity == null)
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

            //if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.HasSendOffer)
            //    return ServiceResult.OprateFailed("非【已发offer】状态下简历不可发送提醒！");

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
            if (order != null)
            {
                var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);
                if (isOffer)
                {
                    entity.Status = DIC_INTERVIEW_ORDER_STATUS.HasOffer;
                    order.Status = DIC_INTERVIEW_ORDER_STATUS.HasOffer;
                    records.ForEach(record => record.Status = DIC_INTERVIEW_ORDER_STATUS.HasOffer);

                    await SyncToStaff(id);
                }
                else
                {
                    entity.Status = DIC_INTERVIEW_ORDER_STATUS.HasSendOffer;
                    order.Status = DIC_INTERVIEW_ORDER_STATUS.HasSendOffer;
                    records.ForEach(record => record.Status = DIC_INTERVIEW_ORDER_STATUS.HasSendOffer);
                }

                await Update(entity, ["Status"]);
                await _ghrh_InterviewOrderServices.Update(order, ["Status"]);
                //await _ghrh_InterviewRecordServices.Update(records, ["Status"]);
                await LogRecord(order.Id, isOffer == true ? await QueryLangValue("F_ResumeMaintenance_0183", "标记为已报到！") : await QueryLangValue("F_ResumeMaintenance_0184", "标记为未报到！"), id, null, "CheckIn");
            }
            else
            {

            }
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 不合适
    public async Task<ServiceResult> Fail(ResumeFailInterviewForm input)
    {
        for (int i = 0; i < input.Ids.Count; i++)
        {
            var id = input.Ids[i];
            var entity = await base.QueryById(id);
            if (entity == null)
                continue;

            await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.Fail);

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);

            order.Status = DIC_INTERVIEW_ORDER_STATUS.Fail;
            order.InterviewResult = "不合适";
            order.FilterFeedback = input.Content;
            await _ghrh_InterviewOrderServices.Update(order, ["Status", "InterviewResult", "FilterFeedback", "UpdateTime"]);

            var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);
            records.ForEach(record =>
            {
                record.Status = DIC_INTERVIEW_ORDER_STATUS.Fail;
                record.InterviewResult = "不合适";
                record.FilterFeedback = input.Content;
            });
            var content = await QueryLangValue("F_ResumeMaintenance_0185", "变更状态为：不合适，不合适原因：{0}", input.Content ?? "无");
            await _ghrh_InterviewRecordServices.Update(records, ["Status", "InterviewResult", "FilterFeedback", "UpdateTime"]);
            await LogRecord(order.Id, content, id, null, "Fail");
        }
        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 取消面试
    public async Task<ServiceResult> CancelInterview(long id, ResumeCancelInterviewForm input)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);


        var record = await _ghrh_InterviewRecordServices.QuerySingle(x => x.Round == order.Round && x.OrderId == order.Id && x.StaffId == GetStaffId());
        record.Status = DIC_INTERVIEW_ORDER_STATUS.Cancel;
        record.CancelReason = input.CancelReason;
        await _ghrh_InterviewRecordServices.Update(record, ["Status", "CancelReason", "UpdateTime"]);

        order.Status = DIC_INTERVIEW_ORDER_STATUS.Cancel;
        await _ghrh_InterviewOrderServices.Update(order, ["Status", "UpdateTime"]);

        var content = await QueryLangValue("F_ResumeMaintenance_0186", "取消面试，取消原因：{0}", record.CancelReason ?? "无");
        await LogRecord(order.Id, content, id, null, "Cancel");

        await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.Cancel);

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 转入待发Offer
    public async Task<ServiceResult> ApplyOfferApproval(List<long> ids)
    {
        for (int i = 0; i < ids.Count; i++)
        {
            var id = ids[i];
            var entity = await base.QueryById(id);
            if (entity == null)
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

            //if (entity.Status != DIC_INTERVIEW_ORDER_STATUS.HasInterview || entity.Status != DIC_INTERVIEW_ORDER_STATUS.WaitRecommended)
            //    return ServiceResult.OprateFailed("只有在【已面试】、【待推荐】状态下，才能转入待发Offer！");

            if (await Db.Queryable<Ghrh_OfferApplyOrder>().AnyAsync(x => x.WorkState == 0 && x.ResumeId == id))
                return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0187", "当前简历录用审批处于审批中，不可转入待发Offer！"));

            await UpdateResumeStatus(entity, DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer);

            var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);

            if (order == null)
            {
                var orderId = await _ghrh_InterviewOrderServices.Add(new InsertGhrh_InterviewOrderInput()
                {
                    ResumeId = id,
                    RecommendTime = DateTime.Now,
                    RecommendId = App.User.ID,
                    Round = 1,
                    OrderNo = await GenerateContinuousSequence("Ghrh_InterviewOrder", "OrderNo", "V"),
                    Status = DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer,
                });
                order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
            }

            if (order != null)
            {

                await UpdateInterviewOrderStatus(order, DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer);
                //await UpdateInterviewRecordStatus(order, DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer);

                var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id && x.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview);

                for (int j = 0; j < records.Count; j++)
                {
                    records[j].Status = DIC_INTERVIEW_ORDER_STATUS.HasInterview;

                    var staff = await Db.Queryable<Ghra_Staff>().Where(x => x.StaffID == records[j].StaffId).FirstAsync();
                    await LogRecord(order.Id, await QueryLangValue("F_ResumeMaintenance_0188", "因状态转入待发Offer，【0】未及时提交评估，自动转入【已面试】！", $"{staff?.StaffName}（{staff?.StaffNo}）"), id, records[j].Id, "TransferWaitSendOffer");
                }
                await _ghrh_InterviewRecordServices.Update(records, ["Status", "UpdateTime", "UpdateBy"]);

                await LogRecord(order.Id, await QueryLangValue("F_ResumeMaintenance_0189", "转入待发Offer！"), id, null, "TransferWaitSendOffer");
            }
        }

        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region ESS端查询简历记录
    public async Task<ServicePageResult<Ghrh_ResumeDto>> QueryESS(QueryBody filter, string condition)
    {
        int? staffId = GetStaffId();
        RefAsync<int> totalCount = 0;
        var dt = DateTime.Now.Date;
        string sql = @$"SELECT DISTINCT A.Id,
       A.Id ResumeId,
       A.CreateBy,
       A.CreateTime,
       A.CreateProg,
       A.CreateIP,
       A.UpdateBy,
       A.UpdateTime,
       A.UpdateProg,
       A.UpdateIP,
       A.OperateLogID,
       A.StaffName,
       A.StaffEname,
       A.PhotoUrl,
       A.Nation,
       A.Birthday,
       A.NativePlace,
       A.Gender,
       A.Age,
       A.Education,
       A.School,
       A.GraduateDate,
       A.ApplyStatus,
       A.TitleId,
       A.MaritalStatus,
       A.PoliticStatus,
       A.Mobile,
       A.Email,
       A.Indate,
       A.UrgentRelation,
       A.UrgentContact,
       A.UrgentContactTel,
       A.UrgentContactAddress,
       A.Urgent2Relation,
       A.Urgent2Contact,
       A.Urgent2ContactTel,
       A.Urgent2ContactAddress,
       A.PreJobSeniority,
       A.EduDegree,
       A.CertificateType,
       A.IdCardNo,
       A.IDCardBegin,
       A.IDCardEnd,
       A.RegisteredType,
       A.IDCardOrg,
       A.RegisteredAddress,
       A.NowAddress,
       A.Introducer,
       A.NationNo,
       A.FundAccountID,
       A.FundBase,
       A.InInsureAccountNo,
       A.InsureAccountID,
       A.InsureBase,
       A.IsInsure,
       A.IsFund,
       A.IsRecommend,
       A.IsStay,
       A.CarNo,
       A.Tags,
       A.Status,
       A.WorkYears,
       A.SalaryPeriod,
       A.ApplicationStatus,
       A.ApplicationTime,
       A.TitleType,
       A.StaffType1,
     --  A.InterviewResult,
       A.FailInterviewReason,
       A.RemarkSz,
       A.IsDefault,
       A.IsEnable,
       A.Reverse1,
       A.Reverse2,
       A.Reverse3,
       A.Reverse4,
       A.Reverse5,
       A.Reverse6,
       A.Reverse7,
       A.Reverse8,
       A.Reverse9,
       A.Reverse10,
       A.ReverseI1,
       A.ReverseI2,
       CASE C.Status WHEN 'HasInterview' THEN NULL ELSE B.OrderNo END
          OrderNo,
       CASE C.Status WHEN 'HasInterview' THEN NULL ELSE B.RecommendTime END
          RecommendTime,
       CASE C.Status WHEN 'HasInterview' THEN NULL ELSE B.[Round] END
          [Round],
       CASE C.Status WHEN 'HasInterview' THEN NULL ELSE B.IsPass END
          IsPass,
       B.InterviewResult,
       C.Status OrderStatus,
       C.StaffId,
       C.CancelReason,
       CONVERT
       (BIT, CASE B.[Round] WHEN C.[Round] THEN 'true' ELSE 'false' END) AS IsAllowAssess,
       ISNULL(A.UpdateTime, A.CreateTime) CreateTime1,
       C.IsPrimary
FROM Ghrh_Resume A
     JOIN Ghrh_InterviewOrder B ON A.ID = B.ResumeId
     JOIN Ghrh_InterviewRecord C
        ON B.ID = C.OrderId 
         AND B.[Round] = C.[Round]
WHERE A.IsEnable = 1 AND C.IsEnable = 1 
-- AND C.Status = A.Status AND B.IsEnable = 1
";

        if (string.IsNullOrWhiteSpace(filter.orderBy))
            filter.orderBy = "CreateTime1 ASC";

        sql += $" AND C.StaffId={staffId}";

        #region 处理查询条件
        //Expression<Func<Ghrh_Resume, bool>> whereExpression = new Expression<Func<Ghrh_Resume, bool>>();
        var whereExpression = Expressionable.Create<Ghrh_Resume>();
        foreach (JProperty jProperty in filter.jsonParam.Properties())
        {
            var name = jProperty.Name;
            var value = jProperty.Value.ToString();
            if (name == "page" || name == "pageSize")
                continue;
            if (value.IsNotEmptyOrNull())
            {
                var jsonParam = JsonHelper.JsonToObj<JsonParam>(value);

                switch (name)
                {
                    case "WaitRecommend":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitRecommended}'";
                        break;
                    case "HasRecommended":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasRecommended}'";
                        break;
                    case "WaitAppointment":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitAppointment}'";
                        break;
                    case "WaitInterview":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                        {
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitInterview}'";
                            //sql += $" AND B.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitInterview}'";
                            //sql += $" AND (B.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasInterview}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.InterviewFail}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasSendOffer}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasOffer}')";

                        }
                        break;
                    case "HasInterview":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                        {
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasInterview}'";
                            //sql += $" AND (B.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasInterview}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.InterviewFail}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasSendOffer}' OR B.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasOffer}')";
                            //sql += $" AND (C.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitInterview}' OR C.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasInterview}' OR C.Status = '{DIC_INTERVIEW_ORDER_STATUS.InterviewFail}')";
                        }
                        break;
                    case "WaitSendOffer":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.WaitSendOffer}'";
                        break;
                    case "HasSendOffer":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasSendOffer}'";
                        break;
                    case "HasOffer":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.HasOffer}'";
                        break;
                    case "Fail":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.Fail}'";
                        break;
                    case "Expire":
                        if (jsonParam.columnValue.ObjToInt() == 1)
                            sql += $" AND C.Status = '{DIC_INTERVIEW_ORDER_STATUS.Expire}'";
                        break;

                    case "SalaryPeriod":
                    case "Education":
                    case "ApplyStatus":
                    case "Gender":
                        //if (jsonParam.columnValue.IsNotEmptyOrNull())
                        //{
                        //    var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                        //    if (!ids1.Any(x => x == "NoFliter"))
                        //        whereExpression.And(x => ids1.Contains(x.Education));
                        //}

                        if (jsonParam.columnValue != null)
                        {
                            var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                            if (!ids1.Any(x => x == "NoFliter"))
                                condition += $" AND {name} IN ({string.Join(",", ids1.Select(id => "'" + id + "'"))})";
                        }
                        break;

                    case "WorkYears":
                    case "Age":
                        if (jsonParam.columnValue.IsNotEmptyOrNull())
                        {
                            var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                            if (!ids1.Any(x => x == "NoFliter"))
                            {
                                var i = 0;
                                condition += " AND (";
                                ids1.ForEach(x =>
                                {
                                    var arr = x.Split(['-']);
                                    if (i == 0)
                                        condition += $"({name} >= {arr[0]} AND {name} <={arr[1]})";
                                    else
                                        condition += $" OR ({name} >= {arr[0]} AND {name} <={arr[1]})";
                                    i++;
                                });
                                condition += ")";
                            }
                        }
                        break;
                    case "Tags":
                        if (jsonParam.columnValue.IsNotEmptyOrNull())
                        {
                            var ids1 = JsonHelper.JsonToObj<List<string>>(jsonParam.columnValue.ToString());
                            if (!ids1.Any(x => x == "NoFliter"))
                            {
                                var i = 0;
                                condition += " AND (";
                                ids1.ForEach(x =>
                                {
                                    var arr = x.Split(['-']);
                                    if (i == 0)
                                        condition += $"({name} like '%{x}%')";
                                    else
                                        condition += $" OR ({name} like '%{x}%')";
                                    i++;
                                });
                                condition += ")";
                            }
                        }
                        break;
                    case "Keywords":
                        if (jsonParam.columnValue.IsNotEmptyOrNull())
                            whereExpression.And(x => (x.StaffName.Contains(jsonParam.columnValue.ToString()) ||
                            x.StaffEname.Contains(jsonParam.columnValue.ToString()) ||
                            x.Tags.Contains(jsonParam.columnValue.ToString()) ||
                            x.RemarkSz.Contains(jsonParam.columnValue.ToString()) ||
                            x.School.Contains(jsonParam.columnValue.ToString())));
                        break;
                    default:
                        break;
                }
            }
        }
        #endregion
        sql += " AND " + condition;
        if (filter.pageSize == 0)
            filter.pageSize = 10000;

        var sql1 = GetQueryString(sql, filter.pageNum, filter.pageSize, filter.orderBy);

        totalCount = await Db.Ado.GetIntAsync("select count(0) from ( " + sql + ") A");
        var list = DbAccess.QueryList<Ghrh_ResumeDto>(sql);
        //var data = await Db.SqlQueryable<object>(sql)
        //    .OrderBy(filter.orderBy)
        //    .ToPageListAsync(filter.pageNum, filter.pageSize, totalCount);

        var ids = list.Select(x => x.ResumeId).ToList();
        var titleIds = list.Where(x => x.TitleId != null).Select(x => x.TitleId.Value).Distinct().ToList();

        var titles = await Db.Queryable<Ghra_Title>().Where(x => x.TitleID != null && titleIds.Contains(x.TitleID.Value)).ToListAsync();
        var eduBGs = await _ghrh_ResumeEduBGServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));
        var workExps = await _ghrh_ResumeWorkExpServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));
        eduBGs.ForEach(async x =>
        {
            x.DegreeLevelLabel = await GetParaLabel("EducationalBGLevel", x.DegreeLevel);
            x.BeginDate1 = DateTimeHelper.ConvertToDayString(x.BeginDate);
            x.EndDate1 = DateTimeHelper.ConvertToDayString(x.EndDate);
        });
        workExps.ForEach(x =>
        {
            x.BeginDate1 = DateTimeHelper.ConvertToDayString(x.BeginDate);
            x.EndDate1 = DateTimeHelper.ConvertToDayString(x.EndDate);
        });

        var tagIds = new List<long>();
        list.ForEach(x =>
        {
            if (x.Tags.IsNotEmptyOrNull())
                tagIds.AddRange(JsonHelper.JsonToObj<List<long>>(x.Tags));
            x.Status = x.OrderStatus;
        });

        tagIds = tagIds.Distinct().ToList();
        var tags = await _ghrh_ResumeTagServices.QueryDto(x => tagIds.Contains(x.Id));
        var orders = await _ghrh_InterviewOrderServices.QueryDto(x => x.ResumeId != null && ids.Contains(x.ResumeId.Value));

        sql = $@"SELECT A.RoleId, B.RoleNo, B.RoleName
            FROM Ghrs_UserRole A LEFT JOIN Ghrs_Role B ON A.RoleId = B.RoleId
            WHERE     A.UserID = {App.User.ID}
                  AND A.IsEnable = 1
                  AND B.IsEnable = 1
                  AND B.RoleNo LIKE 'RecruitResume%'";
        var toolbarRoles = DbAccess.QueryList<ToolbarRole>(sql);

        list.ForEach(async x =>
        {
            //x.ResumeId = x.Id;
            if (x.TitleId != null) x.TitleName = titles.Where(o => o.TitleID == x.TitleId).FirstOrDefault()?.TitleName;
            x.TitleLabel = x.TitleName;
            await SetLabel(x);

            x.EduBG = eduBGs.Where(o => x.ResumeId == o.ResumeId).ToList();
            x.WorkExp = workExps.Where(o => x.ResumeId == o.ResumeId).ToList();
            if (x.Tags.IsNotEmptyOrNull())
            {
                var tagIds1 = JsonHelper.JsonToObj<List<long>>(x.Tags);
                x.TagList = tags.Where(o => tagIds1.Contains(o.Id)).Select(o => o.TagName).ToList();
            }

            var order = orders.Where(o => o.ResumeId == x.ResumeId).FirstOrDefault();
            if (order != null)
            {
                x.IsPass = order.IsPass;
                if (order.IsPass != null)
                    x.IsPassLabel = order.IsPass == true ? "通过" : "不通过";
                x.InterviewResult = order.InterviewResult;
                x.OptionalInterviewTime = order.OptionalInterviewTime;
                x.InterviewTime = order.InterviewTime;
                if (x.Status != DIC_INTERVIEW_ORDER_STATUS.WaitRecommended)
                {
                    x.HireDeptName = order.HireDeptName;
                    x.HireTitleName = order.HireTitleName;
                    x.Interviewer = order.Interviewer;
                }

                x.InterviewTime1 = order.InterviewTime;
                x.InterviewContent = order.InterviewContent;

                if (order.WaitInterviewStatus == "Cancel")
                    x.InterviewTime1 = "已取消";
                else if (order.WaitInterviewStatus == "HasChangeInterviewTime")
                    x.InterviewTime1 = "已改期";

            }

            if (toolbarRoles.Where(x => x.RoleNo == "RecruitResumeESSChangeDate").Any())
                x.IsAllowChangeDate = true;
            if (toolbarRoles.Where(x => x.RoleNo == "RecruitResumeESSCancel").Any())
                x.IsAllowCancel = true;
        });

        return new ServicePageResult<Ghrh_ResumeDto>(filter.pageNum, totalCount, filter.pageSize, list);
    }

    #endregion

    #region 获取安排面试表单信息
    public async Task<ServiceResult<ResumeScheduleInterviewResult>> QueryScheduleInterviewForm(long id)
    {
        var result = new ResumeScheduleInterviewResult();
        var entity = await base.QueryById(id);
        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);

        if (order != null && order.RequestId.IsNotEmptyOrNull())
        {
            var request = await _ghrh_HumanRequestServices.QueryById(order.RequestId);
            result.RequestId = order.RequestId;
            result.DeptName = request?.DeptName;
            result.TitleName = request?.TitleName;
            result.ResumeCount = request?.RequestCount ?? 0;
            result.RecommendCount = request?.RecommendCount ?? 0;
            result.InterviewCount = request?.InterviewCount ?? 0;
            result.OfferCount = request?.OfferCount ?? 0;
        }
        else
        {
            result.DeptName = order?.HireDeptName;
            result.TitleName = order?.HireTitleName;
        }

        result.ResumeStaffName = entity.StaffName;
        result.Times = new List<string>();
        result.Staffs = new();
        var records = new List<Ghrh_InterviewRecord>();
        if (order != null) records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);

        if (records != null && records.Any())
        {
            var record = records.Where(x => x.PlanInterviewTime1 != null).FirstOrDefault();
            if (record != null)
            {

                #region 返回上轮面试结果
                result.LastInterviewName = (await Db.Queryable<Ghra_Staff>().Where(x => x.StaffID == record.StaffId).FirstAsync())?.StaffName;
                result.LastInterviewResult = record.InterviewResult;
                result.LastInterviewContent = record.InterviewResultRemark;
                #endregion

                if (record.PlanInterviewTime1.IsNotEmptyOrNull())
                    result.Times.Add(record.PlanInterviewTime1);

                if (record.PlanInterviewTime2.IsNotEmptyOrNull())
                    result.Times.Add(record.PlanInterviewTime2);

                if (record.PlanInterviewTime3.IsNotEmptyOrNull())
                    result.Times.Add(record.PlanInterviewTime3);
            }
            var staffIds = records.Where(x => x.StaffId != null).Select(x => x.StaffId.Value).ToList();
            result.Staffs = await Db.Queryable<Ghra_Staff>().Where(x => staffIds.Contains(x.StaffID)).ToListAsync();
        }

        #region 返回部门负责人信息
        //var dept = await Db.Queryable<Ghro_Dept>().Where(x => x.DeptID == order.HireDeptId).FirstAsync();
        //if (dept != null)
        //{
        //    var staff = await Db.Queryable<Ghra_Staff>().Where(x => x.StaffID == dept.DeptManagerID).FirstAsync();
        //    if (staff != null)
        //    {
        //        result.DeptManagerID = staff.StaffID;
        //        result.DeptManagerEmail = staff.Email;
        //    }
        //}

        var applyOrder = await Db.Queryable<Ghrh_OfferApplyOrder>().Where(x => x.ResumeId == id).FirstAsync();
        if (applyOrder != null)
        {
            result.DeptName = (await Db.Queryable<Ghro_Dept>().Where(x => x.DeptID == applyOrder.DeptId).FirstAsync())?.DeptName;
            result.TitleName = (await Db.Queryable<Ghra_Title>().Where(x => x.TitleID == applyOrder.TitleId).FirstAsync())?.TitleName;
        }
        #endregion

        return ServiceResult<ResumeScheduleInterviewResult>.OprateSuccess("查询成功", result);
    }
    #endregion

    #region 回收站中的简历30天后自动删除
    public async Task<ServiceResult> OverTimeDeleteResume()
    {
        int day = 30;

        var config = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "Recruit_Over_Time_Delete_Resume").FirstAsync();
        if (config != null)
            day = config.ConfigValue.ObjToInt();

        var dt = Db.GetDate().AddDays(-day);

        var list = await base.Query(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.Recycled && x.RecycledTime <= dt);
        var ids = list.Select(x => x.Id).ToList();
        if (ids.Any())
            await Db.Updateable<Ghrh_Resume>()
                .SetColumns(it => new Ghrh_Resume()
                {
                    IsEnable = 0
                }, true)
                .Where(it => ids.Contains(it.Id))
                .ExecuteCommandAsync();
        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 面试超时逾期
    public async Task<ServiceResult> InterviewOrderOverTime()
    {
        int day = 3;

        var config = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "Recruit_Interview_Order_Over_Time").FirstAsync();
        if (config != null)
            day = config.ConfigValue.ObjToInt();

        var dt = Db.GetDate().AddDays(-day);

        var list = await Db.Queryable<Ghrh_InterviewOrder>().Where(x => x.Status == DIC_INTERVIEW_ORDER_STATUS.HasRecommended && x.RecommendTime <= dt).ToListAsync();
        var ids = list.Select(x => x.Id).ToList();
        var resumeIds = list.Select(x => x.ResumeId).ToList();
        if (ids.Any())
            await Db.Updateable<Ghrh_InterviewOrder>()
                .SetColumns(it => new Ghrh_InterviewOrder()
                {
                    Status = DIC_INTERVIEW_ORDER_STATUS.Expire
                }, true)
                .Where(it => ids.Contains(it.Id))
                .ExecuteCommandAsync();
        if (resumeIds.Any())
            await Db.Updateable<Ghrh_Resume>()
                .SetColumns(it => new Ghrh_Resume()
                {
                    Status = DIC_INTERVIEW_ORDER_STATUS.Expire
                }, true)
                .Where(it => resumeIds.Contains(it.Id))
                .ExecuteCommandAsync();
        return ServiceResult.OprateSuccess();
    }
    #endregion

    #region 简历下载

    public async Task<ServiceResult<string>> Download(List<long> ids)
    {
        string frontUrl = AppSettings.app(["Startup", "FrontUrl"]);
        string path = string.Empty;
        frontUrl += "/Advanced";
        if (Env.IsDevelopment())
            frontUrl = "http://localhost:9292";

        if (ids.Count == 1)
        {
            var entity = await base.QueryById(ids[0]);
            var returnData = await HttpHelper.GetAsync(frontUrl + "/api/Ghrh_ResumeTemplatePreview/" + ids[0]);
            var globalSettings = new GlobalSettings
            {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Portrait,
                PaperSize = PaperKind.A4,
                DocumentTitle = entity.StaffName,
            };

            var objectSettings = new ObjectSettings
            {
                PagesCount = true,
                HtmlContent = returnData,
                WebSettings = { DefaultEncoding = "utf-8" },
            };

            var pdf = new HtmlToPdfDocument()
            {
                GlobalSettings = globalSettings,
                Objects = { objectSettings }
            };
            string pathHeader = "wwwroot/files/pdf_files";
            var fileName = entity.StaffName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";

            var fileBytes = _converter.Convert(pdf);
            if (!Directory.Exists(pathHeader))
                Directory.CreateDirectory(pathHeader);
            var ms = new MemoryStream(fileBytes);
            var file = new FormFile(ms, 0, ms.Length, Path.GetFileNameWithoutExtension(pathHeader), Path.GetFileName(pathHeader));

            await using (var fs = System.IO.File.Create(pathHeader + "/" + fileName))
            {
                await file.CopyToAsync(fs);
                fs.Flush();
            }

            path = "/Advanced/files/pdf_files/" + fileName;
        }
        else
        {
            var files = new List<string>();
            string pathHeader = "/files/pdf_files/" + SnowFlakeSingle.Instance.NextId() + "/";
            if (!Directory.Exists("wwwroot" + pathHeader))
                Directory.CreateDirectory("wwwroot" + pathHeader);

            for (int i = 0; i < ids.Count; i++)
            {
                var entity = await base.QueryById(ids[i]);
                var returnData = await HttpHelper.GetAsync(frontUrl + "/api/Ghrh_ResumeTemplatePreview/" + ids[i]);
                var globalSettings = new GlobalSettings
                {
                    ColorMode = ColorMode.Color,
                    Orientation = Orientation.Portrait,
                    PaperSize = PaperKind.A4,
                    DocumentTitle = entity.StaffName,
                };

                var objectSettings = new ObjectSettings
                {
                    PagesCount = true,
                    HtmlContent = returnData,
                    WebSettings = { DefaultEncoding = "utf-8" },
                };

                var pdf = new HtmlToPdfDocument()
                {
                    GlobalSettings = globalSettings,
                    Objects = { objectSettings }
                };

                var fileBytes = _converter.Convert(pdf);

                var fileName = pathHeader + entity.StaffName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";
                files.Add(_hostingEnvironment.WebRootPath + fileName);
                var ms = new MemoryStream(fileBytes);
                var file = new FormFile(ms, 0, ms.Length, Path.GetFileNameWithoutExtension("wwwroot" + pathHeader), Path.GetFileName("wwwroot" + pathHeader));

                await using (var fs = System.IO.File.Create("wwwroot" + fileName))
                {
                    await file.CopyToAsync(fs);
                    fs.Flush();
                }
            }
            path = $"/files/pdf_files/简历_{DateTime.Now.ToString("yyyyMMddHHmmss")}.zip";
            CreateZip($"{_hostingEnvironment.WebRootPath}{path}", files.ToArray());

            FileHelper.DeleteFolder($"{_hostingEnvironment.WebRootPath}{pathHeader}");
            path = "/Advanced" + path;
        }

        return ServiceResult<string>.OprateSuccess(path);
    }

    public static void CreateZip(string zipPath, string[] filesToZip)
    {
        if (File.Exists(zipPath))
        {
            throw new IOException("File already exists.");
        }

        using (FileStream zipFile = new FileStream(zipPath, FileMode.Create))
        {
            using (ZipArchive zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
            {
                foreach (string file in filesToZip)
                {
                    if (File.Exists(file))
                    {
                        ZipArchiveEntry entry = zipArchive.CreateEntryFromFile(file, Path.GetFileName(file));
                    }
                }
            }
        }
    }
    #endregion

    #region 获取评估单表单信息
    public async Task<ServiceResult<dynamic>> QueryAssessForm(long id, long? orderId = null, [FromBody] ResumeAssessForm assessForm = null)
    {
        dynamic result = new ExpandoObject();

        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult<dynamic>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));
        var order = new Ghrh_InterviewOrder();
        if (orderId is null)
        {
            order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
            if (order == null)
                return ServiceResult<dynamic>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

            orderId = order.Id;
        }
        else
            order = await Db.Ado.SqlQuerySingleAsync<Ghrh_InterviewOrder>($"SELECT * from Ghrh_InterviewOrder where Id={orderId} ");

        var assessConfigs = new List<Ghrh_AssessConfigDto>();

        var assess = await _ghrh_InterviewAssessServices.QuerySingle(x => x.OrderId == orderId);
        if (assess != null)
            assessConfigs = JsonHelper.JsonToObj<List<Ghrh_AssessConfigDto>>(assess.AssessFormContent);
        else
        {
            var data = await _ghrh_AssessConfigServices.QueryFilterPage(new QueryBody()
            {
                pageNum = 1,
                pageSize = 0
            }, null);
            assessConfigs = data.result.DT_TableDataT1;
        }

        #region Header

        var header = new JArray();
        JObject obj = new();
        for (int i = 0; i < 2; i++)
        {
            obj = new();
            obj.Add(new JProperty("title", "标准"));
            obj.Add(new JProperty("field", "Type" + (i + 1) + "Text"));
            obj.Add(new JProperty("align", "center"));
            obj.Add(new JProperty("width", i == 0 ? 80 : 120));
            obj.Add(new JProperty("fixed", null));
            header.Add(obj);
        }

        var records = await _ghrh_InterviewRecordServices.Query(x => x.OrderId == order.Id, "Round ASC");

        for (int i = 0; i < records.Count; i++)
        {
            var x = records[i];
            var staff = await Db.Queryable<Ghra_Staff>().Where(o => o.StaffID == x.StaffId).FirstAsync();
            obj = new();
            obj.Add(new JProperty("title", $"第{x.Round}轮面试\n{staff?.StaffName}\n {x.InterviewTime}"));
            obj.Add(new JProperty("field", "Score" + (i + 1)));
            obj.Add(new JProperty("align", "center"));
            obj.Add(new JProperty("width", 120));
            obj.Add(new JProperty("fixed", null));
            header.Add(obj);
        }

        //obj = new();
        //obj.Add(new JProperty("title", "评价"));
        //obj.Add(new JProperty("field", "EvaluateContent"));
        //obj.Add(new JProperty("align", "center"));
        //obj.Add(new JProperty("width", 120));
        //obj.Add(new JProperty("fixed", null));
        //header.Add(obj);

        result.Header = header;
        #endregion

        #region Body 
        var staffId = GetStaffId();

        var assessDetails = new List<Ghrh_InterviewAssessDetail>();

        if (assess != null)
            assessDetails = await _ghrh_InterviewAssessDetailServices.Query(x => x.InterviewAssessId == assess.Id);
        var body = new JArray();
        assessConfigs.ForEach(x =>
        {
            obj = new();
            obj.Add(new JProperty("Type1", x.ItemClass));
            obj.Add(new JProperty("Type1Text", x.ItemClassLabel));
            obj.Add(new JProperty("Type2", x.Id));
            obj.Add(new JProperty("Type2Text", x.ItemName));


            for (int i = 0; i < records.Count; i++)
            {
                var record = records[i];
                var assessDetail = assessDetails.Where(o => o.AssessConfigId == x.Id.ToString() && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();
                if (assessDetail != null)
                    obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent.ObjToInt()));
                else
                    obj.Add(new JProperty("Score" + (i + 1), null));
                if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                    obj.Add(new JProperty("CanEdit" + (i + 1), true));
                else
                    obj.Add(new JProperty("CanEdit" + (i + 1), false));
            }
            //obj.Add(new JProperty("EvaluateContent", null));
            body.Add(obj);
        });

        obj = new();
        obj.Add(new JProperty("Type1", "TotalScore"));
        obj.Add(new JProperty("Type1Text", "总分"));
        obj.Add(new JProperty("Type2", null));
        obj.Add(new JProperty("Type2Text", null));

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var assessDetail = assessDetails.Where(o => o.AssessConfigId == "TotalScore" && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();
            if (assessDetail != null)
                obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent.ObjToInt()));
            else
                obj.Add(new JProperty("Score" + (i + 1), null));
            if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                obj.Add(new JProperty("CanEdit" + (i + 1), true));
            else
                obj.Add(new JProperty("CanEdit" + (i + 1), false));
        }
        body.Add(obj);

        obj = new();
        obj.Add(new JProperty("Type1", "EvaluateContent"));
        obj.Add(new JProperty("Type1Text", "总体评价"));
        obj.Add(new JProperty("Type2", null));
        obj.Add(new JProperty("Type2Text", null));

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var assessDetail = assessDetails.Where(o => o.AssessConfigId == "EvaluateContent" && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();
            if (assessDetail != null)
                obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent));
            else
                obj.Add(new JProperty("Score" + (i + 1), null));
            if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                obj.Add(new JProperty("CanEdit" + (i + 1), true));
            else
                obj.Add(new JProperty("CanEdit" + (i + 1), false));
        }
        body.Add(obj);

        obj = new();
        obj.Add(new JProperty("Type1", "IsPass"));
        obj.Add(new JProperty("Type1Text", "面试结果"));
        obj.Add(new JProperty("Type2", null));
        obj.Add(new JProperty("Type2Text", null));

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var assessDetail = assessDetails.Where(o => o.AssessConfigId == "IsPass" && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();
            if (assessDetail != null)
                obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent.ObjToInt()));
            else
                obj.Add(new JProperty("Score" + (i + 1), null));
            if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                obj.Add(new JProperty("CanEdit" + (i + 1), true));
            else
                obj.Add(new JProperty("CanEdit" + (i + 1), false));
        }
        body.Add(obj);

        result.Body = body;
        #endregion

        #region Header

        var printHeader = new JArray();
        obj = new();
        for (int i = 0; i < 2; i++)
        {
            obj = new();
            obj.Add(new JProperty("title", "标准"));
            obj.Add(new JProperty("field", "Type" + (i + 1) + "Text"));
            obj.Add(new JProperty("align", "center"));
            obj.Add(new JProperty("width", i == 0 ? 80 : 120));
            obj.Add(new JProperty("fixed", null));
            printHeader.Add(obj);
        }

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var staff = await Db.Queryable<Ghra_Staff>().Where(o => o.StaffID == record.StaffId).FirstAsync();
            obj = new();
            obj.Add(new JProperty("title", $"第{record.Round}轮面试\n{staff?.StaffName}\n {record.InterviewTime}"));
            obj.Add(new JProperty("field", "Score" + (i + 1)));
            obj.Add(new JProperty("align", "center"));
            obj.Add(new JProperty("width", 120));
            obj.Add(new JProperty("fixed", null));

            if (assessDetails.Where(o => o.StaffId == record.StaffId && o.InterviewRecordId == record.Id && o.Status == 1).Any())
                printHeader.Add(obj);
        }
        result.PrintHeader = printHeader;
        #endregion

        #region 打印Body  
        var printBody = new JArray();
        assessConfigs.ForEach(x =>
        {
            obj = new();
            obj.Add(new JProperty("Type1", x.ItemClass));
            obj.Add(new JProperty("Type1Text", x.ItemClassLabel));
            obj.Add(new JProperty("Type2", x.Id));
            obj.Add(new JProperty("Type2Text", x.ItemName));


            for (int i = 0; i < records.Count; i++)
            {
                var record = records[i];
                var assessDetail = assessDetails.Where(o => o.AssessConfigId == x.Id.ObjToString() && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();
                if ((assessDetail != null && assessDetail.Status != 1 && assessDetail.InterviewRecordId == record.Id) || assessDetail is null)
                    continue;

                if (assessDetail != null)
                    obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent.ObjToInt()));
                else
                    obj.Add(new JProperty("Score" + (i + 1), null));
                if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                    obj.Add(new JProperty("CanEdit" + (i + 1), true));
                else
                    obj.Add(new JProperty("CanEdit" + (i + 1), false));
            }
            //obj.Add(new JProperty("EvaluateContent", null));
            printBody.Add(obj);
        });

        obj = new();
        obj.Add(new JProperty("Type1", "TotalScore"));
        obj.Add(new JProperty("Type1Text", "总分"));
        obj.Add(new JProperty("Type2", null));
        obj.Add(new JProperty("Type2Text", null));

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var assessDetail = assessDetails.Where(o => o.AssessConfigId == "TotalScore" && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();

            if ((assessDetail != null && assessDetail.Status != 1 && assessDetail.InterviewRecordId == record.Id) || assessDetail is null)
                continue;

            if (assessDetail != null)
                obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent.ObjToInt()));
            else
                obj.Add(new JProperty("Score" + (i + 1), null));
            if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                obj.Add(new JProperty("CanEdit" + (i + 1), true));
            else
                obj.Add(new JProperty("CanEdit" + (i + 1), false));
        }
        printBody.Add(obj);

        obj = new();
        obj.Add(new JProperty("Type1", "EvaluateContent"));
        obj.Add(new JProperty("Type1Text", "总体评价"));
        obj.Add(new JProperty("Type2", null));
        obj.Add(new JProperty("Type2Text", null));

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var assessDetail = assessDetails.Where(o => o.AssessConfigId == "EvaluateContent" && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();
            if ((assessDetail != null && assessDetail.Status != 1 && assessDetail.InterviewRecordId == record.Id) || assessDetail is null)
                continue;


            if (assessDetail != null)
                obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent));
            else
                obj.Add(new JProperty("Score" + (i + 1), null));
            if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                obj.Add(new JProperty("CanEdit" + (i + 1), true));
            else
                obj.Add(new JProperty("CanEdit" + (i + 1), false));
        }
        printBody.Add(obj);

        obj = new();
        obj.Add(new JProperty("Type1", "IsPass"));
        obj.Add(new JProperty("Type1Text", "面试结果"));
        obj.Add(new JProperty("Type2", null));
        obj.Add(new JProperty("Type2Text", null));

        for (int i = 0; i < records.Count; i++)
        {
            var record = records[i];
            var assessDetail = assessDetails.Where(o => o.AssessConfigId == "IsPass" && o.StaffId == record.StaffId && o.InterviewRecordId == record.Id).FirstOrDefault();

            if ((assessDetail != null && assessDetail.Status != 1 && assessDetail.InterviewRecordId == record.Id) || assessDetail is null)
                continue;


            if (assessDetail != null)
                obj.Add(new JProperty("Score" + (i + 1), assessDetail.AssessContent.ObjToInt() == 1 ? "通过" : "不通过"));
            else
                obj.Add(new JProperty("Score" + (i + 1), null));
            if ((record.StaffId == staffId && record.Round == order.Round) || assessForm.ghrOrEss == "ghr")
                obj.Add(new JProperty("CanEdit" + (i + 1), true));
            else
                obj.Add(new JProperty("CanEdit" + (i + 1), false));
        }
        printBody.Add(obj);

        result.PrintBody = printBody;
        #endregion

        return ServiceResult<dynamic>.OprateSuccess("查询成功", result);
    }
    #endregion

    #region 面试评估
    public async Task<ServiceResult> AssessInterview(long id, int status, List<Dictionary<string, object>> input, string source)
    {
        if (status > 1 || status < 0)
            status = 0;
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        if (order == null)
            return ServiceResult.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));

        //【简历库-录用-已发offer】配置按钮
        var config = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "ESS_Recruit_Assess_Mode").FirstAsync();

        string assessMode = config?.ConfigValue ?? "OR";

        long interviewAssessId = 0;
        var assess = await _ghrh_InterviewAssessServices.QuerySingle(x => x.OrderId == order.Id);
        if (assess is null)
        {
            var data = await _ghrh_AssessConfigServices.QueryFilterPage(new QueryBody()
            {
                pageNum = 1,
                pageSize = 0
            }, null);
            var assessConfigs = data.result.DT_TableDataT1;

            interviewAssessId = await _ghrh_InterviewAssessServices.Add(
                new InsertGhrh_InterviewAssessInput()
                {
                    OrderId = order.Id,
                    AssessTime = DateTime.Now,
                    AssessFormContent = JsonHelper.ObjToJson(assessConfigs)
                });
        }
        else
            interviewAssessId = assess.Id;

        var staffId = GetStaffId();
        var records = await _ghrh_InterviewRecordServices.Query(x => x.OrderId == order.Id, "Round ASC");

        var isPass = false;
        var evaluateContent = string.Empty;
        await _ghrh_InterviewAssessDetailServices.Delete(x => x.InterviewAssessId == interviewAssessId && x.StaffId == staffId);
        for (int i = 0; i < input.Count; i++)
        {
            var dic = input[i];
            var assessConfigId = dic["Type2"];
            if (assessConfigId is null)
                assessConfigId = dic["Type1"];

            for (int j = 0; j < records.Count; j++)
            {
                if (dic["CanEdit" + (j + 1)].ObjToBool() != true)
                    continue;
                var assessContent = dic["Score" + (j + 1)].ObjToString();
                if (records[j].Round == order.Round && assessConfigId.ObjToString() == "IsPass")
                {
                    if (assessContent == "1")
                    {
                        isPass = true;
                        records[j].IsPass = true;
                    }
                }
                if (records[j].Round == order.Round && assessConfigId.ObjToString() == "EvaluateContent")
                {
                    evaluateContent = assessContent;
                    records[j].InterviewResult = isPass == records[j].IsPass == false ? await QueryLangValue("F_ResumeMaintenance_0190", "面试不通过") : await QueryLangValue("F_ResumeMaintenance_0191", "面试通过");
                    records[j].InterviewResultRemark = evaluateContent;
                }
                records[j].AssessTime = DateTime.Now;
                await _ghrh_InterviewAssessDetailServices.Add(new InsertGhrh_InterviewAssessDetailInput()
                {
                    InterviewAssessId = interviewAssessId,
                    StaffId = records[j].StaffId,
                    InterviewRecordId = records[j].Id,
                    AssessConfigId = assessConfigId.ObjToString(),
                    AssessContent = assessContent,
                    Status = status
                });
            }
        }

        if (status == 1)
        {
            //if (order.Status == DIC_INTERVIEW_ORDER_STATUS.InterviewFail || order.Status == DIC_INTERVIEW_ORDER_STATUS.HasInterview)
            //    return ServiceResult.OprateSuccess();

            string Status = isPass == false ? DIC_INTERVIEW_ORDER_STATUS.InterviewFail : DIC_INTERVIEW_ORDER_STATUS.HasInterview;

            if (assessMode != "OR" && source == "ess")
                Status = DIC_INTERVIEW_ORDER_STATUS.WaitInterview;
            string InterviewResult = isPass == false ? await QueryLangValue("F_ResumeMaintenance_0190", "面试不通过") : await QueryLangValue("F_ResumeMaintenance_0191", "面试通过");

            if (entity.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
            {
                order.Status = Status;
                order.IsPass = isPass;
                order.InterviewResult = InterviewResult;
                order.InterviewContent = evaluateContent;
                order.InterviewResultRemark = evaluateContent;
                await _ghrh_InterviewOrderServices.Update(order, ["Status", "IsPass", "InterviewResult", "InterviewContent", "InterviewResultRemark"]);
            }
            if (source == "ess")
            {
                var record = records.Where(x => x.Round == order.Round && x.OrderId == order.Id && x.StaffId == GetStaffId()).FirstOrDefault();
                if (record != null)
                {
                    record.Status = Status;
                    await _ghrh_InterviewRecordServices.Update(record, ["AssessTime", "Status", "IsPass", "InterviewResult", "InterviewResultRemark", "UpdateTime", "UpdateBy"]);
                }
            }
            else
            {
                records.ForEach(record =>
                {
                    record.Status = Status;
                });
                await _ghrh_InterviewRecordServices.Update(records, ["AssessTime", "Status", "IsPass", "InterviewResult", "InterviewResultRemark", "UpdateTime", "UpdateBy"]);
            }

            #region 处理人力需求单 面试人数
            if (order.RequestId.IsNotEmptyOrNull() && entity.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
            {
                var sql = $@"UPDATE Ghrh_HumanRequest
                            SET InterviewCount = ISNULL (InterviewCount, 0) + 1
                            WHERE Id = {order.RequestId}";
                await Db.Ado.ExecuteCommandAsync(sql);
            }
            #endregion

            if (entity.Status == DIC_INTERVIEW_ORDER_STATUS.WaitInterview)
                await UpdateResumeStatus(entity, Status);

            if (source == "ess")
            {
                var content = await QueryLangValue("F_ResumeMaintenance_0194",
                    "提交了面试评估，轮数：{0}，是否通过：{1}，内容：{2}！",
                    order.Round,
                    isPass == true ? await QueryLangValue("F_ResumeMaintenance_0194", "通过") : await QueryLangValue("F_ResumeMaintenance_0193", "不通过"),
                    evaluateContent);
                await LogRecord(order.Id, content, id, null, "AssessInterview");
            }
            else
            {
                var content = await QueryLangValue("F_ResumeMaintenance_0194",
                     "【后台】提交了面试评估，轮数：{0}，是否通过：{1}，内容：{2}！",
                     order.Round,
                     isPass == true ? await QueryLangValue("F_ResumeMaintenance_0195", "通过") : await QueryLangValue("F_ResumeMaintenance_0193", "不通过"),
                     evaluateContent);
                await LogRecord(order.Id, content, id, null, "AssessInterview");

            }
        }
        else
        {
            if (source == "ess")
            {
                var content = await QueryLangValue("F_ResumeMaintenance_0194",
                    "暂存了面试评估，轮数：{0}，是否通过：{1}，内容：{2}！",
                    order.Round,
                    isPass == true ? await QueryLangValue("F_ResumeMaintenance_0196", "通过") : await QueryLangValue("F_ResumeMaintenance_0193", "不通过"),
                    evaluateContent);
                await LogRecord(order.Id, content, id, null, "TempAssessInterview");
            }
            else
            {
                var content = await QueryLangValue("F_ResumeMaintenance_0194",
                    "【后台】暂存了面试评估，轮数：{0}，是否通过：{1}，内容：{2}！",
                    order.Round,
                    isPass == true ? await QueryLangValue("F_ResumeMaintenance_0197", "通过") : await QueryLangValue("F_ResumeMaintenance_0193", "不通过"),
                    evaluateContent);
                await LogRecord(order.Id, content, id, null, "TempAssessInterview");
            }
        }

        return ServiceResult.OprateSuccess();
    }

    #endregion

    #region 世华OA同步
    public async Task Shihua_OA_Sync()
    {
        try
        {
            var startDate = DateTime.Now.AddDays(-30);

            var list = await Db.Queryable<Ghrz_Shihua_OA_Employment>().Where(x => x.CreateTime >= startDate && x.IsRead == 0).ToListAsync();

            for (int i = 0; i < list.Count; i++)
            {
                var data = list[i];

                if (await Db.Queryable<Ghrh_OfferApplyOrder>().AnyAsync(x => x.IdCardNo == data.IDCardNo))
                    continue;

                var resume = await base.QuerySingle(x => x.IdCardNo == data.IDCardNo);

                if (resume is null)
                    continue;

                var deptId = (await Db.Queryable<Ghro_Dept>().Where(x => x.DeptNo == data.DeptNo).FirstAsync())?.DeptID;
                var titleId = (await Db.Queryable<Ghra_Title>().Where(x => x.TitleNo == data.TitleNo).FirstAsync())?.TitleID;
                var gradeId = (await Db.Queryable<Ghra_Grade>().Where(x => x.GradeNo == data.GradeNo).FirstAsync())?.GradeID;
                var workAddress = (await Db.Queryable<Ghra_Zone>().Where(x => x.ZoneNo == data.ZoneNo).FirstAsync())?.ZoneName;
                var periodMasterId = (await Db.Queryable<Ghrc_PeriodMaster>().Where(x => x.PeriodMasterNo == data.PeriodMasterNo).FirstAsync())?.PeriodMasterID;
                var LegalCompanyID = (await Db.Queryable<Ghra_LegalCompany>().Where(x => x.LegalCompanyNo == data.LegalCompanyNo).FirstAsync())?.LegalCompanyID;

                var items1 = await GetParaList("ResumeChannel");
                data.RecruitFrom = items1.Where(x => x.ParaDetailName == data.RecruitFrom).FirstOrDefault()?.ParaDetailNo ?? data.RecruitFrom;

                var items = new List<Ghrh_OfferApplyOrderSalary>();
                var insert = new Ghrh_OfferApplyOrder()
                {
                    CompanyId = LegalCompanyID,
                    OrderNo = data.WorkNo,
                    WorkNo = data.WorkNo,
                    DeptId = deptId,
                    TitleId = titleId,
                    Channel = data.RecruitFrom,
                    StaffName = data.EmployName,
                    IdCardNo = data.IDCardNo,
                    InDate = data.InDate,
                    StaffType1 = data.StaffType,
                    GradeId = gradeId,
                    WorkAddress = workAddress,
                    ProbationSalary = data.TotalAmount,
                    PeriodMasterId = periodMasterId,
                };
                if (resume != null)
                    insert.ResumeId = resume.Id;
                long id = await Db.Insertable(insert).ExecuteReturnSnowflakeIdAsync();

                #region  写入数据,并判断是否有错误 
                var sql = $"SELECT ISNULL(MAX(id)+1,1) FROM Ghrh_OfferApplyOrder WHERE Id !='{id}'";
                var id1 = await Db.Ado.GetLongAsync(sql);
                sql = $"UPDATE Ghrh_OfferApplyOrder SET Id={id1} WHERE  Id ='{id}'";
                await Db.Ado.ExecuteCommandAsync(sql);
                id = id1;
                #endregion
                #region  处理薪资项目

                var currencyId = await Db.Ado.GetIntAsync("SELECT top 1 CurrencyID FROM Ghrc_Currency where CurrencyNo='RMB'");
                if (data.S02.IsNotEmptyOrNull())
                    if (data.S02 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S02").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S02,
                            CurrencyId = currencyId,
                            Reverse1 = "S02"
                        });
                    }

                if (data.S03.IsNotEmptyOrNull())
                    if (data.S03 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S03").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S03,
                            CurrencyId = currencyId,
                            Reverse1 = "S03"
                        });
                    }
                if (data.S04.IsNotEmptyOrNull())
                    if (data.S04 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S04").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S04,
                            CurrencyId = currencyId,
                            Reverse1 = "S04"
                        });
                    }
                if (data.S05.IsNotEmptyOrNull())
                    if (data.S05 > 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S05").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S05,
                            CurrencyId = currencyId,
                            Reverse1 = "S05"
                        });
                    }
                if (data.S06.IsNotEmptyOrNull())
                    if (data.S06 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S06").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S06,
                            CurrencyId = currencyId,
                            Reverse1 = "S06"
                        });
                    }
                if (data.S21.IsNotEmptyOrNull())
                    if (data.S21 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S21").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S21,
                            CurrencyId = currencyId,
                            Reverse1 = "S21"
                        });
                    }
                if (data.S22.IsNotEmptyOrNull())
                    if (data.S22 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S22").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S22,
                            CurrencyId = currencyId,
                            Reverse1 = "S22"
                        });
                    }
                if (data.S23.IsNotEmptyOrNull())
                    if (data.S23 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S23").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S23,
                            CurrencyId = currencyId,
                            Reverse1 = "S23"
                        });
                    }
                if (data.S66.IsNotEmptyOrNull())
                    if (data.S66 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S66").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S66,
                            CurrencyId = currencyId,
                            Reverse1 = "S66"
                        });
                    }
                if (data.S68.IsNotEmptyOrNull())
                    if (data.S68 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S68").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S68,
                            CurrencyId = currencyId,
                            Reverse1 = "S68"
                        });
                    }
                if (data.S70.IsNotEmptyOrNull())
                    if (data.S70 != 0)
                    {
                        var salaryItemId = (await Db.Queryable<Ghrc_SalaryItem>().Where(x => x.SalaryItemNo == "S70").FirstAsync())?.SalaryItemID;
                        items.Add(new Ghrh_OfferApplyOrderSalary()
                        {
                            Id = SnowFlakeSingle.Instance.NextId(),
                            OrderId = id,
                            SalaryItemId = salaryItemId,
                            Amount = data.S70,
                            CurrencyId = currencyId,
                            Reverse1 = "S70"
                        });
                    }
                #endregion
                if (items.Any())
                    await Db.Insertable(items).ExecuteCommandAsync();

                await Db.Updateable<Ghrz_Shihua_OA_Employment>()
                    .SetColumns(it => new Ghrz_Shihua_OA_Employment() { IsRead = 1 })
                   .Where(it => it.EmploymentID == data.EmploymentID)
                   .ExecuteCommandAsync();
            }
        }
        catch (Exception E)
        {

        }
    }
    #endregion

    #region 面试日志
    public async Task<ServiceResult<List<ResumeLog>>> Log(long id)
    {
        var logs = new List<ResumeLog>();
        var sql = $"Select * from Ghrh_InterviewOrder where ResumeId='{id}' Order by CreateTime desc ";
        var orders = await Db.Ado.SqlQueryAsync<Ghrh_InterviewOrder>(sql);
        for (int i = 0; i < orders.Count; i++)
        {
            var items = new List<ResumeLogItem>();
            var interviewLogs = await _ghrh_InterviewLogServices.Query(x => x.InterviewOrderId == orders[i].Id, "CreateTime asc");
            for (int j = 0; j < interviewLogs.Count; j++)
            {
                var interviewLog = interviewLogs[j];
                var attachments = new List<Ghrs_Attachment>();

                var item = new ResumeLogItem()
                {
                    Time = interviewLog.CreateTime,
                    Operator = interviewLog.StaffName ?? interviewLog.UserName,
                    Content = interviewLog.RemarkSz,
                    Source = interviewLog.Source,
                    Attachments = attachments,
                };
                switch (interviewLog.Source)
                {
                    case "Recommended":
                        attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == interviewLog.InterviewOrderId.ObjToString()).ToListAsync();
                        item.RequestId = interviewLog.Source == "Recommended" ? orders[i].RequestId : null;
                        break;
                    case "ScheduleInterview":
                        attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == interviewLog.InterviewRecordId.ObjToString()).ToListAsync();
                        break;
                    case "ApplyOffer":
                        if (interviewLog.ReverseI2 != null && interviewLog.ReverseI2 != 0)
                        {
                            var order = await Db.Queryable<Ghrh_OfferApplyOrder>().FirstAsync(x => x.Id == interviewLog.ReverseI2);
                            if (order != null)
                            {
                                item.WorkId = order.WorkID;
                                item.FlowSignID = await Db.Ado.GetIntAsync($"Select FlowSignID from Ghrw_FlowSign where  WorkID ={order.WorkID} and IsBegin=1");
                            }
                        }

                        break;
                    case "SendOffer":
                    case "OfferDownLoad":
                        if (interviewLog.Reverse1.IsNotEmptyOrNull())
                            item.OfferFileUrl = interviewLog.Reverse1;
                        break;
                    //case "AssessInterview":
                    //    if (interviewLog.Reverse1.IsNotEmptyOrNull())
                    //        item.OfferFileUrl = interviewLog.Reverse1;
                    //    break;
                    default:
                        break;
                }
                item.Attachments = attachments;
                items.Add(item);
            }
            logs.Add(new ResumeLog()
            {
                OrderId = orders[i].Id,
                FirstRecommendTime = orders[i].RecommendTime,
                Items = items
            });

        }

        return ServiceResult<List<ResumeLog>>.OprateSuccess("查询成功！", logs);

    }
    #endregion

    #region Offer下载
    public async Task<ServiceResult<string>> OfferDownLoad(long id, long templateId, Dictionary<string, string> extFields)
    {
        var entity = await base.QueryById(id);
        if (entity == null)
            return ServiceResult<string>.OprateFailed(await QueryLangValue("F_ResumeMaintenance_0149", "无效的简历ID！"));
        var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
        var content = (await _ghrh_TemplateServices.Preview(id, templateId, extFields)).Data;

        #region 生成Offer PDf
        entity.OfferFileUrl = await GenerateOffer(entity, content);
        await Update(entity, ["OfferFileUrl"]);

        await _ghrh_InterviewLogServices.Add(new InsertGhrh_InterviewLogInput()
        {
            InterviewOrderId = order.Id,
            ResumeId = id,
            StaffId = App.User.StaffId,
            StaffName = App.User.StaffName + "（" + App.User.StaffNo + "）",
            Source = "OfferDownLoad",
            Reverse1 = entity.OfferFileUrl,
            UserId = App.User.ID,
            UserName = App.User.Name,
            RemarkSz = await QueryLangValue("F_ResumeMaintenance_0198", "下载Offer！"),
        });

        #endregion

        return ServiceResult<string>.OprateSuccess("获取成功", entity.OfferFileUrl);
    }
    #endregion

    #region Excel导入
    public override async Task<ServiceResult<string>> DownloadExcel(string menuName)
    {
        var physicsPath = $"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}wwwroot";
        var path = $"{$"{Path.DirectorySeparatorChar}files{Path.DirectorySeparatorChar}ExcelTemplate{Path.DirectorySeparatorChar}"}";
        if (!Directory.Exists(physicsPath + path))
            Directory.CreateDirectory(physicsPath + path);

        Type entityType = typeof(Ghre_Teacher);

        DataTable dt = new DataTable("TempTable");

        var formColumns = await QueryResumeFormColumn();
        string fileName = path + SnowFlakeSingle.instance.getID() + ".xlsx";

        formColumns = formColumns.Where(x => x.GroupType == "Base").ToList();
        formColumns.ForEach(x =>
        {
            if (!dt.Columns.Contains(x.ColumnNameDesc))
                dt.Columns.Add(x.ColumnNameDesc, typeof(string));
        });
        var formColumns1 = formColumns.Select(x => new QueryExportColumn()
        {
            sortNum = x.SortNo,
            label = x.ColumnNameDesc,
            field = x.ColumnName,
            dataSource = x.DataSource,
            required = x.IsRequired == true ? "true" : "false",
            dataType = x.DataType,
            elementType = x.ColumnType,
            dataSourceType = x.DataSourceType,
            dataSourceId = null
        }).ToList();
        var dr = dt.NewRow();
        dt.Rows.Add(dr);
        //NPOIHelper.ExportExcel(dt, null, "简历导入模板", physicsPath + fileName);

        var physicsPath1 = physicsPath + fileName;
        physicsPath1 = ReportHelper.ImportTemplate(Db, formColumns1, dt, menuName);

        //if (dataSourceLists.Any())
        //    physicsPath1 = physicsPath + path + newFileName;
        var result = ServiceResult<string>.OprateSuccess("简历导入模板_" + DateTimeHelper.ConvertToSecondString1(DateTime.Now) + ".xlsx", physicsPath1);
        return result;
    }
    public override async Task<ServiceResult<ExcelData>> ImportExcel(IFormFile file, string menuName = null, long? MasterId = null)
    {
        var data = new ExcelData();
        long id = SnowFlakeSingle.instance.getID();
        var physicsPath = $"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}wwwroot";
        var path = $"{$"{Path.DirectorySeparatorChar}files{Path.DirectorySeparatorChar}import{Path.DirectorySeparatorChar}{id}{Path.DirectorySeparatorChar}"}";
        if (!Directory.Exists(physicsPath + path))
            Directory.CreateDirectory(physicsPath + path);

        var filepath = physicsPath + path + file.FileName;
        using (var stream = File.Create(filepath))
        {
            await file.CopyToAsync(stream);
        }
        string extension = Path.GetExtension(filepath);

        bool isExistError = false;
        var id1 = SnowFlakeSingle.instance.getID();
        string errorFileName = path + SnowFlakeSingle.instance.getID() + extension;

        var dt = NPOIHelper.ImportExcel(filepath, "导入数据");
        if (dt.Columns["Comments"] == null)
            dt.Columns.Add("Comments", typeof(string));

        var formColumns = await QueryResumeFormColumn();

        formColumns = formColumns.Where(x => x.GroupType == "Base")
              .ToList();
        for (int i = 0; i < dt.Rows.Count; i++)
        {
            var comments = new List<string>();
            bool isContinue = false;

            var dict = new Dictionary<string, object>
                {
                { "Id",  SnowFlakeSingle.Instance.NextId() },
                { "CreateBy", App.User.ID },
                { "CreateTime", DateTime.Now },
                { "UpdateTime", DateTime.Now }
              };


            formColumns.ForEach(x =>
            {
                if (!dt.Columns.Contains(x.ColumnNameDesc))
                {
                    comments.Add("未查询到【" + x.ColumnNameDesc + "】列！");
                    data.ErrorCount++;
                    dt.Rows[i]["Comments"] = string.Join(";", comments.Select(a => a));
                    isExistError = true;
                    isContinue = true;
                }
            });

            if (isContinue)
                continue;

            for (int j = 0; j < formColumns.Count; j++)
            {
                var column = formColumns[j];
                var value = dt.Rows[i][column.ColumnNameDesc].ToString();
                if (column.IsRequired == true && value.IsNullOrEmpty())
                {
                    comments.Add(column.ColumnNameDesc + "不能为空！");
                    data.ErrorCount++;
                    dt.Rows[i]["Comments"] = string.Join(";", comments.Select(a => a));
                    isExistError = true;
                    continue;
                }
                if (column.DataSourceType.IsNullOrEmpty() && value.IsNotEmptyOrNull())
                {
                    if (column.DataType == "int" || column.DataType == "decimal")
                    {
                        try
                        {
                            Convert.ToInt64(value);
                        }
                        catch (Exception)
                        {
                            comments.Add(column.ColumnNameDesc + "无效的数字类型！");
                            data.ErrorCount++;
                            dt.Rows[i]["Comments"] = string.Join(";", comments.Select(a => a));
                            isExistError = true;
                            continue;
                        }
                    }
                    else if (column.DataType == "date")
                    {
                        try
                        {
                            Convert.ToDateTime(value);
                        }
                        catch (Exception)
                        {
                            comments.Add(column.ColumnNameDesc + "无效的日期类型！");
                            data.ErrorCount++;
                            dt.Rows[i]["Comments"] = string.Join(";", comments.Select(a => a));
                            isExistError = true;
                            continue;
                        }
                    }
                }
                if (value.IsNotEmptyOrNull())
                {
                    if (column.DataSource1.IsNotEmptyOrNull() && column.DataSourceType.IsNotEmptyOrNull())
                    {

                        if (column.DataSourceType == "CommonList")
                        {
                            var dataSource = column.DataSource1.Replace("CommonList_", "");
                            var commonSql = await Db.Queryable<Ghrs_ListCommonSql>().Where(x => x.ListCommonSqlId == column.DataSource1.ObjToInt() || x.ListCommonSqlNo == dataSource).FirstAsync();

                            if (commonSql != null)
                            {
                                string sql = @$"SELECT [value]
                                FROM ({commonSql.SelectSql}) A
                                WHERE label = '{value}'";
                                sql = sql.Replace("{@LangID}", "1");
                                var id2 = await Db.Ado.GetLongAsync(sql);

                                dict.Add(column.ColumnName, id2);
                            }
                        }
                        else if (column.DataSourceType == "ParaDetailNo")
                        {
                            var sql = @$"SELECT ParaDetailNo
                                FROM Ghrs_ParaDetail
                                WHERE     ParaMasterId IN (SELECT ParaMasterId
                                                           FROM Ghrs_ParaMaster
                                                           WHERE ParaMasterId = {column.DataSource1.ObjToInt()})
                                      AND IsEnable = 1
                                      AND ParaDetailName = '{value}'";
                            var id2 = await Db.Ado.GetStringAsync(sql);

                            dict.Add(column.ColumnName, id2);
                        }
                    }
                    else
                        dict.Add(column.ColumnName, value);
                }
            }

            if (dict.ContainsKey("IdCardNo"))
            {
                if (await base.AnyAsync(x => x.IdCardNo == dict["IdCardNo"].ToString()))
                {
                    comments.Add("证件号码不能重复！");
                    data.ErrorCount++;
                    dt.Rows[i]["Comments"] = string.Join(";", comments.Select(a => a));
                    isExistError = true;
                    continue;
                }
            }

            if (isExistError)
                continue;
            try
            {
                dict.Add("Status", "WaitRecommended");
                dict.Add("ApplicationStatus", "Submit");
                await Db.Insertable(dict).AS("Ghrh_Resume").ExecuteCommandAsync();
                data.SuccessCount++;
                //data.SuccessCount += list.Count;
            }
            catch (Exception E)
            {
                comments.Add(E.Message);
                data.ErrorCount++;
                dt.Rows[i]["Comments"] = string.Join(";", comments.Select(a => a));
                isExistError = true;
                continue;
            }
        }
        if (isExistError)
        {
            NPOIHelper.ExportExcel(dt, null, "简历导入模板", physicsPath + errorFileName);
            data.filePath = "/Advanced" + errorFileName;
        }

        return ServiceResult<ExcelData>.OprateSuccess("导入成功！", data);
    }
    #endregion

    #region 招聘工作台初始化接口
    public async Task<ServiceResult<JArray>> QueryWorkstationInit(int langId, string menuName)
    {

        var columns = new JArray();
        var Detail = new JObject { };
        var item = new JObject { };

        string peopleText = await QueryLangValue("F_ResumeMaintenance_0199", "人");

        if (menuName == "F_ESS_RecruitmentBoard")
        {

            var group = await Db.Queryable<Ghrh_InterviewRecord>()
            .Where(x => x.StaffId == App.User.StaffId).GroupBy(x => x.Status)
            .Select(g => new
            {
                g.Status,
                Count = SqlFunc.AggregateCount(g.Id)
            }).ToListAsync();

            #region 待筛选
            Detail = new JObject
            {
                new JProperty("Count", group.Where(x=>x.Status=="HasRecommended").FirstOrDefault()?.Count??0),
                new JProperty("Unit", peopleText),
                new JProperty("MenuName","F_ESS_Candidate"),
                new JProperty("TabKey","HasRecommended"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 0),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112701"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title",await QueryLangValue("F_ResumeMaintenance_0200", "待筛选")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 待预约 
            Detail = new JObject
            {
                new JProperty("Count", group.Where(x=>x.Status=="WaitAppointment").FirstOrDefault()?.Count??0),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ESS_Candidate"),
                new JProperty("TabKey","WaitAppointment"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 2),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112702"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0201", "待预约")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 待面试 
            Detail = new JObject
            {
                new JProperty("Count", group.Where(x=>x.Status=="WaitInterview").FirstOrDefault()?.Count??0),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ESS_Interview"),
                new JProperty("TabKey","WaitInterview"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 4),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112703"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0202", "待面试")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 已面试 
            var sum = group.Where(x => x.Status == "HasInterview" || x.Status == "WaitSendOffer" || x.Status == "HasSendOffer").Sum(x => x.Count);
            Detail = new JObject
            {
                new JProperty("Count", sum),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ESS_Interview"),
                new JProperty("TabKey","HasInterview"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 6),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112704"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0203", "已面试")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 已录用 
            var group1 = await Db.Queryable<Ghrh_Resume>()
             .GroupBy(x => x.Status).Select(g => new
             {
                 Status = g.Status,
                 Count = SqlFunc.AggregateCount(g.Id)
             }).ToListAsync();
            Detail = new JObject {
                new JProperty("Count", group1.Where(x=>x.Status=="HasOffer").FirstOrDefault()?.Count??0),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ESS_Hire"),
                new JProperty("TabKey","HasOffer"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 8),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112705"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0204", "已录用")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion
        }
        else
        {
            var group = await Db.Queryable<Ghrh_Resume>()
              .GroupBy(x => x.Status).Select(g => new
              {
                  Status = g.Status,
                  Count = SqlFunc.AggregateCount(g.Id)
              }).ToListAsync();
            #region 已推荐

            Detail = new JObject
            {
                new JProperty("Count", group.Where(x=>x.Status=="HasRecommended").FirstOrDefault()?.Count??0),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ResumeMaintenance"),
                new JProperty("TabKey","HasRecommended"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 0),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112701"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0205", "已推荐")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 待预约 
            Detail = new JObject
            {
                new JProperty("Count", group.Where(x=>x.Status=="WaitAppointment").FirstOrDefault()?.Count??0),
                new JProperty("Unit", peopleText),
                new JProperty("MenuName","F_ResumeMaintenance"),
                new JProperty("TabKey","WaitAppointment"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 2),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112702"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0206", "待预约")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 待面试 
            Detail = new JObject
            {
                new JProperty("Count", group.Where(x=>x.Status=="WaitInterview").FirstOrDefault()?.Count??0),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ResumeMaintenance"),
                new JProperty("TabKey","WaitInterview"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 4),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112703"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0202", "待面试")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 已面试 
            var sum = group.Where(x => x.Status == "HasInterview" || x.Status == "WaitSendOffer" || x.Status == "HasSendOffer").Sum(x => x.Count);
            Detail = new JObject
            {
                new JProperty("Count", sum),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ResumeMaintenance"),
                new JProperty("TabKey","HasInterview"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 6),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112704"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0203", "已面试")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion

            #region 已报到 
            Detail = new JObject {
                new JProperty("Count", group.Where(x=>x.Status=="HasOffer").FirstOrDefault()?.Count??0),
                new JProperty("Unit",peopleText),
                new JProperty("MenuName","F_ResumeMaintenance"),
                new JProperty("TabKey","HasOffer"),
            };
            item = new JObject
            {
                new JProperty("CanMore", 1),
                new JProperty("ColNum", 8),
                new JProperty("RowNum", 0),
                new JProperty("Height", 17.2),
                new JProperty("Width", 2),
                new JProperty("Id", "112705"),
                new JProperty("CardType", "RecruitmentCountCard"),
                new JProperty("IsStatic", "0"),
                new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0207", "已报到")),
                new JProperty("Detail",Detail),
            };
            columns.Add(item);
            #endregion
        }

        #region 招聘统计 
        Detail = new JObject
        {
        };
        item = new JObject
        {
            new JProperty("CanMore", 0),
            new JProperty("ColNum", 0),
            new JProperty("RowNum", 10),
            new JProperty("Height", 43),
            new JProperty("Width", 10),
            new JProperty("Id", "112706"),
            new JProperty("CardType", "RecruitStatisticsChart"),
            new JProperty("IsStatic", "0"),
            new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0208", "招聘统计")),
            new JProperty("Detail",Detail),
        };
        columns.Add(item);
        #endregion

        #region 招聘平均耗费时长 
        Detail = new JObject
        {
        };
        item = new JObject
        {
            new JProperty("CanMore", 0),
            new JProperty("ColNum", 0),
            new JProperty("RowNum", 20),
            new JProperty("Height", 45),
            new JProperty("Width", 10),
            new JProperty("Id", "112707"),
            new JProperty("CardType", "AverageTimeConsumption"),
            new JProperty("IsStatic", "0"),
            new JProperty("Title", await QueryLangValue("F_ResumeMaintenance_0209", "招聘平均耗费时长")),
            new JProperty("Detail",Detail),
        };
        columns.Add(item);
        #endregion

        return ServiceResult<JArray>.OprateSuccess("获取成功", columns);
    }
    #endregion

    #region 招聘工作台招聘统计
    public async Task<ServiceResult<dynamic>> QueryWorkStationStatisticsChart(int langId = 1)
    {

        dynamic data = new ExpandoObject();
        var series = new JArray();
        var XAxis = new List<string>();
        var date = DateTime.Now.AddMonths(-11);
        var data1 = new List<int>();
        var data2 = new List<int>();
        var data3 = new List<decimal>();

        var random = new Random();
        for (int i = 1; i <= 12; i++)
        {
            var date1 = DateTimeHelper.ConvertToMonthString(date);
            XAxis.Add(date1);

            var group = await Db.Queryable<Ghrh_InterviewOrder>()
                .Where(x => x.RecommendTime != null && x.RecommendTime.Value.Date.Month == date.Month && x.RecommendTime.Value.Date.Year == date.Year)
                .Select(g => new
                {
                    g.Status
                }).ToListAsync();

            var count = group.Count;
            var count1 = group.Where(x => x.Status == "HasOffer").Count();
            data1.Add(count);
            data2.Add(count1);
            decimal count3 = 0;
            if (count1 > 0 && count > 0)
                count3 = ((decimal)count1 / count) * 100;
            data3.Add(StringHelper.TrimDecimal(count3, 2));
            date = date.AddMonths(1);
        }

        var item = new JObject
        {
            new JProperty("color", "rgba(84, 112, 198, 1)"),
            new JProperty("data", data1),
            new JProperty("name", await QueryLangValue("F_ResumeMaintenance_0210", "面试人数")),
        };
        series.Add(item);

        item = new JObject
        {
            new JProperty("color", "rgba(145, 204, 117, 1)"),
            new JProperty("data", data2),
            new JProperty("name", await QueryLangValue("F_ResumeMaintenance_0211", "录用人数")),
        };
        series.Add(item);

        item = new JObject
        {
            new JProperty("color", "rgba(255, 147, 0, 1)"),
            new JProperty("data", data3),
            new JProperty("name", await QueryLangValue("F_ResumeMaintenance_0212", "面试成功率")),
        };
        series.Add(item);

        data.XAxis = XAxis;
        data.series = series;
        return ServiceResult<dynamic>.OprateSuccess("获取成功", data);
    }
    #endregion

    #region 录用名单
    public async Task<ServicePageResult<Ghrh_ResumeDto>> QueryHireList(QueryBody filter)
    {
        if (string.IsNullOrWhiteSpace(filter.orderBy))
            filter.orderBy = "InDate DESC";

        if (filter.pageSize == 0)
            filter.pageSize = 10000;


        var countSql = @$" SELECT COUNT(1) FROM  Ghrh_ResumeHireList_V";
        var sql1 = @$"DECLARE @langId   INT = {filter.langId};";
        var sql = @$" SELECT * 
                    FROM Ghrh_ResumeHireList_V A";

        string conditions = " WHERE 1=1 ";


        if (filter.jsonParam != null)
            foreach (JProperty jProperty in filter.jsonParam.Properties())
            {
                var name = jProperty.Name;
                var value = jProperty.Value.ToString();
                if (name == "page" || name == "pageSize")
                    continue;
                if (!string.IsNullOrWhiteSpace(value))
                    conditions = DealConditions(conditions, name, value);
            }

        sql += conditions;
        countSql += conditions;
        int total = await Db.Ado.GetIntAsync(countSql);

        sql = "SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY " + filter.orderBy + ") NUM FROM (SELECT * FROM (" + sql + " ";
        sql += ") A ) B ) C";

        sql += " WHERE NUM <= " + filter.pageNum * filter.pageSize + " AND NUM >" + (filter.pageNum - 1) * filter.pageSize;
        sql = sql1 + sql;
        var entitys = await Db.Ado.SqlQueryAsync<Ghrh_ResumeDto>(sql);

        var result = new ServicePageResult<Ghrh_ResumeDto>(filter.pageNum, total, filter.pageSize, entitys);

        var list = result.result.DT_TableDataT1;
        var tagIds = new List<long>();

        tagIds = tagIds.Distinct().ToList();
        var tags = await Db.Queryable<Ghrh_ResumeTag>().Where(x => tagIds.Contains(x.Id)).ToListAsync();
        for (int i = 0; i < list.Count; i++)
        {
            await SetLabel(list[i]);
        }

        return result;
    }

    #endregion

    #region 录用名单导出

    public async Task<ServiceResult<ExcelData>> ExportHireExcel(QueryExport body)
    {
        QueryBody filter = new QueryBody();
        filter.pageNum = 1;
        filter.jsonParam = body.jsonParam;
        filter.pageSize = 1000000;
        filter.langId = body.langId;

        var condition = "1=1";
        if (body.exportSet.SelectRowKeys != null && body.exportSet.SelectRowKeys.Any())
            condition += $" AND Id IN({string.Join(",", body.exportSet.SelectRowKeys)})";

        var data = await QueryHireList(filter);

        string sql = $@"SELECT *
                            FROM Ghrs_PageSettingQuery
                            WHERE     IsEnable = 1
                                  AND PageNo = '{body.menuName}'
                                  AND (defaultHidden = 'false' OR defaultHidden is null)
                            ORDER BY SortNo ASC";

        var columns = DbAccess.QueryList<QueryExportColumn>(sql);

        var fieldDescs = new Dictionary<string, string>();
        if (body.exportSet.ExFields.Any())
            body.exportSet.ExFields.ForEach(x =>
            {
                if (columns.Any(o => o.field == x))
                {
                    var label = columns.FirstOrDefault(o => o.field == x)?.label;
                    if (!fieldDescs.ContainsKey(x))
                        fieldDescs.Add(x, label);

                }
            });
        else
            fieldDescs = columns.ToDictionary(item => item.field, item => item.label);
        var dt = ToDataTable(data.result.DT_TableDataT1, fieldDescs, null);
        // 获取所有列名
        var dtColumns = dt.Columns;

        var id = SnowFlakeSingle.instance.getID();
        var physicsPath = $"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}wwwroot";
        var path = $"{$"{Path.DirectorySeparatorChar}files{Path.DirectorySeparatorChar}export{Path.DirectorySeparatorChar}{id}{Path.DirectorySeparatorChar}"}";
        if (!Directory.Exists(physicsPath + path))
            Directory.CreateDirectory(physicsPath + path);

        path = path + body.exportSet.TitleName + ".xlsx";
        NPOIHelper.ExportExcel(dt, body.exportSet.TitleName, "sheet1", physicsPath + path);

        var result = new ExcelData();
        result.filePath = "/Advanced" + path;
        result.fileName = body.exportSet.TitleName + ".xlsx";
        return ServiceResult<ExcelData>.OprateSuccess("导出成功", result);
    }
    #endregion

    #region 通用方法
    /// <summary>
    /// 记录日志
    /// </summary>
    /// <param name="orderId">面试单ID</param>
    /// <param name="content">内容</param>
    /// <returns></returns>
    public async Task LogRecord(long? orderId, string content, long? resumeId = null, long? recordId = null, string source = null, bool? hasAttachment = null)
    {
        await _ghrh_InterviewLogServices.Add(new InsertGhrh_InterviewLogInput()
        {
            InterviewOrderId = orderId,
            InterviewRecordId = recordId,
            StaffId = App.User.StaffId,
            StaffName = App.User.StaffName + "（" + App.User.StaffNo + "）",
            ResumeId = resumeId,
            Source = source,
            UserId = App.User.ID,
            UserName = App.User.Name,
            RemarkSz = content,
            ReverseI1 = hasAttachment == true ? 1 : 0// 是否有附件
        });
    }
    public async Task UpdateResumeStatus(Ghrh_Resume entity, string Status)
    {
        entity.Status = Status;
        await Update(entity, ["Status", "UpdateTime", "UpdateBy"]);
    }
    public async Task UpdateInterviewOrderStatus(Ghrh_InterviewOrder order, string Status)
    {
        order.Status = Status;
        await _ghrh_InterviewOrderServices.Update(order, ["Status", "UpdateTime", "UpdateBy"]);
    }
    /// <summary>
    /// 修改面试记录状态
    /// </summary>
    /// <param name="order"></param>
    /// <param name="Status"></param>
    /// <returns></returns>
    public async Task UpdateInterviewRecordStatus(Ghrh_InterviewOrder order, string Status)
    {
        var records = await _ghrh_InterviewRecordServices.Query(x => x.Round == order.Round && x.OrderId == order.Id);
        records.ForEach(record => record.Status = Status);
        await _ghrh_InterviewRecordServices.Update(records, ["Status", "UpdateTime", "UpdateBy"]);
    }
    /// <summary>
    /// 同步数据进人事资料
    /// </summary>
    /// <param name="resumeId"></param>
    /// <returns></returns>
    public async Task<bool> SyncToStaff(long id)
    {
        try
        {
            var entity = await base.QueryById(id);
            if (entity.IsSyncToStaff == true || await Db.Queryable<Ghra_Staff>().Where(x => x.IdCardNo == entity.IdCardNo && x.OutDate == null).AnyAsync())
            {
                return false;
            }
            //【简历库-录用-已发offer】配置按钮
            var sendOfferConfig = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "ESS_Recruit_Custom_Transfer_Staff").FirstAsync();

            var type = sendOfferConfig?.ConfigValue ?? "Fast";
            var formColumns = await QueryResumeFormColumn();
            formColumns = formColumns.Where(x => x.MapColumnName.IsNotEmptyOrNull()).ToList();

            int staffId = 0;

            var sql = "SELECT Top 1 CompanyNo from Ghro_Company  where IsEnable=1";
            var companyNo = await Db.Ado.GetStringAsync(sql);
            await Db.Ado.BeginTranAsync();


            #region 人事资料
            Dictionary<string, object> dict = new();
            formColumns.Where(x => x.GroupType == "Base")
                .ToList()
                .ForEach(x =>
                {
                    dict.Add(x.MapColumnName, entity.GetPropertyValue(x.ColumnName));
                });

            dict.Add("CreateBy", App.User.ID);
            dict.Add("CreateTime", DateTime.Now);
            dict.Add("IsRelease", 1);
            dict.Add("SortNo", 1);
            dict.Add("IsDefault", 1);

            if (type == "Fast")
            {
                dict.Add("IsEnable", 2);
                dict.Add("ToDoType", "ToDo");
            }
            else
                dict.Add("IsEnable", 1);
            #endregion

            if (companyNo == "ShiHua")
            {
                dict.Add("IsCalSalary", 1);
                dict.Add("Reverse15", "N");
                dict.Add("Reverse13", "Y");
                dict.Add("TaxID", 1);
            }

            DateTime? inTime = null;
            long? applyOrderId = null;
            var applyOrder = await Db.Queryable<Ghrh_OfferApplyOrder>().Where(x => x.ResumeId == id || x.IdCardNo == entity.IdCardNo).FirstAsync();
            if (applyOrder != null)
            {
                inTime = applyOrder.InDate;
                applyOrderId = applyOrder.Id;
                dict.Add("DeptID", applyOrder.DeptId);
                dict.Add("TitleID", applyOrder.TitleId);
                dict.Add("JobID", applyOrder.JobId);
                dict.Add("GradeID", applyOrder.GradeId);
                dict.Add("PeriodMasterId", applyOrder.PeriodMasterId);
                dict.Add("LegalCompanyID", applyOrder.CompanyId);
                dict.Add("Reverse8", applyOrder.ProbationMonths);
                dict.Add("ZoneID", applyOrder.ZoneId);
                var items = await GetParaList("StaffType1");
                dict.Add("StaffType1", items.Where(x => x.ParaDetailNo == applyOrder.StaffType1).FirstOrDefault()?.ParaDetailId);
                items = await GetParaList("StaffType2");
                dict.Add("StaffType2", items.Where(x => x.ParaDetailNo == applyOrder.StaffType).FirstOrDefault()?.ParaDetailId);
                //dict.Add("Reverse8", applyOrder.ProbationMonths);
            }
            else
            {
                dict.Add("PeriodMasterId", null);
            }
            dict.Add("OvertimeRuleID", null);


            if (dict.Count > 0)
                staffId = await Db.Insertable(dict).AS("Ghra_Staff").ExecuteReturnIdentityAsync();
            if (staffId > 0)
            {

                #region 处理人力需求单 报到人数
                var order = await _ghrh_InterviewOrderServices.QuerySingle(x => x.ResumeId == id);
                if (order.RequestId.IsNotEmptyOrNull())
                {
                    sql = $@"UPDATE Ghrh_HumanRequest
                            SET CheckInCount = ISNULL (CheckInCount, 0) + 1
                            WHERE Id = {order.RequestId}";
                    await Db.Ado.ExecuteCommandAsync(sql);
                }
                #endregion

                sql = $"update Ghra_Staff set Indate='{inTime}'  where StaffID={staffId} and Indate is null";
                if (inTime != null)
                    await Db.Ado.ExecuteCommandAsync(sql);
                if (type != "Fast")
                {
                    sql = $"update Ghra_Staff set StaffNo=[dbo].[FA_AutoStaffNo](StaffID) where StaffID={staffId} and StaffNo is null";
                    await Db.Ado.ExecuteCommandAsync(sql);

                    sql = @$"DECLARE @return_value int;

                        EXEC  @return_value = dbo.[PA_NewStaff] 
                            @StaffID = {staffId};

                        SELECT @return_value as N'@Return Value';";
                    await Db.Ado.ExecuteCommandAsync(sql);
                }

                entity.IsSyncToStaff = true;
                entity.StaffId = staffId;
                await Update(entity, ["IsSyncToStaff", "StaffId"]);

                #region 家庭关系 
                //var list = new List<Dictionary<string, object>>();

                //StaffID = staffId,
                //        RelationType = family.RelationType,
                //        RelationName = family.RelationName,
                //        IDCardNo = family.IDCardNo,
                //        Gender = family.Gender,
                //        Birthday = family.Birth,
                //        CompanyName = family.WorkCompany,
                //        //DeptName = family.WorkCompany,
                //        RelationAddress = family.RelationAddress,
                //        RelationTel = family.Telephone,
                //        Reverse1 = family.RelationAge,
                //        RemarkSz = family.RemarkSz

                var familys = await _ghrh_ResumeHomeServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
                for (int i = 0; i < familys.Count; i++)
                {
                    var family = familys[i];
                    Dictionary<string, object> staffFamily = new()
                    {
                        { "StaffID", staffId },
                        { "CreateBy", App.User.ID },
                        { "CreateTime", DateTime.Now },
                        { "CreateProg", null },
                        { "CreateIP", null }
                    };
                    formColumns.Where(x => x.GroupType == "Family" && x.ColumnName != "AttachmentIDs").ToList()
                        .ForEach(x =>
                        {
                            staffFamily.Add(x.MapColumnName, family.GetPropertyValue(x.ColumnName));
                        });

                    var staffSocialRelation = await Db.Insertable(staffFamily).AS("Ghra_StaffSocialRelation").ExecuteReturnIdentityAsync();

                    if (formColumns.Where(x => x.GroupType == "Family" && x.ColumnName == "AttachmentIDs").Any())
                    {
                        var attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == family.Id.ObjToString()).ToListAsync();

                        if (attachments.Any())
                        {
                            attachments.ForEach(x =>
                            {
                                x.CreateTime = DateTime.Now;
                                x.TableName = "Ghra_StaffSocialRelation";
                                x.StaffID = staffId;
                                x.TableKeyID = staffSocialRelation;
                            });
                            await Db.Insertable(attachments).ExecuteReturnIdentityAsync();
                        }
                    }
                }
                #endregion

                #region 教育背景 
                var educations = await _ghrh_ResumeEduBGServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);

                for (int i = 0; i < educations.Count; i++)
                {
                    var education = educations[i];
                    //var staffEduBG = new Ghra_StaffEduBG()
                    //{
                    //    StaffID = staffId,
                    //    BeginDate = education.BeginDate.ObjToString(),
                    //    EndDate = education.EndDate.ObjToString(),
                    //    SchoolName = education.SchoolName,
                    //    DeptName = education.DeptName,
                    //    DegreeLevel = education.DegreeLevel,
                    //    IsGraduate = education.IsGraduate == true ? 1 : 0,
                    //    RemarkSz = education.RemarkSz
                    //};
                    Dictionary<string, object> staffEduBG = new()
                    {
                        { "StaffID", staffId },
                        { "CreateBy", App.User.ID },
                        { "CreateTime", DateTime.Now },
                        { "CreateProg", null },
                        { "CreateIP", null }
                    };
                    formColumns.Where(x => x.GroupType == "Education" && x.ColumnName != "AttachmentIDs").ToList()
                        .ForEach(x =>
                        {
                            if (x.MapColumnName != "IsGraduate")
                                staffEduBG.Add(x.MapColumnName, education.GetPropertyValue(x.ColumnName));
                            else
                                staffEduBG.Add(x.MapColumnName, education.GetPropertyValue(x.ColumnName).ObjToBool() == true ? 1 : 0);
                        });
                    var staffEduBGId = await Db.Insertable(staffEduBG).AS("Ghra_StaffEduBG").ExecuteReturnIdentityAsync();

                    if (formColumns.Where(x => x.GroupType == "Education" && x.ColumnName == "AttachmentIDs").Any())
                    {
                        var attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == education.Id.ObjToString()).ToListAsync();

                        if (attachments.Any())
                        {
                            attachments.ForEach(x =>
                            {
                                x.CreateTime = DateTime.Now;
                                x.TableName = "Ghra_StaffEduBG";
                                x.StaffID = staffId;
                                x.TableKeyID = staffEduBGId;
                            });
                            await Db.Insertable(attachments).ExecuteReturnIdentityAsync();
                        }
                    }
                }
                #endregion

                #region 工作经历 
                var workExps = await _ghrh_ResumeWorkExpServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
                for (int i = 0; i < workExps.Count; i++)
                {
                    var workExp = workExps[i];
                    Dictionary<string, object> staffWorkExp = new()
                    {
                        { "StaffID", staffId },
                        { "CreateBy", App.User.ID },
                        { "CreateTime", DateTime.Now },
                        { "CreateProg", null },
                        { "CreateIP", null }
                    };
                    formColumns.Where(x => x.GroupType == "WorkExp" && x.ColumnName != "AttachmentIDs").ToList()
                        .ForEach(x =>
                        {
                            staffWorkExp.Add(x.MapColumnName, workExp.GetPropertyValue(x.ColumnName));
                        });
                    var staffworkExpId = await Db.Insertable(staffWorkExp).AS("Ghra_StaffWorkExp").ExecuteReturnIdentityAsync();
                    if (formColumns.Where(x => x.GroupType == "WorkExp" && x.ColumnName == "AttachmentIDs").Any())
                    {
                        var attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == workExp.Id.ObjToString()).ToListAsync();

                        if (attachments.Any())
                        {
                            attachments.ForEach(x =>
                            {
                                x.CreateTime = DateTime.Now;
                                x.TableName = "Ghra_StaffWorkExp";
                                x.StaffID = staffId;
                                x.TableKeyID = staffworkExpId;
                            });
                            await Db.Insertable(attachments).ExecuteReturnIdentityAsync();
                        }
                    }
                }
                #endregion

                #region 证件 
                var Licences = await _ghrh_ResumeLicenceServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
                for (int i = 0; i < Licences.Count; i++)
                {
                    var licence = Licences[i];

                    Dictionary<string, object> staffLicences = new()
                    {
                        { "StaffID", staffId },
                        { "CreateBy", App.User.ID },
                        { "CreateTime", DateTime.Now },
                        { "CreateProg", null },
                        { "CreateIP", null }
                    };
                    formColumns.Where(x => x.GroupType == "Licence" && x.ColumnName != "AttachmentIDs").ToList()
                        .ForEach(x =>
                        {
                            staffLicences.Add(x.MapColumnName, licence.GetPropertyValue(x.ColumnName));
                        });

                    var staffLicenceId = await Db.Insertable(staffLicences).AS("Ghra_StaffLicence").ExecuteReturnIdentityAsync();

                    if (formColumns.Where(x => x.GroupType == "Licence" && x.ColumnName == "AttachmentIDs").Any())
                    {
                        var attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == licence.Id.ObjToString()).ToListAsync();

                        if (attachments.Any())
                        {
                            attachments.ForEach(x =>
                            {
                                x.CreateTime = DateTime.Now;
                                x.TableName = "Ghra_StaffLicence";
                                x.StaffID = staffId;
                                x.TableKeyID = staffLicenceId;
                            });
                            await Db.Insertable(attachments).ExecuteReturnIdentityAsync();
                        }
                    }
                }
                #endregion

                #region 简历培训记录  
                var Trainings = await _ghrh_ResumeTrainingServices.QueryDto(x => x.ResumeId != null && x.ResumeId == id);
                for (int i = 0; i < Trainings.Count; i++)
                {
                    var training = Trainings[i];
                    //var staffEduBG = new Ghra_StaffTraining()
                    //{
                    //    StaffID = staffId,
                    //    BeginDate = training.BeginDate,
                    //    EndDate = training.EndDate,
                    //    TrainingOrgName = training.TrainingOrgName,
                    //    ClassHour = training.ClassHour,
                    //    IsPass = training.IsPass == true ? 1 : 0,
                    //    CourseName = training.CourseName,
                    //    //ClassCredit = training.ClassCredit,
                    //    Reverse1 = training.Reverse1,
                    //    RemarkSz = training.RemarkSz
                    //};

                    Dictionary<string, object> staffTraining = new()
                    {
                        { "StaffID", staffId },
                        { "CreateBy", App.User.ID },
                        { "CreateTime", DateTime.Now },
                        { "CreateProg", null },
                        { "CreateIP", null }
                    };
                    formColumns.Where(x => x.GroupType == "Training" && x.ColumnName != "AttachmentIDs").ToList()
                        .ForEach(x =>
                        {
                            if (x.MapColumnName == "IsPasss")
                                staffTraining.Add(x.MapColumnName, training.GetPropertyValue(x.ColumnName).ObjToBool() == true ? 1 : 0);
                            else
                                staffTraining.Add(x.MapColumnName, training.GetPropertyValue(x.ColumnName));
                        });


                    var staffTrainingId = await Db.Insertable(staffTraining).AS("Ghra_StaffTraining").ExecuteReturnIdentityAsync();
                    if (formColumns.Where(x => x.GroupType == "Training" && x.ColumnName == "AttachmentIDs").Any())
                    {
                        var attachments = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == training.Id.ObjToString()).ToListAsync();

                        if (attachments.Any())
                        {
                            attachments.ForEach(x =>
                            {
                                x.CreateTime = DateTime.Now;
                                x.TableName = "Ghra_StaffTraining";
                                x.StaffID = staffId;
                                x.TableKeyID = staffTrainingId;
                            });
                            await Db.Insertable(attachments).ExecuteReturnIdentityAsync();
                        }
                    }
                }
                #endregion

                #region 附件
                var attachments1 = await Db.Queryable<Ghrs_Attachment>().Where(x => x.TableName == id.ObjToString()).ToListAsync();
                if (attachments1.Any())
                {
                    attachments1.ForEach(x =>
                    {
                        x.CreateTime = DateTime.Now;
                        x.TableName = "Ghra_staff";
                        x.StaffID = staffId;
                        x.TableKeyID = staffId;
                    });
                    await Db.Insertable(attachments1).ExecuteReturnIdentityAsync();
                }
                #endregion

                #region 薪资项目同步
                //            if (companyNo == "ShiHua")
                //            {
                //                if (applyOrder != null && inTime != null && applyOrder.ProbationSalary != null)
                //                    if (applyOrder.ProbationSalary > 0)
                //                    {
                //                        var salaryConfig = await Db.Queryable<Ghrh_Config>().Where(x => x.ConfigCode == "ESS_Recruit_Custom_Check_In_Apply_Salary").FirstAsync();
                //                        var TempOrOfficial = salaryConfig?.ConfigValue ?? "Temp";

                //                        var IsEnable = 2;

                //                        sql = $"SELECT SalaryChangeID FROM Ghrc_SalaryChange WHERE IsEnable>=1 and StaffID='{staffId}' and ChangeDate = '{inTime.Value.Date}'";
                //                        var SalaryChangeID = await Db.Ado.SqlQueryAsync<string>(sql);
                //                        if (SalaryChangeID.Any())
                //                        {
                //                            var sql1 = @$"UPDATE Ghrc_SalaryChange
                //                                SET IsEnable = 0,
                //                                    UpdateBy = {App.User.ID},
                //                                    UpdateTime = GETDATE (),
                //                                    UpdateProg = 'SyncToStaff'
                //                                WHERE SalaryChangeID = {SalaryChangeID[0]} AND IsEnable >= 1;

                //                                UPDATE Ghrc_SalaryChangeDetail
                //                                SET IsEnable = 0,
                //                                    UpdateBy = {App.User.ID},
                //                                    UpdateTime = GETDATE (),
                //                                    UpdateProg = 'SyncToStaff'
                //                                WHERE SalaryChangeID = {SalaryChangeID[0]} AND IsEnable >= 1;";
                //                            await Db.Ado.ExecuteCommandAsync(sql1);
                //                        }

                //                        sql = @$"open symmetric key GHR50AesKey decryption by certificate TiobonGHRCer;
                //                        INSERT INTO [dbo].[Ghrc_SalaryChange]
                //			( StaffID,WorkNo,
                //			ChangeDate,
                //			ChangeTypeID  ,    
                //			ChangeReasonID , ChangeFlag,  
                //			ToDoType,WorkState,RemarkSz,
                //			SortNo,IsEnable,IsDefault,OperateLogID,
                //			CreateBy,CreateTime,CreateProg,
                //			TotalAmount  
                //			)
                //	select  {staffId},null,
                //			 '{inTime.Value.Date}',
                //			23,    --         
                //			1130,  -- 
                //			0,'Todo',1,'',
                //			1,2,0,0,
                //			 {App.User.ID},GETDATE(), 'SyncToStaff',
                //			dbo.FS_EncryptByKey({applyOrder?.ProbationSalary ?? 0}) 
                //        Declare @N_SCID int = SCOPE_IDENTITY()
                //                    select @N_SCID";

                //                        var @N_SCID = await Db.Ado.GetIntAsync(sql);

                //                        sql = @"UPDATE A
                //                    SET A.Reverse1 = b.SalaryItemNo,
                //                        A.UpdateTime = getdate (),
                //                        A.UpdateProg = 'SyncToStaff'
                //                    FROM Ghrh_OfferApplyOrderSalary A
                //                         JOIN Ghrc_SalaryItem b ON A.SalaryItemId = b.SalaryItemID
                //                    WHERE A.Reverse1 IS NULL";

                //                        await Db.Ado.ExecuteCommandAsync(sql);

                //                        var items = await Db.Queryable<Ghrh_OfferApplyOrderSalary>().Where(x => x.OrderId == applyOrder.Id).ToListAsync();

                //                        var S02 = items.Where(x => x.Reverse1 == "S02").FirstOrDefault()?.Amount ?? 0;
                //                        var S03 = items.Where(x => x.Reverse1 == "S03").FirstOrDefault()?.Amount ?? 0;
                //                        var S04 = items.Where(x => x.Reverse1 == "S04").FirstOrDefault()?.Amount ?? 0;
                //                        var S05 = items.Where(x => x.Reverse1 == "S05").FirstOrDefault()?.Amount ?? 0;
                //                        var S06 = items.Where(x => x.Reverse1 == "S06").FirstOrDefault()?.Amount ?? 0;
                //                        var S21 = items.Where(x => x.Reverse1 == "S21").FirstOrDefault()?.Amount ?? 0;
                //                        var S22 = items.Where(x => x.Reverse1 == "S22").FirstOrDefault()?.Amount ?? 0;
                //                        var S23 = items.Where(x => x.Reverse1 == "S23").FirstOrDefault()?.Amount ?? 0;
                //                        var S66 = items.Where(x => x.Reverse1 == "S66").FirstOrDefault()?.Amount ?? 0;
                //                        var S68 = items.Where(x => x.Reverse1 == "S68").FirstOrDefault()?.Amount ?? 0;
                //                        var S70 = items.Where(x => x.Reverse1 == "S70").FirstOrDefault()?.Amount ?? 0;

                //                        sql = @$"open symmetric key GHR50AesKey decryption by certificate TiobonGHRCer;
                //            Declare @Regular_SalaryDetail table (SalaryItemNo nvarchar(100), Amount Decimal(18,2))
                //insert into @Regular_SalaryDetail
                //select 'S02',{S02}
                //union all select 'S03',{S03}
                //union all select 'S04',{S04}
                //union all select 'S05',{S05}
                //union all select 'S06',{S06}
                //union all select 'S21',{S21}
                //union all select 'S22',{S22}
                //union all select 'S23',{S23}
                //union all select 'S66',{S66}
                //union all select 'S68',{S68}
                //union all select 'S70',{S70} 
                //insert into ghrc_salaryChangeDetail(SalaryChangeID,StaffID,ChangeDate,SalaryItemID,Amount,CurrencyID,Createby,CreateProg,
                //						           Reverse1,IsEnable )  
                //select {@N_SCID},{staffId},'{inTime.Value.Date}',b.SalaryItemID,dbo.FS_EncryptByKey(a.Amount) Amount,2,
                //		{App.User.ID}, 'SyncToStaff',
                //		null, {IsEnable} IsEnable
                //from @Regular_SalaryDetail a inner join Ghrc_SalaryItem b on a.SalaryItemNo = b.SalaryItemNo
                //where b.IsEnable = 1
                //and ISNULL(a.Amount,0) > 0";
                //                        await Db.Ado.ExecuteCommandAsync(sql);
                //                    }
                //            }
                #endregion
            }
            await Db.Ado.CommitTranAsync();

            return true;
        }
        catch (Exception E)
        {
            await Db.Ado.RollbackTranAsync();
            throw;
        }
    }
    #endregion
}