孟苏慧

Merge branch 'qa/2025-04-10' into 'master'

Qa/2025 04 10



See merge request !17
...@@ -83,9 +83,7 @@ public interface ConfigConstants { ...@@ -83,9 +83,7 @@ public interface ConfigConstants {
83 String MINI_PROGRAM_APP_SECRET = "${miniprogram.appSecret}"; 83 String MINI_PROGRAM_APP_SECRET = "${miniprogram.appSecret}";
84 String MINI_PROGRAM_ORDER_REMINDER_TEMPLATE_ID = "${miniprogram.orderReminderTemplateId}"; 84 String MINI_PROGRAM_ORDER_REMINDER_TEMPLATE_ID = "${miniprogram.orderReminderTemplateId}";
85 85
86 - // 公众号配置(用于订阅消息推送) 86 + // 公众号模板 ID(可选);appId/appSecret 见 WxOfficialAccountHttpClient 字段上的 @Value 默认值
87 - String OFFICIAL_ACCOUNT_APP_ID = "${officialAccount.appId}"; 87 + String OFFICIAL_ACCOUNT_ORDER_REMINDER_TEMPLATE_ID = "${officialAccount.orderReminderTemplateId:}";
88 - String OFFICIAL_ACCOUNT_APP_SECRET = "${officialAccount.appSecret}";
89 - String OFFICIAL_ACCOUNT_ORDER_REMINDER_TEMPLATE_ID = "${officialAccount.orderReminderTemplateId}";
90 88
91 } 89 }
......
...@@ -89,6 +89,10 @@ public class SettlementDTO { ...@@ -89,6 +89,10 @@ public class SettlementDTO {
89 public static class SettlementOrderDetail { 89 public static class SettlementOrderDetail {
90 private String mealTime; 90 private String mealTime;
91 private String code; 91 private String code;
92 + /** 支付完成时间,格式 yyyy-MM-dd HH:mm:ss,无则空 */
93 + private String payTime;
94 + /** 下单人:院区端为操作员名称;住院小程序为客户姓名;阳光厅为客户手机号 */
95 + private String orderer;
92 private String mealName; 96 private String mealName;
93 private String roomNo; 97 private String roomNo;
94 private String customerName; 98 private String customerName;
......
...@@ -2,12 +2,16 @@ package com.infoloop.tianting.service.client; ...@@ -2,12 +2,16 @@ package com.infoloop.tianting.service.client;
2 2
3 import com.infoloop.tianting.COperatorServiceProtoRpcGrpc; 3 import com.infoloop.tianting.COperatorServiceProtoRpcGrpc;
4 import com.infoloop.tianting.GetCOperatorByIdRpcRequest; 4 import com.infoloop.tianting.GetCOperatorByIdRpcRequest;
5 +import com.infoloop.tianting.GetCOperatorByIdsRpcRequest;
5 import com.infoloop.tianting.GetCOperatorByMobileOrEmailRpcRequest; 6 import com.infoloop.tianting.GetCOperatorByMobileOrEmailRpcRequest;
6 import com.infoloop.tianting.SingleCOperatorRpcResponse; 7 import com.infoloop.tianting.SingleCOperatorRpcResponse;
7 import lombok.RequiredArgsConstructor; 8 import lombok.RequiredArgsConstructor;
8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.beans.factory.annotation.Autowired;
9 import org.springframework.stereotype.Service; 10 import org.springframework.stereotype.Service;
10 11
12 +import java.util.Collections;
13 +import java.util.List;
14 +
11 @Service 15 @Service
12 @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 16 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
13 public class OperatorServiceRpcClient { 17 public class OperatorServiceRpcClient {
...@@ -26,5 +30,15 @@ public class OperatorServiceRpcClient { ...@@ -26,5 +30,15 @@ public class OperatorServiceRpcClient {
26 return cOperatorServiceProtoRpcBlockingStub.getCOperatorById(request).getResponse(); 30 return cOperatorServiceProtoRpcBlockingStub.getCOperatorById(request).getResponse();
27 } 31 }
28 32
33 + public List<SingleCOperatorRpcResponse> getCOperatorsByIds(List<Integer> ids) {
34 + if (ids == null || ids.isEmpty()) {
35 + return Collections.emptyList();
36 + }
37 + return cOperatorServiceProtoRpcBlockingStub.getCOperatorByIds(GetCOperatorByIdsRpcRequest.newBuilder()
38 + .addAllIds(ids)
39 + .setIncludeDeleted(false)
40 + .build())
41 + .getResponsesList();
42 + }
29 43
30 } 44 }
......
...@@ -720,7 +720,7 @@ public class OrderServiceRpcClient { ...@@ -720,7 +720,7 @@ public class OrderServiceRpcClient {
720 if (menuById.getResponse().getOrderRuleJson().getModeOfPayment() == ModeOfPaymentEnum.ONLINE) { 720 if (menuById.getResponse().getOrderRuleJson().getModeOfPayment() == ModeOfPaymentEnum.ONLINE) {
721 final var autoCancelParam = new AutoCancelParam(); 721 final var autoCancelParam = new AutoCancelParam();
722 autoCancelParam.setOrderId(orderResponse.getId()); 722 autoCancelParam.setOrderId(orderResponse.getId());
723 - delayedQueue.addToQueue(OrderAutoCancelDelayTask.AUTO_CANCEL_ORDER_DELAY_QUEUE, autoCancelParam, 10, TimeUnit.MINUTES); 723 + delayedQueue.addToQueue(OrderAutoCancelDelayTask.AUTO_CANCEL_ORDER_DELAY_QUEUE, autoCancelParam, 6, TimeUnit.HOURS);
724 } 724 }
725 } 725 }
726 return CreateOrderResponse.builder().id(orderResponse.getId()).isCreated(true).build(); 726 return CreateOrderResponse.builder().id(orderResponse.getId()).isCreated(true).build();
......
...@@ -2,12 +2,13 @@ package com.infoloop.tianting.service.client; ...@@ -2,12 +2,13 @@ package com.infoloop.tianting.service.client;
2 2
3 import com.infoloop.tianting.model.dto.WxUserDto.SendSubscriptionMessageResponse; 3 import com.infoloop.tianting.model.dto.WxUserDto.SendSubscriptionMessageResponse;
4 import com.infoloop.tianting.model.dto.WxUserDto.WxUserTokenDto; 4 import com.infoloop.tianting.model.dto.WxUserDto.WxUserTokenDto;
5 +import org.springframework.lang.Nullable;
5 import com.infoloop.tianting.utils.JsonUtil; 6 import com.infoloop.tianting.utils.JsonUtil;
6 import lombok.RequiredArgsConstructor; 7 import lombok.RequiredArgsConstructor;
7 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
8 import org.apache.commons.lang3.StringUtils; 9 import org.apache.commons.lang3.StringUtils;
9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.beans.factory.annotation.Autowired;
10 -import org.springframework.beans.factory.annotation.Value; 11 +import org.springframework.core.env.Environment;
11 import org.springframework.data.redis.core.StringRedisTemplate; 12 import org.springframework.data.redis.core.StringRedisTemplate;
12 import org.springframework.http.HttpEntity; 13 import org.springframework.http.HttpEntity;
13 import org.springframework.http.HttpHeaders; 14 import org.springframework.http.HttpHeaders;
...@@ -16,13 +17,12 @@ import org.springframework.http.ResponseEntity; ...@@ -16,13 +17,12 @@ import org.springframework.http.ResponseEntity;
16 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
17 import org.springframework.web.client.RestTemplate; 18 import org.springframework.web.client.RestTemplate;
18 19
20 +import javax.annotation.PostConstruct;
21 +
19 import java.util.HashMap; 22 import java.util.HashMap;
20 import java.util.Map; 23 import java.util.Map;
21 import java.util.concurrent.TimeUnit; 24 import java.util.concurrent.TimeUnit;
22 25
23 -import static com.infoloop.tianting.constant.ConfigConstants.OFFICIAL_ACCOUNT_APP_ID;
24 -import static com.infoloop.tianting.constant.ConfigConstants.OFFICIAL_ACCOUNT_APP_SECRET;
25 -
26 /** 26 /**
27 * 微信公众号 HTTP 客户端 27 * 微信公众号 HTTP 客户端
28 * 用于发送公众号一次性订阅消息 28 * 用于发送公众号一次性订阅消息
...@@ -42,19 +42,36 @@ public class WxOfficialAccountHttpClient { ...@@ -42,19 +42,36 @@ public class WxOfficialAccountHttpClient {
42 42
43 private final RestTemplate restTemplate; 43 private final RestTemplate restTemplate;
44 private final StringRedisTemplate stringRedisTemplate; 44 private final StringRedisTemplate stringRedisTemplate;
45 + private final Environment environment;
45 46
46 - @Value(OFFICIAL_ACCOUNT_APP_ID) 47 + /** 由 {@link #bindOfficialAccountConfig()} 从 Environment 读取;缺失配置项时为空白,不触发占位符解析 */
47 - private String appId; 48 + private String appId = "";
48 49
49 - @Value(OFFICIAL_ACCOUNT_APP_SECRET) 50 + private String appSecret = "";
50 - private String appSecret;
51 51
52 private final Object lock = new Object(); 52 private final Object lock = new Object();
53 53
54 + @PostConstruct
55 + void bindOfficialAccountConfig() {
56 + this.appId = StringUtils.trimToEmpty(environment.getProperty("officialAccount.appId"));
57 + this.appSecret = StringUtils.trimToEmpty(environment.getProperty("officialAccount.appSecret"));
58 + }
59 +
60 + private boolean isOfficialAccountConfigured() {
61 + return StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(appSecret);
62 + }
63 +
54 /** 64 /**
55 * 获取公众号 access_token(带缓存) 65 * 获取公众号 access_token(带缓存)
66 + *
67 + * @return token;未配置 appId/appSecret 时返回 null(不请求微信接口)
56 */ 68 */
69 + @Nullable
57 public String fetchStableAccessToken() { 70 public String fetchStableAccessToken() {
71 + if (!isOfficialAccountConfigured()) {
72 + log.warn("未配置 officialAccount.appId / officialAccount.appSecret,跳过获取公众号 access_token");
73 + return null;
74 + }
58 log.info("Fetching Official Account access token..."); 75 log.info("Fetching Official Account access token...");
59 String cachedToken = stringRedisTemplate.opsForValue().get(ACCESS_TOKEN_CACHE_KEY); 76 String cachedToken = stringRedisTemplate.opsForValue().get(ACCESS_TOKEN_CACHE_KEY);
60 if (StringUtils.isNotEmpty(cachedToken)) { 77 if (StringUtils.isNotEmpty(cachedToken)) {
...@@ -117,8 +134,15 @@ public class WxOfficialAccountHttpClient { ...@@ -117,8 +134,15 @@ public class WxOfficialAccountHttpClient {
117 Map<String, Object> data, 134 Map<String, Object> data,
118 String url, 135 String url,
119 Map<String, String> miniprogram) { 136 Map<String, String> miniprogram) {
137 + if (!isOfficialAccountConfigured()) {
138 + log.warn("未配置公众号,跳过发送订阅消息 openId={}", openId);
139 + return new SendSubscriptionMessageResponse(-1, "微信公众号未配置");
140 + }
120 try { 141 try {
121 String accessToken = fetchStableAccessToken(); 142 String accessToken = fetchStableAccessToken();
143 + if (accessToken == null) {
144 + return new SendSubscriptionMessageResponse(-1, "未获取到公众号 access_token");
145 + }
122 HttpHeaders headers = new HttpHeaders(); 146 HttpHeaders headers = new HttpHeaders();
123 headers.setContentType(MediaType.APPLICATION_JSON); 147 headers.setContentType(MediaType.APPLICATION_JSON);
124 148
......
...@@ -3,10 +3,12 @@ package com.infoloop.tianting.service.impl; ...@@ -3,10 +3,12 @@ package com.infoloop.tianting.service.impl;
3 import cn.hutool.core.collection.CollectionUtil; 3 import cn.hutool.core.collection.CollectionUtil;
4 import cn.hutool.core.date.LocalDateTimeUtil; 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 import com.fasterxml.jackson.core.type.TypeReference; 5 import com.fasterxml.jackson.core.type.TypeReference;
6 +import com.infoloop.tianting.SingleCOperatorRpcResponse;
6 import com.infoloop.tianting.SingleDishSkuByIdsRpcResponse; 7 import com.infoloop.tianting.SingleDishSkuByIdsRpcResponse;
7 import com.infoloop.tianting.clientcustomerorderservice.OnlinePayEnum; 8 import com.infoloop.tianting.clientcustomerorderservice.OnlinePayEnum;
8 import com.infoloop.tianting.clientcustomerorderservice.OrderOperationRecord; 9 import com.infoloop.tianting.clientcustomerorderservice.OrderOperationRecord;
9 import com.infoloop.tianting.clientcustomerorderservice.OrderOperationType; 10 import com.infoloop.tianting.clientcustomerorderservice.OrderOperationType;
11 +import com.infoloop.tianting.clientcustomerorderservice.OrderSourceEnum;
10 import com.infoloop.tianting.clientcustomerorderservice.OrderStatusEnum; 12 import com.infoloop.tianting.clientcustomerorderservice.OrderStatusEnum;
11 import com.infoloop.tianting.clientcustomerorderservice.PayStatusEnum; 13 import com.infoloop.tianting.clientcustomerorderservice.PayStatusEnum;
12 import com.infoloop.tianting.clientcustomerorderservice.ServeStatusEnum; 14 import com.infoloop.tianting.clientcustomerorderservice.ServeStatusEnum;
...@@ -39,6 +41,7 @@ import com.infoloop.tianting.service.client.ClientInventoryServiceRpcClient; ...@@ -39,6 +41,7 @@ import com.infoloop.tianting.service.client.ClientInventoryServiceRpcClient;
39 import com.infoloop.tianting.service.client.ClientResourceTagServiceRpcClient; 41 import com.infoloop.tianting.service.client.ClientResourceTagServiceRpcClient;
40 import com.infoloop.tianting.service.client.MealServiceRpcClient; 42 import com.infoloop.tianting.service.client.MealServiceRpcClient;
41 import com.infoloop.tianting.service.client.MenuServiceRpcClient; 43 import com.infoloop.tianting.service.client.MenuServiceRpcClient;
44 +import com.infoloop.tianting.service.client.OperatorServiceRpcClient;
42 import com.infoloop.tianting.service.client.OrderServiceRpcClient; 45 import com.infoloop.tianting.service.client.OrderServiceRpcClient;
43 import com.infoloop.tianting.service.client.SkuServiceRpcClient; 46 import com.infoloop.tianting.service.client.SkuServiceRpcClient;
44 import com.infoloop.tianting.setmealscheduleservice.SingleMealRpcResponse; 47 import com.infoloop.tianting.setmealscheduleservice.SingleMealRpcResponse;
...@@ -92,6 +95,7 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -92,6 +95,7 @@ public class SettlementServiceImpl implements SettlementService {
92 private final MealServiceRpcClient mealServiceRpcClient; 95 private final MealServiceRpcClient mealServiceRpcClient;
93 private final ClientCustomerServiceRpcClient clientCustomerServiceRpcClient; 96 private final ClientCustomerServiceRpcClient clientCustomerServiceRpcClient;
94 private final ClientResourceTagServiceRpcClient clientResourceTagServiceRpcClient; 97 private final ClientResourceTagServiceRpcClient clientResourceTagServiceRpcClient;
98 + private final OperatorServiceRpcClient operatorServiceRpcClient;
95 99
96 public static ByteArrayOutputStream writeToExcelBySku(SettlementDataDto settlementDataDto) 100 public static ByteArrayOutputStream writeToExcelBySku(SettlementDataDto settlementDataDto)
97 throws IOException { 101 throws IOException {
...@@ -347,6 +351,14 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -347,6 +351,14 @@ public class SettlementServiceImpl implements SettlementService {
347 cell1.setCellValue("订单号"); 351 cell1.setCellValue("订单号");
348 cell1.setCellStyle(headerStyle); 352 cell1.setCellStyle(headerStyle);
349 353
354 + Cell cellPayTime = titleRow.createCell(columnNum++);
355 + cellPayTime.setCellValue("支付时间");
356 + cellPayTime.setCellStyle(headerStyle);
357 +
358 + Cell cellOrderer = titleRow.createCell(columnNum++);
359 + cellOrderer.setCellValue("下单人");
360 + cellOrderer.setCellStyle(headerStyle);
361 +
350 Cell cell2 = titleRow.createCell(columnNum++); 362 Cell cell2 = titleRow.createCell(columnNum++);
351 cell2.setCellValue("餐别"); 363 cell2.setCellValue("餐别");
352 cell2.setCellStyle(headerStyle); 364 cell2.setCellStyle(headerStyle);
...@@ -422,6 +434,14 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -422,6 +434,14 @@ public class SettlementServiceImpl implements SettlementService {
422 dataCell1.setCellValue(orderDetail.getCode() != null ? orderDetail.getCode() : ""); 434 dataCell1.setCellValue(orderDetail.getCode() != null ? orderDetail.getCode() : "");
423 dataCell1.setCellStyle(dataStyle); 435 dataCell1.setCellStyle(dataStyle);
424 436
437 + Cell dataCellPayTime = dataRow.createCell(columnNum++);
438 + dataCellPayTime.setCellValue(orderDetail.getPayTime() != null ? orderDetail.getPayTime() : "");
439 + dataCellPayTime.setCellStyle(dataStyle);
440 +
441 + Cell dataCellOrderer = dataRow.createCell(columnNum++);
442 + dataCellOrderer.setCellValue(orderDetail.getOrderer() != null ? orderDetail.getOrderer() : "");
443 + dataCellOrderer.setCellStyle(dataStyle);
444 +
425 Cell dataCell2 = dataRow.createCell(columnNum++); 445 Cell dataCell2 = dataRow.createCell(columnNum++);
426 dataCell2.setCellValue(orderDetail.getMealName() != null ? orderDetail.getMealName() : ""); 446 dataCell2.setCellValue(orderDetail.getMealName() != null ? orderDetail.getMealName() : "");
427 dataCell2.setCellStyle(dataStyle); 447 dataCell2.setCellStyle(dataStyle);
...@@ -511,7 +531,7 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -511,7 +531,7 @@ public class SettlementServiceImpl implements SettlementService {
511 } 531 }
512 532
513 // 自动调整列宽 533 // 自动调整列宽
514 - for (int i = 0; i < 17; i++) { 534 + for (int i = 0; i < 19; i++) {
515 sheet.autoSizeColumn(i); 535 sheet.autoSizeColumn(i);
516 } 536 }
517 537
...@@ -838,6 +858,16 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -838,6 +858,16 @@ public class SettlementServiceImpl implements SettlementService {
838 final List<SingleClientCustomerRpcResponse> customers = customerResponse.getResponsesList(); 858 final List<SingleClientCustomerRpcResponse> customers = customerResponse.getResponsesList();
839 final var customerMap = customers.stream().collect(Collectors.toMap(SingleClientCustomerRpcResponse::getId, c -> c, (c1, c2) -> c1)); 859 final var customerMap = customers.stream().collect(Collectors.toMap(SingleClientCustomerRpcResponse::getId, c -> c, (c1, c2) -> c1));
840 860
861 + final List<Integer> stallOperatorIds = orderIds.stream()
862 + .map(orderMap::get)
863 + .filter(Objects::nonNull)
864 + .filter(o -> o.getCreationSource() == OrderSourceEnum.STALL)
865 + .map(SingleClientCustomerOrderRpcResponse::getCreatedBy)
866 + .filter(id -> id > 0)
867 + .distinct()
868 + .collect(Collectors.toList());
869 + final Map<Integer, String> operatorNameById = buildStallOperatorDisplayNameMap(stallOperatorIds);
870 +
841 // 构建结算订单明细 871 // 构建结算订单明细
842 final var classifyOrders = new ArrayList<SettlementDTO.SettlementOrderDetail>(); 872 final var classifyOrders = new ArrayList<SettlementDTO.SettlementOrderDetail>();
843 for (Integer orderId : orderIds) { 873 for (Integer orderId : orderIds) {
...@@ -854,6 +884,8 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -854,6 +884,8 @@ public class SettlementServiceImpl implements SettlementService {
854 final var customer = customerMap.get(order.getCustomerId()); 884 final var customer = customerMap.get(order.getCustomerId());
855 final String customerName = customer != null ? customer.getName() : ""; 885 final String customerName = customer != null ? customer.getName() : "";
856 final String hisCustomerNo = customer != null ? customer.getHISCustomerNo() : ""; 886 final String hisCustomerNo = customer != null ? customer.getHISCustomerNo() : "";
887 + final String payTimeStr = formatSettlementPayTime(order.getPayTime());
888 + final String orderer = resolveSettlementOrderer(order, customer, operatorNameById);
857 889
858 final var orderOperationRecords = orderOperationRecordByOrderId.getOrDefault(orderId, Collections.emptyList()); 890 final var orderOperationRecords = orderOperationRecordByOrderId.getOrDefault(orderId, Collections.emptyList());
859 891
...@@ -881,10 +913,10 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -881,10 +913,10 @@ public class SettlementServiceImpl implements SettlementService {
881 && addOriginalDetailMap.get(orderDetail.getId()).getCount() != orderDetail.getCount()) { 913 && addOriginalDetailMap.get(orderDetail.getId()).getCount() != orderDetail.getCount()) {
882 final var orderDetailBO = addOriginalDetailMap.get(orderDetail.getId()); 914 final var orderDetailBO = addOriginalDetailMap.get(orderDetail.getId());
883 SettlementDTO.SettlementOrderDetail beforeChange = buildSettlementOrderDetail( 915 SettlementDTO.SettlementOrderDetail beforeChange = buildSettlementOrderDetail(
884 - order, orderDetail.getMenuDetailId(), orderDetailBO.getSkuId(), orderDetailBO.getCount(), orderDetail.getPrice(), "订餐", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 916 + order, orderDetail.getMenuDetailId(), orderDetailBO.getSkuId(), orderDetailBO.getCount(), orderDetail.getPrice(), "订餐", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
885 classifyOrders.add(beforeChange); 917 classifyOrders.add(beforeChange);
886 SettlementDTO.SettlementOrderDetail afterChange = buildSettlementOrderDetail( 918 SettlementDTO.SettlementOrderDetail afterChange = buildSettlementOrderDetail(
887 - order, orderDetail.getMenuDetailId(), orderDetailBO.getSkuId(), orderDetail.getCount() - orderDetailBO.getCount(), orderDetail.getPrice(), "加餐", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 919 + order, orderDetail.getMenuDetailId(), orderDetailBO.getSkuId(), orderDetail.getCount() - orderDetailBO.getCount(), orderDetail.getPrice(), "加餐", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
888 classifyOrders.add(afterChange); 920 classifyOrders.add(afterChange);
889 } 921 }
890 922
...@@ -894,20 +926,20 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -894,20 +926,20 @@ public class SettlementServiceImpl implements SettlementService {
894 final var orderDetailBO = replaceOriginalDetailMap.get(orderDetail.getId()); 926 final var orderDetailBO = replaceOriginalDetailMap.get(orderDetail.getId());
895 final var orderDetailAdjustBO = replaceAdjustDetailMap.get(orderDetail.getId()); 927 final var orderDetailAdjustBO = replaceAdjustDetailMap.get(orderDetail.getId());
896 SettlementDTO.SettlementOrderDetail beforeChange = buildSettlementOrderDetail( 928 SettlementDTO.SettlementOrderDetail beforeChange = buildSettlementOrderDetail(
897 - order, orderDetail.getMenuDetailId(), orderDetailBO.getSkuId(), orderDetailBO.getCount(), orderDetailBO.getPrice(), "换菜前", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 929 + order, orderDetail.getMenuDetailId(), orderDetailBO.getSkuId(), orderDetailBO.getCount(), orderDetailBO.getPrice(), "换菜前", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
898 classifyOrders.add(beforeChange); 930 classifyOrders.add(beforeChange);
899 SettlementDTO.SettlementOrderDetail afterChange = buildSettlementOrderDetail( 931 SettlementDTO.SettlementOrderDetail afterChange = buildSettlementOrderDetail(
900 - order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), orderDetail.getCount(), orderDetailAdjustBO.getPrice(), "换菜后", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 932 + order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), orderDetail.getCount(), orderDetailAdjustBO.getPrice(), "换菜后", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
901 classifyOrders.add(afterChange); 933 classifyOrders.add(afterChange);
902 } 934 }
903 935
904 // 3. 判断已退款 936 // 3. 判断已退款
905 if (orderDetail.getServeStatus() == ServeStatusEnum.SERVE_REJECT) { 937 if (orderDetail.getServeStatus() == ServeStatusEnum.SERVE_REJECT) {
906 SettlementDTO.SettlementOrderDetail refundRecord1 = buildSettlementOrderDetail( 938 SettlementDTO.SettlementOrderDetail refundRecord1 = buildSettlementOrderDetail(
907 - order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), orderDetail.getCount(), orderDetail.getPrice(), "订餐", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 939 + order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), orderDetail.getCount(), orderDetail.getPrice(), "订餐", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
908 classifyOrders.add(refundRecord1); 940 classifyOrders.add(refundRecord1);
909 SettlementDTO.SettlementOrderDetail refundRecord2 = buildSettlementOrderDetail( 941 SettlementDTO.SettlementOrderDetail refundRecord2 = buildSettlementOrderDetail(
910 - order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), -orderDetail.getCount(), orderDetail.getPrice(), "已退款", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 942 + order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), -orderDetail.getCount(), orderDetail.getPrice(), "已退款", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
911 classifyOrders.add(refundRecord2); 943 classifyOrders.add(refundRecord2);
912 } 944 }
913 945
...@@ -920,7 +952,7 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -920,7 +952,7 @@ public class SettlementServiceImpl implements SettlementService {
920 952
921 if (isNormal) { 953 if (isNormal) {
922 SettlementDTO.SettlementOrderDetail normalRecord = buildSettlementOrderDetail( 954 SettlementDTO.SettlementOrderDetail normalRecord = buildSettlementOrderDetail(
923 - order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), orderDetail.getCount(), orderDetail.getPrice(), "订餐", menuName, customerName, hisCustomerNo, menuDetailMap, mealMap, skuMap); 955 + order, orderDetail.getMenuDetailId(), orderDetail.getSkuId(), orderDetail.getCount(), orderDetail.getPrice(), "订餐", menuName, customerName, hisCustomerNo, payTimeStr, orderer, menuDetailMap, mealMap, skuMap);
924 classifyOrders.add(normalRecord); 956 classifyOrders.add(normalRecord);
925 } 957 }
926 } 958 }
...@@ -950,6 +982,8 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -950,6 +982,8 @@ public class SettlementServiceImpl implements SettlementService {
950 String menuName, 982 String menuName,
951 String customerName, 983 String customerName,
952 String hisCustomerNo, 984 String hisCustomerNo,
985 + String payTimeStr,
986 + String orderer,
953 Map<Integer, SingleMenuDetailRpcResponse> menuDetailMap, 987 Map<Integer, SingleMenuDetailRpcResponse> menuDetailMap,
954 Map<Integer, SingleMealRpcResponse> mealMap, 988 Map<Integer, SingleMealRpcResponse> mealMap,
955 Map<Integer, SingleDishSkuByIdsRpcResponse> skuMap) { 989 Map<Integer, SingleDishSkuByIdsRpcResponse> skuMap) {
...@@ -962,6 +996,8 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -962,6 +996,8 @@ public class SettlementServiceImpl implements SettlementService {
962 return SettlementDTO.SettlementOrderDetail.builder() 996 return SettlementDTO.SettlementOrderDetail.builder()
963 .mealTime(order.getMealTime() != 0L ? LocalDateTimeUtil.of(order.getMealTime()).format(DateUtil.YYYY_MM_DD_LEFT_SYMBOL) : "") 997 .mealTime(order.getMealTime() != 0L ? LocalDateTimeUtil.of(order.getMealTime()).format(DateUtil.YYYY_MM_DD_LEFT_SYMBOL) : "")
964 .code(order.getOrderCode()) 998 .code(order.getOrderCode())
999 + .payTime(payTimeStr != null ? payTimeStr : "")
1000 + .orderer(orderer != null ? orderer : "")
965 .mealName(mealName) 1001 .mealName(mealName)
966 .roomNo(roomNo) 1002 .roomNo(roomNo)
967 .customerName(customerName) 1003 .customerName(customerName)
...@@ -1067,6 +1103,69 @@ public class SettlementServiceImpl implements SettlementService { ...@@ -1067,6 +1103,69 @@ public class SettlementServiceImpl implements SettlementService {
1067 } 1103 }
1068 } 1104 }
1069 1105
1106 + private Map<Integer, String> buildStallOperatorDisplayNameMap(List<Integer> operatorIds) {
1107 + if (operatorIds == null || operatorIds.isEmpty()) {
1108 + return Collections.emptyMap();
1109 + }
1110 + final List<SingleCOperatorRpcResponse> operators = operatorServiceRpcClient.getCOperatorsByIds(operatorIds);
1111 + final Map<Integer, String> map = new HashMap<>();
1112 + for (SingleCOperatorRpcResponse op : operators) {
1113 + if (op != null && op.getId() != 0) {
1114 + map.put(op.getId(), stallOperatorDisplayName(op));
1115 + }
1116 + }
1117 + return map;
1118 + }
1119 +
1120 + private static String stallOperatorDisplayName(SingleCOperatorRpcResponse op) {
1121 + if (op == null) {
1122 + return "";
1123 + }
1124 + if (StringUtils.isNotBlank(op.getAlias())) {
1125 + return op.getAlias();
1126 + }
1127 + if (StringUtils.isNotBlank(op.getName())) {
1128 + return op.getName();
1129 + }
1130 + return "";
1131 + }
1132 +
1133 + private static String formatSettlementPayTime(long payTimeMillis) {
1134 + if (payTimeMillis == 0L) {
1135 + return "";
1136 + }
1137 + final String formatted = DateUtil.formatDate(payTimeMillis, DateUtil.Y_M_D_H_M_S);
1138 + return formatted != null ? formatted : "";
1139 + }
1140 +
1141 + /**
1142 + * 院区端(STALL):操作员名称;住院部小程序(MINI_PROGRAM):客户姓名;阳光厅(MIDDLE_PLATFORM):客户手机号。
1143 + */
1144 + private static String resolveSettlementOrderer(SingleClientCustomerOrderRpcResponse order,
1145 + SingleClientCustomerRpcResponse customer,
1146 + Map<Integer, String> stallOperatorNameById) {
1147 + if (order == null) {
1148 + return "";
1149 + }
1150 + return switch (order.getCreationSource()) {
1151 + case STALL -> {
1152 + int createdBy = order.getCreatedBy();
1153 + if (createdBy <= 0 || stallOperatorNameById == null) {
1154 + yield "";
1155 + }
1156 + String name = stallOperatorNameById.get(createdBy);
1157 + yield name != null ? name : "";
1158 + }
1159 + case MINI_PROGRAM -> customer != null && StringUtils.isNotBlank(customer.getName())
1160 + ? customer.getName()
1161 + : "";
1162 + case MIDDLE_PLATFORM -> customer != null && StringUtils.isNotBlank(customer.getMobile())
1163 + ? customer.getMobile()
1164 + : order.getOpenId();
1165 + default -> "";
1166 + };
1167 + }
1168 +
1070 /** 1169 /**
1071 * 计算单价 1170 * 计算单价
1072 */ 1171 */
......