wangyingyang

add order cancel

......@@ -16,4 +16,5 @@ public class BusinessConfig {
private String clientCustomerQueryUrl;
private String payUrl;
private String payClientId;
private String cancelPayUrl;
}
......
......@@ -35,4 +35,11 @@ public class ClientOrderController {
return orderService.getOrdersByPagination(queryOrderDto);
}
@ApiOperation(value = "院区端已撤销订单")
@PostMapping("/client/orders/canceled/pagination")
@ResponseStatus(HttpStatus.OK)
public OrderPageResult<OrderDto> getCanceledOrderByByPagination(@Valid @RequestBody OrderDbDTO.QueryCanceledOrderByPaginationDto queryOrderDto) {
return orderService.getCanceledOrdersByPagination(queryOrderDto);
}
}
......
......@@ -53,4 +53,11 @@ public class PayController {
return payServiceClient.callback(paymentCallbackDTO);
}
@SaIgnore
@ApiOperation(value = "支付撤销")
@PostMapping(value = "/pay/cancel")
@ResponseStatus(HttpStatus.OK)
public boolean cancel(@Valid @RequestBody PayDTO.PayCancelRequestDto payCancelRequestDto) {
return payServiceClient.cancelOrder(payCancelRequestDto);
}
}
\ No newline at end of file
......
......@@ -64,6 +64,17 @@ public class OrderDbDTO {
}
@Data
@ApiModel(description = "查询已撤销订单Dto")
public static class QueryCanceledOrderByPaginationDto {
private Integer enterpriseId;
private Integer clientId;
private Integer pn;
private Integer pz;
private String keyword;
private List<Integer> stallIds;
}
@Data
@Builder
@ApiModel(description = "订单不同状态的数量Dto")
public static class OrderCountByStatusDto {
......@@ -455,12 +466,18 @@ public class OrderDbDTO {
@ApiModelProperty(value = "是否已读")
private Boolean isRead;
@ApiModelProperty(value = "是否已撤销")
private Boolean isCanceled;
@ApiModelProperty(value = "截单类型")
private Integer closeTimeType;
@ApiModelProperty(value = "订单所属用户的信息")
private CustomerDetailDto customer;
@ApiModelProperty(value = "该订单的忌口冲突、医嘱信息")
private List<String> orderNotice;
// @ApiModelProperty(value = "订单菜品详情列表")
// private List<OrderDetailDto> orderDetailDtos;
}
......
......@@ -26,6 +26,17 @@ public class PayDTO {
}
@Data
@ApiModel(description = "支付请求dto")
@NoArgsConstructor
@AllArgsConstructor
public static class PayCancelRequestDto {
// private String clientId;
private Integer orderId;
private String hospitalCode;
// private String price;
}
@Data
@ApiModel(description = "支付MiniPay结果")
@NoArgsConstructor
@AllArgsConstructor
......@@ -76,6 +87,15 @@ public class PayDTO {
}
@Data
@ApiModel(description = "取消支付响应")
@NoArgsConstructor
@AllArgsConstructor
public static class CancelPayResponseDto {
private Integer code;
private String message;
}
@Data
@Builder
@ApiModel(description = "第三方支付平台通知请求dto")
@NoArgsConstructor
......
......@@ -9,6 +9,7 @@ import com.infoloop.tianting.model.dto.OrderDbDTO.OrderAndDetailsDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailBatchUpdateDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryCanceledOrderByPaginationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryOrderByConditionDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryOrderByPaginationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.UpdateOrderDto;
......@@ -21,6 +22,8 @@ public interface OrderService {
OrderAndDetailsDto getOrdersByCustomerId(Integer customerId);
OrderAndDetailsDto getOrdersByCondition(QueryOrderByConditionDto queryOrderDto);
OrderPageResult<OrderDto> getOrdersByPagination(QueryOrderByPaginationDto queryOrderDto);
OrderPageResult<OrderDto> getCanceledOrdersByPagination(
QueryCanceledOrderByPaginationDto queryOrderDto);
List<OrderDetailDto> getOrderDetailsByOrderId(Integer orderId);
CreateOrderResponse createOrder(CreateOrderDto createOrderDto);
CreateOrderResponse orderCreation(OperatorCreateOrderDto createOrderDto);
......
......@@ -64,6 +64,7 @@ import com.infoloop.tianting.model.dto.OrderDbDTO.MealDetailDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailBatchUpdateDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailOperationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryCanceledOrderByPaginationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryOrderByConditionDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryOrderByPaginationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.UpdateOrderDto;
......@@ -461,6 +462,8 @@ public class OrderServiceRpcClient {
.setKeyword(queryOrderDto.getKeyword() == null ? "" : queryOrderDto.getKeyword())
.setPageNo(queryOrderDto.getPn())
.setPageSize(queryOrderDto.getPz())
.setShouldFilterStatus(true)
.setShouldFilterIsCanceled(false)
.setStatus(queryOrderDto.getStatus())
.setEnterpriseId(queryOrderDto.getEnterpriseId())
.setClientId(queryOrderDto.getClientId())
......@@ -469,6 +472,22 @@ public class OrderServiceRpcClient {
return orderServiceRpcBlockingStub.queryClientCustomerOrdersByPagination(request);
}
public QueryClientCustomerOrdersByPaginationRpcResponse queryCanceledClientCustomerOrdersByPagination(
QueryCanceledOrderByPaginationDto queryOrderDto) {
final var request = QueryClientCustomerOrdersByPaginationRpcRequest.newBuilder()
.setKeyword(queryOrderDto.getKeyword() == null ? "" : queryOrderDto.getKeyword())
.setPageNo(queryOrderDto.getPn())
.setPageSize(queryOrderDto.getPz())
.setShouldFilterStatus(false)
.setShouldFilterIsCanceled(true)
.setIsCanceled(true)
.setEnterpriseId(queryOrderDto.getEnterpriseId())
.setClientId(queryOrderDto.getClientId())
.addAllStallIds(queryOrderDto.getStallIds())
.build();
return orderServiceRpcBlockingStub.queryClientCustomerOrdersByPagination(request);
}
public List<SingleClientCustomerOrderDetailRpcResponse> getOrderDetailsByOrderId(Integer orderId) {
return orderServiceRpcBlockingStub.getClientCustomerOrderDetailsByOrderIds(
GetClientCustomerOrderDetailsByOrderIdsRpcRequest.newBuilder()
......
......@@ -2,12 +2,15 @@ package com.infoloop.tianting.service.client;
import cn.hutool.core.convert.Convert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.infoloop.tianting.clientcustomerorderservice.ClientCustomerOrderModification;
import com.infoloop.tianting.clientcustomerorderservice.OnlinePayEnum;
import com.infoloop.tianting.clientcustomerorderservice.OrderStatusEnum;
import com.infoloop.tianting.clientcustomerorderservice.PayStatusEnum;
import com.infoloop.tianting.clientresourcetagservice.SingleResourceLabelEntityRefsResponse;
......@@ -15,6 +18,8 @@ import com.infoloop.tianting.config.BusinessConfig;
import com.infoloop.tianting.context.LoginContextHolder;
import com.infoloop.tianting.exception.ClientEndExceptions;
import com.infoloop.tianting.exception.ErrorCodeEnum;
import com.infoloop.tianting.model.dto.PayDTO.CancelPayResponseDto;
import com.infoloop.tianting.model.dto.PayDTO.PayCancelRequestDto;
import com.infoloop.tianting.model.dto.PayDTO.PayInformRequestDto;
import com.infoloop.tianting.model.dto.PayDTO.PayResponseDto;
import com.infoloop.tianting.model.dto.PayDTO.ThirdPartyPayInformRequestDto;
......@@ -32,8 +37,15 @@ import com.infoloop.tianting.server.session.UserTypeEnum;
import com.infoloop.tianting.utils.DateUtil;
import com.infoloop.tianting.utils.EncryptionUtil;
import com.infoloop.tianting.utils.JsonUtil;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
......@@ -53,196 +65,347 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PayServiceClient {
private final RestTemplate restTemplate;
private final RestTemplate restTemplate;
private final BusinessConfig businessConfig;
private final BusinessConfig businessConfig;
private final OrderServiceRpcClient orderServiceRpcClient;
private final OrderServiceRpcClient orderServiceRpcClient;
private final ClientResourceTagServiceRpcClient clientResourceTagServiceRpcClient;
private final ClientResourceTagServiceRpcClient clientResourceTagServiceRpcClient;
private final EnterpriseResourceServiceRpcClient enterpriseResourceServiceRpcClient;
private final EnterpriseResourceServiceRpcClient enterpriseResourceServiceRpcClient;
private final XmlMapper xmlMapper = new XmlMapper();
private final XmlMapper xmlMapper = new XmlMapper();
public PayResponseDto payInform(PayInformRequestDto payRequestDto) {
final var orderById = orderServiceRpcClient.getOrderById(Integer.valueOf(payRequestDto.getOrderId()));
if (orderById == null || orderById.getPayStatus() == PayStatusEnum.PAY_CANCELED || orderById.getStatus() == OrderStatusEnum.ORDER_CANCELED) {
log.info("Order {} not found or canceled, skipping.", payRequestDto.getOrderId());
throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_CANCEL);
}
String url = MessageFormat.format(businessConfig.getPayUrl(), businessConfig.getPayClientId());
String requestDate = DateUtil.formatDate(LocalDateTime.now(), DateUtil.YMDHMS);
ThirdPartyPayInformRequestDto jsonData = buildRequestData(payRequestDto, requestDate);
String jsonDataString;
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
jsonDataString = objectMapper.writeValueAsString(jsonData);
} catch (JsonProcessingException e) {
log.error("Failed to serialize request data", e);
throw new IllegalArgumentException("支付信息获取失败: JSON 解析错误");
}
HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders());
log.info("payInform request: {}", jsonDataString);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
return processResponse(responseEntity);
}
private final OkHttpClient client = new OkHttpClient();
private ThirdPartyPayInformRequestDto buildRequestData(PayInformRequestDto payRequestDto, String requestDate) {
ThirdPartyPayInformRequestDto jsonData = new ThirdPartyPayInformRequestDto();
jsonData.setClientId(businessConfig.getPayClientId());
jsonData.setTradeId(payRequestDto.getOrderId());
jsonData.setRequestDate(requestDate);
jsonData.setRequestSource("OFWXApplet");
jsonData.setPayerOpenId(LoginContextHolder.getOpenId());
jsonData.setHospitalCode(payRequestDto.getHospitalCode());
jsonData.setPID(payRequestDto.getPhone());
jsonData.setRemark("");
jsonData.setTransAmount(payRequestDto.getPrice());
jsonData.setTradeDesc("");
jsonData.setSign(EncryptionUtil.sha1Encryption(businessConfig.getPayClientId() + payRequestDto.getOrderId() + requestDate + businessConfig.getPayClientId()));
return jsonData;
public PayResponseDto payInform(PayInformRequestDto payRequestDto) {
final var orderById =
orderServiceRpcClient.getOrderById(Integer.valueOf(payRequestDto.getOrderId()));
if (orderById == null
|| orderById.getPayStatus() == PayStatusEnum.PAY_CANCELED
|| orderById.getStatus() == OrderStatusEnum.ORDER_CANCELED) {
log.info("Order {} not found or canceled, skipping.", payRequestDto.getOrderId());
throw ClientEndExceptions.BusinessException.build(ErrorCodeEnum.ORDER_CANCEL);
}
String url = MessageFormat.format(businessConfig.getPayUrl(), businessConfig.getPayClientId());
String requestDate = DateUtil.formatDate(LocalDateTime.now(), DateUtil.YMDHMS);
ThirdPartyPayInformRequestDto jsonData = buildRequestData(payRequestDto, requestDate);
String jsonDataString;
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
jsonDataString = objectMapper.writeValueAsString(jsonData);
} catch (JsonProcessingException e) {
log.error("Failed to serialize request data", e);
throw new IllegalArgumentException("支付信息获取失败: JSON 解析错误");
}
HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders());
log.info("payInform request: {}", jsonDataString);
ResponseEntity<String> responseEntity =
restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
return processResponse(responseEntity);
}
private ThirdPartyPayInformRequestDto buildRequestData(
PayInformRequestDto payRequestDto, String requestDate) {
ThirdPartyPayInformRequestDto jsonData = new ThirdPartyPayInformRequestDto();
jsonData.setClientId(businessConfig.getPayClientId());
jsonData.setTradeId(payRequestDto.getOrderId());
jsonData.setRequestDate(requestDate);
jsonData.setRequestSource("OFWXApplet");
jsonData.setPayerOpenId(LoginContextHolder.getOpenId());
jsonData.setHospitalCode(payRequestDto.getHospitalCode());
jsonData.setPID(LoginContextHolder.getOpenId());
jsonData.setRemark("");
jsonData.setTransAmount(payRequestDto.getPrice());
jsonData.setTradeDesc("");
jsonData.setSign(
EncryptionUtil.sha1Encryption(
businessConfig.getPayClientId()
+ payRequestDto.getOrderId()
+ requestDate
+ businessConfig.getPayClientId()));
return jsonData;
}
private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
private PayResponseDto processResponse(ResponseEntity<String> responseEntity) {
if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) {
log.error("payInform failed with response: {}", responseEntity.getBody());
throw new IllegalArgumentException("支付信息获取失败");
}
try {
log.info("processResponse response: {}", responseEntity.getBody());
return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class);
} catch (Exception e) {
log.error("Failed to parse XML response", e);
throw new RuntimeException("支付信息结果解析失败", e);
}
}
private PayResponseDto processResponse(ResponseEntity<String> responseEntity) {
if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) {
log.error("payInform failed with response: {}", responseEntity.getBody());
throw new IllegalArgumentException("支付信息获取失败");
public boolean callback(PaymentCallbackDTO paymentCallbackDTO) {
log.info("callback request received: {}", paymentCallbackDTO);
try {
final var paymentCallbackResult =
xmlMapper.readValue(paymentCallbackDTO.getPayResult(), PaymentCallbackResult.class);
log.info("Parsed paymentCallbackResult: {}", paymentCallbackResult);
final var orderId = Integer.parseInt(paymentCallbackResult.getTradeId());
final var orderById = orderServiceRpcClient.getOrderById(orderId);
if (orderById == null) {
log.error("Order not found, orderId: {}", orderId);
return false;
}
if (orderById.getPayStatus() != PayStatusEnum.TO_PAY
&& orderById.getPayStatus() != PayStatusEnum.IN_PROGRESS) {
log.error("Order status is not TO_PAY or IN_PROGRESS, orderId: {}", orderId);
return true;
}
long payTime;
try {
payTime =
DateUtil.parseDate(paymentCallbackResult.getTransDate(), "yyyyMMddHHmmss").getTime();
} catch (ParseException e) {
log.error("Failed to parse TransDate: {}", paymentCallbackResult.getTransDate(), e);
throw new IllegalArgumentException("支付时间格式错误: " + paymentCallbackResult.getTransDate(), e);
}
BigDecimal realAmount;
try {
realAmount = Convert.toBigDecimal(paymentCallbackResult.getRealAmount());
if (realAmount == null) {
throw new NumberFormatException("RealAmount is null or empty");
}
try {
log.info("processResponse response: {}", responseEntity.getBody());
return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class);
} catch (Exception e) {
log.error("Failed to parse XML response", e);
throw new RuntimeException("支付信息结果解析失败", e);
} catch (NumberFormatException e) {
log.error("Invalid RealAmount value: {}", paymentCallbackResult.getRealAmount(), e);
throw new IllegalArgumentException("支付金额错误: " + paymentCallbackResult.getRealAmount(), e);
}
final var build =
ClientCustomerOrderModification.newBuilder()
.setId(orderId)
.setShouldUpdatePayTime(true)
.setPayTime(payTime)
.setShouldUpdatePayPrice(true)
.setPayPrice(realAmount.multiply(new BigDecimal("100")).intValue())
.setShouldUpdatePayStatus(true)
.setPayStatus(PayStatusEnum.SUCCEED)
.setShouldUpdateStatus(true)
.setStatus(OrderStatusEnum.PREPARING)
.setShouldUpdateTransactionId(true)
.setTransactionId(paymentCallbackResult.getTransSN())
.build();
final var updateResponse =
orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
log.info("callback updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
if (updateResponse.getIsUpdated()) {
final var orderPaymentSuccessData =
OrderPaymentSuccessData.builder()
.orderId(orderById.getId())
.orderCode(orderById.getOrderCode())
.createTime(DateUtil.formatDate(orderById.getCreatedAt(), DateUtil.Y_M_D_H_M_S))
.payTime(DateUtil.formatDate(orderById.getPayTime(), DateUtil.Y_M_D_H_M_S))
.build();
final var data =
SocketMessage.<OrderPaymentSuccessData>builder()
.type(MessageTypeEnum.ORDER)
.noticeType(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS)
.subject(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS.getDescription())
.message("您的订单:" + orderById.getOrderCode() + " 支付成功啦,快去查看吧~")
.data(orderPaymentSuccessData)
.build();
WebSocketServer.sendMessageToUser(
UserSessionKey.builder()
.userId(orderById.getOpenId())
.userType(UserTypeEnum.MICRO)
.build(),
data);
if (LocalDate.now().equals(DateUtil.toLocalDate(orderById.getMealTime()))) {
final var orderCreateSuccessData =
OrderCreateSuccessData.builder().orderId(orderById.getId()).build();
final var createOrderData =
SocketMessage.<OrderCreateSuccessData>builder()
.type(MessageTypeEnum.ORDER)
.noticeType(NoticeTypeEnum.ORDER_CREATED)
.subject(NoticeTypeEnum.ORDER_CREATED.getDescription())
.message("有新的订单下单成功啦,快去查看吧~")
.data(orderCreateSuccessData)
.build();
final var clientOperatorIds =
clientResourceTagServiceRpcClient
.getOperatorIdsByStallIds(List.of(orderById.getStallId())).stream()
.map(SingleResourceLabelEntityRefsResponse::getEntityId)
.distinct()
.collect(Collectors.toList());
final var enterpriseOperatorIds =
enterpriseResourceServiceRpcClient.getOperatorIdsByStallId(
orderById.getEnterpriseId(), orderById.getStallId());
final var userAuthData = new ArrayList<UserAuthData>();
userAuthData.addAll(
clientOperatorIds.stream()
.map(
e ->
UserAuthData.builder()
.operatorId(e)
.userType(UserTypeEnum.CLIENT)
.build())
.collect(Collectors.toList()));
userAuthData.addAll(
enterpriseOperatorIds.stream()
.map(e -> UserAuthData.builder().operatorId(e).userType(UserTypeEnum.KDS).build())
.collect(Collectors.toList()));
log.info("received userAuthData:{}", JsonUtil.writeAsJson(userAuthData));
try {
WebSocketServer.sendMessageByUserTypes(
List.of(UserTypeEnum.CLIENT, UserTypeEnum.KDS), userAuthData, createOrderData);
} catch (IOException e) {
log.error("Failed to send message to user", e);
}
}
}
return updateResponse.getIsUpdated();
} catch (JsonProcessingException e) {
log.error("Failed to parse XML response, raw XML: {}", paymentCallbackDTO.getPayResult(), e);
throw new RuntimeException("支付通知结果解析失败", e);
} catch (Exception e) {
log.error("Unexpected error in callback, request: {}", paymentCallbackDTO, e);
throw new RuntimeException("支付通知处理失败", e);
}
}
public boolean callback(PaymentCallbackDTO paymentCallbackDTO) {
log.info("callback request received: {}", paymentCallbackDTO);
try {
final var paymentCallbackResult = xmlMapper.readValue(paymentCallbackDTO.getPayResult(), PaymentCallbackResult.class);
log.info("Parsed paymentCallbackResult: {}", paymentCallbackResult);
final var orderId = Integer.parseInt(paymentCallbackResult.getTradeId());
final var orderById = orderServiceRpcClient.getOrderById(orderId);
if (orderById == null) {
log.error("Order not found, orderId: {}", orderId);
return false;
}
if (orderById.getPayStatus() != PayStatusEnum.TO_PAY && orderById.getPayStatus() != PayStatusEnum.IN_PROGRESS) {
log.error("Order status is not TO_PAY or IN_PROGRESS, orderId: {}", orderId);
return true;
}
long payTime;
try {
payTime = DateUtil.parseDate(paymentCallbackResult.getTransDate(), "yyyyMMddHHmmss").getTime();
} catch (ParseException e) {
log.error("Failed to parse TransDate: {}", paymentCallbackResult.getTransDate(), e);
throw new IllegalArgumentException("支付时间格式错误: " + paymentCallbackResult.getTransDate(), e);
}
BigDecimal realAmount;
try {
realAmount = Convert.toBigDecimal(paymentCallbackResult.getRealAmount());
if (realAmount == null) {
throw new NumberFormatException("RealAmount is null or empty");
}
} catch (NumberFormatException e) {
log.error("Invalid RealAmount value: {}", paymentCallbackResult.getRealAmount(), e);
throw new IllegalArgumentException("支付金额错误: " + paymentCallbackResult.getRealAmount(), e);
}
final var build = ClientCustomerOrderModification.newBuilder()
.setId(orderId)
.setShouldUpdatePayTime(true).setPayTime(payTime)
.setShouldUpdatePayPrice(true).setPayPrice(realAmount.multiply(new BigDecimal("100")).intValue())
.setShouldUpdatePayStatus(true).setPayStatus(PayStatusEnum.SUCCEED)
.setShouldUpdateStatus(true).setStatus(OrderStatusEnum.PREPARING)
.setShouldUpdateTransactionId(true).setTransactionId(paymentCallbackResult.getTransSN())
.build();
final var updateResponse = orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
log.info("callback updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
if (updateResponse.getIsUpdated()) {
final var orderPaymentSuccessData = OrderPaymentSuccessData.builder()
.orderId(orderById.getId())
.orderCode(orderById.getOrderCode())
.createTime(DateUtil.formatDate(orderById.getCreatedAt(), DateUtil.Y_M_D_H_M_S))
.payTime(DateUtil.formatDate(orderById.getPayTime(), DateUtil.Y_M_D_H_M_S))
.build();
final var data = SocketMessage.<OrderPaymentSuccessData>builder()
.type(MessageTypeEnum.ORDER)
.noticeType(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS)
.subject(NoticeTypeEnum.ORDER_PAYMENT_SUCCESS.getDescription())
.message("您的订单:" + orderById.getOrderCode() + " 支付成功啦,快去查看吧~")
.data(orderPaymentSuccessData)
.build();
WebSocketServer.sendMessageToUser(UserSessionKey.builder()
.userId(orderById.getOpenId())
.userType(UserTypeEnum.MICRO)
.build(), data);
if (LocalDate.now().equals(DateUtil.toLocalDate(orderById.getMealTime()))) {
final var orderCreateSuccessData = OrderCreateSuccessData.builder()
.orderId(orderById.getId())
.build();
final var createOrderData = SocketMessage.<OrderCreateSuccessData>builder()
.type(MessageTypeEnum.ORDER)
.noticeType(NoticeTypeEnum.ORDER_CREATED)
.subject(NoticeTypeEnum.ORDER_CREATED.getDescription())
.message("有新的订单下单成功啦,快去查看吧~")
.data(orderCreateSuccessData)
.build();
final var clientOperatorIds = clientResourceTagServiceRpcClient.getOperatorIdsByStallIds(List.of(orderById.getStallId())).stream().map(SingleResourceLabelEntityRefsResponse::getEntityId).distinct().collect(Collectors.toList());
final var enterpriseOperatorIds = enterpriseResourceServiceRpcClient.getOperatorIdsByStallId(orderById.getEnterpriseId(), orderById.getStallId());
final var userAuthData = new ArrayList<UserAuthData>();
userAuthData.addAll(clientOperatorIds.stream().map(e -> UserAuthData.builder().operatorId(e).userType(UserTypeEnum.CLIENT).build()).collect(Collectors.toList()));
userAuthData.addAll(enterpriseOperatorIds.stream().map(e -> UserAuthData.builder().operatorId(e).userType(UserTypeEnum.KDS).build()).collect(Collectors.toList()));
log.info("received userAuthData:{}", JsonUtil.writeAsJson(userAuthData));
try {
WebSocketServer.sendMessageByUserTypes(List.of(UserTypeEnum.CLIENT, UserTypeEnum.KDS), userAuthData, createOrderData);
} catch (IOException e) {
log.error("Failed to send message to user", e);
}
}
}
return updateResponse.getIsUpdated();
} catch (JsonProcessingException e) {
log.error("Failed to parse XML response, raw XML: {}", paymentCallbackDTO.getPayResult(), e);
throw new RuntimeException("支付通知结果解析失败", e);
} catch (Exception e) {
log.error("Unexpected error in callback, request: {}", paymentCallbackDTO, e);
throw new RuntimeException("支付通知处理失败", e);
}
public boolean waiting(Integer orderId) {
final var orderById = orderServiceRpcClient.getOrderById(orderId);
if (orderById == null) {
log.error("waiting Order not found, orderId: {}", orderId);
return false;
}
if (orderById.getPayStatus() != PayStatusEnum.TO_PAY) {
log.info("waiting Order {} already processed, skipping.", orderId);
return false;
}
final var build =
ClientCustomerOrderModification.newBuilder()
.setId(orderId)
.setShouldUpdatePayStatus(true)
.setPayStatus(PayStatusEnum.IN_PROGRESS)
.build();
final var updateResponse =
orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
log.info("waiting updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
return updateResponse.getIsUpdated();
}
public boolean waiting(Integer orderId) {
final var orderById = orderServiceRpcClient.getOrderById(orderId);
if (orderById == null) {
log.error("waiting Order not found, orderId: {}", orderId);
return false;
}
if (orderById.getPayStatus() != PayStatusEnum.TO_PAY) {
log.info("waiting Order {} already processed, skipping.", orderId);
return false;
}
final var build = ClientCustomerOrderModification.newBuilder()
public boolean cancelOrder(PayCancelRequestDto payCancelRequestDto) {
Integer orderId = payCancelRequestDto.getOrderId();
final var orderById = orderServiceRpcClient.getOrderById(orderId);
if (orderById == null) {
log.error("Order not found, orderId: {}", orderId);
return false;
}
if (orderById.getOnlinePay() != OnlinePayEnum.ONLINE) {
log.info("Order {} is not online pay.", orderId);
return false;
}
if (orderById.getPayStatus() != PayStatusEnum.SUCCEED) {
log.info("Order {} pay is not succeed.", orderId);
return false;
}
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDateTime tomorrowZero = tomorrow.atStartOfDay();
LocalDateTime orderPayTime = DateUtil.toLocalDateTime(orderById.getPayTime());
if (!orderPayTime.isBefore(tomorrowZero)) {
log.info("Order {} pay is beyond cancel time.", orderId);
return false;
}
String appId = businessConfig.getPayClientId();
// 构建paycontent
String payTime = DateUtil.formatDate(orderById.getPayTime(), DateUtil.YMDHMS);
String payContentStr =
orderById.getId()
+ "^"
+ "01(退款)"
+ "^"
+ ""
+ "^"
+ orderById.getOpenId()
+ "^"
+ orderById.getOrderCode()
+ "^"
+ orderById.getPayPrice()
+ "^"
+ payTime
+ "^"
+ orderById.getTransactionId()
+ "^"
+ payCancelRequestDto.getHospitalCode()
+ "^"
+ ""
+ "^"
+ LoginContextHolder.getId()
+ "^"
+ LoginContextHolder.getName();
String payContent =
URLEncoder.encode(
Base64.encodeBase64String(payContentStr.getBytes()), StandardCharsets.UTF_8);
// 构建signinfo
String signInfoStr = appId + orderById.getId() + payTime + businessConfig.getPayClientId();
String signInfo = DigestUtils.sha1Hex(signInfoStr.getBytes());
// 构建请求
// 设置请求体的媒体类型为JSON
try {
// MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
// RequestBody body = RequestBody.create(mediaType, jsonDataString);
String url = businessConfig.getCancelPayUrl() + "?" + "appid=" + appId + "&paycontent=" + payContent + "&signinfo=" + signInfo;
Request request =
new Request.Builder().url(url).get().build();
Response payResponse = client.newCall(request).execute();
if (payResponse.code() != 200) {
return false;
}
String cancelPayJsonData = payResponse.body().string();
log.info("pay json:" + cancelPayJsonData);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.ALLOW_MISSING_VALUES, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false);
CancelPayResponseDto payResponseDto =
objectMapper.readValue(cancelPayJsonData, CancelPayResponseDto.class);
if (payResponseDto.getCode() != 0) {
return false;
} else {
// 更新订单撤销状态
final var build =
ClientCustomerOrderModification.newBuilder()
.setId(orderId)
.setShouldUpdatePayStatus(true).setPayStatus(PayStatusEnum.IN_PROGRESS)
.setShouldUpdateIsCanceled(true)
.setIsCanceled(true)
.build();
final var updateResponse = orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
log.info("waiting updateClientCustomerOrder result: {}", updateResponse.getIsUpdated());
return updateResponse.getIsUpdated();
orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
return true;
}
} catch (Exception e) {
log.error(e.getStackTrace().toString());
return false;
}
}
}
......
......@@ -7,6 +7,7 @@ import com.infoloop.tianting.clientcustomerorderservice.CloseTimeType;
import com.infoloop.tianting.clientcustomerorderservice.OrderSourceEnum;
import com.infoloop.tianting.clientcustomerorderservice.OrderStatusEnum;
import com.infoloop.tianting.clientcustomerorderservice.OrderTypeEnum;
import com.infoloop.tianting.clientcustomerorderservice.SingleClientCustomerOrderDetailRpcResponse;
import com.infoloop.tianting.clientcustomerorderservice.SingleClientCustomerOrderRpcResponse;
import com.infoloop.tianting.clientcustomerservice.SingleClientCustomerLabelRefRpcResponse;
import com.infoloop.tianting.clientcustomerservice.SingleClientCustomerRpcResponse;
......@@ -16,8 +17,10 @@ import com.infoloop.tianting.deliveryruleservice.SingleClientLabelRpcResponse;
import com.infoloop.tianting.exception.ClientEndExceptions;
import com.infoloop.tianting.exception.ErrorCodeEnum;
import com.infoloop.tianting.menuservice.CloseTimeTypeEnum;
import com.infoloop.tianting.menuservice.SingleClientCustomerMenuRefRpcResponse;
import com.infoloop.tianting.menuservice.SingleMenuDetailRpcResponse;
import com.infoloop.tianting.model.common.OrderPageResult;
import com.infoloop.tianting.model.dto.ClientCustomerDbDTO.CurrentClientCustomerDto;
import com.infoloop.tianting.model.dto.ClientLabelDTO.HisAllergy;
import com.infoloop.tianting.model.dto.ClientLabelDTO.HisMedicalAdviceDto;
import com.infoloop.tianting.model.dto.ClientLabelDTO.QueryLabelsDto;
......@@ -35,6 +38,7 @@ import com.infoloop.tianting.model.dto.OrderDbDTO.OrderCountByStatusDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailBatchUpdateDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDetailDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.OrderDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryCanceledOrderByPaginationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryOrderByConditionDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.QueryOrderByPaginationDto;
import com.infoloop.tianting.model.dto.OrderDbDTO.UpdateOrderDto;
......@@ -50,6 +54,7 @@ import com.infoloop.tianting.service.client.OrderServiceRpcClient;
import com.infoloop.tianting.service.client.SkuServiceRpcClient;
import com.infoloop.tianting.utils.DateUtil;
import com.infoloop.tianting.utils.PageUtil;
import java.util.Arrays;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -318,6 +323,7 @@ public class OrderServiceImpl implements OrderService {
.updateSource(r.getUpdateSource().getNumber())
.updatedAt(DateUtil.formatDate(r.getUpdatedAt()))
.updatedBy(r.getUpdatedBy())
.isCanceled(r.getIsCanceled())
.build()).collect(Collectors.toList());
final var ids = orders.stream().map(OrderDto::getId).collect(Collectors.toList());
final var detailsResponse = orderServiceRpcClient.getOrderDetailsByOrderIds(ids);
......@@ -391,6 +397,7 @@ public class OrderServiceImpl implements OrderService {
.updateSource(r.getUpdateSource().getNumber())
.updatedAt(DateUtil.formatDate(r.getUpdatedAt()))
.updatedBy(r.getUpdatedBy())
.isCanceled(r.getIsCanceled())
.build()).collect(Collectors.toList());
final var ids = orders.stream().map(OrderDto::getId).collect(Collectors.toList());
final var detailsResponse = orderServiceRpcClient.getOrderDetailsByOrderIds(ids);
......@@ -437,7 +444,28 @@ public class OrderServiceImpl implements OrderService {
final var customerIds = response.getResponsesList().stream().map(SingleClientCustomerOrderRpcResponse::getCustomerId).collect(Collectors.toList());
final var customerResponse = clientCustomerServiceRpcClient.getClientCustomersByIds(
queryOrderDto.getEnterpriseId(), customerIds);
final var results = response.getResponsesList().stream().map(r -> OrderDto.builder()
final var orderIds = response.getResponsesList().stream().map(SingleClientCustomerOrderRpcResponse::getId).collect(Collectors.toList());
final var orderDetails = orderServiceRpcClient.getOrderDetailsByOrderIds(orderIds);
final var results = response.getResponsesList().stream().map(r -> {
final var curOrderDetails = orderDetails.stream().filter(d ->
d.getOrderId() == r.getId()).collect(Collectors.toList());
List<String> orderNotice = new ArrayList<>();
for (SingleClientCustomerOrderDetailRpcResponse detail: curOrderDetails) {
final var avoids = detail.getMealDetailJson().getAvoidsList();
final var existAvoids = r.getCustomerNoticeJson().getAvoidsList().stream().anyMatch(avoids::contains);
if (existAvoids) {
orderNotice.add("忌口冲突");
break;
}
}
String[] keywords = {"禁食", "禁水", "禁食水"};
final var result = Arrays.stream(keywords)
.filter(keyword -> r.getCustomerNoticeJson().getDoctorsList().stream().anyMatch(str -> str.contains(keyword)))
.findFirst();
if (result != null) {
orderNotice.add("医嘱包含" + result);
}
return OrderDto.builder()
.id(r.getId())
.enterpriseId(r.getEnterpriseId())
.clientId(r.getClientId())
......@@ -453,8 +481,8 @@ public class OrderServiceImpl implements OrderService {
.adjustRoomNo(!r.getSyncRoomNo().isEmpty())
.status(r.getStatus())
.customerNoticeJson(CustomerNotice.builder()
.avoids(r.getCustomerNoticeJson().getAvoidsList())
.doctors(r.getCustomerNoticeJson().getDoctorsList()).build())
.avoids(r.getCustomerNoticeJson().getAvoidsList())
.doctors(r.getCustomerNoticeJson().getDoctorsList()).build())
.onlinePay(r.getOnlinePay())
.payStatus(r.getPayStatusValue())
.payPrice(r.getPayPrice())
......@@ -472,8 +500,11 @@ public class OrderServiceImpl implements OrderService {
.updatedBy(r.getUpdatedBy())
.closeTimeType(r.getCloseTimeTypeValue())
.totalNum(r.getTotalNum())
.isCanceled(r.getIsCanceled())
.customer(getCustomerDetail(r.getCustomerId(), customerResponse.getResponsesList()))
.build()).collect(Collectors.toList());
.orderNotice(orderNotice)
.build();
}).collect(Collectors.toList());
final var orderCountByStatus = OrderCountByStatusDto.builder()
.allServedCount(response.getCountByStatus().getAllServedCount())
.canceledCount(response.getCountByStatus().getCanceledCount())
......@@ -485,6 +516,77 @@ public class OrderServiceImpl implements OrderService {
queryOrderDto.getPn(), queryOrderDto.getPz(), orderCountByStatus);
}
@Override
public OrderPageResult<OrderDto> getCanceledOrdersByPagination(
QueryCanceledOrderByPaginationDto queryOrderDto) {
final var response = orderServiceRpcClient.queryCanceledClientCustomerOrdersByPagination(queryOrderDto);
final var customerIds = response.getResponsesList().stream().map(SingleClientCustomerOrderRpcResponse::getCustomerId).collect(Collectors.toList());
final var customerResponse = clientCustomerServiceRpcClient.getClientCustomersByIds(
queryOrderDto.getEnterpriseId(), customerIds);
final var orderIds = response.getResponsesList().stream().map(SingleClientCustomerOrderRpcResponse::getId).collect(Collectors.toList());
final var orderDetails = orderServiceRpcClient.getOrderDetailsByOrderIds(orderIds);
final var results = response.getResponsesList().stream().map(r -> {
final var curOrderDetails = orderDetails.stream().filter(d ->
d.getOrderId() == r.getId()).collect(Collectors.toList());
List<String> orderNotice = new ArrayList<>();
for (SingleClientCustomerOrderDetailRpcResponse detail: curOrderDetails) {
final var avoids = detail.getMealDetailJson().getAvoidsList();
final var existAvoids = r.getCustomerNoticeJson().getAvoidsList().stream().anyMatch(avoids::contains);
if (existAvoids) {
orderNotice.add("忌口冲突");
break;
}
}
String[] keywords = {"禁食", "禁水", "禁食水"};
final var result = Arrays.stream(keywords)
.filter(keyword -> r.getCustomerNoticeJson().getDoctorsList().stream().anyMatch(str -> str.contains(keyword)))
.findFirst();
if (result != null) {
orderNotice.add("医嘱包含" + result);
}
return OrderDto.builder()
.id(r.getId())
.enterpriseId(r.getEnterpriseId())
.clientId(r.getClientId())
.stallId(r.getStallId())
.customerId(r.getCustomerId())
.openId(r.getOpenId())
.address(r.getAddress())
.orderCode(r.getOrderCode())
.menuId(r.getMenuId())
.pickupCode(r.getPickupCode())
.tableCode(r.getTableCode())
.roomNo(r.getSyncRoomNo().isEmpty() ? r.getRoomNo() : r.getSyncRoomNo())
.adjustRoomNo(!r.getSyncRoomNo().isEmpty())
.status(r.getStatus())
.customerNoticeJson(CustomerNotice.builder()
.avoids(r.getCustomerNoticeJson().getAvoidsList())
.doctors(r.getCustomerNoticeJson().getDoctorsList()).build())
.onlinePay(r.getOnlinePay())
.payStatus(r.getPayStatusValue())
.payPrice(r.getPayPrice())
.payTime(DateUtil.formatDate(r.getPayTime()))
.mealTime(DateUtil.formatDate(r.getMealTime()))
.orderType(r.getOrderTypeValue())
.remark(r.getRemark())
.isDeleted(r.getIsDeleted())
.isRead(r.getIsRead())
.creationSource(r.getCreationSource().getNumber())
.createdBy(r.getCreatedBy())
.createdAt(DateUtil.formatDate(r.getCreatedAt()))
.updateSource(r.getUpdateSource().getNumber())
.updatedAt(DateUtil.formatDate(r.getUpdatedAt()))
.updatedBy(r.getUpdatedBy())
.closeTimeType(r.getCloseTimeTypeValue())
.totalNum(r.getTotalNum())
.isCanceled(r.getIsCanceled())
.customer(getCustomerDetail(r.getCustomerId(), customerResponse.getResponsesList()))
.orderNotice(orderNotice)
.build();
}).collect(Collectors.toList());
return PageUtil.from(results, response.getTotal(), response.getPages(),
queryOrderDto.getPn(), queryOrderDto.getPz(), OrderCountByStatusDto.builder().build());
}
private CustomerDetailDto getCustomerDetail(Integer customerId,
List<SingleClientCustomerRpcResponse> customers) {
final var filterCustomers = customers.stream().filter(c -> c.getId() == customerId)
......
......@@ -158,4 +158,8 @@ public final class DateUtil extends DateUtils {
return Instant.ofEpochMilli(timestamp).atZone(CHINA_ZONE).toLocalDate();
}
public static LocalDateTime toLocalDateTime(long timestamp) {
return Instant.ofEpochMilli(timestamp).atZone(CHINA_ZONE).toLocalDateTime();
}
}
\ No newline at end of file
......
......@@ -139,6 +139,7 @@ message SingleClientCustomerOrderRpcResponse {
bool isConfirm = 34;
bool isAllocation = 35;
string syncRoomNo = 36;
bool isCanceled = 37;
}
message GetClientCustomerOrderByIdRpcRequest {
......@@ -173,12 +174,15 @@ message GetClientCustomerOrdersByCustomerIdRpcResponse {
message QueryClientCustomerOrdersByPaginationRpcRequest {
string keyword = 1;
OrderStatusEnum status = 2;
int32 clientId = 3;
int32 enterpriseId = 4;
int32 pageNo = 5;
int32 pageSize = 6;
repeated int32 stallIds = 7;
bool shouldFilterStatus = 2;
OrderStatusEnum status = 3;
int32 clientId = 4;
int32 enterpriseId = 5;
int32 pageNo = 6;
int32 pageSize = 7;
repeated int32 stallIds = 8;
bool shouldFilterIsCanceled = 9;
bool isCanceled = 10;
}
message OrderCountByStatus {
......@@ -246,6 +250,8 @@ message QueryClientCustomerOrdersByConditionRpcRequest {
bool isAllocation = 47;
bool shouldFilterStatues = 48;
repeated OrderStatusEnum statues = 49;
bool shouldFilterIsCanceled = 50;
bool isCanceled = 51;
}
message QueryClientCustomerOrdersByConditionRpcResponse {
......@@ -375,6 +381,8 @@ message ClientCustomerOrderModification {
bool isAllocation = 53;
bool shouldUpdateSyncRoomNo = 54;
string syncRoomNo = 55;
bool shouldUpdateIsCanceled = 56;
bool isCanceled = 57;
}
message UpdateClientCustomerOrderRpcRequest {
......
......@@ -80,6 +80,7 @@ business-config.hisCustomerQueryUrl = https://nutri.amcare.com.cn/v3/hiscustomer
business-config.clientCustomerQueryUrl = https://nutri.amcare.com.cn/v3/clientcustomers/current/query
business-config.payUrl = http://10.2.5.162:21051/API/Microapp/CreateOrder?appid={0}
business-config.payClientId = Order
business-config.cancelPayUrl = http://10.2.5.162:21051/frame/requestbase64
##Actuator\u914D\u7F6E
management.server.port=8088
......