|
@@ -3668,35 +3668,24 @@ ORDER BY
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //SubmitAndValidateExpensesAsync
|
|
|
|
|
-
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// 团组报表 - 会务超支验证excel下载
|
|
/// 团组报表 - 会务超支验证excel下载
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <param name="dto"></param>
|
|
/// <param name="dto"></param>
|
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
|
[HttpPost("PostGroupStatementValidateExpenses")]
|
|
[HttpPost("PostGroupStatementValidateExpenses")]
|
|
|
- [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
|
|
public async Task<IActionResult> PostGroupStatementValidateExpenses(PostGroupStatementValidateExpensesDto dto)
|
|
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();
|
|
|
|
|
|
|
+ // 1. 参数与业务校验
|
|
|
|
|
+ if (dto.GroupId <= 0 || dto.CurrUserId <= 0) return Ok(JsonView(false, "参数非法"));
|
|
|
|
|
+
|
|
|
|
|
+ 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, "当前团组非会务团组,禁止操作"));
|
|
if (!isConfGroup) return Ok(JsonView(false, "当前团组非会务团组,禁止操作"));
|
|
|
|
|
|
|
|
- // 3. 异步并行查询:获取实际支付(Fee)与预算成本(Expense)
|
|
|
|
|
- // 实际支付列表
|
|
|
|
|
|
|
+ // 2. 数据查询与处理
|
|
|
var feeList = await _sqlSugar.Queryable<Grp_DecreasePayments>()
|
|
var feeList = await _sqlSugar.Queryable<Grp_DecreasePayments>()
|
|
|
.InnerJoin<Grp_CreditCardPayment>((x, y) => x.Id == y.CId && x.DiId == y.DIId && y.CTable == 98)
|
|
.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)
|
|
.LeftJoin<Sys_Users>((x, y, z) => x.CreateUserId == z.Id)
|
|
@@ -3715,7 +3704,6 @@ ORDER BY
|
|
|
|
|
|
|
|
if (!feeList.Any()) return Ok(JsonView(false, "未检索到实际费用记录"));
|
|
if (!feeList.Any()) return Ok(JsonView(false, "未检索到实际费用记录"));
|
|
|
|
|
|
|
|
- // 成本预算列表
|
|
|
|
|
var expenseData = await _sqlSugar.Queryable<Grp_ConferenceAffairsCostChild>()
|
|
var expenseData = await _sqlSugar.Queryable<Grp_ConferenceAffairsCostChild>()
|
|
|
.InnerJoin<Grp_ConferenceAffairsCost>((x, y) => x.ConferenceAffairsCostId == y.Id)
|
|
.InnerJoin<Grp_ConferenceAffairsCost>((x, y) => x.ConferenceAffairsCostId == y.Id)
|
|
|
.Where((x, y) => y.IsDel == 0 && y.Diid == dto.GroupId && x.ReviewStatus == 1)
|
|
.Where((x, y) => y.IsDel == 0 && y.Diid == dto.GroupId && x.ReviewStatus == 1)
|
|
@@ -3724,18 +3712,13 @@ ORDER BY
|
|
|
|
|
|
|
|
var expenseDict = expenseData.ToDictionary(k => k.PriceName, v => v.Total);
|
|
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;
|
|
int index = 1;
|
|
|
- // 4. 数据处理
|
|
|
|
|
- foreach (var item in feeList)
|
|
|
|
|
|
|
+ foreach (var item in feeList.OrderBy(x => x.PriceName).ThenBy(x => x.ApplicantTime))
|
|
|
{
|
|
{
|
|
|
- item.Index = index;
|
|
|
|
|
|
|
+ item.Index = index++;
|
|
|
item.AuditStatus = MapAuditStatus(item.AuditStatus);
|
|
item.AuditStatus = MapAuditStatus(item.AuditStatus);
|
|
|
item.PayLabel = item.PayLabel == "1" ? "已支付" : "未支付";
|
|
item.PayLabel = item.PayLabel == "1" ? "已支付" : "未支付";
|
|
|
-
|
|
|
|
|
if (expenseDict.TryGetValue(item.PriceName, out decimal budget))
|
|
if (expenseDict.TryGetValue(item.PriceName, out decimal budget))
|
|
|
{
|
|
{
|
|
|
item.ExpenseEntry = budget;
|
|
item.ExpenseEntry = budget;
|
|
@@ -3743,36 +3726,39 @@ ORDER BY
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- if (item.FeeEntry <= 0) item.OverspentLabel = "未超支";
|
|
|
|
|
- else item.OverspentLabel = "成本未录入项(系统判定超支)";
|
|
|
|
|
|
|
+ item.OverspentLabel = item.FeeEntry <= 0 ? "未超支" : "成本未录入项(系统判定超支)";
|
|
|
}
|
|
}
|
|
|
- index++;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 导出动作与异常处理
|
|
|
|
|
|
|
+ // 3. 路径与导出
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
|
- var groupName = _sqlSugar.Queryable<Grp_DelegationInfo>().First(x => x.Id == dto.GroupId)?.TeamName ?? "未知团组";
|
|
|
|
|
|
|
+ var groupName = await _sqlSugar.Queryable<Grp_DelegationInfo>().Where(x => x.Id == dto.GroupId).Select(x => x.TeamName).FirstAsync() ?? "未知团组";
|
|
|
|
|
+
|
|
|
|
|
+ string baseUrl = AppSettingsHelper.Get("ExcelBaseUrl").TrimEnd('/');
|
|
|
|
|
+ string basePath = AppSettingsHelper.Get("ExcelBasePath").TrimEnd('\\').TrimEnd('/');
|
|
|
|
|
+ string fptPath = AppSettingsHelper.Get("ExcelFtpPath");
|
|
|
|
|
+ string dirName = "GroupConf";
|
|
|
|
|
|
|
|
- string fileName = $"会务校验_{groupName}_{DateTime.Now:yyyyMMddHHmm}.xlsx";
|
|
|
|
|
|
|
+ // 目录不存在时创建
|
|
|
|
|
+ string targetDir = Path.Combine(basePath, dirName);
|
|
|
|
|
+ if (!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);
|
|
|
|
|
|
|
|
- // Excel导出:调用通用方法,传入数据列表与文件名
|
|
|
|
|
- byte[] fileData = ExportToExcel(feeList,"超支验证明细");
|
|
|
|
|
|
|
+ string fileName = $"{groupName}_会务校验_{DateTime.Now:yyyyMMddHHmm}.xlsx";
|
|
|
|
|
+ string fullPath = Path.Combine(targetDir, fileName);
|
|
|
|
|
|
|
|
- if (fileData == null || fileData.Length == 0)
|
|
|
|
|
|
|
+ // 生成文件
|
|
|
|
|
+ var isSuccess = await ExportToExcel(feeList, fullPath, "超支验证明细");
|
|
|
|
|
+
|
|
|
|
|
+ if (isSuccess)
|
|
|
{
|
|
{
|
|
|
- return Ok(JsonView(false, "生成文件流失败,请检查数据源"));
|
|
|
|
|
|
|
+ return Ok(JsonView(true, "操作成功", new { url = $"{baseUrl}{fptPath}{dirName}/{fileName}" }));
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- return File(
|
|
|
|
|
- fileData,
|
|
|
|
|
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
|
|
|
- fileName
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ return Ok(JsonView(false, "文件生成失败"));
|
|
|
}
|
|
}
|
|
|
catch (Exception ex)
|
|
catch (Exception ex)
|
|
|
{
|
|
{
|
|
|
- return Ok(JsonView(false, $"导出过程中发生系统错误:{ex.Message}"));
|
|
|
|
|
|
|
+ return Ok(JsonView(false, $"导出异常:{ex.Message}"));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -3785,72 +3771,61 @@ ORDER BY
|
|
|
_ => "未知"
|
|
_ => "未知"
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- public static byte[] ExportToExcel<T>(IEnumerable<T> data, string sheetName)
|
|
|
|
|
|
|
+ public static async Task<bool> ExportToExcel<T>(IEnumerable<T> data, string absolutePath, string sheetName)
|
|
|
{
|
|
{
|
|
|
Workbook workbook = new Workbook();
|
|
Workbook workbook = new Workbook();
|
|
|
Worksheet sheet = workbook.Worksheets[0];
|
|
Worksheet sheet = workbook.Worksheets[0];
|
|
|
sheet.Name = sheetName;
|
|
sheet.Name = sheetName;
|
|
|
-
|
|
|
|
|
PropertyInfo[] props = typeof(T).GetProperties();
|
|
PropertyInfo[] props = typeof(T).GetProperties();
|
|
|
|
|
|
|
|
- // 定义全局字体名称
|
|
|
|
|
- const string MicrosoftYaHei = "微软雅黑";
|
|
|
|
|
|
|
+ // 1. 表头处理
|
|
|
|
|
+ Style headerStyle = workbook.CreateStyle();
|
|
|
|
|
+ headerStyle.Font.Name = "微软雅黑";
|
|
|
|
|
+ headerStyle.Font.IsBold = true;
|
|
|
|
|
+ headerStyle.Pattern = BackgroundType.Solid;
|
|
|
|
|
+ headerStyle.ForegroundColor = System.Drawing.Color.LightGray;
|
|
|
|
|
+ headerStyle.HorizontalAlignment = TextAlignmentType.Center;
|
|
|
|
|
|
|
|
- // --- 1. 表头处理 ---
|
|
|
|
|
for (int i = 0; i < props.Length; i++)
|
|
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);
|
|
|
|
|
|
|
+ var attr = props[i].GetCustomAttribute<DisplayAttribute>();
|
|
|
|
|
+ sheet.Cells[0, i].PutValue(attr?.Name ?? props[i].Name);
|
|
|
|
|
+ sheet.Cells[0, i].SetStyle(headerStyle);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // --- 2. 数据处理 ---
|
|
|
|
|
- var dataList = data.ToList();
|
|
|
|
|
- for (int rowIndex = 0; rowIndex < dataList.Count; rowIndex++)
|
|
|
|
|
|
|
+ // 2. 数据处理
|
|
|
|
|
+ var list = data.ToList();
|
|
|
|
|
+ for (int r = 0; r < list.Count; r++)
|
|
|
{
|
|
{
|
|
|
- var item = dataList[rowIndex];
|
|
|
|
|
- for (int colIndex = 0; colIndex < props.Length; colIndex++)
|
|
|
|
|
|
|
+ for (int c = 0; c < props.Length; c++)
|
|
|
{
|
|
{
|
|
|
- var value = props[colIndex].GetValue(item);
|
|
|
|
|
- Aspose.Cells.Cell cell = sheet.Cells[rowIndex + 1, colIndex];
|
|
|
|
|
- cell.PutValue(value);
|
|
|
|
|
|
|
+ var val = props[c].GetValue(list[r]);
|
|
|
|
|
+ Aspose.Cells.Cell cell = sheet.Cells[r + 1, c];
|
|
|
|
|
+ cell.PutValue(val);
|
|
|
|
|
|
|
|
- // 设置单元格基础样式:微软雅黑
|
|
|
|
|
- Style cellStyle = cell.GetStyle();
|
|
|
|
|
- cellStyle.Font.Name = MicrosoftYaHei;
|
|
|
|
|
- cellStyle.Font.Size = 10;
|
|
|
|
|
-
|
|
|
|
|
- // 成功/错误验证:高亮逻辑
|
|
|
|
|
- if (props[colIndex].Name == "OverspentLabel" && value?.ToString()?.Contains("超支") == true)
|
|
|
|
|
|
|
+ // 高亮逻辑:仅在超支单元格设置红色
|
|
|
|
|
+ if (props[c].Name == "OverspentLabel" && val?.ToString()?.Contains("超支") == true)
|
|
|
{
|
|
{
|
|
|
- cellStyle.Font.Color = System.Drawing.Color.Red;
|
|
|
|
|
- cellStyle.Font.IsBold = true;
|
|
|
|
|
|
|
+ Style s = cell.GetStyle();
|
|
|
|
|
+ s.Font.Color = System.Drawing.Color.Red;
|
|
|
|
|
+ s.Font.IsBold = true;
|
|
|
|
|
+ cell.SetStyle(s);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- cell.SetStyle(cellStyle);
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
sheet.AutoFitColumns();
|
|
sheet.AutoFitColumns();
|
|
|
|
|
|
|
|
- // --- 3. 转化为文件流 ---
|
|
|
|
|
- using (MemoryStream ms = new MemoryStream())
|
|
|
|
|
|
|
+ // 3. 物理保存
|
|
|
|
|
+ try
|
|
|
{
|
|
{
|
|
|
- workbook.Save(ms, SaveFormat.Xlsx);
|
|
|
|
|
- return ms.ToArray();
|
|
|
|
|
|
|
+ using (FileStream fs = new FileStream(absolutePath, FileMode.Create, FileAccess.Write, FileShare.None))
|
|
|
|
|
+ {
|
|
|
|
|
+ workbook.Save(fs, SaveFormat.Xlsx);
|
|
|
|
|
+ await fs.FlushAsync();
|
|
|
|
|
+ }
|
|
|
|
|
+ return System.IO.File.Exists(absolutePath);
|
|
|
}
|
|
}
|
|
|
|
|
+ catch { return false; }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
#endregion
|