using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using NCC.Common.Core.Manager;
using NCC.Common.Filter;
using NCC.Dependency;
using NCC.DynamicApiController;
using NCC.Extend.Entitys.Dto.LqAttendanceSetting;
using NCC.Extend.Entitys.Enum;
using NCC.Extend.Entitys.lq_attendance_absenteeism_rule;
using NCC.Extend.Entitys.lq_attendance_annual_leave_rule;
using NCC.Extend.Entitys.lq_attendance_config_history;
using NCC.Extend.Entitys.lq_attendance_exempt_user;
using NCC.Extend.Entitys.lq_attendance_extra_leave;
using NCC.Extend.Entitys.lq_attendance_funeral_leave_rule;
using NCC.Extend.Entitys.lq_attendance_group;
using NCC.Extend.Entitys.lq_attendance_holiday;
using NCC.Extend.Entitys.lq_attendance_late_rule;
using NCC.Extend.Entitys.lq_attendance_marriage_leave_rule;
using NCC.Extend.Entitys.lq_attendance_maternity_leave_rule;
using NCC.Extend.Entitys.lq_attendance_missing_card_rule;
using NCC.Extend.Entitys.lq_attendance_setting;
using NCC.Extend.Entitys.lq_mdxx;
using NCC.Extend.Interfaces.LqAttendanceSetting;
using NCC.FriendlyException;
using NCC.System.Entitys.Permission;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SqlSugar;
using Yitter.IdGenerator;
namespace NCC.Extend.LqAttendanceSetting
{
///
/// 考勤设置服务
///
[ApiDescriptionSettings(Tag = "绿纤考勤设置服务", Name = "LqAttendanceSetting", Order = 200)]
[Route("api/Extend/[controller]")]
[ApiController]
public class LqAttendanceSettingService : ILqAttendanceSettingService, IDynamicApiController, ITransient
{
private readonly ISqlSugarClient _db;
private readonly IUserManager _userManager;
///
/// 初始化一个类型的新实例
///
public LqAttendanceSettingService(ISqlSugarClient db, IUserManager userManager)
{
_db = db;
_userManager = userManager;
}
///
/// 获取考勤设置聚合信息(兼容旧页面)
///
[HttpGet("Info")]
public async Task GetInfo()
{
var baseInfo = await BuildBaseSettingOutputAsync();
var holidays = await GetHolidayQuery().OrderBy(x => x.HolidayDate).OrderBy(x => x.SortCode).ToListAsync();
var groups = await GetGroupQuery().OrderBy(x => x.SortCode).ToListAsync();
var exemptUsers = await GetExemptUserQuery().OrderBy(x => x.SortCode).ToListAsync();
var extraLeaves = await GetExtraLeaveQuery().OrderBy(x => x.GrantYear, OrderByType.Desc).OrderBy(x => x.SortCode).ToListAsync();
var marriageRules = await GetMarriageRuleQuery().OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToListAsync();
var funeralRules = await GetFuneralRuleQuery().OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToListAsync();
var annualRules = await GetAnnualRuleQuery().OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToListAsync();
var maternityRules = await GetMaternityRuleQuery().OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToListAsync();
var lateRules = await GetLateRuleQuery().OrderBy(x => x.MinMinutes).OrderBy(x => x.SortCode).ToListAsync();
var missingCardRules = await GetMissingCardRuleQuery().OrderBy(x => x.MinCount).OrderBy(x => x.SortCode).ToListAsync();
var absenteeismRules = await GetAbsenteeismRuleQuery().OrderBy(x => x.MinDays).OrderBy(x => x.SortCode).ToListAsync();
return new LqAttendanceSettingInfoOutput
{
leaveDeductDailySalaryRate = baseInfo.leaveDeductDailySalaryRate,
sickLeaveDeductDailySalaryRate = baseInfo.sickLeaveDeductDailySalaryRate,
remark = baseInfo.remark,
holidays = holidays.Select(MapHolidayEntity).ToList(),
groups = groups.Select(MapGroupEntity).ToList(),
exemptUsers = exemptUsers.Select(MapExemptEntity).ToList(),
extraLeaves = extraLeaves.Select(MapExtraLeaveEntity).ToList(),
marriageLeaveRules = marriageRules.Select(MapMarriageEntity).ToList(),
funeralLeaveRules = funeralRules.Select(MapFuneralEntity).ToList(),
annualLeaveRules = annualRules.Select(MapAnnualEntity).ToList(),
maternityLeaveRules = maternityRules.Select(MapMaternityEntity).ToList(),
lateRules = lateRules.Select(MapLateEntity).ToList(),
missingCardRules = missingCardRules.Select(MapMissingCardEntity).ToList(),
absenteeismRules = absenteeismRules.Select(MapAbsenteeismEntity).ToList()
};
}
///
/// 获取考勤设置概览
///
[HttpGet("Overview")]
public async Task GetOverview()
{
var historyLatest = await _db.Queryable()
.OrderBy(x => x.OperateTime, OrderByType.Desc)
.FirstAsync();
return new AttendanceSettingOverviewOutput
{
holidayCount = await GetHolidayQuery().CountAsync(),
groupCount = await GetGroupQuery().CountAsync(),
enabledGroupCount = await GetGroupQuery().Where(x => x.IsEnabled == 1).CountAsync(),
leaveRuleCount = await GetMarriageRuleQuery().CountAsync()
+ await GetFuneralRuleQuery().CountAsync()
+ await GetAnnualRuleQuery().CountAsync()
+ await GetMaternityRuleQuery().CountAsync()
+ await GetExtraLeaveQuery().CountAsync(),
deductRuleCount = await GetLateRuleQuery().CountAsync()
+ await GetMissingCardRuleQuery().CountAsync()
+ await GetAbsenteeismRuleQuery().CountAsync(),
specialCount = await GetExemptUserQuery().CountAsync() + await GetExtraLeaveQuery().CountAsync(),
lastModifyTime = historyLatest?.OperateTime.ToString("yyyy-MM-dd HH:mm:ss")
};
}
///
/// 获取基础扣款设置
///
[HttpGet("BaseInfo")]
public async Task GetBaseInfo()
{
return await BuildBaseSettingOutputAsync();
}
///
/// 保存基础扣款设置
///
[HttpPost("SaveBaseInfo")]
public async Task SaveBaseInfo([FromBody] AttendanceBaseSettingSaveInput input)
{
input ??= new AttendanceBaseSettingSaveInput();
if (input.leaveDeductDailySalaryRate < 0)
{
throw NCCException.Oh("请假扣款倍率不能小于0");
}
if (input.sickLeaveDeductDailySalaryRate < 0)
{
throw NCCException.Oh("病假扣款倍率不能小于0");
}
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
var entity = await GetBaseSettingQuery().OrderBy(x => x.CreateTime, OrderByType.Desc).FirstAsync();
var snapshot = new
{
leaveDeductDailySalaryRate = input.leaveDeductDailySalaryRate,
sickLeaveDeductDailySalaryRate = input.sickLeaveDeductDailySalaryRate,
remark = TrimToNull(input.remark)
};
if (entity == null)
{
entity = new LqAttendanceSettingEntity
{
Id = string.IsNullOrWhiteSpace(input.id) ? YitIdHelper.NextId().ToString() : input.id,
LeaveDeductDailySalaryRate = input.leaveDeductDailySalaryRate,
SickLeaveDeductDailySalaryRate = input.sickLeaveDeductDailySalaryRate,
Remark = TrimToNull(input.remark),
CreateTime = now,
CreateUserId = currentUser.UserId,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.BaseSetting, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Create, "基础扣款设置", snapshot, input.changeReason, currentUser, now);
}
else
{
var beforeSnapshot = new
{
leaveDeductDailySalaryRate = entity.LeaveDeductDailySalaryRate,
sickLeaveDeductDailySalaryRate = entity.SickLeaveDeductDailySalaryRate,
remark = TrimToNull(entity.Remark)
};
if (IsSnapshotEqual(beforeSnapshot, snapshot))
{
return new { changed = false, data = await BuildBaseSettingOutputAsync(entity) };
}
entity.LeaveDeductDailySalaryRate = input.leaveDeductDailySalaryRate;
entity.SickLeaveDeductDailySalaryRate = input.sickLeaveDeductDailySalaryRate;
entity.Remark = TrimToNull(input.remark);
entity.VersionNo = (entity.VersionNo ?? 0) + 1;
entity.LastModifyTime = now;
entity.LastModifyUserId = currentUser.UserId;
entity.LastModifyUserName = currentUser.UserName;
await _db.Updateable(entity).ExecuteCommandAsync();
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.BaseSetting, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, "基础扣款设置", snapshot, input.changeReason, currentUser, now);
}
return new { changed = true, data = await BuildBaseSettingOutputAsync(entity) };
}
///
/// 获取指定模块配置列表
///
[HttpGet("ConfigList")]
public async Task GetConfigList([FromQuery] AttendanceConfigListQueryInput input)
{
input ??= new AttendanceConfigListQueryInput();
input.currentPage = input.currentPage <= 0 ? 1 : input.currentPage;
input.pageSize = input.pageSize <= 0 ? 20 : input.pageSize;
var moduleType = ParseModuleType(input.moduleType);
var keyword = input.keyword?.Trim();
switch (moduleType)
{
case AttendanceConfigModuleTypeEnum.Holiday:
return await GetHolidayListAsync(input.currentPage, input.pageSize, keyword, input.year);
case AttendanceConfigModuleTypeEnum.Group:
return await GetGroupListAsync(input.currentPage, input.pageSize, keyword);
case AttendanceConfigModuleTypeEnum.MarriageLeaveRule:
return await GetMarriageRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.FuneralLeaveRule:
return await GetFuneralRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.AnnualLeaveRule:
return await GetAnnualRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.MaternityLeaveRule:
return await GetMaternityRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.LateRule:
return await GetLateRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.MissingCardRule:
return await GetMissingCardRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.AbsenteeismRule:
return await GetAbsenteeismRuleListAsync(input.currentPage, input.pageSize);
case AttendanceConfigModuleTypeEnum.ExemptUser:
return await GetExemptUserListAsync(input.currentPage, input.pageSize, keyword);
case AttendanceConfigModuleTypeEnum.ExtraLeave:
return await GetExtraLeaveListAsync(input.currentPage, input.pageSize, keyword, input.year);
default:
throw NCCException.Oh("暂不支持的配置模块");
}
}
///
/// 获取指定模块历史记录
///
[HttpGet("ConfigHistory")]
public async Task GetConfigHistory([FromQuery] AttendanceConfigHistoryQueryInput input)
{
input ??= new AttendanceConfigHistoryQueryInput();
input.currentPage = input.currentPage <= 0 ? 1 : input.currentPage;
input.pageSize = input.pageSize <= 0 ? 20 : input.pageSize;
if (string.IsNullOrWhiteSpace(input.bizId))
{
throw NCCException.Oh("业务主键ID不能为空");
}
var moduleType = ParseModuleType(input.moduleType);
var page = await _db.Queryable()
.Where(x => x.ModuleType == (int)moduleType && x.BizId == input.bizId)
.OrderBy(x => x.VersionNo, OrderByType.Desc)
.Select(x => new AttendanceConfigHistoryOutput
{
id = x.Id,
bizId = x.BizId,
versionNo = x.VersionNo,
operateType = x.OperateType,
operateTypeText = "",
title = x.Title,
snapshotJson = x.SnapshotJson,
changeReason = x.ChangeReason,
operateUserId = x.OperateUserId,
operateUserName = x.OperateUserName,
operateTime = ""
})
.ToPagedListAsync(input.currentPage, input.pageSize);
if (page.list != null && page.list.Any())
{
var historyIds = page.list.Select(t => t.id).ToList();
var timeMap = await _db.Queryable()
.Where(x => x.ModuleType == (int)moduleType && x.BizId == input.bizId)
.Where(x => historyIds.Contains(x.Id))
.Select(x => new
{
x.Id,
x.OperateTime
})
.ToListAsync();
var timeLookup = timeMap.ToDictionary(x => x.Id, x => x.OperateTime);
foreach (var item in page.list)
{
item.operateTypeText = GetOperateTypeText((AttendanceConfigOperateTypeEnum)item.operateType);
if (timeLookup.TryGetValue(item.id, out var operateTime))
{
item.operateTime = operateTime.ToString("yyyy-MM-dd HH:mm:ss");
}
}
}
return PageResult.SqlSugarPageResult(page);
}
///
/// 保存指定模块配置
///
[HttpPost("ConfigSave")]
public async Task SaveConfig([FromBody] AttendanceConfigSaveInput input)
{
input ??= new AttendanceConfigSaveInput();
if (input.data == null)
{
throw NCCException.Oh("配置数据不能为空");
}
var moduleType = ParseModuleType(input.moduleType);
switch (moduleType)
{
case AttendanceConfigModuleTypeEnum.Holiday:
return await SaveHolidayAsync(input);
case AttendanceConfigModuleTypeEnum.Group:
return await SaveGroupAsync(input);
case AttendanceConfigModuleTypeEnum.MarriageLeaveRule:
return await SaveMarriageRuleAsync(input);
case AttendanceConfigModuleTypeEnum.FuneralLeaveRule:
return await SaveFuneralRuleAsync(input);
case AttendanceConfigModuleTypeEnum.AnnualLeaveRule:
return await SaveAnnualRuleAsync(input);
case AttendanceConfigModuleTypeEnum.MaternityLeaveRule:
return await SaveMaternityRuleAsync(input);
case AttendanceConfigModuleTypeEnum.LateRule:
return await SaveLateRuleAsync(input);
case AttendanceConfigModuleTypeEnum.MissingCardRule:
return await SaveMissingCardRuleAsync(input);
case AttendanceConfigModuleTypeEnum.AbsenteeismRule:
return await SaveAbsenteeismRuleAsync(input);
case AttendanceConfigModuleTypeEnum.ExemptUser:
return await SaveExemptUserAsync(input);
case AttendanceConfigModuleTypeEnum.ExtraLeave:
return await SaveExtraLeaveAsync(input);
default:
throw NCCException.Oh("暂不支持的配置模块");
}
}
///
/// 删除指定模块配置(逻辑删除)
///
[HttpPost("ConfigDelete")]
public async Task DeleteConfig([FromBody] AttendanceConfigDeleteInput input)
{
input ??= new AttendanceConfigDeleteInput();
if (string.IsNullOrWhiteSpace(input.id))
{
throw NCCException.Oh("主键ID不能为空");
}
var moduleType = ParseModuleType(input.moduleType);
switch (moduleType)
{
case AttendanceConfigModuleTypeEnum.Holiday:
await DeleteHolidayAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.Group:
await DeleteGroupAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.MarriageLeaveRule:
await DeleteMarriageRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.FuneralLeaveRule:
await DeleteFuneralRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.AnnualLeaveRule:
await DeleteAnnualRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.MaternityLeaveRule:
await DeleteMaternityRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.LateRule:
await DeleteLateRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.MissingCardRule:
await DeleteMissingCardRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.AbsenteeismRule:
await DeleteAbsenteeismRuleAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.ExemptUser:
await DeleteExemptUserAsync(input.id, input.changeReason);
break;
case AttendanceConfigModuleTypeEnum.ExtraLeave:
await DeleteExtraLeaveAsync(input.id, input.changeReason);
break;
default:
throw NCCException.Oh("暂不支持的配置模块");
}
}
///
/// 获取考勤分组下拉框
///
[HttpGet("Selector")]
public async Task GetSelector()
{
var list = await GetGroupQuery()
.Where(x => x.IsEnabled == 1)
.OrderBy(x => x.SortCode)
.Select(x => new
{
id = x.Id,
fullName = x.GroupName,
workStartTime = x.WorkStartTime,
workEndTime = x.WorkEndTime,
monthlyRestDays = x.MonthlyRestDays,
halfDaySplitRestDays = x.HalfDaySplitRestDays
})
.ToListAsync();
return new { list };
}
///
/// 获取考勤分组成员列表
///
[HttpGet("GroupUsers")]
public async Task GetGroupUsers([FromQuery] AttendanceGroupUserQueryInput input)
{
input ??= new AttendanceGroupUserQueryInput();
if (string.IsNullOrWhiteSpace(input.GroupId))
{
throw NCCException.Oh("考勤分组ID不能为空");
}
input.currentPage = input.currentPage <= 0 ? 1 : input.currentPage;
input.pageSize = input.pageSize <= 0 ? 20 : input.pageSize;
var keyword = input.Keyword?.Trim();
var query = _db.Queryable((u, s) => new JoinQueryInfos(
JoinType.Left, s.Id == u.Mdid))
.Where((u, s) => u.DeleteMark == null && u.AttendanceGroupId == input.GroupId)
.WhereIF(input.OnJobStatus.HasValue, (u, s) => SqlFunc.IsNull(u.IsOnJob, 1) == input.OnJobStatus.Value)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), (u, s) =>
u.RealName.Contains(keyword) ||
u.Account.Contains(keyword) ||
u.MobilePhone.Contains(keyword))
.Select((u, s) => new AttendanceGroupUserOutput
{
id = u.Id,
realName = u.RealName,
account = u.Account,
mobilePhone = u.MobilePhone,
storeId = u.Mdid,
storeName = s.Dm,
gw = u.Gw,
gwfl = u.Gwfl,
entryDate = u.EntryDate,
isOnJob = SqlFunc.IIF(SqlFunc.IsNull(u.IsOnJob, 1) == 1, 1, 0),
isOnJobText = SqlFunc.IIF(SqlFunc.IsNull(u.IsOnJob, 1) == 1, "在职", "离职")
})
.MergeTable()
.OrderBy(x => x.isOnJob, OrderByType.Desc)
.OrderBy(x => x.storeName)
.OrderBy(x => x.realName);
var pageData = await query.ToPagedListAsync(input.currentPage, input.pageSize);
return PageResult.SqlSugarPageResult(pageData);
}
private async Task BuildBaseSettingOutputAsync(LqAttendanceSettingEntity entity = null)
{
entity ??= await GetBaseSettingQuery().OrderBy(x => x.CreateTime, OrderByType.Desc).FirstAsync();
return new AttendanceBaseSettingOutput
{
id = entity?.Id,
leaveDeductDailySalaryRate = entity?.LeaveDeductDailySalaryRate ?? 1,
sickLeaveDeductDailySalaryRate = entity?.SickLeaveDeductDailySalaryRate ?? 1,
remark = entity?.Remark,
versionNo = entity?.VersionNo ?? 0,
lastModifyTime = entity?.LastModifyTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? entity?.CreateTime?.ToString("yyyy-MM-dd HH:mm:ss"),
lastModifyUserName = entity?.LastModifyUserName
};
}
private async Task GetHolidayListAsync(int currentPage, int pageSize, string keyword, int? year)
{
var query = GetHolidayQuery()
.WhereIF(year.HasValue, x => x.HolidayDate >= new DateTime(year.Value, 1, 1) && x.HolidayDate < new DateTime(year.Value + 1, 1, 1))
.WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.HolidayName.Contains(keyword) || x.Remark.Contains(keyword));
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.HolidayDate).OrderBy(x => x.SortCode)
.ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapHolidayEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetGroupListAsync(int currentPage, int pageSize, string keyword)
{
var query = GetGroupQuery()
.WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.GroupName.Contains(keyword) || x.Remark.Contains(keyword));
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapGroupEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetMarriageRuleListAsync(int currentPage, int pageSize)
{
var query = GetMarriageRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapMarriageEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetFuneralRuleListAsync(int currentPage, int pageSize)
{
var query = GetFuneralRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapFuneralEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetAnnualRuleListAsync(int currentPage, int pageSize)
{
var query = GetAnnualRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapAnnualEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetMaternityRuleListAsync(int currentPage, int pageSize)
{
var query = GetMaternityRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinYears).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapMaternityEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetLateRuleListAsync(int currentPage, int pageSize)
{
var query = GetLateRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinMinutes).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapLateEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetMissingCardRuleListAsync(int currentPage, int pageSize)
{
var query = GetMissingCardRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinCount).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapMissingCardEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetAbsenteeismRuleListAsync(int currentPage, int pageSize)
{
var query = GetAbsenteeismRuleQuery();
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.MinDays).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapAbsenteeismEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetExemptUserListAsync(int currentPage, int pageSize, string keyword)
{
var query = GetExemptUserQuery()
.WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.UserName.Contains(keyword) || x.Remark.Contains(keyword));
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapExemptEntity).ToList(), total, currentPage, pageSize);
}
private async Task GetExtraLeaveListAsync(int currentPage, int pageSize, string keyword, int? year)
{
var query = GetExtraLeaveQuery()
.WhereIF(year.HasValue, x => x.GrantYear == year.Value)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.UserName.Contains(keyword) || x.LeaveName.Contains(keyword) || x.Remark.Contains(keyword));
var total = await query.CountAsync();
var list = await query.OrderBy(x => x.GrantYear, OrderByType.Desc).OrderBy(x => x.SortCode).ToPageListAsync(currentPage, pageSize);
return BuildListResult(list.Select(MapExtraLeaveEntity).ToList(), total, currentPage, pageSize);
}
private async Task SaveHolidayAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
var currentUser = await GetOperatorAsync();
model.holidayName = TrimToNull(model.holidayName);
model.remark = TrimToNull(model.remark);
var existingList = (await GetHolidayQuery().Where(x => x.Id != model.id).OrderBy(x => x.HolidayDate).ToListAsync()).Select(MapHolidayEntity).ToList();
existingList.Add(model);
ValidateHolidays(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceHolidayEntity
{
Id = YitIdHelper.NextId().ToString(),
HolidayDate = ParseDate(model.holidayDate, "公休日期"),
HolidayName = model.holidayName,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetHolidayQuery(), x => (int?)x.SortCode),
CreateTime = now,
CreateUserId = currentUser.UserId,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapHolidayEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.Holiday, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
BuildHolidayTitle(output), output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetHolidayQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("公休配置不存在或已删除");
var before = MapHolidayEntity(dbEntity);
var after = new AttendanceHolidayModel
{
id = dbEntity.Id,
holidayDate = ParseDate(model.holidayDate, "公休日期").ToString("yyyy-MM-dd"),
holidayName = model.holidayName,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.HolidayDate = ParseDate(model.holidayDate, "公休日期");
dbEntity.HolidayName = model.holidayName;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after.id = dbEntity.Id;
after = MapHolidayEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.Holiday, dbEntity.Id, dbEntity.VersionNo ?? 1, AttendanceConfigOperateTypeEnum.Update,
BuildHolidayTitle(after), after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveGroupAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.groupName = TrimToNull(model.groupName);
model.remark = TrimToNull(model.remark);
var currentUser = await GetOperatorAsync();
var existingList = (await GetGroupQuery().Where(x => x.Id != model.id).OrderBy(x => x.SortCode).ToListAsync()).Select(MapGroupEntity).ToList();
existingList.Add(model);
ValidateGroups(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceGroupEntity
{
Id = YitIdHelper.NextId().ToString(),
GroupName = model.groupName,
WorkStartTime = model.workStartTime,
WorkEndTime = model.workEndTime,
MonthlyRestDays = model.monthlyRestDays,
HalfDaySplitRestDays = model.halfDaySplitRestDays,
IsEnabled = model.isEnabled,
Remark = model.remark,
RestUnlockCycle = model.restUnlockCycle,
LateToleranceMinutes = model.lateToleranceMinutes,
EarlyLeaveToleranceMinutes = model.earlyLeaveToleranceMinutes,
SortCode = await GetNextSortCodeAsync(GetGroupQuery(), x => (int?)x.SortCode),
CreateTime = now,
CreateUserId = currentUser.UserId,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapGroupEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.Group, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
output.groupName, output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetGroupQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("考勤分组不存在或已删除");
var before = MapGroupEntity(dbEntity);
var after = new AttendanceGroupModel
{
id = dbEntity.Id,
groupName = model.groupName,
workStartTime = model.workStartTime,
workEndTime = model.workEndTime,
monthlyRestDays = model.monthlyRestDays,
halfDaySplitRestDays = model.halfDaySplitRestDays,
isEnabled = model.isEnabled,
remark = model.remark,
restUnlockCycle = model.restUnlockCycle,
lateToleranceMinutes = model.lateToleranceMinutes,
earlyLeaveToleranceMinutes = model.earlyLeaveToleranceMinutes
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.GroupName = model.groupName;
dbEntity.WorkStartTime = model.workStartTime;
dbEntity.WorkEndTime = model.workEndTime;
dbEntity.MonthlyRestDays = model.monthlyRestDays;
dbEntity.HalfDaySplitRestDays = model.halfDaySplitRestDays;
dbEntity.IsEnabled = model.isEnabled;
dbEntity.Remark = model.remark;
dbEntity.RestUnlockCycle = model.restUnlockCycle;
dbEntity.LateToleranceMinutes = model.lateToleranceMinutes;
dbEntity.EarlyLeaveToleranceMinutes = model.earlyLeaveToleranceMinutes;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapGroupEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.Group, dbEntity.Id, dbEntity.VersionNo ?? 1, AttendanceConfigOperateTypeEnum.Update,
after.groupName, after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveMarriageRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
var currentUser = await GetOperatorAsync();
var existingList = (await GetMarriageRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinYears).ToListAsync()).Select(MapMarriageEntity).ToList();
existingList.Add(model);
ValidateYearRangeRules(existingList, "婚假规则");
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceMarriageLeaveRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinYears = model.minYears,
MaxYears = model.maxYears,
LeaveDays = model.leaveDays,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetMarriageRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapMarriageEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MarriageLeaveRule, entity.Id, 1,
AttendanceConfigOperateTypeEnum.Create, BuildYearRangeTitle(output.minYears, output.maxYears, "婚假规则"),
output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetMarriageRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("婚假规则不存在或已删除");
var before = MapMarriageEntity(dbEntity);
var after = new AttendanceYearRangeRuleModel
{
id = dbEntity.Id,
minYears = model.minYears,
maxYears = model.maxYears,
leaveDays = model.leaveDays,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinYears = model.minYears;
dbEntity.MaxYears = model.maxYears;
dbEntity.LeaveDays = model.leaveDays;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapMarriageEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MarriageLeaveRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildYearRangeTitle(after.minYears, after.maxYears, "婚假规则"),
after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveAnnualRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
var currentUser = await GetOperatorAsync();
var existingList = (await GetAnnualRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinYears).ToListAsync()).Select(MapAnnualEntity).ToList();
existingList.Add(model);
ValidateYearRangeRules(existingList, "年假规则");
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceAnnualLeaveRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinYears = model.minYears,
MaxYears = model.maxYears,
LeaveDays = model.leaveDays,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetAnnualRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapAnnualEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.AnnualLeaveRule, entity.Id, 1,
AttendanceConfigOperateTypeEnum.Create, BuildYearRangeTitle(output.minYears, output.maxYears, "年假规则"),
output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetAnnualRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("年假规则不存在或已删除");
var before = MapAnnualEntity(dbEntity);
var after = new AttendanceYearRangeRuleModel
{
id = dbEntity.Id,
minYears = model.minYears,
maxYears = model.maxYears,
leaveDays = model.leaveDays,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinYears = model.minYears;
dbEntity.MaxYears = model.maxYears;
dbEntity.LeaveDays = model.leaveDays;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapAnnualEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.AnnualLeaveRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildYearRangeTitle(after.minYears, after.maxYears, "年假规则"),
after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveMaternityRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
var currentUser = await GetOperatorAsync();
var existingList = (await GetMaternityRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinYears).ToListAsync()).Select(MapMaternityEntity).ToList();
existingList.Add(model);
ValidateYearRangeRules(existingList, "产假规则");
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceMaternityLeaveRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinYears = model.minYears,
MaxYears = model.maxYears,
LeaveDays = model.leaveDays,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetMaternityRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapMaternityEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MaternityLeaveRule, entity.Id, 1,
AttendanceConfigOperateTypeEnum.Create, BuildYearRangeTitle(output.minYears, output.maxYears, "产假规则"),
output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetMaternityRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("产假规则不存在或已删除");
var before = MapMaternityEntity(dbEntity);
var after = new AttendanceYearRangeRuleModel
{
id = dbEntity.Id,
minYears = model.minYears,
maxYears = model.maxYears,
leaveDays = model.leaveDays,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinYears = model.minYears;
dbEntity.MaxYears = model.maxYears;
dbEntity.LeaveDays = model.leaveDays;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapMaternityEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MaternityLeaveRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildYearRangeTitle(after.minYears, after.maxYears, "产假规则"),
after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveFuneralRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
var currentUser = await GetOperatorAsync();
var existingList = (await GetFuneralRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinYears).ToListAsync()).Select(MapFuneralEntity).ToList();
existingList.Add(model);
ValidateFuneralRules(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceFuneralLeaveRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinYears = model.minYears,
MaxYears = model.maxYears,
DirectRelativeDays = model.directRelativeDays,
IndirectRelativeDays = model.indirectRelativeDays,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetFuneralRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapFuneralEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.FuneralLeaveRule, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
BuildYearRangeTitle(output.minYears, output.maxYears, "丧假规则"), output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetFuneralRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("丧假规则不存在或已删除");
var before = MapFuneralEntity(dbEntity);
var after = new AttendanceFuneralLeaveRuleModel
{
id = dbEntity.Id,
minYears = model.minYears,
maxYears = model.maxYears,
directRelativeDays = model.directRelativeDays,
indirectRelativeDays = model.indirectRelativeDays,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinYears = model.minYears;
dbEntity.MaxYears = model.maxYears;
dbEntity.DirectRelativeDays = model.directRelativeDays;
dbEntity.IndirectRelativeDays = model.indirectRelativeDays;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapFuneralEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.FuneralLeaveRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildYearRangeTitle(after.minYears, after.maxYears, "丧假规则"), after,
input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveLateRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
model.expressionText = TrimToNull(model.expressionText);
var currentUser = await GetOperatorAsync();
var existingList = (await GetLateRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinMinutes).ToListAsync()).Select(MapLateEntity).ToList();
existingList.Add(model);
ValidateLateRules(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceLateRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinMinutes = model.minMinutes,
MaxMinutes = model.maxMinutes,
DeductMode = model.deductMode,
DeductValue = model.deductValue,
ExpressionText = model.expressionText,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetLateRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapLateEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.LateRule, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
BuildMinuteRangeTitle(output.minMinutes, output.maxMinutes, "迟到/早退规则"), output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetLateRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("迟到/早退规则不存在或已删除");
var before = MapLateEntity(dbEntity);
var after = new AttendanceLateRuleModel
{
id = dbEntity.Id,
minMinutes = model.minMinutes,
maxMinutes = model.maxMinutes,
deductMode = model.deductMode,
deductValue = model.deductValue,
expressionText = model.expressionText,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinMinutes = model.minMinutes;
dbEntity.MaxMinutes = model.maxMinutes;
dbEntity.DeductMode = model.deductMode;
dbEntity.DeductValue = model.deductValue;
dbEntity.ExpressionText = model.expressionText;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapLateEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.LateRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildMinuteRangeTitle(after.minMinutes, after.maxMinutes, "迟到/早退规则"), after,
input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveMissingCardRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
model.expressionText = TrimToNull(model.expressionText);
var currentUser = await GetOperatorAsync();
var existingList = (await GetMissingCardRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinCount).ToListAsync()).Select(MapMissingCardEntity).ToList();
existingList.Add(model);
ValidateMissingCardRules(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceMissingCardRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinCount = model.minCount,
MaxCount = model.maxCount,
DeductPerTime = model.deductPerTime,
ExpressionText = model.expressionText,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetMissingCardRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapMissingCardEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MissingCardRule, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
BuildCountRangeTitle(output.minCount, output.maxCount, "缺卡规则"), output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetMissingCardRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("缺卡规则不存在或已删除");
var before = MapMissingCardEntity(dbEntity);
var after = new AttendanceMissingCardRuleModel
{
id = dbEntity.Id,
minCount = model.minCount,
maxCount = model.maxCount,
deductPerTime = model.deductPerTime,
expressionText = model.expressionText,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinCount = model.minCount;
dbEntity.MaxCount = model.maxCount;
dbEntity.DeductPerTime = model.deductPerTime;
dbEntity.ExpressionText = model.expressionText;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapMissingCardEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MissingCardRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildCountRangeTitle(after.minCount, after.maxCount, "缺卡规则"), after,
input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveAbsenteeismRuleAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.remark = TrimToNull(model.remark);
model.actionText = TrimToNull(model.actionText);
var currentUser = await GetOperatorAsync();
var existingList = (await GetAbsenteeismRuleQuery().Where(x => x.Id != model.id).OrderBy(x => x.MinDays).ToListAsync()).Select(MapAbsenteeismEntity).ToList();
existingList.Add(model);
ValidateAbsenteeismRules(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceAbsenteeismRuleEntity
{
Id = YitIdHelper.NextId().ToString(),
MinDays = model.minDays,
MaxDays = model.maxDays,
DeductMode = model.deductMode,
DeductValue = model.deductValue,
ActionType = model.actionType,
ActionText = model.actionText,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetAbsenteeismRuleQuery(), x => (int?)x.SortCode),
CreateTime = now,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapAbsenteeismEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.AbsenteeismRule, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
BuildDecimalRangeTitle(output.minDays, output.maxDays, "旷工规则"), output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetAbsenteeismRuleQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("旷工规则不存在或已删除");
var before = MapAbsenteeismEntity(dbEntity);
var after = new AttendanceAbsenteeismRuleModel
{
id = dbEntity.Id,
minDays = model.minDays,
maxDays = model.maxDays,
deductMode = model.deductMode,
deductValue = model.deductValue,
actionType = model.actionType,
actionText = model.actionText,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.MinDays = model.minDays;
dbEntity.MaxDays = model.maxDays;
dbEntity.DeductMode = model.deductMode;
dbEntity.DeductValue = model.deductValue;
dbEntity.ActionType = model.actionType;
dbEntity.ActionText = model.actionText;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapAbsenteeismEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.AbsenteeismRule, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildDecimalRangeTitle(after.minDays, after.maxDays, "旷工规则"), after,
input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveExemptUserAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
var currentUser = await GetOperatorAsync();
model.userId = TrimToNull(model.userId);
model.remark = TrimToNull(model.remark);
model.userName = await GetRequiredUserNameAsync(model.userId);
var existingList = (await GetExemptUserQuery().Where(x => x.Id != model.id).OrderBy(x => x.SortCode).ToListAsync()).Select(MapExemptEntity).ToList();
existingList.Add(model);
ValidateExemptUsers(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceExemptUserEntity
{
Id = YitIdHelper.NextId().ToString(),
UserId = model.userId,
UserName = model.userName,
StartDate = ParseNullableDate(model.startDate, "免考勤开始日期"),
EndDate = ParseNullableDate(model.endDate, "免考勤结束日期"),
IsEnabled = model.isEnabled,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetExemptUserQuery(), x => (int?)x.SortCode),
CreateTime = now,
CreateUserId = currentUser.UserId,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapExemptEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.ExemptUser, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
output.userName, output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetExemptUserQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("免考勤配置不存在或已删除");
var before = MapExemptEntity(dbEntity);
var after = new AttendanceExemptUserModel
{
id = dbEntity.Id,
userId = model.userId,
userName = model.userName,
startDate = ParseNullableDate(model.startDate, "免考勤开始日期")?.ToString("yyyy-MM-dd"),
endDate = ParseNullableDate(model.endDate, "免考勤结束日期")?.ToString("yyyy-MM-dd"),
isEnabled = model.isEnabled,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.UserId = model.userId;
dbEntity.UserName = model.userName;
dbEntity.StartDate = ParseNullableDate(model.startDate, "免考勤开始日期");
dbEntity.EndDate = ParseNullableDate(model.endDate, "免考勤结束日期");
dbEntity.IsEnabled = model.isEnabled;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapExemptEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.ExemptUser, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, after.userName, after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task SaveExtraLeaveAsync(AttendanceConfigSaveInput input)
{
var model = ParseModel(input.data);
model.userId = TrimToNull(model.userId);
model.leaveName = TrimToNull(model.leaveName);
model.remark = TrimToNull(model.remark);
var currentUser = await GetOperatorAsync();
model.userName = await GetRequiredUserNameAsync(model.userId);
var existingList = (await GetExtraLeaveQuery().Where(x => x.Id != model.id).OrderBy(x => x.GrantYear, OrderByType.Desc).ToListAsync()).Select(MapExtraLeaveEntity).ToList();
existingList.Add(model);
ValidateExtraLeaves(existingList);
var now = DateTime.Now;
if (string.IsNullOrWhiteSpace(model.id))
{
var entity = new LqAttendanceExtraLeaveEntity
{
Id = YitIdHelper.NextId().ToString(),
UserId = model.userId,
UserName = model.userName,
LeaveName = model.leaveName,
GrantYear = model.grantYear,
ExtraDays = model.extraDays,
IsEnabled = model.isEnabled,
Remark = model.remark,
SortCode = await GetNextSortCodeAsync(GetExtraLeaveQuery(), x => (int?)x.SortCode),
CreateTime = now,
CreateUserId = currentUser.UserId,
DeleteMark = 0,
VersionNo = 1,
LastModifyTime = now,
LastModifyUserId = currentUser.UserId,
LastModifyUserName = currentUser.UserName
};
await _db.Insertable(entity).ExecuteCommandAsync();
var output = MapExtraLeaveEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.ExtraLeave, entity.Id, 1, AttendanceConfigOperateTypeEnum.Create,
BuildExtraLeaveTitle(output), output, input.changeReason, currentUser, now);
return new { changed = true, data = output };
}
var dbEntity = await GetExtraLeaveQuery().Where(x => x.Id == model.id).FirstAsync();
_ = dbEntity ?? throw NCCException.Oh("额外假期配置不存在或已删除");
var before = MapExtraLeaveEntity(dbEntity);
var after = new AttendanceExtraLeaveModel
{
id = dbEntity.Id,
userId = model.userId,
userName = model.userName,
leaveName = model.leaveName,
grantYear = model.grantYear,
extraDays = model.extraDays,
isEnabled = model.isEnabled,
remark = model.remark
};
if (IsSnapshotEqual(before, after))
{
return new { changed = false, data = before };
}
dbEntity.UserId = model.userId;
dbEntity.UserName = model.userName;
dbEntity.LeaveName = model.leaveName;
dbEntity.GrantYear = model.grantYear;
dbEntity.ExtraDays = model.extraDays;
dbEntity.IsEnabled = model.isEnabled;
dbEntity.Remark = model.remark;
ApplyModifyMeta(dbEntity, currentUser, now);
await _db.Updateable(dbEntity).ExecuteCommandAsync();
after = MapExtraLeaveEntity(dbEntity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.ExtraLeave, dbEntity.Id, dbEntity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Update, BuildExtraLeaveTitle(after), after, input.changeReason, currentUser, now);
return new { changed = true, data = after };
}
private async Task DeleteHolidayAsync(string id, string changeReason)
{
var entity = await GetHolidayQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("公休配置不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapHolidayEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.Holiday, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildHolidayTitle(snapshot), snapshot, changeReason, currentUser, now);
}
private async Task DeleteGroupAsync(string id, string changeReason)
{
var entity = await GetGroupQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("考勤分组不存在或已删除");
var bindCount = await _db.Queryable().Where(x => x.DeleteMark == null && x.AttendanceGroupId == id).CountAsync();
if (bindCount > 0)
{
throw NCCException.Oh("当前考勤分组下仍有成员绑定,请先解除绑定后再删除");
}
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapGroupEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.Group, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, snapshot.groupName, snapshot, changeReason, currentUser, now);
}
private async Task DeleteMarriageRuleAsync(string id, string changeReason)
{
var entity = await GetMarriageRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("婚假规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapMarriageEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MarriageLeaveRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildYearRangeTitle(snapshot.minYears, snapshot.maxYears, "婚假规则"),
snapshot, changeReason, currentUser, now);
}
private async Task DeleteAnnualRuleAsync(string id, string changeReason)
{
var entity = await GetAnnualRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("年假规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapAnnualEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.AnnualLeaveRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildYearRangeTitle(snapshot.minYears, snapshot.maxYears, "年假规则"),
snapshot, changeReason, currentUser, now);
}
private async Task DeleteFuneralRuleAsync(string id, string changeReason)
{
var entity = await GetFuneralRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("丧假规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapFuneralEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.FuneralLeaveRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildYearRangeTitle(snapshot.minYears, snapshot.maxYears, "丧假规则"), snapshot,
changeReason, currentUser, now);
}
private async Task DeleteMaternityRuleAsync(string id, string changeReason)
{
var entity = await GetMaternityRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("产假规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapMaternityEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MaternityLeaveRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildYearRangeTitle(snapshot.minYears, snapshot.maxYears, "产假规则"),
snapshot, changeReason, currentUser, now);
}
private async Task DeleteLateRuleAsync(string id, string changeReason)
{
var entity = await GetLateRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("迟到/早退规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapLateEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.LateRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildMinuteRangeTitle(snapshot.minMinutes, snapshot.maxMinutes, "迟到/早退规则"), snapshot,
changeReason, currentUser, now);
}
private async Task DeleteMissingCardRuleAsync(string id, string changeReason)
{
var entity = await GetMissingCardRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("缺卡规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapMissingCardEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.MissingCardRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildCountRangeTitle(snapshot.minCount, snapshot.maxCount, "缺卡规则"), snapshot,
changeReason, currentUser, now);
}
private async Task DeleteAbsenteeismRuleAsync(string id, string changeReason)
{
var entity = await GetAbsenteeismRuleQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("旷工规则不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapAbsenteeismEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.AbsenteeismRule, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildDecimalRangeTitle(snapshot.minDays, snapshot.maxDays, "旷工规则"), snapshot,
changeReason, currentUser, now);
}
private async Task DeleteExemptUserAsync(string id, string changeReason)
{
var entity = await GetExemptUserQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("免考勤配置不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapExemptEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.ExemptUser, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, snapshot.userName, snapshot, changeReason, currentUser, now);
}
private async Task DeleteExtraLeaveAsync(string id, string changeReason)
{
var entity = await GetExtraLeaveQuery().Where(x => x.Id == id).FirstAsync();
_ = entity ?? throw NCCException.Oh("额外假期配置不存在或已删除");
var currentUser = await GetOperatorAsync();
var now = DateTime.Now;
entity.DeleteMark = 1;
ApplyModifyMeta(entity, currentUser, now);
await _db.Updateable(entity).ExecuteCommandAsync();
var snapshot = MapExtraLeaveEntity(entity);
await WriteHistoryAsync(AttendanceConfigModuleTypeEnum.ExtraLeave, entity.Id, entity.VersionNo ?? 1,
AttendanceConfigOperateTypeEnum.Delete, BuildExtraLeaveTitle(snapshot), snapshot, changeReason, currentUser, now);
}
private async Task WriteHistoryAsync(AttendanceConfigModuleTypeEnum moduleType, string bizId, int versionNo,
AttendanceConfigOperateTypeEnum operateType, string title, object snapshot, string changeReason, OperatorInfo currentUser, DateTime now)
{
var history = new LqAttendanceConfigHistoryEntity
{
Id = YitIdHelper.NextId().ToString(),
ModuleType = (int)moduleType,
BizId = bizId,
VersionNo = versionNo,
OperateType = (int)operateType,
Title = TrimToNull(title),
SnapshotJson = SerializeSnapshot(snapshot),
ChangeReason = TrimToNull(changeReason),
OperateUserId = currentUser.UserId,
OperateUserName = currentUser.UserName,
OperateTime = now
};
await _db.Insertable(history).ExecuteCommandAsync();
}
private async Task GetOperatorAsync()
{
var user = await _userManager.GetUserInfo();
return new OperatorInfo
{
UserId = user?.userId ?? _userManager?.UserId ?? "admin",
UserName = user?.userName ?? "系统"
};
}
private async Task GetRequiredUserNameAsync(string userId)
{
if (string.IsNullOrWhiteSpace(userId))
{
throw NCCException.Oh("员工不能为空");
}
var user = await _db.Queryable()
.Where(x => x.Id == userId && x.DeleteMark == null)
.Select(x => new { x.Id, x.RealName })
.FirstAsync();
if (user == null)
{
throw NCCException.Oh("所选员工不存在或已删除");
}
return user.RealName;
}
private ISugarQueryable GetBaseSettingQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetHolidayQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetGroupQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetExemptUserQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetExtraLeaveQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetMarriageRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetFuneralRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetAnnualRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetMaternityRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetLateRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetMissingCardRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private ISugarQueryable GetAbsenteeismRuleQuery() => _db.Queryable().Where(x => SqlFunc.IsNull(x.DeleteMark, 0) == 0);
private static dynamic BuildListResult(object list, int total, int currentPage, int pageSize) => new
{
list,
pagination = new
{
total,
currentPage,
pageSize
}
};
private static AttendanceHolidayModel MapHolidayEntity(LqAttendanceHolidayEntity entity) => new AttendanceHolidayModel
{
id = entity.Id,
holidayDate = entity.HolidayDate.ToString("yyyy-MM-dd"),
holidayName = entity.HolidayName,
remark = entity.Remark
};
private static AttendanceGroupModel MapGroupEntity(LqAttendanceGroupEntity entity) => new AttendanceGroupModel
{
id = entity.Id,
groupName = entity.GroupName,
workStartTime = entity.WorkStartTime,
workEndTime = entity.WorkEndTime,
monthlyRestDays = entity.MonthlyRestDays,
halfDaySplitRestDays = entity.HalfDaySplitRestDays,
isEnabled = entity.IsEnabled,
remark = entity.Remark,
restUnlockCycle = entity.RestUnlockCycle,
lateToleranceMinutes = entity.LateToleranceMinutes,
earlyLeaveToleranceMinutes = entity.EarlyLeaveToleranceMinutes
};
private static AttendanceExemptUserModel MapExemptEntity(LqAttendanceExemptUserEntity entity) => new AttendanceExemptUserModel
{
id = entity.Id,
userId = entity.UserId,
userName = entity.UserName,
startDate = entity.StartDate?.ToString("yyyy-MM-dd"),
endDate = entity.EndDate?.ToString("yyyy-MM-dd"),
isEnabled = entity.IsEnabled,
remark = entity.Remark
};
private static AttendanceExtraLeaveModel MapExtraLeaveEntity(LqAttendanceExtraLeaveEntity entity) => new AttendanceExtraLeaveModel
{
id = entity.Id,
userId = entity.UserId,
userName = entity.UserName,
leaveName = entity.LeaveName,
grantYear = entity.GrantYear,
extraDays = entity.ExtraDays,
isEnabled = entity.IsEnabled,
remark = entity.Remark
};
private static AttendanceYearRangeRuleModel MapMarriageEntity(LqAttendanceMarriageLeaveRuleEntity entity) => new AttendanceYearRangeRuleModel
{
id = entity.Id,
minYears = entity.MinYears,
maxYears = entity.MaxYears,
leaveDays = entity.LeaveDays,
remark = entity.Remark
};
private static AttendanceFuneralLeaveRuleModel MapFuneralEntity(LqAttendanceFuneralLeaveRuleEntity entity) => new AttendanceFuneralLeaveRuleModel
{
id = entity.Id,
minYears = entity.MinYears,
maxYears = entity.MaxYears,
directRelativeDays = entity.DirectRelativeDays,
indirectRelativeDays = entity.IndirectRelativeDays,
remark = entity.Remark
};
private static AttendanceYearRangeRuleModel MapAnnualEntity(LqAttendanceAnnualLeaveRuleEntity entity) => new AttendanceYearRangeRuleModel
{
id = entity.Id,
minYears = entity.MinYears,
maxYears = entity.MaxYears,
leaveDays = entity.LeaveDays,
remark = entity.Remark
};
private static AttendanceYearRangeRuleModel MapMaternityEntity(LqAttendanceMaternityLeaveRuleEntity entity) => new AttendanceYearRangeRuleModel
{
id = entity.Id,
minYears = entity.MinYears,
maxYears = entity.MaxYears,
leaveDays = entity.LeaveDays,
remark = entity.Remark
};
private static AttendanceLateRuleModel MapLateEntity(LqAttendanceLateRuleEntity entity) => new AttendanceLateRuleModel
{
id = entity.Id,
minMinutes = entity.MinMinutes,
maxMinutes = entity.MaxMinutes,
deductMode = entity.DeductMode,
deductValue = entity.DeductValue,
expressionText = entity.ExpressionText,
remark = entity.Remark
};
private static AttendanceMissingCardRuleModel MapMissingCardEntity(LqAttendanceMissingCardRuleEntity entity) => new AttendanceMissingCardRuleModel
{
id = entity.Id,
minCount = entity.MinCount,
maxCount = entity.MaxCount,
deductPerTime = entity.DeductPerTime,
expressionText = entity.ExpressionText,
remark = entity.Remark
};
private static AttendanceAbsenteeismRuleModel MapAbsenteeismEntity(LqAttendanceAbsenteeismRuleEntity entity) => new AttendanceAbsenteeismRuleModel
{
id = entity.Id,
minDays = entity.MinDays,
maxDays = entity.MaxDays,
deductMode = entity.DeductMode,
deductValue = entity.DeductValue,
actionType = entity.ActionType,
actionText = entity.ActionText,
remark = entity.Remark
};
private static AttendanceConfigModuleTypeEnum ParseModuleType(string moduleType)
{
switch ((moduleType ?? string.Empty).Trim().ToLowerInvariant())
{
case "basesetting":
case "base":
return AttendanceConfigModuleTypeEnum.BaseSetting;
case "holiday":
return AttendanceConfigModuleTypeEnum.Holiday;
case "group":
return AttendanceConfigModuleTypeEnum.Group;
case "marriagerule":
case "marriageleaverule":
return AttendanceConfigModuleTypeEnum.MarriageLeaveRule;
case "funeralrule":
case "funeralleaverule":
return AttendanceConfigModuleTypeEnum.FuneralLeaveRule;
case "annualrule":
case "annualleaverule":
return AttendanceConfigModuleTypeEnum.AnnualLeaveRule;
case "maternityrule":
case "maternityleaverule":
return AttendanceConfigModuleTypeEnum.MaternityLeaveRule;
case "laterule":
return AttendanceConfigModuleTypeEnum.LateRule;
case "missingcardrule":
return AttendanceConfigModuleTypeEnum.MissingCardRule;
case "absenteeismrule":
return AttendanceConfigModuleTypeEnum.AbsenteeismRule;
case "exemptuser":
return AttendanceConfigModuleTypeEnum.ExemptUser;
case "extraleave":
return AttendanceConfigModuleTypeEnum.ExtraLeave;
default:
throw NCCException.Oh("配置模块类型不正确");
}
}
private static string GetOperateTypeText(AttendanceConfigOperateTypeEnum operateType)
{
switch (operateType)
{
case AttendanceConfigOperateTypeEnum.Create:
return "新增";
case AttendanceConfigOperateTypeEnum.Update:
return "编辑";
case AttendanceConfigOperateTypeEnum.Delete:
return "删除";
default:
return "未知";
}
}
private static string BuildHolidayTitle(AttendanceHolidayModel model) => $"{model.holidayDate}{(string.IsNullOrWhiteSpace(model.holidayName) ? string.Empty : $" · {model.holidayName}")}";
private static string BuildExtraLeaveTitle(AttendanceExtraLeaveModel model) => $"{model.userName} · {model.grantYear}年 · {model.leaveName}";
private static string BuildYearRangeTitle(int min, int? max, string prefix) => max.HasValue ? $"{prefix} {min}-{max}年" : $"{prefix} ≥{min}年";
private static string BuildMinuteRangeTitle(int min, int? max, string prefix) => max.HasValue ? $"{prefix} {min}-{max}分钟" : $"{prefix} ≥{min}分钟";
private static string BuildCountRangeTitle(int min, int? max, string prefix) => max.HasValue ? $"{prefix} {min}-{max}次" : $"{prefix} ≥{min}次";
private static string BuildDecimalRangeTitle(decimal min, decimal? max, string prefix) => max.HasValue ? $"{prefix} {min}-{max}天" : $"{prefix} ≥{min}天";
private static T ParseModel(JObject data)
{
var result = data.ToObject();
_ = result ?? throw NCCException.Oh("配置数据格式不正确");
return result;
}
private static string SerializeSnapshot(object snapshot) => JsonConvert.SerializeObject(snapshot, Formatting.None);
private static bool IsSnapshotEqual(object source, object target)
{
var sourceToken = JToken.Parse(SerializeSnapshot(source));
var targetToken = JToken.Parse(SerializeSnapshot(target));
return JToken.DeepEquals(sourceToken, targetToken);
}
private static string TrimToNull(string value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim();
private static DateTime ParseDate(string value, string title)
{
if (!DateTime.TryParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
{
throw NCCException.Oh($"{title}格式不正确,应为yyyy-MM-dd");
}
return date;
}
private static DateTime? ParseNullableDate(string value, string title)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return ParseDate(value, title);
}
private static void ApplyModifyMeta(object entity, OperatorInfo currentUser, DateTime now)
{
SetProperty(entity, "VersionNo", GetIntProperty(entity, "VersionNo") + 1);
SetProperty(entity, "LastModifyTime", now);
SetProperty(entity, "LastModifyUserId", currentUser.UserId);
SetProperty(entity, "LastModifyUserName", currentUser.UserName);
}
private static int GetIntProperty(object entity, string name)
{
var property = entity.GetType().GetProperty(name);
if (property == null)
{
return 0;
}
var value = property.GetValue(entity);
return value == null ? 0 : Convert.ToInt32(value);
}
private static void SetProperty(object entity, string name, object value)
{
var property = entity.GetType().GetProperty(name);
if (property != null && property.CanWrite)
{
property.SetValue(entity, value);
}
}
private async Task GetNextSortCodeAsync(ISugarQueryable query, Expression> sortExpression) where T : class, new()
{
var max = await query.MaxAsync(sortExpression);
return (max ?? 0) + 1;
}
private static void ValidateHolidays(List holidays)
{
var parsedDates = new List();
for (var i = 0; i < holidays.Count; i++)
{
var item = holidays[i];
if (string.IsNullOrWhiteSpace(item.holidayDate))
{
throw NCCException.Oh($"第{i + 1}条公休日期不能为空");
}
var date = ParseDate(item.holidayDate, $"第{i + 1}条公休日期").Date;
parsedDates.Add(date);
}
var duplicateDate = parsedDates.GroupBy(x => x).FirstOrDefault(x => x.Count() > 1);
if (duplicateDate != null)
{
throw NCCException.Oh($"公休日期 {duplicateDate.Key:yyyy-MM-dd} 重复,请检查");
}
}
private static void ValidateGroups(List groups)
{
var groupNames = new HashSet();
for (var i = 0; i < groups.Count; i++)
{
var group = groups[i];
var groupName = group.groupName?.Trim();
if (string.IsNullOrWhiteSpace(groupName))
{
throw NCCException.Oh($"第{i + 1}个考勤分组名称不能为空");
}
if (!groupNames.Add(groupName))
{
throw NCCException.Oh($"考勤分组名称【{groupName}】重复,请调整后重试");
}
ValidateWorkTime(group.workStartTime, group.workEndTime, groupName);
if (group.monthlyRestDays < 0)
{
throw NCCException.Oh($"考勤分组【{groupName}】的月应休天数不能小于0");
}
if (group.halfDaySplitRestDays < 0)
{
throw NCCException.Oh($"考勤分组【{groupName}】的可拆分半天休假天数不能小于0");
}
if (group.halfDaySplitRestDays > group.monthlyRestDays)
{
throw NCCException.Oh($"考勤分组【{groupName}】的可拆分半天休假天数不能大于月应休天数");
}
}
}
private static void ValidateExemptUsers(List exemptUsers)
{
var userIds = new HashSet();
for (var i = 0; i < exemptUsers.Count; i++)
{
var item = exemptUsers[i];
if (string.IsNullOrWhiteSpace(item.userId))
{
throw NCCException.Oh($"第{i + 1}条免考勤人员未选择员工");
}
if (!userIds.Add(item.userId))
{
throw NCCException.Oh($"免考勤人员【{item.userName}】重复,请检查");
}
if (!string.IsNullOrWhiteSpace(item.startDate) && !string.IsNullOrWhiteSpace(item.endDate))
{
var startDate = ParseDate(item.startDate, $"第{i + 1}条免考勤开始日期");
var endDate = ParseDate(item.endDate, $"第{i + 1}条免考勤结束日期");
if (endDate.Date < startDate.Date)
{
throw NCCException.Oh($"免考勤人员【{item.userName}】的结束日期不能早于开始日期");
}
}
}
}
private static void ValidateExtraLeaves(List extraLeaves)
{
var uniqueKeys = new HashSet();
for (var i = 0; i < extraLeaves.Count; i++)
{
var item = extraLeaves[i];
if (string.IsNullOrWhiteSpace(item.userId))
{
throw NCCException.Oh($"第{i + 1}条额外假期未选择员工");
}
var leaveName = item.leaveName?.Trim();
if (string.IsNullOrWhiteSpace(leaveName))
{
throw NCCException.Oh($"第{i + 1}条额外假期名称不能为空");
}
if (item.grantYear < 2000 || item.grantYear > 2100)
{
throw NCCException.Oh($"额外假期【{leaveName}】的归属年份不合法,请填写 2000 - 2100 之间的年份");
}
if (item.extraDays <= 0)
{
throw NCCException.Oh($"额外假期【{leaveName}】的天数必须大于0");
}
var uniqueKey = $"{item.userId}_{item.grantYear}_{leaveName}";
if (!uniqueKeys.Add(uniqueKey))
{
throw NCCException.Oh($"员工【{item.userName}】在 {item.grantYear} 年的额外假期【{leaveName}】重复,请检查");
}
}
}
private static void ValidateYearRangeRules(List rules, string ruleName)
{
ValidateRangeRules(
rules.Select((x, index) => new RangeRuleItem
{
Title = $"{ruleName}第{index + 1}条",
MinValue = x.minYears,
MaxValue = x.maxYears,
ExtraValidation = () =>
{
if (x.leaveDays < 0)
{
throw NCCException.Oh($"{ruleName}第{index + 1}条的休假天数不能小于0");
}
}
}).ToList(),
ruleName,
"司龄");
}
private static void ValidateFuneralRules(List rules)
{
ValidateRangeRules(
rules.Select((x, index) => new RangeRuleItem
{
Title = $"丧假规则第{index + 1}条",
MinValue = x.minYears,
MaxValue = x.maxYears,
ExtraValidation = () =>
{
if (x.directRelativeDays < 0 || x.indirectRelativeDays < 0)
{
throw NCCException.Oh($"丧假规则第{index + 1}条的休假天数不能小于0");
}
}
}).ToList(),
"丧假规则",
"司龄");
}
private static void ValidateLateRules(List rules)
{
ValidateRangeRules(
rules.Select((x, index) => new RangeRuleItem
{
Title = $"迟到/早退规则第{index + 1}条",
MinValue = x.minMinutes,
MaxValue = x.maxMinutes,
ExtraValidation = () =>
{
ValidateDeductMode(x.deductMode, $"迟到/早退规则第{index + 1}条");
if (x.deductValue < 0)
{
throw NCCException.Oh($"迟到/早退规则第{index + 1}条的扣款值不能小于0");
}
}
}).ToList(),
"迟到/早退规则",
"迟到分钟");
}
private static void ValidateMissingCardRules(List rules)
{
ValidateRangeRules(
rules.Select((x, index) => new RangeRuleItem
{
Title = $"缺卡规则第{index + 1}条",
MinValue = x.minCount,
MaxValue = x.maxCount,
MinLimit = 1,
ExtraValidation = () =>
{
if (x.deductPerTime < 0)
{
throw NCCException.Oh($"缺卡规则第{index + 1}条的每次扣款金额不能小于0");
}
}
}).ToList(),
"缺卡规则",
"缺卡次数");
}
private static void ValidateAbsenteeismRules(List rules)
{
ValidateRangeRules(
rules.Select((x, index) => new RangeRuleItem
{
Title = $"旷工规则第{index + 1}条",
MinValue = x.minDays,
MaxValue = x.maxDays,
ExtraValidation = () =>
{
if (!Enum.IsDefined(typeof(AttendanceAbsenteeismActionTypeEnum), x.actionType))
{
throw NCCException.Oh($"旷工规则第{index + 1}条的处理方式无效");
}
if (x.actionType == (int)AttendanceAbsenteeismActionTypeEnum.Deduct)
{
if (!x.deductMode.HasValue)
{
throw NCCException.Oh($"旷工规则第{index + 1}条请选择扣款方式");
}
ValidateDeductMode(x.deductMode.Value, $"旷工规则第{index + 1}条");
if (!x.deductValue.HasValue || x.deductValue.Value < 0)
{
throw NCCException.Oh($"旷工规则第{index + 1}条的扣款值不能小于0");
}
}
}
}).ToList(),
"旷工规则",
"旷工天数");
}
private static void ValidateDeductMode(int deductMode, string ruleTitle)
{
if (!Enum.IsDefined(typeof(AttendanceDeductModeEnum), deductMode))
{
throw NCCException.Oh($"{ruleTitle}的扣款方式无效");
}
}
private static void ValidateWorkTime(string workStartTime, string workEndTime, string groupName)
{
if (string.IsNullOrWhiteSpace(workStartTime) || string.IsNullOrWhiteSpace(workEndTime))
{
throw NCCException.Oh($"考勤分组【{groupName}】的上下班时间不能为空");
}
if (!TimeSpan.TryParseExact(workStartTime, @"hh\:mm", CultureInfo.InvariantCulture, out var startTime))
{
throw NCCException.Oh($"考勤分组【{groupName}】的上班时间格式不正确,请使用HH:mm");
}
if (!TimeSpan.TryParseExact(workEndTime, @"hh\:mm", CultureInfo.InvariantCulture, out var endTime))
{
throw NCCException.Oh($"考勤分组【{groupName}】的下班时间格式不正确,请使用HH:mm");
}
if (endTime <= startTime)
{
throw NCCException.Oh($"考勤分组【{groupName}】的下班时间必须晚于上班时间");
}
}
private static void ValidateRangeRules(List> rules, string ruleName, string dimensionName)
where T : struct, IComparable
{
var orderedRules = rules.OrderBy(x => x.MinValue).ToList();
var emptyMaxCount = orderedRules.Count(x => !x.MaxValue.HasValue);
if (emptyMaxCount > 1)
{
throw NCCException.Oh($"{ruleName}只能配置一条无上限规则");
}
for (var i = 0; i < orderedRules.Count; i++)
{
var rule = orderedRules[i];
if (rule.MinValue.CompareTo(rule.MinLimit) < 0)
{
throw NCCException.Oh($"{rule.Title}的最小{dimensionName}不能小于{rule.MinLimit}");
}
if (rule.MaxValue.HasValue && rule.MaxValue.Value.CompareTo(rule.MinValue) < 0)
{
throw NCCException.Oh($"{rule.Title}的最大{dimensionName}不能小于最小{dimensionName}");
}
rule.ExtraValidation?.Invoke();
if (i == 0)
{
continue;
}
var previousRule = orderedRules[i - 1];
if (!previousRule.MaxValue.HasValue)
{
throw NCCException.Oh($"{ruleName}存在多条无上限规则,请检查");
}
if (rule.MinValue.CompareTo(previousRule.MaxValue.Value) < 0)
{
throw NCCException.Oh($"{ruleName}区间存在重叠,请检查【{previousRule.Title}】和【{rule.Title}】");
}
}
}
private sealed class RangeRuleItem where T : struct, IComparable
{
public string Title { get; set; }
public T MinValue { get; set; }
public T? MaxValue { get; set; }
public T MinLimit { get; set; } = default;
public Action ExtraValidation { get; set; }
}
private sealed class OperatorInfo
{
public string UserId { get; set; }
public string UserName { get; set; }
}
}
}