Compare commits

...

46 Commits

Author SHA1 Message Date
1059d30c21 前端大图展示逻辑
All checks were successful
Gitea/FrameTour-BE/pipeline/head This commit looks good
2025-07-23 10:29:38 +08:00
4d53986277 deviceId不存在时 2025-07-22 17:17:25 +08:00
156c487c0f claude 2025-07-22 17:15:12 +08:00
38e9763033 用户权限 2025-07-22 14:47:56 +08:00
1dc9e16c55 统计:统计点击支付问题,统计今天支付金额问题 2025-07-22 14:37:59 +08:00
4f0d1813c9 登录时返回superAdmin 2025-07-22 11:41:48 +08:00
d2577f0c0f 兼容 2025-07-22 11:12:38 +08:00
2adce97503 调高压缩率 2025-07-22 11:02:22 +08:00
1eb527574b 文字根据自生偏移量定位,确保居中展示 2025-07-22 00:19:04 +08:00
22dea22fc1 更新后的storageAdapter 2025-07-21 23:32:25 +08:00
a425c155d0 中断后继续执行后续逻辑 2025-07-21 18:38:11 +08:00
ceaf10977d 修复送照片,支持送原片 2025-07-21 15:07:10 +08:00
a5a7957f9e 修复下用户端未购买列表 2025-07-21 13:35:00 +08:00
ce0cbb1c91 水印工具调整 2025-07-19 15:49:35 +08:00
2274ca7010 更新密码 2025-07-18 17:18:06 +08:00
18fd50f5d6 登录规整一下 2025-07-18 17:18:06 +08:00
13ef1d1334 二维码位置调整,大小调整 2025-07-18 17:18:06 +08:00
8b957ee96d 支持stableToken,支持无限WXACode 2025-07-18 17:18:06 +08:00
b3df268964 admin验证 2025-07-18 16:38:59 +08:00
f54595466a 下载不需要水印 2025-07-18 14:23:22 +08:00
d7d503212f 渲染机 2025-07-18 14:22:54 +08:00
52086dbea4 设备支持多个配置 2025-07-18 14:21:39 +08:00
f084b7a21f 调整 2025-07-18 14:20:36 +08:00
3aa039f0ea 支持跳过支付 2025-07-18 09:31:05 +08:00
1ca7182979 上报图片直接进行裁切 2025-07-17 17:59:14 +08:00
78079b242a 额外更新少字段的relation 2025-07-17 17:58:40 +08:00
fd66448f4d 对接支付 2025-07-17 15:17:23 +08:00
64c4180e4d 免费数量 2025-07-17 15:17:23 +08:00
af141161de 一体机照片打印 2025-07-16 15:02:16 +08:00
0317600910 免费送照片 2025-07-16 15:01:59 +08:00
eda2c75533 景区账号限制登录状态 2025-07-16 15:01:40 +08:00
0ecd9d5e20 Aio设备下单等 2025-07-16 15:01:03 +08:00
722b8a9e90 Aio设备获取价格配置 2025-07-15 14:52:57 +08:00
69f4ad11ad ly兼容接口更新 2025-07-15 14:52:43 +08:00
49c313b4a0 AioDevice人脸上传 2025-07-13 17:14:36 +08:00
5bac59653b 写错了 2025-07-13 15:44:41 +08:00
0dbaf5eaba AioDevice 2025-07-13 14:13:12 +08:00
d180128edb 更新时删除缓存 2025-07-10 17:32:25 +08:00
bd308f35ae 1 2025-07-10 17:32:15 +08:00
cb312b1a74 对接n9e 2025-07-09 16:34:21 +08:00
6862ddbf58 设备顺序排序 2025-07-08 17:29:48 +08:00
51ba702d82 申请退款时,process当作已成功 2025-07-08 13:55:51 +08:00
ecc011269c 支持分账 2025-07-07 17:39:15 +08:00
f84adc8ebe 景区端支持设备状态返回 2025-07-07 14:18:54 +08:00
17d63688c6 兼容返回格式 2025-07-05 22:18:52 +08:00
3f5dc41310 bug 2025-07-05 22:18:07 +08:00
82 changed files with 1838 additions and 462 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.idea/
logs/
target/
.claude

123
CLAUDE.md Normal file
View File

@@ -0,0 +1,123 @@
# CLAUDE.md
本文件为 Claude Code (claude.ai/code) 在此代码仓库中工作时提供指导。
## 构建和开发命令
### 构建应用程序
```bash
# 清理构建(默认跳过测试)
mvn clean package
# 清理构建并执行测试
mvn clean package -DskipTests=false
# 运行应用程序
mvn spring-boot:run
```
### 测试命令
```bash
# 运行特定测试类
mvn test -Dtest=FaceCleanerTest
# 运行特定包的测试
mvn test -Dtest="com.ycwl.basic.storage.adapters.*Test"
# 运行所有测试
mvn test -DskipTests=false
```
### 开发环境配置
应用程序使用 Spring 配置文件:
- 默认激活配置文件:`dev`
- 生产环境配置文件:`prod`(启用定时任务)
- 配置文件:`application-dev.yml``application-prod.yml`
## 架构概览
这是一个 Spring Boot 3.3.5 应用程序(Java 21),采用多租户架构,通过不同的 API 端点为不同的客户端类型提供服务。
### 控制器架构
- **移动端 APIs** (`/api/mobile/`):面向移动应用的客户端端点
- **PC 端 APIs** (`/api/`):Web 仪表板/管理面板端点
- **任务 APIs** (`/task/`):后台工作和渲染任务端点
- **外部 APIs**:专用集成(打印机、代理、viid、vpt、wvp)
### 核心业务模块
#### 工厂模式实现
三个主要工厂类管理第三方集成:
1. **StorageFactory** (`com.ycwl.basic.storage.StorageFactory`)
- 管理:本地存储、AWS S3、阿里云 OSS 存储适配器
- 配置节:`storage.configs[]`
2. **PayFactory** (`com.ycwl.basic.pay.PayFactory`)
- 管理:微信支付、聪明支付适配器
- 配置节:`pay.configs[]`
3. **FaceBodyFactory** (`com.ycwl.basic.facebody.FaceBodyFactory`)
- 管理:阿里云、百度人脸识别适配器
- 配置节:`facebody.configs[]`
#### 适配器模式
每个工厂使用标准化接口:
- `IStorageAdapter`:文件操作(上传/下载/删除/ACL)
- `IPayAdapter`:支付生命周期(创建/回调/退款)
- `IFaceBodyAdapter`:人脸识别操作
#### 定时任务系统
`com.ycwl.basic.task` 包中的后台任务(仅生产环境):
- `VideoTaskGenerator`:人脸识别和视频处理
- `FaceCleaner`:人脸和存储清理任务
- `DynamicTaskGenerator`:带延迟队列的动态任务创建
- `ScenicStatsTask`:统计数据聚合
### 数据库和持久化
- **MyBatis Plus**:具有自动 CRUD 操作的 ORM
- **MapperScan**:扫描 `com.ycwl.basic.mapper` 及子包
- **数据库**:MySQL 配合 HikariCP 连接池
- **Redis**:会话管理和缓存
### 主要库和依赖
- Spring Boot 3.3.5 启用 Java 21 虚拟线程
- MyBatis Plus 3.5.5 用于数据库操作
- JWT (jjwt 0.9.0) 用于身份验证
- 微信支付 SDK 用于支付处理
- 阿里云 OSS 和 AWS S3 用于文件存储
- 阿里云和百度 SDK 用于人脸识别
- OpenTelemetry 用于可观测性(开发环境中禁用)
### 业务逻辑组织
- **Service 层**:`service` 包中的业务逻辑实现
- **Biz 层**:`biz` 包中的高级业务编排
- **Repository 模式**:`repository` 包中的数据访问抽象
- **自定义异常**:特定领域的异常处理
### 配置管理
每个模块使用 Spring Boot 自动配置启动器:
- 支持多供应商的命名配置
- 通过配置进行默认供应商选择
- 针对不同环境的特定配置文件
## 常见开发模式
### 添加新的存储/支付/人脸识别供应商
1. 实现相应接口(`IStorageAdapter``IPayAdapter``IFaceBodyAdapter`
2. 在相应的类型枚举中添加枚举值
3. 更新工厂的 switch 表达式
4. 如需要,添加配置类
5. 在 application.yml 中更新新供应商配置
### 身份验证上下文
在整个应用程序中使用 `BaseContextHandler.getUserId()` 获取当前已认证用户 ID。
### API 响应模式
所有 API 都返回 `ApiResponse<T>` 包装器,通过 `CustomExceptionHandle` 进行一致的错误处理。
### 添加新的定时任务
1.`com.ycwl.basic.task` 包中创建类
2. 添加 `@Component``@Profile("prod")` 注解
3. 使用 `@Scheduled` 进行基于 cron 的执行
4. 遵循现有的错误处理和日志记录模式

View File

@@ -0,0 +1,148 @@
package com.ycwl.basic.controller.extern;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.mapper.AioDeviceMapper;
import com.ycwl.basic.mapper.MemberMapper;
import com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity;
import com.ycwl.basic.model.aio.entity.AioDeviceEntity;
import com.ycwl.basic.model.aio.entity.AioDevicePriceConfigEntity;
import com.ycwl.basic.model.aio.req.AioDeviceCreateOrderReq;
import com.ycwl.basic.model.aio.resp.AioDeviceCreateOrderResp;
import com.ycwl.basic.model.aio.resp.AioDeviceInfoResp;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.goods.GoodsDetailVO;
import com.ycwl.basic.model.mobile.goods.GoodsReqQuery;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.member.entity.MemberEntity;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.pay.entity.PayResponse;
import com.ycwl.basic.service.aio.AioDeviceService;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.service.pc.OrderService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import com.ycwl.basic.utils.SnowFlakeUtil;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.Date;
import java.util.List;
@Slf4j
@IgnoreToken
@RestController
@RequestMapping("/api/aio")
public class AioDeviceController {
@Autowired
private GoodsService goodsService;
@Autowired
private FaceService faceService;
@Autowired
private MemberMapper memberMapper;
@Autowired
private AioDeviceMapper aioDeviceMapper;
@Autowired
private AioDeviceService aioDeviceService;
@Autowired
private OrderService orderService;
@GetMapping("/info")
public ApiResponse<AioDeviceInfoResp> getDeviceInfo(HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
List<AioDeviceBannerEntity> banners = aioDeviceMapper.getBannerByDeviceId(aioDevice.getId());
return ApiResponse.success(new AioDeviceInfoResp(aioDevice, banners));
}
@GetMapping("/banners")
public ApiResponse<List<AioDeviceBannerEntity>> getBanners(HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
List<AioDeviceBannerEntity> banners = aioDeviceMapper.getBannerByDeviceId(aioDevice.getId());
return ApiResponse.success(banners);
}
@GetMapping("/config")
public ApiResponse<AioDevicePriceConfigEntity> getPriceConfig(HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
AioDevicePriceConfigEntity config = aioDeviceMapper.getPriceConfigByDeviceId(aioDevice.getId());
return ApiResponse.success(config);
}
@PostMapping("/faceUpload")
public ApiResponse<FaceRecognizeResp> faceUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
MemberEntity memberEntity = new MemberEntity();
memberEntity.setScenicId(aioDevice.getScenicId());
memberEntity.setCreateDate(new Date());
memberEntity.setId(SnowFlakeUtil.getLongId());
memberEntity.setNickname("用户");
memberMapper.add(memberEntity);
FaceRecognizeResp resp = faceService.faceUpload(file, aioDevice.getScenicId(), memberEntity.getId());
return ApiResponse.success(resp);
}
@ApiOperation("人脸信息")
@GetMapping("/{faceId}")
public ApiResponse<FaceRespVO> faceInfo(@PathVariable Long faceId) {
return faceService.getById(faceId);
}
@ApiOperation("照片商品列表")
@GetMapping("/{faceId}/photo")
public ApiResponse<List<GoodsDetailVO>> sourceGoodsList(@PathVariable Long faceId) {
GoodsReqQuery query = new GoodsReqQuery();
query.setSourceType(2);
query.setFaceId(faceId);
List<GoodsDetailVO> goodsDetailVOS = goodsService.sourceGoodsList(query);
return ApiResponse.success(goodsDetailVOS);
}
@ApiOperation("创建订单")
@PostMapping("/order")
public ApiResponse<AioDeviceCreateOrderResp> createOrder(HttpServletRequest request, @RequestBody AioDeviceCreateOrderReq req) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
return ApiResponse.success(aioDeviceService.createOrder(aioDevice, req));
}
@ApiOperation("查询订单")
@GetMapping("/order/{orderId}")
public ApiResponse<PayResponse> queryOrder(HttpServletRequest request, @PathVariable("orderId") Long orderId) {
String deviceId = request.getHeader("X-DeviceId");
AioDeviceEntity aioDevice = aioDeviceMapper.getByKey(deviceId);
if (aioDevice == null) {
return ApiResponse.fail("设备不存在");
}
return ApiResponse.success(orderService.queryOrder(orderId));
}
}

View File

@@ -14,8 +14,10 @@ import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.member.entity.MemberEntity;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
import com.ycwl.basic.repository.VideoRepository;
import com.ycwl.basic.repository.VideoTaskRepository;
import com.ycwl.basic.service.mobile.AppScenicService;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.FaceService;
@@ -62,6 +64,8 @@ public class LyCompatibleController {
private TaskTaskServiceImpl taskTaskServiceImpl;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private VideoTaskRepository videoTaskRepository;
@PostMapping("sendPhoto")
@IgnoreToken
@@ -198,6 +202,7 @@ public class LyCompatibleController {
R response = R.ok();
if (collect.get(0) == null) {
response.put("isgen", 1)
.put("face_id", faceVO.getId().toString())
.put("newvideo", Collections.emptyList())
.put("newuservideo", Collections.emptyList());
return response;
@@ -208,6 +213,11 @@ public class LyCompatibleController {
Map<String, Object> map = new HashMap<>();
VideoEntity videoRespVO = videoRepository.getVideo(contentPageVO.getContentId());
map.put("id", videoRespVO.getId().toString());
map.put("task_id", videoRespVO.getTaskId().toString());
TaskEntity task = videoTaskRepository.getTaskById(videoRespVO.getTaskId());
if (task != null) {
map.put("face_id", String.valueOf(task.getFaceId()));
}
map.put("template_cover_image", contentPageVO.getTemplateCoverUrl());
Date taskShotDate = taskTaskServiceImpl.getTaskShotDate(videoRespVO.getTaskId());
map.put("shoottime", DateUtil.format(taskShotDate, "yyyy-MM-dd HH:mm"));
@@ -224,6 +234,7 @@ public class LyCompatibleController {
List<Map<String, Object>> userVideoList = sourceGoodsList.stream().map(goodsDetailVO -> {
Map<String, Object> map = new HashMap<>();
map.put("id", goodsDetailVO.getGoodsId().toString());
map.put("face_id", String.valueOf(goodsDetailVO.getFaceId()));
map.put("openid", openId);
map.put("template_cover_image", goodsDetailVO.getUrl());
map.put("scenicname", goodsDetailVO.getScenicName());
@@ -233,6 +244,8 @@ public class LyCompatibleController {
}).collect(Collectors.toList());
response
.put("isgen", taskStatusVO.getStatus() == 1 ? 0 : 1)
.put("member_id", faceVO.getMemberId().toString())
.put("face_id", faceVO.getId().toString())
.put("newvideo", videoList)
.put("newuservideo", userVideoList);
redisTemplate.opsForValue().set("ly:"+openId, JSON.toJSONString(response), 5, TimeUnit.SECONDS);

View File

@@ -78,4 +78,13 @@ AppFaceController {
List<ContentPageVO> contentPageVOS = faceService.faceContentList(faceId);
return ApiResponse.success(contentPageVOS);
}
@ApiOperation("绑定人脸")
@PostMapping("/{faceId}/bind")
public ApiResponse<String> bind(@PathVariable Long faceId) {
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
faceService.bindFace(faceId, userId);
return ApiResponse.success("OK");
}
}

View File

@@ -2,6 +2,7 @@ package com.ycwl.basic.controller.mobile.manage;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginOldRespVO;
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginReq;
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO;
import com.ycwl.basic.model.mobile.weChat.DTO.WeChatUserInfoDTO;
@@ -49,8 +50,20 @@ public class AppScenicAccountController {
@ApiOperation("登录")
@PostMapping("/login")
@IgnoreToken
public ApiResponse<ScenicLoginRespVO> login(@RequestBody ScenicLoginReq scenicLoginReq) throws Exception {
return scenicService.login(scenicLoginReq);
public ApiResponse<ScenicLoginOldRespVO> login(@RequestBody ScenicLoginReq scenicLoginReq) throws Exception {
ApiResponse<ScenicLoginRespVO> logined = scenicService.login(scenicLoginReq);
ScenicLoginOldRespVO vo = new ScenicLoginOldRespVO();
if (!logined.isSuccess()) {
return ApiResponse.fail(logined.getMsg());
}
vo.setId(logined.getData().getId());
vo.setScenicId(logined.getData().getScenicId().getFirst());
vo.setIsSuper(logined.getData().getIsSuper());
vo.setName(logined.getData().getName());
vo.setAccount(logined.getData().getAccount());
vo.setStatus(logined.getData().getStatus());
vo.setToken(logined.getData().getToken());
return ApiResponse.success(vo);
}
@GetMapping("/myScenicList")

View File

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

View File

@@ -1,10 +1,14 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.mapper.AdminUserMapper;
import com.ycwl.basic.model.pc.adminUser.entity.LoginEntity;
import com.ycwl.basic.model.pc.permission.entity.PermissionEntity;
import com.ycwl.basic.model.pc.permission.req.PermissionSaveReq;
import com.ycwl.basic.model.pc.permission.resp.PermissionResp;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import com.ycwl.basic.service.pc.PermissionService;
import com.ycwl.basic.service.pc.RoleService;
import com.ycwl.basic.utils.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -28,15 +32,29 @@ import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
public class PermissionController {
@Autowired
private PermissionService permissionService;
@Autowired
private AdminUserMapper adminUserMapper;
@Autowired
private RoleService roleService;
@GetMapping("/get/")
public ApiResponse<PermissionResp> getPermissionByUser() {
String userId = BaseContextHandler.getUserId();
PermissionEntity permission = permissionService.getPermissionByUserId(Long.parseLong(userId));
if (permission == null || StringUtils.isEmpty(permission.getPermString())) {
return ApiResponse.success(new PermissionResp(new ArrayList<>(), new ArrayList<>()));
if (MERCHANT.type.equals(BaseContextHandler.getRoleId())) {
PermissionEntity permission = permissionService.getPermissionByUserId(Long.parseLong(userId));
if (permission == null || StringUtils.isEmpty(permission.getPermString())) {
return ApiResponse.success(new PermissionResp(new ArrayList<>(), new ArrayList<>()));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermString(), ",")), Arrays.asList(StringUtils.split(permission.getMenuString(), ","))));
} else {
// admin
LoginEntity login = adminUserMapper.getById(Long.parseLong(userId));
RolePermissionResp permissionByRoleId = roleService.getPermissionByRoleId(login.getRoleId());
if (permissionByRoleId == null) {
return ApiResponse.success(new PermissionResp(new ArrayList<>(), new ArrayList<>()));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permissionByRoleId.getPermStr(), ",")), Arrays.asList(StringUtils.split(permissionByRoleId.getMenuStr(), ","))));
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermString(), ",")), Arrays.asList(StringUtils.split(permission.getMenuString(), ","))));
}
@ApiOperation("根据用户ID查询权限信息")

View File

@@ -2,16 +2,20 @@ package com.ycwl.basic.controller.pc;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.pc.permission.resp.PermissionResp;
import com.ycwl.basic.model.pc.role.req.AddOrUpdateRoleReqVO;
import com.ycwl.basic.model.pc.role.req.RoleListReqVO;
import com.ycwl.basic.model.pc.role.resp.RoleListRespVO;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import com.ycwl.basic.service.pc.RoleService;
import com.ycwl.basic.utils.ApiResponse;
import 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.*;
import java.util.Arrays;
import java.util.List;
@RestController
@@ -24,34 +28,38 @@ public class RoleController {
@PostMapping(value = "/page")
@ApiOperation(value = "角色列表分页查询")
@IgnoreToken
public ApiResponse<PageInfo<RoleListRespVO>> page(@RequestBody RoleListReqVO roleListReqVO) {
return roleService.pageQuery(roleListReqVO);
}
@PostMapping(value = "/list")
@ApiOperation(value = "角色列表")
@IgnoreToken
public ApiResponse<List<RoleListRespVO>> list(@RequestBody RoleListReqVO roleListReqVO) {
return roleService.list(roleListReqVO);
}
@GetMapping("/{roleId}/permission")
@ApiOperation(value = "角色权限列表")
public ApiResponse<PermissionResp> getPermissionByRoleId(@PathVariable("roleId") Long roleId) {
RolePermissionResp permission = roleService.getPermissionByRoleId(roleId);
if (permission == null) {
return ApiResponse.fail("角色不存在");
}
return ApiResponse.success(new PermissionResp(Arrays.asList(StringUtils.split(permission.getPermStr(), ",")), Arrays.asList(StringUtils.split(permission.getMenuStr(), ","))));
}
@PostMapping(value = "/addOrUpdate")
@ApiOperation(value = "添加或更新角色")
@IgnoreToken
public ApiResponse addOrUpdate(@RequestBody AddOrUpdateRoleReqVO addOrUpdateRoleReqVO) {
return roleService.addOrUpdate(addOrUpdateRoleReqVO);
}
@GetMapping(value = "/delete/{id}")
@ApiOperation(value = "删除")
@IgnoreToken
public ApiResponse delete(@PathVariable("id") String id) {
return roleService.delete(id);
}
@GetMapping(value = "/updateReturnMenu/{id}")
@ApiOperation(value = "编辑回显该角色当前菜单")
@IgnoreToken
public ApiResponse updateReturnMenu(@PathVariable("id") String id) {
return roleService.updateReturnMenu(id);
}
@@ -60,7 +68,6 @@ public class RoleController {
@GetMapping(value = "/updateStatus/{id}")
@ApiOperation(value = "更改角色类型状态")
//@IgnoreToken
public ApiResponse updateStatus(@PathVariable("id") String id) {
return roleService.updateStatus(id);
}

View File

@@ -12,6 +12,7 @@ import com.ycwl.basic.mapper.DeviceMapper;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceCropConfig;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
@@ -53,6 +54,8 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import java.awt.image.RasterFormatException;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -66,7 +69,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.ycwl.basic.constant.StorageConstant.PHOTO_PATH;
import static com.ycwl.basic.constant.StorageConstant.VIID_FACE;
@@ -244,8 +246,14 @@ public class ViidController {
continue;
}
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(device.getId());
if (deviceConfig == null) {
log.warn("设备配置不存在:" + deviceID);
return new VIIDBaseResp(
new ResponseStatusObject(faceId, "/VIID/Faces", "0", "OK", sdfTime.format(new Date()))
);
}
int viidMode = 0;
if (deviceConfig != null && deviceConfig.getViidType() != null) {
if (deviceConfig.getViidType() != null) {
viidMode = deviceConfig.getViidType();
}
Date shotTime = null;
@@ -285,13 +293,15 @@ public class ViidController {
if (viidMode == 0) {
// 遍历每个图片对象
// 先找到type14的图片
List<SubImageInfoObject> type14ImageList = subImageList.getSubImageInfoObject().stream().filter(subImage -> "14".equals(subImage.getType())).collect(Collectors.toList());
List<SubImageInfoObject> type14ImageList = subImageList.getSubImageInfoObject().stream().filter(subImage -> "14".equals(subImage.getType())).toList();
for (SubImageInfoObject subImage : subImageList.getSubImageInfoObject()) {
// base64转换成MultipartFIle
MultipartFile file = ImageUtils.base64ToMultipartFile(subImage.getData());
String ext = subImage.getFileFormat();
if (ext.equalsIgnoreCase("jpeg")) {
String ext;
if (subImage.getFileFormat().equalsIgnoreCase("jpeg")) {
ext = "jpg";
} else {
ext = subImage.getFileFormat();
}
IStorageAdapter adapter = StorageFactory.use("faces");
// Type=11 人脸
@@ -318,7 +328,7 @@ public class ViidController {
faceSampleMapper.updateScore(faceSample.getId(), addFaceResp.getScore());
}
}
if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
if (Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
DynamicTaskGenerator.addTask(faceSample.getId());
}
});
@@ -328,7 +338,6 @@ public class ViidController {
facePosition.setImgHeight(_subImage.getHeight());
facePosition.setImgWidth(_subImage.getWidth());
SourceEntity source = new SourceEntity();
source.setId(SnowFlakeUtil.getLongId());
source.setDeviceId(device.getId());
source.setScenicId(device.getScenicId());
source.setFaceSampleId(newFaceSampleId);
@@ -336,12 +345,43 @@ public class ViidController {
source.setType(2);
// 上传oss
MultipartFile _file = ImageUtils.base64ToMultipartFile(_subImage.getData());
String filename = StorageUtil.joinPath(PHOTO_PATH, UUID.randomUUID() + "." + ext);
String _sourceUrl = scenicStorageAdapter.uploadFile(_file, filename);
scenicStorageAdapter.setAcl(StorageAcl.PUBLIC_READ, filename);
source.setUrl(_sourceUrl);
source.setPosJson(JSON.toJSONString(facePosition));
sourceMapper.add(source);
ThreadPoolExecutor executor = getExecutor(scenicId);
executor.execute(() -> {
List<DeviceCropConfig> cropConfigs = deviceConfig._getCropConfig();
for (DeviceCropConfig cropConfig : cropConfigs) {
source.setId(SnowFlakeUtil.getLongId());
String filename = StorageUtil.joinPath(PHOTO_PATH, UUID.randomUUID() + "." + ext);
MultipartFile _finalFile = _file;
if (cropConfig.getCropType() == 1) {
// 按固定位置截图
try {
_finalFile = ImageUtils.cropImage(_file, cropConfig.getTargetX(), cropConfig.getTargetY(), cropConfig.getTargetWidth(), cropConfig.getTargetHeight());
} catch (IOException e) {
log.error("裁切图片失败!", e);
} catch (RasterFormatException e) {
log.error("裁切图片出错!", e);
}
} else if (cropConfig.getCropType() == 2) {
// 按人脸位置
try {
int targetX = facePosition.getLtX() - (cropConfig.getTargetWidth() - facePosition.getWidth())/2;
int targetY = facePosition.getLtY() - (cropConfig.getTargetHeight() - facePosition.getHeight())/2;
_finalFile = ImageUtils.cropImage(_file, targetX, targetY, cropConfig.getTargetWidth(), cropConfig.getTargetHeight());
} catch (IOException e) {
log.error("裁切图片失败!", e);
} catch (RasterFormatException e) {
log.error("裁切图片出错!", e);
}
facePosition.setImgHeight(cropConfig.getTargetHeight());
facePosition.setImgWidth(cropConfig.getTargetWidth());
}
String _sourceUrl = scenicStorageAdapter.uploadFile(_finalFile, filename);
scenicStorageAdapter.setAcl(StorageAcl.PUBLIC_READ, filename);
source.setUrl(_sourceUrl);
source.setPosJson(JSON.toJSONString(facePosition));
sourceMapper.add(source);
}
});
}
log.info("人脸信息及原图{}张入库成功!设备ID:{}", type14ImageList.size(), deviceID);
}

View File

@@ -278,7 +278,6 @@ public class BceFaceBodyAdapter implements IFaceBodyAdapter {
try {
AipFace client = getClient();
HashMap<String, Object> options = new HashMap<>();
options.put("quality_control", "LOW");
options.put("max_user_num", "50");
try {
searchFaceLimiter.acquire();

View File

@@ -52,8 +52,9 @@ public class LeicaWatermarkOperator implements IOperator {
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_SIZE = 120;
public static int QRCODE_OFFSET_X = 5;
public static int QRCODE_OFFSET_Y = 20;
public static int OFFSET_X = 80;
public static int OFFSET_Y = 30;
public static int SCENIC_FONT_SIZE = 32;
@@ -106,13 +107,30 @@ public class LeicaWatermarkOperator implements IOperator {
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);
// 计算二维码的位置
int qrcodeX = newImage.getWidth() + EXTRA_BORDER_PX - OFFSET_X - newQrcodeWidth - QRCODE_OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth);
int qrcodeY = EXTRA_BORDER_PX + baseImage.getHeight() + OFFSET_Y - QRCODE_OFFSET_Y;
g2d.drawImage(qrcodeImage, qrcodeX, qrcodeY, newQrcodeWidth, newQrcodeHeight, null);
// 计算文字与二维码垂直居中对齐的Y坐标
int qrcodeTop = qrcodeY;
int qrcodeBottom = qrcodeTop + newQrcodeHeight;
int qrcodeCenter = (qrcodeTop + qrcodeBottom) / 2;
// 两行文字的总高度
int totalTextHeight = scenicLineHeight + dtLineHeight;
// 计算第一行文字的Y坐标(基线位置),使两行文字整体垂直居中于二维码
int textStartY = qrcodeCenter - totalTextHeight / 2 + scenicFontMetrics.getAscent();
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.drawString(info.getScenicLine(), newImage.getWidth() + EXTRA_BORDER_PX - OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth), textStartY);
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);
g2d.drawString(info.getDatetimeLine(), newImage.getWidth() + EXTRA_BORDER_PX - OFFSET_X - Math.max(scenicLineWidth, datetimeLineWidth), textStartY + scenicLineHeight);
String fileName = info.getWatermarkedFile().getName();
String formatName = "jpg"; // 默认格式为 jpg
if (fileName.endsWith(".png")) {
@@ -133,7 +151,7 @@ public class LeicaWatermarkOperator implements IOperator {
ImageWriteParam writeParam = writer.getDefaultWriteParam();
if (writeParam.canWriteCompressed()) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionQuality(0.95f); // 设置写入质量为 95%
writeParam.setCompressionQuality(0.75f); // 设置写入质量为 75%
}
writer.write(null, new javax.imageio.IIOImage(newImage, null, null), writeParam);
} catch (IOException e) {

View File

@@ -9,6 +9,7 @@ import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
@@ -45,8 +46,9 @@ public class NormalWatermarkOperator implements IOperator {
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_SIZE = 150;
public static int QRCODE_OFFSET_X = 10;
public static int QRCODE_OFFSET_Y = -20;
public static int SCENIC_FONT_SIZE = 42;
public static Color scenicColor = Color.white;
@@ -82,13 +84,29 @@ public class NormalWatermarkOperator implements IOperator {
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);
Shape originalClip = g2d.getClip();
Ellipse2D circle = new Ellipse2D.Double(offsetX, offsetY + QRCODE_OFFSET_Y, newQrcodeWidth, newQrcodeHeight);
g2d.setClip(circle);
g2d.drawImage(qrcodeImage, offsetX, offsetY + QRCODE_OFFSET_Y, newQrcodeWidth, newQrcodeHeight, null);
g2d.setClip(originalClip);
// 计算文字与二维码垂直居中对齐的Y坐标
int qrcodeTop = offsetY + QRCODE_OFFSET_Y;
int qrcodeBottom = qrcodeTop + newQrcodeHeight;
int qrcodeCenter = (qrcodeTop + qrcodeBottom) / 2;
// 两行文字的总高度
int totalTextHeight = scenicLineHeight + dtLineHeight;
// 计算第一行文字的Y坐标(基线位置),使两行文字整体垂直居中于二维码
int textStartY = qrcodeCenter - totalTextHeight / 2 + scenicFontMetrics.getAscent();
g2d.setFont(scenicFont);
g2d.setColor(scenicColor);
g2d.drawString(info.getScenicLine(), offsetX + newQrcodeWidth + QRCODE_OFFSET_X, offsetY + scenicLineHeight + FONT_GLOBAL_OFFSET_PERCENT * scenicLineHeight);
g2d.drawString(info.getScenicLine(), offsetX + newQrcodeWidth + QRCODE_OFFSET_X, textStartY);
g2d.setFont(datetimeFont);
g2d.setColor(datetimeColor);
g2d.drawString(info.getDatetimeLine(), offsetX + newQrcodeWidth + QRCODE_OFFSET_X, offsetY + scenicLineHeight + dtLineHeight + FONT_GLOBAL_OFFSET_PERCENT * dtLineHeight);
g2d.drawString(info.getDatetimeLine(), offsetX + newQrcodeWidth + QRCODE_OFFSET_X, textStartY + scenicLineHeight);
String fileName = info.getWatermarkedFile().getName();
String formatName = "jpg"; // 默认格式为 jpg
if (fileName.endsWith(".png")) {
@@ -109,7 +127,7 @@ public class NormalWatermarkOperator implements IOperator {
ImageWriteParam writeParam = writer.getDefaultWriteParam();
if (writeParam.canWriteCompressed()) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionQuality(0.95f); // 设置写入质量为 95%
writeParam.setCompressionQuality(0.75f); // 设置写入质量为 75%
}
writer.write(null, new javax.imageio.IIOImage(newImage, null, null), writeParam);
} catch (IOException e) {

View File

@@ -8,7 +8,12 @@ import com.ycwl.basic.constant.RequestConstant;
import com.ycwl.basic.exception.CheckTokenException;
import com.ycwl.basic.exception.MissTokenException;
import com.ycwl.basic.exception.PermissionException;
import com.ycwl.basic.exception.TokenExpireException;
import com.ycwl.basic.mapper.AdminUserMapper;
import com.ycwl.basic.mapper.ScenicAccountMapper;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.pc.adminUser.entity.LoginEntity;
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
import com.ycwl.basic.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -23,14 +28,24 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
import static com.ycwl.basic.constant.JwtRoleConstant.ADMIN;
import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
RedisTemplate redisTemplate;
@Autowired
private ScenicAccountMapper scenicAccountMapper;
@Autowired
private AdminUserMapper adminUserMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
@@ -68,10 +83,22 @@ public class AuthInterceptor implements HandlerInterceptor {
JwtInfo jwtInfo;
try {
jwtInfo = JwtTokenUtil.parsingToken(token);
// LocalDateTime expireTime = jwtInfo.getExpireTime();
// if (LocalDateTime.now(ZoneId.systemDefault()).isAfter(expireTime)) {
// throw new TokenExpireException("token过期");
// }
if (StringUtils.equals(jwtInfo.getRoleId(), MERCHANT.type)) {
Long merchantId = jwtInfo.getUserId();
ScenicAccountEntity account = scenicAccountMapper.findAccountById(merchantId.toString());
LocalDateTime expireTime = jwtInfo.getExpireTime();
if (account.getUpdateTime().toInstant().getEpochSecond() != expireTime.atZone(ZoneId.systemDefault()).toEpochSecond()) {
throw new TokenExpireException("token过期");
}
}
if (StringUtils.equals(jwtInfo.getRoleId(), ADMIN.type)) {
Long adminId = jwtInfo.getUserId();
LoginEntity account = adminUserMapper.getById(adminId);
LocalDateTime expireTime = jwtInfo.getExpireTime();
if (account.getUpdateAt().toInstant().getEpochSecond() != expireTime.atZone(ZoneId.systemDefault()).toEpochSecond()) {
throw new TokenExpireException("token过期");
}
}
BaseContextHandler.setToken(token);
BaseContextHandler.setName(jwtInfo.getName());
BaseContextHandler.setUserId(String.valueOf(jwtInfo.getUserId()));

View File

@@ -25,4 +25,6 @@ public interface AdminUserMapper {
int updatePassword(UpdatePasswordReqVO updatePasswordReqVO);
String getPasswordByAccount(@Param("id")String id);
LoginEntity getById(Long id);
}

View File

@@ -0,0 +1,14 @@
package com.ycwl.basic.mapper;
import com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity;
import com.ycwl.basic.model.aio.entity.AioDeviceEntity;
import com.ycwl.basic.model.aio.entity.AioDevicePriceConfigEntity;
import java.util.List;
public interface AioDeviceMapper {
AioDeviceEntity getById(Long id);
AioDeviceEntity getByKey(String key);
List<AioDeviceBannerEntity> getBannerByDeviceId(Long deviceId);
AioDevicePriceConfigEntity getPriceConfigByDeviceId(Long deviceId);
}

View File

@@ -0,0 +1,11 @@
package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ExtraDeviceMapper {
List<DeviceRespVO> listExtraDeviceByScenicId(Long scenicId);
}

View File

@@ -2,6 +2,7 @@ package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.menu.MenuNode;
import com.ycwl.basic.model.pc.menu.entity.MenuEntity;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -15,18 +16,6 @@ public interface MenuMapper {
* @return
*/
int deleteRoleMenuByRoleId(@Param("id")String roleId);
int addRoleMenu(@Param("id")String roleId,@Param("list") List<Integer> list);
int add(MenuEntity menuEntity);
List<MenuNode>getListByType(@Param("type")Integer type);
int update(MenuEntity menuEntity);
int deleteById(@Param("id")Long id);
/**
* 根据菜单id删除角色菜单
* @param menuId
* @return
*/
int deleteRoleMenuByMenuId(@Param("id")String menuId);
int addRoleMenu(@Param("id")String roleId,@Param("menuStr") String menuStr, @Param("permStr") String permStr);
RolePermissionResp getPermissionByRoleId(Long roleId);
}

View File

@@ -21,4 +21,6 @@ public interface RenderWorkerMapper {
int updateStatus(Long id);
RenderWorkerEntity findByAccessKey(String accessKey);
int updateHost(Long id, RenderWorkerEntity status);
}

View File

@@ -57,6 +57,7 @@ public interface SourceMapper {
int addRelations(List<MemberSourceEntity> list);
int updateRelation(MemberSourceEntity memberSourceEntity);
int freeRelations(List<Long> ids, int type);
List<SourceRespVO> queryByRelation(SourceReqQuery sourceReqQuery);
@@ -68,6 +69,7 @@ public interface SourceMapper {
List<SourceEntity> listVideoByFaceRelation(Long memberId, Long faceId);
List<SourceEntity> listImageByFaceRelation(Long memberId, Long faceId);
List<MemberSourceEntity> listByFaceRelation(Long memberId, Long faceId, Integer type);
SourceEntity getEntity(Long id);

View File

@@ -0,0 +1,35 @@
package com.ycwl.basic.model.aio.entity;
import lombok.Data;
import java.util.Date;
/**
* 智能设备横幅实体类
*/
@Data
public class AioDeviceBannerEntity {
/**
* 主键ID
*/
private Long id;
/**
* 设备ID
*/
private Long deviceId;
/**
* 横幅URL
*/
private String url;
/**
* 状态 (0-禁用 1-启用)
*/
private Integer status;
/**
* 创建时间
*/
private Date createAt;
}

View File

@@ -0,0 +1,45 @@
package com.ycwl.basic.model.aio.entity;
import lombok.Data;
import java.util.Date;
/**
* 智能设备实体类
*/
@Data
public class AioDeviceEntity {
/**
* 主键ID
*/
private Long id;
/**
* 景区ID
*/
private Long scenicId;
/**
* 设备名称
*/
private String name;
/**
* 设备类型
*/
private String type;
/**
* 接入密钥
*/
private String accessKey;
/**
* 状态 (0-离线 1-在线)
*/
private Integer status;
/**
* 创建时间
*/
private Date createAt;
}

View File

@@ -0,0 +1,17 @@
package com.ycwl.basic.model.aio.entity;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class AioDevicePriceConfigEntity {
/**
* 设备ID
*/
private Long id;
private BigDecimal eachPrice;
private Integer status;
private Date createAt;
}

View File

@@ -0,0 +1,18 @@
package com.ycwl.basic.model.aio.req;
import lombok.Data;
import java.util.List;
@Data
public class AioDeviceCreateOrderReq {
private Long faceId;
private String payType;
private List<AioDeviceOrderItem> items;
@Data
public static class AioDeviceOrderItem {
private Long goodsId;
private Integer count;
}
}

View File

@@ -0,0 +1,10 @@
package com.ycwl.basic.model.aio.resp;
import lombok.Data;
@Data
public class AioDeviceCreateOrderResp {
private boolean skipPay;
private Long orderId;
private String payCode;
}

View File

@@ -0,0 +1,15 @@
package com.ycwl.basic.model.aio.resp;
import com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity;
import com.ycwl.basic.model.aio.entity.AioDeviceEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class AioDeviceInfoResp {
private AioDeviceEntity info;
private List<AioDeviceBannerEntity> banners;
}

View File

@@ -19,6 +19,8 @@ import java.util.Date;
public class GoodsDetailVO {
@ApiModelProperty("商品名称")
private String goodsName;
@ApiModelProperty("人脸id")
private Long faceId;
@ApiModelProperty("景区id")
private Long scenicId;
@ApiModelProperty("景区名称")
@@ -41,6 +43,7 @@ public class GoodsDetailVO {
private Integer isBuy;
private Integer isFree;
private Integer parts;
private Integer sort;
public Integer getSourceType() {
return goodsType;
}

View File

@@ -0,0 +1,28 @@
package com.ycwl.basic.model.mobile.scenic.account;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/13 9:44
*/
@Data
@ApiModel("景区账号登录响应类")
public class ScenicLoginOldRespVO {
private Long id;
@ApiModelProperty("景区id")
private Long scenicId;
@ApiModelProperty("是否是超级管理员")
private Integer isSuper;
@ApiModelProperty("账号名称")
private String name;
@ApiModelProperty("账号")
private String account;
@ApiModelProperty("1启用 0禁用")
private Integer status;
private String token;
}

View File

@@ -34,4 +34,6 @@ public class ContentPageVO {
private Integer isBuy;
private BigDecimal duration;
private Integer goodsType;
private int freeCount;
private Integer sort;
}

View File

@@ -2,12 +2,15 @@ package com.ycwl.basic.model.pc.adminUser.entity;
import lombok.Data;
import java.util.Date;
@Data
public class LoginEntity {
private Long staffId;
private String staffName;
private String account;
private String password;
private String roleId;
private Long roleId;
private String typeName;
private Date updateAt;
}

View File

@@ -17,6 +17,5 @@ public class LoginRespVO {
private String name;
@ApiModelProperty(value = "角色名")
private String typeName;
@ApiModelProperty(value = "菜单列表")
private List<MenuNode> menuNodeList;
private boolean superAdmin;
}

View File

@@ -1,11 +1,15 @@
package com.ycwl.basic.model.pc.device.entity;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@Data
@TableName("device_config")
@@ -62,4 +66,18 @@ public class DeviceConfigEntity {
private Integer videoFree;
private Long pairDevice;
private String videoCrop;
private String cropConfig;
public List<DeviceCropConfig> _getCropConfig() {
if (cropConfig == null || cropConfig.isEmpty()) {
return Collections.singletonList(new DeviceCropConfig());
}
if (StringUtils.startsWith(cropConfig, "{")) {
return Collections.singletonList(JSON.parseObject(cropConfig, DeviceCropConfig.class));
}
if (StringUtils.startsWith(cropConfig, "[")) {
return JSON.parseArray(cropConfig, DeviceCropConfig.class);
}
return Collections.singletonList(new DeviceCropConfig());
}
}

View File

@@ -0,0 +1,13 @@
package com.ycwl.basic.model.pc.device.entity;
import lombok.Data;
@Data
public class DeviceCropConfig {
// 截图类型 0:无截图 1:固定截图 2:按人脸位置截图
private int cropType;
private Integer targetWidth;
private Integer targetHeight;
private Integer targetX;
private Integer targetY;
}

View File

@@ -26,5 +26,6 @@ public class OrderItemEntity {
* 商品ID,goods_type=1关联video.id,=2关联source.id
*/
private Long goodsId;
private int count = 1;
}

View File

@@ -13,7 +13,7 @@ public class AddOrUpdateRoleReqVO {
private String name;
// @ApiModelProperty(value = "0系统角色 1业务角色")
// private Integer type;
@ApiModelProperty(value = "菜单ID列表")
private List<Integer> menuIdList;
private List<String> permissions;
private List<String> menus;
}

View File

@@ -0,0 +1,10 @@
package com.ycwl.basic.model.pc.role.resp;
import lombok.Data;
@Data
public class RolePermissionResp {
private String roleId;
private String permStr;
private String menuStr;
}

View File

@@ -93,4 +93,7 @@ public class ScenicConfigEntity {
private String videoSourcePackHint;
private String extraNotificationTime;
private Integer photoFreeNum;
private Integer videoFreeNum;
}

View File

@@ -15,6 +15,7 @@ import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.jsapi.model.SettleInfo;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
@@ -55,6 +56,7 @@ import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_NONCE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SERIAL;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SIGNATURE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_TIMESTAMP;
import static com.wechat.pay.java.service.refund.model.Status.PROCESSING;
import static com.wechat.pay.java.service.refund.model.Status.SUCCESS;
public class WxMpPayAdapter implements IPayAdapter {
@@ -66,6 +68,9 @@ public class WxMpPayAdapter implements IPayAdapter {
public WxMpPayAdapter(WxMpPayConfig config) {
this.config = config;
}
public WxMpPayConfig _config() {
return config;
}
@Override
public void loadConfig(Map<String, String> _config) {
this.config = new WxMpPayConfig();
@@ -77,12 +82,15 @@ public class WxMpPayAdapter implements IPayAdapter {
this.config.setWxPublicKey(_config.get("wxPublicKey"));
this.config.setWxPublicKeyId(_config.get("wxPublicKeyId"));
this.config.setApiV3Key(_config.get("apiV3Key"));
if (_config.containsKey("enableProfitSharing")) {
this.config.setEnableProfitSharing(_config.get("enableProfitSharing").equals("true"));
}
}
}
private Config clientConfig;
private Config getConfig() {
public Config getConfig() {
if (clientConfig == null) {
if (StringUtils.isNotBlank(config.getWxPublicKeyId())) {
clientConfig = new RSAPublicKeyConfig.Builder()
@@ -126,6 +134,9 @@ public class WxMpPayAdapter implements IPayAdapter {
Payer payer = new Payer();
payer.setOpenid(request.getUserIdentify());
prepayRequest.setPayer(payer);
SettleInfo settleInfo = new SettleInfo();
settleInfo.setProfitSharing(config.isEnableProfitSharing());
prepayRequest.setSettleInfo(settleInfo);
PrepayResponse response = service.prepay(prepayRequest);
resp.setSuccess(true);
resp.setSkipPay(false);
@@ -265,7 +276,7 @@ public class WxMpPayAdapter implements IPayAdapter {
createRequest.setAmount(amountReq);
createRequest.setNotifyUrl(request.getNotifyUrl());
Refund refund = service.create(createRequest);
if (refund.getStatus() == SUCCESS) {
if (refund.getStatus() == SUCCESS || refund.getStatus() == PROCESSING) {
resp.setSuccess(true);
resp.setRefundNo(refund.getOutRefundNo());
} else {

View File

@@ -11,4 +11,5 @@ public class WxMpPayConfig {
private String wxPublicKeyId;
private String serialNumber;
private String apiV3Key;
private boolean enableProfitSharing;
}

View File

@@ -8,6 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@@ -52,6 +54,21 @@ public class RenderWorkerRepository {
return;
}
status.setUpdateAt(new Date());
RenderWorkerEntity worker = new RenderWorkerEntity();
worker.setCpuCount(status.getCpu_count());
worker.setCpuUsage(status.getCpu_usage());
// 上报的是字节,存储的是兆
worker.setMemoryAvailable(status.getMemory_available().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setMemoryTotal(status.getMemory_total().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setPlatform(status.getPlatform());
worker.setRuntimeVersion(status.getRuntime_version());
worker.setSupportFeature(String.join(",", status.getSupport_feature()));
worker.setVersion(status.getVersion());
worker.setUpdateAt(status.getUpdateAt());
if (!redisTemplate.hasKey(key)) {
mapper.updateHost(id, worker);
}
redisTemplate.opsForValue().set(key, JSONObject.toJSONString(status), 1, TimeUnit.HOURS);
}

View File

@@ -0,0 +1,9 @@
package com.ycwl.basic.service.aio;
import com.ycwl.basic.model.aio.entity.AioDeviceEntity;
import com.ycwl.basic.model.aio.req.AioDeviceCreateOrderReq;
import com.ycwl.basic.model.aio.resp.AioDeviceCreateOrderResp;
public interface AioDeviceService {
AioDeviceCreateOrderResp createOrder(AioDeviceEntity aioDevice, AioDeviceCreateOrderReq req);
}

View File

@@ -0,0 +1,104 @@
package com.ycwl.basic.service.aio.impl;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import com.ycwl.basic.enums.OrderStateEnum;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.mapper.AioDeviceMapper;
import com.ycwl.basic.mapper.OrderMapper;
import com.ycwl.basic.model.aio.entity.AioDeviceEntity;
import com.ycwl.basic.model.aio.entity.AioDevicePriceConfigEntity;
import com.ycwl.basic.model.aio.req.AioDeviceCreateOrderReq;
import com.ycwl.basic.model.aio.resp.AioDeviceCreateOrderResp;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
import com.ycwl.basic.model.pc.order.entity.OrderItemEntity;
import com.ycwl.basic.pay.adapter.IPayAdapter;
import com.ycwl.basic.pay.adapter.WxMpPayAdapter;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.service.aio.AioDeviceService;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.utils.SnowFlakeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
@Service
public class AioDeviceServiceImpl implements AioDeviceService {
@Autowired
private AioDeviceMapper aioDeviceMapper;
@Autowired
private ScenicService scenicService;
@Autowired
private FaceRepository faceRepository;
@Autowired
private OrderMapper orderMapper;
@Override
public AioDeviceCreateOrderResp createOrder(AioDeviceEntity aioDevice, AioDeviceCreateOrderReq req) {
// 询价
AioDevicePriceConfigEntity config = aioDeviceMapper.getPriceConfigByDeviceId(aioDevice.getId());
if (config == null) {
throw new BaseException("询价失败");
}
if (req.getItems() == null || req.getItems().isEmpty()) {
throw new BaseException("请选择商品");
}
FaceEntity face = faceRepository.getFace(req.getFaceId());
if (face == null) {
throw new BaseException("请选择人脸");
}
AioDeviceCreateOrderResp resp = new AioDeviceCreateOrderResp();
int sum = req.getItems().stream().mapToInt(AioDeviceCreateOrderReq.AioDeviceOrderItem::getCount).sum();
BigDecimal totalPrice = config.getEachPrice().multiply(new BigDecimal(sum));
if (totalPrice.compareTo(BigDecimal.ZERO) <= 0) {
resp.setSkipPay(true);
return resp;
}
OrderEntity order = new OrderEntity();
Long orderId = SnowFlakeUtil.getLongId();
order.setId(orderId);
order.setMemberId(face.getMemberId());
order.setFaceId(face.getId());
order.setScenicId(face.getScenicId());
order.setType(0);
List<OrderItemEntity> orderItems = req.getItems().stream().map(item -> {
OrderItemEntity orderItem = new OrderItemEntity();
orderItem.setOrderId(orderId);
orderItem.setGoodsId(item.getGoodsId());
orderItem.setGoodsType(4);
if (item.getCount() == null) {
item.setCount(1);
}
orderItem.setCount(item.getCount());
return orderItem;
}).toList();
order.setSlashPrice(totalPrice);
order.setPrice(totalPrice);
order.setPayPrice(totalPrice);
order.setStatus(OrderStateEnum.UNPAID.getState());
orderMapper.add(order);
int addOrderItems = orderMapper.addOrderItems(orderItems);
// 支付
IPayAdapter scenicPayAdapter = scenicService.getScenicPayAdapter(aioDevice.getScenicId());
if (scenicPayAdapter instanceof WxMpPayAdapter adapter) {
NativePayService service = new NativePayService.Builder().config(adapter.getConfig()).build();
PrepayRequest request = new PrepayRequest();
request.setAppid(adapter._config().getAppId());
request.setMchid(adapter._config().getMerchantId());
request.setDescription("打印订单");
request.setOutTradeNo(SnowFlakeUtil.getId());
request.setNotifyUrl("https://zhentuai.com/api/mobile/wx/pay/v1/"+aioDevice.getScenicId()+"/payNotify");
Amount amount = new Amount();
amount.setTotal(totalPrice.multiply(new BigDecimal(100)).intValue());
request.setAmount(amount);
PrepayResponse response = service.prepay(request);
resp.setPayCode(response.getCodeUrl());
}
return resp;
}
}

View File

@@ -29,6 +29,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.ycwl.basic.constant.JwtRoleConstant.APP_USER;
/**
* @Author:songmingsong
*/
@@ -116,6 +118,7 @@ public class AppMemberServiceImpl implements AppMemberService {
jwtInfo.setUserId(memberRespVO.getId());
jwtInfo.setName(memberRespVO.getNickname());
jwtInfo.setPhone(memberRespVO.getPhone());
jwtInfo.setRoleId(APP_USER.type);
String jwt = jwtTokenUtil.generateToken(jwtInfo);
Map<String, Object> resMap = new HashMap<>();

View File

@@ -11,19 +11,24 @@ import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO;
import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO;
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginReq;
import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity;
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.service.mobile.AppScenicService;
import com.ycwl.basic.service.pc.ScenicAccountService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
@@ -46,6 +51,12 @@ public class AppScenicServiceImpl implements AppScenicService {
private JwtTokenUtil jwtTokenUtil;
@Autowired
private ScenicAccountService scenicAccountService;
@Autowired
private DeviceRepository deviceRepository;
@Autowired
private ExtraDeviceMapper extraDeviceMapper;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public ApiResponse<PageInfo<ScenicAppVO>> pageQuery(ScenicReqQuery scenicReqQuery) {
@@ -88,7 +99,7 @@ public class AppScenicServiceImpl implements AppScenicService {
jwtInfo.setAccount(scenicAccount.getAccount());
jwtInfo.setUserId(scenicAccount.getId());
jwtInfo.setRoleId(MERCHANT.type);
String token = jwtTokenUtil.generateToken(jwtInfo);
String token = jwtTokenUtil.generateToken(jwtInfo, scenicAccount.getUpdateTime());
ScenicLoginRespVO scenicLoginRespVO = new ScenicLoginRespVO();
BeanUtil.copyProperties(scenicAccount,scenicLoginRespVO);
@@ -105,6 +116,40 @@ public class AppScenicServiceImpl implements AppScenicService {
@Override
public ApiResponse<List<DeviceRespVO>> getDevices(Long scenicId) {
List<DeviceRespVO> deviceRespVOList = deviceMapper.listByScenicIdWithWVP(scenicId);
for (DeviceRespVO deviceRespVO : deviceRespVOList) {
DeviceEntity onlineStatus = deviceRepository.getOnlineStatus(deviceRespVO.getId());
if (onlineStatus != null) {
deviceRespVO.setUpdateAt(onlineStatus.getKeepaliveAt());
deviceRespVO.setKeepaliveAt(onlineStatus.getKeepaliveAt());
if (new Date().getTime() - onlineStatus.getKeepaliveAt().getTime() > 300000) {
deviceRespVO.setOnline(0);
} else {
deviceRespVO.setOnline(onlineStatus.getOnline());
}
} else {
deviceRespVO.setOnline(0);
deviceRespVO.setKeepaliveAt(null);
deviceRespVO.setUpdateAt(null);
}
}
List<DeviceRespVO> extraDeviceList = extraDeviceMapper.listExtraDeviceByScenicId(scenicId);
for (DeviceRespVO deviceRespVO : extraDeviceList) {
if (redisTemplate.hasKey("ext_device:online:"+deviceRespVO.getNo())) {
String onlineTs = redisTemplate.opsForValue().get("ext_device:online:"+deviceRespVO.getNo());
if (!StringUtils.isNumeric(onlineTs)) {
deviceRespVO.setOnline(0);
continue;
}
Long ts = Long.parseLong(onlineTs);
Date keepaliveAt = new Date(ts*1000);
deviceRespVO.setUpdateAt(keepaliveAt);
deviceRespVO.setKeepaliveAt(keepaliveAt);
deviceRespVO.setOnline(keepaliveAt.getTime() > System.currentTimeMillis()-1000*60*5 ? 1 : 0);
} else {
deviceRespVO.setOnline(0);
}
}
deviceRespVOList.addAll(0, extraDeviceList);
return ApiResponse.success(deviceRespVOList);
}
}

View File

@@ -370,14 +370,14 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
//当前周期的推送订阅人数
vo.setNowPushOfPeopleNum(data.getPushOfMemberNum());
//当前周期的预览视频人数
vo.setNowPreviewVideoOfPeopleNum(data.getPreviewOfVideoNum());
vo.setNowPreviewVideoOfPeopleNum(data.getPreviewVideoOfMemberNum());
}else if(cycle==2){
//上一个周期的扫码访问人数
vo.setPreviousScanCodeOfPeopleNum(data.getScanCodeVisitorOfMemberNum());
//上一个周期的推送订阅人数
vo.setPreviousPushOfPeopleNum(data.getPushOfMemberNum());
//上一个周期的预览视频人数
vo.setPreviousPreviewVideoOfPeopleNum(data.getPreviewOfVideoNum());
vo.setPreviousPreviewVideoOfPeopleNum(data.getPreviewVideoOfMemberNum());
}
}

View File

@@ -75,8 +75,6 @@ public class GoodsServiceImpl implements GoodsService {
@Autowired
private SourceMapper sourceMapper;
@Autowired
private ScenicMapper scenicMapper;
@Autowired
private FaceMapper faceMapper;
@Autowired
private TemplateRepository templateRepository;
@@ -87,8 +85,6 @@ public class GoodsServiceImpl implements GoodsService {
@Autowired
private ScenicRepository scenicRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private OrderBiz orderBiz;
@Autowired
private FaceRepository faceRepository;
@@ -98,10 +94,18 @@ public class GoodsServiceImpl implements GoodsService {
private DeviceRepository deviceRepository;
public ApiResponse<List<GoodsPageVO>> goodsList(GoodsReqQuery query) {
Long scenicId = query.getScenicId();
if (query.getFaceId() != null) {
FaceEntity face = faceRepository.getFace(query.getFaceId());
if (face == null) {
return ApiResponse.success(Collections.emptyList());
}
scenicId = face.getScenicId();
}
//查询原素材
List<GoodsPageVO> goodsList = new ArrayList<>();
VideoReqQuery videoReqQuery = new VideoReqQuery();
videoReqQuery.setScenicId(query.getScenicId());
videoReqQuery.setScenicId(scenicId);
videoReqQuery.setIsBuy(query.getIsBuy());
videoReqQuery.setFaceId(query.getFaceId());
videoReqQuery.setMemberId(Long.valueOf(BaseContextHandler.getUserId()));
@@ -121,25 +125,21 @@ public class GoodsServiceImpl implements GoodsService {
});
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(query.getScenicId());
sourceReqQuery.setScenicId(scenicId);
sourceReqQuery.setIsBuy(query.getIsBuy());
sourceReqQuery.setFaceId(query.getFaceId());
sourceReqQuery.setMemberId(Long.valueOf(BaseContextHandler.getUserId()));
//查询源素材
List<SourceRespVO> sourceList = sourceMapper.queryByRelation(sourceReqQuery);
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(query.getScenicId());
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
List<GoodsPageVO> sourceGoods = sourceList.stream().collect(Collectors.groupingBy(SourceRespVO::getFaceId)).entrySet().stream().flatMap((faceEntry) -> {
Long faceId = faceEntry.getKey();
List<SourceRespVO> goods = faceEntry.getValue();
return goods.stream().collect(Collectors.groupingBy(SourceRespVO::getType)).keySet().stream().filter(type -> {
if (Integer.valueOf(1).equals(type)) {
if (Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
return false;
}
return !Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo());
} else if (Integer.valueOf(2).equals(type)) {
if (Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage())) {
return false;
}
return !Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage());
}
return true;
}).map(type -> {
@@ -156,7 +156,7 @@ public class GoodsServiceImpl implements GoodsService {
goodsPageVO.setScenicId(query.getScenicId());
return goodsPageVO;
});
}).collect(Collectors.toList());
}).toList();
if (!sourceGoods.isEmpty()) {
if (goodsList.size() > 2) {
goodsList.addAll(2, sourceGoods);
@@ -175,11 +175,11 @@ public class GoodsServiceImpl implements GoodsService {
}
Integer sourceType = query.getSourceType();
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(query.getScenicId());
sourceReqQuery.setScenicId(face.getScenicId());
sourceReqQuery.setIsBuy(query.getIsBuy());
sourceReqQuery.setMemberId(face.getMemberId());
sourceReqQuery.setType(sourceType);
sourceReqQuery.setFaceId(query.getFaceId());
sourceReqQuery.setFaceId(face.getId());
List<SourceRespVO> list = sourceMapper.listUser(sourceReqQuery);
List<GoodsDetailVO> goodsDetailVOList = new ArrayList<>();
@@ -195,6 +195,7 @@ public class GoodsServiceImpl implements GoodsService {
int i=1;
for (SourceRespVO sourceRespVO : list) {
GoodsDetailVO goodsDetailVO = new GoodsDetailVO();
goodsDetailVO.setFaceId(sourceRespVO.getFaceId());
goodsDetailVO.setGoodsId(sourceRespVO.getId());
String shootingTime = DateUtil.format(sourceRespVO.getCreateTime(), "yyyy.MM.dd HH:mm:ss");
if (i < 10) {
@@ -205,6 +206,7 @@ public class GoodsServiceImpl implements GoodsService {
goodsDetailVO.setScenicId(sourceRespVO.getScenicId());
goodsDetailVO.setScenicName(sourceRespVO.getScenicName());
goodsDetailVO.setGoodsType(sourceType);
goodsDetailVO.setFaceId(face.getId());
goodsDetailVO.setGoodsId(sourceRespVO.getId());
goodsDetailVO.setIsFree(sourceRespVO.getIsFree());
goodsDetailVO.setIsBuy(sourceRespVO.getIsBuy());
@@ -277,6 +279,9 @@ public class GoodsServiceImpl implements GoodsService {
}
}
TaskEntity task = videoTaskRepository.getTaskById(videoRespVO.getTaskId());
if (task == null) {
return ApiResponse.fail("该vlog不存在或已失效");
}
JSONObject paramJson = JSON.parseObject(task.getTaskParams());
AtomicInteger deviceCount = new AtomicInteger();
goodsDetailVO.setShotTime(taskTaskService.getTaskShotDate(task.getId()));
@@ -511,11 +516,11 @@ public class GoodsServiceImpl implements GoodsService {
}
Integer sourceType = query.getSourceType();
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(query.getScenicId());
sourceReqQuery.setScenicId(face.getScenicId());
sourceReqQuery.setIsBuy(query.getIsBuy());
sourceReqQuery.setMemberId(face.getMemberId());
sourceReqQuery.setType(sourceType);
sourceReqQuery.setFaceId(query.getFaceId());
sourceReqQuery.setFaceId(face.getId());
List<SourceRespVO> list = sourceMapper.listUser(sourceReqQuery);
if (!Integer.valueOf(2).equals(query.getSourceType())) {
return list.stream().map(source -> {
@@ -535,7 +540,7 @@ public class GoodsServiceImpl implements GoodsService {
goodsUrlVO.setCreateTime(source.getCreateTime());
return goodsUrlVO;
}).collect(Collectors.toList());
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), query.getScenicId(), query.getSourceType(), query.getFaceId());
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), query.getSourceType(), face.getId());
if (!isBuy.isBuy()) {
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId());
if (scenicConfig != null && ((scenicConfig.getAntiScreenRecordType() & 2) == 0)) {
@@ -607,7 +612,7 @@ public class GoodsServiceImpl implements GoodsService {
sourceReqQuery.setFaceId(query.getFaceId());
List<SourceRespVO> list = sourceMapper.listUser(sourceReqQuery);
if (query.getGoodsId() != null) {
list = list.stream().filter(source -> source.getId().equals(query.getGoodsId())).collect(Collectors.toList());
list = list.stream().filter(source -> source.getId().equals(query.getGoodsId())).toList();
}
if (!Integer.valueOf(2).equals(query.getSourceType())) {
return list.stream().map(source -> {
@@ -628,8 +633,8 @@ public class GoodsServiceImpl implements GoodsService {
}
return true;
}).count();
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), query.getSourceType(), face.getId());
if (count > 0) {
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), query.getScenicId(), query.getSourceType(), query.getFaceId());
if (!isBuy.isBuy()) {
return Collections.emptyList();
}
@@ -648,7 +653,7 @@ public class GoodsServiceImpl implements GoodsService {
log.warn("未配置小程序参数,无法生成二维码");
return defaultUrlList;
}
if (scenicConfig != null && scenicConfig.getWatermarkType() != null) {
if (scenicConfig != null && scenicConfig.getWatermarkType() != null && !isBuy.isBuy()) {
ImageWatermarkOperatorEnum type = ImageWatermarkOperatorEnum.getByCode(scenicConfig.getWatermarkType());
if (type != null) {
IStorageAdapter adapter;
@@ -662,8 +667,7 @@ public class GoodsServiceImpl implements GoodsService {
List<SourceWatermarkEntity> watermarkEntityList = sourceMapper.listSourceWatermark(list.stream().map(SourceRespVO::getId).collect(Collectors.toList()), face.getId(), type.getType());
File qrcode = new File("qrcode_"+face.getId()+".jpg");
try {
String urlLink = WxMpUtil.generateUrlLink(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/index", "scenicId=" + face.getScenicId() + "&faceId=" + face.getId());
QrCodeUtil.generate(urlLink + "?cq=", 300, 300, qrcode);
WxMpUtil.generateUnlimitedWXAQRCode(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/from_face", face.getId().toString(), qrcode);
} catch (Exception e) {
log.error("generateWXQRCode error", e);
return defaultUrlList;
@@ -733,7 +737,7 @@ public class GoodsServiceImpl implements GoodsService {
}
Integer sourceType = query.getSourceType();
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(query.getScenicId());
sourceReqQuery.setScenicId(face.getScenicId());
sourceReqQuery.setIsBuy(query.getIsBuy());
sourceReqQuery.setMemberId(face.getMemberId());
sourceReqQuery.setType(sourceType);

View File

@@ -36,4 +36,6 @@ public interface FaceService {
List<ContentPageVO> faceContentList(Long faceId);
ApiResponse<List<ContentPageVO>> contentListUseDefaultFace();
void bindFace(Long faceId, Long userId);
}

View File

@@ -1,18 +0,0 @@
package com.ycwl.basic.service.pc;
import com.ycwl.basic.model.pc.menu.MenuNode;
import com.ycwl.basic.model.pc.menu.entity.MenuEntity;
import com.ycwl.basic.utils.ApiResponse;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/3 10:40
*/
public interface MenuService {
ApiResponse<List<MenuNode>> list(Integer type);
ApiResponse<Integer> add(MenuEntity menu);
ApiResponse<Integer> update(MenuEntity menu);
ApiResponse<Integer> deleteById(Long id);
}

View File

@@ -4,11 +4,14 @@ import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.role.req.AddOrUpdateRoleReqVO;
import com.ycwl.basic.model.pc.role.req.RoleListReqVO;
import com.ycwl.basic.model.pc.role.resp.RoleListRespVO;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import com.ycwl.basic.utils.ApiResponse;
import java.util.List;
public interface RoleService {
RolePermissionResp getPermissionByRoleId(Long roleId);
ApiResponse<PageInfo<RoleListRespVO>> pageQuery(RoleListReqVO roleListReqVO);
ApiResponse list(RoleListReqVO roleListReqVO);
ApiResponse addOrUpdate(AddOrUpdateRoleReqVO addOrUpdateRoleReqVO);

View File

@@ -21,4 +21,5 @@ public interface SourceService {
ApiResponse<Integer> update(SourceEntity source);
ApiResponse cutVideo(Long id);
ApiResponse<String> uploadAndUpdateUrl(Long id, org.springframework.web.multipart.MultipartFile file);
}

View File

@@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import static com.ycwl.basic.constant.JwtRoleConstant.ADMIN;
import static com.ycwl.basic.constant.PermissionConstant.ROLE_STATUS;
@@ -56,12 +57,17 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override
public ApiResponse addOrUpdate(AddOrUpdateAdminUserReqVO addOrUpdateAdminUserReqVO) {
addOrUpdateAdminUserReqVO.setPassword(DigestUtil.md5Hex(addOrUpdateAdminUserReqVO.getPassword()));
if (StringUtils.isNotBlank(addOrUpdateAdminUserReqVO.getPassword())) {
addOrUpdateAdminUserReqVO.setPassword(DigestUtil.md5Hex(addOrUpdateAdminUserReqVO.getPassword()));
} else {
addOrUpdateAdminUserReqVO.setPassword(null);
}
String id = addOrUpdateAdminUserReqVO.getId();
if (StringUtils.isBlank(id)) {
addOrUpdateAdminUserReqVO.setId(SnowFlakeUtil.getId());
// String password = addOrUpdateAdminUserReqVO.getPassword();
// addOrUpdateAdminUserReqVO.setPassword(password);
if (StringUtils.isBlank(addOrUpdateAdminUserReqVO.getPassword())) {
return ApiResponse.fail("密码不能为空");
}
int add = adminUserMapper.add(addOrUpdateAdminUserReqVO);
if (add > 0) {
return ApiResponse.success(null);
@@ -105,7 +111,7 @@ public class AdminUserServiceImpl implements AdminUserService {
if (!login.getPassword().equals(DigestUtil.md5Hex(password))) {
return ApiResponse.fail("账号不存在或密码错误");
}
String roleId = login.getRoleId();
Long roleId = login.getRoleId();
Object roleObject = redisTemplate.opsForValue().get(ROLE_STATUS + roleId);
@@ -114,27 +120,12 @@ public class AdminUserServiceImpl implements AdminUserService {
throw new RoleStatusException("该角色下的账号已被封禁,请联系管理员");
}
}
List<MenuNode> menuById = roleMapper.getMenuById(roleId);
List<MenuNode> MenuList = new ArrayList<>();
for (MenuNode item : menuById) {
if ("-1".equals(item.getParentId())) {
MenuList.add(item);
}
for (MenuNode item2 : menuById) {
if (item2.getParentId().equals(item.getId())) {
if (item.getChildrenList() == null) {
item.setChildrenList(new ArrayList<>());
}
item.getChildrenList().add(item2);
}
}
}
LoginRespVO loginRespVO = new LoginRespVO();
String token = jwtTokenUtil.generateToken(new JwtInfo(login.getStaffName(), login.getStaffId(), roleId, login.getAccount(), login.getAccount(), null));
String token = jwtTokenUtil.generateToken(new JwtInfo(login.getStaffName(), login.getStaffId(), ADMIN.type, login.getAccount(), login.getAccount(), null), login.getUpdateAt());
loginRespVO.setToken(token);
loginRespVO.setName(login.getStaffName());
loginRespVO.setTypeName(login.getTypeName());
loginRespVO.setMenuNodeList(MenuList);
loginRespVO.setSuperAdmin(Long.valueOf(1L).equals(login.getStaffId()));
return ApiResponse.success(loginRespVO);
}

View File

@@ -8,10 +8,7 @@ import com.ycwl.basic.biz.TemplateBiz;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.enums.StatisticEnum;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.facebody.FaceBodyFactory;
import com.ycwl.basic.facebody.adapter.AliFaceBodyAdapter;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.mapper.StatisticsMapper;
import com.ycwl.basic.mapper.FaceMapper;
@@ -21,6 +18,7 @@ import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
import com.ycwl.basic.model.mobile.statistic.req.StatisticsRecordAddReq;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.face.req.FaceReqQuery;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
@@ -34,6 +32,7 @@ import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.video.entity.MemberVideoEntity;
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.VideoRepository;
@@ -45,6 +44,7 @@ import com.ycwl.basic.service.task.TaskService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.utils.StorageUtil;
import com.ycwl.basic.task.VideoPieceGetter;
import com.ycwl.basic.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -52,11 +52,12 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -99,6 +100,8 @@ public class FaceServiceImpl implements FaceService {
private VideoTaskRepository videoTaskRepository;
@Autowired
private TemplateBiz templateBiz;
@Autowired
private DeviceRepository deviceRepository;
@Override
public ApiResponse<PageInfo<FaceRespVO>> pageQuery(FaceReqQuery faceReqQuery) {
@@ -280,30 +283,72 @@ public class FaceServiceImpl implements FaceService {
if (sampleListIds != null && !sampleListIds.isEmpty()) {// 匹配原片:照片
List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(sampleListIds);
List<MemberSourceEntity> memberSourceEntityList = sourceEntities.stream().map(sourceEntity -> {
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(sourceEntity.getDeviceId());
MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
memberSourceEntity.setScenicId(face.getScenicId());
memberSourceEntity.setFaceId(face.getId());
memberSourceEntity.setMemberId(face.getMemberId());
memberSourceEntity.setSourceId(sourceEntity.getId());
memberSourceEntity.setType(sourceEntity.getType());
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), sourceEntity.getType(), faceEntity.getId());
if (isBuy.isBuy()) { // 如果用户买过
memberSourceEntity.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
memberSourceEntity.setIsBuy(1);
} else {
memberSourceEntity.setIsBuy(0);
memberSourceEntity.setIsFree(0);
if (deviceConfig != null) {
if (sourceEntity.getType() == 1) {
if (Integer.valueOf(1).equals(deviceConfig.getVideoFree())) {
memberSourceEntity.setIsFree(1);
}
} else if (sourceEntity.getType() == 2) {
if (Integer.valueOf(1).equals(deviceConfig.getImageFree())) {
memberSourceEntity.setIsFree(1);
}
}
}
return memberSourceEntity;
}).collect(Collectors.toList());
List<Long> freeSourceIds = new ArrayList<>();
List<MemberSourceEntity> photoSource = memberSourceEntityList.stream().filter(item -> item.getIsFree() == 0).filter(item -> Integer.valueOf(2).equals(item.getType())).toList();
if (isNew) {
// 送照片逻辑
if (scenicConfig != null && scenicConfig.getPhotoFreeNum() != null && scenicConfig.getPhotoFreeNum() > 0) {
if (scenicConfig.getPhotoFreeNum() > photoSource.size()) {
freeSourceIds.addAll(photoSource.stream().map(MemberSourceEntity::getSourceId).toList());
} else {
freeSourceIds.addAll(photoSource.stream().limit(scenicConfig.getPhotoFreeNum()).map(MemberSourceEntity::getSourceId).toList());
}
}
} else {
// 重新切视频逻辑
if (scenicConfig != null && !Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
long videoCount = memberSourceEntityList.stream().filter(item -> item.getType().equals(1)).count();
long photoCount = memberSourceEntityList.stream().filter(item -> item.getType().equals(2)).count();
if (photoCount > videoCount) {
VideoPieceGetter.Task task = new VideoPieceGetter.Task();
task.faceId = faceId;
task.faceSampleIds = sampleListIds;
task.templateId = null;
task.memberId = face.getMemberId();
task.callback = () -> {
log.info("task callback: {}", task);
};
VideoPieceGetter.addTask(task);
}
}
}
if (!memberSourceEntityList.isEmpty()) {
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), memberSourceEntityList.getFirst().getType(), faceEntity.getId());
for (MemberSourceEntity memberSourceEntity : memberSourceEntityList) {
if (isBuy.isBuy()) { // 如果用户买过
memberSourceEntity.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
memberSourceEntity.setIsBuy(1);
} else {
memberSourceEntity.setIsBuy(0);
}
if (freeSourceIds.contains(memberSourceEntity.getSourceId())) {
memberSourceEntity.setIsFree(1);
}
}
sourceMapper.addRelations(memberSourceEntityList);
taskTaskService.autoCreateTaskByFaceId(face.getId());
// VideoPieceGetter.Task task = new VideoPieceGetter.Task();
// task.faceId = face.getId();
// task.faceSampleIds = sampleListIds;
// task.memberId = face.getMemberId();
// VideoPieceGetter.addTask(task);
}
}
return scenicDbSearchResult;
@@ -336,6 +381,7 @@ public class FaceServiceImpl implements FaceService {
List<MemberVideoEntity> memberVideoEntityList = videoMapper.userFaceTemplateVideo(userId, faceId, contentPageVO.getTemplateId());
contentPageVO.setGoodsType(0);
contentPageVO.setContentType(1);
contentPageVO.setSort(contentPageVO.getSort());
if (!memberVideoEntityList.isEmpty()) {
contentPageVO.setIsBuy(memberVideoEntityList.getFirst().getIsBuy());
contentPageVO.setContentId(memberVideoEntityList.getFirst().getVideoId());
@@ -375,6 +421,8 @@ public class FaceServiceImpl implements FaceService {
ContentPageVO sourceImageContent = new ContentPageVO();
sourceVideoContent.setName("录像集");
sourceImageContent.setName("照片集");
sourceVideoContent.setSort(9999);
sourceImageContent.setSort(9999);
sourceVideoContent.setScenicId(faceRespVO.getScenicId());
sourceImageContent.setScenicId(faceRespVO.getScenicId());
sourceVideoContent.setGoodsType(1);
@@ -393,12 +441,14 @@ public class FaceServiceImpl implements FaceService {
} else {
sourceImageContent.setIsBuy(0);
}
List<SourceEntity> sourceEntities = sourceMapper.listImageByFaceRelation(faceRespVO.getMemberId(), faceId);
if (!sourceEntities.isEmpty()) {
List<MemberSourceEntity> relations = sourceMapper.listByFaceRelation(faceRespVO.getMemberId(), faceId, 2);
if (!relations.isEmpty()) {
sourceImageContent.setLockType(-1);
} else {
sourceImageContent.setLockType(1);
}
long freeCount = relations.stream().filter(entity -> Integer.valueOf(1).equals(entity.getIsFree())).count();
sourceImageContent.setFreeCount((int) freeCount);
contentList.add(sourceImageContent);
}
if (!Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
@@ -410,12 +460,14 @@ public class FaceServiceImpl implements FaceService {
} else {
sourceVideoContent.setIsBuy(0);
}
List<SourceEntity> sourceEntities = sourceMapper.listVideoByFaceRelation(faceRespVO.getMemberId(), faceId);
if (!sourceEntities.isEmpty()) {
List<MemberSourceEntity> relations = sourceMapper.listByFaceRelation(faceRespVO.getMemberId(), faceId, 1);
if (!relations.isEmpty()) {
sourceVideoContent.setLockType(-1);
} else {
sourceVideoContent.setLockType(1);
}
long freeCount = relations.stream().filter(entity -> Integer.valueOf(1).equals(entity.getIsFree())).count();
sourceVideoContent.setFreeCount((int) freeCount);
contentList.add(sourceVideoContent);
}
sourceList.stream().collect(Collectors.groupingBy(SourceRespVO::getType)).forEach((type, list) -> {
@@ -440,5 +492,10 @@ public class FaceServiceImpl implements FaceService {
return ApiResponse.success(contentPageVOS);
}
@Override
public void bindFace(Long faceId, Long userId) {
}
}

View File

@@ -1,78 +0,0 @@
package com.ycwl.basic.service.pc.impl;
import com.ycwl.basic.mapper.MenuMapper;
import com.ycwl.basic.model.pc.menu.MenuNode;
import com.ycwl.basic.model.pc.menu.entity.MenuEntity;
import com.ycwl.basic.service.pc.MenuService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.SnowFlakeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/3 10:16
*/
@Service
public class MenuServiceImpl implements MenuService {
@Autowired
private MenuMapper menuMapper;
@Override
public ApiResponse<List<MenuNode>> list(Integer type) {
// if(type==null){
// type=1;
// }
List<MenuNode> listByType = menuMapper.getListByType(type);
List<MenuNode> MenuList = new ArrayList<>();
for (MenuNode item :listByType) {
if ("-1".equals(item.getParentId())) {
MenuList.add(item);
}
for (MenuNode item2 : listByType) {
if (item2.getParentId().equals(item.getId())) {
if (item.getChildrenList() == null) {
item.setChildrenList(new ArrayList<>());
}
item.getChildrenList().add(item2);
}
}
}
return ApiResponse.success(MenuList);
}
@Override
public ApiResponse<Integer> add(MenuEntity menu) {
menu.setId(SnowFlakeUtil.getId());
int add = menuMapper.add(menu);
if(add>0){
return ApiResponse.success(add);
}else {
return ApiResponse.fail("添加失败");
}
}
@Override
public ApiResponse<Integer> update(MenuEntity menu) {
int update = menuMapper.update(menu);
if(update>0){
return ApiResponse.success(update);
}else {
return ApiResponse.fail("更新失败");
}
}
@Override
public ApiResponse<Integer> deleteById(Long id) {
int deleteById = menuMapper.deleteById(id);
if(deleteById>0){
return ApiResponse.success(deleteById);
}else {
return ApiResponse.fail("删除失败");
}
}
}

View File

@@ -8,6 +8,7 @@ import com.ycwl.basic.model.pc.menu.MenuNode;
import com.ycwl.basic.model.pc.role.req.AddOrUpdateRoleReqVO;
import com.ycwl.basic.model.pc.role.req.RoleListReqVO;
import com.ycwl.basic.model.pc.role.resp.RoleListRespVO;
import com.ycwl.basic.model.pc.role.resp.RolePermissionResp;
import com.ycwl.basic.service.pc.RoleService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.SnowFlakeUtil;
@@ -34,6 +35,11 @@ public class RoleServiceImpl implements RoleService {
@Autowired
RedisTemplate redisTemplate;
@Override
public RolePermissionResp getPermissionByRoleId(Long roleId) {
return menuMapper.getPermissionByRoleId(roleId);
}
@Override
public ApiResponse<PageInfo<RoleListRespVO>> pageQuery(RoleListReqVO roleListReqVO) {
PageHelper.startPage(roleListReqVO.getPageNum(),roleListReqVO.getPageSize());
@@ -55,23 +61,22 @@ public class RoleServiceImpl implements RoleService {
}
@Override
public ApiResponse addOrUpdate(AddOrUpdateRoleReqVO addOrUpdateRoleReqVO) {
String id = addOrUpdateRoleReqVO.getId();
public ApiResponse addOrUpdate(AddOrUpdateRoleReqVO vo) {
String id = vo.getId();
if(StringUtils.isBlank(id)){
String roleId = SnowFlakeUtil.getId();
addOrUpdateRoleReqVO.setId(roleId);
if(roleMapper.add(addOrUpdateRoleReqVO)>0){
if (addOrUpdateRoleReqVO.getMenuIdList() != null && !addOrUpdateRoleReqVO.getMenuIdList().isEmpty()) {
menuMapper.addRoleMenu(roleId, addOrUpdateRoleReqVO.getMenuIdList());
}
vo.setId(roleId);
if(roleMapper.add(vo)>0){
String menuStr = StringUtils.join(vo.getMenus(), ",");
String permStr = StringUtils.join(vo.getPermissions(), ",");
menuMapper.addRoleMenu(roleId, menuStr, permStr);
return ApiResponse.success(null);
}
}else {
if(roleMapper.update(addOrUpdateRoleReqVO)>0){
menuMapper.deleteRoleMenuByRoleId(addOrUpdateRoleReqVO.getId());
if (addOrUpdateRoleReqVO.getMenuIdList() != null && !addOrUpdateRoleReqVO.getMenuIdList().isEmpty()) {
menuMapper.addRoleMenu(addOrUpdateRoleReqVO.getId(), addOrUpdateRoleReqVO.getMenuIdList());
}
if(roleMapper.update(vo)>0){
String menuStr = StringUtils.join(vo.getMenus(), ",");
String permStr = StringUtils.join(vo.getPermissions(), ",");
menuMapper.addRoleMenu(id, menuStr, permStr);
return ApiResponse.success(null);
}
}

View File

@@ -145,6 +145,11 @@ public class ScenicServiceImpl implements ScenicService {
int i = scenicMapper.update(scenicUpdateReq);
if (i > 0) {
scenicRepository.clearCache(scenicUpdateReq.getId());
scenicFaceBodyAdapterMap.remove(scenicUpdateReq.getId());
scenicStorageAdapterMap.remove(scenicUpdateReq.getId());
scenicTmpStorageAdapterMap.remove(scenicUpdateReq.getId());
scenicLocalStorageAdapterMap.remove(scenicUpdateReq.getId());
scenicPayAdapterMap.remove(scenicUpdateReq.getId());
return ApiResponse.success(true);
}else {
return ApiResponse.fail("景区修改失败");
@@ -157,6 +162,11 @@ public class ScenicServiceImpl implements ScenicService {
int i = scenicMapper.updateStatus(id);
if (i > 0) {
scenicRepository.clearCache(id);
scenicFaceBodyAdapterMap.remove(id);
scenicStorageAdapterMap.remove(id);
scenicTmpStorageAdapterMap.remove(id);
scenicLocalStorageAdapterMap.remove(id);
scenicPayAdapterMap.remove(id);
return ApiResponse.success(true);
}else {
return ApiResponse.fail("景区状态修改失败");
@@ -182,6 +192,11 @@ public class ScenicServiceImpl implements ScenicService {
int i = scenicMapper.updateConfigById(scenicConfig);
if (i > 0) {
scenicRepository.clearCache(scenicConfig.getScenicId());
scenicFaceBodyAdapterMap.remove(scenicConfig.getScenicId());
scenicStorageAdapterMap.remove(scenicConfig.getScenicId());
scenicTmpStorageAdapterMap.remove(scenicConfig.getScenicId());
scenicLocalStorageAdapterMap.remove(scenicConfig.getScenicId());
scenicPayAdapterMap.remove(scenicConfig.getScenicId());
return ApiResponse.success(true);
}else {
return ApiResponse.fail("景区配置修改失败");

View File

@@ -7,13 +7,17 @@ import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
import com.ycwl.basic.repository.SourceRepository;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.service.pc.SourceService;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.task.VideoPieceGetter;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.SnowFlakeUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.ycwl.basic.storage.StorageFactory;
import java.net.MalformedURLException;
import java.net.URL;
@@ -30,6 +34,8 @@ public class SourceServiceImpl implements SourceService {
private SourceMapper sourceMapper;
@Autowired
private SourceRepository sourceRepository;
@Autowired
private ScenicService scenicService;
@Override
public ApiResponse<PageInfo<SourceRespVO>> pageQuery(SourceReqQuery sourceReqQuery) {
@@ -137,4 +143,29 @@ public class SourceServiceImpl implements SourceService {
}
return ApiResponse.success("任务已下发");
}
@Override
public ApiResponse<String> uploadAndUpdateUrl(Long id, MultipartFile file) {
SourceRespVO source = sourceMapper.getById(id);
if (source == null) {
return ApiResponse.fail("该素材不存在");
}
try {
IStorageAdapter adapter = scenicService.getScenicStorageAdapter(source.getScenicId());
String uploadedUrl = adapter.uploadFile(file, "source", String.valueOf(id));
SourceEntity sourceUpd = new SourceEntity();
sourceUpd.setId(id);
sourceUpd.setUrl(uploadedUrl);
int updateResult = sourceMapper.update(sourceUpd);
if (updateResult > 0) {
return ApiResponse.success(uploadedUrl);
} else {
return ApiResponse.fail("更新URL失败");
}
} catch (Exception e) {
return ApiResponse.fail("文件上传失败: " + e.getMessage());
}
}
}

View File

@@ -87,13 +87,13 @@ public class TaskFaceServiceImpl implements TaskFaceService {
@Override
public SearchFaceRespVo searchFace(Long faceId) {
FaceRespVO faceRespVO = faceMapper.getById(faceId);
if (faceRespVO == null) {
FaceRespVO face = faceMapper.getById(faceId);
if (face == null) {
SearchFaceRespVo vo = new SearchFaceRespVo();
vo.setSampleListIds(new ArrayList<>());
return vo;
}
Long scenicId = faceRespVO.getScenicId();
Long scenicId = face.getScenicId();
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
IFaceBodyAdapter faceBodyAdapter;
if (scenicConfig != null && scenicConfig.getFaceType() != null) {
@@ -102,7 +102,7 @@ public class TaskFaceServiceImpl implements TaskFaceService {
} else {
faceBodyAdapter = FaceBodyFactory.use();
}
SearchFaceRespVo respVo = searchFace(faceBodyAdapter, scenicId.toString(), faceRespVO.getFaceUrl(), "系统定时任务检索");
SearchFaceRespVo respVo = searchFace(faceBodyAdapter, scenicId.toString(), face.getFaceUrl(), "系统定时任务检索");
if (respVo != null) {
FaceEntity faceEntity = new FaceEntity();
faceEntity.setId(faceId);
@@ -120,17 +120,9 @@ public class TaskFaceServiceImpl implements TaskFaceService {
MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
memberSourceEntity.setScenicId(scenicId);
memberSourceEntity.setFaceId(faceEntity.getId());
memberSourceEntity.setMemberId(faceRespVO.getMemberId());
memberSourceEntity.setMemberId(face.getMemberId());
memberSourceEntity.setSourceId(sourceEntity.getId());
memberSourceEntity.setType(sourceEntity.getType());
IsBuyRespVO isBuy = orderBiz.isBuy(faceRespVO.getMemberId(), scenicId, sourceEntity.getType(), sourceEntity.getId());
if (isBuy.isBuy()) { // 如果用户买过
memberSourceEntity.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
memberSourceEntity.setIsBuy(1);
} else {
memberSourceEntity.setIsBuy(0);
}
memberSourceEntity.setIsFree(0);
if (deviceConfig != null) {
if (sourceEntity.getType() == 1) {
@@ -146,11 +138,21 @@ public class TaskFaceServiceImpl implements TaskFaceService {
return memberSourceEntity;
}).collect(Collectors.toList());
if (!memberSourceEntityList.isEmpty()) {
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), memberSourceEntityList.getFirst().getType(), faceEntity.getId());
for (MemberSourceEntity memberSourceEntity : memberSourceEntityList) {
if (isBuy.isBuy()) { // 如果用户买过
memberSourceEntity.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
memberSourceEntity.setIsBuy(1);
} else {
memberSourceEntity.setIsBuy(0);
}
}
sourceMapper.addRelations(memberSourceEntityList);
VideoPieceGetter.Task task = new VideoPieceGetter.Task();
task.faceId = faceEntity.getId();
task.faceSampleIds = sampleListIds;
task.memberId = faceRespVO.getMemberId();
task.memberId = face.getMemberId();
VideoPieceGetter.addTask(task);
}
}

View File

@@ -175,6 +175,7 @@ public class TaskTaskServiceImpl implements TaskService {
taskList.forEach(task -> {
taskMapper.assignToWorker(task.getId(), worker.getId());
videoTaskRepository.clearTaskCache(task.getId());
repository.clearCache(worker.getId());
});
} finally {
lock.unlock();
@@ -445,6 +446,10 @@ public class TaskTaskServiceImpl implements TaskService {
.peek(item -> item.setUrl(item.getVideoUrl()))
.filter(item -> {
DeviceEntity device = deviceRepository.getDevice(item.getDeviceId());
if (device == null) {
log.info("task callback: deviceId:{} is not exist", item.getDeviceId());
return false;
}
return Integer.valueOf(1).equals(device.getStatus());
})
.filter(item -> item.getDeviceId() != null) // 添加对 deviceId 为 null 的检查

View File

@@ -33,25 +33,37 @@ import java.util.Map;
import java.util.stream.Collectors;
final public class AliOssAdapter extends AStorageAdapter {
private AliOssStorageConfig config;
private volatile AliOssStorageConfig config;
private volatile OSS ossClient;
private final Object clientLock = new Object();
private final Object configLock = new Object();
@Override
public String identity() {
return config.identity();
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
return "ALI_OSS_UNCONFIGURED";
}
return currentConfig.identity();
}
@Override
public void loadConfig(Map<String, String> _config) {
AliOssStorageConfig config = new AliOssStorageConfig();
config.setAccessKeyId(_config.get("accessKeyId"));
config.setAccessKeySecret(_config.get("accessKeySecret"));
config.setBucketName(_config.get("bucketName"));
config.setEndpoint(_config.get("endpoint"));
config.setRegion(_config.get("region"));
config.setUrl(_config.get("url"));
config.setPrefix(_config.get("prefix"));
config.checkEverythingOK();
this.config = config;
AliOssStorageConfig newConfig = new AliOssStorageConfig();
newConfig.setAccessKeyId(_config.get("accessKeyId"));
newConfig.setAccessKeySecret(_config.get("accessKeySecret"));
newConfig.setBucketName(_config.get("bucketName"));
newConfig.setEndpoint(_config.get("endpoint"));
newConfig.setRegion(_config.get("region"));
newConfig.setUrl(_config.get("url"));
newConfig.setPrefix(_config.get("prefix"));
newConfig.checkEverythingOK();
synchronized (configLock) {
this.config = newConfig;
// 配置更新后,需要重置客户端连接
resetClient();
}
}
@Override
@@ -59,11 +71,15 @@ final public class AliOssAdapter extends AStorageAdapter {
if (config == null) {
throw new StorageConfigException("配置为空");
}
if (config instanceof AliOssStorageConfig) {
this.config = (AliOssStorageConfig) config;
} else {
if (!(config instanceof AliOssStorageConfig)) {
throw new StorageConfigException("配置类型错误,传入的类为:" + config.getClass().getName());
}
synchronized (configLock) {
this.config = (AliOssStorageConfig) config;
// 配置更新后,需要重置客户端连接
resetClient();
}
}
@Override
@@ -75,12 +91,25 @@ final public class AliOssAdapter extends AStorageAdapter {
try (OSSWrapper wrapper = getOssClient()) {
OSS ossClient = wrapper.getOSSClient();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(inputStream.available());
// 读取完整内容并计算实际长度
byte[] bytes = inputStream.readAllBytes();
metadata.setContentLength(bytes.length);
if (StringUtils.isNotBlank(contentType)) {
metadata.setContentType(contentType);
}
PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, inputStream);
ossClient.putObject(putObjectRequest);
// 使用字节数组创建新的 InputStream
try (InputStream byteInputStream = new java.io.ByteArrayInputStream(bytes)) {
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
PutObjectRequest putObjectRequest = new PutObjectRequest(currentConfig.getBucketName(), fullPath, byteInputStream, metadata);
ossClient.putObject(putObjectRequest);
}
return getUrl(path);
} catch (Exception e) {
throw new UploadFileFailedException("上传文件失败:" + e.getMessage());
@@ -92,7 +121,11 @@ final public class AliOssAdapter extends AStorageAdapter {
public boolean deleteFile(String... path) {
try (OSSWrapper wrapper = getOssClient()) {
OSS ossClient = wrapper.getOSSClient();
ossClient.deleteObject(config.getBucketName(), buildPath(path));
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
return false;
}
ossClient.deleteObject(currentConfig.getBucketName(), buildPath(path));
return true;
} catch (ClientException e) {
return false;
@@ -101,14 +134,22 @@ final public class AliOssAdapter extends AStorageAdapter {
@Override
public String getUrl(String... path) {
return config.getUrl() + "/" + buildPath(path);
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
return currentConfig.getUrl() + "/" + buildPath(path);
}
@Override
public String getUrlForDownload(Date expireDate, String... path) {
try (OSSWrapper wrapper = getOssClient()) {
OSS ossClient = wrapper.getOSSClient();
URL url = ossClient.generatePresignedUrl(config.getBucketName(), buildPath(path), expireDate, HttpMethod.GET);
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
URL url = ossClient.generatePresignedUrl(currentConfig.getBucketName(), buildPath(path), expireDate, HttpMethod.GET);
return url.toString();
}
}
@@ -117,7 +158,11 @@ final public class AliOssAdapter extends AStorageAdapter {
public String getUrlForUpload(Date expireDate, String contentType, String... path) {
try (OSSWrapper wrapper = getOssClient()) {
OSS ossClient = wrapper.getOSSClient();
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(config.getBucketName(), buildPath(path), HttpMethod.PUT);
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(currentConfig.getBucketName(), buildPath(path), HttpMethod.PUT);
if (StringUtils.isNotBlank(contentType)) {
request.setContentType(contentType);
}
@@ -129,7 +174,11 @@ final public class AliOssAdapter extends AStorageAdapter {
@Override
public List<StorageFileObject> listDir(String... path) {
ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(config.getBucketName());
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(currentConfig.getBucketName());
listObjectsV2Request.setPrefix(buildPath(path) + "/");
listObjectsV2Request.setMaxKeys(1000);
boolean isTruncated = true;
@@ -180,7 +229,11 @@ final public class AliOssAdapter extends AStorageAdapter {
}
List<StorageFileObject> subList = objectList.subList(idx, idx + batchSize);
idx += batchSize;
DeleteObjectsRequest request = new DeleteObjectsRequest(config.getBucketName());
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
return false;
}
DeleteObjectsRequest request = new DeleteObjectsRequest(currentConfig.getBucketName());
request.setKeys(subList.stream().map(StorageFileObject::getFullPath).collect(Collectors.toList()));
try {
ossClient.deleteObjects(request);
@@ -208,7 +261,11 @@ final public class AliOssAdapter extends AStorageAdapter {
public boolean setAcl(StorageAcl acl, String... path) {
try (OSSWrapper wrapper = getOssClient()) {
OSS ossClient = wrapper.getOSSClient();
ossClient.setObjectAcl(config.getBucketName(), buildPath(path), convertAcl(acl));
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
return false;
}
ossClient.setObjectAcl(currentConfig.getBucketName(), buildPath(path), convertAcl(acl));
return true;
} catch (OSSException e) {
return false;
@@ -219,32 +276,85 @@ final public class AliOssAdapter extends AStorageAdapter {
public boolean isExists(String ...path) {
try (OSSWrapper wrapper = getOssClient()) {
OSS ossClient = wrapper.getOSSClient();
return ossClient.doesObjectExist(config.getBucketName(), buildPath(path));
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
return false;
}
return ossClient.doesObjectExist(currentConfig.getBucketName(), buildPath(path));
}
}
private OSSWrapper getOssClient() {
OSS ossClient = new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
return new OSSWrapper(ossClient);
AliOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
if (ossClient == null) {
synchronized (clientLock) {
if (ossClient == null) {
// 在同步块内再次检查配置,确保配置一致性
currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
ossClient = new OSSClientBuilder().build(
currentConfig.getEndpoint(),
currentConfig.getAccessKeyId(),
currentConfig.getAccessKeySecret()
);
}
}
}
return new OSSWrapper(ossClient, false); // false 表示不自动关闭共享客户端
}
// 重置客户端连接(配置更新时调用)
private void resetClient() {
synchronized (clientLock) {
if (ossClient != null) {
ossClient.shutdown();
ossClient = null;
}
}
}
// 手动关闭客户端的方法,用于适配器销毁时
public void shutdown() {
synchronized (clientLock) {
if (ossClient != null) {
ossClient.shutdown();
ossClient = null;
}
}
}
private String buildPath(String ...paths) {
if (StringUtils.isNotBlank(config.getPrefix())) {
return StorageUtil.joinPath(config.getPrefix(), paths);
AliOssStorageConfig currentConfig = this.config;
if (currentConfig != null && StringUtils.isNotBlank(currentConfig.getPrefix())) {
return StorageUtil.joinPath(currentConfig.getPrefix(), paths);
} else {
return StorageUtil.joinPath(paths);
}
}
private String getRelativePath(String path) {
return StorageUtil.getRelativePath(path, config.getPrefix());
AliOssStorageConfig currentConfig = this.config;
String prefix = currentConfig != null ? currentConfig.getPrefix() : null;
return StorageUtil.getRelativePath(path, prefix);
}
public static class OSSWrapper implements AutoCloseable {
private final OSS ossClient;
private final boolean autoClose;
public OSSWrapper(OSS ossClient) {
this(ossClient, true);
}
public OSSWrapper(OSS ossClient, boolean autoClose) {
this.ossClient = ossClient;
this.autoClose = autoClose;
}
// 提供对原始对象的方法访问
@@ -254,8 +364,10 @@ final public class AliOssAdapter extends AStorageAdapter {
@Override
public void close() {
// 在此处实现资源关闭逻辑(如调用 OSS 的关闭方法)
ossClient.shutdown(); // 假设 OSS 提供 shutdown 方法关闭资源
// 只有非共享客户端才自动关闭
if (autoClose && ossClient != null) {
ossClient.shutdown();
}
}
}
}

View File

@@ -26,25 +26,37 @@ import java.util.Map;
import java.util.stream.Collectors;
public class AwsOssAdapter extends AStorageAdapter {
private AwsOssStorageConfig config;
private volatile AwsOssStorageConfig config;
private volatile AmazonS3Client s3Client;
private final Object clientLock = new Object();
private final Object configLock = new Object();
@Override
public String identity() {
return config.identity();
AwsOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
return "AWS_OSS_UNCONFIGURED";
}
return currentConfig.identity();
}
@Override
public void loadConfig(Map<String, String> _config) {
AwsOssStorageConfig config = new AwsOssStorageConfig();
config.setAccessKeyId(_config.get("accessKeyId"));
config.setAccessKeySecret(_config.get("accessKeySecret"));
config.setBucketName(_config.get("bucketName"));
config.setEndpoint(_config.get("endpoint"));
config.setRegion(_config.get("region"));
config.setUrl(_config.get("url"));
config.setPrefix(_config.get("prefix"));
config.checkEverythingOK();
this.config = config;
AwsOssStorageConfig newConfig = new AwsOssStorageConfig();
newConfig.setAccessKeyId(_config.get("accessKeyId"));
newConfig.setAccessKeySecret(_config.get("accessKeySecret"));
newConfig.setBucketName(_config.get("bucketName"));
newConfig.setEndpoint(_config.get("endpoint"));
newConfig.setRegion(_config.get("region"));
newConfig.setUrl(_config.get("url"));
newConfig.setPrefix(_config.get("prefix"));
newConfig.checkEverythingOK();
synchronized (configLock) {
this.config = newConfig;
// 配置更新后,需要重置客户端连接
resetClient();
}
}
@Override
@@ -52,11 +64,15 @@ public class AwsOssAdapter extends AStorageAdapter {
if (config == null) {
throw new StorageConfigException("配置为空");
}
if (config instanceof AwsOssStorageConfig) {
this.config = (AwsOssStorageConfig) config;
} else {
if (!(config instanceof AwsOssStorageConfig)) {
throw new StorageConfigException("配置类型错误,传入的类为:" + config.getClass().getName());
}
synchronized (configLock) {
this.config = (AwsOssStorageConfig) config;
// 配置更新后,需要重置客户端连接
resetClient();
}
}
@Override
@@ -68,13 +84,22 @@ public class AwsOssAdapter extends AStorageAdapter {
try (S3Wrapper wrapper = getS3Client()) {
AmazonS3Client s3Client = wrapper.s3Client();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(inputStream.available());
// 读取完整内容并计算实际长度
byte[] bytes = inputStream.readAllBytes();
metadata.setContentLength(bytes.length);
if (StringUtils.isNotBlank(contentType)) {
metadata.setContentType(contentType);
}
PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, inputStream, metadata);
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead); // 设置访问权限,让所有用户都允许访问
s3Client.putObject(putObjectRequest);
// 使用字节数组创建新的 InputStream
try (InputStream byteInputStream = new java.io.ByteArrayInputStream(bytes)) {
PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, byteInputStream, metadata);
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead); // 设置访问权限,让所有用户都允许访问
s3Client.putObject(putObjectRequest);
}
return getUrl(path);
} catch (Exception e) {
throw new UploadFileFailedException("上传文件失败:" + e.getMessage());
@@ -150,6 +175,7 @@ public class AwsOssAdapter extends AStorageAdapter {
object.setPath(getRelativePath(item.getKey().substring(0, item.getKey().lastIndexOf("/"))));
object.setName(item.getKey().substring(item.getKey().lastIndexOf("/") + 1));
object.setSize(item.getSize());
object.setModifyTime(item.getLastModified());
object.setRawObject(item);
return object;
}).collect(Collectors.toList());
@@ -220,14 +246,57 @@ public class AwsOssAdapter extends AStorageAdapter {
}
private S3Wrapper getS3Client() {
BasicAWSCredentials basicAwsCred = new BasicAWSCredentials(config.getAccessKeyId(), config.getAccessKeySecret());
ClientConfiguration clientConfiguration = new ClientConfiguration();
clientConfiguration.setProtocol(Protocol.HTTPS);
AmazonS3Client s3 = new AmazonS3Client(basicAwsCred,clientConfiguration);
S3ClientOptions options = S3ClientOptions.builder().setPathStyleAccess(true).setPayloadSigningEnabled(true).disableChunkedEncoding().build();
s3.setS3ClientOptions(options);
s3.setEndpoint(config.getEndpoint());
return new S3Wrapper(s3);
AwsOssStorageConfig currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
if (s3Client == null) {
synchronized (clientLock) {
if (s3Client == null) {
// 在同步块内再次检查配置,确保配置一致性
currentConfig = this.config;
if (currentConfig == null) {
throw new StorageConfigException("存储适配器未配置");
}
BasicAWSCredentials basicAwsCred = new BasicAWSCredentials(
currentConfig.getAccessKeyId(),
currentConfig.getAccessKeySecret()
);
ClientConfiguration clientConfiguration = new ClientConfiguration();
clientConfiguration.setProtocol(Protocol.HTTPS);
s3Client = new AmazonS3Client(basicAwsCred, clientConfiguration);
S3ClientOptions options = S3ClientOptions.builder()
.setPathStyleAccess(true)
.setPayloadSigningEnabled(true)
.disableChunkedEncoding()
.build();
s3Client.setS3ClientOptions(options);
s3Client.setEndpoint(currentConfig.getEndpoint());
}
}
}
return new S3Wrapper(s3Client, false); // false 表示不自动关闭共享客户端
}
// 重置客户端连接(配置更新时调用)
private void resetClient() {
synchronized (clientLock) {
if (s3Client != null) {
s3Client.shutdown();
s3Client = null;
}
}
}
// 手动关闭客户端的方法,用于适配器销毁时
public void shutdown() {
synchronized (clientLock) {
if (s3Client != null) {
s3Client.shutdown();
s3Client = null;
}
}
}
private String buildPath(String... paths) {
@@ -242,10 +311,18 @@ public class AwsOssAdapter extends AStorageAdapter {
return StorageUtil.getRelativePath(path, config.getPrefix());
}
public record S3Wrapper(AmazonS3Client s3Client) implements AutoCloseable {
public record S3Wrapper(AmazonS3Client s3Client, boolean autoClose) implements AutoCloseable {
public S3Wrapper(AmazonS3Client s3Client) {
this(s3Client, true);
}
@Override
public void close() {
s3Client.shutdown();
// 只有非共享客户端才自动关闭
if (autoClose && s3Client != null) {
s3Client.shutdown();
}
}
}
}

View File

@@ -3,71 +3,207 @@ package com.ycwl.basic.storage.adapters;
import com.ycwl.basic.storage.entity.StorageConfig;
import com.ycwl.basic.storage.entity.StorageFileObject;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.storage.exceptions.StorageConfigException;
import com.ycwl.basic.storage.exceptions.StorageException;
import com.ycwl.basic.storage.exceptions.UploadFileFailedException;
import com.ycwl.basic.storage.utils.StorageUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class LocalStorageAdapter extends AStorageAdapter {
private volatile String basePath;
private volatile String baseUrl;
private volatile String prefix = "";
private final Object configLock = new Object();
public class LocalStorageAdapter extends AStorageAdapter{
@Override
public String identity() {
return "";
return "LOCAL_STORAGE";
}
@Override
public void loadConfig(Map<String, String> config) {
String newBasePath = config.get("basePath");
String newBaseUrl = config.get("baseUrl");
String newPrefix = StringUtils.isNotBlank(config.get("prefix")) ? config.get("prefix") : "";
if (StringUtils.isBlank(newBasePath)) {
throw new StorageConfigException("本地存储配置错误:basePath 不能为空");
}
if (StringUtils.isBlank(newBaseUrl)) {
throw new StorageConfigException("本地存储配置错误:baseUrl 不能为空");
}
// 确保基础目录存在
try {
Files.createDirectories(Paths.get(newBasePath));
} catch (IOException e) {
throw new StorageConfigException("创建本地存储目录失败:" + e.getMessage());
}
synchronized (configLock) {
this.basePath = newBasePath;
this.baseUrl = newBaseUrl;
this.prefix = newPrefix;
}
}
@Override
public void setConfig(StorageConfig config) {
throw new StorageConfigException("LocalStorageAdapter 不支持 StorageConfig 类型配置");
}
@Override
public String uploadFile(String contentType, InputStream inputStream, String... path) {
return "";
if (inputStream == null) {
return null;
}
String fullPath = buildLocalPath(path);
try {
// 确保父目录存在
Path filePath = Paths.get(fullPath);
Files.createDirectories(filePath.getParent());
// 复制文件内容到本地
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
return getUrl(path);
} catch (IOException e) {
throw new UploadFileFailedException("本地文件保存失败:" + e.getMessage());
}
}
@Override
public boolean deleteFile(String... path) {
return false;
try {
String fullPath = buildLocalPath(path);
return Files.deleteIfExists(Paths.get(fullPath));
} catch (IOException e) {
return false;
}
}
@Override
public String getUrl(String... path) {
return "";
String relativePath = buildRelativePath(path);
return baseUrl + "/" + relativePath;
}
@Override
public String getUrlForDownload(Date expireDate, String... path) {
return "";
// 本地存储不支持带过期时间的URL,直接返回普通URL
return getUrl(path);
}
@Override
public String getUrlForUpload(Date expireDate, String contentType, String... path) {
return "";
// 本地存储不支持预签名上传URL
return getUrl(path);
}
@Override
public List<StorageFileObject> listDir(String... path) {
return Collections.emptyList();
String fullPath = buildLocalPath(path);
Path dirPath = Paths.get(fullPath);
if (!Files.exists(dirPath) || !Files.isDirectory(dirPath)) {
return new ArrayList<>();
}
List<StorageFileObject> result = new ArrayList<>();
try (Stream<Path> paths = Files.list(dirPath)) {
paths.forEach(filePath -> {
try {
StorageFileObject obj = new StorageFileObject();
obj.setName(filePath.getFileName().toString());
obj.setPath(getRelativePath(filePath.getParent().toString()));
obj.setSize(Files.size(filePath));
obj.setModifyTime(new Date(Files.getLastModifiedTime(filePath).toMillis()));
obj.setRawObject(filePath.toFile());
result.add(obj);
} catch (IOException e) {
// 忽略无法读取的文件
}
});
} catch (IOException e) {
throw new StorageException("列举本地文件失败:" + e.getMessage());
}
return result;
}
@Override
public boolean deleteDir(String... path) {
return false;
String fullPath = buildLocalPath(path);
Path dirPath = Paths.get(fullPath);
if (!Files.exists(dirPath)) {
return true;
}
try {
// 递归删除目录及其内容
try (Stream<Path> paths = Files.walk(dirPath)) {
paths.sorted((a, b) -> b.compareTo(a)) // 从深到浅排序,确保先删除文件再删除目录
.forEach(filePath -> {
try {
Files.deleteIfExists(filePath);
} catch (IOException e) {
// 忽略删除失败的文件
}
});
}
return !Files.exists(dirPath);
} catch (IOException e) {
return false;
}
}
@Override
public boolean setAcl(StorageAcl acl, String... path) {
return false;
// 本地存储不支持ACL设置
return true;
}
@Override
public boolean isExists(String... path) {
return false;
String fullPath = buildLocalPath(path);
return Files.exists(Paths.get(fullPath));
}
private String buildLocalPath(String... paths) {
String relativePath = buildRelativePath(paths);
return Paths.get(basePath, relativePath).toString();
}
private String buildRelativePath(String... paths) {
if (StringUtils.isNotBlank(prefix)) {
return StorageUtil.joinPath(prefix, paths);
} else {
return StorageUtil.joinPath(paths);
}
}
private String getRelativePath(String fullPath) {
String basePathNormalized = Paths.get(basePath).toString();
if (fullPath.startsWith(basePathNormalized)) {
String relative = fullPath.substring(basePathNormalized.length());
if (relative.startsWith(File.separator)) {
relative = relative.substring(1);
}
return StorageUtil.getRelativePath(relative, prefix);
}
return fullPath;
}
}

View File

@@ -131,7 +131,7 @@ public class DynamicTaskGenerator {
return false;
}
return face.getScenicId().equals(faceSample.getScenicId());
}).collect(Collectors.toList());
}).toList();
if (faceIdList.isEmpty()) {
log.info("本景区人脸样本ID不在人脸样本库中,忽略任务:{}", task);
return;

View File

@@ -0,0 +1,70 @@
package com.ycwl.basic.task;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
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.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import cn.hutool.http.HttpResponse;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@EnableScheduling
public class N9eSyncTask {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Scheduled(fixedRate = 1000 * 60L)
public void syncN9e() {
// 构建Basic认证头
String auth = "Basic " + Base64.encode("user001:ccc26da7b9aba533cbb263a36c07dcc4");
// 构建请求体
JSONObject requestBody = new JSONObject();
JSONArray queries = new JSONArray();
JSONObject query = new JSONObject();
query.put("key", "group_ids");
query.put("op", "==");
JSONArray values = new JSONArray();
values.add(4);
query.put("values", values);
queries.add(query);
requestBody.put("queries", queries);
// 发送POST请求
HttpResponse response = HttpUtil.createPost("https://n9e.jerryyan.top/v1/n9e/target/list")
.header("Authorization", auth)
.header("Content-Type", "application/json")
.body(requestBody.toJSONString())
.execute();
JSONObject respData = JSON.parseObject(response.body());
if (StringUtils.isNotBlank(respData.getString("err"))) {
log.warn("N9E信息获取失败");
return;
}
JSONObject data = respData.getJSONObject("dat");
if (data.getInteger("total") <= 0) {
log.warn("N9E信息为空");
return;
}
JSONArray list = data.getJSONArray("list");
list.forEach(item -> {
JSONObject itemObj = (JSONObject) item;
String ident = itemObj.getString("ident");
Long updateAt = itemObj.getLong("update_at");
redisTemplate.opsForValue().set("ext_device:online:" + ident, updateAt.toString(), 1, TimeUnit.DAYS);
});
}
}

View File

@@ -7,7 +7,9 @@ import com.ycwl.basic.constant.StorageConstant;
import com.ycwl.basic.device.DeviceFactory;
import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.operator.IDeviceStorageOperator;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.SourceMapper;
@@ -16,6 +18,8 @@ import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.TemplateRepository;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
@@ -57,6 +61,8 @@ public class VideoPieceGetter {
@Autowired
private FaceSampleMapper faceSampleMapper;
@Autowired
private FaceRepository faceRepository;
@Autowired
private DeviceRepository deviceRepository;
@Autowired
private SourceMapper sourceMapper;
@@ -68,6 +74,8 @@ public class VideoPieceGetter {
private TaskStatusBiz taskStatusBiz;
@Autowired
private VideoReUploader videoReUploader;
@Autowired
private ScenicRepository scenicRepository;
@Data
public static class Task {
@@ -208,7 +216,7 @@ public class VideoPieceGetter {
log.info("executor已结束![A:{}/T:{}/F:{}]", executor.getActiveCount(), executor.getTaskCount(), executor.getCompletedTaskCount());
executor.close();
} catch (InterruptedException e) {
return;
log.info("executor已中断![A:{}/T:{}/F:{}]", executor.getActiveCount(), executor.getTaskCount(), executor.getCompletedTaskCount());
} finally {
if (task.faceId != null) {
taskStatusBiz.setFaceCutStatus(task.faceId, 1);
@@ -220,6 +228,29 @@ public class VideoPieceGetter {
task.getCallback().onInvoke();
}
}
if (task.getFaceId() != null) {
FaceEntity face = faceRepository.getFace(task.getFaceId());
if (face != null) {
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId());
if (scenicConfig != null) {
// 免费送
List<MemberSourceEntity> sourceEntities = sourceMapper.listByFaceRelation(face.getScenicId(), face.getId(), 1);
if (sourceEntities.stream().noneMatch(item -> Integer.valueOf(1).equals(item.getIsFree()))) {
List<Long> freeSourceRelationIds = new ArrayList<>();
if (scenicConfig.getVideoFreeNum() != null && scenicConfig.getVideoFreeNum() > 0) {
if (scenicConfig.getVideoFreeNum() > sourceEntities.size()) {
freeSourceRelationIds.addAll(sourceEntities.stream().map(MemberSourceEntity::getId).toList());
} else {
freeSourceRelationIds.addAll(sourceEntities.stream().limit(scenicConfig.getVideoFreeNum()).map(MemberSourceEntity::getId).toList());
}
}
if (!freeSourceRelationIds.isEmpty()) {
sourceMapper.freeRelations(freeSourceRelationIds, 1);
}
}
}
}
}
}
private boolean doCut(Long deviceId, Long faceSampleId, Date baseTime, Task task) {

View File

@@ -1,14 +1,19 @@
package com.ycwl.basic.utils;
import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
public class ImageUtils {
public static MultipartFile base64ToMultipartFile(String base64) {
String[] baseStrs = base64.split(",");
@@ -21,6 +26,37 @@ public class ImageUtils {
}
return new Base64DecodedMultipartFile(b, baseStrs[0]);
}
public static MultipartFile cropImage(MultipartFile file, int x, int y, int w, int h) throws IOException {
BufferedImage image = ImageIO.read(file.getInputStream());
log.info("图片宽高:{}", image.getWidth() + "x" + image.getHeight());
log.info("图片裁切:{}@{}", w + "x" + h, x + "," + y);
if (image.getWidth() < w) {
w = image.getWidth();
}
if (image.getHeight() < h) {
h = image.getHeight();
}
int targetX = x;
if (x < 0) {
targetX = 0;
} else if ((x + w) > image.getWidth()) {
targetX = image.getWidth() - w;
}
int targetY = y;
if (y < 0) {
targetY = 0;
} else if ((y + h) > image.getHeight()) {
targetY = image.getHeight() - h;
}
log.info("图片实际裁切:{}@{}", w + "x" + h, targetX + "," + targetY);
BufferedImage targetImage = image.getSubimage(targetX, targetY, w, h);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(targetImage, "jpg", baos);
baos.close();
return new Base64DecodedMultipartFile(baos.toByteArray(), "image/jpeg");
}
public static class Base64DecodedMultipartFile implements MultipartFile {
private final byte[] imgContent;

View File

@@ -13,6 +13,8 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
@@ -49,10 +51,19 @@ public class JwtTokenUtil {
public static String generateToken(JwtInfo jwtInfo, int expire) throws Exception {
LocalDateTime expireTime = LocalDateTime.now().plusDays(expire);
if (jwtInfo.getExpireTime() != null) {
expireTime = jwtInfo.getExpireTime();
}
byte[] bytes = RsaKeyUtil.toBytes(PRI_KEY);
String token = JwtAnalysisUtil.generateToken(jwtInfo, bytes, expireTime);
return token;
}
public static String generateToken(JwtInfo jwtInfo, Date expireTime) throws Exception {
byte[] bytes = RsaKeyUtil.toBytes(PRI_KEY);
LocalDateTime dt = expireTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
String token = JwtAnalysisUtil.generateToken(jwtInfo, bytes, dt);
return token;
}
/**

View File

@@ -3,6 +3,7 @@ package com.ycwl.basic.utils;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Date;
@@ -12,8 +13,10 @@ import java.util.concurrent.locks.ReentrantLock;
public class WxMpUtil {
private static final String GET_WXA_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacode?access_token=%s";
private static final String GET_WXA_CODE_UNLIMITED_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";
private static final String GET_URL_LICK_URL = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s";
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
private static final String STABLE_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token?grant_type=client_credential&appid=%s&secret=%s&force_refresh=false";
public static final String GET_USER_PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";
private static final Map<String, String> tokens = new ConcurrentHashMap<>();
private static final Map<String, Date> expireTimes = new ConcurrentHashMap<>();
@@ -31,12 +34,27 @@ public class WxMpUtil {
tokens.remove(appId);
}
return tokens.computeIfAbsent(appId, (k) -> {
String url = String.format(ACCESS_TOKEN_URL, appId, appSecret);
String response = HttpUtil.get(url);
JSONObject jsonObject = JSONObject.parseObject(response);
String token = jsonObject.getString("access_token");
Date expireTime = new Date(System.currentTimeMillis() + jsonObject.getInteger("expires_in") * 1000 / 2);
expireTimes.put(appId, expireTime);
String token;
if (!System.getProperty("os.name").toLowerCase().startsWith("win")) {
String url = String.format(STABLE_ACCESS_TOKEN_URL, appId, appSecret);
JSONObject params = new JSONObject();
params.put("grant_type", "client_credential");
params.put("appid", appId);
params.put("secret", appSecret);
params.put("force_refresh", false);
String response = HttpUtil.post(url, params.toJSONString());
JSONObject jsonObject = JSONObject.parseObject(response);
token = jsonObject.getString("access_token");
Date expireTime = new Date(System.currentTimeMillis() + jsonObject.getInteger("expires_in") * 1000 / 2);
expireTimes.put(appId, expireTime);
} else {
String url = String.format(ACCESS_TOKEN_URL, appId, appSecret);
String response = HttpUtil.get(url);
JSONObject jsonObject = JSONObject.parseObject(response);
token = jsonObject.getString("access_token");
Date expireTime = new Date(System.currentTimeMillis() + jsonObject.getInteger("expires_in") * 1000 / 2);
expireTimes.put(appId, expireTime);
}
return token;
});
} finally {
@@ -66,6 +84,28 @@ public class WxMpUtil {
}
}
public static void generateUnlimitedWXAQRCode(String appId, String appSecret, String path, String scene, File targetFile) throws Exception {
String url = String.format(GET_WXA_CODE_UNLIMITED_URL, getAccessToken(appId, appSecret));
JSONObject json = new JSONObject();
json.put("page", path);
json.put("scene", scene);
json.put("check_path", false);
try (HttpResponse response = HttpUtil.createPost(url).body(json.toJSONString()).header("Content-Type", "application/json").execute()) {
if (response.getStatus() != 200) {
throw new Exception("获取小程序二维码失败,原因为:" + response.body());
}
InputStream inputStream = response.bodyStream();
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
}
}
public static String generateUrlLink(String appId, String appSecret, String path, String query) throws Exception {
String url = String.format(GET_URL_LICK_URL, getAccessToken(appId, appSecret));
JSONObject json = new JSONObject();

View File

@@ -28,6 +28,7 @@
<update id="update" parameterType="com.ycwl.basic.model.pc.adminUser.req.AddOrUpdateAdminUserReqVO">
update admin_user
set `role_id` =#{roleId}, `account`=#{account}, `name`=#{name}, `phone`=#{phone}
<if test="password != null">, `password`=#{password}</if>, update_at = NOW()
where id = #{id}
</update>
<update id="updatePassword">
@@ -77,7 +78,8 @@
au.name as staffName,
au.id as staffId,
au.password,
au.role_id
au.role_id,
au.update_at
from admin_user au
where account = #{account}
and au.status = 1
@@ -89,4 +91,16 @@
where id = #{id}
and status = 1
</select>
<select id="getById" resultType="com.ycwl.basic.model.pc.adminUser.entity.LoginEntity">
select
au.account,
au.name as staffName,
au.id as staffId,
au.password,
au.role_id,
au.update_at
from admin_user au
where id = #{id}
and au.status = 1
</select>
</mapper>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ycwl.basic.mapper.AioDeviceMapper">
<select id="getById" resultType="com.ycwl.basic.model.aio.entity.AioDeviceEntity">
select * from aio_device where id = #{id} limit 1
</select>
<select id="getByKey" resultType="com.ycwl.basic.model.aio.entity.AioDeviceEntity">
select * from aio_device where access_key = #{accessKey} limit 1
</select>
<select id="getBannerByDeviceId" resultType="com.ycwl.basic.model.aio.entity.AioDeviceBannerEntity">
select * from aio_device_banner where device_id = #{deviceId}
</select>
<select id="getPriceConfigByDeviceId"
resultType="com.ycwl.basic.model.aio.entity.AioDevicePriceConfigEntity">
select * from aio_device_price_config where id = #{deviceId}
</select>
</mapper>

View File

@@ -109,6 +109,7 @@
from device d
left join device_preview_config p on d.id = p.device_id and p.status = 1
where d.scenic_id = #{scenicId}
order by d.sort
</select>
<select id="deviceCountByScenicId" resultType="com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO">
select count(1) totalDeviceCount

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ycwl.basic.mapper.ExtraDeviceMapper">
<select id="listExtraDeviceByScenicId" resultType="com.ycwl.basic.model.pc.device.resp.DeviceRespVO">
select d.id, d.ident as no, d.scenic_id, d.name, d.status, s.name as scenic_name
from extra_device d
left join scenic s on d.scenic_id = s.id
where d.scenic_id = #{scenicId}
and d.status = 1
</select>
</mapper>

View File

@@ -2,75 +2,18 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ycwl.basic.mapper.MenuMapper">
<insert id="addRoleMenu">
insert into role_menu(`role_id`, `menu_id`)
values
<foreach collection="list" item="item" separator=",">
(#{id},#{item})
</foreach>
replace role_menu(`role_id`, `menu_str`, `perm_str`, `update_time`)
values (#{id}, #{menuStr}, #{permStr}, NOW())
</insert>
<insert id="add">
insert into menu(id, parent_id, target, `name`, type, sort, permission_url, is_remove, business_type, icon)
values
(#{id}, #{parentId}, #{target}, #{name}, #{type}, #{sort}, #{permissionUrl}, #{isRemove}, #{businessType}, #{icon})
</insert>
<update id="update">
update menu
<set>
<if test="parentId!= null ">
parent_id = #{parentId},
</if>
<if test="target!= null and target!= ''">
target = #{target},
</if>
<if test="name!= null and name!= ''">
`name` = #{name},
</if>
<if test="type!= null ">
`type` = #{type},
</if>
<if test="sort!= null ">
sort = #{sort},
</if>
<if test="permissionUrl!= null and permissionUrl!= ''">
permission_url = #{permissionUrl},
</if>
<if test="businessType!= null ">
business_type = #{businessType},
</if>
<if test="icon!= null and icon!= ''">
icon = #{icon},
</if>
</set>
where id = #{id}
</update>
<delete id="deleteRoleMenuByRoleId">
delete
from role_menu
where role_id = #{id}
</delete>
<delete id="deleteById">
update menu set is_remove=1 where id = #{id}
</delete>
<delete id="deleteRoleMenuByMenuId">
delete
<select id="getPermissionByRoleId" resultType="com.ycwl.basic.model.pc.role.resp.RolePermissionResp">
select role_id, menu_str, perm_str
from role_menu
where menu_id = #{id}
</delete>
<select id="getListByType" resultType="com.ycwl.basic.model.pc.menu.MenuNode">
select id,
parent_id,
target,
`name`,
`type`,
sort
from menu
where is_remove = 0
<if test="type!= null ">
and business_type = #{type}
</if>
where role_id = #{id}
</select>
</mapper>

View File

@@ -121,6 +121,7 @@
WHEN '1' THEN '录像集'
WHEN '2' THEN '照片集'
WHEN '3' THEN '照片打印'
WHEN '4' THEN '一体机照片打印'
ELSE '其他'
END AS goods_name,
CASE oi.goods_type
@@ -141,6 +142,7 @@
WHEN '1' THEN msd.url
WHEN '2' THEN msd.url
WHEN '3' THEN mpd.url
WHEN '4' THEN msd.url
END AS imgUrl
FROM order_item oi
LEFT JOIN `order` o ON oi.order_id = o.id

View File

@@ -65,6 +65,19 @@
END)
where id = #{id}
</update>
<update id="updateHost">
update render_worker
set platform = #{status.platform},
runtime_version = #{status.runtimeVersion},
version = #{status.version},
cpu_count = #{status.cpuCount},
cpu_usage = #{status.cpuUsage},
memory_total = #{status.memoryTotal},
memory_available = #{status.memoryAvailable},
support_feature = #{status.supportFeature},
update_at = #{status.updateAt}
where id = #{id}
</update>
<delete id="deleteById">
delete from render_worker where id = #{id}
</delete>

View File

@@ -9,14 +9,16 @@
<update id="delete">
update
role
set is_remove=1
set is_remove=1,
`update_time`=NOW()
where id = #{id}
</update>
<update id="update" parameterType="com.ycwl.basic.model.pc.role.req.AddOrUpdateRoleReqVO">
update
role
set `name`=#{name}
set `name`=#{name},
`update_time`=NOW()
where id = #{id}
</update>

View File

@@ -59,7 +59,7 @@
select s.scenic_id, s.device_id
from member_source ms
left join source s on ms.source_id = s.id
where ms.type = 1
where ms.type = 2
and s.create_time >= #{start}
and s.create_time &lt;= #{end}
group by s.scenic_id, s.device_id, ms.face_id

View File

@@ -126,7 +126,9 @@
pay_config_json=#{payConfigJson},
image_source_pack_hint=#{imageSourcePackHint},
video_source_pack_hint=#{videoSourcePackHint},
extra_notification_time=#{extraNotificationTime}
extra_notification_time=#{extraNotificationTime},
photo_free_num= #{photoFreeNum},
video_free_num= #{videoFreeNum}
</set>
where id = #{id}
</update>

View File

@@ -6,11 +6,11 @@
values (#{id}, #{scenicId}, #{deviceId}, #{url}, #{videoUrl}, #{type}, #{faceSampleId}, #{posJson}, #{createTime})
</insert>
<insert id="addRelation">
replace member_source(scenic_id, face_id, member_id, source_id, is_buy, type, order_id, is_free)
values (#{scenicId}, #{faceId}, #{memberId}, #{sourceId}, #{isBuy}, #{type}, #{orderId}, #{isFree})
replace member_source(scenic_id, face_id, member_id, source_id, is_buy, type, order_id<if test="isFree">, is_free</if>)
values (#{scenicId}, #{faceId}, #{memberId}, #{sourceId}, #{isBuy}, #{type}, #{orderId}<if test="isFree">, #{isFree}</if>)
</insert>
<insert id="addRelations">
replace member_source(scenic_id, face_id, member_id, source_id, is_buy, type, order_id, is_free)
insert IGNORE member_source(scenic_id, face_id, member_id, source_id, is_buy, type, order_id, is_free)
values
<foreach collection="list" item="item" separator=",">
(#{item.scenicId}, #{item.faceId}, #{item.memberId}, #{item.sourceId}, #{item.isBuy}, #{item.type}, #{item.orderId}, #{item.isFree})
@@ -42,6 +42,14 @@
</set>
where member_id = #{memberId} and face_id = #{faceId} and `type` = #{type}
</update>
<update id="freeRelations">
update member_source
set is_free = 1
where type = #{type} and id in
<foreach item="item" collection="ids" open="(" separator="," close=")">
#{item}
</foreach>
</update>
<delete id="deleteById">
delete from source where id = #{id}
</delete>
@@ -227,4 +235,11 @@
<if test="type!=null">and ms.type = #{type} </if>
<if test="faceId!=null">and ms.face_id = #{faceId} </if>
</select>
<select id="listByFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.MemberSourceEntity">
select *
from member_source ms
where ms.member_id = #{memberId}
and ms.face_id = #{faceId}
<if test="type!=null">and ms.type = #{type} </if>
</select>
</mapper>

View File

@@ -12,10 +12,10 @@
where
(status = 1 or status = 2) and scenic_id = #{scenicId}
<if test="startTime!= null">
and create_at >= #{startTime}
and pay_at >= #{startTime}
</if>
<if test="endTime!= null">
and create_at &lt;= #{endTime}
and pay_at &lt;= #{endTime}
</if>
</select>
<select id="countPreviewVideoOfMember" resultType="java.lang.Integer">
@@ -61,17 +61,20 @@
SELECT
IFNULL(count(1), 0) AS count
FROM (
select count(1) as count
from `order`
where scenic_id = #{scenicId}
select 1
FROM `t_stats_record` r
left join `t_stats` s on r.trace_id=s.trace_id
where r.trace_id in (select trace_id from `t_stats_record` where action = 'ENTER_SCENIC' and `identifier`=#{scenicId})
and action = 'CLICK'
and identifier = 'BUY'
<if test="startTime!= null">
and create_at >= #{startTime}
and s.create_time >= #{startTime}
</if>
<if test="endTime!= null">
and create_at &lt;= #{endTime}
and s.create_time &lt;= #{endTime}
</if>
group by member_id
)a
group by s.member_id
) AS subquery;
</select>
<select id="countPayOfMember" resultType="java.lang.Integer">
SELECT

View File

@@ -112,7 +112,7 @@
</select>
<select id="listFor" resultType="com.ycwl.basic.model.mobile.scenic.content.ContentPageVO">
select t.id templateId, t.scenic_id, s.name as scenic_name, t.`name`, pid, t.cover_url templateCoverUrl,
0 as sourceType,
0 as sourceType, sort,
t.create_time, t.price
from template t left join scenic s on s.id = t.scenic_id
where t.scenic_id = #{scenicId} and pid = 0 and t.status = 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -16,7 +16,7 @@ public class WatermarkOperatorTest {
@Test
public void testProcess() throws Exception {
WatermarkInfo info = new WatermarkInfo();
info.setOriginalFile(new File("e2d32de7-6e85-4e07-b42f-477347073539.jpg"));
info.setOriginalFile(new File("38e1285c-84d0-464a-810d-053e9502e257.jpg"));
info.setQrcodeFile(new File("cxzh_t.jpg"));
info.setScenicLine("川西竹海一日游!");
info.setDatetimeLine("2XXX年XX月XX日 留念");