package com.ycwl.basic.controller.mobile; import com.github.pagehelper.PageInfo; import com.ycwl.basic.constant.BaseContextHandler; import com.ycwl.basic.mapper.SourceMapper; import com.ycwl.basic.mapper.VideoMapper; import com.ycwl.basic.model.pc.source.req.SourceReqQuery; import com.ycwl.basic.model.pc.task.entity.TaskEntity; import com.ycwl.basic.model.pc.video.entity.MemberVideoEntity; import com.ycwl.basic.model.pc.video.entity.VideoEntity; import com.ycwl.basic.pricing.enums.ProductType; import com.ycwl.basic.repository.TemplateRepository; import com.ycwl.basic.repository.VideoRepository; import com.ycwl.basic.repository.VideoTaskRepository; import com.ycwl.basic.service.pc.OrderService; import com.ycwl.basic.utils.ApiResponse; import com.ycwl.basic.pricing.dto.*; import com.ycwl.basic.pricing.service.IPriceCalculationService; import com.ycwl.basic.service.pc.FaceService; import com.ycwl.basic.service.PriceCacheService; import com.ycwl.basic.model.pc.face.resp.FaceRespVO; import com.ycwl.basic.dto.MobileOrderRequest; import com.ycwl.basic.order.service.IOrderService; import com.ycwl.basic.order.dto.OrderV2DetailResponse; import com.ycwl.basic.order.dto.OrderV2ListResponse; import com.ycwl.basic.order.dto.OrderV2PageRequest; import com.ycwl.basic.order.dto.PaymentParamsRequest; import com.ycwl.basic.order.dto.PaymentParamsResponse; import com.ycwl.basic.order.dto.PaymentCallbackResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletRequest; import java.util.List; /** * 移动端订单控制器V2 * 包含价格查询和订单管理功能 */ @Slf4j @RestController @RequestMapping("/api/mobile/order/v2") @RequiredArgsConstructor public class AppOrderV2Controller { private final IPriceCalculationService priceCalculationService; private final FaceService faceService; private final PriceCacheService priceCacheService; private final IOrderService orderService; private final OrderService oldOrderService; private final SourceMapper sourceMapper; private final VideoMapper videoMapper; private final VideoTaskRepository videoTaskRepository; private final TemplateRepository templateRepository; private final VideoRepository videoRepository; /** * 移动端价格计算 * 包含权限验证:验证人脸所属景区与当前用户匹配 * 集成Redis缓存机制,提升查询性能 */ @PostMapping("/calculate") public ApiResponse calculatePrice(@RequestBody MobilePriceCalculationRequest request) { // 获取当前登录用户ID String currentUserIdStr = BaseContextHandler.getUserId(); if (currentUserIdStr == null) { log.warn("移动端价格计算:用户未登录"); return ApiResponse.fail("用户未登录"); } Long currentUserId = Long.valueOf(currentUserIdStr); log.info("移动端价格计算请求: userId={}, faceId={}, products={}", currentUserId, request.getFaceId(), request.getProducts().size()); // 验证faceId参数 if (request.getFaceId() == null) { log.warn("移动端价格计算:faceId参数缺失"); // return ApiResponse.fail("faceId参数不能为空"); // 兼容:兼容旧版本 ProductItem productItem = request.getProducts().getFirst(); switch (productItem.getProductType()) { case VLOG_VIDEO -> { VideoEntity video = videoRepository.getVideo(Long.valueOf(productItem.getProductId())); TaskEntity task = videoTaskRepository.getTaskById(video.getTaskId()); request.setFaceId(task.getFaceId()); } case RECORDING_SET, PHOTO_SET -> request.setFaceId(Long.valueOf(productItem.getProductId())); } } // 查询人脸信息进行权限验证 ApiResponse faceResponse = faceService.getById(request.getFaceId()); if (!faceResponse.isSuccess() || faceResponse.getData() == null) { log.warn("移动端价格计算:人脸信息不存在, faceId={}", request.getFaceId()); return ApiResponse.fail("人脸信息不存在"); } FaceRespVO face = faceResponse.getData(); Long scenicId = face.getScenicId(); request.getProducts().forEach(product -> { switch (product.getProductType()) { case VLOG_VIDEO: List videoEntities = videoMapper.listRelationByFaceAndTemplate(face.getId(), Long.valueOf(product.getProductId())); if (videoEntities != null && !videoEntities.isEmpty()) { product.setQuantity(videoTaskRepository.getTaskLensNum(videoEntities.getFirst().getTaskId())); } else { product.setQuantity(1); } break; case RECORDING_SET: case PHOTO_SET: SourceReqQuery sourceReqQuery = new SourceReqQuery(); sourceReqQuery.setMemberId(currentUserId); sourceReqQuery.setType(product.getProductType() == ProductType.RECORDING_SET ? 1 : 2); sourceReqQuery.setFaceId(face.getId()); Integer count = sourceMapper.countUser(sourceReqQuery); product.setQuantity(count); break; default: log.warn("未知的商品类型,跳过重复购买检查: productType={}", product.getProductType()); break; } }); // 转换为标准价格计算请求 PriceCalculationRequest standardRequest = request.toStandardRequest(currentUserId, scenicId); // 执行价格计算 PriceCalculationResult result = priceCalculationService.calculatePrice(standardRequest); // 将计算结果缓存到Redis String cacheKey = priceCacheService.cachePriceResult(currentUserId, scenicId, request.getProducts(), result); log.info("移动端价格计算完成: userId={}, scenicId={}, originalAmount={}, finalAmount={}, cacheKey={}", currentUserId, scenicId, result.getOriginalAmount(), result.getFinalAmount(), cacheKey); return ApiResponse.success(result); } /** * 移动端下单接口 * 验证价格缓存有效性,确保5分钟内使用缓存价格下单 */ @PostMapping("/add") public ApiResponse addOrder(@RequestBody MobileOrderRequest request) { // 获取当前登录用户ID String currentUserIdStr = BaseContextHandler.getUserId(); if (currentUserIdStr == null) { log.warn("移动端下单:用户未登录"); return ApiResponse.fail("用户未登录"); } Long currentUserId = Long.valueOf(currentUserIdStr); log.info("移动端下单请求: userId={}, faceId={}, products={}, expectedFinalAmount={}", currentUserId, request.getFaceId(), request.getProducts().size(), request.getExpectedFinalAmount()); // 验证必填参数 if (request.getFaceId() == null) { log.warn("移动端下单:faceId参数缺失"); return ApiResponse.fail("faceId参数不能为空"); } if (request.getProducts() == null || request.getProducts().isEmpty()) { log.warn("移动端下单:商品列表为空"); return ApiResponse.fail("商品列表不能为空"); } if (request.getExpectedFinalAmount() == null) { log.warn("移动端下单:预期价格参数缺失"); return ApiResponse.fail("预期价格不能为空"); } // 查询人脸信息进行权限验证 ApiResponse faceResponse = faceService.getById(request.getFaceId()); if (!faceResponse.isSuccess() || faceResponse.getData() == null) { log.warn("移动端下单:人脸信息不存在, faceId={}", request.getFaceId()); return ApiResponse.fail("人脸信息不存在"); } FaceRespVO face = faceResponse.getData(); Long scenicId = face.getScenicId(); // 验证并消费价格缓存(一次性使用) PriceCalculationResult cachedResult = priceCacheService.validateAndConsumePriceCache( currentUserId, scenicId, request.getProducts()); if (cachedResult == null) { log.warn("移动端下单:价格缓存已过期或不存在, userId={}, scenicId={}", currentUserId, scenicId); return ApiResponse.fail("请重新下单!"); } // 验证价格是否匹配 if (cachedResult.getFinalAmount().compareTo(request.getExpectedFinalAmount()) != 0) { log.warn("移动端下单:价格不匹配, cached={}, expected={}, userId={}, scenicId={}", cachedResult.getFinalAmount(), request.getExpectedFinalAmount(), currentUserId, scenicId); return ApiResponse.fail("价格信息变化,请退出后重新查询价格!"); } // 验证原价是否匹配(可选) if (request.getExpectedOriginalAmount() != null && cachedResult.getOriginalAmount().compareTo(request.getExpectedOriginalAmount()) != 0) { log.warn("移动端下单:原价不匹配, cached={}, expected={}, userId={}, scenicId={}", cachedResult.getOriginalAmount(), request.getExpectedOriginalAmount(), currentUserId, scenicId); return ApiResponse.fail("原价信息不匹配,请重新查询价格后再下单"); } log.info("价格缓存验证通过: userId={}, scenicId={}, finalAmount={}", currentUserId, scenicId, cachedResult.getFinalAmount()); // 使用旧版创建订单逻辑 try { Long orderId = oldOrderService.createOrderCompact(currentUserId, request, cachedResult); return ApiResponse.success(String.valueOf(orderId)); } catch (Exception e) { log.warn("移动端下单:订单创建失败, userId={}, scenicId={}, error={}", currentUserId, scenicId, e.getMessage(), e); return ApiResponse.fail("订单创建失败,请稍后重试"); } // 创建订单 // try { // Long orderId = orderService.createOrder(request, currentUserId, scenicId, cachedResult); // // log.info("移动端订单创建成功: orderId={}, userId={}, scenicId={}, finalAmount={}", // orderId, currentUserId, scenicId, cachedResult.getFinalAmount()); // // return ApiResponse.success(orderId.toString()); // // } catch (Exception e) { // log.error("订单创建失败: userId={}, scenicId={}, error={}", currentUserId, scenicId, e.getMessage(), e); // return ApiResponse.fail("订单创建失败,请稍后重试"); // } } // ====== 新增移动端订单查询功能 ====== /** * 用户分页查询自己的订单列表 */ @PostMapping("/page") public ApiResponse> pageUserOrders(@RequestBody OrderV2PageRequest request) { String currentUserIdStr = BaseContextHandler.getUserId(); if (currentUserIdStr == null) { log.warn("用户未登录"); return ApiResponse.fail("用户未登录"); } Long currentUserId = Long.valueOf(currentUserIdStr); request.setMemberId(currentUserId); // 设置当前用户ID,确保只查询自己的订单 log.info("用户查询订单列表: userId={}, request={}", currentUserId, request); try { PageInfo pageInfo = orderService.pageOrdersByUser(request); return ApiResponse.success(pageInfo); } catch (Exception e) { log.error("查询用户订单列表失败: userId={}", currentUserId, e); return ApiResponse.fail("查询失败:" + e.getMessage()); } } /** * 用户查询自己的订单详情 */ @GetMapping("/detail/{orderId}") public ApiResponse getUserOrderDetail(@PathVariable("orderId") Long orderId) { String currentUserIdStr = BaseContextHandler.getUserId(); if (currentUserIdStr == null) { log.warn("用户未登录"); return ApiResponse.fail("用户未登录"); } Long currentUserId = Long.valueOf(currentUserIdStr); log.info("用户查询订单详情: userId={}, orderId={}", currentUserId, orderId); try { OrderV2DetailResponse detail = orderService.getOrderDetail(orderId); if (detail == null) { return ApiResponse.fail("订单不存在"); } // 验证订单是否属于当前用户 if (!currentUserId.equals(detail.getMemberId())) { log.warn("用户尝试访问他人订单: userId={}, orderId={}, orderOwner={}", currentUserId, orderId, detail.getMemberId()); return ApiResponse.fail("无权访问该订单"); } return ApiResponse.success(detail); } catch (Exception e) { log.error("查询用户订单详情失败: userId={}, orderId={}", currentUserId, orderId, e); return ApiResponse.fail("查询失败:" + e.getMessage()); } } // ====== 支付相关接口 ====== /** * 获取订单支付参数 * 用于小程序调起支付 */ @PostMapping("/{orderId}/payment-params") public ApiResponse getPaymentParams( @PathVariable("orderId") Long orderId, @RequestBody PaymentParamsRequest request) { String currentUserIdStr = BaseContextHandler.getUserId(); if (currentUserIdStr == null) { log.warn("用户未登录"); return ApiResponse.fail("用户未登录"); } Long currentUserId = Long.valueOf(currentUserIdStr); log.info("获取支付参数: userId={}, orderId={}", currentUserId, orderId); return oldOrderService.getPaymentParams(orderId, currentUserId, request); // // try { // PaymentParamsResponse response = orderService.getPaymentParams(orderId, currentUserId, request); // return ApiResponse.success(response); // } catch (Exception e) { // log.error("获取支付参数失败: userId={}, orderId={}", currentUserId, orderId, e); // return ApiResponse.fail(e.getMessage()); // } } /** * 支付回调处理接口 * 供第三方支付平台回调使用 */ @PostMapping("/payment/callback/{scenicId}") public String handlePaymentCallback( @PathVariable("scenicId") Long scenicId, HttpServletRequest request) { log.info("接收支付回调: scenicId={}", scenicId); try { PaymentCallbackResponse response = orderService.handlePaymentCallback(scenicId, request); if (response.isSuccess()) { log.info("支付回调处理成功: scenicId={}, orderId={}, statusChangeType={}", scenicId, response.getOrderId(), response.getStatusChangeType()); return "SUCCESS"; // 返回给第三方支付平台的成功标识 } else { log.error("支付回调处理失败: scenicId={}, message={}", scenicId, response.getMessage()); return "FAIL"; // 返回给第三方支付平台的失败标识 } } catch (Exception e) { log.error("支付回调异常: scenicId={}", scenicId, e); return "FAIL"; } } }