二次分配日志及细节

parent f40cbd61
......@@ -12,6 +12,7 @@
using System.Threading.Tasks;
using Performance.Infrastructure;
using Performance.DtoModels.Second;
using Microsoft.Extensions.Logging;
namespace Performance.Api.Controllers
{
......@@ -21,6 +22,7 @@ namespace Performance.Api.Controllers
[ApiController]
public class SecondAllotController : ControllerBase
{
private readonly ILogger<SecondAllotController> _logger;
private readonly ClaimService claimService;
private readonly AllotService _allotService;
private readonly SecondAllotService secondAllotService;
......@@ -29,6 +31,7 @@ public class SecondAllotController : ControllerBase
private readonly RedistributionService _redistributionService;
public SecondAllotController(
ILogger<SecondAllotController> logger,
ClaimService claimService,
AllotService allotService,
SecondAllotService secondAllotService,
......@@ -37,6 +40,7 @@ public class SecondAllotController : ControllerBase
RedistributionService redistributionService
)
{
_logger = logger;
this.claimService = claimService;
_allotService = allotService;
this.secondAllotService = secondAllotService;
......@@ -549,6 +553,8 @@ public ApiResponse AutoCompleteBodyData([FromRoute] int secondId, SecondEmployee
[HttpPost]
public ApiResponse RedistributionLoad([FromBody] SecondLoadDto request)
{
try
{
if (!Enum.IsDefined(typeof(ComputeMode), request.ComputeMode))
throw new PerformanceException("暂不支持当前计算方式!");
......@@ -559,6 +565,12 @@ public ApiResponse RedistributionLoad([FromBody] SecondLoadDto request)
var result = _redistributionService.Load(request.SecondId, (ComputeMode)request.ComputeMode, overrideMode);
return new ApiResponse(ResponseType.OK, result);
}
catch (Exception ex)
{
_logger.LogError($"二次绩效录入页面:{ex}");
return new ApiResponse(ResponseType.Fail, "数据加载失败");
}
}
/// <summary>
......@@ -570,6 +582,8 @@ public ApiResponse RedistributionLoad([FromBody] SecondLoadDto request)
[HttpPost]
public ApiResponse RedistributionCheck([FromBody] SecondComputeDto request)
{
try
{
if (request?.Body == null)
throw new PerformanceException("提交空参数,无法查看计算结果!");
......@@ -580,6 +594,8 @@ public ApiResponse RedistributionCheck([FromBody] SecondComputeDto request)
if (allot == null)
throw new PerformanceException("绩效记录不存在!");
// 年资职称绩效占比与工作量绩效占比 校验
if ((ComputeMode)request.ComputeMode != ComputeMode.NotCalculate)
{
var loads = _redistributionService.GetWorkLoads(allot.HospitalId, second.UnitType, second.Department);
var workloadGroups = _redistributionService.GetTopWorkloadBodyGroups(loads);
var seniorityTitlesAccountedPerformance = request.Head.GetValue(nameof(ag_headsource.SeniorityTitlesAccountedPerformance), 0m);
......@@ -588,13 +604,20 @@ public ApiResponse RedistributionCheck([FromBody] SecondComputeDto request)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,超过100%!");
else if (seniorityTitlesAccountedPerformance + workloadRatio < 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,不足100%!");
}
// 二次分配人员信息 校验
var result = _redistributionService.CheckData(second, request.Body);
return new ApiResponse(ResponseType.OK, result);
}
catch (Exception ex)
{
_logger.LogError($"提交数据正确性检验:{ex}");
return new ApiResponse(ResponseType.Fail, "数据验证失败");
}
}
/// <summary>
/// 二次绩效录入页面
/// 二次绩效结果计算
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
......@@ -602,6 +625,8 @@ public ApiResponse RedistributionCheck([FromBody] SecondComputeDto request)
[HttpPost]
public ApiResponse RedistributionCompute([FromBody] SecondComputeDto request)
{
try
{
if (!Enum.IsDefined(typeof(ComputeMode), request.ComputeMode))
throw new PerformanceException("暂不支持当前计算方式!");
......@@ -617,12 +642,16 @@ public ApiResponse RedistributionCompute([FromBody] SecondComputeDto request)
// 年资职称绩效占比与工作量绩效占比 校验
var loads = _redistributionService.GetWorkLoads(allot.HospitalId, second.UnitType, second.Department);
var workloadGroups = _redistributionService.GetTopWorkloadBodyGroups(loads);
if ((ComputeMode)request.ComputeMode != ComputeMode.NotCalculate)
{
var seniorityTitlesAccountedPerformance = request.Head.GetValue(nameof(ag_headsource.SeniorityTitlesAccountedPerformance), 0m);
var workloadRatio = workloadGroups.Sum(w => request.Head.GetValue($"Workload_Ratio_{w.Name}", 0m));
if (seniorityTitlesAccountedPerformance + workloadRatio > 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,超过100%!");
else if (seniorityTitlesAccountedPerformance + workloadRatio < 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,不足100%!");
}
// 二次分配人员信息 校验
var checkDatas = _redistributionService.CheckData(second, request.Body);
......@@ -638,13 +667,18 @@ public ApiResponse RedistributionCompute([FromBody] SecondComputeDto request)
_redistributionService.ResultCompute((ComputeMode)request.ComputeMode, request.Head, cleanDatas, loads, workloadGroups);
// 补充医院其他绩效
_redistributionService.SupplementOtherPerfor(second, cleanDatas);
// 重算顶部医院其他绩效
_redistributionService.OverviewCalculate_OtherPerformance(request.Head, cleanDatas);
// 重算部分数据
_redistributionService.RedistributionCompute((ComputeMode)request.ComputeMode, request.Head, cleanDatas);
var dic = _redistributionService.GetTableHeaderDictionary(second, loads);
var dic = _redistributionService.GetTableHeaderDictionary((ComputeMode)request.ComputeMode, second, loads);
return new ApiResponse(ResponseType.OK, new { Head = request.Head, Body = cleanDatas, Dic = dic });
}
catch (Exception ex)
{
_logger.LogError($"二次绩效结果计算:{ex}");
return new ApiResponse(ResponseType.Fail, "计算失败");
}
}
/// <summary>
/// 二次绩效保存
/// </summary>
......@@ -654,8 +688,33 @@ public ApiResponse RedistributionCompute([FromBody] SecondComputeDto request)
[HttpPost]
public ApiResponse RedistributionSave([FromBody] SecondComputeDto request)
{
secondAllotService.SaveSecondAllotData(request.SecondId, request);
return new ApiResponse(ResponseType.OK);
try
{
var second = secondAllotService.GetSecondAllot(request.SecondId);
if (second == null) throw new PerformanceException("参数SecondId无效!");
var allot = _allotService.GetAllot(second.AllotId.Value);
if (allot == null)
throw new PerformanceException("绩效记录不存在!");
// 二次分配人员信息 校验
var checkDatas = _redistributionService.CheckData(second, request.Body);
if (checkDatas != null && checkDatas.Any(w => w.Level == ResponseType.Error.ToString()))
return new ApiResponse(ResponseType.Fail, "数据验证未通过,请修复后查看计算结果!", checkDatas.Where(w => w.Level == ResponseType.Error.ToString()));
// 清理无效数据 没有Tab标签的数据才是要计算的数据
var cleanDatas = request.Body.Where(w => w.Count > 0 && w.GetValue(nameof(ResponseType), "") == ResponseType.OK.ToString()).ToList();
if (cleanDatas == null || cleanDatas.Count == 0)
throw new PerformanceException("提交参数都是无效数据,请重新填写数据后查看计算结果!");
var result = secondAllotService.RedistributionSave(allot, second, request.Head, cleanDatas);
return result ? new ApiResponse(ResponseType.OK) : new ApiResponse(ResponseType.Fail, "保存失败");
}
catch (Exception ex)
{
_logger.LogError($"二次绩效保存:{ex}");
return new ApiResponse(ResponseType.Fail, "保存失败");
}
}
/// <summary>
......@@ -667,15 +726,43 @@ public ApiResponse RedistributionSave([FromBody] SecondComputeDto request)
[HttpPost]
public ApiResponse RedistributionSubmit([FromBody] SecondComputeDto request)
{
try
{
var second = secondAllotService.GetSecondAllot(request.SecondId);
if (second == null)
return new ApiResponse(ResponseType.ParameterError, "二次绩效Id无效");
if (second.Status == 3)
return new ApiResponse(ResponseType.Fail, "该绩效已\"审核通过\",无需再次提交");
var allot = _allotService.GetAllot(second.AllotId.Value);
if (allot == null)
throw new PerformanceException("绩效记录不存在!");
// 二次分配人员信息 校验
var checkDatas = _redistributionService.CheckData(second, request.Body);
if (checkDatas != null && checkDatas.Any(w => w.Level == ResponseType.Error.ToString()))
return new ApiResponse(ResponseType.Fail, "数据验证未通过,请修复后查看计算结果!", checkDatas.Where(w => w.Level == ResponseType.Error.ToString()));
// 清理无效数据 没有Tab标签的数据才是要计算的数据
var cleanDatas = request.Body.Where(w => w.Count > 0 && w.GetValue(nameof(ResponseType), "") == ResponseType.OK.ToString()).ToList();
if (cleanDatas == null || cleanDatas.Count == 0)
throw new PerformanceException("提交参数都是无效数据,请重新填写数据后查看计算结果!");
var saveResult = secondAllotService.RedistributionSave(allot, second, request.Head, cleanDatas);
if (saveResult)
{
var userid = claimService.GetUserId();
var result = secondAllotService.AuditSubmit(second, userid);
return result ? new ApiResponse(ResponseType.OK, "提交成功") : new ApiResponse(ResponseType.Fail, "提交失败");
if (result)
return new ApiResponse(ResponseType.OK, "提交成功");
}
return new ApiResponse(ResponseType.Fail, "提交失败");
}
catch (Exception ex)
{
_logger.LogError($"二次绩效提交:{ex}");
return new ApiResponse(ResponseType.Fail, "提交失败");
}
}
#endregion
}
......
......@@ -73,7 +73,7 @@ public class RequestRateLimitingMiddleware
var response = new ApiResponse
{
State = ResponseType.TooManyRequests,
Message = "访问过于频繁,请稍后重试"
Message = "您的操作正在处理,请稍等片刻!"
};
await context.Response.WriteAsync(JsonHelper.Serialize(response));
}
......
......@@ -38,7 +38,13 @@
"HttpPost": "http://localhost:50997/api/"
},
"RateLimitingConfig": {
"Endpoints": [ "/api/second/savevalue", "/api/second/savedata", "/api/second/other/save" ],
"Endpoints": [
"/api/second/savevalue",
"/api/second/savedata",
"/api/second/other/save",
"/api/second/redistribution/save",
"/api/second/redistribution/submit"
],
"Period": "1", // 单位为秒
"Limit": 1
}
......
......@@ -1628,7 +1628,7 @@
</member>
<member name="M:Performance.Api.Controllers.SecondAllotController.RedistributionCompute(Performance.DtoModels.SecondComputeDto)">
<summary>
二次绩效录入页面
二次绩效结果计算
</summary>
<param name="request"></param>
<returns></returns>
......
......@@ -3768,7 +3768,7 @@
</member>
<member name="F:Performance.DtoModels.Second.ComputeMode.NotCalculate">
<summary>
不计算
填报(不计算)
</summary>
</member>
<member name="F:Performance.DtoModels.Second.ComputeMode.Horizontal">
......
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace Performance.DtoModels.Second
......@@ -10,16 +11,19 @@ namespace Performance.DtoModels.Second
public enum ComputeMode
{
/// <summary>
/// 不计算
/// 填报(不计算)
/// </summary>
[Description("填报")]
NotCalculate = 11,
/// <summary>
/// 横向计算
/// </summary>
[Description("横向计算")]
Horizontal = 12,
/// <summary>
/// 纵向计算
/// </summary>
[Description("纵向计算")]
Vertical = 13,
}
}
......@@ -124,7 +124,7 @@ public SecondDetailDto Load(int secondId, ComputeMode computeMode, EmployeeSourc
}
var head = LoadHead(computeMode, allot, second);
var dic = GetTableHeaderDictionary(second, loads);
var dic = GetTableHeaderDictionary(computeMode, second, loads);
return new SecondDetailDto { Head = head, Body = handson, Dic = dic };
}
......@@ -135,7 +135,7 @@ public SecondDetailDto Load(int secondId, ComputeMode computeMode, EmployeeSourc
/// <param name="second"></param>
/// <param name="loads"></param>
/// <returns></returns>
public List<SecondColumnDictionary> GetTableHeaderDictionary(ag_secondallot second, List<TitleValue<string, decimal?>> loads)
public List<SecondColumnDictionary> GetTableHeaderDictionary(ComputeMode computeMode, ag_secondallot second, List<TitleValue<string, decimal?>> loads)
{
var maps = new List<SecondColumnDictionary>()
{
......@@ -164,29 +164,29 @@ public List<SecondColumnDictionary> GetTableHeaderDictionary(ag_secondallot seco
};
// 工作量
int sort = 300;
foreach (var item in loads.Where(w => !w.Title.StartsWithIgnoreCase("SingleAwards_")))
if (computeMode != ComputeMode.NotCalculate)
{
maps.Add(new SecondColumnDictionary(item.Value, item.Title, false, ++sort, type: "Workload"));
}
// 单项奖励
sort = 400;
foreach (var item in loads.Where(w => w.Title.StartsWithIgnoreCase("SingleAwards_")))
int workloadSort = 300;
foreach (var item in loads.Where(w => !w.Title.StartsWithIgnoreCase("SingleAwards_")))
{
maps.Add(new SecondColumnDictionary(item.Value, item.Title, false, ++sort, type: "SingleAwards"));
maps.Add(new SecondColumnDictionary(item.Value, item.Title, false, ++workloadSort, type: "Workload"));
}
// 多工作量加载
var headDynamic = _agworktypesourceRepository.GetEntities(t => t.SecondId == second.Id);
if (headDynamic != null && headDynamic.Any())
{
foreach (var item in headDynamic.Where(w => w.FieldId.StartsWithIgnoreCase("Workload_Ratio_")).OrderBy(t => t.Id))
foreach (var item in headDynamic.OrderBy(t => t.Id))
{
maps.Add(new SecondColumnDictionary(item.FieldName, item.FieldId, true, 1, "Top"));
}
}
}
// 单项奖励
int singleAwardsSort = 400;
foreach (var item in loads.Where(w => w.Title.StartsWithIgnoreCase("SingleAwards_")))
{
maps.Add(new SecondColumnDictionary(item.Value, item.Title, false, ++singleAwardsSort, type: "SingleAwards"));
}
return maps.OrderBy(w => w.Sort).ToList();
}
......@@ -253,6 +253,11 @@ public List<SecondColumnDictionary> GetTableHeaderDictionary(ag_secondallot seco
{
var head = new Dictionary<string, object>();
// 公共顶部信息
head.AddOrUpdate(nameof(second.Department), second.Department);
head.AddOrUpdate(nameof(second.UnitType), second.UnitType);
head.AddOrUpdate(nameof(ComputeMode), computeMode);
head.AddOrUpdate("ComputeModeDescription", EnumHelper.GetDescription(computeMode));
head.AddOrUpdate(nameof(ag_headsource.SecondId), second.Id);
head.AddOrUpdate(nameof(ag_headsource.PaymentOfTheMonth), $"{allot.Year}{allot.Month.ToString().PadLeft(2, '0')}月");
head.AddOrUpdate(nameof(ag_headsource.TotalDistPerformance), second.RealGiveFee ?? 0);
......@@ -549,7 +554,7 @@ private HandsonTableBase ComputeMode_Format3(List<string> colHeaders, List<Hands
var fs = handson.Columns.Select(col =>
{
var f = loads.FirstOrDefault(w => w.Title.StartsWithIgnoreCase($"Workload_") && w.Title.EqualsIgnoreCase(col.Data));
return f == null ? "" : (f.State ?? 0).ToString("F0");
return f == null ? "" : (f.State ?? 0).ToString("0.####"); ;
});
handson.NestedHeadersArray.Add(handson.ColHeaders);
......@@ -613,7 +618,7 @@ public void SupplementOtherPerfor(ag_secondallot second, List<Dictionary<string,
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
public void OverviewCalculate_OtherPerformance(Dictionary<string, object> head, List<Dictionary<string, object>> rows)
private void otherPerformance(Dictionary<string, object> head, List<Dictionary<string, object>> rows)
{
//医院其他绩效总和
var otherPerformance = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.OtherPerformance)));
......@@ -669,6 +674,22 @@ public void ResultCompute(ComputeMode computeMode, Dictionary<string, object> he
}
/// <summary>
/// 重算顶部医院其他绩效、重算行内实发绩效、重算顶部工作量
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
public void RedistributionCompute(ComputeMode computeMode, Dictionary<string, object> head, List<Dictionary<string, object>> rows)
{
// 重算顶部医院其他绩效
otherPerformance(head, rows);
// 重算行内实发绩效
realAmountCalculate(rows);
// 重算顶部工作量
if (computeMode != ComputeMode.NotCalculate)
overviewCalculate(head, rows);
}
/// <summary>
/// ComputeMode.NotCalculate 不计算时,清空无效数据
/// </summary>
/// <param name="rows"></param>
......@@ -733,12 +754,12 @@ private void topSeniorityCalculate(Dictionary<string, object> head)
/// <param name="rows"></param>
private void overviewCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows)
{
// 可分配绩效(顶栏) = 科室总绩效 - 夜班绩效(顶栏)
var totalPerformance = GetDecimal2(head, nameof(ag_headsource.TotalDistPerformance)) - GetDecimal2(head, nameof(ag_headsource.NightShiftWorkPerforTotal));
head.AddOrUpdate(nameof(ag_headsource.TotalPerformance), totalPerformance);
//夜班工作量绩效总和
var nightShiftWorkPerforTotal = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.NightWorkPerformance)));
head.AddOrUpdate(nameof(ag_headsource.NightShiftWorkPerforTotal), nightShiftWorkPerforTotal);
// 可分配绩效(顶栏) = 科室总绩效 - 夜班绩效(顶栏)
var totalPerformance = GetDecimal2(head, nameof(ag_headsource.TotalDistPerformance)) - GetDecimal2(head, nameof(ag_headsource.NightShiftWorkPerforTotal));
head.AddOrUpdate(nameof(ag_headsource.TotalPerformance), totalPerformance);
////医院其他绩效总和
//var otherPerformance = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.OtherPerformance)));
//head.AddOrUpdate(nameof(ag_headsource.HosOtherPerformance), otherPerformance);
......@@ -1085,12 +1106,12 @@ public List<SecondComputeCheckResultDto> CheckData(ag_secondallot second, List<D
if (string.IsNullOrEmpty(number) || string.IsNullOrEmpty(name))
{
item.AddOrUpdate(nameof(ResponseType), ResponseType.Warning);
result.Add(new SecondComputeCheckResultDto(nameof(ResponseType.Warning), "空", "空", $"第{(i + 1)}行,工号或姓名无效;计算式忽略。"));
result.Add(new SecondComputeCheckResultDto(nameof(ResponseType.Error), "空", "空", $"第{(i + 1)}行,工号或姓名无效;请删除!"));
}
else if (!employees.Any(w => w.PersonnelNumber == number && w.DoctorName == name))
{
item.AddOrUpdate(nameof(ResponseType), ResponseType.Error);
result.Add(new SecondComputeCheckResultDto(nameof(ResponseType.Error), number, name, $"第{(i + 1)}行,工号和姓名在字典中不存在,请修复"));
result.Add(new SecondComputeCheckResultDto(nameof(ResponseType.Error), number, name, $"第{(i + 1)}行,工号和姓名在字典中不存在,请修复"));
}
else
{
......
......@@ -477,26 +477,23 @@ public void SaveSecondAllotData(int secondId, dynamic saveData)
/// </summary>
/// <param name="secondId"></param>
/// <param name="json"></param>
private void SaveSecondAllotHeadData(int secondId, string json)
public void SaveSecondAllotHeadData(int secondId, string json)
{
if (string.IsNullOrEmpty(json)) return;
ag_headsource headsource = JsonHelper.Deserialize<ag_headsource>(json);
if (headsource == null) return;
headsource.SecondId = secondId;
if (headsource.Id == 0)
{
var exist = agheadsourceRepository.GetEntity(w => w.SecondId == secondId);
if (exist != null)
agheadsourceRepository.Remove(exist);
headsource.SecondId = exist.SecondId;
agheadsourceRepository.Add(headsource);
}
else
{
agheadsourceRepository.UpdateByState(headsource);
}
string[] prefix = new string[] { "Workload_Ratio_", "Workload_Amount_" };
Dictionary<string, object> dict = JsonHelper.Deserialize<Dictionary<string, object>>(json);
var keys = dict.Keys.Where(t => t.StartsWith(prefix[0]) || t.StartsWith(prefix[1]));
var keys = dict.Keys.Where(t => t.StartsWithIgnoreCase(prefix[0]) || t.StartsWithIgnoreCase(prefix[1]));
if (keys == null || !keys.Any())
return;
......@@ -507,7 +504,7 @@ private void SaveSecondAllotHeadData(int secondId, string json)
foreach (var key in keys)
{
var update = worktypeSources.FirstOrDefault(t => t.FieldId == key);
var update = worktypeSources.FirstOrDefault(t => t.FieldId.EqualsIgnoreCase(key));
if (update != null)
{
update.Value = ConvertHelper.To<decimal>(dict[key]);
......@@ -522,7 +519,7 @@ private void SaveSecondAllotHeadData(int secondId, string json)
/// <param name="hospitalId"></param>
/// <param name="second"></param>
/// <param name="body"></param>
private void SaveSecondAllotBodyData(int hospitalId, ag_secondallot second, dynamic body)
public void SaveSecondAllotBodyData(int hospitalId, ag_secondallot second, dynamic body)
{
// 允许空行数据提交,删除数据库存数数据
var bodyEntities = agbodysourceRepository.GetEntities(t => t.SecondId == second.Id);
......@@ -551,12 +548,12 @@ private void SaveSecondAllotBodyData(int hospitalId, ag_secondallot second, dyna
if (!result) continue;
Dictionary<string, object> dict = JsonHelper.Deserialize<Dictionary<string, object>>(JsonHelper.Serialize(rowitem));
var keys = dict.Keys.Where(t => t.StartsWith(prefix[0]) || t.StartsWith(prefix[1]) || t.StartsWith(prefix[2]) || t.StartsWith(prefix[3]) || t.StartsWith(prefix[4]));
var keys = dict.Keys.Where(t => t.StartsWithIgnoreCase(prefix[0]) || t.StartsWithIgnoreCase(prefix[1]) || t.StartsWithIgnoreCase(prefix[2]) || t.StartsWithIgnoreCase(prefix[3]) || t.StartsWithIgnoreCase(prefix[4]));
if (keys == null || !keys.Any()) continue;
foreach (var key in keys)
{
var workload = workloads.FirstOrDefault(t => t.ItemId == key);
var workload = workloads.FirstOrDefault(t => t.ItemId.EqualsIgnoreCase(key));
if (workload == null) continue;
workloadSources.Add(new ag_workload_source
......@@ -576,6 +573,23 @@ private void SaveSecondAllotBodyData(int hospitalId, ag_secondallot second, dyna
agworkloadsourceRepository.AddRange(workloadSources.ToArray());
}
public bool RedistributionSave(per_allot allot, ag_secondallot second, Dictionary<string, object> head, List<Dictionary<string, object>> rows)
{
try
{
SaveSecondAllotHeadData(second.Id, JsonHelper.Serialize(head));
SaveSecondAllotBodyData(allot.HospitalId, second, rows);
return true;
}
catch (Exception ex)
{
logger.LogError(ex.ToString());
}
return false;
}
#endregion
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment