zhuyifan

feat(order): add support for partial order refund and not serve status

...@@ -60,4 +60,5 @@ public class PayController { ...@@ -60,4 +60,5 @@ public class PayController {
60 public String cancel(@Valid @RequestBody PayDTO.PayRefundRequestDto payRefundRequestDto) { 60 public String cancel(@Valid @RequestBody PayDTO.PayRefundRequestDto payRefundRequestDto) {
61 return payServiceClient.orderPayRefund(payRefundRequestDto); 61 return payServiceClient.orderPayRefund(payRefundRequestDto);
62 } 62 }
63 +
63 } 64 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -9,6 +9,8 @@ import lombok.Builder; ...@@ -9,6 +9,8 @@ import lombok.Builder;
9 import lombok.Data; 9 import lombok.Data;
10 import lombok.NoArgsConstructor; 10 import lombok.NoArgsConstructor;
11 11
12 +import java.util.List;
13 +
12 @Data 14 @Data
13 @ApiModel(description = "支付DTO") 15 @ApiModel(description = "支付DTO")
14 public class PayDTO { 16 public class PayDTO {
...@@ -31,6 +33,7 @@ public class PayDTO { ...@@ -31,6 +33,7 @@ public class PayDTO {
31 @AllArgsConstructor 33 @AllArgsConstructor
32 public static class PayRefundRequestDto { 34 public static class PayRefundRequestDto {
33 private Integer orderId; 35 private Integer orderId;
36 + private List<Integer> orderDetailIds;
34 private String hospitalCode; 37 private String hospitalCode;
35 } 38 }
36 39
......
...@@ -240,6 +240,7 @@ public class OrderServiceRpcClient { ...@@ -240,6 +240,7 @@ public class OrderServiceRpcClient {
240 final var operator = operatorServiceRpcClient.getCOperatorById(operatorLoginInfo.getId()); 240 final var operator = operatorServiceRpcClient.getCOperatorById(operatorLoginInfo.getId());
241 final var idsOfNeedUpdateMenuDetails = new ArrayList<Integer>(); 241 final var idsOfNeedUpdateMenuDetails = new ArrayList<Integer>();
242 final var idsOfAddCount = new ArrayList<Integer>(); 242 final var idsOfAddCount = new ArrayList<Integer>();
243 + final var idsOfNoReject = new ArrayList<Integer>();
243 List<OrderDetailDto> originalOrderDetails = new ArrayList<>(); 244 List<OrderDetailDto> originalOrderDetails = new ArrayList<>();
244 List<OrderDetailDto> newOrderDetails; 245 List<OrderDetailDto> newOrderDetails;
245 final var modifications = orderDetailBatchUpdateDto.getDetails().stream().map(d -> { 246 final var modifications = orderDetailBatchUpdateDto.getDetails().stream().map(d -> {
...@@ -280,9 +281,11 @@ public class OrderServiceRpcClient { ...@@ -280,9 +281,11 @@ public class OrderServiceRpcClient {
280 .build(); 281 .build();
281 } 282 }
282 } 283 }
283 -
284 ServeStatusEnum serveStatus = ServeStatusEnum.UNKNOWN_SERVE_STATUS; 284 ServeStatusEnum serveStatus = ServeStatusEnum.UNKNOWN_SERVE_STATUS;
285 if (d.getShouldUpdateServeStatus()) { 285 if (d.getShouldUpdateServeStatus()) {
286 + if (d.getServeStatus() == ServeStatusEnum.SERVE_REJECT) {
287 + idsOfNoReject.add(d.getId());
288 + }
286 serveStatus = d.getServeStatus(); 289 serveStatus = d.getServeStatus();
287 } 290 }
288 Integer count = 0; 291 Integer count = 0;
...@@ -319,7 +322,8 @@ public class OrderServiceRpcClient { ...@@ -319,7 +322,8 @@ public class OrderServiceRpcClient {
319 .build(); 322 .build();
320 final var totalIds = new ArrayList<>(idsOfNeedUpdateMenuDetails); 323 final var totalIds = new ArrayList<>(idsOfNeedUpdateMenuDetails);
321 totalIds.addAll(idsOfAddCount); 324 totalIds.addAll(idsOfAddCount);
322 - if (!idsOfNeedUpdateMenuDetails.isEmpty() || !idsOfAddCount.isEmpty()) { 325 + totalIds.addAll(idsOfNoReject);
326 + if (!totalIds.isEmpty()) {
323 final var orgionalResponseList = getClientCustomerOrderDetailsByIds(totalIds); 327 final var orgionalResponseList = getClientCustomerOrderDetailsByIds(totalIds);
324 originalOrderDetails = orgionalResponseList.stream().map(d -> OrderDetailDto.builder() 328 originalOrderDetails = orgionalResponseList.stream().map(d -> OrderDetailDto.builder()
325 .id(d.getId()) 329 .id(d.getId())
...@@ -357,7 +361,7 @@ public class OrderServiceRpcClient { ...@@ -357,7 +361,7 @@ public class OrderServiceRpcClient {
357 } 361 }
358 final var res = orderServiceRpcBlockingStub.batchUpdateClientCustomerOrderDetails(request); 362 final var res = orderServiceRpcBlockingStub.batchUpdateClientCustomerOrderDetails(request);
359 if (res.getIsUpdated()) { 363 if (res.getIsUpdated()) {
360 - if (!idsOfNeedUpdateMenuDetails.isEmpty() || !idsOfAddCount.isEmpty()) { 364 + if (!totalIds.isEmpty()) {
361 final var newResponseList = getClientCustomerOrderDetailsByIds(totalIds); 365 final var newResponseList = getClientCustomerOrderDetailsByIds(totalIds);
362 newOrderDetails = newResponseList.stream().map(d -> 366 newOrderDetails = newResponseList.stream().map(d ->
363 OrderDetailDto.builder() 367 OrderDetailDto.builder()
...@@ -396,7 +400,7 @@ public class OrderServiceRpcClient { ...@@ -396,7 +400,7 @@ public class OrderServiceRpcClient {
396 // 替换菜品的操作记录 400 // 替换菜品的操作记录
397 List<SingleOperationDto> operations = new ArrayList<>(); 401 List<SingleOperationDto> operations = new ArrayList<>();
398 for (OrderDetailDto originalDetail : originalOrderDetails) { 402 for (OrderDetailDto originalDetail : originalOrderDetails) {
399 - if (!idsOfNeedUpdateMenuDetails.contains(originalDetail.getId()) && !idsOfAddCount.contains(originalDetail.getId())) { 403 + if (!totalIds.contains(originalDetail.getId())) {
400 continue; 404 continue;
401 } 405 }
402 OrderOperationType orderOperationType = OrderOperationType.UNKNOWN_OPERATION_TYPE; 406 OrderOperationType orderOperationType = OrderOperationType.UNKNOWN_OPERATION_TYPE;
...@@ -404,6 +408,8 @@ public class OrderServiceRpcClient { ...@@ -404,6 +408,8 @@ public class OrderServiceRpcClient {
404 orderOperationType = OrderOperationType.REPLACE_DISHES; 408 orderOperationType = OrderOperationType.REPLACE_DISHES;
405 } else if (idsOfAddCount.contains(originalDetail.getId())) { 409 } else if (idsOfAddCount.contains(originalDetail.getId())) {
406 orderOperationType = OrderOperationType.ADD_DISHES; 410 orderOperationType = OrderOperationType.ADD_DISHES;
411 + } else if (idsOfNoReject.contains(originalDetail.getId())) {
412 + orderOperationType = OrderOperationType.NOT_SERVE;
407 } 413 }
408 String originalDetailStr; 414 String originalDetailStr;
409 String newDetailStr = ""; 415 String newDetailStr = "";
......
...@@ -38,6 +38,7 @@ import lombok.RequiredArgsConstructor; ...@@ -38,6 +38,7 @@ import lombok.RequiredArgsConstructor;
38 import lombok.extern.slf4j.Slf4j; 38 import lombok.extern.slf4j.Slf4j;
39 import org.apache.commons.codec.binary.Base64; 39 import org.apache.commons.codec.binary.Base64;
40 import org.apache.commons.codec.digest.DigestUtils; 40 import org.apache.commons.codec.digest.DigestUtils;
41 +import org.apache.commons.collections4.CollectionUtils;
41 import org.springframework.beans.factory.annotation.Autowired; 42 import org.springframework.beans.factory.annotation.Autowired;
42 import org.springframework.http.HttpEntity; 43 import org.springframework.http.HttpEntity;
43 import org.springframework.http.HttpHeaders; 44 import org.springframework.http.HttpHeaders;
...@@ -66,333 +67,338 @@ import java.util.stream.Collectors; ...@@ -66,333 +67,338 @@ import java.util.stream.Collectors;
66 @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 67 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
67 public class PayServiceClient { 68 public class PayServiceClient {
68 69
69 - private final RestTemplate restTemplate; 70 + private final RestTemplate restTemplate;
70 71
71 - private final BusinessConfig businessConfig; 72 + private final BusinessConfig businessConfig;
72 73
73 - private final OrderServiceRpcClient orderServiceRpcClient; 74 + private final OrderServiceRpcClient orderServiceRpcClient;
74 75
75 - private final ClientResourceTagServiceRpcClient clientResourceTagServiceRpcClient; 76 + private final ClientResourceTagServiceRpcClient clientResourceTagServiceRpcClient;
76 77
77 - private final EnterpriseResourceServiceRpcClient enterpriseResourceServiceRpcClient; 78 + private final EnterpriseResourceServiceRpcClient enterpriseResourceServiceRpcClient;
78 79
79 - private final XmlMapper xmlMapper = new XmlMapper(); 80 + private final XmlMapper xmlMapper = new XmlMapper();
80 81
81 - public PayResponseDto payInform(PayInformRequestDto payRequestDto) { 82 + public PayResponseDto payInform(PayInformRequestDto payRequestDto) {
82 - final var orderById = 83 + final var orderById =
83 - orderServiceRpcClient.getOrderById(Integer.valueOf(payRequestDto.getOrderId())); 84 + orderServiceRpcClient.getOrderById(Integer.valueOf(payRequestDto.getOrderId()));
84 - if (orderById == null 85 + if (orderById == null
85 - || orderById.getPayStatus() == PayStatusEnum.PAY_CANCELED 86 + || orderById.getPayStatus() == PayStatusEnum.PAY_CANCELED
86 - || orderById.getStatus() == OrderStatusEnum.ORDER_CANCELED) { 87 + || orderById.getStatus() == OrderStatusEnum.ORDER_CANCELED) {
87 - log.info("Order {} not found or canceled, skipping.", payRequestDto.getOrderId()); 88 + log.info("Order {} not found or canceled, skipping.", payRequestDto.getOrderId());
88 - throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_CANCEL); 89 + throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_CANCEL);
89 - } 90 + }
90 - String url = MessageFormat.format(businessConfig.getPayUrl(), businessConfig.getPayClientId()); 91 + String url = MessageFormat.format(businessConfig.getPayUrl(), businessConfig.getPayClientId());
91 - String requestDate = DateUtil.formatDate(LocalDateTime.now(), DateUtil.YMDHMS); 92 + String requestDate = DateUtil.formatDate(LocalDateTime.now(), DateUtil.YMDHMS);
92 - ThirdPartyPayInformRequestDto jsonData = buildRequestData(payRequestDto, requestDate); 93 + ThirdPartyPayInformRequestDto jsonData = buildRequestData(payRequestDto, requestDate);
93 - String jsonDataString; 94 + String jsonDataString;
94 - try { 95 + try {
95 - ObjectMapper objectMapper = new ObjectMapper(); 96 + ObjectMapper objectMapper = new ObjectMapper();
96 - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 97 + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
97 - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 98 + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
98 - objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.UpperCamelCaseStrategy()); 99 + objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.UpperCamelCaseStrategy());
99 - jsonDataString = objectMapper.writeValueAsString(jsonData); 100 + jsonDataString = objectMapper.writeValueAsString(jsonData);
100 - } catch (JsonProcessingException e) { 101 + } catch (JsonProcessingException e) {
101 - log.error("Failed to serialize request data", e); 102 + log.error("Failed to serialize request data", e);
102 - throw new IllegalArgumentException("支付信息获取失败: JSON 解析错误"); 103 + throw new IllegalArgumentException("支付信息获取失败: JSON 解析错误");
104 + }
105 + HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders());
106 + log.info("payInform request: {}", jsonDataString);
107 + ResponseEntity<String> responseEntity =
108 + restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
109 + return processResponse(responseEntity);
103 } 110 }
104 - HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders());
105 - log.info("payInform request: {}", jsonDataString);
106 - ResponseEntity<String> responseEntity =
107 - restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
108 - return processResponse(responseEntity);
109 - }
110 -
111 - private ThirdPartyPayInformRequestDto buildRequestData(
112 - PayInformRequestDto payRequestDto, String requestDate) {
113 - ThirdPartyPayInformRequestDto jsonData = new ThirdPartyPayInformRequestDto();
114 - jsonData.setClientId(businessConfig.getPayClientId());
115 - jsonData.setTradeId(payRequestDto.getOrderId());
116 - jsonData.setRequestDate(requestDate);
117 - jsonData.setRequestSource("OFWXApplet");
118 - jsonData.setPayerOpenId(LoginContextHolder.getOpenId());
119 - jsonData.setHospitalCode(payRequestDto.getHospitalCode());
120 - jsonData.setPID(LoginContextHolder.getOpenId());
121 - jsonData.setRemark("");
122 - jsonData.setTransAmount(payRequestDto.getPrice());
123 - jsonData.setTradeDesc("");
124 - jsonData.setSign(
125 - EncryptionUtil.sha1Encryption(
126 - businessConfig.getPayClientId()
127 - + payRequestDto.getOrderId()
128 - + requestDate
129 - + businessConfig.getPayClientId()));
130 - return jsonData;
131 - }
132 111
133 - private HttpHeaders createHeaders() { 112 + private ThirdPartyPayInformRequestDto buildRequestData(
134 - HttpHeaders headers = new HttpHeaders(); 113 + PayInformRequestDto payRequestDto, String requestDate) {
135 - headers.setContentType(MediaType.APPLICATION_JSON); 114 + ThirdPartyPayInformRequestDto jsonData = new ThirdPartyPayInformRequestDto();
136 - return headers; 115 + jsonData.setClientId(businessConfig.getPayClientId());
137 - } 116 + jsonData.setTradeId(payRequestDto.getOrderId());
138 - 117 + jsonData.setRequestDate(requestDate);
139 - private PayResponseDto processResponse(ResponseEntity<String> responseEntity) { 118 + jsonData.setRequestSource("OFWXApplet");
140 - if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) { 119 + jsonData.setPayerOpenId(LoginContextHolder.getOpenId());
141 - log.error("payInform failed with response: {}", responseEntity.getBody()); 120 + jsonData.setHospitalCode(payRequestDto.getHospitalCode());
142 - throw new IllegalArgumentException("支付信息获取失败"); 121 + jsonData.setPID(LoginContextHolder.getOpenId());
143 - } 122 + jsonData.setRemark("");
144 - try { 123 + jsonData.setTransAmount(payRequestDto.getPrice());
145 - log.info("processResponse response: {}", responseEntity.getBody()); 124 + jsonData.setTradeDesc("");
146 - return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class); 125 + jsonData.setSign(
147 - } catch (Exception e) { 126 + EncryptionUtil.sha1Encryption(
148 - log.error("Failed to parse XML response", e); 127 + businessConfig.getPayClientId()
149 - throw new RuntimeException("支付信息结果解析失败", e); 128 + + payRequestDto.getOrderId()
129 + + requestDate
130 + + businessConfig.getPayClientId()));
131 + return jsonData;
150 } 132 }
151 - }
152 133
153 - public boolean callback(PaymentCallbackDTO paymentCallbackDTO) { 134 + private HttpHeaders createHeaders() {
154 - log.info("callback request received: {}", paymentCallbackDTO); 135 + HttpHeaders headers = new HttpHeaders();
155 - try { 136 + headers.setContentType(MediaType.APPLICATION_JSON);
156 - final var paymentCallbackResult = 137 + return headers;
157 - xmlMapper.readValue(paymentCallbackDTO.getPayResult(), PaymentCallbackResult.class); 138 + }
158 - log.info("Parsed paymentCallbackResult: {}", paymentCallbackResult);
159 - final var orderId = Integer.parseInt(paymentCallbackResult.getTradeId());
160 - final var orderById = orderServiceRpcClient.getOrderById(orderId);
161 - if (orderById == null) {
162 - log.error("Order not found, orderId: {}", orderId);
163 - return false;
164 - }
165 - log.info("Order callback, orderId: {}", orderId);
166 139
167 - final var transType = paymentCallbackResult.getTransType(); 140 + private PayResponseDto processResponse(ResponseEntity<String> responseEntity) {
168 - if (transType.equals("00")) { 141 + if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) {
169 - if (orderById.getPayStatus() != PayStatusEnum.TO_PAY 142 + log.error("payInform failed with response: {}", responseEntity.getBody());
170 - && orderById.getPayStatus() != PayStatusEnum.IN_PROGRESS) { 143 + throw new IllegalArgumentException("支付信息获取失败");
171 - log.error("Order status is not TO_PAY or IN_PROGRESS, orderId: {}", orderId);
172 - return true;
173 } 144 }
174 - long payTime;
175 try { 145 try {
176 - payTime = 146 + log.info("processResponse response: {}", responseEntity.getBody());
177 - DateUtil.parseDate(paymentCallbackResult.getTransDate(), "yyyyMMddHHmmss").getTime(); 147 + return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class);
178 - } catch (ParseException e) { 148 + } catch (Exception e) {
179 - log.error("Failed to parse TransDate: {}", paymentCallbackResult.getTransDate(), e); 149 + log.error("Failed to parse XML response", e);
180 - throw new IllegalArgumentException("支付时间格式错误: " + paymentCallbackResult.getTransDate(), e); 150 + throw new RuntimeException("支付信息结果解析失败", e);
181 } 151 }
182 - BigDecimal realAmount; 152 + }
153 +
154 + public boolean callback(PaymentCallbackDTO paymentCallbackDTO) {
155 + log.info("callback request received: {}", paymentCallbackDTO);
183 try { 156 try {
184 - realAmount = Convert.toBigDecimal(paymentCallbackResult.getRealAmount()); 157 + final var paymentCallbackResult =
185 - if (realAmount == null) { 158 + xmlMapper.readValue(paymentCallbackDTO.getPayResult(), PaymentCallbackResult.class);
186 - throw new NumberFormatException("RealAmount is null or empty"); 159 + log.info("Parsed paymentCallbackResult: {}", paymentCallbackResult);
187 - } 160 + final var orderId = Integer.parseInt(paymentCallbackResult.getTradeId());
188 - } catch (NumberFormatException e) { 161 + final var orderById = orderServiceRpcClient.getOrderById(orderId);
189 - log.error("Invalid RealAmount value: {}", paymentCallbackResult.getRealAmount(), e); 162 + if (orderById == null) {
190 - throw new IllegalArgumentException("支付金额错误: " + paymentCallbackResult.getRealAmount(), e); 163 + log.error("Order not found, orderId: {}", orderId);
191 - } 164 + return false;
192 - final var build =
193 - ClientCustomerOrderModification.newBuilder()
194 - .setId(orderId)
195 - .setShouldUpdatePayTime(true)
196 - .setPayTime(payTime)
197 - .setShouldUpdatePayPrice(true)
198 - .setPayPrice(realAmount.multiply(new BigDecimal("100")).intValue())
199 - .setShouldUpdatePayStatus(true)
200 - .setPayStatus(PayStatusEnum.SUCCEED)
201 - .setShouldUpdateStatus(true)
202 - .setStatus(OrderStatusEnum.PREPARING)
203 - .setShouldUpdateTransactionId(true)
204 - .setTransactionId(paymentCallbackResult.getTradeReferenceNo())
205 - .build();
206 - final var updateResponse =
207 - orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
208 - log.info("callback updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
209 - if (updateResponse.getIsUpdated()) {
210 - String createdAt = Instant.ofEpochMilli(orderById.getCreatedAt())
211 - .atZone(DateUtil.CHINA_ZONE)
212 - .minusHours(8)
213 - .format(DateUtil.Y_M_D_H_M_S);
214 - String payTimed = Instant.ofEpochMilli(orderById.getPayTime())
215 - .atZone(DateUtil.CHINA_ZONE)
216 - .minusHours(8)
217 - .format(DateUtil.Y_M_D_H_M_S);
218 - final var orderPaymentSuccessData =
219 - OrderPaymentSuccessData.builder()
220 - .orderId(orderById.getId())
221 - .orderCode(orderById.getOrderCode())
222 - .createTime(createdAt)
223 - .payTime(payTimed)
224 - .build();
225 - final var data =
226 - SocketMessage.<OrderPaymentSuccessData>builder()
227 - .type(MessageTypeEnum.ORDER)
228 - .noticeType(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS)
229 - .subject(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS.getDescription())
230 - .message("您的订单:" + orderById.getOrderCode() + " 支付成功啦,快去查看吧~")
231 - .data(orderPaymentSuccessData)
232 - .build();
233 - WebSocketServer.sendMessageToUser(
234 - UserSessionKey.builder()
235 - .userId(orderById.getOpenId())
236 - .userType(UserTypeEnum.MICRO)
237 - .build(),
238 - data);
239 - if (LocalDate.now().equals(DateUtil.toLocalDate(orderById.getMealTime()))) {
240 - final var orderCreateSuccessData =
241 - OrderCreateSuccessData.builder().orderId(orderById.getId()).build();
242 - final var createOrderData =
243 - SocketMessage.<OrderCreateSuccessData>builder()
244 - .type(MessageTypeEnum.ORDER)
245 - .noticeType(NoticeTypeEnum.ORDER_CREATED)
246 - .subject(NoticeTypeEnum.ORDER_CREATED.getDescription())
247 - .message("有新的订单下单成功啦,快去查看吧~")
248 - .data(orderCreateSuccessData)
249 - .build();
250 - final var clientOperatorIds =
251 - clientResourceTagServiceRpcClient
252 - .getOperatorIdsByStallIds(List.of(orderById.getStallId())).stream()
253 - .map(SingleResourceLabelEntityRefsResponse::getEntityId)
254 - .distinct()
255 - .collect(Collectors.toList());
256 - final var enterpriseOperatorIds =
257 - enterpriseResourceServiceRpcClient.getOperatorIdsByStallId(
258 - orderById.getEnterpriseId(), orderById.getStallId());
259 - final var userAuthData = new ArrayList<UserAuthData>();
260 - userAuthData.addAll(
261 - clientOperatorIds.stream()
262 - .map(
263 - e ->
264 - UserAuthData.builder()
265 - .operatorId(e)
266 - .userType(UserTypeEnum.CLIENT)
267 - .build())
268 - .collect(Collectors.toList()));
269 - userAuthData.addAll(
270 - enterpriseOperatorIds.stream()
271 - .map(e -> UserAuthData.builder().operatorId(e).userType(UserTypeEnum.KDS).build())
272 - .collect(Collectors.toList()));
273 - log.info("received userAuthData:{}", JsonUtil.writeAsJson(userAuthData));
274 - try {
275 - WebSocketServer.sendMessageByUserTypes(
276 - List.of(UserTypeEnum.CLIENT, UserTypeEnum.KDS), userAuthData, createOrderData);
277 - } catch (IOException e) {
278 - log.error("Failed to send message to user", e);
279 } 165 }
280 - } 166 + log.info("Order callback, orderId: {}", orderId);
167 +
168 + final var transType = paymentCallbackResult.getTransType();
169 + if (transType.equals("00")) {
170 + if (orderById.getPayStatus() != PayStatusEnum.TO_PAY
171 + && orderById.getPayStatus() != PayStatusEnum.IN_PROGRESS) {
172 + log.error("Order status is not TO_PAY or IN_PROGRESS, orderId: {}", orderId);
173 + return true;
174 + }
175 + long payTime;
176 + try {
177 + payTime =
178 + DateUtil.parseDate(paymentCallbackResult.getTransDate(), "yyyyMMddHHmmss").getTime();
179 + } catch (ParseException e) {
180 + log.error("Failed to parse TransDate: {}", paymentCallbackResult.getTransDate(), e);
181 + throw new IllegalArgumentException("支付时间格式错误: " + paymentCallbackResult.getTransDate(), e);
182 + }
183 + BigDecimal realAmount;
184 + try {
185 + realAmount = Convert.toBigDecimal(paymentCallbackResult.getRealAmount());
186 + if (realAmount == null) {
187 + throw new NumberFormatException("RealAmount is null or empty");
188 + }
189 + } catch (NumberFormatException e) {
190 + log.error("Invalid RealAmount value: {}", paymentCallbackResult.getRealAmount(), e);
191 + throw new IllegalArgumentException("支付金额错误: " + paymentCallbackResult.getRealAmount(), e);
192 + }
193 + final var build =
194 + ClientCustomerOrderModification.newBuilder()
195 + .setId(orderId)
196 + .setShouldUpdatePayTime(true)
197 + .setPayTime(payTime)
198 + .setShouldUpdatePayPrice(true)
199 + .setPayPrice(realAmount.multiply(new BigDecimal("100")).intValue())
200 + .setShouldUpdatePayStatus(true)
201 + .setPayStatus(PayStatusEnum.SUCCEED)
202 + .setShouldUpdateStatus(true)
203 + .setStatus(OrderStatusEnum.PREPARING)
204 + .setShouldUpdateTransactionId(true)
205 + .setTransactionId(paymentCallbackResult.getTradeReferenceNo())
206 + .build();
207 + final var updateResponse =
208 + orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
209 + log.info("callback updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
210 + if (updateResponse.getIsUpdated()) {
211 + String createdAt = Instant.ofEpochMilli(orderById.getCreatedAt())
212 + .atZone(DateUtil.CHINA_ZONE)
213 + .minusHours(8)
214 + .format(DateUtil.Y_M_D_H_M_S);
215 + String payTimed = Instant.ofEpochMilli(orderById.getPayTime())
216 + .atZone(DateUtil.CHINA_ZONE)
217 + .minusHours(8)
218 + .format(DateUtil.Y_M_D_H_M_S);
219 + final var orderPaymentSuccessData =
220 + OrderPaymentSuccessData.builder()
221 + .orderId(orderById.getId())
222 + .orderCode(orderById.getOrderCode())
223 + .createTime(createdAt)
224 + .payTime(payTimed)
225 + .build();
226 + final var data =
227 + SocketMessage.<OrderPaymentSuccessData>builder()
228 + .type(MessageTypeEnum.ORDER)
229 + .noticeType(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS)
230 + .subject(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS.getDescription())
231 + .message("您的订单:" + orderById.getOrderCode() + " 支付成功啦,快去查看吧~")
232 + .data(orderPaymentSuccessData)
233 + .build();
234 + WebSocketServer.sendMessageToUser(
235 + UserSessionKey.builder()
236 + .userId(orderById.getOpenId())
237 + .userType(UserTypeEnum.MICRO)
238 + .build(),
239 + data);
240 + if (LocalDate.now().equals(DateUtil.toLocalDate(orderById.getMealTime()))) {
241 + final var orderCreateSuccessData =
242 + OrderCreateSuccessData.builder().orderId(orderById.getId()).build();
243 + final var createOrderData =
244 + SocketMessage.<OrderCreateSuccessData>builder()
245 + .type(MessageTypeEnum.ORDER)
246 + .noticeType(NoticeTypeEnum.ORDER_CREATED)
247 + .subject(NoticeTypeEnum.ORDER_CREATED.getDescription())
248 + .message("有新的订单下单成功啦,快去查看吧~")
249 + .data(orderCreateSuccessData)
250 + .build();
251 + final var clientOperatorIds =
252 + clientResourceTagServiceRpcClient
253 + .getOperatorIdsByStallIds(List.of(orderById.getStallId())).stream()
254 + .map(SingleResourceLabelEntityRefsResponse::getEntityId)
255 + .distinct()
256 + .collect(Collectors.toList());
257 + final var enterpriseOperatorIds =
258 + enterpriseResourceServiceRpcClient.getOperatorIdsByStallId(
259 + orderById.getEnterpriseId(), orderById.getStallId());
260 + final var userAuthData = new ArrayList<UserAuthData>();
261 + userAuthData.addAll(
262 + clientOperatorIds.stream()
263 + .map(
264 + e ->
265 + UserAuthData.builder()
266 + .operatorId(e)
267 + .userType(UserTypeEnum.CLIENT)
268 + .build())
269 + .collect(Collectors.toList()));
270 + userAuthData.addAll(
271 + enterpriseOperatorIds.stream()
272 + .map(e -> UserAuthData.builder().operatorId(e).userType(UserTypeEnum.KDS).build())
273 + .collect(Collectors.toList()));
274 + log.info("received userAuthData:{}", JsonUtil.writeAsJson(userAuthData));
275 + try {
276 + WebSocketServer.sendMessageByUserTypes(
277 + List.of(UserTypeEnum.CLIENT, UserTypeEnum.KDS), userAuthData, createOrderData);
278 + } catch (IOException e) {
279 + log.error("Failed to send message to user", e);
280 + }
281 + }
282 + }
283 + return updateResponse.getIsUpdated();
284 + } else {
285 + final var build =
286 + ClientCustomerOrderModification.newBuilder()
287 + .setId(orderId)
288 + .setShouldUpdatePayStatus(true)
289 + .setPayStatus(PayStatusEnum.REFUND)
290 + .build();
291 + final var updateResponse =
292 + orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
293 + log.info("callback updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
294 + return updateResponse.getIsUpdated();
295 + }
296 + } catch (JsonProcessingException e) {
297 + log.error("Failed to parse XML response, raw XML: {}", paymentCallbackDTO.getPayResult(), e);
298 + throw new RuntimeException("支付/退款通知结果解析失败", e);
299 + } catch (Exception e) {
300 + log.error("Unexpected error in callback, request: {}", paymentCallbackDTO, e);
301 + throw new RuntimeException("支付/退款通知处理失败", e);
302 + }
303 + }
304 +
305 + public boolean waiting(Integer orderId) {
306 + final var orderById = orderServiceRpcClient.getOrderById(orderId);
307 + if (orderById == null) {
308 + log.error("waiting Order not found, orderId: {}", orderId);
309 + return false;
310 + }
311 + if (orderById.getPayStatus() != PayStatusEnum.TO_PAY) {
312 + log.info("waiting Order {} already processed, skipping.", orderId);
313 + return false;
281 } 314 }
282 - return updateResponse.getIsUpdated();
283 - } else {
284 final var build = 315 final var build =
285 - ClientCustomerOrderModification.newBuilder() 316 + ClientCustomerOrderModification.newBuilder()
286 - .setId(orderId) 317 + .setId(orderId)
287 - .setShouldUpdatePayStatus(true) 318 + .setShouldUpdatePayStatus(true)
288 - .setPayStatus(PayStatusEnum.REFUND) 319 + .setPayStatus(PayStatusEnum.IN_PROGRESS)
289 - .build(); 320 + .build();
290 final var updateResponse = 321 final var updateResponse =
291 - orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build); 322 + orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
292 - log.info("callback updateClientCustomerOrder result: {}", updateResponse.getIsUpdated()); 323 + log.info("waiting updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
293 return updateResponse.getIsUpdated(); 324 return updateResponse.getIsUpdated();
294 - }
295 - } catch (JsonProcessingException e) {
296 - log.error("Failed to parse XML response, raw XML: {}", paymentCallbackDTO.getPayResult(), e);
297 - throw new RuntimeException("支付/退款通知结果解析失败", e);
298 - } catch (Exception e) {
299 - log.error("Unexpected error in callback, request: {}", paymentCallbackDTO, e);
300 - throw new RuntimeException("支付/退款通知处理失败", e);
301 } 325 }
302 - }
303 326
304 - public boolean waiting(Integer orderId) { 327 + public String orderPayRefund(PayRefundRequestDto payRefundRequestDto) {
305 - final var orderById = orderServiceRpcClient.getOrderById(orderId); 328 + Integer orderId = payRefundRequestDto.getOrderId();
306 - if (orderById == null) { 329 + final var orderById = orderServiceRpcClient.getOrderById(orderId);
307 - log.error("waiting Order not found, orderId: {}", orderId); 330 + if (orderById == null) {
308 - return false; 331 + log.error("Order not found, orderId: {}", orderId);
309 - } 332 + throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_NOT_FOUND);
310 - if (orderById.getPayStatus() != PayStatusEnum.TO_PAY) { 333 + }
311 - log.info("waiting Order {} already processed, skipping.", orderId); 334 + if (orderById.getOnlinePay() != OnlinePayEnum.ONLINE) {
312 - return false; 335 + log.info("Order {} is not online pay.", orderId);
313 - } 336 + throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_NOT_ONLINE_PAY);
314 - final var build = 337 + }
315 - ClientCustomerOrderModification.newBuilder() 338 + if (orderById.getPayStatus() != PayStatusEnum.SUCCEED) {
316 - .setId(orderId) 339 + log.info("Order {} pay is not succeed.", orderId);
317 - .setShouldUpdatePayStatus(true) 340 + throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_NOT_SUCCESS);
318 - .setPayStatus(PayStatusEnum.IN_PROGRESS) 341 + }
319 - .build(); 342 + LocalDate today = LocalDate.now();
320 - final var updateResponse = 343 + LocalDate tomorrow = today.plusDays(1);
321 - orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build); 344 + LocalDateTime tomorrowZero = tomorrow.atStartOfDay();
322 - log.info("waiting updateClientCustomerOrder result: {}", updateResponse.getIsUpdated()); 345 + LocalDateTime orderPayTime = DateUtil.toLocalDateTime(orderById.getPayTime()).minusHours(8);
323 - return updateResponse.getIsUpdated(); 346 + if (!orderPayTime.isBefore(tomorrowZero)) {
324 - } 347 + log.info("Order {} pay is beyond cancel time.", orderId);
325 - 348 + throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_PAY_REFUND_FAILED);
326 - public String orderPayRefund(PayRefundRequestDto payRefundRequestDto) { 349 + }
327 - Integer orderId = payRefundRequestDto.getOrderId(); 350 + BigDecimal detailPrice = BigDecimal.ZERO;
328 - final var orderById = orderServiceRpcClient.getOrderById(orderId); 351 + if (CollectionUtils.isNotEmpty(payRefundRequestDto.getOrderDetailIds())) {
329 - if (orderById == null) { 352 + final var orderDetails = orderServiceRpcClient.getClientCustomerOrderDetailsByIds(payRefundRequestDto.getOrderDetailIds());
330 - log.error("Order not found, orderId: {}", orderId); 353 + detailPrice = orderDetails.stream().filter(e -> e.getPrice() > 0).map(e -> new BigDecimal(e.getPrice()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)).reduce(BigDecimal.ZERO, BigDecimal::add);
331 - throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_NOT_FOUND); 354 + }
332 - }
333 - if (orderById.getOnlinePay() != OnlinePayEnum.ONLINE) {
334 - log.info("Order {} is not online pay.", orderId);
335 - throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_NOT_ONLINE_PAY);
336 - }
337 - if (orderById.getPayStatus() != PayStatusEnum.SUCCEED) {
338 - log.info("Order {} pay is not succeed.", orderId);
339 - throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_NOT_SUCCESS);
340 - }
341 - LocalDate today = LocalDate.now();
342 - LocalDate tomorrow = today.plusDays(1);
343 - LocalDateTime tomorrowZero = tomorrow.atStartOfDay();
344 - LocalDateTime orderPayTime = DateUtil.toLocalDateTime(orderById.getPayTime()).minusHours(8);
345 - if (!orderPayTime.isBefore(tomorrowZero)) {
346 - log.info("Order {} pay is beyond cancel time.", orderId);
347 - throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_PAY_REFUND_FAILED);
348 - }
349 355
350 - String appId = businessConfig.getPayClientId(); 356 + String appId = businessConfig.getPayClientId();
351 357
352 - // 构建paycontent 358 + // 构建paycontent
353 - String payTime = Instant.ofEpochMilli(orderById.getPayTime()) 359 + String payTime = Instant.ofEpochMilli(orderById.getPayTime())
354 - .atZone(DateUtil.CHINA_ZONE) 360 + .atZone(DateUtil.CHINA_ZONE)
355 - .minusHours(8) 361 + .minusHours(8)
356 - .format(DateUtil.YMDHMS); 362 + .format(DateUtil.YMDHMS);
357 363
358 - BigDecimal price = 364 + BigDecimal price = detailPrice.compareTo(BigDecimal.ZERO) == 0 ?
359 - new BigDecimal(orderById.getPayPrice()) 365 + new BigDecimal(orderById.getPayPrice()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).negate()
360 - .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).negate(); 366 + : detailPrice.negate();
361 367
362 - String payContentStr = 368 + String payContentStr =
363 - orderById.getId() 369 + orderById.getId()
364 - + "^" 370 + + "^"
365 - + "01" 371 + + "01"
366 - + "^" 372 + + "^"
367 - + "^" 373 + + "^"
368 - + orderById.getOpenId() 374 + + orderById.getOpenId()
369 - + "^" 375 + + "^"
370 - + orderById.getOrderCode() 376 + + orderById.getOrderCode()
371 - + "^" 377 + + "^"
372 - + price 378 + + price
373 - + "^" 379 + + "^"
374 - + payTime 380 + + payTime
375 - + "^" 381 + + "^"
376 - + orderById.getTransactionId() 382 + + orderById.getTransactionId()
377 - + "^" 383 + + "^"
378 - + payRefundRequestDto.getHospitalCode() 384 + + payRefundRequestDto.getHospitalCode()
379 - + "^" 385 + + "^"
380 - + "^" 386 + + "^"
381 - + LoginContextHolder.getId() 387 + + LoginContextHolder.getId()
382 - + "^" 388 + + "^"
383 - + LoginContextHolder.getName(); 389 + + LoginContextHolder.getName();
384 - String payContent = 390 + String payContent =
385 - URLEncoder.encode( 391 + URLEncoder.encode(
386 - Base64.encodeBase64String(payContentStr.getBytes()), StandardCharsets.UTF_8); 392 + Base64.encodeBase64String(payContentStr.getBytes()), StandardCharsets.UTF_8);
387 393
388 - // 构建signinfo 394 + // 构建signinfo
389 - String signInfoStr = appId + orderById.getId() + payTime + businessConfig.getRefundSecurityKey(); 395 + String signInfoStr = appId + orderById.getId() + payTime + businessConfig.getRefundSecurityKey();
390 - String signInfo = DigestUtils.sha1Hex(signInfoStr.getBytes()); 396 + String signInfo = DigestUtils.sha1Hex(signInfoStr.getBytes());
391 397
392 - // 构建请求 398 + // 构建请求
393 - try { 399 + try {
394 - String url = businessConfig.getCancelPayUrl() + "?" + "appid=" + appId + "&paycontent=" + payContent + "&signinfo=" + signInfo; 400 + String url = businessConfig.getCancelPayUrl() + "?" + "appid=" + appId + "&paycontent=" + payContent + "&signinfo=" + signInfo;
395 - log.info("cancel order pay url:{}", url); 401 + log.info("cancel order pay url:{}", url);
396 // Request request = 402 // Request request =
397 // new Request.Builder().url(url).get().build(); 403 // new Request.Builder().url(url).get().build();
398 // Response payResponse = client.newCall(request).execute(); 404 // Response payResponse = client.newCall(request).execute();
...@@ -400,10 +406,10 @@ public class PayServiceClient { ...@@ -400,10 +406,10 @@ public class PayServiceClient {
400 // throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_PAY_REFUND_FAILED); 406 // throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_PAY_REFUND_FAILED);
401 // } 407 // }
402 // String htmlContent = payResponse.body().string(); 408 // String htmlContent = payResponse.body().string();
403 - return url; 409 + return url;
404 - } catch (Exception e) { 410 + } catch (Exception e) {
405 - log.error(e.getMessage()); 411 + log.error(e.getMessage());
406 - throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_PAY_REFUND_FAILED); 412 + throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_PAY_REFUND_FAILED);
413 + }
407 } 414 }
408 - }
409 } 415 }
......