ソースを参照

人事模块 计算工资

leiy 1 年間 前
コミット
221ba513c0
共有23 個のファイルを変更した3105 個の追加15 個の削除を含む
  1. 4 2
      OASystem/EntitySync/Program.cs
  2. 135 0
      OASystem/OASystem.Api/Controllers/CallbackController.cs
  3. 596 0
      OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs
  4. 5 5
      OASystem/OASystem.Api/Controllers/ResourceController.cs
  5. 2 1
      OASystem/OASystem.Api/Controllers/SmallFunController.cs
  6. 15 0
      OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/IQiYeWeChatApiService.cs
  7. 214 6
      OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/QiYeWeChatApiService.cs
  8. 11 0
      OASystem/OASystem.Domain/AutoMappers/_baseMappingProfile.cs
  9. 232 0
      OASystem/OASystem.Domain/Common/QiYeWeChatVerify/Cryptography.cs
  10. 133 0
      OASystem/OASystem.Domain/Common/QiYeWeChatVerify/Sample.cs
  11. 259 0
      OASystem/OASystem.Domain/Common/QiYeWeChatVerify/WXBizMsgCrypt.cs
  12. 21 0
      OASystem/OASystem.Domain/Dtos/CallBack/QiYeWeChat/ApproveCallBackInputDTO.cs
  13. 228 0
      OASystem/OASystem.Domain/Dtos/PersonnelModule/WageSheetDto.cs
  14. 35 0
      OASystem/OASystem.Domain/Dtos/QiYeWeChat/ApprovalData_Request.cs
  15. 38 0
      OASystem/OASystem.Domain/Dtos/QiYeWeChat/Checkin_MonthData_Request.cs
  16. 224 0
      OASystem/OASystem.Domain/Entities/PersonnelModule/Pm_WageSheet.cs
  17. 3 0
      OASystem/OASystem.Domain/Entities/System/Sys_Users.cs
  18. 1 1
      OASystem/OASystem.Domain/ViewModels/JsonView.cs
  19. 291 0
      OASystem/OASystem.Domain/ViewModels/PersonnelModule/WageSheetView.cs
  20. 278 0
      OASystem/OASystem.Domain/ViewModels/QiYeWeChat/ApprovalDataView.cs
  21. 213 0
      OASystem/OASystem.Domain/ViewModels/QiYeWeChat/CheckInView.cs
  22. 42 0
      OASystem/OASystem.Domain/ViewModels/QiYeWeChat/UserIdListView.cs
  23. 125 0
      OASystem/OASystem.Infrastructure/Repositories/PersonnelModule/WageSheetRepository.cs

+ 4 - 2
OASystem/EntitySync/Program.cs

@@ -3,6 +3,7 @@ using OASystem.Domain.Entities.Business;
 using OASystem.Domain.Entities.Customer;
 using OASystem.Domain.Entities.Financial;
 using OASystem.Domain.Entities.Groups;
+using OASystem.Domain.Entities.PersonnelModule;
 using OASystem.Domain.Entities.Resource;
 using OASystem.Domain.Entities.System;
 using SqlSugar;
@@ -104,7 +105,8 @@ db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
     //typeof(Grp_DelegationEnData),
     //typeof(Grp_EnterExitCost),
     //typeof(Grp_DayAndCost), 
-    //typeof(Grp_NationalTravelFee)
-    //typeof(Air_TicketBlackCode)
+    //typeof(Grp_NationalTravelFee),
+    //typeof(Air_TicketBlackCode),
+    //typeof(Pm_WageSheet)       //人事模块 工资表单
 });
 Console.WriteLine("数据库结构同步完成!");

+ 135 - 0
OASystem/OASystem.Api/Controllers/CallbackController.cs

@@ -0,0 +1,135 @@
+using Microsoft.AspNetCore.Mvc;
+using OASystem.Domain.Dtos.CallBack.QiYeWeChat;
+using OASystem.Domain.Dtos.SmallFun;
+using OASystem.Infrastructure.Repositories.Business;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
+
+namespace OASystem.API.Controllers
+{
+    /// <summary>
+    /// 回调地址
+    /// </summary>
+    [Route("/callback")]
+    public class CallbackController : Controller
+    {
+        private readonly IMapper _mapper;
+        private readonly ILogger<CallbackController> _logger;
+
+        #region 企业微信 通讯录通知回调key And token
+        private readonly string _qiYeWechat_Token = "WWiCDK";
+        private readonly string _qiYeWechat_EncodingAESKey = "3BWKiWnvp6xJGQ5oD3TBaOKYniNgX1g6kZZEehbM3ym";
+        private readonly string _qiYeWechat_CorpId = "wwe978bef5495a0728";
+        #endregion
+
+        public CallbackController(IMapper mapper, ILogger<CallbackController> logger)
+        {
+            _mapper = mapper;
+            _logger = logger;
+        }
+
+        #region 企业微信回调
+
+        /// <summary>
+        /// 回调通知
+        /// </summary>
+        /// <returns></returns>
+        [Route("memberschange")]
+        [HttpGet, HttpPost]
+        public async Task<ActionResult> ApproveCallBack(string msg_signature, string timestamp, string nonce, string echostr)
+        {
+            _logger.LogInformation("【企业微信】【通讯录助手】【回调】进入回调");
+            ApproveCallBackInputDTO input = new ApproveCallBackInputDTO();
+            input.msg_signature = msg_signature;
+            input.timestamp = timestamp;
+            input.nonce = nonce;
+            input.echostr = echostr;
+            _logger.LogInformation("【企业微信】【通讯录助手】【回调】【参数】"+ input.ToJson());
+            if (HttpContext.Request.Method == System.Net.Http.HttpMethod.Get.Method)
+            {
+                var model = await VerifyURLCallBack(input);
+                return Content(model, "text/xml");
+            }
+            if (HttpContext.Request.Method == System.Net.Http.HttpMethod.Post.Method)
+            {
+                var stream = Request.Body;
+                var model = await ApproveCallBack(stream, input);
+                return Content(model, "text/xml");
+            }
+            _logger.LogInformation("【企业微信】【通讯录助手】【回调】回调成功");
+            return Content("ok", "text/xml");
+        }
+
+        /// <summary>
+        /// 验证URL有效性
+        /// </summary>
+        /// <returns></returns>
+        private async Task<string> VerifyURLCallBack(ApproveCallBackInputDTO input)
+        {
+            int ret = 0;
+            string sEchoStr = "";
+            try
+            {
+                //企业微信官方加解密校验解析类
+                Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(_qiYeWechat_Token, _qiYeWechat_EncodingAESKey, _qiYeWechat_CorpId);
+                string sReqMsgSig = input.msg_signature;
+                string sReqTimeStamp = input.timestamp;
+                string sReqNonce = input.nonce;
+                string sReqEchostr = input.echostr;
+                //企业微信官方验证URL
+                ret = wxcpt.VerifyURL(sReqMsgSig, sReqTimeStamp, sReqNonce, sReqEchostr, ref sEchoStr);
+                if (ret != 0)
+                {
+                    throw new Exception($"ERR: VerifyURL fail, ret: {ret}");
+                }
+                return sEchoStr;
+            }
+            catch (Exception ex)
+            {
+                return ex.Message;
+            }
+        }
+
+        /// <summary>
+        /// 回调通知处理业务
+        /// </summary>
+        /// <returns></returns>
+        private async Task<string> ApproveCallBack(Stream context, ApproveCallBackInputDTO input)
+        {
+            var sReqData = "";
+            int ret = 0;
+            string sMsg = "";
+            try
+            {
+                //企业微信官方加解密校验解析类
+                Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(_qiYeWechat_Token, _qiYeWechat_EncodingAESKey, _qiYeWechat_CorpId);
+                string sReqMsgSig = input.msg_signature;
+                string sReqTimeStamp = input.timestamp;
+                string sReqNonce = input.nonce;
+                string sReqEchostr = input.echostr;
+                // Post请求的密文数据
+                using (var reader = new StreamReader(context))
+                {
+                    sReqData = await reader.ReadToEndAsync();
+                }
+                //回调数据
+                // 解析之后的明文
+                ret = wxcpt.DecryptMsg(sReqMsgSig, sReqTimeStamp, sReqNonce, sReqData, ref sMsg);
+                if (ret != 0)
+                {
+                    throw new Exception($"ERR: Decrypt Fail, ret: {ret}");
+                }
+                // ret==0表示解密成功,sMsg表示解密之后的明文xml串
+                //下一步处理实际业务数据了
+                return sMsg;
+            }
+            catch (Exception ex)
+            {
+                throw new Exception(ex.Message);
+            }
+        }
+
+
+
+        #endregion
+    }
+}

+ 596 - 0
OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs

@@ -0,0 +1,596 @@
+using Autofac.Diagnostics;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using OASystem.API.OAMethodLib.QiYeWeChatAPI;
+using OASystem.Domain;
+using OASystem.Domain.Dtos.PersonnelModule;
+using OASystem.Domain.Dtos.QiYeWeChat;
+using OASystem.Domain.Entities.PersonnelModule;
+using OASystem.Domain.ViewModels.JuHeExchangeRate;
+using OASystem.Domain.ViewModels.PersonnelModule;
+using OASystem.Domain.ViewModels.QiYeWeChat;
+using OASystem.Infrastructure.Repositories.PersonnelModule;
+using SqlSugar;
+using StackExchange.Redis;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace OASystem.API.Controllers
+{
+    /// <summary>
+    /// 人事模块
+    /// </summary>
+    [Route("api/[controller]/[action]")]
+    public class PersonnelModuleController : ControllerBase
+    {
+        private Result _result;
+        private readonly IMapper _mapper;
+        private readonly decimal _chengDuMinimumWage = 2100.00M;
+        private readonly IQiYeWeChatApiService _qiYeWeChatApiService;
+        private readonly WageSheetRepository _wageSheetRep;
+        private readonly UsersRepository _usersRep;
+
+
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        /// <param name="qiYeWeChatApiService"></param>
+        /// <param name="wageSheetRep"></param>
+        /// <param name="usersRep"></param>
+        /// <param name="mapper"></param>
+        public PersonnelModuleController(IQiYeWeChatApiService qiYeWeChatApiService,WageSheetRepository wageSheetRep, UsersRepository usersRep, IMapper mapper)
+        {
+            _mapper = mapper;
+            _usersRep = usersRep;
+            _qiYeWeChatApiService = qiYeWeChatApiService;
+            _wageSheetRep = wageSheetRep;
+            _result = new Result();
+        }
+
+        #region 工资表单
+
+        /// <summary>
+        /// 获取工资表单
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> GetWageSheetList(WageSheetListDto dto)
+        {
+
+            //参数处理
+            string ymFormat = "yyyy-MM";
+            DateTime yearMonthDt;
+            bool yearMonthDttIsValid = DateTime.TryParseExact(dto.YearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out yearMonthDt);
+            if (!yearMonthDttIsValid)
+            {
+                _result.Msg = "年月格式错误!正确时间格式:yyyy-MM  ";
+                return Ok(JsonView(false, _result.Msg));
+            }
+
+            //获取月工资数据
+            string yearMonth = yearMonthDt.ToString("yyyy-MM");
+            string startDt = yearMonthDt.AddDays(-1).ToString("yyyy-MM") + "-28",
+                   endDt = yearMonth + "-27";
+
+            //应发合计 = 基本工资 +绩效工资 + 岗位津贴 + 员工的岗位津贴 + 服装洗理补贴 + 通讯补贴 + 交通补贴 + 保密费 + 操作奖金+ 其他补贴 + 部门集体团建费 + 代扣保险 + 代扣公积金 + 餐补 - 个税
+
+            //事假  病假 合计
+
+            //扣款合计 = 迟到 + 早退 + 矿工 + 未打卡 + 其他扣款 
+
+            _result = await _wageSheetRep.Get_WageSheet_ListByYearMonthAsync(yearMonth);
+            if (_result.Code != 0)
+            {
+                return Ok(JsonView(false, _result.Msg));
+            }
+            
+
+            if (dto.PortType == 1)
+            {
+
+            }
+            else if (dto.PortType == 2)
+            { }
+            else if (dto.PortType == 3)
+            { }
+            else
+            {
+                return Ok(JsonView(false, "请选择正确的端口参数"));
+            }
+
+            return Ok(JsonView(true, _result.Msg, _result.Data));
+        }
+
+        /// <summary>
+        /// 获取工资 详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> GetWageSheetById(WageSheetInfoDto dto)
+        {
+            if (dto.PortType == 1)
+            {
+                _result = await _wageSheetRep.Get_WageSheet_InfoByIdAsync(dto.Id);
+                if (_result.Code != 0)
+                {
+                    return Ok(JsonView(false, _result.Msg));
+                }
+            }
+            else if (dto.PortType == 2)
+            { }
+            else if (dto.PortType == 3)
+            { }
+            else
+            {
+                return Ok(JsonView(false, "请选择正确的端口参数"));
+            }
+
+            return Ok(JsonView(true, _result.Msg, _result.Data));
+        }
+
+        /// <summary>
+        /// 人事模块 工资表单 添加 Or 修改
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> PostWageSheetAddOrEdit(WageAddOrEditDto dto)
+        {
+            try
+            {
+                _result = await _wageSheetRep.Post_WageSheet_AddOrEditAsync(dto);
+                if (_result.Code != 0)
+                {
+                    return Ok(JsonView(false, _result.Msg));
+
+                }
+            }
+            catch (Exception ex)
+            {
+
+                return Ok(JsonView(false, ex.Message));
+            }
+                
+            return Ok(JsonView(true, _result.Msg, _result.Data));
+        }
+
+
+
+        /// <summary>
+        /// 计算工资
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> SalaryCalculatorAsync(SalaryCalculatorDto dto)
+        {
+            Result result = new Result();
+
+            //参数处理
+            string ymFormat = "yyyy-MM";
+            DateTime yearMonthDt;
+            bool yearMonthDttIsValid = DateTime.TryParseExact(dto.yearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out yearMonthDt);
+
+            if (!yearMonthDttIsValid)
+            {
+                return Ok(JsonView(false, "年月格式错误!正确时间格式:yyyy-MM  "));
+            }
+
+            string thisYearMonth = dto.yearMonth;
+            string preYearMonth = yearMonthDt.AddMonths(-1).ToString("yyyy-MM");
+            //本月工资是否有数据 有数据则不计算
+            result = await _wageSheetRep.Get_WageSheet_ListByYearMonthAsync(thisYearMonth);
+            if (result.Code == 0 )
+            {
+                return Ok(JsonView(false, thisYearMonth + " 工资数据已存在,若无人员工资请手动添加!"));
+            }
+
+            //获取上个月工资信息
+            DateTime startDt = Convert.ToDateTime(yearMonthDt.AddMonths(-1).ToString("yyyy-MM") + "-28");
+            DateTime endDt = Convert.ToDateTime(yearMonthDt.ToString("yyyy-MM") + "-27");
+
+            string preWageSheetSql = string.Format(@"Select sys_u1.CnName Name,pm_ws.* From Pm_WageSheet pm_ws 
+                                                     Left Join Sys_Users sys_u1 On pm_ws.UserId = sys_u1.Id Where pm_ws.IsDel = 0 And YearMonth ='{0}'", preYearMonth);
+            List<WageSheetInfos> preWageSheetItems = await _wageSheetRep._sqlSugar.SqlQueryable<WageSheetInfos>(preWageSheetSql).ToListAsync();
+            if (preWageSheetItems.Count <= 0) 
+            {
+                return Ok(JsonView(false, preYearMonth + " 工资数据不存在,请手动添加!"));
+            }
+            
+
+            //获取OA系统内所有用户
+            var nameData = await _usersRep.GetUserNameList(1);
+            List<UserNameView> userNames = nameData.Data;
+
+            //获取所有打卡数据
+            CheckInView checkIn = await _qiYeWeChatApiService.GetCheckin_MonthDataAsync(startDt, endDt); //时间段内所有 打卡数据
+            if (checkIn.errcode != 0)
+            {
+                return Ok(JsonView(false, checkIn.errmsg));
+            }
+
+            List<Data> checkInDatas = checkIn.datas;
+
+            //获取所有打卡补卡,审批 数据
+            DateTime sp_startDt = startDt.AddDays(-10);
+            DateTime sp_centerDt = sp_startDt.AddDays(30);
+            DateTime sp_endDt = endDt.AddDays(10);
+
+
+            List<Sp_Info> sp_Infos = new List<Sp_Info>();
+            string redisName = "ApprovalData_" + sp_startDt.Year + "_" + sp_startDt.Month;
+            string sp_InfosString = await RedisRepository.RedisFactory
+                                            .CreateRedisRepository()
+                                            .StringGetAsync<string>(redisName);//string 取
+            if (string.IsNullOrEmpty(sp_InfosString))
+            {
+                ApprovalDataView approvalData_1 = await _qiYeWeChatApiService.GetApprovalDataAsync(sp_startDt, sp_centerDt);  //时间段内所有 审批数据
+                ApprovalDataView approvalData_2 = await _qiYeWeChatApiService.GetApprovalDataAsync(sp_centerDt, sp_endDt);  //时间段内所有 审批数据
+                if (approvalData_1.errcode != 0)
+                {
+                    result.Msg = "企业微信 获取时间段内审批 Msg:" + approvalData_1.errmsg;
+
+                    return Ok(JsonView(false, "企业微信 获取时间段内审批 Msg:" + approvalData_1.errmsg));
+                }
+                sp_Infos.AddRange(approvalData_1.data);
+                if (approvalData_2.errcode != 0)
+                {
+                    return Ok(JsonView(false, "企业微信 获取时间段内审批 Msg:" + approvalData_2.errmsg));
+                }
+                sp_Infos.AddRange(approvalData_2.data);
+
+                TimeSpan ts = DateTime.Now.AddMinutes(60) - DateTime.Now; //设置redis 过期时间 60分钟
+                await RedisRepository
+                    .RedisFactory
+                    .CreateRedisRepository()
+                    .StringSetAsync<string>(redisName, JsonConvert.SerializeObject(sp_Infos), ts);//string 存
+            }
+            else
+            {
+                sp_Infos = JsonConvert.DeserializeObject<List<Sp_Info>>(sp_InfosString);
+            }
+
+
+            // 筛选 时间段内(请假时间,补卡时间) /审批类型(打卡补卡,请假)/审核通过的数据 
+            List<Sp_Info> sp_leave_InfosData = sp_Infos.Where(it => it.sp_status == 2 && it.spname == "请假" && it.leave.start_time_dt >= startDt && it.leave.start_time_dt <= endDt).ToList(); //请假
+            List<Sp_Info> sp_reissuecard_InfosData = sp_Infos.Where(it => it.sp_status == 2 && it.spname == "打卡补卡" && it.comm.FillingDt >= startDt && it.comm.FillingDt <= endDt).ToList();  //补卡
+
+            
+            List<Pm_WageSheet> wageSheets = new List<Pm_WageSheet>();
+            foreach (var item in preWageSheetItems)
+            {
+                Pm_WageSheet pm_wsInfo = new Pm_WageSheet();
+                pm_wsInfo = _mapper.Map<Pm_WageSheet>(item);
+
+
+                //补贴 金额
+                decimal meal_subsidy = 0.00M;  // 午餐(午餐10元/天)  补贴 * 计算方式:单日上午请假时长(小时)大于或者等于三小时 没有餐补
+
+                //事假 病假 总金额
+                decimal personalLeaveTotal = 0.00M,  // 事假 日薪 *计算方式:日平均工资 = 月工资/当月应出勤天数。
+                        sickLeaveTotal = 0.00M;      // 病假 日薪 *计算方式:日平均工资 = 成都市最低工资标准的80%/当月应出勤天数。 短期病假=当月15天内  
+
+                //扣款金额
+                decimal beLate_deduction = 0.00M,         // 迟到 扣款金额 *计算方式:
+                                                          // 一个自然月内,不足 10 分钟的迟到/早退,不超过 2 次的部分,不做处罚;3 次及以上,按50元 / 次处罚;
+                                                          // 超过 10 分钟(含 10 分钟),不足 60 分钟的迟到/早退,按 50 元/次处罚;
+                                                          // 超过 60 分钟(含 60 分钟),不足 3 小时的迟到/早退,且无请假者,按旷工半日处理;超过3 小时的迟到 / 早退,且无请假者,按旷工一日处理。
+
+                        early_deduction = 0.00M,          // 早退 扣款金额
+                        absenteeism_deduction = 0.00M,    // 旷工 扣款金额 *计算方式:旷工扣发当日工资
+                        unprinted_deduction = 0.00M,      // 未打卡 扣款金额 *计算方式:
+                                                          // 试用期员工每月有 2 次 补卡机会,超过 2 次不足 5 次的部分,按 10 元 / 次处罚,5 次及以上的漏卡,按 50 元 / 次处罚;
+                                                          // 正式员工每月 3 次以内的补卡,按 10 元 / 次处罚,3 次及以上的漏卡,按 50 元 / 次处罚。
+                        sickLeave_deduction = 0.00M,
+                        other_deduction = 0.00M;          // 其他 扣款金额
+
+                decimal meal_deduction = 0.00M;           // 餐补 扣款金额
+                decimal reissuecard_deduction = 0.00M;    // 补卡 扣款金额
+
+
+
+                //打卡数据
+                Data? checkInData = checkInDatas.Where(it => it.base_info.name == item.Name).FirstOrDefault();
+                if (checkInData == null) { continue; }
+                string acctid = checkInData.base_info.acctid; //用户Id
+               
+
+                //当月总计数据
+                Summary_Info? summary_Info = checkInData.summary_info;
+                if (summary_Info == null ) { continue; }
+
+                int work_days = summary_Info.work_days;   //应出勤天数
+                int regular_days = summary_Info.regular_days;  //正常出勤天数
+
+                meal_subsidy = work_days * 10; //应发放餐补
+
+                #region 计算日工资 正常日薪 事假日薪 病假日薪
+
+                //月 - 应发工资
+                decimal amountPayable = pm_wsInfo.Basic + pm_wsInfo.Floats + pm_wsInfo.PostAllowance + pm_wsInfo.GarmentWashSubsidies + pm_wsInfo.CommunicationSubsidies +
+                    pm_wsInfo.TrafficSubsidies + pm_wsInfo.InformationSecurityFee + pm_wsInfo.OperationBonus + pm_wsInfo.SpecialAllowance + pm_wsInfo.OtherSubsidies + pm_wsInfo.GroupCost;
+
+                // 日薪 = 事假日薪 *计算方式:日平均工资 = 月工资/当月应出勤天数。
+                decimal dailyWage = amountPayable / work_days;
+
+                // 病假日薪 *计算方式:日平均工资 = 成都市最低工资标准的80%/当月应出勤天数。 短期病假=当月15天内 
+                decimal sickLeave_dailywage = _chengDuMinimumWage / work_days;
+
+                //病假 一天扣款  
+                sickLeave_deduction = dailyWage - sickLeave_dailywage;
+
+                #endregion
+
+                int annualLeaveNum = 0,    //年假
+                    personalLeaveNum = 0,  //事假
+                    sickLeaveNum = 0,      //病假
+                    lieuLeaveNum = 0,      //调休假
+                    marriageLeaveNum = 0,  //婚嫁
+                    maternityLeaveNum = 0, //产假
+                    paternityLeaveNum = 0, //陪产假
+                    funeralLeaveNum = 0,   //丧假
+                    reissueCardNum = 0,    //补卡 次数
+                    evectionNum = 0,       //出差 次数
+                    outIngNum = 0,         //外出 次数
+                    outWorkNum = 0,        //外勤 次数
+                    otherLeaveNum = 0;     //其他
+
+
+                #region 假勤 处理  1-请假;2-补卡;3-出差;4-外出;100-外勤;
+
+                List<Sp_Item>? sp_items = checkInData.sp_items.Where(it => it.count != 0).ToList();
+                if (sp_items != null && sp_items.Count > 0)
+                {
+                    annualLeaveNum = Fallibilitydispose(sp_items, 1, "年假");
+                    personalLeaveNum = Fallibilitydispose(sp_items, 1, "事假");
+                    sickLeaveNum = Fallibilitydispose(sp_items, 1, "病假");
+                    lieuLeaveNum = Fallibilitydispose(sp_items, 1, "调休假");
+                    marriageLeaveNum = Fallibilitydispose(sp_items, 1, "婚嫁");
+                    maternityLeaveNum = Fallibilitydispose(sp_items, 1, "产假");
+                    paternityLeaveNum = Fallibilitydispose(sp_items, 1, "陪产假");
+                    funeralLeaveNum = Fallibilitydispose(sp_items, 1, "丧假");
+                    otherLeaveNum = Fallibilitydispose(sp_items, 1, "其他");
+                    reissueCardNum = Fallibilitydispose(sp_items, 2, "补卡次数");
+                    evectionNum = Fallibilitydispose(sp_items, 3, "出差");
+                    outIngNum = Fallibilitydispose(sp_items, 4, "外出");
+                    outWorkNum = Fallibilitydispose(sp_items, 3, "外勤");
+                }
+
+                #region 请假类型金额/餐补 处理
+
+                List<Sp_Info> sp_leave_item_infosData = sp_leave_InfosData.Where(it => it.spname == "请假" && it.apply_name == item.Name).ToList();
+
+                foreach (Sp_Info sp_item in sp_leave_item_infosData)
+                {
+                    Leave? sp_leave = sp_item.leave;
+                    if (sp_leave != null)
+                    {
+                        //leave_type 1年假;2事假;3病假;4调休假;5婚假;6产假;7陪产假;8其他
+
+                        switch (sp_leave.leave_type)
+                        {
+                            case 1:    //年假
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3)
+                                    {
+                                        meal_deduction += 10;
+                                    }
+                                }
+                                break;
+                            case 2:    //事假 需调整
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                    personalLeaveTotal += dailyWage / 2;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3)
+                                    {
+                                        meal_deduction += 10;
+                                        personalLeaveTotal += dailyWage;
+                                    }
+                                }
+                                break;
+                            case 3:    //病假 需调整
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                    sickLeaveTotal += sickLeave_deduction / 2;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3) //7.5小时 一天
+                                    {
+                                        meal_deduction += 10;
+                                        sickLeaveTotal += sickLeave_deduction;
+                                    }
+                                }
+                                break;
+                            case 4:    //调休假
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3) //7.5小时 一天
+                                    {
+                                        meal_deduction += 10;
+                                    }
+                                }
+                                break;
+                            case 5:    //婚假
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3) //7.5小时 一天
+                                    {
+                                        meal_deduction += 10;
+                                    }
+                                }
+                                break;
+                            case 6:    //产假
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3) //7.5小时 一天
+                                    {
+                                        meal_deduction += 10;
+                                    }
+                                }
+                                break;
+                            case 7:    //陪产假
+                                if (sp_leave.timeunit == 0) //半天
+                                {
+                                    meal_deduction += 10;
+                                }
+                                else if (sp_leave.timeunit == 1) //小时
+                                {
+                                    if (sp_leave.duration >= 3) //7.5小时 一天
+                                    {
+                                        meal_deduction += 10;
+                                    }
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
+
+                #endregion
+
+
+                #region 补卡 处理
+
+                if (reissueCardNum == 3)
+                {
+                    reissuecard_deduction += 10;
+                }
+                else if (reissueCardNum >= 4)
+                {
+                    reissuecard_deduction += 50;
+                }
+
+                #endregion
+
+
+                #endregion
+
+                //打卡异常处理 统计 1-迟到;2-早退;3-缺卡;4-旷工;5-地点异常;6-设备异常;
+                int beLateNum = 0,           // 1-迟到;
+                    leaveEarlyNum = 0,       // 2-早退;
+                    dummyDeckNum = 0,        // 3-缺卡;
+                    minerNum = 0,            // 4-旷工;
+                    locationAnomalyNum = 0,  // 5-地点异常;
+                    unitExNum = 0;           // 6-设备异常;
+
+                if (summary_Info.except_days > 0)
+                {
+                    List<Exception_Info>? ex_infos = checkInData.exception_infos;
+                    if (ex_infos != null && ex_infos.Count >= 0)
+                    {
+                        beLateNum = ExceptionStatistics(ex_infos, 1);
+                        leaveEarlyNum = ExceptionStatistics(ex_infos, 2);
+                        dummyDeckNum = ExceptionStatistics(ex_infos, 3);
+                        minerNum = ExceptionStatistics(ex_infos, 4);
+                        locationAnomalyNum = ExceptionStatistics(ex_infos, 5);
+                        unitExNum = ExceptionStatistics(ex_infos, 6);
+                    }
+                }
+
+                #region 处理当月工资数据
+                pm_wsInfo.YearMonth = thisYearMonth;
+                pm_wsInfo.StartDate = startDt.ToString("yyyy-MM-dd");
+                pm_wsInfo.EndDate = endDt.ToString("yyyy-MM-dd");
+
+                pm_wsInfo.SickLeave = sickLeaveTotal;           //病假
+                pm_wsInfo.SomethingFalse = personalLeaveTotal;  //事假
+                pm_wsInfo.LateTo = beLate_deduction;            //迟到
+                pm_wsInfo.LeaveEarly = early_deduction;         //早退
+                pm_wsInfo.Absenteeism = absenteeism_deduction;  //旷工
+                pm_wsInfo.NotPunch = unprinted_deduction;       //未打卡
+                pm_wsInfo.OtherDeductions = other_deduction;    //其他
+
+                pm_wsInfo.Mealsupplement = meal_subsidy - meal_deduction;  //餐补
+
+                
+                decimal salaryTotal = amountPayable; //应发合计
+                //decimal eductionTotal =  ;
+                decimal actualReleaseTotal = 0.00M; //实发合计
+
+                pm_wsInfo.LastUpdateUserId = dto.UserId;
+                pm_wsInfo.LastUpdateDt = DateTime.Now;
+                pm_wsInfo.CreateUserId = dto.UserId;
+                pm_wsInfo.CreateTime = DateTime.Now;
+
+                #endregion
+
+
+                wageSheets.Add(pm_wsInfo);
+            }
+           
+
+            
+
+            return Ok(JsonView(true,"操作成功!", wageSheets));
+        }
+
+        /// <summary>
+        /// 打卡数据
+        /// 假勤数据 统计
+        /// </summary>
+        /// <param name="datas">数据源</param>
+        /// <param name="type">
+        /// 1-请假;2-补卡;3-出差;4-外出;100-外勤;
+        /// </param>
+        /// <param name="subTypeName">
+        /// 年假 事假 病假 调休假 婚嫁 产假  陪产假 丧假 补卡次数 出差 外出数 外勤 其他
+        /// </param>
+        /// <returns></returns>
+        private int Fallibilitydispose(List<Sp_Item> datas, int type,string? subTypeName)
+        {
+            int num = 0;
+
+            Sp_Item _Info = datas.Where(it => it.type == type && it.name == subTypeName).FirstOrDefault();
+            if (_Info != null) { num = _Info.count; }
+
+            return num;
+        }
+
+        /// <summary>
+        /// 打卡数据
+        /// 异常数据 统计
+        /// </summary>
+        /// <returns></returns>
+        private int ExceptionStatistics(List<Exception_Info> datas,int type)
+        {
+            int num = 0;
+
+            Exception_Info _Info = datas.Where(it => it.exception == type).FirstOrDefault();
+            if (_Info != null) { num = _Info.count; }
+
+            return num;
+        }
+
+        #endregion
+
+    }
+}

+ 5 - 5
OASystem/OASystem.Api/Controllers/ResourceController.cs

@@ -18,6 +18,7 @@ namespace OASystem.API.Controllers
     public class ResourceController : ControllerBase
     {
         private readonly IMapper _mapper;
+        private readonly SqlSugarClient _sqlSugar;
         private readonly IConfiguration _config;
         private readonly CarDataRepository _carDataRep;
         private readonly LocalGuideDataRepository _localGuideDataRep;
@@ -31,14 +32,13 @@ namespace OASystem.API.Controllers
         private readonly InvitationOfficialActivityDataRepository _InvitationOfficialActivityDataRep;
         private readonly OfficialActivitiesRepository _officialActivitiesRep;
         private readonly AskDataRepository _askDataRep;
-        private readonly SqlSugarClient _sqlSugar;
         private readonly TicketBlackCodeRepository _ticketBlackCodeRep;
 
-        public ResourceController(IMapper mapper, IConfiguration config, SqlSugarClient sqlSugar, CarDataRepository carDataRep,
-            LocalGuideDataRepository localGuideDataRep, ThreeCodeRepository threeCodeRep,
-            HotelDataRepository hotelDataRep, ResItemInfoRepository resItemInfoRep, SetDataRepository setDataRepository,
+        public ResourceController(IMapper mapper, IConfiguration config, SqlSugarClient sqlSugar, CarDataRepository carDataRep,LocalGuideDataRepository localGuideDataRep, 
+            ThreeCodeRepository threeCodeRep,HotelDataRepository hotelDataRep, ResItemInfoRepository resItemInfoRep, SetDataRepository setDataRepository,
             CountryFeeRepository countryFeeRep, SetDataTypeRepository setDataTypeRep, AirTicketAgentRepository airTicketAgentRep,
-            InvitationOfficialActivityDataRepository invitationOfficialActivityDataRep, OfficialActivitiesRepository officialActivitiesRep, AskDataRepository askDataRep, TicketBlackCodeRepository ticketBlackCodeRep)
+            InvitationOfficialActivityDataRepository invitationOfficialActivityDataRep, OfficialActivitiesRepository officialActivitiesRep, AskDataRepository askDataRep, 
+            TicketBlackCodeRepository ticketBlackCodeRep)
         {
             _mapper = mapper;
             _config = config;

+ 2 - 1
OASystem/OASystem.Api/Controllers/SmallFunController.cs

@@ -34,9 +34,10 @@ namespace OASystem.API.Controllers
         private readonly IConfiguration _config;
 
         /// <summary>
-        /// 初始化
+        /// 
         /// </summary>
         /// <param name="juHeApiService"></param>
+        /// <param name="config"></param>
         public SmallFunController(IJuHeApiService juHeApiService, IConfiguration config)
         {
             this._juHeApiService = juHeApiService;

+ 15 - 0
OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/IQiYeWeChatApiService.cs

@@ -27,6 +27,21 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
         /// <returns></returns>
         Task<ResponseBase> CreateAsync(Create_Request create_Request);
 
+        /// <summary>
+        /// 获取月打卡数据
+        /// </summary>
+        /// <param name="startDt"></param>
+        /// <param name="endDt"></param>
+        /// <returns></returns>
+        Task<CheckInView> GetCheckin_MonthDataAsync(DateTime startDt, DateTime endDt);
+
+        /// <summary>
+        /// 获取审批数据(旧)
+        /// </summary>
+        /// <param name="startDt"></param>
+        /// <param name="endDt"></param>
+        /// <returns></returns>
+        Task<ApprovalDataView> GetApprovalDataAsync(DateTime startDt, DateTime endDt);
 
     }
 }

+ 214 - 6
OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/QiYeWeChatApiService.cs

@@ -2,6 +2,7 @@
 using OASystem.Domain.ViewModels.JuHeExchangeRate;
 using OASystem.Domain.ViewModels.QiYeWeChat;
 using Org.BouncyCastle.Ocsp;
+using System.Collections.Generic;
 using System.Text.Json;
 using Ubiety.Dns.Core;
 
@@ -21,6 +22,10 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
         private readonly string Email_AgentId = "1000004";   //E-Mail Id
         private readonly string Email_Corpsecret = "NA1zbJM15GmgjPYwDOqz59dIo1Wnug-MbU107MeUemc"; //E-Mail 凭证密钥
         private readonly string AddressBook_Corpsecret = "Y1tnjh7j-BvbqAytAoXZPUbmDR6dqLTL6mXtc6PZ7fo";  //通讯录同步 凭证密钥
+        private readonly string Approve_AgentId = "3010040";     //审批 Id
+        private readonly string Approve_Corpsecret = "k_Jo69Jw9Hqg_in-Rypbs30PNbxOYa1t4e-dxYuT-kw";  //审批 凭证密钥
+
+        private readonly DateTime _1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);
         private readonly JobPostRepository _jobPostRep;
 
         /// <summary>
@@ -42,6 +47,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
         /// 2:打卡
         /// 3:邮件
         /// 4:通讯录同步
+        /// 5:审批
         /// </param>
         /// <returns></returns>
         private async Task<Access_TokenView> GetTokenAsync(int applicationType)
@@ -65,11 +71,16 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
                 access_Token.corpsecret = Email_Corpsecret; //E-Mail
                 cacheName = "Enail_Access_Token";
             }
-            else if (applicationType == 4)
+            else if (applicationType == 4)       //通讯录同步
             {
-                access_Token.corpsecret = AddressBook_Corpsecret; //通讯录同步
+                access_Token.corpsecret = AddressBook_Corpsecret; 
                 cacheName = "AddressBook_Access_Token";
             }
+            else if (applicationType == 5) //审批
+            {
+                access_Token.corpsecret = Approve_Corpsecret;
+                cacheName = "Approve_Access_Token";
+            }
             else
             {
                 tokenResult.errmsg = "未识别应用类型Id!";
@@ -116,6 +127,44 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
             return tokenResult;
         }
 
+        #region 添加员工
+
+        /// <summary>
+        /// 获取成员ID列表
+        /// </summary>
+        /// <returns></returns>
+        private async Task<UserIdListView> GetUserIdListAsync()
+        {
+            UserIdListView userIdListView = new UserIdListView();
+            Access_TokenView access_Token = await GetTokenAsync(4);
+            if (access_Token.errcode != 0)
+            {
+                userIdListView.errcode = access_Token.errcode;
+                userIdListView.errmsg = access_Token.errmsg;
+                return userIdListView;
+            }
+
+            string url = string.Format("/cgi-bin/user/list_id?access_token={0}&debug=1", access_Token.access_token);
+
+            var jsonData = new
+            {
+                access_token = access_Token.access_token, //调用接口凭证
+            };
+
+            var json = System.Text.Json.JsonSerializer.Serialize(jsonData);
+            var content = new StringContent(json, Encoding.UTF8, "application/json");
+            var create_Req = await _httpClient.PostAsync(url, content);
+            var stringResponse = await create_Req.Content.ReadAsStringAsync();
+
+            userIdListView = System.Text.Json.JsonSerializer.Deserialize<UserIdListView>(stringResponse,
+                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+
+
+
+
+            return userIdListView;
+        }
+
         /// <summary>
         /// 获取部门列表
         /// </summary>
@@ -125,7 +174,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
         {
             DepartmentListView result = new DepartmentListView();
 
-            string dep_url = string.Format("/cgi-bin/department/list?access_token={0}", access_token);
+            string dep_url = string.Format("/cgi-bin/department/list?access_token={0}&debug=1", access_token);
 
 
             var depReq = await _httpClient.GetAsync(dep_url);
@@ -166,7 +215,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
 
             if (jobPost != null)
             {
-                depName = jobPost.DepName; 
+                depName = jobPost.DepName;
                 jobName = jobPost.JobName;
             }
             create_Request.position = jobName;
@@ -185,12 +234,19 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
                 create_Request.access_token = access_Token.access_token;
 
                 #region 处理部门
-                DepartmentListView depData = await GetDepartmentAsync(access_Token.access_token);
+                Access_TokenView dep_Access_Token = await GetTokenAsync(1);
+                if (dep_Access_Token.errcode != 0)
+                {
+                    responseBase.errcode = dep_Access_Token.errcode;
+                    responseBase.errmsg = dep_Access_Token.errmsg;
+                }
+
+                DepartmentListView depData = await GetDepartmentAsync(dep_Access_Token.access_token);
 
                 if (depData.errcode == 0)
                 {
                     var depData1 = depData.department.Where(x => x.name == depName).FirstOrDefault();
-                    if (depData1 != null) 
+                    if (depData1 != null)
                     {
                         create_Request.department = new List<long> { depData1.id };
                     }
@@ -231,5 +287,157 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
 
             return responseBase;
         }
+        #endregion
+
+        #region 打卡
+
+        /// <summary>
+        /// 获取月打卡数据
+        /// </summary>
+        /// <param name="startDt"></param>
+        /// <param name="endDt"></param>
+        /// <returns></returns>
+        public async Task<CheckInView> GetCheckin_MonthDataAsync(DateTime startDt,DateTime endDt)
+        {
+            CheckInView checkInView = new CheckInView();
+
+            string checkInDatastring = await RedisRepository.RedisFactory
+                                                    .CreateRedisRepository()
+                                                    .StringGetAsync<string>("Checkin_MonthData");//List<T> 取
+
+
+            if (!string.IsNullOrEmpty(checkInDatastring))
+            {
+
+                checkInView = JsonConvert.DeserializeObject<CheckInView>(checkInDatastring);
+                return checkInView;
+            }
+
+            //获取员工Id
+            UserIdListView userIdListView = await GetUserIdListAsync();
+            if (userIdListView.errcode != 0)
+            {
+                checkInView.errcode = userIdListView.errcode;
+                checkInView.errmsg = string.Format("【企业微信】【获取员工IdList】【Msg】{0}",userIdListView.errmsg);
+                return checkInView;
+            }
+
+            if (userIdListView.dept_user == null || userIdListView.dept_user.Count <= 0)
+            {
+                checkInView.errmsg = string.Format("【企业微信】【获取员工IdList】【Msg】未查出员工Id");
+                return checkInView;
+            }
+
+            //获取月打卡数据 token
+            Access_TokenView access_Token = await GetTokenAsync(2);
+            if (access_Token.errcode != 0)
+            {
+                checkInView.errcode = access_Token.errcode;
+                checkInView.errmsg = string.Format("【企业微信】【获取月打卡数据】【Token】【Msg】{0}", access_Token.errmsg);
+                return checkInView;
+            }
+
+            string url = string.Format("/cgi-bin/checkin/getcheckin_monthdata?access_token={0}", access_Token.access_token);
+
+            Checkin_MonthData_Request checkInReq = new Checkin_MonthData_Request();
+            checkInReq.access_token = access_Token.access_token;
+            
+            checkInReq.starttime = (uint)(startDt - _1970).TotalSeconds;
+            checkInReq.endtime = (uint)(endDt - _1970).TotalSeconds;
+            checkInReq.useridlist = userIdListView.dept_user.Select(it => it.userid).ToList();
+
+            var json = System.Text.Json.JsonSerializer.Serialize(checkInReq);
+            var content = new StringContent(json, Encoding.UTF8, "application/json");
+            var create_Req = await _httpClient.PostAsync(url, content);
+            var stringResponse = await create_Req.Content.ReadAsStringAsync();
+
+            checkInView = System.Text.Json.JsonSerializer.Deserialize<CheckInView>(stringResponse,
+                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+
+
+            TimeSpan ts = DateTime.Now.AddMinutes(60) - DateTime.Now; //设置redis 过期时间 60分钟
+            await RedisRepository
+                .RedisFactory
+                .CreateRedisRepository()
+                .StringSetAsync<string>("Checkin_MonthData", JsonConvert.SerializeObject(checkInView), ts);//List<T> 存
+
+            return checkInView;
+        }
+
+        #endregion
+
+        #region 审批
+
+        /// <summary>
+        /// 获取审批数据(旧)
+        /// </summary>
+        /// <param name="startDt"></param>
+        /// <param name="endDt"></param>
+        /// <returns></returns>
+        public async Task<ApprovalDataView> GetApprovalDataAsync(DateTime startDt, DateTime endDt)
+        {
+            ApprovalDataView approvalDataView = new ApprovalDataView();
+
+            //获取审批数据 token
+            Access_TokenView access_Token = await GetTokenAsync(5);
+            if (access_Token.errcode != 0)
+            {
+                approvalDataView.errcode = access_Token.errcode;
+                approvalDataView.errmsg = string.Format("【企业微信】【获取审批数据】【Token】【Msg】{0}", access_Token.errmsg);
+                return approvalDataView;
+            }
+
+            string url = string.Format("/cgi-bin/corp/getapprovaldata?access_token={0}", access_Token.access_token);
+
+            ApprovalData_Request approvalDataReq = new ApprovalData_Request();
+            approvalDataReq.access_token = access_Token.access_token;
+            approvalDataReq.starttime = (uint)(startDt - _1970).TotalSeconds;
+            approvalDataReq.endtime = (uint)(endDt - _1970).TotalSeconds;
+
+            var json = System.Text.Json.JsonSerializer.Serialize(approvalDataReq);
+            var content = new StringContent(json, Encoding.UTF8, "application/json");
+            var create_Req = await _httpClient.PostAsync(url, content);
+            var stringResponse = await create_Req.Content.ReadAsStringAsync();
+
+            approvalDataView = System.Text.Json.JsonSerializer.Deserialize<ApprovalDataView>(stringResponse,
+                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+
+            List<Sp_Info> sp_datas = new List<Sp_Info>();
+            sp_datas.AddRange(approvalDataView.data);
+
+            int index = 0;
+            //多次访问审批接口
+            if (approvalDataView.total>=100)
+            {
+                approvalDataView.total -= 100;
+                int forTotal = approvalDataView.total % 100 == 0 ? approvalDataView.total / 100 : approvalDataView.total / 100 + 1;
+
+                long? next_spnum = approvalDataView.next_spnum;
+                approvalDataReq.next_spnum = next_spnum;
+
+                
+                for (int i = 0; i < forTotal; i++)
+                {
+                    index++;
+                    var for_json = System.Text.Json.JsonSerializer.Serialize(approvalDataReq);
+                    var for_content = new StringContent(for_json, Encoding.UTF8, "application/json");
+                    var for_create_Req = await _httpClient.PostAsync(url, for_content);
+                    var for_stringResponse = await for_create_Req.Content.ReadAsStringAsync();
+
+                    ApprovalDataView for_approvalDataView = System.Text.Json.JsonSerializer.Deserialize<ApprovalDataView>(for_stringResponse,
+                        new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+
+                    approvalDataReq.next_spnum = for_approvalDataView.next_spnum;  //重新定义游标
+                    sp_datas.AddRange(for_approvalDataView.data); //追加数据
+                }
+
+                approvalDataView.total += 100;
+            }
+
+            approvalDataView.data = sp_datas;
+            return approvalDataView;
+        }
+
+        #endregion
     }
 }

+ 11 - 0
OASystem/OASystem.Domain/AutoMappers/_baseMappingProfile.cs

@@ -18,6 +18,9 @@ using OASystem.Domain.Entities.Financial;
 using static OASystem.Domain.Dtos.CRM.NewClientDataQueryDto;
 using OASystem.Domain.ViewModels.Groups;
 using OASystem.Domain.ViewModels.CRM;
+using OASystem.Domain.ViewModels.PersonnelModule;
+using OASystem.Domain.Entities.PersonnelModule;
+using OASystem.Domain.Dtos.PersonnelModule;
 
 namespace OASystem.Domain.AutoMappers
 {
@@ -203,6 +206,14 @@ namespace OASystem.Domain.AutoMappers
             CreateMap<EditDailyFeePaymentContentDto, Fin_DailyFeePaymentContent>();
 
             #endregion
+
+            #region 人事 模块
+
+            //CreateMap<WageSheetInfoView, Pm_WageSheet>();
+            CreateMap<Pm_WageSheet, WageSheetItemInfoView>();
+            CreateMap<WageAddOrEditDto, Pm_WageSheet>();
+            CreateMap<WageSheetInfos, Pm_WageSheet>();
+            #endregion
         }
     }
 }

+ 232 - 0
OASystem/OASystem.Domain/Common/QiYeWeChatVerify/Cryptography.cs

@@ -0,0 +1,232 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Security.Cryptography;
+using System.IO;
+using System.Net;
+namespace Tencent
+{
+    class Cryptography
+    {
+        public static UInt32 HostToNetworkOrder(UInt32 inval)
+        {
+            UInt32 outval = 0;
+            for (int i = 0; i < 4; i++)
+                outval = (outval << 8) + ((inval >> (i * 8)) & 255);
+            return outval;
+        }
+
+        public static Int32 HostToNetworkOrder(Int32 inval)
+        {
+            Int32 outval = 0;
+            for (int i = 0; i < 4; i++)
+                outval = (outval << 8) + ((inval >> (i * 8)) & 255);
+            return outval;
+        }
+        /// <summary>
+        /// 解密方法
+        /// </summary>
+        /// <param name="Input">密文</param>
+        /// <param name="EncodingAESKey"></param>
+        /// <returns></returns>
+        /// 
+        public static string AES_decrypt(String Input, string EncodingAESKey, ref string corpid)
+        {
+            byte[] Key;
+            Key = Convert.FromBase64String(EncodingAESKey + "=");
+            byte[] Iv = new byte[16];
+            Array.Copy(Key, Iv, 16);
+            byte[] btmpMsg = AES_decrypt(Input, Iv, Key);
+
+            int len = BitConverter.ToInt32(btmpMsg, 16);
+            len = IPAddress.NetworkToHostOrder(len);
+
+
+            byte[] bMsg = new byte[len];
+            byte[] bCorpid = new byte[btmpMsg.Length - 20 - len];
+            Array.Copy(btmpMsg, 20, bMsg, 0, len);
+            Array.Copy(btmpMsg, 20+len , bCorpid, 0, btmpMsg.Length - 20 - len);
+            string oriMsg = Encoding.UTF8.GetString(bMsg);
+            corpid = Encoding.UTF8.GetString(bCorpid);
+
+            
+            return oriMsg;
+        }
+
+        public static String AES_encrypt(String Input, string EncodingAESKey, string corpid)
+        {
+            byte[] Key;
+            Key = Convert.FromBase64String(EncodingAESKey + "=");
+            byte[] Iv = new byte[16];
+            Array.Copy(Key, Iv, 16);
+            string Randcode = CreateRandCode(16);
+            byte[] bRand = Encoding.UTF8.GetBytes(Randcode);
+            byte[] bCorpid = Encoding.UTF8.GetBytes(corpid);
+            byte[] btmpMsg = Encoding.UTF8.GetBytes(Input);
+            byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length));
+            byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bCorpid.Length + btmpMsg.Length];
+
+            Array.Copy(bRand, bMsg, bRand.Length);
+            Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length);
+            Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length);
+            Array.Copy(bCorpid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bCorpid.Length);
+   
+            return AES_encrypt(bMsg, Iv, Key);
+
+        }
+        private static string CreateRandCode(int codeLen)
+        {
+            string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z";
+            if (codeLen == 0)
+            {
+                codeLen = 16;
+            }
+            string[] arr = codeSerial.Split(',');
+            string code = "";
+            int randValue = -1;
+            Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
+            for (int i = 0; i < codeLen; i++)
+            {
+                randValue = rand.Next(0, arr.Length - 1);
+                code += arr[randValue];
+            }
+            return code;
+        }
+
+        private static String AES_encrypt(String Input, byte[] Iv, byte[] Key)
+        {
+            var aes = new RijndaelManaged();
+            //秘钥的大小,以位为单位
+            aes.KeySize = 256;
+            //支持的块大小
+            aes.BlockSize = 128;
+            //填充模式
+            aes.Padding = PaddingMode.PKCS7;
+            aes.Mode = CipherMode.CBC;
+            aes.Key = Key;
+            aes.IV = Iv;
+            var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
+            byte[] xBuff = null;
+
+            using (var ms = new MemoryStream())
+            {
+                using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
+                {
+                    byte[] xXml = Encoding.UTF8.GetBytes(Input);
+                    cs.Write(xXml, 0, xXml.Length);
+                }
+                xBuff = ms.ToArray();
+            }
+            String Output = Convert.ToBase64String(xBuff);
+            return Output;
+        }
+
+        private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key)
+        {
+            var aes = new RijndaelManaged();
+            //秘钥的大小,以位为单位
+            aes.KeySize = 256;
+            //支持的块大小
+            aes.BlockSize = 128;
+            //填充模式
+            //aes.Padding = PaddingMode.PKCS7;
+            aes.Padding = PaddingMode.None;
+            aes.Mode = CipherMode.CBC;
+            aes.Key = Key;
+            aes.IV = Iv;
+            var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
+            byte[] xBuff = null;
+
+            #region 自己进行PKCS7补位,用系统自己带的不行
+            byte[] msg = new byte[Input.Length + 32 - Input.Length % 32];
+            Array.Copy(Input, msg, Input.Length);
+            byte[] pad = KCS7Encoder(Input.Length);
+            Array.Copy(pad, 0, msg, Input.Length, pad.Length);
+            #endregion
+
+            #region 注释的也是一种方法,效果一样
+            //ICryptoTransform transform = aes.CreateEncryptor();
+            //byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length);
+            #endregion
+
+            using (var ms = new MemoryStream())
+            {
+                using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
+                {
+                    cs.Write(msg, 0, msg.Length);
+                }
+                xBuff = ms.ToArray();
+            }
+
+            String Output = Convert.ToBase64String(xBuff);
+            return Output;
+        }
+
+        private static byte[] KCS7Encoder(int text_length)
+        {
+            int block_size = 32;
+            // 计算需要填充的位数
+            int amount_to_pad = block_size - (text_length % block_size);
+            if (amount_to_pad == 0)
+            {
+                amount_to_pad = block_size;
+            }
+            // 获得补位所用的字符
+            char pad_chr = chr(amount_to_pad);
+            string tmp = "";
+            for (int index = 0; index < amount_to_pad; index++)
+            {
+                tmp += pad_chr;
+            }
+            return Encoding.UTF8.GetBytes(tmp);
+        }
+        /**
+         * 将数字转化成ASCII码对应的字符,用于对明文进行补码
+         * 
+         * @param a 需要转化的数字
+         * @return 转化得到的字符
+         */
+        static char chr(int a)
+        {
+
+            byte target = (byte)(a & 0xFF);
+            return (char)target;
+        }
+        private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key)
+        {
+            RijndaelManaged aes = new RijndaelManaged();
+            aes.KeySize = 256;
+            aes.BlockSize = 128;
+            aes.Mode = CipherMode.CBC;
+            aes.Padding = PaddingMode.None;
+            aes.Key = Key;
+            aes.IV = Iv;
+            var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);
+            byte[] xBuff = null;
+            using (var ms = new MemoryStream())
+            {
+                using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
+                {
+                    byte[] xXml = Convert.FromBase64String(Input);
+                    byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];
+                    Array.Copy(xXml, msg, xXml.Length);
+                    cs.Write(xXml, 0, xXml.Length);
+                }
+                xBuff = decode2(ms.ToArray());
+            }
+            return xBuff;
+        }
+        private static byte[] decode2(byte[] decrypted)
+        {
+            int pad = (int)decrypted[decrypted.Length - 1];
+            if (pad < 1 || pad > 32)
+            {
+                pad = 0;
+            }
+            byte[] res = new byte[decrypted.Length - pad];
+            Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad);
+            return res;
+        }
+    }
+}

File diff suppressed because it is too large
+ 133 - 0
OASystem/OASystem.Domain/Common/QiYeWeChatVerify/Sample.cs


+ 259 - 0
OASystem/OASystem.Domain/Common/QiYeWeChatVerify/WXBizMsgCrypt.cs

@@ -0,0 +1,259 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using System.Collections;
+//using System.Web;
+using System.Security.Cryptography;
+//-40001 : 签名验证错误
+//-40002 :  xml解析失败
+//-40003 :  sha加密生成签名失败
+//-40004 :  AESKey 非法
+//-40005 :  corpid 校验错误
+//-40006 :  AES 加密失败
+//-40007 : AES 解密失败
+//-40008 : 解密后得到的buffer非法
+//-40009 :  base64加密异常
+//-40010 :  base64解密异常
+namespace Tencent
+{
+   public class WXBizMsgCrypt
+    {
+        string m_sToken;
+        string m_sEncodingAESKey;
+        string m_sReceiveId;
+        enum WXBizMsgCryptErrorCode
+        {
+            WXBizMsgCrypt_OK = 0,
+            WXBizMsgCrypt_ValidateSignature_Error = -40001,
+            WXBizMsgCrypt_ParseXml_Error = -40002,
+            WXBizMsgCrypt_ComputeSignature_Error = -40003,
+            WXBizMsgCrypt_IllegalAesKey = -40004,
+            WXBizMsgCrypt_ValidateCorpid_Error = -40005,
+            WXBizMsgCrypt_EncryptAES_Error = -40006,
+            WXBizMsgCrypt_DecryptAES_Error = -40007,
+            WXBizMsgCrypt_IllegalBuffer = -40008,
+            WXBizMsgCrypt_EncodeBase64_Error = -40009,
+            WXBizMsgCrypt_DecodeBase64_Error = -40010
+        };
+
+        //构造函数
+	    // @param sToken: 企业微信后台,开发者设置的Token
+	    // @param sEncodingAESKey: 企业微信后台,开发者设置的EncodingAESKey
+	    // @param sReceiveId: 不同场景含义不同,详见文档说明
+        public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sReceiveId)
+        {
+            m_sToken = sToken;
+            m_sReceiveId = sReceiveId;
+            m_sEncodingAESKey = sEncodingAESKey;
+        }
+
+        //验证URL
+        // @param sMsgSignature: 签名串,对应URL参数的msg_signature
+        // @param sTimeStamp: 时间戳,对应URL参数的timestamp
+        // @param sNonce: 随机串,对应URL参数的nonce
+        // @param sEchoStr: 随机串,对应URL参数的echostr
+        // @param sReplyEchoStr: 解密之后的echostr,当return返回0时有效
+        // @return:成功0,失败返回对应的错误码
+        public int VerifyURL(string sMsgSignature, string sTimeStamp, string sNonce, string sEchoStr, ref string sReplyEchoStr)
+        {
+            int ret = 0;
+			if (m_sEncodingAESKey.Length!=43)
+			{
+				return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
+			}
+            ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEchoStr, sMsgSignature);
+            if (0 != ret)
+            {
+                return ret;
+            }
+            sReplyEchoStr = "";
+            string cpid = "";
+            try
+            {
+                sReplyEchoStr = Cryptography.AES_decrypt(sEchoStr, m_sEncodingAESKey, ref cpid); //m_sReceiveId);
+            }
+            catch (Exception)
+            {
+                sReplyEchoStr = "";
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;
+            }
+            if (cpid != m_sReceiveId)
+            {
+                sReplyEchoStr = "";
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateCorpid_Error;
+            }
+            return 0;
+        }
+
+        // 检验消息的真实性,并且获取解密后的明文
+        // @param sMsgSignature: 签名串,对应URL参数的msg_signature
+        // @param sTimeStamp: 时间戳,对应URL参数的timestamp
+        // @param sNonce: 随机串,对应URL参数的nonce
+        // @param sPostData: 密文,对应POST请求的数据
+        // @param sMsg: 解密后的原文,当return返回0时有效
+        // @return: 成功0,失败返回对应的错误码
+        public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg)
+        {
+			if (m_sEncodingAESKey.Length!=43)
+			{
+				return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
+			}
+            XmlDocument doc = new XmlDocument();
+            XmlNode root;
+            string sEncryptMsg;
+            try
+            {
+                doc.LoadXml(sPostData);
+                root = doc.FirstChild;
+                sEncryptMsg = root["Encrypt"].InnerText;
+            }
+            catch (Exception)
+            {
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error;
+            }
+            //verify signature
+            int ret = 0;
+            ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature);
+            if (ret != 0)
+                return ret;
+            //decrypt
+            string cpid = "";
+            try
+            {
+                sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid);
+            }
+            catch (FormatException)
+            {
+                sMsg = "";
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error;
+            }
+            catch (Exception)
+            {
+                sMsg = "";
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;
+            }
+            if (cpid != m_sReceiveId)
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateCorpid_Error;
+            return 0;
+        }
+
+        //将企业号回复用户的消息加密打包
+        // @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串
+        // @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp
+        // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
+        // @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
+        //						当return返回0时有效
+        // return:成功0,失败返回对应的错误码
+        public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg)
+        {
+			if (m_sEncodingAESKey.Length!=43)
+			{
+				return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
+			}
+            string raw = "";
+            try
+            {
+                raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sReceiveId);
+            }
+            catch (Exception)
+            {
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error;
+            }
+            string MsgSigature = "";
+            int ret = 0;
+            ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature);
+            if (0 != ret)
+                return ret;
+            sEncryptMsg = "";
+
+            string EncryptLabelHead = "<Encrypt><![CDATA[";
+            string EncryptLabelTail = "]]></Encrypt>";
+            string MsgSigLabelHead = "<MsgSignature><![CDATA[";
+            string MsgSigLabelTail = "]]></MsgSignature>";
+            string TimeStampLabelHead = "<TimeStamp><![CDATA[";
+            string TimeStampLabelTail = "]]></TimeStamp>";
+            string NonceLabelHead = "<Nonce><![CDATA[";
+            string NonceLabelTail = "]]></Nonce>";
+            sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail;
+            sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;
+            sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;
+            sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;
+            sEncryptMsg += "</xml>";
+            return 0;
+        }
+
+        public class DictionarySort : System.Collections.IComparer
+        {
+            public int Compare(object oLeft, object oRight)
+            {
+                string sLeft = oLeft as string;
+                string sRight = oRight as string;
+                int iLeftLength = sLeft.Length;
+                int iRightLength = sRight.Length;
+                int index = 0;
+                while (index < iLeftLength && index < iRightLength)
+                {
+                    if (sLeft[index] < sRight[index])
+                        return -1;
+                    else if (sLeft[index] > sRight[index])
+                        return 1;
+                    else
+                        index++;
+                }
+                return iLeftLength - iRightLength;
+
+            }
+        }
+        //Verify Signature
+        private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture)
+        {
+            string hash = "";
+            int ret = 0;
+            ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash);
+            if (ret != 0)
+                return ret;
+            if (hash == sSigture)
+                return 0;
+            else
+            {
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error;
+            }
+        }
+
+        public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature)
+        {
+            ArrayList AL = new ArrayList();
+            AL.Add(sToken);
+            AL.Add(sTimeStamp);
+            AL.Add(sNonce);
+            AL.Add(sMsgEncrypt);
+            AL.Sort(new DictionarySort());
+            string raw = "";
+            for (int i = 0; i < AL.Count; ++i)
+            {
+                raw += AL[i];
+            }
+
+            SHA1 sha;
+            ASCIIEncoding enc;
+            string hash = "";
+            try
+            {
+                sha = new SHA1CryptoServiceProvider();
+                enc = new ASCIIEncoding();
+                byte[] dataToHash = enc.GetBytes(raw);
+                byte[] dataHashed = sha.ComputeHash(dataToHash);
+                hash = BitConverter.ToString(dataHashed).Replace("-", "");
+                hash = hash.ToLower();
+            }
+            catch (Exception)
+            {
+                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error;
+            }
+            sMsgSignature = hash;
+            return 0;
+        }
+    }
+}

+ 21 - 0
OASystem/OASystem.Domain/Dtos/CallBack/QiYeWeChat/ApproveCallBackInputDTO.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Dtos.CallBack.QiYeWeChat
+{
+    /// <summary>
+    /// 企业微信
+    /// 回调通知
+    /// </summary>
+    public class ApproveCallBackInputDTO
+    {
+        public string? msg_signature { get; set; }
+
+        public string? timestamp { get; set; }
+        public string? nonce { get; set; }
+        public string? echostr { get; set; }
+    }
+}

+ 228 - 0
OASystem/OASystem.Domain/Dtos/PersonnelModule/WageSheetDto.cs

@@ -0,0 +1,228 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Dtos.PersonnelModule
+{
+    /// <summary>
+    /// 人事模块
+    /// 工资表单 Dto
+    /// </summary>
+    public class WageSheetDto
+    {
+    }
+
+    /// <summary>
+    /// 工资表单List Dto
+    /// </summary>
+    public class WageSheetListDto : PortDtoBase
+    {
+        /// <summary>
+        /// 年月
+        /// </summary>
+        public string? YearMonth { get; set; }
+
+        ///// <summary>
+        ///// 开始日期
+        ///// </summary>
+        //public string? StartDt { get; set; }
+
+        ///// <summary>
+        ///// 结束日期
+        ///// </summary>
+        //public string? EndDt { get; set; }
+    }
+
+    /// <summary>
+    /// 工资表单Info Dto
+    /// </summary>
+    public class WageSheetInfoDto : PortDtoBase
+    {
+        /// <summary>
+        /// id
+        /// </summary>
+        public int Id { get; set; }
+
+    }
+
+
+    /// <summary>
+    /// 添加或者修改
+    /// </summary>
+    public class WageAddOrEditDto : OpBaseDto
+    {
+        /// <summary>
+        /// 用户Id
+        /// </summary>
+        public int UserId { get; set; }
+
+        /// <summary>
+        /// 年月
+        /// </summary>
+        public string? YearMonth { get; set; }
+
+        /// <summary>
+        /// 工资日期 起
+        /// </summary>
+        public string? StartDate { get; set; }
+
+        /// <summary>
+        /// 工资日期 止
+        /// </summary>
+        public string? EndDate { get; set; }
+
+        /// <summary>
+        /// 基本工资
+        /// </summary>
+        public decimal Basic { get; set; }
+
+        /// <summary>
+        /// 绩效工资
+        /// </summary>
+        public decimal Floats { get; set; }
+
+        /// <summary>
+        /// 岗位津贴
+        /// </summary>
+        public decimal PostAllowance { get; set; }
+
+        /// <summary>
+        /// 服装洗理补贴
+        /// </summary>
+        public decimal GarmentWashSubsidies { get; set; }
+
+        /// <summary>
+        /// 通讯补贴
+        /// </summary>
+        public decimal CommunicationSubsidies { get; set; }
+
+        /// <summary>
+        /// 交通补贴
+        /// </summary>
+        public decimal TrafficSubsidies { get; set; }
+
+        /// <summary>
+        /// 保密费
+        /// </summary>
+        public decimal InformationSecurityFee { get; set; }
+
+        /// <summary>
+        /// 操作奖金
+        /// </summary>
+        public decimal OperationBonus { get; set; }
+
+        /// <summary>
+        /// 特殊津贴
+        /// </summary>
+        public decimal SpecialAllowance { get; set; }
+
+        /// <summary>
+        /// 其他补贴
+        /// </summary>
+        public decimal OtherSubsidies { get; set; }
+
+        /// <summary>
+        /// 代扣保险
+        /// </summary>
+        public decimal WithholdingInsurance { get; set; }
+
+        /// <summary>
+        /// 餐补
+        /// </summary>
+        public decimal Mealsupplement { get; set; }
+
+        /// <summary>
+        /// 代扣公积金
+        /// </summary>
+        public decimal ReservedFunds { get; set; }
+
+
+        /// <summary>
+        /// 部门集体团建费
+        /// </summary>
+        public decimal GroupCost { get; set; }
+
+        /// <summary>
+        /// 病假
+        /// </summary>
+        public decimal SickLeave { get; set; }
+
+        /// <summary>
+        /// 事假
+        /// </summary>
+        public decimal SomethingFalse { get; set; }
+
+        /// <summary>
+        /// 迟到 
+        /// 计算次数
+        /// </summary>
+        public decimal LateTo { get; set; }
+
+        /// <summary>
+        /// 早退 
+        /// 计算次数
+        /// </summary>
+        public decimal LeaveEarly { get; set; }
+
+        /// <summary>
+        /// 旷工 
+        /// 小时计算 7.5小时一天
+        /// </summary>
+        public decimal Absenteeism { get; set; }
+
+        /// <summary>
+        /// 未打卡
+        /// </summary>
+        public decimal NotPunch { get; set; }
+
+        /// <summary>
+        /// 其他扣款
+        /// </summary>
+        public decimal OtherDeductions { get; set; }
+
+        /// <summary>
+        /// 应发合计
+        /// </summary>
+        public decimal Should { get; set; }
+
+        /// <summary>
+        /// 扣款合计
+        /// </summary>
+        public decimal TotalDeductions { get; set; }
+
+        /// <summary>
+        /// 实发合计
+        /// </summary>
+        public decimal TotalRealHair { get; set; }
+
+        /// <summary>
+        /// 代扣个税
+        /// </summary>
+        public decimal WithholdingTax { get; set; }
+
+        /// <summary>
+        /// 税后工资
+        /// </summary>
+        public decimal AfterTax { get; set; }
+
+
+    }
+
+    /// <summary>
+    /// 计算工资 Dto
+    /// </summary>
+    public class SalaryCalculatorDto
+    {
+        /// <summary>
+        /// 员工Id
+        /// </summary>
+        public int UserId { get; set; }
+
+        /// <summary>
+        /// 年月
+        /// </summary>
+        public string? yearMonth { get; set; }
+    }
+}

+ 35 - 0
OASystem/OASystem.Domain/Dtos/QiYeWeChat/ApprovalData_Request.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Dtos.QiYeWeChat
+{
+    /// <summary>
+    /// 企业微信 
+    /// 审批数据 Dto
+    /// </summary>
+    public class ApprovalData_Request
+    {
+        /// <summary>
+        /// 调用接口凭证。必须使用审批应用的secret获取
+        /// </summary>
+        public string? access_token { get; set; }
+
+        /// <summary>
+        /// 获取审批记录的开始时间。Unix时间戳
+        /// </summary>
+        public uint starttime { get; set; }
+
+        /// <summary>
+        /// 获取审批记录的结束时间。Unix时间戳
+        /// </summary>
+        public uint endtime { get; set; }
+
+        /// <summary>
+        /// 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取
+        /// </summary>
+        public long? next_spnum { get; set; }
+    }
+}

+ 38 - 0
OASystem/OASystem.Domain/Dtos/QiYeWeChat/Checkin_MonthData_Request.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Dtos.QiYeWeChat
+{
+    /// <summary>
+    /// 企业微信 打卡 
+    /// 打卡月报数据 Request
+    /// </summary>
+    public class Checkin_MonthData_Request
+    {
+        /// <summary>
+        /// 调用接口凭证,必须使用打卡应用的Secret获取access_token
+        /// </summary>
+        public string? access_token { get; set; }
+
+        /// <summary>
+        /// 获取月报的开始时间。0点Unix时间戳
+        /// </summary>
+        public uint starttime { get; set; }
+
+        /// <summary>
+        /// 获取月报的结束时间。0点Unix时间戳
+        /// </summary>
+        public uint endtime { get; set; }
+
+        /// <summary>
+        ///  -
+        /// 不少于1字节
+        /// 不多于64字节
+        /// 可填充个数:1 ~ 100
+        /// </summary>
+        public List<string>? useridlist { get; set; }
+    }
+}

+ 224 - 0
OASystem/OASystem.Domain/Entities/PersonnelModule/Pm_WageSheet.cs

@@ -0,0 +1,224 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Entities.PersonnelModule
+{
+    /// <summary>
+    /// 人事模块
+    /// 工资表单
+    /// </summary>
+    [SugarTable("Pm_WageSheet")]
+    public class Pm_WageSheet:EntityBase
+    {
+        /// <summary>
+        /// 员工Id
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public int UserId { get; set; }
+
+        /// <summary>
+        /// 年月
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string? YearMonth { get; set; }
+
+        /// <summary>
+        /// 工资日期 起
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string? StartDate { get; set; }
+
+        /// <summary>
+        /// 工资日期 止
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string? EndDate { get; set; }
+
+        /// <summary>
+        /// 基本工资
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal Basic { get; set; }
+
+        /// <summary>
+        /// 绩效工资
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal Floats { get; set; }
+
+        /// <summary>
+        /// 岗位津贴
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal PostAllowance { get; set; }
+
+        /// <summary>
+        /// 服装洗理补贴
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal GarmentWashSubsidies { get; set; }
+
+        /// <summary>
+        /// 通讯补贴
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal CommunicationSubsidies { get; set; }
+
+        /// <summary>
+        /// 交通补贴
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal TrafficSubsidies { get; set; }
+
+        /// <summary>
+        /// 保密费
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal InformationSecurityFee { get; set; }
+
+        /// <summary>
+        /// 操作奖金
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal OperationBonus { get; set; }
+
+        /// <summary>
+        /// 特殊津贴
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal SpecialAllowance { get; set; }
+
+        /// <summary>
+        /// 其他补贴
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal OtherSubsidies { get; set; }
+
+        /// <summary>
+        /// 代扣保险
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal WithholdingInsurance { get; set; }
+
+        /// <summary>
+        /// 餐补
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal Mealsupplement { get; set; }
+
+        /// <summary>
+        /// 代扣公积金
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal ReservedFunds { get; set; }
+
+        /// <summary>
+        /// 部门集体团建费
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal GroupCost { get; set; }
+
+        /// <summary>
+        /// 病假
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal SickLeave { get; set; }
+
+        /// <summary>
+        /// 事假
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal SomethingFalse { get; set; }
+
+        /// <summary>
+        /// 迟到 
+        /// 计算次数
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal LateTo { get; set; }
+
+        /// <summary>
+        /// 早退 
+        /// 计算次数
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal LeaveEarly { get; set; }
+
+        /// <summary>
+        /// 旷工 
+        /// 小时计算 7.5小时一天
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal Absenteeism { get; set; }
+
+        /// <summary>
+        /// 未打卡
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal NotPunch { get; set; }
+
+        /// <summary>
+        /// 其他扣款
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal OtherDeductions { get; set; }
+
+        /// <summary>
+        /// 应发合计
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal Should { get; set; }
+
+        /// <summary>
+        /// 扣款合计
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal TotalDeductions { get; set; }
+
+        /// <summary>
+        /// 实发合计
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal TotalRealHair { get; set; }
+
+        /// <summary>
+        /// 代扣个税
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal WithholdingTax { get; set; }
+
+        /// <summary>
+        /// 税后工资
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(8,2)")]
+        public decimal AfterTax { get; set; }
+
+        /// <summary>
+        /// 锁定标识
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public decimal IsLock { get; set; } = 0;
+
+        /// <summary>
+        /// 是否确认
+        /// 0 否 1 是
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")] 
+        public int IsSure { get; set; } = 0;
+
+        /// <summary>
+        /// 最后操作人
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public int LastUpdateUserId { get; set; }
+
+        /// <summary>
+        /// 最后操作时间
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "datetime")]
+        public DateTime LastUpdateDt { get; set; } = DateTime.Now;
+    }
+}

+ 3 - 0
OASystem/OASystem.Domain/Entities/System/Sys_Users.cs

@@ -113,13 +113,16 @@ namespace OASystem.Domain.Entities.System
         /// </summary>
         [SugarColumn(IsNullable = true,ColumnDataType ="varchar(50)")]
         public string Professional { get; set; }
+
         /// <summary>
         /// 学历
+        /// 0 未设置 1 小学、2 初中、3 高中、4 专科、5 本科、6 研究生
         /// </summary>
         [SugarColumn(IsNullable = true,ColumnDataType ="int")]
         public int Education { get; set; }
         /// <summary>
         /// 学历类型
+        ///0 未设置 1 成教 2 统招
         /// </summary>
         [SugarColumn(IsNullable = true,ColumnDataType ="int")]
         public int TheOrAdultEducation { get; set; }

+ 1 - 1
OASystem/OASystem.Domain/ViewModels/JsonView.cs

@@ -20,5 +20,5 @@ public class JsonView
     /// <summary>
     /// 数据
     /// </summary>
-    public object? Data { get; set; } = new { };
+    public object? Data { get; set; } = new { } ;
 }

+ 291 - 0
OASystem/OASystem.Domain/ViewModels/PersonnelModule/WageSheetView.cs

@@ -0,0 +1,291 @@
+using OASystem.Domain.Entities.PersonnelModule;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.ViewModels.PersonnelModule
+{
+    /// <summary>
+    /// 工资表详情
+    /// </summary>
+    public class WageSheetView:Pm_WageSheet 
+    {
+      
+    }
+
+    /// <summary>
+    /// 工资表单详情
+    /// </summary>
+    public class WageSheetInfoView 
+    {
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 员工Id
+        /// </summary>
+        public int UserId { get; set; }
+
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        public string? Name { get; set; }
+
+        /// <summary>
+        /// 年月
+        /// </summary>
+        public string? YearMonth { get; set; }
+
+        /// <summary>
+        /// 工资日期 起
+        /// </summary>
+        public string? StartDate { get; set; }
+
+        /// <summary>
+        /// 工资日期 止
+        /// </summary>
+        public string? EndDate { get; set; }
+
+        /// <summary>
+        /// 基本工资
+        /// </summary>
+        public decimal Basic { get; set; }
+
+        /// <summary>
+        /// 绩效工资
+        /// </summary>
+        public decimal Floats { get; set; }
+
+        /// <summary>
+        /// 岗位津贴
+        /// </summary>
+        public decimal PostAllowance { get; set; }
+
+        /// <summary>
+        /// 服装洗理补贴
+        /// </summary>
+        public decimal GarmentWashSubsidies { get; set; }
+
+        /// <summary>
+        /// 通讯补贴
+        /// </summary>
+        public decimal CommunicationSubsidies { get; set; }
+
+        /// <summary>
+        /// 交通补贴
+        /// </summary>
+        public decimal TrafficSubsidies { get; set; }
+
+        /// <summary>
+        /// 保密费
+        /// </summary>
+        public decimal InformationSecurityFee { get; set; }
+
+        /// <summary>
+        /// 操作奖金
+        /// </summary>
+        public decimal OperationBonus { get; set; }
+
+        /// <summary>
+        /// 特殊津贴
+        /// </summary>
+        public decimal SpecialAllowance { get; set; }
+
+        /// <summary>
+        /// 其他补贴
+        /// </summary>
+        public decimal OtherSubsidies { get; set; }
+
+        /// <summary>
+        /// 代扣保险
+        /// </summary>
+        public decimal WithholdingInsurance { get; set; }
+
+        /// <summary>
+        /// 餐补
+        /// </summary>
+        public decimal Mealsupplement { get; set; }
+
+        /// <summary>
+        /// 代扣公积金
+        /// </summary>
+        public decimal ReservedFunds { get; set; }
+
+
+        /// <summary>
+        /// 部门集体团建费
+        /// </summary>
+        public decimal GroupCost { get; set; }
+
+        /// <summary>
+        /// 病假
+        /// </summary>
+        public decimal SickLeave { get; set; }
+
+        /// <summary>
+        /// 事假
+        /// </summary>
+        public decimal SomethingFalse { get; set; }
+
+        /// <summary>
+        /// 迟到 
+        /// 计算次数
+        /// </summary>
+        public decimal LateTo { get; set; }
+
+        /// <summary>
+        /// 早退 
+        /// </summary>
+        public decimal LeaveEarly { get; set; }
+
+        /// <summary>
+        /// 旷工 
+        /// 小时计算 7.5小时一天
+        /// </summary>
+        public decimal Absenteeism { get; set; }
+
+        /// <summary>
+        /// 未打卡
+        /// </summary>
+        public decimal NotPunch { get; set; }
+
+        /// <summary>
+        /// 其他扣款
+        /// </summary>
+        public decimal OtherDeductions { get; set; }
+
+        /// <summary>
+        /// 应发合计
+        /// </summary>
+        public decimal Should { get; set; }
+
+        /// <summary>
+        /// 扣款合计
+        /// </summary>
+        public decimal TotalDeductions { get; set; }
+
+        /// <summary>
+        /// 实发合计
+        /// </summary>
+        public decimal TotalRealHair { get; set; }
+
+        /// <summary>
+        /// 代扣个税
+        /// </summary>
+        public decimal WithholdingTax { get; set; }
+
+        /// <summary>
+        /// 税后工资
+        /// </summary>
+        public decimal AfterTax { get; set; }
+
+        /// <summary>
+        /// 锁定标识
+        /// </summary>
+        public decimal IsLock { get; set; }
+
+        /// <summary>
+        /// 最后操作人
+        /// </summary>
+        public int LastUpdateUserId { get; set; }
+
+        /// <summary>
+        /// 最后操作人Name
+        /// </summary>
+        public string? LastUpdateUserName { get; set; }
+
+        /// <summary>
+        /// 最后操作时间
+        /// </summary>
+        public string? LastUpdateDt { get; set; }
+
+        /// <summary>
+        /// 备注
+        /// </summary>
+        public string? Remark { get; set; }
+    }
+
+
+    public class WageSheetItemInfoView
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 年月
+        /// </summary>
+        public string? YearMonth { get; set; }
+
+        /// <summary>
+        /// 工资日期 起
+        /// </summary>
+        public string? StartDate { get; set; }
+
+        /// <summary>
+        /// 工资日期 止
+        /// </summary>
+        public string? EndDate { get; set; }
+
+        /// <summary>
+        /// 员工Id
+        /// </summary>
+        public int UserId { get; set; }
+
+        /// <summary>
+        /// 员工Name
+        /// </summary>
+        public string? Name { get; set; }
+
+        /// <summary>
+        /// 应发合计
+        /// </summary>
+        public decimal Should { get; set; }
+
+        /// <summary>
+        /// 扣款合计
+        /// </summary>
+        public decimal TotalDeductions { get; set; }
+
+        /// <summary>
+        /// 实发合计
+        /// </summary>
+        public decimal TotalRealHair { get; set; }
+
+        /// <summary>
+        /// 代扣个税
+        /// </summary>
+        public decimal WithholdingTax { get; set; }
+
+        /// <summary>
+        /// 税后工资
+        /// </summary>
+        public decimal AfterTax { get; set; }
+
+        /// <summary>
+        /// 最后操作人
+        /// </summary>
+        public int LastUpdateUserId { get; set; }
+
+        /// <summary>
+        /// 员工Name
+        /// </summary>
+        public string? LastUpdateUserName { get; set; }
+
+        /// <summary>
+        /// 最后操作时间
+        /// </summary>
+        public string? LastUpdateDt { get; set; }
+    }
+
+
+    /// <summary>
+    /// 工资列表信息
+    /// </summary>
+    public class WageSheetInfos:Pm_WageSheet
+    {
+        public string Name { get; set; }
+    }
+}

+ 278 - 0
OASystem/OASystem.Domain/ViewModels/QiYeWeChat/ApprovalDataView.cs

@@ -0,0 +1,278 @@
+using Google.Protobuf.WellKnownTypes;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.ViewModels.QiYeWeChat
+{
+    /// <summary>
+    /// 企业微信 审批 View
+    /// </summary>
+    public class ApprovalDataView : ResponseBase
+    {
+        /// <summary>
+        /// 拉取的审批单个数,最大值为100,当total参数大于100时,可运用next_spnum参数进行多次拉取
+        /// </summary>
+        public int Count { get; set; }
+
+        /// <summary>
+        /// 时间段内的总审批单个数
+        /// </summary>
+        public int total { get; set; }
+
+        /// <summary>
+        /// 拉取列表的最后一个审批单号
+        /// </summary>
+        public long next_spnum { get; set; }
+
+        /// <summary>
+        /// 审批Data
+        /// </summary>
+        public List<Sp_Info>? data { get; set; }
+    }
+
+    /// <summary>
+    /// 审批基本信息
+    /// </summary>
+    public class Sp_Info 
+    {
+        /// <summary>
+        /// 审批名称(请假,报销,自定义审批名称)
+        /// </summary>
+        public string? spname { get; set; }
+
+        /// <summary>
+        /// 申请人姓名
+        /// </summary>
+        public string? apply_name { get; set; }
+
+        /// <summary>
+        /// 申请人部门
+        /// </summary>
+        public string? apply_org { get; set; }
+
+        /// <summary>
+        /// 审批人姓名
+        /// </summary>
+        public List<string>? approval_name { get; set; }
+
+        /// <summary>
+        /// 抄送人姓名
+        /// </summary>
+        public List<string>? notify_name { get; set; }
+
+        /// <summary>
+        /// 审批状态:1审批中;2 已通过;3已驳回;4已取消;6通过后撤销;10已支付
+        /// </summary>
+        public int sp_status { get; set; }
+
+        /// <summary>
+        /// 审批单号
+        /// </summary>
+        public long sp_num { get; set; }
+
+        /// <summary>
+        /// 请假类型(只有请假模板审批记录有此数据项)
+        /// </summary>
+        public Leave? leave { get; set; }
+
+        /// <summary>
+        /// 审批模板信息
+        /// </summary>
+        public Comm? comm { get; set; }
+
+        ///// <summary>
+        ///// 补卡时间
+        ///// 打卡补卡 筛选使用
+        ///// </summary>
+        //public DateTime? comm_applydata_dt
+        //{
+        //    get
+        //    {
+        //        DateTime? dt = null;
+        //        if (comm == null) return dt;
+
+        //        if (comm.FillingDt != null)
+        //        {
+        //            dt = comm.FillingDt;
+        //        }
+        //        return dt;
+        //    }
+        //}
+
+
+        /// <summary>
+        /// 审批的附件media_id,可使用media/get获取附件
+        /// </summary>
+        public List<string>? mediaids { get; set; }
+
+        /// <summary>
+        /// 审批单提交时间 
+        /// Unix时间戳
+        /// </summary>
+        public uint apply_time { get; set; }
+
+        /// <summary>
+        /// 审批单提交时间 
+        /// datetime
+        /// </summary>
+        public DateTime apply_time_dt
+        {
+            get
+            {
+                return new DateTime(long.Parse(apply_time.ToString()) * 10000000 + 621355968000000000L).ToLocalTime();
+            }
+        }
+
+        /// <summary>
+        /// 审批单提交者的userid
+        /// </summary>
+        public string? apply_user_id { get; set; }
+
+    }
+
+    /// <summary>
+    /// 审批类型
+    /// </summary>
+    public class Leave
+    {
+        /// <summary>
+        /// 请假时间单位:0半天;1小时
+        /// </summary>
+        public int timeunit { get; set; }
+
+        /// <summary>
+        /// 请假类型
+        /// 1年假;2事假;3病假;4调休假;5婚假;6产假;7陪产假;8其他
+        /// </summary>
+        public int leave_type { get; set; }
+
+        /// <summary>
+        /// 请假开始时间,unix时间
+        /// </summary>
+        public uint start_time { get; set; }
+
+        /// <summary>
+        /// 请假开始时间,datetime时间
+        /// </summary>
+        public DateTime start_time_dt
+        {
+            get
+            {
+                return new DateTime(long.Parse(start_time.ToString()) * 10000000 + 621355968000000000L).ToLocalTime();
+            }
+        }
+
+        /// <summary>
+        /// 请假结束时间,unix时间
+        /// </summary>
+        public uint end_time { get; set; }
+
+        /// <summary>
+        /// 请假开始时间,datetime时间
+        /// </summary>
+        public DateTime end_time_dt
+        {
+            get
+            {
+                return new DateTime(long.Parse(end_time.ToString()) * 10000000 + 621355968000000000L).ToLocalTime();
+            }
+        }
+
+
+        /// <summary>
+        /// 请假时长,单位小时
+        /// </summary>
+        public int duration { get; set; }
+
+        /// <summary>
+        /// 请假事由
+        /// </summary>
+        public string? reason { get; set; }
+
+    }
+
+    /// <summary>
+    /// 审批模板信息
+    /// </summary>
+    public class Comm
+    {
+        /// <summary>
+        /// 模板数据
+        /// </summary>
+        public string? apply_data { get; set; }
+
+        public List<ApplyInfo>? applydata
+        {
+            get
+            {
+                List<ApplyInfo> applyInfos = new List<ApplyInfo>();
+
+                if (!string.IsNullOrEmpty(apply_data)) 
+                {
+                    applyInfos = JsonConvert.DeserializeObject<List<ApplyInfo>>(apply_data);
+
+                }
+                return applyInfos;
+            }
+        }
+
+        /// <summary>
+        /// 补卡时间
+        /// 筛选使用
+        /// </summary>
+        public DateTime? FillingDt {
+
+            get 
+            {
+                DateTime? dt = null;
+                if (applydata != null && applydata.Count > 0)
+                {
+                    ApplyInfo applyInfo = applydata.Where(it => it.id == "checkin-time").FirstOrDefault();
+                    if (applyInfo != null)
+                    {
+                        dt = applyInfo.valueDt;
+
+                    }
+                }
+
+                return dt;
+
+            }
+
+        }
+    }
+
+    /// <summary>
+    /// 
+    /// </summary>
+    public class ApplyInfo
+    {
+        public string? id { get; set; }
+
+        public string? title { get; set; }
+
+        public string? type { get; set; }
+
+        public object? value { get; set; }
+
+        public DateTime? valueDt
+        {
+            get
+            {
+                if (id == "checkin-time" && value != null)
+                {
+                    long timeSpan = long.Parse(value.ToString()) / 1000;
+                    return new DateTime(timeSpan * 10000000 + 621355968000000000L).ToLocalTime();
+                }
+
+                return null;
+            }
+        } 
+    }
+}

+ 213 - 0
OASystem/OASystem.Domain/ViewModels/QiYeWeChat/CheckInView.cs

@@ -0,0 +1,213 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.ViewModels.QiYeWeChat
+{
+    /// <summary>
+    /// 企业微信 API
+    /// 打卡月数据
+    /// </summary>
+    public class CheckInView:ResponseBase
+    {
+        /// <summary>
+        /// 基础信息
+        /// </summary>
+        public List<Data>? datas { get; set; }
+    }
+
+    /// <summary>
+    /// 基础信息 Data
+    /// </summary>
+    public class Data
+    {
+        /// <summary>
+        /// 基础信息
+        /// </summary>
+        public Base_Info? base_info { get; set; }
+
+        /// <summary>
+        /// 汇总信息
+        /// </summary>
+        public Summary_Info? summary_info { get; set; }
+
+        /// <summary>
+        /// 异常状态统计信息
+        /// </summary>
+        public List<Exception_Info>? exception_infos { get; set; }
+
+        /// <summary>
+        /// 假勤统计信息
+        /// </summary>
+        public List<Sp_Item>? sp_items { get; set; }
+
+        /// <summary>
+        /// 加班情况
+        /// </summary>
+        public Overwork_Info? overwork_info { get; set; }
+    }
+
+    /// <summary>
+    /// 成员基础信息
+    /// </summary>
+    public class Base_Info
+    {
+        /// <summary>
+        /// 记录类型:1-固定上下班;2-外出(此报表中不会出现外出打卡数据);3-按班次上下班;4-自由签到;5-加班;7-无规则
+        /// </summary>
+        public int record_type { get; set; }
+
+        /// <summary>
+        /// 打卡人员姓名
+        /// </summary>
+        public string? name { get; set; }
+
+        /// <summary>
+        /// 打卡人员别名
+        /// </summary>
+        public string? name_ex { get; set; }
+
+        /// <summary>
+        /// 打卡人员所在部门,会显示所有所在部门
+        /// </summary>
+        public string? departs_name { get; set; }
+
+        /// <summary>
+        /// 打卡人员账号,即userid
+        /// </summary>
+        public string? acctid { get; set; }
+
+        /// <summary>
+        /// 打卡人员所属规则信息
+        /// </summary>
+        public Rule_Info? rule_info { get; set; }
+    }
+
+    /// <summary>
+    /// 打卡人员所属规则信息
+    /// </summary>
+    public class Rule_Info
+    {
+        /// <summary>
+        /// 所属规则的id
+        /// </summary>
+        public int groupid { get; set; }
+
+        /// <summary>
+        /// 打卡规则名
+        /// </summary>
+        public string? groupname { get; set; }
+
+    }
+
+    /// <summary>
+    /// 汇总信息
+    /// </summary>
+    public class Summary_Info
+    {
+        /// <summary>
+        /// 应打卡天数
+        /// </summary>
+        public int work_days { get; set; }
+
+        /// <summary>
+        /// 正常天数
+        /// </summary>
+        public int regular_days { get; set; }
+
+        /// <summary>
+        /// 异常天数
+        /// </summary>
+        public int except_days { get; set; }
+
+        /// <summary>
+        /// 实际工作时长,为统计周期每日实际工作时长之和
+        /// </summary>
+        public int regular_work_sec { get; set; }
+
+        /// <summary>
+        /// 标准工作时长,为统计周期每日标准工作时长之和
+        /// </summary>
+        public int standard_work_sec { get; set; }
+    }
+
+    /// <summary>
+    /// 异常状态统计信息
+    /// </summary>
+    public class Exception_Info
+    {
+        /// <summary>
+        /// 异常类型:1-迟到;2-早退;3-缺卡;4-旷工;5-地点异常;6-设备异常
+        /// </summary>
+        public int exception { get; set; }
+
+        /// <summary>
+        /// 异常次数,为统计周期内每日此异常次数之和
+        /// </summary>
+        public int count { get; set; }
+
+        /// <summary>
+        /// 异常时长(迟到/早退/旷工才有值),为统计周期内每日此异常时长之和
+        /// </summary>
+        public int duration { get; set; }
+    }
+
+    /// <summary>
+    /// 假勤统计信息
+    /// </summary>
+    public class Sp_Item
+    {
+        /// <summary>
+        /// 假勤类型:1-请假;2-补卡;3-出差;4-外出;100-外勤
+        /// </summary>
+        public int type { get; set; }
+
+        /// <summary>
+        /// 具体请假类型,当type为1请假时,具体的请假类型id,可通过审批相关接口获取假期详情
+        /// </summary>
+        public int vacation_id { get; set; }
+
+        /// <summary>
+        /// 假勤次数,为统计周期内每日此假勤发生次数之和
+        /// </summary>
+        public int count { get; set; }
+
+        /// <summary>
+        /// 假勤时长,为统计周期内每日此假勤发生时长之和,时长单位为天直接除以86400即为天数,单位为小时直接除以3600即为小时数
+        /// </summary>
+        public int duration { get; set; }
+
+        /// <summary>
+        /// 时长单位:0-按天 1-按小时
+        /// </summary>
+        public int time_type { get; set; }
+
+        /// <summary>
+        /// 统计项名称
+        /// </summary>
+        public string? name { get; set; }
+    }
+
+    /// <summary>
+    /// 加班情况
+    /// </summary>
+    public class Overwork_Info
+    {
+        /// <summary>
+        /// 工作日加班时长
+        /// </summary>
+        public int? workday_over_sec { get; set; }
+
+        /// <summary>
+        /// 节假日加班时长
+        /// </summary>
+        public int? holidays_over_sec { get; set; }
+
+        /// <summary>
+        /// 休息日加班时长
+        /// </summary>
+        public int? restdays_over_sec { get; set; }
+    }
+}

+ 42 - 0
OASystem/OASystem.Domain/ViewModels/QiYeWeChat/UserIdListView.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.ViewModels.QiYeWeChat
+{
+    /// <summary>
+    /// 企业微信
+    /// 用户Id List  View
+    /// </summary>
+    public class UserIdListView : ResponseBase
+    {
+        /// <summary>
+        /// 分页游标,下次请求时填写以获取之后分页的记录。如果该字段返回空则表示已没有更多数据
+        /// </summary>
+        public string? next_cursor { get; set; }
+
+        /// <summary>
+        /// 用户-部门关系列表
+        /// </summary>
+        public List<UserIdInfo>? dept_user { get; set; }
+    }
+
+    /// <summary>
+    /// 企业微信
+    /// 用户Id Info  View
+    /// </summary>
+    public class UserIdInfo
+    {
+        /// <summary>
+        /// 用户userid,当用户在多个部门下时会有多条记录
+        /// </summary>
+        public string? userid { get; set; }
+
+        /// <summary>
+        /// 用户所属部门
+        /// </summary>
+        public uint department { get; set; }
+    }
+}

+ 125 - 0
OASystem/OASystem.Infrastructure/Repositories/PersonnelModule/WageSheetRepository.cs

@@ -0,0 +1,125 @@
+using AutoMapper;
+using OASystem.Domain.Dtos.PersonnelModule;
+using OASystem.Domain.Entities.PersonnelModule;
+using OASystem.Domain.ViewModels.PersonnelModule;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SqlSugar;
+using Result = OASystem.Domain.Result;
+
+namespace OASystem.Infrastructure.Repositories.PersonnelModule
+{
+    /// <summary>
+    /// 人事模块 工资表 仓库
+    /// </summary>
+    public class WageSheetRepository : BaseRepository<Pm_WageSheet, WageSheetView>
+    {
+        private readonly IMapper _mapper;
+        private Result _result;
+
+        public WageSheetRepository(SqlSugarClient sqlSugar, IMapper mapper)
+            : base(sqlSugar)
+        {
+            _mapper = mapper;
+            _result = new Result();
+        }
+
+        /// <summary>
+        /// 查询工资 List
+        /// </summary>
+        /// <returns></returns>
+        public async Task<Result> Get_WageSheet_ListByYearMonthAsync(string yearMonth )
+        {
+            if (string.IsNullOrEmpty(yearMonth))
+            {
+                _result.Msg = "参数为空";
+                return _result;
+            }
+
+            string sql = string.Format(@"Select sys_u1.CnName Name,sys_u2.CnName LastUpdateUserName,pm_ws.Id, 
+                                         pm_ws.YearMonth,pm_ws.StartDate,pm_ws.EndDate,pm_ws.UserId,pm_ws.Should,
+                                         pm_ws.TotalDeductions,pm_ws.TotalRealHair,pm_ws.WithholdingTax,pm_ws.AfterTax
+                                         From Pm_WageSheet pm_ws
+                                         Left Join Sys_Users sys_u1 On pm_ws.UserId = sys_u1.Id
+                                         Left Join Sys_Users sys_u2 On pm_ws.LastUpdateUserId = sys_u2.Id
+                                         Where pm_ws.IsDel = 0 And pm_ws.YearMonth = '{0}'", yearMonth);
+            var wageSheetList = await _sqlSugar.SqlQueryable<WageSheetItemInfoView>(sql).ToListAsync();
+
+            if (wageSheetList.Count <= 0 )
+            {
+                _result.Msg = "暂无数据!";
+                return _result;
+            }
+
+            _result.Code = 0;
+            _result.Msg = "查询成功!";
+            _result.Data = wageSheetList;
+
+            return _result;
+        }
+
+        /// <summary>
+        /// 查询工资 Info
+        /// </summary>
+        /// <returns></returns>
+        public async Task<Result> Get_WageSheet_InfoByIdAsync(int id)
+        {
+            string sql = string.Format(@"Select sys_u1.CnName Name,sys_u2.CnName LastUpdateUserName,pm_ws.* From Pm_WageSheet pm_ws
+                                         Left Join Sys_Users sys_u1 On pm_ws.UserId = sys_u1.Id
+                                         Left Join Sys_Users sys_u2 On pm_ws.LastUpdateUserId = sys_u2.Id
+                                         Where pm_ws.IsDel = 0 And pm_ws.Id = {0}", id);
+            var wageSheetInfo = await _sqlSugar.SqlQueryable<WageSheetInfoView>(sql).ToListAsync();
+
+            _result.Code = 0;
+            _result.Msg = "查询成功!";
+            _result.Data = wageSheetInfo;
+
+            return _result;
+        }
+
+        /// <summary>
+        /// 修改
+        /// </summary>
+        /// <returns></returns>
+        public async Task<Result> Post_WageSheet_AddOrEditAsync(WageAddOrEditDto dto)
+        {
+            Pm_WageSheet pm_WageSheet = new Pm_WageSheet();
+            pm_WageSheet = _mapper.Map<Pm_WageSheet>(dto);
+            pm_WageSheet.LastUpdateUserId = dto.CreateUserId;
+
+            if (dto.Status == 1) //添加
+            {
+                int add = await _sqlSugar.Insertable(pm_WageSheet).ExecuteReturnIdentityAsync();
+                if (add <= 0)
+                {
+                    _result.Msg = "操作失败!";
+                    return _result;
+                }
+            }
+            else if(dto.Status == 2) //修改
+            {
+                int update = await _sqlSugar.Updateable(pm_WageSheet)
+                                            .IgnoreColumns(it =>  new { it.CreateUserId, it.CreateTime,it.DeleteUserId,it.DeleteTime })
+                                            .WhereColumns(it => new { it.Id })
+                                            .ExecuteCommandAsync();
+                if (update <= 0)
+                {
+                    _result.Msg = "操作失败!";
+                    return _result;
+                }
+            }
+            else{
+                _result.Msg = "请输入操作状态!1 添加 2 修改";
+            }
+
+            _result.Code = 0;
+            _result.Msg = "操作成功!";
+
+            return _result;
+        }
+
+    }
+}