package com.tcm.training.admin.app.points.service; 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.app.auth.service.AppAuthSessionService; import com.tcm.training.admin.app.points.vo.AppPointsExchangeOrderDetailVO; import com.tcm.training.admin.app.points.vo.AppPointsExchangeOrderItemVO; import com.tcm.training.admin.common.PageResponse; import com.tcm.training.admin.live.entity.Clinic; import com.tcm.training.admin.live.mapper.ClinicMapper; import com.tcm.training.admin.member.entity.AppUser; import com.tcm.training.admin.member.mapper.UserMapper; import com.tcm.training.admin.points.entity.PointsExchangeOrder; import com.tcm.training.admin.points.entity.PointsExchangeOrderItem; import com.tcm.training.admin.points.mapper.PointsExchangeOrderItemMapper; import com.tcm.training.admin.points.mapper.PointsExchangeOrderMapper; import com.tcm.training.admin.points.row.PointsExchangeItemRow; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; /** * APP 兑换订单服务。 */ @Service public class AppPointsExchangeOrderService { private static final int STATUS_PENDING = 0; private static final int STATUS_RECEIVING = 1; private static final int STATUS_COMPLETED = 2; private final AppAuthSessionService appAuthSessionService; private final PointsExchangeOrderItemMapper pointsExchangeOrderItemMapper; private final PointsExchangeOrderMapper pointsExchangeOrderMapper; private final UserMapper userMapper; private final ClinicMapper clinicMapper; /** * 注入 APP 兑换订单所需依赖。 */ public AppPointsExchangeOrderService(AppAuthSessionService appAuthSessionService, PointsExchangeOrderItemMapper pointsExchangeOrderItemMapper, PointsExchangeOrderMapper pointsExchangeOrderMapper, UserMapper userMapper, ClinicMapper clinicMapper) { this.appAuthSessionService = appAuthSessionService; this.pointsExchangeOrderItemMapper = pointsExchangeOrderItemMapper; this.pointsExchangeOrderMapper = pointsExchangeOrderMapper; this.userMapper = userMapper; this.clinicMapper = clinicMapper; } /** * 分页查询当前登录用户的兑换订单列表。 */ public PageResponse page(int page, int pageSize, Integer status) { Long appUserId = appAuthSessionService.requireLoginUserId(); int normalizedPage = page <= 0 ? 1 : page; int normalizedPageSize = pageSize <= 0 ? 10 : pageSize; IPage result = pointsExchangeOrderItemMapper.selectAppItemPage( new Page(normalizedPage, normalizedPageSize), appUserId, normalizeStatus(status) ); return PageResponse.of(toItemList(result.getRecords()), result.getTotal(), result.getCurrent(), result.getSize()); } /** * 获取当前登录用户的兑换订单详情。 */ public AppPointsExchangeOrderDetailVO detail(Long id) { Long appUserId = appAuthSessionService.requireLoginUserId(); PointsExchangeItemRow row = requireOwnedItemRow(id, appUserId); AppUser user = userMapper.selectById(appUserId); Clinic clinic = resolveClinic(user == null ? null : user.getClinicId()); AppPointsExchangeOrderDetailVO detail = new AppPointsExchangeOrderDetailVO(); detail.setId(row.getId()); detail.setOrderNo(trimToEmpty(row.getOrderNo())); detail.setStatus(row.getStatus()); detail.setStatusText(resolveStatusText(row.getStatus())); detail.setTrackingNo(trimToEmpty(row.getTrackingNo())); detail.setCreatedAt(row.getCreatedAt()); detail.setShippedAt(row.getShippedAt()); detail.setCompletedAt(row.getCompletedAt()); detail.setCanConfirmReceived(Boolean.valueOf(isReceivable(row.getStatus()))); detail.setCanDelete(Boolean.valueOf(isDeletable(row.getStatus()))); AppPointsExchangeOrderDetailVO.GiftVO gift = new AppPointsExchangeOrderDetailVO.GiftVO(); gift.setCover(trimToEmpty(row.getGiftCover())); gift.setName(trimToEmpty(row.getGiftName())); gift.setRequiredPoints(defaultNumber(row.getRequiredPoints())); gift.setItemCount(Integer.valueOf(1)); gift.setTotalPoints(defaultNumber(row.getSettledPoints())); detail.setGift(gift); AppPointsExchangeOrderDetailVO.ClinicVO clinicVO = new AppPointsExchangeOrderDetailVO.ClinicVO(); clinicVO.setName(trimToEmpty(clinic == null ? null : clinic.getName())); clinicVO.setAddress(trimToEmpty(clinic == null ? null : clinic.getAddress())); detail.setClinic(clinicVO); return detail; } /** * 确认当前登录用户的兑换订单已取货。 */ @Transactional public void confirmReceived(Long id) { Long appUserId = appAuthSessionService.requireLoginUserId(); PointsExchangeOrderItem item = requireOwnedItem(id, appUserId); if (!isReceivable(item.getStatus())) { throw new IllegalArgumentException("当前订单不允许确认取货"); } LocalDateTime now = LocalDateTime.now(); item.setStatus(STATUS_COMPLETED); item.setCompletedAt(now); pointsExchangeOrderItemMapper.updateById(item); refreshSummaryOrderStatus(item.getOrderId(), now); } /** * 删除当前登录用户已完成的兑换订单。 */ @Transactional public void delete(Long id) { Long appUserId = appAuthSessionService.requireLoginUserId(); PointsExchangeOrderItem item = requireOwnedItem(id, appUserId); if (!isDeletable(item.getStatus())) { throw new IllegalArgumentException("当前订单不允许删除"); } Long orderId = item.getOrderId(); pointsExchangeOrderItemMapper.deleteById(item.getId()); refreshSummaryOrderAfterDelete(orderId); } /** * 将数据库行转换为 APP 列表展示结构。 */ private List toItemList(List rows) { List list = new ArrayList(); if (rows == null) { return list; } for (PointsExchangeItemRow row : rows) { AppPointsExchangeOrderItemVO item = new AppPointsExchangeOrderItemVO(); item.setId(row.getId()); item.setOrderNo(trimToEmpty(row.getOrderNo())); item.setGiftCover(trimToEmpty(row.getGiftCover())); item.setGiftName(trimToEmpty(row.getGiftName())); item.setRequiredPoints(defaultNumber(row.getRequiredPoints())); item.setItemCount(Integer.valueOf(1)); item.setTotalPoints(defaultNumber(row.getSettledPoints())); item.setStatus(row.getStatus()); item.setStatusText(resolveStatusText(row.getStatus())); list.add(item); } return list; } /** * 校验并返回当前登录用户名下的兑换订单行。 */ private PointsExchangeItemRow requireOwnedItemRow(Long id, Long appUserId) { PointsExchangeItemRow row = pointsExchangeOrderItemMapper.selectAppItemDetail(id, appUserId); if (row == null) { throw new IllegalArgumentException("兑换订单不存在"); } return row; } /** * 校验并返回当前登录用户名下的兑换订单实体。 */ private PointsExchangeOrderItem requireOwnedItem(Long id, Long appUserId) { PointsExchangeOrderItem item = pointsExchangeOrderItemMapper.selectOne(new LambdaQueryWrapper() .eq(PointsExchangeOrderItem::getId, id) .eq(PointsExchangeOrderItem::getAppUserId, appUserId) .eq(PointsExchangeOrderItem::getDeleted, 0) .last("limit 1")); if (item == null) { throw new IllegalArgumentException("兑换订单不存在"); } return item; } /** * 读取用户绑定诊所,不存在时返回空。 */ private Clinic resolveClinic(Long clinicId) { if (clinicId == null) { return null; } Clinic clinic = clinicMapper.selectById(clinicId); if (clinic == null || clinic.getDeleted() == null || clinic.getDeleted().intValue() != 0) { return null; } return clinic; } /** * 刷新汇总订单状态,保证确认取货后与明细一致。 */ private void refreshSummaryOrderStatus(Long orderId, LocalDateTime completedAt) { if (orderId == null) { return; } PointsExchangeOrder order = pointsExchangeOrderMapper.selectById(orderId); if (order == null || order.getDeleted() == null || order.getDeleted().intValue() != 0) { return; } List items = pointsExchangeOrderItemMapper.selectList(new LambdaQueryWrapper() .eq(PointsExchangeOrderItem::getDeleted, 0) .eq(PointsExchangeOrderItem::getOrderId, orderId)); boolean allCompleted = !items.isEmpty(); for (PointsExchangeOrderItem item : items) { if (!isCompleted(item.getStatus())) { allCompleted = false; break; } } if (allCompleted) { order.setStatus(STATUS_COMPLETED); order.setCompletedAt(completedAt); pointsExchangeOrderMapper.updateById(order); } } /** * 删除订单项后刷新汇总订单数量与状态;无明细时同步删除汇总单。 */ private void refreshSummaryOrderAfterDelete(Long orderId) { if (orderId == null) { return; } PointsExchangeOrder order = pointsExchangeOrderMapper.selectById(orderId); if (order == null || order.getDeleted() == null || order.getDeleted().intValue() != 0) { return; } List items = pointsExchangeOrderItemMapper.selectList(new LambdaQueryWrapper() .eq(PointsExchangeOrderItem::getDeleted, 0) .eq(PointsExchangeOrderItem::getOrderId, orderId)); if (items == null || items.isEmpty()) { pointsExchangeOrderMapper.deleteById(orderId); return; } int totalPoints = 0; boolean allCompleted = true; for (PointsExchangeOrderItem item : items) { totalPoints += defaultNumber(item.getSettledPoints()).intValue(); if (!isCompleted(item.getStatus())) { allCompleted = false; } } order.setItemCount(Integer.valueOf(items.size())); order.setTotalPoints(Integer.valueOf(totalPoints)); if (allCompleted) { order.setStatus(STATUS_COMPLETED); } pointsExchangeOrderMapper.updateById(order); } /** * 归一化列表筛选状态,仅允许已定义状态值进入 SQL 条件。 */ private Integer normalizeStatus(Integer status) { if (status == null) { return null; } if (status.intValue() == STATUS_PENDING || status.intValue() == STATUS_RECEIVING || status.intValue() == STATUS_COMPLETED) { return status; } return null; } /** * 判断当前状态是否允许确认取货。 */ private boolean isReceivable(Integer status) { return status != null && status.intValue() == STATUS_RECEIVING; } /** * 判断当前状态是否允许删除。 */ private boolean isDeletable(Integer status) { return status != null && status.intValue() == STATUS_COMPLETED; } /** * 判断当前状态是否为已完成。 */ private boolean isCompleted(Integer status) { return status != null && status.intValue() == STATUS_COMPLETED; } /** * 将订单状态转换为页面展示文案。 */ private String resolveStatusText(Integer status) { if (status == null) { return ""; } if (status.intValue() == STATUS_PENDING) { return "待发货"; } if (status.intValue() == STATUS_RECEIVING) { return "待取货"; } if (status.intValue() == STATUS_COMPLETED) { return "兑换完成"; } return ""; } /** * 将可空文本归一化为空字符串。 */ private String trimToEmpty(String value) { if (value == null) { return ""; } return value.trim(); } /** * 将可空积分归一化为非空数字。 */ private Integer defaultNumber(Integer value) { return Integer.valueOf(value == null ? 0 : value.intValue()); } }