You've already forked FrameTour-BE
Compare commits
17 Commits
2f88699bb0
...
221f0175e6
Author | SHA1 | Date | |
---|---|---|---|
221f0175e6 | |||
cce0b45e70 | |||
f8c7cc2db6 | |||
4b58c03ad2 | |||
8ed38bd229 | |||
ccddab37ea | |||
8c37f2bf2f | |||
89a2e19419 | |||
63c2fdfece | |||
048780071b | |||
c5f7003077 | |||
bf672a8af7 | |||
91e68c3272 | |||
96c56bd8c1 | |||
be2750c162 | |||
b5b2c12a15 | |||
c194c169be |
@@ -13,17 +13,14 @@ import com.ycwl.basic.model.pc.couponRecord.resp.CouponRecordQueryResp;
|
||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
|
||||
import com.ycwl.basic.model.pc.order.entity.OrderItemEntity;
|
||||
import com.ycwl.basic.model.pc.order.req.OrderUpdateReq;
|
||||
import com.ycwl.basic.model.pc.order.resp.OrderAppRespVO;
|
||||
import com.ycwl.basic.model.pc.order.resp.OrderItemVO;
|
||||
import com.ycwl.basic.model.pc.order.resp.OrderRespVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
|
||||
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
|
||||
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
|
||||
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
|
||||
import com.ycwl.basic.model.pc.video.resp.VideoRespVO;
|
||||
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
|
||||
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||
@@ -38,13 +35,11 @@ import com.ycwl.basic.repository.TemplateRepository;
|
||||
import com.ycwl.basic.repository.VideoRepository;
|
||||
import com.ycwl.basic.repository.VideoTaskRepository;
|
||||
import com.ycwl.basic.service.printer.PrinterService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
@@ -121,7 +116,7 @@ public class OrderBiz {
|
||||
ProductItem vlogProductItem = new ProductItem();
|
||||
vlogProductItem.setProductType(ProductType.VLOG_VIDEO);
|
||||
vlogProductItem.setProductId(template.getId().toString());
|
||||
vlogProductItem.setQuantity(videoTaskRepository.getTaskDeviceCount(video.getTaskId()));
|
||||
vlogProductItem.setQuantity(videoTaskRepository.getTaskLensNum(video.getTaskId()));
|
||||
vlogProductItem.setScenicId(scenic.getId().toString());
|
||||
vlogCalculationRequest.setProducts(Collections.singletonList(vlogProductItem));
|
||||
vlogCalculationRequest.setFaceId(priceObj.getFaceId());
|
||||
|
@@ -15,7 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -91,8 +93,8 @@ public class TemplateBiz {
|
||||
}
|
||||
if (minimalPlaceholderFill == null) {
|
||||
// 未开启
|
||||
log.info("模板:{},未配置最小自动生成功能,默认不生成", templateId);
|
||||
return false;
|
||||
log.info("模板:{},未配置最小自动生成功能,默认生成!", templateId);
|
||||
minimalPlaceholderFill = 1;
|
||||
}
|
||||
if (minimalPlaceholderFill <= 0) {
|
||||
return true;
|
||||
@@ -121,4 +123,39 @@ public class TemplateBiz {
|
||||
return count >= minimalPlaceholderFill;
|
||||
}
|
||||
|
||||
public Map<String, List<SourceEntity>> filterTaskParams(Long templateId, Map<String, List<SourceEntity>> allTaskParams) {
|
||||
if (allTaskParams == null || allTaskParams.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
List<String> templatePlaceholders = templateRepository.getTemplatePlaceholder(templateId);
|
||||
if (templatePlaceholders == null || templatePlaceholders.isEmpty()) {
|
||||
log.info("filterTaskParams: templateId:{} has no placeholders", templateId);
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
Map<String, List<SourceEntity>> filteredParams = new HashMap<>();
|
||||
|
||||
for (String placeholder : templatePlaceholders) {
|
||||
if (placeholder.startsWith("P")) {
|
||||
// 图片源:占位符格式为 "P{deviceId}"
|
||||
String imageKey = placeholder;
|
||||
if (allTaskParams.containsKey(imageKey)) {
|
||||
filteredParams.put(imageKey, allTaskParams.get(imageKey));
|
||||
}
|
||||
} else {
|
||||
// 视频源:占位符直接对应设备ID
|
||||
String videoKey = placeholder;
|
||||
if (allTaskParams.containsKey(videoKey)) {
|
||||
filteredParams.put(videoKey, allTaskParams.get(videoKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("filterTaskParams: templateId:{}, original keys:{}, filtered keys:{}",
|
||||
templateId, allTaskParams.keySet().size(), filteredParams.keySet().size());
|
||||
|
||||
return filteredParams;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,4 +4,6 @@ public class FaceConstant {
|
||||
public static final String FACE_DB_NAME_PFX="face:db:";
|
||||
public static final String USER_FACE_DB_NAME="userFace";
|
||||
public static final String FACE_USER_URL_PFX="face:user:url:";
|
||||
public static final String FACE_RECOGNITION_COUNT_PFX="face:recognition:count:";
|
||||
public static final String FACE_LOW_THRESHOLD_PFX="face:low:threshold:";
|
||||
}
|
||||
|
@@ -2,8 +2,11 @@ package com.ycwl.basic.controller.mobile;
|
||||
|
||||
import com.ycwl.basic.model.jwt.JwtInfo;
|
||||
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
|
||||
import com.ycwl.basic.model.mobile.face.FaceStatusResp;
|
||||
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
||||
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
|
||||
import com.ycwl.basic.service.pc.FaceService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.JwtTokenUtil;
|
||||
@@ -20,8 +23,7 @@ import java.util.List;
|
||||
@RestController
|
||||
@RequestMapping("/api/mobile/face/v1")
|
||||
// 用户人脸相关接口
|
||||
public class
|
||||
AppFaceController {
|
||||
public class AppFaceController {
|
||||
|
||||
@Autowired
|
||||
private FaceService faceService;
|
||||
@@ -85,4 +87,25 @@ AppFaceController {
|
||||
faceService.bindFace(faceId, userId);
|
||||
return ApiResponse.success("OK");
|
||||
}
|
||||
|
||||
@GetMapping("/{faceId}/status")
|
||||
public ApiResponse<FaceStatusResp> status(@PathVariable Long faceId) {
|
||||
return ApiResponse.success(faceService.getFaceStatus(faceId));
|
||||
}
|
||||
|
||||
@GetMapping("/{faceId}/extraCheck")
|
||||
public ApiResponse<Boolean> hasExtraCheck(@PathVariable Long faceId) {
|
||||
return ApiResponse.success(faceService.checkHasExtraCheck(faceId));
|
||||
}
|
||||
|
||||
@GetMapping("/{faceId}/queryOtherFace")
|
||||
public ApiResponse<List<FaceSampleEntity>> queryOtherFace(@PathVariable Long faceId) {
|
||||
return ApiResponse.success(faceService.getLowMatchedFaceSamples(faceId));
|
||||
}
|
||||
|
||||
@PostMapping("/{faceId}/queryOtherFace")
|
||||
public ApiResponse<String> queryOtherFace(@PathVariable Long faceId, @RequestBody List<Long> faceIds) {
|
||||
faceService.matchCustomFaceId(faceId, faceIds);
|
||||
return ApiResponse.success("OK");
|
||||
}
|
||||
}
|
||||
|
@@ -99,4 +99,16 @@ public class AppGoodsController {
|
||||
JwtInfo worker = JwtTokenUtil.getWorker();
|
||||
return ApiResponse.success(goodsService.getTaskStatusByTemplateId(faceId, templateId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查视频是否可更新
|
||||
*
|
||||
* @param videoId 视频ID
|
||||
* @return 视频更新检查结果
|
||||
*/
|
||||
@GetMapping("/video/{videoId}/updateCheck")
|
||||
public ApiResponse<VideoUpdateCheckVO> checkVideoUpdate(@PathVariable("videoId") Long videoId) {
|
||||
VideoUpdateCheckVO result = goodsService.checkVideoUpdate(videoId);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,6 @@ import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.mapper.VideoMapper;
|
||||
import com.ycwl.basic.model.pc.order.req.CreateOrderReqVO;
|
||||
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
|
||||
import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
|
||||
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
|
||||
import com.ycwl.basic.model.pc.video.entity.MemberVideoEntity;
|
||||
@@ -29,17 +27,13 @@ import com.ycwl.basic.order.dto.OrderV2PageRequest;
|
||||
import com.ycwl.basic.order.dto.PaymentParamsRequest;
|
||||
import com.ycwl.basic.order.dto.PaymentParamsResponse;
|
||||
import com.ycwl.basic.order.dto.PaymentCallbackResponse;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 移动端订单控制器V2
|
||||
@@ -111,7 +105,7 @@ public class AppOrderV2Controller {
|
||||
case VLOG_VIDEO:
|
||||
List<MemberVideoEntity> videoEntities = videoMapper.listRelationByFaceAndTemplate(face.getId(), Long.valueOf(product.getProductId()));
|
||||
if (videoEntities != null && !videoEntities.isEmpty()) {
|
||||
product.setQuantity(videoTaskRepository.getTaskDeviceCount(videoEntities.getFirst().getTaskId()));
|
||||
product.setQuantity(videoTaskRepository.getTaskLensNum(videoEntities.getFirst().getTaskId()));
|
||||
} else {
|
||||
product.setQuantity(1);
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import com.ycwl.basic.model.mobile.scenic.ScenicAppVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO;
|
||||
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.model.pc.scenic.resp.ScenicConfigResp;
|
||||
@@ -70,26 +70,27 @@ public class AppScenicController {
|
||||
@GetMapping("/{id}/config")
|
||||
@IgnoreToken
|
||||
public ApiResponse<ScenicConfigResp> getConfig(@PathVariable Long id){
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(id);
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(id);
|
||||
ScenicConfigResp resp = new ScenicConfigResp();
|
||||
resp.setBookRoutine(scenicConfig.getBookRoutine());
|
||||
resp.setForceFinishTime(scenicConfig.getForceFinishTime());
|
||||
resp.setTourTime(scenicConfig.getTourTime());
|
||||
resp.setSampleStoreDay(scenicConfig.getSampleStoreDay());
|
||||
resp.setFaceStoreDay(scenicConfig.getFaceStoreDay());
|
||||
resp.setVideoStoreDay(scenicConfig.getVideoStoreDay());
|
||||
resp.setAllFree(scenicConfig.getAllFree());
|
||||
resp.setDisableSourceVideo(scenicConfig.getDisableSourceVideo());
|
||||
resp.setDisableSourceImage(scenicConfig.getDisableSourceImage());
|
||||
resp.setAntiScreenRecordType(scenicConfig.getAntiScreenRecordType());
|
||||
resp.setVideoSourceStoreDay(scenicConfig.getVideoSourceStoreDay());
|
||||
resp.setImageSourceStoreDay(scenicConfig.getImageSourceStoreDay());
|
||||
resp.setUserSourceExpireDay(scenicConfig.getUserSourceExpireDay());
|
||||
resp.setBrokerDirectRate(scenicConfig.getBrokerDirectRate());
|
||||
resp.setVideoSourcePackHint(scenicConfig.getVideoSourcePackHint());
|
||||
resp.setImageSourcePackHint(scenicConfig.getImageSourcePackHint());
|
||||
resp.setVoucherEnable(scenicConfig.getVoucherEnable());
|
||||
resp.setEnableVoucher(scenicConfig.getVoucherEnable()); // compactible
|
||||
resp.setBookRoutine(scenicConfig.getInteger("book_routine"));
|
||||
resp.setForceFinishTime(scenicConfig.getInteger("force_finish_time"));
|
||||
resp.setTourTime(scenicConfig.getInteger("tour_time"));
|
||||
resp.setSampleStoreDay(scenicConfig.getInteger("sample_store_day"));
|
||||
resp.setFaceStoreDay(scenicConfig.getInteger("face_store_day"));
|
||||
resp.setVideoStoreDay(scenicConfig.getInteger("video_store_day"));
|
||||
resp.setAllFree(scenicConfig.getBoolean("all_free"));
|
||||
resp.setDisableSourceVideo(scenicConfig.getBoolean("disable_source_video"));
|
||||
resp.setDisableSourceImage(scenicConfig.getBoolean("disable_source_image"));
|
||||
resp.setAntiScreenRecordType(scenicConfig.getInteger("anti_screen_record_type"));
|
||||
resp.setVideoSourceStoreDay(scenicConfig.getInteger("video_source_store_day"));
|
||||
resp.setImageSourceStoreDay(scenicConfig.getInteger("image_source_store_day"));
|
||||
resp.setUserSourceExpireDay(scenicConfig.getInteger("user_source_expire_day"));
|
||||
resp.setBrokerDirectRate(scenicConfig.getBigDecimal("broker_direct_rate"));
|
||||
resp.setVideoSourcePackHint(scenicConfig.getString("video_source_pack_hint"));
|
||||
resp.setImageSourcePackHint(scenicConfig.getString("image_source_pack_hint"));
|
||||
resp.setVoucherEnable(scenicConfig.getBoolean("voucher_enable"));
|
||||
resp.setEnableVoucher(scenicConfig.getBoolean("voucher_enable")); // compactible
|
||||
resp.setGroupingEnable(scenicConfig.getBoolean("grouping_enable"));
|
||||
return ApiResponse.success(resp);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,102 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.ycwl.basic.model.pc.project.entity.ProjectEntity;
|
||||
import com.ycwl.basic.model.pc.project.req.ProjectReqQuery;
|
||||
import com.ycwl.basic.model.pc.project.resp.ProjectRespVO;
|
||||
import com.ycwl.basic.service.pc.ProjectService;
|
||||
import com.ycwl.basic.storage.enums.StorageAcl;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.WxMpUtil;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.storage.StorageFactory;
|
||||
import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 景区项目管理控制器
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/project/v1")
|
||||
public class ProjectController {
|
||||
|
||||
@Autowired
|
||||
private ProjectService projectService;
|
||||
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
|
||||
// 分页查询
|
||||
@PostMapping("/page")
|
||||
public ApiResponse page(@RequestBody ProjectReqQuery projectReqQuery) {
|
||||
return ApiResponse.success(projectService.pageQuery(projectReqQuery));
|
||||
}
|
||||
|
||||
// 列表查询
|
||||
@PostMapping("/list")
|
||||
public ApiResponse list(@RequestBody ProjectReqQuery projectReqQuery) {
|
||||
return ApiResponse.success(projectService.list(projectReqQuery));
|
||||
}
|
||||
|
||||
// 详情查询
|
||||
@GetMapping("/getDetails/{id}")
|
||||
public ApiResponse getDetails(@PathVariable("id") Long id) {
|
||||
return ApiResponse.success(projectService.getById(id));
|
||||
}
|
||||
|
||||
// 新增或修改
|
||||
@PostMapping("/addOrUpdate")
|
||||
public ApiResponse addOrUpdate(@RequestBody ProjectEntity project) {
|
||||
return ApiResponse.success(projectService.addOrUpdate(project));
|
||||
}
|
||||
|
||||
// 删除
|
||||
@DeleteMapping("/delete/{id}")
|
||||
public ApiResponse delete(@PathVariable("id") Long id) {
|
||||
return ApiResponse.success(projectService.delete(id));
|
||||
}
|
||||
|
||||
// 修改状态
|
||||
@PutMapping("/updateStatus/{id}")
|
||||
public ApiResponse updateStatus(@PathVariable("id") Long id) {
|
||||
return ApiResponse.success(projectService.updateStatus(id));
|
||||
}
|
||||
|
||||
// 根据项目ID下载小程序二维码
|
||||
@GetMapping("/{id}/QRCode")
|
||||
public ApiResponse<String> downloadQrCode(@PathVariable Long id) {
|
||||
ProjectRespVO project = projectService.getById(id);
|
||||
if (project == null) {
|
||||
return ApiResponse.fail("项目不存在");
|
||||
}
|
||||
MpConfigEntity mpConfig = scenicRepository.getScenicMpConfig(project.getScenicId());
|
||||
if (mpConfig == null) {
|
||||
return ApiResponse.fail("小程序配置不存在");
|
||||
}
|
||||
String appId = mpConfig.getAppId();
|
||||
String appSecret = mpConfig.getAppSecret();
|
||||
String appState = mpConfig.getState();
|
||||
String path = "pages/home/index?scenicId=" + project.getScenicId() + "&projectId=" + id;
|
||||
String filePath = "qr_code_project_" + id + ".jpg";
|
||||
IStorageAdapter adapter = StorageFactory.use();
|
||||
if (adapter.isExists(filePath)) {
|
||||
return ApiResponse.success(adapter.getUrl(filePath));
|
||||
}
|
||||
try {
|
||||
WxMpUtil.generateWXAQRCode(appId, appSecret, appState, path, filePath);
|
||||
File file = new File(filePath);
|
||||
String s = adapter.uploadFile(null, file, filePath);
|
||||
file.delete();
|
||||
adapter.setAcl(StorageAcl.PUBLIC_READ, filePath);
|
||||
return ApiResponse.success(s);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.fail("生成二维码失败");
|
||||
}
|
||||
}
|
||||
}
|
48
src/main/java/com/ycwl/basic/mapper/ProjectMapper.java
Normal file
48
src/main/java/com/ycwl/basic/mapper/ProjectMapper.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.ycwl.basic.mapper;
|
||||
|
||||
import com.ycwl.basic.model.pc.project.entity.ProjectEntity;
|
||||
import com.ycwl.basic.model.pc.project.req.ProjectReqQuery;
|
||||
import com.ycwl.basic.model.pc.project.resp.ProjectRespVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 景区项目管理Mapper
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProjectMapper {
|
||||
|
||||
/**
|
||||
* 分页查询项目列表
|
||||
*/
|
||||
List<ProjectRespVO> list(ProjectReqQuery projectReqQuery);
|
||||
|
||||
/**
|
||||
* 根据ID查询项目详情
|
||||
*/
|
||||
ProjectRespVO getById(Long id);
|
||||
|
||||
/**
|
||||
* 新增项目
|
||||
*/
|
||||
int add(ProjectEntity project);
|
||||
|
||||
/**
|
||||
* 根据ID删除项目
|
||||
*/
|
||||
int deleteById(Long id);
|
||||
|
||||
/**
|
||||
* 更新项目信息
|
||||
*/
|
||||
int update(ProjectEntity project);
|
||||
|
||||
/**
|
||||
* 更新项目状态
|
||||
*/
|
||||
int updateStatus(Long id);
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package com.ycwl.basic.model.mobile.face;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 人脸状态响应对象
|
||||
*
|
||||
* @author Claude Code
|
||||
*/
|
||||
@Data
|
||||
public class FaceStatusResp {
|
||||
/**
|
||||
* 人脸ID
|
||||
*/
|
||||
private Long faceId;
|
||||
private String faceUrl;
|
||||
|
||||
/**
|
||||
* 识别次数,0表示未识别过
|
||||
*/
|
||||
private Long recognitionCount;
|
||||
|
||||
/**
|
||||
* 是否触发过低阈值检测
|
||||
*/
|
||||
private Boolean hasLowThreshold;
|
||||
private String displayText;
|
||||
private boolean step1Status;
|
||||
// private String step1Text;
|
||||
private boolean step2Status;
|
||||
// private String step2Text;
|
||||
private boolean step3Status;
|
||||
// private String step3Text;
|
||||
}
|
@@ -30,6 +30,7 @@ public class VideoGoodsDetailVO {
|
||||
private Integer sourceType;
|
||||
// 商品id goodsType=1时为videoId,goodsType=2时为sourceId
|
||||
private Long goodsId;
|
||||
private Long templateId;
|
||||
// 模版封面图片
|
||||
private String templateCoverUrl;
|
||||
// 图片文件存储地址
|
||||
@@ -50,6 +51,7 @@ public class VideoGoodsDetailVO {
|
||||
// 是否已购买 0否 1是
|
||||
private Integer isBuy;
|
||||
// 镜头数
|
||||
private Integer devicesNum;
|
||||
private Integer lensNum;
|
||||
private Long faceId;
|
||||
private boolean share = false;
|
||||
|
@@ -17,6 +17,7 @@ public class IsBuyBatchRespVO {
|
||||
private BigDecimal origPrice = BigDecimal.ZERO;
|
||||
private Integer couponId;
|
||||
private Integer couponRecordId;
|
||||
private Integer countdown = 3600;
|
||||
private BigDecimal couponPrice = BigDecimal.ZERO;
|
||||
private BigDecimal slashPrice;
|
||||
|
||||
|
@@ -13,6 +13,7 @@ import java.math.BigDecimal;
|
||||
public class ContentPageVO {
|
||||
// 内容名称
|
||||
private String name;
|
||||
private String group;
|
||||
// 景区id
|
||||
private Long scenicId;
|
||||
// 景区名称
|
||||
|
@@ -0,0 +1,49 @@
|
||||
package com.ycwl.basic.model.pc.project.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 景区项目管理实体类
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@TableName("project")
|
||||
public class ProjectEntity {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 最少游玩时间(分钟)
|
||||
*/
|
||||
private Integer minPlayTime;
|
||||
|
||||
/**
|
||||
* 最长游玩时间(分钟)
|
||||
*/
|
||||
private Integer maxPlayTime;
|
||||
|
||||
/**
|
||||
* 状态,0禁用,1启用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private Date createAt;
|
||||
private Date updateAt;
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.ycwl.basic.model.pc.project.req;
|
||||
|
||||
import com.ycwl.basic.model.common.BaseQueryParameterReq;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 项目查询请求参数
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectReqQuery extends BaseQueryParameterReq {
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 项目名称(模糊查询)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 状态,0禁用,1启用
|
||||
*/
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.ycwl.basic.model.pc.project.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目响应数据
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
public class ProjectRespVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 景区名称
|
||||
*/
|
||||
private String scenicName;
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 最少游玩时间(分钟)
|
||||
*/
|
||||
private Integer minPlayTime;
|
||||
|
||||
/**
|
||||
* 最长游玩时间(分钟)
|
||||
*/
|
||||
private Integer maxPlayTime;
|
||||
|
||||
/**
|
||||
* 状态,0禁用,1启用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private Date createAt;
|
||||
private Date updateAt;
|
||||
}
|
@@ -47,4 +47,5 @@ public class ScenicConfigResp {
|
||||
private String videoSourcePackHint = "";
|
||||
private Boolean voucherEnable;
|
||||
private Boolean enableVoucher;
|
||||
private Boolean groupingEnable;
|
||||
}
|
||||
|
@@ -10,4 +10,5 @@ public class SearchFaceRespVo {
|
||||
private List<Long> sampleListIds;
|
||||
private String searchResultJson;
|
||||
private Float firstMatchRate;
|
||||
private boolean lowThreshold;
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
|
||||
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.pricing.dto.VoucherInfo;
|
||||
import com.ycwl.basic.pricing.enums.VoucherDiscountType;
|
||||
import com.ycwl.basic.pricing.service.IVoucherService;
|
||||
@@ -13,7 +14,9 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -26,6 +29,10 @@ public class SourceRepository {
|
||||
private IVoucherService iVoucherService;
|
||||
@Autowired
|
||||
private FaceRepository faceRepository;
|
||||
@Autowired
|
||||
private TemplateRepository templateRepository;
|
||||
@Autowired
|
||||
private DeviceRepository deviceRepository;
|
||||
|
||||
public void addSource(SourceEntity source) {
|
||||
sourceMapper.add(source);
|
||||
@@ -86,4 +93,65 @@ public class SourceRepository {
|
||||
public SourceEntity getSource(Long id) {
|
||||
return sourceMapper.getEntity(id);
|
||||
}
|
||||
|
||||
public Map<String, List<SourceEntity>> getTaskParams(Long faceId, Long templateId) {
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
log.info("getTaskParams: faceId:{} is not exist", faceId);
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
List<SourceEntity> videoSourceList = sourceMapper.listVideoByScenicFaceRelation(face.getScenicId(), faceId);
|
||||
Map<String, List<SourceEntity>> sourcesMap = videoSourceList.stream()
|
||||
.peek(item -> item.setUrl(item.getVideoUrl()))
|
||||
.filter(item -> item.getDeviceId() != null)
|
||||
.filter(item -> {
|
||||
DeviceEntity device = deviceRepository.getDevice(item.getDeviceId());
|
||||
if (device == null) {
|
||||
log.info("getTaskParams: deviceId:{} is not exist", item.getDeviceId());
|
||||
return false;
|
||||
}
|
||||
return Integer.valueOf(1).equals(device.getStatus());
|
||||
})
|
||||
.collect(Collectors.groupingBy(item -> item.getDeviceId().toString()));
|
||||
|
||||
if (sourcesMap.isEmpty()) {
|
||||
log.info("getTaskParams: 没有视频源,templateId: {}", templateId);
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
List<String> templatePlaceholder = templateRepository.getTemplatePlaceholder(templateId);
|
||||
if (templatePlaceholder.stream().distinct().count() == templatePlaceholder.size()) {
|
||||
sourcesMap.forEach((key, value) -> {
|
||||
value.removeIf(item -> !value.getFirst().equals(item));
|
||||
});
|
||||
}
|
||||
|
||||
boolean hasPPlaceholder = templatePlaceholder.stream().anyMatch(placeholder -> placeholder.startsWith("P"));
|
||||
if (hasPPlaceholder) {
|
||||
List<SourceEntity> imageSourceList = sourceMapper.listImageSourcesByFaceId(faceId);
|
||||
if (!imageSourceList.isEmpty()) {
|
||||
Map<String, List<SourceEntity>> imageSourceMap = imageSourceList.stream()
|
||||
.peek(item -> item.setUrl(item.getUrl()))
|
||||
.filter(item -> item.getDeviceId() != null)
|
||||
.filter(item -> {
|
||||
DeviceEntity device = deviceRepository.getDevice(item.getDeviceId());
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
return Integer.valueOf(1).equals(device.getStatus());
|
||||
})
|
||||
.collect(Collectors.groupingBy(item -> "P" + item.getDeviceId().toString()));
|
||||
|
||||
imageSourceMap.forEach((key, value) -> {
|
||||
sourcesMap.merge(key, value, (existing, replacement) -> {
|
||||
existing.addAll(replacement);
|
||||
return existing;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sourcesMap;
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package com.ycwl.basic.repository;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.mapper.TaskMapper;
|
||||
import com.ycwl.basic.mapper.VideoMapper;
|
||||
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
|
||||
import com.ycwl.basic.model.pc.task.resp.TaskRespVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -11,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -68,7 +68,35 @@ public class VideoTaskRepository {
|
||||
return shotTime;
|
||||
}
|
||||
|
||||
public Integer getTaskDeviceCount(Long taskId) {
|
||||
public Integer getTaskDeviceNum(Long taskId) {
|
||||
TaskEntity task = getTaskById(taskId);
|
||||
if (task == null) {
|
||||
return 1;
|
||||
}
|
||||
Map<String, Object> paramJson = JacksonUtil.parseObject(task.getTaskParams(), Map.class);
|
||||
if (paramJson == null) {
|
||||
return 1;
|
||||
}
|
||||
List<String> deviceIds = new ArrayList<>();
|
||||
List<String> templatePlaceholder = templateRepository.getTemplatePlaceholder(task.getTemplateId());
|
||||
paramJson.entrySet().stream()
|
||||
.filter(entry -> StringUtils.isNumeric(entry.getKey()))
|
||||
.forEach(entry -> {
|
||||
List<Object> jsonArray = JacksonUtil.parseArray(JacksonUtil.toJSONString(entry.getValue()), Object.class);
|
||||
if (jsonArray != null && !jsonArray.isEmpty()) {
|
||||
for (Object ignored : jsonArray) {
|
||||
if (templatePlaceholder.contains(entry.getKey())) {
|
||||
if (!deviceIds.contains(entry.getKey())) {
|
||||
deviceIds.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return deviceIds.size();
|
||||
}
|
||||
|
||||
public Integer getTaskLensNum(Long taskId) {
|
||||
TaskEntity task = getTaskById(taskId);
|
||||
if (task == null) {
|
||||
return 1;
|
||||
|
@@ -50,4 +50,11 @@ public interface GoodsService {
|
||||
List<GoodsUrlVO> sourceGoodsListDownload(GoodsReqQuery query);
|
||||
|
||||
Integer sourceGoodsCount(GoodsReqQuery query);
|
||||
|
||||
/**
|
||||
* 检查视频是否可更新
|
||||
* @param videoId 视频ID
|
||||
* @return 视频更新检查结果
|
||||
*/
|
||||
VideoUpdateCheckVO checkVideoUpdate(Long videoId);
|
||||
}
|
||||
|
@@ -1,10 +1,8 @@
|
||||
package com.ycwl.basic.service.mobile.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.extra.qrcode.QrCodeUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.biz.CouponBiz;
|
||||
import com.ycwl.basic.biz.OrderBiz;
|
||||
@@ -33,15 +31,19 @@ import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
|
||||
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
|
||||
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
|
||||
import com.ycwl.basic.model.pc.video.entity.MemberVideoEntity;
|
||||
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
|
||||
import com.ycwl.basic.model.pc.video.req.VideoReqQuery;
|
||||
import com.ycwl.basic.model.pc.video.resp.VideoRespVO;
|
||||
import com.ycwl.basic.repository.DeviceRepository;
|
||||
import com.ycwl.basic.repository.FaceRepository;
|
||||
import com.ycwl.basic.repository.OrderRepository;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.repository.VideoRepository;
|
||||
import com.ycwl.basic.repository.VideoTaskRepository;
|
||||
import com.ycwl.basic.service.mobile.GoodsService;
|
||||
import com.ycwl.basic.repository.TemplateRepository;
|
||||
import com.ycwl.basic.repository.SourceRepository;
|
||||
import com.ycwl.basic.biz.TemplateBiz;
|
||||
import com.ycwl.basic.config.VideoUpdateConfig;
|
||||
import com.ycwl.basic.service.task.TaskService;
|
||||
import com.ycwl.basic.storage.StorageFactory;
|
||||
import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
@@ -54,15 +56,12 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -81,6 +80,8 @@ public class GoodsServiceImpl implements GoodsService {
|
||||
@Autowired
|
||||
private TemplateRepository templateRepository;
|
||||
@Autowired
|
||||
private VideoRepository videoRepository;
|
||||
@Autowired
|
||||
private VideoTaskRepository videoTaskRepository;
|
||||
@Autowired
|
||||
private TaskService taskTaskService;
|
||||
@@ -96,6 +97,12 @@ public class GoodsServiceImpl implements GoodsService {
|
||||
private DeviceRepository deviceRepository;
|
||||
@Autowired
|
||||
private CouponBiz couponBiz;
|
||||
@Autowired
|
||||
private SourceRepository sourceRepository;
|
||||
@Autowired
|
||||
private TemplateBiz templateBiz;
|
||||
@Autowired
|
||||
private VideoUpdateConfig videoUpdateConfig;
|
||||
|
||||
public ApiResponse<List<GoodsPageVO>> goodsList(GoodsReqQuery query) {
|
||||
Long scenicId = query.getScenicId();
|
||||
@@ -266,6 +273,7 @@ public class GoodsServiceImpl implements GoodsService {
|
||||
goodsDetailVO.setGoodsType(0);
|
||||
goodsDetailVO.setGoodsId(videoRespVO.getId());
|
||||
goodsDetailVO.setVideoUrl(videoRespVO.getVideoUrl());
|
||||
goodsDetailVO.setTemplateId(videoRespVO.getTemplateId());
|
||||
goodsDetailVO.setTemplateCoverUrl(videoRespVO.getTemplateCoverUrl());
|
||||
goodsDetailVO.setCreateTime(videoRespVO.getCreateTime());
|
||||
goodsDetailVO.setHeight(videoRespVO.getHeight());
|
||||
@@ -308,7 +316,8 @@ public class GoodsServiceImpl implements GoodsService {
|
||||
return ApiResponse.fail("该vlog不存在或已失效");
|
||||
}
|
||||
goodsDetailVO.setShotTime(taskTaskService.getTaskShotDate(task.getId()));
|
||||
goodsDetailVO.setLensNum(videoTaskRepository.getTaskDeviceCount(task.getId()));
|
||||
goodsDetailVO.setLensNum(videoTaskRepository.getTaskLensNum(task.getId()));
|
||||
goodsDetailVO.setDevicesNum(videoTaskRepository.getTaskDeviceNum(task.getId()));
|
||||
CouponRecordQueryResp couponRecord = couponBiz.queryUserCouponRecord(task.getScenicId(), userId, task.getFaceId(), task.getTemplateId().toString());
|
||||
if (couponRecord != null) {
|
||||
if (couponRecord.isUsable()) {
|
||||
@@ -772,4 +781,147 @@ public class GoodsServiceImpl implements GoodsService {
|
||||
sourceReqQuery.setFaceId(query.getFaceId());
|
||||
return sourceMapper.countUser(sourceReqQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoUpdateCheckVO checkVideoUpdate(Long videoId) {
|
||||
VideoUpdateCheckVO result = new VideoUpdateCheckVO();
|
||||
result.setVideoId(videoId);
|
||||
|
||||
if (!videoUpdateConfig.isEnabled()) {
|
||||
log.info("视频更新检查功能已禁用");
|
||||
result.setCanUpdate(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
VideoEntity video = videoRepository.getVideo(videoId);
|
||||
if (video == null) {
|
||||
log.error("视频不存在: videoId={}", videoId);
|
||||
result.setCanUpdate(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
Long taskId = video.getTaskId();
|
||||
TaskEntity task = videoTaskRepository.getTaskById(taskId);
|
||||
if (task == null) {
|
||||
log.error("关联任务不存在: videoId={}, taskId={}", videoId, taskId);
|
||||
result.setCanUpdate(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
result.setTaskId(taskId);
|
||||
|
||||
result.setFaceId(task.getFaceId());
|
||||
result.setTemplateId(task.getTemplateId());
|
||||
|
||||
try {
|
||||
Map<String, Object> originalTaskParams = JacksonUtil.parseObject(task.getTaskParams(), Map.class);
|
||||
if (originalTaskParams == null) {
|
||||
log.error("原始任务参数解析失败: taskId={}", taskId);
|
||||
result.setCanUpdate(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
int originalSegmentCount = calculateSegmentCount(originalTaskParams);
|
||||
result.setOriginalSegmentCount(originalSegmentCount);
|
||||
|
||||
Map<String, List<SourceEntity>> currentTaskParams = sourceRepository.getTaskParams(task.getFaceId(), task.getTemplateId());
|
||||
if (currentTaskParams.isEmpty()) {
|
||||
log.info("当前没有可用的任务参数: faceId={}, templateId={}", task.getFaceId(), task.getTemplateId());
|
||||
result.setCanUpdate(false);
|
||||
result.setTotalSegmentCount(0);
|
||||
result.setNewSegmentCount(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
Map<String, List<SourceEntity>> filteredTaskParams = templateBiz.filterTaskParams(task.getTemplateId(), currentTaskParams);
|
||||
int currentSegmentCount = calculateSegmentCount(filteredTaskParams);
|
||||
result.setTotalSegmentCount(currentSegmentCount);
|
||||
|
||||
boolean hasNewSegments = videoUpdateConfig.isDetectChangesAsNew()
|
||||
? hasNewSegments(originalTaskParams, filteredTaskParams)
|
||||
: currentSegmentCount > originalSegmentCount;
|
||||
int newSegmentCount = Math.max(0, currentSegmentCount - originalSegmentCount);
|
||||
|
||||
boolean canUpdate = hasNewSegments && newSegmentCount >= videoUpdateConfig.getMinNewSegmentCount();
|
||||
result.setCanUpdate(canUpdate);
|
||||
result.setNewSegmentCount(newSegmentCount);
|
||||
|
||||
log.info("视频更新检查完成: videoId={}, taskId={}, canUpdate={}, newSegmentCount={}, originalCount={}, currentCount={}",
|
||||
videoId, taskId, result.isCanUpdate(), newSegmentCount, originalSegmentCount, currentSegmentCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("检查视频更新失败: videoId={}, taskId={}", videoId, taskId, e);
|
||||
result.setCanUpdate(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int calculateSegmentCount(Map<String, ?> taskParams) {
|
||||
if (taskParams == null || taskParams.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int totalCount = 0;
|
||||
for (Map.Entry<String, ?> entry : taskParams.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof List) {
|
||||
totalCount += ((List<?>) value).size();
|
||||
} else if (value instanceof String && StringUtils.isNumeric(entry.getKey())) {
|
||||
try {
|
||||
List<?> jsonArray = JacksonUtil.parseArray((String) value, Object.class);
|
||||
if (jsonArray != null) {
|
||||
totalCount += jsonArray.size();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析任务参数失败: key={}, value={}", entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
private boolean hasNewSegments(Map<String, ?> originalParams, Map<String, List<SourceEntity>> currentParams) {
|
||||
if (currentParams == null || currentParams.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (originalParams == null || originalParams.isEmpty()) {
|
||||
return !currentParams.isEmpty();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<SourceEntity>> entry : currentParams.entrySet()) {
|
||||
String deviceKey = entry.getKey();
|
||||
List<SourceEntity> currentSources = entry.getValue();
|
||||
|
||||
if (!originalParams.containsKey(deviceKey)) {
|
||||
if (currentSources != null && !currentSources.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Object originalValue = originalParams.get(deviceKey);
|
||||
int originalCount = 0;
|
||||
|
||||
if (originalValue instanceof List) {
|
||||
originalCount = ((List<?>) originalValue).size();
|
||||
} else if (originalValue instanceof String) {
|
||||
try {
|
||||
List<?> jsonArray = JacksonUtil.parseArray((String) originalValue, Object.class);
|
||||
if (jsonArray != null) {
|
||||
originalCount = jsonArray.size();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析原始参数失败: key={}, value={}", deviceKey, originalValue);
|
||||
}
|
||||
}
|
||||
|
||||
int currentCount = currentSources != null ? currentSources.size() : 0;
|
||||
if (currentCount > originalCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -2,10 +2,13 @@ package com.ycwl.basic.service.pc;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
|
||||
import com.ycwl.basic.model.mobile.face.FaceStatusResp;
|
||||
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||
import com.ycwl.basic.model.pc.face.req.FaceReqQuery;
|
||||
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
|
||||
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -40,4 +43,12 @@ public interface FaceService {
|
||||
void bindFace(Long faceId, Long memberId);
|
||||
|
||||
String bindWxaCode(Long faceId);
|
||||
|
||||
FaceStatusResp getFaceStatus(Long faceId);
|
||||
|
||||
Boolean checkHasExtraCheck(Long faceId);
|
||||
|
||||
List<FaceSampleEntity> getLowMatchedFaceSamples(Long faceId);
|
||||
|
||||
void matchCustomFaceId(Long faceId, List<Long> faceSampleIds);
|
||||
}
|
||||
|
47
src/main/java/com/ycwl/basic/service/pc/ProjectService.java
Normal file
47
src/main/java/com/ycwl/basic/service/pc/ProjectService.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.ycwl.basic.service.pc;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.pc.project.entity.ProjectEntity;
|
||||
import com.ycwl.basic.model.pc.project.req.ProjectReqQuery;
|
||||
import com.ycwl.basic.model.pc.project.resp.ProjectRespVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 景区项目管理服务接口
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
public interface ProjectService {
|
||||
|
||||
/**
|
||||
* 分页查询项目列表
|
||||
*/
|
||||
PageInfo<ProjectRespVO> pageQuery(ProjectReqQuery projectReqQuery);
|
||||
|
||||
/**
|
||||
* 查询项目列表
|
||||
*/
|
||||
List<ProjectRespVO> list(ProjectReqQuery projectReqQuery);
|
||||
|
||||
/**
|
||||
* 根据ID查询项目详情
|
||||
*/
|
||||
ProjectRespVO getById(Long id);
|
||||
|
||||
/**
|
||||
* 新增或修改项目
|
||||
*/
|
||||
int addOrUpdate(ProjectEntity project);
|
||||
|
||||
/**
|
||||
* 删除项目
|
||||
*/
|
||||
int delete(Long id);
|
||||
|
||||
/**
|
||||
* 更新项目状态
|
||||
*/
|
||||
int updateStatus(Long id);
|
||||
}
|
@@ -9,7 +9,9 @@ import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.enums.StatisticEnum;
|
||||
import com.ycwl.basic.exception.BaseException;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.facebody.entity.SearchFaceResultItem;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.mapper.FaceSampleMapper;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.mapper.StatisticsMapper;
|
||||
import com.ycwl.basic.mapper.FaceMapper;
|
||||
@@ -17,6 +19,8 @@ import com.ycwl.basic.mapper.TemplateMapper;
|
||||
import com.ycwl.basic.mapper.VideoMapper;
|
||||
import com.ycwl.basic.mapper.OrderMapper;
|
||||
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
|
||||
import com.ycwl.basic.model.mobile.face.FaceStatusResp;
|
||||
import com.ycwl.basic.model.mobile.goods.VideoTaskStatusVO;
|
||||
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;
|
||||
@@ -25,6 +29,7 @@ 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;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
|
||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
|
||||
@@ -41,6 +46,7 @@ import com.ycwl.basic.repository.FaceRepository;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.repository.VideoRepository;
|
||||
import com.ycwl.basic.repository.VideoTaskRepository;
|
||||
import com.ycwl.basic.service.mobile.GoodsService;
|
||||
import com.ycwl.basic.service.pc.FaceService;
|
||||
import com.ycwl.basic.service.pc.ScenicService;
|
||||
import com.ycwl.basic.service.task.TaskFaceService;
|
||||
@@ -52,7 +58,9 @@ 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.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -61,12 +69,17 @@ import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.ycwl.basic.constant.FaceConstant.FACE_LOW_THRESHOLD_PFX;
|
||||
import static com.ycwl.basic.constant.FaceConstant.FACE_RECOGNITION_COUNT_PFX;
|
||||
import static com.ycwl.basic.constant.FaceConstant.USER_FACE_DB_NAME;
|
||||
import static com.ycwl.basic.constant.StorageConstant.USER_FACE;
|
||||
|
||||
@@ -110,6 +123,12 @@ public class FaceServiceImpl implements FaceService {
|
||||
private DeviceRepository deviceRepository;
|
||||
@Autowired
|
||||
private OrderMapper orderMapper;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private FaceSampleMapper faceSampleMapper;
|
||||
@Autowired
|
||||
private GoodsService goodsService;
|
||||
|
||||
@Override
|
||||
public ApiResponse<PageInfo<FaceRespVO>> pageQuery(FaceReqQuery faceReqQuery) {
|
||||
@@ -251,6 +270,9 @@ public class FaceServiceImpl implements FaceService {
|
||||
|
||||
log.debug("开始人脸匹配:faceId={}, isNew={}", faceId, isNew);
|
||||
|
||||
// 记录识别次数到Redis,设置2天过期时间
|
||||
recordFaceRecognitionCount(faceId);
|
||||
|
||||
try {
|
||||
// 1. 数据准备:获取人脸信息、景区配置、适配器等
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
@@ -325,6 +347,12 @@ public class FaceServiceImpl implements FaceService {
|
||||
}
|
||||
} else {
|
||||
log.warn("人脸匹配无结果:faceId={}", faceId);
|
||||
|
||||
// 检查低阈值检测结果,如果为true则记录该人脸ID到Redis
|
||||
if (scenicDbSearchResult != null && scenicDbSearchResult.isLowThreshold()) {
|
||||
recordLowThresholdFace(faceId);
|
||||
log.debug("触发低阈值检测,记录faceId: {}", faceId);
|
||||
}
|
||||
}
|
||||
|
||||
return scenicDbSearchResult;
|
||||
@@ -683,6 +711,8 @@ public class FaceServiceImpl implements FaceService {
|
||||
sourceImageContent.setContentType(2);
|
||||
sourceVideoContent.setLockType(-1);
|
||||
sourceImageContent.setLockType(-1);
|
||||
sourceVideoContent.setGroup("直出原片");
|
||||
sourceImageContent.setGroup("直出原片");
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(faceRespVO.getScenicId());
|
||||
if (!Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_image"))) {
|
||||
IsBuyRespVO isBuyRespVO = orderBiz.isBuy(userId, faceRespVO.getScenicId(), 2, faceId);
|
||||
@@ -800,4 +830,321 @@ public class FaceServiceImpl implements FaceService {
|
||||
throw new BaseException("生成二维码失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaceStatusResp getFaceStatus(Long faceId) {
|
||||
if (faceId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FaceStatusResp statusResp = new FaceStatusResp();
|
||||
statusResp.setFaceId(faceId);
|
||||
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
statusResp.setStep1Status(false);
|
||||
statusResp.setDisplayText("请重新录入人脸");
|
||||
return statusResp;
|
||||
}
|
||||
statusResp.setStep1Status(true);
|
||||
|
||||
statusResp.setFaceUrl(face.getFaceUrl());
|
||||
|
||||
// 查询识别次数
|
||||
String countKey = FACE_RECOGNITION_COUNT_PFX + faceId;
|
||||
String countStr = redisTemplate.opsForValue().get(countKey);
|
||||
long recognitionCount = 0L;
|
||||
if (countStr != null) {
|
||||
try {
|
||||
recognitionCount = Long.parseLong(countStr);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("识别次数解析失败,faceId={}, count={}", faceId, countStr);
|
||||
}
|
||||
}
|
||||
statusResp.setRecognitionCount(recognitionCount);
|
||||
|
||||
// 查询是否触发过低阈值检测
|
||||
String lowThresholdKey = FACE_LOW_THRESHOLD_PFX + faceId;
|
||||
Boolean hasLowThreshold = redisTemplate.hasKey(lowThresholdKey);
|
||||
statusResp.setHasLowThreshold(hasLowThreshold);
|
||||
|
||||
log.debug("查询人脸状态:faceId={}, recognitionCount={}, hasLowThreshold={}",
|
||||
faceId, recognitionCount, hasLowThreshold);
|
||||
|
||||
SourceReqQuery sourceReqQuery = new SourceReqQuery();
|
||||
sourceReqQuery.setMemberId(face.getMemberId());
|
||||
sourceReqQuery.setFaceId(faceId);
|
||||
sourceReqQuery.setType(2);
|
||||
Integer countUser = sourceMapper.countUser(sourceReqQuery);
|
||||
if (countUser != null && countUser > 0) {
|
||||
statusResp.setStep2Status(true);
|
||||
} else {
|
||||
statusResp.setStep2Status(false);
|
||||
statusResp.setDisplayText("Hey,快去智能机位打卡吧");
|
||||
}
|
||||
VideoTaskStatusVO taskStatusByFaceId = goodsService.getTaskStatusByFaceId(faceId);
|
||||
if (Integer.valueOf(1).equals(taskStatusByFaceId.getStatus())) {
|
||||
if (taskStatusByFaceId.getCount() > 0) {
|
||||
statusResp.setStep3Status(true);
|
||||
statusResp.setDisplayText("帧途AI已为您渲染"+ taskStatusByFaceId.getCount() +"个vlog");
|
||||
} else {
|
||||
statusResp.setStep3Status(false);
|
||||
statusResp.setDisplayText("帧途AI将会为您渲染vlog,请稍候");
|
||||
}
|
||||
} else {
|
||||
statusResp.setStep3Status(false);
|
||||
statusResp.setDisplayText("帧途AI正在为您渲染vlog,请稍候");
|
||||
}
|
||||
return statusResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkHasExtraCheck(Long faceId) {
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
return false;
|
||||
}
|
||||
String countKey = FACE_RECOGNITION_COUNT_PFX + faceId;
|
||||
String countStr = redisTemplate.opsForValue().get(countKey);
|
||||
long recognitionCount = 0L;
|
||||
if (countStr != null) {
|
||||
try {
|
||||
recognitionCount = Long.parseLong(countStr);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("识别次数解析失败,faceId={}, count={}", faceId, countStr);
|
||||
}
|
||||
}
|
||||
// 查询是否触发过低阈值检测
|
||||
String lowThresholdKey = FACE_LOW_THRESHOLD_PFX + faceId;
|
||||
Boolean hasLowThreshold = redisTemplate.hasKey(lowThresholdKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FaceSampleEntity> getLowMatchedFaceSamples(Long faceId) {
|
||||
FaceEntity face = faceMapper.get(faceId);
|
||||
if (face == null) {
|
||||
return List.of();
|
||||
}
|
||||
String matchResult = face.getMatchResult();
|
||||
if (matchResult == null || Strings.isBlank(matchResult)) {
|
||||
return List.of();
|
||||
}
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
|
||||
if (scenicConfig == null) {
|
||||
return List.of();
|
||||
}
|
||||
Float lowThreshold = scenicConfig.getFloat("face_score_low_threshold", 30.0F);
|
||||
List<SearchFaceResultItem> resultItems = JacksonUtil.fromJsonToList(matchResult, SearchFaceResultItem.class);
|
||||
if (resultItems == null || resultItems.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<Long> idList = resultItems.stream().filter(item -> item.getScore() > lowThreshold/100F)
|
||||
.map(SearchFaceResultItem::getExtData).limit(9).map(Long::valueOf).toList();
|
||||
if (idList.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<FaceSampleEntity> sampleEntities = faceSampleMapper.listByIds(idList);
|
||||
return sampleEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void matchCustomFaceId(Long faceId, List<Long> faceSampleIds) {
|
||||
// 参数验证
|
||||
if (faceId == null) {
|
||||
throw new IllegalArgumentException("faceId 不能为空");
|
||||
}
|
||||
if (faceSampleIds == null || faceSampleIds.isEmpty()) {
|
||||
throw new IllegalArgumentException("faceSampleIds 不能为空");
|
||||
}
|
||||
|
||||
log.debug("开始自定义人脸匹配:faceId={}, faceSampleIds={}", faceId, faceSampleIds);
|
||||
|
||||
try {
|
||||
// 1. 获取基础数据
|
||||
FaceEntity face = faceRepository.getFace(faceId);
|
||||
if (face == null) {
|
||||
log.warn("人脸不存在,faceId: {}", faceId);
|
||||
throw new BaseException("人脸不存在");
|
||||
}
|
||||
|
||||
List<FaceSampleEntity> faceSamples = faceSampleMapper.listByIds(faceSampleIds);
|
||||
if (faceSamples.isEmpty()) {
|
||||
log.warn("未找到指定的人脸样本,faceSampleIds: {}", faceSampleIds);
|
||||
throw new BaseException("未找到指定的人脸样本");
|
||||
}
|
||||
|
||||
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
|
||||
IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(face.getScenicId());
|
||||
|
||||
if (faceBodyAdapter == null) {
|
||||
log.error("无法获取人脸识别适配器,scenicId: {}", face.getScenicId());
|
||||
throw new BaseException("人脸识别服务不可用,请稍后再试");
|
||||
}
|
||||
|
||||
// 2. 对每个faceSample进行人脸搜索
|
||||
List<SearchFaceRespVo> searchResults = new ArrayList<>();
|
||||
for (FaceSampleEntity faceSample : faceSamples) {
|
||||
try {
|
||||
SearchFaceRespVo result = faceService.searchFace(faceBodyAdapter,
|
||||
String.valueOf(face.getScenicId()),
|
||||
faceSample.getFaceUrl(),
|
||||
"自定义人脸匹配");
|
||||
if (result != null) {
|
||||
searchResults.add(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("人脸样本搜索失败,faceSampleId={}, faceUrl={}",
|
||||
faceSample.getId(), faceSample.getFaceUrl(), e);
|
||||
// 继续处理其他样本,不中断整个流程
|
||||
}
|
||||
}
|
||||
|
||||
if (searchResults.isEmpty()) {
|
||||
log.warn("所有人脸样本搜索都失败,faceId={}, faceSampleIds={}", faceId, faceSampleIds);
|
||||
throw new BaseException("人脸识别失败,请重试");
|
||||
}
|
||||
|
||||
// 3. 整合多个搜索结果
|
||||
SearchFaceRespVo mergedResult = mergeSearchResults(searchResults);
|
||||
|
||||
// 4. 应用后置筛选逻辑
|
||||
if (mergedResult.getSampleListIds() != null && !mergedResult.getSampleListIds().isEmpty()) {
|
||||
List<FaceSampleEntity> allFaceSampleList = faceSampleMapper.listByIds(mergedResult.getSampleListIds());
|
||||
List<Long> filteredSampleIds = faceService.applySampleFilters(mergedResult.getSampleListIds(), allFaceSampleList, scenicConfig);
|
||||
mergedResult.setSampleListIds(filteredSampleIds);
|
||||
log.debug("应用后置筛选:原始样本数={}, 筛选后样本数={}", allFaceSampleList.size(), filteredSampleIds.size());
|
||||
}
|
||||
|
||||
// 5. 更新人脸实体结果
|
||||
updateFaceEntityResult(face, mergedResult, faceId);
|
||||
|
||||
// 6. 执行后续业务逻辑
|
||||
List<Long> sampleListIds = mergedResult.getSampleListIds();
|
||||
if (sampleListIds != null && !sampleListIds.isEmpty()) {
|
||||
try {
|
||||
List<MemberSourceEntity> memberSourceEntityList = processMemberSources(sampleListIds, face);
|
||||
|
||||
if (!memberSourceEntityList.isEmpty()) {
|
||||
List<Long> freeSourceIds = processFreeSourceLogic(memberSourceEntityList, scenicConfig, false);
|
||||
processBuyStatus(memberSourceEntityList, freeSourceIds, face.getMemberId(),
|
||||
face.getScenicId(), faceId);
|
||||
|
||||
handleVideoRecreation(scenicConfig, memberSourceEntityList, faceId,
|
||||
face.getMemberId(), sampleListIds, false);
|
||||
|
||||
sourceMapper.addRelations(memberSourceEntityList);
|
||||
taskTaskService.autoCreateTaskByFaceId(face.getId());
|
||||
|
||||
log.info("自定义人脸匹配完成:faceId={}, 匹配样本数={}, 关联源文件数={}, 免费数={}",
|
||||
faceId, sampleListIds.size(), memberSourceEntityList.size(), freeSourceIds.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理源文件关联失败,faceId={}", faceId, e);
|
||||
// 源文件关联失败不影响主流程
|
||||
}
|
||||
} else {
|
||||
log.warn("自定义人脸匹配无结果:faceId={}, faceSampleIds={}", faceId, faceSampleIds);
|
||||
}
|
||||
|
||||
} catch (BaseException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("自定义人脸匹配处理异常,faceId={}, faceSampleIds={}", faceId, faceSampleIds, e);
|
||||
throw new BaseException("自定义人脸匹配处理失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个搜索结果
|
||||
*/
|
||||
private SearchFaceRespVo mergeSearchResults(List<SearchFaceRespVo> searchResults) {
|
||||
SearchFaceRespVo mergedResult = new SearchFaceRespVo();
|
||||
|
||||
// 收集所有样本ID并去重
|
||||
Set<Long> allSampleIds = new LinkedHashSet<>();
|
||||
List<String> allSearchJsons = new ArrayList<>();
|
||||
float maxScore = 0f;
|
||||
float maxFirstMatchRate = 0f;
|
||||
boolean hasLowThreshold = false;
|
||||
|
||||
for (SearchFaceRespVo result : searchResults) {
|
||||
if (result.getSampleListIds() != null) {
|
||||
allSampleIds.addAll(result.getSampleListIds());
|
||||
}
|
||||
if (result.getSearchResultJson() != null) {
|
||||
allSearchJsons.add(result.getSearchResultJson());
|
||||
}
|
||||
if (result.getScore() > maxScore) {
|
||||
maxScore = result.getScore();
|
||||
}
|
||||
if (result.getFirstMatchRate() > maxFirstMatchRate) {
|
||||
maxFirstMatchRate = result.getFirstMatchRate();
|
||||
}
|
||||
if (result.isLowThreshold()) {
|
||||
hasLowThreshold = true;
|
||||
}
|
||||
}
|
||||
|
||||
mergedResult.setSampleListIds(new ArrayList<>(allSampleIds));
|
||||
mergedResult.setSearchResultJson(String.join("|", allSearchJsons));
|
||||
mergedResult.setScore(maxScore);
|
||||
mergedResult.setFirstMatchRate(maxFirstMatchRate);
|
||||
mergedResult.setLowThreshold(hasLowThreshold);
|
||||
|
||||
log.debug("合并搜索结果完成,总样本数: {}", allSampleIds.size());
|
||||
|
||||
return mergedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录人脸识别次数到Redis
|
||||
*
|
||||
* @param faceId 人脸ID
|
||||
*/
|
||||
private void recordFaceRecognitionCount(Long faceId) {
|
||||
if (faceId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String redisKey = FACE_RECOGNITION_COUNT_PFX + faceId;
|
||||
|
||||
// 使用Redis原子操作INCR增加计数
|
||||
Long count = redisTemplate.opsForValue().increment(redisKey);
|
||||
|
||||
// 设置2天过期时间(48小时)
|
||||
redisTemplate.expire(redisKey, 2, TimeUnit.DAYS);
|
||||
|
||||
log.debug("人脸识别计数更新:faceId={}, count={}", faceId, count);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 计数失败不应影响主要业务逻辑,只记录错误日志
|
||||
log.error("记录人脸识别次数失败:faceId={}", faceId, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录低阈值检测的人脸ID到Redis
|
||||
*
|
||||
* @param faceId 人脸ID
|
||||
*/
|
||||
private void recordLowThresholdFace(Long faceId) {
|
||||
if (faceId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String redisKey = FACE_LOW_THRESHOLD_PFX + faceId;
|
||||
|
||||
// 设置标记,表示该人脸ID触发了低阈值检测
|
||||
redisTemplate.opsForValue().set(redisKey, "1", 2, TimeUnit.DAYS);
|
||||
|
||||
log.debug("记录低阈值检测人脸:faceId={}", faceId);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 记录失败不应影响主要业务逻辑,只记录错误日志
|
||||
log.error("记录低阈值检测人脸失败:faceId={}", faceId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||
import com.ycwl.basic.pricing.service.ICouponService;
|
||||
import com.ycwl.basic.pricing.service.IVoucherService;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.biz.CouponBiz;
|
||||
@@ -64,7 +63,6 @@ import com.ycwl.basic.service.task.impl.TaskTaskServiceImpl;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.SnowFlakeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@@ -342,7 +340,7 @@ public class OrderServiceImpl implements OrderService {
|
||||
goods.setGoodsType(0);
|
||||
goods.setTemplateCoverUrl(template.getCoverUrl());
|
||||
goods.setCreateTime(videoTaskRepository.getTaskShotDate(task.getId()));
|
||||
goods.setParts(videoTaskRepository.getTaskDeviceCount(task.getId()));
|
||||
goods.setParts(videoTaskRepository.getTaskLensNum(task.getId()));
|
||||
goodsList.add(goods);
|
||||
item.setShootingTime(videoTaskRepository.getTaskShotDate(videoMapperById.getTaskId()));
|
||||
item.setVideoUrl(videoMapperById.getVideoUrl());
|
||||
|
@@ -0,0 +1,104 @@
|
||||
package com.ycwl.basic.service.pc.impl;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.mapper.ProjectMapper;
|
||||
import com.ycwl.basic.model.pc.project.entity.ProjectEntity;
|
||||
import com.ycwl.basic.model.pc.project.req.ProjectReqQuery;
|
||||
import com.ycwl.basic.model.pc.project.resp.ProjectRespVO;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.pc.ProjectService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 景区项目管理服务实现
|
||||
*
|
||||
* @Author: Claude
|
||||
* @Date: 2025-01-15
|
||||
*/
|
||||
@Service
|
||||
public class ProjectServiceImpl implements ProjectService {
|
||||
|
||||
@Autowired
|
||||
private ProjectMapper projectMapper;
|
||||
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
|
||||
@Override
|
||||
public PageInfo<ProjectRespVO> pageQuery(ProjectReqQuery projectReqQuery) {
|
||||
PageHelper.startPage(projectReqQuery.getPageNum(), projectReqQuery.getPageSize());
|
||||
List<ProjectRespVO> list = projectMapper.list(projectReqQuery);
|
||||
|
||||
// 批量获取景区名称
|
||||
List<Long> scenicIds = list.stream()
|
||||
.map(ProjectRespVO::getScenicId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<Long, String> scenicNames = scenicRepository.batchGetScenicNames(scenicIds);
|
||||
|
||||
// 设置景区名称
|
||||
list.forEach(item -> {
|
||||
if (item.getScenicId() != null) {
|
||||
item.setScenicName(scenicNames.get(item.getScenicId()));
|
||||
}
|
||||
});
|
||||
|
||||
PageInfo<ProjectRespVO> pageInfo = new PageInfo(list);
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProjectRespVO> list(ProjectReqQuery projectReqQuery) {
|
||||
List<ProjectRespVO> list = projectMapper.list(projectReqQuery);
|
||||
|
||||
// 批量获取景区名称
|
||||
List<Long> scenicIds = list.stream()
|
||||
.map(ProjectRespVO::getScenicId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<Long, String> scenicNames = scenicRepository.batchGetScenicNames(scenicIds);
|
||||
|
||||
// 设置景区名称
|
||||
list.forEach(item -> {
|
||||
if (item.getScenicId() != null) {
|
||||
item.setScenicName(scenicNames.get(item.getScenicId()));
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectRespVO getById(Long id) {
|
||||
return projectMapper.getById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addOrUpdate(ProjectEntity project) {
|
||||
Long id = project.getId();
|
||||
if (id == null) {
|
||||
return projectMapper.add(project);
|
||||
} else {
|
||||
return projectMapper.update(project);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Long id) {
|
||||
return projectMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateStatus(Long id) {
|
||||
return projectMapper.updateStatus(id);
|
||||
}
|
||||
}
|
@@ -1,9 +1,13 @@
|
||||
package com.ycwl.basic.service.task;
|
||||
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TaskFaceService {
|
||||
|
||||
SearchFaceRespVo searchFace(Long faceId);
|
||||
@@ -13,4 +17,15 @@ public interface TaskFaceService {
|
||||
boolean deleteFaceSample(Long scenicId, String dbName, String entityId);
|
||||
|
||||
boolean assureFaceDb(IFaceBodyAdapter faceBodyAdapter, String dbName);
|
||||
|
||||
/**
|
||||
* 应用样本筛选逻辑
|
||||
* @param acceptedSampleIds 已接受的样本ID列表
|
||||
* @param allFaceSampleList 所有人脸样本实体列表
|
||||
* @param scenicConfig 景区配置管理器
|
||||
* @return 筛选后的样本ID列表
|
||||
*/
|
||||
List<Long> applySampleFilters(List<Long> acceptedSampleIds,
|
||||
List<FaceSampleEntity> allFaceSampleList,
|
||||
ScenicConfigManager scenicConfig);
|
||||
}
|
||||
|
@@ -171,6 +171,7 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
request.setLimit(200);
|
||||
FaceDetectLog logEntity = FaceDetectLog.quickCreate(reason, request);
|
||||
float threshold = 0;
|
||||
float lowThreshold = 100;
|
||||
ScenicConfigManager scenicConfig = null;
|
||||
if (StringUtils.isNumeric(dbName)) {
|
||||
scenicConfig = scenicRepository.getScenicConfigManager(Long.valueOf(dbName));
|
||||
@@ -178,6 +179,9 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
if (scenicConfig.getFloat("face_score_threshold") != null) {
|
||||
threshold = scenicConfig.getFloat("face_score_threshold") / 100F;
|
||||
}
|
||||
if (scenicConfig.getFloat("face_score_low_threshold") != null) {
|
||||
lowThreshold = scenicConfig.getFloat("face_score_low_threshold") / 100F;
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.isNumeric(dbName.replace(USER_FACE_DB_NAME, ""))) {
|
||||
Long scenicId = Long.valueOf(dbName.replace(USER_FACE_DB_NAME, ""));
|
||||
@@ -186,6 +190,9 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
if (scenicConfig.getFloat("face_score_threshold") != null) {
|
||||
threshold = scenicConfig.getFloat("face_score_threshold") / 100F;
|
||||
}
|
||||
if (scenicConfig.getFloat("face_score_low_threshold") != null) {
|
||||
lowThreshold = scenicConfig.getFloat("face_score_low_threshold") / 100F;
|
||||
}
|
||||
}
|
||||
}
|
||||
final float _threshold = threshold;
|
||||
@@ -206,11 +213,16 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
return respVo;
|
||||
}
|
||||
acceptFaceSampleIds = records.stream()
|
||||
.sorted(Comparator.comparing(SearchFaceResultItem::getScore).reversed())
|
||||
.filter(record -> record.getScore() > _threshold)
|
||||
.map(SearchFaceResultItem::getExtData)
|
||||
.filter(StringUtils::isNumeric)
|
||||
.map(Long::valueOf)
|
||||
.collect(Collectors.toList());
|
||||
float _lowThreshold = lowThreshold;
|
||||
boolean isLowThreshold = records.stream()
|
||||
.anyMatch(record -> record.getScore() > _lowThreshold);
|
||||
respVo.setLowThreshold(isLowThreshold);
|
||||
allFaceSampleIds = records.stream()
|
||||
.map(SearchFaceResultItem::getExtData)
|
||||
.filter(StringUtils::isNumeric)
|
||||
@@ -340,7 +352,8 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
* @param scenicConfig 景区配置,包含各种筛选策略的参数
|
||||
* @return 筛选后的样本ID列表
|
||||
*/
|
||||
private List<Long> applySampleFilters(List<Long> acceptedSampleIds,
|
||||
@Override
|
||||
public List<Long> applySampleFilters(List<Long> acceptedSampleIds,
|
||||
List<FaceSampleEntity> allFaceSampleList,
|
||||
ScenicConfigManager scenicConfig) {
|
||||
if (acceptedSampleIds == null || acceptedSampleIds.isEmpty()) {
|
||||
|
@@ -5,6 +5,7 @@ import cn.hutool.crypto.digest.MD5;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.integration.common.manager.RenderWorkerConfigManager;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.repository.SourceRepository;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.biz.OrderBiz;
|
||||
import com.ycwl.basic.biz.TaskStatusBiz;
|
||||
@@ -126,6 +127,8 @@ public class TaskTaskServiceImpl implements TaskService {
|
||||
private VideoReUploader videoReUploader;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private SourceRepository sourceRepository;
|
||||
|
||||
private RenderWorkerEntity getWorker(@NonNull WorkerAuthReqVo req) {
|
||||
String accessKey = req.getAccessKey();
|
||||
@@ -348,58 +351,15 @@ public class TaskTaskServiceImpl implements TaskService {
|
||||
}
|
||||
}
|
||||
|
||||
List<SourceEntity> videoSourceList = sourceMapper.listVideoByScenicFaceRelation(face.getScenicId(), faceId);
|
||||
Map<String, List<SourceEntity>> sourcesMap = videoSourceList.stream()
|
||||
.peek(item -> item.setUrl(item.getVideoUrl()))
|
||||
.filter(item -> item.getDeviceId() != null) // 添加对 deviceId 为 null 的检查
|
||||
.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());
|
||||
})
|
||||
.collect(Collectors.groupingBy(item -> item.getDeviceId().toString()));
|
||||
if (sourcesMap.isEmpty()) {
|
||||
// 主动禁止没有视频源视频生成
|
||||
log.info("task callback: 没有视频源,templateId: {}", templateId);
|
||||
Map<String, List<SourceEntity>> allTaskParams = sourceRepository.getTaskParams(faceId, templateId);
|
||||
if (allTaskParams.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> templatePlaceholder = templateRepository.getTemplatePlaceholder(templateId);
|
||||
if (templatePlaceholder.stream().distinct().count() == templatePlaceholder.size()) {
|
||||
sourcesMap.forEach((key, value) -> {
|
||||
// 每个value只保留第一个
|
||||
value.removeIf(item -> !value.getFirst().equals(item));
|
||||
});
|
||||
}
|
||||
|
||||
// 处理以P开头的templatePlaceHolder,添加type=2的source
|
||||
boolean hasPPlaceholder = templatePlaceholder.stream().anyMatch(placeholder -> placeholder.startsWith("P"));
|
||||
if (hasPPlaceholder) {
|
||||
List<SourceEntity> imageSourceList = sourceMapper.listImageSourcesByFaceId(faceId);
|
||||
if (!imageSourceList.isEmpty()) {
|
||||
// 将图片source按设备ID分组并添加到sourcesMap中
|
||||
Map<String, List<SourceEntity>> imageSourceMap = imageSourceList.stream()
|
||||
.peek(item -> item.setUrl(item.getUrl())) // 图片使用url字段
|
||||
.filter(item -> item.getDeviceId() != null)
|
||||
.filter(item -> {
|
||||
DeviceEntity device = deviceRepository.getDevice(item.getDeviceId());
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
return Integer.valueOf(1).equals(device.getStatus());
|
||||
})
|
||||
.collect(Collectors.groupingBy(item -> Strings.concat("P", item.getDeviceId().toString())));
|
||||
|
||||
// 合并到现有的sourcesMap中
|
||||
imageSourceMap.forEach((key, value) -> {
|
||||
sourcesMap.merge(key, value, (existing, replacement) -> {
|
||||
existing.addAll(replacement);
|
||||
return existing;
|
||||
});
|
||||
});
|
||||
}
|
||||
Map<String, List<SourceEntity>> sourcesMap = templateBiz.filterTaskParams(templateId, allTaskParams);
|
||||
if (sourcesMap.isEmpty()) {
|
||||
log.info("task callback: 筛选后无有效源数据,templateId: {}", templateId);
|
||||
return;
|
||||
}
|
||||
TaskReqQuery taskReqQuery = new TaskReqQuery();
|
||||
taskReqQuery.setFaceId(faceId);
|
||||
@@ -522,8 +482,8 @@ public class TaskTaskServiceImpl implements TaskService {
|
||||
}
|
||||
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(task.getScenicId());
|
||||
IStorageAdapter adapter = scenicService.getScenicTmpStorageAdapter(task.getScenicId());
|
||||
String hash = MD5.create().digestHex(task.getTaskParams());
|
||||
String filename = StorageUtil.joinPath(StorageConstant.VLOG_PATH, hash + "_" + task.getScenicId() + ".mp4");
|
||||
String hash = MD5.create().digestHex(task.getTaskParams() + task.getFaceId().toString());
|
||||
String filename = StorageUtil.joinPath(StorageConstant.VLOG_PATH, task.getTemplateId().toString() + "_" + hash + "_" + task.getScenicId() + ".mp4");
|
||||
adapter.setAcl(StorageAcl.PUBLIC_READ, filename);
|
||||
int isBuy = 0;
|
||||
FaceEntity face = faceRepository.getFace(task.getFaceId());
|
||||
@@ -591,8 +551,8 @@ public class TaskTaskServiceImpl implements TaskService {
|
||||
} catch (Exception e) {
|
||||
adapter = scenicService.getScenicStorageAdapter(task.getScenicId());
|
||||
}
|
||||
String hash = MD5.create().digestHex(task.getTaskParams());
|
||||
String filename = StorageUtil.joinPath(StorageConstant.VLOG_PATH, hash + "_" + task.getScenicId() + ".mp4");
|
||||
String hash = MD5.create().digestHex(task.getTaskParams() + task.getFaceId().toString());
|
||||
String filename = StorageUtil.joinPath(StorageConstant.VLOG_PATH, task.getTemplateId().toString() + "_" + hash + "_" + task.getScenicId() + ".mp4");
|
||||
// 生成
|
||||
String url = adapter.getUrl(filename);
|
||||
TaskEntity updateTask = new TaskEntity();
|
||||
@@ -648,7 +608,16 @@ public class TaskTaskServiceImpl implements TaskService {
|
||||
return;
|
||||
}
|
||||
ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId());
|
||||
String title = "您在【" + scenic.getName() + "】的专属影像";
|
||||
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(item.getScenicId());
|
||||
String configTitle = configManager.getString("first_notification_title");
|
||||
String configContent = configManager.getString("first_notification_content");
|
||||
|
||||
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
|
||||
log.info("景区[{}]未配置第一次通知内容,跳过发送通知", scenic.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
String title = configTitle.replace("【景区】", scenic.getName());
|
||||
String page = "pages/videoSynthesis/index?type=1&scenicId=" + item.getScenicId() + "&faceId=" + item.getFaceId();
|
||||
/**
|
||||
* 视频名称 {{thing1.DATA}}
|
||||
@@ -664,7 +633,7 @@ public class TaskTaskServiceImpl implements TaskService {
|
||||
timeMap2.put("value", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm"));
|
||||
dataParam.put("time4", timeMap2);
|
||||
Map<String, String> remarkMap = new HashMap<>();
|
||||
remarkMap.put("value", "请及时查看视频,48小时后系统删除");
|
||||
remarkMap.put("value", configContent);
|
||||
dataParam.put("thing3", remarkMap);
|
||||
params.put("data", dataParam);
|
||||
params.put("page", page);
|
||||
|
@@ -18,6 +18,7 @@ import com.ycwl.basic.notify.entity.NotifyContent;
|
||||
import com.ycwl.basic.notify.enums.NotifyType;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.repository.TemplateRepository;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -65,7 +66,16 @@ public class DownloadNotificationTasker {
|
||||
}
|
||||
log.info("发送模板消息");
|
||||
ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId());
|
||||
String title = "您在【" + scenic.getName() + "】的专属影像";
|
||||
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(item.getScenicId());
|
||||
String configTitle = configManager.getString("second_notification_title");
|
||||
String configContent = configManager.getString("second_notification_content");
|
||||
|
||||
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
|
||||
log.info("景区[{}]未配置第二次通知内容,跳过发送通知", scenic.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
String title = configTitle.replace("【景区】", scenic.getName());
|
||||
String page = "pages/videoSynthesis/index?type=2&scenicId=" + item.getScenicId() + "&faceId=" + item.getFaceId();
|
||||
/**
|
||||
* 景区 {{thing1.DATA}}
|
||||
@@ -77,17 +87,7 @@ public class DownloadNotificationTasker {
|
||||
videoMap.put("value", title);
|
||||
dataParam.put("thing1", videoMap);
|
||||
Map<String, String> remarkMap = new HashMap<>();
|
||||
// 查询是否有优惠券可用,如果有,则显示优惠券配置的内容
|
||||
CouponQueryReq query = new CouponQueryReq();
|
||||
query.setScenicId(item.getScenicId());
|
||||
query.setType(2);
|
||||
query.setStatus(1);
|
||||
List<CouponRespVO> coupons = couponMapper.selectByQuery(query);
|
||||
if (coupons.isEmpty() || StringUtils.isBlank(coupons.getFirst().getBroadcast())) {
|
||||
remarkMap.put("value", "系统删除前,请下载好您的旅行视频");
|
||||
} else {
|
||||
remarkMap.put("value", coupons.getFirst().getBroadcast());
|
||||
}
|
||||
remarkMap.put("value", configContent);
|
||||
dataParam.put("thing3", remarkMap);
|
||||
params.put("data", dataParam);
|
||||
params.put("page", page);
|
||||
@@ -121,7 +121,16 @@ public class DownloadNotificationTasker {
|
||||
}
|
||||
log.info("发送模板消息");
|
||||
ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId());
|
||||
String title = "您在【" + scenic.getName() + "】的专属影像";
|
||||
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(item.getScenicId());
|
||||
String configTitle = configManager.getString("third_notification_title");
|
||||
String configContent = configManager.getString("third_notification_content");
|
||||
|
||||
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
|
||||
log.info("景区[{}]未配置第三次通知内容,跳过发送通知", scenic.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
String title = configTitle.replace("【景区】", scenic.getName());
|
||||
String page = "pages/videoSynthesis/index?type=3&scenicId=" + item.getScenicId() + "&faceId=" + item.getFaceId();
|
||||
/**
|
||||
* 影像名称 {{thing1.DATA}}
|
||||
@@ -138,17 +147,7 @@ public class DownloadNotificationTasker {
|
||||
dateMap.put("value", DateUtil.format(expireDate, "yyyy-MM-dd HH:mm"));
|
||||
dataParam.put("time2", dateMap);
|
||||
Map<String, String> remarkMap = new HashMap<>();
|
||||
// 查询是否有优惠券可用,如果有,则显示优惠券配置的内容
|
||||
CouponQueryReq query = new CouponQueryReq();
|
||||
query.setScenicId(item.getScenicId());
|
||||
query.setType(3);
|
||||
query.setStatus(1);
|
||||
List<CouponRespVO> coupons = couponMapper.selectByQuery(query);
|
||||
if (coupons.isEmpty() || StringUtils.isBlank(coupons.getFirst().getBroadcast())) {
|
||||
remarkMap.put("value", "视频即将删除,花点小钱买下回忆");
|
||||
} else {
|
||||
remarkMap.put("value", coupons.getFirst().getBroadcast());
|
||||
}
|
||||
remarkMap.put("value", configContent);
|
||||
dataParam.put("thing3", remarkMap);
|
||||
params.put("data", dataParam);
|
||||
params.put("page", page);
|
||||
@@ -201,8 +200,17 @@ public class DownloadNotificationTasker {
|
||||
log.info("模板消息为空");
|
||||
return;
|
||||
}
|
||||
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(scenicId);
|
||||
String configTitle = configManager.getString("second_notification_title");
|
||||
String configContent = configManager.getString("second_notification_content");
|
||||
|
||||
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
|
||||
log.info("景区[{}]未配置第一次通知内容,跳过发送通知", scenic.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("发送模板消息");
|
||||
String title = "您在【" + scenic.getName() + "】的专属影像";
|
||||
String title = configTitle.replace("【景区】", scenic.getName());
|
||||
String page = "pages/videoSynthesis/index?type=2&scenicId=" + item.getScenicId() + "&faceId=" + item.getFaceId();
|
||||
/**
|
||||
* 景区 {{thing1.DATA}}
|
||||
@@ -214,7 +222,7 @@ public class DownloadNotificationTasker {
|
||||
videoMap.put("value", title);
|
||||
dataParam.put("thing1", videoMap);
|
||||
Map<String, String> remarkMap = new HashMap<>();
|
||||
remarkMap.put("value", "请及时购买并下载好您的旅行视频");
|
||||
remarkMap.put("value", configContent);
|
||||
dataParam.put("thing3", remarkMap);
|
||||
params.put("data", dataParam);
|
||||
params.put("page", page);
|
||||
|
59
src/main/resources/mapper/ProjectMapper.xml
Normal file
59
src/main/resources/mapper/ProjectMapper.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?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.ProjectMapper">
|
||||
<insert id="add">
|
||||
insert into project(scenic_id, `name`, min_play_time, max_play_time, status, create_at, update_at)
|
||||
values (#{scenicId}, #{name}, #{minPlayTime}, #{maxPlayTime}, #{status}, now(), now())
|
||||
</insert>
|
||||
|
||||
<update id="update">
|
||||
update project set
|
||||
`name` = #{name},
|
||||
min_play_time = #{minPlayTime},
|
||||
max_play_time = #{maxPlayTime},
|
||||
status = #{status},
|
||||
update_at = now()
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updateStatus">
|
||||
update project
|
||||
set status = (CASE
|
||||
status
|
||||
WHEN 1 THEN
|
||||
0
|
||||
WHEN 0 THEN
|
||||
1
|
||||
ELSE 1
|
||||
END),
|
||||
update_at = now()
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteById">
|
||||
delete from project where id = #{id}
|
||||
</delete>
|
||||
|
||||
<select id="list" resultType="com.ycwl.basic.model.pc.project.resp.ProjectRespVO">
|
||||
select p.id, p.scenic_id, p.`name`, p.min_play_time, p.max_play_time, p.status, p.create_at, p.update_at
|
||||
from project p
|
||||
<where>
|
||||
<if test="scenicId != null">
|
||||
and p.scenic_id = #{scenicId}
|
||||
</if>
|
||||
<if test="name != null and name != ''">
|
||||
and p.`name` like concat('%', #{name}, '%')
|
||||
</if>
|
||||
<if test="status != null">
|
||||
and p.`status` = #{status}
|
||||
</if>
|
||||
</where>
|
||||
order by p.create_at desc
|
||||
</select>
|
||||
|
||||
<select id="getById" resultType="com.ycwl.basic.model.pc.project.resp.ProjectRespVO">
|
||||
select p.id, p.scenic_id, p.`name`, p.min_play_time, p.max_play_time, p.status, p.create_at, p.update_at
|
||||
from project p
|
||||
where p.id = #{id}
|
||||
</select>
|
||||
</mapper>
|
@@ -114,7 +114,7 @@
|
||||
order by sort
|
||||
</select>
|
||||
<select id="listFor" resultType="com.ycwl.basic.model.mobile.scenic.content.ContentPageVO">
|
||||
select t.id templateId, t.scenic_id, t.`name`, pid, t.cover_url templateCoverUrl,
|
||||
select t.id templateId, t.scenic_id, t.`group`, t.`name`, pid, t.cover_url templateCoverUrl,
|
||||
0 as sourceType, sort,
|
||||
t.create_time, t.price
|
||||
from template t
|
||||
|
Reference in New Issue
Block a user