diff --git a/pom.xml b/pom.xml
index db7bdb0b..f781cd68 100644
--- a/pom.xml
+++ b/pom.xml
@@ -316,6 +316,17 @@
1.41.5
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
diff --git a/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java b/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java
index b3aee7cd..23fb9496 100644
--- a/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java
+++ b/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java
@@ -1,24 +1,22 @@
package com.ai.da.common.RabbitMQ;
-import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.model.dto.GenerateThroughImageTextDTO;
import com.ai.da.model.vo.GenerateResultVO;
+import com.ai.da.model.vo.PoseTransformationVO;
import com.ai.da.service.GenerateService;
import com.ai.da.service.UserLikeGroupService;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
-import org.apache.tomcat.jni.Time;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.io.IOException;
@@ -258,6 +256,55 @@ public class GenerateConsumer {
log.info("============ProcessRelightResult End listening==========");
}
+ public void processPoseTransformResult(Message msg, Channel channel) {
+ log.info("============ProcessPoseTransformResult listening==========");
+ long start = System.currentTimeMillis();
+
+ Map generateResult = JSONObject.parseObject(msg.getBody(), Map.class);
+ log.info("PoseTransformation response : {}", generateResult);
+
+ try {
+ log.info("tasks_id : {} start ", generateResult.get("tasks_id"));
+ if (generateResult.get("status").equals("SUCCESS")) {
+ String gifUrl = generateResult.get("gif_url");
+ String taskId = generateResult.get("tasks_id");
+ String videoUrl = generateResult.get("video_url");
+ String imageUrl = generateResult.get("image_url");
+ generateService.processPoseTransformResult(taskId, gifUrl, videoUrl, imageUrl);
+ } else {
+ // 修改redis中的数据状态为exception
+ String key = generateResultKey + ":" + generateResult.get("tasks_id");
+ redisUtil.addToString(key, new Gson().toJson(new PoseTransformationVO(null, generateResult.get("tasks_id"),null, null, null, (byte)0, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
+ // 将异常信息存到exception中
+ HashMap exceptionInfo = new HashMap<>();
+ exceptionInfo.put(generateResult.get("tasks_id"), generateResult.get("message"));
+ // 存redis
+ redisUtil.addToMap(exceptionMapKey, exceptionInfo);
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ try {
+ channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
+ // 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
+ redisUtil.removeFromZSet(consumptionOrderKey, generateResult.get("tasks_id"));
+ } catch (IOException exception) {
+ log.error("手动确认,取消返回队列,不再重新消费");
+ }
+ // 将入参和错误信息存入数据库
+ String exceptionMessage = JSONObject.toJSONString(generateResult) +
+ " Exception message : " + e.getMessage();
+ HashMap exceptionInfo = new HashMap<>();
+ exceptionInfo.put(String.valueOf(generateResult.get("tasks_id")), exceptionMessage);
+ // 存redis
+ redisUtil.addToMap(exceptionMapKey, exceptionInfo);
+ }
+
+ long end = System.currentTimeMillis();
+ log.info("tasks_id : {}, end , message : {}, 执行时长: {} 毫秒", generateResult.get("tasks_id"), generateResult.get("message"), (end - start));
+ log.info("============ProcessPoseTransformResult End listening==========");
+
+ }
+
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
@RabbitHandler
public void generateConsumer1(Message msg, Channel channel) {
@@ -329,4 +376,10 @@ public class GenerateConsumer {
public void getRelightResult(Message msg, Channel channel) {
processRelightResult(msg, channel);
}
+
+ @RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransform}")
+ @RabbitHandler
+ public void getPoseTransformationResult(Message msg, Channel channel) {
+ processPoseTransformResult(msg, channel);
+ }
}
diff --git a/src/main/java/com/ai/da/common/RabbitMQ/RabbitMQProperties.java b/src/main/java/com/ai/da/common/RabbitMQ/RabbitMQProperties.java
index 29a2e1de..55990193 100644
--- a/src/main/java/com/ai/da/common/RabbitMQ/RabbitMQProperties.java
+++ b/src/main/java/com/ai/da/common/RabbitMQ/RabbitMQProperties.java
@@ -20,6 +20,7 @@ public class RabbitMQProperties {
private String generateResult;
private String toProductImageResult;
private String relightResult;
+ private String poseTransform;
}
@Data
diff --git a/src/main/java/com/ai/da/common/constant/CommonConstant.java b/src/main/java/com/ai/da/common/constant/CommonConstant.java
index b5dabb24..8b790c52 100644
--- a/src/main/java/com/ai/da/common/constant/CommonConstant.java
+++ b/src/main/java/com/ai/da/common/constant/CommonConstant.java
@@ -30,6 +30,8 @@ public class CommonConstant {
public static final String GENERATE_LOGO_SINGLE_CANCEL = "/api/generate_single_logo_cancel/";
+ public static final String POSE_TRANSFORMATION_CANCEL = "/api/pose_transform_cancel/";
+
public static final String PYTHON_PORT_9996 = "9996";
public static final String PYTHON_PORT_9997 = "9997";
diff --git a/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java b/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java
index e8565984..2711d04b 100644
--- a/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java
+++ b/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java
@@ -35,6 +35,7 @@ public enum CreditsEventsEnum {
RELIGHT("Relight","5"),
QUESTIONNAIRE("Questionnaire","100"),
IMAGE_TO_SKETCH("ImageToSketch","5"),
+ POSE_TRANSFORMATION("PoseTransformation","10"),
OTHER("Other","5");
diff --git a/src/main/java/com/ai/da/common/utils/FileUtil.java b/src/main/java/com/ai/da/common/utils/FileUtil.java
index 43801af6..30f4018b 100644
--- a/src/main/java/com/ai/da/common/utils/FileUtil.java
+++ b/src/main/java/com/ai/da/common/utils/FileUtil.java
@@ -174,7 +174,7 @@ public class FileUtil extends cn.hutool.core.io.FileUtil {
URL url = new URL(path);
return url.openStream();
} catch (IOException ioException) {
- log.error("获取文件尺寸异常###{}###path##{}", ExceptionUtil.stacktraceToString(ioException), path);
+ log.error("获取源文件异常###{}###path##{}", ExceptionUtil.stacktraceToString(ioException), path);
throw new BusinessException("get.file.failed");
}
}
diff --git a/src/main/java/com/ai/da/common/utils/MailUtil.java b/src/main/java/com/ai/da/common/utils/MailUtil.java
new file mode 100644
index 00000000..cd5882ec
--- /dev/null
+++ b/src/main/java/com/ai/da/common/utils/MailUtil.java
@@ -0,0 +1,168 @@
+package com.ai.da.common.utils;
+
+import com.ai.da.model.dto.BasicEmailParamDTO;
+import com.alibaba.fastjson.JSONObject;
+import com.sun.mail.smtp.SMTPTransport;
+import io.netty.util.internal.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.InputStreamSource;
+import org.springframework.mail.MailException;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import javax.annotation.Resource;
+import javax.mail.MessagingException;
+import javax.mail.internet.*;
+import java.util.List;
+import java.util.Objects;
+
+@Slf4j
+@Component
+public class MailUtil {
+
+ @Resource
+ private JavaMailSender javaMailSender;
+
+ @Resource
+ private TemplateEngine templateEngine;
+
+ /**
+ * 发送邮件 - 默认发件人
+ *
+ * @param basicEmailParamDTO 发送邮件所需参数
+ * @param inputStreamSource 附件(如果有)
+ */
+ public int sendMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
+ MimeMessage mimeMessage = createSimpleMail(basicEmailParamDTO, fileName, inputStreamSource);
+ // 提取配置
+ String host;
+ String username;
+ String password;
+ if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
+ host = ((JavaMailSenderImpl) javaMailSender).getHost();
+ } else {
+ host = basicEmailParamDTO.getServiceAddress();
+ }
+ if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getSenderUser())) {
+ username = ((JavaMailSenderImpl) javaMailSender).getUsername();
+ } else {
+ username = basicEmailParamDTO.getSenderUser();
+ }
+ if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
+ password = ((JavaMailSenderImpl) javaMailSender).getPassword();
+ } else {
+ password = basicEmailParamDTO.getPassword();
+ }
+ return sendMail(mimeMessage, host, username, password);
+ }
+
+ private int sendMail(MimeMessage mimeMessage, String host, String username, String password) throws MessagingException {
+ SMTPTransport transport = null;
+ try {
+ // 获取 SMTPTransport
+ transport = (SMTPTransport) mimeMessage.getSession().getTransport("smtp");
+ // 连接到 SMTP 服务器
+ transport.connect(host, username, password);
+ // 发送邮件
+ transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
+ // 获取 SMTP 服务器的响应
+ String lastServerResponse = transport.getLastServerResponse();
+ int lastReturnCode = transport.getLastReturnCode();
+
+ log.info("SMTP 状态码: {}, SMTP 服务器响应: {}", lastReturnCode, lastServerResponse);
+ return lastReturnCode;
+ } catch (MailException | MessagingException e) {
+ // 记录日志或执行其他补偿逻辑
+ log.info("邮件发送失败:{}", e.getMessage());
+ } finally {
+ // 关闭连接
+ assert transport != null;
+ transport.close();
+ }
+ return 0;
+ }
+
+ /**
+ * 创建一封邮件
+ *
+ * @param basicEmailParamDTO 创建邮件需要的参数
+ * @param inputStreamSource 附件(如果有)
+ * @return 一封邮件
+ */
+ private MimeMessage createSimpleMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
+ // 创建邮件对象
+ MimeMessage message = javaMailSender.createMimeMessage();
+ // 使用 MimeMessageHelper 简化邮件内容和附件的设置
+ MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
+ // 设置发件人
+ mimeMessageHelper.setFrom(new InternetAddress(basicEmailParamDTO.getSenderUserMail()));
+ // 设置收件人
+ mimeMessageHelper.setTo(basicEmailParamDTO.getMailTo());
+ // 设置抄送人
+ if (basicEmailParamDTO.getCc() != null && basicEmailParamDTO.getCc().length > 0) {
+ mimeMessageHelper.setCc(basicEmailParamDTO.getCc());
+ }
+ // 设置暗送人
+ if (basicEmailParamDTO.getBcc() != null && basicEmailParamDTO.getBcc().length > 0) {
+ mimeMessageHelper.setBcc(basicEmailParamDTO.getBcc());
+ }
+ // 设置邮件主题
+ mimeMessageHelper.setSubject(basicEmailParamDTO.getSubject());
+ // 设置邮件内容(HTML 格式)
+ mimeMessageHelper.setText(basicEmailParamDTO.getContent(), true);
+ // 设置附件
+ if (inputStreamSource != null) {
+ mimeMessageHelper.addAttachment(fileName, inputStreamSource);
+ }
+ return message;
+ }
+
+
+ /**
+ * 设置实体参数
+ *
+ * @param mailTo 接收邮件的邮箱地址
+ * @param jsonObject 模板中变量的值
+ * @return 返回一个MailEntity
+ * @throws AddressException 邮箱地址值异常
+ */
+ public BasicEmailParamDTO setBasicEmailParams(List mailTo, JSONObject jsonObject, String templatePath, String title) throws AddressException {
+ BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
+ basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
+ basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
+ basicEmailParamDTO.setSubject(title);
+ // todo 邮件模板不存在的报错与重试机制
+ basicEmailParamDTO.setContent(setContent(jsonObject, templatePath));
+ return basicEmailParamDTO;
+ }
+
+ /**
+ * 将地址转换为InternetAddress类型
+ *
+ * @param addressList 普通的地址字符串列表
+ * @return InternetAddress类型的地址列表
+ * @throws AddressException 地址异常
+ */
+ public InternetAddress[] getInternetAddressList(List addressList) throws AddressException {
+ InternetAddress[] toAddress = new InternetAddress[addressList.size()];
+ for (String address : addressList) {
+ toAddress[addressList.indexOf(address)] = new InternetAddress(address);
+ }
+ return toAddress;
+ }
+
+ public String setContent(JSONObject jsonObject, String templatePath) {
+ Context context = new Context();
+ if (Objects.nonNull(jsonObject)) {
+ for (String key : jsonObject.keySet()) {
+ context.setVariable(key, jsonObject.get(key));
+ }
+ }
+ return templateEngine.process(templatePath, context);
+ }
+
+}
diff --git a/src/main/java/com/ai/da/controller/EmailController.java b/src/main/java/com/ai/da/controller/EmailController.java
new file mode 100644
index 00000000..eebe05e0
--- /dev/null
+++ b/src/main/java/com/ai/da/controller/EmailController.java
@@ -0,0 +1,42 @@
+package com.ai.da.controller;
+
+import com.ai.da.service.EmailService;
+import com.alibaba.fastjson.JSONObject;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.thymeleaf.context.Context;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+
+@Api(tags = "邮件模块")
+@Slf4j
+@RestController
+@RequestMapping("/api/email")
+public class EmailController {
+
+ @Resource
+ private EmailService emailService;
+
+ @GetMapping("/loadSingleTemplate")
+ public void loadSingleEmailTemplate(){
+ emailService.loadSingleEmailTemplate("templates\\upgrade\\122899_AiDA发版完成通知中文版.html");
+ }
+
+ @GetMapping("/loadTemplate")
+ public void loadTemplatesFromResources(){
+ emailService.loadTemplatesFromResources("templates");
+ }
+
+ @GetMapping("/sendEmailTest")
+ public void sendEmailTest(){
+ Context context = new Context();
+ context.setVariable("username", "小白");
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("username", "小白");
+ emailService.sendEmail(Collections.singletonList("xupei3360@163.com"), jsonObject, "132124_affiliate_accepted_en.html", "测试邮件", null, null );
+ }
+}
diff --git a/src/main/java/com/ai/da/controller/GenerateController.java b/src/main/java/com/ai/da/controller/GenerateController.java
index 94a0da8a..8dbd4478 100644
--- a/src/main/java/com/ai/da/controller/GenerateController.java
+++ b/src/main/java/com/ai/da/controller/GenerateController.java
@@ -1,10 +1,7 @@
package com.ai.da.controller;
import com.ai.da.common.response.Response;
-import com.ai.da.model.dto.GenerateLikeDTO;
-import com.ai.da.model.dto.GenerateModifyDTO;
-import com.ai.da.model.dto.GenerateThroughImageTextDTO;
-import com.ai.da.model.dto.ImageToSketchDTO;
+import com.ai.da.model.dto.*;
import com.ai.da.model.vo.*;
import com.ai.da.service.GenerateService;
import io.swagger.annotations.Api;
@@ -12,11 +9,11 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
-
/**
* @author XP
*/
@@ -88,7 +85,7 @@ public class GenerateController {
@ApiOperation(value = "imageToSketch")
@PostMapping("/imageToSketch")
public Response imageToSketch(@Valid @RequestBody ImageToSketchDTO imageToSketchDTO) {
- return Response.success(generateService.imageToSketch(imageToSketchDTO));
+ return Response.success(generateService.imageToSketch(imageToSketchDTO, null, null));
}
// modifySketch
@@ -98,4 +95,50 @@ public class GenerateController {
return Response.success(generateService.modifySketch(generateModifyDTO));
}
+ @ApiOperation(value = "请求进行姿势变换")
+ @GetMapping("/poseTransform")
+ public Response poseTransform(@ApiParam("projectId") @RequestParam Long projectId,
+ @ApiParam("productImage") @RequestParam String productImage,
+ @ApiParam("poseId") @RequestParam int poseId) {
+ return Response.success(generateService.poseTransform(projectId, productImage, poseId));
+ }
+
+ @ApiOperation(value = "获取姿势变换生成结果")
+ @GetMapping("/poseTransformResult")
+ public Response getPoseTransformationResults(@ApiParam("taskId") @RequestParam String taskId) {
+ PoseTransformationVO generateResult = generateService.getPoseTransformationResult(taskId);
+ return Response.success(generateResult);
+ }
+
+ @ApiOperation(value = "修改模特比例")
+ @PostMapping("/modifyProportion")
+ public Response modifyModelProportion(@Valid @RequestBody ModifyModelProportionDTO proportionDTO){
+ String path = generateService.modifyModelProportion(proportionDTO);
+ return Response.success(path);
+ }
+
+ @ApiOperation(value = "拼贴图生成线稿")
+ @PostMapping("/genSketchRecon")
+ public Response sketchReconstructionGenerate(@Valid @RequestBody SketchReconstructionDTO sketchReconstructionDTO){
+ GenerateResultVO generateResultVO = generateService.sketchReconstructionGenerate(sketchReconstructionDTO);
+ return Response.success(generateResultVO);
+ }
+
+ @ApiOperation(value = "拼贴图画布保存")
+ @GetMapping("/saveReconCanvas")
+ public Response sketchReconstructionSave(@RequestParam("file") MultipartFile file, @RequestParam("projectId") Long projectId){
+ generateService.sketchReconstructionSave(file, projectId);
+ return Response.success("success");
+ }
+
+ @ApiOperation(value = "获取拼贴图画布")
+ @GetMapping("/getReconCanvas")
+ public Response getSketchReconstruction(@RequestParam("projectId") Long projectId){
+ SketchReconstructionVO sketchReconstruction = generateService.getSketchReconstruction(projectId);
+ return Response.success(sketchReconstruction);
+ }
+
+
+
+
}
diff --git a/src/main/java/com/ai/da/mapper/primary/EmailLogMapper.java b/src/main/java/com/ai/da/mapper/primary/EmailLogMapper.java
new file mode 100644
index 00000000..8bdeabc9
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/EmailLogMapper.java
@@ -0,0 +1,7 @@
+package com.ai.da.mapper.primary;
+
+import com.ai.da.common.config.mybatis.plus.CommonMapper;
+import com.ai.da.mapper.primary.entity.EmailLog;
+
+public interface EmailLogMapper extends CommonMapper {
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/EmailTemplateMapper.java b/src/main/java/com/ai/da/mapper/primary/EmailTemplateMapper.java
new file mode 100644
index 00000000..570cc302
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/EmailTemplateMapper.java
@@ -0,0 +1,7 @@
+package com.ai.da.mapper.primary;
+
+import com.ai.da.common.config.mybatis.plus.CommonMapper;
+import com.ai.da.mapper.primary.entity.EmailTemplate;
+
+public interface EmailTemplateMapper extends CommonMapper {
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/PoseTransformationMapper.java b/src/main/java/com/ai/da/mapper/primary/PoseTransformationMapper.java
new file mode 100644
index 00000000..4c6da51b
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/PoseTransformationMapper.java
@@ -0,0 +1,7 @@
+package com.ai.da.mapper.primary;
+
+import com.ai.da.common.config.mybatis.plus.CommonMapper;
+import com.ai.da.mapper.primary.entity.PoseTransformation;
+
+public interface PoseTransformationMapper extends CommonMapper {
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/SketchReconstructionMapper.java b/src/main/java/com/ai/da/mapper/primary/SketchReconstructionMapper.java
new file mode 100644
index 00000000..712191b0
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/SketchReconstructionMapper.java
@@ -0,0 +1,7 @@
+package com.ai.da.mapper.primary;
+
+import com.ai.da.mapper.primary.entity.SketchReconstruction;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface SketchReconstructionMapper extends BaseMapper {
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/EmailLog.java b/src/main/java/com/ai/da/mapper/primary/entity/EmailLog.java
new file mode 100644
index 00000000..4fb2e205
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/entity/EmailLog.java
@@ -0,0 +1,35 @@
+package com.ai.da.mapper.primary.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName("t_email_log")
+public class EmailLog extends BaseEntity {
+
+ private Long templateId;
+
+ private String parameter;
+
+ // from是SQL关键字,直接使用会报错
+ private String sender;
+
+ private String recipients;
+
+ private String cc = null;
+
+ private String bcc = null;
+
+ private String subject;
+
+ /**
+ * failed 邮件发送失败(如网络问题、邮件服务器问题等)。
+ * retrying 邮件发送失败后,正在重试发送。
+ * delivered 邮件已成功投递到收件人的邮箱服务器。
+ */
+ private String status;
+
+ private int retryCount = 0;
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/EmailTemplate.java b/src/main/java/com/ai/da/mapper/primary/entity/EmailTemplate.java
new file mode 100644
index 00000000..e43ebf09
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/entity/EmailTemplate.java
@@ -0,0 +1,24 @@
+package com.ai.da.mapper.primary.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName("t_email_template")
+public class EmailTemplate extends BaseEntity {
+
+ // 考虑添加唯一索引
+ private String templateName;
+
+ private String templatePath;
+
+ private String content;
+
+ private int version;
+
+ private String language;
+
+ private byte isDeleted = 0;
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/Generate.java b/src/main/java/com/ai/da/mapper/primary/entity/Generate.java
index 7a3157ab..ef5f6809 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/Generate.java
+++ b/src/main/java/com/ai/da/mapper/primary/entity/Generate.java
@@ -85,6 +85,15 @@ public class Generate {
*/
private Long styleImageElementId;
+ /**
+ * 由拼贴图生成线稿的项目id
+ */
+ private Long projectId;
+ /**
+ * 输入模型的拼贴图
+ */
+ private String inputImageUrl;
+
/**
* 创建时间
*/
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/PoseTransformation.java b/src/main/java/com/ai/da/mapper/primary/entity/PoseTransformation.java
new file mode 100644
index 00000000..55c95939
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/entity/PoseTransformation.java
@@ -0,0 +1,33 @@
+package com.ai.da.mapper.primary.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@TableName("t_pose_transformation")
+@Data
+public class PoseTransformation extends BaseEntity {
+
+ private Long projectId;
+
+ private Long accountId;
+
+ private String uniqueId;
+
+ private String productImage;
+
+ private int poseId;
+
+ private String gifUrl;
+
+ private String videoUrl;
+ // GIF第一帧截图
+ private String imageUrl;
+
+ private byte isLiked;
+
+ private byte isDeleted;
+
+
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/SketchReconstruction.java b/src/main/java/com/ai/da/mapper/primary/entity/SketchReconstruction.java
new file mode 100644
index 00000000..3ba957a3
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/entity/SketchReconstruction.java
@@ -0,0 +1,20 @@
+package com.ai.da.mapper.primary.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName("t_sketch_reconstruction")
+public class SketchReconstruction extends BaseEntity{
+
+ private Long projectId;
+ // 最后一次拼贴图生成的sketch
+ private String collageImgSketchUrl;
+
+ private Long generateDetailId;
+
+ private String canvasUrl;
+
+}
diff --git a/src/main/java/com/ai/da/model/dto/BasicEmailParamDTO.java b/src/main/java/com/ai/da/model/dto/BasicEmailParamDTO.java
new file mode 100644
index 00000000..e973f914
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/BasicEmailParamDTO.java
@@ -0,0 +1,29 @@
+package com.ai.da.model.dto;
+
+import lombok.Data;
+
+import javax.mail.internet.InternetAddress;
+
+@Data
+public class BasicEmailParamDTO {
+ /** 邮箱服务器 */
+ private String serviceAddress;
+ /** 邮箱服务器端口 */
+ private String servicePort;
+ /** 发件人邮箱地址 */
+ private String senderUserMail;
+ /** 发件人账号 */
+ private String senderUser;
+ /** 发件人密码 */
+ private String password;
+ /** 邮件标题 */
+ private String subject;
+ /** 邮件内容 */
+ private String content;
+ /** 收件人邮箱地址 */
+ private InternetAddress[] mailTo;
+ /** 抄送人 */
+ private InternetAddress[] cc;
+ /** 暗抄送人 */
+ private InternetAddress[] bcc;
+}
diff --git a/src/main/java/com/ai/da/model/dto/ImageToSketchDTO.java b/src/main/java/com/ai/da/model/dto/ImageToSketchDTO.java
index 983172c9..e356a7d1 100644
--- a/src/main/java/com/ai/da/model/dto/ImageToSketchDTO.java
+++ b/src/main/java/com/ai/da/model/dto/ImageToSketchDTO.java
@@ -19,4 +19,13 @@ public class ImageToSketchDTO {
@ApiModelProperty("性别")
private String gender;
+
+ public ImageToSketchDTO() {
+ }
+
+ public ImageToSketchDTO(Long elementId, String style, String gender) {
+ this.elementId = elementId;
+ this.style = style;
+ this.gender = gender;
+ }
}
diff --git a/src/main/java/com/ai/da/model/dto/ModifyModelProportionDTO.java b/src/main/java/com/ai/da/model/dto/ModifyModelProportionDTO.java
new file mode 100644
index 00000000..499b9b51
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/ModifyModelProportionDTO.java
@@ -0,0 +1,68 @@
+package com.ai.da.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("ModifyModelProportionDTO")
+public class ModifyModelProportionDTO {
+ @ApiModelProperty("模特id")
+ @NotNull(message = "model id cannot be empty")
+ private Long id;
+
+ @ApiModelProperty("Library || System")
+ @NotBlank(message = "model type cannot be empty")
+ private String type;
+
+ @ApiModelProperty("top")
+ @NotNull(message = "top cannot be empty")
+ private Integer top;
+
+ @ApiModelProperty("bottom")
+ @NotNull(message = "bottom cannot be empty")
+ private Integer bottom;
+
+ @ApiModelProperty("stretch")
+ @NotNull(message = "stretch cannot be empty")
+ private Float stretch;
+
+ @ApiModelProperty("模特minio地址")
+ @NotBlank(message = "modelPath type cannot be empty")
+ private String modelPath;
+
+ @NotNull(message = "handLeft cannot be null")
+ @NotEmpty(message = "handLeft cannot be empty")
+ @ApiModelProperty("handLeft")
+ private Float[] handLeft;
+
+ @NotNull(message = "handRight cannot be null")
+ @NotEmpty(message = "handRight cannot be empty")
+ @ApiModelProperty("handRight")
+ private Float[] handRight;
+
+ @NotNull(message = "shoulderLeft cannot be null")
+ @NotEmpty(message = "shoulderLeft cannot be empty")
+ @ApiModelProperty("shoulderLeft")
+ private Float[] shoulderLeft;
+
+ @NotNull(message = "shoulderRight cannot be null")
+ @NotEmpty(message = "shoulderRight cannot be empty")
+ @ApiModelProperty("shoulderRight")
+ private Float[] shoulderRight;
+
+ @NotNull(message = "waistbandLeft cannot be null")
+ @NotEmpty(message = "waistbandLeft cannot be empty")
+ @ApiModelProperty("waistbandLeft")
+ private Float[] waistbandLeft;
+
+ @NotNull(message = "waistbandRight cannot be null")
+ @NotEmpty(message = "waistbandRight cannot be empty")
+ @ApiModelProperty("waistbandRight")
+ private Float[] waistbandRight;
+
+}
diff --git a/src/main/java/com/ai/da/model/dto/SketchReconstructionDTO.java b/src/main/java/com/ai/da/model/dto/SketchReconstructionDTO.java
new file mode 100644
index 00000000..e44fd76c
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/SketchReconstructionDTO.java
@@ -0,0 +1,19 @@
+package com.ai.da.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(value = "sketch拼贴")
+public class SketchReconstructionDTO {
+
+ @ApiModelProperty("项目id")
+ private Long projectId;
+
+ @ApiModelProperty("拼贴图的base64数据")
+ private String collagePicture;
+ // 识别衣服类型用
+ @ApiModelProperty("性别")
+ private String gender;
+}
diff --git a/src/main/java/com/ai/da/model/vo/PoseTransformationVO.java b/src/main/java/com/ai/da/model/vo/PoseTransformationVO.java
new file mode 100644
index 00000000..fb7e57b5
--- /dev/null
+++ b/src/main/java/com/ai/da/model/vo/PoseTransformationVO.java
@@ -0,0 +1,25 @@
+package com.ai.da.model.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PoseTransformationVO {
+
+ private Long id;
+
+ private String taskId;
+
+ private String gifUrl;
+
+ private String videoUrl;
+ // GIF第一帧截图
+ private String imageUrl;
+
+ private byte isLiked;
+
+ private String status;
+}
diff --git a/src/main/java/com/ai/da/model/vo/SketchReconstructionVO.java b/src/main/java/com/ai/da/model/vo/SketchReconstructionVO.java
new file mode 100644
index 00000000..047d2d43
--- /dev/null
+++ b/src/main/java/com/ai/da/model/vo/SketchReconstructionVO.java
@@ -0,0 +1,15 @@
+package com.ai.da.model.vo;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+@Data
+public class SketchReconstructionVO {
+
+ private JSONObject canvasFile;
+
+ private String collageSketchUrl;
+
+ private boolean isLiked;
+
+}
diff --git a/src/main/java/com/ai/da/python/PythonService.java b/src/main/java/com/ai/da/python/PythonService.java
index 02c9a245..3e050c7f 100644
--- a/src/main/java/com/ai/da/python/PythonService.java
+++ b/src/main/java/com/ai/da/python/PythonService.java
@@ -4016,4 +4016,124 @@ public class PythonService {
//生成失败
throw new BusinessException("segProduct.interface.exception");
}
+ public Boolean poseTransformation(String productImage, int poseId, String taskId) {
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
+ .readTimeout(60, TimeUnit.SECONDS)//读取超时(单位:秒)
+ .writeTimeout(60, TimeUnit.SECONDS)//写入超时(单位:秒)
+ .build();
+ MediaType mediaType = MediaType.parse("application/json");
+ Map content = Maps.newHashMap();
+ content.put("image_url", productImage);
+ content.put("tasks_id", taskId);
+ content.put("pose_id", String.valueOf(poseId));
+ RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
+
+ log.info("poseTransformation 请求地址: {}", accessPythonIp + ":" + accessPythonPort + "/api/pose_transform");
+ Request request = new Request.Builder()
+ .url(accessPythonIp + ":" + accessPythonPort + "/api/pose_transform")
+ .method("POST", body)
+ .addHeader("Content-Type", "application/json")
+ .build();
+ Response response = null;
+ String bodyString;
+ try {
+ log.info("poseTransformation请求入参content###{}", JSON.toJSONString(content));
+ response = client.newCall(request).execute();
+ } catch (IOException ioException) {
+ log.error("PythonService##poseTransformation异常###{}", ExceptionUtil.getThrowableList(ioException));
+ throw new BusinessException(ioException.getMessage());
+ }
+
+ // 判断是否生成失败
+ if (Objects.isNull(response.body())) {
+ log.error("PythonService##poseTransformation异常###{}", "response or body is empty!");
+ throw new BusinessException("PythonService##poseTransformation异常###: response or body is empty!");
+ } else if (response.code() != HttpURLConnection.HTTP_OK) {
+ log.error("PythonService##poseTransformation异常###{}", "Response error!Response code ## " + response.code() + " ##");
+ throw new BusinessException("PythonService##poseTransformation异常### Response error!Response code ## " + response.code() + " ##");
+ } else {
+ try {
+ bodyString = response.body().string();
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ throw new BusinessException(e.getMessage());
+ }
+ }
+ JSONObject jsonObject = JSON.parseObject(bodyString);
+ Boolean result = JSON.parseObject(JSON.toJSONString(response)).getBoolean("successful");
+
+ if (result && jsonObject.get("code").equals(200)) {
+ log.info("poseTransformation##responseObject###{}", jsonObject);
+ return Boolean.TRUE;
+ } else {
+ log.info("poseTransformation失败###{}", jsonObject);
+ log.info("poseTransformation Exception! Code : {}", jsonObject.get("code"));
+ return Boolean.FALSE;
+ }
+ }
+
+ public String modifyModelProportion(String mannequinPath, Float scale, String name, int top, int bottom) {
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
+ .readTimeout(60, TimeUnit.SECONDS)//读取超时(单位:秒)
+ .writeTimeout(60, TimeUnit.SECONDS)//写入超时(单位:秒)
+ .build();
+
+ MediaType mediaType = MediaType.parse("application/json");
+ Map content = Maps.newHashMap();
+ // 模特的minio地址
+ content.put("mannequins", mannequinPath);
+ // 缩放比
+ content.put("scale", scale.toString());
+ // 结果存放桶名
+ content.put("bucket_name", "aida-users");
+ // 模特名uuid
+ content.put("mannequin_name", name);
+ content.put("top", String.valueOf(top));
+ content.put("bottom", String.valueOf(bottom));
+ RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
+
+ log.info("modifyModelProportion 请求地址: {},\n 参数:{}", accessPythonIp + ":" + accessPythonPort + "/api/mannequins_edit", JSON.toJSONString(content));
+ Request request = new Request.Builder()
+ .url(accessPythonIp + ":" + accessPythonPort + "/api/mannequins_edit")
+ .method("POST", body)
+ .addHeader("Content-Type", "application/json")
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ } catch (IOException ioException) {
+ log.error("PythonService##modifyModelProportion异常###{}", ExceptionUtil.getThrowableList(ioException));
+ throw new BusinessException("generate.interface.error");
+ }
+ int responseCode = response.code();
+ String bodyString;
+ try {
+ bodyString = response.body().string();
+ if (responseCode != HttpURLConnection.HTTP_OK) {
+ // 基本不会有除200以外的code
+ log.info("modifyModelProportion 失败。 Response code {}", responseCode);
+ throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode);
+ }
+ JSONObject jsonObject = JSON.parseObject(bodyString);
+ if (response.isSuccessful() && jsonObject.get("msg").equals("OK!")) {
+ String modifiedModel = jsonObject.get("data").toString();
+ log.info("modifyModelProportion 结果 : {}", modifiedModel);
+ return modifiedModel;
+ }else {
+ log.info("modifyModelProportion 失败。 Response code {}", responseCode);
+ throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode);
+ }
+ } catch (IOException e) {
+ log.error("modifyModelProportion 失败; error message => {}", e.getMessage());
+ response.close();
+ throw new BusinessException("generate.interface.error");
+ } finally {
+ response.close();
+ }
+ }
+
}
diff --git a/src/main/java/com/ai/da/service/EmailService.java b/src/main/java/com/ai/da/service/EmailService.java
new file mode 100644
index 00000000..00373e8a
--- /dev/null
+++ b/src/main/java/com/ai/da/service/EmailService.java
@@ -0,0 +1,138 @@
+package com.ai.da.service;
+
+import com.ai.da.mapper.primary.entity.Account;
+import com.ai.da.mapper.primary.entity.TrialOrder;
+import com.ai.da.model.dto.AffiliateEmailParamsDTO;
+import com.ai.da.model.dto.BasicEmailParamDTO;
+import com.ai.da.model.dto.SubscriptionEmailParamsDTO;
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.core.io.InputStreamSource;
+
+import java.util.List;
+
+public interface EmailService {
+
+
+ void loadSingleEmailTemplate(String templatePath);
+
+ void loadTemplatesFromResources(String resourcesPath);
+
+ /**
+ * 发邮件
+ *
+ * @param mailTo 收件人邮箱
+ * @param jsonObject 动态邮件模板参数
+ * @param templateName 邮件模板名(只有文件名且需要带文件后缀)
+ * @param title 邮件标题
+ * @param fileName 附件文件名。没有附件置为null
+ * @param inputStreamSource 附件文件数据。没有附件置为null
+ */
+
+ void sendEmail(List mailTo, JSONObject jsonObject, String templateName, String title, String fileName, InputStreamSource inputStreamSource);
+
+ /**
+ * 适用于 : 需要自定义发件人信息
+ *
+ * @param jsonObject 模板参数
+ * @param basicEmailParamDTO 包含发件人信息、邮件标题、收件人信息
+ * @param templateName 使用的模板文件名
+ * @param fileName 附件文件名
+ * @param inputStreamSource 附件文件信息
+ */
+
+ void sendEmail(JSONObject jsonObject, BasicEmailParamDTO basicEmailParamDTO, String templateName, String fileName, InputStreamSource inputStreamSource);
+
+ // 登入模板id
+ String LOGIN_TEMPLATE_ID = "58020_login_aida_en.html";
+ // 修改密码模板id
+ String UPDATE_PWD_TEMPLATE_ID = "58022_update_password.html";
+ // 异常ip模板id
+ String EXCEPTION_ID_TEMPLATE_ID = "58021_exception_ip.html";
+ // 绑定邮箱模板id
+ String BIND_MAILBOX_TEMPLATE_ID = "132754_绑定邮箱.html";
+ // 更换绑定邮箱
+ String CHANGE_MAILBOX_TEMPLATE_ID = "128210_change_mailbox_en.html";
+
+ /**
+ * 发送登录相关的邮件
+ *
+ * @param receiverAddress 收件地址
+ * @param ip 请求ip
+ * @param templateId 模板ID名
+ * @param verifyCode 验证码
+ */
+ Boolean send(String receiverAddress, String ip, String templateId, String verifyCode);
+
+ /**
+ * 发送试用订单相关的邮件
+ *
+ * @param receiverAddress 收件人邮箱地址
+ * @param trialOrder 试用订单相关参数
+ * @param emailType 邮件类型:1 - 提交试用请求,2 - 审批通过,3 - 试用请求通过通知
+ * @param country 通过城市判断邮件模板的语言
+ * @param link ?
+ */
+ void sendCustomEmail(String receiverAddress, TrialOrder trialOrder, int emailType, String country, Boolean link);
+
+ /**
+ * 发送昨日的试用订单用户数据
+ *
+ * @param receiverAddress 收件人地址
+ * @param fileName 附件文件名
+ * @param inputStreamSource 附件文件数据
+ */
+ void sendExcelEmail(List receiverAddress, String fileName, InputStreamSource inputStreamSource);
+
+ /**
+ * 发送昨日的试用订单用户数据--无试用订单情况
+ *
+ * @param receiverAddress 收件人地址
+ */
+ void sendNoExcelEmail(List receiverAddress);
+
+ /**
+ * 向账号快要到期的用户发送提醒邮件
+ *
+ * @param account 账号信息
+ */
+ void sendWillBeExpiredEmail(Account account);
+
+ /**
+ * 发送系统升级通知邮件
+ *
+ * @param account 用户信息
+ * @param senderAddress 发件人邮件
+ * @param type 邮件类型
+ */
+ void sendUpgradeNotification(Account account, String senderAddress, Integer type);
+
+ /**
+ * 通知在Code_Create上付费的用户,AiDA账号的更新
+ *
+ * @param receiverAddress 收件人地址
+ * @param emailType 邮件类型
+ * @param country 国家(确定发送邮件的语言)
+ * @param userName 用户名
+ * @param date 账号到期时间
+ */
+ void notificationForPaidUser(String receiverAddress, int emailType, String country, String userName, String date);
+
+ /**
+ * 广场用户注册通知邮件
+ *
+ * @param userEmail
+ * @param randomVerifyCode
+ * @return
+ */
+ Boolean designWorksRegister(String userEmail, String randomVerifyCode);
+
+ void uploadTimeoutReminder(String userName, String time);
+
+ boolean subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress);
+
+ void affiliateEmailReminder(List receiverAddress, AffiliateEmailParamsDTO paramsDTO, String type);
+
+ void creditsPurchaseReminder(String username, String quantity, String amount);
+
+ void commonExceptionReminder(String functionName, List destination);
+}
diff --git a/src/main/java/com/ai/da/service/GenerateService.java b/src/main/java/com/ai/da/service/GenerateService.java
index dda193e6..f86f70a0 100644
--- a/src/main/java/com/ai/da/service/GenerateService.java
+++ b/src/main/java/com/ai/da/service/GenerateService.java
@@ -2,12 +2,10 @@ package com.ai.da.service;
import com.ai.da.mapper.primary.entity.Generate;
import com.ai.da.mapper.primary.entity.GenerateDetail;
-import com.ai.da.model.dto.GenerateLikeDTO;
-import com.ai.da.model.dto.GenerateModifyDTO;
-import com.ai.da.model.dto.GenerateThroughImageTextDTO;
-import com.ai.da.model.dto.ImageToSketchDTO;
+import com.ai.da.model.dto.*;
import com.ai.da.model.vo.*;
import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@@ -44,7 +42,21 @@ public interface GenerateService extends IService {
List