加个普通水印、推送订单纠正、水印预览和下载

This commit is contained in:
Jerry Yan 2025-03-18 18:23:18 +08:00
parent 46f6532164
commit 26dc53ca6a
20 changed files with 408 additions and 188 deletions

View File

@ -240,18 +240,12 @@ public class OrderBiz {
StatisticsRecordAddReq statisticsRecordAddReq = new StatisticsRecordAddReq(); StatisticsRecordAddReq statisticsRecordAddReq = new StatisticsRecordAddReq();
statisticsRecordAddReq.setMemberId(order.getMemberId()); statisticsRecordAddReq.setMemberId(order.getMemberId());
Calendar calendar = Calendar.getInstance(); Long enterType = statisticsMapper.getUserRecentEnterType(order.getMemberId(), order.getCreateAt());
calendar.setTime(goodsCreateTime); if(!Long.valueOf(1014).equals(enterType)){//
calendar.set(Calendar.HOUR_OF_DAY, 21);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
// TODO: 他的购买的内容于内容生成当天晚9点之前算现场订单否则算推送订单
if(calendar.getTime().compareTo(payAt)>0){//
statisticsRecordAddReq.setType(StatisticEnum.ON_SITE_PAYMENT.code); statisticsRecordAddReq.setType(StatisticEnum.ON_SITE_PAYMENT.code);
}else { }else {
statisticsRecordAddReq.setType(StatisticEnum.POST_PAYMENT.code); statisticsRecordAddReq.setType(StatisticEnum.POST_PAYMENT.code);
} }
calendar.clear();
statisticsRecordAddReq.setScenicId(order.getScenicId()); statisticsRecordAddReq.setScenicId(order.getScenicId());
statisticsRecordAddReq.setMorphId(orderId); statisticsRecordAddReq.setMorphId(orderId);
statisticsMapper.addStatisticsRecord(statisticsRecordAddReq); statisticsMapper.addStatisticsRecord(statisticsRecordAddReq);

View File

@ -40,6 +40,19 @@ public class AppGoodsController {
return ApiResponse.success(goodsDetailVOS); return ApiResponse.success(goodsDetailVOS);
} }
@PostMapping("/sourceGoodsList/preview")
public ApiResponse<List<GoodsUrlVO>> sourceGoodsListPreview(@RequestBody GoodsReqQuery query) {
List<GoodsUrlVO> goodsUrlList = goodsService.sourceGoodsListPreview(query);
return ApiResponse.success(goodsUrlList);
}
@PostMapping("/sourceGoodsList/download")
public ApiResponse<List<GoodsUrlVO>> sourceGoodsListDownload(@RequestBody GoodsReqQuery query) {
List<GoodsUrlVO> goodsUrlList = goodsService.sourceGoodsListDownload(query);
return ApiResponse.success(goodsUrlList);
}
@ApiOperation("成片vlog商品详情") @ApiOperation("成片vlog商品详情")
@GetMapping("/getVideoGoodsDetail/{videoId}") @GetMapping("/getVideoGoodsDetail/{videoId}")
@IgnoreToken @IgnoreToken

View File

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

View File

@ -9,6 +9,9 @@ import java.util.Date;
@Data @Data
public class WatermarkInfo { public class WatermarkInfo {
private File originalFile; private File originalFile;
/**
* 输出文件
*/
private File watermarkedFile; private File watermarkedFile;
private File qrcodeFile; private File qrcodeFile;
private String scenicLine; private String scenicLine;

View File

@ -1,6 +1,10 @@
package com.ycwl.basic.image.watermark.enums; package com.ycwl.basic.image.watermark.enums;
import lombok.Getter;
@Getter
public enum ImageWatermarkOperatorEnum { public enum ImageWatermarkOperatorEnum {
WATERMARK("defW"),
LEICA("leica"), LEICA("leica"),
NORMAL("normal"); NORMAL("normal");

View File

@ -0,0 +1,46 @@
package com.ycwl.basic.image.watermark.operator;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
public class DefaultImageWatermarkOperator implements IOperator {
@Override
public File process(WatermarkInfo info) throws ImageWatermarkException {
BufferedImage baseImage;
BufferedImage watermarkImage;
InputStream logoInputStream = getClass().getResourceAsStream("/watermark.png");
if (logoInputStream == null) {
throw new ImageWatermarkException("无法找到 watermark.png 资源文件");
}
try {
baseImage = ImageIO.read(info.getOriginalFile());
watermarkImage = ImageIO.read(logoInputStream);
} catch (IOException e) {
throw new ImageWatermarkException("图片打开失败");
}
// 新图像画布
BufferedImage newImage = new BufferedImage(baseImage.getWidth(), baseImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newImage.createGraphics();
g2d.drawImage(baseImage, 0, 0, null);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f));
g2d.drawImage(watermarkImage, 0, 0, baseImage.getWidth(), baseImage.getHeight(), null);
try {
ImageIO.write(newImage, "jpg", info.getWatermarkedFile());
} catch (IOException e) {
throw new ImageWatermarkException("图片保存失败");
} finally {
g2d.dispose();
}
return info.getWatermarkedFile();
}
}

View File

@ -46,7 +46,7 @@ public class LeicaWatermarkOperator implements IOperator {
public static int EXTRA_BORDER_PX = 0; public static int EXTRA_BORDER_PX = 0;
public static Color BG_COLOR = Color.WHITE; public static Color BG_COLOR = Color.WHITE;
public static int LOGO_SIZE = 50; public static int LOGO_SIZE = 50;
public static int LOGO_EXTRA_BORDER = 10; public static int LOGO_EXTRA_BORDER = 20;
public static int LOGO_FONT_SIZE = 38; public static int LOGO_FONT_SIZE = 38;
public static Color logoTextColor = new Color(0x33, 0x33, 0x33); public static Color logoTextColor = new Color(0x33, 0x33, 0x33);
public static int QRCODE_SIZE = 80; public static int QRCODE_SIZE = 80;

View File

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

View File

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

View File

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

View File

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

View File

@ -15,5 +15,4 @@ public class MemberSourceEntity {
private Integer isBuy; private Integer isBuy;
private Long orderId; private Long orderId;
private Integer isFree; private Integer isFree;
private String waterUrl;
} }

View File

@ -0,0 +1,12 @@
package com.ycwl.basic.model.pc.source.entity;
import lombok.Data;
@Data
public class SourceWatermarkEntity {
private Integer id;
private Long sourceId;
private Long faceId;
private String watermarkType;
private String watermarkUrl;
}

View File

@ -1,16 +1,27 @@
package com.ycwl.basic.service.impl.mobile; package com.ycwl.basic.service.impl.mobile;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.ycwl.basic.biz.OrderBiz; import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.biz.TaskStatusBiz; import com.ycwl.basic.biz.TaskStatusBiz;
import com.ycwl.basic.constant.BaseContextHandler; import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.constant.StorageConstant;
import com.ycwl.basic.image.watermark.ImageWatermarkFactory;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import com.ycwl.basic.image.watermark.operator.IOperator;
import com.ycwl.basic.mapper.*; import com.ycwl.basic.mapper.*;
import com.ycwl.basic.model.mobile.goods.*; import com.ycwl.basic.model.mobile.goods.*;
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
import com.ycwl.basic.model.mobile.order.PriceObj; import com.ycwl.basic.model.mobile.order.PriceObj;
import com.ycwl.basic.model.pc.face.entity.FaceEntity; import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO; import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity; import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
import com.ycwl.basic.model.pc.source.entity.SourceWatermarkEntity;
import com.ycwl.basic.model.pc.source.req.SourceReqQuery; import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
import com.ycwl.basic.model.pc.source.resp.SourceRespVO; import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
import com.ycwl.basic.model.pc.task.entity.TaskEntity; import com.ycwl.basic.model.pc.task.entity.TaskEntity;
@ -25,21 +36,30 @@ import com.ycwl.basic.repository.VideoTaskRepository;
import com.ycwl.basic.service.mobile.GoodsService; import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.repository.TemplateRepository; import com.ycwl.basic.repository.TemplateRepository;
import com.ycwl.basic.service.task.TaskService; import com.ycwl.basic.service.task.TaskService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.utils.ApiResponse; import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.DateUtils; import com.ycwl.basic.utils.DateUtils;
import com.ycwl.basic.utils.WxMpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* @Authorlongbinbin * @Authorlongbinbin
* @Date2024/12/5 15:04 * @Date2024/12/5 15:04
*/ */
@Slf4j
@Service @Service
public class GoodsServiceImpl implements GoodsService { public class GoodsServiceImpl implements GoodsService {
@Autowired @Autowired
@ -427,4 +447,206 @@ public class GoodsServiceImpl implements GoodsService {
return ApiResponse.success(goodsDetailVO); return ApiResponse.success(goodsDetailVO);
} }
@Override
public List<GoodsUrlVO> sourceGoodsListPreview(GoodsReqQuery query) {
List<File> tmpFile = new ArrayList<>();
FaceEntity face = faceRepository.getFace(query.getFaceId());
if (face == null) {
return Collections.emptyList();
}
Integer sourceType = query.getSourceType();
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(query.getScenicId());
sourceReqQuery.setIsBuy(query.getIsBuy());
sourceReqQuery.setMemberId(face.getMemberId());
sourceReqQuery.setType(sourceType);
sourceReqQuery.setFaceId(query.getFaceId());
List<SourceRespVO> list = sourceMapper.listUser(sourceReqQuery);
if (!Integer.valueOf(2).equals(query.getSourceType())) {
return list.stream().map(source -> {
GoodsUrlVO goodsUrlVO = new GoodsUrlVO();
goodsUrlVO.setGoodsType(source.getType());
goodsUrlVO.setGoodsId(source.getId());
goodsUrlVO.setUrl(source.getVideoUrl());
goodsUrlVO.setCreateTime(source.getCreateTime());
return goodsUrlVO;
}).collect(Collectors.toList());
}
List<GoodsUrlVO> defaultUrlList = list.stream().map(source -> {
GoodsUrlVO goodsUrlVO = new GoodsUrlVO();
goodsUrlVO.setGoodsType(source.getType());
goodsUrlVO.setGoodsId(source.getId());
goodsUrlVO.setUrl(source.getUrl());
goodsUrlVO.setCreateTime(source.getCreateTime());
return goodsUrlVO;
}).collect(Collectors.toList());
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), query.getScenicId(), query.getSourceType(), query.getFaceId());
if (!isBuy.isBuy()) {
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId());
if (scenicConfig != null && ((scenicConfig.getAntiScreenRecordType() & 2) == 0)) {
// 未启用水印
return defaultUrlList;
}
IStorageAdapter adapter;
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
adapter = StorageFactory.use("assets-ext");
}
IOperator operator = ImageWatermarkFactory.get(ImageWatermarkOperatorEnum.WATERMARK);
List<SourceWatermarkEntity> watermarkEntityList = sourceMapper.listSourceWatermark(defaultUrlList.stream().map(GoodsUrlVO::getGoodsId).collect(Collectors.toList()), null, ImageWatermarkOperatorEnum.WATERMARK.getType());
List<GoodsUrlVO> collect = defaultUrlList.stream().peek(item -> {
Optional<SourceWatermarkEntity> any = watermarkEntityList.stream()
.filter(watermark -> watermark.getSourceId().equals(item.getGoodsId()))
.findAny();
if (any.isPresent()) {
item.setUrl(any.get().getWatermarkUrl());
} else {
// 生成
File dstFile = new File(item.getGoodsId() + ".jpg");
File watermarkedFile = new File(item.getGoodsId() + "_" + ImageWatermarkOperatorEnum.WATERMARK.getType() + ".png");
try {
HttpUtil.downloadFile(item.getUrl(), dstFile);
} catch (Exception e) {
log.error("downloadFile error", e);
return;
}
tmpFile.add(dstFile);
WatermarkInfo info = new WatermarkInfo();
info.setOriginalFile(dstFile);
info.setWatermarkedFile(watermarkedFile);
try {
operator.process(info);
} catch (ImageWatermarkException e) {
log.error("process error", e);
return;
}
String url = adapter.uploadFile(watermarkedFile, StorageConstant.PHOTO_WATERMARKED_PATH, watermarkedFile.getName());
adapter.setAcl(StorageAcl.PUBLIC_READ, StorageConstant.PHOTO_WATERMARKED_PATH, watermarkedFile.getName());
sourceMapper.addSourceWatermark(item.getGoodsId(), null, ImageWatermarkOperatorEnum.WATERMARK.getType(), url);
tmpFile.add(watermarkedFile);
item.setUrl(url);
}
}).collect(Collectors.toList());
for (File file : tmpFile) {
file.delete();
}
return collect;
}
return defaultUrlList;
}
@Override
public List<GoodsUrlVO> sourceGoodsListDownload(GoodsReqQuery query) {
List<File> tmpFile = new ArrayList<>();
FaceEntity face = faceRepository.getFace(query.getFaceId());
if (face == null) {
return Collections.emptyList();
}
IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), query.getScenicId(), query.getSourceType(), query.getFaceId());
if (!isBuy.isBuy()) {
return Collections.emptyList();
}
Integer sourceType = query.getSourceType();
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(query.getScenicId());
sourceReqQuery.setIsBuy(query.getIsBuy());
sourceReqQuery.setMemberId(face.getMemberId());
sourceReqQuery.setType(sourceType);
sourceReqQuery.setFaceId(query.getFaceId());
List<SourceRespVO> list = sourceMapper.listUser(sourceReqQuery);
if (query.getGoodsId() != null) {
list = list.stream().filter(source -> source.getId().equals(query.getGoodsId())).collect(Collectors.toList());
}
if (!Integer.valueOf(2).equals(query.getSourceType())) {
return list.stream().map(source -> {
GoodsUrlVO goodsUrlVO = new GoodsUrlVO();
goodsUrlVO.setGoodsType(source.getType());
goodsUrlVO.setGoodsId(source.getId());
goodsUrlVO.setUrl(source.getVideoUrl());
goodsUrlVO.setCreateTime(source.getCreateTime());
return goodsUrlVO;
}).collect(Collectors.toList());
}
List<GoodsUrlVO> defaultUrlList = list.stream().map(source -> {
GoodsUrlVO goodsUrlVO = new GoodsUrlVO();
goodsUrlVO.setGoodsType(source.getType());
goodsUrlVO.setGoodsId(source.getId());
goodsUrlVO.setUrl(source.getUrl());
goodsUrlVO.setCreateTime(source.getCreateTime());
return goodsUrlVO;
}).collect(Collectors.toList());
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId());
MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(face.getScenicId());
if (scenicMpConfig == null) {
log.warn("未配置小程序参数,无法生成二维码");
return defaultUrlList;
}
if (scenicConfig != null && scenicConfig.getWatermarkType() != null) {
ImageWatermarkOperatorEnum type = ImageWatermarkOperatorEnum.getByCode(scenicConfig.getWatermarkType());
if (type != null) {
IStorageAdapter adapter;
if (scenicConfig != null && scenicConfig.getStoreType() != null) {
adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
} else {
adapter = StorageFactory.use("assets-ext");
}
IOperator operator = ImageWatermarkFactory.get(type);
List<SourceWatermarkEntity> watermarkEntityList = sourceMapper.listSourceWatermark(list.stream().map(SourceRespVO::getId).collect(Collectors.toList()), face.getId(), type.getType());
File qrcode = new File("qrcode_"+face.getId()+".jpg");
try {
String urlLink = WxMpUtil.generateUrlLink(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/index", "scenicId=" + face.getScenicId() + "&faceId=" + face.getId());
QrCodeUtil.generate(urlLink + "?cq=", 300, 300, qrcode);
} catch (Exception e) {
log.error("generateWXQRCode error", e);
return defaultUrlList;
}
tmpFile.add(qrcode);
List<GoodsUrlVO> collect = defaultUrlList.stream().peek(item -> {
Optional<SourceWatermarkEntity> any = watermarkEntityList.stream()
.filter(watermark -> watermark.getSourceId().equals(item.getGoodsId()))
.findAny();
if (any.isPresent()) {
item.setUrl(any.get().getWatermarkUrl());
} else {
// 生成
File dstFile = new File(item.getGoodsId() + ".jpg");
File watermarkedFile = new File(item.getGoodsId() + "_" + type.getType() + ".png");
try {
HttpUtil.downloadFile(item.getUrl(), dstFile);
} catch (Exception e) {
log.error("downloadFile error", e);
return;
}
tmpFile.add(dstFile);
WatermarkInfo info = new WatermarkInfo();
info.setOriginalFile(dstFile);
info.setQrcodeFile(qrcode);
info.setScenicLine(scenicConfig.getWatermarkScenicText());
info.setDatetime(item.getCreateTime());
info.setDtFormat(scenicConfig.getWatermarkDtFormat());
info.setWatermarkedFile(watermarkedFile);
try {
operator.process(info);
} catch (ImageWatermarkException e) {
log.error("process error", e);
return;
}
String url = adapter.uploadFile(watermarkedFile, StorageConstant.PHOTO_WATERMARKED_PATH, watermarkedFile.getName());
adapter.setAcl(StorageAcl.PUBLIC_READ, StorageConstant.PHOTO_WATERMARKED_PATH, watermarkedFile.getName());
sourceMapper.addSourceWatermark(item.getGoodsId(), face.getId(), type.getType(), url);
tmpFile.add(watermarkedFile);
item.setUrl(url);
}
}).collect(Collectors.toList());
for (File file : tmpFile) {
file.delete();
}
return collect;
}
}
return defaultUrlList;
}
} }

View File

@ -44,4 +44,8 @@ public interface GoodsService {
VideoTaskStatusVO getTaskStatusByScenicId(Long userId, Long scenicId); VideoTaskStatusVO getTaskStatusByScenicId(Long userId, Long scenicId);
ApiResponse<GoodsDetailVO> sourceGoodsInfo(Long sourceId); ApiResponse<GoodsDetailVO> sourceGoodsInfo(Long sourceId);
List<GoodsUrlVO> sourceGoodsListPreview(GoodsReqQuery query);
List<GoodsUrlVO> sourceGoodsListDownload(GoodsReqQuery query);
} }

View File

@ -1,170 +0,0 @@
package com.ycwl.basic.task;
import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.ycwl.basic.image.watermark.ImageWatermarkFactory;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import com.ycwl.basic.image.watermark.exception.ImageWatermarkException;
import com.ycwl.basic.image.watermark.operator.IOperator;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
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.MemberSourceEntity;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.notify.entity.WxMpSrvConfig;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.utils.WxMpUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static com.ycwl.basic.constant.StorageConstant.PHOTO_WATERMARKED_PATH;
@Component
@EnableScheduling
@Slf4j
public class ImageWatermarkTask {
@Autowired
private FaceRepository faceRepository;
@Autowired
private ScenicRepository scenicRepository;
@Autowired
private SourceMapper sourceMapper;
@Data
@AllArgsConstructor
public static class Task {
public Long memberId;
public Long faceId;
}
public static ConcurrentLinkedQueue<Task> queue = new ConcurrentLinkedQueue<>();
public static void addTask(Long memberId, Long faceId) {
queue.add(new Task(memberId, faceId));
}
@Scheduled(fixedRate = 200L)
public void doTask() {
Task task = queue.poll();
if (task == null) {
return;
}
log.info("poll task: {}/{}", task, queue.size());
new Thread(() -> {
try {
runTask(task);
} catch (Exception e) {
log.error("run task error", e);
}
}).start();
}
public void runTask(Task task) {
// 生成二维码
FaceEntity face = faceRepository.getFace(task.faceId);
if (face == null) {
return;
}
ScenicEntity scenic = scenicRepository.getScenic(face.getScenicId());
if (scenic == null) {
return;
}
ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId());
MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(face.getScenicId());
if (scenicMpConfig == null) {
return;
}
List<SourceEntity> sourceEntities = sourceMapper.listImageByFaceRelation(task.memberId, task.faceId);
if (sourceEntities == null || sourceEntities.isEmpty()) {
return;
}
File qrcode = new File("qrcode_"+face.getMemberId()+".jpg");
try {
String urlLink = WxMpUtil.generateUrlLink(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/index", "scenicId=" + face.getScenicId() + "&faceId=" + face.getId());
QrCodeUtil.generate(urlLink + "?cq=", 300, 300, qrcode);
} catch (Exception e) {
log.error("generateWXQRCode error", e);
return;
}
IStorageAdapter adapter = StorageFactory.get(scenicConfig.getStoreType());
adapter.loadConfig(JSONObject.parseObject(scenicConfig.getStoreConfigJson(), Map.class));
// TODO
WatermarkInfo info = new WatermarkInfo();
info.setQrcodeFile(qrcode);
final ThreadPoolExecutor executor = new ThreadPoolExecutor(16, 128, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(128));
for (SourceEntity sourceEntity : sourceEntities) {
executor.execute(() -> {
String url;
try {
IOperator operator = ImageWatermarkFactory.get(scenicConfig.getWatermarkType());
File dstFile = new File(sourceEntity.getId() + ".jpg");
File watermarkedFile = new File(sourceEntity.getId() + "_w.png");
try {
HttpUtil.downloadFile(sourceEntity.getUrl(), dstFile);
} catch (Exception e) {
log.error("downloadFile error", e);
return;
}
info.setOriginalFile(dstFile);
info.setScenicLine(scenicConfig.getWatermarkScenicText());
info.setDatetime(sourceEntity.getCreateTime());
info.setDtFormat(scenicConfig.getWatermarkDtFormat());
info.setWatermarkedFile(watermarkedFile);
try {
operator.process(info);
} catch (ImageWatermarkException e) {
log.error("process error", e);
return;
}
url = adapter.uploadFile(watermarkedFile, PHOTO_WATERMARKED_PATH, watermarkedFile.getName());
adapter.setAcl(StorageAcl.PUBLIC_READ, PHOTO_WATERMARKED_PATH, watermarkedFile.getName());
} catch (ImageWatermarkException e) {
// 不支持
url = sourceEntity.getUrl();
}
MemberSourceEntity memberSource = new MemberSourceEntity();
memberSource.setMemberId(task.memberId);
memberSource.setScenicId(face.getScenicId());
memberSource.setFaceId(task.faceId);
memberSource.setType(sourceEntity.getType());
memberSource.setSourceId(sourceEntity.getId());
memberSource.setWaterUrl(url);
sourceMapper.updateWaterUrl(memberSource);
});
}
try {
Thread.sleep(2000L);
log.info("executor等待被结束[A:{}/T:{}/F:{}]", executor.getActiveCount(), executor.getTaskCount(), executor.getCompletedTaskCount());
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
log.info("executor已结束[A:{}/T:{}/F:{}]", executor.getActiveCount(), executor.getTaskCount(), executor.getCompletedTaskCount());
} catch (InterruptedException e) {
return;
}
}
}

View File

@ -16,6 +16,10 @@
(#{item.scenicId}, #{item.faceId}, #{item.memberId}, #{item.sourceId}, #{item.isBuy}, #{item.type}, #{item.orderId}, #{item.isFree}) (#{item.scenicId}, #{item.faceId}, #{item.memberId}, #{item.sourceId}, #{item.isBuy}, #{item.type}, #{item.orderId}, #{item.isFree})
</foreach> </foreach>
</insert> </insert>
<insert id="addSourceWatermark">
insert source_watermark(source_id, face_id, watermark_type, watermark_url)
values (#{sourceId}, #{faceId}, #{type}, #{url})
</insert>
<update id="update"> <update id="update">
update source update source
<set> <set>
@ -38,11 +42,6 @@
</set> </set>
where member_id = #{memberId} and face_id = #{faceId} and `type` = #{type} where member_id = #{memberId} and face_id = #{faceId} and `type` = #{type}
</update> </update>
<update id="updateWaterUrl">
update member_source
set water_url = #{waterUrl}
where member_id = #{memberId} and source_id = #{sourceId} and `type` = #{type}
</update>
<delete id="deleteById"> <delete id="deleteById">
delete from source where id = #{id} delete from source where id = #{id}
</delete> </delete>
@ -130,7 +129,7 @@
limit 1 limit 1
</select> </select>
<select id="listUser" resultType="com.ycwl.basic.model.pc.source.resp.SourceRespVO"> <select id="listUser" resultType="com.ycwl.basic.model.pc.source.resp.SourceRespVO">
select so.id, ms.scenic_id, device_id, url, ms.is_free, ms.water_url, so.create_time, so.update_time,sc.`name` as scenicName, ms.is_buy, video_url select so.id, ms.scenic_id, device_id, url, ms.is_free, so.create_time, so.update_time,sc.`name` as scenicName, ms.is_buy, video_url
from member_source ms from member_source ms
left join source so on ms.source_id = so.id left join source so on ms.source_id = so.id
left join scenic sc on sc.id = so.scenic_id left join scenic sc on sc.id = so.scenic_id
@ -200,4 +199,19 @@
from source from source
where id = #{id} where id = #{id}
</select> </select>
<select id="listSourceWatermark" resultType="com.ycwl.basic.model.pc.source.entity.SourceWatermarkEntity">
select *
from source_watermark
where source_id in
<foreach collection="sourceIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
<if test="faceId == null">
and face_id is null
</if>
<if test="faceId != null">
and face_id = #{faceId}
</if>
and watermark_type = #{watermarkType}
</select>
</mapper> </mapper>

View File

@ -263,4 +263,14 @@
) a ) a
order by createTime desc order by createTime desc
</select> </select>
<select id="getUserRecentEnterType" resultType="java.lang.Long">
select morph_id
from statistics
where type = 10 and member_id = #{memberId}
<if test="endTime!= null">
and create_time &lt;= #{endTime}
</if>
order by create_time desc
limit 1
</select>
</mapper> </mapper>

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -0,0 +1,31 @@
package com.ycwl.basic.image.watermark.operator;
import com.ycwl.basic.image.watermark.entity.WatermarkInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
@SpringBootTest
@RunWith(SpringRunner.class)
public class WatermarkOperatorTest {
@Test
public void testProcess() throws Exception {
WatermarkInfo info = new WatermarkInfo();
info.setOriginalFile(new File("e2d32de7-6e85-4e07-b42f-477347073539.jpg"));
info.setQrcodeFile(new File("cxzh_t.jpg"));
info.setScenicLine("川西竹海一日游!");
info.setDatetimeLine("2XXX年XX月XX日 留念");
info.setWatermarkedFile(new File("test2.png"));
IOperator operator = new NormalWatermarkOperator();
operator.process(info);
info.setWatermarkedFile(new File("test1.png"));
operator = new LeicaWatermarkOperator();
operator.process(info);
}
}