Commit 450d4d8bdef369111268bd024377b8f7c5c3bc84

Authored by “wangming”
1 parent 7200c7ad

refactor: 废弃 lq_mdxx.kjb,科技部门店改由 lq_md_target 按月份获取

- LqReportService: 门店基础信息/数据分析/对比分析 科技部归属及同组织门店改为 lq_md_target
- LqTechGeneralManagerSalaryService: 科技部总经理管理门店改为 lq_md_target 按统计月
- LqShareStatisticsTechDeptService: 科技部股份统计收入成本按 lq_md_target 取科技部门店
- LqZjlMdsmxszService: GetManagedStores 科技一部/二部门店改为 lq_md_target 按当前月
- LqAnnualSummaryService: 移除对 Kjb 的 Select 依赖
- 新增接口测试脚本 test-kjb-removal-apis.sh
- 新增 SQL: 根据产品平均单价更新库存领取金额
netcore/src/Modularity/Extend/NCC.Extend/LqAnnualSummaryService.cs
... ... @@ -82,7 +82,7 @@ namespace NCC.Extend
82 82 var storeIds = list.list.Select(x => x.StoreId).Distinct().ToList();
83 83 var stores = await _db.Queryable<LqMdxxEntity>()
84 84 .Where(x => storeIds.Contains(x.Id))
85   - .Select(x => new { x.Id, x.Syb, x.Kjb })
  85 + .Select(x => new { x.Id, x.Syb })
86 86 .ToListAsync();
87 87 var storeDict = stores.ToDictionary(x => x.Id, x => x);
88 88  
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
... ... @@ -4653,7 +4653,7 @@ namespace NCC.Extend
4653 4653  
4654 4654 // 从门店目标表获取归属ID
4655 4655 var businessUnitId = target?.BusinessUnit ?? store.Syb;
4656   - var techDepartmentId = target?.TechDepartment ?? store.Kjb;
  4656 + var techDepartmentId = target?.TechDepartment;
4657 4657 var educationDepartmentId = target?.EducationDepartment ?? store.Jyb;
4658 4658 var majorProjectDepartmentId = target?.MajorProjectDepartment ?? store.Dxmb;
4659 4659  
... ... @@ -4783,7 +4783,7 @@ namespace NCC.Extend
4783 4783  
4784 4784 // 从门店目标表获取归属ID
4785 4785 var businessUnitId = target?.BusinessUnit ?? store.Syb;
4786   - var techDepartmentId = target?.TechDepartment ?? store.Kjb;
  4786 + var techDepartmentId = target?.TechDepartment;
4787 4787 var educationDepartmentId = target?.EducationDepartment ?? store.Jyb;
4788 4788 var majorProjectDepartmentId = target?.MajorProjectDepartment ?? store.Dxmb;
4789 4789  
... ... @@ -5353,11 +5353,13 @@ namespace NCC.Extend
5353 5353 .Select(x => x.Id)
5354 5354 .ToListAsync();
5355 5355 }
5356   - else if (!string.IsNullOrWhiteSpace(store.Kjb))
  5356 + else if (target != null && !string.IsNullOrWhiteSpace(target.TechDepartment))
5357 5357 {
5358   - sameOrgStoreIds = await _db.Queryable<LqMdxxEntity>()
5359   - .Where(x => x.Kjb == store.Kjb && x.Zxzt == "开店" && x.Id != input.StoreId)
5360   - .Select(x => x.Id)
  5358 + // 科技部已废弃 lq_mdxx.kjb,按统计月份从门店目标表取同科技部门店(且开店)
  5359 + sameOrgStoreIds = await _db.Queryable<LqMdTargetEntity, LqMdxxEntity>((t, m) => t.StoreId == m.Id && m.Zxzt == "开店")
  5360 + .Where((t, m) => t.Month == statisticsMonth && t.TechDepartment == target.TechDepartment && t.StoreId != input.StoreId)
  5361 + .Select((t, m) => t.StoreId)
  5362 + .Distinct()
5361 5363 .ToListAsync();
5362 5364 }
5363 5365  
... ... @@ -6212,6 +6214,11 @@ namespace NCC.Extend
6212 6214 throw NCCException.Oh("门店不存在");
6213 6215 }
6214 6216  
  6217 + // 获取当月门店目标(用于同组织门店-科技部从 lq_md_target 取)
  6218 + var target = await _db.Queryable<LqMdTargetEntity>()
  6219 + .Where(x => x.StoreId == input.StoreId && x.Month == statisticsMonth)
  6220 + .FirstAsync();
  6221 +
6215 6222 // 获取业绩排名
6216 6223 var allStoresPerformance = await _db.Ado.SqlQueryAsync<dynamic>($@"
6217 6224 SELECT
... ... @@ -6302,11 +6309,13 @@ namespace NCC.Extend
6302 6309 .Select(x => x.Id)
6303 6310 .ToListAsync();
6304 6311 }
6305   - else if (!string.IsNullOrWhiteSpace(store.Kjb))
  6312 + else if (target != null && !string.IsNullOrWhiteSpace(target.TechDepartment))
6306 6313 {
6307   - sameOrgStoreIds = await _db.Queryable<LqMdxxEntity>()
6308   - .Where(x => x.Kjb == store.Kjb && x.Zxzt == "开店" && x.Id != input.StoreId)
6309   - .Select(x => x.Id)
  6314 + // 科技部已废弃 lq_mdxx.kjb,按统计月份从门店目标表取同科技部门店(且开店)
  6315 + sameOrgStoreIds = await _db.Queryable<LqMdTargetEntity, LqMdxxEntity>((t, m) => t.StoreId == m.Id && m.Zxzt == "开店")
  6316 + .Where((t, m) => t.Month == statisticsMonth && t.TechDepartment == target.TechDepartment && t.StoreId != input.StoreId)
  6317 + .Select((t, m) => t.StoreId)
  6318 + .Distinct()
6310 6319 .ToListAsync();
6311 6320 }
6312 6321  
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqShareStatisticsTechDeptService.cs
... ... @@ -4,6 +4,7 @@ using NCC.Dependency;
4 4 using NCC.DynamicApiController;
5 5 using NCC.Extend.Entitys.Dto.LqShareStatisticsTechDept;
6 6 using NCC.Extend.Entitys.lq_share_statistics_tech_dept;
  7 +using NCC.Extend.Entitys.lq_md_target;
7 8 using NCC.Extend.Entitys.lq_mdxx;
8 9 using NCC.Extend.Entitys.lq_kd_pxmx;
9 10 using NCC.Extend.Entitys.lq_kd_kdjlb;
... ... @@ -88,7 +89,7 @@ namespace NCC.Extend
88 89 };
89 90  
90 91 // 计算各项数据
91   - await CalculateIncome(entity, deptName, startDate, endDate);
  92 + await CalculateIncome(entity, deptName, startDate, endDate, input.StatisticsMonth);
92 93 await CalculateCost(entity, deptName, startDate, endDate, input.StatisticsMonth);
93 94 CalculateProfit(entity);
94 95  
... ... @@ -175,7 +176,7 @@ namespace NCC.Extend
175 176 /// <summary>
176 177 /// 计算收入部分
177 178 /// </summary>
178   - private async Task CalculateIncome(LqShareStatisticsTechDeptEntity entity, string deptName, DateTime startDate, DateTime endDate)
  179 + private async Task CalculateIncome(LqShareStatisticsTechDeptEntity entity, string deptName, DateTime startDate, DateTime endDate, string statisticsMonth)
179 180 {
180 181 // 1. 通过组织名称找到组织ID
181 182 var organize = await _db.Queryable<OrganizeEntity>()
... ... @@ -188,10 +189,11 @@ namespace NCC.Extend
188 189 return;
189 190 }
190 191  
191   - // 2. 找到该科技部管辖的所有门店(通过组织ID)
192   - var stores = await _db.Queryable<LqMdxxEntity>()
193   - .Where(x => x.Kjb == organize.Id)
194   - .Select(x => x.Id)
  192 + // 2. 找到该科技部当月管辖的门店(lq_mdxx.kjb 已废弃,从 lq_md_target 按月份取)
  193 + var stores = await _db.Queryable<LqMdTargetEntity>()
  194 + .Where(x => x.Month == statisticsMonth && x.TechDepartment == organize.Id)
  195 + .Select(x => x.StoreId)
  196 + .Distinct()
195 197 .ToListAsync();
196 198  
197 199 if (stores.Count == 0)
... ... @@ -282,10 +284,11 @@ namespace NCC.Extend
282 284 return;
283 285 }
284 286  
285   - // 2. 找到该科技部管辖的所有门店(通过组织ID)
286   - var stores = await _db.Queryable<LqMdxxEntity>()
287   - .Where(x => x.Kjb == organize.Id)
288   - .Select(x => x.Id)
  287 + // 2. 找到该科技部当月管辖的门店(lq_mdxx.kjb 已废弃,从 lq_md_target 按月份取)
  288 + var stores = await _db.Queryable<LqMdTargetEntity>()
  289 + .Where(x => x.Month == statisticsMonth && x.TechDepartment == organize.Id)
  290 + .Select(x => x.StoreId)
  291 + .Distinct()
289 292 .ToListAsync();
290 293  
291 294 // 1. 成本-报销:筛选一级分类为"科技部费用"的申请,根据申请门店的归属区分一部/二部
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqTechGeneralManagerSalaryService.cs
... ... @@ -15,6 +15,7 @@ using NCC.Extend.Entitys.lq_kd_jksyj;
15 15 using NCC.Extend.Entitys.lq_kd_kdjlb;
16 16 using NCC.Extend.Entitys.lq_kd_pxmx;
17 17 using NCC.Extend.Entitys.lq_md_general_manager_lifeline;
  18 +using NCC.Extend.Entitys.lq_md_target;
18 19 using NCC.Extend.Entitys.lq_mdxx;
19 20 using NCC.Extend.Entitys.lq_tech_general_manager_salary_statistics;
20 21 using NCC.Extend.Entitys.lq_xmzl;
... ... @@ -273,35 +274,30 @@ namespace NCC.Extend
273 274 }
274 275  
275 276 // 1.3 获取科技部总经理归属信息(从lq_md_general_manager_lifeline表)
276   - // 通过门店的科技部组织ID(kjb字段)找到科技一部或科技二部管理的门店
277   - // 然后在lifeline表中找到这些门店的记录,这些记录对应的总经理就是科技部总经理
278   - var lifelineList = await _db.Queryable<LqMdGeneralManagerLifelineEntity, LqMdxxEntity>(
279   - (lifeline, store) => lifeline.StoreId == store.Id)
280   - .Where((lifeline, store) =>
281   - lifeline.Month == monthStr
282   - && techOrganizeIds.Contains(store.Kjb))
283   - .Select((lifeline, store) => lifeline)
  277 + // lq_mdxx.kjb 已废弃,按统计月份从 lq_md_target 取科技一部/二部管理的门店,再关联 lifeline
  278 + var techManagedStoreIdsForMonth = await _db.Queryable<LqMdTargetEntity>()
  279 + .Where(x => x.Month == monthStr && techOrganizeIds.Contains(x.TechDepartment))
  280 + .Select(x => x.StoreId)
  281 + .Distinct()
284 282 .ToListAsync();
285   -
286   - // 1.4 获取科技一部和科技二部管理的门店(通过门店的kjb字段)
287   - var techManagedStoreIds = await _db.Queryable<LqMdxxEntity>()
288   - .Where(x => techOrganizeIds.Contains(x.Kjb))
289   - .Select(x => x.Id)
  283 + var lifelineList = await _db.Queryable<LqMdGeneralManagerLifelineEntity>()
  284 + .Where(x => x.Month == monthStr && techManagedStoreIdsForMonth.Contains(x.StoreId))
290 285 .ToListAsync();
291 286  
292   - // 1.5 按科技部总经理ID分组,获取每个科技部总经理管理的门店
293   - // 科技部总经理管理的门店 = 科技一部/科技二部管理的所有门店(通过门店的kjb字段确定)
  287 + // 1.4 获取科技一部和科技二部当月管理的门店(从 lq_md_target)
  288 + var techManagedStoreIds = techManagedStoreIdsForMonth;
  289 +
  290 + // 1.5 按科技部总经理ID分组,获取每个科技部总经理当月管理的门店(从 lq_md_target)
294 291 var managerStoreDict = new Dictionary<string, List<string>>();
295 292 foreach (var managerUser in techGeneralManagerUserList)
296 293 {
297 294 var managerId = managerUser.Id;
298 295 var managerOrganizeId = managerUser.OrganizeId;
299 296  
300   - // 如果该总经理属于科技一部,则管理所有科技一部的门店
301   - // 如果该总经理属于科技二部,则管理所有科技二部的门店
302   - var managedStores = await _db.Queryable<LqMdxxEntity>()
303   - .Where(x => x.Kjb == managerOrganizeId)
304   - .Select(x => x.Id)
  297 + var managedStores = await _db.Queryable<LqMdTargetEntity>()
  298 + .Where(x => x.Month == monthStr && x.TechDepartment == managerOrganizeId)
  299 + .Select(x => x.StoreId)
  300 + .Distinct()
305 301 .ToListAsync();
306 302  
307 303 managerStoreDict[managerId] = managedStores;
... ... @@ -415,6 +411,15 @@ namespace NCC.Extend
415 411 }
416 412 }
417 413  
  414 + // 批量查询当月门店科技部归属(lq_md_target),避免循环内查询
  415 + var storeTechDeptList = await _db.Queryable<LqMdTargetEntity>()
  416 + .Where(x => x.Month == monthStr && allManagedStoreIds.Contains(x.StoreId))
  417 + .Select(x => new { x.StoreId, x.TechDepartment })
  418 + .ToListAsync();
  419 + var storeTechDeptDict = storeTechDeptList
  420 + .GroupBy(x => x.StoreId)
  421 + .ToDictionary(g => g.Key, g => g.First().TechDepartment);
  422 +
418 423 // 遍历所有门店,构建门店明细
419 424 foreach (var storeId in allManagedStoreIds)
420 425 {
... ... @@ -429,16 +434,14 @@ namespace NCC.Extend
429 434 _logger.LogInformation($"[科技部总经理工资计算] 门店ID: {storeId}, 开单Cell金额: {storeCellBilling}, 退卡Cell金额: {storeCellRefund}, Cell金额: {storeCellBilling - storeCellRefund}");
430 435 }
431 436  
432   - // 获取该门店属于哪些科技部总经理
433   - // 通过门店的kjb字段确定:如果门店的kjb等于科技一部的组织ID,则该门店属于科技一部总经理
  437 + // 获取该门店属于哪些科技部总经理(按当月 lq_md_target 的科技部归属)
434 438 var store = storeDict.ContainsKey(storeId) ? storeDict[storeId] : null;
435 439 var managersOfStore = new List<string>();
436   -
437   - if (store != null && !string.IsNullOrEmpty(store.Kjb))
  440 + var storeTechDept = storeTechDeptDict.ContainsKey(storeId) ? storeTechDeptDict[storeId] : null;
  441 + if (!string.IsNullOrEmpty(storeTechDept))
438 442 {
439   - // 找到组织ID等于门店kjb的科技部总经理
440 443 var managers = techGeneralManagerUserList
441   - .Where(x => x.OrganizeId == store.Kjb)
  444 + .Where(x => x.OrganizeId == storeTechDept)
442 445 .Select(x => x.Id)
443 446 .ToList();
444 447 managersOfStore.AddRange(managers);
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqZjlMdsmxszService.cs
... ... @@ -8,6 +8,7 @@ using NCC.Extend.Entitys.Dto.LqZjlMdsmxsz;
8 8 using NCC.Extend.Entitys.Entity.lq_zjl_mdsmxsz;
9 9 using NCC.Extend.Interfaces.LqZjlMdsmxsz;
10 10 using NCC.System.Entitys.Permission;
  11 +using NCC.Extend.Entitys.lq_md_target;
11 12 using NCC.Extend.Entitys.lq_mdxx;
12 13 using SqlSugar;
13 14 using System;
... ... @@ -263,10 +264,38 @@ namespace NCC.Extend
263 264 public async Task<dynamic> GetManagedStores(string userid)
264 265 {
265 266 var userOrganizeId = await _db.Queryable<UserEntity>().Where(x => x.Id == userid && x.DeleteMark == null).Select(x => x.OrganizeId).FirstAsync();
266   - // 根据组织权限获取门店
267   - var stores = await _db.Queryable<LqMdxxEntity>()
268   - .Where(s => s.Syb == userOrganizeId || s.Jyb == userOrganizeId || s.Kjb == userOrganizeId || s.Dxmb == userOrganizeId)
  267 + if (string.IsNullOrEmpty(userOrganizeId))
  268 + {
  269 + return new { list = new List<LqMdxxEntity>() };
  270 + }
  271 +
  272 + // 科技部(科技一部/科技二部)已废弃 lq_mdxx.kjb,按当月 lq_md_target 取管理门店
  273 + var techOrganizeIds = await _db.Queryable<OrganizeEntity>()
  274 + .Where(x => x.FullName != null && (x.FullName.Contains("科技一部") || x.FullName.Contains("科技二部")) && x.DeleteMark == null && x.EnabledMark == 1)
  275 + .Select(x => x.Id)
269 276 .ToListAsync();
  277 +
  278 + List<LqMdxxEntity> stores;
  279 + if (techOrganizeIds.Contains(userOrganizeId))
  280 + {
  281 + var monthStr = DateTime.Now.ToString("yyyyMM");
  282 + var storeIds = await _db.Queryable<LqMdTargetEntity>()
  283 + .Where(x => x.Month == monthStr && x.TechDepartment == userOrganizeId)
  284 + .Select(x => x.StoreId)
  285 + .Distinct()
  286 + .ToListAsync();
  287 + stores = storeIds.Any()
  288 + ? await _db.Queryable<LqMdxxEntity>().Where(s => storeIds.Contains(s.Id)).ToListAsync()
  289 + : new List<LqMdxxEntity>();
  290 + }
  291 + else
  292 + {
  293 + // 事业部、教育部、大项目部:按门店表当前归属(不再使用已废弃的 Kjb)
  294 + stores = await _db.Queryable<LqMdxxEntity>()
  295 + .Where(s => s.Syb == userOrganizeId || s.Jyb == userOrganizeId || s.Dxmb == userOrganizeId)
  296 + .ToListAsync();
  297 + }
  298 +
270 299 return new { list = stores };
271 300 }
272 301 #endregion
... ...
netcore/test-kjb-removal-apis.sh 0 → 100755
  1 +#!/bin/bash
  2 +# 测试废弃 kjb 后修改的接口是否返回正确
  3 +# 使用方式: ./test-kjb-removal-apis.sh [BASE_URL],默认 http://localhost:2011
  4 +
  5 +set -e
  6 +BASE_URL="${1:-http://localhost:2011}"
  7 +echo "=== 使用 API 地址: $BASE_URL ==="
  8 +
  9 +# 1. 登录获取 token
  10 +echo ""
  11 +echo ">>> 1. 登录获取 token..."
  12 +LOGIN_RESP=$(curl -s -X POST "$BASE_URL/api/oauth/Login" \
  13 + -H "Content-Type: application/x-www-form-urlencoded" \
  14 + -d "account=admin&password=e10adc3949ba59abbe56e057f20f883e")
  15 +TOKEN=$(echo "$LOGIN_RESP" | grep -o '"token":"[^"]*"' | sed 's/"token":"//;s/"$//')
  16 +if [ -z "$TOKEN" ]; then
  17 + echo "登录失败,响应: $LOGIN_RESP"
  18 + exit 1
  19 +fi
  20 +echo "登录成功,已获取 token"
  21 +
  22 +# 2. LqReport - get-store-base-info(空 storeId 校验 + 可选真实门店校验)
  23 +echo ""
  24 +echo ">>> 2. POST /api/Extend/LqReport/get-store-base-info (空 storeId 校验)"
  25 +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/Extend/LqReport/get-store-base-info" \
  26 + -H "Content-Type: application/json" \
  27 + -H "Authorization: $TOKEN" \
  28 + -d '{"storeId":"","statisticsMonth":"202601"}')
  29 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  30 +BODY=$(echo "$RESP" | sed '$d')
  31 +if [ "$HTTP_CODE" = "400" ] || echo "$BODY" | grep -q "门店ID不能为空"; then
  32 + echo " [预期] 未传 storeId 时返回 400 或提示门店ID不能为空: HTTP $HTTP_CODE"
  33 +else
  34 + echo " HTTP $HTTP_CODE (若为200且 body 含 code 非200 也为参数校验行为)"
  35 + echo "$BODY" | head -c 400
  36 + echo ""
  37 +fi
  38 +# 使用真实门店ID验证归属来自 lq_md_target(如 绿纤龙湖店)
  39 +if [ -n "${STORE_ID:-}" ]; then
  40 + echo ">>> 2b. get-store-base-info (真实 storeId=$STORE_ID)"
  41 + curl -s -X POST "$BASE_URL/api/Extend/LqReport/get-store-base-info" \
  42 + -H "Content-Type: application/json" -H "Authorization: $TOKEN" \
  43 + -d "{\"storeId\":\"$STORE_ID\",\"statisticsMonth\":\"202601\"}" | head -c 600
  44 + echo ""
  45 +fi
  46 +
  47 +# 3. LqReport get-store-data-analysis(同上,验证接口可调通)
  48 +echo ""
  49 +echo ">>> 3. POST /api/Extend/LqReport/get-store-data-analysis"
  50 +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/Extend/LqReport/get-store-data-analysis" \
  51 + -H "Content-Type: application/json" \
  52 + -H "Authorization: $TOKEN" \
  53 + -d '{"storeId":"","statisticsMonth":"202601"}')
  54 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  55 +BODY=$(echo "$RESP" | sed '$d')
  56 +if [ "$HTTP_CODE" = "400" ] || echo "$BODY" | grep -q "门店ID不能为空"; then
  57 + echo " [预期] 未传 storeId 时返回 400 或提示门店ID不能为空: HTTP $HTTP_CODE"
  58 +else
  59 + echo " HTTP $HTTP_CODE"
  60 + echo "$BODY" | head -c 500
  61 + echo ""
  62 +fi
  63 +
  64 +# 4. LqReport get-store-comparison-analysis
  65 +echo ""
  66 +echo ">>> 4. POST /api/Extend/LqReport/get-store-comparison-analysis"
  67 +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/Extend/LqReport/get-store-comparison-analysis" \
  68 + -H "Content-Type: application/json" \
  69 + -H "Authorization: $TOKEN" \
  70 + -d '{"storeId":"","statisticsMonth":"202601"}')
  71 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  72 +BODY=$(echo "$RESP" | sed '$d')
  73 +if [ "$HTTP_CODE" = "400" ] || echo "$BODY" | grep -q "门店ID不能为空"; then
  74 + echo " [预期] 未传 storeId 时返回 400 或提示门店ID不能为空: HTTP $HTTP_CODE"
  75 +else
  76 + echo " HTTP $HTTP_CODE"
  77 + echo "$BODY" | head -c 500
  78 + echo ""
  79 +fi
  80 +
  81 +# 5. LqTechGeneralManagerSalary - GET tech-general-manager
  82 +echo ""
  83 +echo ">>> 5. GET /api/Extend/LqTechGeneralManagerSalary/tech-general-manager (2026年1月)"
  84 +RESP=$(curl -s -w "\n%{http_code}" -X GET "$BASE_URL/api/Extend/LqTechGeneralManagerSalary/tech-general-manager?Year=2026&Month=1&currentPage=1&pageSize=10" \
  85 + -H "Authorization: $TOKEN")
  86 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  87 +BODY=$(echo "$RESP" | sed '$d')
  88 +echo " HTTP $HTTP_CODE"
  89 +echo "$BODY" | head -c 800
  90 +echo ""
  91 +
  92 +# 6. LqTechGeneralManagerSalary - POST calculate/tech-general-manager(仅触发计算,不校验结果)
  93 +echo ""
  94 +echo ">>> 6. POST /api/Extend/LqTechGeneralManagerSalary/calculate/tech-general-manager (year=2026, month=1)"
  95 +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/Extend/LqTechGeneralManagerSalary/calculate/tech-general-manager?year=2026&month=1" \
  96 + -H "Authorization: $TOKEN")
  97 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  98 +BODY=$(echo "$RESP" | sed '$d')
  99 +echo " HTTP $HTTP_CODE"
  100 +echo "$BODY" | head -c 400
  101 +echo ""
  102 +
  103 +# 7. LqShareStatisticsTechDept - generate
  104 +echo ""
  105 +echo ">>> 7. POST /api/Extend/LqShareStatisticsTechDept/generate (statisticsMonth=202601)"
  106 +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/Extend/LqShareStatisticsTechDept/generate" \
  107 + -H "Content-Type: application/json" \
  108 + -H "Authorization: $TOKEN" \
  109 + -d '{"statisticsMonth":"202601"}')
  110 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  111 +BODY=$(echo "$RESP" | sed '$d')
  112 +echo " HTTP $HTTP_CODE"
  113 +echo "$BODY"
  114 +echo ""
  115 +
  116 +# 8. LqZjlMdsmxsz - GET ManagedStores/{userid}(科技部总经理 userid 需从系统获取,这里用占位验证 404/200)
  117 +echo ""
  118 +echo ">>> 8. GET /api/Extend/LqZjlMdsmxsz/ManagedStores/{userid} (使用占位 userid 验证路由与逻辑)"
  119 +RESP=$(curl -s -w "\n%{http_code}" -X GET "$BASE_URL/api/Extend/LqZjlMdsmxsz/ManagedStores/000000000000000001" \
  120 + -H "Authorization: $TOKEN")
  121 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  122 +BODY=$(echo "$RESP" | sed '$d')
  123 +echo " HTTP $HTTP_CODE"
  124 +echo "$BODY" | head -c 500
  125 +echo ""
  126 +
  127 +# 9. LqMdTarget GetManagedStores(未改 kjb 逻辑,但可顺带验证科技部按 lq_md_target 返回)
  128 +echo ""
  129 +echo ">>> 9. POST /api/Extend/LqMdTarget/GetManagedStores (科技部+月份 202601,需有效 DepartmentId)"
  130 +RESP=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/Extend/LqMdTarget/GetManagedStores" \
  131 + -H "Content-Type: application/json" \
  132 + -H "Authorization: $TOKEN" \
  133 + -d '{"month":"202601","organizationType":"科技部","departmentId":""}')
  134 +HTTP_CODE=$(echo "$RESP" | tail -n1)
  135 +BODY=$(echo "$RESP" | sed '$d')
  136 +echo " HTTP $HTTP_CODE"
  137 +if echo "$BODY" | grep -q "部门ID不能为空"; then
  138 + echo " [预期] departmentId 为空时返回错误提示"
  139 +else
  140 + echo "$BODY" | head -c 500
  141 +fi
  142 +echo ""
  143 +
  144 +echo "=== 接口测试脚本执行完毕 ==="
... ...
sql/根据产品平均单价更新库存领取金额.sql 0 → 100644
  1 +-- ============================================================
  2 +-- 根据产品 F_AveragePrice 更新库存领取的单价、总价
  3 +-- 说明:领取记录在 lq_inventory_usage,单价 F_UnitPrice、总价 F_TotalAmount;
  4 +-- 单价取产品 F_AveragePrice,若为 0 或 NULL 则取 F_Price;总价 = 单价 × 领取数量。
  5 +-- 同时按批次汇总,回写 lq_inventory_usage_application 的 F_TotalAmount。
  6 +-- ============================================================
  7 +
  8 +-- 一、预览:将要更新的使用记录条数及金额变化(不修改数据)
  9 +SELECT
  10 + u.F_Id AS 使用记录ID,
  11 + u.F_ProductId AS 产品ID,
  12 + p.F_ProductName AS 产品名称,
  13 + u.F_UsageQuantity AS 领取数量,
  14 + u.F_UnitPrice AS 当前单价,
  15 + u.F_TotalAmount AS 当前总价,
  16 + IFNULL(NULLIF(p.F_AveragePrice, 0), p.F_Price) AS 新单价_取自产品,
  17 + IFNULL(NULLIF(p.F_AveragePrice, 0), p.F_Price) * u.F_UsageQuantity AS 新总价
  18 +FROM lq_inventory_usage u
  19 +INNER JOIN lq_product p ON u.F_ProductId = p.F_Id
  20 +WHERE u.F_IsEffective = 1
  21 + AND (u.F_UnitPrice != IFNULL(NULLIF(p.F_AveragePrice, 0), p.F_Price)
  22 + OR u.F_TotalAmount != IFNULL(NULLIF(p.F_AveragePrice, 0), p.F_Price) * u.F_UsageQuantity)
  23 +LIMIT 500;
  24 +
  25 +-- 二、更新库存使用记录:单价、总价(按产品 F_AveragePrice,否则 F_Price)
  26 +UPDATE lq_inventory_usage u
  27 +INNER JOIN lq_product p ON u.F_ProductId = p.F_Id
  28 +SET
  29 + u.F_UnitPrice = IFNULL(NULLIF(p.F_AveragePrice, 0), p.F_Price),
  30 + u.F_TotalAmount = IFNULL(NULLIF(p.F_AveragePrice, 0), p.F_Price) * u.F_UsageQuantity,
  31 + u.F_UpdateTime = NOW()
  32 +WHERE u.F_IsEffective = 1;
  33 +
  34 +-- 三、按批次汇总,更新申请表的 F_TotalAmount
  35 +UPDATE lq_inventory_usage_application a
  36 +INNER JOIN (
  37 + SELECT F_UsageBatchId, SUM(F_TotalAmount) AS batch_total
  38 + FROM lq_inventory_usage
  39 + WHERE F_IsEffective = 1
  40 + GROUP BY F_UsageBatchId
  41 +) t ON a.F_UsageBatchId = t.F_UsageBatchId
  42 +SET a.F_TotalAmount = t.batch_total
  43 +WHERE a.F_IsEffective = 1;
... ...