Compare commits

..

1 Commits

Author SHA1 Message Date
8a17392ae5 Jenkinsfile
Some checks are pending
Gitea/FrameTour-BE/pipeline/head Build queued...
2025-06-30 17:51:34 +08:00
999 changed files with 7324 additions and 82425 deletions

4
.gitignore vendored
View File

@@ -1,7 +1,3 @@
.idea/
logs/
target/
.serena
.claude
.vscode
*.jpg

View File

@@ -1,27 +0,0 @@
# Repository Guidelines
## Build, Test, and Development Commands
- Build artifact: `mvn clean package` (tests are skipped by default via `pom.xml`).
- Run locally (dev): `mvn spring-boot:run -Dspring-boot.run.profiles=dev`.
- Run jar: `java -jar target/basic21-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev`.
- Execute all tests: `mvn -DskipTests=false test` (note: `pom.xml` excludes `**/*Test.java` from test-compile; temporarily remove/override that config if you need to compile and run tests).
- Run single test: `mvn -DskipTests=false test -Dtest=ClassNameTest` (after removing testExcludes from maven-compiler-plugin).
## Code Style Guidelines
- Java 21. Use 4-space indentation; UTF-8; no wildcard imports.
- Packages: `com.ycwl.basic.*`; classes PascalCase; methods/fields camelCase; constants UPPER_SNAKE_CASE.
- Controllers in `controller`, business logic in `service`, persistence in `mapper` + `resources/mapper/*.xml`.
- Prefer Lombok for boilerplate and constructor injection where applicable.
- Error handling: Use custom exceptions in `exception` package; proper logging with SLF4J.
- Testing: Spring Boot testing + JUnit; test names end with `Test` or `Tests` and mirror package structure.
## Project Structure
- Application code: `src/main/java/com/ycwl/basic/**` (controllers, services, mapper/repository, dto/model, config, util).
- Resources: `src/main/resources/**` (Spring configs, `mapper/*.xml`, static assets, logging).
- Tests: `src/test/java/**` mirrors main packages.
- Build output: `target/` (never commit).
## Agent-Specific Notes
- Keep changes minimal and within existing package boundaries.
- Do not reorganize MyBatis XML names or mapper interfaces without updating both sides.
- If altering APIs, update affected tests and documentation in the same PR.

View File

@@ -1,27 +0,0 @@
# Repository Guidelines
## Build, Test, and Development Commands
- Build artifact: `mvn clean package` (tests are skipped by default via `pom.xml`).
- Run locally (dev): `mvn spring-boot:run -Dspring-boot.run.profiles=dev`.
- Run jar: `java -jar target/basic21-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev`.
- Execute all tests: `mvn -DskipTests=false test` (note: `pom.xml` excludes `**/*Test.java` from test-compile; temporarily remove/override that config if you need to compile and run tests).
- Run single test: `mvn -DskipTests=false test -Dtest=ClassNameTest` (after removing testExcludes from maven-compiler-plugin).
## Code Style Guidelines
- Java 21. Use 4-space indentation; UTF-8; no wildcard imports.
- Packages: `com.ycwl.basic.*`; classes PascalCase; methods/fields camelCase; constants UPPER_SNAKE_CASE.
- Controllers in `controller`, business logic in `service`, persistence in `mapper` + `resources/mapper/*.xml`.
- Prefer Lombok for boilerplate and constructor injection where applicable.
- Error handling: Use custom exceptions in `exception` package; proper logging with SLF4J.
- Testing: Spring Boot testing + JUnit; test names end with `Test` or `Tests` and mirror package structure.
## Project Structure
- Application code: `src/main/java/com/ycwl/basic/**` (controllers, services, mapper/repository, dto/model, config, util).
- Resources: `src/main/resources/**` (Spring configs, `mapper/*.xml`, static assets, logging).
- Tests: `src/test/java/**` mirrors main packages.
- Build output: `target/` (never commit).
## Agent-Specific Notes
- Keep changes minimal and within existing package boundaries.
- Do not reorganize MyBatis XML names or mapper interfaces without updating both sides.
- If altering APIs, update affected tests and documentation in the same PR.

12
Jenkinsfile vendored
View File

@@ -1,20 +1,20 @@
pipeline {
agent any
tools {
jdk 'openjdk21'
maven 'Default'
environment {
JAVA_HOME = "/opt/openjdk21.0.7"
MAVEN_HOME = "/opt/apache-maven-3.9.9"
PATH = "${env.JAVA_HOME}/bin:${env.MAVEN_HOME}/bin:${env.PATH}"
}
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests=true'
bat 'mvn clean package -DskipTests=true'
}
}
}
post {
always {
archiveArtifacts artifacts: 'target/*.jar', allowEmptyArchive: true, onlyIfSuccessful: true
publishGiteaAssets assets: 'target/*.jar', followSymlinks: false, onlyIfSuccessful: true
archiveArtifacts artifacts: 'target/*.jar', allowEmptyArchive: false
}
}
}

0
README.md Normal file
View File

139
pom.xml
View File

@@ -19,30 +19,21 @@
<java.version>21</java.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<spring.cloud.alibaba.version>2023.0.1.2</spring.cloud.alibaba.version>
<spring.cloud.version>2023.0.3</spring.cloud.version>
<hutool-all.version>5.8.24</hutool-all.version>
<mysql-connector.version>8.0.33</mysql-connector.version>
<fastjson.version>1.2.83</fastjson.version>
<knife4j-spring-boot-starter.version>2.0.7</knife4j-spring-boot-starter.version>
<pagehelper.version>5.3.1</pagehelper.version>
<!--跳过单元测试-->
<skipTests>true</skipTests>
</properties>
<!-- Dependency Management -->
<!-- OpenTelemetry -->
<dependencyManagement>
<dependencies>
<!-- Spring Cloud BOM -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba BOM -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-bom</artifactId>
<version>2.16.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -75,47 +66,6 @@
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Nacos服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Nacos配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Spring Cloud Bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 引入redis,并且redis使用jedis连接 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -124,9 +74,9 @@
<!-- 引入mysql连接 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
<scope>runtime</scope>
</dependency>
@@ -173,30 +123,24 @@
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<!-- Jackson JSON处理库 -->
<!-- json处理工具 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- 引入commons-lang3 工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<!-- 引入接口文档工具 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j-spring-boot-starter.version}</version>
</dependency>
<!-- pageHelper -->
@@ -225,7 +169,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.11.0</version>
<version>1.1</version>
</dependency>
<!-- 阿里云对象存储 -->
@@ -266,36 +210,9 @@
<version>4.16.19</version>
</dependency>
<!-- 阿里云媒体处理 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>mts20140618</artifactId>
<version>5.0.0</version>
</dependency>
<!-- 智谱AI SDK -->
<dependency>
<groupId>ai.z.openapi</groupId>
<artifactId>zai-sdk</artifactId>
<version>0.1.3</version>
</dependency>
<!-- Spring Kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<!-- Apache POI - 处理Excel文件 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.4.0</version>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
</dependencies>
@@ -324,6 +241,16 @@
<skip>${skipTests}</skip>
</configuration>
</plugin>
<!-- 跳过测试编译 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<testExcludes>
<testExclude>**/*Test.java</testExclude>
</testExcludes>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -1,13 +1,12 @@
package com.ycwl.basic;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan(basePackages = "com.ycwl.basic.mapper")
@MapperScan(basePackages = "com.ycwl.basic.*.mapper")
public class Application {
public static void main(String[] args) {

View File

@@ -1,6 +1,6 @@
package com.ycwl.basic.aspectj;
import com.ycwl.basic.utils.JacksonUtil;
import com.alibaba.fastjson.JSON;
import com.ycwl.basic.annotation.IgnoreLogReq;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@@ -67,10 +67,10 @@ public class RequestParameterAspectj {
}
if (parameterUrlMap.isEmpty()) {
LOGGER.info("当前请求的路径为-> {} 请求方式为-> {} 参数为-> {}", requestURI, method, JacksonUtil.toJSONString(parameterValueSet));
LOGGER.info("当前请求的路径为-> {} 请求方式为-> {} 参数为-> {}", requestURI, method, JSON.toJSONString(parameterValueSet));
} else {
LOGGER.info("当前请求的路径为-> {} 请求方式为-> {} 参数为-> {} 路径传参为-> {}", requestURI, method,
JacksonUtil.toJSONString(parameterValueSet), JacksonUtil.toJSONString(parameterUrlMap));
JSON.toJSONString(parameterValueSet), JSON.toJSONString(parameterUrlMap));
}
}
return joinPoint.proceed();

View File

@@ -1,12 +1,10 @@
package com.ycwl.basic.biz;
import com.ycwl.basic.enums.StatisticEnum;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.mapper.OrderMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.mapper.StatisticsMapper;
import com.ycwl.basic.mapper.VideoMapper;
import com.ycwl.basic.model.mobile.order.IsBuyBatchRespVO;
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
import com.ycwl.basic.model.mobile.order.PriceObj;
import com.ycwl.basic.model.mobile.statistic.req.StatisticsRecordAddReq;
@@ -15,18 +13,17 @@ import com.ycwl.basic.model.pc.couponRecord.resp.CouponRecordQueryResp;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
import com.ycwl.basic.model.pc.order.entity.OrderItemEntity;
import com.ycwl.basic.model.pc.order.req.OrderUpdateReq;
import com.ycwl.basic.model.pc.order.resp.OrderAppRespVO;
import com.ycwl.basic.model.pc.order.resp.OrderItemVO;
import com.ycwl.basic.model.pc.order.resp.OrderRespVO;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
import com.ycwl.basic.pricing.dto.ProductItem;
import com.ycwl.basic.pricing.enums.ProductType;
import com.ycwl.basic.pricing.service.IPriceCalculationService;
import com.ycwl.basic.model.pc.video.resp.VideoRespVO;
import com.ycwl.basic.profitsharing.biz.ProfitSharingBiz;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.OrderRepository;
@@ -36,12 +33,13 @@ import com.ycwl.basic.repository.TemplateRepository;
import com.ycwl.basic.repository.VideoRepository;
import com.ycwl.basic.repository.VideoTaskRepository;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
@@ -81,16 +79,16 @@ public class OrderBiz {
@Autowired
@Lazy
private PrinterService printerService;
@Autowired
private IPriceCalculationService iPriceCalculationService;
public PriceObj queryPrice(Long scenicId, int goodsType, Long goodsId) {
PriceObj priceObj = new PriceObj();
priceObj.setGoodsType(goodsType);
priceObj.setGoodsId(goodsId);
ScenicEntity scenic = scenicRepository.getScenic(scenicId);
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
priceObj.setScenicAllPrice(scenic.getPrice());
if (scenicConfig != null) {
if (Boolean.TRUE.equals(scenicConfig.getAllFree())) {
if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) {
// 景区全免
priceObj.setFree(true);
priceObj.setPrice(BigDecimal.ZERO);
@@ -112,62 +110,31 @@ public class OrderBiz {
if (template == null) {
return priceObj;
}
PriceCalculationRequest vlogCalculationRequest = new PriceCalculationRequest();
ProductItem vlogProductItem = new ProductItem();
vlogProductItem.setProductType(ProductType.VLOG_VIDEO);
vlogProductItem.setProductId(template.getId().toString());
vlogProductItem.setQuantity(videoTaskRepository.getTaskLensNum(video.getTaskId()));
vlogProductItem.setScenicId(scenicId.toString());
vlogCalculationRequest.setProducts(Collections.singletonList(vlogProductItem));
vlogCalculationRequest.setFaceId(priceObj.getFaceId());
vlogCalculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠
PriceCalculationResult vlogCalculationResult = iPriceCalculationService.calculatePrice(vlogCalculationRequest);
priceObj.setPrice(vlogCalculationResult.getFinalAmount());
priceObj.setSlashPrice(vlogCalculationResult.getOriginalAmount());
priceObj.setFaceId(goodsId);
priceObj.setPrice(template.getPrice());
BigDecimal slashPrice = template.getSlashPrice();
if (slashPrice == null) {
priceObj.setSlashPrice(priceObj.getPrice());
} else {
priceObj.setSlashPrice(slashPrice);
}
priceObj.setScenicId(video.getScenicId());
break;
case 1: // source
case 2: // source
FaceEntity face = faceRepository.getFace(goodsId);
PriceCalculationRequest calculationRequest = new PriceCalculationRequest();
ProductItem productItem = new ProductItem();
productItem.setProductType(goodsType == 1 ? ProductType.RECORDING_SET : ProductType.PHOTO_SET);
productItem.setProductId(scenicId.toString());
productItem.setPurchaseCount(1);
productItem.setScenicId(scenicId.toString());
calculationRequest.setProducts(Collections.singletonList(productItem));
if (face != null) {
calculationRequest.setUserId(face.getMemberId());
}
calculationRequest.setFaceId(goodsId);
calculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠
PriceCalculationResult priceCalculationResult = iPriceCalculationService.calculatePrice(calculationRequest);
priceObj.setPrice(priceCalculationResult.getFinalAmount());
priceObj.setSlashPrice(priceCalculationResult.getOriginalAmount());
priceObj.setPrice(scenic.getSourceVideoPrice());
priceObj.setSlashPrice(scenic.getSourceVideoPrice());
priceObj.setFaceId(goodsId);
break;
case 13:
PriceCalculationRequest aiCamCalculationRequest = new PriceCalculationRequest();
ProductItem aiCamProductItem = new ProductItem();
aiCamProductItem.setProductType(ProductType.AI_CAM_PHOTO_SET);
aiCamProductItem.setProductId(scenicId.toString());
aiCamProductItem.setPurchaseCount(1);
aiCamProductItem.setScenicId(scenicId.toString());
aiCamCalculationRequest.setProducts(Collections.singletonList(aiCamProductItem));
aiCamCalculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠
PriceCalculationResult aiCamPriceCalculationResult = iPriceCalculationService.calculatePrice(aiCamCalculationRequest);
priceObj.setPrice(aiCamPriceCalculationResult.getFinalAmount());
priceObj.setSlashPrice(aiCamPriceCalculationResult.getOriginalAmount());
case 2: // source
priceObj.setPrice(scenic.getSourceImagePrice());
priceObj.setSlashPrice(scenic.getSourceImagePrice());
priceObj.setFaceId(goodsId);
priceObj.setScenicId(scenicId);
break;
}
return priceObj;
}
public OrderEntity hasTypeOrder(Long userId, Long faceId, Long scenicId, int orderType, Integer configId) {
OrderEntity orderEntity = orderMapper.queryTypeOrder(userId, faceId, scenicId, orderType, configId);
public OrderEntity hasTypeOrder(Long userId, Long scenicId, int orderType, Integer configId) {
OrderEntity orderEntity = orderMapper.queryTypeOrder(userId, scenicId, orderType, configId);
if (orderEntity == null) {
return null;
}
@@ -177,46 +144,82 @@ public class OrderBiz {
return null;
}
}
public IsBuyRespVO isBuy(Long scenicId, Long memberId, Long faceId, int goodsType, Long goodsId) {
public IsBuyRespVO isBuy(Long userId, Long scenicId, int goodsType, Long goodsId) {
IsBuyRespVO respVO = new IsBuyRespVO();
respVO.setGoodsType(goodsType);
respVO.setGoodsId(goodsId);
OrderEntity orderEntity = orderMapper.getUserBuyFaceItem(memberId, faceId, goodsType, goodsId);
boolean isBuy = orderRepository.checkUserBuyItem(userId, goodsType, goodsId);
// 模板购买逻辑
if (!isBuy) {
if (goodsType == 0) {
VideoEntity video = videoRepository.getVideo(goodsId);
TaskEntity task = videoTaskRepository.getTaskById(video.getTaskId());
Long templateId = video.getTemplateId();
// -1为整个模板购买
OrderEntity orderEntity = orderRepository.getUserBuyItem(userId, -1, templateId);
if (orderEntity != null && task != null) {
respVO.setOrderId(orderEntity.getId());
if (orderEntity.getFaceId() != null && task.getFaceId() != null) {
isBuy = orderEntity.getFaceId().equals(task.getFaceId());
}
}
}
}
// 免费送逻辑,之前已经赠送了的
if (!isBuy) {
isBuy = switch (goodsType) {
case 0 -> videoRepository.getUserIsBuy(userId, goodsId);
case 1, 2 -> sourceRepository.getUserIsBuy(userId, goodsType, goodsId);
default -> false;
};
} else {
OrderEntity orderEntity = orderRepository.getUserBuyItem(userId, goodsType, goodsId);
if (orderEntity != null) {
respVO.setOrderId(orderEntity.getId());
respVO.setBuy(true);
respVO.setFree(false);
return respVO;
}
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
if (Boolean.TRUE.equals(scenicConfig.getBoolean("all_free"))) {
// 景区全免
respVO.setFree(true);
respVO.setOrigPrice(BigDecimal.ZERO);
respVO.setSlashPrice(BigDecimal.ZERO);
return respVO;
}
// 未来模板一口价
if (goodsType == 0) {
// 视频,可以买断模板
VideoEntity video = videoRepository.getVideo(goodsId);
if (video != null && video.getTemplateId() != null) {
OrderEntity templateBuy = orderMapper.getUserBuyFaceItem(memberId, faceId, -1, video.getTemplateId());
if (templateBuy != null) {
respVO.setOrderId(templateBuy.getId());
respVO.setBuy(true);
respVO.setFree(false);
return respVO;
}
}
}
respVO.setBuy(isBuy);
// 还是没买
if (!isBuy) {
PriceObj priceObj = queryPrice(scenicId, goodsType, goodsId);
if (priceObj == null) {
return respVO;
}
respVO.setBuy(false);
respVO.setFree(priceObj.isFree());
respVO.setGoodsType(goodsType);
respVO.setGoodsId(goodsId);
respVO.setOrigPrice(priceObj.getPrice());
respVO.setSlashPrice(priceObj.getSlashPrice());
switch (goodsType) {
case 0: // vlog
VideoEntity video = videoRepository.getVideo(goodsId);
TaskEntity taskById = videoTaskRepository.getTaskById(video.getTaskId());
if (taskById != null) {
CouponRecordQueryResp recordQueryResp = couponBiz.queryUserCouponRecord(scenicId, userId, taskById.getFaceId(), taskById.getTemplateId().toString());
if (recordQueryResp.isUsable()) {
respVO.setCouponId(recordQueryResp.getCouponId());
respVO.setCouponRecordId(recordQueryResp.getId());
CouponEntity coupon = recordQueryResp.getCoupon();
if (coupon != null) {
respVO.setCouponPrice(coupon.calculateDiscountPrice(priceObj.getPrice()));
}
}
}
break;
case 1:
case 2:
CouponRecordQueryResp recordQueryResp = couponBiz.queryUserCouponRecord(scenicId, userId, goodsId, String.valueOf(goodsType));
if (recordQueryResp.isUsable()) {
respVO.setCouponId(recordQueryResp.getCouponId());
respVO.setCouponRecordId(recordQueryResp.getId());
CouponEntity coupon = recordQueryResp.getCoupon();
if (coupon != null) {
respVO.setCouponPrice(coupon.calculateDiscountPrice(priceObj.getPrice()));
}
}
break;
}
}
return respVO;
}
@@ -230,27 +233,51 @@ public class OrderBiz {
orderRepository.updateOrder(orderId, orderUpdate);
orderItems.forEach(item -> {
switch (item.getGoodsType()) {
case -1: // vlog视频模板
videoRepository.setUserIsBuyTemplate(order.getMemberId(), item.getGoodsId(), order.getId(), order.getFaceId());
break;
case 0: // vlog视频
videoRepository.setUserIsBuyItem(order.getMemberId(), item.getGoodsId(), order.getId());
break;
case 1: // 视频原素材
case 2: // 照片原素材
case 13: // AI微单
sourceRepository.setUserIsBuyItem(order.getMemberId(), item.getGoodsType(), item.getGoodsId(), order.getId());
break;
case 3:
printerService.setUserIsBuyItem(order.getMemberId(), item.getGoodsId(), order.getId());
break;
}
});
orderRepository.clearOrderCache(orderId); // 更新完了,清理下
Integer couponRecordId = order.getCouponRecordId();
if (couponRecordId != null) {
couponBiz.userUseCoupon(order.getMemberId(), order.getFaceId(), couponRecordId, orderId);
if (order.getCouponRecordId() != null) {
couponBiz.userUseCoupon(order.getMemberId(), order.getFaceId(), order.getCouponRecordId(), orderId);
}
//支付时间
OrderAppRespVO orderDetail = orderMapper.appDetail(orderId);
Date payAt = orderDetail.getPayAt();
//商品创建时间
Date goodsCreateTime = new Date();
if (!orderDetail.getOrderItemList().isEmpty()) {
OrderItemVO orderItemVO = orderDetail.getOrderItemList().getFirst();
switch (orderItemVO.getGoodsType()) {
case 0:
VideoEntity video = videoRepository.getVideo(orderItemVO.getGoodsId());
if (video != null) {
goodsCreateTime = video.getCreateTime();
}
break;
case 1:
List<SourceEntity> imageSource = sourceMapper.listImageByFaceRelation(order.getMemberId(), orderItemVO.getGoodsId());
Optional<SourceEntity> min = imageSource.stream().min(Comparator.comparing(SourceEntity::getCreateTime));
if (min.isPresent()) {
goodsCreateTime = min.get().getCreateTime();
}
break;
case 2:
List<SourceEntity> videoSource = sourceMapper.listImageByFaceRelation(order.getMemberId(), orderItemVO.getGoodsId());
Optional<SourceEntity> minTime = videoSource.stream().min(Comparator.comparing(SourceEntity::getCreateTime));
if (minTime.isPresent()) {
goodsCreateTime = minTime.get().getCreateTime();
}
break;
}
}
StatisticsRecordAddReq statisticsRecordAddReq = new StatisticsRecordAddReq();
statisticsRecordAddReq.setMemberId(order.getMemberId());
Long enterType = statisticsMapper.getUserRecentEnterType(order.getMemberId(), order.getCreateAt());
@@ -278,11 +305,9 @@ public class OrderBiz {
switch (item.getGoodsType()) {
case 0: // vlog视频
videoRepository.setUserNotBuyItem(order.getMemberId(), item.getGoodsId());
break;
case 1: // 视频原素材
case 2: // 照片原素材
sourceRepository.setUserNotBuyItem(order.getMemberId(), item.getGoodsType(), item.getGoodsId());
break;
}
});
orderRepository.clearOrderCache(orderId); // 更新完了,清理下
@@ -302,26 +327,12 @@ public class OrderBiz {
switch (item.getGoodsType()) {
case 0: // vlog视频
videoRepository.setUserNotBuyItem(order.getMemberId(), item.getGoodsId());
break;
case 1: // 视频原素材
case 2: // 照片原素材
sourceRepository.setUserNotBuyItem(order.getMemberId(), item.getGoodsType(), item.getGoodsId());
break;
}
});
orderRepository.clearOrderCache(orderId); // 更新完了,清理下
profitSharingBiz.revokeProfitSharing(order.getScenicId(), orderId, "订单已退款");
}
/**
* 检查用户是否购买了指定商品,并额外校验订单的faceId是否匹配
* @param userId 用户ID
* @param faceId 人脸ID
* @param goodsType 商品类型
* @param goodsId 商品ID
* @return 是否已购买且faceId匹配
*/
public boolean checkUserBuyFaceItem(Long userId, Long faceId, int goodsType, Long goodsId) {
return orderRepository.checkUserBuyFaceItem(userId, faceId, goodsType, goodsId);
}
}

View File

@@ -7,23 +7,14 @@ import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
import com.ycwl.basic.model.pc.price.entity.PriceConfigEntity;
import com.ycwl.basic.model.pc.price.resp.GoodsListRespVO;
import com.ycwl.basic.model.pc.price.resp.SimpleGoodsRespVO;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.model.pc.video.entity.MemberVideoEntity;
import com.ycwl.basic.product.capability.ProductTypeCapability;
import com.ycwl.basic.product.service.IProductTypeCapabilityManagementService;
import com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity;
import com.ycwl.basic.puzzle.mapper.PuzzleTemplateMapper;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.MemberRelationRepository;
import com.ycwl.basic.repository.PriceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.TemplateRepository;
import com.ycwl.basic.service.pc.FaceService;
import org.apache.commons.lang3.Strings;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@@ -45,16 +36,7 @@ public class PriceBiz {
@Autowired
private FaceRepository faceRepository;
@Autowired
@Lazy
private FaceService faceService;
@Autowired
private CouponBiz couponBiz;
@Autowired
private MemberRelationRepository memberRelationRepository;
@Autowired
private PuzzleTemplateMapper puzzleTemplateMapper;
@Autowired
private IProductTypeCapabilityManagementService productTypeCapabilityManagementService;
public List<GoodsListRespVO> listGoodsByScenic(Long scenicId) {
List<GoodsListRespVO> goodsList = new ArrayList<>();
@@ -64,129 +46,17 @@ public class PriceBiz {
GoodsListRespVO goods = new GoodsListRespVO();
goods.setGoodsId(template.getId());
goods.setGoodsName(template.getName());
goods.setGoodsType(0);
return goods;
}).forEach(goodsList::add);
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null) {
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
goodsList.add(new GoodsListRespVO(1L, "录像集", 1));
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
goodsList.add(new GoodsListRespVO(1L, "录像集"));
}
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceImage())) {
goodsList.add(new GoodsListRespVO(2L, "照片集", 2));
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage())) {
goodsList.add(new GoodsListRespVO(2L, "照片集"));
}
}
// 拼图
puzzleTemplateMapper.list(scenicId, null, 1).forEach(puzzleTemplate -> {
GoodsListRespVO goods = new GoodsListRespVO();
goods.setGoodsId(puzzleTemplate.getId());
goods.setGoodsName(puzzleTemplate.getName());
goods.setGoodsType(5);
goodsList.add(goods);
});
return goodsList;
}
/**
* 根据景区ID和商品类型查询简化的商品列表
*
* @param scenicId 景区ID
* @param productType 商品类型(可选,为空时返回所有商品)
* @return 简化的商品列表
*/
public List<SimpleGoodsRespVO> listSimpleGoodsByScenic(Long scenicId, String productType) {
List<SimpleGoodsRespVO> goodsList = new ArrayList<>();
// 如果 productType 为空,兼容旧逻辑
if (productType == null || productType.isEmpty()) {
return listAllSimpleGoods(scenicId);
}
// 根据 productType 查询不同数据源
switch (productType) {
case "VLOG_VIDEO":
// 从 template 表查询视频模板
List<TemplateRespVO> templateList = templateRepository.getTemplateListByScenicId(scenicId);
templateList.stream()
.map(template -> new SimpleGoodsRespVO(template.getId(), template.getName(), productType))
.forEach(goodsList::add);
break;
case "PHOTO_VLOG":
// TODO
goodsList.add(new SimpleGoodsRespVO(scenicId, "【待实现】pLog视频", productType));
break;
case "PHOTO":
goodsList.add(new SimpleGoodsRespVO(scenicId, "单张照片", productType));
break;
case "PHOTO_SET":
// 返回固定的照片集条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "照片集", productType));
break;
case "AI_CAM_PHOTO_SET":
// 返回固定的照片集条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "AI微单照片集", productType));
break;
case "PHOTO_LOG":
// 从 template 表查询pLog模板
goodsList.add(new SimpleGoodsRespVO(scenicId, "pLog图<景区打包>", productType));
List<PuzzleTemplateEntity> puzzleList = puzzleTemplateMapper.list(scenicId, null, null);
puzzleList.stream()
.map(template -> new SimpleGoodsRespVO(template.getId(), template.getName(), productType))
.forEach(goodsList::add);
break;
case "RECORDING_SET":
// 返回固定的录像集条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "录像集", productType));
break;
case "PHOTO_PRINT":
// 打印类返回单一通用条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "照片打印", productType));
break;
case "PHOTO_PRINT_MU":
// 打印类返回单一通用条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "手机照片打印", productType));
break;
case "PHOTO_PRINT_FX":
// 打印类返回单一通用条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "效果图片打印", productType));
break;
case "MACHINE_PRINT":
// 打印类返回单一通用条目
goodsList.add(new SimpleGoodsRespVO(scenicId, "一体机打印", productType));
break;
default:
// 不支持的 productType,返回空列表
break;
}
return goodsList;
}
/**
* 兼容旧逻辑:返回所有商品
* 通过查询系统中所有已知的 productType,将结果综合到一起
*/
private List<SimpleGoodsRespVO> listAllSimpleGoods(Long scenicId) {
List<SimpleGoodsRespVO> goodsList = new ArrayList<>();
// 从 ProductTypeCapability 服务查询所有已知的商品类型(仅包含启用的)
List<ProductTypeCapability> capabilities = productTypeCapabilityManagementService.queryAll(false);
// 轮询每个商品类型,获取对应的商品列表
for (ProductTypeCapability capability : capabilities) {
String productType = capability.getProductType();
List<SimpleGoodsRespVO> typeGoodsList = listSimpleGoodsByScenic(scenicId, productType);
goodsList.addAll(typeGoodsList);
}
return goodsList;
}
@@ -202,7 +72,7 @@ public class PriceBiz {
String[] goodsIds = priceConfig.getGoodsIds().split(",");
return goodsList.stream().filter(goods -> {
for (String goodsId : goodsIds) {
if (Strings.CS.equals(goods.getGoodsId().toString(), goodsId)) {
if (StringUtils.equals(goods.getGoodsId().toString(), goodsId)) {
return true;
}
}
@@ -210,7 +80,7 @@ public class PriceBiz {
}).collect(Collectors.toList());
}
public IsBuyBatchRespVO isOnePriceBuy(Long userId, Long faceId, Long scenicId, Integer type, String goodsIds) {
public IsBuyBatchRespVO isBuy(Long userId, Long faceId, Long scenicId, Integer type, String goodsIds) {
IsBuyBatchRespVO respVO = new IsBuyBatchRespVO();
PriceConfigEntity priceConfig = priceRepository.getPriceConfigByScenicTypeGoods(scenicId, type, goodsIds);
if (priceConfig == null) {
@@ -222,7 +92,7 @@ public class PriceBiz {
}
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null) {
if (Boolean.TRUE.equals(scenicConfig.getAllFree())) {
if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) {
// 景区全免
respVO.setFree(true);
respVO.setSlashPrice(BigDecimal.ZERO);
@@ -262,62 +132,12 @@ public class PriceBiz {
// 查询用户是否有此类订单
respVO.setBuy(false);
if (userId != null) {
OrderEntity orderEntity = orderBiz.hasTypeOrder(userId, faceId, scenicId, type, priceConfig.getId());
OrderEntity orderEntity = orderBiz.hasTypeOrder(userId, scenicId, type, priceConfig.getId());
if (orderEntity != null) {
respVO.setOrderId(orderEntity.getId());
respVO.setBuy(Integer.valueOf(1).equals(orderEntity.getStatus()));
}
}
if (type == -1 && !respVO.isBuy()) {
// 直接查询用户购买状态,避免调用faceContentList造成循环调用
boolean allContentsPurchased = true;
// 检查视频模板购买状态
List<TemplateRespVO> templateList = templateRepository.getTemplateListByScenicId(scenicId);
for (TemplateRespVO template : templateList) {
// 使用OrderRepository直接检查是否购买了该模板下的内容
List<MemberVideoEntity> videoEntities = memberRelationRepository.listRelationByFaceAndTemplate(faceId, template.getId());
if (videoEntities == null || videoEntities.isEmpty()) {
allContentsPurchased = false;
break;
}
boolean hasPurchasedTemplate = orderBiz.checkUserBuyFaceItem(userId, faceId, -1, videoEntities.getFirst().getVideoId());
if (!hasPurchasedTemplate) {
allContentsPurchased = false;
break;
}
}
// 检查源文件购买状态(录像集和照片集)
if (allContentsPurchased) {
if (scenicConfig != null) {
// 检查录像集
if (!Boolean.TRUE.equals(scenicConfig.getDisableSourceVideo())) {
boolean hasPurchasedRecording = orderBiz.checkUserBuyFaceItem(userId, faceId, 1, faceId);
if (!hasPurchasedRecording) {
allContentsPurchased = false;
}
}
// 检查照片集
if (allContentsPurchased && !Boolean.TRUE.equals(scenicConfig.getDisableSourceImage())) {
boolean hasPurchasedPhoto = orderBiz.checkUserBuyFaceItem(userId, faceId, 2, faceId);
if (!hasPurchasedPhoto) {
allContentsPurchased = false;
}
}
}
}
// 如果所有内容都已购买,则认为已购买套餐
if (allContentsPurchased) {
respVO.setBuy(true);
}
}
respVO.setShare(false);
if (face == null || !face.getMemberId().equals(userId)) {
respVO.setShare(true);
}
return respVO;
}
}

View File

@@ -14,11 +14,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -32,6 +29,8 @@ public class TemplateBiz {
private FaceRepository faceRepository;
@Autowired
private SourceMapper sourceMapper;
@Autowired
private SourceRepository sourceRepository;
public boolean determineTemplateCanGenerate(Long templateId, Long faceId) {
return determineTemplateCanGenerate(templateId, faceId, true);
@@ -54,7 +53,6 @@ public class TemplateBiz {
if (scanSource) {
List<SourceEntity> sourceEntities = sourceMapper.listVideoByScenicFaceRelation(face.getScenicId(), faceId);
if (sourceEntities == null || sourceEntities.isEmpty()) {
log.info("faceId:{} has no source", faceId);
return false;
}
count = sourceEntities.stream()
@@ -66,7 +64,6 @@ public class TemplateBiz {
} else {
List<FaceSampleEntity> faceSampleList = faceRepository.getFaceSampleList(faceId);
if (faceSampleList == null || faceSampleList.isEmpty()) {
log.info("faceId:{} has no faceSample", faceId);
return false;
}
count = faceSampleList.stream()
@@ -94,8 +91,8 @@ public class TemplateBiz {
}
if (minimalPlaceholderFill == null) {
// 未开启
log.info("模板:{},未配置最小自动生成功能,默认生成", templateId);
minimalPlaceholderFill = 1;
log.info("模板:{},未配置最小自动生成功能,默认生成", templateId);
return false;
}
if (minimalPlaceholderFill <= 0) {
return true;
@@ -124,97 +121,4 @@ public class TemplateBiz {
return count >= minimalPlaceholderFill;
}
public Map<String, List<SourceEntity>> filterTaskParams(Long templateId, Map<String, List<SourceEntity>> allTaskParams) {
if (allTaskParams == null || allTaskParams.isEmpty()) {
return Map.of();
}
List<String> templatePlaceholders = templateRepository.getTemplatePlaceholder(templateId);
if (templatePlaceholders == null || templatePlaceholders.isEmpty()) {
log.info("filterTaskParams: templateId:{} has no placeholders", templateId);
return Map.of();
}
TemplateConfigEntity templateConfig = templateRepository.getTemplateConfig(templateId);
// 统计每个 placeholder 在模板中出现的次数
Map<String, Long> placeholderCounts = templatePlaceholders.stream()
.collect(Collectors.groupingBy(
placeholder -> placeholder,
Collectors.counting()
));
Map<String, List<SourceEntity>> filteredParams = new HashMap<>();
// 判断是否允许片段重复
boolean allowDuplicate = templateConfig != null && Integer.valueOf(1).equals(templateConfig.getDuplicateEnable());
for (Map.Entry<String, Long> entry : placeholderCounts.entrySet()) {
String placeholder = entry.getKey();
Long requiredCount = entry.getValue();
if (placeholder.startsWith("P")) {
// 图片源:占位符格式为 "P{deviceId}"
String imageKey = placeholder;
if (allTaskParams.containsKey(imageKey)) {
List<SourceEntity> allSources = allTaskParams.get(imageKey);
List<SourceEntity> selectedSources = selectSources(allSources, requiredCount.intValue(), allowDuplicate);
if (!selectedSources.isEmpty()) {
filteredParams.put(imageKey, selectedSources);
}
}
} else {
// 视频源:占位符直接对应设备ID
String videoKey = placeholder;
if (allTaskParams.containsKey(videoKey)) {
List<SourceEntity> allSources = allTaskParams.get(videoKey);
List<SourceEntity> selectedSources = selectSources(allSources, requiredCount.intValue(), allowDuplicate);
if (!selectedSources.isEmpty()) {
filteredParams.put(videoKey, selectedSources);
}
}
}
}
log.debug("filterTaskParams: templateId:{}, original keys:{}, filtered keys:{}, placeholder counts:{}, allowDuplicate:{}",
templateId, allTaskParams.keySet().size(), filteredParams.keySet().size(), placeholderCounts, allowDuplicate);
return filteredParams;
}
private List<SourceEntity> selectSources(List<SourceEntity> allSources, int requiredCount, boolean allowDuplicate) {
if (allSources == null || allSources.isEmpty()) {
return new ArrayList<>();
}
if (!allowDuplicate) {
// 不允许重复,使用原有逻辑
int actualCount = Math.min(requiredCount, allSources.size());
return new ArrayList<>(allSources.subList(0, actualCount));
}
// 允许重复,循环填充到所需数量
List<SourceEntity> selectedSources = new ArrayList<>();
int sourceIndex = 0;
for (int i = 0; i < requiredCount; i++) {
selectedSources.add(allSources.get(sourceIndex));
sourceIndex = (sourceIndex + 1) % allSources.size();
}
return selectedSources;
}
public Long findFirstAvailableTemplate(List<Long> templateIds, Long faceId, boolean scanSource) {
if (templateIds == null || templateIds.isEmpty() || faceId == null) {
return null;
}
for (Long templateId : templateIds) {
if (determineTemplateCanGenerate(templateId, faceId, scanSource)) {
return templateId;
}
}
return null;
}
}

View File

@@ -1,26 +1,34 @@
package com.ycwl.basic.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* @author wenshijia
* @date 2021年07月05日 18:34
* 修改redis缓存序列化器
*/
@Configuration
public class CustomRedisCacheManager {
@Autowired
private ObjectMapper objectMapper;
@EnableCaching
public class CustomRedisCacheManager extends CachingConfigurerSupport {
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).entryTtl(Duration.ofMinutes(1));
return configuration;
}
/**
* 处理redis连接工具显示redis key值显示乱码问题,value值没处理
@@ -37,23 +45,10 @@ public class CustomRedisCacheManager {
final StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// Configure Jackson2JsonRedisSerializer with JavaTimeModule for value serialization
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
// Configure type handling to prevent ClassCastException
PolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator.builder()
.allowIfBaseType(Object.class)
.build();
objectMapper.activateDefaultTyping(typeValidator, ObjectMapper.DefaultTyping.NON_FINAL);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}

View File

@@ -1,16 +1,10 @@
package com.ycwl.basic.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class JacksonConfiguration {
@@ -19,16 +13,6 @@ public class JacksonConfiguration {
return builder -> {
// 把 Long 类型序列化为 String
builder.serializerByType(Long.class, ToStringSerializer.instance);
// 添加 JavaTimeModule 以支持 Java 8 时间类型
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
builder.deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
};
}
@Bean
public JavaTimeModule javaTimeModule() {
return new JavaTimeModule();
}
}

View File

@@ -1,109 +0,0 @@
package com.ycwl.basic.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.*;
import org.springframework.kafka.listener.ContainerProperties;
import java.util.HashMap;
import java.util.Map;
@Configuration
@ConditionalOnProperty(name = "kafka.enabled", havingValue = "true", matchIfMissing = false)
public class KafkaConfig {
@Value("${kafka.bootstrap-servers:100.64.0.12:39092}")
private String bootstrapServers;
@Value("${kafka.consumer.group-id:liuying-microservice}")
private String consumerGroupId;
@Value("${kafka.consumer.auto-offset-reset:earliest}")
private String autoOffsetReset;
@Value("${kafka.producer.acks:all}")
private String acks;
@Value("${kafka.producer.retries:3}")
private Integer retries;
@Value("${kafka.producer.batch-size:16384}")
private Integer batchSize;
@Value("${kafka.producer.linger-ms:1}")
private Integer lingerMs;
@Value("${kafka.producer.buffer-memory:33554432}")
private Integer bufferMemory;
@Value("${kafka.producer.enable-idempotence:true}")
private boolean enableIdempotence;
@Value("${kafka.producer.compression-type:snappy}")
private String compressionType;
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.ACKS_CONFIG, acks);
configProps.put(ProducerConfig.RETRIES_CONFIG, retries);
configProps.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
configProps.put(ProducerConfig.LINGER_MS_CONFIG, lingerMs);
configProps.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
configProps.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, enableIdempotence);
configProps.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, compressionType);
return new DefaultKafkaProducerFactory<>(configProps);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
@Bean
public ConsumerFactory<String, String> consumerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroupId);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
return new DefaultKafkaConsumerFactory<>(props);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
return factory;
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> manualCommitKafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroupId);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(props));
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
return factory;
}
}

View File

@@ -3,7 +3,6 @@ package com.ycwl.basic.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -12,15 +11,6 @@ import org.springframework.context.annotation.Configuration;
* @date 2021年06月04日 9:42
*/
@Configuration
@MapperScan(basePackages = {
"com.ycwl.basic.mapper",
"com.ycwl.basic.order.mapper",
"com.ycwl.basic.pricing.mapper",
"com.ycwl.basic.product.mapper",
"com.ycwl.basic.profitsharing.mapper",
"com.ycwl.basic.puzzle.mapper",
"com.ycwl.basic.stats.mapper"
})
public class MybatisPlusPageConfig {
/* 旧版本配置

View File

@@ -0,0 +1,74 @@
package com.ycwl.basic.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger 配置类
* 原生: /swagger-ui.html
* 美化: /doc.html
*/
@Configuration
@EnableSwagger2WebMvc
@Profile({"test"})
public class SwaggerConfig {
/**
* Swagger 实例 Bean 是 Docket, 所以通过配置 Docket 实例来配置 Swagger
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
// 展示在 Swagger 页面上的自定义工程描述信息
.apiInfo(apiInfo())
// 选择展示哪些接口
.select()
//只有com.zcy.e.firstaid包内的才去展示
.apis(RequestHandlerSelectors.basePackage("com.ycwl.basic.controller.mobile"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(getGlobalRequestParameters());
}
/**
* Swagger 的描述信息
*/
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("liuyin-re")
.description("流影重构")
.contact(new Contact("ycwl", "www.xxx.com", "xxxxxxxxx.com"))
.version("1.0")
.build();
}
private List<Parameter> getGlobalRequestParameters() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(new ParameterBuilder()
.name("token")
.description("登录令牌")
.parameterType("header")
.modelRef(new ModelRef("String"))
.required(true)
.build());
return parameters;
}
}

View File

@@ -1,32 +0,0 @@
package com.ycwl.basic.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 视频更新检查配置
* @author Claude
*/
@Data
@Component
@ConfigurationProperties(prefix = "video.update")
public class VideoUpdateConfig {
/**
* 是否将片段变化检测为新增
* true: 任何变化都视为新增
* false: 只有数量增加才视为新增
*/
private boolean detectChangesAsNew = true;
/**
* 最小新增片段数量才认为可更新
*/
private int minNewSegmentCount = 1;
/**
* 是否启用视频更新检查功能
*/
private boolean enabled = true;
}

View File

@@ -1,71 +0,0 @@
package com.ycwl.basic.constant;
/**
* 购买状态枚举
* 定义源文件的已购买和未购买两种状态
*
* @author Claude
* @since 2025-10-31
*/
public enum BuyStatus {
/**
* 未购买状态
*/
NOT_BOUGHT(0, "未购买"),
/**
* 已购买状态
*/
BOUGHT(1, "已购买");
private final int code;
private final String description;
BuyStatus(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据代码值获取枚举
*
* @param code 状态代码
* @return 对应的枚举值,如果不存在返回 null
*/
public static BuyStatus fromCode(int code) {
for (BuyStatus status : values()) {
if (status.code == code) {
return status;
}
}
return null;
}
/**
* 判断给定的代码是否为已购买状态
*
* @param code 状态代码
* @return true-已购买,false-未购买
*/
public static boolean isBought(Integer code) {
return code != null && code == BOUGHT.code;
}
/**
* 判断给定的代码是否为未购买状态
*
* @param code 状态代码
* @return true-未购买,false-已购买
*/
public static boolean isNotBought(Integer code) {
return code != null && code == NOT_BOUGHT.code;
}
}

View File

@@ -4,7 +4,4 @@ public class FaceConstant {
public static final String FACE_DB_NAME_PFX="face:db:";
public static final String USER_FACE_DB_NAME="userFace";
public static final String FACE_USER_URL_PFX="face:user:url:";
public static final String FACE_RECOGNITION_COUNT_PFX="face:recognition:count:";
public static final String FACE_CUSTOM_MATCH_COUNT_PFX="face:custom:match:count:";
public static final String FACE_LOW_THRESHOLD_PFX="face:low:threshold:";
}

View File

@@ -1,71 +0,0 @@
package com.ycwl.basic.constant;
/**
* 免费状态枚举
* 定义源文件的收费和免费两种状态
*
* @author Claude
* @since 2025-10-31
*/
public enum FreeStatus {
/**
* 收费状态
*/
PAID(0, "收费"),
/**
* 免费状态
*/
FREE(1, "免费");
private final int code;
private final String description;
FreeStatus(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据代码值获取枚举
*
* @param code 状态代码
* @return 对应的枚举值,如果不存在返回 null
*/
public static FreeStatus fromCode(int code) {
for (FreeStatus status : values()) {
if (status.code == code) {
return status;
}
}
return null;
}
/**
* 判断给定的代码是否为免费状态
*
* @param code 状态代码
* @return true-免费,false-收费
*/
public static boolean isFree(Integer code) {
return code != null && code == FREE.code;
}
/**
* 判断给定的代码是否为收费状态
*
* @param code 状态代码
* @return true-收费,false-免费
*/
public static boolean isPaid(Integer code) {
return code != null && code == PAID.code;
}
}

View File

@@ -0,0 +1,18 @@
package com.ycwl.basic.constant;
public class ShareParkingSpaceRedisKeyConstant {
// 更改数量时候的锁
public final static String UPDATE_NUMBER_LOCK_KEY="ShareParking:updateNumberLockKey";
// 地上车位
public final static String GROUND_PARKING_SPACE_NUMBER="ShareParking:groundParkingSpaceNumber";
// 地下车位数
public final static String UNDERGROUND_PARKING_SPACE_NUMBER="ShareParking:undergroundParkingSpaceNumber";
// 每日开放预约时间
public final static String OPEN_TIME="ShareParking:openTime";
// 预约后当日车辆最晚停留时间
public final static String RESIDENCE_TIME="ShareParking:residenceTime";
//取消时间
public final static String CANCEL_TIME="ShareParking:cancelTime";
//支付时间
public final static String PAY_TIME="ShareParking:payTime";
}

View File

@@ -1,86 +0,0 @@
package com.ycwl.basic.constant;
/**
* 源文件类型枚举
* 定义视频和图片两种源文件类型
*
* @author Claude
* @since 2025-10-31
*/
public enum SourceType {
/**
* 视频类型
*/
VIDEO(1, "视频"),
/**
* 图片类型
*/
IMAGE(2, "图片"),
/**
* AI微单类型
*/
AI_CAM(3, "AI微单");
private final int code;
private final String description;
SourceType(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据代码值获取枚举
*
* @param code 类型代码
* @return 对应的枚举值,如果不存在返回 null
*/
public static SourceType fromCode(int code) {
for (SourceType type : values()) {
if (type.code == code) {
return type;
}
}
return null;
}
/**
* 判断给定的代码是否为视频类型
*
* @param code 类型代码
* @return true-是视频,false-不是视频
*/
public static boolean isVideo(Integer code) {
return code != null && code == VIDEO.code;
}
/**
* 判断给定的代码是否为图片类型
*
* @param code 类型代码
* @return true-是图片,false-不是图片
*/
public static boolean isImage(Integer code) {
return code != null && code == IMAGE.code;
}
/**
* 判断给定的代码是否为AI微单类型
*
* @param code 类型代码
* @return true-是AI微单,false-不是AI微单
*/
public static boolean isAiCam(Integer code) {
return code != null && code == AI_CAM.code;
}
}

View File

@@ -3,7 +3,8 @@ package com.ycwl.basic.constant;
public class StorageConstant {
public static final String VLOG_PATH = "vlog";
public static final String VIDEO_PIECE_PATH = "source_video";
public static final String PHOTO_PATH = "viid";
public static final String PHOTO_PATH = "source_photo";
public static final String PHOTO_WATERMARKED_PATH = "photo_w";
public static final String VIID_FACE = "viid_face";
public static final String USER_FACE = "user_face";
}

View File

@@ -5,6 +5,8 @@ import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.enums.BizCodeEnum;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -24,10 +26,10 @@ import java.util.UUID;
@RestController
@RequestMapping("/api/file/v1")
@Slf4j
// 文件接口
@Api(tags = "文件接口")
public class FileController {
// 上传文件
@ApiOperation(value = "上传文件")
@PostMapping("/upload")
@IgnoreToken
public ApiResponse<?> upload(@RequestParam(value = "file") MultipartFile file) throws IOException {
@@ -37,7 +39,7 @@ public class FileController {
return ApiResponse.success(url);
}
// 删除文件
@ApiOperation(value = "删除文件")
@PostMapping("/delete")
@IgnoreToken
public ApiResponse<?> delete(@RequestParam(value = "fileName") String fileName) throws IOException {

View File

@@ -1,94 +0,0 @@
package com.ycwl.basic.controller;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewAddReqDTO;
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewListReqDTO;
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewRespDTO;
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewStatisticsRespDTO;
import com.ycwl.basic.service.VideoReviewService;
import com.ycwl.basic.utils.ApiResponse;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.net.URLEncoder;
/**
* 视频评价Controller
* 管理端使用,通过token角色控制权限
*/
@Slf4j
@RestController
@RequestMapping("/api/video-review/v1")
public class VideoReviewController {
@Autowired
private VideoReviewService videoReviewService;
/**
* 新增视频评价
*
* @param reqDTO 评价信息
* @return 评价ID
*/
@PostMapping("/add")
public ApiResponse<Long> addReview(@RequestBody VideoReviewAddReqDTO reqDTO) {
log.info("新增视频评价,videoId: {}", reqDTO.getVideoId());
Long reviewId = videoReviewService.addReview(reqDTO);
return ApiResponse.success(reviewId);
}
/**
* 分页查询评价列表
*
* @param reqDTO 查询条件
* @return 分页结果
*/
@GetMapping("/list")
public ApiResponse<PageInfo<VideoReviewRespDTO>> getReviewList(VideoReviewListReqDTO reqDTO) {
log.info("查询视频评价列表,pageNum: {}, pageSize: {}", reqDTO.getPageNum(), reqDTO.getPageSize());
PageInfo<VideoReviewRespDTO> pageInfo = videoReviewService.getReviewList(reqDTO);
return ApiResponse.success(pageInfo);
}
/**
* 获取评价统计数据
*
* @return 统计结果
*/
@GetMapping("/statistics")
public ApiResponse<VideoReviewStatisticsRespDTO> getStatistics() {
log.info("获取视频评价统计数据");
VideoReviewStatisticsRespDTO statistics = videoReviewService.getStatistics();
return ApiResponse.success(statistics);
}
/**
* 导出评价数据到Excel
*
* @param reqDTO 查询条件
* @param response HTTP响应
*/
@GetMapping("/export")
public void exportReviews(VideoReviewListReqDTO reqDTO, HttpServletResponse response) {
log.info("导出视频评价数据");
try {
// 设置响应头
String fileName = "video_reviews_" + System.currentTimeMillis() + ".xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 导出数据
videoReviewService.exportReviews(reqDTO, response.getOutputStream());
response.getOutputStream().flush();
} catch (IOException e) {
log.error("导出视频评价数据失败", e);
throw new RuntimeException("导出失败: " + e.getMessage());
}
}
}

View File

@@ -1,262 +0,0 @@
package com.ycwl.basic.controller.extern;
import cn.hutool.http.HttpUtil;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.image.enhancer.adapter.BceImageEnhancer;
import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig;
import com.ycwl.basic.image.pipeline.core.PhotoProcessContext;
import com.ycwl.basic.image.pipeline.stages.DownloadStage;
import com.ycwl.basic.image.pipeline.stages.ImageEnhanceStage;
import com.ycwl.basic.image.pipeline.stages.ImageSRStage;
import com.ycwl.basic.image.pipeline.stages.SourcePhotoUpdateStage;
import com.ycwl.basic.image.pipeline.stages.CleanupStage;
import com.ycwl.basic.pipeline.core.Pipeline;
import com.ycwl.basic.pipeline.core.PipelineBuilder;
import com.ycwl.basic.mapper.AioDeviceMapper;
import com.ycwl.basic.mapper.MemberMapper;
import com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity;
import com.ycwl.basic.model.aio.entity.AioDeviceEntity;
import com.ycwl.basic.model.aio.entity.AioDevicePriceConfigEntity;
import com.ycwl.basic.model.aio.req.AioDeviceCreateOrderReq;
import com.ycwl.basic.model.aio.resp.AioDeviceCreateOrderResp;
import com.ycwl.basic.model.aio.resp.AioDeviceInfoResp;
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.goods.GoodsDetailVO;
import com.ycwl.basic.model.mobile.goods.GoodsReqQuery;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.member.entity.MemberEntity;
import com.ycwl.basic.pay.entity.PayResponse;
import com.ycwl.basic.service.aio.AioDeviceService;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.service.pc.SourceService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.SnowFlakeUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@IgnoreToken
@RestController
@RequestMapping("/api/aio")
public class AioDeviceController {
@Autowired
private GoodsService goodsService;
@Autowired
private FaceService faceService;
@Autowired
private MemberMapper memberMapper;
@Autowired
private AioDeviceMapper aioDeviceMapper;
@Autowired
private AioDeviceService aioDeviceService;
@Autowired
private OrderService orderService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private SourceService sourceService;
@GetMapping("/info")
public ApiResponse<AioDeviceInfoResp> getDeviceInfo(HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
List<AioDeviceBannerEntity> banners = aioDeviceMapper.getBannerByDeviceId(aioDevice.getId());
return ApiResponse.success(new AioDeviceInfoResp(aioDevice, banners));
}
@GetMapping("/banners")
public ApiResponse<List<AioDeviceBannerEntity>> getBanners(HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
List<AioDeviceBannerEntity> banners = aioDeviceMapper.getBannerByDeviceId(aioDevice.getId());
return ApiResponse.success(banners);
}
@GetMapping("/config")
public ApiResponse<AioDevicePriceConfigEntity> getPriceConfig(HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
AioDevicePriceConfigEntity config = aioDeviceMapper.getPriceConfigByDeviceId(aioDevice.getId());
return ApiResponse.success(config);
}
@PostMapping("/faceUpload")
public ApiResponse<FaceRecognizeResp> faceUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
MemberEntity memberEntity = new MemberEntity();
memberEntity.setScenicId(aioDevice.getScenicId());
memberEntity.setCreateDate(new Date());
memberEntity.setId(SnowFlakeUtil.getLongId());
memberEntity.setNickname("用户");
memberMapper.add(memberEntity);
FaceRecognizeResp resp = faceService.faceUpload(file, aioDevice.getScenicId(), memberEntity.getId(), "");
// 尝试超分
new Thread(() -> {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
return;
}
GoodsReqQuery query = new GoodsReqQuery();
query.setSourceType(2);
query.setFaceId(resp.getFaceId());
List<GoodsDetailVO> sourcePhotoList = goodsService.sourceGoodsList(query);
if (sourcePhotoList == null || sourcePhotoList.isEmpty()) {
log.info("无源图片");
redisTemplate.opsForValue().set("aio:faceId:"+resp.getFaceId().toString()+":pass", "1", 1, TimeUnit.DAYS);
return;
}
log.info("超分开始!共{}张图片待处理", sourcePhotoList.size());
sourcePhotoList.forEach(photo -> {
if (StringUtils.contains(photo.getUrl(), "_q_")) {
log.debug("跳过已增强的图片: {}", photo.getUrl());
return;
}
try {
// 创建超分Pipeline
Pipeline<PhotoProcessContext> superResolutionPipeline = createSuperResolutionPipeline(photo.getGoodsId());
// 使用静态工厂方法创建Context
PhotoProcessContext context = PhotoProcessContext.forSuperResolution(
photo.getGoodsId(), photo.getUrl(), photo.getScenicId()
);
// 启用图像增强和超分的Stage
context.enableStage("image_enhance");
context.enableStage("image_sr");
// 执行Pipeline
boolean success = superResolutionPipeline.execute(context);
if (success) {
log.info("超分成功: {} -> {}", photo.getUrl(), context.getResultUrl());
} else {
log.error("超分失败: {}", photo.getGoodsId());
}
} catch (Exception e) {
log.error("超分失败:{}", photo.getGoodsId(), e);
}
});
redisTemplate.opsForValue().set("aio:faceId:"+sourcePhotoList.getFirst().getFaceId().toString()+":pass", "1", 1, TimeUnit.DAYS);
}).start();
return ApiResponse.success(resp);
}
// 人脸信息
@GetMapping("/{faceId}")
public ApiResponse<FaceRespVO> faceInfo(@PathVariable Long faceId) {
return faceService.getById(faceId);
}
@GetMapping("/face/{faceId}/check")
public ApiResponse<Boolean> faceCheck(@PathVariable Long faceId) {
if (redisTemplate.hasKey("aio:faceId:"+faceId.toString()+":pass")) {
return ApiResponse.success(true);
} else {
return ApiResponse.success(false);
}
}
@GetMapping("/face/{faceId}/bindWxaCode")
public ApiResponse<String> bindWxaCode(@PathVariable Long faceId) {
return ApiResponse.success(faceService.bindWxaCode(faceId));
}
// 照片商品列表
@GetMapping("/{faceId}/photo")
public ApiResponse<List<GoodsDetailVO>> sourceGoodsList(@PathVariable Long faceId) {
GoodsReqQuery query = new GoodsReqQuery();
query.setSourceType(2);
query.setFaceId(faceId);
List<GoodsDetailVO> goodsDetailVOS = goodsService.sourceGoodsList(query);
return ApiResponse.success(goodsDetailVOS);
}
// 创建订单
@PostMapping("/order")
public ApiResponse<AioDeviceCreateOrderResp> createOrder(HttpServletRequest request, @RequestBody AioDeviceCreateOrderReq req) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
return ApiResponse.success(aioDeviceService.createOrder(aioDevice, req));
}
// 查询订单
@GetMapping("/order/{orderId}")
public ApiResponse<PayResponse> queryOrder(HttpServletRequest request, @PathVariable("orderId") Long orderId) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
return ApiResponse.success(orderService.queryOrder(orderId));
}
/**
* 创建源图片超分辨率增强Pipeline
*
* @param sourceId 源图片ID
* @return 超分Pipeline
*/
private Pipeline<PhotoProcessContext> createSuperResolutionPipeline(Long sourceId) {
// 创建带有百度云配置的ImageEnhanceStage
BceEnhancerConfig config = new BceEnhancerConfig();
config.setQps(1);
config.setAppId("119554288");
config.setApiKey("OX6QoijgKio3eVtA0PiUVf7f");
config.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk");
return new PipelineBuilder<PhotoProcessContext>("SourcePhotoSuperResolutionPipeline")
.addStage(new DownloadStage()) // 1. 下载图片
.addStage(new ImageEnhanceStage(config)).addStage(new ImageSRStage(config)) // 2. 图像增强(超分)
.addStage(new SourcePhotoUpdateStage(sourceService, sourceId)) // 3. 上传并更新数据库
.addStage(new CleanupStage()) // 4. 清理临时文件
.build();
}
private BceImageEnhancer getEnhancer() {
BceImageEnhancer enhancer = new BceImageEnhancer();
BceEnhancerConfig config = new BceEnhancerConfig();
config.setQps(1);
config.setAppId("119554288");
config.setApiKey("OX6QoijgKio3eVtA0PiUVf7f");
config.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk");
enhancer.setConfig(config);
return enhancer;
}
}

View File

@@ -1,83 +0,0 @@
package com.ycwl.basic.controller.extern;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.custom.req.AliyunCallbackReq;
import com.ycwl.basic.model.custom.req.CreateUploadTaskReq;
import com.ycwl.basic.model.custom.req.UploadCompleteReq;
import com.ycwl.basic.model.custom.req.UploadFailedReq;
import com.ycwl.basic.model.custom.resp.CreateUploadTaskResp;
import com.ycwl.basic.service.custom.CustomUploadTaskService;
import com.ycwl.basic.utils.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/extern/custom-device")
@IgnoreToken
public class CustomDeviceController {
@Autowired
private CustomUploadTaskService customUploadTaskService;
@PostMapping("/upload/create")
public ApiResponse<CreateUploadTaskResp> createUploadTask(@RequestBody CreateUploadTaskReq req) {
try {
CreateUploadTaskResp resp = customUploadTaskService.createUploadTask(req);
return ApiResponse.success(resp);
} catch (Exception e) {
log.error("创建上传任务失败", e);
return ApiResponse.fail(e.getMessage());
}
}
@PostMapping("/upload/complete")
public ApiResponse<String> uploadComplete(@RequestBody UploadCompleteReq req) {
try {
customUploadTaskService.completeUpload(req.getAccessKey(), req.getTaskId());
return ApiResponse.success("上传完成,人脸识别任务已提交");
} catch (Exception e) {
log.error("上传完成处理失败", e);
return ApiResponse.fail(e.getMessage());
}
}
@PostMapping("/upload/failed")
public ApiResponse<String> uploadFailed(@RequestBody UploadFailedReq req) {
try {
customUploadTaskService.markTaskFailed(req.getAccessKey(), req.getTaskId(), req.getErrorMsg());
return ApiResponse.success("任务已标记为失败");
} catch (Exception e) {
log.error("标记任务失败处理异常", e);
return ApiResponse.fail(e.getMessage());
}
}
@PostMapping("/aliyun/mps/callback")
public ApiResponse<String> aliyunCallback(@RequestBody AliyunCallbackReq req) {
try {
customUploadTaskService.handleAliyunCallback(req.getJobId(), req.getStatus());
return ApiResponse.success("回调处理完成");
} catch (Exception e) {
log.error("阿里云回调处理失败", e);
return ApiResponse.fail(e.getMessage());
}
}
@GetMapping("/aliyun/mps/callback")
public ApiResponse<String> aliyunCallback(@RequestParam("jobId") String jobId, @RequestParam("status") String status) {
try {
customUploadTaskService.handleAliyunCallback(jobId, status);
return ApiResponse.success("回调处理完成");
} catch (Exception e) {
log.error("阿里云回调处理失败", e);
return ApiResponse.fail(e.getMessage());
}
}
}

View File

@@ -1,7 +1,7 @@
package com.ycwl.basic.controller.extern;
import cn.hutool.core.date.DateUtil;
import com.ycwl.basic.utils.JacksonUtil;
import com.alibaba.fastjson.JSON;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.mapper.MemberMapper;
@@ -14,10 +14,9 @@ import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.member.entity.MemberEntity;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
import com.ycwl.basic.repository.VideoRepository;
import com.ycwl.basic.repository.VideoTaskRepository;
import com.ycwl.basic.service.mobile.AppScenicService;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.service.task.impl.TaskTaskServiceImpl;
@@ -63,8 +62,6 @@ public class LyCompatibleController {
private TaskTaskServiceImpl taskTaskServiceImpl;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private VideoTaskRepository videoTaskRepository;
@PostMapping("sendPhoto")
@IgnoreToken
@@ -87,7 +84,7 @@ public class LyCompatibleController {
if (StringUtils.isBlank(scid)) {
return R.error("景区ID为空!");
}
// if (Strings.CS.equals("288",scid)) {
// if (StringUtils.equals("288",scid)) {
scenicId = 3955650120997015552L;
// } else {
// scenicId = 3946669713328836608L;
@@ -113,7 +110,7 @@ public class LyCompatibleController {
}
FaceRecognizeResp resp;
try {
resp = faceService.faceUpload(file, scenicId, member.getId(), "");
resp = faceService.faceUpload(file, scenicId, member.getId());
} catch (Exception e) {
return R.error("上传失败!报错:"+e.getMessage());
}
@@ -183,7 +180,7 @@ public class LyCompatibleController {
}
String openId = headersMap.get("client");
if (redisTemplate.hasKey("ly:"+openId)) {
return JacksonUtil.parseObject(redisTemplate.opsForValue().get("ly:"+openId), R.class);
return JSON.parseObject(redisTemplate.opsForValue().get("ly:"+openId), R.class);
}
MemberRespVO member = memberMapper.getByOpenId(openId);
if (member == null) {
@@ -201,7 +198,6 @@ public class LyCompatibleController {
R response = R.ok();
if (collect.get(0) == null) {
response.put("isgen", 1)
.put("face_id", faceVO.getId().toString())
.put("newvideo", Collections.emptyList())
.put("newuservideo", Collections.emptyList());
return response;
@@ -212,11 +208,6 @@ public class LyCompatibleController {
Map<String, Object> map = new HashMap<>();
VideoEntity videoRespVO = videoRepository.getVideo(contentPageVO.getContentId());
map.put("id", videoRespVO.getId().toString());
map.put("task_id", videoRespVO.getTaskId().toString());
TaskEntity task = videoTaskRepository.getTaskById(videoRespVO.getTaskId());
if (task != null) {
map.put("face_id", String.valueOf(task.getFaceId()));
}
map.put("template_cover_image", contentPageVO.getTemplateCoverUrl());
Date taskShotDate = taskTaskServiceImpl.getTaskShotDate(videoRespVO.getTaskId());
map.put("shoottime", DateUtil.format(taskShotDate, "yyyy-MM-dd HH:mm"));
@@ -233,7 +224,6 @@ public class LyCompatibleController {
List<Map<String, Object>> userVideoList = sourceGoodsList.stream().map(goodsDetailVO -> {
Map<String, Object> map = new HashMap<>();
map.put("id", goodsDetailVO.getGoodsId().toString());
map.put("face_id", String.valueOf(goodsDetailVO.getFaceId()));
map.put("openid", openId);
map.put("template_cover_image", goodsDetailVO.getUrl());
map.put("scenicname", goodsDetailVO.getScenicName());
@@ -243,11 +233,9 @@ public class LyCompatibleController {
}).collect(Collectors.toList());
response
.put("isgen", taskStatusVO.getStatus() == 1 ? 0 : 1)
.put("member_id", faceVO.getMemberId().toString())
.put("face_id", faceVO.getId().toString())
.put("newvideo", videoList)
.put("newuservideo", userVideoList);
redisTemplate.opsForValue().set("ly:"+openId, JacksonUtil.toJSONString(response), 5, TimeUnit.SECONDS);
redisTemplate.opsForValue().set("ly:"+openId, JSON.toJSONString(response), 5, TimeUnit.SECONDS);
log.info("> {}", response);
return response;
}

View File

@@ -1,81 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.goods.GoodsDetailVO;
import com.ycwl.basic.service.mobile.AppAiCamService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* AI相机相关接口
*/
@Slf4j
@RestController
@RequestMapping("/api/mobile/ai_cam/v1")
@RequiredArgsConstructor
public class AppAiCamController {
private final AppAiCamService appAiCamService;
/**
* 根据faceId获取AI相机识别到的商品列表
* @param faceId 人脸ID
* @return 商品详情列表
*/
@GetMapping("/{faceId}/content")
public ApiResponse<List<GoodsDetailVO>> getAiCamGoods(@PathVariable Long faceId) {
try {
List<GoodsDetailVO> goods = appAiCamService.getAiCamGoodsByFaceId(faceId);
return ApiResponse.success(goods);
} catch (Exception e) {
log.error("获取AI相机商品失败: faceId={}", faceId, e);
return ApiResponse.fail("获取商品列表失败");
}
}
/**
* 批量添加会员与source的关联关系
* @param faceId 人脸ID
* @param sourceIds source ID列表
* @return 添加结果
*/
@PostMapping("/{faceId}/relations")
public ApiResponse<String> addMemberSourceRelations(
@PathVariable Long faceId,
@RequestBody List<Long> sourceIds
) {
try {
int count = appAiCamService.addMemberSourceRelations(faceId, sourceIds);
return ApiResponse.success("成功添加" + count + "条关联记录");
} catch (IllegalArgumentException e) {
log.warn("添加关联失败: faceId={}, error={}", faceId, e.getMessage());
return ApiResponse.fail(e.getMessage());
} catch (Exception e) {
log.error("添加关联失败: faceId={}", faceId, e);
return ApiResponse.fail("添加关联失败");
}
}
/**
* 使用人脸样本创建或获取Face记录
* @param faceSampleId 人脸样本ID
* @return 人脸识别响应
*/
@GetMapping("/useSample/{faceSampleId}")
public ApiResponse<FaceRecognizeResp> useSample(@PathVariable Long faceSampleId) {
try {
JwtInfo worker = JwtTokenUtil.getWorker();
FaceRecognizeResp resp = appAiCamService.useSample(worker.getUserId(), faceSampleId);
return ApiResponse.success(resp);
} catch (Exception e) {
log.error("使用人脸样本失败: faceSampleId={}", faceSampleId, e);
return ApiResponse.fail("使用人脸样本失败: " + e.getMessage());
}
}
}

View File

@@ -1,98 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.chat.*;
import com.ycwl.basic.service.mobile.FaceChatService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import java.util.concurrent.CompletableFuture;
/**
* 小程序人脸智能聊天接口。
*/
@Slf4j
@RestController
@RequestMapping("/api/mobile/chat/v1")
@RequiredArgsConstructor
public class AppChatController {
private final FaceChatService faceChatService;
/**
* 获取或创建会话(同一人脸只保留一条)。
*/
@PostMapping("/faces/{faceId}/conversation")
public ApiResponse<ChatConversationVO> createConversation(@PathVariable Long faceId) {
JwtInfo worker = JwtTokenUtil.getWorker();
ChatConversationVO vo = faceChatService.getOrCreateConversation(faceId, worker.getUserId());
return ApiResponse.success(vo);
}
/**
* 同步发送消息,适用于短回复或前端自行轮询。
*/
@PostMapping("/conversations/{conversationId}/messages")
public ApiResponse<ChatSendMessageResp> sendMessage(@PathVariable Long conversationId,
@RequestBody ChatSendMessageReq req) {
JwtInfo worker = JwtTokenUtil.getWorker();
ChatSendMessageResp resp = faceChatService.sendMessage(conversationId, worker.getUserId(),
req.getContent(), req.getTraceId());
return ApiResponse.success(resp);
}
/**
* 流式返回,使用 HTTP chunked。小程序侧用 wx.request 的 onChunkReceived 消费。
*/
@PostMapping(value = "/conversations/{conversationId}/messages/stream", produces = "text/plain;charset=UTF-8")
public ResponseBodyEmitter streamMessage(@PathVariable Long conversationId,
@RequestBody ChatSendMessageReq req) {
JwtInfo worker = JwtTokenUtil.getWorker();
ResponseBodyEmitter emitter = new ResponseBodyEmitter(30_000L);
CompletableFuture.runAsync(() -> {
try {
faceChatService.sendMessageStream(
conversationId,
worker.getUserId(),
req.getContent(),
req.getTraceId(),
chunk -> {
try {
emitter.send(chunk, new MediaType("text", "plain", java.nio.charset.StandardCharsets.UTF_8));
} catch (Exception e) {
emitter.completeWithError(e);
}
});
emitter.complete();
} catch (Exception e) {
log.error("streamMessage error", e);
emitter.completeWithError(e);
}
});
return emitter;
}
/**
* 查询历史消息,cursor 为最后一条 seq,limit 为条数。
*/
@GetMapping("/conversations/{conversationId}/messages")
public ApiResponse<ChatMessagePageResp> listMessages(@PathVariable Long conversationId,
@RequestParam(value = "cursor", required = false) Integer cursor,
@RequestParam(value = "limit", required = false) Integer limit) {
JwtInfo worker = JwtTokenUtil.getWorker();
ChatMessagePageResp resp = faceChatService.listMessages(conversationId, cursor, limit, worker.getUserId());
return ApiResponse.success(resp);
}
@PostMapping("/conversations/{conversationId}/close")
public ApiResponse<String> closeConversation(@PathVariable Long conversationId) {
JwtInfo worker = JwtTokenUtil.getWorker();
faceChatService.closeConversation(conversationId, worker.getUserId());
return ApiResponse.success("OK");
}
}

View File

@@ -1,113 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.model.mobile.claim.ClaimReq;
import com.ycwl.basic.model.mobile.claim.ClaimResp;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.pricing.dto.CouponClaimRequest;
import com.ycwl.basic.pricing.dto.CouponClaimResult;
import com.ycwl.basic.pricing.dto.req.VoucherPrintReq;
import com.ycwl.basic.pricing.dto.resp.VoucherPrintResp;
import com.ycwl.basic.pricing.enums.CouponType;
import com.ycwl.basic.pricing.service.ICouponService;
import com.ycwl.basic.pricing.service.VoucherPrintService;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.utils.ApiResponse;
import lombok.AllArgsConstructor;
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.math.BigDecimal;
import java.math.RoundingMode;
@RestController
@RequestMapping("/api/mobile/claim/v1")
@AllArgsConstructor
public class AppClaimController {
private final FaceRepository faceRepository;
private final ScenicRepository scenicRepository;
private final VoucherPrintService voucherPrintService;
private final ICouponService couponService;
@PostMapping("tryClaim")
public ApiResponse<ClaimResp> tryClaim(@RequestBody ClaimReq req) {
FaceEntity face = faceRepository.getFace(req.getFaceId());
if (face == null) {
return ApiResponse.fail("请选择人脸");
}
ClaimResp claimResp = new ClaimResp();
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
if (Boolean.TRUE.equals(scenicConfig.getBoolean("voucher_enable"))) {
// 可以领券
VoucherPrintResp voucherPrintResp = voucherPrintService.queryPrintedVoucher(face.getId());
if (voucherPrintResp == null) {
// 打印
if (req.getMorphId() != null) {
voucherPrintResp = voucherPrintService.printVoucherTicket(new VoucherPrintReq(face.getId(), req.getMorphId(), face.getScenicId()));
}
}
if (voucherPrintResp != null) {
claimResp.setHasCoupon(false);
claimResp.setHasPrint(true);
claimResp.setPrintCode(voucherPrintResp.getCode());
claimResp.setPrintType(voucherPrintResp.getType());
return ApiResponse.success(claimResp);
}
}
if (Boolean.TRUE.equals(scenicConfig.getBoolean("booking_enable"))) {
VoucherPrintResp voucherPrintResp = voucherPrintService.queryPrintedVoucher(face.getId());
if (voucherPrintResp == null) {
// 打印
if (req.getMorphId() != null) {
voucherPrintResp = voucherPrintService.printBookingTicket(new VoucherPrintReq(face.getId(), req.getMorphId(), face.getScenicId()));
}
}
if (voucherPrintResp != null) {
claimResp.setHasCoupon(false);
claimResp.setHasPrint(true);
claimResp.setPrintCode(voucherPrintResp.getCode());
claimResp.setPrintType(voucherPrintResp.getType());
return ApiResponse.success(claimResp);
}
}
if (req.getType() != null) {
// 第几次进入
Integer couponId = scenicConfig.getInteger("coupon_id_for_type_" + req.getType());
if (couponId != null) {
// 可以领券
CouponClaimRequest request = new CouponClaimRequest(face.getMemberId(), Long.valueOf(couponId));
CouponClaimResult claimResult = couponService.claimCoupon(request);
if (claimResult.isSuccess()) {
// 领到了
claimResp.setHasCoupon(true);
switch (claimResult.getCoupon().getCouponType()) {
case CouponType.PERCENTAGE:
claimResp.setCouponType("折扣优惠券");
claimResp.setCouponDesc("" + (BigDecimal.valueOf(1).setScale(2, RoundingMode.HALF_UP).subtract(claimResult.getCoupon().getDiscountValue())).multiply(BigDecimal.valueOf(10)) + "");
break;
case CouponType.FIXED_AMOUNT:
if (claimResult.getCoupon().getMinAmount().compareTo(BigDecimal.ZERO) > 0) {
claimResp.setCouponType("满减优惠券");
claimResp.setCouponDesc("" + claimResult.getCoupon().getMinAmount() + "" + claimResult.getCoupon().getDiscountValue());
} else {
claimResp.setCouponType("直减优惠券");
claimResp.setCouponDesc("直减" + claimResult.getCoupon().getDiscountValue());
}
break;
default:
claimResp.setCouponType("普通优惠券");
break;
}
claimResp.setCouponDesc(scenicConfig.getString("coupon_desc_for_type_" + req.getType(), "专属折扣券"));
claimResp.setCouponCountdown(scenicConfig.getString("coupon_countdown_for_type_" + req.getType(), "送你优惠,保存美好!"));
return ApiResponse.success(claimResp);
}
}
}
return ApiResponse.fail("异常");
}
}

View File

@@ -1,47 +1,9 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.model.mobile.coupon.req.ClaimCouponReq;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
import com.ycwl.basic.model.pc.couponRecord.entity.CouponRecordEntity;
import com.ycwl.basic.service.mobile.AppCouponRecordService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/mobile/coupon/v1")
@RequestMapping("/api/mobile/controller/v1")
public class AppCouponController {
@Autowired
private AppCouponRecordService appCouponRecordService;
/**
* 根据memberId、faceId和type查找优惠券记录
*/
@GetMapping("/record")
public ApiResponse<CouponRecordEntity> getCouponRecords(
@RequestParam Long faceId,
@RequestParam Integer type) {
CouponRecordEntity record = appCouponRecordService.queryByMemberIdAndFaceIdAndType(Long.valueOf(BaseContextHandler.getUserId()), faceId, type);
return ApiResponse.success(record);
}
/**
* 领取优惠券
*/
@PostMapping("/claim")
public ApiResponse<CouponEntity> claimCoupon(@RequestBody ClaimCouponReq request) {
request.setMemberId(Long.valueOf(BaseContextHandler.getUserId()));
try {
CouponEntity coupon = appCouponRecordService.claimCoupon(
request.getMemberId(),
request.getFaceId(),
request.getType()
);
return ApiResponse.success(coupon);
} catch (RuntimeException e) {
return ApiResponse.fail(e.getMessage());
}
}
}

View File

@@ -1,19 +1,14 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.face.FaceStatusResp;
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
import com.ycwl.basic.model.mobile.face.FaceRecognitionUpdateReq;
import com.ycwl.basic.model.mobile.face.FaceRecognitionDetailVO;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -26,13 +21,12 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/mobile/face/v1")
// 用户人脸相关接口
public class AppFaceController {
@Api(tags = "用户人脸相关接口")
public class
AppFaceController {
@Autowired
private FaceService faceService;
@Autowired
private FaceRepository faceRepository;
/**
* 1、上传人脸照片
@@ -43,15 +37,13 @@ public class AppFaceController {
* @param scenicId
* @return
*/
// 人脸照片上传
@ApiOperation("人脸照片上传")
@PostMapping("/faceUPload")
public ApiResponse<FaceRecognizeResp> faceUpload(@RequestParam("file")MultipartFile file,
@RequestParam(value = "scene", defaultValue = "", required = false) String scene,
@RequestParam("scenicId") Long scenicId) {
public ApiResponse<FaceRecognizeResp> faceUpload(@RequestParam("file")MultipartFile file, @RequestParam("scenicId") Long scenicId) {
//获取用户id
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
FaceRecognizeResp resp = faceService.faceUpload(file, scenicId, userId, scene);
FaceRecognizeResp resp = faceService.faceUpload(file, scenicId, userId);
return ApiResponse.success(resp);
}
@@ -59,7 +51,7 @@ public class AppFaceController {
public ApiResponse<List<FaceRespVO>> list(@PathVariable("scenicId") String scenicId) {
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
List<FaceRespVO> list = faceService.listByUser(userId, Long.parseLong(scenicId));
List<FaceRespVO> list = faceService.listByUser(userId, scenicId);
return ApiResponse.success(list);
}
@@ -70,18 +62,6 @@ public class AppFaceController {
@DeleteMapping("/{faceId}")
public ApiResponse<String> deleteFace(@PathVariable("faceId") Long faceId) {
// 添加权限检查:验证当前用户是否拥有该 face
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
FaceEntity face = faceRepository.getFace(faceId);
if (face == null) {
throw new BaseException("人脸数据不存在");
}
if (!face.getMemberId().equals(userId)) {
throw new BaseException("无权删除此人脸");
}
return faceService.deleteFace(faceId);
}
@@ -92,55 +72,10 @@ public class AppFaceController {
}
// 景区视频源素材列表
@ApiOperation("景区视频源素材列表")
@GetMapping("/{faceId}/contentList")
public ApiResponse<List<ContentPageVO>> contentList(@PathVariable Long faceId) {
List<ContentPageVO> contentPageVOS = faceService.faceContentList(faceId);
return ApiResponse.success(contentPageVOS);
}
// 绑定人脸
@PostMapping("/{faceId}/bind")
public ApiResponse<String> bind(@PathVariable Long faceId) {
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
faceService.bindFace(faceId, userId);
return ApiResponse.success("OK");
}
@GetMapping("/{faceId}/status")
public ApiResponse<FaceStatusResp> status(@PathVariable Long faceId) {
return ApiResponse.success(faceService.getFaceStatus(faceId));
}
@GetMapping("/{faceId}/extraCheck")
public ApiResponse<Boolean> hasExtraCheck(@PathVariable Long faceId) {
return ApiResponse.success(faceService.checkHasExtraCheck(faceId));
}
@GetMapping("/{faceId}/queryOtherFace")
public ApiResponse<List<FaceSampleEntity>> queryOtherFace(@PathVariable Long faceId) {
return ApiResponse.success(faceService.getLowMatchedFaceSamples(faceId));
}
@PostMapping("/{faceId}/queryOtherFace")
public ApiResponse<String> queryOtherFace(@PathVariable Long faceId, @RequestBody List<Long> faceIds) {
faceService.matchCustomFaceId(faceId, faceIds);
return ApiResponse.success("OK");
}
@PutMapping("/{faceId}/recognition")
public ApiResponse<?> updateRecognition(@PathVariable Long faceId,
@RequestBody FaceRecognitionUpdateReq req) {
req.setFaceId(faceId);
faceService.updateRecognition(req);
return ApiResponse.success("OK");
}
@GetMapping("/{faceId}/recognition/detail")
public ApiResponse<FaceRecognitionDetailVO> recognitionDetail(@PathVariable Long faceId) {
return ApiResponse.success(faceService.getRecognitionDetail(faceId));
}
}

View File

@@ -4,11 +4,11 @@ import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.exception.CheckTokenException;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.goods.*;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -21,32 +21,26 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/mobile/goods/v1")
// 商品相关接口
@Api(tags = "商品相关接口")
public class AppGoodsController {
@Autowired
private GoodsService goodsService;
@Autowired
private FaceService faceService;
// 商品列表
@ApiOperation("商品列表")
@PostMapping("/goodsList")
public ApiResponse<List<GoodsPageVO>> goodsList(@RequestBody GoodsReqQuery query) {
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
List<FaceRespVO> faceRespVOS = faceService.listByUser(userId, query.getScenicId());
List<Long> faceIds = faceRespVOS.stream().map(FaceRespVO::getId).toList();
return goodsService.listGoodsByFaceIdList(faceIds, query.getIsBuy(), query.getScenicId());
return goodsService.goodsList(query);
}
// 源素材(原片/照片)商品列表
@ApiOperation("源素材(原片/照片)商品列表")
@PostMapping("/sourceGoodsList")
public ApiResponse<List<GoodsDetailVO>> sourceGoodsList(@RequestBody GoodsReqQuery query) {
List<GoodsDetailVO> goodsDetailVOS = goodsService.sourceGoodsList(query);
return ApiResponse.success(goodsDetailVOS);
}
// 源素材(原片/照片)商品数量
@ApiOperation("源素材(原片/照片)商品数量")
@PostMapping("/sourceGoodsCount")
public ApiResponse<Integer> sourceGoodsCount(@RequestBody GoodsReqQuery query) {
Integer count = goodsService.sourceGoodsCount(query);
@@ -66,7 +60,7 @@ public class AppGoodsController {
}
// 成片vlog商品详情
@ApiOperation("成片vlog商品详情")
@GetMapping("/getVideoGoodsDetail/{videoId}")
@IgnoreToken
public ApiResponse<VideoGoodsDetailVO> videoGoodsDetail(@PathVariable("videoId") Long videoId) {
@@ -88,7 +82,7 @@ public class AppGoodsController {
*
* @return 0没有任务 1 合成中 2 合成成功
*/
// 查询用户当前景区的整体视频合成任务状态 0没有任务 1 合成中 2 合成成功
@ApiOperation("查询用户当前景区的整体视频合成任务状态 0没有任务 1 合成中 2 合成成功 ")
@GetMapping("/getTaskStatus/")
public ApiResponse<VideoTaskStatusVO> getAllTaskStatus() {
JwtInfo worker = JwtTokenUtil.getWorker();
@@ -101,22 +95,10 @@ public class AppGoodsController {
* @param templateId 模版id
* @return 1 合成中 2 合成成功
*/
// 查询用户当前景区的具体模版视频合成任务状态 1 合成中 2 合成成功
@ApiOperation("查询用户当前景区的具体模版视频合成任务状态 1 合成中 2 合成成功 ")
@GetMapping("/task/face/{faceId}/template/{templateId}")
public ApiResponse<VideoTaskStatusVO> getTemplateTaskStatus(@PathVariable("faceId") Long faceId, @PathVariable("templateId") Long templateId) {
JwtInfo worker = JwtTokenUtil.getWorker();
return ApiResponse.success(goodsService.getTaskStatusByTemplateId(faceId, templateId));
}
/**
* 检查视频是否可更新
*
* @param videoId 视频ID
* @return 视频更新检查结果
*/
@GetMapping("/video/{videoId}/updateCheck")
public ApiResponse<VideoUpdateCheckVO> checkVideoUpdate(@PathVariable("videoId") Long videoId) {
VideoUpdateCheckVO result = goodsService.checkVideoUpdate(videoId);
return ApiResponse.success(result);
}
}

View File

@@ -6,6 +6,8 @@ import com.ycwl.basic.model.mobile.weChat.DTO.WeChatUserInfoUpdateDTO;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.service.mobile.AppMemberService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -16,7 +18,7 @@ import org.springframework.web.bind.annotation.*;
*/
@RestController
@RequestMapping("/api/mobile/member/v1")
// 用户相关接口
@Api(tags = "用户相关接口")
@Slf4j
public class AppMemberController {
@@ -30,29 +32,19 @@ public class AppMemberController {
* @return
* @throws Exception
*/
// 登录
@ApiOperation("登录")
@PostMapping("/{scenicId}/login")
@IgnoreToken
public ApiResponse<?> login(@PathVariable("scenicId") Long scenicId ,@RequestBody WeChatUserInfoDTO userInfoDTO) throws Exception {
return memberService.login(scenicId, userInfoDTO.getCode(), userInfoDTO);
}
@PostMapping("/undefined/login")
@IgnoreToken
public ApiResponse<?> loginUndefined(@RequestBody WeChatUserInfoDTO userInfoDTO) throws Exception {
return memberService.login(null, userInfoDTO.getCode(), userInfoDTO);
}
@PostMapping({"//login", "/login"})
@IgnoreToken
public ApiResponse<?> loginNoScenicId(@RequestBody WeChatUserInfoDTO userInfoDTO) throws Exception {
return memberService.login(null, userInfoDTO.getCode(), userInfoDTO);
}
/**
* 获取用户信息
*
* @return
*/
// 获取用户信息
@ApiOperation("获取用户信息")
@GetMapping("/getUserInfo")
public ApiResponse<MemberRespVO> getUserInfo() {
return memberService.getUserInfo();
@@ -64,19 +56,19 @@ public class AppMemberController {
* @param userInfoUpdateDTO
* @return
*/
// 修改用户信息
@ApiOperation("修改用户信息")
@PostMapping("/update")
public ApiResponse<?> update(@RequestBody WeChatUserInfoUpdateDTO userInfoUpdateDTO) {
return memberService.update(userInfoUpdateDTO);
}
// 新增或修改景区服务通知状态
@ApiOperation("新增或修改景区服务通知状态")
@GetMapping("/updateScenicServiceNoticeStatus")
public ApiResponse<String> updateScenicServiceNoticeStatus(Long scenicId) {
return memberService.updateScenicServiceNoticeStatus(scenicId);
}
// 查看景区服务通知状态 0关闭 1开启
@ApiOperation("查看景区服务通知状态 0关闭 1开启")
@GetMapping("/getScenicServiceNoticeStatus")
public ApiResponse<Integer> getScenicServiceNoticeStatus(Long scenicId) {
return memberService.getScenicServiceNoticeStatus(scenicId);

View File

@@ -1,7 +1,6 @@
package com.ycwl.basic.controller.mobile;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.biz.PriceBiz;
import com.ycwl.basic.constant.BaseContextHandler;
@@ -19,6 +18,8 @@ import com.ycwl.basic.pay.entity.PayResponse;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -30,7 +31,7 @@ import java.util.Map;
*/
@RestController
@RequestMapping("/api/mobile/order/v1")
// 订单相关接口
@Api(tags = "订单相关接口")
public class AppOrderController {
@Autowired
@@ -42,7 +43,7 @@ public class AppOrderController {
@Autowired
private FaceMapper faceMapper;
// 用户端订单列表查询
@ApiOperation("用户端订单列表查询")
@PostMapping("/page")
public ApiResponse<PageInfo<OrderAppRespVO>> pageQuery(@RequestBody OrderAppPageReq orderReqQuery) {
String userId = BaseContextHandler.getUserId();
@@ -50,27 +51,26 @@ public class AppOrderController {
return orderService.appPageQuery(orderReqQuery);
}
// 用户端订单详情查询
@ApiOperation("用户端订单详情查询")
@GetMapping("getOrderDetails/{id}")
@IgnoreToken
public ApiResponse<OrderAppRespVO> getOrderDetails(@PathVariable("id") Long id) {
return orderService.appDetail(id);
}
// 用户端订单新增
@ApiOperation("用户端订单新增")
@PostMapping("/addOrder")
public ApiResponse<Map<String, Object>> addOrder(@RequestBody CreateOrderReqVO orderAddReq) throws Exception {
JwtInfo worker = JwtTokenUtil.getWorker();
return orderService.createOrder(worker.getUserId(), orderAddReq);
}
// 查询订单
@ApiOperation("查询订单")
@GetMapping("/queryOrder")
public ApiResponse<PayResponse> queryOrder(@RequestParam("orderId") Long orderId) {
return ApiResponse.success(orderService.queryOrder(orderId));
}
// 用户端打包订单新增
@ApiOperation("用户端打包订单新增")
@PostMapping("/addBatchOrder")
public ApiResponse<Map<String, Object>> addOrder(@RequestBody CreateBatchOrderReqVO batchOrderReqVO) throws Exception {
JwtInfo worker = JwtTokenUtil.getWorker();
@@ -78,7 +78,7 @@ public class AppOrderController {
}
// 获取用户订单数量
@ApiOperation("获取用户订单数量")
@GetMapping("/getUserOrderCount")
public ApiResponse<Integer> getUserOrderCount() {
Long userId = Long.parseLong(BaseContextHandler.getUserId());
@@ -86,16 +86,16 @@ public class AppOrderController {
return orderService.getOrderCountByUserId(userId);
}
// 发起退款
@ApiOperation(value = "发起退款", notes = "发起退款")
@PostMapping("/refundOrder")
public ApiResponse<?> refundOrder(@RequestBody RefundOrderReq refundOrderReq) {
return orderService.refundOrder(refundOrderReq);
}
@GetMapping("/scenic/{scenicId}/query")
public ApiResponse<IsBuyRespVO> isBuy(@PathVariable("scenicId") Long scenicId, @RequestParam("type") Integer type, @RequestParam("goodsId") Long goodsId, @RequestParam(value = "faceId", required = false) Long faceId) {
public ApiResponse<IsBuyRespVO> isBuy(@PathVariable("scenicId") Long scenicId, @RequestParam("type") Integer type, @RequestParam("goodsId") Long goodsId) {
Long userId = Long.parseLong(BaseContextHandler.getUserId());
return ApiResponse.success(orderBiz.isBuy(scenicId, userId, faceId, type, goodsId));
return ApiResponse.success(orderBiz.isBuy(userId, scenicId, type, goodsId));
}
@GetMapping("/scenic/{scenicId}/queryBatchPrice")
@@ -108,7 +108,7 @@ public class AppOrderController {
}
faceId = lastFaceByUserId.getId();
}
IsBuyBatchRespVO buy = priceBiz.isOnePriceBuy(userId, faceId, scenicId, type, goodsIds);
IsBuyBatchRespVO buy = priceBiz.isBuy(userId, faceId, scenicId, type, goodsIds);
if (buy == null) {
return ApiResponse.fail("该套餐暂未开放购买");
}

View File

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

View File

@@ -2,7 +2,6 @@ package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.pc.printer.resp.MemberPrintResp;
import com.ycwl.basic.model.pc.printer.resp.PrinterResp;
import com.ycwl.basic.model.printer.req.FromSourceReq;
@@ -17,7 +16,6 @@ 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.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@@ -37,16 +35,10 @@ public class AppPrinterController {
return ApiResponse.success(printerService.listByScenicId(scenicId));
}
@GetMapping("/useSample/{sampleId}")
public ApiResponse<FaceRecognizeResp> useSample(@PathVariable("sampleId") Long sampleId) throws IOException {
JwtInfo worker = JwtTokenUtil.getWorker();
return ApiResponse.success(printerService.useSample(worker.getUserId(), sampleId));
}
@GetMapping("/getListFor/{scenicId}")
public ApiResponse<List<MemberPrintResp>> getListFor(@PathVariable("scenicId") Long scenicId, @RequestParam(required = false) String faceId) {
public ApiResponse<List<MemberPrintResp>> getListFor(@PathVariable("scenicId") Long scenicId) {
JwtInfo worker = JwtTokenUtil.getWorker();
return ApiResponse.success(printerService.getUserPhotoList(worker.getUserId(), scenicId, parseFaceId(faceId)));
return ApiResponse.success(printerService.getUserPhotoList(worker.getUserId(), scenicId));
}
@GetMapping("/getItem/{scenicId}/{id}")
@@ -60,38 +52,31 @@ public class AppPrinterController {
}
@PostMapping("/deleteFrom/{scenicId}/{id}")
public ApiResponse<?> deleteFrom(@PathVariable("scenicId") Long scenicId, @PathVariable("id") Long id) {
public ApiResponse<?> deleteFrom(@PathVariable("scenicId") Long scenicId, @PathVariable("id") Long id) throws IOException {
JwtInfo worker = JwtTokenUtil.getWorker();
printerService.deleteUserPhoto(worker.getUserId(), scenicId, id);
return ApiResponse.success(null);
}
@PostMapping("/uploadTo/{scenicId}")
public ApiResponse<?> upload(@PathVariable("scenicId") Long scenicId,
@RequestParam(value = "file") MultipartFile file,
@RequestParam(value = "faceId", required = false) String faceId) {
public ApiResponse<?> upload(@PathVariable("scenicId") Long scenicId, @RequestParam(value = "file") MultipartFile file) throws IOException {
String[] split = file.getOriginalFilename().split("\\.");
String ext = split[split.length - 1];
String url = StorageFactory.use().uploadFile(file, "printer", UUID.randomUUID() + "." + ext);
Integer id = printerService.addUserPhoto(JwtTokenUtil.getWorker().getUserId(), scenicId, url, parseFaceId(faceId), null);
return ApiResponse.success(id);
printerService.addUserPhoto(JwtTokenUtil.getWorker().getUserId(), scenicId, url);
return ApiResponse.success(url);
}
@PostMapping(value = "/uploadTo/{scenicId}/cropped/{id}", consumes = "multipart/form-data")
public ApiResponse<?> uploadReplace(@PathVariable("scenicId") Long scenicId,
@PathVariable("id") Long id,
@RequestPart(value = "crop", required = false) String crop,
@RequestPart(value = "file") MultipartFile file) {
@PostMapping("/uploadTo/{scenicId}/cropped/{id}")
public ApiResponse<?> uploadReplace(@PathVariable("scenicId") Long scenicId, @PathVariable("id") Long id, @RequestParam(value = "file") MultipartFile file) throws IOException {
String[] split = file.getOriginalFilename().split("\\.");
String ext = split[split.length - 1];
String url = StorageFactory.use().uploadFile(file, "printer", UUID.randomUUID() + "." + ext);
printerService.setPhotoCropped(JwtTokenUtil.getWorker().getUserId(), scenicId, id, url, crop);
printerService.setPhotoCropped(JwtTokenUtil.getWorker().getUserId(), scenicId, id, url);
return ApiResponse.success(url);
}
@PostMapping("/uploadTo/{scenicId}/formSource")
public ApiResponse<?> uploadFromSource(@PathVariable("scenicId") Long scenicId,
@RequestBody FromSourceReq req,
@RequestParam(value = "faceId", required = false) String faceId) {
List<Integer> list = printerService.addUserPhotoFromSource(JwtTokenUtil.getWorker().getUserId(), scenicId, req, parseFaceId(faceId));
return ApiResponse.success(list);
public ApiResponse<?> uploadFromSource(@PathVariable("scenicId") Long scenicId, @RequestBody FromSourceReq req) throws IOException {
printerService.addUserPhotoFromSource(JwtTokenUtil.getWorker().getUserId(), scenicId, req);
return ApiResponse.success(null);
}
@PostMapping("/setQuantity/{scenicId}/{id}")
@@ -106,35 +91,16 @@ public class AppPrinterController {
return ApiResponse.success(null);
}
@GetMapping("/price/{scenicId}")
public ApiResponse<?> queryPrice(@PathVariable("scenicId") Long scenicId,
@RequestParam(value = "faceId", required = false) String faceId) {
return ApiResponse.success(printerService.queryPrice(JwtTokenUtil.getWorker().getUserId(), scenicId, parseFaceId(faceId)));
public ApiResponse<?> queryPrice(@PathVariable("scenicId") Long scenicId) {
return ApiResponse.success(printerService.queryPrice(JwtTokenUtil.getWorker().getUserId(), scenicId));
}
@PostMapping("/order/{scenicId}")
public ApiResponse<Map<String, Object>> createOrder(@PathVariable("scenicId") Long scenicId,
@RequestParam(value = "faceId", required = false) String faceId) {
return ApiResponse.success(printerService.createOrder(JwtTokenUtil.getWorker().getUserId(), scenicId, null, parseFaceId(faceId)));
public ApiResponse<Map<String, Object>> createOrder(@PathVariable("scenicId") Long scenicId) {
return ApiResponse.success(printerService.createOrder(JwtTokenUtil.getWorker().getUserId(), scenicId, null));
}
@PostMapping("/order/{scenicId}/toPrinter/{printerId}")
public ApiResponse<Map<String, Object>> createOrderToPrinter(@PathVariable("scenicId") Long scenicId,
@PathVariable("printerId") Integer printerId,
@RequestParam(value = "faceId", required = false) String faceId) {
return ApiResponse.success(printerService.createOrder(JwtTokenUtil.getWorker().getUserId(), scenicId, printerId, parseFaceId(faceId)));
}
/**
* 解析 faceId 字符串为 Long 类型
* 如果字符串不是有效数字,则返回 null
*/
private Long parseFaceId(String faceId) {
if (faceId == null || faceId.trim().isEmpty()) {
return null;
}
try {
return Long.parseLong(faceId.trim());
} catch (NumberFormatException e) {
return null;
}
public ApiResponse<Map<String, Object>> createOrderToPrinter(@PathVariable("scenicId") Long scenicId, @PathVariable("printerId") Integer printerId) {
return ApiResponse.success(printerService.createOrder(JwtTokenUtil.getWorker().getUserId(), scenicId, printerId));
}
}

View File

@@ -1,237 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.constant.SourceType;
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
import com.ycwl.basic.pricing.dto.ProductItem;
import com.ycwl.basic.pricing.enums.ProductType;
import com.ycwl.basic.pricing.service.IPriceCalculationService;
import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity;
import com.ycwl.basic.puzzle.mapper.PuzzleGenerationRecordMapper;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/mobile/puzzle/v1")
@RequiredArgsConstructor
public class AppPuzzleController {
private final PuzzleGenerationRecordMapper recordMapper;
private final FaceRepository faceRepository;
private final IPriceCalculationService iPriceCalculationService;
private final PrinterService printerService;
private final OrderBiz orderBiz;
/**
* 根据faceId查询三拼图数量
*/
@GetMapping("/count/{faceId}")
public ApiResponse<Integer> countByFaceId(@PathVariable("faceId") Long faceId) {
if (faceId == null) {
return ApiResponse.fail("faceId不能为空");
}
int count = recordMapper.countByFaceId(faceId);
return ApiResponse.success(count);
}
/**
* 根据faceId查询所有三拼图记录
*/
@GetMapping("/list/{faceId}")
public ApiResponse<List<ContentPageVO>> listByFaceId(@PathVariable("faceId") Long faceId) {
if (faceId == null) {
return ApiResponse.fail("faceId不能为空");
}
List<PuzzleGenerationRecordEntity> records = recordMapper.listByFaceId(faceId);
List<ContentPageVO> result = records.stream()
.map(this::convertToContentPageVO)
.collect(Collectors.toList());
return ApiResponse.success(result);
}
/**
* 根据recordId查询单个三拼图记录
*/
@GetMapping("/detail/{recordId}")
public ApiResponse<ContentPageVO> getByRecordId(@PathVariable("recordId") Long recordId) {
if (recordId == null) {
return ApiResponse.fail("recordId不能为空");
}
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
if (record == null) {
return ApiResponse.fail("未找到对应的拼图记录");
}
ContentPageVO result = convertToContentPageVO(record);
return ApiResponse.success(result);
}
/**
* 根据recordId下载拼图资源
*/
@GetMapping("/download/{recordId}")
public ApiResponse<List<String>> download(@PathVariable("recordId") Long recordId) {
if (recordId == null) {
return ApiResponse.fail("recordId不能为空");
}
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
if (record == null) {
return ApiResponse.fail("未找到对应的拼图记录");
}
String resultImageUrl = record.getResultImageUrl();
if (resultImageUrl == null || resultImageUrl.isEmpty()) {
return ApiResponse.fail("该拼图记录没有可用的图片URL");
}
return ApiResponse.success(Collections.singletonList(resultImageUrl));
}
/**
* 根据recordId查询拼图价格
*/
@GetMapping("/price/{recordId}")
public ApiResponse<PriceCalculationResult> getPriceByRecordId(@PathVariable("recordId") Long recordId) {
if (recordId == null) {
return ApiResponse.fail("recordId不能为空");
}
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
if (record == null) {
return ApiResponse.fail("未找到对应的拼图记录");
}
FaceEntity face = faceRepository.getFace(record.getFaceId());
if (face == null) {
return ApiResponse.fail("未找到对应的人脸信息");
}
PriceCalculationRequest calculationRequest = new PriceCalculationRequest();
ProductItem productItem = new ProductItem();
productItem.setProductType(ProductType.PHOTO_LOG);
productItem.setProductId(record.getTemplateId().toString());
productItem.setPurchaseCount(1);
productItem.setScenicId(face.getScenicId().toString());
calculationRequest.setProducts(Collections.singletonList(productItem));
calculationRequest.setUserId(face.getMemberId());
calculationRequest.setFaceId(record.getFaceId());
calculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠
PriceCalculationResult calculationResult = iPriceCalculationService.calculatePrice(calculationRequest);
return ApiResponse.success(calculationResult);
}
/**
* 将拼图导入到打印列表
*/
@PostMapping("/import-to-print/{recordId}")
public ApiResponse<Integer> importToPrint(@PathVariable("recordId") Long recordId) {
if (recordId == null) {
return ApiResponse.fail("recordId不能为空");
}
// 查询拼图记录
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
if (record == null) {
return ApiResponse.fail("未找到对应的拼图记录");
}
// 检查是否有图片URL
String resultImageUrl = record.getResultImageUrl();
if (resultImageUrl == null || resultImageUrl.isEmpty()) {
return ApiResponse.fail("该拼图记录没有可用的图片URL");
}
// 获取人脸信息
FaceEntity face = faceRepository.getFace(record.getFaceId());
if (face == null) {
return ApiResponse.fail("未找到对应的人脸信息");
}
// 调用服务添加到打印列表
Integer memberPrintId = printerService.addUserPhotoFromPuzzle(
face.getMemberId(),
face.getScenicId(),
record.getFaceId(),
resultImageUrl,
0L // 打印特有
);
if (memberPrintId == null) {
return ApiResponse.fail("添加到打印列表失败");
}
return ApiResponse.success(memberPrintId);
}
/**
* 将PuzzleGenerationRecordEntity转换为ContentPageVO
*/
private ContentPageVO convertToContentPageVO(PuzzleGenerationRecordEntity record) {
ContentPageVO vo = new ContentPageVO();
// 内容类型为3(拼图)
vo.setContentType(3);
// 源素材类型为3(拼图)
vo.setSourceType(3);
vo.setGroup("拼图");
// 只要存在记录,lockType不为0(设置为-1表示已生成)
vo.setLockType(-1);
// 通过faceId填充scenicId的信息
FaceEntity face = faceRepository.getFace(record.getFaceId());
if (record.getFaceId() != null) {
vo.setScenicId(face.getScenicId());
}
// contentId为生成记录id
vo.setContentId(record.getId());
// templateCoverUrl和生成的图是一致的
vo.setTemplateCoverUrl(record.getResultImageUrl());
// 设置模板ID
vo.setTemplateId(record.getTemplateId());
IsBuyRespVO isBuyScenic = orderBiz.isBuy(face.getScenicId(), face.getMemberId(), face.getId(), 5, face.getScenicId());
if (isBuyScenic.isBuy()) {
vo.setIsBuy(1);
} else {
IsBuyRespVO isBuyRespVO = orderBiz.isBuy(face.getScenicId(), face.getMemberId(), face.getId(), 5, record.getTemplateId());
if (isBuyRespVO.isBuy()) {
vo.setIsBuy(1);
} else {
vo.setIsBuy(0);
PriceCalculationRequest calculationRequest = new PriceCalculationRequest();
ProductItem productItem = new ProductItem();
productItem.setProductType(ProductType.PHOTO_LOG);
productItem.setProductId(record.getTemplateId().toString());
productItem.setPurchaseCount(1);
productItem.setScenicId(face.getScenicId().toString());
calculationRequest.setProducts(Collections.singletonList(productItem));
calculationRequest.setUserId(face.getMemberId());
calculationRequest.setFaceId(record.getFaceId());
calculationRequest.setPreviewOnly(true); // 仅查询价格,不实际使用优惠
PriceCalculationResult calculationResult = iPriceCalculationService.calculatePrice(calculationRequest);
if (calculationResult.getFinalAmount().compareTo(BigDecimal.ZERO) > 0) {
vo.setFreeCount(0);
} else {
vo.setFreeCount(1);
}
}
}
return vo;
}
}

View File

@@ -1,76 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.integration.questionnaire.dto.answer.ResponseDetailResponse;
import com.ycwl.basic.integration.questionnaire.dto.answer.SubmitAnswerRequest;
import com.ycwl.basic.integration.questionnaire.dto.questionnaire.QuestionnaireResponse;
import com.ycwl.basic.integration.questionnaire.service.QuestionnaireIntegrationService;
import com.ycwl.basic.utils.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 移动端问卷接口控制器
*
* @author Claude Code
* @date 2025-09-05
*/
@Slf4j
@RestController
@RequestMapping("/api/mobile/questionnaire/v1")
@RequiredArgsConstructor
public class AppQuestionnaireController {
private final QuestionnaireIntegrationService questionnaireIntegrationService;
/**
* 获取问卷详情
* 包含问卷基本信息和所有题目
*/
@IgnoreToken
@GetMapping("/{id}")
public ApiResponse<QuestionnaireResponse> getQuestionnaire(@PathVariable Long id) {
log.info("移动端获取问卷详情, id: {}", id);
try {
QuestionnaireResponse questionnaire = questionnaireIntegrationService.getQuestionnaire(id);
// 检查问卷状态,只有已发布的问卷才能被移动端访问
if (questionnaire.getStatus() != 2) {
return ApiResponse.fail("问卷未发布或已停止");
}
return ApiResponse.success(questionnaire);
} catch (Exception e) {
log.error("移动端获取问卷详情失败, id: {}", id, e);
return ApiResponse.fail("获取问卷详情失败: " + e.getMessage());
}
}
/**
* 提交问卷答案
*/
@PostMapping("/{id}/submit")
public ApiResponse<ResponseDetailResponse> submitAnswer(
@PathVariable Long id,
@Valid @RequestBody SubmitAnswerRequest request) {
String userId = BaseContextHandler.getUserId();
log.info("移动端提交问卷答案, questionnaireId: {}, userId: {}, answers count: {}",
id, userId, request.getAnswers() != null ? request.getAnswers().size() : 0);
try {
// 设置问卷ID和用户ID
request.setQuestionnaireId(id);
request.setUserId(userId);
ResponseDetailResponse response = questionnaireIntegrationService.submitAnswer(request);
return ApiResponse.success(response);
} catch (Exception e) {
log.error("移动端提交问卷答案失败, questionnaireId: {}, userId: {}", id, userId, e);
return ApiResponse.fail("提交问卷答案失败: " + e.getMessage());
}
}
}

View File

@@ -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.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
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,14 +16,13 @@ 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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@@ -33,9 +32,10 @@ import java.util.List;
* @Date:2024/12/5 10:22
*/
@Slf4j
@Deprecated
@RestController
@RequestMapping("/api/mobile/scenic/v1")
// 景区相关接口
@Api(tags = "景区相关接口")
public class AppScenicController {
@Autowired
@@ -48,12 +48,11 @@ public class AppScenicController {
add("3932535453961555968");
add("3936121342868459520");
add("3936940597855784960");
add("4049850382325780480");
}};
// 分页查询景区列表
@ApiOperation("分页查询景区列表")
@PostMapping("/page")
public ApiResponse<PageInfo<ScenicEntity>> pageQuery(@RequestBody ScenicReqQuery scenicReqQuery){
public ApiResponse<PageInfo<ScenicAppVO>> pageQuery(@RequestBody ScenicReqQuery scenicReqQuery){
String userId = BaseContextHandler.getUserId();
if (ENABLED_USER_IDs.contains(userId)) {
return appScenicService.pageQuery(scenicReqQuery);
@@ -61,7 +60,7 @@ public class AppScenicController {
return ApiResponse.success(new PageInfo<>(new ArrayList<>()));
}
}
// 根据id查询景区详情
@ApiOperation("根据id查询景区详情")
@IgnoreToken
@GetMapping("/{id}")
public ApiResponse<ScenicRespVO> getDetails(@PathVariable Long id){
@@ -71,41 +70,40 @@ public class AppScenicController {
@GetMapping("/{id}/config")
@IgnoreToken
public ApiResponse<ScenicConfigResp> getConfig(@PathVariable Long id){
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(id);
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(id);
ScenicConfigResp resp = new ScenicConfigResp();
resp.setWatermarkUrl(scenicConfig.getString("watermark_url"));
resp.setVideoStoreDay(scenicConfig.getInteger("video_store_day"));
resp.setAntiScreenRecordType(scenicConfig.getInteger("anti_screen_record_type"));
resp.setGroupingEnable(scenicConfig.getBoolean("grouping_enable", false));
resp.setVoucherEnable(scenicConfig.getBoolean("voucher_enable", false));
resp.setShowPhotoWhenWaiting(scenicConfig.getBoolean("show_photo_when_waiting", false));
resp.setImageSourcePackHint(scenicConfig.getString("image_source_pack_hint"));
resp.setVideoSourcePackHint(scenicConfig.getString("video_source_pack_hint"));
resp.setShareBeforeBuy(scenicConfig.getBoolean("share_before_buy"));
resp.setFaceSelectFirst(scenicConfig.getBoolean("face_select_first", false));
resp.setPrintEnableSource(scenicConfig.getBoolean("print_enable_source", true));
resp.setPrintForceFaceUpload(scenicConfig.getBoolean("print_force_face_upload", false));
resp.setPrintEnableManual(scenicConfig.getBoolean("print_enable_manual", true));
resp.setSceneMode(scenicConfig.getInteger("scene_mode", 0));
resp.setPrintEnable(scenicConfig.getBoolean("print_enable", false));
resp.setShowMyPagePaid(scenicConfig.getBoolean("show_my_page_paid", true));
resp.setShowMyPageUnpaid(scenicConfig.getBoolean("show_my_page_unpaid", true));
resp.setBookRoutine(scenicConfig.getBookRoutine());
resp.setForceFinishTime(scenicConfig.getForceFinishTime());
resp.setTourTime(scenicConfig.getTourTime());
resp.setSampleStoreDay(scenicConfig.getSampleStoreDay());
resp.setFaceStoreDay(scenicConfig.getFaceStoreDay());
resp.setVideoStoreDay(scenicConfig.getVideoStoreDay());
resp.setAllFree(scenicConfig.getAllFree());
resp.setDisableSourceVideo(scenicConfig.getDisableSourceVideo());
resp.setDisableSourceImage(scenicConfig.getDisableSourceImage());
resp.setAntiScreenRecordType(scenicConfig.getAntiScreenRecordType());
resp.setVideoSourceStoreDay(scenicConfig.getVideoSourceStoreDay());
resp.setImageSourceStoreDay(scenicConfig.getImageSourceStoreDay());
resp.setUserSourceExpireDay(scenicConfig.getUserSourceExpireDay());
resp.setBrokerDirectRate(scenicConfig.getBrokerDirectRate());
resp.setVideoSourcePackHint(scenicConfig.getVideoSourcePackHint());
resp.setImageSourcePackHint(scenicConfig.getImageSourcePackHint());
return ApiResponse.success(resp);
}
// 查询景区设备总数和拍到用户的机位数量
@ApiOperation("查询景区设备总数和拍到用户的机位数量")
@GetMapping("/{scenicId}/deviceCount/")
public ApiResponse<ScenicDeviceCountVO> deviceCountByScenicId(@PathVariable Long scenicId){
return appScenicService.deviceCountByScenicId(scenicId);
}
// 景区视频源素材列表
@ApiOperation("景区视频源素材列表")
@GetMapping("/contentList/")
public ApiResponse<List<ContentPageVO>> contentList() {
return faceService.contentListUseDefaultFace();
}
// 景区视频源素材列表
@ApiOperation("景区视频源素材列表")
@GetMapping("/face/{faceId}/contentList")
public ApiResponse<List<ContentPageVO>> contentList(@PathVariable Long faceId) {
List<ContentPageVO> contentPageVOS = faceService.faceContentList(faceId);

View File

@@ -8,6 +8,7 @@ import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.task.TaskService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -45,7 +46,7 @@ public class AppTaskController {
* @param templateId 模版id
* @return 1 合成中 2 合成成功
*/
// 查询用户当前景区的具体模版视频合成任务状态 1 合成中 2 合成成功
@ApiOperation("查询用户当前景区的具体模版视频合成任务状态 1 合成中 2 合成成功 ")
@GetMapping("/face/{faceId}/template/{templateId}")
@IgnoreLogReq
public ApiResponse<VideoTaskStatusVO> getTemplateTaskStatus(@PathVariable("faceId") Long faceId, @PathVariable("templateId") Long templateId) {
@@ -55,7 +56,7 @@ public class AppTaskController {
@PostMapping("/submit")
public ApiResponse<String> submitVideoTask(@RequestBody VideoTaskReq videoTaskReq) {
taskService.createTaskByFaceIdAndTemplateId(videoTaskReq.getFaceId(),videoTaskReq.getTemplateId(),false);
taskService.createTaskByFaceIdAndTempalteId(videoTaskReq.getFaceId(),videoTaskReq.getTemplateId(),0);
return ApiResponse.success("成功");
}
}

View File

@@ -1,93 +1,23 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.model.mobile.video.dto.VideoViewPermissionDTO;
import com.ycwl.basic.model.task.req.VideoInfoReq;
import com.ycwl.basic.repository.VideoRepository;
import com.ycwl.basic.service.mobile.VideoViewPermissionService;
import com.ycwl.basic.utils.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Slf4j
@Deprecated
@RestController
@RequestMapping("/api/mobile/video/v1")
public class AppVideoController {
@Autowired
private VideoRepository videoRepository;
@Autowired
private VideoViewPermissionService videoViewPermissionService;
@PostMapping("/{videoId}/updateMeta")
public void updateMeta(@PathVariable("videoId") Long videoId, @RequestBody VideoInfoReq req) {
videoRepository.updateMeta(videoId, req);
}
/**
* 记录用户查看视频并返回权限信息
*
* @param videoId 视频ID
* @return 查看权限信息
*/
@PostMapping("/{videoId}/recordView")
public ApiResponse<VideoViewPermissionDTO> recordView(@PathVariable("videoId") Long videoId) {
try {
String userIdStr = BaseContextHandler.getUserId();
if (userIdStr == null || userIdStr.isEmpty()) {
log.warn("用户未登录,无法记录查看: videoId={}", videoId);
return ApiResponse.fail("用户未登录");
}
Long userId = Long.valueOf(userIdStr);
log.debug("记录用户查看视频: userId={}, videoId={}", userId, videoId);
VideoViewPermissionDTO permission = videoViewPermissionService.checkAndRecordView(userId, videoId);
return ApiResponse.success(permission);
} catch (NumberFormatException e) {
log.error("用户ID格式错误: userId={}, videoId={}", BaseContextHandler.getUserId(), videoId, e);
return ApiResponse.fail("用户信息无效");
} catch (Exception e) {
log.error("记录用户查看视频失败: videoId={}", videoId, e);
return ApiResponse.fail("记录查看失败,请稍后重试");
}
}
/**
* 检查用户查看权限(不记录查看次数)
*
* @param videoId 视频ID
* @return 查看权限信息
*/
@GetMapping("/{videoId}/checkPermission")
public ApiResponse<VideoViewPermissionDTO> checkPermission(@PathVariable("videoId") Long videoId) {
try {
String userIdStr = BaseContextHandler.getUserId();
if (userIdStr == null || userIdStr.isEmpty()) {
log.warn("用户未登录,无法查看权限: videoId={}", videoId);
return ApiResponse.fail("用户未登录");
}
Long userId = Long.valueOf(userIdStr);
log.debug("检查用户查看权限: userId={}, videoId={}", userId, videoId);
VideoViewPermissionDTO permission = videoViewPermissionService.checkViewPermission(userId, videoId);
return ApiResponse.success(permission);
} catch (NumberFormatException e) {
log.error("用户ID格式错误: userId={}, videoId={}", BaseContextHandler.getUserId(), videoId, e);
return ApiResponse.fail("用户信息无效");
} catch (Exception e) {
log.error("检查用户查看权限失败: videoId={}", videoId, e);
return ApiResponse.fail("权限检查失败,请稍后重试");
}
}
}

View File

@@ -1,72 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.pricing.dto.req.VoucherClaimReq;
import com.ycwl.basic.pricing.dto.req.VoucherPrintReq;
import com.ycwl.basic.pricing.dto.resp.VoucherCodeResp;
import com.ycwl.basic.pricing.dto.resp.VoucherPrintResp;
import com.ycwl.basic.pricing.service.VoucherCodeService;
import com.ycwl.basic.pricing.service.VoucherPrintService;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.utils.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/api/mobile/voucher/v1")
public class AppVoucherController {
@Autowired
private VoucherPrintService voucherPrintService;
@Autowired
private VoucherCodeService voucherCodeService;
@Autowired
private FaceRepository faceRepository;
/**
* 打印小票
* @param request 打印请求
* @return 打印结果
*/
@PostMapping("/print")
public ApiResponse<VoucherPrintResp> printVoucherTicket(@RequestBody VoucherPrintReq request) {
log.info("收到打印小票请求: faceId={}, brokerId={}, scenicId={}",
request.getFaceId(), request.getBrokerId(), request.getScenicId());
VoucherPrintResp response = voucherPrintService.printVoucherTicket(request);
log.info("打印小票完成: code={}, voucherCode={}, status={}",
response.getCode(), response.getVoucherCode(), response.getPrintStatus());
return ApiResponse.success(response);
}
@GetMapping("/printed")
public ApiResponse<VoucherPrintResp> queryPrintedVoucher(
@RequestParam Long faceId
) {
return ApiResponse.success(voucherPrintService.queryPrintedVoucher(faceId));
}
@PostMapping("/claim")
public ApiResponse<VoucherCodeResp> claimVoucher(@RequestBody VoucherClaimReq req) {
FaceEntity face = faceRepository.getFace(req.getFaceId());
if (face == null) {
throw new BaseException("请选择人脸");
}
req.setScenicId(face.getScenicId());
VoucherCodeResp result = voucherCodeService.claimVoucher(req);
return ApiResponse.success(result);
}
}

View File

@@ -0,0 +1,68 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: songmingsong
* @CreateTime: 2024-12-06
* @Description: 微信消息模板通知
* @Version: 1.0
*/
@RestController
@RequestMapping("/api/mobile/wx/notify/v1")
@Api(tags = "微信消息模板通知")
public class AppWxNotifyController {
@Autowired
private ScenicRepository scenicRepository;
//
// @ApiOperation(value = "通知", notes = "通知")
// @PostMapping("/pushMessage")
// @IgnoreToken
// public ApiResponse<?> pushMessage(@RequestBody WechatMessageSubscribeForm req) {
// JSONObject resJson = wxNotifyService.pushMessage(req);
// return ApiResponse.success(resJson);
// }
@GetMapping({"/getIds", "/"})
@IgnoreToken
public ApiResponse<List<String>> getIds() {
return ApiResponse.success(new ArrayList<>() {{
add("5b8vTm7kvwYubqDxb3dxBs0BqxMsgVgGw573aahTEd8");
add("vPIzbkA0x4mMj-vdbWx6_45e8juWXzs3FGYnDsIPv3A");
add("HB1vp-0BXc2WyYeoYN3a3GuZV9HtPLXUTT7blCBq9eY");
}});
}
@GetMapping("/{scenicId}")
@IgnoreToken
public ApiResponse<List<String>> getIds(@PathVariable("scenicId") Long scenicId) {
return ApiResponse.success(new ArrayList<>() {{
String videoGeneratedTemplateId = scenicRepository.getVideoGeneratedTemplateId(scenicId);
if (StringUtils.isNotBlank(videoGeneratedTemplateId)) {
add(videoGeneratedTemplateId);
}
String videoDownloadTemplateId = scenicRepository.getVideoDownloadTemplateId(scenicId);
if (StringUtils.isNotBlank(videoDownloadTemplateId)) {
add(videoDownloadTemplateId);
}
String videoPreExpireTemplateId = scenicRepository.getVideoPreExpireTemplateId(scenicId);
if (StringUtils.isNotBlank(videoPreExpireTemplateId)) {
add(videoPreExpireTemplateId);
}
}});
}
}

View File

@@ -8,6 +8,8 @@ import com.ycwl.basic.model.wx.WxPayRespVO;
import com.ycwl.basic.pay.entity.PayResponse;
import com.ycwl.basic.service.mobile.WxPayService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
@@ -29,13 +31,13 @@ import java.security.GeneralSecurityException;
*/
@RestController
@RequestMapping("/api/mobile/wx/pay/v1")
// 微信支付相关接口
@Api(tags = "微信支付相关接口")
public class AppWxPayController {
@Autowired
private WxPayService wxPayService;
// 微信支付回调
@ApiOperation(value = "微信支付回调", notes = "微信支付回调")
@PostMapping("/payNotify")
@IgnoreToken
public ApiResponse<?> payNotify(HttpServletRequest request) {
@@ -56,7 +58,7 @@ public class AppWxPayController {
return ApiResponse.success(BizCodeEnum.REQUEST_OK);
}
// 微信支付退款回调
@ApiOperation(value = "微信支付退款回调", notes = "微信支付退款回调")
@PostMapping("/{scenicId}/refundNotify")
@IgnoreToken
public ApiResponse<?> refundNotify(@PathVariable Long scenicId, HttpServletRequest request) throws GeneralSecurityException, IOException {

View File

@@ -2,23 +2,21 @@ 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;
import com.ycwl.basic.model.mobile.scenic.account.ScenicRegisterReq;
import com.ycwl.basic.model.mobile.scenic.account.ScenicRegisterRespVO;
import com.ycwl.basic.model.mobile.weChat.DTO.WeChatUserInfoDTO;
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;
import com.ycwl.basic.utils.ApiResponse;
import org.apache.commons.lang3.Strings;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -27,10 +25,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;
/**
@@ -39,7 +37,7 @@ import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
*/
@RestController
@RequestMapping("/api/mobile/scenicAccount/v1")
// 景区账号相关接口
@Api(tags = "景区账号相关接口")
public class AppScenicAccountController {
@Autowired
private ScenicAccountService accountService;
@@ -47,51 +45,28 @@ public class AppScenicAccountController {
private AppScenicService scenicService;
@Autowired
private ScenicService adminScenicService;
@Autowired
private ScenicRepository scenicRepository;
// 登录
@ApiOperation("登录")
@PostMapping("/login")
@IgnoreToken
public ApiResponse<ScenicLoginOldRespVO> login(@RequestBody ScenicLoginReq scenicLoginReq) throws Exception {
ApiResponse<ScenicLoginRespVO> logined = scenicService.login(scenicLoginReq);
ScenicLoginOldRespVO vo = new ScenicLoginOldRespVO();
if (!logined.isSuccess()) {
return ApiResponse.fail(logined.getMsg());
}
vo.setId(logined.getData().getId());
vo.setScenicId(logined.getData().getScenicId().getFirst());
vo.setIsSuper(logined.getData().getIsSuper());
vo.setName(logined.getData().getName());
vo.setAccount(logined.getData().getAccount());
vo.setStatus(logined.getData().getStatus());
vo.setToken(logined.getData().getToken());
return ApiResponse.success(vo);
}
// 注册
@PostMapping("/register")
@IgnoreToken
public ApiResponse<ScenicRegisterRespVO> register(@RequestBody ScenicRegisterReq scenicRegisterReq) {
return scenicService.register(scenicRegisterReq);
public ApiResponse<ScenicLoginRespVO> login(@RequestBody ScenicLoginReq scenicLoginReq) throws Exception {
return scenicService.login(scenicLoginReq);
}
@GetMapping("/myScenicList")
public ApiResponse<List<ScenicV2DTO>> myScenicList() {
List<ScenicV2DTO> list = Collections.emptyList();
if (Strings.CS.equals(BaseContextHandler.getRoleId(), MERCHANT.type)) {
public ApiResponse<List<ScenicRespVO>> myScenicList() {
List<ScenicRespVO> list = Collections.emptyList();
if (StringUtils.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 -> scenicRepository.getScenicBasic(id))
.toList();
} else if (Strings.CS.equals(BaseContextHandler.getRoleId(), ADMIN.type)) {
ScenicReqQuery query = new ScenicReqQuery();
query.setPageSize(1000);
list = scenicRepository.list(query);
list = account.getScenicId().stream().map(id -> {
return scenicService.getDetails(id).getData();
}).toList();
} else {
list = adminScenicService.list(new ScenicReqQuery()).getData();
}
return ApiResponse.success(list);
}
@@ -120,7 +95,6 @@ public class AppScenicAccountController {
}
@GetMapping("/devices")
@Deprecated
public ApiResponse<List<DeviceRespVO>> getDeviceList() {
String userId = BaseContextHandler.getUserId();
ScenicAccountEntity account = accountService.getScenicAccountById(Long.valueOf(userId));

View File

@@ -9,6 +9,7 @@ import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.service.pc.ScenicAccountService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@@ -20,7 +21,7 @@ import java.util.List;
@RestController
@RequestMapping("/api/mobile/scenic/order/v1")
// 景区账号相关接口
@Api(tags = "景区账号相关接口")
public class AppScenicOrderController {
@Autowired
private OrderService orderService;

View File

@@ -11,6 +11,8 @@ import com.ycwl.basic.model.mobile.statistic.resp.AppStatisticsFunnelVO;
import com.ycwl.basic.service.mobile.AppStatisticsService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -22,38 +24,46 @@ import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/api/mobile/statistics/v1")
// 数据统计相关接口
@Api(tags = "数据统计相关接口")
public class AppStatisticsController {
@Autowired
private AppStatisticsService statisticsService;
// 支付订单金额、预览_支付转化率、扫码_付费用户转化率
@ApiOperation("支付订单金额、预览_支付转化率、扫码_付费用户转化率")
@PostMapping("/one")
public ApiResponse<AppSta1VO> oneStatistics(@RequestBody CommonQueryReq query) {
return statisticsService.oneStatistics(query);
}
// 支付订单数、现场订单数、推送订单数统计
@ApiOperation("支付订单数、现场订单数、推送订单数统计")
@PostMapping("/two")
public ApiResponse<AppSta2VO> twoStatistics(@RequestBody CommonQueryReq query) {
return statisticsService.twoStatistics(query);
}
// 扫码访问人数、推送订阅人数、预览视频人数统计
@ApiOperation("扫码访问人数、推送订阅人数、预览视频人数统计")
@PostMapping("/free")
public ApiResponse<AppSta3VO> freeStatistics(@RequestBody CommonQueryReq query) {
return statisticsService.freeStatistics(query);
}
// 用户转化漏斗
@ApiOperation("用户转化漏斗")
@PostMapping("/userConversionFunnel")
public ApiResponse<AppStatisticsFunnelVO> userConversionFunnel(@RequestBody CommonQueryReq query) {
return statisticsService.userConversionFunnel(query);
}
@ApiOperation("统计数据记录")
@PostMapping("/addStatistics")
@IgnoreToken
public ApiResponse<String> addStatistics(@RequestBody StatisticsRecordAddReq req) {
return statisticsService.addStatistics(req);
}
}

View File

@@ -1,190 +0,0 @@
package com.ycwl.basic.controller.mobile.notify;
import com.ycwl.basic.model.mobile.notify.req.NotificationAuthRecordReq;
import com.ycwl.basic.model.mobile.notify.resp.NotificationAuthRecordResp;
import com.ycwl.basic.model.mobile.notify.resp.ScenicTemplateAuthResp;
import com.ycwl.basic.service.UserNotificationAuthorizationService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import com.ycwl.basic.repository.ScenicRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 用户通知授权记录Controller (移动端API)
* 只提供用户主动授权记录功能,支持批量授权,其他检查和消费功能由系统内部调用
*
* @Author: System
* @Date: 2024/12/28
*/
@RestController
@RequestMapping("/api/mobile/notify/auth")
@Slf4j
public class UserNotificationAuthController {
@Autowired
private UserNotificationAuthorizationService userNotificationAuthorizationService;
@Autowired
private ScenicRepository scenicRepository;
/**
* 记录用户通知授权 - 支持批量授权
* 用户主动同意通知授权时调用
*/
@PostMapping("/record")
public ApiResponse<NotificationAuthRecordResp> recordAuthorization(
@RequestBody NotificationAuthRecordReq req) {
log.debug("记录用户通知授权: templateIds={}, scenicId={}", req.getTemplateIds(), req.getScenicId());
try {
// 获取当前用户ID
Long memberId = JwtTokenUtil.getWorker().getUserId();
// 调用批量授权记录方法
List<UserNotificationAuthorizationService.AuthorizationRecord> records =
userNotificationAuthorizationService.batchRecordAuthorization(
memberId, req.getTemplateIds(), req.getScenicId());
NotificationAuthRecordResp resp = new NotificationAuthRecordResp();
// 转换响应数据
List<NotificationAuthRecordResp.AuthorizationRecord> successRecords = new ArrayList<>();
List<String> failedTemplateIds = new ArrayList<>();
List<String> failureReasons = new ArrayList<>();
for (UserNotificationAuthorizationService.AuthorizationRecord record : records) {
if (record.isSuccess()) {
NotificationAuthRecordResp.AuthorizationRecord successRecord =
new NotificationAuthRecordResp.AuthorizationRecord();
successRecord.setId(record.getId());
successRecord.setTemplateId(record.getTemplateId());
successRecord.setScenicId(record.getScenicId());
successRecord.setAuthorizationCount(record.getAuthorizationCount());
successRecord.setConsumedCount(record.getConsumedCount());
successRecord.setRemainingCount(record.getRemainingCount());
successRecord.setLastAuthorizedTime(record.getLastAuthorizedTime());
successRecord.setLastConsumedTime(record.getLastConsumedTime());
successRecord.setStatus(record.getStatus());
successRecord.setCreateTime(record.getCreateTime());
successRecords.add(successRecord);
} else {
failedTemplateIds.add(record.getTemplateId());
failureReasons.add(record.getFailureReason());
}
}
resp.setAllSuccess(CollectionUtils.isEmpty(failedTemplateIds));
resp.setSuccessRecords(successRecords);
resp.setFailedTemplateIds(failedTemplateIds);
resp.setFailureReasons(failureReasons);
return ApiResponse.success(resp);
} catch (Exception e) {
log.error("记录用户通知授权失败", e);
return ApiResponse.fail("记录授权失败: " + e.getMessage());
}
}
/**
* 获取景区通知模板ID及用户授权余额
* 复制AppWxNotifyController中的逻辑,并额外返回用户对应的授权余额
*/
@GetMapping("/{scenicId}/templates")
public ApiResponse<ScenicTemplateAuthResp> getScenicTemplatesWithAuth(@PathVariable("scenicId") Long scenicId) {
log.debug("获取景区通知模板ID及用户授权余额: scenicId={}", scenicId);
try {
// 获取当前用户ID
Long memberId = JwtTokenUtil.getWorker().getUserId();
// 获取景区的所有模板ID(复制自AppWxNotifyController的逻辑)
List<String> templateIds = new ArrayList<>() {{
String videoGeneratedTemplateId = scenicRepository.getVideoGeneratedTemplateId(scenicId);
if (StringUtils.isNotBlank(videoGeneratedTemplateId)) {
add(videoGeneratedTemplateId);
}
String videoDownloadTemplateId = scenicRepository.getVideoDownloadTemplateId(scenicId);
if (StringUtils.isNotBlank(videoDownloadTemplateId)) {
add(videoDownloadTemplateId);
}
String videoPreExpireTemplateId = scenicRepository.getVideoPreExpireTemplateId(scenicId);
if (StringUtils.isNotBlank(videoPreExpireTemplateId)) {
add(videoPreExpireTemplateId);
}
}};
// 构建响应对象
ScenicTemplateAuthResp resp = new ScenicTemplateAuthResp();
resp.setScenicId(scenicId);
// 查询每个模板的授权余额信息
List<ScenicTemplateAuthResp.TemplateAuthInfo> templateAuthInfos = new ArrayList<>();
for (String templateId : templateIds) {
ScenicTemplateAuthResp.TemplateAuthInfo templateAuthInfo =
new ScenicTemplateAuthResp.TemplateAuthInfo();
templateAuthInfo.setTemplateId(templateId);
if (templateId.equals(scenicRepository.getVideoGeneratedTemplateId(scenicId))) {
templateAuthInfo.setTitle("视频生成通知");
templateAuthInfo.setDescription("当视频生成完成时,我们将提醒您");
} else if (templateId.equals(scenicRepository.getVideoDownloadTemplateId(scenicId))) {
templateAuthInfo.setTitle("视频下载通知");
templateAuthInfo.setDescription("当您的视频未购买时,我们将提醒您");
} else if (templateId.equals(scenicRepository.getVideoPreExpireTemplateId(scenicId))) {
templateAuthInfo.setTitle("视频即将过期通知");
templateAuthInfo.setDescription("当您的视频即将过期时,我们将提醒您及时下载");
} else {
templateAuthInfo.setTitle("未知模板类型");
templateAuthInfo.setDescription("未知的模板类型");
}
// 获取授权详情
try {
com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity authEntity =
userNotificationAuthorizationService.checkAuthorization(memberId, templateId, scenicId);
if (authEntity != null) {
templateAuthInfo.setAuthorizationCount(authEntity.getAuthorizationCount());
templateAuthInfo.setConsumedCount(authEntity.getConsumedCount());
templateAuthInfo.setRemainingCount(authEntity.getRemainingCount());
templateAuthInfo.setHasAuthorization(authEntity.getRemainingCount() != null && authEntity.getRemainingCount() > 0);
} else {
// 没有授权记录
templateAuthInfo.setAuthorizationCount(0);
templateAuthInfo.setConsumedCount(0);
templateAuthInfo.setRemainingCount(0);
templateAuthInfo.setHasAuthorization(false);
}
} catch (Exception e) {
log.warn("获取模板授权信息失败: templateId={}, scenicId={}, memberId={}, error={}",
templateId, scenicId, memberId, e.getMessage());
// 获取失败时设置为无授权
templateAuthInfo.setAuthorizationCount(0);
templateAuthInfo.setConsumedCount(0);
templateAuthInfo.setRemainingCount(0);
templateAuthInfo.setHasAuthorization(false);
}
templateAuthInfos.add(templateAuthInfo);
}
resp.setTemplates(templateAuthInfos);
log.debug("成功获取景区通知模板ID及用户授权余额: scenicId={}, templateCount={}, memberId={}",
scenicId, templateIds.size(), memberId);
return ApiResponse.success(resp);
} catch (Exception e) {
log.error("获取景区通知模板ID及用户授权余额失败: scenicId={}", scenicId, e);
return ApiResponse.fail("获取授权信息失败: " + e.getMessage());
}
}
}

View File

@@ -1,50 +0,0 @@
package com.ycwl.basic.controller.monitor;
import com.ycwl.basic.integration.kafka.scheduler.AccountFaceSchedulerManager;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* 人脸识别监控接口
* 提供调度器状态查询功能
*/
@RestController
@RequestMapping("/api/monitor/face-recognition")
@RequiredArgsConstructor
public class FaceRecognitionMonitorController {
private final AccountFaceSchedulerManager schedulerManager;
/**
* 获取所有账号的调度器统计信息
* <p>
* 示例返回:
* {
* "LTAI5xxx": {
* "accountKey": "LTAI5xxx",
* "cloudType": "ALI",
* "activeThreads": 3,
* "executorQueueSize": 12,
* "schedulerQueueSize": 45
* },
* "245xxx": {
* "accountKey": "245xxx",
* "cloudType": "BAIDU",
* "activeThreads": 8,
* "executorQueueSize": 5,
* "schedulerQueueSize": 20
* }
* }
*
* @return 所有账号的调度器状态
*/
@GetMapping("/schedulers")
public ApiResponse<Map<String, AccountFaceSchedulerManager.AccountSchedulerStats>> getAllSchedulerStats() {
return ApiResponse.success(schedulerManager.getAllStats());
}
}

View File

@@ -8,6 +8,8 @@ import com.ycwl.basic.model.pc.adminUser.resp.AdminUserListRespVO;
import com.ycwl.basic.model.pc.adminUser.resp.StaffSimpleInfoRespVO;
import com.ycwl.basic.service.pc.AdminUserService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -17,7 +19,7 @@ import java.util.List;
@RestController
@RequestMapping("/api/adminUser/v1")
@Slf4j
// 系统后台用户管理
@Api(tags = "系统后台用户管理")
public class AdminUserController {
@Autowired
@@ -25,7 +27,7 @@ public class AdminUserController {
@PostMapping(value = "/login")
// 登录
@ApiOperation(value = "登录")
@IgnoreToken
public ApiResponse login(@RequestBody LoginReqVO loginReqVO) throws Exception {
log.info("{}:开始登录管理后台", loginReqVO.getAccount());
@@ -33,49 +35,49 @@ public class AdminUserController {
}
@PostMapping(value = "/updatePassword")
// 用户自己修改密码
@ApiOperation(value = "用户自己修改密码")
public ApiResponse updatePassword(@RequestBody UpdatePasswordReqVO updatePasswordReqVO) throws Exception {
log.info("{}:开始修改管理后台密码", updatePasswordReqVO.getId());
return adminUserService.updatePassword(updatePasswordReqVO);
}
@PostMapping(value = "/list")
// 系统后台用户列表
@ApiOperation(value = "系统后台用户列表")
//@IgnoreToken
public ApiResponse<PageInfo<List<AdminUserListRespVO>>> list(@RequestBody AdminUserListReqVO adminUserListReqVO) {
return adminUserService.list(adminUserListReqVO);
}
@PostMapping(value = "/query/list")
// 系统后台用户列表查询
@ApiOperation(value = "系统后台用户列表查询")
@IgnoreToken
public ApiResponse<PageInfo<List<AdminUserListRespVO>>> queryList(@RequestBody AdminUserListReqVO adminUserListReqVO) {
return adminUserService.list(adminUserListReqVO);
}
@PostMapping(value = "/add")
// 添加系统后台用户
@ApiOperation(value = "添加系统后台用户")
//@IgnoreToken
public ApiResponse add(@RequestBody AddOrUpdateAdminUserReqVO addOrUpdateAdminUserReqVO) {
return adminUserService.addOrUpdate(addOrUpdateAdminUserReqVO);
}
@PostMapping(value = "/update")
// 更新系统后台用户
@ApiOperation(value = "更新系统后台用户")
//@IgnoreToken
public ApiResponse update(@RequestBody AddOrUpdateAdminUserReqVO addOrUpdateAdminUserReqVO) {
return adminUserService.addOrUpdate(addOrUpdateAdminUserReqVO);
}
@GetMapping(value = "/delete/{id}")
// 删除
@ApiOperation(value = "删除")
//@IgnoreToken
public ApiResponse delete(@PathVariable("id") String id) {
return adminUserService.delete(id);
}
@PostMapping(value = "/resetPassword")
// 重置密码
@ApiOperation(value = "重置密码")
//@IgnoreToken
public ApiResponse resetPassword(@RequestBody ResetPasswordReqVO resetPasswordReqVO) {
log.info("{}:开始重置后台密码", resetPasswordReqVO.getId());

View File

@@ -14,6 +14,8 @@ import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
@@ -28,7 +30,7 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/broker/v1")
// 推客(推广人)管理
@Api(tags = "推客(推广人)管理")
public class BrokerController {
@Autowired
private BrokerService brokerService;
@@ -39,64 +41,64 @@ public class BrokerController {
@Autowired
private ScenicRepository scenicRepository;
// 分页查询
@ApiOperation("分页查询")
@PostMapping("/page")
public ApiResponse page(@RequestBody BrokerReqQuery brokerReqQuery){
return ApiResponse.success(brokerService.pageQuery(brokerReqQuery));
}
// 列表查询
@ApiOperation("列表查询")
@PostMapping("/list")
public ApiResponse list(@RequestBody BrokerReqQuery brokerReqQuery){
return ApiResponse.success(brokerService.list(brokerReqQuery));
}
// 详情查询
@ApiOperation("详情查询")
@GetMapping("/getDetails/{id}")
public ApiResponse getDetails(@PathVariable("id") Long id){
return ApiResponse.success(brokerService.getById(id));
}
// 新增或修改
@ApiOperation("新增或修改")
@PostMapping("/addOrUpdate")
public ApiResponse addOrUpdate(@RequestBody BrokerEntity broker){
return ApiResponse.success(brokerService.addOrUpdate(broker));
}
// 删除
@ApiOperation("删除")
@DeleteMapping("/delete/{id}")
public ApiResponse delete(@PathVariable("id") Long id){
return ApiResponse.success(brokerService.delete(id));
}
// 修改状态
@ApiOperation("修改状态")
@PutMapping("/updateStatus/{id}")
public ApiResponse updateStatus(@PathVariable("id") Long id){
return ApiResponse.success(brokerService.updateStatus(id));
}
// 修改状态
@ApiOperation("修改状态")
@PutMapping("/updateBrokerEnable/{id}")
public ApiResponse updateBrokerEnable(@PathVariable("id") Long id){
return ApiResponse.success(brokerService.updateBrokerEnable(id));
}
// 推客记录分页查询
@ApiOperation("推客记录分页查询")
@PostMapping("/record/page")
public ApiResponse pageRecord(@RequestBody BrokerRecordReqQuery brokerRecordReqQuery) {
return ApiResponse.success(brokerRecordService.pageQuery(brokerRecordReqQuery));
}
// 推客记录列表查询
@ApiOperation("推客记录列表查询")
@PostMapping("/record/list")
public ApiResponse listRecord(@RequestBody BrokerRecordReqQuery brokerRecordReqQuery) {
return ApiResponse.success(brokerRecordService.list(brokerRecordReqQuery));
}
// 推客记录详情查询
@ApiOperation("推客记录详情查询")
@GetMapping("/record/getDetails/{id}")
public ApiResponse getRecordDetails(@PathVariable("id") Long id) {
return ApiResponse.success(brokerRecordService.getById(id));
}
// 根据brokerId和时间范围查询每天的记录数量和orderPrice汇总
@ApiOperation("根据brokerId和时间范围查询每天的记录数量和orderPrice汇总")
@GetMapping("/{id}/record/summary")
public ApiResponse<List<DailySummaryRespVO>> getDailySummaryByBrokerId(
@PathVariable("id") Long brokerId,
@@ -105,7 +107,7 @@ public class BrokerController {
return ApiResponse.success(brokerRecordService.getDailySummaryByBrokerId(brokerId, startTime, endTime));
}
// 根据景区ID下载小程序二维码
@ApiOperation("根据景区ID下载小程序二维码")
@GetMapping("/{id}/QRCode")
public ApiResponse<String> downloadQrCode(@PathVariable Long id) {
BrokerRespVO broker = brokerService.getById(id);

View File

@@ -8,6 +8,8 @@ import com.ycwl.basic.model.pc.coupon.resp.CouponRespVO;
import com.ycwl.basic.model.pc.price.resp.GoodsListRespVO;
import com.ycwl.basic.service.pc.CouponService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -15,7 +17,7 @@ import java.util.List;
@RestController
@RequestMapping("/api/coupon/v1")
// 优惠券管理
@Api(tags = "优惠券管理")
public class CouponController {
@Autowired
private CouponService couponService;
@@ -25,17 +27,17 @@ public class CouponController {
@GetMapping("/{scenicId}/goodsList")
public ApiResponse<List<GoodsListRespVO>> scenicGoodsList(@PathVariable Long scenicId) {
List<GoodsListRespVO> data = priceBiz.listGoodsByScenic(scenicId);
data.add(new GoodsListRespVO(-1L, "一口价", -1));
data.add(new GoodsListRespVO(-1L, "一口价"));
return ApiResponse.success(data);
}
// 新增优惠券
@ApiOperation("新增优惠券")
@PostMapping("/add")
public ApiResponse<Integer> add(@RequestBody CouponEntity coupon) {
return ApiResponse.success(couponService.add(coupon));
}
// 更新优惠券
@ApiOperation("更新优惠券")
@PostMapping("/update/{id}")
public ApiResponse<Boolean> update(@PathVariable Integer id, @RequestBody CouponEntity coupon) {
coupon.setId(id);
@@ -47,19 +49,19 @@ public class CouponController {
return ApiResponse.success(couponService.updateStatus(id));
}
// 删除优惠券
@ApiOperation("删除优惠券")
@DeleteMapping("/delete/{id}")
public ApiResponse<Boolean> delete(@PathVariable Integer id) {
return ApiResponse.success(couponService.delete(id));
}
// 根据ID查询优惠券
@ApiOperation("根据ID查询优惠券")
@GetMapping("/get/{id}")
public ApiResponse<CouponEntity> getById(@PathVariable Integer id) {
return ApiResponse.success(couponService.getById(id));
}
// 分页查询优惠券列表
@ApiOperation("分页查询优惠券列表")
@PostMapping("/page")
public ApiResponse<PageInfo<CouponRespVO>> list(@RequestBody CouponQueryReq couponQuery) {
PageHelper.startPage(couponQuery.getPageNum(), couponQuery.getPageSize());

View File

@@ -1,22 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.couponRecord.req.CouponRecordPageQueryReq;
import com.ycwl.basic.model.pc.couponRecord.resp.CouponRecordPageResp;
import com.ycwl.basic.service.pc.CouponRecordService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/coupon/record/v1")
public class CouponRecordController {
@Autowired
private CouponRecordService couponRecordService;
@PostMapping("/page")
public ApiResponse<PageInfo<CouponRecordPageResp>> pageQuery(@RequestBody CouponRecordPageQueryReq query) {
return couponRecordService.pageQuery(query);
}
}

View File

@@ -0,0 +1,81 @@
package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.req.DeviceAddOrUpdateReq;
import com.ycwl.basic.model.pc.device.req.DeviceBatchSortRequest;
import com.ycwl.basic.model.pc.device.req.DeviceReqQuery;
import com.ycwl.basic.model.pc.device.req.DeviceSortRequest;
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
import com.ycwl.basic.model.pc.template.req.TemplateSortRequest;
import com.ycwl.basic.service.pc.DeviceService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @Author:longbinbin
* @Date:2024/12/2 16:13
*/
@RestController
@RequestMapping("/api/device/v1")
@Api(tags = "设备管理")
public class DeviceController {
@Autowired
private DeviceService deviceService;
@ApiOperation("设备分页查询")
@PostMapping("/page")
public ApiResponse<PageInfo<DeviceRespVO>> pageQuery(@RequestBody DeviceReqQuery deviceReqQuery) {
return deviceService.pageQuery(deviceReqQuery);
}
@ApiOperation("设备列表查询")
@PostMapping("/list")
public ApiResponse list(@RequestBody DeviceReqQuery deviceReqQuery) {
return deviceService.list(deviceReqQuery);
}
@ApiOperation("设备详情查询")
@GetMapping("/getDetails/{id}")
public ApiResponse<DeviceRespVO> getDetails(@PathVariable("id") Long id) {
return deviceService.getById(id);
}
@ApiOperation("新增或修改设备")
@PostMapping("/addOrUpdate")
public ApiResponse addOrUpdate(@RequestBody DeviceAddOrUpdateReq deviceReqQuery) {
return deviceService.addOrUpdate(deviceReqQuery);
}
@ApiOperation("删除设备")
@DeleteMapping("/delete/{id}")
public ApiResponse delete(@PathVariable("id") Long id) {
return deviceService.deleteById(id);
}
@ApiOperation("修改设备状态")
@PutMapping("/updateStatus/{id}")
public ApiResponse updateStatus(@PathVariable("id") Long id) {
return deviceService.updateStatus(id);
}
@ApiOperation("排序设备")
@PostMapping("/sort")
public ApiResponse<Boolean> sortDevice(@RequestBody DeviceSortRequest request) {
return deviceService.sortDevice(request.getDeviceId(), request.getAfterDeviceId());
}
@PostMapping("/scenic/{scenicId}/sortBatch")
public ApiResponse<Boolean> sortDeviceBatch(@PathVariable("scenicId") Long scenicId, @RequestBody DeviceBatchSortRequest request) {
return deviceService.batchSort(scenicId, request);
}
@GetMapping("/config/{id}")
public ApiResponse<DeviceConfigEntity> getConfig(@PathVariable("id") Long id) {
return ApiResponse.success(deviceService.getConfig(id));
}
@PostMapping("/saveConfig/{configId}")
public ApiResponse saveConfig(@PathVariable("configId") Long configId, @RequestBody DeviceConfigEntity deviceConfigEntity) {
deviceService.saveConfig(configId, deviceConfigEntity);
return ApiResponse.success(null);
}
}

View File

@@ -1,390 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.device.dto.config.*;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.device.*;
import com.ycwl.basic.integration.device.dto.status.DeviceStatusDTO;
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
import com.ycwl.basic.integration.device.service.DeviceStatusIntegrationService;
import com.ycwl.basic.utils.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 设备管理 V2 版本控制器 - 基于 zt-device 集成服务
*
* @author Claude Code
* @date 2025-09-01
*/
@Slf4j
@RestController
@RequestMapping("/api/device/v2")
@RequiredArgsConstructor
public class DeviceV2Controller {
private final DeviceIntegrationService deviceIntegrationService;
private final DeviceConfigIntegrationService deviceConfigIntegrationService;
private final DeviceStatusIntegrationService deviceStatusIntegrationService;
// ========== 设备基础 CRUD 操作 ==========
/**
* 设备V2核心信息分页列表
*/
@GetMapping("/")
public ApiResponse<PageResponse<DeviceV2DTO>> listDevices(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name,
@RequestParam(required = false) String no,
@RequestParam(required = false) String type,
@RequestParam(required = false) Integer isActive,
@RequestParam(required = false) Long scenicId) {
log.info("分页查询设备核心信息列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
page, pageSize, name, no, type, isActive, scenicId);
// 参数验证:限制pageSize最大值为100
if (pageSize > 100) {
pageSize = 100;
}
try {
PageResponse<DeviceV2DTO> response = deviceIntegrationService.listDevices(page, pageSize, name, no, type, isActive, scenicId, null);
return ApiResponse.success(response);
} catch (Exception e) {
log.error("分页查询设备核心信息列表失败", e);
return ApiResponse.fail("分页查询设备列表失败: " + e.getMessage());
}
}
/**
* 根据ID获取设备信息
*/
@GetMapping("/{id}")
public ApiResponse<DeviceV2DTO> getDevice(@PathVariable Long id) {
try {
DeviceV2DTO device = deviceIntegrationService.getDevice(id);
return ApiResponse.success(device);
} catch (Exception e) {
log.error("获取设备信息失败, id: {}", id, e);
return ApiResponse.fail("获取设备信息失败: " + e.getMessage());
}
}
/**
* 根据设备编号获取设备信息
*/
@GetMapping("/no/{no}")
public ApiResponse<DeviceV2DTO> getDeviceByNo(@PathVariable String no) {
try {
DeviceV2DTO device = deviceIntegrationService.getDeviceByNo(no);
return ApiResponse.success(device);
} catch (Exception e) {
log.error("根据设备编号获取设备信息失败, no: {}", no, e);
return ApiResponse.fail("根据设备编号获取设备信息失败: " + e.getMessage());
}
}
/**
* 根据设备ID获取设备在线状态
*/
@GetMapping("/{id}/status")
public ApiResponse<DeviceStatusDTO> getDeviceOnlineStatus(@PathVariable Long id) {
log.info("获取设备在线状态, deviceId: {}", id);
try {
// 首先获取设备信息以获得设备编号
DeviceV2DTO device = deviceIntegrationService.getDevice(id);
if (device == null) {
return ApiResponse.fail("设备不存在");
}
// 使用设备编号查询在线状态
DeviceStatusDTO onlineStatus = deviceStatusIntegrationService.getDeviceStatus(device.getNo());
return ApiResponse.success(onlineStatus);
} catch (Exception e) {
log.error("获取设备在线状态失败, deviceId: {}", id, e);
return ApiResponse.fail("获取设备在线状态失败: " + e.getMessage());
}
}
/**
* 创建设备
*/
@PostMapping("/")
public ApiResponse<DeviceV2DTO> createDevice(@Valid @RequestBody CreateDeviceRequest request) {
log.info("创建设备, name: {}, no: {}, type: {}, sort: {}",
request.getName(), request.getNo(), request.getType(), request.getSort());
try {
DeviceV2DTO device = deviceIntegrationService.createDevice(request);
return ApiResponse.success(device);
} catch (Exception e) {
log.error("创建设备失败", e);
return ApiResponse.fail("创建设备失败: " + e.getMessage());
}
}
/**
* 创建IPC摄像头设备(快捷方法)
*/
@PostMapping("/ipc")
public ApiResponse<DeviceV2DTO> createIpcDevice(@RequestBody Map<String, Object> request) {
String name = (String) request.get("name");
String deviceNo = (String) request.get("no");
Long scenicId = Long.valueOf(request.get("scenicId").toString());
Integer sort = request.get("sort") != null ? Integer.valueOf(request.get("sort").toString()) : null;
log.info("创建IPC摄像头设备, name: {}, no: {}, scenicId: {}, sort: {}", name, deviceNo, scenicId, sort);
try {
DeviceV2DTO device;
if (sort != null) {
device = deviceIntegrationService.createIpcDeviceWithSort(name, deviceNo, scenicId, sort);
} else {
device = deviceIntegrationService.createIpcDevice(name, deviceNo, scenicId);
}
return ApiResponse.success(device);
} catch (Exception e) {
log.error("创建IPC摄像头设备失败", e);
return ApiResponse.fail("创建IPC摄像头设备失败: " + e.getMessage());
}
}
/**
* 创建自定义设备(快捷方法)
*/
@PostMapping("/custom")
public ApiResponse<DeviceV2DTO> createCustomDevice(@RequestBody Map<String, Object> request) {
String name = (String) request.get("name");
String deviceNo = (String) request.get("no");
Long scenicId = Long.valueOf(request.get("scenicId").toString());
Integer sort = request.get("sort") != null ? Integer.valueOf(request.get("sort").toString()) : null;
log.info("创建自定义设备, name: {}, no: {}, scenicId: {}, sort: {}", name, deviceNo, scenicId, sort);
try {
DeviceV2DTO device;
if (sort != null) {
device = deviceIntegrationService.createCustomDeviceWithSort(name, deviceNo, scenicId, sort);
} else {
device = deviceIntegrationService.createCustomDevice(name, deviceNo, scenicId);
}
return ApiResponse.success(device);
} catch (Exception e) {
log.error("创建自定义设备失败", e);
return ApiResponse.fail("创建自定义设备失败: " + e.getMessage());
}
}
/**
* 更新设备信息
*/
@PutMapping("/{id}")
public ApiResponse<String> updateDevice(@PathVariable Long id, @Valid @RequestBody UpdateDeviceRequest request) {
log.info("更新设备信息, id: {}", id);
try {
deviceIntegrationService.updateDevice(id, request);
return ApiResponse.success("设备信息更新成功");
} catch (Exception e) {
log.error("更新设备信息失败, id: {}", id, e);
return ApiResponse.fail("更新设备信息失败: " + e.getMessage());
}
}
/**
* 更新设备排序
*/
@PutMapping("/{id}/sort")
public ApiResponse<String> updateDeviceSort(@PathVariable Long id, @RequestBody Map<String, Integer> request) {
Integer sort = request.get("sort");
log.info("更新设备排序, id: {}, sort: {}", id, sort);
try {
deviceIntegrationService.updateDeviceSort(id, sort);
return ApiResponse.success("设备排序更新成功");
} catch (Exception e) {
log.error("更新设备排序失败, id: {}, sort: {}", id, sort, e);
return ApiResponse.fail("更新设备排序失败: " + e.getMessage());
}
}
/**
* 启用设备
*/
@PutMapping("/{id}/enable")
public ApiResponse<String> enableDevice(@PathVariable Long id) {
log.info("启用设备, id: {}", id);
try {
deviceIntegrationService.enableDevice(id);
return ApiResponse.success("设备启用成功");
} catch (Exception e) {
log.error("启用设备失败, id: {}", id, e);
return ApiResponse.fail("启用设备失败: " + e.getMessage());
}
}
/**
* 禁用设备
*/
@PutMapping("/{id}/disable")
public ApiResponse<String> disableDevice(@PathVariable Long id) {
log.info("禁用设备, id: {}", id);
try {
deviceIntegrationService.disableDevice(id);
return ApiResponse.success("设备禁用成功");
} catch (Exception e) {
log.error("禁用设备失败, id: {}", id, e);
return ApiResponse.fail("禁用设备失败: " + e.getMessage());
}
}
/**
* 删除设备
*/
@DeleteMapping("/{id}")
public ApiResponse<String> deleteDevice(@PathVariable Long id) {
log.info("删除设备, id: {}", id);
try {
deviceIntegrationService.deleteDevice(id);
return ApiResponse.success("设备删除成功");
} catch (Exception e) {
log.error("删除设备失败, id: {}", id, e);
return ApiResponse.fail("删除设备失败: " + e.getMessage());
}
}
// ========== 设备配置管理操作 ==========
/**
* 获取设备配置列表
*/
@GetMapping("/{id}/config")
public ApiResponse<List<DeviceConfigV2DTO>> getDeviceConfigs(@PathVariable Long id) {
try {
List<DeviceConfigV2DTO> configs = deviceConfigIntegrationService.getDeviceConfigs(id);
return ApiResponse.success(configs);
} catch (Exception e) {
log.error("获取设备配置列表失败, deviceId: {}", id, e);
return ApiResponse.fail("获取设备配置列表失败: " + e.getMessage());
}
}
/**
* 根据配置键获取配置
*/
@GetMapping("/{id}/config/{configKey}")
public ApiResponse<DeviceConfigV2DTO> getDeviceConfigByKey(@PathVariable Long id,
@PathVariable String configKey) {
try {
DeviceConfigV2DTO config = deviceConfigIntegrationService.getDeviceConfigByKey(id, configKey);
return ApiResponse.success(config);
} catch (Exception e) {
log.error("根据键获取设备配置失败, deviceId: {}, configKey: {}", id, configKey, e);
return ApiResponse.fail("根据键获取设备配置失败: " + e.getMessage());
}
}
/**
* 根据设备编号获取配置列表
*/
@GetMapping("/no/{no}/config")
public ApiResponse<List<DeviceConfigV2DTO>> getDeviceConfigsByNo(@PathVariable String no) {
log.info("根据设备编号获取配置列表, deviceNo: {}", no);
try {
List<DeviceConfigV2DTO> configs = deviceConfigIntegrationService.getDeviceConfigsByNo(no);
return ApiResponse.success(configs);
} catch (Exception e) {
log.error("根据设备编号获取配置列表失败, deviceNo: {}", no, e);
return ApiResponse.fail("根据设备编号获取配置列表失败: " + e.getMessage());
}
}
/**
* 创建设备配置
*/
@PostMapping("/{id}/config")
public ApiResponse<DeviceConfigV2DTO> createDeviceConfig(@PathVariable Long id,
@Valid @RequestBody CreateDeviceConfigRequest request) {
log.info("创建设备配置, deviceId: {}, configKey: {}", id, request.getConfigKey());
try {
DeviceConfigV2DTO config = deviceConfigIntegrationService.createDeviceConfig(id, request);
return ApiResponse.success(config);
} catch (Exception e) {
log.error("创建设备配置失败, deviceId: {}, configKey: {}", id, request.getConfigKey(), e);
return ApiResponse.fail("创建设备配置失败: " + e.getMessage());
}
}
/**
* 批量创建/更新设备配置
*/
@PostMapping("/{id}/config/batch")
public ApiResponse<BatchUpdateResponse> batchUpdateDeviceConfig(@PathVariable Long id,
@Valid @RequestBody BatchDeviceConfigRequest request) {
log.info("批量更新设备配置, deviceId: {}, configs count: {}", id, request.getConfigs().size());
try {
BatchUpdateResponse result = deviceConfigIntegrationService.batchUpdateDeviceConfig(id, request);
return ApiResponse.success(result);
} catch (Exception e) {
log.error("批量更新设备配置失败, deviceId: {}", id, e);
return ApiResponse.fail("批量更新设备配置失败: " + e.getMessage());
}
}
/**
* 更新设备配置
*/
@PutMapping("/{id}/config/{configId}")
public ApiResponse<String> updateDeviceConfig(@PathVariable Long id, @PathVariable Long configId,
@Valid @RequestBody UpdateDeviceConfigRequest request) {
log.info("更新设备配置, deviceId: {}, configId: {}", id, configId);
try {
deviceConfigIntegrationService.updateDeviceConfig(id, configId, request);
return ApiResponse.success("设备配置更新成功");
} catch (Exception e) {
log.error("更新设备配置失败, deviceId: {}, configId: {}", id, configId, e);
return ApiResponse.fail("更新设备配置失败: " + e.getMessage());
}
}
/**
* 删除设备配置
*/
@DeleteMapping("/{id}/config/{configId}")
public ApiResponse<String> deleteDeviceConfig(@PathVariable Long id, @PathVariable Long configId) {
log.info("删除设备配置, deviceId: {}, configId: {}", id, configId);
try {
deviceConfigIntegrationService.deleteDeviceConfig(id, configId);
return ApiResponse.success("设备配置删除成功");
} catch (Exception e) {
log.error("删除设备配置失败, deviceId: {}, configId: {}", id, configId, e);
return ApiResponse.fail("删除设备配置失败: " + e.getMessage());
}
}
// ========== 景区设备管理操作 ==========
/**
* 获取景区所有设备列表
*/
@GetMapping("/scenic/{scenicId}")
public ApiResponse<PageResponse<DeviceV2DTO>> getScenicAllDevices(@PathVariable Long scenicId,
@RequestParam(required = false) String name,
@RequestParam(required = false) String type,
@RequestParam(required = false) String no,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) {
log.info("获取景区所有设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
try {
PageResponse<DeviceV2DTO> response = deviceIntegrationService.listDevices(page, pageSize, name, no, type, null, scenicId, null);
return ApiResponse.success(response);
} catch (Exception e) {
log.error("获取景区所有设备列表失败, scenicId: {}", scenicId, e);
return ApiResponse.fail("获取景区所有设备列表失败: " + e.getMessage());
}
}
}

View File

@@ -1,106 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ycwl.basic.device.entity.common.DeviceVideoContinuityCache;
import com.ycwl.basic.task.DeviceVideoContinuityCheckTask;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
/**
* 设备视频连续性检查控制器
* 提供查询设备视频连续性检查结果的接口
*
* @author Claude Code
* @date 2025-09-01
*/
@Slf4j
@RestController
@RequestMapping("/api/device/video-continuity")
@RequiredArgsConstructor
public class DeviceVideoContinuityController {
private static final String REDIS_KEY_PREFIX = "device:video:continuity:";
private final RedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper;
private final DeviceVideoContinuityCheckTask checkTask;
/**
* 查询设备最近的视频连续性检查结果
*
* @param deviceId 设备ID
* @return 检查结果
*/
@GetMapping("/{deviceId}")
public ApiResponse<DeviceVideoContinuityCache> getDeviceContinuityResult(@PathVariable Long deviceId) {
log.info("查询设备 {} 的视频连续性检查结果", deviceId);
try {
String redisKey = REDIS_KEY_PREFIX + deviceId;
String cacheJson = redisTemplate.opsForValue().get(redisKey);
if (cacheJson == null) {
log.warn("未找到设备 {} 的视频连续性检查结果", deviceId);
return ApiResponse.buildResponse(404, null, "未找到该设备的检查结果,可能设备未配置存储或尚未执行检查");
}
DeviceVideoContinuityCache cache = objectMapper.readValue(cacheJson, DeviceVideoContinuityCache.class);
return ApiResponse.success(cache);
} catch (Exception e) {
log.error("查询设备 {} 视频连续性检查结果失败", deviceId, e);
return ApiResponse.buildResponse(500, null, "查询失败: " + e.getMessage());
}
}
/**
* 手动触发设备视频连续性检查
* 注意:仅用于测试和紧急情况,正常情况下由定时任务自动执行
*
* @param deviceId 设备ID
* @return 检查结果
*/
@PostMapping("/{deviceId}/check")
public ApiResponse<DeviceVideoContinuityCache> manualCheck(@PathVariable Long deviceId) {
log.info("手动触发设备 {} 的视频连续性检查", deviceId);
try {
DeviceVideoContinuityCache result = checkTask.manualCheck(deviceId);
return ApiResponse.success(result);
} catch (Exception e) {
log.error("手动检查设备 {} 视频连续性失败", deviceId, e);
return ApiResponse.buildResponse(500, null, "检查失败: " + e.getMessage());
}
}
/**
* 删除设备的视频连续性检查缓存
* 用于清理过期或错误的缓存数据
*
* @param deviceId 设备ID
* @return 删除结果
*/
@DeleteMapping("/{deviceId}")
public ApiResponse<String> deleteContinuityCache(@PathVariable Long deviceId) {
log.info("删除设备 {} 的视频连续性检查缓存", deviceId);
try {
String redisKey = REDIS_KEY_PREFIX + deviceId;
Boolean deleted = redisTemplate.delete(redisKey);
if (deleted != null && deleted) {
return ApiResponse.success("缓存删除成功");
} else {
return ApiResponse.buildResponse(404, null, "未找到该设备的缓存数据");
}
} catch (Exception e) {
log.error("删除设备 {} 视频连续性检查缓存失败", deviceId, e);
return ApiResponse.buildResponse(500, null, "删除失败: " + e.getMessage());
}
}
}

View File

@@ -6,6 +6,8 @@ import com.ycwl.basic.model.pc.face.req.FaceReqQuery;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -17,40 +19,41 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/face/v1")
// 用户人脸管理
@Api(tags = "用户人脸管理")
public class FaceController {
@Autowired
private FaceService faceService;
// 分页查询用户人脸
@ApiOperation("分页查询用户人脸")
@PostMapping("/page")
public ApiResponse<PageInfo<FaceRespVO>> pageQuery(@RequestBody FaceReqQuery faceReqQuery) {
return faceService.pageQuery(faceReqQuery);
}
// 用户人脸列表查询
@ApiOperation("用户人脸列表查询")
@PostMapping("/list")
public ApiResponse<List<FaceRespVO>> list(@RequestBody FaceReqQuery faceReqQuery) {
return faceService.list(faceReqQuery);
}
// 用户人脸详情查询
@ApiOperation("用户人脸详情查询")
@GetMapping("/getDetail/{id}")
public ApiResponse<FaceRespVO> getDetail(@PathVariable("id") Long id) {
return faceService.getById(id);
}
// 添加用户人脸信息
@ApiOperation("添加用户人脸信息")
@PostMapping("/add")
public ApiResponse<Integer> add(@RequestBody FaceEntity face) {
return faceService.add(face);
}
// 删除用户人脸信息
@ApiOperation("删除用户人脸信息")
@PostMapping("/deleteById/{id}")
public ApiResponse<Integer> deleteById(@PathVariable Long id) {
return faceService.deleteById(id);
}
// 批量删除用户人脸
@ApiOperation("批量删除用户人脸")
@PostMapping("/deleteByIds")
public ApiResponse<Integer> deleteByIds(@RequestBody List<Long> ids) {
return faceService.deleteByIds(ids);
}
}

View File

@@ -1,5 +1,4 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.kafka.service.FaceProcessingKafkaService;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
@@ -7,6 +6,8 @@ import com.ycwl.basic.model.pc.faceSample.req.FaceSampleReqQuery;
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
import com.ycwl.basic.service.pc.FaceSampleService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -16,50 +17,28 @@ import java.util.List;
* @Author:longbinbin
* @Date:2024/12/2 16:33
*/
@Deprecated
@RestController
@RequestMapping("/api/faceSample/v1")
// 人脸样本管理
@Api(tags = "人脸样本管理")
public class FaceSampleController {
@Autowired
private FaceSampleService FaceSampleService;
@Autowired(required = false)
private FaceProcessingKafkaService faceProcessingKafkaService;
// 分页查询人脸样本
@ApiOperation("分页查询人脸样本")
@PostMapping("/page")
public ApiResponse<PageInfo<FaceSampleRespVO>> pageQuery(@RequestBody FaceSampleReqQuery FaceSampleReqQuery) {
return FaceSampleService.pageQuery(FaceSampleReqQuery);
}
// 人脸样本列表查询
@ApiOperation("人脸样本列表查询")
@PostMapping("/list")
public ApiResponse<List<FaceSampleRespVO>> list(@RequestBody FaceSampleReqQuery FaceSampleReqQuery) {
return FaceSampleService.list(FaceSampleReqQuery);
}
// 人脸样本详情查询
@ApiOperation("人脸样本详情查询")
@GetMapping("/getDetail/{id}")
public ApiResponse<FaceSampleRespVO> getDetail(@PathVariable("id") Long id) {
return FaceSampleService.getById(id);
}
/**
* 重试失败的人脸识别
* 用于手动重试状态为-1的人脸样本
*
* @param id 人脸样本ID
* @return 重试结果
*/
@PostMapping("/retry/{id}")
public ApiResponse<String> retryFaceRecognition(@PathVariable("id") Long id) {
if (faceProcessingKafkaService == null) {
return ApiResponse.fail("Kafka服务未启用,无法重试人脸识别");
}
boolean success = faceProcessingKafkaService.retryFaceRecognition(id);
if (success) {
return ApiResponse.success("人脸识别重试任务已提交");
} else {
return ApiResponse.fail("提交重试任务失败,请检查人脸样本状态");
}
}
}

View File

@@ -6,6 +6,8 @@ import com.ycwl.basic.model.pc.member.req.MemberReqQuery;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.service.pc.MemberService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -17,37 +19,37 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/member/v1")
// 前台用户管理
@Api(tags = "前台用户管理")
public class MemberController {
@Autowired
private MemberService memberService;
// 前台用户分页查询
@ApiOperation("前台用户分页查询")
@PostMapping("/page")
public ApiResponse<PageInfo<MemberRespVO>> pageQuery(@RequestBody MemberReqQuery memberReqQuery) {
return memberService.pageQuery(memberReqQuery);
}
// 前台用户列表查询
@ApiOperation("前台用户列表查询")
@PostMapping("/list")
public ApiResponse<List<MemberRespVO>> list(@RequestBody MemberReqQuery memberReqQuery) {
return memberService.list(memberReqQuery);
}
// 前台用户详情查询
@ApiOperation("前台用户详情查询")
@GetMapping("/getDetail/{id}")
public ApiResponse<MemberRespVO> getDetail(@PathVariable("id") Long id) {
return memberService.getById(id);
}
// 前台用户删除
@ApiOperation("前台用户删除")
@DeleteMapping("/delete/{id}")
public ApiResponse<Integer> delete(@PathVariable("id") Long id) {
return memberService.deleteById(id);
}
// 前台用户新增
@ApiOperation("前台用户新增")
@PostMapping("/add")
public ApiResponse<Integer> add(@RequestBody MemberEntity member) {
return memberService.add(member);
}
// 前台用户修改
@ApiOperation("前台用户修改")
@PostMapping("/update")
public ApiResponse<Integer> update(@RequestBody MemberEntity member) {
return memberService.update(member);

View File

@@ -0,0 +1,45 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.pc.menu.entity.MenuEntity;
import com.ycwl.basic.service.pc.MenuService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
/**
* @Author:longbinbin
* @Date:2024/12/3 10:03
*/
@RestController
@RequestMapping("/api/menu/v1")
@Api(tags = "系统菜单管理")
public class MenuController {
private MenuService menuService;
@GetMapping(value = "/list/{type}")
@ApiOperation(value = " 菜单列表")
@IgnoreToken
public ApiResponse list(@PathVariable("type") Integer type) {
return menuService.list(type);
}
@PostMapping("/add")
@ApiOperation(value = "添加菜单")
public ApiResponse add(@RequestBody MenuEntity menu) {
return menuService.add(menu);
}
@PostMapping("/update")
@ApiOperation(value = "修改菜单")
public ApiResponse update(@RequestBody MenuEntity menu) {
return menuService.update(menu);
}
@GetMapping("/delete/{id}")
@ApiOperation(value = "删除菜单")
public ApiResponse delete(@PathVariable("id") Long id) {
return menuService.deleteById(id);
}
}

View File

@@ -1,60 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.message.dto.ChannelsResponse;
import com.ycwl.basic.integration.message.dto.MessageListData;
import com.ycwl.basic.integration.message.service.MessageIntegrationService;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/api/message/v1")
@RequiredArgsConstructor
public class MessageController {
private final MessageIntegrationService messageService;
@GetMapping("/messages")
public ApiResponse<MessageListData> listMessages(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer pageSize,
@RequestParam(required = false) String channelId,
@RequestParam(required = false) String title,
@RequestParam(required = false) String content,
@RequestParam(required = false) String sendBiz,
@RequestParam(required = false) String sentAtStart,
@RequestParam(required = false) String sentAtEnd,
@RequestParam(required = false) String createdAtStart,
@RequestParam(required = false) String createdAtEnd
) {
log.debug("PC|消息列表查询 page={}, pageSize={}, channelId={}, title={}, sendBiz={}", page, pageSize, channelId, title, sendBiz);
if (pageSize > 100) {
pageSize = 100;
}
try {
MessageListData data = messageService.listMessages(page, pageSize, channelId, title, content, sendBiz,
sentAtStart, sentAtEnd, createdAtStart, createdAtEnd);
return ApiResponse.success(data);
} catch (Exception e) {
log.error("PC|消息列表查询失败", e);
return ApiResponse.fail("消息列表查询失败: " + e.getMessage());
}
}
@GetMapping("/channels")
public ApiResponse<ChannelsResponse> listChannels() {
log.debug("PC|获取消息通道列表");
try {
ChannelsResponse data = messageService.listChannels();
return ApiResponse.success(data);
} catch (Exception e) {
log.error("PC|获取消息通道列表失败", e);
return ApiResponse.fail("获取消息通道列表失败: " + e.getMessage());
}
}
}

View File

@@ -7,6 +7,8 @@ import com.ycwl.basic.model.pc.order.req.OrderReqQuery;
import com.ycwl.basic.model.pc.order.resp.OrderRespVO;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -18,38 +20,38 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/order/v1")
// 订单管理
@Api(tags = "订单管理")
public class OrderController {
@Autowired
private OrderService orderService;
// 审核退款: 审核退款
@ApiOperation(value = "审核退款", notes = "审核退款")
@PostMapping("/auditRefundOrder")
public ApiResponse<?> auditRefundOrder(@RequestBody RefundOrderReq refundOrderReq) {
return orderService.auditRefundOrder(refundOrderReq);
}
// 分页查询订单
@ApiOperation("分页查询订单")
@PostMapping("page")
public ApiResponse<PageInfo<OrderRespVO>> pageQuery(@RequestBody OrderReqQuery query) {
return orderService.pageQuery(query);
}
// 订单列表查询
@ApiOperation("订单列表查询")
@PostMapping("list")
public ApiResponse<List<OrderRespVO>> list(@RequestBody OrderReqQuery query) {
return orderService.list(query);
}
// 订单详情查询
@ApiOperation("订单详情查询")
@GetMapping("detail/{id}")
public ApiResponse<OrderRespVO> detail(@PathVariable("id") Long orderId) {
return orderService.detail(orderId);
}
// 订单备注
@ApiOperation("订单备注")
@PostMapping("remark/{id}")
public ApiResponse<?> updateRemark(@PathVariable("id") Long orderId, @RequestBody OrderEntity query) {
orderService.remarkOrder(orderId, query);

View File

@@ -1,15 +1,13 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.mapper.AdminUserMapper;
import com.ycwl.basic.model.pc.adminUser.entity.LoginEntity;
import com.ycwl.basic.model.pc.permission.entity.PermissionEntity;
import com.ycwl.basic.model.pc.permission.req.PermissionSaveReq;
import com.ycwl.basic.model.pc.permission.resp.PermissionResp;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import com.ycwl.basic.service.pc.PermissionService;
import com.ycwl.basic.service.pc.RoleService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -26,36 +24,22 @@ import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
@RestController
@RequestMapping("/api/permission/v1")
// 权限管理接口
@Api(tags = "权限管理接口")
public class PermissionController {
@Autowired
private PermissionService permissionService;
@Autowired
private AdminUserMapper adminUserMapper;
@Autowired
private RoleService roleService;
@GetMapping("/get/")
public ApiResponse<PermissionResp> getPermissionByUser() {
String userId = BaseContextHandler.getUserId();
if (MERCHANT.type.equals(BaseContextHandler.getRoleId())) {
PermissionEntity permission = permissionService.getPermissionByUserId(Long.parseLong(userId));
if (permission == null || StringUtils.isEmpty(permission.getPermString())) {
return ApiResponse.success(new PermissionResp(new ArrayList<>(), new ArrayList<>()));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermString(), ",")), Arrays.asList(StringUtils.split(permission.getMenuString(), ","))));
} else {
// admin
LoginEntity login = adminUserMapper.getById(Long.parseLong(userId));
RolePermissionResp permissionByRoleId = roleService.getPermissionByRoleId(login.getRoleId());
if (permissionByRoleId == null) {
return ApiResponse.success(new PermissionResp(new ArrayList<>(), new ArrayList<>()));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permissionByRoleId.getPermStr(), ",")), Arrays.asList(StringUtils.split(permissionByRoleId.getMenuStr(), ","))));
}
}
// 根据用户ID查询权限信息
@ApiOperation("根据用户ID查询权限信息")
@GetMapping("/get/{userId}")
public ApiResponse<PermissionResp> getPermissionByUser(@PathVariable Long userId) {
PermissionEntity permission = permissionService.getPermissionByUserId(userId);
@@ -65,7 +49,7 @@ public class PermissionController {
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermString(), ",")), Arrays.asList(StringUtils.split(permission.getMenuString(), ","))));
}
// 保存或更新权限信息
@ApiOperation("保存或更新权限信息")
@PostMapping("/save/{userId}")
public ApiResponse saveOrUpdate(@PathVariable Long userId, @RequestBody PermissionSaveReq req) {
permissionService.saveOrUpdate(userId, StringUtils.join(req.getPermissions(), ","), StringUtils.join(req.getMenus(), ","));

View File

@@ -1,8 +1,13 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.biz.PriceBiz;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.price.entity.PriceConfigEntity;
import com.ycwl.basic.model.pc.price.req.PriceConfigListReq;
import com.ycwl.basic.model.pc.price.resp.GoodsListRespVO;
import com.ycwl.basic.model.pc.price.resp.SimpleGoodsRespVO;
import com.ycwl.basic.model.pc.price.resp.PriceConfigRespVO;
import com.ycwl.basic.repository.PriceRepository;
import com.ycwl.basic.service.pc.PriceConfigService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -14,13 +19,61 @@ import java.util.List;
public class PriceConfigController {
@Autowired
private PriceBiz priceBiz;
private PriceConfigService priceConfigService;
@Autowired
private PriceRepository priceRepository;
@GetMapping("/goodsList")
public ApiResponse<List<SimpleGoodsRespVO>> goodsList(
@RequestParam Long scenicId,
@RequestParam(required = false) String productType) {
return ApiResponse.success(priceBiz.listSimpleGoodsByScenic(scenicId, productType));
public ApiResponse<List<GoodsListRespVO>> goodsList(@RequestParam Long scenicId) {
return ApiResponse.success(priceConfigService.listGoodsByScenic(scenicId));
}
@PostMapping("/add")
public ApiResponse<PriceConfigEntity> addPriceConfig(@RequestBody PriceConfigEntity priceConfig) {
priceConfig.setId(null);
priceConfigService.save(priceConfig);
return ApiResponse.success(priceConfig);
}
@PostMapping("/update")
public ApiResponse<PriceConfigEntity> updatePriceConfig(@RequestBody PriceConfigEntity priceConfig) {
priceRepository.clearPriceCache(priceConfig.getId());
priceConfigService.updateById(priceConfig);
priceRepository.clearPriceCache(priceConfig.getId());
return ApiResponse.success(priceConfig);
}
@DeleteMapping("/delete/{id}")
public ApiResponse<Boolean> deletePriceConfig(@PathVariable Integer id) {
priceRepository.clearPriceCache(id);
priceConfigService.removeById(id);
priceRepository.clearPriceCache(id);
return ApiResponse.success(true);
}
@PostMapping("/{id}/status")
public ApiResponse<Boolean> updateStatus(@PathVariable Integer id) {
priceRepository.clearPriceCache(id);
priceConfigService.updateStatus(id);
priceRepository.clearPriceCache(id);
return ApiResponse.success(true);
}
@GetMapping("/{id}")
public ApiResponse<PriceConfigRespVO> getPriceConfigById(@PathVariable Integer id) {
PriceConfigRespVO config = priceConfigService.findById(id);
priceConfigService.fillGoodsName(config);
return ApiResponse.success(config);
}
@GetMapping("/list")
public ApiResponse<PageInfo<PriceConfigRespVO>> list(@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@ModelAttribute PriceConfigListReq req) {
PageHelper.startPage(pageNum, pageSize);
List<PriceConfigRespVO> result = priceConfigService.listByCondition(req);
priceConfigService.fillGoodsName(result);
PageInfo<PriceConfigRespVO> pageInfo = new PageInfo<>(result);
return ApiResponse.success(pageInfo);
}
}

View File

@@ -1,14 +1,10 @@
package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.mapper.PrintTaskMapper;
import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity;
import com.ycwl.basic.model.pc.printer.entity.PrinterEntity;
import com.ycwl.basic.model.pc.printer.req.PrintTaskReqQuery;
import com.ycwl.basic.model.pc.printer.req.ReprintRequest;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -26,97 +22,33 @@ public class PrinterController {
@Autowired
private PrinterService printerService;
@Autowired
private PrintTaskMapper printTaskMapper;
// 查询列表
@ApiOperation("查询列表")
@PostMapping("/list")
public ApiResponse<List<PrinterEntity>> list(@RequestBody PrinterEntity condition) {
return printerService.list(condition);
}
// 获取详情
@ApiOperation("获取详情")
@GetMapping("/get/{id}")
public ApiResponse<PrinterEntity> get(@PathVariable("id") Integer id) {
return printerService.get(id);
}
// 新增
@ApiOperation("新增")
@PostMapping("/add")
public ApiResponse<Integer> add(@RequestBody PrinterEntity entity) {
return printerService.add(entity);
}
// 更新
@ApiOperation("更新")
@PostMapping("/update")
public ApiResponse<Integer> update(@RequestBody PrinterEntity entity) {
return printerService.update(entity);
}
// 删除
@ApiOperation("删除")
@DeleteMapping("/delete/{id}")
public ApiResponse<Integer> delete(@PathVariable("id") Integer id) {
return printerService.delete(id);
}
// 分页查询打印任务
@PostMapping("/task/page")
public ApiResponse<PageInfo<PrintTaskEntity>> taskPage(@RequestBody PrintTaskReqQuery req) {
PageHelper.startPage(req.getPageNum(), req.getPageSize());
List<PrintTaskEntity> list = printTaskMapper.queryByCondition(req.getPrinterId(), req.getStatus());
PageInfo<PrintTaskEntity> pageInfo = new PageInfo<>(list);
return ApiResponse.success(pageInfo);
}
// 重新打印(将状态设置为0-未开始,并更新打印机名称)
@PostMapping("/task/reprint/{id}")
public ApiResponse<Integer> reprint(@PathVariable("id") Integer id, @RequestBody ReprintRequest request) {
int result = printerService.handleReprint(id, request);
return ApiResponse.success(result);
}
/**
* 查询待审核的打印任务
* @param printerId 打印机ID(可选)
* @return 待审核任务列表
*/
@GetMapping("/task/pending-review")
public ApiResponse<List<PrintTaskEntity>> getPendingReviewTasks(Integer printerId) {
List<PrintTaskEntity> tasks = printerService.getPendingReviewTasks(printerId);
return ApiResponse.success(tasks);
}
/**
* 更新待审核任务的URL(重新处理水印等)
* @param taskId 任务ID
* @param url 新的打印URL
* @return 操作结果
*/
@PostMapping("/task/{taskId}/url")
public ApiResponse<Boolean> updateTaskUrl(@PathVariable("taskId") Integer taskId, @RequestBody String url) {
boolean success = printerService.updatePendingReviewTaskUrl(taskId, url);
return ApiResponse.success(success);
}
/**
* 批准待审核任务,下发到打印队列
* @param taskIds 任务ID列表
* @return 成功数量
*/
@PostMapping("/task/approve")
public ApiResponse<Integer> approveTasks(@RequestBody List<Integer> taskIds) {
int count = printerService.approvePrintTasks(taskIds);
return ApiResponse.success(count);
}
/**
* 拒绝待审核任务
* @param taskIds 任务ID列表
* @return 成功数量
*/
@PostMapping("/task/reject")
public ApiResponse<Integer> rejectTasks(@RequestBody List<Integer> taskIds) {
int count = printerService.rejectPrintTasks(taskIds);
return ApiResponse.success(count);
}
}

View File

@@ -1,131 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.model.pc.printer.entity.PrinterEntity;
import com.ycwl.basic.model.pc.printer.req.PrinterPreferredSizeUpdateReq;
import com.ycwl.basic.model.pc.printer.req.PrinterStatusUpdateReq;
import com.ycwl.basic.model.pc.printer.req.PrinterUsePrinterUpdateReq;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.utils.ApiConst;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
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;
/**
* 打印机管理接口
*/
@RestController
@RequestMapping("/api/pc/printers/v1")
@RequiredArgsConstructor
public class PrinterManageController {
private final PrinterService printerService;
/**
* 打印机列表查询
*/
@GetMapping
public ApiResponse<List<PrinterEntity>> list(@RequestParam(value = "scenicId", required = false) Long scenicId,
@RequestParam(value = "status", required = false) Integer status,
@RequestParam(value = "name", required = false) String name) {
PrinterEntity condition = new PrinterEntity();
condition.setScenicId(scenicId);
condition.setStatus(status);
condition.setName(name);
return printerService.list(condition);
}
/**
* 打印机详情
*/
@GetMapping("/{id}")
public ApiResponse<PrinterEntity> detail(@PathVariable("id") Integer id) {
ApiResponse<PrinterEntity> response = printerService.get(id);
if (response.getData() == null) {
return ApiResponse.buildResponse(ApiConst.Code.CODE_NOT_EXIST, "打印机不存在");
}
return response;
}
/**
* 新增打印机
*/
@PostMapping
public ApiResponse<Integer> create(@RequestBody PrinterEntity request) {
request.setId(null);
return printerService.add(request);
}
/**
* 更新打印机信息
*/
@PutMapping("/{id}")
public ApiResponse<Integer> update(@PathVariable("id") Integer id, @RequestBody PrinterEntity request) {
request.setId(id);
return printerService.update(request);
}
/**
* 更新打印机状态
*/
@PatchMapping("/{id}/status")
public ApiResponse<Integer> updateStatus(@PathVariable("id") Integer id,
@RequestBody PrinterStatusUpdateReq req) {
if (req == null || req.getStatus() == null) {
return ApiResponse.buildResponse(ApiConst.Code.CODE_PARAM_ERROR, "状态不能为空");
}
PrinterEntity entity = new PrinterEntity();
entity.setId(id);
entity.setStatus(req.getStatus());
return printerService.update(entity);
}
/**
* 更新打印机首选尺寸
*/
@PatchMapping("/{id}/preferred-size")
public ApiResponse<Integer> updatePreferredSize(@PathVariable("id") Integer id,
@RequestBody PrinterPreferredSizeUpdateReq req) {
if (req == null || (req.getPreferW() == null && req.getPreferH() == null)) {
return ApiResponse.buildResponse(ApiConst.Code.CODE_PARAM_ERROR, "首选尺寸不能为空");
}
PrinterEntity entity = new PrinterEntity();
entity.setId(id);
entity.setPreferW(req.getPreferW());
entity.setPreferH(req.getPreferH());
return printerService.update(entity);
}
/**
* 更新当前使用的打印机
*/
@PatchMapping("/{id}/use-printer")
public ApiResponse<Integer> updateUsePrinter(@PathVariable("id") Integer id,
@RequestBody PrinterUsePrinterUpdateReq req) {
if (req == null) {
return ApiResponse.buildResponse(ApiConst.Code.CODE_PARAM_ERROR, "请求参数不能为空");
}
PrinterEntity entity = new PrinterEntity();
entity.setId(id);
entity.setUsePrinter(req.getUsePrinter());
return printerService.update(entity);
}
/**
* 删除打印机
*/
@DeleteMapping("/{id}")
public ApiResponse<Integer> delete(@PathVariable("id") Integer id) {
return printerService.delete(id);
}
}

View File

@@ -1,102 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.model.pc.project.entity.ProjectEntity;
import com.ycwl.basic.model.pc.project.req.ProjectReqQuery;
import com.ycwl.basic.model.pc.project.resp.ProjectRespVO;
import com.ycwl.basic.service.pc.ProjectService;
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.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.File;
/**
* 景区项目管理控制器
*
* @Author: Claude
* @Date: 2025-01-15
*/
@RestController
@RequestMapping("/api/project/v1")
public class ProjectController {
@Autowired
private ProjectService projectService;
@Autowired
private ScenicRepository scenicRepository;
// 分页查询
@PostMapping("/page")
public ApiResponse page(@RequestBody ProjectReqQuery projectReqQuery) {
return ApiResponse.success(projectService.pageQuery(projectReqQuery));
}
// 列表查询
@PostMapping("/list")
public ApiResponse list(@RequestBody ProjectReqQuery projectReqQuery) {
return ApiResponse.success(projectService.list(projectReqQuery));
}
// 详情查询
@GetMapping("/getDetails/{id}")
public ApiResponse getDetails(@PathVariable("id") Long id) {
return ApiResponse.success(projectService.getById(id));
}
// 新增或修改
@PostMapping("/addOrUpdate")
public ApiResponse addOrUpdate(@RequestBody ProjectEntity project) {
return ApiResponse.success(projectService.addOrUpdate(project));
}
// 删除
@DeleteMapping("/delete/{id}")
public ApiResponse delete(@PathVariable("id") Long id) {
return ApiResponse.success(projectService.delete(id));
}
// 修改状态
@PutMapping("/updateStatus/{id}")
public ApiResponse updateStatus(@PathVariable("id") Long id) {
return ApiResponse.success(projectService.updateStatus(id));
}
// 根据项目ID下载小程序二维码
@GetMapping("/{id}/QRCode")
public ApiResponse<String> downloadQrCode(@PathVariable Long id) {
ProjectRespVO project = projectService.getById(id);
if (project == null) {
return ApiResponse.fail("项目不存在");
}
MpConfigEntity mpConfig = scenicRepository.getScenicMpConfig(project.getScenicId());
if (mpConfig == null) {
return ApiResponse.fail("小程序配置不存在");
}
String appId = mpConfig.getAppId();
String appSecret = mpConfig.getAppSecret();
String appState = mpConfig.getState();
String path = "pages/home/index?scenicId=" + project.getScenicId() + "&projectId=" + id;
String filePath = "qr_code_project_" + id + ".jpg";
IStorageAdapter adapter = StorageFactory.use();
if (adapter.isExists(filePath)) {
return ApiResponse.success(adapter.getUrl(filePath));
}
try {
WxMpUtil.generateWXAQRCode(appId, appSecret, appState, path, filePath);
File file = new File(filePath);
String s = adapter.uploadFile(null, file, filePath);
file.delete();
adapter.setAcl(StorageAcl.PUBLIC_READ, filePath);
return ApiResponse.success(s);
} catch (Exception e) {
return ApiResponse.fail("生成二维码失败");
}
}
}

View File

@@ -1,313 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.questionnaire.dto.answer.ResponseDetailResponse;
import com.ycwl.basic.integration.questionnaire.dto.questionnaire.CreateQuestionnaireRequest;
import com.ycwl.basic.integration.questionnaire.dto.questionnaire.QuestionnaireResponse;
import com.ycwl.basic.integration.questionnaire.dto.statistics.QuestionnaireStatistics;
import com.ycwl.basic.integration.questionnaire.service.QuestionnaireIntegrationService;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.storage.StorageFactory;
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 jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.util.Map;
/**
* 问卷管理 V2 版本控制器 - 基于 zt-questionnaire 集成服务
*
* @author Claude Code
* @date 2025-09-05
*/
@Slf4j
@RestController
@RequestMapping("/api/questionnaire/v2")
@RequiredArgsConstructor
public class QuestionnaireV2Controller {
private final QuestionnaireIntegrationService questionnaireIntegrationService;
private final ScenicRepository scenicRepository;
// ========== 问卷管理 CRUD 操作 ==========
/**
* 分页查询问卷列表
*/
@GetMapping("/")
public ApiResponse<PageResponse<QuestionnaireResponse>> listQuestionnaires(
@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 {
PageResponse<QuestionnaireResponse> response =
questionnaireIntegrationService.getQuestionnaireList(page, pageSize, name, status, null);
return ApiResponse.success(response);
} catch (Exception e) {
log.error("分页查询问卷列表失败", e);
return ApiResponse.fail("分页查询问卷列表失败: " + e.getMessage());
}
}
/**
* 获取问卷详情
*/
@GetMapping("/{id}")
public ApiResponse<QuestionnaireResponse> getQuestionnaire(@PathVariable Long id) {
log.info("获取问卷详情, id: {}", id);
try {
QuestionnaireResponse questionnaire = questionnaireIntegrationService.getQuestionnaire(id);
return ApiResponse.success(questionnaire);
} catch (Exception e) {
log.error("获取问卷详情失败, id: {}", id, e);
return ApiResponse.fail("获取问卷详情失败: " + e.getMessage());
}
}
/**
* 创建问卷
*/
@PostMapping("/")
public ApiResponse<QuestionnaireResponse> createQuestionnaire(@Valid @RequestBody CreateQuestionnaireRequest request) {
log.info("创建问卷, name: {}, questions count: {}",
request.getName(), request.getQuestions() != null ? request.getQuestions().size() : 0);
try {
QuestionnaireResponse questionnaire = questionnaireIntegrationService.createQuestionnaire(request, "admin");
return ApiResponse.success(questionnaire);
} catch (Exception e) {
log.error("创建问卷失败", e);
return ApiResponse.fail("创建问卷失败: " + e.getMessage());
}
}
/**
* 更新问卷
*/
@PutMapping("/{id}")
public ApiResponse<QuestionnaireResponse> updateQuestionnaire(
@PathVariable Long id,
@Valid @RequestBody CreateQuestionnaireRequest request) {
log.info("更新问卷, id: {}", id);
try {
QuestionnaireResponse questionnaire = questionnaireIntegrationService.updateQuestionnaire(id, request, "admin");
return ApiResponse.success(questionnaire);
} catch (Exception e) {
log.error("更新问卷失败, id: {}", id, e);
return ApiResponse.fail("更新问卷失败: " + e.getMessage());
}
}
/**
* 更新问卷状态
*/
@PutMapping("/{id}/status")
public ApiResponse<String> updateQuestionnaireStatus(@PathVariable Long id, @RequestBody Map<String, Integer> request) {
Integer status = request.get("status");
log.info("更新问卷状态, id: {}, status: {}", id, status);
try {
// 根据状态调用不同的方法
if (status == 2) {
questionnaireIntegrationService.publishQuestionnaire(id, "admin");
} else if (status == 3) {
questionnaireIntegrationService.stopQuestionnaire(id, "admin");
}
return ApiResponse.success("问卷状态更新成功");
} catch (Exception e) {
log.error("更新问卷状态失败, id: {}, status: {}", id, status, e);
return ApiResponse.fail("更新问卷状态失败: " + e.getMessage());
}
}
/**
* 发布问卷
*/
@PutMapping("/{id}/publish")
public ApiResponse<String> publishQuestionnaire(@PathVariable Long id) {
log.info("发布问卷, id: {}", id);
try {
questionnaireIntegrationService.publishQuestionnaire(id, "admin");
return ApiResponse.success("问卷发布成功");
} catch (Exception e) {
log.error("发布问卷失败, id: {}", id, e);
return ApiResponse.fail("发布问卷失败: " + e.getMessage());
}
}
/**
* 停止问卷
*/
@PutMapping("/{id}/stop")
public ApiResponse<String> stopQuestionnaire(@PathVariable Long id) {
log.info("停止问卷, id: {}", id);
try {
questionnaireIntegrationService.stopQuestionnaire(id, "admin");
return ApiResponse.success("问卷停止成功");
} catch (Exception e) {
log.error("停止问卷失败, id: {}", id, e);
return ApiResponse.fail("停止问卷失败: " + e.getMessage());
}
}
/**
* 删除问卷
*/
@DeleteMapping("/{id}")
public ApiResponse<String> deleteQuestionnaire(@PathVariable Long id) {
log.info("删除问卷, id: {}", id);
try {
questionnaireIntegrationService.deleteQuestionnaire(id, "admin");
return ApiResponse.success("问卷删除成功");
} catch (Exception e) {
log.error("删除问卷失败, id: {}", id, e);
return ApiResponse.fail("删除问卷失败: " + e.getMessage());
}
}
// ========== 问卷答案查看操作 ==========
/**
* 分页查询问卷答案
*/
@GetMapping("/{id}/answers")
public ApiResponse<PageResponse<ResponseDetailResponse>> getQuestionnaireAnswers(
@PathVariable Long id,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String userId,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime) {
log.info("分页查询问卷答案, questionnaireId: {}, page: {}, pageSize: {}, userId: {}",
id, page, pageSize, userId);
// 参数验证:限制pageSize最大值为100
if (pageSize > 100) {
pageSize = 100;
}
try {
PageResponse<ResponseDetailResponse> response =
questionnaireIntegrationService.getResponseList(page, pageSize, id, userId, startTime, endTime);
return ApiResponse.success(response);
} catch (Exception e) {
log.error("分页查询问卷答案失败, questionnaireId: {}", id, e);
return ApiResponse.fail("分页查询问卷答案失败: " + e.getMessage());
}
}
/**
* 获取特定答案详情
*/
@GetMapping("/{id}/answers/{answerId}")
public ApiResponse<ResponseDetailResponse> getQuestionnaireAnswer(@PathVariable Long id, @PathVariable Long answerId) {
log.info("获取问卷答案详情, questionnaireId: {}, answerId: {}", id, answerId);
try {
ResponseDetailResponse answer = questionnaireIntegrationService.getResponseDetail(answerId);
return ApiResponse.success(answer);
} catch (Exception e) {
log.error("获取问卷答案详情失败, questionnaireId: {}, answerId: {}", id, answerId, e);
return ApiResponse.fail("获取问卷答案详情失败: " + e.getMessage());
}
}
/**
* 查询用户答题记录
*/
@GetMapping("/answers/user/{userId}")
public ApiResponse<PageResponse<ResponseDetailResponse>> getUserAnswers(
@PathVariable String userId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Long questionnaireId) {
log.info("查询用户答题记录, userId: {}, page: {}, pageSize: {}, questionnaireId: {}",
userId, page, pageSize, questionnaireId);
// 参数验证:限制pageSize最大值为100
if (pageSize > 100) {
pageSize = 100;
}
try {
PageResponse<ResponseDetailResponse> response =
questionnaireIntegrationService.getResponseList(page, pageSize, questionnaireId, userId, null, null);
return ApiResponse.success(response);
} catch (Exception e) {
log.error("查询用户答题记录失败, userId: {}", userId, e);
return ApiResponse.fail("查询用户答题记录失败: " + e.getMessage());
}
}
// ========== 统计功能 ==========
/**
* 获取问卷统计信息
*/
@GetMapping("/{id}/statistics")
public ApiResponse<QuestionnaireStatistics> getQuestionnaireStatistics(@PathVariable Long id) {
log.info("获取问卷统计信息, id: {}", id);
try {
QuestionnaireStatistics statistics = questionnaireIntegrationService.getStatistics(id);
return ApiResponse.success(statistics);
} catch (Exception e) {
log.error("获取问卷统计信息失败, id: {}", id, e);
return ApiResponse.fail("获取问卷统计信息失败: " + e.getMessage());
}
}
/**
* 下载问卷小程序二维码
*/
@GetMapping("/{id}/QRCode")
public ApiResponse<String> downloadQrCode(@PathVariable Long id) {
log.info("下载问卷小程序二维码, id: {}", id);
try {
// 获取问卷详情
QuestionnaireResponse questionnaire = questionnaireIntegrationService.getQuestionnaire(id);
if (questionnaire == null) {
return ApiResponse.fail("问卷不存在");
}
MpConfigEntity mpConfig = scenicRepository.getScenicMpConfig(3930324797233434624L);
if (mpConfig == null) {
return ApiResponse.fail("小程序配置不存在");
}
String appId = mpConfig.getAppId();
String appSecret = mpConfig.getAppSecret();
String appState = mpConfig.getState();
String path = "pages/questionnaire/index?id=" + id;
String filePath = "qr_code_questionnaire_" + id + ".jpg";
IStorageAdapter adapter = StorageFactory.use();
if (adapter.isExists(filePath)) {
return ApiResponse.success(adapter.getUrl(filePath));
}
WxMpUtil.generateWXAQRCode(appId, appSecret, appState, path, filePath);
File file = new File(filePath);
String s = adapter.uploadFile(null, file, filePath);
file.delete();
adapter.setAcl(StorageAcl.PUBLIC_READ, filePath);
return ApiResponse.success(s);
} catch (Exception e) {
log.error("生成问卷二维码失败, id: {}", id, e);
return ApiResponse.fail("生成二维码失败: " + e.getMessage());
}
}
}

View File

@@ -7,6 +7,8 @@ import com.ycwl.basic.model.pc.order.req.OrderReqQuery;
import com.ycwl.basic.model.pc.order.resp.OrderRespVO;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -23,38 +25,38 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/refund/v1")
// 退款订单管理
@Api(tags = "退款订单管理")
public class RefundController {
@Autowired
private OrderService orderService;
// 审核退款: 审核退款
@ApiOperation(value = "审核退款", notes = "审核退款")
@PostMapping("/auditRefundOrder")
public ApiResponse<?> auditRefundOrder(@RequestBody RefundOrderReq refundOrderReq) {
return orderService.auditRefundOrder(refundOrderReq);
}
// 分页查询订单
@ApiOperation("分页查询订单")
@PostMapping("page")
public ApiResponse<PageInfo<OrderRespVO>> pageQuery(@RequestBody OrderReqQuery query) {
return orderService.refundPageQuery(query);
}
// 订单列表查询
@ApiOperation("订单列表查询")
@PostMapping("list")
public ApiResponse<List<OrderRespVO>> list(@RequestBody OrderReqQuery query) {
return orderService.list(query);
}
// 订单详情查询
@ApiOperation("订单详情查询")
@GetMapping("detail/{id}")
public ApiResponse<OrderRespVO> detail(@PathVariable("id") Long orderId) {
return orderService.detail(orderId);
}
// 订单备注
@ApiOperation("订单备注")
@PostMapping("remark/{id}")
public ApiResponse<?> updateRemark(@PathVariable("id") Long orderId, @RequestBody OrderEntity query) {
orderService.remarkOrder(orderId, query);

View File

@@ -1,196 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.render.dto.config.BatchRenderWorkerConfigRequest;
import com.ycwl.basic.integration.render.dto.config.RenderWorkerConfigV2DTO;
import com.ycwl.basic.integration.render.service.RenderWorkerConfigIntegrationService;
import com.ycwl.basic.utils.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 渲染工作器配置管理 V2 版本控制器
* 基于 zt-render-worker 微服务标准接口实现
*
* @author Claude Code
* @date 2025-09-06
*/
@Slf4j
@RestController
@RequestMapping("/api/render/worker/config/v2")
@RequiredArgsConstructor
public class RenderWorkerConfigV2Controller {
private final RenderWorkerConfigIntegrationService configIntegrationService;
/**
* 获取工作器所有配置
*
* @param workerId 工作器ID
* @return 工作器配置列表
*/
@GetMapping("/{workerId}")
public ApiResponse<List<RenderWorkerConfigV2DTO>> getWorkerConfigs(@PathVariable Long workerId) {
log.info("获取渲染工作器配置列表, workerId: {}", workerId);
try {
List<RenderWorkerConfigV2DTO> configs = configIntegrationService.getWorkerConfigs(workerId);
return ApiResponse.success(configs);
} catch (Exception e) {
log.error("获取渲染工作器配置列表失败, workerId: {}", workerId, e);
return ApiResponse.fail("获取渲染工作器配置列表失败: " + e.getMessage());
}
}
/**
* 获取工作器平铺配置
*
* @param workerId 工作器ID
* @return 平铺配置Map
*/
@GetMapping("/{workerId}/flat")
public ApiResponse<Map<String, Object>> getWorkerFlatConfig(@PathVariable Long workerId) {
log.info("获取渲染工作器平铺配置, workerId: {}", workerId);
try {
Map<String, Object> flatConfig = configIntegrationService.getWorkerFlatConfig(workerId);
return ApiResponse.success(flatConfig);
} catch (Exception e) {
log.error("获取渲染工作器平铺配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("获取渲染工作器平铺配置失败: " + e.getMessage());
}
}
/**
* 根据配置键获取特定配置
*
* @param workerId 工作器ID
* @param configKey 配置键
* @return 配置信息
*/
@GetMapping("/{workerId}/key/{configKey}")
public ApiResponse<RenderWorkerConfigV2DTO> getWorkerConfigByKey(@PathVariable Long workerId,
@PathVariable String configKey) {
log.info("根据配置键获取渲染工作器配置, workerId: {}, configKey: {}", workerId, configKey);
try {
RenderWorkerConfigV2DTO config = configIntegrationService.getWorkerConfigByKey(workerId, configKey);
return ApiResponse.success(config);
} catch (Exception e) {
log.error("根据配置键获取渲染工作器配置失败, workerId: {}, configKey: {}", workerId, configKey, e);
return ApiResponse.fail("根据配置键获取渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 创建工作器配置
*
* @param workerId 工作器ID
* @param config 配置信息
* @return 创建的配置信息
*/
@PostMapping("/{workerId}")
public ApiResponse<RenderWorkerConfigV2DTO> createWorkerConfig(@PathVariable Long workerId,
@Valid @RequestBody RenderWorkerConfigV2DTO config) {
log.info("创建渲染工作器配置, workerId: {}, configKey: {}", workerId, config.getConfigKey());
try {
RenderWorkerConfigV2DTO result = configIntegrationService.createWorkerConfig(workerId, config);
return ApiResponse.success(result);
} catch (Exception e) {
log.error("创建渲染工作器配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("创建渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 更新工作器配置
*
* @param workerId 工作器ID
* @param configId 配置ID
* @param updates 更新内容
* @return 操作结果
*/
@PutMapping("/{workerId}/{configId}")
public ApiResponse<Void> updateWorkerConfig(@PathVariable Long workerId,
@PathVariable Long configId,
@Valid @RequestBody Map<String, Object> updates) {
log.info("更新渲染工作器配置, workerId: {}, configId: {}", workerId, configId);
try {
configIntegrationService.updateWorkerConfig(workerId, configId, updates);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("更新渲染工作器配置失败, workerId: {}, configId: {}", workerId, configId, e);
return ApiResponse.fail("更新渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 删除工作器配置
*
* @param workerId 工作器ID
* @param configId 配置ID
* @return 操作结果
*/
@DeleteMapping("/{workerId}/{configId}")
public ApiResponse<Void> deleteWorkerConfig(@PathVariable Long workerId,
@PathVariable Long configId) {
log.info("删除渲染工作器配置, workerId: {}, configId: {}", workerId, configId);
try {
configIntegrationService.deleteWorkerConfig(workerId, configId);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("删除渲染工作器配置失败, workerId: {}, configId: {}", workerId, configId, e);
return ApiResponse.fail("删除渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 批量更新工作器配置
*
* @param workerId 工作器ID
* @param request 批量配置请求
* @return 操作结果
*/
@PostMapping("/{workerId}/batch")
public ApiResponse<Void> batchUpdateWorkerConfigs(@PathVariable Long workerId,
@Valid @RequestBody BatchRenderWorkerConfigRequest request) {
log.info("批量更新渲染工作器配置, workerId: {}, configCount: {}",
workerId, request.getConfigs() != null ? request.getConfigs().size() : 0);
try {
configIntegrationService.batchUpdateWorkerConfigs(workerId, request);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("批量更新渲染工作器配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("批量更新渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 批量平铺更新工作器配置
*
* @param workerId 工作器ID
* @param flatConfigs 平铺配置Map
* @return 操作结果
*/
@PostMapping("/{workerId}/flat-batch")
public ApiResponse<Void> batchFlatUpdateWorkerConfigs(@PathVariable Long workerId,
@Valid @RequestBody Map<String, Object> flatConfigs) {
log.info("批量平铺更新渲染工作器配置, workerId: {}, configCount: {}", workerId, flatConfigs.size());
try {
configIntegrationService.batchFlatUpdateWorkerConfigs(workerId, flatConfigs);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("批量平铺更新渲染工作器配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("批量平铺更新渲染工作器配置失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,63 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.pc.renderWorker.req.RenderWorkerReqQuery;
import com.ycwl.basic.service.pc.RenderWorkerService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @Author:longbinbin
* @Date:2024/12/3 14:59
*/
@RestController
@RequestMapping("/api/renderWorker/v1")
@Api(tags = "渲染机管理")
public class RenderWorkerController {
@Autowired
private RenderWorkerService renderWorkerService;
@ApiOperation("分页查询渲染机")
@PostMapping("/page")
public ApiResponse pageQuery(@RequestBody RenderWorkerReqQuery renderWorkerReqQuery){
return renderWorkerService.pageQuery(renderWorkerReqQuery);
}
@ApiOperation("渲染机列表查询")
@PostMapping("/list")
public ApiResponse list(@RequestBody RenderWorkerReqQuery renderWorkerReqQuery){
return renderWorkerService.list(renderWorkerReqQuery);
}
@ApiOperation("渲染机详情查询")
@GetMapping("/detail/{id}")
public ApiResponse detail(@PathVariable Long id){
return renderWorkerService.detail(id);
}
@ApiOperation("渲染机新增")
@PostMapping("/add")
public ApiResponse add(@RequestBody RenderWorkerEntity renderWorker){
return renderWorkerService.add(renderWorker);
}
@ApiOperation("渲染机删除")
@DeleteMapping("/delete/{id}")
public ApiResponse deleteById(@PathVariable Long id){
return renderWorkerService.deleteById(id);
}
@ApiOperation("渲染机修改")
@PostMapping("/update")
public ApiResponse update(@RequestBody RenderWorkerEntity renderWorker){
return renderWorkerService.update(renderWorker);
}
@ApiOperation("渲染机修改状态")
@PutMapping("/updateStatus/{id}")
public ApiResponse updateStatus(@PathVariable Long id) {
return renderWorkerService.updateStatus(id);
}
}

View File

@@ -1,175 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.dto.RenderWorkerWithStatusDTO;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.render.dto.worker.CreateRenderWorkerRequest;
import com.ycwl.basic.integration.render.dto.worker.RenderWorkerV2DTO;
import com.ycwl.basic.integration.render.dto.worker.UpdateRenderWorkerRequest;
import com.ycwl.basic.integration.render.service.RenderWorkerIntegrationService;
import com.ycwl.basic.model.task.req.ClientStatusReqVo;
import com.ycwl.basic.repository.RenderWorkerRepository;
import com.ycwl.basic.utils.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 渲染工作器管理 V2 版本控制器
* 基于 zt-render-worker 微服务标准接口实现
*
* @author Claude Code
* @date 2025-09-06
*/
@Slf4j
@RestController
@RequestMapping("/api/render/worker/v2")
@RequiredArgsConstructor
public class RenderWorkerV2Controller {
private final RenderWorkerIntegrationService renderWorkerIntegrationService;
private final RenderWorkerRepository renderWorkerRepository;
/**
* 分页查询渲染工作器列表(带保活信息)
*
* @param page 页码,从1开始
* @param pageSize 每页大小,默认10,最大100
* @param isEnabled 是否启用(0-禁用,1-启用)
* @param name 工作器名称(模糊搜索)
* @return 分页查询结果(包含保活信息)
*/
@GetMapping
public ApiResponse<PageResponse<RenderWorkerWithStatusDTO>> listWorkers(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer isEnabled,
@RequestParam(required = false) String name) {
log.debug("分页查询渲染工作器列表, page: {}, pageSize: {}, isEnabled: {}, name: {}",
page, pageSize, isEnabled, name);
// 参数验证:限制pageSize最大值为100
if (pageSize > 100) {
pageSize = 100;
}
try {
// 获取基础工作器列表
PageResponse<RenderWorkerV2DTO> basicResult = renderWorkerIntegrationService.listWorkers(
page, pageSize, isEnabled, name);
// 转换为带保活信息的DTO列表
List<RenderWorkerWithStatusDTO> workersWithStatus = new ArrayList<>();
for (RenderWorkerV2DTO worker : basicResult.getList()) {
RenderWorkerWithStatusDTO workerWithStatus = new RenderWorkerWithStatusDTO();
// 复制基础信息
BeanUtils.copyProperties(worker, workerWithStatus);
// 查询保活信息
ClientStatusReqVo hostStatus = renderWorkerRepository.getWorkerHostStatus(worker.getId());
workerWithStatus.setHostStatus(hostStatus);
workerWithStatus.setIsOnline(hostStatus != null);
workersWithStatus.add(workerWithStatus);
}
// 构建带保活信息的分页响应
PageResponse<RenderWorkerWithStatusDTO> result = new PageResponse<>();
result.setList(workersWithStatus);
result.setTotal(basicResult.getTotal());
result.setPage(basicResult.getPage());
result.setPageSize(basicResult.getPageSize());
return ApiResponse.success(result);
} catch (Exception e) {
log.error("分页查询渲染工作器列表失败", e);
return ApiResponse.fail("分页查询渲染工作器列表失败: " + e.getMessage());
}
}
/**
* 根据ID获取渲染工作器详情
*
* @param id 工作器ID
* @return 工作器详情
*/
@GetMapping("/{id}")
public ApiResponse<RenderWorkerV2DTO> getWorker(@PathVariable Long id) {
log.debug("获取渲染工作器详情, id: {}", id);
try {
RenderWorkerV2DTO worker = renderWorkerIntegrationService.getWorker(id);
return ApiResponse.success(worker);
} catch (Exception e) {
log.error("获取渲染工作器详情失败, id: {}", id, e);
return ApiResponse.fail("获取渲染工作器详情失败: " + e.getMessage());
}
}
/**
* 创建渲染工作器
*
* @param request 创建请求
* @return 创建的工作器信息
*/
@PostMapping
public ApiResponse<RenderWorkerV2DTO> createWorker(@Valid @RequestBody CreateRenderWorkerRequest request) {
log.debug("创建渲染工作器, name: {}, key: {}, isActive: {}",
request.getName(), request.getKey(), request.getIsActive());
try {
RenderWorkerV2DTO worker = renderWorkerIntegrationService.createWorker(request);
return ApiResponse.success(worker);
} catch (Exception e) {
log.error("创建渲染工作器失败", e);
return ApiResponse.fail("创建渲染工作器失败: " + e.getMessage());
}
}
/**
* 更新渲染工作器
*
* @param id 工作器ID
* @param request 更新请求
* @return 操作结果
*/
@PutMapping("/{id}")
public ApiResponse<Void> updateWorker(@PathVariable Long id,
@Valid @RequestBody UpdateRenderWorkerRequest request) {
log.debug("更新渲染工作器, id: {}, name: {}, isActive: {}",
id, request.getName(), request.getIsActive());
try {
renderWorkerIntegrationService.updateWorker(id, request);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("更新渲染工作器失败, id: {}", id, e);
return ApiResponse.fail("更新渲染工作器失败: " + e.getMessage());
}
}
/**
* 删除渲染工作器
*
* @param id 工作器ID
* @return 操作结果
*/
@DeleteMapping("/{id}")
public ApiResponse<Void> deleteWorker(@PathVariable Long id) {
log.debug("删除渲染工作器, id: {}", id);
try {
renderWorkerIntegrationService.deleteWorker(id);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("删除渲染工作器失败, id: {}", id, e);
return ApiResponse.fail("删除渲染工作器失败: " + e.getMessage());
}
}
}

View File

@@ -2,62 +2,56 @@ package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.pc.permission.resp.PermissionResp;
import com.ycwl.basic.model.pc.role.req.AddOrUpdateRoleReqVO;
import com.ycwl.basic.model.pc.role.req.RoleListReqVO;
import com.ycwl.basic.model.pc.role.resp.RoleListRespVO;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import com.ycwl.basic.service.pc.RoleService;
import com.ycwl.basic.utils.ApiResponse;
import org.apache.commons.lang3.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/api/role/v1")
// 系统角色管理
@Api(tags = "系统角色管理")
public class RoleController {
@Autowired
RoleService roleService;
@PostMapping(value = "/page")
// 角色列表分页查询
@ApiOperation(value = "角色列表分页查询")
@IgnoreToken
public ApiResponse<PageInfo<RoleListRespVO>> page(@RequestBody RoleListReqVO roleListReqVO) {
return roleService.pageQuery(roleListReqVO);
}
@PostMapping(value = "/list")
// 角色列表
@ApiOperation(value = "角色列表")
@IgnoreToken
public ApiResponse<List<RoleListRespVO>> list(@RequestBody RoleListReqVO roleListReqVO) {
return roleService.list(roleListReqVO);
}
@GetMapping("/{roleId}/permission")
// 角色权限列表
public ApiResponse<PermissionResp> getPermissionByRoleId(@PathVariable("roleId") Long roleId) {
RolePermissionResp permission = roleService.getPermissionByRoleId(roleId);
if (permission == null) {
return ApiResponse.fail("角色不存在");
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermStr(), ",")), Arrays.asList(StringUtils.split(permission.getMenuStr(), ","))));
}
@PostMapping(value = "/addOrUpdate")
// 添加或更新角色
@ApiOperation(value = "添加或更新角色")
@IgnoreToken
public ApiResponse addOrUpdate(@RequestBody AddOrUpdateRoleReqVO addOrUpdateRoleReqVO) {
return roleService.addOrUpdate(addOrUpdateRoleReqVO);
}
@GetMapping(value = "/delete/{id}")
// 删除
@ApiOperation(value = "删除")
@IgnoreToken
public ApiResponse delete(@PathVariable("id") String id) {
return roleService.delete(id);
}
@GetMapping(value = "/updateReturnMenu/{id}")
// 编辑回显该角色当前菜单
@ApiOperation(value = "编辑回显该角色当前菜单")
@IgnoreToken
public ApiResponse updateReturnMenu(@PathVariable("id") String id) {
return roleService.updateReturnMenu(id);
}
@@ -65,7 +59,8 @@ public class RoleController {
@GetMapping(value = "/updateStatus/{id}")
// 更改角色类型状态
@ApiOperation(value = "更改角色类型状态")
//@IgnoreToken
public ApiResponse updateStatus(@PathVariable("id") String id) {
return roleService.updateStatus(id);
}

View File

@@ -39,13 +39,6 @@ public class ScenicAccountController {
return result > 0 ? ApiResponse.success("更新成功") : ApiResponse.fail("更新失败");
}
// 激活/停用景区账号
@PostMapping("/updateActiveStatus/{id}")
public ApiResponse updateActiveStatus(@PathVariable Long id) {
int result = service.updateActiveStatus(id);
return result > 0 ? ApiResponse.success("操作成功") : ApiResponse.fail("操作失败");
}
// 更新景区账号
@PostMapping("/update")
public ApiResponse updateScenicAccount(@RequestBody ScenicAccountEntity entity) {

View File

@@ -1,12 +1,13 @@
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.repository.ScenicRepository;
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
import com.ycwl.basic.service.mobile.AppScenicService;
import com.ycwl.basic.service.mobile.AppStatisticsService;
import com.ycwl.basic.service.pc.ScenicAccountService;
@@ -16,20 +17,18 @@ 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 org.apache.commons.lang3.Strings;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.*;
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;
/**
@@ -38,7 +37,7 @@ import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
*/
@RestController
@RequestMapping("/api/scenic/v1")
// 景区管理
@Api(tags = "景区管理")
public class ScenicController {
@Autowired
@@ -53,7 +52,69 @@ public class ScenicController {
@Autowired
private ScenicAccountService accountService;
// 根据景区ID下载小程序二维码
@ApiOperation("分页查询景区")
@PostMapping("/page")
public ApiResponse<PageInfo<ScenicRespVO>> pageQuery(@RequestBody ScenicReqQuery scenicReqQuery) {
return scenicService.pageQuery(scenicReqQuery);
}
@ApiOperation("查询景区列表")
@PostMapping("/list")
public ApiResponse<List<ScenicRespVO>> list(@RequestBody ScenicReqQuery scenicReqQuery) {
return scenicService.list(scenicReqQuery);
}
@ApiOperation("查询景区详情")
@GetMapping("/getDetail/{id}")
public ApiResponse<ScenicRespVO> getDetail(@PathVariable Long id) {
return scenicService.getById(id);
}
@ApiOperation("新增景区")
@PostMapping("/add")
public ApiResponse<Boolean> add(@RequestBody ScenicAddOrUpdateReq scenicAddReq) {
return scenicService.add(scenicAddReq);
}
@ApiOperation("删除景区")
@GetMapping("/delete/{id}")
public ApiResponse<Boolean> delete(@PathVariable Long id) {
return scenicService.deleteById(id);
}
@ApiOperation("修改景区")
@PostMapping("/update")
public ApiResponse<Boolean> update(@RequestBody ScenicAddOrUpdateReq scenicAddReq) {
return scenicService.update(scenicAddReq);
}
@ApiOperation("修改景区状态")
@GetMapping("/updateStatus/{id}")
public ApiResponse<Boolean> updateStatus(@PathVariable Long id) {
return scenicService.updateStatus(id);
}
@ApiOperation("新增景区配置")
@PostMapping("/addConfig")
public ApiResponse<Boolean> addConfig(@RequestBody ScenicConfigEntity scenicConfig) {
return scenicService.addConfig(scenicConfig);
}
@ApiOperation("修改景区配置")
@PostMapping("/updateConfig")
public ApiResponse<Boolean> updateConfig(@RequestBody ScenicConfigEntity scenicConfig) {
return scenicService.updateConfigById(scenicConfig);
}
@ApiOperation("查询景区配置")
@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);
}
@ApiOperation("根据景区ID下载小程序二维码")
@GetMapping("/{id}/QRCode")
public ApiResponse<String> downloadQrCode(@PathVariable Long id) {
MpConfigEntity mpConfig = scenicRepository.getScenicMpConfig(id);
@@ -108,19 +169,19 @@ public class ScenicController {
}
@GetMapping("/myScenicList")
public ApiResponse<List<ScenicV2DTO>> myScenicList() {
List<ScenicV2DTO> list = Collections.emptyList();
if (Strings.CS.equals(BaseContextHandler.getRoleId(), MERCHANT.type)) {
public ApiResponse<List<ScenicRespVO>> myScenicList() {
List<ScenicRespVO> list = Collections.emptyList();
if (StringUtils.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 -> scenicRepository.getScenicBasic(id)).toList();
} else if (Strings.CS.equals(BaseContextHandler.getRoleId(), ADMIN.type)) {
ScenicReqQuery query = new ScenicReqQuery();
query.setPageSize(1000);
list = scenicRepository.list(query);
list = account.getScenicId().stream().map(id -> {
return appScenicService.getDetails(id).getData();
}).toList();
} else {
list = scenicService.list(new ScenicReqQuery()).getData();
}
return ApiResponse.success(list);
}

View File

@@ -1,264 +0,0 @@
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.common.response.PageResponse;
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<PageResponse<ScenicV2DTO>> 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 {
PageResponse<ScenicV2DTO> response = scenicIntegrationService.listScenics(page, pageSize, status, name, null);
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<PageResponse<ScenicV2DTO>> listScenicsByStatus(@RequestParam(required = false) Integer status) {
log.info("查询景区列表, status: {}", status);
try {
// 默认查询1000条数据,第1页
PageResponse<ScenicV2DTO> scenics = scenicIntegrationService.listScenics(1, 1000, status, null, null);
return ApiResponse.success(scenics);
} catch (Exception e) {
log.error("查询景区列表失败, status: {}", status, 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());
}
}
}

View File

@@ -7,6 +7,8 @@ import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
import com.ycwl.basic.service.pc.SourceService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -16,19 +18,20 @@ import org.springframework.web.bind.annotation.*;
*/
@RestController
@RequestMapping("/api/source/v1")
// 视频源管理
@Api(tags = "视频源管理")
public class SourceController {
@Autowired
private SourceService sourceService;
// 分页查询视频源
@Deprecated
@ApiOperation("分页查询视频源")
@PostMapping("/page")
public ApiResponse pageQuery(@RequestBody SourceReqQuery sourceReqQuery) {
return sourceService.pageQuery(sourceReqQuery);
}
@Deprecated
// 查询视频源列表
@ApiOperation("查询视频源列表")
@PostMapping("/list")
public ApiResponse list(@RequestBody SourceReqQuery sourceReqQuery) {
return sourceService.list(sourceReqQuery);
@@ -39,7 +42,7 @@ public class SourceController {
return sourceService.cutVideo(id);
}
@Deprecated
// 删除视频源
@ApiOperation("删除视频源")
@DeleteMapping("/delete/{id}")
public ApiResponse deleteById(@PathVariable Long id) {
return sourceService.deleteById(id);

View File

@@ -1,57 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
import com.ycwl.basic.model.pc.statistics.resp.OrderStatisticsResp;
import com.ycwl.basic.service.mobile.AppStatisticsService;
import com.ycwl.basic.service.pc.StatisticsService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/12 14:30
*/
@RestController
@RequestMapping("/api/statistics/v1")
public class StatisticsController {
@Autowired
private StatisticsService statisticsService;
@Autowired
private AppStatisticsService appStatisticsService;
/**
* 智能获取扫码访问人数统计数据(自动选择粒度)
* @param query 查询参数(包含景区ID、开始时间、结束时间;如果scenicId为空则统计全部景区,否则统计指定景区)
* @return 统计数据(超过7天返回日期级别,否则返回小时级别)
*/
@PostMapping("/scanCodeMemberChart")
public ApiResponse<List<HashMap<String, String>>> getScanCodeMemberChart(@RequestBody CommonQueryReq query) {
return ApiResponse.success(statisticsService.getScanCodeMemberChartAuto(query));
}
@PostMapping("/one")
public ApiResponse getStatisticsOne(@RequestBody CommonQueryReq query) {
return appStatisticsService.oneStatistics(query);
}
@PostMapping("/two")
public ApiResponse getStatisticsTwo(@RequestBody CommonQueryReq query) {
return appStatisticsService.twoStatistics(query);
}
/**
* 获取订单统计数据(包含订单数量和金额、推送订单数量、现场订单数量)
* @param query 查询参数(包含景区ID、开始时间、结束时间;如果scenicId为空则统计全部景区,否则统计指定景区)
* @return 统计数据(totalOrderCount: 总订单数量, totalOrderAmount: 总订单金额, pushOrderCount: 推送订单数量, sceneOrderCount: 现场订单数量)
*/
@PostMapping("/orderStatistics")
public ApiResponse<OrderStatisticsResp> getOrderStatistics(@RequestBody CommonQueryReq query) {
return ApiResponse.success(statisticsService.getOrderStatistics(query));
}
}

View File

@@ -4,6 +4,8 @@ import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.task.req.TaskReqQuery;
import com.ycwl.basic.service.pc.TaskService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -13,38 +15,39 @@ import org.springframework.web.bind.annotation.*;
*/
@RestController
@RequestMapping("/api/task/v1")
// 任务列表管理
@Deprecated
@Api(tags = "任务列表管理")
public class TaskController {
@Autowired
private TaskService taskService;
// 分页查询任务列表
@ApiOperation("分页查询任务列表")
@PostMapping("/page")
public ApiResponse pageQuery(@RequestBody TaskReqQuery taskReqQuery) {
return taskService.pageQuery(taskReqQuery);
}
// 查询任务列表
@ApiOperation("查询任务列表")
@PostMapping("/list")
public ApiResponse list(@RequestBody TaskReqQuery taskReqQuery) {
return taskService.list(taskReqQuery);
}
// 查询任务详情
@ApiOperation("查询任务详情")
@GetMapping("/getDetail/{id}")
public ApiResponse getById(@PathVariable Long id) {
return taskService.getById(id);
}
// 删除任务
@ApiOperation("删除任务")
@DeleteMapping("/delete/{id}")
public ApiResponse deleteById(@PathVariable Long id) {
return taskService.deleteById(id);
}
// 修改任务
@ApiOperation("修改任务")
@PostMapping("/update")
public ApiResponse update(@RequestBody TaskEntity taskEntity) {
return taskService.update(taskEntity);
}
// 修改任务状态
@ApiOperation("修改任务状态")
@PostMapping("/updateStatus")
public ApiResponse updateStatus(@RequestParam Long id, @RequestParam Integer status) {
return taskService.updateStatus(id,status);

View File

@@ -9,6 +9,8 @@ import com.ycwl.basic.model.pc.template.req.TemplateSortRequest;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.service.pc.TemplateService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -20,60 +22,54 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/template/v1")
// 模板管理
@Api(tags = "模板管理")
public class TemplateController {
@Autowired
private TemplateService templateService;
// 分页查询模板
@ApiOperation("分页查询模板")
@PostMapping("/page")
public ApiResponse<PageInfo<TemplateRespVO>> pageQuery(@RequestBody TemplateReqQuery templateReqQuery) {
return templateService.pageQuery(templateReqQuery);
}
// 查询模板列表
@ApiOperation("查询模板列表")
@PostMapping("/list")
public ApiResponse<List<TemplateRespVO>> list(@RequestBody TemplateReqQuery templateReqQuery) {
return templateService.list(templateReqQuery);
}
// 查询模板详情
@ApiOperation("查询模板详情")
@GetMapping("getDetail/{id}")
public ApiResponse<TemplateRespVO> getById(@PathVariable Long id) {
return templateService.getById(id);
}
// 添加模板
@ApiOperation("添加模板")
@PostMapping("/add")
public ApiResponse<Boolean> add(@RequestBody TemplateEntity template) {
return templateService.add(template);
}
// 删除模板
@ApiOperation("删除模板")
@DeleteMapping("/delete/{id}")
public ApiResponse<Integer> deleteById(@PathVariable Long id) {
return templateService.deleteById(id);
}
// 修改模板
@ApiOperation("修改模板")
@PostMapping("/update")
public ApiResponse<Boolean> update(@RequestBody TemplateEntity template) {
return templateService.update(template);
}
// 修改模板状态
@ApiOperation("修改模板状态")
@PostMapping("/updateStatus/{id}")
public ApiResponse<Boolean> updateStatus(@PathVariable("id") Long id) {
return templateService.updateStatus(id);
}
// 排序模板
@ApiOperation("排序模板")
@PostMapping("/sort")
public ApiResponse<Boolean> sortTemplate(@RequestBody TemplateSortRequest request) {
return templateService.sortTemplate(request.getTemplateId(), request.getAfterTemplateId());
}
// 修改模板排序值
@PostMapping("/updateSort/{id}")
public ApiResponse<Boolean> updateSort(@PathVariable("id") Long id, @RequestParam Integer sort) {
return templateService.updateSort(id, sort);
}
@GetMapping("/config/{id}")
public ApiResponse<TemplateConfigEntity> getConfig(@PathVariable("id") Long id) {
return ApiResponse.success(templateService.getConfig(id));

View File

@@ -6,6 +6,8 @@ import com.ycwl.basic.model.pc.video.req.VideoReqQuery;
import com.ycwl.basic.model.pc.video.resp.VideoRespVO;
import com.ycwl.basic.service.pc.VideoService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -18,38 +20,27 @@ import java.util.List;
@RestController
@RequestMapping("/api/video/v1")
// 视频成片管理
@Deprecated
@Api(tags = "视频成片管理")
public class VideoController {
@Autowired
private VideoService videoService;
// 分页查询成片
@ApiOperation("分页查询成片")
@PostMapping("/page")
public ApiResponse<PageInfo<VideoRespVO>> pageQuery(@RequestBody VideoReqQuery videoReqQuery) {
return videoService.pageQuery(videoReqQuery);
}
// 查询成片列表
@ApiOperation("查询成片列表")
@PostMapping("/list")
public ApiResponse<List<VideoRespVO>> list(@RequestBody VideoReqQuery videoReqQuery) {
return videoService.list(videoReqQuery);
}
// 查询成片详情
@ApiOperation("查询成片详情")
@GetMapping("/getDetail/{id}")
public ApiResponse<VideoRespVO> getById(@PathVariable Long id) {
return videoService.getById(id);
}
/**
* 查询视频是否被购买
*
* @param videoId 视频ID
* @return 是否已购买
*/
@GetMapping("/checkBuyStatus")
public ApiResponse<Boolean> checkBuyStatus(@RequestParam("videoId") Long videoId) {
Boolean isBuy = videoService.checkVideoBuyStatus(videoId);
return ApiResponse.success(isBuy);
}
}

View File

@@ -7,13 +7,14 @@ import com.ycwl.basic.model.printer.resp.PrintTaskResp;
import com.ycwl.basic.model.printer.resp.TaskSyncResp;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@IgnoreToken
// 打印机对接接口
@Api(tags = "打印机对接接口")
@RestController
@RequestMapping("/printer/v1")
public class PrinterTaskController {

View File

@@ -1,277 +0,0 @@
package com.ycwl.basic.controller.printer;
import cn.hutool.core.date.DateUtil;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.repository.MemberRelationRepository;
import com.ycwl.basic.repository.SourceRepository;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.service.task.TaskFaceService;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.printer.FaceRecognizeWithSourcesResp;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.storage.utils.StorageUtil;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.SnowFlakeUtil;
import com.ycwl.basic.utils.WxMpUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import static com.ycwl.basic.constant.StorageConstant.USER_FACE;
@IgnoreToken
// 打印机大屏对接接口
@RestController
@RequestMapping("/printer/v1/tv")
@RequiredArgsConstructor
public class PrinterTvController {
private final DeviceRepository deviceRepository;
private final ScenicRepository scenicRepository;
private final FaceRepository faceRepository;
private final TaskFaceService faceService;
private final FaceService pcFaceService;
private final ScenicService scenicService;
private final SourceMapper sourceMapper;
private final FaceMapper faceMapper;
private final MemberRelationRepository memberRelationRepository;
private final SourceRepository sourceRepository;
private final PrinterService printerService;
/**
* 获取景区列表
*
* @return 景区列表
*/
@GetMapping("/scenic/list")
public ApiResponse<List<ScenicV2DTO>> getScenicList() {
ScenicReqQuery query = new ScenicReqQuery();
query.setStatus("1"); // 只查询启用状态的景区
query.setPageNum(1);
query.setPageSize(1000);
return ApiResponse.success(scenicRepository.list(query));
}
/**
* 根据景区ID查询设备列表
*
* @param scenicId 景区ID
* @return 设备列表
*/
@GetMapping("/device/list")
public ApiResponse<List<DeviceV2DTO>> getDeviceListByScenicId(@RequestParam Long scenicId) {
List<DeviceV2DTO> result = deviceRepository.getAllDeviceByScenicId(scenicId);
return ApiResponse.success(result);
}
@GetMapping("/{sampleId}/qrcode")
public void getQrcode(@PathVariable("sampleId") Long sampleId, HttpServletResponse response) throws Exception {
File qrcode = new File("qrcode_"+sampleId+".jpg");
FaceSampleEntity faceSample = faceRepository.getFaceSample(sampleId);
if (faceSample == null) {
response.setStatus(404);
return;
}
String targetPath = "pages/printer/from_sample";
DeviceV2DTO device = deviceRepository.getDeviceBasic(faceSample.getDeviceId());
if (device.getType().equals("AI_CAM")) {
// AI_CAM,需要修改path
targetPath = "pages/ai-cam/from_sample";
}
try {
MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(faceSample.getScenicId());
WxMpUtil.generateUnlimitedWXAQRCode(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), targetPath, sampleId.toString(), qrcode);
// 设置响应头
response.setContentType("image/jpeg");
response.setHeader("Content-Disposition", "inline; filename=\"" + qrcode.getName() + "\"");
// 将二维码文件写入响应输出流
try (FileInputStream fis = new FileInputStream(qrcode);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
}
} finally {
// 删除临时文件
if (qrcode.exists()) {
qrcode.delete();
}
}
}
/**
* 获取人脸绑定二维码
* 生成小程序二维码,用于绑定人脸到用户账号
*
* @param faceId 人脸ID
* @param response HTTP响应
*/
@GetMapping("/face/{faceId}/qrcode")
public void getFaceQrcode(@PathVariable("faceId") Long faceId, HttpServletResponse response) throws Exception {
File qrcode = new File("qrcode_face_" + faceId + ".jpg");
try {
FaceEntity face = faceRepository.getFace(faceId);
if (face == null) {
response.setStatus(404);
return;
}
MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(face.getScenicId());
if (scenicMpConfig == null) {
response.setStatus(500);
return;
}
WxMpUtil.generateUnlimitedWXAQRCode(
scenicMpConfig.getAppId(),
scenicMpConfig.getAppSecret(),
"pages/videoSynthesis/bind_face",
faceId.toString(),
qrcode
);
// 设置响应头
response.setContentType("image/jpeg");
response.setHeader("Content-Disposition", "inline; filename=\"" + qrcode.getName() + "\"");
// 将二维码文件写入响应输出流
try (FileInputStream fis = new FileInputStream(qrcode);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
}
} finally {
// 删除临时文件
if (qrcode.exists()) {
qrcode.delete();
}
}
}
/**
* 根据人脸样本ID查询图像素材
*
* @param faceSampleId 人脸样本ID
* @return type=2且face_sample_id匹配的source记录
*/
@GetMapping("/{faceSampleId}/source")
public ApiResponse<SourceEntity> getSourceByFaceSampleId(@PathVariable Long faceSampleId) {
SourceEntity source = sourceMapper.getBySampleIdAndType(faceSampleId, 2);
if (source == null) {
return ApiResponse.fail("未找到对应的图像素材");
}
return ApiResponse.success(source);
}
/**
* 打印机大屏人脸识别
* 上传照片,在景区人脸库中搜索匹配的人脸样本,返回识别结果和匹配到的图像素材
*
* @param file 人脸照片文件
* @param scenicId 景区ID
* @return 人脸识别结果和匹配的source列表
*/
@PostMapping("/{scenicId}/faceRecognize")
public ApiResponse<FaceRecognizeWithSourcesResp> faceRecognize(
@RequestParam("file") MultipartFile file,
@PathVariable Long scenicId) throws Exception {
// 1. 上传人脸照片到存储
IStorageAdapter adapter = StorageFactory.use("faces");
String filePath = StorageUtil.joinPath(USER_FACE, DateUtil.format(new Date(), "yyyy-MM-dd"));
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.split("\\.", 2)[1];
String fileName = UUID.randomUUID() + "." + suffix;
String faceUrl = adapter.uploadFile(file, filePath, fileName);
// 2. 保存人脸数据到数据库
Long faceId = SnowFlakeUtil.getLongId();
FaceEntity faceEntity = new FaceEntity();
faceEntity.setId(faceId);
faceEntity.setScenicId(scenicId);
faceEntity.setFaceUrl(faceUrl);
faceEntity.setCreateAt(new Date());
faceEntity.setMemberId(0L); // 打印机大屏端没有用户ID
faceMapper.add(faceEntity);
// 3. 在景区人脸库中搜索(注意:这里使用scenicId作为数据库名,搜索的是景区内的人脸样本)
pcFaceService.matchFaceId(faceId);
// 4. 自动添加照片到打印列表,并获取添加成功的照片列表
List<SourceEntity> addedSources = printerService.autoAddPhotosToPreferPrint(faceId);
// 5. 根据自动添加结果决定返回的sources
List<SourceEntity> sources;
if (addedSources != null && !addedSources.isEmpty()) {
// 如果自动添加成功,返回添加的照片列表
sources = addedSources;
} else {
// 如果自动添加为空,按原逻辑查询匹配到的图像素材(type=2)
sources = new ArrayList<>();
List<MemberSourceEntity> memberSourceEntities = memberRelationRepository.listSourceByFaceRelation(faceId, 2);
for (MemberSourceEntity memberSourceEntity : memberSourceEntities) {
SourceEntity source = sourceRepository.getSource(memberSourceEntity.getSourceId());
if (source != null) {
sources.add(source);
}
}
}
// 6. 构造响应
FaceRecognizeWithSourcesResp resp = new FaceRecognizeWithSourcesResp();
resp.setUrl(faceUrl);
resp.setFaceId(faceId);
resp.setScenicId(scenicId);
resp.setSources(sources);
// 只有当添加了照片时才返回二维码URL
if (addedSources != null && !addedSources.isEmpty()) {
resp.setQrcodeUrl("https://zhentuai.com/printer/v1/tv/face/" + faceId + "/qrcode");
} else {
resp.setQrcodeUrl(null);
}
return ApiResponse.success(resp);
}
}

View File

@@ -10,6 +10,7 @@ import com.ycwl.basic.model.task.req.WorkerAuthReqVo;
import com.ycwl.basic.model.task.resp.TaskSyncRespVo;
import com.ycwl.basic.service.task.TaskService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -20,7 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
@IgnoreToken
@RestController
// 渲染端对接接口
@Api(tags = "渲染端对接接口")
@RequestMapping("/task/v1/")
public class TaskTaskController {

View File

@@ -0,0 +1,408 @@
package com.ycwl.basic.controller.viid;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.ycwl.basic.annotation.IgnoreLogReq;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.facebody.entity.AddFaceResp;
import com.ycwl.basic.mapper.DeviceMapper;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.viid.entity.DeviceIdObject;
import com.ycwl.basic.model.viid.entity.FaceListObject;
import com.ycwl.basic.model.viid.entity.FaceObject;
import com.ycwl.basic.model.viid.entity.FacePositionObject;
import com.ycwl.basic.model.viid.entity.ResponseStatusObject;
import com.ycwl.basic.model.viid.entity.SubImageInfoObject;
import com.ycwl.basic.model.viid.entity.SubImageList;
import com.ycwl.basic.model.viid.entity.SystemTimeObject;
import com.ycwl.basic.model.viid.req.FaceUploadReq;
import com.ycwl.basic.model.viid.req.ImageUploadReq;
import com.ycwl.basic.model.viid.req.KeepaliveReq;
import com.ycwl.basic.model.viid.req.RegisterReq;
import com.ycwl.basic.model.viid.req.UnRegisterReq;
import com.ycwl.basic.model.viid.resp.SystemTimeResp;
import com.ycwl.basic.model.viid.resp.VIIDBaseResp;
import com.ycwl.basic.repository.DeviceRepository;
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.enums.StorageAcl;
import com.ycwl.basic.storage.utils.StorageUtil;
import com.ycwl.basic.task.DynamicTaskGenerator;
import com.ycwl.basic.utils.ImageUtils;
import com.ycwl.basic.utils.IpUtils;
import com.ycwl.basic.utils.SnowFlakeUtil;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.ycwl.basic.constant.StorageConstant.PHOTO_PATH;
import static com.ycwl.basic.constant.StorageConstant.VIID_FACE;
@IgnoreToken
@RestController
@Api(tags = "摄像头对接接口")
@RequestMapping("/VIID")
@Slf4j
public class ViidController {
@Autowired
private DeviceMapper deviceMapper;
private static final String serverId = "00000000000000000001";
@Autowired
private SourceMapper sourceMapper;
@Autowired
private DeviceRepository deviceRepository;
@Autowired
private ScenicRepository scenicRepository;
@Autowired
private TaskFaceService taskFaceService;
private final Map<Long, ThreadPoolExecutor> executors = new ConcurrentHashMap<>();
@Autowired
private ScenicService scenicService;
private ThreadPoolExecutor getExecutor(Long scenicId) {
return executors.computeIfAbsent(scenicId, k -> {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNamePrefix("VIID-" + scenicId + "-t")
.build();
return new ThreadPoolExecutor(
4, 1024, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1024),
threadFactory);
});
}
// region 注册注销基础接口
/**
* 注册接口
*
* @param req 注册的信息
* @param request 请求
* @return 返回
*/
@RequestMapping(value = "/System/Register", method = RequestMethod.POST)
public VIIDBaseResp register(@RequestBody RegisterReq req, HttpServletRequest request) {
DeviceIdObject deviceIdObject = req.getRegisterObject();
log.info("注册的设备信息:{}", deviceIdObject);
// 保存设备注册时间
String deviceId = deviceIdObject.getDeviceId();
DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceId);
if (device == null) {
device = new DeviceEntity();
device.setName("未配置设备");
device.setNo(deviceId);
device.setOnline(1);
}
device.setKeepaliveAt(new Date());
device.setIpAddr(IpUtils.getIpAddr(request));
if (device.getId() != null) {
deviceMapper.updateEntity(device);
} else {
device.setId(SnowFlakeUtil.getLongId());
deviceMapper.addEntity(device);
deviceRepository.clearDeviceCache(deviceId);
}
return new VIIDBaseResp(
new ResponseStatusObject(serverId, "/VIID/System/Register", "0", "注册成功", sdfTime.format(new Date()))
);
}
/**
* 保活接口
*
* @param req 保活的设备信息
* @param request 请求
* @return 返回
*/
@IgnoreLogReq
@RequestMapping(value = "/System/Keepalive", method = RequestMethod.POST)
public VIIDBaseResp keepalive(@RequestBody KeepaliveReq req, HttpServletRequest request) {
DeviceIdObject keepaliveObject = req.getKeepaliveObject();
// log.info("对方发送的心跳的信息:{}", keepaliveObject);
String deviceId = keepaliveObject.getDeviceId();
DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceId);
// 判断设备状态
if (device == null) {
// 不存在设备就注册
device = new DeviceEntity();
device.setName("未配置设备");
device.setNo(deviceId);
device.setOnline(1);
device.setKeepaliveAt(new Date());
device.setIpAddr(IpUtils.getIpAddr(request));
device.setId(SnowFlakeUtil.getLongId());
deviceMapper.addEntity(device);
deviceRepository.clearDeviceCache(deviceId);
} else {
deviceRepository.updateOnlineStatus(device.getId(), IpUtils.getIpAddr(request), 1, new Date());
}
// log.info("已经解析过的心跳信息:{}", keepaliveObject);
return new VIIDBaseResp(
new ResponseStatusObject(deviceId, "/VIID/System/Keepalive", "0", "保活", sdfTime.format(new Date()))
);
}
/**
* 注销设备
*
* @param req 参数
* @return 返回
*/
@RequestMapping(value = "/System/UnRegister", method = RequestMethod.POST)
public VIIDBaseResp unRegister(@RequestBody UnRegisterReq req, HttpServletRequest request) {
// 获取设备id
DeviceIdObject unRegisterObject = req.getUnRegisterObject();
String deviceId = unRegisterObject.getDeviceId();
log.info("获取的注销的请求参数:{}", unRegisterObject);
// 首先查询该设备是否存在
DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceId);
// 判断
if (device != null) {
deviceRepository.updateOnlineStatus(device.getId(), IpUtils.getIpAddr(request), 0, new Date());
}
return new VIIDBaseResp(
new ResponseStatusObject(deviceId, "/VIID/System/UnRegister", "0", "注销成功", sdfTime.format(new Date()))
);
}
/**
* 校时接口
*
* @return 返回
*/
@RequestMapping(value = "/System/Time", method = RequestMethod.GET)
public SystemTimeResp time() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
return new SystemTimeResp(
new SystemTimeObject(serverId, "2", sdf.format(new Date()), TimeZone.getTimeZone("Asia/Shanghai").toString())
);
}
// endregion
@Autowired
private FaceSampleMapper faceSampleMapper;
private final SimpleDateFormat sdfTime = new SimpleDateFormat("yyyyMMddHHmmss");
/**
* 批量新增人脸
*/
@RequestMapping(value = "/Faces", method = RequestMethod.POST)
@IgnoreLogReq
public VIIDBaseResp faces(@RequestBody FaceUploadReq req) {
FaceListObject faceListObject = req.getFaceListObject();
List<FaceObject> faceObject = faceListObject.getFaceObject();
String faceId = null;
// 遍历人脸列表
for (FaceObject face : faceObject) {
// 设置FaceId
faceId = face.getFaceID();
// 获取图片信息
SubImageList subImageList = face.getSubImageList();
// 判断人脸对象中的列表是否为空
String deviceID = face.getDeviceID();
DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceID);
if (device == null) {
continue;
}
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(device.getId());
int viidMode = 0;
if (deviceConfig != null && deviceConfig.getViidType() != null) {
viidMode = deviceConfig.getViidType();
}
Date shotTime = null;
if (StringUtils.isNotBlank(face.getShotTime())) {
try {
shotTime = sdfTime.parse(face.getShotTime());
} catch (ParseException e) {
log.warn("拍摄时间时间转换失败,使用当前时间。错误entity:{}", face);
}
}
if (shotTime == null) {
if (StringUtils.isNotBlank(face.getFaceAppearTime())) {
try {
shotTime = sdfTime.parse(face.getFaceAppearTime());
} catch (ParseException e) {
log.warn("拍摄时间时间转换失败,使用当前时间。错误entity:{}", face);
}
}
}
if (shotTime == null) {
shotTime = new Date();
} else if (Math.abs(shotTime.getTime() - System.currentTimeMillis()) > 24 * 60 * 60 * 1000) {
shotTime = new Date();
}
Long scenicId = device.getScenicId();
if (scenicId == null) {
continue;
}
IStorageAdapter scenicStorageAdapter = scenicService.getScenicStorageAdapter(scenicId);
IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(scenicId);
FacePositionObject facePosition = new FacePositionObject();
facePosition.setLtY(face.getLeftTopY());
facePosition.setLtX(face.getLeftTopX());
facePosition.setRbY(face.getRightBtmY());
facePosition.setRbX(face.getRightBtmX());
if (ObjectUtil.isNotEmpty(subImageList) && CollUtil.isNotEmpty(subImageList.getSubImageInfoObject())) {
if (viidMode == 0) {
// 遍历每个图片对象
// 先找到type14的图片
List<SubImageInfoObject> type14ImageList = subImageList.getSubImageInfoObject().stream().filter(subImage -> "14".equals(subImage.getType())).collect(Collectors.toList());
for (SubImageInfoObject subImage : subImageList.getSubImageInfoObject()) {
// base64转换成MultipartFIle
MultipartFile file = ImageUtils.base64ToMultipartFile(subImage.getData());
String ext = subImage.getFileFormat();
if (ext.equalsIgnoreCase("jpeg")) {
ext = "jpg";
}
IStorageAdapter adapter = StorageFactory.use("faces");
// Type=11 人脸
if (subImage.getType().equals("11")) {
// 上传oss
Long newFaceSampleId = SnowFlakeUtil.getLongId();
if (Integer.valueOf(1).equals(device.getStatus())) {
FaceSampleEntity faceSample = new FaceSampleEntity();
faceSample.setId(newFaceSampleId);
faceSample.setScenicId(scenicId);
faceSample.setDeviceId(device.getId());
faceSample.setStatus(0);
faceSample.setCreateAt(shotTime);
String url = adapter.uploadFile(file, VIID_FACE, UUID.randomUUID() + "." + ext);
faceSample.setFaceUrl(url);
faceSampleMapper.add(faceSample);
ThreadPoolExecutor executor = getExecutor(scenicId);
executor.execute(() -> {
if (faceBodyAdapter != null) {
taskFaceService.assureFaceDb(faceBodyAdapter, scenicId.toString());
AddFaceResp addFaceResp = faceBodyAdapter.addFace(scenicId.toString(), faceSample.getId().toString(), url, newFaceSampleId.toString());
if (addFaceResp != null) {
faceSample.setScore(addFaceResp.getScore());
faceSampleMapper.updateScore(faceSample.getId(), addFaceResp.getScore());
}
}
if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
DynamicTaskGenerator.addTask(faceSample.getId());
}
});
}
for (SubImageInfoObject _subImage : type14ImageList) {
facePosition.setImgHeight(_subImage.getHeight());
facePosition.setImgWidth(_subImage.getWidth());
SourceEntity source = new SourceEntity();
source.setId(SnowFlakeUtil.getLongId());
source.setDeviceId(device.getId());
source.setScenicId(device.getScenicId());
source.setFaceSampleId(newFaceSampleId);
source.setCreateTime(shotTime);
source.setType(2);
// 上传oss
MultipartFile _file = ImageUtils.base64ToMultipartFile(_subImage.getData());
String filename = StorageUtil.joinPath(PHOTO_PATH, UUID.randomUUID() + "." + ext);
String _sourceUrl = scenicStorageAdapter.uploadFile(_file, filename);
scenicStorageAdapter.setAcl(StorageAcl.PUBLIC_READ, filename);
source.setUrl(_sourceUrl);
source.setPosJson(JSON.toJSONString(facePosition));
sourceMapper.add(source);
}
log.info("人脸信息及原图{}张入库成功!设备ID:{}", type14ImageList.size(), deviceID);
}
}
} else if (viidMode == 1) {
for (SubImageInfoObject subImage : subImageList.getSubImageInfoObject()) {
// base64转换成MultipartFIle
MultipartFile file = ImageUtils.base64ToMultipartFile(subImage.getData());
String ext = subImage.getFileFormat();
if (ext.equalsIgnoreCase("jpeg")) {
ext = "jpg";
}
IStorageAdapter adapter = StorageFactory.use("faces");
// Type=14 人脸,传™的,有这么传的嘛
if (subImage.getType().equals("14")) {
// 上传oss
if (Integer.valueOf(1).equals(device.getStatus())) {
FaceSampleEntity faceSample = new FaceSampleEntity();
Long newFaceSampleId = SnowFlakeUtil.getLongId();
faceSample.setId(newFaceSampleId);
faceSample.setScenicId(scenicId);
faceSample.setDeviceId(device.getId());
faceSample.setStatus(0);
faceSample.setCreateAt(shotTime);
String url = adapter.uploadFile(file, VIID_FACE, UUID.randomUUID() + "." + ext);
faceSample.setFaceUrl(url);
faceSampleMapper.add(faceSample);
DynamicTaskGenerator.addTask(faceSample.getId());
ThreadPoolExecutor executor = getExecutor(scenicId);
executor.execute(() -> {
if (faceBodyAdapter != null) {
taskFaceService.assureFaceDb(faceBodyAdapter, scenicId.toString());
AddFaceResp addFaceResp = faceBodyAdapter.addFace(scenicId.toString(), faceSample.getId().toString(), url, newFaceSampleId.toString());
if (addFaceResp != null) {
faceSample.setScore(addFaceResp.getScore());
faceSampleMapper.updateScore(faceSample.getId(), addFaceResp.getScore());
}
}
if (Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
DynamicTaskGenerator.addTask(faceSample.getId());
}
});
log.info("模式1人脸信息入库成功!设备ID:{}", deviceID);
}
}
}
}
}
}
return new VIIDBaseResp(
new ResponseStatusObject(faceId, "/VIID/Faces", "0", "OK", sdfTime.format(new Date()))
);
}
@RequestMapping(value = "/Images", method = RequestMethod.POST)
@IgnoreLogReq
public VIIDBaseResp images(HttpServletRequest request, @RequestBody ImageUploadReq req) throws IOException {
return new VIIDBaseResp(
new ResponseStatusObject("1", "/VIID/Images", "0", "OK", sdfTime.format(new Date()))
);
}
}

View File

@@ -11,6 +11,7 @@ import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.storage.utils.StorageUtil;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
@@ -25,7 +26,7 @@ import java.util.List;
@Slf4j
@IgnoreToken
@RestController
// 自定义切片工具对接接口
@Api(tags = "自定义切片工具对接接口")
@RequestMapping("/vpt/v1/")
public class VptController {

View File

@@ -6,11 +6,13 @@ import com.ycwl.basic.constant.StorageConstant;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.operator.WvpPassiveStorageOperator;
import com.ycwl.basic.model.wvp.WvpSyncReqVo;
import com.ycwl.basic.service.pc.DeviceService;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.storage.utils.StorageUtil;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
@@ -25,16 +27,19 @@ import java.util.List;
@Slf4j
@IgnoreToken
@RestController
// WVP对接接口
@Api(tags = "WVP对接接口")
@RequestMapping("/wvp/v1/")
public class WvpController {
@Autowired
private DeviceService deviceService;
@Autowired
private ScenicService scenicService;
@IgnoreLogReq
@PostMapping("/scenic/{scenicId}/sync")
public ApiResponse<List<WvpPassiveStorageOperator.Task>> sync(@PathVariable("scenicId") Long scenicId, @RequestBody WvpSyncReqVo reqVo) {
deviceService.updateDevices(scenicId, reqVo);
return ApiResponse.success(WvpPassiveStorageOperator.getTaskListByScenicId(scenicId));
}

View File

@@ -11,13 +11,10 @@ import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
import com.ycwl.basic.device.operator.WvpActiveStorageOperator;
import com.ycwl.basic.device.operator.WvpPassiveStorageOperator;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import java.time.ZoneId;
import java.util.Date;
public class DeviceFactory {
public static IDeviceStorageOperator getDeviceStorageOperator(DeviceV2DTO device, DeviceConfigEntity config) {
public static IDeviceStorageOperator getDeviceStorageOperator(DeviceEntity device, DeviceConfigEntity config) {
IDeviceStorageOperator operator = null;
if (config == null) {
return null;
@@ -38,33 +35,11 @@ public class DeviceFactory {
if (operator == null) {
return null;
}
operator.setDevice(convertToEntity(device));
operator.setDevice(device);
operator.setDeviceConfig(config);
return operator;
}
/**
* 将DeviceV2DTO转换为DeviceEntity
*/
private static DeviceEntity convertToEntity(DeviceV2DTO dto) {
if (dto == null) {
return null;
}
DeviceEntity entity = new DeviceEntity();
entity.setId(dto.getId());
entity.setName(dto.getName());
entity.setNo(dto.getNo());
entity.setScenicId(dto.getScenicId());
entity.setStatus(dto.getIsActive());
if (dto.getCreateTime() != null) {
entity.setCreateAt(dto.getCreateTime());
}
if (dto.getUpdateTime() != null) {
entity.setUpdateAt(dto.getUpdateTime());
}
return entity;
}
public static IDeviceStatusChecker getDeviceStatusChecker(DeviceEntity device, DeviceConfigEntity config) {
IDeviceStatusChecker checker = null;
if (config.getOnlineCheck() <= 0) {

View File

@@ -1,6 +1,6 @@
package com.ycwl.basic.device.checker.helper;
import com.ycwl.basic.utils.JacksonUtil;
import com.alibaba.fastjson.JSON;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import lombok.Setter;
@@ -13,7 +13,7 @@ public abstract class CommonDeviceChecker<C> {
protected DeviceConfigEntity deviceConfig;
public CommonDeviceChecker(String configJson) {
config = JacksonUtil.parseObject(configJson, getConfigClass());
config = JSON.parseObject(configJson, getConfigClass());
}
@SuppressWarnings("unchecked")

View File

@@ -1,155 +0,0 @@
package com.ycwl.basic.device.entity.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 设备视频连续性检查缓存实体
* 用于存储在Redis中的检查结果
*
* @author Claude Code
* @date 2025-09-01
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeviceVideoContinuityCache implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 设备ID
*/
private Long deviceId;
/**
* 检查时间
*/
private Date checkTime;
/**
* 检查的开始时间
*/
private Date startTime;
/**
* 检查的结束时间
*/
private Date endTime;
/**
* 是否支持连续性检查
*/
private Boolean support;
/**
* 视频是否连续
*/
private Boolean continuous;
/**
* 视频总数
*/
private Integer totalVideos;
/**
* 总时长(毫秒)
*/
private Long totalDurationMs;
/**
* 允许的最大间隙(毫秒)
*/
private Long maxAllowedGapMs;
/**
* 间隙数量
*/
private Integer gapCount;
/**
* 间隙列表(简化版,只包含关键信息)
*/
private List<GapInfo> gaps;
/**
* 间隙信息简化类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class GapInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 前一个文件名
*/
private String beforeFileName;
/**
* 后一个文件名
*/
private String afterFileName;
/**
* 间隙时长(毫秒)
*/
private Long gapMs;
/**
* 间隙开始时间
*/
private Date gapStartTime;
/**
* 间隙结束时间
*/
private Date gapEndTime;
}
/**
* 从VideoContinuityResult创建缓存对象
*
* @param deviceId 设备ID
* @param result 检查结果
* @param startTime 检查开始时间
* @param endTime 检查结束时间
* @return 缓存对象
*/
public static DeviceVideoContinuityCache fromResult(Long deviceId, VideoContinuityResult result,
Date startTime, Date endTime) {
DeviceVideoContinuityCache cache = new DeviceVideoContinuityCache();
cache.setDeviceId(deviceId);
cache.setCheckTime(new Date());
cache.setStartTime(startTime);
cache.setEndTime(endTime);
cache.setSupport(result.isSupport());
cache.setContinuous(result.isContinuous());
cache.setTotalVideos(result.getTotalVideos());
cache.setTotalDurationMs(result.getTotalDurationMs());
cache.setMaxAllowedGapMs(result.getMaxAllowedGapMs());
cache.setGapCount(result.getGapCount());
// 转换间隙列表
if (result.getGaps() != null && !result.getGaps().isEmpty()) {
List<GapInfo> gapInfos = result.getGaps().stream()
.map(gap -> new GapInfo(
gap.getBeforeFile() != null ? gap.getBeforeFile().getName() : null,
gap.getAfterFile() != null ? gap.getAfterFile().getName() : null,
gap.getGapMs(),
gap.getGapStartTime(),
gap.getGapEndTime()
))
.toList();
cache.setGaps(gapInfos);
}
return cache;
}
}

View File

@@ -1,40 +0,0 @@
package com.ycwl.basic.device.entity.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 视频连续性检查中的间隙信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class VideoContinuityGap {
/**
* 间隙前的视频文件
*/
private FileObject beforeFile;
/**
* 间隙后的视频文件
*/
private FileObject afterFile;
/**
* 间隙时长(毫秒)
*/
private long gapMs;
/**
* 间隙开始时间(前一个视频的endTime)
*/
private Date gapStartTime;
/**
* 间隙结束时间(后一个视频的createTime)
*/
private Date gapEndTime;
}

View File

@@ -1,56 +0,0 @@
package com.ycwl.basic.device.entity.common;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 视频连续性检查结果
*/
@Data
public class VideoContinuityResult {
/**
* 是否支持连续性检查功能
*/
private boolean support;
/**
* 视频是否连续(所有间隙都在允许范围内)
*/
private boolean continuous;
/**
* 检测到的间隙列表
*/
private List<VideoContinuityGap> gaps = new ArrayList<>();
/**
* 视频文件总数
*/
private int totalVideos;
/**
* 总时长(毫秒)
*/
private long totalDurationMs;
/**
* 允许的最大间隙(毫秒)
*/
private long maxAllowedGapMs;
/**
* 添加一个间隙
*/
public void addGap(VideoContinuityGap gap) {
this.gaps.add(gap);
}
/**
* 获取间隙数量
*/
public int getGapCount() {
return gaps.size();
}
}

View File

@@ -1,50 +1,12 @@
package com.ycwl.basic.device.operator;
import com.ycwl.basic.device.entity.common.VideoContinuityResult;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import lombok.Setter;
import java.util.Calendar;
import java.util.Date;
public abstract class ADeviceStorageOperator implements IDeviceStorageOperator {
@Setter
protected DeviceEntity device;
@Setter
protected DeviceConfigEntity deviceConfig;
/**
* 默认实现:不支持视频连续性检查
*
* @param startDate 开始时间
* @param endDate 结束时间
* @param maxGapMs 允许的最大间隔时间(毫秒)
* @return support=false的结果
*/
@Override
public VideoContinuityResult checkVideoContinuity(Date startDate, Date endDate, long maxGapMs) {
VideoContinuityResult result = new VideoContinuityResult();
result.setSupport(false);
result.setContinuous(false);
result.setTotalVideos(0);
result.setTotalDurationMs(0);
result.setMaxAllowedGapMs(maxGapMs);
return result;
}
/**
* 默认实现:不支持视频连续性检查
*
* @return support=false的结果
*/
@Override
public VideoContinuityResult checkRecentVideoContinuity() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, -2);
Date endDate = calendar.getTime();
calendar.add(Calendar.MINUTE, -5);
Date startDate = calendar.getTime();
return checkVideoContinuity(startDate, endDate, 2000L);
}
}

View File

@@ -1,10 +1,8 @@
package com.ycwl.basic.device.operator;
import cn.hutool.core.date.DateUtil;
import com.ycwl.basic.utils.JacksonUtil;
import com.alibaba.fastjson.JSON;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.entity.common.VideoContinuityGap;
import com.ycwl.basic.device.entity.common.VideoContinuityResult;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.entity.AliOssStorageConfig;
@@ -30,7 +28,7 @@ public class AliOssStorageOperator extends ADeviceStorageOperator {
@Override
public void loadConfig(String configJson) {
AliOssStorageConfig config = JacksonUtil.parseObject(configJson, AliOssStorageConfig.class);
AliOssStorageConfig config = JSON.parseObject(configJson, AliOssStorageConfig.class);
adapter = StorageFactory.get(StorageType.ALI_OSS, config);
}
@@ -71,15 +69,23 @@ public class AliOssStorageOperator extends ADeviceStorageOperator {
if (startDate == null || endDate == null) {
return null;
}
List<FileObject> fileList = new ArrayList<>();
if (startDate.after(endDate)) {
return fileList;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.set(Calendar.SECOND, 0);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
while (calendar.getTime().before(endDate)) {
String prefix = dateFormat.format(calendar.getTime());
List<FileObject> fileList = getOssFileListByPrefix(prefix);
if (fileList == null) {
List<FileObject> fileListByPrefix = getOssFileListByPrefix(prefix);
if (fileListByPrefix == null) {
return null;
}
fileList.addAll(fileListByPrefix);
calendar.add(Calendar.MINUTE, 1);
}
calendar.clear();
return fileList.stream()
.sorted(Comparator.comparing(FileObject::getCreateTime))
@@ -100,104 +106,4 @@ public class AliOssStorageOperator extends ADeviceStorageOperator {
String prefix = dateFormat.format(calendar.getTime());
return removeFilesByPrefix(prefix);
}
/**
* 检查视频片段的连续性
*
* @param startDate 开始时间
* @param endDate 结束时间
* @param maxGapMs 允许的最大间隔时间(毫秒)
* @return 包含缺口信息的验证结果
*/
@Override
public VideoContinuityResult checkVideoContinuity(Date startDate, Date endDate, long maxGapMs) {
VideoContinuityResult result = new VideoContinuityResult();
result.setSupport(true);
result.setMaxAllowedGapMs(maxGapMs);
// 获取时间范围内的视频列表
List<FileObject> fileList = getFileListByDtRange(startDate, endDate);
if (fileList == null || fileList.isEmpty()) {
result.setContinuous(false);
result.setTotalVideos(0);
result.setTotalDurationMs(0);
log.warn("未找到指定时间范围内的视频文件: {} - {}", startDate, endDate);
return result;
}
result.setTotalVideos(fileList.size());
// 只有一个视频文件时,认为是连续的
if (fileList.size() == 1) {
FileObject file = fileList.get(0);
long duration = file.getEndTime().getTime() - file.getCreateTime().getTime();
result.setContinuous(true);
result.setTotalDurationMs(duration);
return result;
}
// 检查相邻视频之间的间隙
long totalDuration = 0;
for (int i = 0; i < fileList.size() - 1; i++) {
FileObject currentFile = fileList.get(i);
FileObject nextFile = fileList.get(i + 1);
// 计算当前视频的时长
totalDuration += currentFile.getEndTime().getTime() - currentFile.getCreateTime().getTime();
// 计算间隙: 后一个视频的开始时间 - 前一个视频的结束时间
long gapMs = nextFile.getCreateTime().getTime() - currentFile.getEndTime().getTime();
// 如果间隙超过允许值,记录该间隙
if (gapMs > maxGapMs) {
VideoContinuityGap gap = new VideoContinuityGap();
gap.setBeforeFile(currentFile);
gap.setAfterFile(nextFile);
gap.setGapMs(gapMs);
gap.setGapStartTime(currentFile.getEndTime());
gap.setGapEndTime(nextFile.getCreateTime());
result.addGap(gap);
log.debug("检测到视频间隙: {} -> {}, 间隙时长: {}ms",
currentFile.getName(), nextFile.getName(), gapMs);
}
}
// 加上最后一个视频的时长
FileObject lastFile = fileList.get(fileList.size() - 1);
totalDuration += lastFile.getEndTime().getTime() - lastFile.getCreateTime().getTime();
result.setTotalDurationMs(totalDuration);
result.setContinuous(result.getGapCount() == 0);
log.info("视频连续性检查完成: 总视频数={}, 总时长={}ms, 间隙数={}, 连续={}",
result.getTotalVideos(), result.getTotalDurationMs(), result.getGapCount(), result.isContinuous());
return result;
}
/**
* 检查近期视频的连续性(测试用)
* 时间范围: 当前时间向前2分钟后,再向前10分钟(即前12分钟到前2分钟)
* 允许的最大间隙: 2秒
*
* @return 包含缺口信息的验证结果
*/
@Override
public VideoContinuityResult checkRecentVideoContinuity() {
Calendar calendar = Calendar.getInstance();
// 结束时间: 当前时间 - 2分钟
calendar.add(Calendar.MINUTE, -2);
Date endDate = calendar.getTime();
// 开始时间: 当前时间 - 12分钟 (再向前10分钟)
calendar.add(Calendar.MINUTE, -10);
Date startDate = calendar.getTime();
log.info("检查近期视频连续性: {} - {}", startDate, endDate);
// 允许的最大间隙为2秒(2000毫秒)
return checkVideoContinuity(startDate, endDate, 2000L);
}
}

View File

@@ -2,7 +2,6 @@ package com.ycwl.basic.device.operator;
import com.ycwl.basic.device.IDeviceCommon;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.entity.common.VideoContinuityResult;
import java.util.Date;
import java.util.List;
@@ -20,29 +19,10 @@ public interface IDeviceStorageOperator extends IDeviceCommon {
List<FileObject> getFileListByDtRange(Date startDate, Date endDate);
/**
* 删除指定日期之前的文件,不包含指定的日期当天
* 删除指定日期之前的文件不包含指定的日期当天
*
* @param date 指定日期,不包含指定日期当天
* @param date 指定日期不包含指定日期当天
* @return
*/
boolean removeFilesBeforeDate(Date date);
/**
* 检查视频片段的连续性
*
* @param startDate 开始时间
* @param endDate 结束时间
* @param maxGapMs 允许的最大间隔时间(毫秒)
* @return 包含缺口信息的验证结果
*/
VideoContinuityResult checkVideoContinuity(Date startDate, Date endDate, long maxGapMs);
/**
* 检查近期视频的连续性(便捷方法)
* 时间范围: 当前时间向前2分钟后,再向前5分钟(即前7分钟到前2分钟)
* 允许的最大间隙: 2秒
*
* @return 包含缺口信息的验证结果
*/
VideoContinuityResult checkRecentVideoContinuity();
}

View File

@@ -1,12 +1,10 @@
package com.ycwl.basic.device.operator;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.entity.common.VideoContinuityResult;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import lombok.Setter;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -36,24 +34,4 @@ public class LocalStorageOperator implements IDeviceStorageOperator {
return false;
}
@Override
public VideoContinuityResult checkVideoContinuity(Date startDate, Date endDate, long maxGapMs) {
VideoContinuityResult result = new VideoContinuityResult();
result.setSupport(false);
result.setContinuous(false);
result.setTotalVideos(0);
result.setTotalDurationMs(0);
result.setMaxAllowedGapMs(maxGapMs);
return result;
}
@Override
public VideoContinuityResult checkRecentVideoContinuity() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, -2);
Date endDate = calendar.getTime();
calendar.add(Calendar.MINUTE, -5);
Date startDate = calendar.getTime();
return checkVideoContinuity(startDate, endDate, 2000L);
}
}

View File

@@ -1,6 +1,7 @@
package com.ycwl.basic.device.operator;
import com.ycwl.basic.utils.JacksonUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.entity.vpt_passive.VptPassiveStorageConfig;
@@ -28,8 +29,10 @@ public class VptPassiveStorageOperator extends ADeviceStorageOperator {
public Long scenicId;
public Long deviceId;
public String deviceNo;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date startTime;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date endTime;
}
@@ -72,7 +75,7 @@ public class VptPassiveStorageOperator extends ADeviceStorageOperator {
@Override
public void loadConfig(String configJson) {
this.config = JacksonUtil.parseObject(configJson, VptPassiveStorageConfig.class);
this.config = JSON.parseObject(configJson, VptPassiveStorageConfig.class);
}
@Override
@@ -84,15 +87,14 @@ public class VptPassiveStorageOperator extends ADeviceStorageOperator {
if (StringUtils.isNotBlank(config.getDeviceNo())) {
task.deviceNo = config.getDeviceNo();
} else {
log.warn("设备未配置deviceNo:{}", device);
return Collections.emptyList();
task.deviceNo = device.getNo2();
}
task.startTime = startDate;
task.endTime = endDate;
if (redisTemplate == null) {
taskList.add(task);
} else {
redisTemplate.opsForValue().set(String.format(TASK_KEY, task.scenicId, task.deviceNo) + task.taskId, JacksonUtil.toJSONString(task), 10 * 60L, TimeUnit.SECONDS);
redisTemplate.opsForValue().set(String.format(TASK_KEY, task.scenicId, task.deviceNo) + task.taskId, JSON.toJSONString(task), 10 * 60L, TimeUnit.SECONDS);
}
log.info("任务{}获取视频开始!共{}", task.taskId, taskList.size());
Date taskStartTime = new Date();

View File

@@ -3,8 +3,8 @@ package com.ycwl.basic.device.operator;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.ycwl.basic.utils.JacksonUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.entity.wvp_active.WvpActiveStorageConfig;
import com.ycwl.basic.storage.exceptions.StorageConfigException;
@@ -30,7 +30,7 @@ public class WvpActiveStorageOperator extends ADeviceStorageOperator {
@Override
public void loadConfig(String configJson) {
this.config = JacksonUtil.parseObject(configJson, WvpActiveStorageConfig.class);
this.config = JSON.parseObject(configJson, WvpActiveStorageConfig.class);
}
@Override
@@ -55,14 +55,13 @@ public class WvpActiveStorageOperator extends ADeviceStorageOperator {
String password = MD5.create().digestHex(this.config.getLoginPassword());
params.put("password", password);
String jsonResult = HttpUtil.get(url, params);
Map<String, Object> result = JacksonUtil.parseObject(jsonResult, Map.class);
Integer code = (Integer) result.get("code");
if (code != null && code == 0) {
Map<String, Object> data = (Map<String, Object>) result.get("data");
this.token = (String) data.get("accessToken");
JSONObject result = JSON.parseObject(jsonResult);
if (result.getInteger("code") == 0) {
JSONObject data = result.getJSONObject("data");
this.token = data.getString("accessToken");
return this.token;
} else {
throw new StorageConfigException("获取token失败,原因为:" + result.get("msg"));
throw new StorageConfigException("获取token失败,原因为:" + result.getString("msg"));
}
}
public List<FileObject> listDirByDtRange(Date startDate, Date endDate) {
@@ -76,23 +75,19 @@ public class WvpActiveStorageOperator extends ADeviceStorageOperator {
params.put("page", 1);
params.put("count", 100);
String jsonResult = HttpRequest.get(url).form(params).header("Access-Token", getToken()).execute().body();
Map<String, Object> result = JacksonUtil.parseObject(jsonResult, Map.class);
Integer code = (Integer) result.get("code");
if (code != null && code == 0) {
Map<String, Object> data = (Map<String, Object>) result.get("data");
List<Map<String, Object>> recordList = JacksonUtil.parseObject(JacksonUtil.toJSONString(((Map<String, Object>) result.get("data")).get("list")), new TypeReference<List<Map<String, Object>>>() {});
JSONObject result = JSON.parseObject(jsonResult);
if (result.getInteger("code") == 0) {
JSONObject data = result.getJSONObject("data");
List<JSONObject> recordList = data.getJSONArray("list").toJavaList(JSONObject.class);
return recordList.stream()
.map(record -> {
FileObject object = new FileObject();
object.setName((String) record.get("id"));
object.setPath((String) record.get("folder"));
Integer recordId = (Integer) record.get("id");
object.setUrl(getUrlForDownload(recordId != null ? recordId : 0));
object.setName(record.getString("id"));
object.setPath(record.getString("folder"));
object.setUrl(getUrlForDownload(record.getInteger("id")));
object.setNeedDownload(true);
Number startTime = (Number) record.get("startTime");
Number endTime = (Number) record.get("endTime");
object.setCreateTime(new Date(startTime != null ? startTime.longValue() : 0));
object.setEndTime(new Date(endTime != null ? endTime.longValue() : 0));
object.setCreateTime(new Date(record.getLongValue("startTime")));
object.setEndTime(new Date(record.getLongValue("endTime")));
return object;
})
.sorted(Comparator.comparing(FileObject::getCreateTime))
@@ -105,11 +100,10 @@ public class WvpActiveStorageOperator extends ADeviceStorageOperator {
// assume path is recordId
String url = this.config.getHost() + "/api/cloud/record/play/path?recordId=" + id;
String jsonResult = HttpRequest.get(url).header("Access-Token", getToken()).execute().body();
Map<String, Object> result = JacksonUtil.parseObject(jsonResult, Map.class);
Integer code = (Integer) result.get("code");
if (code != null && code == 0) {
Map<String, Object> data = (Map<String, Object>) result.get("data");
String httpPath = (String) data.get("httpPath");
JSONObject result = JSON.parseObject(jsonResult);
if (result.getInteger("code") == 0) {
JSONObject data = result.getJSONObject("data");
String httpPath = data.getString("httpPath");
if (StringUtils.isBlank(this.config.getZlmHost())) {
return httpPath;
}
@@ -120,7 +114,7 @@ public class WvpActiveStorageOperator extends ADeviceStorageOperator {
}
return this.config.getZlmHost() + uri.getPath() + "?" + uri.getQuery();
} else {
throw new StorageUnsupportedException("获取播放地址失败,原因为:" + result.get("msg"));
throw new StorageUnsupportedException("获取播放地址失败,原因为:" + result.getString("msg"));
}
}

Some files were not shown because too many files have changed in this diff Show More