Files
FrameTour-BE/src/main/java/com/ycwl/basic/controller/mobile/AppOrderV2Controller.java
2025-09-23 17:54:49 +08:00

360 lines
16 KiB
Java

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<PriceCalculationResult> 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<FaceRespVO> 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<MemberVideoEntity> 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<String> 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<FaceRespVO> 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<PageInfo<OrderV2ListResponse>> 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<OrderV2ListResponse> 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<OrderV2DetailResponse> 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<PaymentParamsResponse> 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";
}
}
}