diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListOutput.cs new file mode 100644 index 0000000..132e466 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListOutput.cs @@ -0,0 +1,71 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqKdKdjlb +{ + /// + /// 开单品项明细记录输出 + /// + public class BillingItemDetailListOutput + { + /// + /// 品项明细ID + /// + public string id { get; set; } + + /// + /// 开单ID + /// + public string billingId { get; set; } + + /// + /// 开单时间 + /// + public DateTime? billingTime { get; set; } + + /// + /// 营销活动名称 + /// + public string activityName { get; set; } + + /// + /// 会员名称 + /// + public string memberName { get; set; } + + /// + /// 会员手机 + /// + public string memberPhone { get; set; } + + /// + /// 品项名称 + /// + public string itemName { get; set; } + + /// + /// 品项类型(分类④-统计品项用) + /// + public string itemType { get; set; } + + /// + /// 实付金额 + /// + public decimal actualPrice { get; set; } + + /// + /// 项目次数 + /// + public decimal projectNumber { get; set; } + + /// + /// 来源类型(购买/赠送/体验) + /// + public string sourceType { get; set; } + + /// + /// 备注 + /// + public string remark { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs new file mode 100644 index 0000000..1d5f3a4 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListQueryInput.cs @@ -0,0 +1,71 @@ +using NCC.Common.Filter; + +namespace NCC.Extend.Entitys.Dto.LqKdKdjlb +{ + /// + /// 开单品项明细记录查询输入参数 + /// + public class BillingItemDetailListQueryInput : PageInputBase + { + /// + /// 品项明细ID + /// + public string Id { get; set; } + + /// + /// 开单ID + /// + public string BillingId { get; set; } + + /// + /// 开单时间(开始) + /// + public string StartBillingTime { get; set; } + + /// + /// 开单时间(结束) + /// + public string EndBillingTime { get; set; } + + /// + /// 营销活动ID + /// + public string ActivityId { get; set; } + + /// + /// 会员ID + /// + public string MemberId { get; set; } + + /// + /// 会员名称(模糊查询) + /// + public string MemberName { get; set; } + + /// + /// 会员手机号 + /// + public string MemberPhone { get; set; } + + /// + /// 品项ID + /// + public string ItemId { get; set; } + + /// + /// 品项名称(模糊查询) + /// + public string ItemName { get; set; } + + /// + /// 品项类型(分类④-统计品项用) + /// + public string ItemType { get; set; } + + /// + /// 来源类型(购买/赠送/体验) + /// + public string SourceType { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoOutput.cs new file mode 100644 index 0000000..645b424 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoOutput.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; + +namespace NCC.Extend.Entitys.Dto.LqKhxx +{ + /// + /// 会员品项信息输出 + /// + public class MemberItemInfoOutput + { + /// + /// 会员ID + /// + public string MemberId { get; set; } + + /// + /// 会员编号(档案号) + /// + public string MemberCode { get; set; } + + /// + /// 会员名称 + /// + public string MemberName { get; set; } + + /// + /// 会员电话 + /// + public string MemberPhone { get; set; } + + /// + /// 所属门店ID + /// + public string BelongStoreId { get; set; } + + /// + /// 所属门店名称 + /// + public string BelongStoreName { get; set; } + + /// + /// 开单信息列表 + /// + public List BillingItems { get; set; } = new List(); + } + + /// + /// 开单信息 + /// + public class BillingItemInfo + { + /// + /// 开单ID + /// + public string BillingId { get; set; } + + /// + /// 开单时间 + /// + public DateTime? BillingTime { get; set; } + + /// + /// 开单门店ID + /// + public string BillingStoreId { get; set; } + + /// + /// 开单门店名称 + /// + public string BillingStoreName { get; set; } + + /// + /// 开单人ID + /// + public string BillingUserId { get; set; } + + /// + /// 开单人名称 + /// + public string BillingUserName { get; set; } + + /// + /// 购买品项列表 + /// + public List PurchaseItems { get; set; } = new List(); + + /// + /// 购买品项总金额 + /// + public decimal PurchaseItemsTotalAmount { get; set; } + + /// + /// 赠送品项列表 + /// + public List GiftItems { get; set; } = new List(); + + /// + /// 体验品项列表 + /// + public List ExperienceItems { get; set; } = new List(); + + /// + /// 已消耗品项列表 + /// + public List ConsumedItems { get; set; } = new List(); + + /// + /// 已消耗品项总金额 + /// + public decimal ConsumedItemsTotalAmount { get; set; } + + /// + /// 已退卡品项列表 + /// + public List RefundedItems { get; set; } = new List(); + + /// + /// 已退卡品项总金额 + /// + public decimal RefundedItemsTotalAmount { get; set; } + + /// + /// 已储扣品项列表 + /// + public List DeductedItems { get; set; } = new List(); + + /// + /// 已储扣品项总金额 + /// + public decimal DeductedItemsTotalAmount { get; set; } + } + + /// + /// 品项明细 + /// + public class ItemDetail + { + /// + /// 品项ID + /// + public string ItemId { get; set; } + + /// + /// 品项名称 + /// + public string ItemName { get; set; } + + /// + /// 品项价格 + /// + public decimal ItemPrice { get; set; } + + /// + /// 项目次数 + /// + public decimal ProjectNumber { get; set; } + + /// + /// 总金额 + /// + public decimal TotalAmount { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoQueryInput.cs new file mode 100644 index 0000000..6f3b19d --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/MemberItemInfoQueryInput.cs @@ -0,0 +1,47 @@ +using System; +using NCC.Common.Filter; + +namespace NCC.Extend.Entitys.Dto.LqKhxx +{ + /// + /// 会员品项信息查询输入参数 + /// + public class MemberItemInfoQueryInput : PageInputBase + { + /// + /// 会员ID + /// + public string MemberId { get; set; } + + /// + /// 所属门店ID + /// + public string BelongStoreId { get; set; } + + /// + /// 开单门店ID + /// + public string BillingStoreId { get; set; } + + /// + /// 开单人ID + /// + public string BillingUserId { get; set; } + + /// + /// 购买品项ID + /// + public string PurchaseItemId { get; set; } + + /// + /// 赠送品项ID + /// + public string GiftItemId { get; set; } + + /// + /// 体验品项ID + /// + public string ExperienceItemId { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs index 9f0cdb0..19701a8 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs @@ -1745,6 +1745,8 @@ namespace NCC.Extend } #endregion + + #region 获取储值扣减金额统计 /// /// 获取储值扣减金额统计 diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs index 2006ffc..27806f2 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs @@ -3211,5 +3211,218 @@ namespace NCC.Extend.LqKdKdjlb } } #endregion + + #region 获取开单品项明细记录列表 + /// + /// 获取开单品项明细记录列表(分页) + /// + /// + /// 查询开单品项明细记录,支持多条件筛选和分页查询。采用先分页后关联的查询方式,确保分页准确性和查询性能。 + /// + /// 示例请求: + /// ```json + /// { + /// "currentPage": 1, + /// "pageSize": 10, + /// "sidx": "yjsj", + /// "sort": "DESC", + /// "Id": "品项明细ID", + /// "BillingId": "开单ID", + /// "StartBillingTime": "2025-01-01", + /// "EndBillingTime": "2025-12-31", + /// "ActivityId": "营销活动ID", + /// "MemberId": "会员ID", + /// "MemberName": "会员名称", + /// "MemberPhone": "会员手机号", + /// "ItemId": "品项ID", + /// "ItemName": "品项名称", + /// "ItemType": "品项类型", + /// "SourceType": "来源类型" + /// } + /// ``` + /// + /// 查询参数说明: + /// - currentPage: 当前页码(必填,从1开始) + /// - pageSize: 每页数量(必填) + /// - sidx: 排序字段(可选,默认:yjsj,支持:id、billingId、billingTime、itemName、actualPrice、projectNumber等) + /// - sort: 排序方式(可选,默认:DESC,支持:ASC、DESC) + /// - Id: 品项明细ID(可选,精确匹配) + /// - BillingId: 开单ID(可选,精确匹配) + /// - StartBillingTime: 开单时间开始(可选,格式:yyyy-MM-dd,与EndBillingTime同时使用) + /// - EndBillingTime: 开单时间结束(可选,格式:yyyy-MM-dd,与StartBillingTime同时使用) + /// - ActivityId: 营销活动ID(可选,精确匹配) + /// - MemberId: 会员ID(可选,精确匹配) + /// - MemberName: 会员名称(可选,模糊查询) + /// - MemberPhone: 会员手机号(可选,精确匹配) + /// - ItemId: 品项ID(可选,精确匹配) + /// - ItemName: 品项名称(可选,模糊查询) + /// - ItemType: 品项类型(可选,精确匹配,对应项目资料表的qt2字段) + /// - SourceType: 来源类型(可选,精确匹配,如:购买、赠送、体验) + /// + /// 返回数据结构: + /// ```json + /// { + /// "pagination": { + /// "pageIndex": 1, + /// "pageSize": 10, + /// "total": 100 + /// }, + /// "list": [ + /// { + /// "id": "品项明细ID", + /// "billingId": "开单ID", + /// "billingTime": "2025-11-15T10:00:00", + /// "activityName": "营销活动名称", + /// "memberName": "会员名称", + /// "memberPhone": "会员手机号", + /// "itemName": "品项名称", + /// "itemType": "品项类型", + /// "actualPrice": 500.00, + /// "projectNumber": 5.0, + /// "sourceType": "购买", + /// "remark": "备注" + /// } + /// ] + /// } + /// ``` + /// + /// 返回字段说明: + /// - id: 品项明细ID(主键) + /// - billingId: 开单ID(关联开单记录表) + /// - billingTime: 开单时间(业绩时间yjsj,DateTime格式) + /// - activityName: 营销活动名称(关联营销活动表) + /// - memberName: 会员名称(关联会员表) + /// - memberPhone: 会员手机号(关联会员表) + /// - itemName: 品项名称(品项明细表字段) + /// - itemType: 品项类型(关联项目资料表的qt2字段) + /// - actualPrice: 实付金额(decimal类型) + /// - projectNumber: 项目次数(decimal类型,支持小数) + /// - sourceType: 来源类型(字符串,如:购买、赠送、体验) + /// - remark: 备注(字符串) + /// + /// 查询参数 + /// 开单品项明细记录列表(分页) + /// 查询成功,返回开单品项明细记录列表 + /// 参数错误,如页码或页大小无效 + /// 服务器错误,查询过程中发生异常 + [HttpGet("billing-item-detail-list")] + public async Task GetBillingItemDetailList([FromQuery] BillingItemDetailListQueryInput input) + { + try + { + var sidx = input.sidx == null ? "yjsj" : input.sidx; + var sort = string.IsNullOrEmpty(input.sort) ? "DESC" : input.sort; + + // 处理开单时间范围 + List queryBillingTime = null; + DateTime? startBillingTime = null; + DateTime? endBillingTime = null; + if (!string.IsNullOrEmpty(input.StartBillingTime) && !string.IsNullOrEmpty(input.EndBillingTime)) + { + queryBillingTime = new List { input.StartBillingTime, input.EndBillingTime }; + startBillingTime = Ext.GetDateTime(queryBillingTime.First()); + endBillingTime = Ext.GetDateTime(queryBillingTime.Last()); + } + + // 优化查询:先分页查询主表,再批量查询关联数据,避免子查询性能问题 + // 1. 先构建基础查询条件 + var baseQuery = _db.Queryable() + .Where(pxmx => pxmx.IsEffective == StatusEnum.有效.GetHashCode()) + .WhereIF(!string.IsNullOrEmpty(input.Id), pxmx => pxmx.Id == input.Id) + .WhereIF(!string.IsNullOrEmpty(input.BillingId), pxmx => pxmx.Glkdbh == input.BillingId) + .WhereIF(queryBillingTime != null && startBillingTime.HasValue, pxmx => pxmx.Yjsj >= new DateTime(startBillingTime.Value.Year, startBillingTime.Value.Month, startBillingTime.Value.Day, 0, 0, 0)) + .WhereIF(queryBillingTime != null && endBillingTime.HasValue, pxmx => pxmx.Yjsj <= new DateTime(endBillingTime.Value.Year, endBillingTime.Value.Month, endBillingTime.Value.Day, 23, 59, 59)) + .WhereIF(!string.IsNullOrEmpty(input.ActivityId), pxmx => pxmx.ActivityId == input.ActivityId) + .WhereIF(!string.IsNullOrEmpty(input.MemberId), pxmx => pxmx.MemberId == input.MemberId) + .WhereIF(!string.IsNullOrEmpty(input.ItemId), pxmx => pxmx.Px == input.ItemId) + .WhereIF(!string.IsNullOrEmpty(input.ItemName), pxmx => pxmx.Pxmc != null && pxmx.Pxmc.Contains(input.ItemName)) + .WhereIF(!string.IsNullOrEmpty(input.SourceType), pxmx => pxmx.SourceType == input.SourceType); + + // 2. 通过 EXISTS 子查询筛选关联字段(在分页前筛选,确保分页准确) + baseQuery = baseQuery + .WhereIF(!string.IsNullOrEmpty(input.MemberName), pxmx => + SqlFunc.Subqueryable().Where(x => x.Id == pxmx.MemberId && x.Khmc != null && x.Khmc.Contains(input.MemberName)).Any()) + .WhereIF(!string.IsNullOrEmpty(input.MemberPhone), pxmx => + SqlFunc.Subqueryable().Where(x => x.Id == pxmx.MemberId && x.Sjh == input.MemberPhone).Any()) + .WhereIF(!string.IsNullOrEmpty(input.ItemType), pxmx => + SqlFunc.Subqueryable().Where(x => x.Id == pxmx.Px && x.Fl4 == input.ItemType).Any()); + + // 3. 先分页查询主表数据(查询实体类,提高性能) + var pagedData = await baseQuery + .OrderBy(sidx + " " + sort) + .ToPagedListAsync(input.currentPage, input.pageSize); + + // 4. 批量查询关联数据 + var itemIds = pagedData.list.Select(x => x.Id).ToList(); + var memberIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.MemberId)).Select(x => x.MemberId).Distinct().ToList(); + var activityIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.ActivityId)).Select(x => x.ActivityId).Distinct().ToList(); + var projectIds = pagedData.list.Where(x => !string.IsNullOrEmpty(x.Px)).Select(x => x.Px).Distinct().ToList(); + + // 批量查询会员信息 + var memberDict = new Dictionary(); + if (memberIds.Any()) + { + var members = await _db.Queryable() + .Where(x => memberIds.Contains(x.Id)) + .Select(x => new { x.Id, x.Khmc, x.Sjh }) + .ToListAsync(); + memberDict = members.ToDictionary(x => x.Id, x => (x.Khmc ?? "", x.Sjh ?? "")); + } + + // 批量查询活动信息 + var activityDict = new Dictionary(); + if (activityIds.Any()) + { + var activities = await _db.Queryable() + .Where(x => activityIds.Contains(x.Id)) + .Select(x => new { x.Id, x.ActivityName }) + .ToListAsync(); + activityDict = activities.ToDictionary(x => x.Id, x => x.ActivityName ?? ""); + } + + // 批量查询项目资料 + var projectDict = new Dictionary(); + if (projectIds.Any()) + { + var projects = await _db.Queryable() + .Where(x => projectIds.Contains(x.Id)) + .Select(x => new { x.Id, x.Qt2 }) + .ToListAsync(); + projectDict = projects.ToDictionary(x => x.Id, x => x.Qt2 ?? ""); + } + + // 5. 组装返回数据 + var resultList = pagedData.list.Select(pxmx => new BillingItemDetailListOutput + { + id = pxmx.Id, + billingId = pxmx.Glkdbh, + billingTime = pxmx.Yjsj, + activityName = pxmx.ActivityId != null && activityDict.ContainsKey(pxmx.ActivityId) ? activityDict[pxmx.ActivityId] : "", + memberName = pxmx.MemberId != null && memberDict.ContainsKey(pxmx.MemberId) ? memberDict[pxmx.MemberId].Name : "", + memberPhone = pxmx.MemberId != null && memberDict.ContainsKey(pxmx.MemberId) ? memberDict[pxmx.MemberId].Phone : "", + itemName = pxmx.Pxmc, + itemType = pxmx.Px != null && projectDict.ContainsKey(pxmx.Px) ? projectDict[pxmx.Px] : "", + actualPrice = pxmx.ActualPrice, + projectNumber = pxmx.ProjectNumber, + sourceType = pxmx.SourceType, + remark = pxmx.Remark + }).ToList(); + + // 6. 返回分页结果 + var result = new SqlSugarPagedList + { + list = resultList, + pagination = pagedData.pagination + }; + + return PageResult.SqlSugarPageResult(result); + } + catch (Exception ex) + { + _logger.LogError(ex, $"获取开单品项明细记录列表失败: {ex.ToString()}"); + throw NCCException.Oh(ErrorCode.COM1005, $"获取开单品项明细记录列表失败: {ex.Message}"); + } + } + #endregion } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs index e7b3951..8f431f7 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs @@ -776,5 +776,676 @@ namespace NCC.Extend.LqKhxx } #endregion + #region 获取会员品项信息 + /// + /// 获取所有会员的品项信息(分页) + /// + /// + /// 查询所有会员的品项信息,包括购买品项、赠送品项、体验品项、已消耗品项、已退卡品项、已储扣品项 + /// + /// 示例请求: + /// ```json + /// { + /// "currentPage": 1, + /// "pageSize": 10, + /// "MemberId": "会员ID", + /// "BelongStoreId": "所属门店ID", + /// "BillingStoreId": "开单门店ID", + /// "BillingUserId": "开单人ID", + /// "PurchaseItemId": "购买品项ID", + /// "GiftItemId": "赠送品项ID", + /// "ExperienceItemId": "体验品项ID" + /// } + /// ``` + /// + /// 参数说明: + /// - currentPage: 当前页码(必填) + /// - pageSize: 每页数量(必填) + /// - MemberId: 会员ID(可选) + /// - BelongStoreId: 所属门店ID(可选) + /// - BillingStoreId: 开单门店ID(可选) + /// - BillingUserId: 开单人ID(可选) + /// - PurchaseItemId: 购买品项ID(可选) + /// - GiftItemId: 赠送品项ID(可选) + /// - ExperienceItemId: 体验品项ID(可选) + /// + /// 返回数据结构说明: + /// ```json + /// { + /// "pagination": { + /// "pageIndex": 1, + /// "pageSize": 10, + /// "total": 100 + /// }, + /// "list": [ + /// { + /// "memberId": "会员ID", + /// "memberCode": "会员编号(档案号)", + /// "memberName": "会员名称", + /// "memberPhone": "会员电话", + /// "belongStoreId": "所属门店ID", + /// "belongStoreName": "所属门店名称", + /// "billingItems": [ + /// { + /// "billingId": "开单ID", + /// "billingTime": "2025-11-15T10:00:00", + /// "billingStoreId": "开单门店ID", + /// "billingStoreName": "开单门店名称", + /// "billingUserId": "开单人ID", + /// "billingUserName": "开单人名称", + /// "purchaseItems": [ + /// { + /// "itemId": "品项ID", + /// "itemName": "品项名称", + /// "itemPrice": 100.00, + /// "projectNumber": 5.0, + /// "totalAmount": 500.00 + /// } + /// ], + /// "purchaseItemsTotalAmount": 500.00, + /// "giftItems": [ + /// { + /// "itemId": "品项ID", + /// "itemName": "品项名称", + /// "itemPrice": 50.00, + /// "projectNumber": 2.0, + /// "totalAmount": 100.00 + /// } + /// ], + /// "experienceItems": [ + /// { + /// "itemId": "品项ID", + /// "itemName": "品项名称", + /// "itemPrice": 30.00, + /// "projectNumber": 1.0, + /// "totalAmount": 30.00 + /// } + /// ], + /// "consumedItems": [ + /// { + /// "itemId": "品项ID", + /// "itemName": "品项名称", + /// "itemPrice": 100.00, + /// "projectNumber": 2.0, + /// "totalAmount": 200.00 + /// } + /// ], + /// "consumedItemsTotalAmount": 200.00, + /// "refundedItems": [ + /// { + /// "itemId": "品项ID", + /// "itemName": "品项名称", + /// "itemPrice": 100.00, + /// "projectNumber": 1.0, + /// "totalAmount": 100.00 + /// } + /// ], + /// "refundedItemsTotalAmount": 100.00, + /// "deductedItems": [ + /// { + /// "itemId": "品项ID", + /// "itemName": "品项名称", + /// "itemPrice": 100.00, + /// "projectNumber": 1.0, + /// "totalAmount": 100.00 + /// } + /// ], + /// "deductedItemsTotalAmount": 100.00 + /// } + /// ] + /// } + /// ] + /// } + /// ``` + /// + /// 返回字段说明: + /// + /// **分页信息 (pagination)**: + /// - pageIndex: 当前页码 + /// - pageSize: 每页数量 + /// - total: 总记录数 + /// + /// **会员信息 (list[].memberInfo)**: + /// - memberId: 会员ID + /// - memberCode: 会员编号(档案号) + /// - memberName: 会员名称 + /// - memberPhone: 会员电话 + /// - belongStoreId: 所属门店ID + /// - belongStoreName: 所属门店名称 + /// + /// **开单信息 (list[].billingItems[])**: + /// - billingId: 开单ID + /// - billingTime: 开单时间(DateTime格式) + /// - billingStoreId: 开单门店ID + /// - billingStoreName: 开单门店名称 + /// - billingUserId: 开单人ID + /// - billingUserName: 开单人名称 + /// + /// **品项信息 (list[].billingItems[].*Items[])**: + /// - purchaseItems: 购买品项列表 + /// - purchaseItemsTotalAmount: 购买品项总金额 + /// - giftItems: 赠送品项列表 + /// - experienceItems: 体验品项列表 + /// - consumedItems: 已消耗品项列表 + /// - consumedItemsTotalAmount: 已消耗品项总金额 + /// - refundedItems: 已退卡品项列表 + /// - refundedItemsTotalAmount: 已退卡品项总金额 + /// - deductedItems: 已储扣品项列表 + /// - deductedItemsTotalAmount: 已储扣品项总金额 + /// + /// **品项明细 (ItemDetail)**: + /// - itemId: 品项ID + /// - itemName: 品项名称 + /// - itemPrice: 品项单价 + /// - projectNumber: 项目次数(支持小数) + /// - totalAmount: 总金额(单价 × 次数) + /// + /// 查询参数 + /// 会员品项信息列表(分页) + /// 查询成功,返回会员品项信息列表 + /// 参数错误,如页码或页大小无效 + /// 服务器错误,查询过程中发生异常 + [HttpPost("get-member-item-info")] + public async Task GetMemberItemInfo([FromBody] MemberItemInfoQueryInput input) + { + try + { + // 1. 构建会员查询条件 + var memberQuery = _db.Queryable(); + + // 筛选条件 + if (!string.IsNullOrEmpty(input.MemberId)) + { + memberQuery = memberQuery.Where(x => x.Id == input.MemberId); + } + + if (!string.IsNullOrEmpty(input.BelongStoreId)) + { + memberQuery = memberQuery.Where(x => x.Gsmd == input.BelongStoreId); + } + + // 如果指定了开单门店、开单人、品项ID等条件,需要通过开单表进行筛选 + if (!string.IsNullOrEmpty(input.BillingStoreId) || + !string.IsNullOrEmpty(input.BillingUserId) || + !string.IsNullOrEmpty(input.PurchaseItemId) || + !string.IsNullOrEmpty(input.GiftItemId) || + !string.IsNullOrEmpty(input.ExperienceItemId)) + { + // 先查询符合条件的开单记录,获取会员ID列表 + var billingQuery = _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()); + + if (!string.IsNullOrEmpty(input.BillingStoreId)) + { + billingQuery = billingQuery.Where(x => x.Djmd == input.BillingStoreId); + } + + if (!string.IsNullOrEmpty(input.BillingUserId)) + { + billingQuery = billingQuery.Where(x => x.CreateUser == input.BillingUserId); + } + + // 如果指定了品项ID,需要通过品项明细表进行筛选 + if (!string.IsNullOrEmpty(input.PurchaseItemId) || + !string.IsNullOrEmpty(input.GiftItemId) || + !string.IsNullOrEmpty(input.ExperienceItemId)) + { + var itemQuery = _db.Queryable() + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()); + + if (!string.IsNullOrEmpty(input.PurchaseItemId)) + { + itemQuery = itemQuery.Where(x => x.Px == input.PurchaseItemId && x.SourceType == "购买"); + } + + if (!string.IsNullOrEmpty(input.GiftItemId)) + { + itemQuery = itemQuery.Where(x => x.Px == input.GiftItemId && x.SourceType == "赠送"); + } + + if (!string.IsNullOrEmpty(input.ExperienceItemId)) + { + itemQuery = itemQuery.Where(x => x.Px == input.ExperienceItemId && x.SourceType == "体验"); + } + + var itemBillingIds = await itemQuery.Select(x => x.Glkdbh).Distinct().ToListAsync(); + if (itemBillingIds != null && itemBillingIds.Any()) + { + billingQuery = billingQuery.Where(x => itemBillingIds.Contains(x.Id)); + } + else + { + // 如果没有匹配的品项记录,返回空结果 + return PageResult.SqlSugarPageResult(new SqlSugarPagedList + { + list = new List(), + pagination = new PagedModel + { + PageIndex = input.currentPage, + PageSize = input.pageSize, + Total = 0 + } + }); + } + } + + var memberIds = await billingQuery.Select(x => x.Kdhy).Distinct().ToListAsync(); + if (memberIds != null && memberIds.Any()) + { + memberQuery = memberQuery.Where(x => memberIds.Contains(x.Id)); + } + else + { + // 如果没有匹配的开单记录,返回空结果 + return PageResult.SqlSugarPageResult(new SqlSugarPagedList + { + list = new List(), + pagination = new PagedModel + { + PageIndex = input.currentPage, + PageSize = input.pageSize, + Total = 0 + } + }); + } + } + + // 2. 分页查询会员列表 + var totalCount = await memberQuery.CountAsync(); + var skipCount = (input.currentPage - 1) * input.pageSize; + var memberList = await memberQuery + .OrderBy(x => x.Id) + .Skip(skipCount) + .Take(input.pageSize) + .Select(x => new + { + x.Id, + x.Dah, + x.Khmc, + x.Sjh, + x.Gsmd + }) + .ToListAsync(); + + var memberData = new SqlSugarPagedList + { + list = memberList.Cast().ToList(), + pagination = new PagedModel + { + PageIndex = input.currentPage, + PageSize = input.pageSize, + Total = totalCount + } + }; + + if (memberList == null || !memberList.Any()) + { + return PageResult.SqlSugarPageResult(new SqlSugarPagedList + { + list = new List(), + pagination = new PagedModel + { + PageIndex = input.currentPage, + PageSize = input.pageSize, + Total = totalCount + } + }); + } + + var memberIdsList = memberList.Select(x => x.Id).ToList(); + + // 3. 批量查询会员的开单记录 + var billingRecords = await _db.Queryable() + .Where(x => memberIdsList.Contains(x.Kdhy) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new + { + x.Id, + x.Kdhy, + x.Kdrq, + x.Djmd, + x.CreateUser + }) + .ToListAsync(); + + var billingIds = billingRecords.Select(x => x.Id).ToList(); + + // 4. 批量查询开单的品项明细 + var itemDetails = new List(); + if (billingIds.Any()) + { + var itemDetailsData = await _db.Queryable() + .Where(x => billingIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new + { + x.Id, + x.Glkdbh, + x.Px, + x.Pxmc, + x.Pxjg, + x.SourceType, + x.ProjectNumber, + x.TotalPrice + }) + .ToListAsync(); + itemDetails = itemDetailsData.Cast().ToList(); + } + + var itemDetailIds = itemDetails.Select(x => (string)x.Id).ToList(); + + // 5. 批量查询消耗品项 + var consumedItems = new List(); + if (itemDetailIds.Any()) + { + var consumedItemsData = await _db.Queryable() + .Where(x => itemDetailIds.Contains(x.BillingItemId) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new + { + x.BillingItemId, + x.Px, + x.Pxmc, + x.Pxjg, + x.ProjectNumber, + x.TotalPrice + }) + .ToListAsync(); + consumedItems = consumedItemsData.Cast().ToList(); + } + + // 6. 批量查询退卡品项 + var refundedItems = new List(); + if (itemDetailIds.Any()) + { + var refundedItemsData = await _db.Queryable() + .Where(x => itemDetailIds.Contains(x.BillingItemId) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new + { + x.BillingItemId, + x.Px, + x.Pxmc, + x.Pxjg, + x.ProjectNumber, + x.Tkje + }) + .ToListAsync(); + refundedItems = refundedItemsData.Cast().ToList(); + } + + // 7. 批量查询储扣品项 + var deductedItems = new List(); + if (billingIds.Any()) + { + var deductedItemsData = await _db.Queryable() + .Where(x => billingIds.Contains(x.BillingId) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new + { + x.BillingId, + x.ItemId, + x.ItemName, + x.UnitPrice, + x.ProjectNumber, + x.Amount + }) + .ToListAsync(); + deductedItems = deductedItemsData.Cast().ToList(); + } + + // 8. 批量查询门店信息 + var storeIds = new List(); + if (memberList != null && memberList.Any()) + { + storeIds.AddRange(memberList.Select(x => x.Gsmd).Where(x => !string.IsNullOrEmpty(x))); + } + if (billingRecords != null && billingRecords.Any()) + { + storeIds.AddRange(billingRecords.Select(x => x.Djmd).Where(x => !string.IsNullOrEmpty(x))); + } + storeIds = storeIds.Distinct().ToList(); + + var stores = new Dictionary(); + if (storeIds.Any()) + { + var storeList = await _db.Queryable() + .Where(x => storeIds.Contains(x.Id)) + .Select(x => new { x.Id, x.Dm }) + .ToListAsync(); + + stores = storeList.ToDictionary(x => x.Id, x => x.Dm ?? ""); + } + + // 9. 批量查询用户信息 + var userIds = new List(); + if (billingRecords != null && billingRecords.Any()) + { + userIds = billingRecords.Select(x => x.CreateUser) + .Where(x => !string.IsNullOrEmpty(x)) + .Distinct() + .ToList(); + } + + var users = new Dictionary(); + if (userIds.Any()) + { + var userList = await _db.Queryable() + .Where(x => userIds.Contains(x.Id)) + .Select(x => new { x.Id, x.RealName }) + .ToListAsync(); + + users = userList.ToDictionary(x => x.Id, x => x.RealName ?? ""); + } + + // 10. 组装数据 + var result = new List(); + + foreach (var member in memberList) + { + var memberOutput = new MemberItemInfoOutput + { + MemberId = member.Id, + MemberCode = member.Dah ?? "", + MemberName = member.Khmc ?? "", + MemberPhone = member.Sjh ?? "", + BelongStoreId = member.Gsmd ?? "", + BelongStoreName = stores.ContainsKey(member.Gsmd ?? "") ? stores[member.Gsmd] : "" + }; + + // 获取该会员的所有开单 + var memberBillings = billingRecords.Where(x => x.Kdhy == member.Id).ToList(); + + foreach (var billing in memberBillings) + { + var billingItem = new BillingItemInfo + { + BillingId = billing.Id, + BillingTime = billing.Kdrq, + BillingStoreId = billing.Djmd ?? "", + BillingStoreName = stores.ContainsKey(billing.Djmd ?? "") ? stores[billing.Djmd] : "", + BillingUserId = billing.CreateUser ?? "", + BillingUserName = users.ContainsKey(billing.CreateUser ?? "") ? users[billing.CreateUser] : "" + }; + + // 获取该开单的所有品项明细 + var billingItemDetails = itemDetails.Where(x => + { + try + { + return x.Glkdbh?.ToString() == billing.Id; + } + catch + { + return false; + } + }).ToList(); + + // 分类品项 + foreach (var item in billingItemDetails) + { + try + { + var itemDetail = new ItemDetail + { + ItemId = item.Px?.ToString() ?? "", + ItemName = item.Pxmc?.ToString() ?? "", + ItemPrice = Convert.ToDecimal(item.Pxjg ?? 0), + ProjectNumber = Convert.ToDecimal(item.ProjectNumber ?? 0), + TotalAmount = Convert.ToDecimal(item.TotalPrice ?? 0) + }; + + var sourceType = item.SourceType?.ToString() ?? ""; + if (sourceType == "购买") + { + billingItem.PurchaseItems.Add(itemDetail); + billingItem.PurchaseItemsTotalAmount += itemDetail.TotalAmount; + } + else if (sourceType == "赠送") + { + billingItem.GiftItems.Add(itemDetail); + } + else if (sourceType == "体验") + { + billingItem.ExperienceItems.Add(itemDetail); + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "处理品项明细时出错,开单ID:{BillingId}", billing.Id); + } + } + + // 获取消耗品项 + var billingItemIds = billingItemDetails.Select(x => + { + try + { + return x.Id?.ToString() ?? ""; + } + catch + { + return ""; + } + }).Where(x => !string.IsNullOrEmpty(x)).ToList(); + + var consumed = consumedItems.Where(x => + { + try + { + return billingItemIds.Contains(x.BillingItemId?.ToString() ?? ""); + } + catch + { + return false; + } + }).ToList(); + + foreach (var consumedItem in consumed) + { + try + { + billingItem.ConsumedItems.Add(new ItemDetail + { + ItemId = consumedItem.Px?.ToString() ?? "", + ItemName = consumedItem.Pxmc?.ToString() ?? "", + ItemPrice = Convert.ToDecimal(consumedItem.Pxjg ?? 0), + ProjectNumber = Convert.ToDecimal(consumedItem.ProjectNumber ?? 0), + TotalAmount = Convert.ToDecimal(consumedItem.TotalPrice ?? 0) + }); + billingItem.ConsumedItemsTotalAmount += Convert.ToDecimal(consumedItem.TotalPrice ?? 0); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "处理消耗品项时出错"); + } + } + + // 获取退卡品项 + var refunded = refundedItems.Where(x => + { + try + { + return billingItemIds.Contains(x.BillingItemId?.ToString() ?? ""); + } + catch + { + return false; + } + }).ToList(); + + foreach (var refundedItem in refunded) + { + try + { + billingItem.RefundedItems.Add(new ItemDetail + { + ItemId = refundedItem.Px?.ToString() ?? "", + ItemName = refundedItem.Pxmc?.ToString() ?? "", + ItemPrice = Convert.ToDecimal(refundedItem.Pxjg ?? 0), + ProjectNumber = Convert.ToDecimal(refundedItem.ProjectNumber ?? 0), + TotalAmount = Convert.ToDecimal(refundedItem.Tkje ?? 0) + }); + billingItem.RefundedItemsTotalAmount += Convert.ToDecimal(refundedItem.Tkje ?? 0); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "处理退卡品项时出错"); + } + } + + // 获取储扣品项 + var deducted = deductedItems.Where(x => + { + try + { + return x.BillingId?.ToString() == billing.Id; + } + catch + { + return false; + } + }).ToList(); + + foreach (var deductedItem in deducted) + { + try + { + billingItem.DeductedItems.Add(new ItemDetail + { + ItemId = deductedItem.ItemId?.ToString() ?? "", + ItemName = deductedItem.ItemName?.ToString() ?? "", + ItemPrice = Convert.ToDecimal(deductedItem.UnitPrice ?? 0), + ProjectNumber = Convert.ToDecimal(deductedItem.ProjectNumber ?? 0), + TotalAmount = Convert.ToDecimal(deductedItem.Amount ?? 0) + }); + billingItem.DeductedItemsTotalAmount += Convert.ToDecimal(deductedItem.Amount ?? 0); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "处理储扣品项时出错"); + } + } + + memberOutput.BillingItems.Add(billingItem); + } + + result.Add(memberOutput); + } + + return PageResult.SqlSugarPageResult(new SqlSugarPagedList + { + list = result, + pagination = new PagedModel + { + PageIndex = input.currentPage, + PageSize = input.pageSize, + Total = totalCount + } + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "查询会员品项信息失败,异常详情:{ExceptionDetail}", ex.ToString()); + throw NCCException.Oh(ErrorCode.COM1005, $"查询会员品项信息失败: {ex.Message}"); + } + } + #endregion + } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs index 25a252b..3870d34 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs @@ -971,6 +971,8 @@ namespace NCC.Extend.LqXhHyhk { memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); } + //保存会员信息 + await _db.Updateable(memberInfo).ExecuteCommandAsync(); // 新增会员耗卡记录 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); // 收集所有需要插入的实体,然后批量插入 @@ -1265,6 +1267,15 @@ namespace NCC.Extend.LqXhHyhk //更新会员耗卡记录 await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); + var memberInfo = await _db.Queryable().Where(w => w.Id == entity.Hy).FirstAsync(); + //如果会员类型是线索,那么就更新为新客 + if (memberInfo.Khlx == MemberTypeEnum.线索.GetHashCode().ToString()) + { + memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); + } + //保存会员信息 + await _db.Updateable(memberInfo).ExecuteCommandAsync(); + //清空原有数据 await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync(); diff --git a/sql/更新潜客为新客.sql b/sql/更新潜客为新客.sql new file mode 100644 index 0000000..372112d --- /dev/null +++ b/sql/更新潜客为新客.sql @@ -0,0 +1,54 @@ +-- ============================================ +-- 更新会员类型:将存在耗卡记录的潜客(0)更新为新客(1) +-- ============================================ +-- +-- 功能说明: +-- 1. 查找会员类型为 0(潜客/线索)的会员 +-- 2. 如果该会员存在有效的耗卡记录(lq_xh_hyhk 表中有记录且 F_IsEffective = 1) +-- 3. 将该会员的类型更新为 1(新客) +-- +-- 执行前建议: +-- 1. 先执行查询语句查看会更新多少条记录 +-- 2. 确认无误后再执行更新语句 +-- ============================================ + +-- 1. 查询语句:查看将要更新的会员数量和信息 +SELECT + kh.F_Id AS 会员ID, + kh.khmc AS 会员名称, + kh.sjh AS 手机号, + kh.khlx AS 当前类型, + COUNT(hyhk.F_Id) AS 耗卡记录数 +FROM lq_khxx kh +INNER JOIN lq_xh_hyhk hyhk ON kh.F_Id = hyhk.hy +WHERE kh.khlx = '0' -- 会员类型为 0(潜客/线索) + AND hyhk.F_IsEffective = 1 -- 耗卡记录有效 +GROUP BY kh.F_Id, kh.khmc, kh.sjh, kh.khlx; + +-- 2. 更新语句:将存在耗卡记录的潜客更新为新客 +UPDATE lq_khxx kh +SET kh.khlx = '1' -- 更新为新客(1) +WHERE kh.khlx = '0' -- 会员类型为 0(潜客/线索) + AND EXISTS ( + -- 确保至少有一条有效的耗卡记录 + SELECT 1 + FROM lq_xh_hyhk hyhk + WHERE hyhk.hy = kh.F_Id + AND hyhk.F_IsEffective = 1 + LIMIT 1 + ); + +-- 3. 验证语句:查看更新后的结果 +SELECT + kh.F_Id AS 会员ID, + kh.khmc AS 会员名称, + kh.sjh AS 手机号, + kh.khlx AS 更新后类型, + COUNT(hyhk.F_Id) AS 耗卡记录数 +FROM lq_khxx kh +INNER JOIN lq_xh_hyhk hyhk ON kh.F_Id = hyhk.hy +WHERE kh.khlx = '1' -- 会员类型为 1(新客) + AND hyhk.F_IsEffective = 1 -- 耗卡记录有效 +GROUP BY kh.F_Id, kh.khmc, kh.sjh, kh.khlx +HAVING COUNT(hyhk.F_Id) > 0; +