diff --git a/antis-ncc-admin/public/index.html b/antis-ncc-admin/public/index.html
index 631c754..85113bb 100644
--- a/antis-ncc-admin/public/index.html
+++ b/antis-ncc-admin/public/index.html
@@ -8,6 +8,10 @@
<%= webpackConfig.name %>
+
+
+
+
diff --git a/antis-ncc-admin/src/styles/index.scss b/antis-ncc-admin/src/styles/index.scss
index 4dd01b1..cdfb3cc 100644
--- a/antis-ncc-admin/src/styles/index.scss
+++ b/antis-ncc-admin/src/styles/index.scss
@@ -10,7 +10,7 @@ body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
- font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
// label {
diff --git a/antis-ncc-admin/src/views/extend/storeDashboard/index.vue b/antis-ncc-admin/src/views/extend/storeDashboard/index.vue
index ae7c4a9..2097749 100644
--- a/antis-ncc-admin/src/views/extend/storeDashboard/index.vue
+++ b/antis-ncc-admin/src/views/extend/storeDashboard/index.vue
@@ -74,6 +74,30 @@
formatMoney(storeData.Performance.NetPerformance) : '0.00' }}
+15.8%
+
+
生美业绩
+
¥{{ storeData && storeData.Performance &&
+ storeData.Performance.LifeBeautyPerformance ?
+ formatMoney(storeData.Performance.LifeBeautyPerformance) : '0.00' }}
+
+12.5%
+
+
+
医美业绩
+
¥{{ storeData && storeData.Performance &&
+ storeData.Performance.MedicalBeautyPerformance ?
+ formatMoney(storeData.Performance.MedicalBeautyPerformance) : '0.00' }}
+
+8.3%
+
+
+
科美业绩
+
¥{{ storeData && storeData.Performance &&
+ storeData.Performance.TechBeautyPerformance ?
+ formatMoney(storeData.Performance.TechBeautyPerformance) : '0.00' }}
+
+2.1%
+
@@ -182,7 +206,7 @@
- ¥{{
+ ¥{{
formatMoney(scope.row.billingAmount) }}
@@ -235,7 +259,7 @@
- ¥{{
+ ¥{{
formatMoney(scope.row.consumeAmount) }}
@@ -619,7 +643,10 @@ export default {
RefundAmount: response.data.RefundAmount || 0,
RefundCount: response.data.RefundCount || 0,
RemainingRightsAmount: response.data.RemainingRightsAmount || 0,
- TargetPerformance: response.data.TargetPerformance || 0
+ TargetPerformance: response.data.TargetPerformance || 0,
+ LifeBeautyPerformance: response.data.LifeBeautyPerformance || 0,
+ MedicalBeautyPerformance: response.data.MedicalBeautyPerformance || 0,
+ TechBeautyPerformance: response.data.TechBeautyPerformance || 0
},
Operation: {
HeadCount: response.data.HeadCount || 0,
@@ -1901,17 +1928,49 @@ export default {
\ No newline at end of file
+
+
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberRemainingItemsExportOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberRemainingItemsExportOutput.cs
new file mode 100644
index 0000000..c2b89f5
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberRemainingItemsExportOutput.cs
@@ -0,0 +1,85 @@
+using System;
+
+namespace NCC.Extend.Entitys.Dto.LqKhxx
+{
+ ///
+ /// 会员剩余品项导出输出
+ ///
+ public class MemberRemainingItemsExportOutput
+ {
+ ///
+ /// 会员ID
+ ///
+ public string memberId { get; set; }
+
+ ///
+ /// 会员姓名
+ ///
+ public string memberName { get; set; }
+
+ ///
+ /// 手机号
+ ///
+ public string phone { get; set; }
+
+ ///
+ /// 归属门店
+ ///
+ public string storeName { get; set; }
+
+ ///
+ /// 开单品项ID
+ ///
+ public string billingItemId { get; set; }
+
+ ///
+ /// 品项ID
+ ///
+ public string itemId { get; set; }
+
+ ///
+ /// 品项名称
+ ///
+ public string itemName { get; set; }
+
+ ///
+ /// 品项单价
+ ///
+ public decimal itemPrice { get; set; }
+
+ ///
+ /// 来源类型
+ ///
+ public string sourceType { get; set; }
+
+ ///
+ /// 总购买数量
+ ///
+ public decimal totalPurchased { get; set; }
+
+ ///
+ /// 已耗卡数量
+ ///
+ public decimal consumedCount { get; set; }
+
+ ///
+ /// 已退卡数量
+ ///
+ public decimal refundedCount { get; set; }
+
+ ///
+ /// 已储扣数量
+ ///
+ public decimal deductCount { get; set; }
+
+ ///
+ /// 剩余数量
+ ///
+ public decimal remainingCount { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string remark { get; set; }
+ }
+}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreDashboard/StoreDashboardStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreDashboard/StoreDashboardStatisticsOutput.cs
index 4381c10..eb375cd 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreDashboard/StoreDashboardStatisticsOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreDashboard/StoreDashboardStatisticsOutput.cs
@@ -94,6 +94,21 @@ namespace NCC.Extend.Entitys.Dto.LqStoreDashboard
/// 人均项目数(项目数/人头数)
///
public decimal AvgProjectPerHead { get; set; }
+
+ ///
+ /// 生美业绩(消耗业绩)
+ ///
+ public decimal LifeBeautyPerformance { get; set; }
+
+ ///
+ /// 医美业绩(消耗业绩)
+ ///
+ public decimal MedicalBeautyPerformance { get; set; }
+
+ ///
+ /// 科美业绩(消耗业绩)
+ ///
+ public decimal TechBeautyPerformance { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
index 012c63a..68d1ed8 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using Mapster;
using Microsoft.AspNetCore.Mvc;
@@ -40,6 +41,9 @@ using SqlSugar;
using Yitter.IdGenerator;
using NCC.Extend.Entitys.lq_kd_deductinfo;
using Microsoft.AspNetCore.Authorization;
+using NPOI.XSSF.UserModel;
+using NPOI.SS.UserModel;
+using System.Reflection;
namespace NCC.Extend.LqKhxx
{
@@ -1249,6 +1253,389 @@ namespace NCC.Extend.LqKhxx
}
#endregion
+ #region 导出所有会员剩余品项
+ ///
+ /// 导出所有会员剩余品项
+ ///
+ ///
+ /// 导出所有会员的剩余品项信息到Excel文件
+ ///
+ /// 示例请求:
+ /// ```http
+ /// GET /api/Extend/LqKhxx/Actions/ExportAllMemberRemainingItems
+ /// ```
+ ///
+ /// 返回数据说明:
+ /// - 返回Excel文件下载链接
+ /// - Excel文件包含所有会员的剩余品项信息
+ ///
+ /// Excel文件下载信息
+ /// 成功返回Excel文件下载链接
+ /// 服务器错误
+ [HttpGet("Actions/ExportAllMemberRemainingItems")]
+ public async Task ExportAllMemberRemainingItems()
+ {
+ try
+ {
+ _logger.LogInformation("开始导出所有会员剩余品项");
+
+ // 1. 查询所有有效会员
+ var members = await _db.Queryable()
+ .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
+ .Select(x => new { x.Id, x.Khmc, x.Sjh, x.Gsmd })
+ .ToListAsync();
+
+ if (members == null || !members.Any())
+ {
+ throw NCCException.Oh("没有找到会员数据");
+ }
+
+ // 2. 批量查询门店信息
+ var storeIds = members.Where(x => !string.IsNullOrEmpty(x.Gsmd)).Select(x => x.Gsmd).Distinct().ToList();
+ var storeDict = new Dictionary();
+ if (storeIds.Any())
+ {
+ var stores = await _db.Queryable()
+ .Where(x => storeIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.Dm })
+ .ToListAsync();
+ storeDict = stores.ToDictionary(x => x.Id, x => x.Dm ?? "");
+ }
+
+ var memberIds = members.Select(x => x.Id).ToList();
+
+ // 3. 批量查询所有开单品项数据
+ var baseItems = await _db.Queryable()
+ .Where(x => memberIds.Contains(x.MemberId))
+ .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
+ .Select(x => new
+ {
+ x.Id,
+ x.MemberId,
+ x.Px,
+ x.Pxmc,
+ x.Pxjg,
+ x.SourceType,
+ x.ProjectNumber,
+ x.Remark
+ })
+ .ToListAsync();
+
+ if (!baseItems.Any())
+ {
+ throw NCCException.Oh("没有找到品项数据");
+ }
+
+ var billingItemIds = baseItems.Select(x => x.Id).ToList();
+
+ // 4. 批量查询消费数据
+ var consumedData = await _db.Queryable()
+ .Where(x => billingItemIds.Contains(x.BillingItemId))
+ .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
+ .GroupBy(x => x.BillingItemId)
+ .Select(x => new
+ {
+ BillingItemId = x.BillingItemId,
+ TotalConsumed = SqlFunc.AggregateSum(x.OriginalProjectNumber)
+ })
+ .ToListAsync();
+
+ // 5. 批量查询退卡数据
+ var refundedData = await _db.Queryable()
+ .Where(x => billingItemIds.Contains(x.BillingItemId))
+ .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
+ .GroupBy(x => x.BillingItemId)
+ .Select(x => new
+ {
+ BillingItemId = x.BillingItemId,
+ TotalRefunded = SqlFunc.AggregateSum(x.ProjectNumber)
+ })
+ .ToListAsync();
+
+ // 6. 批量查询储扣数据
+ var deductData = await _db.Queryable()
+ .Where(x => billingItemIds.Contains(x.DeductId))
+ .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
+ .GroupBy(x => x.DeductId)
+ .Select(x => new
+ {
+ BillingItemId = x.DeductId,
+ TotalDeduct = SqlFunc.AggregateSum(x.ProjectNumber)
+ })
+ .ToListAsync();
+
+ // 7. 构建字典以提高查询效率
+ var memberDict = members.ToDictionary(x => x.Id, x => x);
+
+ // 8. 组装导出数据
+ var exportData = new List();
+ foreach (var item in baseItems)
+ {
+ if (!memberDict.ContainsKey(item.MemberId)) continue;
+
+ var member = memberDict[item.MemberId];
+ var consumed = consumedData.FirstOrDefault(c => c.BillingItemId == item.Id)?.TotalConsumed ?? 0m;
+ var refunded = refundedData.FirstOrDefault(r => r.BillingItemId == item.Id)?.TotalRefunded ?? 0m;
+ var deduct = deductData.FirstOrDefault(d => d.BillingItemId == item.Id)?.TotalDeduct ?? 0m;
+ var remaining = item.ProjectNumber - consumed - refunded - deduct;
+
+ // 只导出剩余数量不等于0的品项
+ if (remaining != 0)
+ {
+ exportData.Add(new MemberRemainingItemsExportOutput
+ {
+ memberId = item.MemberId,
+ memberName = member.Khmc ?? "",
+ phone = member.Sjh ?? "",
+ storeName = !string.IsNullOrEmpty(member.Gsmd) && storeDict.ContainsKey(member.Gsmd) ? storeDict[member.Gsmd] : "",
+ billingItemId = item.Id,
+ itemId = item.Px ?? "",
+ itemName = item.Pxmc ?? "",
+ itemPrice = item.Pxjg,
+ sourceType = item.SourceType ?? "",
+ totalPurchased = item.ProjectNumber,
+ consumedCount = consumed,
+ refundedCount = refunded,
+ deductCount = deduct,
+ remainingCount = remaining,
+ remark = item.Remark ?? ""
+ });
+ }
+ }
+
+ // 9. 配置Excel导出
+ ExcelConfig excelconfig = new ExcelConfig();
+ excelconfig.FileName = "所有会员剩余品项_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx";
+ excelconfig.HeadFont = "微软雅黑";
+ excelconfig.HeadPoint = 10;
+ excelconfig.IsAllSizeColumn = true;
+ excelconfig.ColumnModel = new List
+ {
+ new ExcelColumnModel { Column = "memberId", ExcelColumn = "会员ID" },
+ new ExcelColumnModel { Column = "memberName", ExcelColumn = "会员姓名" },
+ new ExcelColumnModel { Column = "phone", ExcelColumn = "手机号" },
+ new ExcelColumnModel { Column = "storeName", ExcelColumn = "归属门店" },
+ new ExcelColumnModel { Column = "billingItemId", ExcelColumn = "开单品项ID" },
+ new ExcelColumnModel { Column = "itemId", ExcelColumn = "品项ID" },
+ new ExcelColumnModel { Column = "itemName", ExcelColumn = "品项名称" },
+ new ExcelColumnModel { Column = "itemPrice", ExcelColumn = "品项单价" },
+ new ExcelColumnModel { Column = "sourceType", ExcelColumn = "来源类型" },
+ new ExcelColumnModel { Column = "totalPurchased", ExcelColumn = "总购买数量" },
+ new ExcelColumnModel { Column = "consumedCount", ExcelColumn = "已耗卡数量" },
+ new ExcelColumnModel { Column = "refundedCount", ExcelColumn = "已退卡数量" },
+ new ExcelColumnModel { Column = "deductCount", ExcelColumn = "已储扣数量" },
+ new ExcelColumnModel { Column = "remainingCount", ExcelColumn = "剩余数量" },
+ new ExcelColumnModel { Column = "remark", ExcelColumn = "备注" }
+ };
+
+ // 10. 查找项目根目录并创建ExportFiles文件夹
+ var baseDir = AppContext.BaseDirectory;
+ var projectRoot = baseDir;
+ var dir = new DirectoryInfo(baseDir);
+ while (dir != null && dir.Parent != null)
+ {
+ try
+ {
+ if (dir.GetDirectories(".git").Any() || dir.GetFiles("*.sln").Any())
+ {
+ projectRoot = dir.FullName;
+ break;
+ }
+ }
+ catch
+ {
+ // 忽略访问错误,继续向上查找
+ }
+ dir = dir.Parent;
+ }
+
+ // 如果没找到 .git 或 .sln 目录,再查找包含 .sln 文件的目录
+ if (projectRoot == baseDir)
+ {
+ dir = new DirectoryInfo(baseDir);
+ while (dir != null && dir.Parent != null)
+ {
+ try
+ {
+ if (dir.GetFiles("*.sln").Any())
+ {
+ projectRoot = dir.FullName;
+ break;
+ }
+ }
+ catch
+ {
+ // 忽略访问错误,继续向上查找
+ }
+ dir = dir.Parent;
+ }
+ }
+
+ // 在项目根目录下创建 ExportFiles 文件夹
+ var exportFilesPath = Path.Combine(projectRoot, "ExportFiles");
+ if (!Directory.Exists(exportFilesPath))
+ {
+ Directory.CreateDirectory(exportFilesPath);
+ }
+
+ var addPath = Path.Combine(exportFilesPath, excelconfig.FileName);
+
+ // 11. 导出Excel文件(支持多sheet,每个sheet最多65535行数据)
+ ExportToExcelWithMultipleSheets(exportData, excelconfig, addPath);
+
+ var fileName = _userManager.UserId + "|" + addPath + "|xlsx";
+ var output = new
+ {
+ name = excelconfig.FileName,
+ url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC")
+ };
+
+ _logger.LogInformation("导出所有会员剩余品项完成,共{Count}条记录", exportData.Count);
+
+ return output;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "导出所有会员剩余品项失败");
+ throw NCCException.Oh($"导出所有会员剩余品项失败:{ex.Message}");
+ }
+ }
+
+ ///
+ /// 导出Excel文件(支持多sheet,每个sheet最多65535行数据)
+ ///
+ private void ExportToExcelWithMultipleSheets(List exportData, ExcelConfig excelConfig, string filePath)
+ {
+ const int maxRowsPerSheet = 65535; // 每个sheet最多65535行数据(不包括表头)
+ var workbook = new XSSFWorkbook();
+ var type = typeof(MemberRemainingItemsExportOutput);
+ var properties = type.GetProperties();
+
+ // 创建样式
+ var headStyle = workbook.CreateCellStyle();
+ var headFont = workbook.CreateFont();
+ headFont.FontHeightInPoints = (short)excelConfig.HeadPoint;
+ headFont.FontName = excelConfig.HeadFont;
+ headFont.Boldweight = (short)700; // 粗体
+ headStyle.SetFont(headFont);
+ headStyle.Alignment = HorizontalAlignment.Left;
+
+ var dateStyle = workbook.CreateCellStyle();
+ var format = workbook.CreateDataFormat();
+ dateStyle.DataFormat = format.GetFormat("yyyy-MM-dd HH:mm:ss");
+
+ // 计算列宽
+ var columnWidths = new int[excelConfig.ColumnModel.Count];
+ for (int i = 0; i < excelConfig.ColumnModel.Count; i++)
+ {
+ var columnName = excelConfig.ColumnModel[i].ExcelColumn;
+ columnWidths[i] = Encoding.UTF8.GetBytes(columnName).Length + 2;
+ }
+
+ int dataIndex = 0;
+ int sheetIndex = 0;
+
+ while (dataIndex < exportData.Count)
+ {
+ // 创建新的sheet
+ var sheetName = sheetIndex == 0 ? "Sheet1" : $"Sheet{sheetIndex + 1}";
+ var sheet = workbook.CreateSheet(sheetName);
+
+ // 创建表头
+ var headerRow = sheet.CreateRow(0);
+ for (int col = 0; col < excelConfig.ColumnModel.Count; col++)
+ {
+ var cell = headerRow.CreateCell(col);
+ cell.SetCellValue(excelConfig.ColumnModel[col].ExcelColumn);
+ cell.CellStyle = headStyle;
+ sheet.SetColumnWidth(col, (columnWidths[col] + 1) * 256);
+ }
+
+ // 填充数据
+ int rowIndex = 1;
+ while (dataIndex < exportData.Count && rowIndex <= maxRowsPerSheet)
+ {
+ var item = exportData[dataIndex];
+ var dataRow = sheet.CreateRow(rowIndex);
+
+ for (int col = 0; col < excelConfig.ColumnModel.Count; col++)
+ {
+ var column = excelConfig.ColumnModel[col];
+ var property = properties.FirstOrDefault(p => p.Name == column.Column);
+ if (property != null)
+ {
+ var cell = dataRow.CreateCell(col);
+ var value = property.GetValue(item);
+ SetCellValue(cell, value, property.PropertyType, dateStyle);
+ }
+ }
+
+ rowIndex++;
+ dataIndex++;
+ }
+
+ sheetIndex++;
+ }
+
+ // 保存文件
+ using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
+ {
+ workbook.Write(fileStream);
+ }
+ }
+
+ ///
+ /// 设置单元格值
+ ///
+ private void SetCellValue(ICell cell, object value, Type propertyType, ICellStyle dateStyle)
+ {
+ if (value == null)
+ {
+ cell.SetCellValue("");
+ return;
+ }
+
+ var typeName = propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)
+ ? propertyType.GetGenericArguments()[0].ToString()
+ : propertyType.ToString();
+
+ switch (typeName)
+ {
+ case "System.String":
+ cell.SetCellValue(value.ToString());
+ break;
+ case "System.DateTime":
+ if (value is DateTime dateTime)
+ {
+ cell.SetCellValue(dateTime);
+ cell.CellStyle = dateStyle;
+ }
+ else
+ {
+ cell.SetCellValue("");
+ }
+ break;
+ case "System.Boolean":
+ cell.SetCellValue((bool)value);
+ break;
+ case "System.Int16":
+ case "System.Int32":
+ case "System.Int64":
+ case "System.Byte":
+ cell.SetCellValue(Convert.ToDouble(value));
+ break;
+ case "System.Decimal":
+ case "System.Double":
+ case "System.Single":
+ cell.SetCellValue(Convert.ToDouble(value));
+ break;
+ default:
+ cell.SetCellValue(value.ToString());
+ break;
+ }
+ }
+ #endregion
+
#region 获取会员类型枚举内容
///
/// 获取会员类型枚举内容
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStoreDashboardService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStoreDashboardService.cs
index d6b7e1f..7e57e8e 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqStoreDashboardService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStoreDashboardService.cs
@@ -200,6 +200,25 @@ namespace NCC.Extend
// 15. 计算人均项目数(项目数/人头数)
var avgProjectPerHead = headCount > 0 ? projectCount / (decimal)headCount : 0m;
+ // 16. 计算各分类消耗业绩(生美、医美、科美)
+ var categoryPerformanceSql = $@"
+ SELECT
+ COALESCE(SUM(CASE WHEN COALESCE(xmzl.qt2, '其他') = '生美' THEN CAST(COALESCE(xhpx.F_TotalPrice, xhpx.pxjg * xhpx.F_ProjectNumber, 0) AS DECIMAL(18,2)) ELSE 0 END), 0) as LifeBeautyPerformance,
+ COALESCE(SUM(CASE WHEN COALESCE(xmzl.qt2, '其他') = '医美' THEN CAST(COALESCE(xhpx.F_TotalPrice, xhpx.pxjg * xhpx.F_ProjectNumber, 0) AS DECIMAL(18,2)) ELSE 0 END), 0) as MedicalBeautyPerformance,
+ COALESCE(SUM(CASE WHEN COALESCE(xmzl.qt2, '其他') = '科美' THEN CAST(COALESCE(xhpx.F_TotalPrice, xhpx.pxjg * xhpx.F_ProjectNumber, 0) AS DECIMAL(18,2)) ELSE 0 END), 0) as TechBeautyPerformance
+ FROM lq_xh_pxmx xhpx
+ INNER JOIN lq_xh_hyhk xh ON xhpx.F_ConsumeInfoId = xh.F_Id AND xh.F_IsEffective = 1
+ LEFT JOIN lq_xmzl xmzl ON xhpx.px = xmzl.F_Id AND xmzl.F_IsEffective = 1
+ WHERE xhpx.F_IsEffective = 1
+ AND xh.md = '{input.StoreId}'
+ AND xh.hksj >= '{startDate:yyyy-MM-dd 00:00:00}'
+ AND xh.hksj <= '{endDateTime:yyyy-MM-dd HH:mm:ss}'";
+ var categoryPerformanceResult = await _db.Ado.SqlQueryAsync(categoryPerformanceSql);
+ var categoryPerformance = categoryPerformanceResult?.FirstOrDefault();
+ var lifeBeautyPerformance = categoryPerformance != null ? Convert.ToDecimal(categoryPerformance.LifeBeautyPerformance ?? 0) : 0m;
+ var medicalBeautyPerformance = categoryPerformance != null ? Convert.ToDecimal(categoryPerformance.MedicalBeautyPerformance ?? 0) : 0m;
+ var techBeautyPerformance = categoryPerformance != null ? Convert.ToDecimal(categoryPerformance.TechBeautyPerformance ?? 0) : 0m;
+
var result = new StoreDashboardStatisticsOutput
{
BillingPerformance = billingAmount,
@@ -219,7 +238,10 @@ namespace NCC.Extend
ProjectCount = Convert.ToInt32(projectCount),
AvgAmountPerPerson = avgAmountPerPerson,
AvgAmountPerProject = avgAmountPerProject,
- AvgProjectPerHead = avgProjectPerHead
+ AvgProjectPerHead = avgProjectPerHead,
+ LifeBeautyPerformance = lifeBeautyPerformance,
+ MedicalBeautyPerformance = medicalBeautyPerformance,
+ TechBeautyPerformance = techBeautyPerformance
};
_logger.LogInformation("门店驾驶舱统计数据查询完成,门店ID:{StoreId},开单业绩:{BillingPerformance},消耗业绩:{ConsumePerformance},完成率:{CompletionRate}%,净业绩:{NetPerformance},开单次数:{BillingCount},消耗次数:{ConsumeCount},退卡次数:{RefundCount}",
diff --git a/netcore/src/Modularity/System/NCC.System.Interfaces/Permission/IAuthorizeService.cs b/netcore/src/Modularity/System/NCC.System.Interfaces/Permission/IAuthorizeService.cs
index 3dbd477..7d82e63 100644
--- a/netcore/src/Modularity/System/NCC.System.Interfaces/Permission/IAuthorizeService.cs
+++ b/netcore/src/Modularity/System/NCC.System.Interfaces/Permission/IAuthorizeService.cs
@@ -46,6 +46,14 @@ namespace NCC.System.Interfaces.Permission
Task> GetCurrentUserResourceAuthorize(string userId, bool isAdmin);
///
+ /// 当前用户App模块权限
+ ///
+ /// 用户ID
+ /// 是否超管
+ ///
+ Task> GetCurrentUserAppModuleAuthorize(string userId, bool isAdmin);
+
+ ///
/// 获取权限项ids
///
/// 角色id
diff --git a/netcore/src/Modularity/System/NCC.System/Service/Permission/AuthorizeService.cs b/netcore/src/Modularity/System/NCC.System/Service/Permission/AuthorizeService.cs
index ce64751..0ceaa74 100644
--- a/netcore/src/Modularity/System/NCC.System/Service/Permission/AuthorizeService.cs
+++ b/netcore/src/Modularity/System/NCC.System/Service/Permission/AuthorizeService.cs
@@ -864,6 +864,63 @@ namespace NCC.System.Service.Permission
}
///
+ /// 当前用户App模块权限
+ ///
+ /// 用户ID
+ /// 是否超管
+ ///
+ [NonAction]
+ public async Task> GetCurrentUserAppModuleAuthorize(string userId, bool isAdmin)
+ {
+ var output = new List();
+ if (!isAdmin)
+ {
+ // 获取用户角色
+ var user = _userRepository.FirstOrDefault(u => u.Id == userId && u.DeleteMark == null);
+ if (user == null || string.IsNullOrEmpty(user.RoleId)) return output;
+
+ var roleArray = user.RoleId.Split(',');
+ // 验证角色是否启用且未删除
+ var roleId = await _roleRepository.Entities.In(r => r.Id, roleArray)
+ .Where(r => r.EnabledMark.Equals(1) && r.DeleteMark == null)
+ .Select(r => r.Id)
+ .ToListAsync();
+
+ if (roleId.Count == 0) return output;
+
+ // 获取角色拥有的模块权限项ID
+ var items = await _authorizeRepository.Entities
+ .In(a => a.ObjectId, roleId)
+ .Where(a => a.ItemType == "module")
+ .GroupBy(it => new { it.ItemId })
+ .Select(it => new { it.ItemId })
+ .ToListAsync();
+
+ if (items.Count == 0) return output;
+
+ // 获取模块信息,并过滤Category为"App"的模块
+ output = await _moduleRepository.Entities
+ .In(m => m.Id, items.Select(it => it.ItemId).ToArray())
+ .Where(a => a.EnabledMark.Equals(1)
+ && a.DeleteMark == null
+ && a.Category == "App")
+ .OrderBy(o => o.SortCode)
+ .ToListAsync();
+ }
+ else
+ {
+ // 管理员返回所有App模块
+ output = await _moduleRepository
+ .Where(a => a.EnabledMark.Equals(1)
+ && a.DeleteMark == null
+ && a.Category == "App")
+ .OrderBy(o => o.SortCode)
+ .ToListAsync();
+ }
+ return output;
+ }
+
+ ///
/// 当前用户模块权限资源
///
/// 用户ID
diff --git a/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersCurrentService.cs b/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersCurrentService.cs
index a955f1e..15cc069 100644
--- a/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersCurrentService.cs
+++ b/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersCurrentService.cs
@@ -156,6 +156,46 @@ namespace NCC.System.Service.Permission
}
///
+ /// 获取当前用户App权限
+ ///
+ ///
+ /// 获取当前登录用户的App权限列表,根据用户的角色获取对应的App模块权限
+ ///
+ /// 权限获取逻辑:
+ /// 1. 获取用户关联的角色ID列表
+ /// 2. 验证角色是否启用且未删除
+ /// 3. 根据角色ID从权限表中获取模块权限项
+ /// 4. 过滤Category为"App"的模块
+ /// 5. 返回树形结构的App权限列表
+ ///
+ /// 注意事项:
+ /// - 管理员用户返回所有App权限
+ /// - 普通用户只返回其角色拥有的App权限
+ /// - 只返回启用且未删除的模块
+ ///
+ /// App权限树形列表
+ /// 成功返回App权限列表
+ [HttpGet("AppAuthorize")]
+ public async Task GetAppAuthorize()
+ {
+ var userId = _userManager.UserId;
+ var isAdmin = _userManager.IsAdministrator;
+
+ // 获取App模块权限列表
+ var appModuleList = await _authorizeService.GetCurrentUserAppModuleAuthorize(userId, isAdmin);
+
+ if (appModuleList.Count == 0)
+ {
+ return new List();
+ }
+
+ // 转换为树形结构
+ var appModuleTree = appModuleList.Adapt>().ToTree("-1");
+
+ return appModuleTree;
+ }
+
+ ///
/// 获取系统日志
///
/// 参数