using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using NCC.Common.Core.Manager; using NCC.Common.Filter; using NCC.Common.Util; using NCC.Dependency; using NCC.DynamicApiController; using NCC.Extend.Entitys.Dto.LqEmployeeRoster; using NCC.Extend.Entitys.lq_attendance_summary; using NCC.Extend.Entitys.lq_mdxx; using NCC.FriendlyException; using NCC.System.Entitys.Model.Permission.User; using NCC.System.Entitys.Permission; using SqlSugar; namespace NCC.Extend { /// /// 员工花名册(按年月、组织机构查询在职人员) /// [ApiDescriptionSettings(Tag = "绿纤员工花名册", Name = "LqEmployeeRoster", Order = 203)] [Route("api/Extend/LqEmployeeRoster")] public class LqEmployeeRosterService : IDynamicApiController, ITransient { private readonly ISqlSugarClient _db; private readonly IUserManager _userManager; /// /// 初始化员工花名册服务 /// public LqEmployeeRosterService(ISqlSugarClient db, IUserManager userManager) { _db = db; _userManager = userManager; } /// /// 分页查询指定年月在职员工花名册。口径:优先取该月有效考勤汇总(F_IsEffective=1)中员工状态为在职(F_EmployeeStatus=1); /// 无汇总且查询月不晚于当前月时:入职不晚于该月末,且(无离职日或离职日≥该月 1 日)。含义:离职日在上月或更早则本月不出现;离职日在本月或之后则本月仍出现(含当月中途离职);查下月时因「离职日≥下月1日」不成立,上月已离职者不会出现在下月(不依赖 F_IsOnJob)。 /// 有有效汇总且员工状态非在职则不列入。未来月仅认汇总。 /// /// 年 /// 月(1-12) /// 组织机构节点 ID,含其下级;不传则按数据权限范围内全部 /// 账号或姓名模糊 /// 分页参数 [HttpGet("")] public async Task GetList( [FromQuery] int year, [FromQuery] int month, [FromQuery] string organizeId, [FromQuery] string keyword, [FromQuery] PageInputBase page) { if (year < 1990 || year > 2100 || month < 1 || month > 12) { throw NCCException.Oh("year/month 参数不合法"); } page ??= new PageInputBase(); var user = await _userManager.GetUserInfo(); var orgFilter = await ResolveOrganizeFilterAsync(user, organizeId); var now = DateTime.Now; var isFutureMonth = year > now.Year || (year == now.Year && month > now.Month); var monthEnd = new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59); var firstDayOfMonth = new DateTime(year, month, 1); var kw = string.IsNullOrWhiteSpace(keyword) ? null : keyword.Trim(); var query = _db.Queryable( (u, o, s, m) => new JoinQueryInfos( JoinType.Left, o.Id == SqlFunc.ToString(u.OrganizeId), JoinType.Left, s.UserId == u.Id && s.Year == year && s.Month == month && s.IsEffective == 1, JoinType.Left, m.Id == u.Mdid)) .Where((u, o, s, m) => u.DeleteMark == null) .Where((u, o, s, m) => (!SqlFunc.IsNullOrEmpty(s.Id) && s.EmployeeStatus == 1) || (!isFutureMonth && SqlFunc.IsNullOrEmpty(s.Id) && u.EntryDate != null && u.EntryDate <= monthEnd && (u.LeaveDate == null || u.LeaveDate >= firstDayOfMonth))) .WhereIF(kw != null, (u, o, s, m) => u.Account.Contains(kw) || u.RealName.Contains(kw)) .Select((u, o, s, m) => new LqEmployeeRosterListOutput { id = u.Id, account = u.Account, realName = u.RealName, department = o.FullName, organizeId = u.OrganizeId, mdid = u.Mdid, mdName = m.Dm, gw = u.Gw, zw = u.Zw, enabledMark = u.EnabledMark, isOnJob = u.IsOnJob, summaryEmployeeStatus = SqlFunc.IIF(SqlFunc.IsNullOrEmpty(s.Id), (int?)null, s.EmployeeStatus), workDays = SqlFunc.IIF(SqlFunc.IsNullOrEmpty(s.Id), (decimal?)null, s.WorkDays), mobilePhone = u.MobilePhone, entryDate = u.EntryDate }) .MergeTable(); if (orgFilter != null) { query = query.Where(x => orgFilter.Contains(x.organizeId)); } query = query .OrderBy(x => x.organizeId) .OrderBy(x => x.realName); var paged = await query.ToPagedListAsync(page.currentPage, page.pageSize); return PageResult.SqlSugarPageResult(paged); } /// /// 管理员:未选组织时不限制;已选组织则限制为该节点及下级。非管理员:限制在数据权限机构(含下级)内,并可与所选组织求交。 /// private async Task> ResolveOrganizeFilterAsync(UserInfo user, string organizeId) { var allOrgs = await _db.Queryable() .Where(x => x.DeleteMark == null) .ToListAsync(); List subtree = null; if (!string.IsNullOrWhiteSpace(organizeId)) { subtree = allOrgs .TreeChildNode(organizeId.Trim(), t => t.Id, t => t.ParentId) .Select(x => x.Id) .ToList(); } if (user.isAdministrator) { return subtree; } var allowed = new HashSet(); foreach (var scope in user.dataScope ?? new List()) { if (!scope.Add && !scope.Edit && !scope.Delete) { continue; } if (string.IsNullOrEmpty(scope.organizeId)) { continue; } foreach (var id in allOrgs.TreeChildNode(scope.organizeId, t => t.Id, t => t.ParentId) .Select(x => x.Id)) { allowed.Add(id); } } if (allowed.Count == 0) { return new List { "__no_scope__" }; } if (subtree != null && subtree.Count > 0) { var sel = subtree.ToHashSet(); allowed.IntersectWith(sel); if (allowed.Count == 0) { return new List { "__no_scope__" }; } } return allowed.ToList(); } } }