|
|
@@ -1,6 +1,7 @@
|
|
|
using Aspose.Cells;
|
|
|
using FluentValidation;
|
|
|
using Humanizer;
|
|
|
+using NPOI.SS.Formula.Functions;
|
|
|
using OASystem.API.OAMethodLib;
|
|
|
using OASystem.API.OAMethodLib.JuHeAPI;
|
|
|
using OASystem.Domain.AesEncryption;
|
|
|
@@ -9,10 +10,15 @@ using OASystem.Domain.Entities.Customer;
|
|
|
using OASystem.Domain.Entities.Financial;
|
|
|
using OASystem.Domain.Entities.Groups;
|
|
|
using OASystem.Domain.ViewModels.Financial;
|
|
|
+using OASystem.Domain.ViewModels.Groups;
|
|
|
using OASystem.Domain.ViewModels.Statistics;
|
|
|
using OASystem.Infrastructure.Repositories.Groups;
|
|
|
using SqlSugar;
|
|
|
+using StackExchange.Redis;
|
|
|
+using System.ComponentModel;
|
|
|
+using System.ComponentModel.DataAnnotations;
|
|
|
using System.Data;
|
|
|
+using System.Diagnostics.Eventing.Reader;
|
|
|
using static OASystem.API.OAMethodLib.GeneralMethod;
|
|
|
using TypeInfo = OASystem.Domain.ViewModels.Statistics.TypeInfo;
|
|
|
|
|
|
@@ -3662,6 +3668,191 @@ ORDER BY
|
|
|
|
|
|
}
|
|
|
|
|
|
+ //SubmitAndValidateExpensesAsync
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 团组报表 - 会务超支验证excel下载
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="dto"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost("PostGroupStatementValidateExpenses")]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> PostGroupStatementValidateExpenses(PostGroupStatementValidateExpensesDto dto)
|
|
|
+ {
|
|
|
+ // 1. 严格参数校验
|
|
|
+ if (dto.GroupId <= 0 || dto.CurrUserId <= 0)
|
|
|
+ return Ok(JsonView(false, "参数非法"));
|
|
|
+
|
|
|
+ // 2. 业务校验
|
|
|
+ var isConfGroup = await _sqlSugar.Queryable<Grp_DelegationInfo, Sys_SetData>(
|
|
|
+ (g, s) => g.TeamDid == s.Id
|
|
|
+ )
|
|
|
+ .Where((g, s) => g.IsDel == 0
|
|
|
+ && g.Id == dto.GroupId
|
|
|
+ && s.STid == 10
|
|
|
+ && s.Name.Contains("会务活动"))
|
|
|
+ .AnyAsync();
|
|
|
+
|
|
|
+ if (!isConfGroup) return Ok(JsonView(false, "当前团组非会务团组,禁止操作"));
|
|
|
+
|
|
|
+ // 3. 异步并行查询:获取实际支付(Fee)与预算成本(Expense)
|
|
|
+ // 实际支付列表
|
|
|
+ var feeList = await _sqlSugar.Queryable<Grp_DecreasePayments>()
|
|
|
+ .InnerJoin<Grp_CreditCardPayment>((x, y) => x.Id == y.CId && x.DiId == y.DIId && y.CTable == 98)
|
|
|
+ .LeftJoin<Sys_Users>((x, y, z) => x.CreateUserId == z.Id)
|
|
|
+ .Where((x, y, z) => x.IsDel == 0 && x.DiId == dto.GroupId)
|
|
|
+ .Select((x, y, z) => new ConfValidateExpensesView
|
|
|
+ {
|
|
|
+ PriceName = x.PriceName,
|
|
|
+ FeeEntry = y.PayMoney * y.DayRate,
|
|
|
+ AuditStatus = y.IsAuditGM.ToString(),
|
|
|
+ AuditTime = y.AuditGMDate,
|
|
|
+ PayLabel = y.IsPay.ToString(),
|
|
|
+ Remark = x.Remark,
|
|
|
+ Applicant = z.CnName,
|
|
|
+ ApplicantTime = x.CreateTime.ToString("yyyy-MM-dd HH:mm:ss")
|
|
|
+ }).ToListAsync();
|
|
|
+
|
|
|
+ if (!feeList.Any()) return Ok(JsonView(false, "未检索到实际费用记录"));
|
|
|
+
|
|
|
+ // 成本预算列表
|
|
|
+ var expenseData = await _sqlSugar.Queryable<Grp_ConferenceAffairsCostChild>()
|
|
|
+ .InnerJoin<Grp_ConferenceAffairsCost>((x, y) => x.ConferenceAffairsCostId == y.Id)
|
|
|
+ .Where((x, y) => y.IsDel == 0 && y.Diid == dto.GroupId && x.ReviewStatus == 1)
|
|
|
+ .Select((x, y) => new { x.PriceName, Total = x.CostPrice * x.Rate * x.Count })
|
|
|
+ .ToListAsync();
|
|
|
+
|
|
|
+ var expenseDict = expenseData.ToDictionary(k => k.PriceName, v => v.Total);
|
|
|
+
|
|
|
+ if (!feeList.Any()) return Ok(JsonView(false, "未检索到实际费用记录"));
|
|
|
+
|
|
|
+ feeList = feeList.OrderBy(x => x.PriceName).ThenBy(x => x.ApplicantTime).ToList();
|
|
|
+
|
|
|
+ int index = 1;
|
|
|
+ // 4. 数据处理
|
|
|
+ foreach (var item in feeList)
|
|
|
+ {
|
|
|
+ item.Index = index;
|
|
|
+ item.AuditStatus = MapAuditStatus(item.AuditStatus);
|
|
|
+ item.PayLabel = item.PayLabel == "1" ? "已支付" : "未支付";
|
|
|
+
|
|
|
+ if (expenseDict.TryGetValue(item.PriceName, out decimal budget))
|
|
|
+ {
|
|
|
+ item.ExpenseEntry = budget;
|
|
|
+ item.OverspentLabel = item.FeeEntry > budget ? "超支" : "未超支";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (item.FeeEntry <= 0) item.OverspentLabel = "未超支";
|
|
|
+ else item.OverspentLabel = "成本未录入项(系统判定超支)";
|
|
|
+ }
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 导出动作与异常处理
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var groupName = _sqlSugar.Queryable<Grp_DelegationInfo>().First(x => x.Id == dto.GroupId)?.TeamName ?? "未知团组";
|
|
|
+
|
|
|
+ string fileName = $"会务校验_{groupName}_{DateTime.Now:yyyyMMddHHmm}.xlsx";
|
|
|
+
|
|
|
+ // Excel导出:调用通用方法,传入数据列表与文件名
|
|
|
+ byte[] fileData = ExportToExcel(feeList,"超支验证明细");
|
|
|
+
|
|
|
+ if (fileData == null || fileData.Length == 0)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, "生成文件流失败,请检查数据源"));
|
|
|
+ }
|
|
|
+
|
|
|
+ return File(
|
|
|
+ fileData,
|
|
|
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
|
+ fileName
|
|
|
+ );
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ return Ok(JsonView(false, $"导出过程中发生系统错误:{ex.Message}"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private string MapAuditStatus(string status) => status switch
|
|
|
+ {
|
|
|
+ "0" => "未审核",
|
|
|
+ "1" => "已审核",
|
|
|
+ "2" => "未通过",
|
|
|
+ "3" => "自动审核",
|
|
|
+ _ => "未知"
|
|
|
+ };
|
|
|
+
|
|
|
+ public static byte[] ExportToExcel<T>(IEnumerable<T> data, string sheetName)
|
|
|
+ {
|
|
|
+ Workbook workbook = new Workbook();
|
|
|
+ Worksheet sheet = workbook.Worksheets[0];
|
|
|
+ sheet.Name = sheetName;
|
|
|
+
|
|
|
+ PropertyInfo[] props = typeof(T).GetProperties();
|
|
|
+
|
|
|
+ // 定义全局字体名称
|
|
|
+ const string MicrosoftYaHei = "微软雅黑";
|
|
|
+
|
|
|
+ // --- 1. 表头处理 ---
|
|
|
+ for (int i = 0; i < props.Length; i++)
|
|
|
+ {
|
|
|
+ var displayAttr = props[i].GetCustomAttribute<DisplayAttribute>();
|
|
|
+ string headerName = displayAttr?.Name ?? props[i].Name;
|
|
|
+
|
|
|
+ Aspose.Cells.Cell cell = sheet.Cells[0, i];
|
|
|
+ cell.PutValue(headerName);
|
|
|
+
|
|
|
+ // 设置表头样式:加粗 + 微软雅黑
|
|
|
+ Style style = cell.GetStyle();
|
|
|
+ style.Font.Name = MicrosoftYaHei;
|
|
|
+ style.Font.IsBold = true;
|
|
|
+ style.Font.Size = 11;
|
|
|
+ style.BackgroundColor = System.Drawing.Color.FromArgb(235, 235, 235);
|
|
|
+ style.Pattern = BackgroundType.Solid;
|
|
|
+ style.HorizontalAlignment = TextAlignmentType.Center;
|
|
|
+ cell.SetStyle(style);
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- 2. 数据处理 ---
|
|
|
+ var dataList = data.ToList();
|
|
|
+ for (int rowIndex = 0; rowIndex < dataList.Count; rowIndex++)
|
|
|
+ {
|
|
|
+ var item = dataList[rowIndex];
|
|
|
+ for (int colIndex = 0; colIndex < props.Length; colIndex++)
|
|
|
+ {
|
|
|
+ var value = props[colIndex].GetValue(item);
|
|
|
+ Aspose.Cells.Cell cell = sheet.Cells[rowIndex + 1, colIndex];
|
|
|
+ cell.PutValue(value);
|
|
|
+
|
|
|
+ // 设置单元格基础样式:微软雅黑
|
|
|
+ Style cellStyle = cell.GetStyle();
|
|
|
+ cellStyle.Font.Name = MicrosoftYaHei;
|
|
|
+ cellStyle.Font.Size = 10;
|
|
|
+
|
|
|
+ // 成功/错误验证:高亮逻辑
|
|
|
+ if (props[colIndex].Name == "OverspentLabel" && value?.ToString()?.Contains("超支") == true)
|
|
|
+ {
|
|
|
+ cellStyle.Font.Color = System.Drawing.Color.Red;
|
|
|
+ cellStyle.Font.IsBold = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ cell.SetStyle(cellStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ sheet.AutoFitColumns();
|
|
|
+
|
|
|
+ // --- 3. 转化为文件流 ---
|
|
|
+ using (MemoryStream ms = new MemoryStream())
|
|
|
+ {
|
|
|
+ workbook.Save(ms, SaveFormat.Xlsx);
|
|
|
+ return ms.ToArray();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
#region 报表/折线图统计
|