Compare commits

...

63 Commits

Author SHA1 Message Date
79ea08898b 加字段 2025-04-07 12:16:59 +08:00
52d0c4bc36 清理逻辑 2025-04-07 09:51:10 +08:00
84287df87b 避免重复创建 2025-04-07 09:51:03 +08:00
389c28300f 添加字段 2025-04-07 09:39:36 +08:00
10e7421672 统计优化 2025-04-07 09:38:41 +08:00
cf3a49d590 人脸搜索优化 2025-04-07 09:38:41 +08:00
083f67e516 数据库列表不允许返回null 2025-04-07 09:38:41 +08:00
3e53fd8367 订单接口支持memberId筛选 2025-04-07 09:38:41 +08:00
86d09134a6 接口调整,使用受限制的 2025-04-07 09:38:41 +08:00
082d8830bd 不重复生成二维码 2025-04-07 09:38:41 +08:00
d95c16aa01 百度云接口调整 2025-04-06 17:56:08 +08:00
a45929753f 删除手动添加限制 2025-04-06 17:56:08 +08:00
a46d4d8fac 添加exist 2025-04-06 17:56:08 +08:00
c8874064f0 初始化时吧所有的都用了 2025-04-06 17:56:08 +08:00
cecc7aa181 分设备队列 2025-04-06 17:56:08 +08:00
7cea8093c7 删除时添加token 2025-04-06 17:56:08 +08:00
ff82644f47 添加更多锁 2025-04-06 17:56:08 +08:00
b4aa3619ba 规整下代码 2025-04-05 16:42:46 +08:00
117a13cc2c 百度人脸识别 2025-04-05 16:42:46 +08:00
ab0f38cd97 逻辑调整 2025-04-05 14:48:12 +08:00
0ab142e1c4 修改 2025-04-05 13:27:20 +08:00
67dca0d4d4 人脸 2025-04-05 13:21:41 +08:00
36f1242e79 打印机 2025-04-03 15:56:01 +08:00
06ebc1d05e 删除realName相关 2025-04-01 16:50:47 +08:00
7d8483b6e4 打印机相关 2025-04-01 16:22:52 +08:00
dc7c4a13cb 统计bug 2025-04-01 16:22:27 +08:00
c963324cfb 景区删除逻辑 2025-03-31 18:52:47 +08:00
af2947e44a 退款订单列表信息 2025-03-31 18:19:49 +08:00
fcddc4fc8c 权限 2025-03-31 18:19:36 +08:00
da72e7e0a9 景区账号管理 2025-03-31 17:15:47 +08:00
d9619e6fea 列表显示商品内容、添加用户UID返回 2025-03-31 17:15:21 +08:00
938f9702ea 优惠券、通知加参数 2025-03-29 17:22:48 +08:00
4b03bfb871 1 2025-03-28 20:10:57 +08:00
80e93ecd39 价格 2025-03-28 16:23:35 +08:00
e128101563 人脸检索 2025-03-28 14:24:22 +08:00
a1eb29c49d 升级 2025-03-28 14:23:30 +08:00
48a3dfb313 AutoClosable 2025-03-26 14:31:11 +08:00
a08f4adf2d 时间线兜底逻辑 2025-03-25 12:43:42 +08:00
a0703e48c9 1 2025-03-25 12:43:42 +08:00
2b43d8a7b7 规整规整项目 2025-03-25 12:43:42 +08:00
decfe18b9a 添加游玩时间 2025-03-25 10:49:42 +08:00
9ec6825372 乱七八糟的调整,wx接口对其 2025-03-25 10:49:28 +08:00
f9a8a5f20e 优惠券 2025-03-23 17:45:53 +08:00
a5f67b1eac 任务调度修改 2025-03-23 17:45:38 +08:00
a8601548c6 图片可指定格式,可推荐格式,压缩 2025-03-19 17:56:18 +08:00
fd85b7ad77 修改接口,务必返回视频地址 2025-03-19 17:55:54 +08:00
bcf8e8e88d 设备关联逻辑 2025-03-19 16:57:33 +08:00
fb530a1eb4 统计添加注释 2025-03-19 16:57:25 +08:00
8a7021d759 下载逻辑换成内网地址 2025-03-19 16:57:25 +08:00
26dc53ca6a 加个普通水印、推送订单纠正、水印预览和下载 2025-03-19 16:57:25 +08:00
46f6532164 用常量 2025-03-17 18:37:37 +08:00
50a7c2e9c4 清理数据根据景区清理 2025-03-17 18:36:38 +08:00
f36416bad2 改下逻辑 2025-03-17 18:36:23 +08:00
180ba67de8 水印 2025-03-17 18:35:31 +08:00
7e8eebdef5 free 2025-03-17 18:35:31 +08:00
eac1c48cb2 支持别人看我的照片信息 2025-03-17 18:35:31 +08:00
0ae47d3f71 画画的 2025-03-17 18:35:31 +08:00
072a1a6131 一口价避免买到别人的东西(不展示) 2025-03-17 10:15:26 +08:00
aa4048ad26 订单,查询限制放宽 2025-03-17 09:50:30 +08:00
6bfff19c5a 删除无用内容 2025-03-14 17:01:54 +08:00
41aba63e5d 用户UID 2025-03-14 16:48:54 +08:00
05bc2773b9 3 2025-03-14 11:47:47 +08:00
c2ebbd71e2 2 2025-03-11 12:07:34 +08:00
198 changed files with 5063 additions and 1752 deletions
pom.xml
src
main
java
com
ycwl
basic
aspectj
biz
config
constant
controller
device
exception
facebody
filter
image
mapper
model
printer
repository
service
storage
task
utils
resources
test
java
com
ycwl

69
pom.xml

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<version>2.7.18</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ycwl</groupId>
@ -21,16 +21,18 @@
<maven.compiler.target>8</maven.compiler.target>
<mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.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>
<tomcat.version>9.0.102</tomcat.version>
<!--跳过单元测试-->
<skipTests>true</skipTests>
</properties>
<dependencies>
<!-- 微信支付-->
<!-- 微信支付 -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
@ -52,23 +54,13 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- 引入mysql连接 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
<scope>runtime</scope>
</dependency>
@ -78,6 +70,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -95,7 +92,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--<scope>provided</scope>-->
<scope>provided</scope>
</dependency>
<!-- hutool工具类 -->
@ -159,17 +156,11 @@
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.22.0</version>
</dependency>
<!-- 阿里云对象存储 -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
<version>3.18.0</version>
</dependency>
<!-- S3对象存储 -->
@ -195,14 +186,50 @@
<artifactId>aliyun-java-sdk-facebody</artifactId>
<version>2.0.12</version>
</dependency>
<!-- 百度智能云人脸识别 -->
<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.16.19</version>
</dependency>
</dependencies>
<build>
<resources>
<!-- 确保 src/main/resources 下的资源文件被包含 -->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.png</include> <!-- 明确包含 PNG 文件 -->
<include>**/*.ttf</include> <!-- 明确包含 TTF 文件 -->
<include>**/*</include> <!-- 包含其他所有资源文件 -->
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 跳过测试执行 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<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>

@ -1,6 +1,7 @@
package com.ycwl.basic.aspectj;
import com.ycwl.basic.annotation.RequestToFile;
import com.ycwl.basic.config.CachedBodyHttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
@ -12,6 +13,7 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
@ -37,27 +39,8 @@ public class HttpSaver {
}
public static void saveRequestToFile(HttpServletRequest request) throws IOException {
StringBuilder rawReq = new StringBuilder();
rawReq.append(request.getMethod()).append(" ").append(request.getRequestURL());
String queryString = request.getQueryString();
if (queryString != null) {
rawReq.append("?").append(queryString);
}
rawReq.append("\r\n");
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
rawReq.append(headerName).append(": ").append(request.getHeader(headerName)).append("\r\n");
}
rawReq.append("\r\n");
// 获取body
try {
rawReq.append(request.getReader().lines().collect(Collectors.joining("\r\n")));
rawReq.append("\r\n");
} catch (IOException ignore) {
}
// 写入文件
File file = new File("./request/"+System.currentTimeMillis()+".http");
// 写入文件
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
@ -65,7 +48,22 @@ public class HttpSaver {
file.createNewFile();
}
try (java.io.FileWriter writer = new java.io.FileWriter(file, true)) {
writer.write(rawReq.toString());
writer.append(request.getMethod()).append(" ").append(request.getRequestURL());
String queryString = request.getQueryString();
if (queryString != null) {
writer.append("?").append(queryString);
}
writer.append("\r\n");
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
writer.append(headerName).append(": ").append(request.getHeader(headerName)).append("\r\n");
}
writer.append("\r\n");
// 获取body
CachedBodyHttpServletRequest cachedRequest = (CachedBodyHttpServletRequest) request;
writer.append(new String(cachedRequest.getCachedBody()));
writer.append("\r\n");
}
}
}

@ -0,0 +1,91 @@
package com.ycwl.basic.biz;
import com.ycwl.basic.mapper.CouponMapper;
import com.ycwl.basic.mapper.CouponRecordMapper;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
import com.ycwl.basic.model.pc.couponRecord.entity.CouponRecordEntity;
import com.ycwl.basic.model.pc.couponRecord.resp.CouponRecordQueryResp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Component
public class CouponBiz {
@Autowired
private CouponMapper couponMapper;
@Autowired
private CouponRecordMapper couponRecordMapper;
public CouponRecordQueryResp queryUserCouponRecord(Long scenicId, Long memberId, Long faceId, String goodsId) {
CouponRecordQueryResp resp = new CouponRecordQueryResp();
List<CouponRecordEntity> recordList = couponRecordMapper.queryByUserWithGoodsId(scenicId, memberId, goodsId);
if (recordList != null && !recordList.isEmpty()) {
Optional<CouponRecordEntity> record = recordList.stream().filter(item -> item.getStatus() == 0).filter(item -> item.getFaceId() == null || item.getFaceId().equals(faceId)).findAny();
if (record.isPresent()) {
CouponRecordEntity recordEntity = record.get();
resp.setExist(true);
resp.setId(recordEntity.getId());
resp.setCouponId(recordEntity.getCouponId());
CouponEntity coupon = couponMapper.getById(recordEntity.getCouponId());
if (coupon != null) {
resp.setMemberId(recordEntity.getMemberId());
resp.setFaceId(recordEntity.getFaceId());
resp.setStatus(recordEntity.getStatus());
resp.setCreateTime(recordEntity.getCreateTime());
resp.setUsedTime(recordEntity.getUsedTime());
resp.setUsedOrderId(recordEntity.getUsedOrderId());
resp.setCoupon(coupon);
} else {
resp.setExist(false);
}
} else {
Optional<CouponRecordEntity> usedRecord = recordList.stream().filter(item -> item.getStatus() != 0).filter(item -> item.getFaceId() == null || item.getFaceId().equals(faceId)).findAny();
if (usedRecord.isPresent()) {
CouponRecordEntity recordEntity = usedRecord.get();
resp.setExist(true);
resp.setId(recordEntity.getId());
resp.setCouponId(recordEntity.getCouponId());
CouponEntity coupon = couponMapper.getById(recordEntity.getCouponId());
if (coupon != null) {
resp.setMemberId(recordEntity.getMemberId());
resp.setFaceId(recordEntity.getFaceId());
resp.setStatus(recordEntity.getStatus());
resp.setCreateTime(recordEntity.getCreateTime());
resp.setUsedTime(recordEntity.getUsedTime());
resp.setUsedOrderId(recordEntity.getUsedOrderId());
resp.setCoupon(coupon);
} else {
resp.setExist(false);
}
}
}
}
return resp;
}
public boolean userGetCoupon(Long memberId, Long faceId, Integer couponId) {
CouponEntity coupon = couponMapper.getById(couponId);
if (coupon == null) {
return false;
}
CouponRecordEntity entity = new CouponRecordEntity();
entity.setCouponId(couponId);
entity.setFaceId(faceId);
entity.setMemberId(memberId);
entity.setStatus(0);
entity.setCreateTime(new Date());
return couponRecordMapper.insert(entity) > 0;
}
public boolean userUseCoupon(Long memberId, Long faceId, Integer couponRecordId, Long orderId) {
CouponRecordEntity entity = new CouponRecordEntity();
entity.setId(couponRecordId);
entity.setStatus(1);
entity.setUsedTime(new Date());
entity.setUsedOrderId(orderId);
return couponRecordMapper.updateById(entity) > 0;
}
}

@ -8,6 +8,8 @@ import com.ycwl.basic.mapper.VideoMapper;
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;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
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;
@ -70,6 +72,8 @@ public class OrderBiz {
private VideoTaskRepository videoTaskRepository;
@Autowired
private BrokerBiz brokerBiz;
@Autowired
private CouponBiz couponBiz;
public PriceObj queryPrice(Long scenicId, int goodsType, Long goodsId) {
PriceObj priceObj = new PriceObj();
@ -124,6 +128,18 @@ public class OrderBiz {
return priceObj;
}
public OrderEntity hasTypeOrder(Long userId, Long scenicId, int orderType, Integer configId) {
OrderEntity orderEntity = orderMapper.queryTypeOrder(userId, scenicId, orderType, configId);
if (orderEntity == null) {
return null;
}
if (Integer.valueOf(1).equals(orderEntity.getStatus())) {
return orderEntity;
} else {
return null;
}
}
public IsBuyRespVO isBuy(Long userId, Long scenicId, int goodsType, Long goodsId) {
IsBuyRespVO respVO = new IsBuyRespVO();
boolean isBuy = orderRepository.checkUserBuyItem(userId, goodsType, goodsId);
@ -160,8 +176,8 @@ public class OrderBiz {
respVO.setOrderId(orderEntity.getId());
}
}
// 还是没买
respVO.setBuy(isBuy);
// 还是没买
if (!isBuy) {
PriceObj priceObj = queryPrice(scenicId, goodsType, goodsId);
if (priceObj == null) {
@ -170,8 +186,38 @@ public class OrderBiz {
respVO.setFree(priceObj.isFree());
respVO.setGoodsType(goodsType);
respVO.setGoodsId(goodsId);
respVO.setPrice(priceObj.getPrice());
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;
}
@ -194,6 +240,9 @@ public class OrderBiz {
}
});
orderRepository.clearOrderCache(orderId); // 更新完了,清理下
if (order.getCouponRecordId() != null) {
couponBiz.userUseCoupon(order.getMemberId(), order.getFaceId(), order.getCouponRecordId(), orderId);
}
//支付时间
OrderAppRespVO orderDetail = orderMapper.appDetail(orderId);
@ -228,18 +277,12 @@ public class OrderBiz {
StatisticsRecordAddReq statisticsRecordAddReq = new StatisticsRecordAddReq();
statisticsRecordAddReq.setMemberId(order.getMemberId());
Calendar calendar = Calendar.getInstance();
calendar.setTime(goodsCreateTime);
calendar.set(Calendar.HOUR_OF_DAY, 21);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
// TODO: 他的购买的内容于内容生成当天晚9点之前算现场订单,否则算推送订单
if(calendar.getTime().compareTo(payAt)>0){//
Long enterType = statisticsMapper.getUserRecentEnterType(order.getMemberId(), order.getCreateAt());
if(!Long.valueOf(1014).equals(enterType)){//
statisticsRecordAddReq.setType(StatisticEnum.ON_SITE_PAYMENT.code);
}else {
statisticsRecordAddReq.setType(StatisticEnum.POST_PAYMENT.code);
}
calendar.clear();
statisticsRecordAddReq.setScenicId(order.getScenicId());
statisticsRecordAddReq.setMorphId(orderId);
statisticsMapper.addStatisticsRecord(statisticsRecordAddReq);

@ -1,9 +1,15 @@
package com.ycwl.basic.biz;
import com.ycwl.basic.model.mobile.order.IsBuyBatchRespVO;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
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.price.entity.PriceConfigEntity;
import com.ycwl.basic.model.pc.price.resp.GoodsListRespVO;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.PriceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.TemplateRepository;
@ -11,6 +17,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -24,6 +31,12 @@ public class PriceBiz {
private ScenicRepository scenicRepository;
@Autowired
private PriceRepository priceRepository;
@Autowired
private OrderBiz orderBiz;
@Autowired
private FaceRepository faceRepository;
@Autowired
private CouponBiz couponBiz;
public List<GoodsListRespVO> listGoodsByScenic(Long scenicId) {
List<GoodsListRespVO> goodsList = new ArrayList<>();
@ -66,4 +79,65 @@ public class PriceBiz {
return false;
}).collect(Collectors.toList());
}
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) {
return null;
}
FaceEntity face = faceRepository.getFace(faceId);
if (face != null && !face.getMemberId().equals(userId)) {
return null;
}
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null) {
if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) {
// 景区全免
respVO.setFree(true);
respVO.setSlashPrice(BigDecimal.ZERO);
return respVO;
}
}
switch (type) {
case 0: // 单个定价
CouponRecordQueryResp recordQueryResp = couponBiz.queryUserCouponRecord(scenicId, userId, faceId, goodsIds);
if (recordQueryResp.isUsable()) {
respVO.setCouponId(recordQueryResp.getCouponId());
respVO.setCouponRecordId(recordQueryResp.getId());
CouponEntity coupon = recordQueryResp.getCoupon();
if (coupon != null) {
respVO.setCouponPrice(coupon.calculateDiscountPrice(priceConfig.getPrice()));
}
}
break;
case -1:
CouponRecordQueryResp oneCouponRecordQueryResp = couponBiz.queryUserCouponRecord(scenicId, userId, faceId, "-1");
if (oneCouponRecordQueryResp.isUsable()) {
respVO.setCouponId(oneCouponRecordQueryResp.getCouponId());
respVO.setCouponRecordId(oneCouponRecordQueryResp.getId());
CouponEntity coupon = oneCouponRecordQueryResp.getCoupon();
if (coupon != null) {
respVO.setCouponPrice(coupon.calculateDiscountPrice(priceConfig.getPrice()));
}
}
break;
}
respVO.setConfigId(priceConfig.getId());
respVO.setGoodsIds(goodsIds);
respVO.setType(type);
respVO.setOrigPrice(priceConfig.getPrice());
respVO.setSlashPrice(priceConfig.getSlashPrice());
// 查询用户是否有此类订单
respVO.setBuy(false);
if (userId != null) {
OrderEntity orderEntity = orderBiz.hasTypeOrder(userId, scenicId, type, priceConfig.getId());
if (orderEntity != null) {
respVO.setOrderId(orderEntity.getId());
respVO.setBuy(Integer.valueOf(1).equals(orderEntity.getStatus()));
}
}
return respVO;
}
}

@ -0,0 +1,59 @@
package com.ycwl.basic.config;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
// 缓存请求体内容
InputStream requestInputStream = request.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int read;
while ((read = requestInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, read);
}
cachedBody = byteArrayOutputStream.toByteArray();
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public int read() {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
// 不需要实现
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public byte[] getCachedBody() {
return cachedBody;
}
}

@ -1,26 +0,0 @@
package com.ycwl.basic.config;
import com.ycwl.basic.storage.entity.AliOssStorageConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* 阿里云OSS配置
*
* @author songmingsong
**/
@Data
@Component
public class FaceDetectConfig {
@Value("${aliFace.accessKeyId}")
private String accessKeyId;
@Value("${aliFace.accessKeySecret}")
private String accessKeySecret;
@Value("${aliFace.region}")
private String region;
}

@ -1,50 +0,0 @@
package com.ycwl.basic.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.StringRedisSerializer;
import javax.annotation.Resource;
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory redisConnectionFactory;
@Bean
public RedisTemplate<Object, Object> redisTemplate() {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
redisTemplate.setEnableDefaultSerializer(true);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

@ -1,31 +0,0 @@
//package com.ycwl.basic.config;
//
//import org.redisson.Redisson;
//import org.redisson.api.RedissonClient;
//import org.redisson.config.Config;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
//@Configuration
//public class RedissonConfig {
//
// @Value("${spring.redis.host}")
// private String host;
// @Value("${spring.redis.port}")
// private String port;
// @Value("${spring.redis.password}")
// private String password;
//
// @Bean
// public RedissonClient getRedisSon() {
// Config config = new Config();
// String address = new StringBuilder("redis://").append(host).append(":").append(port).toString();
// config.useSingleServer().setAddress(address);
// if (null != password && !"".equals(password.trim())) {
// config.useSingleServer().setPassword(password);
// }
// return Redisson.create(config);
// }
//
//}

@ -12,8 +12,11 @@ public class SchedulerConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(256);
scheduler.setPoolSize(32); // 核心/最大线程数
scheduler.setAwaitTerminationSeconds(0); // 空闲线程存活时间(秒)
scheduler.setThreadNamePrefix("Scheduler-");
scheduler.setAwaitTerminationSeconds(60); // 等待任务终止的时间
scheduler.setRemoveOnCancelPolicy(true); // 取消任务后移除线程
return scheduler;
}
}

@ -25,7 +25,7 @@ import java.util.List;
*/
@Configuration
@EnableSwagger2WebMvc
@Profile({"!prod"})
@Profile({"test"})
public class SwaggerConfig {
/**

@ -58,7 +58,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// config.setAllowCredentials(true);
// 允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.addAllowedOrigin("*");
// 允许访问的头信息,*表示全部

@ -4,4 +4,5 @@ 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 = "source_photo";
public static final String PHOTO_WATERMARKED_PATH = "photo_w";
}

@ -15,20 +15,16 @@ 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.video.entity.VideoEntity;
import com.ycwl.basic.model.pc.video.resp.VideoRespVO;
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;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.SnowFlakeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@ -197,7 +193,7 @@ public class LyCompatibleController {
return R.error("用户没有上传过照片!");
}
VideoTaskStatusVO taskStatusVO = goodsService.getTaskStatusByScenicId(member.getId(), member.getScenicId());
List<ContentPageVO> listApiResponse = appScenicService.faceContentList(member.getId(), faceVO.getId());
List<ContentPageVO> listApiResponse = appScenicService.faceContentList(faceVO.getId());
Map<Integer, List<ContentPageVO>> collect = listApiResponse.stream()
.filter(contentPageVO -> contentPageVO.getLockType() < 0)
.collect(Collectors.groupingBy(ContentPageVO::getGoodsType));
@ -224,8 +220,9 @@ public class LyCompatibleController {
return map;
}).collect(Collectors.toList());
GoodsReqQuery goodsReqQuery = new GoodsReqQuery();
goodsReqQuery.setFaceId(faceVO.getId());
goodsReqQuery.setSourceType(1);
List<GoodsDetailVO> sourceGoodsList = goodsService.sourceGoodsList(member.getId(), goodsReqQuery);
List<GoodsDetailVO> sourceGoodsList = goodsService.sourceGoodsList(goodsReqQuery);
List<Map<String, Object>> userVideoList = sourceGoodsList.stream().map(goodsDetailVO -> {
Map<String, Object> map = new HashMap<>();
map.put("id", goodsDetailVO.getGoodsId().toString());

@ -0,0 +1,9 @@
package com.ycwl.basic.controller.mobile;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/mobile/controller/v1")
public class AppCouponController {
}

@ -1,15 +0,0 @@
package com.ycwl.basic.controller.mobile;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author:longbinbin
* @Date:2024/12/6 10:18
*/
@RestController
@RequestMapping("/api/mobile/scenic/v1")
@Api(tags = "设备相关接口")
public class AppDeviceController {
}

@ -1,13 +1,10 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.biz.TaskStatusBiz;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.exception.CheckTokenException;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.goods.*;
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.Api;
@ -15,7 +12,6 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
/**
@ -30,10 +26,6 @@ public class AppGoodsController {
@Autowired
private GoodsService goodsService;
@Autowired
private TaskService taskService;
@Autowired
private TaskStatusBiz taskStatusBiz;
@ApiOperation("商品列表")
@PostMapping("/goodsList")
@ -44,10 +36,23 @@ public class AppGoodsController {
@ApiOperation("源素材(原片/照片)商品列表")
@PostMapping("/sourceGoodsList")
public ApiResponse<List<GoodsDetailVO>> sourceGoodsList(@RequestBody GoodsReqQuery query) {
List<GoodsDetailVO> goodsDetailVOS = goodsService.sourceGoodsList(Long.valueOf(BaseContextHandler.getUserId()), query);
List<GoodsDetailVO> goodsDetailVOS = goodsService.sourceGoodsList(query);
return ApiResponse.success(goodsDetailVOS);
}
@PostMapping("/sourceGoodsList/preview")
public ApiResponse<List<GoodsUrlVO>> sourceGoodsListPreview(@RequestBody GoodsReqQuery query) {
List<GoodsUrlVO> goodsUrlList = goodsService.sourceGoodsListPreview(query);
return ApiResponse.success(goodsUrlList);
}
@PostMapping("/sourceGoodsList/download")
public ApiResponse<List<GoodsUrlVO>> sourceGoodsListDownload(@RequestBody GoodsReqQuery query) {
List<GoodsUrlVO> goodsUrlList = goodsService.sourceGoodsListDownload(query);
return ApiResponse.success(goodsUrlList);
}
@ApiOperation("成片vlog商品详情")
@GetMapping("/getVideoGoodsDetail/{videoId}")
@IgnoreToken
@ -62,8 +67,7 @@ public class AppGoodsController {
@GetMapping("/sourceGoods/{sourceId}")
public ApiResponse<GoodsDetailVO> sourceGoodsInfo(@PathVariable("sourceId") Long sourceId) {
JwtInfo worker = JwtTokenUtil.getWorker();
return goodsService.sourceGoodsInfo(worker.getUserId(), sourceId);
return goodsService.sourceGoodsInfo(sourceId);
}
/**
@ -88,6 +92,6 @@ public class AppGoodsController {
@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(worker.getUserId(), faceId, templateId));
return ApiResponse.success(goodsService.getTaskStatusByTemplateId(faceId, templateId));
}
}

@ -1,54 +0,0 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.mobile.index.TopStateResp;
import com.ycwl.basic.model.mobile.scenic.ScenicAppVO;
import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO;
import com.ycwl.basic.service.mobile.AppScenicService;
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.*;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/5 10:20
*/
@RestController
@RequestMapping("/api/mobile/index/v1")
@Api(tags = "首页相关接口")
public class AppIndexController {
@Autowired
private AppScenicService scenicService;
/**
* 首页景区列表
*
* @return
*/
// @ApiOperation(value = "首页景区列表", notes = "首页景区列表")
// @PostMapping("/scenicList")
// @IgnoreToken
// public ApiResponse<List<ScenicAppVO>> scenicList(@RequestBody ScenicIndexVO scenicIndexVO) {
// return scenicService.scenicList(scenicIndexVO);
// }
// @ApiOperation(value = "顶部状态", notes = "顶部状态")
// @GetMapping("/topState")
// @IgnoreToken
// public ApiResponse<TopStateResp> topState() {
// return scenicService.topState();
// }
@GetMapping("/faceAgreement")
public ApiResponse<String> faceAgreement() {
return ApiResponse.success("人脸识别隐私协议:\n" +
"1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n" +
"1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n" +
"1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n1、xxx\n");
}
}

@ -62,26 +62,6 @@ public class AppMemberController {
return memberService.update(userInfoUpdateDTO);
}
/**
* 同意用户协议
*
* @return
*/
@ApiOperation("同意用户协议")
@GetMapping("/agreement")
public ApiResponse<?> agreement() {
return memberService.agreement();
}
@ApiOperation("是否首次获取视频")
@GetMapping("/isFirstObtainVideo")
public ApiResponse isFirstTimeObtainingVideo() {
// TODO 判断是否首次获取视频逻辑
return ApiResponse.success("");
}
@ApiOperation("新增或修改景区服务通知状态")
@GetMapping("/updateScenicServiceNoticeStatus")
public ApiResponse updateScenicServiceNoticeStatus(Long scenicId) {

@ -2,18 +2,23 @@ package com.ycwl.basic.controller.mobile;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.biz.PriceBiz;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.goods.GoodsPriceQueryReq;
import com.ycwl.basic.model.mobile.order.IsBuyBatchRespVO;
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
import com.ycwl.basic.model.mobile.order.OrderAppPageReq;
import com.ycwl.basic.model.mobile.order.RefundOrderReq;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.order.req.CreateBatchOrderReqVO;
import com.ycwl.basic.model.pc.order.req.CreateOrderReqVO;
import com.ycwl.basic.model.pc.order.req.OrderAddReq;
import com.ycwl.basic.model.pc.order.resp.OrderAppRespVO;
import com.ycwl.basic.model.wx.WxPayRespVO;
import com.ycwl.basic.repository.OrderRepository;
import com.ycwl.basic.repository.PriceRepository;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.utils.ApiResponse;
@ -35,9 +40,11 @@ public class AppOrderController {
@Autowired
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
@Autowired
private OrderBiz orderBiz;
@Autowired
private PriceBiz priceBiz;
@Autowired
private FaceMapper faceMapper;
@ApiOperation("用户端订单列表查询")
@PostMapping("/page")
@ -88,4 +95,18 @@ public class AppOrderController {
Long userId = Long.parseLong(BaseContextHandler.getUserId());
return ApiResponse.success(orderBiz.isBuy(userId, scenicId, type, goodsId));
}
@GetMapping("/scenic/{scenicId}/queryBatchPrice")
public ApiResponse<IsBuyBatchRespVO> queryPrice(@PathVariable("scenicId") Long scenicId, @RequestParam("type") Integer type, @RequestParam(value = "faceId", required = false) Long faceId, @RequestParam(value = "goodsIds", required = false) String goodsIds) {
Long userId = Long.parseLong(BaseContextHandler.getUserId());
if (faceId == null) {
FaceRespVO lastFaceByUserId = faceMapper.findLastFaceByUserId(BaseContextHandler.getUserId());
faceId = lastFaceByUserId.getId();
}
IsBuyBatchRespVO buy = priceBiz.isBuy(userId, faceId, scenicId, type, goodsIds);
if (buy == null) {
return ApiResponse.fail("该套餐暂未开放购买");
}
return ApiResponse.success(buy);
}
}

@ -2,10 +2,7 @@ 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.goods.GoodsPageVO;
import com.ycwl.basic.model.mobile.goods.GoodsReqQuery;
import com.ycwl.basic.model.mobile.scenic.ScenicAppVO;
import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO;
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
@ -14,7 +11,6 @@ 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.ScenicService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import io.swagger.annotations.Api;
@ -74,16 +70,8 @@ public class AppScenicController {
@ApiOperation("景区视频源素材列表")
@GetMapping("/face/{faceId}/contentList")
public ApiResponse<List<ContentPageVO>> contentList(@PathVariable String faceId) {
if (!StringUtils.isNumeric(faceId)) {
log.error("请求异常, faceId: [{}]",faceId);
return ApiResponse.fail("请求异常");
}
Long id = Long.parseLong(faceId);
JwtInfo worker = JwtTokenUtil.getWorker();
List<ContentPageVO> contentPageVOS = appScenicService.faceContentList(worker.getUserId(), id);
public ApiResponse<List<ContentPageVO>> contentList(@PathVariable Long faceId) {
List<ContentPageVO> contentPageVOS = appScenicService.faceContentList(faceId);
return ApiResponse.success(contentPageVOS);
}
}

@ -1,7 +1,6 @@
package com.ycwl.basic.controller.mobile;
import com.ycwl.basic.annotation.IgnoreLogReq;
import com.ycwl.basic.biz.TaskStatusBiz;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.goods.VideoTaskReq;
import com.ycwl.basic.model.mobile.goods.VideoTaskStatusVO;
@ -30,7 +29,7 @@ public class AppTaskController {
@IgnoreLogReq
public ApiResponse<VideoTaskStatusVO> getTaskStatusByFaceId(@PathVariable("faceId") Long faceId) {
JwtInfo worker = JwtTokenUtil.getWorker();
return ApiResponse.success(goodsService.getTaskStatusByFaceId(worker.getUserId(), faceId));
return ApiResponse.success(goodsService.getTaskStatusByFaceId(faceId));
}
@GetMapping("/scenic/{scenicId}")
@IgnoreLogReq
@ -52,7 +51,7 @@ public class AppTaskController {
@IgnoreLogReq
public ApiResponse<VideoTaskStatusVO> getTemplateTaskStatus(@PathVariable("faceId") Long faceId, @PathVariable("templateId") Long templateId) {
JwtInfo worker = JwtTokenUtil.getWorker();
return ApiResponse.success(goodsService.getTaskStatusByTemplateId(worker.getUserId(), faceId, templateId));
return ApiResponse.success(goodsService.getTaskStatusByTemplateId(faceId, templateId));
}
@PostMapping("/submit")

@ -1,20 +1,14 @@
package com.ycwl.basic.controller.mobile;
import com.alibaba.fastjson.JSONObject;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.wx.WechatMessageSubscribeForm;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.mobile.WxNotifyService;
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;
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;
@ -31,8 +25,6 @@ import java.util.List;
@RequestMapping("/api/mobile/wx/notify/v1")
@Api(tags = "微信消息模板通知")
public class AppWxNotifyController {
@Autowired
private WxNotifyService wxNotifyService;
@Autowired
private ScenicRepository scenicRepository;
//

@ -8,6 +8,7 @@ import com.ycwl.basic.model.pc.broker.resp.BrokerRespVO;
import com.ycwl.basic.model.pc.broker.resp.DailySummaryRespVO;
import com.ycwl.basic.service.pc.BrokerRecordService;
import com.ycwl.basic.service.pc.BrokerService;
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;
@ -123,12 +124,16 @@ public class BrokerController {
String appState = mpConfig.getState();
String path = "pages/home/index?scenicId=" + broker.getScenicId() + "&morphId=" + id;
String filePath = "qr_code_tk_" + id + ".jpg";
IStorageAdapter adapter = StorageFactory.use();
if (adapter.isExists(filePath)) {
return ApiResponse.success(adapter.getUrl(filePath));
}
try {
WxMpUtil.generateWXAQRCode(appId, appSecret, appState, path, filePath);
IStorageAdapter adapter = StorageFactory.use();
File file = new File(filePath);
String s = adapter.uploadFile(file, filePath);
file.delete();
adapter.setAcl(StorageAcl.PUBLIC_READ, filePath);
return ApiResponse.success(s);
} catch (Exception e) {
return ApiResponse.fail("生成二维码失败");

@ -0,0 +1,72 @@
package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.biz.PriceBiz;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
import com.ycwl.basic.model.pc.coupon.req.CouponQueryReq;
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.*;
import java.util.List;
@RestController
@RequestMapping("/api/coupon/v1")
@Api(tags = "优惠券管理")
public class CouponController {
@Autowired
private CouponService couponService;
@Autowired
private PriceBiz priceBiz;
@GetMapping("/{scenicId}/goodsList")
public ApiResponse<List<GoodsListRespVO>> scenicGoodsList(@PathVariable Long scenicId) {
List<GoodsListRespVO> data = priceBiz.listGoodsByScenic(scenicId);
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);
return ApiResponse.success(couponService.update(coupon));
}
@PutMapping("/updateStatus/{id}")
public ApiResponse<Boolean> updateStatus(@PathVariable Integer id) {
return ApiResponse.success(couponService.updateStatus(id));
}
@ApiOperation("删除优惠券")
@DeleteMapping("/delete/{id}")
public ApiResponse<Boolean> delete(@PathVariable Integer id) {
return ApiResponse.success(couponService.delete(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());
List<CouponRespVO> list = couponService.list(couponQuery);
PageInfo<CouponRespVO> pageInfo = new PageInfo<>(list);
return ApiResponse.success(pageInfo);
}
}

@ -3,6 +3,7 @@ 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;
@ -62,6 +63,11 @@ public class DeviceController {
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));

@ -0,0 +1,56 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.constant.BaseContextHandler;
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.service.pc.PermissionService;
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;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
@RestController
@RequestMapping("/api/permission/v1")
@Api(tags = "权限管理接口")
public class PermissionController {
@Autowired
private PermissionService permissionService;
@GetMapping("/get/")
public ApiResponse<PermissionResp> getPermissionByUser() {
String userId = BaseContextHandler.getUserId();
PermissionEntity permission = permissionService.getPermissionByUserId(Long.parseLong(userId));
if (permission == null || StringUtils.isEmpty(permission.getPermString())) {
return ApiResponse.success(new PermissionResp(new ArrayList<>()));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermString(), ","))));
}
@ApiOperation("根据用户ID查询权限信息")
@GetMapping("/get/{userId}")
public ApiResponse<PermissionResp> getPermissionByUser(@PathVariable Long userId) {
PermissionEntity permission = permissionService.getPermissionByUserId(userId);
if (permission == null || StringUtils.isEmpty(permission.getPermString())) {
return ApiResponse.success(new PermissionResp(new ArrayList<>()));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermString(), ","))));
}
@ApiOperation("保存或更新权限信息")
@PostMapping("/save/{userId}")
public ApiResponse saveOrUpdate(@PathVariable Long userId, @RequestBody PermissionSaveReq req) {
permissionService.saveOrUpdate(userId, StringUtils.join(req.getPermissions(), ","));
return ApiResponse.success(true);
}
}

@ -0,0 +1,54 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.model.pc.printer.entity.PrinterEntity;
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;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/printer/v1")
public class PrinterController {
@Autowired
private PrinterService printerService;
@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);
}
}

@ -0,0 +1,68 @@
package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
import com.ycwl.basic.model.pc.scenic.req.ScenicAccountReqQuery;
import com.ycwl.basic.service.pc.ScenicAccountService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@RestController
@RequestMapping("/api/scenicAccount/v1")
public class ScenicAccountController {
@Autowired
private ScenicAccountService service;
// 添加景区账号
@PostMapping("/add")
public ApiResponse addScenicAccount(@RequestBody ScenicAccountEntity entity) {
int result = service.addScenicAccount(entity);
return result > 0 ? ApiResponse.success("添加成功") : ApiResponse.fail("添加失败");
}
// 删除景区账号
@DeleteMapping("/delete/{id}")
public ApiResponse deleteScenicAccount(@PathVariable Long id) {
int result = service.deleteScenicAccount(id);
return result > 0 ? ApiResponse.success("删除成功") : ApiResponse.fail("删除失败");
}
@PostMapping("/updateStatus/{id}")
public ApiResponse updateStatus(@PathVariable Long id) {
int result = service.updateStatus(id);
return result > 0 ? ApiResponse.success("更新成功") : ApiResponse.fail("更新失败");
}
// 更新景区账号
@PostMapping("/update")
public ApiResponse updateScenicAccount(@RequestBody ScenicAccountEntity entity) {
int result = service.updateScenicAccount(entity);
return result > 0 ? ApiResponse.success("更新成功") : ApiResponse.fail("更新失败");
}
// 根据ID查询景区账号
@GetMapping("/get/{id}")
public ApiResponse getScenicAccount(@PathVariable Long id) {
ScenicAccountEntity entity = service.getScenicAccountById(id);
return entity != null ? ApiResponse.success(entity) : ApiResponse.fail("未找到该账号");
}
// 根据景区ID查询超级账号
@GetMapping("/super/{scenicId}")
public ApiResponse getSuperAccount(@PathVariable Long scenicId) {
ScenicAccountEntity entity = service.getSuperAccountByScenicId(scenicId);
return entity != null ? ApiResponse.success(entity) : ApiResponse.fail("未找到超级账号");
}
// 新增分页查询接口
@PostMapping("/page")
public ApiResponse<PageInfo<ScenicAccountEntity>> pageQuery(@RequestBody ScenicAccountReqQuery req) {
PageHelper.startPage(req.getPageNum(), req.getPageSize());
List<ScenicAccountEntity> list = service.pageQuery(req);
PageInfo<ScenicAccountEntity> pageInfo = new PageInfo<>(list);
return ApiResponse.success(pageInfo);
}
}

@ -8,6 +8,7 @@ import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
import com.ycwl.basic.service.pc.ScenicService;
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 com.ycwl.basic.repository.ScenicRepository;
@ -109,12 +110,16 @@ public class ScenicController {
String appState = mpConfig.getState();
String path = "pages/home/index?scenicId=" + id;
String filePath = "qr_code_" + id + ".jpg";
IStorageAdapter adapter = StorageFactory.use();
if (adapter.isExists(filePath)) {
return ApiResponse.success(adapter.getUrl(filePath));
}
try {
WxMpUtil.generateWXAQRCode(appId, appSecret, appState, path, filePath);
IStorageAdapter adapter = StorageFactory.use();
File file = new File(filePath);
String s = adapter.uploadFile(file, filePath);
file.delete();
adapter.setAcl(StorageAcl.PUBLIC_READ, filePath);
return ApiResponse.success(s);
} catch (Exception e) {
return ApiResponse.fail("生成二维码失败");

@ -0,0 +1,44 @@
package com.ycwl.basic.controller.printer;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.printer.req.PrinterSyncReq;
import com.ycwl.basic.model.printer.req.WorkerAuthReqVo;
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 {
@Autowired
private PrinterService printerService;
@PostMapping("/sync")
public ApiResponse<TaskSyncResp> sync(@RequestBody PrinterSyncReq req) {
List<PrintTaskResp> tasks = printerService.sync(req);
TaskSyncResp resp = new TaskSyncResp();
resp.setTasks(tasks);
return ApiResponse.success(resp);
}
@PostMapping("/{taskId}/success")
public ApiResponse taskSuccess(@PathVariable Integer taskId, @RequestBody WorkerAuthReqVo req) {
printerService.taskSuccess(taskId, req);
return ApiResponse.success("OK");
}
@PostMapping("/{taskId}/fail")
public ApiResponse taskFail(@PathVariable Integer taskId, @RequestBody WorkerAuthReqVo req) {
printerService.taskFail(taskId, req);
return ApiResponse.success("OK");
}
}

@ -7,6 +7,10 @@ import com.alibaba.fastjson.JSONObject;
import com.ycwl.basic.annotation.IgnoreLogReq;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.annotation.RequestToFile;
import com.ycwl.basic.aspectj.HttpSaver;
import com.ycwl.basic.facebody.FaceBodyFactory;
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;
@ -19,6 +23,8 @@ 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.ImageListObject;
import com.ycwl.basic.model.viid.entity.ImageObject;
import com.ycwl.basic.model.viid.entity.ResponseStatusObject;
import com.ycwl.basic.model.viid.entity.SubImageInfoObject;
import com.ycwl.basic.model.viid.entity.SubImageList;
@ -32,6 +38,7 @@ 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;
@ -52,6 +59,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -59,9 +67,14 @@ 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.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.service.task.impl.TaskFaceServiceImpl.generateEntityId;
@IgnoreToken
@RestController
@ -78,6 +91,20 @@ public class ViidController {
private DeviceRepository deviceRepository;
@Autowired
private ScenicRepository scenicRepository;
@Autowired
private TaskFaceService taskFaceService;
private final Map<String, ThreadPoolExecutor> executors = new ConcurrentHashMap<>();
@Autowired
private ScenicService scenicService;
private ThreadPoolExecutor getExecutor(String deviceId) {
ThreadPoolExecutor executor = executors.get(deviceId);
if (executor == null) {
executor = new ThreadPoolExecutor(4, 4096, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(4096));
executors.put(deviceId, executor);
}
return executor;
}
// region 注册注销基础接口
/**
@ -194,8 +221,6 @@ public class ViidController {
@Autowired
private FaceSampleMapper faceSampleMapper;
@Autowired
private TaskFaceService taskFaceService;
private final SimpleDateFormat sdfTime = new SimpleDateFormat("yyyyMMddHHmmss");
@ -220,10 +245,6 @@ public class ViidController {
if (device == null) {
continue;
}
if (!Integer.valueOf(1).equals(device.getStatus())) {
log.info("设备状态为关闭,跳过该设备。deviceId:{}", deviceID);
continue;
}
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(device.getId());
int viidMode = 0;
if (deviceConfig != null && deviceConfig.getViidType() != null) {
@ -255,14 +276,8 @@ public class ViidController {
if (scenicId == null) {
continue;
}
IStorageAdapter scenicStorageAdapter;
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
scenicStorageAdapter = StorageFactory.get(scenicConfig.getStoreType());
scenicStorageAdapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
scenicStorageAdapter = StorageFactory.use("video");
}
IStorageAdapter scenicStorageAdapter = scenicService.getScenicStorageAdapter(scenicId);
IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(scenicId);
FacePositionObject facePosition = new FacePositionObject();
facePosition.setLtY(face.getLeftTopY());
facePosition.setLtX(face.getLeftTopX());
@ -284,22 +299,33 @@ public class ViidController {
// Type=11 人脸
if (subImage.getType().equals("11")) {
// 上传oss
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, "user-face", UUID.randomUUID() + "." + ext);
faceSample.setFaceUrl(url);
faceSampleMapper.add(faceSample);
new Thread(() -> {
taskFaceService.addFaceSample(faceSample.getId());
if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
DynamicTaskGenerator.addTask(faceSample.getId());
}
}).start();
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, "user-face", UUID.randomUUID() + "." + ext);
faceSample.setFaceUrl(url);
faceSampleMapper.add(faceSample);
ThreadPoolExecutor executor = getExecutor(device.getId().toString());
executor.execute(() -> {
if (faceBodyAdapter != null) {
taskFaceService.assureFaceDb(faceBodyAdapter, scenicId.toString());
AddFaceResp addFaceResp = faceBodyAdapter.addFace(scenicId.toString(), generateEntityId(faceSample), url, newFaceSampleId.toString());
if (addFaceResp != null) {
faceSample.setScore(addFaceResp.getScore());
faceSampleMapper.update(faceSample);
}
}
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());
@ -334,24 +360,34 @@ public class ViidController {
// Type=14 人脸,传™的,有这么传的嘛
if (subImage.getType().equals("14")) {
// 上传oss
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, "user-face", UUID.randomUUID() + "." + ext);
faceSample.setFaceUrl(url);
faceSampleMapper.add(faceSample);
DynamicTaskGenerator.addTask(faceSample.getId());
new Thread(() -> {
taskFaceService.addFaceSample(faceSample.getId());
if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
DynamicTaskGenerator.addTask(faceSample.getId());
}
}).start();
log.info("模式1人脸信息入库成功!设备ID:{}", deviceID);
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, "user-face", UUID.randomUUID() + "." + ext);
faceSample.setFaceUrl(url);
faceSampleMapper.add(faceSample);
DynamicTaskGenerator.addTask(faceSample.getId());
ThreadPoolExecutor executor = getExecutor(device.getId().toString());
executor.execute(() -> {
if (faceBodyAdapter != null) {
taskFaceService.assureFaceDb(faceBodyAdapter, scenicId.toString());
AddFaceResp addFaceResp = faceBodyAdapter.addFace(scenicId.toString(), generateEntityId(faceSample), url, newFaceSampleId.toString());
if (addFaceResp != null) {
faceSample.setScore(addFaceResp.getScore());
faceSampleMapper.update(faceSample);
}
}
if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
DynamicTaskGenerator.addTask(faceSample.getId());
}
});
log.info("模式1人脸信息入库成功!设备ID:{}", deviceID);
}
}
}
}
@ -365,8 +401,9 @@ public class ViidController {
@RequestMapping(value = "/Images", method = RequestMethod.POST)
@IgnoreLogReq
public VIIDBaseResp images(@RequestBody ImageUploadReq req) {
log.info("Images:{}", req);
public VIIDBaseResp images(HttpServletRequest request, @RequestBody ImageUploadReq req) throws IOException {
// log.info("Images:{}", req);
HttpSaver.saveRequestToFile(request);
return new VIIDBaseResp(
new ResponseStatusObject("1", "/VIID/Images", "0", "OK", sdfTime.format(new Date()))
);

@ -7,9 +7,11 @@ import com.ycwl.basic.constant.StorageConstant;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.wvp.WvpSyncReqVo;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;
@ -18,6 +20,7 @@ import com.ycwl.basic.utils.ApiResponse;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -35,11 +38,8 @@ import java.util.Map;
@RequestMapping("/vpt/v1/")
public class VptController {
private final ScenicRepository scenicRepository;
public VptController(ScenicRepository scenicRepository) {
this.scenicRepository = scenicRepository;
}
@Autowired
private ScenicService scenicService;
@IgnoreLogReq
@PostMapping("/scenic/{scenicId}/sync")
@ -48,14 +48,7 @@ public class VptController {
}
@PostMapping("/scenic/{scenicId}/{taskId}/uploadUrl")
public String uploadUrl(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId) {
IStorageAdapter adapter;
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
adapter = StorageFactory.use("video");
}
IStorageAdapter adapter = scenicService.getScenicStorageAdapter(scenicId);
String filename = StorageUtil.joinPath(StorageConstant.VIDEO_PIECE_PATH, taskId.toString() + ".mp4");
String urlForUpload = adapter.getUrlForUpload(new Date(System.currentTimeMillis() + 1000 * 60 * 60), "video/mp4", filename);
urlForUpload = urlForUpload.replace("-internal.aliyuncs.com", ".aliyuncs.com");
@ -63,14 +56,7 @@ public class VptController {
}
@PostMapping("/scenic/{scenicId}/{taskId}/success")
public ApiResponse<String> success(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId, @RequestBody FileObject fileObject) {
IStorageAdapter adapter;
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
adapter = StorageFactory.use("video");
}
IStorageAdapter adapter = scenicService.getScenicStorageAdapter(scenicId);
String filename = StorageUtil.joinPath(StorageConstant.VIDEO_PIECE_PATH, taskId.toString() + ".mp4");
fileObject.setUrl(adapter.getUrl(filename));
adapter.setAcl(StorageAcl.PUBLIC_READ, filename);

@ -10,6 +10,7 @@ import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.wvp.WvpSyncReqVo;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.pc.DeviceService;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;
@ -39,7 +40,7 @@ public class WvpController {
@Autowired
private DeviceService deviceService;
@Autowired
private ScenicRepository scenicRepository;
private ScenicService scenicService;
@IgnoreLogReq
@PostMapping("/scenic/{scenicId}/sync")
@ -50,14 +51,7 @@ public class WvpController {
@PostMapping("/scenic/{scenicId}/{taskId}/uploadUrl")
public String uploadUrl(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId) {
IStorageAdapter adapter;
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
adapter = StorageFactory.use("video");
}
IStorageAdapter adapter = scenicService.getScenicStorageAdapter(scenicId);
String filename = StorageUtil.joinPath(StorageConstant.VIDEO_PIECE_PATH, taskId.toString() + ".mp4");
String urlForUpload = adapter.getUrlForUpload(new Date(System.currentTimeMillis() + 1000 * 60 * 60), "video/mp4", filename);
urlForUpload = urlForUpload.replace("-internal.aliyuncs.com", ".aliyuncs.com");
@ -65,14 +59,7 @@ public class WvpController {
}
@PostMapping("/scenic/{scenicId}/{taskId}/success")
public ApiResponse<String> success(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId, @RequestBody FileObject fileObject) {
IStorageAdapter adapter;
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
adapter = StorageFactory.use("video");
}
IStorageAdapter adapter = scenicService.getScenicStorageAdapter(scenicId);
String filename = StorageUtil.joinPath(StorageConstant.VIDEO_PIECE_PATH, taskId.toString() + ".mp4");
fileObject.setUrl(adapter.getUrl(filename));
adapter.setAcl(StorageAcl.PUBLIC_READ, filename);

@ -76,7 +76,7 @@ public class AliOssStorageOperator extends ADeviceStorageOperator {
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.set(Calendar.SECOND, 0);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd/HHmm");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
while (calendar.getTime().before(endDate)) {
String prefix = dateFormat.format(calendar.getTime());
List<FileObject> fileListByPrefix = getOssFileListByPrefix(prefix);

@ -81,7 +81,7 @@ public class VptPassiveStorageOperator extends ADeviceStorageOperator {
log.info("任务{}获取视频开始!共{}", task.taskId, taskList.size());
Date taskStartTime = new Date();
while (true) {
if (new Date().getTime() - taskStartTime.getTime() > 80000L) {
if (new Date().getTime() - taskStartTime.getTime() > 60000L) {
log.info("任务{}获取视频超时!", task.taskId);
fileListMap.remove(task.taskId);
return Collections.emptyList();

@ -75,7 +75,7 @@ public class WvpPassiveStorageOperator extends ADeviceStorageOperator {
taskList.add(task);
Date taskStartTime = new Date();
while (true) {
if (new Date().getTime() - taskStartTime.getTime() > 80000L) {
if (new Date().getTime() - taskStartTime.getTime() > 60000L) {
log.info("任务{}获取视频超时!", task.taskId);
fileListMap.remove(task.taskId);
return Collections.emptyList();

@ -74,55 +74,9 @@ public class CustomExceptionHandle {
@ExceptionHandler(value = Exception.class)
public ApiResponse<String> handle(Exception e) {
LOGGER.error("系统异常 -> {}", e.getMessage(), e);
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
try {
HttpSaver.saveRequestToFile(attributes.getRequest());
} catch (IOException ex) {
LOGGER.error("保存请求信息失败 -> {}", e.getMessage(), e);
}
}
return ApiResponse.buildResult(BizCodeEnum.SERVER_UNKONWN_ERROR);
}
public String getStackTrace(Throwable e) {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = e.getStackTrace();
sb.append(e.getClass().getName()).append(": ").append(e.getMessage()).append("\r\n");
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append("\tat ").append(stackTraceElement.toString()).append("\r\n");
}
return sb.toString();
}
public String getRequestAsText() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return "---";
}
HttpServletRequest request = attributes.getRequest();
StringBuilder rawReq = new StringBuilder();
rawReq.append(request.getMethod()).append(" ").append(request.getRequestURL());
String queryString = request.getQueryString();
if (queryString != null) {
rawReq.append("?").append(queryString);
}
rawReq.append("\r\n");
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
rawReq.append(headerName).append(": ").append(request.getHeader(headerName)).append("\r\n");
}
rawReq.append("\r\n");
// 获取body
try {
rawReq.append(request.getReader().lines().collect(Collectors.joining("\r\n")));
rawReq.append("\r\n");
} catch (IOException ignored) {
}
return rawReq.toString();
}
/**
* 移动端自定义异常统一处理类
*/

@ -0,0 +1,62 @@
package com.ycwl.basic.facebody;
import com.ycwl.basic.facebody.adapter.AliFaceBodyAdapter;
import com.ycwl.basic.facebody.adapter.BceFaceBodyAdapter;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.facebody.enums.FaceBodyAdapterType;
import com.ycwl.basic.facebody.exceptions.FaceBodyUnsupportedException;
import com.ycwl.basic.storage.exceptions.StorageConfigException;
import com.ycwl.basic.storage.exceptions.StorageUndefinedException;
import java.util.HashMap;
import java.util.Map;
public class FaceBodyFactory {
public static IFaceBodyAdapter getAdapter(String typeName) {
FaceBodyAdapterType adapterEnum;
try {
adapterEnum = FaceBodyAdapterType.valueOf(typeName);
} catch (Exception e) {
throw new FaceBodyUnsupportedException("不支持的Adapter类型");
}
return getAdapter(adapterEnum);
}
public static IFaceBodyAdapter getAdapter(FaceBodyAdapterType type) {
switch (type) {
case ALI:
return new AliFaceBodyAdapter();
case BCE:
return new BceFaceBodyAdapter();
default:
throw new FaceBodyUnsupportedException("不支持的Adapter类型");
}
}
protected static Map<String, IFaceBodyAdapter> namedAdapter = new HashMap<>();
protected static IFaceBodyAdapter defaultAdapter = null;
public static void register(String name, IFaceBodyAdapter adapter) {
namedAdapter.put(name, adapter);
}
public static IFaceBodyAdapter use(String name) {
IFaceBodyAdapter adapter = namedAdapter.get(name);
if (adapter == null) {
throw new StorageUndefinedException("未定义的存储方式:"+name);
}
return adapter;
}
public static IFaceBodyAdapter use() {
if (defaultAdapter == null) {
throw new StorageConfigException("未定义默认存储方式");
}
return defaultAdapter;
}
public static void setDefault(String defaultName) {
FaceBodyFactory.defaultAdapter = use(defaultName);
}
}

@ -0,0 +1,333 @@
package com.ycwl.basic.facebody.adapter;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.facebody.model.v20191230.AddFaceEntityRequest;
import com.aliyuncs.facebody.model.v20191230.AddFaceRequest;
import com.aliyuncs.facebody.model.v20191230.AddFaceResponse;
import com.aliyuncs.facebody.model.v20191230.CreateFaceDbRequest;
import com.aliyuncs.facebody.model.v20191230.DeleteFaceDbRequest;
import com.aliyuncs.facebody.model.v20191230.DeleteFaceEntityRequest;
import com.aliyuncs.facebody.model.v20191230.ListFaceDbsRequest;
import com.aliyuncs.facebody.model.v20191230.ListFaceDbsResponse;
import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesRequest;
import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesResponse;
import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.ycwl.basic.facebody.entity.AddFaceResp;
import com.ycwl.basic.facebody.entity.AliFaceBodyConfig;
import com.ycwl.basic.facebody.entity.SearchFaceResp;
import com.ycwl.basic.facebody.entity.SearchFaceResultItem;
import com.ycwl.basic.utils.ratelimiter.FixedRateLimiter;
import com.ycwl.basic.utils.ratelimiter.IRateLimiter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
public class AliFaceBodyAdapter implements IFaceBodyAdapter {
private static final Map<String, IRateLimiter> addEntityLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> listDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> listFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> searchFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteEntityLimiters = new ConcurrentHashMap<>();
private AliFaceBodyConfig config;
public boolean setConfig(AliFaceBodyConfig config) {
this.config = config;
return true;
}
@Override
public boolean loadConfig(Map<String, String> _config) {
AliFaceBodyConfig config = new AliFaceBodyConfig();
config.setAccessKeyId(_config.get("accessKeyId"));
config.setAccessKeySecret(_config.get("accessKeySecret"));
config.setRegion(_config.get("region"));
this.config = config;
return true;
}
private IRateLimiter getLimiter(LOCK_TYPE type) {
switch (type) {
case ADD_DB:
return addDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
case ADD_ENTITY:
return addEntityLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
case ADD_FACE:
return addFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
case LIST_DB:
return listDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(500, TimeUnit.MILLISECONDS));
case LIST_FACE:
return listFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(2));
case SEARCH_FACE:
return searchFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(200, TimeUnit.MILLISECONDS));
case DELETE_DB:
return deleteDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
case DELETE_ENTITY:
return deleteEntityLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
default:
return new FixedRateLimiter(600, TimeUnit.MILLISECONDS);
}
}
@Override
public boolean addFaceDb(String dbName) {
IRateLimiter addDbLimiter = getLimiter(LOCK_TYPE.ADD_DB);
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
CreateFaceDbRequest request = new CreateFaceDbRequest();
request.setName(dbName);
try {
addDbLimiter.acquire();
} catch (InterruptedException ignored) {
}
client.getAcsResponse(request);
return true;
} catch (ClientException e) {
log.error("阿里云添加人脸数据库失败!", e);
return false;
}
}
@Override
public boolean deleteFaceDb(String dbName) {
ListFaceEntitiesRequest request = new ListFaceEntitiesRequest();
IRateLimiter deleteEntityLimiter = getLimiter(LOCK_TYPE.DELETE_ENTITY);
IRateLimiter deleteDbLimiter = getLimiter(LOCK_TYPE.DELETE_DB);
request.setDbName(dbName);
request.setLimit(200);
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
while (true) {
ListFaceEntitiesResponse response = client.getAcsResponse(request);
if (response.getData().getTotalCount() == 0) {
break;
}
response.getData().getEntities().forEach(entity -> {
DeleteFaceEntityRequest deleteFaceEntityRequest = new DeleteFaceEntityRequest();
deleteFaceEntityRequest.setDbName(entity.getDbName());
deleteFaceEntityRequest.setEntityId(entity.getEntityId());
try {
deleteEntityLimiter.acquire();
} catch (InterruptedException ignored) {
}
try {
client.getAcsResponse(deleteFaceEntityRequest);
} catch (ClientException e) {
log.error("删除人脸数据失败!", e);
}
});
}
DeleteFaceDbRequest deleteFaceDbRequest = new DeleteFaceDbRequest();
deleteFaceDbRequest.setName(dbName);
try {
deleteDbLimiter.acquire();
} catch (InterruptedException ignored) {
}
client.getAcsResponse(deleteFaceDbRequest);
} catch (ClientException e) {
log.error("删除人脸数据库失败!", e);
return false;
}
return true;
}
@Override
public List<String> listFaceDb() {
ListFaceDbsRequest request = new ListFaceDbsRequest();
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
ListFaceDbsResponse response = client.getAcsResponse(request);
return response.getData().getDbList().stream().map(ListFaceDbsResponse.Data.DbListItem::getName).collect(Collectors.toList());
} catch (ClientException e) {
log.error("获取人脸数据库失败!", e);
return Collections.emptyList();
}
}
@Override
public AddFaceResp addFace(String dbName, String entityId, String faceUrl, String extData) {
IRateLimiter addEntityLimiter = getLimiter(LOCK_TYPE.ADD_ENTITY);
IRateLimiter addFaceLimiter = getLimiter(LOCK_TYPE.ADD_FACE);
AddFaceEntityRequest request = new AddFaceEntityRequest();
request.setDbName(dbName);
request.setEntityId(entityId);
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
try {
addEntityLimiter.acquire();
} catch (InterruptedException ignored) {
}
try {
client.getAcsResponse(request);
} catch (ClientException e) {
log.error("addFaceEntity, {}/{}", dbName, entityId, e);
return null;
}
AddFaceRequest addFaceRequest = new AddFaceRequest();
addFaceRequest.setDbName(dbName);
addFaceRequest.setEntityId(entityId);
addFaceRequest.setImageUrl(faceUrl);
addFaceRequest.setExtraData(extData);
AddFaceResp respVo = new AddFaceResp();
try {
addFaceLimiter.acquire();
} catch (InterruptedException ignored) {
}
try {
AddFaceResponse acsResponse = client.getAcsResponse(addFaceRequest);
respVo.setScore(acsResponse.getData().getQualitieScore());
return respVo;
} catch (ClientException e) {
log.error("addFace, {}/{}", dbName, entityId, e);
return null;
}
}
}
@Override
public boolean deleteFace(String dbName, String entityId) {
IRateLimiter deleteEntityLimiter = getLimiter(LOCK_TYPE.DELETE_ENTITY);
DeleteFaceEntityRequest request = new DeleteFaceEntityRequest();
request.setDbName(dbName);
request.setEntityId(entityId);
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
try {
deleteEntityLimiter.acquire();
} catch (InterruptedException ignored) {
}
try {
client.getAcsResponse(request);
return true;
} catch (ClientException e) {
log.error("删除人脸数据失败!", e);
return false;
}
}
}
@Override
public List<String> listFace(String dbName, String prefix, Integer offset, Integer size) {
IRateLimiter listFaceLimiter = getLimiter(LOCK_TYPE.LIST_FACE);
ListFaceEntitiesRequest listFaceEntitiesRequest = new ListFaceEntitiesRequest();
listFaceEntitiesRequest.setDbName(dbName);
listFaceEntitiesRequest.setOrder("asc");
if (offset != null) {
listFaceEntitiesRequest.setOffset(offset);
}
if (size != null) {
listFaceEntitiesRequest.setLimit(size);
} else {
listFaceEntitiesRequest.setLimit(200);
}
if (StringUtils.isNotEmpty(prefix)) {
listFaceEntitiesRequest.setEntityIdPrefix(prefix);
}
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
try {
listFaceLimiter.acquire();
} catch (InterruptedException ignored) {
}
try {
ListFaceEntitiesResponse response = client.getAcsResponse(listFaceEntitiesRequest);
return response.getData().getEntities().stream().map(ListFaceEntitiesResponse.Data.Entity::getEntityId).collect(Collectors.toList());
} catch (ClientException e) {
log.error("获取人脸数据失败!", e);
return null;
}
}
}
@Override
public SearchFaceResp searchFace(String dbName, String faceUrl) {
SearchFaceResp resp = new SearchFaceResp();
IRateLimiter searchFaceLimiter = getLimiter(LOCK_TYPE.SEARCH_FACE);
try (ClientWrapper clientWrapper = getClient()) {
IAcsClient client = clientWrapper.getClient();
SearchFaceRequest request = new SearchFaceRequest();
request.setDbName(dbName);
request.setImageUrl(faceUrl);
request.setLimit(100);
try {
searchFaceLimiter.acquire();
} catch (InterruptedException ignored) {
}
try {
SearchFaceResponse response = client.getAcsResponse(request);
List<SearchFaceResponse.Data.MatchListItem> matchList = response.getData().getMatchList();
if (matchList.isEmpty()) {
resp.setOriginalFaceScore(0f);
return resp;
}
SearchFaceResponse.Data.MatchListItem matchItem = matchList.get(0);
resp.setOriginalFaceScore(matchItem.getQualitieScore());
resp.setResult(matchItem.getFaceItems().stream().map(item -> {
SearchFaceResultItem resultItem = new SearchFaceResultItem();
resultItem.setDbName(dbName);
resultItem.setFaceId(item.getFaceId());
resultItem.setExtData(item.getExtraData());
resultItem.setScore(item.getScore());
return resultItem;
}).collect(Collectors.toList()));
if (!resp.getResult().isEmpty()) {
resp.setFirstMatchRate(resp.getResult().get(0).getScore());
}
return resp;
} catch (ClientException e) {
log.error("搜索人脸失败!", e);
return null;
}
}
}
public ClientWrapper getClient() {
DefaultProfile profile = DefaultProfile.getProfile(
config.getRegion(), config.getAccessKeyId(), config.getAccessKeySecret());
IAcsClient client = new DefaultAcsClient(profile);
return new ClientWrapper(client);
}
@Getter
public static class ClientWrapper implements AutoCloseable {
private final IAcsClient client;
public ClientWrapper(IAcsClient client) {
this.client = client;
}
@Override
public void close() {
if (client == null) {
return;
}
client.shutdown();
}
}
protected enum LOCK_TYPE {
ADD_DB,
ADD_ENTITY,
ADD_FACE,
LIST_DB,
LIST_FACE,
SEARCH_FACE,
DELETE_DB,
DELETE_ENTITY,
}
}

@ -0,0 +1,358 @@
package com.ycwl.basic.facebody.adapter;
import com.baidu.aip.face.AipFace;
import com.ycwl.basic.facebody.entity.AddFaceResp;
import com.ycwl.basic.facebody.entity.AliFaceBodyConfig;
import com.ycwl.basic.facebody.entity.BceFaceBodyConfig;
import com.ycwl.basic.facebody.entity.SearchFaceResp;
import com.ycwl.basic.facebody.entity.SearchFaceResultItem;
import com.ycwl.basic.utils.ratelimiter.FixedRateLimiter;
import com.ycwl.basic.utils.ratelimiter.IRateLimiter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Slf4j
public class BceFaceBodyAdapter implements IFaceBodyAdapter {
protected static final Map<String, AipFace> clients = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addEntityLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> addDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> listDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> listFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> searchFaceLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteDbLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteEntityLimiters = new ConcurrentHashMap<>();
private static final Map<String, IRateLimiter> deleteFaceLimiters = new ConcurrentHashMap<>();
private BceFaceBodyConfig config;
public boolean setConfig(BceFaceBodyConfig config) {
this.config = config;
return true;
}
@Override
public boolean loadConfig(Map<String, String> _config) {
BceFaceBodyConfig config = new BceFaceBodyConfig();
config.setAppId(_config.get("appId"));
config.setApiKey(_config.get("apiKey"));
config.setSecretKey(_config.get("secretKey"));
config.setAddQps(Float.parseFloat(_config.get("addQps")));
config.setSearchQps(Float.parseFloat(_config.get("searchQps")));
this.config = config;
return true;
}
@Override
public boolean addFaceDb(String dbName) {
IRateLimiter addDbLimiter = getLimiter(LOCK_TYPE.ADD_DB);
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
try {
addDbLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.groupAdd(dbName, options);
if (response.getInt("error_code") == 0) {
return true;
} else {
log.warn("创建人脸库失败!{}", response);
return false;
}
} catch (Exception e) {
log.error("创建人脸库失败!", e);
return false;
}
}
@Override
public boolean deleteFaceDb(String dbName) {
IRateLimiter deleteDbLimiter = getLimiter(LOCK_TYPE.DELETE_DB);
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
try {
deleteDbLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.groupDelete(dbName, options);
if (response.getInt("error_code") == 0) {
return true;
} else {
log.warn("删除人脸库失败!{}", response);
return false;
}
} catch (Exception e) {
log.error("删除人脸库失败!", e);
return false;
}
}
@Override
public List<String> listFaceDb() {
IRateLimiter listDbLimiter = getLimiter(LOCK_TYPE.LIST_DB);
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
options.put("start", "0");
options.put("length", "1000");
try {
listDbLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.getGroupList(options);
if (response.getInt("error_code") == 0) {
JSONObject resultObj = response.getJSONObject("result");
if (resultObj != null) {
JSONArray data = resultObj.getJSONArray("group_id_list");
List<String> result = new ArrayList<>();
for (int i = 0; i < data.length(); i++) {
result.add(data.getString(i));
}
return result;
} else {
return Collections.emptyList();
}
} else {
log.warn("获取人脸库列表失败!{}", response);
return Collections.emptyList();
}
} catch (Exception e) {
log.error("获取人脸库列表失败!", e);
return Collections.emptyList();
}
}
@Override
public AddFaceResp addFace(String dbName, String entityId, String faceUrl, String extData) {
IRateLimiter addEntityLimiter = getLimiter(LOCK_TYPE.ADD_FACE);
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
options.put("user_info", extData);
// options.put("quality_control", "LOW");
options.put("action_type", "REPLACE");
try {
addEntityLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.addUser(faceUrl, "URL", dbName, entityId, options);
if (response.getInt("error_code") == 0) {
AddFaceResp resp = new AddFaceResp();
resp.setScore(100f);
return resp;
} else {
log.warn("创建人脸失败!{}", response);
return null;
}
} catch (Exception e) {
log.error("创建人脸失败!", e);
return null;
}
}
@Override
public boolean deleteFace(String dbName, String entityId) {
IRateLimiter deleteFaceLimiter = getLimiter(LOCK_TYPE.DELETE_FACE);
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
List<String> tokenList = listUserFace(dbName, entityId);
AtomicInteger count = new AtomicInteger(0);
tokenList.forEach(faceToken -> {
try {
try {
deleteFaceLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.faceDelete(entityId, dbName, faceToken, options);
if (response.getInt("error_code") != 0) {
log.warn("删除人脸失败!{}", response);
} else {
count.incrementAndGet();
}
} catch (Exception e) {
log.error("删除人脸失败!", e);
}
});
return Integer.valueOf(count.get()).equals(tokenList.size());
} catch (Exception e) {
log.error("删除人脸失败!", e);
return false;
}
}
@Override
public List<String> listFace(String dbName, String prefix, Integer offset, Integer size) {
IRateLimiter listFaceLimiter = getLimiter(LOCK_TYPE.LIST_FACE);
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
options.put("start", offset == null ? "0" : offset.toString());
options.put("length", size == null ? "1000" : size.toString());
try {
listFaceLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.getGroupUsers(dbName, options);
if (response.getInt("error_code") == 0) {
JSONObject resultObj = response.getJSONObject("result");
if (resultObj != null) {
JSONArray data = resultObj.getJSONArray("user_id_list");
List<String> result = new ArrayList<>();
for (int i = 0; i < data.length(); i++) {
result.add(data.getString(i));
}
return result;
} else {
return Collections.emptyList();
}
} else {
log.warn("获取人脸列表失败!{}", response);
return Collections.emptyList();
}
} catch (Exception e) {
log.error("获取人脸列表失败!", e);
return Collections.emptyList();
}
}
public List<String> listUserFace(String dbName, String entityId) {
try {
AipFace client = getClient();
HashMap<String, String> options = new HashMap<>();
JSONObject response = client.faceGetlist(entityId, dbName, options);
if (response.getInt("error_code") == 0) {
JSONObject resultObj = response.getJSONObject("result");
if (resultObj != null) {
JSONArray faceList = resultObj.getJSONArray("face_list");
List<String> result = new ArrayList<>();
for (int i = 0; i < faceList.length(); i++) {
JSONObject jsonObject = faceList.getJSONObject(i);
result.add(jsonObject.getString("face_token"));
}
return result;
} else {
return Collections.emptyList();
}
} else {
log.warn("获取人脸列表失败!{}", response);
return Collections.emptyList();
}
} catch (Exception e) {
log.error("获取人脸列表失败!", e);
return Collections.emptyList();
}
}
@Override
public SearchFaceResp searchFace(String dbName, String faceUrl) {
IRateLimiter searchFaceLimiter = getLimiter(LOCK_TYPE.SEARCH_FACE);
SearchFaceResp resp = new SearchFaceResp();
try {
AipFace client = getClient();
HashMap<String, Object> options = new HashMap<>();
options.put("quality_control", "LOW");
options.put("max_user_num", "50");
try {
searchFaceLimiter.acquire();
} catch (InterruptedException ignored) {
}
JSONObject response = client.search(faceUrl, "URL", dbName, options);
if (response.getInt("error_code") == 0) {
resp.setOriginalFaceScore(100f);
JSONObject resultObj = response.getJSONObject("result");
if (resultObj == null) {
resp.setFirstMatchRate(0f);
return resp;
}
JSONArray userList = resultObj.getJSONArray("user_list");
List<SearchFaceResultItem> result = new ArrayList<>();
for (int i = 0; i < userList.length(); i++) {
JSONObject user = userList.getJSONObject(i);
SearchFaceResultItem item = new SearchFaceResultItem();
item.setDbName(dbName);
item.setFaceId(user.getString("user_id"));
item.setExtData(user.getString("user_info"));
item.setScore(user.getBigDecimal("score").divide(BigDecimal.valueOf(100), 6, RoundingMode.HALF_UP).floatValue());
result.add(item);
}
resp.setResult(result);
if (!result.isEmpty()) {
resp.setFirstMatchRate(result.get(0).getScore());
}
return resp;
} else {
resp.setOriginalFaceScore(0f);
return resp;
}
} catch (Exception e) {
log.error("搜索人脸失败!", e);
return null;
}
}
public AipFace getClient() {
if (clients.containsKey(config.getAppId())) {
return clients.get(config.getAppId());
}
synchronized (clients) {
if (clients.containsKey(config.getAppId())) {
return clients.get(config.getAppId());
}
AipFace client = new AipFace(config.getAppId(), config.getApiKey(), config.getSecretKey());
client.setConnectionTimeoutInMillis(5000);
client.setSocketTimeoutInMillis(60000);
clients.put(config.getAppId(), client);
return client;
}
}
private IRateLimiter getLimiter(LOCK_TYPE type) {
switch (type) {
case ADD_DB:
return addDbLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(100, TimeUnit.MILLISECONDS));
case ADD_FACE:
return addFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(config.getAddQps()));
case LIST_DB:
return listDbLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(100, TimeUnit.MILLISECONDS));
case LIST_FACE:
return listFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(100, TimeUnit.MILLISECONDS));
case SEARCH_FACE:
return searchFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(config.getSearchQps()));
case DELETE_DB:
return deleteDbLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(100, TimeUnit.MILLISECONDS));
case DELETE_ENTITY:
return deleteEntityLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(100, TimeUnit.MILLISECONDS));
case DELETE_FACE:
return deleteFaceLimiters.computeIfAbsent(config.getAppId(), k -> new FixedRateLimiter(100, TimeUnit.MILLISECONDS));
default:
return new FixedRateLimiter(500, TimeUnit.MILLISECONDS);
}
}
protected enum LOCK_TYPE {
ADD_DB,
ADD_FACE,
LIST_DB,
LIST_FACE,
SEARCH_FACE,
DELETE_DB,
DELETE_ENTITY,
DELETE_FACE,
}
}

@ -0,0 +1,29 @@
package com.ycwl.basic.facebody.adapter;
import com.ycwl.basic.facebody.entity.AddFaceResp;
import com.ycwl.basic.facebody.entity.SearchFaceResp;
import java.util.List;
import java.util.Map;
public interface IFaceBodyAdapter {
boolean loadConfig(Map<String, String> _config);
default boolean assureFaceDb(String dbName) {
List<String> faceDbs = listFaceDb();
return faceDbs.contains(dbName) || addFaceDb(dbName);
}
boolean addFaceDb(String dbName);
boolean deleteFaceDb(String dbName);
List<String> listFaceDb();
AddFaceResp addFace(String dbName, String entityId, String faceUrl, String extData);
boolean deleteFace(String dbName, String entityId);
List<String> listFace(String dbName, String prefix, Integer offset, Integer size);
SearchFaceResp searchFace(String dbName, String faceUrl);
}

@ -0,0 +1,8 @@
package com.ycwl.basic.facebody.entity;
import lombok.Data;
@Data
public class AddFaceResp {
private Float score;
}

@ -0,0 +1,10 @@
package com.ycwl.basic.facebody.entity;
import lombok.Data;
@Data
public class AliFaceBodyConfig {
private String accessKeyId;
private String accessKeySecret;
private String region;
}

@ -0,0 +1,12 @@
package com.ycwl.basic.facebody.entity;
import lombok.Data;
@Data
public class BceFaceBodyConfig {
private String appId;
private String apiKey;
private String secretKey;
private float addQps = 2.0f;
private float searchQps = 2.0f;
}

@ -0,0 +1,13 @@
package com.ycwl.basic.facebody.entity;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class SearchFaceResp {
private Float originalFaceScore;
private List<SearchFaceResultItem> result = new ArrayList<>();
private Float firstMatchRate;
}

@ -0,0 +1,12 @@
package com.ycwl.basic.facebody.entity;
import lombok.Data;
@Data
public class SearchFaceResultItem {
private String dbName;
private String faceId;
private String extData;
/** 置信度,0~1 */
private Float score;
}

@ -0,0 +1,18 @@
package com.ycwl.basic.facebody.enums;
import lombok.Getter;
@Getter
public enum FaceBodyAdapterType {
ALI("ALI"),
BCE("BCE"),
;
private final String code;
FaceBodyAdapterType(String code) {
this.code = code;
}
}

@ -0,0 +1,7 @@
package com.ycwl.basic.facebody.exceptions;
public class FaceBodyException extends RuntimeException {
public FaceBodyException(String message) {
super(message);
}
}

@ -0,0 +1,7 @@
package com.ycwl.basic.facebody.exceptions;
public class FaceBodyUnsupportedException extends RuntimeException {
public FaceBodyUnsupportedException(String message) {
super(message);
}
}

@ -0,0 +1,32 @@
package com.ycwl.basic.facebody.starter;
import com.ycwl.basic.facebody.FaceBodyFactory;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.facebody.starter.config.FaceBodyConfig;
import com.ycwl.basic.facebody.starter.config.OverallFaceBodyConfig;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FaceBodyAutoConfiguration {
private final OverallFaceBodyConfig config;
public FaceBodyAutoConfiguration(OverallFaceBodyConfig config) {
this.config = config;
if (config != null) {
if (config.getConfigs() != null) {
loadConfig();
}
if (StringUtils.isNotBlank(config.getDefaultUse())) {
FaceBodyFactory.setDefault(config.getDefaultUse());
}
}
}
private void loadConfig() {
for (FaceBodyConfig item : config.getConfigs()) {
IFaceBodyAdapter adapter = FaceBodyFactory.getAdapter(item.getType());
adapter.loadConfig(item.getConfig());
FaceBodyFactory.register(item.getName(), adapter);
}
}
}

@ -0,0 +1,13 @@
package com.ycwl.basic.facebody.starter.config;
import com.ycwl.basic.facebody.enums.FaceBodyAdapterType;
import lombok.Data;
import java.util.Map;
@Data
public class FaceBodyConfig {
private String name;
private FaceBodyAdapterType type;
private Map<String, String> config;
}

@ -0,0 +1,15 @@
package com.ycwl.basic.facebody.starter.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "facebody")
@Data
public class OverallFaceBodyConfig {
private String defaultUse;
private List<FaceBodyConfig> configs;
}

@ -0,0 +1,21 @@
package com.ycwl.basic.filter;
import com.ycwl.basic.config.CachedBodyHttpServletRequest;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
@Component
public class RequestCacheFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 包装原始请求
HttpServletRequestWrapper wrappedRequest = new CachedBodyHttpServletRequest((HttpServletRequest) request);
// 继续处理请求链
chain.doFilter(wrappedRequest, response);
}
}

@ -0,0 +1,4 @@
package com.ycwl.basic.image.util;
public class ImageUtil {
}

@ -0,0 +1,30 @@
package com.ycwl.basic.image.watermark;
import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkUnsupportedException;
import com.ycwl.basic.image.watermark.operator.IOperator;
import com.ycwl.basic.image.watermark.operator.DefaultImageWatermarkOperator;
import com.ycwl.basic.image.watermark.operator.LeicaWatermarkOperator;
import com.ycwl.basic.image.watermark.operator.NormalWatermarkOperator;
public class ImageWatermarkFactory {
public static IOperator get(String watermarkType) {
ImageWatermarkOperatorEnum type = ImageWatermarkOperatorEnum.getByCode(watermarkType);
if (type == null) {
throw new ImageWatermarkUnsupportedException(watermarkType);
}
return get(type);
}
public static IOperator get(ImageWatermarkOperatorEnum type) {
switch (type) {
case WATERMARK:
return new DefaultImageWatermarkOperator();
case NORMAL:
return new NormalWatermarkOperator();
case LEICA:
return new LeicaWatermarkOperator();
default:
throw new ImageWatermarkUnsupportedException("不支持的类型"+type.name());
}
}
}

@ -0,0 +1,38 @@
package com.ycwl.basic.image.watermark.entity;
import cn.hutool.core.date.DateUtil;
import lombok.Data;
import java.io.File;
import java.util.Date;
@Data
public class WatermarkInfo {
private File originalFile;
/**
* 输出文件
*/
private File watermarkedFile;
private File qrcodeFile;
private String scenicLine;
private String secondLine;
private String thirdLine;
private String fourthLine;
private Date datetime;
private String dtFormat;
private String datetimeLine;
public String getDatetimeLine() {
if (datetimeLine == null) {
datetimeLine = DateUtil.format(datetime, dtFormat);
}
return datetimeLine;
}
public String getScenicLine() {
if (scenicLine == null) {
scenicLine = "";
}
return scenicLine;
}
}

@ -0,0 +1,27 @@
package com.ycwl.basic.image.watermark.enums;
import lombok.Getter;
@Getter
public enum ImageWatermarkOperatorEnum {
WATERMARK("defW", "jpg"),
LEICA("leica", "png"),
NORMAL("normal", "png");
private final String type;
private final String preferFileType;
ImageWatermarkOperatorEnum(String type, String preferFileType) {
this.type = type;
this.preferFileType = preferFileType;
}
public static ImageWatermarkOperatorEnum getByCode(String type) {
for (ImageWatermarkOperatorEnum imageWatermarkOperatorEnum : ImageWatermarkOperatorEnum.values()) {
if (imageWatermarkOperatorEnum.type.equals(type)) {
return imageWatermarkOperatorEnum;
}
}
return null;
}
}

@ -0,0 +1,7 @@
package com.ycwl.basic.image.watermark.exception;
public class ImageWatermarkException extends RuntimeException {
public ImageWatermarkException(String message) {
super(message);
}
}

@ -0,0 +1,7 @@
package com.ycwl.basic.image.watermark.exception;
public class ImageWatermarkUnsupportedException extends ImageWatermarkException {
public ImageWatermarkUnsupportedException(String message) {
super(message);
}
}

@ -0,0 +1,76 @@
package com.ycwl.basic.image.watermark.operator;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
public class DefaultImageWatermarkOperator implements IOperator {
@Override
public File process(WatermarkInfo info) throws ImageWatermarkException {
BufferedImage baseImage;
BufferedImage watermarkImage;
InputStream logoInputStream = getClass().getResourceAsStream("/watermark.png");
if (logoInputStream == null) {
throw new ImageWatermarkException("无法找到 watermark.png 资源文件");
}
try {
baseImage = ImageIO.read(info.getOriginalFile());
watermarkImage = ImageIO.read(logoInputStream);
} catch (IOException e) {
throw new ImageWatermarkException("图片打开失败");
}
// 新图像画布
BufferedImage newImage = new BufferedImage(baseImage.getWidth(), baseImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newImage.createGraphics();
g2d.drawImage(baseImage, 0, 0, null);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f));
g2d.drawImage(watermarkImage, 0, 0, baseImage.getWidth(), baseImage.getHeight(), null);
String fileName = info.getWatermarkedFile().getName();
String formatName = "jpg"; // 默认格式为 jpg
if (fileName.endsWith(".png")) {
formatName = "png";
} else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
formatName = "jpg";
}
ImageWriter writer = ImageIO.getImageWritersByFormatName(formatName).next();
ImageOutputStream ios;
try {
ios = ImageIO.createImageOutputStream(info.getWatermarkedFile());
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败,目标文件无法写入");
}
writer.setOutput(ios);
try {
// 使用 ImageWriter 设置写入质量
ImageWriteParam writeParam = writer.getDefaultWriteParam();
if (writeParam.canWriteCompressed()) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionQuality(0.8f); // 设置写入质量为 80%
}
writer.write(null, new javax.imageio.IIOImage(newImage, null, null), writeParam);
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败");
}
finally {
g2d.dispose();
try {
ios.close();
} catch (IOException ignore) {
}
writer.dispose();
}
return info.getWatermarkedFile();
}
}

@ -0,0 +1,10 @@
package com.ycwl.basic.image.watermark.operator;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import java.io.File;
public interface IOperator {
File process(WatermarkInfo info) throws ImageWatermarkException;
}

@ -0,0 +1,152 @@
package com.ycwl.basic.image.watermark.operator;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* 徕卡水印
*/
@Slf4j
public class LeicaWatermarkOperator implements IOperator {
private static final String FONT_PATH = "/PingFang_SC.ttf";
public static String defaultFontName;
public static float FONT_GLOBAL_OFFSET_PERCENT = 0;
static {
try {
// 加载字体文件流
InputStream fontStream = LeicaWatermarkOperator.class.getResourceAsStream(FONT_PATH);
if (fontStream == null) {
throw new RuntimeException("字体文件未找到!路径:" + FONT_PATH);
}
// 创建字体对象
Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream);
// 注册字体到系统
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(customFont);
// 更新默认字体名称为新字体的逻辑名称
defaultFontName = customFont.getName(); // 如 "PingFang SC"
FONT_GLOBAL_OFFSET_PERCENT = -0.3f;
} catch (FontFormatException | IOException e) {
log.error("加载字体文件失败", e);
defaultFontName = "宋体";
}
}
public static int EXTRA_BOTTOM_PX = 140;
public static int EXTRA_BORDER_PX = 0;
public static Color BG_COLOR = Color.WHITE;
public static int LOGO_SIZE = 50;
public static int LOGO_EXTRA_BORDER = 20;
public static int LOGO_FONT_SIZE = 38;
public static Color logoTextColor = new Color(0x33, 0x33, 0x33);
public static int QRCODE_SIZE = 80;
public static int QRCODE_OFFSET_X = 5;
public static int OFFSET_X = 80;
public static int OFFSET_Y = 30;
public static int SCENIC_FONT_SIZE = 32;
public static Color scenicColor = new Color(0x33, 0x33, 0x33);
public static int DATETIME_FONT_SIZE = 28;
public static Color datetimeColor = new Color(0x99, 0x99, 0x99);
@Override
public File process(WatermarkInfo info) throws ImageWatermarkException {
BufferedImage baseImage;
BufferedImage qrcodeImage;
BufferedImage logoImage;
// 从类路径加载 zt-logo.png
InputStream logoInputStream = getClass().getResourceAsStream("/zt-logo.png");
if (logoInputStream == null) {
throw new ImageWatermarkException("无法找到 zt-logo.png 资源文件");
}
try {
baseImage = ImageIO.read(info.getOriginalFile());
qrcodeImage = ImageIO.read(info.getQrcodeFile());
logoImage = ImageIO.read(logoInputStream);
logoInputStream.close();
} catch (IOException e) {
throw new ImageWatermarkException("图片打开失败");
}
// 新图像画布
BufferedImage newImage = new BufferedImage(baseImage.getWidth() + 2 * EXTRA_BORDER_PX, baseImage.getHeight() + 2 * EXTRA_BORDER_PX + EXTRA_BOTTOM_PX, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newImage.createGraphics();
g2d.setColor(BG_COLOR);
g2d.fillRect(0, 0, newImage.getWidth(), newImage.getHeight());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(baseImage, EXTRA_BORDER_PX, EXTRA_BORDER_PX, null);
int logoHeight = LOGO_SIZE;
int logoWidth = (int) (logoHeight * 1.0 / logoImage.getHeight() * logoImage.getWidth());
g2d.drawImage(logoImage, EXTRA_BORDER_PX + OFFSET_X, EXTRA_BORDER_PX + baseImage.getHeight() + OFFSET_Y + LOGO_EXTRA_BORDER, logoWidth, logoHeight, null);
Font logoTextFont = new Font(defaultFontName, Font.PLAIN, LOGO_FONT_SIZE);
g2d.setFont(logoTextFont);
g2d.setColor(logoTextColor);
FontMetrics logoFontMetrics = g2d.getFontMetrics(logoTextFont);
int logoTextHeight = logoFontMetrics.getHeight();
int logoTextOffsetY = (LOGO_SIZE - logoHeight) / 2;
g2d.drawString("帧途", EXTRA_BORDER_PX + OFFSET_X + logoWidth + 5, EXTRA_BORDER_PX + baseImage.getHeight() + OFFSET_Y + logoTextHeight + LOGO_EXTRA_BORDER + logoTextOffsetY + logoTextHeight * FONT_GLOBAL_OFFSET_PERCENT);
int newQrcodeHeight = QRCODE_SIZE;
int newQrcodeWidth = (int) (newQrcodeHeight * 1.0 / qrcodeImage.getHeight() * qrcodeImage.getWidth());
Font scenicFont = new Font(defaultFontName, Font.PLAIN, SCENIC_FONT_SIZE);
Font datetimeFont = new Font(defaultFontName, Font.PLAIN, DATETIME_FONT_SIZE);
FontMetrics scenicFontMetrics = g2d.getFontMetrics(scenicFont);
FontMetrics datetimeFontMetrics = g2d.getFontMetrics(datetimeFont);
int scenicLineHeight = scenicFontMetrics.getHeight();
int dtLineHeight = datetimeFontMetrics.getHeight();
int scenicLineWidth = scenicFontMetrics.stringWidth(info.getScenicLine());
int datetimeLineWidth = scenicFontMetrics.stringWidth(info.getDatetimeLine());
g2d.drawImage(qrcodeImage, newImage.getWidth() + EXTRA_BORDER_PX - OFFSET_X - newQrcodeWidth - QRCODE_OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth), EXTRA_BORDER_PX + baseImage.getHeight() + + OFFSET_Y, newQrcodeWidth, newQrcodeHeight, null);
g2d.setFont(scenicFont);
g2d.setColor(scenicColor);
g2d.drawString(info.getScenicLine(), newImage.getWidth() + EXTRA_BORDER_PX - OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth), EXTRA_BORDER_PX + baseImage.getHeight() + + OFFSET_Y + scenicLineHeight + scenicLineHeight * FONT_GLOBAL_OFFSET_PERCENT);
g2d.setFont(datetimeFont);
g2d.setColor(datetimeColor);
g2d.drawString(info.getDatetimeLine(), newImage.getWidth() + EXTRA_BORDER_PX - OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth), EXTRA_BORDER_PX + baseImage.getHeight() + + OFFSET_Y + scenicLineHeight + dtLineHeight + dtLineHeight * FONT_GLOBAL_OFFSET_PERCENT);
String fileName = info.getWatermarkedFile().getName();
String formatName = "jpg"; // 默认格式为 jpg
if (fileName.endsWith(".png")) {
formatName = "png";
} else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
formatName = "jpg";
}
ImageWriter writer = ImageIO.getImageWritersByFormatName(formatName).next();
ImageOutputStream ios;
try {
ios = ImageIO.createImageOutputStream(info.getWatermarkedFile());
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败,目标文件无法写入");
}
writer.setOutput(ios);
try {
// 使用 ImageWriter 设置写入质量
ImageWriteParam writeParam = writer.getDefaultWriteParam();
if (writeParam.canWriteCompressed()) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionQuality(0.95f); // 设置写入质量为 95%
}
writer.write(null, new javax.imageio.IIOImage(newImage, null, null), writeParam);
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败");
}
finally {
g2d.dispose();
try {
ios.close();
} catch (IOException ignore) {
}
writer.dispose();
}
return info.getWatermarkedFile();
}
}

@ -0,0 +1,128 @@
package com.ycwl.basic.image.watermark.operator;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
public class NormalWatermarkOperator implements IOperator {
private static final String FONT_PATH = "/PingFang_SC.ttf";
public static String defaultFontName;
public static float FONT_GLOBAL_OFFSET_PERCENT = 0;
static {
try {
// 加载字体文件流
InputStream fontStream = LeicaWatermarkOperator.class.getResourceAsStream(FONT_PATH);
if (fontStream == null) {
throw new RuntimeException("字体文件未找到!路径:" + FONT_PATH);
}
// 创建字体对象
Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream);
// 注册字体到系统
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(customFont);
// 更新默认字体名称为新字体的逻辑名称
defaultFontName = customFont.getName(); // 如 "PingFang SC"
FONT_GLOBAL_OFFSET_PERCENT = -0.3f;
} catch (FontFormatException | IOException e) {
log.error("加载字体文件失败", e);
defaultFontName = "宋体";
}
}
public static int EXTRA_BORDER_PX = 0;
public static int OFFSET_Y = 90;
public static Color BG_COLOR = Color.WHITE;
public static int QRCODE_SIZE = 100;
public static int QRCODE_OFFSET_X = 10;
public static int SCENIC_FONT_SIZE = 42;
public static Color scenicColor = Color.white;
public static int DATETIME_FONT_SIZE = 42;
public static Color datetimeColor = Color.white;
@Override
public File process(WatermarkInfo info) throws ImageWatermarkException {
BufferedImage baseImage;
BufferedImage qrcodeImage;
try {
baseImage = ImageIO.read(info.getOriginalFile());
qrcodeImage = ImageIO.read(info.getQrcodeFile());
} catch (IOException e) {
throw new ImageWatermarkException("图片打开失败");
}
// 新图像画布
BufferedImage newImage = new BufferedImage(baseImage.getWidth() + 2 * EXTRA_BORDER_PX, baseImage.getHeight() + 2 * EXTRA_BORDER_PX, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newImage.createGraphics();
g2d.setColor(BG_COLOR);
g2d.fillRect(0, 0, newImage.getWidth(), newImage.getHeight());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(baseImage, EXTRA_BORDER_PX, EXTRA_BORDER_PX, null);
int newQrcodeHeight = QRCODE_SIZE;
int newQrcodeWidth = (int) (newQrcodeHeight * 1.0 / qrcodeImage.getHeight() * qrcodeImage.getWidth());
Font scenicFont = new Font(defaultFontName, Font.PLAIN, SCENIC_FONT_SIZE);
Font datetimeFont = new Font(defaultFontName, Font.PLAIN, DATETIME_FONT_SIZE);
FontMetrics scenicFontMetrics = g2d.getFontMetrics(scenicFont);
FontMetrics datetimeFontMetrics = g2d.getFontMetrics(datetimeFont);
int scenicLineHeight = scenicFontMetrics.getHeight();
int dtLineHeight = datetimeFontMetrics.getHeight();
int scenicLineWidth = scenicFontMetrics.stringWidth(info.getScenicLine());
int datetimeLineWidth = scenicFontMetrics.stringWidth(info.getDatetimeLine());
int offsetX = (newImage.getWidth() - newQrcodeWidth - QRCODE_OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth)) / 2;
int offsetY = EXTRA_BORDER_PX + baseImage.getHeight() - OFFSET_Y - newQrcodeHeight;
g2d.drawImage(qrcodeImage, offsetX, offsetY, newQrcodeWidth, newQrcodeHeight, null);
g2d.setFont(scenicFont);
g2d.setColor(scenicColor);
g2d.drawString(info.getScenicLine(), offsetX + newQrcodeWidth + QRCODE_OFFSET_X, offsetY + scenicLineHeight + FONT_GLOBAL_OFFSET_PERCENT * scenicLineHeight);
g2d.setFont(datetimeFont);
g2d.setColor(datetimeColor);
g2d.drawString(info.getDatetimeLine(), offsetX + newQrcodeWidth + QRCODE_OFFSET_X, offsetY + scenicLineHeight + dtLineHeight + FONT_GLOBAL_OFFSET_PERCENT * dtLineHeight);
String fileName = info.getWatermarkedFile().getName();
String formatName = "jpg"; // 默认格式为 jpg
if (fileName.endsWith(".png")) {
formatName = "png";
} else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
formatName = "jpg";
}
ImageWriter writer = ImageIO.getImageWritersByFormatName(formatName).next();
ImageOutputStream ios;
try {
ios = ImageIO.createImageOutputStream(info.getWatermarkedFile());
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败,目标文件无法写入");
}
writer.setOutput(ios);
try {
// 使用 ImageWriter 设置写入质量
ImageWriteParam writeParam = writer.getDefaultWriteParam();
if (writeParam.canWriteCompressed()) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionQuality(0.95f); // 设置写入质量为 95%
}
writer.write(null, new javax.imageio.IIOImage(newImage, null, null), writeParam);
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败");
}
finally {
g2d.dispose();
try {
ios.close();
} catch (IOException ignore) {
}
writer.dispose();
}
return info.getWatermarkedFile();
}
}

@ -0,0 +1,17 @@
package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.model.pc.coupon.req.CouponQueryReq;
import com.ycwl.basic.model.pc.coupon.resp.CouponRespVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CouponMapper extends BaseMapper<CouponEntity> {
List<CouponRespVO> selectByQuery(CouponQueryReq query);
int updateStatus(Integer id);
CouponEntity getById(Integer couponId);
}

@ -0,0 +1,12 @@
package com.ycwl.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.model.pc.couponRecord.entity.CouponRecordEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CouponRecordMapper extends BaseMapper<CouponRecordEntity> {
List<CouponRecordEntity> queryByUserWithGoodsId(Long scenicId, Long memberId, String goodsId);
}

@ -6,6 +6,7 @@ import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
@ -16,6 +17,7 @@ import java.util.List;
@Mapper
public interface FaceMapper {
List<FaceRespVO> list(FaceReqQuery faceReqQuery);
List<FaceRespVO> test();
List<FaceRespVO> listByScenicIdAndNotFinished(Long scenicId);
FaceRespVO getById(Long id);
FaceEntity get(Long id);
@ -32,4 +34,6 @@ public interface FaceMapper {
FaceRespVO findLastFaceByScenicAndUserId(Long scenicId, Long userId);
List<FaceRespVO> listByScenicAndUserId(String scenicId, Long userId);
List<FaceEntity> listEntityBeforeDate(Long scenicId, Date endDate);
}

@ -3,7 +3,6 @@ package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.member.entity.MemberEntity;
import com.ycwl.basic.model.pc.member.req.MemberReqQuery;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.utils.ApiResponse;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@ -49,5 +49,10 @@ public interface OrderMapper {
OrderItemEntity getOrderItem(Long orderItemId);
int updateOrderPrice(OrderEntity updateEntity);
int updateOrder(OrderEntity updateEntity);
OrderEntity queryTypeOrder(Long userId, Long scenicId, int orderType, Integer priceConfigId);
OrderEntity getUserOrderItem(Long userId, Long scenicId, int orderType, Long configId, Integer goodsType, Long goodsId);
}

@ -0,0 +1,15 @@
package com.ycwl.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.model.pc.permission.entity.PermissionEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface PermissionMapper extends BaseMapper<PermissionEntity> {
// 新增用户ID查询方法
PermissionEntity selectByUserId(@Param("userId") Long userId);
void insertPermission(PermissionEntity entity);
void updatePermission(PermissionEntity entity);
}

@ -0,0 +1,29 @@
package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity;
import com.ycwl.basic.model.pc.printer.entity.PrinterEntity;
import com.ycwl.basic.model.printer.resp.PrintTaskResp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface PrinterMapper {
List<PrinterEntity> list(@Param("condition") PrinterEntity condition);
PrinterEntity getById(@Param("id") Integer id);
int add(PrinterEntity entity);
int update(PrinterEntity entity);
int deleteById(@Param("id") Integer id);
PrinterEntity findByAccessKey(String accessKey);
PrintTaskResp findTaskByPrinterId(Integer printerId);
int updateTaskStatus(@Param("id") Integer id, @Param("status") Integer status);
PrintTaskEntity getTaskById(Integer id);
}

@ -1,8 +1,11 @@
package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
import com.ycwl.basic.model.pc.scenic.req.ScenicAccountReqQuery;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ScenicAccountMapper {
ScenicAccountEntity getByAccount(String account);
@ -14,4 +17,6 @@ public interface ScenicAccountMapper {
int deleteByScenicId(Long scenicId);
ScenicAccountEntity findAccountById(String id);
List<ScenicAccountEntity> pageQuery(ScenicAccountReqQuery req);
}

@ -2,6 +2,7 @@ package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.pc.source.entity.SourceWatermarkEntity;
import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
import org.apache.ibatis.annotations.Mapper;
@ -74,4 +75,7 @@ public interface SourceMapper {
int deleteNotBuyRelations(Long scenicId, Date endDate);
int deleteNotBuyFaceRelation(Long userId, Long faceId);
List<SourceWatermarkEntity> listSourceWatermark(List<Long> sourceIds, Long faceId, String watermarkType);
void addSourceWatermark(Long sourceId, Long faceId, String type, String url);
}

@ -91,4 +91,6 @@ public interface StatisticsMapper {
int addStatisticsRecord(StatisticsRecordAddReq req);
List<Long> getBrokerIdListForUser(Long memberId, Date startTime, Date endTime);
Long getUserRecentEnterType(Long memberId, Date endTime);
}

@ -53,4 +53,6 @@ public interface TaskMapper {
TaskEntity get(Long taskId);
List<TaskEntity> listEntity(TaskReqQuery taskReqQuery);
List<TaskRespVO> selectNotRunningByScenicId(Long scenicOnly);
}

@ -39,7 +39,7 @@ public interface VideoMapper {
MemberVideoEntity queryRelationByMemberTask(Long userId, Long taskId);
List<MemberVideoEntity> listRelationByTask(Long taskId);
List<MemberVideoEntity> listRelationByFace(Long userId, Long faceId);
List<MemberVideoEntity> listRelationByFaceAndTemplate(Long userId, Long faceId, Long templateId);
List<MemberVideoEntity> listRelationByFaceAndTemplate(Long faceId, Long templateId);
List<TaskEntity> listTaskByScenicRelation(Long userId, Long scenicId);

@ -43,4 +43,5 @@ public class GoodsDetailVO {
private BigDecimal price;
@ApiModelProperty("是否已购买 0否 1是")
private Integer isBuy;
private Integer isFree;
}

@ -14,6 +14,7 @@ public class GoodsReqQuery {
@ApiModelProperty("是否已购买 0否 1是")
private Integer isBuy;
private Long faceId;
private Long goodsId;
@ApiModelProperty("景区id")
private Long scenicId;
@ApiModelProperty("源素材商品类型 1视频 2图像")

@ -0,0 +1,26 @@
package com.ycwl.basic.model.mobile.goods;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* @Author:longbinbin
* @Date:2024/12/5 15:10
*/
@Data
@ApiModel("商品详情")
public class GoodsUrlVO {
@ApiModelProperty("商品类型 0:vlog 1:成片视频 2:源素材")
private Integer goodsType;
@ApiModelProperty("商品id goodsType=0时为videoId,goodsType=2时为sourceId")
private Long goodsId;
@ApiModelProperty("图片文件存储地址")
private String url;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
}

@ -0,0 +1,33 @@
package com.ycwl.basic.model.mobile.order;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class IsBuyBatchRespVO {
private boolean buy;
private boolean free;
private Long orderId;
private Integer configId;
private int type;
private String goodsIds;
private BigDecimal origPrice = BigDecimal.ZERO;
private Integer couponId;
private Integer couponRecordId;
private BigDecimal couponPrice = BigDecimal.ZERO;
private BigDecimal slashPrice;
public BigDecimal getPrice() {
return origPrice.subtract(couponPrice);
}
public BigDecimal getDiscountPrice() {
if (slashPrice == null) {
return BigDecimal.ZERO;
}
if (slashPrice.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
}
return slashPrice.subtract(origPrice);
}
}

@ -11,6 +11,22 @@ public class IsBuyRespVO {
private Long orderId;
private int goodsType;
private Long goodsId;
private BigDecimal price;
private BigDecimal origPrice = BigDecimal.ZERO;
private Integer couponId;
private Integer couponRecordId;
private BigDecimal couponPrice = BigDecimal.ZERO;
private BigDecimal slashPrice;
public BigDecimal getPrice() {
return origPrice.subtract(couponPrice);
}
public BigDecimal getDiscountPrice() {
if (slashPrice == null) {
return BigDecimal.ZERO;
}
if (slashPrice.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
}
return slashPrice.subtract(origPrice);
}
}

@ -0,0 +1,48 @@
package com.ycwl.basic.model.pc.coupon.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
@Data
@TableName("coupon")
public class CouponEntity {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Long scenicId;
// 新增优惠券名称字段
private String name;
/**
* 优惠券类别,0:普通优惠券;1:第一次推送;2:第二次;3:第三次
*/
private Integer type;
/**
* 价格配置ID,逗号分隔字符串
*/
private String configIds;
/**
* 0降价,1打折
*/
private Integer discountType;
private BigDecimal discountPrice;
/**
* 状态:0不开启;1开启
*/
private Integer status;
private Date createAt;
public BigDecimal calculateDiscountPrice(BigDecimal originalPrice) {
if (discountType == 0) {
return discountPrice;
} else {
return originalPrice.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_DOWN).multiply(discountPrice);
}
}
}

@ -0,0 +1,33 @@
package com.ycwl.basic.model.pc.coupon.req;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.ycwl.basic.model.common.BaseQueryParameterReq;
import lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("优惠券查询请求参数")
public class CouponQueryReq extends BaseQueryParameterReq {
@ApiModelProperty("景区ID")
private Long scenicId;
private String name;
@ApiModelProperty("优惠券类型:0普通/1首次推送/2二次/3三次")
private Integer type;
@ApiModelProperty("折扣类型:0降价/1打折")
private Integer discountType;
@ApiModelProperty("状态:0关闭/1开启")
private Integer status;
@ApiModelProperty("创建时间起始")
private Date createAtStart;
@ApiModelProperty("创建时间结束")
private Date createAtEnd;
}

@ -0,0 +1,34 @@
package com.ycwl.basic.model.pc.coupon.resp;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class CouponRespVO {
private Integer id;
private Long scenicId;
private String scenicName;
// 新增优惠券名称字段
private String name;
/**
* 优惠券类别,0:普通优惠券;1:第一次推送;2:第二次;3:第三次
*/
private Integer type;
/**
* 价格配置ID,逗号分隔字符串
*/
private String configIds;
/**
* 0降价,1打折
*/
private Integer discountType;
private BigDecimal discountPrice;
/**
* 状态:0不开启;1开启
*/
private Integer status;
private Date createAt;
}

@ -0,0 +1,20 @@
package com.ycwl.basic.model.pc.couponRecord.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.util.Date;
@Data
public class CouponRecordEntity {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer couponId;
private Long memberId;
private Long faceId;
private Integer status;
private Date createTime;
private Date usedTime;
private Long usedOrderId;
}

@ -0,0 +1,11 @@
package com.ycwl.basic.model.pc.couponRecord.req;
import lombok.Data;
@Data
public class CouponRecordUserQueryReq {
private Long scenicId;
private Long memberId;
private Long faceId;
private Integer couponType;
}

@ -0,0 +1,26 @@
package com.ycwl.basic.model.pc.couponRecord.resp;
import com.ycwl.basic.model.pc.coupon.entity.CouponEntity;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class CouponRecordQueryResp {
private boolean exist = false;
private Integer id;
private Long scenicId;
private Integer couponId;
private Long memberId;
private Long faceId;
private Integer status;
private Date createTime;
private Date usedTime;
private Long usedOrderId;
private CouponEntity coupon;
public boolean isUsable() {
return Integer.valueOf(0).equals(status);
}
}

@ -58,4 +58,7 @@ public class DeviceConfigEntity {
*/
private BigDecimal cutPost;
private Integer enablePreBook;
private Integer imageFree;
private Integer videoFree;
private Long pairDevice;
}

@ -0,0 +1,16 @@
package com.ycwl.basic.model.pc.device.req;
import lombok.Data;
import java.util.List;
@Data
public class DeviceBatchSortRequest {
private List<SortItem> list;
@Data
public static class SortItem {
private Long id;
private Integer sort;
}
}

@ -31,6 +31,9 @@ public class DeviceRespVO {
private Integer status;
@ApiModelProperty("是否在线,0不在线,1在线")
private Integer online;
private String coverUrl;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date coverTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

@ -33,7 +33,7 @@ public class FaceDetectLog {
private String matchLocalRecord;
@TableField(exist = false)
private List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> matchRawRecord;
private List matchRawRecord;
public static FaceDetectLog quickCreate(String reason) {
FaceDetectLog log = new FaceDetectLog();
@ -53,19 +53,19 @@ public class FaceDetectLog {
return this;
}
public FaceDetectLog fillResponse(SearchFaceResponse response) {
this.matchRawResult = JSONObject.toJSONString(response.getData());
public FaceDetectLog fillResponse(Object response) {
this.matchRawResult = JSONObject.toJSONString(response);
return this;
}
public List<MatchLocalRecord> matchLocalRecord() {
public List matchLocalRecord() {
if (matchLocalRecord == null) {
return null;
}
return JSONArray.parseArray(matchLocalRecord, MatchLocalRecord.class);
}
public void matchLocalRecord(List<MatchLocalRecord> matchLocalRecord) {
public void matchLocalRecord(List matchLocalRecord) {
if (matchLocalRecord == null) {
this.matchLocalRecord = null;
} else {

@ -10,8 +10,8 @@ public class MatchLocalRecord {
private String deviceName;
private String faceUrl;
private Float score;
private Float confidence;
private String idStr;
private Date shotDate;
private Boolean matched;
private Boolean accept;
}

@ -14,6 +14,7 @@ import java.util.Date;
@Data
@ApiModel("查询用户信息响应参数")
public class MemberRespVO {
private Long uid;
private Long id;
private Long scenicId;
/**

@ -35,6 +35,9 @@ public class OrderEntity {
* 划线价
*/
private BigDecimal slashPrice;
private BigDecimal couponPrice = BigDecimal.ZERO;
private Integer couponId;
private Integer couponRecordId;
/**
* 优惠价格
*/
@ -88,4 +91,13 @@ public class OrderEntity {
*/
private Date refundAt;
public BigDecimal getDiscountPrice() {
if (slashPrice == null) {
return BigDecimal.ZERO;
}
if (slashPrice.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
}
return slashPrice.subtract(price);
}
}

@ -23,6 +23,7 @@ public class OrderReqQuery extends BaseQueryParameterReq {
private Long memberId;
@ApiModelProperty("用户昵称")
private String memberNickname;
private String memberUid;
@ApiModelProperty("用户真实名称")
private String memberRealName;
/**

@ -89,4 +89,14 @@ public class OrderAppRespVO {
private Integer goodsType;
@ApiModelProperty("订单明细")
private List<OrderItemVO> orderItemList;
public BigDecimal getDiscountPrice() {
if (slashPrice == null) {
return BigDecimal.ZERO;
}
if (slashPrice.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
}
return slashPrice.subtract(price);
}
}

@ -22,8 +22,11 @@ public class OrderRespVO {
private Long faceId;
private String faceUrl;
private Integer type;
private String orderType;
private String goodsName;
@ApiModelProperty("用户昵称")
private String memberNickname;
private String memberUid;
@ApiModelProperty("用户真实名称")
private String memberRealName;
/**
@ -108,4 +111,14 @@ public class OrderRespVO {
private List<OrderItemVO> orderItemList;
private Long scenicId;
private String scenicName;
public BigDecimal getDiscountPrice() {
if (slashPrice == null) {
return BigDecimal.ZERO;
}
if (slashPrice.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
}
return slashPrice.subtract(price);
}
}

@ -0,0 +1,31 @@
package com.ycwl.basic.model.pc.permission.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("permission")
public class PermissionEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("user_id")
private Long userId; // 确保字段映射
@TableField("perm_str")
private String permString;
@TableField("create_time")
private Date createTime;
@TableField("update_time")
private Date updateTime;
}

@ -0,0 +1,10 @@
package com.ycwl.basic.model.pc.permission.req;
import lombok.Data;
import java.util.List;
@Data
public class PermissionSaveReq {
private List<String> permissions;
}

@ -0,0 +1,12 @@
package com.ycwl.basic.model.pc.permission.resp;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class PermissionResp {
private List<String> permissions;
}

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