diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..834dd81
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,35 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": ".NET Core Launch (web)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/netcore/src/Application/NCC.API/bin/Debug/net6.0/NCC.API.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/netcore/src/Application/NCC.API",
+ "stopAtEntry": false,
+ // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
+ "serverReadyAction": {
+ "action": "openExternally",
+ "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..02fcf0f
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,42 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/netcore/src/Application/NCC.API/NCC.API.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/netcore/src/Application/NCC.API/NCC.API.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "${workspaceFolder}/netcore/src/Application/NCC.API/NCC.API.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/PROJECT_RULES.md b/PROJECT_RULES.md
index 48c51c9..908782f 100644
--- a/PROJECT_RULES.md
+++ b/PROJECT_RULES.md
@@ -35,6 +35,7 @@
- **图标显示**: 所有列表数据都要有图标,不同颜色区分类型
- **空值显示**: 没有信息的字段显示"无"
- **列表规范**: 列表数据不能换行
+- **弹窗显示**: 弹窗需要使用圆角 12px
### 性能要求
- 启用懒加载和代码分割
diff --git a/antis-ncc-admin/.env.development b/antis-ncc-admin/.env.development
index c236b7d..f6590fa 100644
--- a/antis-ncc-admin/.env.development
+++ b/antis-ncc-admin/.env.development
@@ -2,7 +2,7 @@
VUE_CLI_BABEL_TRANSPILE_MODULES = true
# VUE_APP_BASE_API = 'https://erp.lvqianmeiye.com'
-VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com'
-# VUE_APP_BASE_API = 'http://localhost:2011'
+# VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com'
+VUE_APP_BASE_API = 'http://localhost:2011'
# VUE_APP_BASE_API = 'http://localhost:2011'
VUE_APP_BASE_WSS = 'ws://192.168.110.45:2011/websocket'
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemsByMemberIdQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemsByMemberIdQueryInput.cs
new file mode 100644
index 0000000..a7c6d60
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemsByMemberIdQueryInput.cs
@@ -0,0 +1,46 @@
+using System;
+using NCC.Common.Filter;
+
+namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
+{
+ ///
+ /// 会员开单品项列表查询输入
+ ///
+ public class BillingItemsByMemberIdQueryInput : PageInputBase
+ {
+ ///
+ /// 会员ID
+ ///
+ public string MemberId { get; set; }
+
+ ///
+ /// 品项分类
+ ///
+ public string ItemCategory { get; set; }
+
+ ///
+ /// 开始时间(业绩时间)
+ ///
+ public DateTime? StartTime { get; set; }
+
+ ///
+ /// 结束时间(业绩时间)
+ ///
+ public DateTime? EndTime { get; set; }
+
+ ///
+ /// 最小实付金额
+ ///
+ public decimal? MinActualPrice { get; set; }
+
+ ///
+ /// 最大实付金额
+ ///
+ public decimal? MaxActualPrice { get; set; }
+
+ ///
+ /// 开单编号
+ ///
+ public string BillingId { get; set; }
+ }
+}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
index 235f3c5..7a59134 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
@@ -89,5 +89,10 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
/// 消耗项目数 - 统计该健康师在指定时间周期内消耗的项目总次数
///
public decimal projectCount { get; set; }
+
+ ///
+ /// 退卡金额 - 统计该健康师在指定时间周期内的退卡业绩总金额
+ ///
+ public decimal refundAmount { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxInfoOutput.cs
index e8762af..17e3ab7 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxInfoOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxInfoOutput.cs
@@ -151,6 +151,11 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
public int isTechMember { get; set; }
///
+ /// 是否教育部会员(0-否,1-是)
+ ///
+ public int isEducationMember { get; set; }
+
+ ///
/// 成为生美会员时间
///
public DateTime? beautyMemberTime { get; set; }
@@ -166,6 +171,11 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
public DateTime? techMemberTime { get; set; }
///
+ /// 成为教育部会员时间
+ ///
+ public DateTime? educationMemberTime { get; set; }
+
+ ///
/// 首次到店时间
///
public DateTime? firstVisitTime { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
index 924fde8..5108881 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListOutput.cs
@@ -178,6 +178,11 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
public int isTechMember { get; set; }
///
+ /// 是否教育部会员(0-否,1-是)
+ ///
+ public int isEducationMember { get; set; }
+
+ ///
/// 成为生美会员时间
///
public DateTime? beautyMemberTime { get; set; }
@@ -193,6 +198,11 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
public DateTime? techMemberTime { get; set; }
///
+ /// 成为教育部会员时间
+ ///
+ public DateTime? educationMemberTime { get; set; }
+
+ ///
/// 首次到店时间
///
public DateTime? firstVisitTime { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListQueryInput.cs
index c60cd12..3e8df55 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListQueryInput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKhxx/LqKhxxListQueryInput.cs
@@ -83,6 +83,15 @@ namespace NCC.Extend.Entitys.Dto.LqKhxx
///
public string tjr { get; set; }
+ ///
+ /// 是否科技部会员
+ ///
+ public int? IsTechMemberbh { get; set; }
+
+ ///
+ /// 是否教育部会员
+ ///
+ public int? IsEducationMember { get; set; }
///
/// 进店渠道
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhFeedback/LqXhFeedbackListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhFeedback/LqXhFeedbackListQueryInput.cs
index 7b50695..2c810c7 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhFeedback/LqXhFeedbackListQueryInput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqXhFeedback/LqXhFeedbackListQueryInput.cs
@@ -13,6 +13,11 @@ namespace NCC.Extend.Entitys.Dto.LqXhFeedback
public string ConsumeId { get; set; }
///
+ /// 会员ID
+ ///
+ public string MemberId { get; set; }
+
+ ///
/// 添加人ID
///
public string CreateUser { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_khxx/LqKhxxEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_khxx/LqKhxxEntity.cs
index 64c4b33..bb078a7 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_khxx/LqKhxxEntity.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_khxx/LqKhxxEntity.cs
@@ -184,6 +184,18 @@ namespace NCC.Extend.Entitys.lq_khxx
public DateTime? TechMemberTime { get; set; }
///
+ /// 是否教育部会员(0-否,1-是)
+ ///
+ [SugarColumn(ColumnName = "F_IsEducationMember")]
+ public int IsEducationMember { get; set; } = 0;
+
+ ///
+ /// 成为教育部会员时间
+ ///
+ [SugarColumn(ColumnName = "F_EducationMemberTime")]
+ public DateTime? EducationMemberTime { get; set; }
+
+ ///
/// 首次到店时间
///
[SugarColumn(ColumnName = "F_FirstVisitTime")]
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
index 2906262..adbe367 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
@@ -435,6 +435,8 @@ namespace NCC.Extend.LqHytkHytk
F_CreateUser = userInfo.userId,
CardReturn = lqHytkMxEntity.Id,
IsEffective = StatusEnum.有效.GetHashCode(),
+ ItemCategory = lqHytkMxEntity.ItemCategory,
+ ItemId = lqHytkMxEntity.Px,
}
);
}
@@ -462,6 +464,8 @@ namespace NCC.Extend.LqHytkHytk
F_CreateUser = userInfo.userId,
CardReturn = lqHytkMxEntity.Id,
IsEffective = StatusEnum.有效.GetHashCode(),
+ ItemCategory = lqHytkMxEntity.ItemCategory,
+ ItemId = lqHytkMxEntity.Px,
}
);
}
@@ -586,6 +590,8 @@ namespace NCC.Extend.LqHytkHytk
F_tkpxNumber = ijks_tem.F_tkpxNumber,
F_CreateTime = DateTime.Now,
F_CreateUser = userInfo.userId,
+ ItemCategory = lqHytkMxEntity.ItemCategory,
+ ItemId = lqHytkMxEntity.Px,
}
);
}
@@ -611,6 +617,8 @@ namespace NCC.Extend.LqHytkHytk
F_tkpxNumber = ikjbs_tem.F_tkpxNumber,
F_CreateTime = DateTime.Now,
F_CreateUser = userInfo.userId,
+ ItemCategory = lqHytkMxEntity.ItemCategory,
+ ItemId = lqHytkMxEntity.Px,
}
);
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
index 53b99e0..03c555d 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -953,6 +953,8 @@ namespace NCC.Extend.LqKdKdjlb
Kdpxid = lqKdPxmxEntity.Id,
IsEffective = StatusEnum.有效.GetHashCode(),
ActivityId = input.activityId,
+ ItemCategory = lqKdPxmxEntity.ItemCategory,
+ ItemId = lqKdPxmxEntity.Px,
});
}
}
@@ -974,6 +976,8 @@ namespace NCC.Extend.LqKdKdjlb
Kdpxid = lqKdPxmxEntity.Id,
IsEffective = StatusEnum.有效.GetHashCode(),
ActivityId = input.activityId,
+ ItemCategory = lqKdPxmxEntity.ItemCategory,
+ ItemId = lqKdPxmxEntity.Px,
}
);
}
@@ -1750,6 +1754,8 @@ namespace NCC.Extend.LqKdKdjlb
Kdpxid = lqKdPxmxEntity.Id,
IsEffective = StatusEnum.有效.GetHashCode(),
ActivityId = input.activityId,
+ ItemCategory = lqKdPxmxEntity.ItemCategory,
+ ItemId = lqKdPxmxEntity.Px,
});
}
}
@@ -1771,6 +1777,8 @@ namespace NCC.Extend.LqKdKdjlb
Kdpxid = lqKdPxmxEntity.Id,
IsEffective = StatusEnum.有效.GetHashCode(),
ActivityId = input.activityId,
+ ItemCategory = lqKdPxmxEntity.ItemCategory,
+ ItemId = lqKdPxmxEntity.Px,
}
);
}
@@ -2308,6 +2316,48 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
+ #region 根据会员id获取会员的开单品项列表
+ ///
+ /// 根据会员id获取会员的开单品项列表
+ ///
+ /// 查询参数
+ /// 会员的开单品项列表
+ [HttpPost("GetBillingItemsByMemberId")]
+ public async Task GetBillingItemsByMemberId([FromBody] BillingItemsByMemberIdQueryInput input)
+ {
+ try
+ {
+ var query = _db.Queryable()
+ .Where(p => p.MemberId == input.MemberId)
+ .WhereIF(!string.IsNullOrEmpty(input.ItemCategory), p => p.ItemCategory == input.ItemCategory)
+ .WhereIF(!string.IsNullOrEmpty(input.BillingId), p => p.Glkdbh == input.BillingId)
+ .WhereIF(input.StartTime.HasValue, p => p.Yjsj >= input.StartTime.Value)
+ .WhereIF(input.EndTime.HasValue, p => p.Yjsj <= input.EndTime.Value)
+ .WhereIF(input.MinActualPrice.HasValue, p => p.ActualPrice >= input.MinActualPrice.Value)
+ .WhereIF(input.MaxActualPrice.HasValue, p => p.ActualPrice <= input.MaxActualPrice.Value)
+ .OrderBy(p => p.Yjsj, OrderByType.Desc);
+
+ var list = await query.ToPageListAsync(input.currentPage, input.pageSize);
+ var totalCount = await query.CountAsync();
+
+ return new
+ {
+ list = list,
+ pagination = new
+ {
+ pageIndex = input.currentPage,
+ pageSize = input.pageSize,
+ totalCount = totalCount
+ }
+ };
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"获取会员开单品项列表失败: {ex.Message}");
+ }
+ }
+ #endregion
+
#region 根据开单id获取当前开单欠款信息
///
/// 根据开单id获取当前开单欠款信息
@@ -3173,6 +3223,7 @@ namespace NCC.Extend.LqKdKdjlb
/// - HeadCount: 人头(按客户去重)
/// - PersonCount: 人次(按客户+日期去重,同一客户不同天算多次)
/// - ProjectCount: 消耗项目数(项目总次数)
+ /// - RefundAmount: 退卡金额(健康师退卡业绩总金额)
///
/// 查询参数
/// 健康师统计数据列表
@@ -3217,7 +3268,10 @@ namespace NCC.Extend.LqKdKdjlb
CAST(COALESCE(personcount_stats.PersonCount, 0) AS DECIMAL(18,2)) as PersonCount,
CAST(COALESCE(invalid_headcount_stats.HeadCount, 0) AS DECIMAL(18,2)) as InvalidHeadCount,
CAST(COALESCE(invalid_personcount_stats.PersonCount, 0) AS DECIMAL(18,2)) as InvalidPersonCount,
- CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount
+ CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount,
+
+ -- 退卡金额
+ COALESCE(refund_stats.RefundAmount, 0) as RefundAmount
FROM BASE_USER u
LEFT JOIN lq_mdxx md ON u.F_MDID = md.F_Id
@@ -3382,6 +3436,19 @@ namespace NCC.Extend.LqKdKdjlb
GROUP BY F_PersonId
) invalid_personcount_stats ON u.F_Id = invalid_personcount_stats.EmployeeId
+ -- 退卡统计子查询
+ LEFT JOIN (
+ SELECT
+ jkszh as EmployeeId,
+ SUM(CAST(jksyj AS DECIMAL(18,2))) as RefundAmount
+ FROM lq_hytk_jksyj
+ WHERE jkszh IS NOT NULL
+ AND F_IsEffective = 1
+ AND tksj >= @startTime
+ AND tksj <= @endTime
+ GROUP BY jkszh
+ ) refund_stats ON u.F_Id = refund_stats.EmployeeId
+
WHERE u.F_GW = '健康师'
";
@@ -3966,5 +4033,101 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
+ #region 清空欠款
+ ///
+ /// 清空欠款(减免剩余欠款)
+ ///
+ ///
+ /// 返回参数说明:
+ /// - billingId: 开单ID
+ /// - original: 原始数据(zdyj-整单业绩, qk-欠款, paidDebt-已交欠款, remainingDebt-剩余欠款)
+ /// - updated: 更新后数据(zdyj-整单业绩, qk-欠款, paidDebt-已交欠款, remainingDebt-剩余欠款)
+ /// - reducedAmount: 减免金额
+ ///
+ /// 开单记录ID
+ /// 操作结果
+ /// 成功清空欠款
+ /// 参数错误或无剩余欠款
+ /// 开单记录不存在
+ /// 服务器错误
+ [HttpPost("clear-debt/{id}")]
+ public async Task ClearDebt(string id)
+ {
+ try
+ {
+ // 1. 参数验证
+ if (string.IsNullOrEmpty(id))
+ {
+ throw NCCException.Oh("开单ID不能为空");
+ }
+ // 2. 查询开单记录
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+
+ if (entity == null)
+ {
+ throw NCCException.Oh("开单记录不存在");
+ }
+ // 3. 验证开单是否有效
+ if (entity.IsEffective != StatusEnum.有效.GetHashCode())
+ {
+ throw NCCException.Oh("开单记录已作废,无法清空欠款");
+ }
+ // 4. 计算剩余欠款
+ var remainingDebt = entity.Qk - entity.PaidDebt;
+
+ // 5. 验证是否有剩余欠款
+ if (remainingDebt <= 0)
+ {
+ return new
+ {
+ billingId = id,
+ totalDebt = entity.Qk,
+ paidDebt = entity.PaidDebt,
+ remainingDebt = remainingDebt,
+ message = "该开单无剩余欠款,无需清空"
+ };
+ }
+
+ // 6. 记录原始数据(用于返回)
+ var originalZdyj = entity.Zdyj;
+ var originalQk = entity.Qk;
+ // 7. 执行欠款减免
+ // 从整单业绩中减去剩余欠款
+ entity.Zdyj = entity.Zdyj - remainingDebt;
+ // 欠款金额调整为已交金额(让剩余欠款归零)
+ entity.Qk = entity.PaidDebt;
+ // 更新时间
+ entity.UpdateTime = DateTime.Now;
+ // 8. 更新数据库
+ await _db.Updateable(entity).UpdateColumns(it => new { it.Zdyj, it.Qk, it.UpdateTime }).ExecuteCommandAsync();
+ // 9. 返回结果
+ return new
+ {
+ billingId = id,
+ original = new
+ {
+ zdyj = originalZdyj,
+ qk = originalQk,
+ paidDebt = entity.PaidDebt,
+ remainingDebt = remainingDebt
+ },
+ updated = new
+ {
+ zdyj = entity.Zdyj,
+ qk = entity.Qk,
+ paidDebt = entity.PaidDebt,
+ remainingDebt = entity.Qk - entity.PaidDebt
+ },
+ reducedAmount = remainingDebt
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"清空欠款失败 - 开单ID: {id}");
+ throw NCCException.Oh($"清空欠款失败: {ex.Message}");
+ }
+ }
+ #endregion
+
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
index f6a4761..841589c 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKhxxService.cs
@@ -116,6 +116,8 @@ namespace NCC.Extend.LqKhxx
DateTime? endZcsj = queryZcsj != null ? Ext.GetDateTime(queryZcsj.Last()) : null;
var data = await _db.Queryable()
.WhereIF(!string.IsNullOrEmpty(input.keyWord), p => p.Khmc.Contains(input.keyWord) || p.Sjh.Contains(input.keyWord) || p.Dah.Contains(input.keyWord))
+ .WhereIF(input.IsTechMemberbh.HasValue, p => p.IsTechMember == input.IsTechMemberbh)
+ .WhereIF(input.IsEducationMember.HasValue, p => p.IsEducationMember == input.IsEducationMember)
.WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id))
.WhereIF(!string.IsNullOrEmpty(input.khmc), p => p.Khmc.Contains(input.khmc))
.WhereIF(!string.IsNullOrEmpty(input.sjh), p => p.Sjh.Contains(input.sjh))
@@ -163,9 +165,11 @@ namespace NCC.Extend.LqKhxx
isBeautyMember = it.IsBeautyMember,
isMedicalMember = it.IsMedicalMember,
isTechMember = it.IsTechMember,
+ isEducationMember = it.IsEducationMember,
beautyMemberTime = it.BeautyMemberTime,
medicalMemberTime = it.MedicalMemberTime,
techMemberTime = it.TechMemberTime,
+ educationMemberTime = it.EducationMemberTime,
firstVisitTime = it.FirstVisitTime,
lastVisitTime = it.LastVisitTime,
visitDays = it.VisitDays,
@@ -1636,7 +1640,6 @@ namespace NCC.Extend.LqKhxx
var techThreshold = MemberInfoUpdateConfig.TechMemberAmountThreshold;
var minSleepThreshold = MemberInfoUpdateConfig.SleepDaysThresholds.Min();
var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-
// 构建SQL更新语句
var updateSql = $@"
UPDATE lq_khxx kh
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
index 5c106cc..7f3a344 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
@@ -741,6 +741,24 @@ namespace NCC.Extend.LqTkjlb
///
/// 获取拓客活动漏斗统计数据(高性能版本 - 分步查询)
///
+ ///
+ /// 统计指定活动的漏斗转化数据
+ ///
+ /// 返回字段说明:
+ /// - store_id: 门店ID
+ /// - store_name: 门店名称
+ /// - tk_count: 拓客数量(活动拓客总人数)
+ /// - yaoy_count: 邀约数量(已邀约人数)
+ /// - yy_count: 预约数量(已确认预约人数)
+ /// - hk_count: 耗卡数量(实际到店耗卡人数)
+ /// - kd_count: 开单数量(实际成交开单人数)
+ /// - hk_amount: 耗卡金额(总耗卡业绩)
+ /// - kd_amount: 开单金额(总成交业绩)
+ /// - yy_conversion_rate: 预约转化率 (预约数量/拓客数量)
+ /// - hk_conversion_rate: 耗卡转化率 (耗卡数量/预约数量)
+ /// - visit_rate: 到店率 (耗卡数量/拓客数量)
+ /// - deal_rate: 成交率 (开单数量/耗卡数量)
+ ///
/// 活动ID
/// 漏斗统计数据
[HttpGet("GetFunnelStatisticsFast/{eventId}")]
@@ -803,6 +821,7 @@ namespace NCC.Extend.LqTkjlb
var hkDict = hkData.ToDictionary(x => (string)x.F_StoreId, x => new { count = (int)x.hk_count, amount = (decimal)x.hk_amount });
// 第五步:获取开单数据(按会员ID去重,但金额不去重)
+ // 修改:增加 sfyj > 0 的条件
var kdSql = @"
SELECT
tk.F_StoreId,
@@ -810,26 +829,40 @@ namespace NCC.Extend.LqTkjlb
COALESCE(SUM(kd.sfyj), 0) as kd_amount
FROM lq_tkjlb tk
LEFT JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy AND kd.F_IsEffective = 1
- WHERE tk.F_EventId = @eventId AND kd.F_Id IS NOT NULL
+ WHERE tk.F_EventId = @eventId AND kd.F_Id IS NOT NULL AND kd.sfyj > 0
GROUP BY tk.F_StoreId";
var kdData = await _db.Ado.SqlQueryAsync(kdSql, new { eventId });
var kdDict = kdData.ToDictionary(x => (string)x.F_StoreId, x => new { count = (int)x.kd_count, amount = (decimal)x.kd_amount });
// 合并结果
- var result = tkDict.Values.Select(tk => new
+ var result = tkDict.Values.Select(tk =>
{
- store_id = tk.F_StoreId,
- store_name = tk.store_name,
- tk_count = (int)tk.tk_count,
- yaoy_count = yaoyDict.ContainsKey(tk.F_StoreId) ? yaoyDict[tk.F_StoreId] : 0,
- yy_count = yyDict.ContainsKey(tk.F_StoreId) ? yyDict[tk.F_StoreId] : 0,
- hk_count = hkDict.ContainsKey(tk.F_StoreId) ? hkDict[tk.F_StoreId].count : 0,
- kd_count = kdDict.ContainsKey(tk.F_StoreId) ? kdDict[tk.F_StoreId].count : 0,
- hk_amount = hkDict.ContainsKey(tk.F_StoreId) ? hkDict[tk.F_StoreId].amount : 0m,
- kd_amount = kdDict.ContainsKey(tk.F_StoreId) ? kdDict[tk.F_StoreId].amount : 0m,
- yy_conversion_rate = (int)tk.tk_count > 0 ? Math.Round((yyDict.ContainsKey(tk.F_StoreId) ? yyDict[tk.F_StoreId] : 0) * 100.0 / (int)tk.tk_count, 2) : 0,
- hk_conversion_rate = (yyDict.ContainsKey(tk.F_StoreId) ? yyDict[tk.F_StoreId] : 0) > 0 ? Math.Round((hkDict.ContainsKey(tk.F_StoreId) ? hkDict[tk.F_StoreId].count : 0) * 100.0 / (yyDict.ContainsKey(tk.F_StoreId) ? yyDict[tk.F_StoreId] : 0), 2) : 0
+ var tkCount = (int)tk.tk_count;
+ var yyCount = yyDict.ContainsKey(tk.F_StoreId) ? yyDict[tk.F_StoreId] : 0;
+ var hkCount = hkDict.ContainsKey(tk.F_StoreId) ? hkDict[tk.F_StoreId].count : 0;
+ var kdCount = kdDict.ContainsKey(tk.F_StoreId) ? kdDict[tk.F_StoreId].count : 0;
+
+ return new
+ {
+ store_id = tk.F_StoreId,
+ store_name = tk.store_name,
+ tk_count = tkCount,
+ yaoy_count = yaoyDict.ContainsKey(tk.F_StoreId) ? yaoyDict[tk.F_StoreId] : 0,
+ yy_count = yyCount,
+ hk_count = hkCount,
+ kd_count = kdCount,
+ hk_amount = hkDict.ContainsKey(tk.F_StoreId) ? hkDict[tk.F_StoreId].amount : 0m,
+ kd_amount = kdDict.ContainsKey(tk.F_StoreId) ? kdDict[tk.F_StoreId].amount : 0m,
+ // 预约转化率 = 预约 / 拓客
+ yy_conversion_rate = tkCount > 0 ? Math.Round(yyCount * 100.0 / tkCount, 2) : 0,
+ // 耗卡转化率 = 耗卡 / 预约
+ hk_conversion_rate = yyCount > 0 ? Math.Round(hkCount * 100.0 / yyCount, 2) : 0,
+ // 到店率 = 耗卡 / 拓客
+ visit_rate = tkCount > 0 ? Math.Round(hkCount * 100.0 / tkCount, 2) : 0,
+ // 成交率 = 开单 / 耗卡
+ deal_rate = hkCount > 0 ? Math.Round(kdCount * 100.0 / hkCount, 2) : 0
+ };
}).OrderByDescending(x => x.tk_count).ToList();
return new
@@ -868,7 +901,7 @@ namespace NCC.Extend.LqTkjlb
-- 预约信息
yy.F_Id as yy_id, -- 预约ID
yy.F_Status as yy_status, -- 预约状态
- yy.F_CreateTime as yy_time, -- 预约时间
+ yy.czsj as yy_time, -- 预约操作时间
yy.yysj as appointment_time, -- 预约到店时间
-- 耗卡信息(聚合)
xh_summary.total_consume_amount, -- 耗卡总金额
@@ -891,7 +924,7 @@ namespace NCC.Extend.LqTkjlb
ELSE '未预约'
END as appointment_status, -- 预约状态描述
CASE
- WHEN xh_summary.total_consume_amount > 0 THEN '已耗卡'
+ WHEN xh_summary.consume_count > 0 THEN '已耗卡'
ELSE '未耗卡'
END as consume_status, -- 耗卡状态描述
CASE
@@ -905,14 +938,14 @@ namespace NCC.Extend.LqTkjlb
AND yy.F_Status = '已确认'
LEFT JOIN (
SELECT
- hy as member_id,
+ hyzh as member_id,
SUM(COALESCE(xfje, 0)) as total_consume_amount,
COUNT(*) as consume_count,
MIN(hksj) as first_consume_time,
MAX(hksj) as last_consume_time
FROM lq_xh_hyhk
WHERE F_IsEffective = 1
- GROUP BY hy
+ GROUP BY hyzh
) xh_summary ON tk.F_MemberId = xh_summary.member_id
LEFT JOIN (
SELECT
@@ -962,6 +995,34 @@ namespace NCC.Extend.LqTkjlb
///
/// 获取门店拓客活动顾客详情(分页)
///
+ ///
+ /// 返回字段说明:
+ /// - tk_id: 拓客记录ID
+ /// - customer_phone: 顾客手机号
+ /// - member_id: 会员ID
+ /// - customer_name: 顾客姓名
+ /// - tk_time: 拓客时间
+ /// - yaoy_id: 邀约ID
+ /// - yaoy_time: 邀约创建时间
+ /// - yaoy_appointment_time: 邀约预约时间
+ /// - yy_id: 预约ID
+ /// - yy_status: 预约状态
+ /// - yy_time: 预约创建时间
+ /// - appointment_time: 预约到店时间
+ /// - total_consume_amount: 耗卡总金额
+ /// - consume_count: 耗卡次数
+ /// - first_consume_time: 首次耗卡时间
+ /// - last_consume_time: 最后耗卡时间
+ /// - total_billing_amount: 开卡总金额
+ /// - total_debt_amount: 总欠款金额
+ /// - billing_count: 开卡次数
+ /// - first_billing_time: 首次开卡时间
+ /// - last_billing_time: 最后开卡时间
+ /// - invitation_status: 邀约状态描述 (已邀约/未邀约)
+ /// - appointment_status: 预约状态描述 (已预约/未预约)
+ /// - consume_status: 耗卡状态描述 (已耗卡/未耗卡)
+ /// - billing_status: 开卡状态描述 (已开卡/未开卡)
+ ///
/// 活动ID
/// 门店ID
/// 页码
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhFeedbackService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhFeedbackService.cs
index e3681fb..b4baecf 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhFeedbackService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhFeedbackService.cs
@@ -303,5 +303,79 @@ namespace NCC.Extend
}
}
#endregion
+
+ #region 获取耗卡反馈列表
+ ///
+ /// 获取耗卡反馈列表
+ ///
+ ///
+ /// 获取耗卡反馈列表,支持分页和条件查询
+ ///
+ /// 示例请求:
+ /// POST /api/Extend/LqXhFeedback/GetList
+ /// {
+ /// "currentPage": 1,
+ /// "pageSize": 10,
+ /// "memberId": "会员ID",
+ /// "startTime": "2025-01-01",
+ /// "endTime": "2025-01-31"
+ /// }
+ ///
+ /// 参数说明:
+ /// - currentPage: 当前页码
+ /// - pageSize: 每页条数
+ /// - memberId: 会员ID(可选)
+ /// - consumeId: 耗卡记录ID(可选)
+ /// - startTime: 开始时间(可选)
+ /// - endTime: 结束时间(可选)
+ ///
+ /// 查询参数
+ /// 耗卡反馈列表
+ [HttpPost("GetList")]
+ public async Task GetList([FromBody] LqXhFeedbackListQueryInput input)
+ {
+ try
+ {
+ var query = _db.Queryable()
+ .WhereIF(!string.IsNullOrEmpty(input.MemberId), p => p.MemberId == input.MemberId)
+ .WhereIF(!string.IsNullOrEmpty(input.ConsumeId), p => p.ConsumeId == input.ConsumeId)
+ .WhereIF(!string.IsNullOrEmpty(input.CreateUser), p => p.CreateUser == input.CreateUser)
+ .WhereIF(input.StartTime.HasValue, p => p.CreateTime >= input.StartTime.Value)
+ .WhereIF(input.EndTime.HasValue, p => p.CreateTime <= input.EndTime.Value)
+ .OrderBy(p => p.CreateTime, OrderByType.Desc);
+
+ var list = await query.ToPageListAsync(input.currentPage, input.pageSize);
+ var totalCount = await query.CountAsync();
+
+ var data = list.Adapt>();
+
+ // 填充添加人姓名
+ if (data.Any())
+ {
+ var userIds = data.Select(x => x.createUser).Distinct().ToList();
+ var users = await _db.Queryable().Where(x => userIds.Contains(x.Id)).ToListAsync();
+ foreach (var item in data)
+ {
+ item.createUserName = users.FirstOrDefault(x => x.Id == item.createUser)?.RealName;
+ }
+ }
+
+ return new
+ {
+ list = data,
+ pagination = new
+ {
+ pageIndex = input.currentPage,
+ pageSize = input.pageSize,
+ totalCount = totalCount
+ }
+ };
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"获取耗卡反馈列表失败: {ex.Message}");
+ }
+ }
+ #endregion
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
index e13d609..e6a8089 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
@@ -968,6 +968,8 @@ namespace NCC.Extend.LqXhHyhk
IsAccompanied = ijks_tem.isAccompanied,
AccompaniedProjectNumber = ijks_tem.accompaniedProjectNumber,
IsEffective = StatusEnum.有效.GetHashCode(),
+ ItemCategory = lqXhPxmxEntity.ItemCategory,
+ ItemId = lqXhPxmxEntity.Px,
}
);
}
@@ -1003,6 +1005,8 @@ namespace NCC.Extend.LqXhHyhk
OvertimeLaborCost = 0,
LaborCost = ikjbs_tem.laborCost,
IsEffective = StatusEnum.有效.GetHashCode(),
+ ItemCategory = lqXhPxmxEntity.ItemCategory,
+ ItemId = lqXhPxmxEntity.Px,
}
);
}
@@ -1335,6 +1339,8 @@ namespace NCC.Extend.LqXhHyhk
IsEffective = StatusEnum.有效.GetHashCode(),
IsAccompanied = ijks_tem.isAccompanied,
AccompaniedProjectNumber = ijks_tem.accompaniedProjectNumber,
+ ItemCategory = lqXhPxmxEntity.ItemCategory,
+ ItemId = lqXhPxmxEntity.Px,
}
);
}
@@ -1369,6 +1375,8 @@ namespace NCC.Extend.LqXhHyhk
OvertimeLaborCost = 0,
LaborCost = ikjbs_tem.laborCost,
IsEffective = StatusEnum.有效.GetHashCode(),
+ ItemCategory = lqXhPxmxEntity.ItemCategory,
+ ItemId = lqXhPxmxEntity.Px,
});
}
}