You've already forked FrameTour-BE
360 lines
16 KiB
Java
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";
|
|
}
|
|
}
|
|
}
|