You've already forked FrameTour-BE
Merge branch 'refs/heads/order_v2'
# Conflicts: # src/main/java/com/ycwl/basic/controller/mobile/manage/AppScenicAccountController.java
This commit is contained in:
@@ -88,7 +88,7 @@ public class OrderBiz {
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
priceObj.setScenicAllPrice(scenic.getPrice());
|
||||
if (scenicConfig != null) {
|
||||
if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) {
|
||||
if (Boolean.TRUE.equals(scenicConfig.getAllFree())) {
|
||||
// 景区全免
|
||||
priceObj.setFree(true);
|
||||
priceObj.setPrice(BigDecimal.ZERO);
|
||||
|
@@ -50,10 +50,10 @@ public class PriceBiz {
|
||||
}).forEach(goodsList::add);
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null) {
|
||||
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
|
||||
goodsList.add(new GoodsListRespVO(1L, "录像集"));
|
||||
}
|
||||
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage())) {
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceImage())) {
|
||||
goodsList.add(new GoodsListRespVO(2L, "照片集"));
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public class PriceBiz {
|
||||
}
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null) {
|
||||
if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) {
|
||||
if (Boolean.TRUE.equals(scenicConfig.getAllFree())) {
|
||||
// 景区全免
|
||||
respVO.setFree(true);
|
||||
respVO.setSlashPrice(BigDecimal.ZERO);
|
||||
|
@@ -1,63 +0,0 @@
|
||||
package com.ycwl.basic.config;
|
||||
|
||||
import feign.Logger;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class FeignConfig {
|
||||
|
||||
@Bean
|
||||
public Logger.Level feignLoggerLevel() {
|
||||
return Logger.Level.BASIC;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestInterceptor requestInterceptor() {
|
||||
return requestTemplate -> {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
|
||||
// 传递认证头
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (authorization != null) {
|
||||
requestTemplate.header("Authorization", authorization);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ErrorDecoder errorDecoder() {
|
||||
return new FeignErrorDecoder();
|
||||
}
|
||||
|
||||
public static class FeignErrorDecoder implements ErrorDecoder {
|
||||
private final ErrorDecoder defaultErrorDecoder = new Default();
|
||||
|
||||
@Override
|
||||
public Exception decode(String methodKey, feign.Response response) {
|
||||
log.error("Feign调用失败: method={}, status={}, reason={}",
|
||||
methodKey, response.status(), response.reason());
|
||||
|
||||
if (response.status() >= 400 && response.status() < 500) {
|
||||
// 4xx错误,客户端错误
|
||||
return new RuntimeException("客户端请求错误: " + response.reason());
|
||||
} else if (response.status() >= 500) {
|
||||
// 5xx错误,服务器错误
|
||||
return new RuntimeException("服务器内部错误: " + response.reason());
|
||||
}
|
||||
|
||||
return defaultErrorDecoder.decode(methodKey, response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,304 @@
|
||||
package com.ycwl.basic.controller.mobile;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
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.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 移动端订单控制器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;
|
||||
|
||||
/**
|
||||
* 移动端价格计算
|
||||
* 包含权限验证:验证人脸所属景区与当前用户匹配
|
||||
* 集成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参数不能为空");
|
||||
}
|
||||
|
||||
// 查询人脸信息进行权限验证
|
||||
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();
|
||||
|
||||
// 先尝试从Redis缓存获取价格计算结果
|
||||
PriceCalculationResult cachedResult = priceCacheService.getCachedPriceResult(
|
||||
currentUserId, scenicId, request.getProducts());
|
||||
|
||||
if (cachedResult != null) {
|
||||
log.info("命中价格缓存: userId={}, scenicId={}, finalAmount={}",
|
||||
currentUserId, scenicId, cachedResult.getFinalAmount());
|
||||
return ApiResponse.success(cachedResult);
|
||||
}
|
||||
|
||||
// 转换为标准价格计算请求
|
||||
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 = 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);
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,12 +3,12 @@ package com.ycwl.basic.controller.mobile;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.annotation.IgnoreToken;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.model.jwt.JwtInfo;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicAppVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicConfigResp;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
@@ -16,11 +16,14 @@ import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.mobile.AppScenicService;
|
||||
import com.ycwl.basic.service.pc.FaceService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.JwtTokenUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -49,7 +52,7 @@ public class AppScenicController {
|
||||
|
||||
// 分页查询景区列表
|
||||
@PostMapping("/page")
|
||||
public ApiResponse<PageInfo<ScenicAppVO>> pageQuery(@RequestBody ScenicReqQuery scenicReqQuery){
|
||||
public ApiResponse<PageInfo<ScenicEntity>> pageQuery(@RequestBody ScenicReqQuery scenicReqQuery){
|
||||
String userId = BaseContextHandler.getUserId();
|
||||
if (ENABLED_USER_IDs.contains(userId)) {
|
||||
return appScenicService.pageQuery(scenicReqQuery);
|
||||
|
@@ -2,6 +2,7 @@ package com.ycwl.basic.controller.mobile.manage;
|
||||
|
||||
import com.ycwl.basic.annotation.IgnoreToken;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginOldRespVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginReq;
|
||||
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO;
|
||||
@@ -12,6 +13,7 @@ import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.mobile.AppScenicService;
|
||||
import com.ycwl.basic.service.pc.ScenicAccountService;
|
||||
import com.ycwl.basic.service.pc.ScenicService;
|
||||
@@ -25,10 +27,10 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.ycwl.basic.constant.JwtRoleConstant.ADMIN;
|
||||
import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
|
||||
|
||||
/**
|
||||
@@ -45,6 +47,8 @@ public class AppScenicAccountController {
|
||||
private AppScenicService scenicService;
|
||||
@Autowired
|
||||
private ScenicService adminScenicService;
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
|
||||
// 登录
|
||||
@PostMapping("/login")
|
||||
@@ -73,8 +77,8 @@ public class AppScenicAccountController {
|
||||
}
|
||||
|
||||
@GetMapping("/myScenicList")
|
||||
public ApiResponse<List<ScenicRespVO>> myScenicList() {
|
||||
List<ScenicRespVO> list;
|
||||
public ApiResponse<List<ScenicV2DTO>> myScenicList() {
|
||||
List<ScenicV2DTO> list = Collections.emptyList();
|
||||
if (Strings.CS.equals(BaseContextHandler.getRoleId(), MERCHANT.type)) {
|
||||
String userId = BaseContextHandler.getUserId();
|
||||
ScenicAccountEntity account = accountService.getScenicAccountById(Long.valueOf(userId));
|
||||
@@ -82,10 +86,12 @@ public class AppScenicAccountController {
|
||||
return ApiResponse.fail("景区账号未绑定景区");
|
||||
}
|
||||
list = account.getScenicId().stream()
|
||||
.map(id -> scenicService.getDetails(id).getData())
|
||||
.map(id -> scenicRepository.getScenicBasic(id))
|
||||
.toList();
|
||||
} else {
|
||||
list = adminScenicService.list(new ScenicReqQuery()).getData();
|
||||
} else if (Strings.CS.equals(BaseContextHandler.getRoleId(), ADMIN.type)) {
|
||||
ScenicReqQuery query = new ScenicReqQuery();
|
||||
query.setPageSize(1000);
|
||||
list = scenicRepository.list(query);
|
||||
}
|
||||
return ApiResponse.success(list);
|
||||
}
|
||||
|
@@ -0,0 +1,100 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.ycwl.basic.integration.scenic.dto.config.DefaultConfigDTO;
|
||||
import com.ycwl.basic.integration.scenic.service.DefaultConfigIntegrationService;
|
||||
import com.ycwl.basic.utils.ApiConst;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认配置管理控制器
|
||||
* 提供默认配置的增删查改功能
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/default-config")
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultConfigController {
|
||||
|
||||
private final DefaultConfigIntegrationService defaultConfigIntegrationService;
|
||||
|
||||
/**
|
||||
* 获取默认配置列表
|
||||
*/
|
||||
@GetMapping("/")
|
||||
public ApiResponse<List<DefaultConfigDTO>> listDefaultConfigs() {
|
||||
log.info("获取默认配置列表");
|
||||
try {
|
||||
List<DefaultConfigDTO> configs = defaultConfigIntegrationService.listDefaultConfigs();
|
||||
return ApiResponse.success(configs);
|
||||
} catch (Exception e) {
|
||||
log.error("获取默认配置列表失败", e);
|
||||
return ApiResponse.fail("获取默认配置列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键获取默认配置
|
||||
*/
|
||||
@GetMapping("/{configKey}")
|
||||
public ApiResponse<DefaultConfigDTO> getDefaultConfig(@PathVariable String configKey) {
|
||||
log.info("获取默认配置, configKey: {}", configKey);
|
||||
try {
|
||||
DefaultConfigDTO config = defaultConfigIntegrationService.getDefaultConfig(configKey);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("获取默认配置失败, configKey: {}", configKey, e);
|
||||
return ApiResponse.fail("获取默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认配置
|
||||
*/
|
||||
@PostMapping("/")
|
||||
public ApiResponse<DefaultConfigDTO> createDefaultConfig(@RequestBody DefaultConfigDTO request) {
|
||||
log.info("创建默认配置, configKey: {}", request.getConfigKey());
|
||||
try {
|
||||
DefaultConfigDTO config = defaultConfigIntegrationService.createDefaultConfig(request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("创建默认配置失败, configKey: {}", request.getConfigKey(), e);
|
||||
return ApiResponse.fail("创建默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新默认配置
|
||||
*/
|
||||
@PutMapping("/{configKey}")
|
||||
public ApiResponse<DefaultConfigDTO> updateDefaultConfig(@PathVariable String configKey,
|
||||
@RequestBody DefaultConfigDTO request) {
|
||||
log.info("更新默认配置, configKey: {}", configKey);
|
||||
try {
|
||||
DefaultConfigDTO config = defaultConfigIntegrationService.updateDefaultConfig(configKey, request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("更新默认配置失败, configKey: {}", configKey, e);
|
||||
return ApiResponse.fail("更新默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除默认配置
|
||||
*/
|
||||
@DeleteMapping("/{configKey}")
|
||||
public ApiResponse<Void> deleteDefaultConfig(@PathVariable String configKey) {
|
||||
log.info("删除默认配置, configKey: {}", configKey);
|
||||
try {
|
||||
defaultConfigIntegrationService.deleteDefaultConfig(configKey);
|
||||
return ApiResponse.buildResponse(ApiConst.Code.CODE_SUCCESS.code(), null, "删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除默认配置失败, configKey: {}", configKey, e);
|
||||
return ApiResponse.fail("删除默认配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,13 +1,12 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
|
||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.mobile.AppScenicService;
|
||||
import com.ycwl.basic.service.mobile.AppStatisticsService;
|
||||
import com.ycwl.basic.service.pc.ScenicAccountService;
|
||||
@@ -17,16 +16,20 @@ import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
import com.ycwl.basic.storage.enums.StorageAcl;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.WxMpUtil;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||
import org.apache.commons.lang3.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.ycwl.basic.constant.JwtRoleConstant.ADMIN;
|
||||
import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
|
||||
|
||||
/**
|
||||
@@ -50,68 +53,6 @@ public class ScenicController {
|
||||
@Autowired
|
||||
private ScenicAccountService accountService;
|
||||
|
||||
// 分页查询景区
|
||||
@PostMapping("/page")
|
||||
public ApiResponse<PageInfo<ScenicRespVO>> pageQuery(@RequestBody ScenicReqQuery scenicReqQuery) {
|
||||
return scenicService.pageQuery(scenicReqQuery);
|
||||
}
|
||||
// 查询景区列表
|
||||
@PostMapping("/list")
|
||||
public ApiResponse<List<ScenicRespVO>> list(@RequestBody ScenicReqQuery scenicReqQuery) {
|
||||
return scenicService.list(scenicReqQuery);
|
||||
}
|
||||
// 查询景区详情
|
||||
@GetMapping("/getDetail/{id}")
|
||||
public ApiResponse<ScenicRespVO> getDetail(@PathVariable Long id) {
|
||||
return scenicService.getById(id);
|
||||
}
|
||||
// 新增景区
|
||||
@PostMapping("/add")
|
||||
public ApiResponse<Boolean> add(@RequestBody ScenicAddOrUpdateReq scenicAddReq) {
|
||||
return scenicService.add(scenicAddReq);
|
||||
}
|
||||
// 删除景区
|
||||
@GetMapping("/delete/{id}")
|
||||
public ApiResponse<Boolean> delete(@PathVariable Long id) {
|
||||
return scenicService.deleteById(id);
|
||||
}
|
||||
// 修改景区
|
||||
@PostMapping("/update")
|
||||
public ApiResponse<Boolean> update(@RequestBody ScenicAddOrUpdateReq scenicAddReq) {
|
||||
return scenicService.update(scenicAddReq);
|
||||
}
|
||||
// 修改景区状态
|
||||
@GetMapping("/updateStatus/{id}")
|
||||
public ApiResponse<Boolean> updateStatus(@PathVariable Long id) {
|
||||
return scenicService.updateStatus(id);
|
||||
}
|
||||
// 新增景区配置
|
||||
@PostMapping("/addConfig")
|
||||
public ApiResponse<Boolean> addConfig(@RequestBody ScenicConfigEntity scenicConfig) {
|
||||
return scenicService.addConfig(scenicConfig);
|
||||
}
|
||||
// 修改景区配置
|
||||
@PostMapping("/updateConfig")
|
||||
public ApiResponse<Boolean> updateConfig(@RequestBody ScenicConfigEntity scenicConfig) {
|
||||
return scenicService.updateConfigById(scenicConfig);
|
||||
}
|
||||
|
||||
// 查询景区配置
|
||||
@GetMapping("/config/{id}")
|
||||
public ApiResponse<ScenicConfigEntity> getConfig(@PathVariable("id") Long id) {
|
||||
return ApiResponse.success(scenicService.getConfig(id));
|
||||
}
|
||||
@PostMapping("/saveConfig/{id}")
|
||||
public ApiResponse saveConfig(@PathVariable("id") Long id, @RequestBody ScenicConfigEntity config) {
|
||||
scenicService.saveConfig(id, config);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
@PostMapping("/saveConfig/undefined")
|
||||
public ApiResponse saveConfig(@RequestBody ScenicConfigEntity config) {
|
||||
scenicService.addConfig(config);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
// 根据景区ID下载小程序二维码
|
||||
@GetMapping("/{id}/QRCode")
|
||||
public ApiResponse<String> downloadQrCode(@PathVariable Long id) {
|
||||
@@ -167,19 +108,19 @@ public class ScenicController {
|
||||
}
|
||||
|
||||
@GetMapping("/myScenicList")
|
||||
public ApiResponse<List<ScenicRespVO>> myScenicList() {
|
||||
List<ScenicRespVO> list = Collections.emptyList();
|
||||
public ApiResponse<List<ScenicV2DTO>> myScenicList() {
|
||||
List<ScenicV2DTO> list = Collections.emptyList();
|
||||
if (Strings.CS.equals(BaseContextHandler.getRoleId(), MERCHANT.type)) {
|
||||
String userId = BaseContextHandler.getUserId();
|
||||
ScenicAccountEntity account = accountService.getScenicAccountById(Long.valueOf(userId));
|
||||
if (account == null || account.getScenicId().isEmpty()) {
|
||||
return ApiResponse.fail("景区账号未绑定景区");
|
||||
}
|
||||
list = account.getScenicId().stream().map(id -> {
|
||||
return appScenicService.getDetails(id).getData();
|
||||
}).toList();
|
||||
} else {
|
||||
list = scenicService.list(new ScenicReqQuery()).getData();
|
||||
list = account.getScenicId().stream().map(id -> scenicRepository.getScenicBasic(id)).toList();
|
||||
} else if (Strings.CS.equals(BaseContextHandler.getRoleId(), ADMIN.type)) {
|
||||
ScenicReqQuery query = new ScenicReqQuery();
|
||||
query.setPageSize(1000);
|
||||
list = scenicRepository.list(query);
|
||||
}
|
||||
return ApiResponse.success(list);
|
||||
}
|
||||
|
@@ -0,0 +1,336 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.ycwl.basic.integration.scenic.dto.config.BatchConfigRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.BatchUpdateResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.CreateConfigRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.ScenicConfigV2DTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.UpdateConfigRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/12/26
|
||||
* 景区管理 V2 版本控制器 - 基于 zt-scenic 集成服务
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/scenic/v2")
|
||||
@RequiredArgsConstructor
|
||||
public class ScenicV2Controller {
|
||||
|
||||
private final ScenicIntegrationService scenicIntegrationService;
|
||||
private final ScenicConfigIntegrationService scenicConfigIntegrationService;
|
||||
|
||||
// ========== 景区基础 CRUD 操作 ==========
|
||||
|
||||
/**
|
||||
* 景区V2核心信息分页列表
|
||||
*/
|
||||
@GetMapping("/")
|
||||
public ApiResponse<ScenicV2ListResponse> listScenics(@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(required = false) String name) {
|
||||
log.info("分页查询景区核心信息列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
|
||||
|
||||
// 参数验证:限制pageSize最大值为100
|
||||
if (pageSize > 100) {
|
||||
pageSize = 100;
|
||||
}
|
||||
|
||||
try {
|
||||
ScenicV2ListResponse response = scenicIntegrationService.listScenics(page, pageSize, status, name);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询景区核心信息列表失败", e);
|
||||
return ApiResponse.fail("分页查询景区列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 景区V2带配置信息分页列表
|
||||
*/
|
||||
@GetMapping("/with-config")
|
||||
public ApiResponse<ScenicV2WithConfigListResponse> listScenicsWithConfig(@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(required = false) String name) {
|
||||
log.info("分页查询景区带配置信息列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
|
||||
|
||||
// 参数验证:限制pageSize最大值为100
|
||||
if (pageSize > 100) {
|
||||
pageSize = 100;
|
||||
}
|
||||
|
||||
try {
|
||||
ScenicV2WithConfigListResponse response = scenicIntegrationService.listScenicsWithConfig(page, pageSize, status, name);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询景区带配置信息列表失败", e);
|
||||
return ApiResponse.fail("分页查询景区带配置信息列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单个景区详情
|
||||
*/
|
||||
@GetMapping("/{scenicId}")
|
||||
public ApiResponse<ScenicV2DTO> getScenic(@PathVariable Long scenicId) {
|
||||
log.info("查询景区详情, scenicId: {}", scenicId);
|
||||
try {
|
||||
ScenicV2DTO scenic = scenicIntegrationService.getScenic(scenicId);
|
||||
return ApiResponse.success(scenic);
|
||||
} catch (Exception e) {
|
||||
log.error("查询景区详情失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("查询景区详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询景区列表(支持筛选和分页)- 高级筛选
|
||||
*/
|
||||
@PostMapping("/filter")
|
||||
public ApiResponse<ScenicFilterPageResponse> filterScenics(@RequestBody @Valid ScenicFilterRequest request) {
|
||||
log.info("高级筛选景区列表, 筛选条件数: {}, 页码: {}, 页大小: {}",
|
||||
request.getFilters().size(), request.getPage(), request.getPageSize());
|
||||
try {
|
||||
ScenicFilterPageResponse response = scenicIntegrationService.filterScenics(request);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("高级筛选景区列表失败", e);
|
||||
return ApiResponse.fail("高级筛选景区列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增景区
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public ApiResponse<ScenicV2DTO> createScenic(@RequestBody @Valid CreateScenicRequest request) {
|
||||
log.info("新增景区, name: {}, mpId: {}", request.getName(), request.getMpId());
|
||||
try {
|
||||
ScenicV2DTO scenic = scenicIntegrationService.createScenic(request);
|
||||
return ApiResponse.success(scenic);
|
||||
} catch (Exception e) {
|
||||
log.error("新增景区失败, name: {}", request.getName(), e);
|
||||
return ApiResponse.fail("新增景区失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改景区
|
||||
*/
|
||||
@PutMapping("/{scenicId}")
|
||||
public ApiResponse<ScenicV2DTO> updateScenic(@PathVariable Long scenicId,
|
||||
@RequestBody @Valid UpdateScenicRequest request) {
|
||||
log.info("修改景区, scenicId: {}", scenicId);
|
||||
try {
|
||||
ScenicV2DTO scenic = scenicIntegrationService.updateScenic(scenicId, request);
|
||||
return ApiResponse.success(scenic);
|
||||
} catch (Exception e) {
|
||||
log.error("修改景区失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("修改景区失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除景区
|
||||
*/
|
||||
@DeleteMapping("/{scenicId}")
|
||||
public ApiResponse<Void> deleteScenic(@PathVariable Long scenicId) {
|
||||
log.info("删除景区, scenicId: {}", scenicId);
|
||||
try {
|
||||
scenicIntegrationService.deleteScenic(scenicId);
|
||||
return ApiResponse.success(null);
|
||||
} catch (Exception e) {
|
||||
log.error("删除景区失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("删除景区失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 景区列表查询(默认1000条)
|
||||
* 只支持根据状态筛选
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public ApiResponse<ScenicV2ListResponse> listScenicsByStatus(@RequestParam(required = false) Integer status) {
|
||||
log.info("查询景区列表, status: {}", status);
|
||||
try {
|
||||
// 默认查询1000条数据,第1页
|
||||
ScenicV2ListResponse scenics = scenicIntegrationService.listScenics(1, 1000, status, null);
|
||||
return ApiResponse.success(scenics);
|
||||
} catch (Exception e) {
|
||||
log.error("查询景区列表失败, status: {}", status, e);
|
||||
return ApiResponse.fail("查询景区列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ========== 景区配置管理 ==========
|
||||
|
||||
/**
|
||||
* 获取景区及其配置信息
|
||||
*/
|
||||
@GetMapping("/{scenicId}/with-config")
|
||||
public ApiResponse<ScenicV2WithConfigDTO> getScenicWithConfig(@PathVariable Long scenicId) {
|
||||
log.info("获取景区配置信息, scenicId: {}", scenicId);
|
||||
try {
|
||||
ScenicV2WithConfigDTO scenic = scenicIntegrationService.getScenicWithConfig(scenicId);
|
||||
return ApiResponse.success(scenic);
|
||||
} catch (Exception e) {
|
||||
log.error("获取景区配置信息失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("获取景区配置信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区扁平化配置
|
||||
*/
|
||||
@GetMapping("/{scenicId}/flat-config")
|
||||
public ApiResponse<Map<String, Object>> getScenicFlatConfig(@PathVariable Long scenicId) {
|
||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||
try {
|
||||
Map<String, Object> config = scenicIntegrationService.getScenicFlatConfig(scenicId);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("获取景区扁平化配置失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("获取景区扁平化配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区配置列表
|
||||
*/
|
||||
@GetMapping("/{scenicId}/config")
|
||||
public ApiResponse<List<ScenicConfigV2DTO>> listConfigs(@PathVariable Long scenicId) {
|
||||
log.info("获取景区配置列表, scenicId: {}", scenicId);
|
||||
try {
|
||||
List<ScenicConfigV2DTO> configs = scenicConfigIntegrationService.listConfigs(scenicId);
|
||||
return ApiResponse.success(configs);
|
||||
} catch (Exception e) {
|
||||
log.error("获取景区配置列表失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("获取景区配置列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键获取配置
|
||||
*/
|
||||
@GetMapping("/{scenicId}/config/{configKey}")
|
||||
public ApiResponse<ScenicConfigV2DTO> getConfigByKey(@PathVariable Long scenicId,
|
||||
@PathVariable String configKey) {
|
||||
log.info("根据键获取景区配置, scenicId: {}, configKey: {}", scenicId, configKey);
|
||||
try {
|
||||
ScenicConfigV2DTO config = scenicConfigIntegrationService.getConfigByKey(scenicId, configKey);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("根据键获取景区配置失败, scenicId: {}, configKey: {}", scenicId, configKey, e);
|
||||
return ApiResponse.fail("获取配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建景区配置
|
||||
*/
|
||||
@PostMapping("/{scenicId}/config")
|
||||
public ApiResponse<ScenicConfigV2DTO> createConfig(@PathVariable Long scenicId,
|
||||
@RequestBody @Valid CreateConfigRequest request) {
|
||||
log.info("创建景区配置, scenicId: {}, configKey: {}", scenicId, request.getConfigKey());
|
||||
try {
|
||||
ScenicConfigV2DTO config = scenicConfigIntegrationService.createConfig(scenicId, request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("创建景区配置失败, scenicId: {}, configKey: {}", scenicId, request.getConfigKey(), e);
|
||||
return ApiResponse.fail("创建配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新景区配置
|
||||
*/
|
||||
@PutMapping("/{scenicId}/config/{configId}")
|
||||
public ApiResponse<ScenicConfigV2DTO> updateConfig(@PathVariable Long scenicId,
|
||||
@PathVariable String configId,
|
||||
@RequestBody @Valid UpdateConfigRequest request) {
|
||||
log.info("更新景区配置, scenicId: {}, configId: {}", scenicId, configId);
|
||||
try {
|
||||
ScenicConfigV2DTO config = scenicConfigIntegrationService.updateConfig(scenicId, configId, request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("更新景区配置失败, scenicId: {}, configId: {}", scenicId, configId, e);
|
||||
return ApiResponse.fail("更新配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除景区配置
|
||||
*/
|
||||
@DeleteMapping("/{scenicId}/config/{configId}")
|
||||
public ApiResponse<Void> deleteConfig(@PathVariable Long scenicId, @PathVariable String configId) {
|
||||
log.info("删除景区配置, scenicId: {}, configId: {}", scenicId, configId);
|
||||
try {
|
||||
scenicConfigIntegrationService.deleteConfig(scenicId, configId);
|
||||
return ApiResponse.success(null);
|
||||
} catch (Exception e) {
|
||||
log.error("删除景区配置失败, scenicId: {}, configId: {}", scenicId, configId, e);
|
||||
return ApiResponse.fail("删除配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新景区配置
|
||||
*/
|
||||
@PutMapping("/{scenicId}/config/batch")
|
||||
public ApiResponse<BatchUpdateResponse> batchUpdateConfigs(@PathVariable Long scenicId,
|
||||
@RequestBody @Valid BatchConfigRequest request) {
|
||||
log.info("批量更新景区配置, scenicId: {}, configs count: {}", scenicId, request.getConfigs().size());
|
||||
try {
|
||||
BatchUpdateResponse response = scenicConfigIntegrationService.batchUpdateConfigs(scenicId, request);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("批量更新景区配置失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("批量更新配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扁平化批量更新景区配置
|
||||
*/
|
||||
@PutMapping("/{scenicId}/flat-config")
|
||||
public ApiResponse<BatchUpdateResponse> batchFlatUpdateConfigs(@PathVariable Long scenicId,
|
||||
@RequestBody Map<String, Object> configs) {
|
||||
log.info("扁平化批量更新景区配置, scenicId: {}, configs count: {}", scenicId, configs.size());
|
||||
try {
|
||||
BatchUpdateResponse response = scenicConfigIntegrationService.batchFlatUpdateConfigs(scenicId, configs);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("扁平化批量更新景区配置失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("扁平化批量更新配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
54
src/main/java/com/ycwl/basic/dto/MobileOrderRequest.java
Normal file
54
src/main/java/com/ycwl/basic/dto/MobileOrderRequest.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package com.ycwl.basic.dto;
|
||||
|
||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 移动端下单请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class MobileOrderRequest {
|
||||
|
||||
/**
|
||||
* 商品列表
|
||||
*/
|
||||
private List<ProductItem> products;
|
||||
|
||||
/**
|
||||
* 人脸ID(必填,用于权限验证)
|
||||
*/
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 预期原价(用于价格验证)
|
||||
*/
|
||||
private BigDecimal expectedOriginalAmount;
|
||||
|
||||
/**
|
||||
* 预期最终价格(用于价格验证)
|
||||
*/
|
||||
private BigDecimal expectedFinalAmount;
|
||||
|
||||
/**
|
||||
* 是否自动使用优惠券
|
||||
*/
|
||||
private Boolean autoUseCoupon = true;
|
||||
|
||||
/**
|
||||
* 用户输入的券码
|
||||
*/
|
||||
private String voucherCode;
|
||||
|
||||
/**
|
||||
* 是否自动使用券码优惠
|
||||
*/
|
||||
private Boolean autoUseVoucher = true;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remarks;
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package com.ycwl.basic.exception;
|
||||
|
||||
import com.ycwl.basic.enums.BizCodeEnum;
|
||||
import com.ycwl.basic.order.exception.DuplicatePurchaseException;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
|
||||
@@ -106,4 +107,18 @@ public class CustomExceptionHandle {
|
||||
public ApiResponse<String> handle(SizeLimitExceededException sizeLimitExceededException) {
|
||||
return ApiResponse.buildResponse(415, "文件过大,请重新上传");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重复购买异常处理
|
||||
*/
|
||||
@ExceptionHandler(value = DuplicatePurchaseException.class)
|
||||
public ApiResponse<String> handle(HttpServletResponse response, DuplicatePurchaseException exception) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
LOGGER.warn("检测到重复购买: productType={}, productId={}, existingOrderId={}, existingOrderNo={}",
|
||||
exception.getProductType(), exception.getProductId(),
|
||||
exception.getExistingOrderId(), exception.getExistingOrderNo());
|
||||
|
||||
// 返回友好的错误信息给前端
|
||||
return ApiResponse.buildResponse(4001, exception.getFriendlyMessage());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,32 @@
|
||||
package com.ycwl.basic.integration.common.config;
|
||||
|
||||
import com.ycwl.basic.integration.common.exception.FeignErrorDecoder;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class FeignConfig {
|
||||
|
||||
private final FeignErrorDecoder feignErrorDecoder;
|
||||
|
||||
@Bean
|
||||
public ErrorDecoder errorDecoder() {
|
||||
return feignErrorDecoder;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestInterceptor requestInterceptor() {
|
||||
return template -> {
|
||||
template.header("Accept", "application/json");
|
||||
template.header("Content-Type", "application/json");
|
||||
// 可以在这里添加统一的鉴权头
|
||||
// template.header("Authorization", "Bearer " + getToken());
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.ycwl.basic.integration.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@RefreshScope
|
||||
@ConfigurationProperties(prefix = "integration")
|
||||
public class IntegrationProperties {
|
||||
|
||||
/**
|
||||
* 景区服务配置
|
||||
*/
|
||||
private ScenicConfig scenic = new ScenicConfig();
|
||||
|
||||
@Data
|
||||
public static class ScenicConfig {
|
||||
/**
|
||||
* 是否启用景区服务集成
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
private String serviceName = "zt-scenic";
|
||||
|
||||
/**
|
||||
* 超时配置(毫秒)
|
||||
*/
|
||||
private int connectTimeout = 5000;
|
||||
private int readTimeout = 10000;
|
||||
|
||||
/**
|
||||
* 重试配置
|
||||
*/
|
||||
private boolean retryEnabled = false;
|
||||
private int maxRetries = 3;
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.ycwl.basic.integration.common.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import feign.Response;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FeignErrorDecoder implements ErrorDecoder {
|
||||
|
||||
private final ErrorDecoder defaultDecoder = new Default();
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public Exception decode(String methodKey, Response response) {
|
||||
log.warn("Feign调用失败, methodKey: {}, status: {}, reason: {}",
|
||||
methodKey, response.status(), response.reason());
|
||||
|
||||
try {
|
||||
if (response.body() != null) {
|
||||
String body = new String(response.body().asInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||
log.warn("响应内容: {}", body);
|
||||
|
||||
try {
|
||||
CommonResponse<?> errorResponse = objectMapper.readValue(body, CommonResponse.class);
|
||||
if (errorResponse.getCode() != null && !errorResponse.getCode().equals(200)) {
|
||||
return new IntegrationException(
|
||||
errorResponse.getCode(),
|
||||
errorResponse.getMessage() != null ? errorResponse.getMessage() : "服务调用失败",
|
||||
extractServiceName(methodKey)
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析错误响应失败", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("读取响应体失败", e);
|
||||
}
|
||||
|
||||
return new IntegrationException(
|
||||
response.status(),
|
||||
String.format("服务调用失败: %s", response.reason()),
|
||||
extractServiceName(methodKey)
|
||||
);
|
||||
}
|
||||
|
||||
private String extractServiceName(String methodKey) {
|
||||
if (methodKey != null && methodKey.contains("#")) {
|
||||
return methodKey.substring(0, methodKey.indexOf("#"));
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.ycwl.basic.integration.common.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class IntegrationException extends RuntimeException {
|
||||
private final Integer code;
|
||||
private final String serviceName;
|
||||
|
||||
public IntegrationException(Integer code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.serviceName = null;
|
||||
}
|
||||
|
||||
public IntegrationException(Integer code, String message, String serviceName) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public IntegrationException(Integer code, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
this.serviceName = null;
|
||||
}
|
||||
|
||||
public IntegrationException(Integer code, String message, String serviceName, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.ycwl.basic.integration.common.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class CommonResponse<T> {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public static <T> CommonResponse<T> success() {
|
||||
return new CommonResponse<>(200, "OK", null);
|
||||
}
|
||||
|
||||
public static <T> CommonResponse<T> success(T data) {
|
||||
return new CommonResponse<>(200, "OK", data);
|
||||
}
|
||||
|
||||
public static <T> CommonResponse<T> success(String message, T data) {
|
||||
return new CommonResponse<>(200, message, data);
|
||||
}
|
||||
|
||||
public static <T> CommonResponse<T> error(Integer code, String message) {
|
||||
return new CommonResponse<>(code, message, null);
|
||||
}
|
||||
|
||||
public static <T> CommonResponse<T> error(String message) {
|
||||
return new CommonResponse<>(5000, message, null);
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return code != null && code == 200;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.ycwl.basic.integration.common.response;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageResponse<T> {
|
||||
private List<T> list;
|
||||
private Integer total;
|
||||
private Integer page;
|
||||
private Integer pageSize;
|
||||
}
|
@@ -0,0 +1,397 @@
|
||||
package com.ycwl.basic.integration.common.util;
|
||||
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 配置值转换工具类
|
||||
*
|
||||
* 提供统一的配置Map值类型转换方法,支持多种数据类型的安全转换
|
||||
*/
|
||||
public class ConfigValueUtil {
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Integer值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return Integer值,如果转换失败返回null
|
||||
*/
|
||||
public static Integer getIntValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Integer) return (Integer) value;
|
||||
if (value instanceof Number) return ((Number) value).intValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Long值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return Long值,如果转换失败返回null
|
||||
*/
|
||||
public static Long getLongValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Long) return (Long) value;
|
||||
if (value instanceof Number) return ((Number) value).longValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Long.parseLong((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Float值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return Float值,如果转换失败返回null
|
||||
*/
|
||||
public static Float getFloatValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Float) return (Float) value;
|
||||
if (value instanceof Double) return ((Double) value).floatValue();
|
||||
if (value instanceof Number) return ((Number) value).floatValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Float.parseFloat((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Double值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return Double值,如果转换失败返回null
|
||||
*/
|
||||
public static Double getDoubleValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Double) return (Double) value;
|
||||
if (value instanceof Number) return ((Number) value).doubleValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Double.parseDouble((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取BigDecimal值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return BigDecimal值,如果转换失败返回null
|
||||
*/
|
||||
public static BigDecimal getBigDecimalValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
if (value instanceof BigDecimal) return (BigDecimal) value;
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return new BigDecimal((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return new BigDecimal(value.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取String值
|
||||
* 如果值是复杂对象(Map/List),会自动转换为JSON字符串
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return String值,如果value为null返回null
|
||||
*/
|
||||
public static String getStringValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
|
||||
// 如果是基础类型,直接转字符串
|
||||
if (value instanceof String || value instanceof Number || value instanceof Boolean) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
// 如果是复杂对象(Map, List等),转换为JSON字符串
|
||||
try {
|
||||
return JacksonUtil.toJSONString(value);
|
||||
} catch (Exception e) {
|
||||
// JSON转换失败,降级为toString
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Boolean值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return Boolean值,如果转换失败返回null
|
||||
*/
|
||||
public static Boolean getBooleanValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Boolean) return (Boolean) value;
|
||||
if (value instanceof String) {
|
||||
String str = (String) value;
|
||||
if ("true".equalsIgnoreCase(str) || "1".equals(str)) return true;
|
||||
if ("false".equalsIgnoreCase(str) || "0".equals(str)) return false;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue() != 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取枚举值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @param enumClass 枚举类型
|
||||
* @param <T> 枚举类型泛型
|
||||
* @return 枚举值,如果转换失败返回null
|
||||
*/
|
||||
public static <T extends Enum<T>> T getEnumValue(Map<String, Object> config, String key, Class<T> enumClass) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
try {
|
||||
if (value instanceof String) {
|
||||
return Enum.valueOf(enumClass, (String) value);
|
||||
}
|
||||
return Enum.valueOf(enumClass, value.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Integer值,如果为null则返回默认值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return Integer值或默认值
|
||||
*/
|
||||
public static Integer getIntValue(Map<String, Object> config, String key, Integer defaultValue) {
|
||||
Integer value = getIntValue(config, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取String值,如果为null则返回默认值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return String值或默认值
|
||||
*/
|
||||
public static String getStringValue(Map<String, Object> config, String key, String defaultValue) {
|
||||
String value = getStringValue(config, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Boolean值,如果为null则返回默认值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return Boolean值或默认值
|
||||
*/
|
||||
public static Boolean getBooleanValue(Map<String, Object> config, String key, Boolean defaultValue) {
|
||||
Boolean value = getBooleanValue(config, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
// ========== 对象和JSON转换方法 ==========
|
||||
|
||||
/**
|
||||
* 从配置Map中获取原始对象值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return 原始Object值
|
||||
*/
|
||||
public static Object getObjectValue(Map<String, Object> config, String key) {
|
||||
return config.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取并转换为指定类型的对象
|
||||
* 支持JSON字符串自动反序列化
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @param clazz 目标类型
|
||||
* @param <T> 目标类型泛型
|
||||
* @return 转换后的对象,如果转换失败返回null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getObjectValue(Map<String, Object> config, String key, Class<T> clazz) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
|
||||
// 如果类型匹配,直接返回
|
||||
if (clazz.isInstance(value)) {
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
// 如果是String类型的JSON,尝试反序列化
|
||||
if (value instanceof String && !clazz.equals(String.class)) {
|
||||
try {
|
||||
return JacksonUtil.parseObject((String) value, clazz);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果目标是String,使用增强的字符串转换
|
||||
if (clazz.equals(String.class)) {
|
||||
return (T) getStringValue(config, key);
|
||||
}
|
||||
|
||||
// 其他情况尝试JSON转换
|
||||
try {
|
||||
String json = JacksonUtil.toJSONString(value);
|
||||
return JacksonUtil.parseObject(json, clazz);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取Map类型的值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return Map值,如果转换失败返回null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> getMapValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof Map) {
|
||||
return (Map<String, Object>) value;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return JacksonUtil.parseObject((String) value, Map.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取List类型的值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return List值,如果转换失败返回null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Object> getListValue(Map<String, Object> config, String key) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof List) {
|
||||
return (List<Object>) value;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return JacksonUtil.parseObject((String) value, List.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置Map中获取指定元素类型的List值
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @param elementClass List元素类型
|
||||
* @param <T> List元素类型泛型
|
||||
* @return 指定类型的List,如果转换失败返回null
|
||||
*/
|
||||
public static <T> List<T> getListValue(Map<String, Object> config, String key, Class<T> elementClass) {
|
||||
Object value = config.get(key);
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return JacksonUtil.parseArray((String) value, elementClass);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String json = JacksonUtil.toJSONString(value);
|
||||
return JacksonUtil.parseArray(json, elementClass);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置键是否存在
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return true如果键存在,false如果不存在
|
||||
*/
|
||||
public static boolean hasKey(Map<String, Object> config, String key) {
|
||||
return config != null && config.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置键是否存在且值不为null
|
||||
*
|
||||
* @param config 配置Map
|
||||
* @param key 配置键
|
||||
* @return true如果键存在且值不为null
|
||||
*/
|
||||
public static boolean hasNonNullValue(Map<String, Object> config, String key) {
|
||||
return config != null && config.containsKey(key) && config.get(key) != null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package com.ycwl.basic.integration.scenic.client;
|
||||
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.DefaultConfigDTO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = "zt-scenic", contextId = "scenic-default-config", path = "/api/scenic/default-config")
|
||||
public interface DefaultConfigClient {
|
||||
|
||||
@GetMapping("/")
|
||||
CommonResponse<List<DefaultConfigDTO>> listDefaultConfigs();
|
||||
|
||||
@GetMapping("/{configKey}")
|
||||
CommonResponse<DefaultConfigDTO> getDefaultConfig(@PathVariable("configKey") String configKey);
|
||||
|
||||
@PostMapping("/")
|
||||
CommonResponse<DefaultConfigDTO> createDefaultConfig(@RequestBody DefaultConfigDTO request);
|
||||
|
||||
@PutMapping("/{configKey}")
|
||||
CommonResponse<DefaultConfigDTO> updateDefaultConfig(@PathVariable("configKey") String configKey,
|
||||
@RequestBody DefaultConfigDTO request);
|
||||
|
||||
@DeleteMapping("/{configKey}")
|
||||
CommonResponse<Void> deleteDefaultConfig(@PathVariable("configKey") String configKey);
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package com.ycwl.basic.integration.scenic.client;
|
||||
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.*;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(name = "zt-scenic", contextId = "scenic-config-v2", path = "/api/scenic/config/v2")
|
||||
public interface ScenicConfigV2Client {
|
||||
|
||||
@GetMapping("/{scenicId}")
|
||||
CommonResponse<List<ScenicConfigV2DTO>> listConfigs(@PathVariable("scenicId") Long scenicId);
|
||||
|
||||
@GetMapping("/{scenicId}/key/{configKey}")
|
||||
CommonResponse<ScenicConfigV2DTO> getConfigByKey(@PathVariable("scenicId") Long scenicId,
|
||||
@PathVariable("configKey") String configKey);
|
||||
|
||||
@GetMapping("/{scenicId}/keys")
|
||||
CommonResponse<Map<String, Object>> getFlatConfigs(@PathVariable("scenicId") Long scenicId);
|
||||
|
||||
@PostMapping("/{scenicId}")
|
||||
CommonResponse<ScenicConfigV2DTO> createConfig(@PathVariable("scenicId") Long scenicId,
|
||||
@RequestBody CreateConfigRequest request);
|
||||
|
||||
@PutMapping("/{scenicId}/{id}")
|
||||
CommonResponse<ScenicConfigV2DTO> updateConfig(@PathVariable("scenicId") Long scenicId,
|
||||
@PathVariable("id") String id,
|
||||
@RequestBody UpdateConfigRequest request);
|
||||
|
||||
@DeleteMapping("/{scenicId}/{id}")
|
||||
CommonResponse<Void> deleteConfig(@PathVariable("scenicId") Long scenicId,
|
||||
@PathVariable("id") String id);
|
||||
|
||||
@PostMapping("/{scenicId}/batch")
|
||||
CommonResponse<BatchUpdateResponse> batchUpdateConfigs(@PathVariable("scenicId") Long scenicId,
|
||||
@RequestBody BatchConfigRequest request);
|
||||
|
||||
@PostMapping("/{scenicId}/batchFlatUpdate")
|
||||
CommonResponse<BatchUpdateResponse> batchFlatUpdateConfigs(@PathVariable("scenicId") Long scenicId,
|
||||
@RequestBody Map<String, Object> configs);
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.ycwl.basic.integration.scenic.client;
|
||||
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(name = "zt-scenic", contextId = "scenic-v2", path = "/api/scenic/v2")
|
||||
public interface ScenicV2Client {
|
||||
|
||||
@GetMapping("/{scenicId}")
|
||||
CommonResponse<ScenicV2DTO> getScenic(@PathVariable("scenicId") Long scenicId);
|
||||
|
||||
@GetMapping("/{scenicId}/with-config")
|
||||
CommonResponse<ScenicV2WithConfigDTO> getScenicWithConfig(@PathVariable("scenicId") Long scenicId);
|
||||
|
||||
|
||||
@PostMapping("/")
|
||||
CommonResponse<ScenicV2DTO> createScenic(@RequestBody CreateScenicRequest request);
|
||||
|
||||
@PutMapping("/{scenicId}")
|
||||
CommonResponse<ScenicV2DTO> updateScenic(@PathVariable("scenicId") Long scenicId,
|
||||
@RequestBody UpdateScenicRequest request);
|
||||
|
||||
@DeleteMapping("/{scenicId}")
|
||||
CommonResponse<Void> deleteScenic(@PathVariable("scenicId") Long scenicId);
|
||||
|
||||
@PostMapping("/filter")
|
||||
CommonResponse<ScenicFilterPageResponse> filterScenics(@RequestBody ScenicFilterRequest request);
|
||||
|
||||
@GetMapping("/")
|
||||
CommonResponse<ScenicV2ListResponse> listScenics(@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(required = false) String name);
|
||||
|
||||
@GetMapping("/with-config")
|
||||
CommonResponse<ScenicV2WithConfigListResponse> listScenicsWithConfig(@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(required = false) String name);
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.ycwl.basic.integration.scenic.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "integration.scenic")
|
||||
public class ScenicIntegrationConfig {
|
||||
|
||||
public ScenicIntegrationConfig() {
|
||||
log.info("ZT-Scenic集成配置初始化完成");
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BatchConfigRequest {
|
||||
@JsonProperty("configs")
|
||||
@NotEmpty(message = "配置列表不能为空")
|
||||
@Valid
|
||||
private List<BatchConfigItem> configs;
|
||||
|
||||
@Data
|
||||
public static class BatchConfigItem {
|
||||
@JsonProperty("configKey")
|
||||
@NotEmpty(message = "配置键不能为空")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
private String configValue;
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BatchUpdateResponse {
|
||||
@JsonProperty("updatedCount")
|
||||
private Integer updatedCount;
|
||||
|
||||
@JsonProperty("createdCount")
|
||||
private Integer createdCount;
|
||||
|
||||
@JsonProperty("message")
|
||||
private String message;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class CreateConfigRequest {
|
||||
@JsonProperty("configKey")
|
||||
@NotBlank(message = "配置键不能为空")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
private String configValue;
|
||||
|
||||
@JsonProperty("configType")
|
||||
@NotBlank(message = "配置类型不能为空")
|
||||
private String configType;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DefaultConfigDTO {
|
||||
@JsonProperty("configKey")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
private String configValue;
|
||||
|
||||
@JsonProperty("configType")
|
||||
private String configType;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ScenicConfigV2DTO {
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("scenicID")
|
||||
@JsonAlias({"scenicId", "scenicID"})
|
||||
private String scenicId;
|
||||
|
||||
@JsonProperty("configKey")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
private String configValue;
|
||||
|
||||
@JsonProperty("configType")
|
||||
private String configType;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("isActive")
|
||||
private Integer isActive;
|
||||
|
||||
@JsonProperty("createTime")
|
||||
private Long createTime;
|
||||
|
||||
@JsonProperty("updateTime")
|
||||
private Long updateTime;
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateConfigRequest {
|
||||
@JsonProperty("configKey")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
private String configValue;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.filter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class FilterCondition {
|
||||
@JsonProperty("configKey")
|
||||
@NotBlank(message = "配置键不能为空")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
@NotBlank(message = "配置值不能为空")
|
||||
private String configValue;
|
||||
|
||||
@JsonProperty("operator")
|
||||
private String operator = "eq";
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.filter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ScenicFilterPageResponse {
|
||||
@JsonProperty("list")
|
||||
private List<ScenicFilterItem> list;
|
||||
|
||||
@JsonProperty("total")
|
||||
private Long total;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page;
|
||||
|
||||
@JsonProperty("pageSize")
|
||||
private Integer pageSize;
|
||||
|
||||
@Data
|
||||
public static class ScenicFilterItem {
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.filter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ScenicFilterRequest {
|
||||
@JsonProperty("filters")
|
||||
@NotEmpty(message = "筛选条件不能为空")
|
||||
@Valid
|
||||
private List<FilterCondition> filters;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page = 1;
|
||||
|
||||
@JsonProperty("pageSize")
|
||||
private Integer pageSize = 20;
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.scenic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class CreateScenicRequest {
|
||||
@JsonProperty("name")
|
||||
@NotBlank(message = "景区名称不能为空")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("mpId")
|
||||
@NotNull(message = "小程序ID不能为空")
|
||||
private Integer mpId;
|
||||
|
||||
@JsonProperty("status")
|
||||
private Integer status = 1;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.scenic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ScenicV2DTO {
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("mpId")
|
||||
private Integer mpId;
|
||||
|
||||
@JsonProperty("status")
|
||||
private Integer status;
|
||||
|
||||
@JsonProperty("createTime")
|
||||
private Long createTime;
|
||||
|
||||
@JsonProperty("updateTime")
|
||||
private Long updateTime;
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.scenic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ScenicV2ListResponse {
|
||||
@JsonProperty("list")
|
||||
private List<ScenicV2DTO> list;
|
||||
|
||||
@JsonProperty("total")
|
||||
private Integer total;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page;
|
||||
|
||||
@JsonProperty("pageSize")
|
||||
private Integer pageSize;
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.scenic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ScenicV2WithConfigDTO extends ScenicV2DTO {
|
||||
@JsonProperty("config")
|
||||
private Map<String, Object> config;
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.scenic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ScenicV2WithConfigListResponse {
|
||||
@JsonProperty("list")
|
||||
private List<ScenicV2WithConfigDTO> list;
|
||||
|
||||
@JsonProperty("total")
|
||||
private Integer total;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page;
|
||||
|
||||
@JsonProperty("pageSize")
|
||||
private Integer pageSize;
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.ycwl.basic.integration.scenic.dto.scenic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateScenicRequest {
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("mpId")
|
||||
private Integer mpId;
|
||||
|
||||
@JsonProperty("status")
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package com.ycwl.basic.integration.scenic.example;
|
||||
|
||||
import com.ycwl.basic.integration.scenic.dto.config.CreateConfigRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.FilterCondition;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* ZT-Scenic集成服务使用示例
|
||||
* 仅供参考,实际使用时根据业务需要调用相应的服务方法
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ScenicIntegrationExample {
|
||||
|
||||
private final ScenicIntegrationService scenicIntegrationService;
|
||||
private final ScenicConfigIntegrationService scenicConfigIntegrationService;
|
||||
|
||||
/**
|
||||
* 示例:创建景区并设置配置
|
||||
*/
|
||||
public void createScenicWithConfig() {
|
||||
try {
|
||||
// 1. 创建景区
|
||||
CreateScenicRequest createRequest = new CreateScenicRequest();
|
||||
createRequest.setName("测试景区");
|
||||
createRequest.setMpId(1001);
|
||||
|
||||
var scenic = scenicIntegrationService.createScenic(createRequest);
|
||||
log.info("创建景区成功: {}", scenic.getName());
|
||||
|
||||
// 2. 为景区添加配置
|
||||
CreateConfigRequest configRequest = new CreateConfigRequest();
|
||||
configRequest.setConfigKey("tour_time");
|
||||
configRequest.setConfigValue("120");
|
||||
configRequest.setConfigType("int");
|
||||
configRequest.setDescription("游览时长");
|
||||
|
||||
var config = scenicConfigIntegrationService.createConfig(
|
||||
Long.valueOf(scenic.getId()), configRequest);
|
||||
log.info("创建配置成功: {} = {}", config.getConfigKey(), config.getConfigValue());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建景区和配置失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 示例:筛选景区
|
||||
*/
|
||||
public void filterScenics() {
|
||||
try {
|
||||
FilterCondition condition = new FilterCondition();
|
||||
condition.setConfigKey("tour_time");
|
||||
condition.setConfigValue("120");
|
||||
condition.setOperator("gte");
|
||||
|
||||
ScenicFilterRequest filterRequest = new ScenicFilterRequest();
|
||||
filterRequest.setFilters(Collections.singletonList(condition));
|
||||
filterRequest.setPage(1);
|
||||
filterRequest.setPageSize(10);
|
||||
|
||||
var result = scenicIntegrationService.filterScenics(filterRequest);
|
||||
log.info("筛选到 {} 个景区", result.getTotal());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("筛选景区失败", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.ycwl.basic.integration.scenic.service;
|
||||
|
||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.scenic.client.DefaultConfigClient;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.DefaultConfigDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultConfigIntegrationService {
|
||||
|
||||
private final DefaultConfigClient defaultConfigClient;
|
||||
|
||||
public List<DefaultConfigDTO> listDefaultConfigs() {
|
||||
log.info("获取默认配置列表");
|
||||
CommonResponse<List<DefaultConfigDTO>> response = defaultConfigClient.listDefaultConfigs();
|
||||
return handleResponse(response, "获取默认配置列表失败");
|
||||
}
|
||||
|
||||
public DefaultConfigDTO getDefaultConfig(String configKey) {
|
||||
log.info("获取指定默认配置, configKey: {}", configKey);
|
||||
CommonResponse<DefaultConfigDTO> response = defaultConfigClient.getDefaultConfig(configKey);
|
||||
return handleResponse(response, "获取指定默认配置失败");
|
||||
}
|
||||
|
||||
public DefaultConfigDTO createDefaultConfig(DefaultConfigDTO request) {
|
||||
log.info("创建默认配置, configKey: {}", request.getConfigKey());
|
||||
CommonResponse<DefaultConfigDTO> response = defaultConfigClient.createDefaultConfig(request);
|
||||
return handleResponse(response, "创建默认配置失败");
|
||||
}
|
||||
|
||||
public DefaultConfigDTO updateDefaultConfig(String configKey, DefaultConfigDTO request) {
|
||||
log.info("更新默认配置, configKey: {}", configKey);
|
||||
CommonResponse<DefaultConfigDTO> response = defaultConfigClient.updateDefaultConfig(configKey, request);
|
||||
return handleResponse(response, "更新默认配置失败");
|
||||
}
|
||||
|
||||
public void deleteDefaultConfig(String configKey) {
|
||||
log.info("删除默认配置, configKey: {}", configKey);
|
||||
CommonResponse<Void> response = defaultConfigClient.deleteDefaultConfig(configKey);
|
||||
handleResponse(response, "删除默认配置失败");
|
||||
}
|
||||
|
||||
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
|
||||
if (response == null || !response.isSuccess()) {
|
||||
String msg = response != null && response.getMessage() != null
|
||||
? response.getMessage()
|
||||
: errorMessage;
|
||||
Integer code = response != null ? response.getCode() : 5000;
|
||||
throw new IntegrationException(code, msg, "zt-scenic");
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package com.ycwl.basic.integration.scenic.service;
|
||||
|
||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ScenicConfigIntegrationService {
|
||||
|
||||
private final ScenicConfigV2Client scenicConfigV2Client;
|
||||
|
||||
public List<ScenicConfigV2DTO> listConfigs(Long scenicId) {
|
||||
log.info("获取景区配置列表, scenicId: {}", scenicId);
|
||||
CommonResponse<List<ScenicConfigV2DTO>> response = scenicConfigV2Client.listConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区配置列表失败");
|
||||
}
|
||||
|
||||
public ScenicConfigV2DTO getConfigByKey(Long scenicId, String configKey) {
|
||||
log.info("根据键获取景区配置, scenicId: {}, configKey: {}", scenicId, configKey);
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.getConfigByKey(scenicId, configKey);
|
||||
return handleResponse(response, "根据键获取景区配置失败");
|
||||
}
|
||||
|
||||
public Map<String, Object> getFlatConfigs(Long scenicId) {
|
||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区扁平化配置失败");
|
||||
}
|
||||
|
||||
public ScenicConfigV2DTO createConfig(Long scenicId, CreateConfigRequest request) {
|
||||
log.info("创建景区配置, scenicId: {}, configKey: {}", scenicId, request.getConfigKey());
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.createConfig(scenicId, request);
|
||||
return handleResponse(response, "创建景区配置失败");
|
||||
}
|
||||
|
||||
public ScenicConfigV2DTO updateConfig(Long scenicId, String id, UpdateConfigRequest request) {
|
||||
log.info("更新景区配置, scenicId: {}, id: {}", scenicId, id);
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.updateConfig(scenicId, id, request);
|
||||
return handleResponse(response, "更新景区配置失败");
|
||||
}
|
||||
|
||||
public void deleteConfig(Long scenicId, String id) {
|
||||
log.info("删除景区配置, scenicId: {}, id: {}", scenicId, id);
|
||||
CommonResponse<Void> response = scenicConfigV2Client.deleteConfig(scenicId, id);
|
||||
handleResponse(response, "删除景区配置失败");
|
||||
}
|
||||
|
||||
public BatchUpdateResponse batchUpdateConfigs(Long scenicId, BatchConfigRequest request) {
|
||||
log.info("批量更新景区配置, scenicId: {}, configs count: {}", scenicId, request.getConfigs().size());
|
||||
CommonResponse<BatchUpdateResponse> response = scenicConfigV2Client.batchUpdateConfigs(scenicId, request);
|
||||
return handleResponse(response, "批量更新景区配置失败");
|
||||
}
|
||||
|
||||
public BatchUpdateResponse batchFlatUpdateConfigs(Long scenicId, Map<String, Object> configs) {
|
||||
log.info("扁平化批量更新景区配置, scenicId: {}, configs count: {}", scenicId, configs.size());
|
||||
CommonResponse<BatchUpdateResponse> response = scenicConfigV2Client.batchFlatUpdateConfigs(scenicId, configs);
|
||||
return handleResponse(response, "扁平化批量更新景区配置失败");
|
||||
}
|
||||
|
||||
|
||||
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
|
||||
if (response == null || !response.isSuccess()) {
|
||||
String msg = response != null && response.getMessage() != null
|
||||
? response.getMessage()
|
||||
: errorMessage;
|
||||
Integer code = response != null ? response.getCode() : 5000;
|
||||
throw new IntegrationException(code, msg, "zt-scenic");
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package com.ycwl.basic.integration.scenic.service;
|
||||
|
||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
||||
import com.ycwl.basic.integration.scenic.client.ScenicV2Client;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ScenicIntegrationService {
|
||||
|
||||
private final ScenicV2Client scenicV2Client;
|
||||
private final ScenicConfigV2Client scenicConfigV2Client;
|
||||
|
||||
public ScenicV2DTO getScenic(Long scenicId) {
|
||||
log.info("获取景区信息, scenicId: {}", scenicId);
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.getScenic(scenicId);
|
||||
return handleResponse(response, "获取景区信息失败");
|
||||
}
|
||||
|
||||
public ScenicV2WithConfigDTO getScenicWithConfig(Long scenicId) {
|
||||
log.info("获取景区配置信息, scenicId: {}", scenicId);
|
||||
CommonResponse<ScenicV2WithConfigDTO> response = scenicV2Client.getScenicWithConfig(scenicId);
|
||||
return handleResponse(response, "获取景区配置信息失败");
|
||||
}
|
||||
|
||||
public Map<String, Object> getScenicFlatConfig(Long scenicId) {
|
||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区扁平化配置失败");
|
||||
}
|
||||
|
||||
public ScenicV2DTO createScenic(CreateScenicRequest request) {
|
||||
log.info("创建景区, name: {}", request.getName());
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.createScenic(request);
|
||||
return handleResponse(response, "创建景区失败");
|
||||
}
|
||||
|
||||
public ScenicV2DTO updateScenic(Long scenicId, UpdateScenicRequest request) {
|
||||
log.info("更新景区信息, scenicId: {}", scenicId);
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.updateScenic(scenicId, request);
|
||||
return handleResponse(response, "更新景区信息失败");
|
||||
}
|
||||
|
||||
public void deleteScenic(Long scenicId) {
|
||||
log.info("删除景区, scenicId: {}", scenicId);
|
||||
CommonResponse<Void> response = scenicV2Client.deleteScenic(scenicId);
|
||||
handleResponse(response, "删除景区失败");
|
||||
}
|
||||
|
||||
public ScenicFilterPageResponse filterScenics(ScenicFilterRequest request) {
|
||||
log.info("筛选景区, filters: {}", request.getFilters().size());
|
||||
CommonResponse<ScenicFilterPageResponse> response = scenicV2Client.filterScenics(request);
|
||||
return handleResponse(response, "筛选景区失败");
|
||||
}
|
||||
|
||||
public ScenicV2ListResponse listScenics(Integer page, Integer pageSize, Integer status, String name) {
|
||||
log.info("分页查询景区列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
|
||||
CommonResponse<ScenicV2ListResponse> response = scenicV2Client.listScenics(page, pageSize, status, name);
|
||||
return handleResponse(response, "分页查询景区列表失败");
|
||||
}
|
||||
|
||||
public ScenicV2WithConfigListResponse listScenicsWithConfig(Integer page, Integer pageSize, Integer status, String name) {
|
||||
log.info("分页查询景区带配置列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
|
||||
CommonResponse<ScenicV2WithConfigListResponse> response = scenicV2Client.listScenicsWithConfig(page, pageSize, status, name);
|
||||
return handleResponse(response, "分页查询景区带配置列表失败");
|
||||
}
|
||||
|
||||
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
|
||||
if (response == null || !response.isSuccess()) {
|
||||
String msg = response != null && response.getMessage() != null
|
||||
? response.getMessage()
|
||||
: errorMessage;
|
||||
Integer code = response != null ? response.getCode() : 5000;
|
||||
throw new IntegrationException(code, msg, "zt-scenic");
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
package com.ycwl.basic.mapper;
|
||||
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicAppVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/12/2 10:07
|
||||
* 景区管理表
|
||||
*/
|
||||
@Mapper
|
||||
public interface ScenicMapper {
|
||||
List<ScenicRespVO> list(ScenicReqQuery scenicReqQuery);
|
||||
|
||||
ScenicEntity get(Long id);
|
||||
|
||||
ScenicRespVO getById(Long id);
|
||||
|
||||
int add(ScenicAddOrUpdateReq scenic);
|
||||
|
||||
int deleteById(Long id);
|
||||
|
||||
int update(ScenicAddOrUpdateReq scenic);
|
||||
|
||||
int updateStatus(Long id);
|
||||
|
||||
ScenicConfigEntity getConfig(Long scenicId);
|
||||
/**
|
||||
* 添加景区配置
|
||||
*
|
||||
* @param scenicConfig
|
||||
* @return
|
||||
*/
|
||||
int addConfig(ScenicConfigEntity scenicConfig);
|
||||
|
||||
/**
|
||||
* 修改景区配置
|
||||
*
|
||||
* @param scenicConfigEntity
|
||||
* @return
|
||||
*/
|
||||
int updateConfigById(ScenicConfigEntity scenicConfigEntity);
|
||||
|
||||
/**
|
||||
* 根据景区id删除配置
|
||||
*
|
||||
* @param scenicId
|
||||
*/
|
||||
void deleteConfigByScenicId(Long scenicId);
|
||||
|
||||
List<ScenicAppVO> appList(ScenicReqQuery scenicReqQuery);
|
||||
|
||||
ScenicRespVO getAppById(Long id);
|
||||
|
||||
/**
|
||||
* 通过经纬度计算景区距离
|
||||
*
|
||||
* @param scenicIndexVO
|
||||
* @return
|
||||
*/
|
||||
List<ScenicAppVO> scenicListByLnLa(ScenicIndexVO scenicIndexVO);
|
||||
}
|
@@ -67,17 +67,17 @@ public class ScenicConfigEntity {
|
||||
/**
|
||||
* 是否开启全部免费
|
||||
*/
|
||||
private Integer allFree;
|
||||
private Boolean allFree;
|
||||
/**
|
||||
* 是否禁用源视频
|
||||
* 0-否 1-是
|
||||
*/
|
||||
private Integer disableSourceVideo;
|
||||
private Boolean disableSourceVideo;
|
||||
/**
|
||||
* 是否禁用源图片
|
||||
* 0-否 1-是
|
||||
*/
|
||||
private Integer disableSourceImage;
|
||||
private Boolean disableSourceImage;
|
||||
private Integer templateNewVideoType;
|
||||
/**
|
||||
* 是否开启防录屏
|
||||
@@ -130,5 +130,5 @@ public class ScenicConfigEntity {
|
||||
* 是否启用券码功能
|
||||
* 0-禁用 1-启用
|
||||
*/
|
||||
private Integer voucherEnable;
|
||||
private Boolean voucherEnable;
|
||||
}
|
||||
|
@@ -1,11 +1,8 @@
|
||||
package com.ycwl.basic.model.pc.scenic.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
@@ -13,15 +10,17 @@ import java.util.Date;
|
||||
* 景区管理表
|
||||
*/
|
||||
@Data
|
||||
@TableName("scenic")
|
||||
public class ScenicEntity {
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 景区名称
|
||||
*/
|
||||
private String name;
|
||||
private Integer mpId;
|
||||
private String phone;
|
||||
private String logoUrl;
|
||||
// 封面图
|
||||
private String coverUrl;
|
||||
/**
|
||||
* 景区介绍
|
||||
*/
|
||||
@@ -58,11 +57,10 @@ public class ScenicEntity {
|
||||
* 状态 1启用0关闭
|
||||
*/
|
||||
private String status;
|
||||
private Date createTime;
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 景区源素材价格,元
|
||||
*/
|
||||
private String kfCodeUrl;
|
||||
private BigDecimal price;
|
||||
private BigDecimal sourceVideoPrice;
|
||||
private BigDecimal sourceImagePrice;
|
||||
|
@@ -23,51 +23,9 @@ public class ScenicReqQuery extends BaseQueryParameterReq {
|
||||
*/
|
||||
// 景区名称
|
||||
private String name;
|
||||
/**
|
||||
* 景区介绍
|
||||
*/
|
||||
// 景区介绍
|
||||
private String introduction;
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
// 经度
|
||||
private BigDecimal longitude;
|
||||
/***
|
||||
* 纬度
|
||||
*/
|
||||
// 纬度
|
||||
private BigDecimal latitude;
|
||||
/**
|
||||
* 半径(km)
|
||||
*/
|
||||
// 半径(km)
|
||||
private BigDecimal radius;
|
||||
/**
|
||||
* 省份
|
||||
*/
|
||||
// 省份
|
||||
private String province;
|
||||
/**
|
||||
* 城市
|
||||
*/
|
||||
// 城市
|
||||
private String city;
|
||||
/**
|
||||
* 区
|
||||
*/
|
||||
// 区
|
||||
private String area;
|
||||
/**
|
||||
* 详细地址
|
||||
*/
|
||||
// 详细地址
|
||||
private String address;
|
||||
/**
|
||||
* 状态 1启用0关闭
|
||||
*/
|
||||
// 状态 1启用0关闭
|
||||
private String status;
|
||||
private Date startTime;
|
||||
private Date endTime;
|
||||
}
|
||||
|
@@ -34,9 +34,9 @@ public class ScenicConfigResp {
|
||||
* 视频保存时间
|
||||
*/
|
||||
private Integer videoStoreDay;
|
||||
private Integer allFree;
|
||||
private Integer disableSourceVideo;
|
||||
private Integer disableSourceImage;
|
||||
private Boolean allFree;
|
||||
private Boolean disableSourceVideo;
|
||||
private Boolean disableSourceImage;
|
||||
private Integer antiScreenRecordType;
|
||||
private Integer videoSourceStoreDay;
|
||||
private Integer imageSourceStoreDay;
|
||||
@@ -45,9 +45,5 @@ public class ScenicConfigResp {
|
||||
|
||||
private String imageSourcePackHint = "";
|
||||
private String videoSourcePackHint = "";
|
||||
/**
|
||||
* 是否启用券码功能
|
||||
* 0-禁用 1-启用
|
||||
*/
|
||||
private Integer voucherEnable;
|
||||
private Boolean voucherEnable;
|
||||
}
|
||||
|
@@ -74,19 +74,6 @@ public class ScenicRespVO {
|
||||
*/
|
||||
// 详细地址
|
||||
private String address;
|
||||
/**
|
||||
* 状态 1启用0关闭
|
||||
*/
|
||||
// 状态 1启用0关闭
|
||||
private Integer status;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
// 景区源素材价格,元
|
||||
private BigDecimal price;
|
||||
private BigDecimal sourceVideoPrice;
|
||||
private BigDecimal sourceImagePrice;
|
||||
// 镜头数
|
||||
private Integer lensNum;
|
||||
private String kfCodeUrl;
|
||||
|
@@ -0,0 +1,89 @@
|
||||
package com.ycwl.basic.order.controller;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.order.dto.*;
|
||||
import com.ycwl.basic.order.service.IOrderService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 订单管理控制器V2 - 管理端
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/order/v2")
|
||||
@RequiredArgsConstructor
|
||||
public class OrderV2Controller {
|
||||
|
||||
private final IOrderService orderService;
|
||||
|
||||
/**
|
||||
* 分页查询订单列表
|
||||
*/
|
||||
@PostMapping("/page")
|
||||
public ApiResponse<PageInfo<OrderV2ListResponse>> pageOrders(@RequestBody OrderV2PageRequest request) {
|
||||
log.info("分页查询订单列表: {}", request);
|
||||
try {
|
||||
PageInfo<OrderV2ListResponse> pageInfo = orderService.pageOrders(request);
|
||||
return ApiResponse.success(pageInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询订单列表失败", e);
|
||||
return ApiResponse.fail("查询失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单详情
|
||||
*/
|
||||
@GetMapping("/detail/{orderId}")
|
||||
public ApiResponse<OrderV2DetailResponse> getOrderDetail(@PathVariable("orderId") Long orderId) {
|
||||
log.info("查询订单详情: orderId={}", orderId);
|
||||
try {
|
||||
OrderV2DetailResponse detail = orderService.getOrderDetail(orderId);
|
||||
if (detail == null) {
|
||||
return ApiResponse.fail("订单不存在");
|
||||
}
|
||||
return ApiResponse.success(detail);
|
||||
} catch (Exception e) {
|
||||
log.error("查询订单详情失败: orderId={}", orderId, e);
|
||||
return ApiResponse.fail("查询失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单备注
|
||||
*/
|
||||
@PutMapping("/remark/{orderId}")
|
||||
public ApiResponse<String> updateOrderRemarks(@PathVariable("orderId") Long orderId,
|
||||
@RequestBody OrderRemarkRequest request) {
|
||||
log.info("更新订单备注: orderId={}, remarks={}", orderId, request.getRemarks());
|
||||
try {
|
||||
boolean success = orderService.updateOrderRemarks(orderId, request.getRemarks());
|
||||
if (success) {
|
||||
return ApiResponse.success("备注更新成功");
|
||||
} else {
|
||||
return ApiResponse.fail("备注更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("更新订单备注失败: orderId={}", orderId, e);
|
||||
return ApiResponse.fail("更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款
|
||||
*/
|
||||
@PostMapping("/refund")
|
||||
public ApiResponse<String> createRefund(@RequestBody RefundRequest request) {
|
||||
log.info("申请退款: {}", request);
|
||||
try {
|
||||
Long refundId = orderService.createRefundRecord(request);
|
||||
return ApiResponse.success("退款申请已提交,退款ID:" + refundId);
|
||||
} catch (Exception e) {
|
||||
log.error("申请退款失败: {}", request, e);
|
||||
return ApiResponse.fail("退款申请失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 订单备注修改请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class OrderRemarkRequest {
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remarks;
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import com.ycwl.basic.order.entity.OrderDiscountV2;
|
||||
import com.ycwl.basic.order.entity.OrderItemV2;
|
||||
import com.ycwl.basic.order.entity.OrderRefundV2;
|
||||
import com.ycwl.basic.order.enums.OrderStatus;
|
||||
import com.ycwl.basic.order.enums.PaymentStatus;
|
||||
import com.ycwl.basic.order.enums.RefundStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单详情响应DTO
|
||||
*/
|
||||
@Data
|
||||
public class OrderV2DetailResponse {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 会员ID
|
||||
*/
|
||||
private Long memberId;
|
||||
|
||||
/**
|
||||
* 小程序openId
|
||||
*/
|
||||
private String openId;
|
||||
|
||||
/**
|
||||
* 人脸ID
|
||||
*/
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 原始金额
|
||||
*/
|
||||
private BigDecimal originalAmount;
|
||||
|
||||
/**
|
||||
* 优惠金额
|
||||
*/
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
/**
|
||||
* 最终金额
|
||||
*/
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
private OrderStatus orderStatus;
|
||||
|
||||
/**
|
||||
* 订单状态描述
|
||||
*/
|
||||
private String orderStatusDesc;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
*/
|
||||
private PaymentStatus paymentStatus;
|
||||
|
||||
/**
|
||||
* 支付状态描述
|
||||
*/
|
||||
private String paymentStatusDesc;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
*/
|
||||
private RefundStatus refundStatus;
|
||||
|
||||
/**
|
||||
* 退款状态描述
|
||||
*/
|
||||
private String refundStatusDesc;
|
||||
|
||||
/**
|
||||
* 总退款金额
|
||||
*/
|
||||
private BigDecimal totalRefundAmount;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remarks;
|
||||
|
||||
/**
|
||||
* 支付时间
|
||||
*/
|
||||
private Date payTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
private Date completeTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 订单商品明细列表
|
||||
*/
|
||||
private List<OrderItemV2> orderItems;
|
||||
|
||||
/**
|
||||
* 订单优惠记录列表
|
||||
*/
|
||||
private List<OrderDiscountV2> orderDiscounts;
|
||||
|
||||
/**
|
||||
* 订单退款记录列表
|
||||
*/
|
||||
private List<OrderRefundV2> orderRefunds;
|
||||
}
|
131
src/main/java/com/ycwl/basic/order/dto/OrderV2ListResponse.java
Normal file
131
src/main/java/com/ycwl/basic/order/dto/OrderV2ListResponse.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import com.ycwl.basic.order.enums.OrderStatus;
|
||||
import com.ycwl.basic.order.enums.PaymentStatus;
|
||||
import com.ycwl.basic.order.enums.RefundStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单列表响应DTO
|
||||
*/
|
||||
@Data
|
||||
public class OrderV2ListResponse {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 会员ID
|
||||
*/
|
||||
private Long memberId;
|
||||
|
||||
/**
|
||||
* 小程序openId
|
||||
*/
|
||||
private String openId;
|
||||
|
||||
/**
|
||||
* 人脸ID
|
||||
*/
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 原始金额
|
||||
*/
|
||||
private BigDecimal originalAmount;
|
||||
|
||||
/**
|
||||
* 优惠金额
|
||||
*/
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
/**
|
||||
* 最终金额
|
||||
*/
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
private OrderStatus orderStatus;
|
||||
|
||||
/**
|
||||
* 订单状态描述
|
||||
*/
|
||||
private String orderStatusDesc;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
*/
|
||||
private PaymentStatus paymentStatus;
|
||||
|
||||
/**
|
||||
* 支付状态描述
|
||||
*/
|
||||
private String paymentStatusDesc;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
*/
|
||||
private RefundStatus refundStatus;
|
||||
|
||||
/**
|
||||
* 退款状态描述
|
||||
*/
|
||||
private String refundStatusDesc;
|
||||
|
||||
/**
|
||||
* 总退款金额
|
||||
*/
|
||||
private BigDecimal totalRefundAmount;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remarks;
|
||||
|
||||
/**
|
||||
* 支付时间
|
||||
*/
|
||||
private Date payTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
private Date completeTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 商品数量(订单中商品种类数)
|
||||
*/
|
||||
private Integer itemCount;
|
||||
|
||||
/**
|
||||
* 商品总数量(所有商品数量之和)
|
||||
*/
|
||||
private Integer totalQuantity;
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单分页查询请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class OrderV2PageRequest {
|
||||
|
||||
/**
|
||||
* 页码(从1开始)
|
||||
*/
|
||||
private Integer pageNum = 1;
|
||||
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
private Integer pageSize = 10;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 用户ID(移动端查询时自动设置)
|
||||
*/
|
||||
private Long memberId;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
private String orderStatus;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
*/
|
||||
private String paymentStatus;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
*/
|
||||
private String refundStatus;
|
||||
|
||||
/**
|
||||
* 开始创建时间
|
||||
*/
|
||||
private Date createTimeStart;
|
||||
|
||||
/**
|
||||
* 结束创建时间
|
||||
*/
|
||||
private Date createTimeEnd;
|
||||
|
||||
/**
|
||||
* 开始支付时间
|
||||
*/
|
||||
private Date payTimeStart;
|
||||
|
||||
/**
|
||||
* 结束支付时间
|
||||
*/
|
||||
private Date payTimeEnd;
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 支付回调响应DTO
|
||||
*/
|
||||
@Data
|
||||
public class PaymentCallbackResponse {
|
||||
|
||||
/**
|
||||
* 处理是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 支付状态变化类型
|
||||
*/
|
||||
private String statusChangeType;
|
||||
|
||||
/**
|
||||
* 创建成功响应
|
||||
*/
|
||||
public static PaymentCallbackResponse success(Long orderId, String orderNo, String statusChangeType) {
|
||||
PaymentCallbackResponse response = new PaymentCallbackResponse();
|
||||
response.success = true;
|
||||
response.message = "回调处理成功";
|
||||
response.orderId = orderId;
|
||||
response.orderNo = orderNo;
|
||||
response.statusChangeType = statusChangeType;
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败响应
|
||||
*/
|
||||
public static PaymentCallbackResponse failure(String message) {
|
||||
PaymentCallbackResponse response = new PaymentCallbackResponse();
|
||||
response.success = false;
|
||||
response.message = message;
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败响应(包含订单信息)
|
||||
*/
|
||||
public static PaymentCallbackResponse failure(String message, Long orderId, String orderNo) {
|
||||
PaymentCallbackResponse response = new PaymentCallbackResponse();
|
||||
response.success = false;
|
||||
response.message = message;
|
||||
response.orderId = orderId;
|
||||
response.orderNo = orderNo;
|
||||
return response;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 获取支付参数请求DTO
|
||||
* 所有参数都是可选的,系统会自动生成商品名称和描述
|
||||
*/
|
||||
@Data
|
||||
public class PaymentParamsRequest {
|
||||
|
||||
// 预留字段,目前所有信息都由系统自动生成
|
||||
// 可以在未来版本中扩展支持自定义参数
|
||||
|
||||
public PaymentParamsRequest() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付参数响应DTO
|
||||
*/
|
||||
@Data
|
||||
public class PaymentParamsResponse {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private BigDecimal payAmount;
|
||||
|
||||
/**
|
||||
* 是否需要支付(false表示免费订单)
|
||||
*/
|
||||
private Boolean needPay;
|
||||
|
||||
/**
|
||||
* 支付参数(微信小程序调起支付所需的参数)
|
||||
* 包含:appId, timeStamp, nonceStr, package, signType, paySign等
|
||||
*/
|
||||
private Map<String, Object> paymentParams;
|
||||
|
||||
/**
|
||||
* 支付描述信息
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String goodsName;
|
||||
|
||||
/**
|
||||
* 创建成功的支付参数响应
|
||||
*/
|
||||
public static PaymentParamsResponse success(Long orderId, String orderNo, BigDecimal payAmount,
|
||||
Boolean needPay, Map<String, Object> paymentParams) {
|
||||
PaymentParamsResponse response = new PaymentParamsResponse();
|
||||
response.orderId = orderId;
|
||||
response.orderNo = orderNo;
|
||||
response.payAmount = payAmount;
|
||||
response.needPay = needPay;
|
||||
response.paymentParams = paymentParams;
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建免费订单的响应
|
||||
*/
|
||||
public static PaymentParamsResponse free(Long orderId, String orderNo) {
|
||||
PaymentParamsResponse response = new PaymentParamsResponse();
|
||||
response.orderId = orderId;
|
||||
response.orderNo = orderNo;
|
||||
response.payAmount = BigDecimal.ZERO;
|
||||
response.needPay = false;
|
||||
response.paymentParams = null;
|
||||
return response;
|
||||
}
|
||||
}
|
52
src/main/java/com/ycwl/basic/order/dto/RefundRequest.java
Normal file
52
src/main/java/com/ycwl/basic/order/dto/RefundRequest.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package com.ycwl.basic.order.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 退款申请请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class RefundRequest {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 退款类型
|
||||
*/
|
||||
private String refundType;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
/**
|
||||
* 退款手续费
|
||||
*/
|
||||
private BigDecimal refundFee = BigDecimal.ZERO;
|
||||
|
||||
/**
|
||||
* 退款原因
|
||||
*/
|
||||
private String refundReason;
|
||||
|
||||
/**
|
||||
* 退款详细说明
|
||||
*/
|
||||
private String refundDescription;
|
||||
|
||||
/**
|
||||
* 操作备注
|
||||
*/
|
||||
private String operatorRemarks;
|
||||
|
||||
/**
|
||||
* 退款渠道
|
||||
*/
|
||||
private String refundChannel = "ORIGINAL";
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
package com.ycwl.basic.order.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ycwl.basic.order.enums.DiscountType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单优惠记录表实体V2
|
||||
*/
|
||||
@Data
|
||||
@TableName("order_discount_v2")
|
||||
public class OrderDiscountV2 {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@TableField("order_id")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 优惠类型
|
||||
*/
|
||||
@TableField("discount_type")
|
||||
private DiscountType discountType;
|
||||
|
||||
/**
|
||||
* 优惠名称
|
||||
*/
|
||||
@TableField("discount_name")
|
||||
private String discountName;
|
||||
|
||||
/**
|
||||
* 优惠金额
|
||||
*/
|
||||
@TableField("discount_amount")
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
/**
|
||||
* 优惠比例
|
||||
*/
|
||||
@TableField("discount_rate")
|
||||
private BigDecimal discountRate;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
@TableField("sort_order")
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 优惠券ID
|
||||
*/
|
||||
@TableField("coupon_id")
|
||||
private Long couponId;
|
||||
|
||||
/**
|
||||
* 优惠券码
|
||||
*/
|
||||
@TableField("coupon_code")
|
||||
private String couponCode;
|
||||
|
||||
/**
|
||||
* 券码
|
||||
*/
|
||||
@TableField("voucher_code")
|
||||
private String voucherCode;
|
||||
|
||||
/**
|
||||
* 券码批次ID
|
||||
*/
|
||||
@TableField("voucher_batch_id")
|
||||
private Long voucherBatchId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private Date createTime;
|
||||
}
|
84
src/main/java/com/ycwl/basic/order/entity/OrderItemV2.java
Normal file
84
src/main/java/com/ycwl/basic/order/entity/OrderItemV2.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.ycwl.basic.order.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单商品明细表实体V2
|
||||
*/
|
||||
@Data
|
||||
@TableName("order_item_v2")
|
||||
public class OrderItemV2 {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@TableField("order_id")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 商品类型
|
||||
*/
|
||||
@TableField("product_type")
|
||||
private String productType;
|
||||
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
@TableField("product_id")
|
||||
private String productId;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
@TableField("product_name")
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
@TableField("quantity")
|
||||
private Integer quantity;
|
||||
|
||||
/**
|
||||
* 单价
|
||||
*/
|
||||
@TableField("unit_price")
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
/**
|
||||
* 原始小计
|
||||
*/
|
||||
@TableField("original_amount")
|
||||
private BigDecimal originalAmount;
|
||||
|
||||
/**
|
||||
* 最终小计
|
||||
*/
|
||||
@TableField("final_amount")
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private Date updateTime;
|
||||
}
|
146
src/main/java/com/ycwl/basic/order/entity/OrderRefundV2.java
Normal file
146
src/main/java/com/ycwl/basic/order/entity/OrderRefundV2.java
Normal file
@@ -0,0 +1,146 @@
|
||||
package com.ycwl.basic.order.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ycwl.basic.order.enums.RefundStatus;
|
||||
import com.ycwl.basic.order.enums.RefundType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单退款记录表实体V2
|
||||
*/
|
||||
@Data
|
||||
@TableName("order_refund_v2")
|
||||
public class OrderRefundV2 {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@TableField("order_id")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 退款单号
|
||||
*/
|
||||
@TableField("refund_no")
|
||||
private String refundNo;
|
||||
|
||||
/**
|
||||
* 退款类型
|
||||
*/
|
||||
@TableField("refund_type")
|
||||
private RefundType refundType;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
@TableField("refund_amount")
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
/**
|
||||
* 退款手续费
|
||||
*/
|
||||
@TableField("refund_fee")
|
||||
private BigDecimal refundFee;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
*/
|
||||
@TableField("refund_status")
|
||||
private RefundStatus refundStatus;
|
||||
|
||||
/**
|
||||
* 退款原因
|
||||
*/
|
||||
@TableField("refund_reason")
|
||||
private String refundReason;
|
||||
|
||||
/**
|
||||
* 退款详细说明
|
||||
*/
|
||||
@TableField("refund_description")
|
||||
private String refundDescription;
|
||||
|
||||
/**
|
||||
* 申请人ID
|
||||
*/
|
||||
@TableField("apply_by")
|
||||
private Long applyBy;
|
||||
|
||||
/**
|
||||
* 审批人ID
|
||||
*/
|
||||
@TableField("approve_by")
|
||||
private Long approveBy;
|
||||
|
||||
/**
|
||||
* 操作备注
|
||||
*/
|
||||
@TableField("operator_remarks")
|
||||
private String operatorRemarks;
|
||||
|
||||
/**
|
||||
* 支付平台退款单号
|
||||
*/
|
||||
@TableField("payment_refund_id")
|
||||
private String paymentRefundId;
|
||||
|
||||
/**
|
||||
* 退款渠道
|
||||
*/
|
||||
@TableField("refund_channel")
|
||||
private String refundChannel;
|
||||
|
||||
/**
|
||||
* 申请时间
|
||||
*/
|
||||
@TableField("apply_time")
|
||||
private Date applyTime;
|
||||
|
||||
/**
|
||||
* 审批时间
|
||||
*/
|
||||
@TableField("approve_time")
|
||||
private Date approveTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@TableField("complete_time")
|
||||
private Date completeTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField("create_by")
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@TableField("update_by")
|
||||
private Long updateBy;
|
||||
}
|
153
src/main/java/com/ycwl/basic/order/entity/OrderV2.java
Normal file
153
src/main/java/com/ycwl/basic/order/entity/OrderV2.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package com.ycwl.basic.order.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ycwl.basic.order.enums.OrderStatus;
|
||||
import com.ycwl.basic.order.enums.PaymentStatus;
|
||||
import com.ycwl.basic.order.enums.RefundStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单主表实体V2
|
||||
*/
|
||||
@Data
|
||||
@TableName("order_v2")
|
||||
public class OrderV2 {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
@TableField("order_no")
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 会员ID
|
||||
*/
|
||||
@TableField("member_id")
|
||||
private Long memberId;
|
||||
|
||||
/**
|
||||
* 小程序openId
|
||||
*/
|
||||
@TableField("open_id")
|
||||
private String openId;
|
||||
|
||||
/**
|
||||
* 人脸ID
|
||||
*/
|
||||
@TableField("face_id")
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
@TableField("scenic_id")
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 原始金额
|
||||
*/
|
||||
@TableField("original_amount")
|
||||
private BigDecimal originalAmount;
|
||||
|
||||
/**
|
||||
* 优惠金额
|
||||
*/
|
||||
@TableField("discount_amount")
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
/**
|
||||
* 最终金额
|
||||
*/
|
||||
@TableField("final_amount")
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
@TableField("order_status")
|
||||
private OrderStatus orderStatus;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
*/
|
||||
@TableField("payment_status")
|
||||
private PaymentStatus paymentStatus;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
*/
|
||||
@TableField("refund_status")
|
||||
private RefundStatus refundStatus;
|
||||
|
||||
/**
|
||||
* 总退款金额
|
||||
*/
|
||||
@TableField("total_refund_amount")
|
||||
private BigDecimal totalRefundAmount;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
@TableField("remarks")
|
||||
private String remarks;
|
||||
|
||||
/**
|
||||
* 支付时间
|
||||
*/
|
||||
@TableField("pay_time")
|
||||
private Date payTime;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@TableField("complete_time")
|
||||
private Date completeTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField("create_by")
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@TableField("update_by")
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
@TableField("deleted")
|
||||
private Integer deleted;
|
||||
|
||||
/**
|
||||
* 删除时间
|
||||
*/
|
||||
@TableField("deleted_at")
|
||||
private Date deletedAt;
|
||||
}
|
60
src/main/java/com/ycwl/basic/order/enums/DiscountType.java
Normal file
60
src/main/java/com/ycwl/basic/order/enums/DiscountType.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.ycwl.basic.order.enums;
|
||||
|
||||
/**
|
||||
* 优惠类型枚举
|
||||
*/
|
||||
public enum DiscountType {
|
||||
|
||||
/**
|
||||
* 优惠券
|
||||
*/
|
||||
COUPON("COUPON", "优惠券"),
|
||||
|
||||
/**
|
||||
* 券码
|
||||
*/
|
||||
VOUCHER("VOUCHER", "券码"),
|
||||
|
||||
/**
|
||||
* 限时立减
|
||||
*/
|
||||
LIMITED_TIME("LIMITED_TIME", "限时立减"),
|
||||
|
||||
/**
|
||||
* 套餐优惠
|
||||
*/
|
||||
BUNDLE("BUNDLE", "套餐优惠"),
|
||||
|
||||
/**
|
||||
* 一口价优惠
|
||||
*/
|
||||
FIXED_PRICE("FIXED_PRICE", "一口价优惠");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
DiscountType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*/
|
||||
public static DiscountType fromCode(String code) {
|
||||
for (DiscountType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown DiscountType code: " + code);
|
||||
}
|
||||
}
|
70
src/main/java/com/ycwl/basic/order/enums/OrderStatus.java
Normal file
70
src/main/java/com/ycwl/basic/order/enums/OrderStatus.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package com.ycwl.basic.order.enums;
|
||||
|
||||
/**
|
||||
* 订单状态枚举
|
||||
*/
|
||||
public enum OrderStatus {
|
||||
|
||||
/**
|
||||
* 待支付
|
||||
*/
|
||||
PENDING_PAYMENT("PENDING_PAYMENT", "待支付"),
|
||||
|
||||
/**
|
||||
* 已支付
|
||||
*/
|
||||
PAID("PAID", "已支付"),
|
||||
|
||||
/**
|
||||
* 处理中
|
||||
*/
|
||||
PROCESSING("PROCESSING", "处理中"),
|
||||
|
||||
/**
|
||||
* 已完成
|
||||
*/
|
||||
COMPLETED("COMPLETED", "已完成"),
|
||||
|
||||
/**
|
||||
* 已取消
|
||||
*/
|
||||
CANCELLED("CANCELLED", "已取消"),
|
||||
|
||||
/**
|
||||
* 退款中
|
||||
*/
|
||||
REFUNDING("REFUNDING", "退款中"),
|
||||
|
||||
/**
|
||||
* 已退款
|
||||
*/
|
||||
REFUNDED("REFUNDED", "已退款");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
OrderStatus(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*/
|
||||
public static OrderStatus fromCode(String code) {
|
||||
for (OrderStatus status : values()) {
|
||||
if (status.code.equals(code)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown OrderStatus code: " + code);
|
||||
}
|
||||
}
|
50
src/main/java/com/ycwl/basic/order/enums/PaymentStatus.java
Normal file
50
src/main/java/com/ycwl/basic/order/enums/PaymentStatus.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.ycwl.basic.order.enums;
|
||||
|
||||
/**
|
||||
* 支付状态枚举
|
||||
*/
|
||||
public enum PaymentStatus {
|
||||
|
||||
/**
|
||||
* 未支付
|
||||
*/
|
||||
UNPAID("UNPAID", "未支付"),
|
||||
|
||||
/**
|
||||
* 已支付
|
||||
*/
|
||||
PAID("PAID", "已支付"),
|
||||
|
||||
/**
|
||||
* 已退款
|
||||
*/
|
||||
REFUNDED("REFUNDED", "已退款");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
PaymentStatus(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*/
|
||||
public static PaymentStatus fromCode(String code) {
|
||||
for (PaymentStatus status : values()) {
|
||||
if (status.code.equals(code)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown PaymentStatus code: " + code);
|
||||
}
|
||||
}
|
55
src/main/java/com/ycwl/basic/order/enums/RefundStatus.java
Normal file
55
src/main/java/com/ycwl/basic/order/enums/RefundStatus.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.ycwl.basic.order.enums;
|
||||
|
||||
/**
|
||||
* 退款状态枚举
|
||||
*/
|
||||
public enum RefundStatus {
|
||||
|
||||
/**
|
||||
* 无退款
|
||||
*/
|
||||
NO_REFUND("NO_REFUND", "无退款"),
|
||||
|
||||
/**
|
||||
* 部分退款
|
||||
*/
|
||||
PARTIAL_REFUND("PARTIAL_REFUND", "部分退款"),
|
||||
|
||||
/**
|
||||
* 全额退款
|
||||
*/
|
||||
FULL_REFUND("FULL_REFUND", "全额退款"),
|
||||
|
||||
/**
|
||||
* 退款处理中
|
||||
*/
|
||||
REFUND_PROCESSING("REFUND_PROCESSING", "退款处理中");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
RefundStatus(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*/
|
||||
public static RefundStatus fromCode(String code) {
|
||||
for (RefundStatus status : values()) {
|
||||
if (status.code.equals(code)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown RefundStatus code: " + code);
|
||||
}
|
||||
}
|
50
src/main/java/com/ycwl/basic/order/enums/RefundType.java
Normal file
50
src/main/java/com/ycwl/basic/order/enums/RefundType.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.ycwl.basic.order.enums;
|
||||
|
||||
/**
|
||||
* 退款类型枚举
|
||||
*/
|
||||
public enum RefundType {
|
||||
|
||||
/**
|
||||
* 全额退款
|
||||
*/
|
||||
FULL_REFUND("FULL_REFUND", "全额退款"),
|
||||
|
||||
/**
|
||||
* 部分退款
|
||||
*/
|
||||
PARTIAL_REFUND("PARTIAL_REFUND", "部分退款"),
|
||||
|
||||
/**
|
||||
* 商品退款
|
||||
*/
|
||||
ITEM_REFUND("ITEM_REFUND", "商品退款");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
RefundType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*/
|
||||
public static RefundType fromCode(String code) {
|
||||
for (RefundType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown RefundType code: " + code);
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package com.ycwl.basic.order.event;
|
||||
|
||||
/**
|
||||
* 订单事件监听器接口
|
||||
*/
|
||||
public interface OrderEventListener {
|
||||
|
||||
/**
|
||||
* 支付状态变更事件
|
||||
*
|
||||
* @param event 支付状态变更事件
|
||||
*/
|
||||
void onPaymentStatusChanged(PaymentStatusChangeEvent event);
|
||||
|
||||
/**
|
||||
* 退款状态变更事件
|
||||
*
|
||||
* @param event 退款状态变更事件
|
||||
*/
|
||||
void onRefundStatusChanged(RefundStatusChangeEvent event);
|
||||
|
||||
/**
|
||||
* 订单状态变更事件
|
||||
*
|
||||
* @param event 订单状态变更事件
|
||||
*/
|
||||
void onOrderStatusChanged(OrderStatusChangeEvent event);
|
||||
}
|
125
src/main/java/com/ycwl/basic/order/event/OrderEventManager.java
Normal file
125
src/main/java/com/ycwl/basic/order/event/OrderEventManager.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package com.ycwl.basic.order.event;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 订单事件管理器
|
||||
* 管理事件监听器的注册和事件分发
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class OrderEventManager {
|
||||
|
||||
private final List<OrderEventListener> listeners = new ArrayList<>();
|
||||
private final Executor eventExecutor = Executors.newFixedThreadPool(5,
|
||||
r -> new Thread(r, "order-event-thread"));
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("订单事件管理器初始化完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册事件监听器
|
||||
*
|
||||
* @param listener 监听器实例
|
||||
*/
|
||||
public void registerListener(OrderEventListener listener) {
|
||||
if (listener != null && !listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
log.info("注册订单事件监听器: {}", listener.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
*
|
||||
* @param listener 监听器实例
|
||||
*/
|
||||
public void removeListener(OrderEventListener listener) {
|
||||
if (listeners.remove(listener)) {
|
||||
log.info("移除订单事件监听器: {}", listener.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布支付状态变更事件
|
||||
*
|
||||
* @param event 支付状态变更事件
|
||||
*/
|
||||
public void publishPaymentStatusChangeEvent(PaymentStatusChangeEvent event) {
|
||||
log.info("发布支付状态变更事件: orderId={}, {} -> {}",
|
||||
event.getOrderId(), event.getOldPaymentStatus(), event.getNewPaymentStatus());
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
for (OrderEventListener listener : listeners) {
|
||||
try {
|
||||
listener.onPaymentStatusChanged(event);
|
||||
} catch (Exception e) {
|
||||
log.error("处理支付状态变更事件失败: listener={}, orderId={}",
|
||||
listener.getClass().getSimpleName(), event.getOrderId(), e);
|
||||
}
|
||||
}
|
||||
}, eventExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布退款状态变更事件
|
||||
*
|
||||
* @param event 退款状态变更事件
|
||||
*/
|
||||
public void publishRefundStatusChangeEvent(RefundStatusChangeEvent event) {
|
||||
log.info("发布退款状态变更事件: orderId={}, refundId={}, {} -> {}",
|
||||
event.getOrderId(), event.getRefundId(),
|
||||
event.getOldRefundStatus(), event.getNewRefundStatus());
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
for (OrderEventListener listener : listeners) {
|
||||
try {
|
||||
listener.onRefundStatusChanged(event);
|
||||
} catch (Exception e) {
|
||||
log.error("处理退款状态变更事件失败: listener={}, orderId={}, refundId={}",
|
||||
listener.getClass().getSimpleName(), event.getOrderId(), event.getRefundId(), e);
|
||||
}
|
||||
}
|
||||
}, eventExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布订单状态变更事件
|
||||
*
|
||||
* @param event 订单状态变更事件
|
||||
*/
|
||||
public void publishOrderStatusChangeEvent(OrderStatusChangeEvent event) {
|
||||
log.info("发布订单状态变更事件: orderId={}, {} -> {}",
|
||||
event.getOrderId(), event.getOldOrderStatus(), event.getNewOrderStatus());
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
for (OrderEventListener listener : listeners) {
|
||||
try {
|
||||
listener.onOrderStatusChanged(event);
|
||||
} catch (Exception e) {
|
||||
log.error("处理订单状态变更事件失败: listener={}, orderId={}",
|
||||
listener.getClass().getSimpleName(), event.getOrderId(), e);
|
||||
}
|
||||
}
|
||||
}, eventExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已注册的监听器数量
|
||||
*
|
||||
* @return 监听器数量
|
||||
*/
|
||||
public int getListenerCount() {
|
||||
return listeners.size();
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.ycwl.basic.order.event;
|
||||
|
||||
import com.ycwl.basic.order.enums.OrderStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单状态变更事件
|
||||
*/
|
||||
@Data
|
||||
public class OrderStatusChangeEvent {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 旧的订单状态
|
||||
*/
|
||||
private OrderStatus oldOrderStatus;
|
||||
|
||||
/**
|
||||
* 新的订单状态
|
||||
*/
|
||||
private OrderStatus newOrderStatus;
|
||||
|
||||
/**
|
||||
* 变更时间
|
||||
*/
|
||||
private Date changeTime;
|
||||
|
||||
/**
|
||||
* 变更原因
|
||||
*/
|
||||
private String changeReason;
|
||||
|
||||
/**
|
||||
* 操作人ID
|
||||
*/
|
||||
private Long operatorId;
|
||||
|
||||
public OrderStatusChangeEvent(Long orderId, String orderNo,
|
||||
OrderStatus oldStatus, OrderStatus newStatus,
|
||||
String changeReason, Long operatorId) {
|
||||
this.orderId = orderId;
|
||||
this.orderNo = orderNo;
|
||||
this.oldOrderStatus = oldStatus;
|
||||
this.newOrderStatus = newStatus;
|
||||
this.changeReason = changeReason;
|
||||
this.operatorId = operatorId;
|
||||
this.changeTime = new Date();
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.ycwl.basic.order.event;
|
||||
|
||||
import com.ycwl.basic.order.enums.PaymentStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 支付状态变更事件
|
||||
*/
|
||||
@Data
|
||||
public class PaymentStatusChangeEvent {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 旧的支付状态
|
||||
*/
|
||||
private PaymentStatus oldPaymentStatus;
|
||||
|
||||
/**
|
||||
* 新的支付状态
|
||||
*/
|
||||
private PaymentStatus newPaymentStatus;
|
||||
|
||||
/**
|
||||
* 变更时间
|
||||
*/
|
||||
private Date changeTime;
|
||||
|
||||
/**
|
||||
* 变更原因
|
||||
*/
|
||||
private String changeReason;
|
||||
|
||||
/**
|
||||
* 操作人ID
|
||||
*/
|
||||
private Long operatorId;
|
||||
|
||||
public PaymentStatusChangeEvent(Long orderId, String orderNo,
|
||||
PaymentStatus oldStatus, PaymentStatus newStatus,
|
||||
String changeReason, Long operatorId) {
|
||||
this.orderId = orderId;
|
||||
this.orderNo = orderNo;
|
||||
this.oldPaymentStatus = oldStatus;
|
||||
this.newPaymentStatus = newStatus;
|
||||
this.changeReason = changeReason;
|
||||
this.operatorId = operatorId;
|
||||
this.changeTime = new Date();
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package com.ycwl.basic.order.event;
|
||||
|
||||
import com.ycwl.basic.order.enums.RefundStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 退款状态变更事件
|
||||
*/
|
||||
@Data
|
||||
public class RefundStatusChangeEvent {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 退款记录ID
|
||||
*/
|
||||
private Long refundId;
|
||||
|
||||
/**
|
||||
* 退款单号
|
||||
*/
|
||||
private String refundNo;
|
||||
|
||||
/**
|
||||
* 旧的退款状态
|
||||
*/
|
||||
private RefundStatus oldRefundStatus;
|
||||
|
||||
/**
|
||||
* 新的退款状态
|
||||
*/
|
||||
private RefundStatus newRefundStatus;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
/**
|
||||
* 变更时间
|
||||
*/
|
||||
private Date changeTime;
|
||||
|
||||
/**
|
||||
* 变更原因
|
||||
*/
|
||||
private String changeReason;
|
||||
|
||||
/**
|
||||
* 操作人ID
|
||||
*/
|
||||
private Long operatorId;
|
||||
|
||||
public RefundStatusChangeEvent(Long orderId, String orderNo, Long refundId, String refundNo,
|
||||
RefundStatus oldStatus, RefundStatus newStatus,
|
||||
BigDecimal refundAmount, String changeReason, Long operatorId) {
|
||||
this.orderId = orderId;
|
||||
this.orderNo = orderNo;
|
||||
this.refundId = refundId;
|
||||
this.refundNo = refundNo;
|
||||
this.oldRefundStatus = oldStatus;
|
||||
this.newRefundStatus = newStatus;
|
||||
this.refundAmount = refundAmount;
|
||||
this.changeReason = changeReason;
|
||||
this.operatorId = operatorId;
|
||||
this.changeTime = new Date();
|
||||
}
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
package com.ycwl.basic.order.event.impl;
|
||||
|
||||
import com.ycwl.basic.order.event.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 支付状态变更监听器示例
|
||||
* 应用启动后自动注册到事件管理器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PaymentStatusChangeListener implements OrderEventListener {
|
||||
|
||||
@Resource
|
||||
private OrderEventManager orderEventManager;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 应用启动后自动注册到事件管理器
|
||||
orderEventManager.registerListener(this);
|
||||
log.info("支付状态变更监听器注册完成");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPaymentStatusChanged(PaymentStatusChangeEvent event) {
|
||||
log.info("处理支付状态变更事件: orderId={}, orderNo={}, {} -> {}, reason={}",
|
||||
event.getOrderId(), event.getOrderNo(),
|
||||
event.getOldPaymentStatus(), event.getNewPaymentStatus(),
|
||||
event.getChangeReason());
|
||||
|
||||
// 根据支付状态执行相应的业务逻辑
|
||||
switch (event.getNewPaymentStatus()) {
|
||||
case PAID:
|
||||
handlePaymentSuccess(event);
|
||||
break;
|
||||
case REFUNDED:
|
||||
handlePaymentRefunded(event);
|
||||
break;
|
||||
case UNPAID:
|
||||
handlePaymentPending(event);
|
||||
break;
|
||||
default:
|
||||
log.warn("未处理的支付状态: {}", event.getNewPaymentStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefundStatusChanged(RefundStatusChangeEvent event) {
|
||||
log.info("处理退款状态变更事件: orderId={}, refundId={}, {} -> {}",
|
||||
event.getOrderId(), event.getRefundId(),
|
||||
event.getOldRefundStatus(), event.getNewRefundStatus());
|
||||
|
||||
// 可以在这里添加退款相关的业务逻辑
|
||||
// 比如发送通知、更新库存等
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOrderStatusChanged(OrderStatusChangeEvent event) {
|
||||
log.info("处理订单状态变更事件: orderId={}, {} -> {}",
|
||||
event.getOrderId(), event.getOldOrderStatus(), event.getNewOrderStatus());
|
||||
|
||||
// 可以在这里添加订单状态相关的业务逻辑
|
||||
// 比如发送用户通知、触发后续流程等
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支付成功事件
|
||||
*/
|
||||
private void handlePaymentSuccess(PaymentStatusChangeEvent event) {
|
||||
log.info("处理支付成功: orderId={}", event.getOrderId());
|
||||
|
||||
// 这里可以添加支付成功后的业务逻辑:
|
||||
// 1. 发送支付成功通知
|
||||
// 2. 更新商品库存
|
||||
// 3. 生成相关凭证
|
||||
// 4. 发送短信/邮件通知
|
||||
// 5. 启动履约流程
|
||||
|
||||
// 示例:记录支付成功日志
|
||||
log.info("订单支付成功处理完成: orderId={}, changeTime={}",
|
||||
event.getOrderId(), event.getChangeTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支付退款事件
|
||||
*/
|
||||
private void handlePaymentRefunded(PaymentStatusChangeEvent event) {
|
||||
log.info("处理支付退款: orderId={}", event.getOrderId());
|
||||
|
||||
// 这里可以添加支付退款后的业务逻辑:
|
||||
// 1. 恢复商品库存
|
||||
// 2. 取消相关服务
|
||||
// 3. 发送退款通知
|
||||
// 4. 更新会员积分
|
||||
|
||||
// 示例:记录退款处理日志
|
||||
log.info("订单退款处理完成: orderId={}, changeTime={}",
|
||||
event.getOrderId(), event.getChangeTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支付待处理事件
|
||||
*/
|
||||
private void handlePaymentPending(PaymentStatusChangeEvent event) {
|
||||
log.info("处理支付待处理: orderId={}", event.getOrderId());
|
||||
|
||||
// 这里可以添加支付待处理的业务逻辑:
|
||||
// 1. 设置支付超时提醒
|
||||
// 2. 预留库存
|
||||
// 3. 发送支付提醒
|
||||
|
||||
// 示例:记录待支付处理日志
|
||||
log.info("订单待支付处理完成: orderId={}, changeTime={}",
|
||||
event.getOrderId(), event.getChangeTime());
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package com.ycwl.basic.order.exception;
|
||||
|
||||
import com.ycwl.basic.exception.BaseException;
|
||||
import com.ycwl.basic.pricing.enums.ProductType;
|
||||
|
||||
/**
|
||||
* 重复购买异常
|
||||
* 当用户尝试购买已经购买过的内容时抛出此异常
|
||||
*/
|
||||
public class DuplicatePurchaseException extends BaseException {
|
||||
|
||||
private final Long existingOrderId;
|
||||
private final String existingOrderNo;
|
||||
private final ProductType productType;
|
||||
private final String productId;
|
||||
|
||||
public DuplicatePurchaseException(String message, Long existingOrderId, String existingOrderNo,
|
||||
ProductType productType, String productId) {
|
||||
super(message);
|
||||
this.existingOrderId = existingOrderId;
|
||||
this.existingOrderNo = existingOrderNo;
|
||||
this.productType = productType;
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public DuplicatePurchaseException(String message, Long existingOrderId, String existingOrderNo,
|
||||
ProductType productType) {
|
||||
this(message, existingOrderId, existingOrderNo, productType, null);
|
||||
}
|
||||
|
||||
public Long getExistingOrderId() {
|
||||
return existingOrderId;
|
||||
}
|
||||
|
||||
public String getExistingOrderNo() {
|
||||
return existingOrderNo;
|
||||
}
|
||||
|
||||
public ProductType getProductType() {
|
||||
return productType;
|
||||
}
|
||||
|
||||
public String getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取友好的错误消息
|
||||
*/
|
||||
public String getFriendlyMessage() {
|
||||
String productDesc = productType != null ? productType.getDescription() : "商品";
|
||||
if (productId != null) {
|
||||
return String.format("您已购买过该%s(商品ID:%s),订单号:%s", productDesc, productId, existingOrderNo);
|
||||
} else {
|
||||
return String.format("您已购买过%s,订单号:%s", productDesc, existingOrderNo);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.order.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ycwl.basic.order.entity.OrderDiscountV2;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 订单优惠记录表Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface OrderDiscountMapper extends BaseMapper<OrderDiscountV2> {
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.order.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ycwl.basic.order.entity.OrderItemV2;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 订单商品明细表Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface OrderItemMapper extends BaseMapper<OrderItemV2> {
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.order.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ycwl.basic.order.entity.OrderRefundV2;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 订单退款记录表Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface OrderRefundMapper extends BaseMapper<OrderRefundV2> {
|
||||
}
|
12
src/main/java/com/ycwl/basic/order/mapper/OrderV2Mapper.java
Normal file
12
src/main/java/com/ycwl/basic/order/mapper/OrderV2Mapper.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.order.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ycwl.basic.order.entity.OrderV2;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 订单表Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface OrderV2Mapper extends BaseMapper<OrderV2> {
|
||||
}
|
145
src/main/java/com/ycwl/basic/order/service/IOrderService.java
Normal file
145
src/main/java/com/ycwl/basic/order/service/IOrderService.java
Normal file
@@ -0,0 +1,145 @@
|
||||
package com.ycwl.basic.order.service;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.dto.MobileOrderRequest;
|
||||
import com.ycwl.basic.order.dto.*;
|
||||
import com.ycwl.basic.order.entity.OrderV2;
|
||||
import com.ycwl.basic.order.enums.RefundStatus;
|
||||
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
||||
import com.ycwl.basic.order.dto.PaymentParamsRequest;
|
||||
import com.ycwl.basic.order.dto.PaymentParamsResponse;
|
||||
import com.ycwl.basic.order.dto.PaymentCallbackResponse;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 订单服务接口
|
||||
*/
|
||||
public interface IOrderService {
|
||||
|
||||
/**
|
||||
* 创建订单
|
||||
*
|
||||
* @param request 移动端下单请求
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param priceResult 价格计算结果
|
||||
* @return 订单ID
|
||||
*/
|
||||
Long createOrder(MobileOrderRequest request, Long userId, Long scenicId, PriceCalculationResult priceResult);
|
||||
|
||||
/**
|
||||
* 根据订单号查询订单
|
||||
*
|
||||
* @param orderNo 订单号
|
||||
* @return 订单信息
|
||||
*/
|
||||
OrderV2 getByOrderNo(String orderNo);
|
||||
|
||||
/**
|
||||
* 根据订单ID查询订单
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 订单信息
|
||||
*/
|
||||
OrderV2 getById(Long orderId);
|
||||
|
||||
/**
|
||||
* 更新订单状态
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param orderStatus 订单状态
|
||||
* @param paymentStatus 支付状态
|
||||
* @return 更新结果
|
||||
*/
|
||||
boolean updateOrderStatus(Long orderId, String orderStatus, String paymentStatus);
|
||||
|
||||
/**
|
||||
* 生成订单号
|
||||
*
|
||||
* @return 订单号
|
||||
*/
|
||||
String generateOrderNo();
|
||||
|
||||
// ====== 新增方法 ======
|
||||
|
||||
/**
|
||||
* 分页查询订单列表
|
||||
*
|
||||
* @param request 分页查询请求
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<OrderV2ListResponse> pageOrders(OrderV2PageRequest request);
|
||||
|
||||
/**
|
||||
* 查询订单详情(包含商品明细、优惠记录、退款记录)
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 订单详情
|
||||
*/
|
||||
OrderV2DetailResponse getOrderDetail(Long orderId);
|
||||
|
||||
/**
|
||||
* 更新订单备注
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param remarks 备注内容
|
||||
* @return 更新结果
|
||||
*/
|
||||
boolean updateOrderRemarks(Long orderId, String remarks);
|
||||
|
||||
/**
|
||||
* 更新支付状态
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param paymentStatus 支付状态
|
||||
* @return 更新结果
|
||||
*/
|
||||
boolean updatePaymentStatus(Long orderId, String paymentStatus);
|
||||
|
||||
/**
|
||||
* 更新退款状态
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param refundStatus 退款状态
|
||||
* @return 更新结果
|
||||
*/
|
||||
boolean updateRefundStatus(Long orderId, RefundStatus refundStatus);
|
||||
|
||||
/**
|
||||
* 创建退款记录
|
||||
*
|
||||
* @param request 退款申请请求
|
||||
* @return 退款记录ID
|
||||
*/
|
||||
Long createRefundRecord(RefundRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页查询订单列表(移动端使用)
|
||||
*
|
||||
* @param request 分页查询请求(已设置用户ID)
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<OrderV2ListResponse> pageOrdersByUser(OrderV2PageRequest request);
|
||||
|
||||
// ====== 支付相关方法 ======
|
||||
|
||||
/**
|
||||
* 获取订单支付参数
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param userId 用户ID(用于权限验证和获取openId)
|
||||
* @param request 支付参数请求
|
||||
* @return 支付参数响应
|
||||
*/
|
||||
PaymentParamsResponse getPaymentParams(Long orderId, Long userId, PaymentParamsRequest request);
|
||||
|
||||
/**
|
||||
* 处理支付回调
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
* @param request HTTP请求对象
|
||||
* @return 回调处理结果
|
||||
*/
|
||||
PaymentCallbackResponse handlePaymentCallback(Long scenicId, HttpServletRequest request);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -438,7 +438,7 @@ public interface IDiscountDetectionService {
|
||||
@Component
|
||||
public class FlashSaleDiscountProvider implements IDiscountProvider {
|
||||
@Override
|
||||
public String getProviderType() { return "FLASH_SALE"; }
|
||||
public String getProviderType() { return "LIMITED_TIME"; }
|
||||
|
||||
@Override
|
||||
public int getPriority() { return 90; } // 介于券码和优惠券之间
|
||||
|
@@ -0,0 +1,51 @@
|
||||
package com.ycwl.basic.pricing.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 优惠券领取请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class CouponClaimRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 优惠券ID
|
||||
*/
|
||||
private Long couponId;
|
||||
|
||||
/**
|
||||
* 景区ID(可选,记录在哪个景区领取)
|
||||
*/
|
||||
private String scenicId;
|
||||
|
||||
/**
|
||||
* 领取来源(可选,如:活动页面、签到奖励等)
|
||||
*/
|
||||
private String claimSource;
|
||||
|
||||
public CouponClaimRequest() {
|
||||
}
|
||||
|
||||
public CouponClaimRequest(Long userId, Long couponId) {
|
||||
this.userId = userId;
|
||||
this.couponId = couponId;
|
||||
}
|
||||
|
||||
public CouponClaimRequest(Long userId, Long couponId, String scenicId) {
|
||||
this.userId = userId;
|
||||
this.couponId = couponId;
|
||||
this.scenicId = scenicId;
|
||||
}
|
||||
|
||||
public CouponClaimRequest(Long userId, Long couponId, String scenicId, String claimSource) {
|
||||
this.userId = userId;
|
||||
this.couponId = couponId;
|
||||
this.scenicId = scenicId;
|
||||
this.claimSource = claimSource;
|
||||
}
|
||||
}
|
100
src/main/java/com/ycwl/basic/pricing/dto/CouponClaimResult.java
Normal file
100
src/main/java/com/ycwl/basic/pricing/dto/CouponClaimResult.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.ycwl.basic.pricing.dto;
|
||||
|
||||
import com.ycwl.basic.pricing.entity.PriceCouponClaimRecord;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 优惠券领取结果DTO
|
||||
*/
|
||||
@Data
|
||||
public class CouponClaimResult {
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 错误消息(失败时)
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 错误码(失败时)
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 领取记录ID(成功时)
|
||||
*/
|
||||
private Long claimRecordId;
|
||||
|
||||
/**
|
||||
* 优惠券ID
|
||||
*/
|
||||
private Long couponId;
|
||||
|
||||
/**
|
||||
* 优惠券名称
|
||||
*/
|
||||
private String couponName;
|
||||
|
||||
/**
|
||||
* 领取时间
|
||||
*/
|
||||
private Date claimTime;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private String scenicId;
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
*/
|
||||
public static CouponClaimResult success(PriceCouponClaimRecord record, String couponName) {
|
||||
CouponClaimResult result = new CouponClaimResult();
|
||||
result.success = true;
|
||||
result.claimRecordId = record.getId();
|
||||
result.couponId = record.getCouponId();
|
||||
result.couponName = couponName;
|
||||
result.claimTime = record.getClaimTime();
|
||||
result.userId = record.getUserId();
|
||||
result.scenicId = record.getScenicId();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
*/
|
||||
public static CouponClaimResult failure(String errorCode, String errorMessage) {
|
||||
CouponClaimResult result = new CouponClaimResult();
|
||||
result.success = false;
|
||||
result.errorCode = errorCode;
|
||||
result.errorMessage = errorMessage;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果(仅错误消息)
|
||||
*/
|
||||
public static CouponClaimResult failure(String errorMessage) {
|
||||
return failure("CLAIM_FAILED", errorMessage);
|
||||
}
|
||||
|
||||
// 常用错误码常量
|
||||
public static final String ERROR_COUPON_NOT_FOUND = "COUPON_NOT_FOUND";
|
||||
public static final String ERROR_COUPON_EXPIRED = "COUPON_EXPIRED";
|
||||
public static final String ERROR_COUPON_INACTIVE = "COUPON_INACTIVE";
|
||||
public static final String ERROR_COUPON_OUT_OF_STOCK = "COUPON_OUT_OF_STOCK";
|
||||
public static final String ERROR_ALREADY_CLAIMED = "ALREADY_CLAIMED";
|
||||
public static final String ERROR_INVALID_PARAMS = "INVALID_PARAMS";
|
||||
public static final String ERROR_SYSTEM_ERROR = "SYSTEM_ERROR";
|
||||
}
|
@@ -44,7 +44,7 @@ public class DiscountDetail {
|
||||
detail.setDiscountName("限时立减");
|
||||
detail.setDiscountAmount(discountAmount);
|
||||
detail.setDescription("限时优惠,立即享受");
|
||||
detail.setSortOrder(2); // 限时立减排在券码后面
|
||||
detail.setSortOrder(1); // 限时立减排在最前面
|
||||
return detail;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class DiscountDetail {
|
||||
detail.setDiscountName("券码优惠");
|
||||
detail.setDiscountAmount(discountAmount);
|
||||
detail.setDescription(String.format("券码 %s - %s", voucherCode, discountTypeName));
|
||||
detail.setSortOrder(1); // 券码优先级最高,排在最前面
|
||||
detail.setSortOrder(2); // 券码显示顺序低于限时立减
|
||||
return detail;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,62 @@
|
||||
package com.ycwl.basic.pricing.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 移动端价格计算请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class MobilePriceCalculationRequest {
|
||||
|
||||
/**
|
||||
* 商品列表
|
||||
*/
|
||||
private List<ProductItem> products;
|
||||
|
||||
/**
|
||||
* 人脸ID(必填,用于权限验证)
|
||||
*/
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 是否自动使用优惠券
|
||||
*/
|
||||
private Boolean autoUseCoupon = true;
|
||||
|
||||
/**
|
||||
* 用户输入的券码
|
||||
*/
|
||||
private String voucherCode;
|
||||
|
||||
/**
|
||||
* 是否自动使用券码优惠
|
||||
*/
|
||||
private Boolean autoUseVoucher = true;
|
||||
|
||||
/**
|
||||
* 是否仅预览优惠(不实际使用)
|
||||
*/
|
||||
private Boolean previewOnly = false;
|
||||
|
||||
/**
|
||||
* 转换为标准价格计算请求
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @return 标准价格计算请求
|
||||
*/
|
||||
public PriceCalculationRequest toStandardRequest(Long userId, Long scenicId) {
|
||||
PriceCalculationRequest request = new PriceCalculationRequest();
|
||||
request.setProducts(this.products);
|
||||
request.setUserId(userId);
|
||||
request.setScenicId(scenicId);
|
||||
request.setFaceId(this.faceId);
|
||||
request.setAutoUseCoupon(this.autoUseCoupon);
|
||||
request.setVoucherCode(this.voucherCode);
|
||||
request.setAutoUseVoucher(this.autoUseVoucher);
|
||||
request.setPreviewOnly(this.previewOnly);
|
||||
return request;
|
||||
}
|
||||
}
|
@@ -3,6 +3,8 @@ package com.ycwl.basic.pricing.service;
|
||||
import com.ycwl.basic.pricing.dto.CouponInfo;
|
||||
import com.ycwl.basic.pricing.dto.CouponUseRequest;
|
||||
import com.ycwl.basic.pricing.dto.CouponUseResult;
|
||||
import com.ycwl.basic.pricing.dto.CouponClaimRequest;
|
||||
import com.ycwl.basic.pricing.dto.CouponClaimResult;
|
||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||
import com.ycwl.basic.pricing.entity.PriceCouponConfig;
|
||||
|
||||
@@ -59,4 +61,12 @@ public interface ICouponService {
|
||||
* @return 可用优惠券列表
|
||||
*/
|
||||
List<CouponInfo> getUserAvailableCoupons(Long userId);
|
||||
|
||||
/**
|
||||
* 领取优惠券(内部调用方法)
|
||||
*
|
||||
* @param request 领取请求
|
||||
* @return 领取结果
|
||||
*/
|
||||
CouponClaimResult claimCoupon(CouponClaimRequest request);
|
||||
}
|
@@ -14,7 +14,7 @@ public interface IDiscountProvider {
|
||||
|
||||
/**
|
||||
* 获取提供者类型
|
||||
* @return 提供者类型标识,如 "COUPON", "VOUCHER", "FLASH_SALE" 等
|
||||
* @return 提供者类型标识,如 "COUPON", "VOUCHER", "LIMITED_TIME" 等
|
||||
*/
|
||||
String getProviderType();
|
||||
|
||||
|
@@ -2,6 +2,8 @@ package com.ycwl.basic.pricing.service;
|
||||
|
||||
import com.ycwl.basic.pricing.entity.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 价格管理服务接口(用于配置管理,手动处理时间字段)
|
||||
*/
|
||||
@@ -90,4 +92,21 @@ public interface IPricingManagementService {
|
||||
* 删除一口价配置
|
||||
*/
|
||||
boolean deleteBundleConfig(Long id);
|
||||
|
||||
// ==================== 快速设置价格 ====================
|
||||
|
||||
/**
|
||||
* 快速设置商品基础价格(内部方法,如果存在则更新,不存在则新增)
|
||||
* @param productType 商品类型
|
||||
* @param productId 商品ID
|
||||
* @param scenicId 景区ID
|
||||
* @param productName 商品名称
|
||||
* @param basePrice 基础价格
|
||||
* @param originalPrice 原价(可选,可为null)
|
||||
* @param unit 价格单位(可选,可为null,默认为"元")
|
||||
* @return 配置的ID
|
||||
*/
|
||||
Long quickSetupProductPrice(String productType, String productId, String scenicId,
|
||||
String productName, BigDecimal basePrice,
|
||||
BigDecimal originalPrice, String unit);
|
||||
}
|
@@ -196,4 +196,90 @@ public class CouponServiceImpl implements ICouponService {
|
||||
info.setActualDiscountAmount(actualDiscountAmount);
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CouponClaimResult claimCoupon(CouponClaimRequest request) {
|
||||
log.info("开始领取优惠券: userId={}, couponId={}, scenicId={}",
|
||||
request.getUserId(), request.getCouponId(), request.getScenicId());
|
||||
|
||||
try {
|
||||
// 1. 参数验证
|
||||
if (request.getUserId() == null || request.getCouponId() == null) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_INVALID_PARAMS, "用户ID和优惠券ID不能为空");
|
||||
}
|
||||
|
||||
// 2. 查询优惠券配置
|
||||
PriceCouponConfig coupon = couponConfigMapper.selectById(request.getCouponId());
|
||||
if (coupon == null || coupon.getDeleted() == 1) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_NOT_FOUND, "优惠券不存在");
|
||||
}
|
||||
|
||||
// 3. 检查优惠券是否启用
|
||||
if (!Boolean.TRUE.equals(coupon.getIsActive())) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_INACTIVE, "优惠券已停用");
|
||||
}
|
||||
|
||||
// 4. 检查优惠券有效期
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (coupon.getValidFrom() != null && now.isBefore(coupon.getValidFrom())) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券尚未生效");
|
||||
}
|
||||
if (coupon.getValidUntil() != null && now.isAfter(coupon.getValidUntil())) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券已过期");
|
||||
}
|
||||
|
||||
// 5. 检查库存(如果有总量限制)
|
||||
if (coupon.getTotalQuantity() != null && coupon.getUsedQuantity() != null) {
|
||||
if (coupon.getUsedQuantity() >= coupon.getTotalQuantity()) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_OUT_OF_STOCK, "优惠券已领完");
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 检查用户是否已经领取过该优惠券
|
||||
PriceCouponClaimRecord existingRecord = couponClaimRecordMapper.selectUserCouponRecord(
|
||||
request.getUserId(), request.getCouponId());
|
||||
if (existingRecord != null) {
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_ALREADY_CLAIMED, "您已经领取过该优惠券");
|
||||
}
|
||||
|
||||
// 7. 创建领取记录
|
||||
Date claimTime = new Date();
|
||||
PriceCouponClaimRecord claimRecord = new PriceCouponClaimRecord();
|
||||
claimRecord.setCouponId(request.getCouponId());
|
||||
claimRecord.setUserId(request.getUserId());
|
||||
claimRecord.setClaimTime(claimTime);
|
||||
claimRecord.setStatus(CouponStatus.CLAIMED);
|
||||
claimRecord.setScenicId(request.getScenicId());
|
||||
claimRecord.setCreateTime(claimTime);
|
||||
claimRecord.setUpdateTime(claimTime);
|
||||
claimRecord.setDeleted(0);
|
||||
|
||||
// 8. 插入领取记录
|
||||
int insertResult = couponClaimRecordMapper.insert(claimRecord);
|
||||
if (insertResult <= 0) {
|
||||
log.error("插入优惠券领取记录失败: userId={}, couponId={}",
|
||||
request.getUserId(), request.getCouponId());
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_SYSTEM_ERROR, "领取失败,请稍后重试");
|
||||
}
|
||||
|
||||
// 9. 更新优惠券已使用数量(如果有总量限制)
|
||||
if (coupon.getTotalQuantity() != null) {
|
||||
int updatedUsedQuantity = (coupon.getUsedQuantity() == null ? 0 : coupon.getUsedQuantity()) + 1;
|
||||
coupon.setUsedQuantity(updatedUsedQuantity);
|
||||
couponConfigMapper.updateById(coupon);
|
||||
}
|
||||
|
||||
log.info("优惠券领取成功: userId={}, couponId={}, claimRecordId={}",
|
||||
request.getUserId(), request.getCouponId(), claimRecord.getId());
|
||||
|
||||
// 10. 返回成功结果
|
||||
return CouponClaimResult.success(claimRecord, coupon.getCouponName());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("领取优惠券失败: userId={}, couponId={}",
|
||||
request.getUserId(), request.getCouponId(), e);
|
||||
return CouponClaimResult.failure(CouponClaimResult.ERROR_SYSTEM_ERROR, "系统错误,领取失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@@ -165,4 +166,49 @@ public class PricingManagementServiceImpl implements IPricingManagementService {
|
||||
log.info("删除一口价配置: id={}", id);
|
||||
return bundleConfigMapper.deleteById(id) > 0;
|
||||
}
|
||||
|
||||
// ==================== 快速设置价格 ====================
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Long quickSetupProductPrice(String productType, String productId, String scenicId,
|
||||
String productName, BigDecimal basePrice,
|
||||
BigDecimal originalPrice, String unit) {
|
||||
log.info("快速设置商品价格: productType={}, productId={}, scenicId={}, basePrice={}",
|
||||
productType, productId, scenicId, basePrice);
|
||||
|
||||
// 查询是否已存在配置
|
||||
PriceProductConfig existingConfig = productConfigMapper.selectByProductTypeAndId(productType, productId);
|
||||
|
||||
if (existingConfig != null) {
|
||||
// 存在则更新
|
||||
existingConfig.setScenicId(scenicId);
|
||||
existingConfig.setProductName(productName);
|
||||
existingConfig.setBasePrice(basePrice);
|
||||
existingConfig.setOriginalPrice(originalPrice);
|
||||
existingConfig.setUnit(unit != null ? unit : "元");
|
||||
existingConfig.setUpdateTime(new Date());
|
||||
|
||||
productConfigMapper.updateProductConfig(existingConfig);
|
||||
log.info("更新商品价格配置: id={}", existingConfig.getId());
|
||||
return existingConfig.getId();
|
||||
} else {
|
||||
// 不存在则新增
|
||||
PriceProductConfig newConfig = new PriceProductConfig();
|
||||
newConfig.setProductType(productType);
|
||||
newConfig.setProductId(productId);
|
||||
newConfig.setScenicId(scenicId);
|
||||
newConfig.setProductName(productName);
|
||||
newConfig.setBasePrice(basePrice);
|
||||
newConfig.setOriginalPrice(originalPrice);
|
||||
newConfig.setUnit(unit != null ? unit : "元");
|
||||
newConfig.setIsActive(true);
|
||||
newConfig.setCreateTime(new Date());
|
||||
newConfig.setUpdateTime(new Date());
|
||||
|
||||
productConfigMapper.insertProductConfig(newConfig);
|
||||
log.info("创建新商品价格配置: id={}", newConfig.getId());
|
||||
return newConfig.getId();
|
||||
}
|
||||
}
|
||||
}
|
@@ -238,6 +238,5 @@ public class VoucherPrintServiceImpl implements VoucherPrintService {
|
||||
content += "<C>赠品兑换码</C>";
|
||||
content += "<C>有效期:"+sdf2.format(new Date())+"</C>";
|
||||
FeiETicketPrinter.doPrint("550519002", content, 1);
|
||||
// 小票配对码、赠品兑换码、抵扣兑换码
|
||||
}
|
||||
}
|
@@ -1,30 +1,45 @@
|
||||
package com.ycwl.basic.repository;
|
||||
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.facebody.enums.FaceBodyAdapterType;
|
||||
import com.ycwl.basic.integration.common.util.ConfigValueUtil;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.ScenicConfigV2DTO;
|
||||
import com.ycwl.basic.mapper.MpConfigMapper;
|
||||
import com.ycwl.basic.mapper.MpNotifyConfigMapper;
|
||||
import com.ycwl.basic.mapper.ScenicMapper;
|
||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||
import com.ycwl.basic.model.pc.mp.MpNotifyConfigEntity;
|
||||
import com.ycwl.basic.model.pc.mp.ScenicMpNotifyVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.pay.enums.PayAdapterType;
|
||||
import com.ycwl.basic.storage.enums.StorageType;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.util.ScenicConfigManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ScenicRepository {
|
||||
@Autowired
|
||||
private ScenicMapper scenicMapper;
|
||||
@Autowired
|
||||
private MpConfigMapper mpConfigMapper;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private ScenicIntegrationService scenicIntegrationService;
|
||||
@Autowired
|
||||
private ScenicConfigIntegrationService scenicConfigIntegrationService;
|
||||
|
||||
public static final String SCENIC_CACHE_KEY = "scenic:%s";
|
||||
public static final String SCENIC_BASIC_CACHE_KEY = "scenic:basic:%s";
|
||||
public static final String SCENIC_FULL_CACHE_KEY = "scenic:f%s";
|
||||
public static final String SCENIC_CONFIG_CACHE_KEY = "scenic:%s:config";
|
||||
public static final String SCENIC_MP_CACHE_KEY = "scenic:%s:mp";
|
||||
@@ -32,26 +47,78 @@ public class ScenicRepository {
|
||||
@Autowired
|
||||
private MpNotifyConfigMapper mpNotifyConfigMapper;
|
||||
|
||||
public ScenicV2DTO getScenicBasic(Long id) {
|
||||
String key = String.format(SCENIC_BASIC_CACHE_KEY, id);
|
||||
try {
|
||||
ScenicV2DTO scenicDTO = scenicIntegrationService.getScenic(id);
|
||||
|
||||
// 请求成功,写入缓存
|
||||
if (scenicDTO != null) {
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(scenicDTO)
|
||||
);
|
||||
}
|
||||
return scenicDTO;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
String cacheKey = key;
|
||||
if (redisTemplate.hasKey(cacheKey)) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(cacheKey), ScenicV2DTO.class);
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ScenicEntity getScenic(Long id) {
|
||||
if (redisTemplate.hasKey(String.format(SCENIC_CACHE_KEY, id))) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(String.format(SCENIC_CACHE_KEY, id)), ScenicEntity.class);
|
||||
String key = String.format(SCENIC_CACHE_KEY, id);
|
||||
try {
|
||||
ScenicV2WithConfigDTO scenicDTO = scenicIntegrationService.getScenicWithConfig(id);
|
||||
ScenicEntity scenicEntity = convertToScenicEntity(scenicDTO);
|
||||
|
||||
// 请求成功,写入缓存
|
||||
if (scenicEntity != null) {
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(scenicEntity)
|
||||
);
|
||||
}
|
||||
return scenicEntity;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
String cacheKey = key;
|
||||
if (redisTemplate.hasKey(cacheKey)) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(cacheKey), ScenicEntity.class);
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
}
|
||||
ScenicEntity scenic = scenicMapper.get(id);
|
||||
if (scenic != null) {
|
||||
redisTemplate.opsForValue().set(String.format(SCENIC_CACHE_KEY, id), JacksonUtil.toJSONString(scenic));
|
||||
}
|
||||
return scenic;
|
||||
}
|
||||
|
||||
public ScenicConfigEntity getScenicConfig(Long scenicId) {
|
||||
if (redisTemplate.hasKey(String.format(SCENIC_CONFIG_CACHE_KEY, scenicId))) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(String.format(SCENIC_CONFIG_CACHE_KEY, scenicId)), ScenicConfigEntity.class);
|
||||
String key = String.format(SCENIC_CONFIG_CACHE_KEY, scenicId);
|
||||
try {
|
||||
ScenicV2WithConfigDTO scenicWithConfigDTO = scenicIntegrationService.getScenicWithConfig(scenicId);
|
||||
ScenicConfigEntity configEntity = convertToScenicConfigEntity(scenicWithConfigDTO, scenicId);
|
||||
|
||||
// 请求成功,写入缓存
|
||||
if (configEntity != null) {
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(configEntity)
|
||||
);
|
||||
}
|
||||
return configEntity;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
String cacheKey = key;
|
||||
if (redisTemplate.hasKey(cacheKey)) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(cacheKey), ScenicConfigEntity.class);
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
}
|
||||
ScenicConfigEntity scenicConfig = scenicMapper.getConfig(scenicId);
|
||||
if (scenicConfig != null) {
|
||||
redisTemplate.opsForValue().set(String.format(SCENIC_CONFIG_CACHE_KEY, scenicId), JacksonUtil.toJSONString(scenicConfig));
|
||||
}
|
||||
return scenicConfig;
|
||||
}
|
||||
|
||||
public MpConfigEntity getScenicMpConfig(Long scenicId) {
|
||||
@@ -121,12 +188,191 @@ public class ScenicRepository {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ScenicV2DTO> list(ScenicReqQuery scenicReqQuery) {
|
||||
try {
|
||||
// 将 ScenicReqQuery 参数转换为 zt-scenic 服务需要的参数
|
||||
Integer page = scenicReqQuery.getPageNum();
|
||||
Integer pageSize = scenicReqQuery.getPageSize();
|
||||
Integer status = null;
|
||||
if (scenicReqQuery.getStatus() != null) {
|
||||
status = Integer.valueOf(scenicReqQuery.getStatus());
|
||||
}
|
||||
String name = scenicReqQuery.getName();
|
||||
|
||||
// 调用 zt-scenic 服务的 list 方法
|
||||
ScenicV2ListResponse response = scenicIntegrationService.listScenics(page, pageSize, status, name);
|
||||
|
||||
// 将 ScenicV2DTO 列表转换为 ScenicEntity 列表
|
||||
if (response != null && response.getList() != null) {
|
||||
return response.getList();
|
||||
}
|
||||
return new java.util.ArrayList<>();
|
||||
} catch (Exception e) {
|
||||
// 如果调用失败,返回空列表
|
||||
return new java.util.ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCache(Long scenicId) {
|
||||
redisTemplate.delete(String.format(SCENIC_CACHE_KEY, scenicId));
|
||||
redisTemplate.delete(String.format(SCENIC_BASIC_CACHE_KEY, scenicId));
|
||||
redisTemplate.delete(String.format(SCENIC_FULL_CACHE_KEY, scenicId));
|
||||
redisTemplate.delete(String.format(SCENIC_CONFIG_CACHE_KEY, scenicId));
|
||||
redisTemplate.delete(String.format(SCENIC_MP_CACHE_KEY, scenicId));
|
||||
redisTemplate.delete(String.format(SCENIC_MP_NOTIFY_CACHE_KEY, scenicId));
|
||||
}
|
||||
|
||||
private ScenicEntity convertToScenicEntity(ScenicV2DTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
ScenicEntity entity = new ScenicEntity();
|
||||
entity.setId(Long.parseLong(dto.getId()));
|
||||
entity.setName(dto.getName());
|
||||
entity.setMpId(dto.getMpId());
|
||||
entity.setStatus(dto.getStatus().toString());
|
||||
return entity;
|
||||
}
|
||||
|
||||
private ScenicEntity convertToScenicEntity(ScenicV2WithConfigDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
ScenicEntity entity = new ScenicEntity();
|
||||
entity.setId(Long.parseLong(dto.getId()));
|
||||
entity.setName(dto.getName());
|
||||
entity.setMpId(dto.getMpId());
|
||||
entity.setStatus(dto.getStatus().toString());
|
||||
if (dto.getConfig() != null) {
|
||||
entity.setAddress(ConfigValueUtil.getStringValue(dto.getConfig(), "address"));
|
||||
entity.setArea(ConfigValueUtil.getStringValue(dto.getConfig(), "area"));
|
||||
entity.setCity(ConfigValueUtil.getStringValue(dto.getConfig(), "city"));
|
||||
entity.setProvince(ConfigValueUtil.getStringValue(dto.getConfig(), "province"));
|
||||
entity.setLatitude(ConfigValueUtil.getBigDecimalValue(dto.getConfig(), "latitude"));
|
||||
entity.setLongitude(ConfigValueUtil.getBigDecimalValue(dto.getConfig(), "longitude"));
|
||||
entity.setRadius(ConfigValueUtil.getBigDecimalValue(dto.getConfig(), "radius"));
|
||||
entity.setPhone(ConfigValueUtil.getStringValue(dto.getConfig(), "phone"));
|
||||
entity.setLogoUrl(ConfigValueUtil.getStringValue(dto.getConfig(), "logoUrl"));
|
||||
entity.setCoverUrl(ConfigValueUtil.getStringValue(dto.getConfig(), "coverUrl"));
|
||||
entity.setKfCodeUrl(ConfigValueUtil.getStringValue(dto.getConfig(), "kfCodeUrl"));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
private ScenicConfigEntity convertToScenicConfigEntity(ScenicV2WithConfigDTO dto, Long scenicId) {
|
||||
if (dto == null || dto.getConfig() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ScenicConfigEntity entity = new ScenicConfigEntity();
|
||||
entity.setScenicId(scenicId);
|
||||
|
||||
java.util.Map<String, Object> config = dto.getConfig();
|
||||
|
||||
entity.setBookRoutine(ConfigValueUtil.getIntValue(config, "bookRoutine"));
|
||||
entity.setForceFinishTime(ConfigValueUtil.getIntValue(config, "forceFinishTime"));
|
||||
entity.setTourTime(ConfigValueUtil.getIntValue(config, "tourTime"));
|
||||
entity.setSampleStoreDay(ConfigValueUtil.getIntValue(config, "sampleStoreDay"));
|
||||
entity.setFaceStoreDay(ConfigValueUtil.getIntValue(config, "faceStoreDay"));
|
||||
entity.setVideoStoreDay(ConfigValueUtil.getIntValue(config, "videoStoreDay"));
|
||||
entity.setAllFree(ConfigValueUtil.getBooleanValue(config, "allFree"));
|
||||
entity.setDisableSourceVideo(ConfigValueUtil.getBooleanValue(config, "disableSourceVideo"));
|
||||
entity.setDisableSourceImage(ConfigValueUtil.getBooleanValue(config, "disableSourceImage"));
|
||||
entity.setTemplateNewVideoType(ConfigValueUtil.getIntValue(config, "templateNewVideoType"));
|
||||
entity.setAntiScreenRecordType(ConfigValueUtil.getIntValue(config, "antiScreenRecordType"));
|
||||
entity.setVideoSourceStoreDay(ConfigValueUtil.getIntValue(config, "videoSourceStoreDay"));
|
||||
entity.setImageSourceStoreDay(ConfigValueUtil.getIntValue(config, "imageSourceStoreDay"));
|
||||
entity.setUserSourceExpireDay(ConfigValueUtil.getIntValue(config, "userSourceExpireDay"));
|
||||
entity.setFaceDetectHelperThreshold(ConfigValueUtil.getIntValue(config, "faceDetectHelperThreshold"));
|
||||
entity.setPhotoFreeNum(ConfigValueUtil.getIntValue(config, "photoFreeNum"));
|
||||
entity.setVideoFreeNum(ConfigValueUtil.getIntValue(config, "videoFreeNum"));
|
||||
entity.setVoucherEnable(ConfigValueUtil.getBooleanValue(config, "voucherEnable"));
|
||||
|
||||
entity.setFaceScoreThreshold(ConfigValueUtil.getFloatValue(config, "faceScoreThreshold"));
|
||||
entity.setBrokerDirectRate(ConfigValueUtil.getBigDecimalValue(config, "brokerDirectRate"));
|
||||
|
||||
entity.setWatermarkType(ConfigValueUtil.getStringValue(config, "watermarkType"));
|
||||
entity.setWatermarkScenicText(ConfigValueUtil.getStringValue(config, "watermarkScenicText"));
|
||||
entity.setWatermarkDtFormat(ConfigValueUtil.getStringValue(config, "watermarkDtFormat"));
|
||||
entity.setImageSourcePackHint(ConfigValueUtil.getStringValue(config, "imageSourcePackHint"));
|
||||
entity.setVideoSourcePackHint(ConfigValueUtil.getStringValue(config, "videoSourcePackHint"));
|
||||
entity.setExtraNotificationTime(ConfigValueUtil.getStringValue(config, "extraNotificationTime"));
|
||||
|
||||
entity.setStoreType(ConfigValueUtil.getEnumValue(config, "storeType", StorageType.class));
|
||||
entity.setStoreConfigJson(ConfigValueUtil.getStringValue(config, "storeConfigJson"));
|
||||
entity.setTmpStoreType(ConfigValueUtil.getEnumValue(config, "tmpStoreType", StorageType.class));
|
||||
entity.setTmpStoreConfigJson(ConfigValueUtil.getStringValue(config, "tmpStoreConfigJson"));
|
||||
entity.setLocalStoreType(ConfigValueUtil.getEnumValue(config, "localStoreType", StorageType.class));
|
||||
entity.setLocalStoreConfigJson(ConfigValueUtil.getStringValue(config, "localStoreConfigJson"));
|
||||
|
||||
entity.setFaceType(ConfigValueUtil.getEnumValue(config, "faceType", FaceBodyAdapterType.class));
|
||||
entity.setFaceConfigJson(ConfigValueUtil.getStringValue(config, "faceConfigJson"));
|
||||
|
||||
entity.setPayType(ConfigValueUtil.getEnumValue(config, "payType", PayAdapterType.class));
|
||||
entity.setPayConfigJson(ConfigValueUtil.getStringValue(config, "payConfigJson"));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区配置管理器
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
* @return ScenicConfigManager实例,如果获取失败返回null
|
||||
*/
|
||||
public ScenicConfigManager getScenicConfigManager(Long scenicId) {
|
||||
try {
|
||||
List<ScenicConfigV2DTO> configList = scenicConfigIntegrationService.listConfigs(scenicId);
|
||||
if (configList != null) {
|
||||
return new ScenicConfigManager(configList);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区配置管理器,带缓存支持
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
* @return ScenicConfigManager实例,如果获取失败返回null
|
||||
*/
|
||||
public ScenicConfigManager getScenicConfigManagerWithCache(Long scenicId) {
|
||||
String key = String.format(SCENIC_CONFIG_CACHE_KEY + ":manager", scenicId);
|
||||
|
||||
try {
|
||||
List<com.ycwl.basic.integration.scenic.dto.config.ScenicConfigV2DTO> configList =
|
||||
scenicConfigIntegrationService.listConfigs(scenicId);
|
||||
if (configList != null) {
|
||||
ScenicConfigManager manager = new ScenicConfigManager(configList);
|
||||
|
||||
// 请求成功,写入缓存(将配置列表序列化存储)
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(configList)
|
||||
);
|
||||
|
||||
return manager;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
if (redisTemplate.hasKey(key)) {
|
||||
try {
|
||||
String cachedConfigJson = redisTemplate.opsForValue().get(key);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ScenicConfigV2DTO> cachedConfigList =
|
||||
JacksonUtil.parseArray(cachedConfigJson, ScenicConfigV2DTO.class);
|
||||
return new ScenicConfigManager(cachedConfigList);
|
||||
} catch (Exception cacheException) {
|
||||
// 缓存解析失败,返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
238
src/main/java/com/ycwl/basic/service/PriceCacheService.java
Normal file
238
src/main/java/com/ycwl/basic/service/PriceCacheService.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package com.ycwl.basic.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 价格缓存服务
|
||||
* 使用 Redis 实现价格查询结果的缓存,缓存时间为5分钟
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PriceCacheService {
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
// 缓存Key前缀
|
||||
private static final String CACHE_PREFIX = "pricing:cache:";
|
||||
|
||||
// 缓存过期时间:5分钟
|
||||
private static final Duration CACHE_DURATION = Duration.ofMinutes(5);
|
||||
|
||||
/**
|
||||
* 生成缓存键
|
||||
* 格式:pricing:cache:{userId}:{scenicId}:{productsHash}
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @return 缓存键
|
||||
*/
|
||||
public String generateCacheKey(Long userId, Long scenicId, List<ProductItem> products) {
|
||||
String productsHash = calculateProductsHash(products);
|
||||
return CACHE_PREFIX + userId + ":" + scenicId + ":" + productsHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算商品列表的哈希值
|
||||
* 基于商品类型、ID、购买数量等必传字段生成唯一哈希,忽略可选的quantity字段
|
||||
*
|
||||
* @param products 商品列表
|
||||
* @return 哈希值
|
||||
*/
|
||||
private String calculateProductsHash(List<ProductItem> products) {
|
||||
try {
|
||||
// 构建缓存用的商品信息字符串,只包含必传字段
|
||||
StringBuilder cacheInfo = new StringBuilder();
|
||||
for (ProductItem product : products) {
|
||||
cacheInfo.append(product.getProductType()).append(":")
|
||||
.append(product.getProductId()).append(":")
|
||||
.append(product.getPurchaseCount() != null ? product.getPurchaseCount() : 1).append(":")
|
||||
.append(product.getScenicId()).append(";");
|
||||
}
|
||||
|
||||
// 计算SHA-256哈希
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(cacheInfo.toString().getBytes());
|
||||
|
||||
// 转换为16进制字符串
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
// 返回前16位作为哈希值(足够避免冲突)
|
||||
return hexString.substring(0, 16);
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.error("计算商品列表哈希值失败", e);
|
||||
// 降级策略:使用关键字段的hashCode
|
||||
StringBuilder fallback = new StringBuilder();
|
||||
for (ProductItem product : products) {
|
||||
fallback.append(product.getProductType()).append(":")
|
||||
.append(product.getProductId()).append(":")
|
||||
.append(product.getPurchaseCount() != null ? product.getPurchaseCount() : 1).append(";");
|
||||
}
|
||||
return String.valueOf(fallback.toString().hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存价格计算结果
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @param result 价格计算结果
|
||||
* @return 缓存键
|
||||
*/
|
||||
public String cachePriceResult(Long userId, Long scenicId, List<ProductItem> products, PriceCalculationResult result) {
|
||||
String cacheKey = generateCacheKey(userId, scenicId, products);
|
||||
|
||||
try {
|
||||
String resultJson = objectMapper.writeValueAsString(result);
|
||||
redisTemplate.opsForValue().set(cacheKey, resultJson, CACHE_DURATION);
|
||||
|
||||
log.info("价格计算结果已缓存: cacheKey={}, userId={}, scenicId={}",
|
||||
cacheKey, userId, scenicId);
|
||||
|
||||
return cacheKey;
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("缓存价格计算结果失败: userId={}, scenicId={}", userId, scenicId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存的价格计算结果
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @return 缓存的价格计算结果,如果不存在则返回null
|
||||
*/
|
||||
public PriceCalculationResult getCachedPriceResult(Long userId, Long scenicId, List<ProductItem> products) {
|
||||
String cacheKey = generateCacheKey(userId, scenicId, products);
|
||||
return getCachedPriceResult(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据缓存键获取价格计算结果
|
||||
*
|
||||
* @param cacheKey 缓存键
|
||||
* @return 缓存的价格计算结果,如果不存在则返回null
|
||||
*/
|
||||
public PriceCalculationResult getCachedPriceResult(String cacheKey) {
|
||||
try {
|
||||
String resultJson = redisTemplate.opsForValue().get(cacheKey);
|
||||
if (resultJson == null) {
|
||||
log.debug("价格缓存不存在: cacheKey={}", cacheKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
PriceCalculationResult result = objectMapper.readValue(resultJson, PriceCalculationResult.class);
|
||||
log.info("获取到缓存的价格计算结果: cacheKey={}", cacheKey);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("解析缓存的价格计算结果失败: cacheKey={}", cacheKey, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查价格缓存是否存在
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @return true如果缓存存在且未过期
|
||||
*/
|
||||
public boolean isCacheExists(Long userId, Long scenicId, List<ProductItem> products) {
|
||||
String cacheKey = generateCacheKey(userId, scenicId, products);
|
||||
return Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并消费价格缓存(一次性使用)
|
||||
* 查询缓存结果后立即删除,确保缓存只能被使用一次
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @return 缓存的价格计算结果,如果不存在则返回null
|
||||
*/
|
||||
public PriceCalculationResult validateAndConsumePriceCache(Long userId, Long scenicId, List<ProductItem> products) {
|
||||
String cacheKey = generateCacheKey(userId, scenicId, products);
|
||||
|
||||
try {
|
||||
// 使用 Redis 的 GETDEL 命令原子性地获取并删除缓存
|
||||
String resultJson = redisTemplate.opsForValue().getAndDelete(cacheKey);
|
||||
|
||||
if (resultJson == null) {
|
||||
log.warn("价格缓存不存在或已过期: userId={}, scenicId={}, cacheKey={}",
|
||||
userId, scenicId, cacheKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
PriceCalculationResult result = objectMapper.readValue(resultJson, PriceCalculationResult.class);
|
||||
log.info("价格缓存验证成功并已消费: userId={}, scenicId={}, cacheKey={}",
|
||||
userId, scenicId, cacheKey);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("解析价格缓存失败: userId={}, scenicId={}, cacheKey={}",
|
||||
userId, scenicId, cacheKey, e);
|
||||
// 缓存解析失败时也要删除无效缓存
|
||||
redisTemplate.delete(cacheKey);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除价格缓存
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @return true如果删除成功
|
||||
*/
|
||||
public boolean deletePriceCache(Long userId, Long scenicId, List<ProductItem> products) {
|
||||
String cacheKey = generateCacheKey(userId, scenicId, products);
|
||||
return Boolean.TRUE.equals(redisTemplate.delete(cacheKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存剩余过期时间
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @return 剩余时间(秒),-1表示永不过期,-2表示不存在
|
||||
*/
|
||||
public long getCacheExpireTime(Long userId, Long scenicId, List<ProductItem> products) {
|
||||
String cacheKey = generateCacheKey(userId, scenicId, products);
|
||||
Long expire = redisTemplate.getExpire(cacheKey);
|
||||
return expire != null ? expire : -2;
|
||||
}
|
||||
}
|
@@ -13,7 +13,6 @@ import com.ycwl.basic.facebody.entity.AddFaceResp;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.mapper.CustomUploadTaskMapper;
|
||||
import com.ycwl.basic.mapper.FaceSampleMapper;
|
||||
import com.ycwl.basic.mapper.ScenicMapper;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.custom.entity.CustomUploadTaskEntity;
|
||||
|
@@ -9,6 +9,7 @@ import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.account.ScenicRegisterReq;
|
||||
import com.ycwl.basic.model.mobile.scenic.account.ScenicRegisterRespVO;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
@@ -20,7 +21,7 @@ import java.util.List;
|
||||
* @Date:2024/12/6 10:23
|
||||
*/
|
||||
public interface AppScenicService {
|
||||
ApiResponse<PageInfo<ScenicAppVO>> pageQuery(ScenicReqQuery scenicReqQuery);
|
||||
ApiResponse<PageInfo<ScenicEntity>> pageQuery(ScenicReqQuery scenicReqQuery);
|
||||
|
||||
ApiResponse<ScenicDeviceCountVO> deviceCountByScenicId(Long scenicId);
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package com.ycwl.basic.service.mobile.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.mapper.*;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.mapper.ExtraDeviceMapper;
|
||||
import com.ycwl.basic.mapper.ScenicAccountMapper;
|
||||
import com.ycwl.basic.model.jwt.JwtInfo;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicAppVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO;
|
||||
@@ -16,9 +17,11 @@ import com.ycwl.basic.model.mobile.scenic.account.ScenicRegisterRespVO;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.repository.DeviceRepository;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.mobile.AppScenicService;
|
||||
import com.ycwl.basic.service.pc.ScenicAccountService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
@@ -30,6 +33,7 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,8 +47,6 @@ import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
|
||||
@Service
|
||||
public class AppScenicServiceImpl implements AppScenicService {
|
||||
|
||||
@Autowired
|
||||
private ScenicMapper scenicMapper;
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
@Autowired
|
||||
@@ -59,12 +61,19 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
private ExtraDeviceMapper extraDeviceMapper;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
|
||||
@Override
|
||||
public ApiResponse<PageInfo<ScenicAppVO>> pageQuery(ScenicReqQuery scenicReqQuery) {
|
||||
PageHelper.startPage(scenicReqQuery.getPageNum(), scenicReqQuery.getPageSize());
|
||||
List<ScenicAppVO> list = scenicMapper.appList(scenicReqQuery);
|
||||
PageInfo<ScenicAppVO> pageInfo = new PageInfo<>(list);
|
||||
public ApiResponse<PageInfo<ScenicEntity>> pageQuery(ScenicReqQuery scenicReqQuery) {
|
||||
|
||||
ScenicReqQuery query = new ScenicReqQuery();
|
||||
query.setPageSize(1000);
|
||||
List<ScenicV2DTO> scenicList = scenicRepository.list(query);
|
||||
List<ScenicEntity> list = scenicList.stream().map(scenic -> {
|
||||
return scenicRepository.getScenic(Long.valueOf(scenic.getId()));
|
||||
}).toList();
|
||||
PageInfo<ScenicEntity> pageInfo = new PageInfo<>(list);
|
||||
return ApiResponse.success(pageInfo);
|
||||
}
|
||||
|
||||
@@ -77,7 +86,27 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
|
||||
@Override
|
||||
public ApiResponse<ScenicRespVO> getDetails(Long id) {
|
||||
ScenicRespVO scenicRespVO = scenicMapper.getAppById(id);
|
||||
ScenicEntity scenic = scenicRepository.getScenic(id);
|
||||
ScenicRespVO scenicRespVO = new ScenicRespVO();
|
||||
|
||||
// 将ScenicEntity的值通过set/get方式写入到ScenicRespVO
|
||||
if (scenic != null) {
|
||||
scenicRespVO.setId(scenic.getId());
|
||||
scenicRespVO.setName(scenic.getName());
|
||||
scenicRespVO.setPhone(scenic.getPhone());
|
||||
scenicRespVO.setLogoUrl(scenic.getLogoUrl());
|
||||
scenicRespVO.setCoverUrl(scenic.getCoverUrl());
|
||||
scenicRespVO.setIntroduction(scenic.getIntroduction());
|
||||
scenicRespVO.setLongitude(scenic.getLongitude());
|
||||
scenicRespVO.setLatitude(scenic.getLatitude());
|
||||
scenicRespVO.setRadius(scenic.getRadius());
|
||||
scenicRespVO.setProvince(scenic.getProvince());
|
||||
scenicRespVO.setCity(scenic.getCity());
|
||||
scenicRespVO.setArea(scenic.getArea());
|
||||
scenicRespVO.setAddress(scenic.getAddress());
|
||||
scenicRespVO.setKfCodeUrl(scenic.getKfCodeUrl());
|
||||
}
|
||||
|
||||
ScenicDeviceCountVO scenicDeviceCountVO = deviceMapper.deviceCountByScenicId(id);
|
||||
scenicRespVO.setLensNum(scenicDeviceCountVO.getTotalDeviceCount());
|
||||
return ApiResponse.success(scenicRespVO);
|
||||
@@ -171,8 +200,59 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
|
||||
@Override
|
||||
public List<ScenicAppVO> scenicListByLnLa(ScenicIndexVO scenicIndexVO) {
|
||||
List<ScenicAppVO> scenicAppVOS = scenicMapper.scenicListByLnLa(scenicIndexVO);
|
||||
return scenicAppVOS.stream().filter(scenic -> scenic.getDistance().compareTo(scenic.getRadius().multiply(BigDecimal.valueOf(1_000L))) < 0).toList();
|
||||
// 从 scenicRepository 获取所有景区(1000个)
|
||||
ScenicReqQuery query = new ScenicReqQuery();
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(1000);
|
||||
List<ScenicV2DTO> scenicList = scenicRepository.list(query);
|
||||
|
||||
List<ScenicAppVO> list = new ArrayList<>();
|
||||
|
||||
// 为每个景区获取详细信息(包含经纬度)
|
||||
for (ScenicV2DTO scenicDTO : scenicList) {
|
||||
try {
|
||||
// 获取景区详细信息(包含经纬度)
|
||||
ScenicEntity scenicEntity = scenicRepository.getScenic(Long.parseLong(scenicDTO.getId()));
|
||||
if (scenicEntity != null && scenicEntity.getLatitude() != null && scenicEntity.getLongitude() != null) {
|
||||
// 计算距离
|
||||
BigDecimal distance = calculateDistance(
|
||||
scenicIndexVO.getLatitude(),
|
||||
scenicIndexVO.getLongitude(),
|
||||
scenicEntity.getLatitude(),
|
||||
scenicEntity.getLongitude()
|
||||
);
|
||||
|
||||
// 根据距离和范围筛选景区
|
||||
if (scenicEntity.getRadius() != null &&
|
||||
distance.compareTo(scenicEntity.getRadius().multiply(BigDecimal.valueOf(1_000L))) < 0) {
|
||||
|
||||
// 转换为 ScenicAppVO
|
||||
ScenicAppVO scenicAppVO = new ScenicAppVO();
|
||||
scenicAppVO.setId(scenicEntity.getId());
|
||||
scenicAppVO.setName(scenicEntity.getName());
|
||||
scenicAppVO.setPhone(scenicEntity.getPhone());
|
||||
scenicAppVO.setIntroduction(scenicEntity.getIntroduction());
|
||||
scenicAppVO.setCoverUrl(scenicEntity.getCoverUrl());
|
||||
scenicAppVO.setLongitude(scenicEntity.getLongitude());
|
||||
scenicAppVO.setLatitude(scenicEntity.getLatitude());
|
||||
scenicAppVO.setRadius(scenicEntity.getRadius());
|
||||
scenicAppVO.setProvince(scenicEntity.getProvince());
|
||||
scenicAppVO.setCity(scenicEntity.getCity());
|
||||
scenicAppVO.setArea(scenicEntity.getArea());
|
||||
scenicAppVO.setAddress(scenicEntity.getAddress());
|
||||
scenicAppVO.setDistance(distance);
|
||||
scenicAppVO.setDeviceNum(deviceRepository.getAllDeviceByScenicId(scenicEntity.getId()).size());
|
||||
|
||||
list.add(scenicAppVO);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 单个景区获取失败,继续处理下一个
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -214,4 +294,29 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
deviceRespVOList.addAll(0, extraDeviceList);
|
||||
return ApiResponse.success(deviceRespVOList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离(米)
|
||||
* 使用 Haversine 公式
|
||||
*/
|
||||
private BigDecimal calculateDistance(BigDecimal lat1, BigDecimal lon1, BigDecimal lat2, BigDecimal lon2) {
|
||||
if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
final double R = 6371000; // 地球半径(米)
|
||||
|
||||
double lat1Rad = Math.toRadians(lat1.doubleValue());
|
||||
double lat2Rad = Math.toRadians(lat2.doubleValue());
|
||||
double deltaLat = Math.toRadians(lat2.subtract(lat1).doubleValue());
|
||||
double deltaLon = Math.toRadians(lon2.subtract(lon1).doubleValue());
|
||||
|
||||
double a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
|
||||
Math.cos(lat1Rad) * Math.cos(lat2Rad) *
|
||||
Math.sin(deltaLon/2) * Math.sin(deltaLon/2);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
|
||||
double distance = R * c;
|
||||
return BigDecimal.valueOf(distance);
|
||||
}
|
||||
}
|
||||
|
@@ -139,9 +139,9 @@ public class GoodsServiceImpl implements GoodsService {
|
||||
List<SourceRespVO> goods = faceEntry.getValue();
|
||||
return goods.stream().collect(Collectors.groupingBy(SourceRespVO::getType)).keySet().stream().filter(type -> {
|
||||
if (Integer.valueOf(1).equals(type)) {
|
||||
return !Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo());
|
||||
return !Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo());
|
||||
} else if (Integer.valueOf(2).equals(type)) {
|
||||
return !Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage());
|
||||
return !Boolean.TRUE.equals(scenicConfig.getDisableSourceImage());
|
||||
}
|
||||
return true;
|
||||
}).map(type -> {
|
||||
|
@@ -1,11 +1,8 @@
|
||||
package com.ycwl.basic.service.pc;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicAddOrUpdateReq;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.pay.adapter.IPayAdapter;
|
||||
import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
@@ -17,23 +14,7 @@ import java.util.List;
|
||||
* @Date:2024/12/3 15:22
|
||||
*/
|
||||
public interface ScenicService {
|
||||
ApiResponse<PageInfo<ScenicRespVO>> pageQuery(ScenicReqQuery scenicReqQuery);
|
||||
ApiResponse<List<ScenicRespVO>> list(ScenicReqQuery scenicReqQuery);
|
||||
ApiResponse<ScenicRespVO> getById(Long id);
|
||||
ApiResponse<Boolean> add(ScenicAddOrUpdateReq scenicAddOrUpdateReq);
|
||||
ApiResponse<Boolean> deleteById(Long id);
|
||||
ApiResponse<Boolean> update(ScenicAddOrUpdateReq scenicAddOrUpdateReq);
|
||||
ApiResponse<Boolean> updateStatus(Long id);
|
||||
ApiResponse<Boolean> addConfig(ScenicConfigEntity scenicConfig);
|
||||
/**
|
||||
* 修改景区配置
|
||||
* @param scenicConfig
|
||||
* @return
|
||||
*/
|
||||
ApiResponse<Boolean> updateConfigById(ScenicConfigEntity scenicConfig);
|
||||
|
||||
ScenicConfigEntity getConfig(Long id);
|
||||
void saveConfig(Long configId, ScenicConfigEntity config);
|
||||
ApiResponse<List<ScenicV2DTO>> list(ScenicReqQuery scenicReqQuery);
|
||||
|
||||
IStorageAdapter getScenicStorageAdapter(Long scenicId);
|
||||
|
||||
|
@@ -324,7 +324,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
}
|
||||
} else {
|
||||
// 重新切视频逻辑
|
||||
if (scenicConfig != null && !Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
|
||||
if (scenicConfig != null && !Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
|
||||
long videoCount = memberSourceEntityList.stream().filter(item -> item.getType().equals(1)).count();
|
||||
long photoCount = memberSourceEntityList.stream().filter(item -> item.getType().equals(2)).count();
|
||||
if (photoCount > videoCount) {
|
||||
@@ -443,7 +443,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
sourceVideoContent.setLockType(-1);
|
||||
sourceImageContent.setLockType(-1);
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(faceRespVO.getScenicId());
|
||||
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage())) {
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceImage())) {
|
||||
IsBuyRespVO isBuyRespVO = orderBiz.isBuy(userId, faceRespVO.getScenicId(), 2, faceId);
|
||||
sourceImageContent.setSourceType(isBuyRespVO.getGoodsType());
|
||||
sourceImageContent.setContentId(isBuyRespVO.getGoodsId());
|
||||
@@ -462,7 +462,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
sourceImageContent.setFreeCount((int) freeCount);
|
||||
contentList.add(sourceImageContent);
|
||||
}
|
||||
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
|
||||
IsBuyRespVO isBuyRespVO = orderBiz.isBuy(userId, faceRespVO.getScenicId(), 1, faceId);
|
||||
sourceVideoContent.setSourceType(isBuyRespVO.getGoodsType());
|
||||
sourceVideoContent.setContentId(isBuyRespVO.getGoodsId());
|
||||
|
@@ -57,6 +57,7 @@ import com.ycwl.basic.utils.SnowFlakeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -78,7 +79,7 @@ import java.util.stream.Collectors;
|
||||
* @Date:2024/12/3 13:54
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Service("OrderServiceOldImpl")
|
||||
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
|
||||
@@ -86,6 +87,7 @@ public class OrderServiceImpl implements OrderService {
|
||||
private OrderMapper orderMapper;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(value = "OrderServiceOldImpl")
|
||||
private OrderServiceImpl self;
|
||||
@Autowired
|
||||
private SourceMapper sourceMapper;
|
||||
|
@@ -1,38 +1,29 @@
|
||||
package com.ycwl.basic.service.pc.impl;
|
||||
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.facebody.FaceBodyFactory;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.mapper.ScenicAccountMapper;
|
||||
import com.ycwl.basic.mapper.ScenicMapper;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
|
||||
import com.ycwl.basic.pay.PayFactory;
|
||||
import com.ycwl.basic.pay.adapter.IPayAdapter;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.pc.ScenicService;
|
||||
import com.ycwl.basic.service.task.TaskFaceService;
|
||||
import com.ycwl.basic.storage.StorageFactory;
|
||||
import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
import com.ycwl.basic.storage.exceptions.StorageUnsupportedException;
|
||||
import com.ycwl.basic.util.ScenicConfigManager;
|
||||
import com.ycwl.basic.util.TtlCacheMap;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.SnowFlakeUtil;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.ycwl.basic.constant.FaceConstant.USER_FACE_DB_NAME;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
@@ -41,199 +32,39 @@ import static com.ycwl.basic.constant.FaceConstant.USER_FACE_DB_NAME;
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ScenicServiceImpl implements ScenicService {
|
||||
@Autowired
|
||||
private ScenicMapper scenicMapper;
|
||||
@Autowired
|
||||
private ScenicAccountMapper scenicAccountMapper;
|
||||
@Autowired
|
||||
private TaskFaceService taskFaceService;
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
|
||||
@Override
|
||||
public ApiResponse<PageInfo<ScenicRespVO>> pageQuery(ScenicReqQuery scenicReqQuery) {
|
||||
PageHelper.startPage(scenicReqQuery.getPageNum(), scenicReqQuery.getPageSize());
|
||||
List<ScenicRespVO> list = scenicMapper.list(scenicReqQuery);
|
||||
PageInfo<ScenicRespVO> pageInfo = new PageInfo<>(list);
|
||||
return ApiResponse.success(pageInfo);
|
||||
}
|
||||
// TTL缓存配置,默认10分钟过期
|
||||
private static final long DEFAULT_CACHE_TTL_MINUTES = 10;
|
||||
|
||||
// 使用TTL缓存替代静态Map
|
||||
private static final TtlCacheMap<Long, IStorageAdapter> scenicStorageAdapterCache =
|
||||
new TtlCacheMap<>(TimeUnit.MINUTES.toMillis(DEFAULT_CACHE_TTL_MINUTES));
|
||||
private static final TtlCacheMap<Long, IStorageAdapter> scenicTmpStorageAdapterCache =
|
||||
new TtlCacheMap<>(TimeUnit.MINUTES.toMillis(DEFAULT_CACHE_TTL_MINUTES));
|
||||
private static final TtlCacheMap<Long, IStorageAdapter> scenicLocalStorageAdapterCache =
|
||||
new TtlCacheMap<>(TimeUnit.MINUTES.toMillis(DEFAULT_CACHE_TTL_MINUTES));
|
||||
private static final TtlCacheMap<Long, IFaceBodyAdapter> scenicFaceBodyAdapterCache =
|
||||
new TtlCacheMap<>(TimeUnit.MINUTES.toMillis(DEFAULT_CACHE_TTL_MINUTES));
|
||||
private static final TtlCacheMap<Long, IPayAdapter> scenicPayAdapterCache =
|
||||
new TtlCacheMap<>(TimeUnit.MINUTES.toMillis(DEFAULT_CACHE_TTL_MINUTES));
|
||||
|
||||
@Override
|
||||
public ApiResponse<List<ScenicRespVO>> list(ScenicReqQuery scenicReqQuery) {
|
||||
return ApiResponse.success(scenicMapper.list(scenicReqQuery));
|
||||
@Deprecated
|
||||
public ApiResponse<List<ScenicV2DTO>> list(ScenicReqQuery scenicReqQuery) {
|
||||
return ApiResponse.success(scenicRepository.list(scenicReqQuery));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<ScenicRespVO> getById(Long id) {
|
||||
return ApiResponse.success(scenicMapper.getById(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ApiResponse<Boolean> add(ScenicAddOrUpdateReq scenicAddReq) {
|
||||
Long scenicId = SnowFlakeUtil.getLongId();
|
||||
scenicAddReq.setId(scenicId);
|
||||
int add = scenicMapper.add(scenicAddReq);
|
||||
ScenicAccountEntity scenicAccount = scenicAccountMapper.getByAccount(scenicAddReq.getAccount());
|
||||
if (scenicAccount == null) {
|
||||
scenicAccount = new ScenicAccountEntity();
|
||||
scenicAccount.setId(SnowFlakeUtil.getLongId());
|
||||
scenicAccount.setName(scenicAddReq.getName() + "管理员");
|
||||
scenicAccount.setAccount(scenicAddReq.getAccount());
|
||||
scenicAccount.setPassword(scenicAddReq.getPassword());
|
||||
scenicAccount.setIsSuper(1);
|
||||
scenicAccountMapper.add(scenicAccount);
|
||||
}
|
||||
scenicAccountMapper.addAccountScenicRelation(scenicAccount.getId(), scenicId, 1);
|
||||
if (add > 0) {
|
||||
return ApiResponse.success(true);
|
||||
} else {
|
||||
return ApiResponse.fail("景区添加失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ApiResponse<Boolean> deleteById(Long id) {
|
||||
int i = scenicMapper.deleteById(id);
|
||||
if (i > 0) {
|
||||
scenicAccountMapper.deleteRelationByScenicId(id);
|
||||
IFaceBodyAdapter adapter = getScenicFaceBodyAdapter(id);
|
||||
Thread.ofVirtual().start(() -> {
|
||||
adapter.deleteFaceDb(id.toString());
|
||||
adapter.deleteFaceDb(USER_FACE_DB_NAME + id);
|
||||
});
|
||||
scenicMapper.deleteConfigByScenicId(id);
|
||||
scenicRepository.clearCache(id);
|
||||
scenicFaceBodyAdapterMap.remove(id);
|
||||
scenicStorageAdapterMap.remove(id);
|
||||
scenicPayAdapterMap.remove(id);
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
return ApiResponse.fail("景区删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<Boolean> update(ScenicAddOrUpdateReq scenicUpdateReq) {
|
||||
if (scenicUpdateReq.getId() == null) {
|
||||
return ApiResponse.fail("参数错误");
|
||||
}
|
||||
if (StringUtils.isNotBlank(scenicUpdateReq.getAccount()) && StringUtils.isNotBlank(scenicUpdateReq.getPassword())) {
|
||||
ScenicAccountEntity scenicAccount = scenicAccountMapper.getByAccount(scenicUpdateReq.getAccount());
|
||||
if (scenicAccount != null) {
|
||||
if (!scenicAccount.getScenicId().equals(scenicUpdateReq.getId())) {
|
||||
return ApiResponse.fail("账号已存在");
|
||||
}
|
||||
}
|
||||
ScenicAccountEntity account = scenicAccountMapper.getSuperAccountOfScenic(scenicUpdateReq.getId());
|
||||
if (account != null) {
|
||||
account.setAccount(scenicUpdateReq.getAccount());
|
||||
account.setPassword(scenicUpdateReq.getPassword());
|
||||
scenicAccountMapper.update(account);
|
||||
} else {
|
||||
account = new ScenicAccountEntity();
|
||||
account.setId(SnowFlakeUtil.getLongId());
|
||||
account.setName(scenicUpdateReq.getName() + "管理员");
|
||||
account.setAccount(scenicUpdateReq.getAccount());
|
||||
account.setPassword(scenicUpdateReq.getPassword());
|
||||
account.setIsSuper(1);
|
||||
scenicAccountMapper.add(account);
|
||||
}
|
||||
scenicAccountMapper.addAccountScenicRelation(account.getId(), scenicUpdateReq.getId(), 1);
|
||||
}
|
||||
int i = scenicMapper.update(scenicUpdateReq);
|
||||
if (i > 0) {
|
||||
scenicRepository.clearCache(scenicUpdateReq.getId());
|
||||
scenicFaceBodyAdapterMap.remove(scenicUpdateReq.getId());
|
||||
scenicStorageAdapterMap.remove(scenicUpdateReq.getId());
|
||||
scenicTmpStorageAdapterMap.remove(scenicUpdateReq.getId());
|
||||
scenicLocalStorageAdapterMap.remove(scenicUpdateReq.getId());
|
||||
scenicPayAdapterMap.remove(scenicUpdateReq.getId());
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
return ApiResponse.fail("景区修改失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<Boolean> updateStatus(Long id) {
|
||||
int i = scenicMapper.updateStatus(id);
|
||||
if (i > 0) {
|
||||
scenicRepository.clearCache(id);
|
||||
scenicFaceBodyAdapterMap.remove(id);
|
||||
scenicStorageAdapterMap.remove(id);
|
||||
scenicTmpStorageAdapterMap.remove(id);
|
||||
scenicLocalStorageAdapterMap.remove(id);
|
||||
scenicPayAdapterMap.remove(id);
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
return ApiResponse.fail("景区状态修改失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<Boolean> addConfig(ScenicConfigEntity scenicConfig) {
|
||||
if (scenicConfig.getId() == null) {
|
||||
scenicConfig.setId(SnowFlakeUtil.getLongId());
|
||||
}
|
||||
int i = scenicMapper.addConfig(scenicConfig);
|
||||
if (i > 0) {
|
||||
scenicRepository.clearCache(scenicConfig.getScenicId());
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
return ApiResponse.fail("景区配置添加失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<Boolean> updateConfigById(ScenicConfigEntity scenicConfig) {
|
||||
int i = scenicMapper.updateConfigById(scenicConfig);
|
||||
if (i > 0) {
|
||||
scenicRepository.clearCache(scenicConfig.getScenicId());
|
||||
scenicFaceBodyAdapterMap.remove(scenicConfig.getScenicId());
|
||||
scenicStorageAdapterMap.remove(scenicConfig.getScenicId());
|
||||
scenicTmpStorageAdapterMap.remove(scenicConfig.getScenicId());
|
||||
scenicLocalStorageAdapterMap.remove(scenicConfig.getScenicId());
|
||||
scenicPayAdapterMap.remove(scenicConfig.getScenicId());
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
return ApiResponse.fail("景区配置修改失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScenicConfigEntity getConfig(Long id) {
|
||||
return scenicRepository.getScenicConfig(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfig(Long configId, ScenicConfigEntity config) {
|
||||
config.setId(configId);
|
||||
if (config.getScenicId() == null) {
|
||||
throw new RuntimeException("景区ID不能为空");
|
||||
}
|
||||
scenicMapper.updateConfigById(config);
|
||||
scenicRepository.clearCache(config.getScenicId());
|
||||
scenicFaceBodyAdapterMap.remove(config.getScenicId());
|
||||
scenicStorageAdapterMap.remove(config.getScenicId());
|
||||
scenicTmpStorageAdapterMap.remove(config.getScenicId());
|
||||
scenicLocalStorageAdapterMap.remove(config.getScenicId());
|
||||
scenicPayAdapterMap.remove(config.getScenicId());
|
||||
}
|
||||
|
||||
|
||||
private static final Map<Long, IStorageAdapter> scenicStorageAdapterMap = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public IStorageAdapter getScenicStorageAdapter(Long scenicId) {
|
||||
return scenicStorageAdapterMap.computeIfAbsent(scenicId, (key) -> {
|
||||
return scenicStorageAdapterCache.computeIfAbsent(scenicId, (key) -> {
|
||||
IStorageAdapter adapter;
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
|
||||
if (Strings.isNotBlank(scenicConfig.getString("store_type"))) {
|
||||
try {
|
||||
adapter = StorageFactory.get(scenicConfig.getStoreType());
|
||||
adapter.loadConfig(JacksonUtil.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
|
||||
adapter = StorageFactory.get(scenicConfig.getString("store_type"));
|
||||
adapter.loadConfig(scenicConfig.getObject("store_config_json", Map.class));
|
||||
} catch (StorageUnsupportedException ignored) {
|
||||
return StorageFactory.use("video");
|
||||
}
|
||||
@@ -243,16 +74,15 @@ public class ScenicServiceImpl implements ScenicService {
|
||||
return adapter;
|
||||
});
|
||||
}
|
||||
private static final Map<Long, IStorageAdapter> scenicTmpStorageAdapterMap = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public IStorageAdapter getScenicTmpStorageAdapter(Long scenicId) {
|
||||
return scenicTmpStorageAdapterMap.computeIfAbsent(scenicId, (key) -> {
|
||||
return scenicTmpStorageAdapterCache.computeIfAbsent(scenicId, (key) -> {
|
||||
IStorageAdapter adapter;
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null && scenicConfig.getTmpStoreType() != null) {
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
|
||||
if (Strings.isNotBlank(scenicConfig.getString("tmp_store_type"))) {
|
||||
try {
|
||||
adapter = StorageFactory.get(scenicConfig.getTmpStoreType());
|
||||
adapter.loadConfig(JacksonUtil.parseObject(scenicConfig.getTmpStoreConfigJson(), Map.class));
|
||||
adapter = StorageFactory.get(scenicConfig.getString("tmp_store_type"));
|
||||
adapter.loadConfig(scenicConfig.getObject("tmp_store_config_json", Map.class));
|
||||
} catch (StorageUnsupportedException ignored) {
|
||||
return getScenicStorageAdapter(scenicId);
|
||||
}
|
||||
@@ -262,16 +92,15 @@ public class ScenicServiceImpl implements ScenicService {
|
||||
return adapter;
|
||||
});
|
||||
}
|
||||
private static final Map<Long, IStorageAdapter> scenicLocalStorageAdapterMap = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public IStorageAdapter getScenicLocalStorageAdapter(Long scenicId) {
|
||||
return scenicLocalStorageAdapterMap.computeIfAbsent(scenicId, (key) -> {
|
||||
return scenicLocalStorageAdapterCache.computeIfAbsent(scenicId, (key) -> {
|
||||
IStorageAdapter adapter;
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null && scenicConfig.getLocalStoreType() != null) {
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
|
||||
if (Strings.isNotBlank(scenicConfig.getString("local_store_type"))) {
|
||||
try {
|
||||
adapter = StorageFactory.get(scenicConfig.getLocalStoreType());
|
||||
adapter.loadConfig(JacksonUtil.parseObject(scenicConfig.getLocalStoreConfigJson(), Map.class));
|
||||
adapter = StorageFactory.get(scenicConfig.getString("local_store_type"));
|
||||
adapter.loadConfig(scenicConfig.getObject("local_store_config_json", Map.class));
|
||||
} catch (StorageUnsupportedException ignored) {
|
||||
return getScenicStorageAdapter(scenicId);
|
||||
}
|
||||
@@ -282,15 +111,14 @@ public class ScenicServiceImpl implements ScenicService {
|
||||
});
|
||||
}
|
||||
|
||||
private static final Map<Long, IFaceBodyAdapter> scenicFaceBodyAdapterMap = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public IFaceBodyAdapter getScenicFaceBodyAdapter(Long scenicId) {
|
||||
return scenicFaceBodyAdapterMap.computeIfAbsent(scenicId, (key) -> {
|
||||
return scenicFaceBodyAdapterCache.computeIfAbsent(scenicId, (key) -> {
|
||||
IFaceBodyAdapter adapter;
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null && scenicConfig.getFaceType() != null) {
|
||||
adapter = FaceBodyFactory.getAdapter(scenicConfig.getFaceType());
|
||||
adapter.loadConfig(JacksonUtil.parseObject(scenicConfig.getFaceConfigJson(), Map.class));
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
|
||||
if (Strings.isNotBlank(scenicConfig.getString("face_type"))) {
|
||||
adapter = FaceBodyFactory.getAdapter(scenicConfig.getString("face_type"));
|
||||
adapter.loadConfig(scenicConfig.getObject("face_config_json", Map.class));
|
||||
} else {
|
||||
adapter = FaceBodyFactory.use();
|
||||
}
|
||||
@@ -298,19 +126,108 @@ public class ScenicServiceImpl implements ScenicService {
|
||||
});
|
||||
}
|
||||
|
||||
private static final Map<Long, IPayAdapter> scenicPayAdapterMap = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public IPayAdapter getScenicPayAdapter(Long scenicId) {
|
||||
return scenicPayAdapterMap.computeIfAbsent(scenicId, (key) -> {
|
||||
return scenicPayAdapterCache.computeIfAbsent(scenicId, (key) -> {
|
||||
IPayAdapter adapter;
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
|
||||
if (scenicConfig != null && scenicConfig.getPayType() != null) {
|
||||
adapter = PayFactory.getAdapter(scenicConfig.getPayType());
|
||||
adapter.loadConfig(JacksonUtil.parseObject(scenicConfig.getPayConfigJson(), Map.class));
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
|
||||
if (Strings.isNotBlank(scenicConfig.getString("pay_type"))) {
|
||||
adapter = PayFactory.getAdapter(scenicConfig.getString("pay_type"));
|
||||
adapter.loadConfig(scenicConfig.getObject("pay_config_json", Map.class));
|
||||
} else {
|
||||
adapter = PayFactory.use();
|
||||
}
|
||||
return adapter;
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 缓存管理方法 ====================
|
||||
|
||||
/**
|
||||
* 清除指定景区的所有适配器缓存
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
*/
|
||||
public void clearScenicAdapterCache(Long scenicId) {
|
||||
log.info("清除景区 {} 的所有适配器缓存", scenicId);
|
||||
scenicStorageAdapterCache.remove(scenicId);
|
||||
scenicTmpStorageAdapterCache.remove(scenicId);
|
||||
scenicLocalStorageAdapterCache.remove(scenicId);
|
||||
scenicFaceBodyAdapterCache.remove(scenicId);
|
||||
scenicPayAdapterCache.remove(scenicId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有适配器缓存
|
||||
*/
|
||||
public void clearAllAdapterCache() {
|
||||
log.info("清除所有适配器缓存");
|
||||
scenicStorageAdapterCache.clear();
|
||||
scenicTmpStorageAdapterCache.clear();
|
||||
scenicLocalStorageAdapterCache.clear();
|
||||
scenicFaceBodyAdapterCache.clear();
|
||||
scenicPayAdapterCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发过期缓存清理
|
||||
*
|
||||
* @return 清理的过期缓存项总数
|
||||
*/
|
||||
public int cleanupExpiredCache() {
|
||||
log.info("手动触发过期缓存清理");
|
||||
int totalCleaned = 0;
|
||||
totalCleaned += scenicStorageAdapterCache.cleanupExpired();
|
||||
totalCleaned += scenicTmpStorageAdapterCache.cleanupExpired();
|
||||
totalCleaned += scenicLocalStorageAdapterCache.cleanupExpired();
|
||||
totalCleaned += scenicFaceBodyAdapterCache.cleanupExpired();
|
||||
totalCleaned += scenicPayAdapterCache.cleanupExpired();
|
||||
log.info("清理了 {} 个过期缓存项", totalCleaned);
|
||||
return totalCleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存统计信息
|
||||
*
|
||||
* @return 缓存统计信息
|
||||
*/
|
||||
public String getCacheStats() {
|
||||
StringBuilder stats = new StringBuilder();
|
||||
stats.append("=== ScenicServiceImpl 缓存统计信息 ===\n");
|
||||
stats.append("Storage Adapter Cache: ").append(scenicStorageAdapterCache.getStats()).append("\n");
|
||||
stats.append("Tmp Storage Adapter Cache: ").append(scenicTmpStorageAdapterCache.getStats()).append("\n");
|
||||
stats.append("Local Storage Adapter Cache: ").append(scenicLocalStorageAdapterCache.getStats()).append("\n");
|
||||
stats.append("FaceBody Adapter Cache: ").append(scenicFaceBodyAdapterCache.getStats()).append("\n");
|
||||
stats.append("Pay Adapter Cache: ").append(scenicPayAdapterCache.getStats()).append("\n");
|
||||
return stats.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置所有缓存统计信息
|
||||
*/
|
||||
public void resetCacheStats() {
|
||||
log.info("重置所有缓存统计信息");
|
||||
scenicStorageAdapterCache.resetStats();
|
||||
scenicTmpStorageAdapterCache.resetStats();
|
||||
scenicLocalStorageAdapterCache.resetStats();
|
||||
scenicFaceBodyAdapterCache.resetStats();
|
||||
scenicPayAdapterCache.resetStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定景区缓存的剩余TTL时间
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
* @return 各类型适配器缓存的剩余TTL时间(毫秒)
|
||||
*/
|
||||
public String getScenicCacheTtl(Long scenicId) {
|
||||
StringBuilder ttlInfo = new StringBuilder();
|
||||
ttlInfo.append("景区 ").append(scenicId).append(" 缓存TTL信息:\n");
|
||||
ttlInfo.append("Storage: ").append(scenicStorageAdapterCache.getRemainTtl(scenicId)).append("ms\n");
|
||||
ttlInfo.append("TmpStorage: ").append(scenicTmpStorageAdapterCache.getRemainTtl(scenicId)).append("ms\n");
|
||||
ttlInfo.append("LocalStorage: ").append(scenicLocalStorageAdapterCache.getRemainTtl(scenicId)).append("ms\n");
|
||||
ttlInfo.append("FaceBody: ").append(scenicFaceBodyAdapterCache.getRemainTtl(scenicId)).append("ms\n");
|
||||
ttlInfo.append("Pay: ").append(scenicPayAdapterCache.getRemainTtl(scenicId)).append("ms\n");
|
||||
return ttlInfo.toString();
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ import com.ycwl.basic.model.pc.template.entity.TemplateConfigEntity;
|
||||
import com.ycwl.basic.model.pc.template.entity.TemplateEntity;
|
||||
import com.ycwl.basic.model.pc.template.req.TemplateReqQuery;
|
||||
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
|
||||
import com.ycwl.basic.pricing.enums.ProductType;
|
||||
import com.ycwl.basic.pricing.service.IPricingManagementService;
|
||||
import com.ycwl.basic.service.pc.TemplateService;
|
||||
import com.ycwl.basic.repository.TemplateRepository;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
@@ -30,6 +32,8 @@ public class TemplateServiceImpl implements TemplateService {
|
||||
private TemplateMapper templateMapper;
|
||||
@Autowired
|
||||
private TemplateRepository templateRepository;
|
||||
@Autowired
|
||||
private IPricingManagementService pricingManagementService;
|
||||
|
||||
@Override
|
||||
public ApiResponse<PageInfo<TemplateRespVO>> pageQuery(TemplateReqQuery templateReqQuery) {
|
||||
@@ -75,6 +79,7 @@ public class TemplateServiceImpl implements TemplateService {
|
||||
templateMapper.add(item);
|
||||
});
|
||||
}
|
||||
pricingManagementService.quickSetupProductPrice(ProductType.VLOG_VIDEO.getCode(), template.getId().toString(), template.getScenicId().toString(), template.getName(), template.getPrice(), template.getSlashPrice(), "个");
|
||||
if (i > 0) {
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
@@ -112,6 +117,7 @@ public class TemplateServiceImpl implements TemplateService {
|
||||
});
|
||||
}
|
||||
templateRepository.clearTemplateCache(template.getId());
|
||||
pricingManagementService.quickSetupProductPrice(ProductType.VLOG_VIDEO.getCode(), template.getId().toString(), template.getScenicId().toString(), template.getName(), template.getPrice(), template.getSlashPrice(), "个");
|
||||
if (i > 0) {
|
||||
return ApiResponse.success(true);
|
||||
}else {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user