From 1d724dadd967d84391d645b2fed8d15d20881c00 Mon Sep 17 00:00:00 2001 From: z Date: Mon, 4 May 2026 00:39:31 +0800 Subject: [PATCH] feat: include MifaPay orders in admin list --- ...InAppPurchaseDetailsClientServiceImpl.java | 347 ++++++++++++++++-- 1 file changed, 307 insertions(+), 40 deletions(-) diff --git a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/InAppPurchaseDetailsClientServiceImpl.java b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/InAppPurchaseDetailsClientServiceImpl.java index aa2fc767..a21b1e26 100644 --- a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/InAppPurchaseDetailsClientServiceImpl.java +++ b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/InAppPurchaseDetailsClientServiceImpl.java @@ -1,17 +1,38 @@ -package com.red.circle.order.inner.service.impl; - -import com.red.circle.order.infra.database.mongo.order.InAppPurchaseDetailsService; -import com.red.circle.order.infra.database.mongo.order.entity.assist.CountUserInAppPurchase; -import com.red.circle.order.inner.convertor.InAppPurchaseDetailsInnerConvert; -import com.red.circle.order.inner.model.cmd.inapp.InAppPurchaseDetailsQryCmd; -import com.red.circle.order.inner.model.dto.inapp.CountUserInAppPurchaseDTO; -import com.red.circle.order.inner.model.dto.inapp.InAppPurchaseDetailsDTO; -import com.red.circle.order.inner.service.InAppPurchaseDetailsClientService; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; +package com.red.circle.order.inner.service.impl; + +import com.red.circle.framework.mybatis.constant.PageConstant; +import com.red.circle.order.app.common.MiFaPayMysqlOrderSupport; +import com.red.circle.order.infra.database.mongo.order.InAppPurchaseDetailsService; +import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails; +import com.red.circle.order.infra.database.mongo.order.entity.assist.CountUserInAppPurchase; +import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseEventNotice; +import com.red.circle.order.infra.database.rds.entity.order.OrderUserPurchasePay; +import com.red.circle.order.infra.database.rds.entity.order.OrderUserPurchasePayNotice; +import com.red.circle.order.infra.database.rds.service.order.OrderUserPurchasePayNoticeService; +import com.red.circle.order.infra.database.rds.service.order.OrderUserPurchasePayService; +import com.red.circle.order.inner.convertor.InAppPurchaseDetailsInnerConvert; +import com.red.circle.order.inner.model.cmd.inapp.InAppPurchaseDetailsQryCmd; +import com.red.circle.order.inner.model.dto.inapp.CountUserInAppPurchaseDTO; +import com.red.circle.order.inner.model.dto.inapp.InAppPurchaseDetailsDTO; +import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseStatus; +import com.red.circle.order.inner.service.InAppPurchaseDetailsClientService; +import com.red.circle.tool.core.text.StringUtils; +import com.red.circle.tool.core.json.JacksonUtils; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; /** * 内购详情. @@ -20,32 +41,278 @@ import org.springframework.stereotype.Service; */ @Service @RequiredArgsConstructor -public class InAppPurchaseDetailsClientServiceImpl implements InAppPurchaseDetailsClientService { - - private final InAppPurchaseDetailsService inAppPurchaseDetailsService; - private final InAppPurchaseDetailsInnerConvert inAppPurchaseDetailsInnerConvert; - - @Override - public InAppPurchaseDetailsDTO getById(String id) { - return inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsDTO( - inAppPurchaseDetailsService.getById(id) - ); - } - - @Override - public List list(InAppPurchaseDetailsQryCmd qryCmd) { - return inAppPurchaseDetailsInnerConvert.toListInAppPurchaseDetailsDTO( - inAppPurchaseDetailsService.list( - inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsQuery(qryCmd)) - ); - } - - @Override - public Map statistics(InAppPurchaseDetailsQryCmd qryCmd) { - return inAppPurchaseDetailsService.statistics( - inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsQuery(qryCmd) - ); - } +public class InAppPurchaseDetailsClientServiceImpl implements InAppPurchaseDetailsClientService { + + private static final String H5_PLATFORM = "H5"; + + private final InAppPurchaseDetailsService inAppPurchaseDetailsService; + private final InAppPurchaseDetailsInnerConvert inAppPurchaseDetailsInnerConvert; + private final OrderUserPurchasePayService orderUserPurchasePayService; + private final OrderUserPurchasePayNoticeService orderUserPurchasePayNoticeService; + + @Override + public InAppPurchaseDetailsDTO getById(String id) { + InAppPurchaseDetails details = inAppPurchaseDetailsService.getById(id); + if (Objects.isNull(details)) { + details = getMiFaPayDetails(id); + } + return inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsDTO( + details + ); + } + + @Override + public List list(InAppPurchaseDetailsQryCmd qryCmd) { + List mongoOrders = safeList( + inAppPurchaseDetailsInnerConvert.toListInAppPurchaseDetailsDTO( + inAppPurchaseDetailsService.list( + inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsQuery(qryCmd)) + )); + List miFaPayOrders = listMiFaPayOrders(qryCmd, true).stream() + .map(order -> inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsDTO( + toMiFaPayDetails(order, false))) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + return mergePage(mongoOrders, miFaPayOrders, normalizeLimit(qryCmd.getLimit())); + } + + @Override + public Map statistics(InAppPurchaseDetailsQryCmd qryCmd) { + Map result = new HashMap<>(inAppPurchaseDetailsService.statistics( + inAppPurchaseDetailsInnerConvert.toInAppPurchaseDetailsQuery(qryCmd) + )); + + List miFaPayOrders = listMiFaPayOrders(qryCmd, false); + if (miFaPayOrders.isEmpty()) { + return result; + } + + long totalOrderCount = longValue(result.get("totalOrderCount")) + miFaPayOrders.size(); + BigDecimal totalAmount = decimalValue(result.get("totalAmount")) + .add(sum(miFaPayOrders, OrderUserPurchasePay::getPaymentAmount)); + BigDecimal totalAmountUsd = decimalValue(result.get("totalAmountUsd")) + .add(sum(miFaPayOrders, OrderUserPurchasePay::getComputeUsdAmount)); + + result.put("totalOrderCount", totalOrderCount); + result.put("totalAmount", totalAmount); + result.put("totalAmountUsd", totalAmountUsd); + result.put("averageOrderAmount", average(totalAmount, totalOrderCount)); + return result; + } + + private InAppPurchaseDetails getMiFaPayDetails(String id) { + Long mysqlOrderId = MiFaPayMysqlOrderSupport.parseOrderId(id); + if (Objects.isNull(mysqlOrderId)) { + return null; + } + OrderUserPurchasePay order = orderUserPurchasePayService.getById(mysqlOrderId); + if (Objects.isNull(order) || !MiFaPayMysqlOrderSupport.isMiFaPay(order.getFactoryCode())) { + return null; + } + return toMiFaPayDetails(order, true); + } + + private List listMiFaPayOrders(InAppPurchaseDetailsQryCmd qryCmd, + boolean pageable) { + if (!shouldIncludeMiFaPay(qryCmd)) { + return Collections.emptyList(); + } + + Long id = parseFilterId(qryCmd.getId()); + if (StringUtils.isNotBlank(qryCmd.getId()) && Objects.isNull(id)) { + return Collections.emptyList(); + } + + Long lastId = parseFilterId(qryCmd.getLastId()); + if (pageable && StringUtils.isNotBlank(qryCmd.getLastId()) && Objects.isNull(lastId)) { + return Collections.emptyList(); + } + + String payStatus = toMiFaPayStatus(qryCmd.getStatus()); + if (Objects.nonNull(qryCmd.getStatus()) && Objects.isNull(payStatus)) { + return Collections.emptyList(); + } + + List sysOrigins = parseSysOrigins(qryCmd.getSysOrigins()); + var query = orderUserPurchasePayService.query() + .eq(OrderUserPurchasePay::getFactoryCode, MiFaPayMysqlOrderSupport.FACTORY_CODE) + .eq(Objects.nonNull(id), OrderUserPurchasePay::getId, id) + .eq(Objects.nonNull(qryCmd.getReceiptType()), OrderUserPurchasePay::getReceiptType, + Objects.nonNull(qryCmd.getReceiptType()) ? qryCmd.getReceiptType().name() : null) + .eq(StringUtils.isNotBlank(qryCmd.getTrackId()), OrderUserPurchasePay::getReferenceId, + qryCmd.getTrackId()) + .in(!sysOrigins.isEmpty(), OrderUserPurchasePay::getSysOrigin, sysOrigins) + .eq(Objects.nonNull(qryCmd.getAcceptUserId()), OrderUserPurchasePay::getUserId, + qryCmd.getAcceptUserId()) + .eq(StringUtils.isNotBlank(qryCmd.getOrderId()), OrderUserPurchasePay::getFactoryOrderId, + qryCmd.getOrderId()) + .eq(StringUtils.isNotBlank(qryCmd.getEnv()), OrderUserPurchasePay::getEvn, + qryCmd.getEnv()) + .eq(StringUtils.isNotBlank(qryCmd.getCountryCode()), OrderUserPurchasePay::getCountryCode, + qryCmd.getCountryCode()) + .eq(Objects.nonNull(payStatus), OrderUserPurchasePay::getPayStatus, payStatus) + .ge(Objects.nonNull(qryCmd.getStartTime()), OrderUserPurchasePay::getCreateTime, + toTimestamp(qryCmd.getStartTime())) + .le(Objects.nonNull(qryCmd.getEndTime()), OrderUserPurchasePay::getCreateTime, + toTimestamp(qryCmd.getEndTime())) + .lt(pageable && Objects.nonNull(lastId), OrderUserPurchasePay::getId, lastId) + .orderByDesc(OrderUserPurchasePay::getId); + + if (pageable) { + query.last("LIMIT " + normalizeLimit(qryCmd.getLimit())); + } + return query.list(); + } + + private boolean shouldIncludeMiFaPay(InAppPurchaseDetailsQryCmd qryCmd) { + if (StringUtils.isNotBlank(qryCmd.getFactoryPlatform()) + && !Objects.equals(H5_PLATFORM, qryCmd.getFactoryPlatform())) { + return false; + } + return StringUtils.isBlank(qryCmd.getSubscribeId()); + } + + private InAppPurchaseDetails toMiFaPayDetails(OrderUserPurchasePay order, boolean withNotices) { + InAppPurchaseDetails details = MiFaPayMysqlOrderSupport.toInAppPurchaseDetails(order) + .setMetadata(miFaPayMetadata(order)) + .setPayNotices(withNotices ? miFaPayNotices(order.getId()) : Collections.emptyList()) + .setVersion(0); + return details; + } + + private Map miFaPayMetadata(OrderUserPurchasePay order) { + Map metadata = new HashMap<>(); + putMetadata(metadata, "payStatus", order.getPayStatus()); + putMetadata(metadata, "refundStatus", order.getRefundStatus()); + putMetadata(metadata, "paymentChannel", order.getPaymentChannel()); + putMetadata(metadata, "factoryChannel", order.getFactoryChannel()); + putMetadata(metadata, "payCountryId", order.getPayCountryId()); + putMetadata(metadata, "countryId", order.getCountryId()); + putMetadata(metadata, "reason", order.getReason()); + return metadata; + } + + private void putMetadata(Map metadata, String key, Object value) { + if (Objects.nonNull(value) && StringUtils.isNotBlank(Objects.toString(value, ""))) { + metadata.put(key, Objects.toString(value)); + } + } + + private List miFaPayNotices(Long purchasePayId) { + if (Objects.isNull(purchasePayId)) { + return Collections.emptyList(); + } + return orderUserPurchasePayNoticeService.query() + .eq(OrderUserPurchasePayNotice::getPurchasePayId, purchasePayId) + .orderByAsc(OrderUserPurchasePayNotice::getId) + .list() + .stream() + .map(notice -> new InAppPurchaseEventNotice() + .setEventId(notice.getEventId()) + .setNoticeType(notice.getNoticeType()) + .setNoticeData(parseNoticeData(notice.getNoticeData())) + .setCreateTime(notice.getCreateTime())) + .collect(Collectors.toList()); + } + + private Object parseNoticeData(String noticeData) { + if (StringUtils.isBlank(noticeData)) { + return ""; + } + try { + return JacksonUtils.readValue(noticeData, Object.class); + } catch (Exception ignore) { + return noticeData; + } + } + + private List mergePage(List first, + List second, int limit) { + List result = new ArrayList<>(); + result.addAll(first); + result.addAll(second); + return result.stream() + .filter(Objects::nonNull) + .sorted(Comparator.comparing(InAppPurchaseDetailsDTO::getId, + this::compareIdDesc)) + .limit(limit) + .collect(Collectors.toList()); + } + + private int compareIdDesc(String left, String right) { + Long leftId = parseFilterId(left); + Long rightId = parseFilterId(right); + if (Objects.nonNull(leftId) && Objects.nonNull(rightId)) { + return rightId.compareTo(leftId); + } + return Objects.toString(right, "").compareTo(Objects.toString(left, "")); + } + + private List safeList(List list) { + return Objects.isNull(list) ? Collections.emptyList() : list; + } + + private List parseSysOrigins(String sysOrigins) { + if (StringUtils.isBlank(sysOrigins)) { + return Collections.emptyList(); + } + return Arrays.stream(sysOrigins.split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + } + + private Long parseFilterId(String id) { + return MiFaPayMysqlOrderSupport.parseOrderId(id); + } + + private String toMiFaPayStatus(InAppPurchaseStatus status) { + if (Objects.isNull(status)) { + return null; + } + if (Objects.equals(InAppPurchaseStatus.CREATE, status)) { + return MiFaPayMysqlOrderSupport.PAY_STATUS_PREPAYMENT; + } + if (Objects.equals(InAppPurchaseStatus.SUCCESS, status)) { + return MiFaPayMysqlOrderSupport.PAY_STATUS_SUCCESSFUL; + } + if (Objects.equals(InAppPurchaseStatus.FAIL, status)) { + return MiFaPayMysqlOrderSupport.PAY_STATUS_FAIL; + } + return null; + } + + private Timestamp toTimestamp(Long value) { + return Objects.isNull(value) ? null : new Timestamp(value); + } + + private int normalizeLimit(Integer limit) { + return Objects.isNull(limit) || limit <= 0 ? PageConstant.DEFAULT_LIMIT_SIZE : limit; + } + + private long longValue(Object value) { + return Objects.isNull(value) ? 0L : Long.parseLong(Objects.toString(value)); + } + + private BigDecimal decimalValue(Object value) { + return Objects.isNull(value) ? BigDecimal.ZERO : new BigDecimal(Objects.toString(value)); + } + + private BigDecimal sum(List orders, + java.util.function.Function mapper) { + return orders.stream() + .map(mapper) + .filter(Objects::nonNull) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + private BigDecimal average(BigDecimal amount, long count) { + if (count <= 0) { + return BigDecimal.ZERO; + } + return amount.divide(BigDecimal.valueOf(count), 8, RoundingMode.HALF_UP).stripTrailingZeros(); + } @Override public List countUserInAppPurchase(String sysOrigin,