SystemBackgroundService.cs 12.1 KB
using HHECS.WorkHourStatistics.Dtos;
using HHECS.WorkHourStatistics.Models;
using NPOI.SS.Formula.Functions;

namespace HHECS.WorkHourStatistics.Service
{
    public class SystemBackgroundService : BackgroundService
    {
        private readonly IFreeSql _freeSql;
        private readonly HttpService _httpService;
        private readonly ILogger<SystemBackgroundService> _logger;

        public SystemBackgroundService(IFreeSql freeSql, HttpService httpService, ILogger<SystemBackgroundService> logger)
        {
            _freeSql = freeSql;
            _httpService = httpService;
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var _actionA = DateTime.MinValue;
            var _actionB = DateTime.MinValue;
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    if ((DateTime.Now - _actionA).TotalSeconds >= 8)
                    {
                        //生成日期记录
                        _actionA = DateTime.Now;
                        GenerateCalendar();

                        //同步部门数据
                        var departmentResult = _httpService.GetDepartment();
                        var oldDepartment = _freeSql.Queryable<Department>().ToList();
                        if (departmentResult.Success)
                        {
                            static string keySelector(Department department) => department.Id;
                            var addTemps = departmentResult.Data.ExceptBy(oldDepartment.Select(keySelector), keySelector).ToList();
                            var updateTemps = departmentResult.Data.IntersectBy(oldDepartment.Select(keySelector), keySelector).ToList();
                            var removeTemps = oldDepartment.ExceptBy(departmentResult.Data.Select(keySelector), keySelector).ToList();
                            _freeSql.Insert(addTemps).ExecuteAffrows();
                            _freeSql.Update<Department>().SetSource(updateTemps).ExecuteAffrows();
                            _freeSql.Delete<Department>().Where(removeTemps).ExecuteAffrows();
                        }

                        //同步用户数据
                        var personResult = _httpService.GetPersons();
                        var oldPersons = _freeSql.Queryable<Person>().ToList();
                        if (personResult.Success)
                        {
                            static string keySelector(Person person) => person.Id;
                            var addTemps = personResult.Data.ExceptBy(oldPersons.Select(keySelector), keySelector).ToList();
                            var updateTemps = personResult.Data.ExceptBy(oldPersons.Select(keySelector), keySelector).ToList();
                            var removeTemps = oldPersons.ExceptBy(personResult.Data.Select(keySelector), keySelector).ToList();
                            _freeSql.Insert(addTemps).ExecuteAffrows();
                            _freeSql.Update<Person>().SetSource(updateTemps).ExecuteAffrows();
                            _freeSql.Delete<Person>().Where(removeTemps).ExecuteAffrows();
                        }

                        var personWorkNo = _freeSql.Queryable<Person>().ToList(x => x.Number);
                        //同步打卡数据
                        var calendars = _freeSql.Queryable<CalendarRecord>().Where(x => !x.IsSync && x.Date < DateTime.Today).OrderBy(x => x.Date).ToList();
                        foreach (var calendar in calendars)
                        {
                            var isEmpty = false;
                            var pageIndex = 1;

                            var workRecordDtos = new List<WorkRecordDto>();
                            var currentDate = calendar.Date.ToString("yyyy-MM-dd");
                            var isOk = false;//是否每页都获取成功
                            do
                            {
                                //打卡接口调用频率 10次/分钟
                                await Task.Delay(6000, stoppingToken);
                                var workRecordResult = _httpService.GetWorkRecod(currentDate, pageIndex);
                                if (!workRecordResult.Success)
                                {
                                    _freeSql.Update<CalendarRecord>(calendar.Id).Set(x => x.ErrorMessage, workRecordResult.Msg).ExecuteAffrows();
                                    _logger.LogError($"[{currentDate}]考勤数据同步失败:{workRecordResult.Msg}");
                                    isOk = false;
                                    workRecordDtos.Clear();//获取数据失败,则清空,不同步
                                    break;
                                }
                                isOk = true;
                                if (workRecordResult.Data.Count == 0)
                                {
                                    isEmpty = true;
                                    break;
                                }
                                pageIndex++;
                                //存储
                                workRecordDtos.AddRange(workRecordResult.Data);
                            } while (!isEmpty);

                            if (isOk)
                            {
                                if (workRecordDtos.Count != 0)
                                {
                                    var addTemps = new List<WorkRecord>();
                                    var updateTemps = new List<WorkRecord>();

                                    var oldWorkRecords = _freeSql.Queryable<WorkRecord>().Where(x => x.BaseWorkCard.Date == calendar.Date).ToList();
                                    foreach (var item in workRecordDtos.Where(x => personWorkNo.Contains(x.Number)).GroupBy(x => x.Number))
                                    {
                                        DateTime? workCard = null;
                                        DateTime? offDutyCard = null;
                                        var workCardStr = item.Where(x => x.IsOnWork).Select(x => x.CreateTime).FirstOrDefault();
                                        var offDutyCardStr = item.Where(x => !x.IsOnWork).Select(x => x.CreateTime).FirstOrDefault();
                                        if (!string.IsNullOrWhiteSpace(workCardStr))
                                        {
                                            _ = DateTime.TryParse(workCardStr, out var time);
                                            workCard = time;
                                        }

                                        if (!string.IsNullOrWhiteSpace(offDutyCardStr))
                                        {
                                            _ = DateTime.TryParse(offDutyCardStr, out var time);
                                            offDutyCard = time;
                                        }

                                        var workCardLocationAddress = item.Where(x => x.IsOnWork).Select(x => x.LocationAddress).FirstOrDefault();
                                        var offDutyCardLocationAddress = item.Where(x => !x.IsOnWork).Select(x => x.LocationAddress).FirstOrDefault();

                                        var oldRecord = oldWorkRecords.Where(x => x.Number == item.Key).FirstOrDefault();
                                        if (oldRecord == null)
                                        {
                                            _ = DateTime.TryParse(item.Where(x => x.IsOnWork).Select(x => x.BaseCheckTime).FirstOrDefault(), out var baseWorkCard);
                                            _ = DateTime.TryParse(item.Where(x => !x.IsOnWork).Select(x => x.BaseCheckTime).FirstOrDefault(), out var baseOffDutyCard);

                                            oldRecord = new WorkRecord
                                            {
                                                Number = item.Key,
                                                PersonName = item.Select(x => x.PersonName).First(),
                                                BaseWorkCard = baseWorkCard,
                                                BaseOffDutyCard = baseOffDutyCard,
                                                WorkCard = workCard,
                                                OffDutyCard = offDutyCard,
                                                WorkCardLocationAddress = workCardLocationAddress,
                                                OffDutyCardLocationAddress = offDutyCardLocationAddress,
                                                CreateTime = DateTime.Now,
                                            };
                                            addTemps.Add(oldRecord);
                                        }
                                        else
                                        {
                                            oldRecord.WorkCard = workCard;
                                            oldRecord.OffDutyCard = offDutyCard;
                                            oldRecord.WorkCardLocationAddress = workCardLocationAddress;
                                            oldRecord.OffDutyCardLocationAddress = offDutyCardLocationAddress;
                                            oldRecord.CreateTime = DateTime.Now;
                                            updateTemps.Add(oldRecord);
                                        }
                                    }

                                    _freeSql.Insert(addTemps).ExecuteAffrows();
                                    _freeSql.Update<WorkRecord>().SetSource(updateTemps).UpdateColumns(x => new
                                    {
                                        x.WorkCard,
                                        x.OffDutyCard,
                                        x.WorkCardLocationAddress,
                                        x.OffDutyCardLocationAddress,
                                        x.CreateTime,
                                    }).ExecuteAffrows();
                                }

                                _freeSql.Update<CalendarRecord>(calendar.Id)
                                      .Set(x => x.IsSync, true)
                                      .Set(x => x.ErrorMessage, null)
                                      .ExecuteAffrows();
                                _logger.LogInformation($"[{currentDate}]考勤数据同步完成");
                            }
                        }
                    }

                    //清理历史数据
                    if ((DateTime.Now - _actionB).Minutes >= 5)
                    {
                        _actionB = DateTime.Now;
                        _freeSql.Queryable<WorkRecord>().Where(x => x.BaseOffDutyCard < DateTime.Now.AddMonths(-18)).Limit(1000).ToDelete();
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError($"[{nameof(SystemBackgroundService)}]线程异常:{ex.Message}");
                }
                await Task.Delay(10000, stoppingToken);
            }
        }

        /// <summary>
        /// 生成日期记录
        /// </summary>
        private void GenerateCalendar()
        {
            try
            {
                var startDate = _freeSql.Queryable<CalendarRecord>().OrderByDescending(x => x.Date).First(x => x.Date);
                if (startDate == default)
                {
                    //无数据,取近三个月的数据
                    startDate = DateTime.Today.AddMonths(-3);
                }

                var totalDays = (int)(DateTime.Today - startDate).TotalDays;
                var records = Enumerable.Range(1, totalDays - 1).Select(x => new CalendarRecord
                {
                    Date = startDate.AddDays(x),
                    IsSync = false,
                }).ToList();
                _freeSql.Insert(records).ExecuteAffrows();
            }
            catch (Exception ex)
            {
                _logger.LogError($"生成日期记录出现异常:{ex.Message}");
            }
        }
    }
}