package com.tcm.training.admin.live.service; import cn.idev.excel.FastExcel; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.tcm.training.admin.common.DateTimes; import com.tcm.training.admin.common.PageResponse; import com.tcm.training.admin.common.PhoneMasker; import com.tcm.training.admin.live.dto.CoursePurchaseOrderRefundRequest; import com.tcm.training.admin.live.entity.CoursePurchaseOrder; import com.tcm.training.admin.live.excel.CoursePurchaseOrderExcelRow; import com.tcm.training.admin.live.mapper.CoursePurchaseOrderMapper; import com.tcm.training.admin.live.vo.CoursePurchaseOrderListItemVO; import com.tcm.training.admin.live.vo.CoursePurchaseOrderListRow; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; import java.net.URLEncoder; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Service public class CoursePurchaseOrderAppService { private static final String COURSE_TYPE_HEALTH = "HEALTH_COURSE"; private static final String STATUS_PAID = "PAID"; private static final String STATUS_REFUNDED = "REFUNDED"; private static final String STATUS_WAITING_PAYMENT = "WAITING_PAYMENT"; private static final String STATUS_CANCELLED = "CANCELLED"; private static final String PAYMENT_METHOD_WECHAT = "WECHAT"; private static final String PAYMENT_METHOD_ALIPAY = "ALIPAY"; private final CoursePurchaseOrderMapper coursePurchaseOrderMapper; public CoursePurchaseOrderAppService(CoursePurchaseOrderMapper coursePurchaseOrderMapper) { this.coursePurchaseOrderMapper = coursePurchaseOrderMapper; } public PageResponse page(int page, int pageSize, String orderNo, String userPhone, String productName, String paymentMethod, String status, LocalDateTime createdFrom, LocalDateTime createdTo) { IPage result = coursePurchaseOrderMapper.selectOrderPage( new Page(page, pageSize), trimToNull(orderNo), trimToNull(userPhone), trimToNull(productName), trimToNull(paymentMethod), normalizeStatus(status), createdFrom, createdTo ); return PageResponse.of(toList(result.getRecords()), result.getTotal(), result.getCurrent(), result.getSize()); } public void export(HttpServletResponse response, String orderNo, String userPhone, String productName, String paymentMethod, String status, LocalDateTime createdFrom, LocalDateTime createdTo) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String filename = URLEncoder.encode("养生课程购买订单", "UTF-8").replace("+", "%20"); response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + filename + ".xlsx"); List rows = coursePurchaseOrderMapper.selectOrderList( trimToNull(orderNo), trimToNull(userPhone), trimToNull(productName), trimToNull(paymentMethod), normalizeStatus(status), createdFrom, createdTo ); List exports = new ArrayList(); for (CoursePurchaseOrderListRow row : rows) { CoursePurchaseOrderExcelRow export = new CoursePurchaseOrderExcelRow(); export.setOrderNo(row.getOrderNo()); export.setUserNickname(defaultText(row.getUserNickname())); export.setUserPhone(PhoneMasker.mask(row.getUserPhone())); export.setProductName(defaultText(row.getProductName())); export.setOrderAmount(row.getOrderAmount()); export.setPaymentMethod(paymentMethodText(row.getPaymentMethod())); export.setStatus(statusText(row.getStatus())); export.setRefundAmount(resolveRefundAmount(row.getStatus(), row.getOrderAmount(), row.getRefundAmount())); export.setRefundRemark(defaultText(row.getRefundRemark())); export.setCreatedAt(DateTimes.format(row.getCreatedAt())); exports.add(export); } FastExcel.write(response.getOutputStream(), CoursePurchaseOrderExcelRow.class) .sheet("养生课程购买订单") .doWrite(exports); } public void refund(Long id, CoursePurchaseOrderRefundRequest request) { CoursePurchaseOrder order = requireOrder(id); if (!STATUS_PAID.equals(order.getStatus())) { throw new IllegalArgumentException("仅已支付订单允许退款"); } if (request.getRefundAmount().compareTo(order.getOrderAmount()) > 0) { throw new IllegalArgumentException("退款金额不能超过用户实付金额"); } // TODO: 支付方案确认后,在这里接入真实退款流程并在退款成功后更新订单状态与退款时间。 throw new IllegalArgumentException("退款功能待支付方案确认后接入"); } @Transactional public void delete(Long id) { CoursePurchaseOrder order = requireOrder(id); if (!STATUS_REFUNDED.equals(order.getStatus())) { throw new IllegalArgumentException("仅已退款订单允许删除"); } coursePurchaseOrderMapper.deleteById(id); } private CoursePurchaseOrder requireOrder(Long id) { CoursePurchaseOrder order = coursePurchaseOrderMapper.selectOne(new LambdaQueryWrapper() .eq(CoursePurchaseOrder::getId, id) .eq(CoursePurchaseOrder::getCourseType, COURSE_TYPE_HEALTH) .eq(CoursePurchaseOrder::getDeleted, 0) .last("limit 1")); if (order == null) { throw new IllegalArgumentException("养生课程购买订单不存在"); } return order; } private List toList(List rows) { List list = new ArrayList(); for (CoursePurchaseOrderListRow row : rows) { CoursePurchaseOrderListItemVO item = new CoursePurchaseOrderListItemVO(); item.setId(row.getId()); item.setOrderNo(row.getOrderNo()); item.setUserNickname(row.getUserNickname()); item.setUserPhone(PhoneMasker.mask(row.getUserPhone())); item.setProductName(row.getProductName()); item.setOrderAmount(row.getOrderAmount()); item.setPaymentMethod(row.getPaymentMethod()); item.setRefundAmount(resolveRefundAmount(row.getStatus(), row.getOrderAmount(), row.getRefundAmount())); item.setRefundRemark(row.getRefundRemark()); item.setStatus(row.getStatus()); item.setCreatedAt(row.getCreatedAt()); list.add(item); } return list; } private BigDecimal resolveRefundAmount(String status, BigDecimal orderAmount, BigDecimal refundAmount) { if (!STATUS_REFUNDED.equals(status)) { return null; } return refundAmount == null ? orderAmount : refundAmount; } private String normalizeStatus(String status) { String normalized = trimToNull(status); if (normalized == null) { return null; } if (STATUS_PAID.equals(normalized) || STATUS_REFUNDED.equals(normalized)) { return normalized; } return null; } private String trimToNull(String value) { if (value == null) { return null; } String trimmed = value.trim(); return trimmed.isEmpty() ? null : trimmed; } private String defaultText(String value) { return value == null || value.trim().isEmpty() ? "-" : value.trim(); } private String paymentMethodText(String paymentMethod) { if (PAYMENT_METHOD_WECHAT.equals(paymentMethod)) { return "微信支付"; } if (PAYMENT_METHOD_ALIPAY.equals(paymentMethod)) { return "支付宝支付"; } return "-"; } private String statusText(String status) { if (STATUS_WAITING_PAYMENT.equals(status)) { return "待支付"; } if (STATUS_PAID.equals(status)) { return "已支付"; } if (STATUS_REFUNDED.equals(status)) { return "已退款"; } if (STATUS_CANCELLED.equals(status)) { return "已取消"; } return "-"; } }