diff --git a/src/main/java/com/ycwl/basic/controller/printer/PrinterTvController.java b/src/main/java/com/ycwl/basic/controller/printer/PrinterTvController.java index 1dfb99b6..0df92089 100644 --- a/src/main/java/com/ycwl/basic/controller/printer/PrinterTvController.java +++ b/src/main/java/com/ycwl/basic/controller/printer/PrinterTvController.java @@ -3,7 +3,13 @@ package com.ycwl.basic.controller.printer; import cn.hutool.core.date.DateUtil; import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter; +import com.ycwl.basic.mapper.FaceMapper; +import com.ycwl.basic.model.pc.face.entity.FaceEntity; +import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity; import com.ycwl.basic.model.task.resp.SearchFaceRespVo; +import com.ycwl.basic.repository.MemberRelationRepository; +import com.ycwl.basic.repository.SourceRepository; +import com.ycwl.basic.service.printer.PrinterService; import com.ycwl.basic.service.task.TaskFaceService; import com.ycwl.basic.storage.adapters.IStorageAdapter; import com.ycwl.basic.annotation.IgnoreToken; @@ -23,6 +29,7 @@ import com.ycwl.basic.service.pc.FaceService; import com.ycwl.basic.service.pc.ScenicService; import com.ycwl.basic.storage.utils.StorageUtil; import com.ycwl.basic.utils.ApiResponse; +import com.ycwl.basic.utils.SnowFlakeUtil; import com.ycwl.basic.utils.WxMpUtil; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -55,8 +62,13 @@ public class PrinterTvController { private final ScenicRepository scenicRepository; private final FaceRepository faceRepository; private final TaskFaceService faceService; + private final FaceService pcFaceService; private final ScenicService scenicService; private final SourceMapper sourceMapper; + private final FaceMapper faceMapper; + private final MemberRelationRepository memberRelationRepository; + private final SourceRepository sourceRepository; + private final PrinterService printerService; /** * 获取景区列表 @@ -118,6 +130,57 @@ public class PrinterTvController { } } + /** + * 获取人脸绑定二维码 + * 生成小程序二维码,用于绑定人脸到用户账号 + * + * @param faceId 人脸ID + * @param response HTTP响应 + */ + @GetMapping("/face/{faceId}/qrcode") + public void getFaceQrcode(@PathVariable("faceId") Long faceId, HttpServletResponse response) throws Exception { + File qrcode = new File("qrcode_face_" + faceId + ".jpg"); + try { + FaceEntity face = faceRepository.getFace(faceId); + if (face == null) { + response.setStatus(404); + return; + } + MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(face.getScenicId()); + if (scenicMpConfig == null) { + response.setStatus(500); + return; + } + WxMpUtil.generateUnlimitedWXAQRCode( + scenicMpConfig.getAppId(), + scenicMpConfig.getAppSecret(), + "pages/videoSynthesis/bind_face", + faceId.toString(), + qrcode + ); + + // 设置响应头 + response.setContentType("image/jpeg"); + response.setHeader("Content-Disposition", "inline; filename=\"" + qrcode.getName() + "\""); + + // 将二维码文件写入响应输出流 + try (FileInputStream fis = new FileInputStream(qrcode); + OutputStream os = response.getOutputStream()) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.flush(); + } + } finally { + // 删除临时文件 + if (qrcode.exists()) { + qrcode.delete(); + } + } + } + /** * 根据人脸样本ID查询图像素材 * @@ -154,35 +217,44 @@ public class PrinterTvController { String fileName = UUID.randomUUID() + "." + suffix; String faceUrl = adapter.uploadFile(file, filePath, fileName); - // 2. 获取景区人脸识别适配器 - IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(scenicId); + // 2. 保存人脸数据到数据库 + Long faceId = SnowFlakeUtil.getLongId(); + FaceEntity faceEntity = new FaceEntity(); + faceEntity.setId(faceId); + faceEntity.setScenicId(scenicId); + faceEntity.setFaceUrl(faceUrl); + faceEntity.setCreateAt(new Date()); + faceEntity.setMemberId(0L); // 打印机大屏端没有用户ID + faceMapper.add(faceEntity); // 3. 在景区人脸库中搜索(注意:这里使用scenicId作为数据库名,搜索的是景区内的人脸样本) - SearchFaceRespVo searchResult = faceService.searchFace( - faceBodyAdapter, - String.valueOf(scenicId), - faceUrl, - "大屏端人脸识别" - ); + pcFaceService.matchFaceId(faceId); - // 4. 查询匹配到的图像素材(type=2) + // 4. 自动添加照片到打印列表 + int addedCount = printerService.autoAddPhotosToPreferPrint(faceId); + + // 5. 查询匹配到的图像素材(type=2) List sources = new ArrayList<>(); - if (searchResult != null && searchResult.getSampleListIds() != null && !searchResult.getSampleListIds().isEmpty()) { - // 遍历匹配到的人脸样本ID,查询对应的图像素材 - for (Long sampleId : searchResult.getSampleListIds()) { - SourceEntity source = sourceMapper.getBySampleIdAndType(sampleId, 2); - if (source != null) { - sources.add(source); - } + List memberSourceEntities = memberRelationRepository.listSourceByFaceRelation(faceId, 2); + for (MemberSourceEntity memberSourceEntity : memberSourceEntities) { + SourceEntity source = sourceRepository.getSource(memberSourceEntity.getSourceId()); + if (source != null) { + sources.add(source); } } - // 5. 构造响应 + // 6. 构造响应 FaceRecognizeWithSourcesResp resp = new FaceRecognizeWithSourcesResp(); resp.setUrl(faceUrl); - resp.setFaceId(null); // 不保存人脸,所以没有faceId + resp.setFaceId(faceId); resp.setScenicId(scenicId); resp.setSources(sources); + // 只有当添加了照片时才返回二维码URL + if (addedCount > 0) { + resp.setQrcodeUrl("https://zhentuai.com/printer/v1/tv/face/" + faceId + "/qrcode"); + } else { + resp.setQrcodeUrl(null); + } return ApiResponse.success(resp); } diff --git a/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java b/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java index 9527b7b2..c94c3ab8 100644 --- a/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java +++ b/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java @@ -418,7 +418,7 @@ public class BceFaceBodyAdapter implements IFaceBodyAdapter { ByteArrayOutputStream baos = null; try { // 下载图片 - URL url = new URL(imageUrl.replace("oss-cn-shanghai.aliyuncs.com", "oss-cn-shanghai-internal.aliyuncs.com")); + URL url = new URL(imageUrl); image = ImageIO.read(url); if (image == null) { log.error("无法读取图片,URL: {}", imageUrl); diff --git a/src/main/java/com/ycwl/basic/model/printer/FaceRecognizeWithSourcesResp.java b/src/main/java/com/ycwl/basic/model/printer/FaceRecognizeWithSourcesResp.java index 1a37eceb..1ec72216 100644 --- a/src/main/java/com/ycwl/basic/model/printer/FaceRecognizeWithSourcesResp.java +++ b/src/main/java/com/ycwl/basic/model/printer/FaceRecognizeWithSourcesResp.java @@ -31,4 +31,9 @@ public class FaceRecognizeWithSourcesResp { * 匹配到的图像素材列表(type=2) */ private List sources; + + /** + * 二维码URL + */ + private String qrcodeUrl; } diff --git a/src/main/java/com/ycwl/basic/service/printer/PrinterService.java b/src/main/java/com/ycwl/basic/service/printer/PrinterService.java index e993fcf9..9897c197 100644 --- a/src/main/java/com/ycwl/basic/service/printer/PrinterService.java +++ b/src/main/java/com/ycwl/basic/service/printer/PrinterService.java @@ -61,7 +61,7 @@ public interface PrinterService { FaceRecognizeResp useSample(Long userId, Long sampleId); - void autoAddPhotosToPreferPrint(Long faceId); + int autoAddPhotosToPreferPrint(Long faceId); /** * 查询待审核的打印任务 diff --git a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java index 9303ffd3..1a74ba38 100644 --- a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java @@ -1126,15 +1126,16 @@ public class PrinterServiceImpl implements PrinterService { * 根据景区和设备配置自动添加type=2的照片到用户打印列表 * * @param faceId 人脸ID + * @return 成功添加的照片数量 */ @Override - public void autoAddPhotosToPreferPrint(Long faceId) { + public int autoAddPhotosToPreferPrint(Long faceId) { try { // 1. 获取人脸信息 FaceEntity face = faceRepository.getFace(faceId); if (face == null) { log.warn("人脸不存在,无法自动添加打印: faceId={}", faceId); - return; + return 0; } Long scenicId = face.getScenicId(); @@ -1144,21 +1145,21 @@ public class PrinterServiceImpl implements PrinterService { ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId); if (scenicConfig == null) { log.warn("景区配置不存在,跳过自动添加打印: scenicId={}", scenicId); - return; + return 0; } // 3. 检查景区是否启用打印功能 Boolean printEnable = scenicConfig.getBoolean("print_enable"); if (printEnable == null || !printEnable) { log.debug("景区未启用打印功能,跳过自动添加: scenicId={}", scenicId); - return; + return 0; } // 4. 查询该faceId关联的所有type=2的照片 List imageSources = sourceMapper.listImageSourcesByFaceId(faceId); if (imageSources == null || imageSources.isEmpty()) { log.debug("该人脸没有关联的照片,跳过自动添加: faceId={}", faceId); - return; + return 0; } // 5. 按照deviceId分组处理 @@ -1246,15 +1247,19 @@ public class PrinterServiceImpl implements PrinterService { log.error("等待照片添加任务完成时发生异常: faceId={}", faceId, e); } - if (totalAdded.get() > 0) { - log.info("自动添加打印完成: faceId={}, 成功添加{}张照片", faceId, totalAdded.get()); + int added = totalAdded.get(); + if (added > 0) { + log.info("自动添加打印完成: faceId={}, 成功添加{}张照片", faceId, added); } else { log.debug("自动添加打印完成: faceId={}, 无符合条件的照片", faceId); } + return added; + } catch (Exception e) { // 出现异常则放弃,不影响主流程 log.error("自动添加打印失败,已忽略: faceId={}", faceId, e); + return 0; } } /**