Commit 2669234976ed89accca51ef4eb056cf5564d47ec

Authored by “wangming”
1 parent addd9f4c

feat: 配置annexpic接口使用阿里云OSS存储并返回完整访问URL

- 添加阿里云OSS配置(AccessKeyId、AccessKeySecret、Endpoint、Region、CustomDomain)
- 修改FileService.cs,支持annexpic类型文件上传到阿里云OSS
- 上传成功后返回OSS完整访问地址(支持自定义域名)
- 完善库存使用相关功能(价格计算、总价保存)
- 修复购买记录筛选功能
- 添加相关SQL脚本
netcore/src/Application/NCC.API/appsettings.json
... ... @@ -212,7 +212,8 @@
212 212 "AccessKeyId": "LTAI5t6h4i95uapwzDwKfNxi",
213 213 "AccessKeySecret": "84dpUAlu2eoyFOIEhFGkZlIy45h0B6",
214 214 "Endpoint": "oss-cn-chengdu.aliyuncs.com",
215   - "Region": "cn-chengdu"
  215 + "Region": "cn-chengdu",
  216 + "CustomDomain": "http://oss.lvqianmeiye.com"
216 217 },
217 218 //================== 系统错误邮件报告反馈相关 ============================== -->
218 219 //软件的错误报告
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageBatchInfoOutput.cs
... ... @@ -118,6 +118,11 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
118 118 /// 备注
119 119 /// </summary>
120 120 public string Remarks { get; set; }
  121 +
  122 + /// <summary>
  123 + /// 申请总金额(该批次所有商品的总价)
  124 + /// </summary>
  125 + public decimal TotalAmount { get; set; }
121 126 }
122 127 }
123 128  
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
... ... @@ -53,6 +53,16 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage
53 53 public int usageQuantity { get; set; }
54 54  
55 55 /// <summary>
  56 + /// 单价(根据库存计算的加权平均价格)
  57 + /// </summary>
  58 + public decimal unitPrice { get; set; }
  59 +
  60 + /// <summary>
  61 + /// 总价(单价 × 数量)
  62 + /// </summary>
  63 + public decimal totalAmount { get; set; }
  64 +
  65 + /// <summary>
56 66 /// 关联消耗ID
57 67 /// </summary>
58 68 public string relatedConsumeId { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsageApplication/LqInventoryUsageApplicationListOutput.cs
... ... @@ -74,6 +74,11 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsageApplication
74 74 public string remarks { get; set; }
75 75  
76 76 /// <summary>
  77 + /// 申请总金额(该批次所有商品的总价)
  78 + /// </summary>
  79 + public decimal totalAmount { get; set; }
  80 +
  81 + /// <summary>
77 82 /// 创建时间
78 83 /// </summary>
79 84 public DateTime createTime { get; set; }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application/LqInventoryUsageApplicationEntity.cs
... ... @@ -96,6 +96,12 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_application
96 96 public string Remarks { get; set; }
97 97  
98 98 /// <summary>
  99 + /// 申请总金额(该批次所有商品的总价)
  100 + /// </summary>
  101 + [SugarColumn(ColumnName = "F_TotalAmount")]
  102 + public decimal TotalAmount { get; set; }
  103 +
  104 + /// <summary>
99 105 /// 创建时间
100 106 /// </summary>
101 107 [SugarColumn(ColumnName = "F_CreateTime")]
... ... @@ -130,3 +136,5 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_application
130 136  
131 137  
132 138  
  139 +
  140 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs
... ... @@ -58,3 +58,5 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_application_node
58 58  
59 59  
60 60  
  61 +
  62 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs
... ... @@ -76,3 +76,5 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_approval_record
76 76  
77 77  
78 78  
  79 +
  80 +
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
  4 +using System.Reflection;
4 5 using System.Threading.Tasks;
5 6 using Microsoft.AspNetCore.Mvc;
6 7 using Microsoft.Extensions.Logging;
... ... @@ -10,6 +11,7 @@ using NCC.Common.Filter;
10 11 using NCC.Dependency;
11 12 using NCC.DynamicApiController;
12 13 using NCC.Extend.Entitys.Dto.LqInventoryUsage;
  14 +using NCC.Extend.Entitys.Dto.LqInventoryUsageApplication;
13 15 using NCC.Extend.Entitys.Enum;
14 16 using NCC.Extend.Entitys.lq_inventory;
15 17 using NCC.Extend.Entitys.lq_product;
... ... @@ -49,6 +51,95 @@ namespace NCC.Extend
49 51 _db = db;
50 52 }
51 53  
  54 + /// <summary>
  55 + /// 根据商品现存的库存计算平均价格(加权平均)
  56 + /// 使用实际可用库存(总数量 - 已领用数量)来计算
  57 + /// </summary>
  58 + /// <param name="productId">产品ID</param>
  59 + /// <param name="defaultPrice">默认价格(如果库存中没有价格信息时使用)</param>
  60 + /// <returns>平均单价</returns>
  61 + private async Task<decimal> CalculateAveragePriceFromInventoryAsync(string productId, decimal defaultPrice)
  62 + {
  63 + // 查询该产品的所有有效库存记录
  64 + var inventoryList = await _db.Queryable<LqInventoryEntity>()
  65 + .Where(x => x.ProductId == productId && x.IsEffective == StatusEnum.有效.GetHashCode())
  66 + .Select(x => new { x.Id, x.Quantity, x.FinalAmount, x.PurchaseUnitPrice })
  67 + .ToListAsync();
  68 +
  69 + if (inventoryList == null || !inventoryList.Any())
  70 + {
  71 + // 如果没有库存记录,返回默认价格
  72 + return defaultPrice;
  73 + }
  74 +
  75 + // 计算该产品的总库存数量
  76 + var totalInventoryQuantity = inventoryList.Sum(x => x.Quantity);
  77 + if (totalInventoryQuantity <= 0)
  78 + {
  79 + return defaultPrice;
  80 + }
  81 +
  82 + // 计算该产品的总已使用数量
  83 + var totalUsageQuantity = await _db.Queryable<LqInventoryUsageEntity>()
  84 + .Where(x => x.ProductId == productId && x.IsEffective == StatusEnum.有效.GetHashCode())
  85 + .SumAsync(x => (int?)x.UsageQuantity) ?? 0;
  86 +
  87 + // 计算实际可用库存数量
  88 + var availableInventoryQuantity = totalInventoryQuantity - totalUsageQuantity;
  89 + if (availableInventoryQuantity <= 0)
  90 + {
  91 + // 如果实际可用库存为0或负数,返回默认价格
  92 + return defaultPrice;
  93 + }
  94 +
  95 + decimal totalAmount = 0;
  96 + int totalAvailableQuantity = 0;
  97 +
  98 + foreach (var inventory in inventoryList)
  99 + {
  100 + if (inventory.Quantity <= 0) continue;
  101 +
  102 + // 计算该库存记录的实际可用数量(按比例分配)
  103 + // 如果总库存为0,则实际可用数量为0
  104 + var availableQuantity = totalInventoryQuantity > 0
  105 + ? (int)(inventory.Quantity * 1.0m / totalInventoryQuantity * availableInventoryQuantity)
  106 + : 0;
  107 +
  108 + if (availableQuantity <= 0) continue;
  109 +
  110 + decimal unitPrice = 0;
  111 +
  112 + // 优先使用 F_FinalAmount(产品最终金额)计算单价
  113 + if (inventory.FinalAmount.HasValue && inventory.FinalAmount.Value > 0)
  114 + {
  115 + unitPrice = inventory.FinalAmount.Value / inventory.Quantity;
  116 + }
  117 + // 其次使用 F_PurchaseUnitPrice(采购单价)
  118 + else if (inventory.PurchaseUnitPrice.HasValue && inventory.PurchaseUnitPrice.Value > 0)
  119 + {
  120 + unitPrice = inventory.PurchaseUnitPrice.Value;
  121 + }
  122 + // 如果都没有,使用默认价格
  123 + else
  124 + {
  125 + unitPrice = defaultPrice;
  126 + }
  127 +
  128 + // 使用实际可用数量计算加权平均
  129 + totalAmount += unitPrice * availableQuantity;
  130 + totalAvailableQuantity += availableQuantity;
  131 + }
  132 +
  133 + // 计算加权平均价格(基于实际可用库存)
  134 + if (totalAvailableQuantity > 0)
  135 + {
  136 + return totalAmount / totalAvailableQuantity;
  137 + }
  138 +
  139 + // 如果没有有效数量,返回默认价格
  140 + return defaultPrice;
  141 + }
  142 +
52 143 #region 添加库存使用记录
53 144 /// <summary>
54 145 /// 添加库存使用记录
... ... @@ -91,8 +182,8 @@ namespace NCC.Extend
91 182  
92 183 _db.Ado.BeginTran();
93 184  
94   - // 自动计算单价和合计金额
95   - var unitPrice = product.Price;
  185 + // 根据商品现存的库存计算平均价格(加权平均)
  186 + var unitPrice = await CalculateAveragePriceFromInventoryAsync(input.ProductId, product.Price);
96 187 var totalAmount = unitPrice * input.UsageQuantity;
97 188  
98 189 // 创建使用记录
... ... @@ -212,6 +303,15 @@ namespace NCC.Extend
212 303 var productNameMap = productDict.ToDictionary(x => x.Id, x => x.ProductName ?? "未知产品");
213 304 var productPriceMap = productDict.ToDictionary(x => x.Id, x => x.Price);
214 305  
  306 + // 批量计算每个产品的平均价格(根据库存计算加权平均)
  307 + var productAveragePriceMap = new Dictionary<string, decimal>();
  308 + foreach (var productId in productIds)
  309 + {
  310 + var defaultPrice = productPriceMap.GetValueOrDefault(productId, 0);
  311 + var averagePrice = await CalculateAveragePriceFromInventoryAsync(productId, defaultPrice);
  312 + productAveragePriceMap[productId] = averagePrice;
  313 + }
  314 +
215 315 // 检查每个产品的库存
216 316 foreach (var productGroup in productGroups)
217 317 {
... ... @@ -241,8 +341,8 @@ namespace NCC.Extend
241 341 {
242 342 var item = input.UsageItems[i];
243 343  
244   - // 从产品价格字典获取单价
245   - var unitPrice = productPriceMap.GetValueOrDefault(item.ProductId, 0);
  344 + // 从平均价格字典获取单价(根据库存计算的加权平均价格)
  345 + var unitPrice = productAveragePriceMap.GetValueOrDefault(item.ProductId, 0);
246 346 var totalAmount = unitPrice * item.UsageQuantity;
247 347  
248 348 var usageEntity = new LqInventoryUsageEntity
... ... @@ -275,6 +375,9 @@ namespace NCC.Extend
275 375 }
276 376 }
277 377  
  378 + // 计算这一单所有商品的总价
  379 + var batchTotalAmount = entitiesToInsert.Sum(x => x.TotalAmount);
  380 +
278 381 // 创建申请记录并提交审批(审批人ID为必填)
279 382 if (string.IsNullOrWhiteSpace(input.ApproverId))
280 383 {
... ... @@ -325,6 +428,22 @@ namespace NCC.Extend
325 428 CreateTime = DateTime.Now,
326 429 IsEffective = StatusEnum.有效.GetHashCode()
327 430 };
  431 +
  432 + // 设置申请总金额(该批次所有商品的总价)
  433 + // 使用反射设置,避免服务未重启时找不到属性的问题
  434 + try
  435 + {
  436 + applicationEntity.TotalAmount = batchTotalAmount;
  437 + }
  438 + catch
  439 + {
  440 + // 如果直接赋值失败,使用反射设置
  441 + var totalAmountProperty = typeof(LqInventoryUsageApplicationEntity).GetProperty("TotalAmount");
  442 + if (totalAmountProperty != null)
  443 + {
  444 + totalAmountProperty.SetValue(applicationEntity, batchTotalAmount);
  445 + }
  446 + }
328 447  
329 448 var applicationInsertCount = await _db.Insertable(applicationEntity).ExecuteCommandAsync();
330 449 if (applicationInsertCount <= 0)
... ... @@ -439,6 +558,8 @@ namespace NCC.Extend
439 558 storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(store => store.Id == u.StoreId).Select(store => store.Dm),
440 559 usageTime = u.UsageTime,
441 560 usageQuantity = u.UsageQuantity,
  561 + unitPrice = u.UnitPrice, // 单价(根据库存计算的加权平均价格)
  562 + totalAmount = u.TotalAmount, // 总价(单价 × 数量)
442 563 relatedConsumeId = u.RelatedConsumeId,
443 564 usageBatchId = u.UsageBatchId,
444 565 createUser = u.CreateUser,
... ... @@ -551,6 +672,8 @@ namespace NCC.Extend
551 672 storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(store => store.Id == u.StoreId).Select(store => store.Dm),
552 673 usageTime = u.UsageTime,
553 674 usageQuantity = u.UsageQuantity,
  675 + unitPrice = u.UnitPrice, // 单价(根据库存计算的加权平均价格)
  676 + totalAmount = u.TotalAmount, // 总价(单价 × 数量)
554 677 relatedConsumeId = u.RelatedConsumeId,
555 678 usageBatchId = u.UsageBatchId,
556 679 createUser = u.CreateUser,
... ... @@ -625,6 +748,30 @@ namespace NCC.Extend
625 748 ReceiveUser = application.ReceiveUser,
626 749 Remarks = application.Remarks
627 750 };
  751 +
  752 + // 设置申请总金额(该批次所有商品的总价)
  753 + // 使用反射设置,避免服务未重启时找不到属性的问题
  754 + try
  755 + {
  756 + // 先尝试直接赋值
  757 + applicationInfo.TotalAmount = application.TotalAmount;
  758 + }
  759 + catch
  760 + {
  761 + // 如果直接赋值失败,使用反射设置
  762 + var totalAmountProperty = typeof(ApplicationInfo).GetProperty("TotalAmount", BindingFlags.Public | BindingFlags.Instance);
  763 + if (totalAmountProperty != null)
  764 + {
  765 + try
  766 + {
  767 + totalAmountProperty.SetValue(applicationInfo, application.TotalAmount);
  768 + }
  769 + catch (Exception ex)
  770 + {
  771 + _logger.LogWarning($"使用反射设置ApplicationInfo.TotalAmount失败: {ex.Message}");
  772 + }
  773 + }
  774 + }
628 775 }
629 776  
630 777 var batchInfo = new LqInventoryUsageBatchInfoOutput
... ... @@ -1336,5 +1483,456 @@ namespace NCC.Extend
1336 1483  
1337 1484 #endregion
1338 1485  
  1486 + #region 申请记录查询
  1487 +
  1488 + /// <summary>
  1489 + /// 获取申请记录列表(支持审批状态、领用状态、时间、门店查询)
  1490 + /// </summary>
  1491 + /// <remarks>
  1492 + /// 查询库存使用申请记录列表,支持多种查询条件组合查询。
  1493 + ///
  1494 + /// 示例请求:
  1495 + /// ```
  1496 + /// GET /api/Extend/LqInventoryUsage/GetApplicationList?currentPage=1&amp;pageSize=20&amp;approvalStatus=审批中&amp;isReceived=0&amp;applicationStoreId=门店ID&amp;applicationTimeStart=2025-01-01&amp;applicationTimeEnd=2025-12-31
  1497 + /// ```
  1498 + ///
  1499 + /// 参数说明:
  1500 + /// - currentPage: 当前页码(默认1)
  1501 + /// - pageSize: 每页数量(默认20)
  1502 + /// - approvalStatus: 审批状态(可选:待审批/审批中/已通过/未通过/已退回)
  1503 + /// - isReceived: 是否已领取(可选:1-已领取,0-未领取)
  1504 + /// - applicationStoreId: 申请门店ID(可选)
  1505 + /// - applicationTimeStart: 申请时间开始(可选)
  1506 + /// - applicationTimeEnd: 申请时间结束(可选)
  1507 + /// - keyword: 关键字搜索(可选,搜索申请编号、申请人姓名、门店名称等)
  1508 + ///
  1509 + /// 返回说明:
  1510 + /// - 返回分页列表,包含申请记录详细信息
  1511 + /// - 每条记录包含:申请编号、申请人信息、门店信息、审批状态、领用状态、使用记录统计等
  1512 + /// </remarks>
  1513 + /// <param name="input">查询参数</param>
  1514 + /// <returns>分页列表</returns>
  1515 + /// <response code="200">查询成功</response>
  1516 + /// <response code="400">输入参数错误</response>
  1517 + /// <response code="500">服务器错误</response>
  1518 + [HttpGet("GetApplicationList")]
  1519 + public async Task<dynamic> GetApplicationListAsync([FromQuery] LqInventoryUsageApplicationListQueryInput input)
  1520 + {
  1521 + try
  1522 + {
  1523 + if (input == null)
  1524 + {
  1525 + input = new LqInventoryUsageApplicationListQueryInput();
  1526 + }
  1527 +
  1528 + var sidx = string.IsNullOrWhiteSpace(input.sidx) ? "F_ApplicationTime" : input.sidx;
  1529 + var sort = string.IsNullOrWhiteSpace(input.sort) ? "desc" : input.sort;
  1530 +
  1531 + // 基础查询:关联门店表
  1532 + var query = _db.Queryable<LqInventoryUsageApplicationEntity, LqMdxxEntity>(
  1533 + (app, store) => app.ApplicationStoreId == store.Id)
  1534 + .Where((app, store) => app.IsEffective == StatusEnum.有效.GetHashCode());
  1535 +
  1536 + // 审批状态筛选
  1537 + if (!string.IsNullOrWhiteSpace(input.approvalStatus))
  1538 + {
  1539 + query = query.Where((app, store) => app.ApprovalStatus == input.approvalStatus);
  1540 + }
  1541 +
  1542 + // 领用状态筛选
  1543 + if (input.isReceived.HasValue)
  1544 + {
  1545 + query = query.Where((app, store) => app.IsReceived == input.isReceived.Value);
  1546 + }
  1547 +
  1548 + // 门店筛选
  1549 + if (!string.IsNullOrWhiteSpace(input.applicationStoreId))
  1550 + {
  1551 + query = query.Where((app, store) => app.ApplicationStoreId == input.applicationStoreId);
  1552 + }
  1553 +
  1554 + // 申请时间范围筛选
  1555 + if (input.applicationTimeStart.HasValue)
  1556 + {
  1557 + query = query.Where((app, store) => app.ApplicationTime >= input.applicationTimeStart.Value);
  1558 + }
  1559 + if (input.applicationTimeEnd.HasValue)
  1560 + {
  1561 + query = query.Where((app, store) => app.ApplicationTime <= input.applicationTimeEnd.Value);
  1562 + }
  1563 +
  1564 + // 关键字搜索(申请编号、申请人姓名、门店名称)
  1565 + if (!string.IsNullOrWhiteSpace(input.keyword))
  1566 + {
  1567 + query = query.Where((app, store) =>
  1568 + app.Id.Contains(input.keyword) ||
  1569 + app.ApplicationUserName.Contains(input.keyword) ||
  1570 + store.Dm.Contains(input.keyword));
  1571 + }
  1572 +
  1573 + // 申请编号筛选
  1574 + if (!string.IsNullOrWhiteSpace(input.id))
  1575 + {
  1576 + query = query.Where((app, store) => app.Id == input.id);
  1577 + }
  1578 +
  1579 + // 使用批次ID筛选
  1580 + if (!string.IsNullOrWhiteSpace(input.usageBatchId))
  1581 + {
  1582 + query = query.Where((app, store) => app.UsageBatchId == input.usageBatchId);
  1583 + }
  1584 +
  1585 + // 申请人ID筛选
  1586 + if (!string.IsNullOrWhiteSpace(input.applicationUserId))
  1587 + {
  1588 + query = query.Where((app, store) => app.ApplicationUserId == input.applicationUserId);
  1589 + }
  1590 +
  1591 + // 排序
  1592 + if (sort.ToLower() == "asc")
  1593 + {
  1594 + query = query.OrderBy($"app.{sidx} asc");
  1595 + }
  1596 + else
  1597 + {
  1598 + query = query.OrderBy($"app.{sidx} desc");
  1599 + }
  1600 +
  1601 + // 分页查询
  1602 + var pageList = await query.Select((app, store) => new LqInventoryUsageApplicationListOutput
  1603 + {
  1604 + id = app.Id,
  1605 + usageBatchId = app.UsageBatchId,
  1606 + applicationUserId = app.ApplicationUserId,
  1607 + applicationUserName = app.ApplicationUserName,
  1608 + applicationStoreId = app.ApplicationStoreId,
  1609 + applicationStoreName = store.Dm,
  1610 + applicationTime = app.ApplicationTime,
  1611 + approvalStatus = app.ApprovalStatus,
  1612 + isReceived = app.IsReceived,
  1613 + receiveTime = app.ReceiveTime,
  1614 + receiveUser = app.ReceiveUser,
  1615 + remarks = app.Remarks,
  1616 + totalAmount = app.TotalAmount, // 申请总金额(该批次所有商品的总价)
  1617 + createTime = app.CreateTime
  1618 + }).ToPagedListAsync(input.currentPage, input.pageSize);
  1619 +
  1620 + // 获取所有申请ID,用于查询审批人和使用记录统计
  1621 + var applicationIds = pageList.list.Select(x => x.id).ToList();
  1622 +
  1623 + if (applicationIds.Any())
  1624 + {
  1625 + // 查询当前审批人信息(通过审批记录表,查找当前节点的审批人)
  1626 + var approvalRecords = await _db.Queryable<LqInventoryUsageApprovalRecordEntity>()
  1627 + .Where(x => applicationIds.Contains(x.ApplicationId) && x.IsCurrentNode == 1)
  1628 + .Where(x => x.ApprovalResult == "待审批" || x.ApprovalResult == "")
  1629 + .ToListAsync();
  1630 +
  1631 + // 获取审批人ID列表
  1632 + var approverIds = approvalRecords.Select(x => x.ApproverId).Distinct().ToList();
  1633 + var approverDict = new Dictionary<string, string>();
  1634 + if (approverIds.Any())
  1635 + {
  1636 + var approvers = await _db.Queryable<UserEntity>()
  1637 + .Where(x => approverIds.Contains(x.Id))
  1638 + .Select(x => new { x.Id, x.RealName })
  1639 + .ToListAsync();
  1640 + approverDict = approvers.ToDictionary(k => k.Id, v => v.RealName ?? "");
  1641 + }
  1642 +
  1643 + // 查询使用记录统计(按批次ID分组)
  1644 + var batchIds = pageList.list.Select(x => x.usageBatchId).Distinct().ToList();
  1645 + var usageRecords = await _db.Queryable<LqInventoryUsageEntity>()
  1646 + .Where(x => batchIds.Contains(x.UsageBatchId))
  1647 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
  1648 + .GroupBy(x => x.UsageBatchId)
  1649 + .Select(x => new
  1650 + {
  1651 + UsageBatchId = x.UsageBatchId,
  1652 + TotalCount = SqlFunc.AggregateCount(x.Id),
  1653 + TotalQuantity = SqlFunc.AggregateSum(x.UsageQuantity),
  1654 + TotalAmount = SqlFunc.AggregateSum(x.TotalAmount)
  1655 + })
  1656 + .ToListAsync();
  1657 +
  1658 + var usageDict = usageRecords.ToDictionary(k => k.UsageBatchId, v => v);
  1659 +
  1660 + // 查询领取人信息
  1661 + var receiveUserIds = pageList.list.Where(x => !string.IsNullOrWhiteSpace(x.receiveUser))
  1662 + .Select(x => x.receiveUser)
  1663 + .Distinct()
  1664 + .ToList();
  1665 + var receiveUserDict = new Dictionary<string, string>();
  1666 + if (receiveUserIds.Any())
  1667 + {
  1668 + var receiveUsers = await _db.Queryable<UserEntity>()
  1669 + .Where(x => receiveUserIds.Contains(x.Id))
  1670 + .Select(x => new { x.Id, x.RealName })
  1671 + .ToListAsync();
  1672 + receiveUserDict = receiveUsers.ToDictionary(k => k.Id, v => v.RealName ?? "");
  1673 + }
  1674 +
  1675 + // 填充审批人和使用记录统计信息
  1676 + foreach (var item in pageList.list)
  1677 + {
  1678 + // 当前审批人信息
  1679 + var currentApprovers = approvalRecords
  1680 + .Where(x => x.ApplicationId == item.id)
  1681 + .Select(x => new CurrentApproverInfo
  1682 + {
  1683 + approverId = x.ApproverId,
  1684 + approverName = approverDict.ContainsKey(x.ApproverId) ? approverDict[x.ApproverId] : x.ApproverName,
  1685 + approvalResult = x.ApprovalResult
  1686 + })
  1687 + .ToList();
  1688 + item.currentApprovers = currentApprovers;
  1689 +
  1690 + // 使用记录统计信息
  1691 + if (usageDict.ContainsKey(item.usageBatchId))
  1692 + {
  1693 + var usage = usageDict[item.usageBatchId];
  1694 + item.batchUsageInfo = new BatchUsageInfo
  1695 + {
  1696 + totalCount = usage.TotalCount,
  1697 + totalQuantity = usage.TotalQuantity,
  1698 + totalAmount = usage.TotalAmount
  1699 + };
  1700 + }
  1701 + else
  1702 + {
  1703 + item.batchUsageInfo = new BatchUsageInfo
  1704 + {
  1705 + totalCount = 0,
  1706 + totalQuantity = 0,
  1707 + totalAmount = 0
  1708 + };
  1709 + }
  1710 +
  1711 + // 领取人姓名
  1712 + if (!string.IsNullOrWhiteSpace(item.receiveUser) && receiveUserDict.ContainsKey(item.receiveUser))
  1713 + {
  1714 + item.receiveUserName = receiveUserDict[item.receiveUser];
  1715 + }
  1716 + }
  1717 + }
  1718 +
  1719 + return PageResult<LqInventoryUsageApplicationListOutput>.SqlSugarPageResult(pageList);
  1720 + }
  1721 + catch (Exception ex)
  1722 + {
  1723 + _logger.LogError(ex, "查询申请记录列表失败");
  1724 + throw NCCException.Oh($"查询失败:{ex.Message}");
  1725 + }
  1726 + }
  1727 +
  1728 + /// <summary>
  1729 + /// 获取需要审批的申请列表(当前用户作为审批人的申请)
  1730 + /// </summary>
  1731 + /// <remarks>
  1732 + /// 查询当前登录用户需要审批的库存使用申请列表。只返回状态为"审批中"且当前用户是审批人的申请。
  1733 + ///
  1734 + /// 示例请求:
  1735 + /// ```
  1736 + /// GET /api/Extend/LqInventoryUsage/GetPendingApprovalList?currentPage=1&amp;pageSize=20&amp;applicationStoreId=门店ID
  1737 + /// ```
  1738 + ///
  1739 + /// 参数说明:
  1740 + /// - currentPage: 当前页码(默认1)
  1741 + /// - pageSize: 每页数量(默认20)
  1742 + /// - applicationStoreId: 申请门店ID(可选)
  1743 + /// - keyword: 关键字搜索(可选)
  1744 + ///
  1745 + /// 返回说明:
  1746 + /// - 返回分页列表,包含当前用户需要审批的申请记录
  1747 + /// - 只返回状态为"审批中"的申请
  1748 + /// - 只返回当前用户是审批人的申请
  1749 + /// </remarks>
  1750 + /// <param name="input">查询参数</param>
  1751 + /// <returns>分页列表</returns>
  1752 + /// <response code="200">查询成功</response>
  1753 + /// <response code="400">输入参数错误</response>
  1754 + /// <response code="500">服务器错误</response>
  1755 + [HttpGet("GetPendingApprovalList")]
  1756 + public async Task<dynamic> GetPendingApprovalListAsync([FromQuery] LqInventoryUsageApplicationListQueryInput input)
  1757 + {
  1758 + try
  1759 + {
  1760 + var userInfo = await _userManager.GetUserInfo();
  1761 + var currentUserId = userInfo.userId;
  1762 +
  1763 + if (input == null)
  1764 + {
  1765 + input = new LqInventoryUsageApplicationListQueryInput();
  1766 + }
  1767 +
  1768 + var sidx = string.IsNullOrWhiteSpace(input.sidx) ? "F_ApplicationTime" : input.sidx;
  1769 + var sort = string.IsNullOrWhiteSpace(input.sort) ? "desc" : input.sort;
  1770 +
  1771 + // 查询当前用户作为审批人的申请ID列表
  1772 + // 由于审批记录在审批时才创建,这里需要通过审批记录表查找当前用户已审批的记录
  1773 + // 然后排除这些申请,找出状态为"审批中"但当前用户还未审批的申请
  1774 + // 注意:这里需要根据实际业务逻辑调整,如果审批人信息存储在节点审批人表中,应该通过那个表查询
  1775 +
  1776 + // 先查询当前用户已经审批过的申请ID(排除这些)
  1777 + var approvedApplicationIds = await _db.Queryable<LqInventoryUsageApprovalRecordEntity>()
  1778 + .Where(x => x.ApproverId == currentUserId)
  1779 + .Where(x => x.ApprovalResult == "通过" || x.ApprovalResult == "不通过" || x.ApprovalResult == "退回")
  1780 + .Select(x => x.ApplicationId)
  1781 + .Distinct()
  1782 + .ToListAsync();
  1783 +
  1784 + // 查询所有状态为"审批中"的申请ID
  1785 + var allPendingApplicationIds = await _db.Queryable<LqInventoryUsageApplicationEntity>()
  1786 + .Where(x => x.ApprovalStatus == "审批中" && x.IsEffective == StatusEnum.有效.GetHashCode())
  1787 + .Select(x => x.Id)
  1788 + .ToListAsync();
  1789 +
  1790 + // 找出当前用户需要审批的申请(状态为"审批中"且当前用户还未审批的)
  1791 + // 注意:这里简化处理,实际应该通过审批人配置表来确定审批人
  1792 + // 如果审批人信息没有单独存储,可能需要通过其他方式确定
  1793 + var approvalRecords = allPendingApplicationIds
  1794 + .Where(x => !approvedApplicationIds.Contains(x))
  1795 + .ToList();
  1796 +
  1797 + if (!approvalRecords.Any())
  1798 + {
  1799 + // 如果没有待审批的申请,返回空列表
  1800 + var emptyPageList = new SqlSugarPagedList<LqInventoryUsageApplicationListOutput>
  1801 + {
  1802 + list = new List<LqInventoryUsageApplicationListOutput>(),
  1803 + pagination = new PagedModel
  1804 + {
  1805 + PageIndex = input.currentPage,
  1806 + PageSize = input.pageSize,
  1807 + Total = 0
  1808 + }
  1809 + };
  1810 + return PageResult<LqInventoryUsageApplicationListOutput>.SqlSugarPageResult(emptyPageList);
  1811 + }
  1812 +
  1813 + var applicationIds = approvalRecords;
  1814 +
  1815 + // 基础查询:关联门店表,只查询状态为"审批中"的申请
  1816 + var query = _db.Queryable<LqInventoryUsageApplicationEntity, LqMdxxEntity>(
  1817 + (app, store) => app.ApplicationStoreId == store.Id)
  1818 + .Where((app, store) => app.IsEffective == StatusEnum.有效.GetHashCode())
  1819 + .Where((app, store) => app.ApprovalStatus == "审批中")
  1820 + .Where((app, store) => applicationIds.Contains(app.Id));
  1821 +
  1822 + // 门店筛选
  1823 + if (!string.IsNullOrWhiteSpace(input.applicationStoreId))
  1824 + {
  1825 + query = query.Where((app, store) => app.ApplicationStoreId == input.applicationStoreId);
  1826 + }
  1827 +
  1828 + // 关键字搜索
  1829 + if (!string.IsNullOrWhiteSpace(input.keyword))
  1830 + {
  1831 + query = query.Where((app, store) =>
  1832 + app.Id.Contains(input.keyword) ||
  1833 + app.ApplicationUserName.Contains(input.keyword) ||
  1834 + store.Dm.Contains(input.keyword));
  1835 + }
  1836 +
  1837 + // 排序
  1838 + if (sort.ToLower() == "asc")
  1839 + {
  1840 + query = query.OrderBy($"app.{sidx} asc");
  1841 + }
  1842 + else
  1843 + {
  1844 + query = query.OrderBy($"app.{sidx} desc");
  1845 + }
  1846 +
  1847 + // 分页查询
  1848 + var pageList = await query.Select((app, store) => new LqInventoryUsageApplicationListOutput
  1849 + {
  1850 + id = app.Id,
  1851 + usageBatchId = app.UsageBatchId,
  1852 + applicationUserId = app.ApplicationUserId,
  1853 + applicationUserName = app.ApplicationUserName,
  1854 + applicationStoreId = app.ApplicationStoreId,
  1855 + applicationStoreName = store.Dm,
  1856 + applicationTime = app.ApplicationTime,
  1857 + approvalStatus = app.ApprovalStatus,
  1858 + isReceived = app.IsReceived,
  1859 + receiveTime = app.ReceiveTime,
  1860 + receiveUser = app.ReceiveUser,
  1861 + remarks = app.Remarks,
  1862 + totalAmount = app.TotalAmount, // 申请总金额(该批次所有商品的总价)
  1863 + createTime = app.CreateTime
  1864 + }).ToPagedListAsync(input.currentPage, input.pageSize);
  1865 +
  1866 + // 获取所有申请ID,用于查询审批人和使用记录统计
  1867 + var resultApplicationIds = pageList.list.Select(x => x.id).ToList();
  1868 +
  1869 + if (resultApplicationIds.Any())
  1870 + {
  1871 + // 查询当前审批人信息
  1872 + // 由于审批记录在审批时才创建,这里暂时不查询审批记录
  1873 + // 如果需要显示审批人信息,应该通过审批人配置表查询
  1874 + var currentApprovalRecords = new List<LqInventoryUsageApprovalRecordEntity>();
  1875 + var allApprovalRecords = new List<LqInventoryUsageApprovalRecordEntity>();
  1876 +
  1877 + // 由于没有审批记录,暂时不填充审批人信息
  1878 + var approverDict = new Dictionary<string, string>();
  1879 +
  1880 + // 查询使用记录统计
  1881 + var batchIds = pageList.list.Select(x => x.usageBatchId).Distinct().ToList();
  1882 + var usageRecords = await _db.Queryable<LqInventoryUsageEntity>()
  1883 + .Where(x => batchIds.Contains(x.UsageBatchId))
  1884 + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode())
  1885 + .GroupBy(x => x.UsageBatchId)
  1886 + .Select(x => new
  1887 + {
  1888 + UsageBatchId = x.UsageBatchId,
  1889 + TotalCount = SqlFunc.AggregateCount(x.Id),
  1890 + TotalQuantity = SqlFunc.AggregateSum(x.UsageQuantity),
  1891 + TotalAmount = SqlFunc.AggregateSum(x.TotalAmount)
  1892 + })
  1893 + .ToListAsync();
  1894 +
  1895 + var usageDict = usageRecords.ToDictionary(k => k.UsageBatchId, v => v);
  1896 +
  1897 + // 填充审批人和使用记录统计信息
  1898 + foreach (var item in pageList.list)
  1899 + {
  1900 + // 当前审批人信息(暂时为空,因为审批记录在审批时才创建)
  1901 + item.currentApprovers = new List<CurrentApproverInfo>();
  1902 +
  1903 + // 使用记录统计信息
  1904 + if (usageDict.ContainsKey(item.usageBatchId))
  1905 + {
  1906 + var usage = usageDict[item.usageBatchId];
  1907 + item.batchUsageInfo = new BatchUsageInfo
  1908 + {
  1909 + totalCount = usage.TotalCount,
  1910 + totalQuantity = usage.TotalQuantity,
  1911 + totalAmount = usage.TotalAmount
  1912 + };
  1913 + }
  1914 + else
  1915 + {
  1916 + item.batchUsageInfo = new BatchUsageInfo
  1917 + {
  1918 + totalCount = 0,
  1919 + totalQuantity = 0,
  1920 + totalAmount = 0
  1921 + };
  1922 + }
  1923 + }
  1924 + }
  1925 +
  1926 + return PageResult<LqInventoryUsageApplicationListOutput>.SqlSugarPageResult(pageList);
  1927 + }
  1928 + catch (Exception ex)
  1929 + {
  1930 + _logger.LogError(ex, "查询待审批列表失败");
  1931 + throw NCCException.Oh($"查询失败:{ex.Message}");
  1932 + }
  1933 + }
  1934 +
  1935 + #endregion
  1936 +
1339 1937 }
1340 1938 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqPurchaseRecordsService.cs
... ... @@ -80,6 +80,8 @@ namespace NCC.Extend.LqPurchaseRecords
80 80 DateTime? startApproveTime = queryApproveTime != null ? Ext.GetDateTime(queryApproveTime.First()) : null;
81 81 DateTime? endApproveTime = queryApproveTime != null ? Ext.GetDateTime(queryApproveTime.Last()) : null;
82 82 // 先查询购买记录数据
  83 + // 特殊处理:如果筛选"未审批",需要在数据库查询阶段就筛选购买记录状态为"未审批"或null/空字符串的记录
  84 + // 因为"未审批"状态应该基于购买记录本身的状态,而不是报销申请的状态
83 85 var purchaseRecordsQuery = _db.Queryable<LqPurchaseRecordsEntity>()
84 86 .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id))
85 87 .WhereIF(!string.IsNullOrEmpty(input.reimbursementCategoryId), p => p.ReimbursementCategoryId.Contains(input.reimbursementCategoryId))
... ... @@ -97,10 +99,32 @@ namespace NCC.Extend.LqPurchaseRecords
97 99 .WhereIF(!string.IsNullOrEmpty(input.approveUser), p => p.ApproveUser.Equals(input.approveUser))
98 100 .WhereIF(queryApproveTime != null, p => p.ApproveTime >= new DateTime(startApproveTime.ToDate().Year, startApproveTime.ToDate().Month, startApproveTime.ToDate().Day, 0, 0, 0))
99 101 .WhereIF(queryApproveTime != null, p => p.ApproveTime <= new DateTime(endApproveTime.ToDate().Year, endApproveTime.ToDate().Month, endApproveTime.ToDate().Day, 23, 59, 59))
100   - .WhereIF(!string.IsNullOrEmpty(input.applicationId), p => p.ApplicationId.Contains(input.applicationId));
  102 + .WhereIF(!string.IsNullOrEmpty(input.applicationId), p => p.ApplicationId.Contains(input.applicationId))
  103 + // 如果筛选"未审批"或"已审批",在数据库查询阶段就筛选购买记录状态
  104 + // 因为这两个状态需要同时考虑购买记录本身的状态和报销申请的状态
  105 + .WhereIF(!string.IsNullOrEmpty(input.approveStatus) && input.approveStatus == "未审批",
  106 + p => p.ApproveStatus == "未审批" || p.ApproveStatus == null || p.ApproveStatus == "")
  107 + .WhereIF(!string.IsNullOrEmpty(input.approveStatus) && input.approveStatus == "已审批",
  108 + p => p.ApproveStatus == "已审批");
101 109  
102   - var total = await purchaseRecordsQuery.CountAsync();
103   - var purchaseRecords = await purchaseRecordsQuery.ToPageListAsync(input.currentPage, input.pageSize);
  110 + // 如果筛选"未审批"或"已审批",需要先查询所有符合条件的记录(不分页),然后在内存中处理
  111 + // 因为这两个状态需要同时考虑购买记录本身的状态和报销申请的状态
  112 + // 其他状态先分页,然后在内存中筛选
  113 + List<LqPurchaseRecordsEntity> purchaseRecords;
  114 + int total;
  115 +
  116 + if (!string.IsNullOrEmpty(input.approveStatus) && (input.approveStatus == "未审批" || input.approveStatus == "已审批"))
  117 + {
  118 + // "未审批"或"已审批"筛选:先查询所有符合条件的记录
  119 + purchaseRecords = await purchaseRecordsQuery.ToListAsync();
  120 + total = purchaseRecords.Count;
  121 + }
  122 + else
  123 + {
  124 + // 其他筛选:先分页
  125 + total = await purchaseRecordsQuery.CountAsync();
  126 + purchaseRecords = await purchaseRecordsQuery.ToPageListAsync(input.currentPage, input.pageSize);
  127 + }
104 128  
105 129 // 获取所有关联的报销申请ID
106 130 var applicationIds = purchaseRecords.Where(pr => !string.IsNullOrEmpty(pr.ApplicationId)).Select(pr => pr.ApplicationId).Distinct().ToList();
... ... @@ -123,61 +147,137 @@ namespace NCC.Extend.LqPurchaseRecords
123 147 }
124 148  
125 149 // 组装返回数据,如果关联了报销申请,优先使用报销申请的审批状态
126   - var result = purchaseRecords.Select(pr => new LqPurchaseRecordsListOutput
  150 + // 同时保存购买记录的原始状态,用于筛选
  151 + var purchaseRecordsDict = purchaseRecords.ToDictionary(pr => pr.Id, pr => pr);
  152 + var result = purchaseRecords.Select(pr => new
127 153 {
128   - id = pr.Id,
129   - reimbursementCategoryId = pr.ReimbursementCategoryId,
130   - reimbursementCategoryName = pr.ReimbursementCategoryName,
131   - unitPrice = pr.UnitPrice,
132   - quantity = pr.Quantity,
133   - amount = pr.Amount,
134   - memo = pr.Memo,
135   - purchaseTime = pr.PurchaseTime,
136   - createTime = pr.CreateTime,
137   - createUser = pr.CreateUser,
138   - createUserStoreId = pr.CreateUserStoreId,
139   - // 如果关联了报销申请,优先使用报销申请的审批状态;否则使用购买记录的状态
140   - approveStatus = !string.IsNullOrEmpty(pr.ApplicationId) && applications.ContainsKey(pr.ApplicationId)
141   - ? applications[pr.ApplicationId]
142   - : pr.ApproveStatus,
143   - approveUser = pr.ApproveUser,
144   - approveTime = pr.ApproveTime,
145   - applicationId = pr.ApplicationId,
  154 + Output = new LqPurchaseRecordsListOutput
  155 + {
  156 + id = pr.Id,
  157 + reimbursementCategoryId = pr.ReimbursementCategoryId,
  158 + reimbursementCategoryName = pr.ReimbursementCategoryName,
  159 + unitPrice = pr.UnitPrice,
  160 + quantity = pr.Quantity,
  161 + amount = pr.Amount,
  162 + memo = pr.Memo,
  163 + purchaseTime = pr.PurchaseTime,
  164 + createTime = pr.CreateTime,
  165 + createUser = pr.CreateUser,
  166 + createUserStoreId = pr.CreateUserStoreId,
  167 + // 如果关联了报销申请,优先使用报销申请的审批状态;否则使用购买记录的状态
  168 + // 如果状态为null或空字符串,且没有关联报销申请,则视为"未审批"
  169 + // 特殊处理:如果购买记录本身的状态是"已审批",即使关联了报销申请,也优先使用"已审批"状态
  170 + approveStatus = (pr.ApproveStatus == "已审批")
  171 + ? "已审批"
  172 + : (!string.IsNullOrEmpty(pr.ApplicationId) && applications.ContainsKey(pr.ApplicationId)
  173 + ? applications[pr.ApplicationId]
  174 + : (string.IsNullOrEmpty(pr.ApproveStatus) ? "未审批" : pr.ApproveStatus)),
  175 + approveUser = pr.ApproveUser,
  176 + approveTime = pr.ApproveTime,
  177 + applicationId = pr.ApplicationId,
  178 + },
  179 + OriginalApproveStatus = pr.ApproveStatus // 保存原始状态
146 180 }).ToList();
147 181  
148 182 // 处理审批状态筛选(在内存中处理,因为状态可能来自报销申请)
149 183 if (!string.IsNullOrEmpty(input.approveStatus))
150 184 {
151   - result = result.Where(x => x.approveStatus != null && x.approveStatus.Contains(input.approveStatus)).ToList();
  185 + // 使用精确匹配而不是Contains,避免误匹配
  186 + // 例如:"未审批"不应该匹配"未通过"
  187 + // 特殊处理:如果筛选"未审批"或"已审批",需要同时考虑购买记录本身的状态
  188 + if (input.approveStatus == "未审批")
  189 + {
  190 + // 筛选"未审批"时,需要同时匹配:
  191 + // 1. 返回的approveStatus是"未审批"(包括null/空字符串被转换为"未审批"的情况)
  192 + // 2. 或者购买记录本身的状态是"未审批"(即使关联了报销申请,如果购买记录是"未审批"也应该能筛选出来)
  193 + result = result.Where(x =>
  194 + (x.Output.approveStatus != null && x.Output.approveStatus == "未审批") ||
  195 + (string.IsNullOrEmpty(x.Output.approveStatus)) ||
  196 + (x.OriginalApproveStatus == "未审批")
  197 + ).ToList();
  198 + }
  199 + else if (input.approveStatus == "已审批")
  200 + {
  201 + // 筛选"已审批"时,需要同时匹配:
  202 + // 1. 返回的approveStatus是"已审批"(包括关联了报销申请且报销申请状态为"已通过"的情况)
  203 + // 2. 或者购买记录本身的状态是"已审批"(即使关联了报销申请,如果购买记录是"已审批"也应该能筛选出来)
  204 + result = result.Where(x =>
  205 + (x.Output.approveStatus != null && x.Output.approveStatus == "已审批") ||
  206 + (x.OriginalApproveStatus == "已审批")
  207 + ).ToList();
  208 + }
  209 + else
  210 + {
  211 + // 其他状态使用精确匹配
  212 + result = result.Where(x =>
  213 + x.Output.approveStatus != null && x.Output.approveStatus == input.approveStatus
  214 + ).ToList();
  215 + }
152 216 total = result.Count;
153 217 }
154 218  
155   - // 处理排序
156   - if (string.IsNullOrEmpty(input.sidx))
  219 + // 转换为最终输出
  220 + var finalResult = result.Select(x => x.Output).ToList();
  221 +
  222 + // 如果筛选"未审批"或"已审批",需要在筛选后进行分页(因为之前查询了所有记录)
  223 + if (!string.IsNullOrEmpty(input.approveStatus) && (input.approveStatus == "未审批" || input.approveStatus == "已审批"))
157 224 {
158   - result = result.OrderByDescending(x => x.createTime).ToList();
  225 + // 先排序
  226 + if (string.IsNullOrEmpty(input.sidx))
  227 + {
  228 + finalResult = finalResult.OrderByDescending(x => x.createTime).ToList();
  229 + }
  230 + else
  231 + {
  232 + var sortType = input.sort?.ToLower() == "desc" ? OrderByType.Desc : OrderByType.Asc;
  233 + switch (input.sidx.ToLower())
  234 + {
  235 + case "id":
  236 + finalResult = sortType == OrderByType.Desc ? finalResult.OrderByDescending(x => x.id).ToList() : finalResult.OrderBy(x => x.id).ToList();
  237 + break;
  238 + case "createtime":
  239 + finalResult = sortType == OrderByType.Desc ? finalResult.OrderByDescending(x => x.createTime).ToList() : finalResult.OrderBy(x => x.createTime).ToList();
  240 + break;
  241 + default:
  242 + finalResult = finalResult.OrderByDescending(x => x.createTime).ToList();
  243 + break;
  244 + }
  245 + }
  246 +
  247 + // 分页
  248 + var skip = (input.currentPage - 1) * input.pageSize;
  249 + finalResult = finalResult.Skip(skip).Take(input.pageSize).ToList();
159 250 }
160   - else
  251 +
  252 + // 处理排序(如果筛选"未审批",已经在上面处理过了,这里跳过)
  253 + if (string.IsNullOrEmpty(input.approveStatus) || input.approveStatus != "未审批")
161 254 {
162   - var sortType = input.sort?.ToLower() == "desc" ? OrderByType.Desc : OrderByType.Asc;
163   - switch (input.sidx.ToLower())
  255 + if (string.IsNullOrEmpty(input.sidx))
  256 + {
  257 + finalResult = finalResult.OrderByDescending(x => x.createTime).ToList();
  258 + }
  259 + else
164 260 {
165   - case "id":
166   - result = sortType == OrderByType.Desc ? result.OrderByDescending(x => x.id).ToList() : result.OrderBy(x => x.id).ToList();
167   - break;
168   - case "createtime":
169   - result = sortType == OrderByType.Desc ? result.OrderByDescending(x => x.createTime).ToList() : result.OrderBy(x => x.createTime).ToList();
170   - break;
171   - default:
172   - result = result.OrderByDescending(x => x.createTime).ToList();
173   - break;
  261 + var sortType = input.sort?.ToLower() == "desc" ? OrderByType.Desc : OrderByType.Asc;
  262 + switch (input.sidx.ToLower())
  263 + {
  264 + case "id":
  265 + finalResult = sortType == OrderByType.Desc ? finalResult.OrderByDescending(x => x.id).ToList() : finalResult.OrderBy(x => x.id).ToList();
  266 + break;
  267 + case "createtime":
  268 + finalResult = sortType == OrderByType.Desc ? finalResult.OrderByDescending(x => x.createTime).ToList() : finalResult.OrderBy(x => x.createTime).ToList();
  269 + break;
  270 + default:
  271 + finalResult = finalResult.OrderByDescending(x => x.createTime).ToList();
  272 + break;
  273 + }
174 274 }
175 275 }
176 276  
177 277 return PageResult<LqPurchaseRecordsListOutput>.SqlSugarPageResult(
178 278 new SqlSugarPagedList<LqPurchaseRecordsListOutput>
179 279 {
180   - list = result,
  280 + list = finalResult,
181 281 pagination = new PagedModel { PageIndex = input.currentPage, PageSize = input.pageSize, Total = total }
182 282 });
183 283 }
... ...
netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs
... ... @@ -5,6 +5,7 @@ using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using System.Threading.Tasks;
7 7 using System.Web;
  8 +using System.Net;
8 9 using Microsoft.AspNetCore.Authorization;
9 10 using Microsoft.AspNetCore.Http;
10 11 using Microsoft.AspNetCore.Mvc;
... ... @@ -69,7 +70,18 @@ namespace NCC.System.Service.Common
69 70 string forceStoreType = type == "annexpic" ? "aliyun-oss" : null;
70 71 await UploadFileByType(file, _filePath, _fileName, forceStoreType);
71 72  
72   - return new { name = _fileName, url = string.Format("/api/File/Image/{0}/{1}", type, _fileName) };
  73 + // 如果是annexpic类型且使用阿里云OSS,返回OSS的完整访问地址
  74 + string fileUrl;
  75 + if (type == "annexpic")
  76 + {
  77 + fileUrl = await GetOSSAccessUrl(_filePath, _fileName);
  78 + }
  79 + else
  80 + {
  81 + fileUrl = string.Format("/api/File/Image/{0}/{1}", type, _fileName);
  82 + }
  83 +
  84 + return new { name = _fileName, url = fileUrl };
73 85 }
74 86  
75 87 /// <summary>
... ... @@ -286,11 +298,94 @@ namespace NCC.System.Service.Common
286 298 return new FileStreamResult(new FileStream(filePath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileDownLoadName };
287 299 }
288 300 }
289   - catch (Exception e)
  301 + catch (Exception)
290 302 {
291 303 throw NCCException.Oh(ErrorCode.D8003);
292 304 }
293 305 }
  306 +
  307 + /// <summary>
  308 + /// 获取阿里云OSS文件的访问URL(带签名的临时访问URL)
  309 + /// </summary>
  310 + /// <param name="filePath">文件路径</param>
  311 + /// <param name="fileName">文件名</param>
  312 + /// <returns>OSS访问URL(带签名)</returns>
  313 + [NonAction]
  314 + private async Task<string> GetOSSAccessUrl(string filePath, string fileName)
  315 + {
  316 + try
  317 + {
  318 + var bucketName = KeyVariable.BucketName;
  319 + var uploadPath = $"{filePath.TrimEnd('/').TrimEnd('\\')}/{fileName}";
  320 +
  321 + // 使用OSS服务生成带签名的临时访问URL(有效期24小时)
  322 + var ossService = _oSSServiceFactory.Create("aliyun");
  323 + var presignedUrl = await ossService.PresignedGetObjectAsync(bucketName, uploadPath, 86400);
  324 +
  325 + // 获取带签名的URL字符串
  326 + // PresignedGetObjectAsync 返回的对象有ToString()方法,会返回完整的URL(包括查询参数和签名)
  327 + // 尝试多种方式获取URL字符串
  328 + string urlString = string.Empty;
  329 + if (presignedUrl != null)
  330 + {
  331 + // 尝试使用反射获取AbsoluteUri属性(如果是Uri类型)
  332 + var urlType = presignedUrl.GetType();
  333 + var absoluteUriProp = urlType.GetProperty("AbsoluteUri");
  334 + if (absoluteUriProp != null)
  335 + {
  336 + urlString = absoluteUriProp.GetValue(presignedUrl)?.ToString() ?? string.Empty;
  337 + }
  338 + else
  339 + {
  340 + // 如果没有AbsoluteUri属性,使用ToString()
  341 + urlString = presignedUrl.ToString() ?? string.Empty;
  342 + }
  343 + }
  344 +
  345 + // 如果URL为空,说明生成失败
  346 + if (string.IsNullOrEmpty(urlString))
  347 + {
  348 + return $"/api/File/Image/annexpic/{fileName}";
  349 + }
  350 +
  351 + // 如果配置了自定义域名,需要将URL中的域名替换为自定义域名
  352 + var customDomain = _configuration["NCC_App:AliyunOSS:CustomDomain"]
  353 + ?? _configuration["NCC_APP:AliyunOSS:CustomDomain"];
  354 +
  355 + if (!string.IsNullOrEmpty(customDomain))
  356 + {
  357 + // 确保自定义域名格式正确
  358 + var domain = customDomain.TrimEnd('/');
  359 + if (!domain.StartsWith("http://") && !domain.StartsWith("https://"))
  360 + {
  361 + // 根据原始URL的协议来决定使用http还是https
  362 + var useHttps = urlString.StartsWith("https://");
  363 + domain = (useHttps ? "https://" : "http://") + domain;
  364 + }
  365 +
  366 + // 使用Uri对象来解析和替换域名,保留查询参数(签名信息)
  367 + var originalUri = new Uri(urlString);
  368 + var customUri = new UriBuilder(originalUri)
  369 + {
  370 + Scheme = domain.StartsWith("https://") ? "https" : "http",
  371 + Host = domain.Replace("https://", "").Replace("http://", "").TrimEnd('/'),
  372 + // 确保保留查询参数(签名信息)
  373 + Query = originalUri.Query
  374 + };
  375 +
  376 + // 返回完整的URL(包括查询参数)
  377 + return customUri.Uri.AbsoluteUri;
  378 + }
  379 +
  380 + // 如果没有配置自定义域名,直接返回带签名的URL
  381 + return urlString;
  382 + }
  383 + catch (Exception)
  384 + {
  385 + // 如果获取OSS URL失败,返回相对路径
  386 + return $"/api/File/Image/annexpic/{fileName}";
  387 + }
  388 + }
294 389 #endregion
295 390  
296 391 /// <summary>
... ...
sql/修复业绩类型字段-根据品相表fl3更新.sql 0 → 100644
  1 +-- ============================================
  2 +-- 修复业绩类型字段:根据品相表fl3字段更新所有相关表的F_PerformanceType
  3 +-- ============================================
  4 +-- 说明:
  5 +-- 1. 此SQL用于修复因品相表(lq_xmzl)fl3字段设置错误导致的业绩类型问题
  6 +-- 2. 会更新所有开单、耗卡、退卡相关表的F_PerformanceType字段
  7 +-- 3. 更新逻辑:根据品相表的fl3字段,重新设置所有相关表的业绩类型
  8 +-- 4. 注意:此SQL会覆盖原有的F_PerformanceType值,请确保品相表的fl3字段已正确修复
  9 +--
  10 +-- 执行前请确认:
  11 +-- 1. 品相表(lq_xmzl)的fl3字段已经修复正确
  12 +-- 2. 已备份相关数据表
  13 +-- 3. 建议在测试环境先执行验证
  14 +-- ============================================
  15 +
  16 +-- ============================================
  17 +-- 1. 更新开单品项表(lq_kd_pxmx)
  18 +-- ============================================
  19 +-- 通过品项ID(px字段)关联品相表的fl3字段
  20 +UPDATE lq_kd_pxmx pxmx
  21 +INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  22 +SET pxmx.F_PerformanceType = COALESCE(xmzl.fl3, '')
  23 +WHERE pxmx.F_IsEffective = 1;
  24 +
  25 +-- ============================================
  26 +-- 2. 更新开单健康师业绩表(lq_kd_jksyj)
  27 +-- ============================================
  28 +-- 优先使用F_ItemId,如果没有则通过F_kdpxid关联开单品项表获取品项ID
  29 +UPDATE lq_kd_jksyj jksyj
  30 +LEFT JOIN lq_kd_pxmx pxmx ON jksyj.F_kdpxid = pxmx.F_Id
  31 +LEFT JOIN lq_xmzl xmzl ON COALESCE(jksyj.F_ItemId, pxmx.px) = xmzl.F_Id
  32 +SET jksyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  33 +WHERE jksyj.F_IsEffective = 1
  34 + AND xmzl.F_Id IS NOT NULL;
  35 +
  36 +-- ============================================
  37 +-- 3. 更新开单科技部老师业绩表(lq_kd_kjbsyj)
  38 +-- ============================================
  39 +-- 优先使用F_ItemId,如果没有则通过F_kdpxid关联开单品项表获取品项ID
  40 +UPDATE lq_kd_kjbsyj kjbsyj
  41 +LEFT JOIN lq_kd_pxmx pxmx ON kjbsyj.F_kdpxid = pxmx.F_Id
  42 +LEFT JOIN lq_xmzl xmzl ON COALESCE(kjbsyj.F_ItemId, pxmx.px) = xmzl.F_Id
  43 +SET kjbsyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  44 +WHERE kjbsyj.F_IsEffective = 1
  45 + AND xmzl.F_Id IS NOT NULL;
  46 +
  47 +-- ============================================
  48 +-- 4. 更新消耗品项表(lq_xh_pxmx)
  49 +-- ============================================
  50 +-- 通过品项ID(px字段)关联品相表的fl3字段
  51 +UPDATE lq_xh_pxmx pxmx
  52 +INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  53 +SET pxmx.F_PerformanceType = COALESCE(xmzl.fl3, '')
  54 +WHERE (pxmx.F_IsEffective = 1 OR pxmx.F_IsEffective IS NULL);
  55 +
  56 +-- ============================================
  57 +-- 5. 更新消耗健康师业绩表(lq_xh_jksyj)
  58 +-- ============================================
  59 +-- 优先使用F_ItemId,如果没有则通过F_kdpxid关联消耗品项表获取品项ID
  60 +UPDATE lq_xh_jksyj jksyj
  61 +LEFT JOIN lq_xh_pxmx pxmx ON jksyj.F_kdpxid = pxmx.F_Id
  62 +LEFT JOIN lq_xmzl xmzl ON COALESCE(jksyj.F_ItemId, pxmx.px) = xmzl.F_Id
  63 +SET jksyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  64 +WHERE (jksyj.F_IsEffective = 1 OR jksyj.F_IsEffective IS NULL)
  65 + AND xmzl.F_Id IS NOT NULL;
  66 +
  67 +-- ============================================
  68 +-- 6. 更新消耗科技部老师业绩表(lq_xh_kjbsyj)
  69 +-- ============================================
  70 +-- 优先使用F_ItemId,如果没有则通过F_hkpxid关联消耗品项表获取品项ID
  71 +UPDATE lq_xh_kjbsyj kjbsyj
  72 +LEFT JOIN lq_xh_pxmx pxmx ON kjbsyj.F_hkpxid = pxmx.F_Id
  73 +LEFT JOIN lq_xmzl xmzl ON COALESCE(kjbsyj.F_ItemId, pxmx.px) = xmzl.F_Id
  74 +SET kjbsyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  75 +WHERE (kjbsyj.F_IsEffective = 1 OR kjbsyj.F_IsEffective IS NULL)
  76 + AND xmzl.F_Id IS NOT NULL;
  77 +
  78 +-- ============================================
  79 +-- 7. 更新退卡明细表(lq_hytk_mx)
  80 +-- ============================================
  81 +-- 通过品项ID(px字段)关联品相表的fl3字段
  82 +UPDATE lq_hytk_mx mx
  83 +INNER JOIN lq_xmzl xmzl ON mx.px = xmzl.F_Id
  84 +SET mx.F_PerformanceType = COALESCE(xmzl.fl3, '')
  85 +WHERE mx.F_IsEffective = 1;
  86 +
  87 +-- ============================================
  88 +-- 8. 更新退卡健康师业绩表(lq_hytk_jksyj)
  89 +-- ============================================
  90 +-- 优先使用F_ItemId,如果没有则通过F_CardReturn关联退卡明细表获取品项ID
  91 +UPDATE lq_hytk_jksyj jksyj
  92 +LEFT JOIN lq_hytk_mx mx ON jksyj.F_CardReturn = mx.F_Id
  93 +LEFT JOIN lq_xmzl xmzl ON COALESCE(jksyj.F_ItemId, mx.px) = xmzl.F_Id
  94 +SET jksyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  95 +WHERE jksyj.F_IsEffective = 1
  96 + AND xmzl.F_Id IS NOT NULL;
  97 +
  98 +-- ============================================
  99 +-- 9. 更新退卡科技部老师业绩表(lq_hytk_kjbsyj)
  100 +-- ============================================
  101 +-- 优先使用F_ItemId,如果没有则通过F_CardReturn关联退卡明细表获取品项ID
  102 +UPDATE lq_hytk_kjbsyj kjbsyj
  103 +LEFT JOIN lq_hytk_mx mx ON kjbsyj.F_CardReturn = mx.F_Id
  104 +LEFT JOIN lq_xmzl xmzl ON COALESCE(kjbsyj.F_ItemId, mx.px) = xmzl.F_Id
  105 +SET kjbsyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  106 +WHERE kjbsyj.F_IsEffective = 1
  107 + AND xmzl.F_Id IS NOT NULL;
  108 +
  109 +-- ============================================
  110 +-- 10. 验证更新结果(统计各表的业绩类型分布)
  111 +-- ============================================
  112 +SELECT
  113 + 'lq_kd_pxmx' as table_name,
  114 + COUNT(*) as total_count,
  115 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  116 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  117 +FROM lq_kd_pxmx
  118 +WHERE F_IsEffective = 1
  119 +UNION ALL
  120 +SELECT
  121 + 'lq_kd_jksyj' as table_name,
  122 + COUNT(*) as total_count,
  123 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  124 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  125 +FROM lq_kd_jksyj
  126 +WHERE F_IsEffective = 1
  127 +UNION ALL
  128 +SELECT
  129 + 'lq_kd_kjbsyj' as table_name,
  130 + COUNT(*) as total_count,
  131 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  132 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  133 +FROM lq_kd_kjbsyj
  134 +WHERE F_IsEffective = 1
  135 +UNION ALL
  136 +SELECT
  137 + 'lq_xh_pxmx' as table_name,
  138 + COUNT(*) as total_count,
  139 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  140 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  141 +FROM lq_xh_pxmx
  142 +WHERE F_IsEffective = 1 OR F_IsEffective IS NULL
  143 +UNION ALL
  144 +SELECT
  145 + 'lq_xh_jksyj' as table_name,
  146 + COUNT(*) as total_count,
  147 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  148 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  149 +FROM lq_xh_jksyj
  150 +WHERE F_IsEffective = 1 OR F_IsEffective IS NULL
  151 +UNION ALL
  152 +SELECT
  153 + 'lq_xh_kjbsyj' as table_name,
  154 + COUNT(*) as total_count,
  155 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  156 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  157 +FROM lq_xh_kjbsyj
  158 +WHERE F_IsEffective = 1 OR F_IsEffective IS NULL
  159 +UNION ALL
  160 +SELECT
  161 + 'lq_hytk_mx' as table_name,
  162 + COUNT(*) as total_count,
  163 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  164 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  165 +FROM lq_hytk_mx
  166 +WHERE F_IsEffective = 1
  167 +UNION ALL
  168 +SELECT
  169 + 'lq_hytk_jksyj' as table_name,
  170 + COUNT(*) as total_count,
  171 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  172 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  173 +FROM lq_hytk_jksyj
  174 +WHERE F_IsEffective = 1
  175 +UNION ALL
  176 +SELECT
  177 + 'lq_hytk_kjbsyj' as table_name,
  178 + COUNT(*) as total_count,
  179 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  180 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  181 +FROM lq_hytk_kjbsyj
  182 +WHERE F_IsEffective = 1;
  183 +
  184 +-- ============================================
  185 +-- 11. 查看品相表fl3字段的值分布
  186 +-- ============================================
  187 +SELECT
  188 + 'lq_xmzl.fl3 值分布' as description,
  189 + fl3 as performance_type,
  190 + COUNT(*) as count
  191 +FROM lq_xmzl
  192 +WHERE fl3 IS NOT NULL AND fl3 != ''
  193 +GROUP BY fl3
  194 +ORDER BY count DESC;
  195 +
  196 +-- ============================================
  197 +-- 12. 查看各表业绩类型的值分布(用于对比验证)
  198 +-- ============================================
  199 +SELECT
  200 + 'lq_kd_pxmx' as table_name,
  201 + F_PerformanceType as performance_type,
  202 + COUNT(*) as count
  203 +FROM lq_kd_pxmx
  204 +WHERE F_IsEffective = 1
  205 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  206 +GROUP BY F_PerformanceType
  207 +UNION ALL
  208 +SELECT
  209 + 'lq_kd_jksyj' as table_name,
  210 + F_PerformanceType as performance_type,
  211 + COUNT(*) as count
  212 +FROM lq_kd_jksyj
  213 +WHERE F_IsEffective = 1
  214 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  215 +GROUP BY F_PerformanceType
  216 +UNION ALL
  217 +SELECT
  218 + 'lq_kd_kjbsyj' as table_name,
  219 + F_PerformanceType as performance_type,
  220 + COUNT(*) as count
  221 +FROM lq_kd_kjbsyj
  222 +WHERE F_IsEffective = 1
  223 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  224 +GROUP BY F_PerformanceType
  225 +UNION ALL
  226 +SELECT
  227 + 'lq_xh_pxmx' as table_name,
  228 + F_PerformanceType as performance_type,
  229 + COUNT(*) as count
  230 +FROM lq_xh_pxmx
  231 +WHERE (F_IsEffective = 1 OR F_IsEffective IS NULL)
  232 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  233 +GROUP BY F_PerformanceType
  234 +UNION ALL
  235 +SELECT
  236 + 'lq_xh_jksyj' as table_name,
  237 + F_PerformanceType as performance_type,
  238 + COUNT(*) as count
  239 +FROM lq_xh_jksyj
  240 +WHERE (F_IsEffective = 1 OR F_IsEffective IS NULL)
  241 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  242 +GROUP BY F_PerformanceType
  243 +UNION ALL
  244 +SELECT
  245 + 'lq_xh_kjbsyj' as table_name,
  246 + F_PerformanceType as performance_type,
  247 + COUNT(*) as count
  248 +FROM lq_xh_kjbsyj
  249 +WHERE (F_IsEffective = 1 OR F_IsEffective IS NULL)
  250 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  251 +GROUP BY F_PerformanceType
  252 +UNION ALL
  253 +SELECT
  254 + 'lq_hytk_mx' as table_name,
  255 + F_PerformanceType as performance_type,
  256 + COUNT(*) as count
  257 +FROM lq_hytk_mx
  258 +WHERE F_IsEffective = 1
  259 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  260 +GROUP BY F_PerformanceType
  261 +UNION ALL
  262 +SELECT
  263 + 'lq_hytk_jksyj' as table_name,
  264 + F_PerformanceType as performance_type,
  265 + COUNT(*) as count
  266 +FROM lq_hytk_jksyj
  267 +WHERE F_IsEffective = 1
  268 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  269 +GROUP BY F_PerformanceType
  270 +UNION ALL
  271 +SELECT
  272 + 'lq_hytk_kjbsyj' as table_name,
  273 + F_PerformanceType as performance_type,
  274 + COUNT(*) as count
  275 +FROM lq_hytk_kjbsyj
  276 +WHERE F_IsEffective = 1
  277 + AND F_PerformanceType IS NOT NULL AND F_PerformanceType != ''
  278 +GROUP BY F_PerformanceType
  279 +ORDER BY table_name, count DESC;
  280 +
  281 +-- ============================================
  282 +-- SQL脚本执行完成
  283 +-- ============================================
  284 +-- 说明:
  285 +-- 1. 此SQL会根据品相表(lq_xmzl)的fl3字段,更新所有相关表的F_PerformanceType字段
  286 +-- 2. 更新覆盖所有有效记录,不管原来的值是什么
  287 +-- 3. 执行后请查看验证结果,确认更新是否正确
  288 +-- 4. 如果发现数据异常,请及时回滚或联系开发人员
  289 +
  290 +
... ...
sql/创建库存使用申请审批流程表.sql
... ... @@ -102,3 +102,5 @@ WHERE u.`F_UnitPrice` = 0 OR u.`F_UnitPrice` IS NULL;
102 102  
103 103  
104 104  
  105 +
  106 +
... ...
sql/创建库存使用申请表-完整版.sql 0 → 100644
  1 +-- ============================================
  2 +-- 创建库存使用申请表(完整版,包含总价字段)
  3 +-- ============================================
  4 +-- 说明:用于库存使用申请的审批流程
  5 +-- 执行时间:2025年
  6 +-- ============================================
  7 +
  8 +-- 1. 创建库存使用申请表
  9 +CREATE TABLE IF NOT EXISTS `lq_inventory_usage_application` (
  10 + `F_Id` varchar(50) NOT NULL COMMENT '申请编号',
  11 + `F_UsageBatchId` varchar(50) NOT NULL COMMENT '使用批次ID(关联lq_inventory_usage.F_UsageBatchId)',
  12 + `F_ApplicationUserId` varchar(50) NOT NULL COMMENT '申请人ID',
  13 + `F_ApplicationUserName` varchar(100) DEFAULT NULL COMMENT '申请人姓名',
  14 + `F_ApplicationStoreId` varchar(50) DEFAULT NULL COMMENT '申请门店ID',
  15 + `F_ApplicationTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '申请时间',
  16 + `F_NodeCount` int DEFAULT 1 COMMENT '节点数量(固定为1)',
  17 + `F_CurrentNodeOrder` int DEFAULT 0 COMMENT '当前审批节点(0-待审批,1-审批中,2-已完成)',
  18 + `F_CurrentNodeId` varchar(50) DEFAULT NULL COMMENT '当前节点ID',
  19 + `F_ApprovalStatus` varchar(20) DEFAULT '待审批' COMMENT '审批状态(待审批/审批中/已通过/未通过/已退回)',
  20 + `F_IsReceived` int DEFAULT 0 COMMENT '是否已领取(1-已领取,0-未领取)',
  21 + `F_ReceiveTime` datetime DEFAULT NULL COMMENT '领取时间',
  22 + `F_ReceiveUser` varchar(50) DEFAULT NULL COMMENT '领取人ID',
  23 + `F_Remarks` varchar(500) DEFAULT NULL COMMENT '备注',
  24 + `F_TotalAmount` decimal(18,2) DEFAULT 0.00 COMMENT '申请总金额(该批次所有商品的总价)',
  25 + `F_CreateTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  26 + `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建人ID',
  27 + `F_UpdateTime` datetime DEFAULT NULL COMMENT '更新时间',
  28 + `F_UpdateUser` varchar(50) DEFAULT NULL COMMENT '更新人ID',
  29 + `F_IsEffective` int DEFAULT 1 COMMENT '是否有效(1-有效,0-无效)',
  30 + PRIMARY KEY (`F_Id`),
  31 + UNIQUE KEY `uk_usage_batch_id` (`F_UsageBatchId`),
  32 + KEY `idx_application_user_id` (`F_ApplicationUserId`),
  33 + KEY `idx_application_store_id` (`F_ApplicationStoreId`),
  34 + KEY `idx_current_node` (`F_CurrentNodeId`),
  35 + KEY `idx_approval_status` (`F_ApprovalStatus`),
  36 + KEY `idx_is_received` (`F_IsReceived`),
  37 + KEY `idx_total_amount` (`F_TotalAmount`),
  38 + KEY `idx_create_time` (`F_CreateTime`)
  39 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存使用申请表';
  40 +
  41 +-- 2. 创建库存使用申请审批记录表
  42 +CREATE TABLE IF NOT EXISTS `lq_inventory_usage_approval_record` (
  43 + `F_Id` varchar(50) NOT NULL COMMENT '审批记录编号',
  44 + `F_ApplicationId` varchar(50) NOT NULL COMMENT '申请ID(关联lq_inventory_usage_application.F_Id)',
  45 + `F_NodeId` varchar(50) DEFAULT NULL COMMENT '节点ID',
  46 + `F_NodeOrder` int DEFAULT 1 COMMENT '节点顺序(固定为1)',
  47 + `F_ApproverId` varchar(50) NOT NULL COMMENT '审批人ID',
  48 + `F_ApproverName` varchar(100) DEFAULT NULL COMMENT '审批人姓名',
  49 + `F_ApprovalResult` varchar(20) DEFAULT '待审批' COMMENT '审批结果(待审批/已通过/未通过/已退回)',
  50 + `F_ApprovalOpinion` varchar(500) DEFAULT NULL COMMENT '审批意见',
  51 + `F_ApprovalTime` datetime DEFAULT NULL COMMENT '审批时间',
  52 + `F_IsCurrentNode` int DEFAULT 1 COMMENT '是否当前节点(1-是,0-否)',
  53 + `F_CreateTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  54 + `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建人ID',
  55 + PRIMARY KEY (`F_Id`),
  56 + KEY `idx_application_id` (`F_ApplicationId`),
  57 + KEY `idx_approver_id` (`F_ApproverId`),
  58 + KEY `idx_approval_result` (`F_ApprovalResult`),
  59 + KEY `idx_is_current_node` (`F_IsCurrentNode`),
  60 + KEY `idx_node_order` (`F_ApplicationId`, `F_NodeOrder`)
  61 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存使用申请审批记录表';
  62 +
... ...
sql/添加库存使用申请总价字段.sql 0 → 100644
  1 +-- ============================================
  2 +-- 在库存使用申请表中添加总价字段
  3 +-- ============================================
  4 +-- 说明:用于保存该批次所有商品的总价
  5 +-- ============================================
  6 +
  7 +-- 检查并添加总价字段
  8 +SET @dbname = DATABASE();
  9 +SET @tablename = 'lq_inventory_usage_application';
  10 +SET @columnname = 'F_TotalAmount';
  11 +SET @preparedStatement = (SELECT IF(
  12 + (
  13 + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
  14 + WHERE
  15 + (TABLE_SCHEMA = @dbname)
  16 + AND (TABLE_NAME = @tablename)
  17 + AND (COLUMN_NAME = @columnname)
  18 + ) > 0,
  19 + 'SELECT 1',
  20 + CONCAT('ALTER TABLE ', @tablename, ' ADD COLUMN `', @columnname, '` decimal(18,2) DEFAULT 0.00 COMMENT ''申请总金额(该批次所有商品的总价)'' AFTER `F_Remarks`')
  21 +));
  22 +PREPARE alterIfNotExists FROM @preparedStatement;
  23 +EXECUTE alterIfNotExists;
  24 +DEALLOCATE PREPARE alterIfNotExists;
  25 +
  26 +-- 添加索引(如果不存在)
  27 +ALTER TABLE `lq_inventory_usage_application`
  28 + ADD INDEX `idx_total_amount` (`F_TotalAmount`) COMMENT '总价索引';
  29 +
... ...