LqMonthlyEmployeeFactHelper.cs 8.84 KB
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NCC.Extend.Entitys.lq_attendance_record;
using NCC.Extend.Entitys.lq_attendance_summary;
using NCC.Extend.Entitys.lq_employee_store_assignment;
using NCC.Extend.Entitys.lq_hytk_jksyj;
using NCC.Extend.Entitys.lq_hytk_kjbsyj;
using NCC.Extend.Entitys.lq_jinsanjiao_user;
using NCC.Extend.Entitys.lq_kd_jksyj;
using NCC.Extend.Entitys.lq_kd_kjbsyj;
using NCC.Extend.Entitys.lq_person_times_record;
using NCC.Extend.Entitys.lq_salary_statistics;
using NCC.Extend.Entitys.lq_xh_hyhk;
using NCC.Extend.Entitys.lq_xh_jksyj;
using NCC.Extend.Entitys.lq_xh_kjbsyj;
using NCC.Extend.Entitys.lq_assistant_salary_statistics;
using NCC.Extend.Entitys.lq_business_unit_manager_salary_statistics;
using NCC.Extend.Entitys.lq_director_salary_statistics;
using NCC.Extend.Entitys.lq_major_project_director_salary_statistics;
using NCC.Extend.Entitys.lq_major_project_teacher_salary_statistics;
using NCC.Extend.Entitys.lq_store_manager_salary_statistics;
using NCC.Extend.Entitys.lq_tech_general_manager_salary_statistics;
using NCC.Extend.Entitys.lq_tech_teacher_salary_statistics;
using NCC.System.Entitys.Permission;
using SqlSugar;

namespace NCC.Extend
{
    /// <summary>
    /// 统计自然月内「有业务事实」的员工(BASE_USER.F_Id),用于月中离职仍参与当月算薪、以及各模块复用名单口径。
    /// </summary>
    public static class LqMonthlyEmployeeFactHelper
    {
        private const int KeyResolveChunkSize = 500;

        /// <summary>
        /// 汇总指定年月中,在考勤/业绩/消耗/金三角/工资归档等任一来源出现过的用户主键(已解析为 BASE_USER.F_Id)。
        /// jkszh/kjblszh 等与账号或主键混存的字段会按 Id 或 Account 匹配用户行(未软删)。
        /// </summary>
        public static async Task<List<string>> GetUserIdsWithMonthBusinessFactsAsync(
            ISqlSugarClient db,
            int year,
            int month)
        {
            if (year < 1990 || year > 2100 || month < 1 || month > 12)
            {
                return new List<string>();
            }

            var startDate = new DateTime(year, month, 1);
            var endDate = startDate.AddMonths(1).AddDays(-1);
            var monthStr = $"{year}{month:D2}";
            var endOpen = endDate.AddDays(1);

            var rawKeys = new HashSet<string>(StringComparer.Ordinal);

            void AddKey(string k)
            {
                if (!string.IsNullOrWhiteSpace(k))
                {
                    rawKeys.Add(k.Trim());
                }
            }

            // 与算薪扩大候选一致
            foreach (var id in await LqSalaryUserSnapshotHelper.GetSalaryCandidateUserIdsAsync(db, startDate, endDate, year, month))
            {
                AddKey(id);
            }

            var attUserIds = await db.Queryable<LqAttendanceRecordEntity>()
                .Where(x => x.AttendanceDate >= startDate.Date && x.AttendanceDate <= endDate.Date && x.IsEffective == 1)
                .Select(x => x.UserId)
                .ToListAsync();
            foreach (var id in attUserIds)
            {
                AddKey(id);
            }

            var personIds = await db.Queryable<LqPersonTimesRecordEntity>()
                .Where(x => x.WorkMonth == monthStr && x.IsEffective == 1)
                .Select(x => x.PersonId)
                .ToListAsync();
            foreach (var id in personIds)
            {
                AddKey(id);
            }

            var jsUserIds = await db.Queryable<LqJinsanjiaoUserEntity>()
                .Where(x => x.Month == monthStr && x.DeleteMark == 0)
                .Select(x => x.UserId)
                .ToListAsync();
            foreach (var id in jsUserIds)
            {
                AddKey(id);
            }

            void AddJkszhBatch(List<string> zhList)
            {
                foreach (var z in zhList)
                {
                    AddKey(z);
                }
            }

            AddJkszhBatch(await db.Queryable<LqKdJksyjEntity>()
                .Where(x => x.Yjsj >= startDate && x.Yjsj < endOpen && x.IsEffective == 1)
                .Select(x => x.Jkszh)
                .ToListAsync());

            AddJkszhBatch(await db.Queryable<LqXhJksyjEntity>()
                .Where(x => x.Yjsj >= startDate && x.Yjsj < endOpen && x.IsEffective == 1)
                .Select(x => x.Jkszh)
                .ToListAsync());

            AddJkszhBatch(await db.Queryable<LqHytkJksyjEntity>()
                .Where(x => x.Tksj >= startDate && x.Tksj < endOpen && x.IsEffective == 1)
                .Select(x => x.Jkszh)
                .ToListAsync());

            AddJkszhBatch(await db.Queryable<LqKdKjbsyjEntity>()
                .Where(x => x.Yjsj >= startDate && x.Yjsj < endOpen && x.IsEffective == 1)
                .Select(x => x.Kjblszh)
                .ToListAsync());

            AddJkszhBatch(await db.Queryable<LqXhKjbsyjEntity, LqXhHyhkEntity>(
                    (kj, hk) => kj.Glkdbh == hk.Id && hk.IsEffective == 1)
                .Where((kj, hk) => kj.IsEffective == 1 && hk.Hksj >= startDate && hk.Hksj < endOpen)
                .Select((kj, hk) => kj.Kjblszh)
                .ToListAsync());

            AddJkszhBatch(await db.Queryable<LqHytkKjbsyjEntity>()
                .Where(x => x.Tksj >= startDate && x.Tksj < endOpen && x.IsEffective == 1)
                .Select(x => x.Kjblszh)
                .ToListAsync());

            // 各岗位工资归档(已算过薪的月份仍应出现在「有事实」名单中,便于补算/核对)
            AddJkszhBatch(await db.Queryable<LqSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqTechTeacherSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqTechGeneralManagerSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqDirectorSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqStoreManagerSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqAssistantSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqMajorProjectDirectorSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqMajorProjectTeacherSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());
            AddJkszhBatch(await db.Queryable<LqBusinessUnitManagerSalaryStatisticsEntity>()
                .Where(x => x.StatisticsMonth == monthStr)
                .Select(x => x.EmployeeId)
                .ToListAsync());

            var resolved = await ResolveKeysToUserIdsAsync(db, rawKeys);
            return resolved.ToList();
        }

        /// <summary>
        /// 将 jkszh/账号/主键等字符串解析为未软删用户的 F_Id。
        /// </summary>
        public static async Task<HashSet<string>> ResolveKeysToUserIdsAsync(ISqlSugarClient db, IEnumerable<string> rawKeys)
        {
            var set = new HashSet<string>(StringComparer.Ordinal);
            if (rawKeys == null)
            {
                return set;
            }

            var keys = rawKeys.Where(k => !string.IsNullOrWhiteSpace(k)).Select(k => k.Trim()).Distinct().ToList();
            for (var i = 0; i < keys.Count; i += KeyResolveChunkSize)
            {
                var chunk = keys.Skip(i).Take(KeyResolveChunkSize).ToList();
                var ids = await db.Queryable<UserEntity>()
                    .Where(u => u.DeleteMark == null)
                    .Where(u => chunk.Contains(u.Id) || chunk.Contains(u.Account))
                    .Select(u => u.Id)
                    .ToListAsync();
                foreach (var id in ids)
                {
                    if (!string.IsNullOrEmpty(id))
                    {
                        set.Add(id);
                    }
                }
            }

            return set;
        }
    }
}