更新配置: 支付和邮件登录模块配置优化, 删除临时文档

This commit is contained in:
AIGC Developer
2025-11-03 10:55:48 +08:00
parent 7964d87954
commit b5bbd8841e
73 changed files with 2075 additions and 6364 deletions

View File

@@ -7,8 +7,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -35,9 +33,6 @@ public class AuthApiController {
@Autowired
private UserService userService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@@ -45,48 +40,16 @@ public class AuthApiController {
private VerificationCodeService verificationCodeService;
/**
* 用户登录
* 用户登录(已禁用,仅支持邮箱验证码登录)
* 为了向后兼容,保留此接口但返回提示信息
*/
@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(@RequestBody Map<String, String> credentials,
HttpServletRequest request,
HttpServletResponse response) {
try {
String username = credentials.get("username");
String password = credentials.get("password");
if (username == null || password == null) {
return ResponseEntity.badRequest()
.body(createErrorResponse("用户名和密码不能为空"));
}
// 使用Spring Security进行认证
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(username, password);
Authentication authentication = authenticationManager.authenticate(authToken);
User user = userService.findByUsername(username);
// 生成JWT Token
String token = jwtUtils.generateToken(username, user.getRole(), user.getId());
Map<String, Object> body = new HashMap<>();
body.put("success", true);
body.put("message", "登录成功");
Map<String, Object> data = new HashMap<>();
data.put("user", user);
data.put("token", token);
body.put("data", data);
logger.info("用户登录成功:{}", username);
return ResponseEntity.ok(body);
} catch (Exception e) {
logger.error("登录失败:", e);
return ResponseEntity.badRequest()
.body(createErrorResponse("用户名或密码错误"));
}
logger.warn("尝试使用用户名密码登录,但系统已禁用此方式");
return ResponseEntity.badRequest()
.body(createErrorResponse("系统已禁用用户名密码登录,请使用邮箱验证码登录"));
}
/**

View File

@@ -0,0 +1,69 @@
package com.example.demo.dto;
import java.util.HashMap;
import java.util.Map;
/**
* 邮件消息DTO
* 用于封装邮件发送请求
*/
public class MailMessage {
private String toEmail;
private String subject;
private Long templateId;
private Map<String, Object> templateData;
public MailMessage() {
this.templateData = new HashMap<>();
}
public MailMessage(String toEmail, String subject, Long templateId) {
this();
this.toEmail = toEmail;
this.subject = subject;
this.templateId = templateId;
}
public String getToEmail() {
return toEmail;
}
public void setToEmail(String toEmail) {
this.toEmail = toEmail;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public Long getTemplateId() {
return templateId;
}
public void setTemplateId(Long templateId) {
this.templateId = templateId;
}
public Map<String, Object> getTemplateData() {
return templateData;
}
public void setTemplateData(Map<String, Object> templateData) {
this.templateData = templateData;
}
/**
* 添加模板参数
*/
public MailMessage addParam(String key, Object value) {
this.templateData.put(key, value);
return this;
}
}

View File

@@ -197,3 +197,7 @@ public class PointsFreezeRecord {

View File

@@ -265,3 +265,7 @@ public class TaskQueue {

View File

@@ -257,3 +257,7 @@ public class TaskStatus {

View File

@@ -65,3 +65,7 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {

View File

@@ -32,6 +32,10 @@ public class PlainTextPasswordEncoder implements PasswordEncoder {

View File

@@ -0,0 +1,155 @@
package com.example.demo.service;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Base64;
import java.util.List;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* 图片网格拼接服务
* 用于将多张分镜图片拼接成一个网格布局
* 参考Comfly项目的图片处理方式
*/
@Service
public class ImageGridService {
private static final Logger logger = LoggerFactory.getLogger(ImageGridService.class);
/**
* 将多张图片URL拼接成一个网格图片
*
* @param imageUrls 图片URL列表
* @param gridCols 网格列数默认3列适合6张或9张图
* @return Base64编码的拼接后图片
*/
public String mergeImagesToGrid(List<String> imageUrls, int gridCols) {
if (imageUrls == null || imageUrls.isEmpty()) {
throw new IllegalArgumentException("图片URL列表不能为空");
}
try {
int imageCount = imageUrls.size();
// 自动计算网格布局:如果未指定列数,根据图片数量自动计算
if (gridCols <= 0) {
gridCols = calculateOptimalColumns(imageCount);
}
int gridRows = (int) Math.ceil((double) imageCount / gridCols);
logger.info("开始拼接图片网格: 图片数量={}, 列数={}, 行数={}", imageCount, gridCols, gridRows);
// 读取所有图片
BufferedImage[] images = new BufferedImage[imageCount];
int maxWidth = 512; // 默认最小宽度避免网格尺寸为0
int maxHeight = 512; // 默认最小高度避免网格尺寸为0
for (int i = 0; i < imageCount; i++) {
BufferedImage img = loadImageFromUrl(imageUrls.get(i));
if (img != null) {
images[i] = img;
maxWidth = Math.max(maxWidth, img.getWidth());
maxHeight = Math.max(maxHeight, img.getHeight());
} else {
logger.warn("无法加载图片: {}", imageUrls.get(i));
// 创建一个空白图片占位
BufferedImage placeholder = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
Graphics2D g = placeholder.createGraphics();
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, 512, 512);
g.dispose();
images[i] = placeholder;
}
}
// 创建网格图片,每张图片使用统一尺寸
int cellWidth = maxWidth;
int cellHeight = maxHeight;
BufferedImage gridImage = new BufferedImage(
gridCols * cellWidth,
gridRows * cellHeight,
BufferedImage.TYPE_INT_RGB
);
Graphics2D g = gridImage.createGraphics();
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, gridImage.getWidth(), gridImage.getHeight());
// 将图片放置到网格中
for (int i = 0; i < imageCount; i++) {
int row = i / gridCols;
int col = i % gridCols;
int x = col * cellWidth;
int y = row * cellHeight;
BufferedImage img = images[i];
// 如果图片尺寸不同,居中放置
int imgX = x + (cellWidth - img.getWidth()) / 2;
int imgY = y + (cellHeight - img.getHeight()) / 2;
g.drawImage(img, imgX, imgY, null);
}
g.dispose();
// 转换为Base64
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(gridImage, "PNG", baos);
byte[] imageBytes = baos.toByteArray();
String base64 = Base64.getEncoder().encodeToString(imageBytes);
logger.info("图片网格拼接完成: 总尺寸={}x{}", gridImage.getWidth(), gridImage.getHeight());
return "data:image/png;base64," + base64;
} catch (Exception e) {
logger.error("拼接图片网格失败", e);
throw new RuntimeException("图片拼接失败: " + e.getMessage(), e);
}
}
/**
* 计算最优列数
*/
private int calculateOptimalColumns(int imageCount) {
// 根据图片数量选择最佳列数
if (imageCount <= 2) return 2;
if (imageCount <= 4) return 2;
if (imageCount <= 6) return 3;
if (imageCount <= 9) return 3;
if (imageCount <= 12) return 4;
return 4; // 默认4列
}
/**
* 从URL加载图片
*/
private BufferedImage loadImageFromUrl(String imageUrl) {
try {
// 处理base64格式的图片
if (imageUrl.startsWith("data:image")) {
String base64Data = imageUrl.substring(imageUrl.indexOf(",") + 1);
byte[] imageBytes = Base64.getDecoder().decode(base64Data);
return ImageIO.read(new java.io.ByteArrayInputStream(imageBytes));
}
// 处理普通URL
URI uri = new URI(imageUrl);
URL url = uri.toURL();
return ImageIO.read(url);
} catch (IOException | URISyntaxException e) {
logger.error("加载图片失败: {}", imageUrl, e);
return null;
}
}
}

View File

@@ -388,25 +388,40 @@ public class RealAIService {
/**
* 提交文生图任务(分镜视频使用)
* 调用Qwen文生图API
* 调用Comfly API的文生图接口参考Comfly项目的Comfly_qwen_image节点实现
* 支持分镜图生成,可以生成多张图片用于拼接成分镜图网格
*
* @param prompt 提示词
* @param aspectRatio 宽高比
* @param numImages 生成图片数量默认6张用于分镜图
* @return API响应包含多张图片的URL
*/
public Map<String, Object> submitTextToImageTask(String prompt, String aspectRatio) {
public Map<String, Object> submitTextToImageTask(String prompt, String aspectRatio, int numImages) {
try {
logger.info("提交文生图任务: prompt={}, aspectRatio={}", prompt, aspectRatio);
logger.info("提交文生图任务: prompt={}, aspectRatio={}, numImages={}", prompt, aspectRatio, numImages);
// 根据aspectRatio转换尺寸
// 限制生成图片数量在1-12之间参考Comfly项目的限制
if (numImages < 1) {
numImages = 1;
} else if (numImages > 12) {
numImages = 12;
}
// 根据aspectRatio转换尺寸参考Comfly项目的尺寸映射
String size = convertAspectRatioToImageSize(aspectRatio);
// 使用文生图的API端点Comfly API
String url = aiImageApiBaseUrl + "/v1/images/generations";
// 构建请求体
// 构建请求体参考Comfly_qwen_image节点的参数设置
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("prompt", prompt);
requestBody.put("size", size);
requestBody.put("model", "qwen-image");
requestBody.put("n", 1);
requestBody.put("n", numImages); // 支持生成多张图片
requestBody.put("response_format", "url");
// 添加guidance_scale参数以提高图片质量参考Comfly项目
requestBody.put("guidance_scale", 2.5);
String requestBodyJson = objectMapper.writeValueAsString(requestBody);
@@ -451,15 +466,16 @@ public class RealAIService {
/**
* 将宽高比转换为图片尺寸
* 参考Comfly项目的尺寸映射支持分镜图生成的各种常用尺寸
*/
private String convertAspectRatioToImageSize(String aspectRatio) {
return switch (aspectRatio) {
case "16:9" -> "1024x576";
case "9:16" -> "576x1024";
case "4:3" -> "1024x768";
case "3:4" -> "768x1024";
case "1:1" -> "1024x1024";
default -> "1024x768";
case "16:9" -> "1024x576"; // 横屏,适合宽屏分镜
case "9:16" -> "576x1024"; // 竖屏,适合手机视频分镜
case "4:3" -> "1024x768"; // 标准横屏
case "3:4" -> "768x1024"; // 标准竖屏
case "1:1" -> "1024x1024"; // 正方形
default -> "1024x768"; // 默认标准横屏
};
}
@@ -654,12 +670,14 @@ public class RealAIService {
优化要求:
1. 将中文描述翻译成流畅的英文,确保语义准确
2. 关注镜头构图、画面布局、视觉元素composition, framing, visual hierarchy等
3. 适合生成12格黑白分镜图风格,强调构图和画面元素
3. 适合生成专业分镜图风格,强调构图和画面元素,包含清晰的场景描述和视觉细节
4. 确保提示词清晰描述每个镜头的关键视觉元素和构图方式
5. 使用专业的电影分镜术语establishing shot, medium shot, close-up等
6. 如果原始提示词已经是英文,直接优化,保持语言一致
7. 输出优化后的提示词,不要添加额外说明、引号或其他格式标记
8. 优化后的提示词应该直接可用,长度控制在合理范围内
5. 使用专业的电影分镜术语establishing shot, medium shot, close-up, wide shot, over-the-shoulder等)
6. 添加画面细节描述:场景环境、人物/物体的位置、光线、氛围等
7. 如果原始提示词已经是英文,直接优化,保持语言一致
8. 输出优化后的提示词,不要添加额外说明、引号或其他格式标记
9. 优化后的提示词应该直接可用长度控制在合理范围内200-500词
10. 参考格式:"(scene description), (shot type), (composition details), (visual elements), (lighting and atmosphere), professional storyboard style, cinematic framing"
""";
default -> """
你是一个专业的提示词优化专家。请将用户提供的简单描述优化为详细、专业的英文提示词。

View File

@@ -1,5 +1,6 @@
package com.example.demo.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -32,6 +33,12 @@ public class StoryboardVideoService {
@Autowired
private RealAIService realAIService;
@Autowired
private ImageGridService imageGridService;
// 默认生成6张分镜图
private static final int DEFAULT_STORYBOARD_IMAGES = 6;
/**
* 创建分镜视频任务
*/
@@ -92,43 +99,57 @@ public class StoryboardVideoService {
task.updateStatus(StoryboardVideoTask.TaskStatus.PROCESSING);
taskRepository.flush(); // 强制刷新到数据库
// 调用真实文生图API
logger.info("分镜视频任务已提交正在调用文生图API生成分镜图...");
// 调用真实文生图API,生成多张分镜图
logger.info("分镜视频任务已提交正在调用文生图API生成{}张分镜图...", DEFAULT_STORYBOARD_IMAGES);
Map<String, Object> apiResponse = realAIService.submitTextToImageTask(
task.getPrompt(),
task.getAspectRatio()
task.getAspectRatio(),
DEFAULT_STORYBOARD_IMAGES // 生成多张图片用于分镜图
);
// 从API响应中提取图片URL
// 从API响应中提取所有图片URL
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) apiResponse.get("data");
if (data != null && !data.isEmpty()) {
String imageUrl = null;
Map<String, Object> firstImage = data.get(0);
// 检查是否有url或b64_json字段
if (firstImage.get("url") != null) {
imageUrl = (String) firstImage.get("url");
} else if (firstImage.get("b64_json") != null) {
// base64编码的图片
String base64Data = (String) firstImage.get("b64_json");
imageUrl = "data:image/png;base64," + base64Data;
// 收集所有图片URL
List<String> imageUrls = new ArrayList<>();
for (Map<String, Object> imageData : data) {
String imageUrl = null;
if (imageData.get("url") != null) {
imageUrl = (String) imageData.get("url");
} else if (imageData.get("b64_json") != null) {
// base64编码的图片
String base64Data = (String) imageData.get("b64_json");
imageUrl = "data:image/png;base64," + base64Data;
}
if (imageUrl != null) {
imageUrls.add(imageUrl);
}
}
if (imageUrls.isEmpty()) {
throw new RuntimeException("未能从API响应中提取任何图片URL");
}
logger.info("成功获取{}张图片,开始拼接成分镜图网格...", imageUrls.size());
// 拼接多张图片成网格
String mergedImageUrl = imageGridService.mergeImagesToGrid(imageUrls, 0); // 0表示自动计算列数
// 重新加载任务因为之前的flush可能使实体detached
task = taskRepository.findByTaskId(taskId)
.orElseThrow(() -> new RuntimeException("任务未找到: " + taskId));
// 设置结果
task.setResultUrl(imageUrl);
// 设置拼接后的结果图片URL
task.setResultUrl(mergedImageUrl);
task.setRealTaskId(taskId + "_image");
task.updateStatus(StoryboardVideoTask.TaskStatus.COMPLETED);
task.updateProgress(100);
taskRepository.save(task);
logger.info("分镜图生成完成任务ID: {}, 图片URL: {}", taskId, imageUrl);
logger.info("分镜图生成并拼接完成任务ID: {}, 共生成{}张图片", taskId, imageUrls.size());
} else {
throw new RuntimeException("API返回的图片数据为空");
}

View File

@@ -0,0 +1,170 @@
package com.example.demo.service;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ses.v20201002.SesClient;
import com.tencentcloudapi.ses.v20201002.models.SendEmailRequest;
import com.tencentcloudapi.ses.v20201002.models.SendEmailResponse;
import com.tencentcloudapi.ses.v20201002.models.Template;
/**
* 腾讯云SES邮件推送服务
* 基于 mails-over-tencent-cloud 模块实现
* 参考https://github.com/starter-go/mails-over-tencent-cloud
*/
@Service
public class TencentSesMailService {
private static final Logger logger = LoggerFactory.getLogger(TencentSesMailService.class);
@Autowired
private ObjectMapper objectMapper;
@Value("${tencent.ses.secret-id}")
private String secretId;
@Value("${tencent.ses.secret-key}")
private String secretKey;
@Value("${tencent.ses.region:ap-beijing}")
private String region;
@Value("${tencent.ses.from-email}")
private String fromEmail;
@Value("${tencent.ses.from-name:AIGC平台}")
private String fromName;
/**
* 获取腾讯云SES客户端
*/
private SesClient getClient() {
Credential cred = new Credential(secretId, secretKey);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
return new SesClient(cred, region, clientProfile);
}
/**
* 发送邮件(使用模板)
*
* @param toEmail 收件人邮箱
* @param subject 邮件主题
* @param templateId 模板ID在腾讯云SES控制台创建
* @param templateData 模板数据JSON格式的Map
* @return 是否发送成功
*/
public boolean sendEmailWithTemplate(String toEmail, String subject, Long templateId, Map<String, Object> templateData) {
try {
logger.info("开始发送邮件,收件人: {}, 主题: {}, 模板ID: {}", toEmail, subject, templateId);
SesClient client = getClient();
SendEmailRequest req = new SendEmailRequest();
// 设置发件人
req.setFromEmailAddress(fromEmail);
// 设置收件人(仅支持单个收件人)
req.setDestination(new String[]{toEmail});
// 设置邮件主题
req.setSubject(subject);
// 设置模板
Template template = new Template();
template.setTemplateID(templateId);
// 将模板数据转换为JSON字符串
String templateDataJson = objectMapper.writeValueAsString(templateData);
template.setTemplateData(templateDataJson);
req.setTemplate(template);
// 设置触发类型1-批量触发(模板邮件)
req.setTriggerType(1L);
// 调用API发送邮件
SendEmailResponse resp = client.SendEmail(req);
String messageId = resp.getMessageId();
logger.info("邮件发送成功,收件人: {}, 消息ID: {}", toEmail, messageId);
return true;
} catch (TencentCloudSDKException e) {
logger.error("腾讯云SES API调用失败收件人: {}, 错误码: {}, 错误信息: {}",
toEmail, e.getErrorCode(), e.getMessage(), e);
return false;
} catch (Exception e) {
logger.error("发送邮件异常,收件人: {}", toEmail, e);
return false;
}
}
/**
* 发送验证码邮件(使用默认模板)
*
* @param toEmail 收件人邮箱
* @param code 验证码
* @param templateId 验证码邮件模板ID从配置读取
* @return 是否发送成功
*/
public boolean sendVerificationCodeEmail(String toEmail, String code, Long templateId) {
Map<String, Object> templateData = new HashMap<>();
templateData.put("code", code);
return sendEmailWithTemplate(toEmail, "验证码", templateId, templateData);
}
/**
* 发送通用模板邮件
*
* @param toEmail 收件人邮箱
* @param subject 邮件主题
* @param templateId 模板ID
* @param params 模板参数(键值对)
* @return 是否发送成功
*/
public boolean sendTemplateEmail(String toEmail, String subject, Long templateId, Map<String, Object> params) {
return sendEmailWithTemplate(toEmail, subject, templateId, params);
}
/**
* 发送JSON格式模板数据的邮件
*
* @param toEmail 收件人邮箱
* @param subject 邮件主题
* @param templateId 模板ID
* @param jsonData JSON格式的模板数据字符串
* @return 是否发送成功
*/
public boolean sendEmailWithJsonData(String toEmail, String subject, Long templateId, String jsonData) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> templateData = objectMapper.readValue(jsonData, Map.class);
return sendEmailWithTemplate(toEmail, subject, templateId, templateData);
} catch (Exception e) {
logger.error("解析JSON模板数据失败: {}", jsonData, e);
return false;
}
}
}

View File

@@ -1,27 +1,17 @@
package com.example.demo.service;
// import com.example.demo.config.TencentCloudConfig;
// import com.tencentcloudapi.common.Credential;
// import com.tencentcloudapi.common.exception.TencentCloudSDKException;
// import com.tencentcloudapi.common.profile.ClientProfile;
// import com.tencentcloudapi.common.profile.HttpProfile;
// import com.tencentcloudapi.sms.v20210111.SmsClient;
// import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
// import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
// import com.tencentcloudapi.ses.v20201002.SesClient;
// import com.tencentcloudapi.ses.v20201002.models.SendEmailRequest;
// import com.tencentcloudapi.ses.v20201002.models.SendEmailResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* 验证码服务
*/
@@ -30,8 +20,11 @@ public class VerificationCodeService {
private static final Logger logger = LoggerFactory.getLogger(VerificationCodeService.class);
// @Autowired
// private TencentCloudConfig tencentCloudConfig;
@Autowired
private TencentSesMailService tencentSesMailService;
@Value("${tencent.ses.template-id:0}")
private Long templateId;
// 使用内存存储验证码
private final ConcurrentHashMap<String, String> verificationCodes = new ConcurrentHashMap<>();
@@ -152,20 +145,30 @@ public class VerificationCodeService {
/**
* 发送邮件(简化版本实际使用时需要配置正确的腾讯云SES API
* 发送邮件(使用腾讯云SES邮件推送服务
*/
private boolean sendEmail(String email, String code) {
try {
// TODO: 实现腾讯云SES邮件发送
// 这里暂时使用日志输出实际部署时需要配置正确的腾讯云SES API
logger.info("发送邮件验证码: {}, 验证码: {}", email, code);
// 如果没有配置模板ID使用开发模式仅记录日志
if (templateId == null || templateId == 0) {
logger.warn("未配置邮件模板ID使用开发模式。验证码: {}, 邮箱: {}", code, email);
logger.info("开发模式:邮件验证码发送到: {}, 验证码: {}", email, code);
return true; // 开发模式下返回成功
}
// 在实际环境中,这里应该调用腾讯云SES API
// 由于腾讯云SES API配置较复杂这里先返回true进行测试
return true;
// 使用腾讯云SES发送邮件
boolean success = tencentSesMailService.sendVerificationCodeEmail(email, code, templateId);
if (success) {
logger.info("邮件验证码发送成功,邮箱: {}", email);
} else {
logger.error("邮件验证码发送失败,邮箱: {}", email);
}
return success;
} catch (Exception e) {
logger.error("邮件发送失败", e);
logger.error("邮件发送异常,邮箱: {}", email, e);
return false;
}
}

View File

@@ -22,11 +22,16 @@ jwt.secret=mySecretKey1234567890123456789012345678901234567890123456789012345678
jwt.expiration=86400000
# 腾讯云SES配置
tencent.ses.secret-id=AKIDz8krbsJ5yKBZQpn74WFkmLPx3gnPhESA
tencent.ses.secret-key=Gu5t9xGARNpqDXcd3I4WZkFpAALPVZ7pKbNScfFsj
# 主账号ID: 100040185043
# 用户名: test
tencent.ses.secret-id=AKIDXw8HBtNfjdJm480xljV4QZUDi05wa0DE
tencent.ses.secret-key=tZyHMDsKadS4ScZhhU3PYUErGUVIqBIB
tencent.ses.region=ap-beijing
tencent.ses.from-email=noreply@vionow.com
tencent.ses.from-name=AIGC平台
# 邮件模板ID在腾讯云SES控制台创建模板后获取
# 如果未配置或为0将使用开发模式仅记录日志
tencent.ses.template-id=0
# AI API配置
# 文生视频、图生视频、分镜视频都使用Comfly API

View File

@@ -24,3 +24,7 @@ CREATE TABLE IF NOT EXISTS task_queue (

View File

@@ -23,3 +23,7 @@ CREATE TABLE IF NOT EXISTS points_freeze_records (

View File

@@ -26,3 +26,7 @@ CREATE TABLE task_status (

View File

@@ -568,6 +568,10 @@

View File

@@ -484,6 +484,10 @@

View File

@@ -523,6 +523,10 @@