7 Commits

19 changed files with 255 additions and 357 deletions

View File

@@ -202,7 +202,7 @@ public class MyTaskScheduler {
}
}
@Scheduled(cron = "0 0 9 * * ?")
// @Scheduled(cron = "0 0 9 * * ?")
public void sendTrialOrderExcelToManagements() {
// 获取前一天日期
LocalDate yesterday = LocalDate.now().minusDays(1);

View File

@@ -34,7 +34,7 @@ public class AccountTask {
accountService.refreshCreditsMonthly();
}
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
public void getPaidUser() {
// 获取code-create 表中 指定日期之后 订单状态为wc-processing的订单
accountService.extendValidityForCC();

View File

@@ -45,7 +45,7 @@ public class PaymentTask {
@Resource
private PayPalCheckoutService payPalCheckoutService;
@Scheduled(cron = "0/30 * * * * ?")
// @Scheduled(cron = "0/30 * * * * ?")
public void orderConfirmForPaypal() throws SerializeException {
// log.info("PayPal orderConfirm 被执行......");
@@ -97,7 +97,7 @@ public class PaymentTask {
//
}
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
public void updateAffiliateInfoWithPayment(){
// log.info("佣金计算定时器");
affiliateService.updateAffiliateInfoWithPayment();
@@ -109,7 +109,7 @@ public class PaymentTask {
affiliateService.syncLinkViewCountToDB();
}
@Scheduled(cron = "0 0 8 28-31 * ?")
// @Scheduled(cron = "0 0 8 28-31 * ?")
public void commissionSummaryReminder(){
// 每个月末的最后一天的早上八点执行
LocalDate today = LocalDate.now();

View File

@@ -40,7 +40,7 @@ public class SubscriptionReminderTask {
REMINDER_DAYS_CONFIG.put("year", 14);
}
@Scheduled(cron = "0 0 9 * * ?")
// @Scheduled(cron = "0 0 9 * * ?")
public void subscriptionReminder() {
// 获取所有需要通知的订阅
List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions();
@@ -97,7 +97,7 @@ public class SubscriptionReminderTask {
return subscriptionInfoMapper.selectList(qw);
}
@Scheduled(cron = "0 0 9 * * ?")
// @Scheduled(cron = "0 0 9 * * ?")
public void trialReminder() {
// 今天的 00:00:00 和 23:59:59
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();

View File

@@ -767,7 +767,7 @@ public class SendEmailUtil {
try {
String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com";
String[] receiverEmail = {merchantEmail, developer};
String[] receiverEmail = {/*merchantEmail,*/ developer};
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
@@ -968,7 +968,7 @@ public class SendEmailUtil {
req.setFromEmailAddress(SEND_ADDRESS);
String merchantEmail = "kimwong@code-create.com.hk";
String developerEmail = "xupei@code-create.com.hk";
req.setDestination(new String[]{merchantEmail, developerEmail});
req.setDestination(new String[]{/*merchantEmail,*/ developerEmail});
Template template = new Template();
req.setSubject("New Credit Purchase Order");
template.setTemplateID(CREDITS_PURCHASE_MERCHANT);
@@ -1030,15 +1030,25 @@ public class SendEmailUtil {
private final static Long CN_2025_618 = 141425L;
private final static Long EN_2025_618 = 141424L;
public static void send618PromotionEmailTemp(String receiver, String language){
try {
// 实例化一个认证对象
private final static Long CN_GLOBAL_AWARDS = 166716L;
private final static Long EN_GLOBAL_AWARDS = 166715L;
private final static Long GLOBAL_AWARDS_2 = 173919L;
// 复用 SesClient避免每次发送都创建新连接
private static SesClient getSesClient() {
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
return new SesClient(cred, "ap-hongkong", clientProfile);
}
public static void send618PromotionEmailTemp(String receiver, String language){
SesClient client = null;
try {
client = getSesClient();
SendEmailRequest req = new SendEmailRequest();
req.setFromEmailAddress(CODE_CREATE_SEND_ADDRESS);
req.setDestination(new String[]{receiver});
@@ -1046,20 +1056,15 @@ public class SendEmailUtil {
// 根据邮件类型设置不同的主题和模板
String subject = "";
Template template = new Template();
// if (type == 1) {
// subject = "Upcoming System Upgrade for AiDA 3.0";
// template.setTemplateID(UPGRADE_NOTIFICATION_ID);
// }else {
// subject = "即将到来的AiDA 3.0系统升级";
// template.setTemplateID(UPGRADE_NOTIFICATION_ID_CHINESE);
// }
if (language.equals("ENGLISH")) {
subject = "Welcome back Subscribe AiDA with the discount code to enjoy 50% OFF!";
template.setTemplateID(EN_2025_618);
subject = "AiDA Global Design Awards 2026 | The Journey Starts Now ! \nAiDA 全球设计大奖 2026 | 从现在开始你的创作之旅 !";
template.setTemplateID(GLOBAL_AWARDS_2);
/*if (language.equals("ENGLISH")) {
subject = "From Idea to Wow. \uD83D\uDD25 | AiDA Demo & Global Awards";
template.setTemplateID(EN_GLOBAL_AWARDS);
}else {
subject = "设计时速狂飙AiDA 618半价让灵感永不限流";
template.setTemplateID(CN_2025_618);
}
subject = "灵感开挂,创作起飞 \uD83D\uDD25 | AiDA Demo & 全球设计大奖";
template.setTemplateID(CN_GLOBAL_AWARDS);
}*/
req.setSubject(subject);
req.setTemplate(template);
@@ -1069,9 +1074,7 @@ public class SendEmailUtil {
log.info("邮件发送成功,收件人地址:{}", receiver);
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.info(receiver);
log.error("邮件发送失败###{},收件人地址:{}", e.toString(), receiver);
log.error("邮件发送失败,收件人地址:{},错误信息:{}", receiver, e.toString());
}
}

View File

@@ -20,72 +20,92 @@ public class SendRequestUtil {
@Value("${FREEPIK_API_KEY}")
private String FREEPIK_API_KEY;
public String sendAliYunPostAsync(String apiUrl, String requestBody){
// 发送POST请求 todo 异常处理
HttpResponse execute = HttpRequest.post(apiUrl)
public String sendAliYunPostAsync(String apiUrl, String requestBody) {
// 发送POST请求
try (HttpResponse execute = HttpRequest.post(apiUrl)
.header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY)
.header("X-DashScope-Async", "enable")
.header(Header.CONTENT_TYPE, "application/json")
.body(requestBody)
.timeout(20000) // 设置超时时间20秒
.execute();
.execute()) {
int status = execute.getStatus();
if (status == 200){
if (status == 200) {
String body = execute.body();
JSONObject bodyJson = JSONUtil.parseObj(body);
return body;
}
log.warn("请求失败,状态码为 {}", status);
log.warn("请求失败,状态码为{},响应内容:{}", status, execute.body());
} catch (Exception e) {
log.error("请求阿里云API时发生异常URL{},请求体:{},异常信息:{}",
apiUrl, requestBody, e.getMessage(), e);
}
return null;
}
public String sendAliYunPost(String apiUrl, String requestBody){
// 发送POST请求 todo 异常处理
HttpResponse execute = HttpRequest.post(apiUrl)
public String sendAliYunPost(String apiUrl, String requestBody) {
// 发送POST请求
try (HttpResponse execute = HttpRequest.post(apiUrl)
.header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY)
.header(Header.CONTENT_TYPE, "application/json")
.body(requestBody)
.timeout(20000) // 设置超时时间20秒
.execute();
.execute()) {
int status = execute.getStatus();
if (status == 200){
if (status == 200) {
String body = execute.body();
JSONObject bodyJson = JSONUtil.parseObj(body);
return body;
}
log.warn("请求失败,状态码为 {}", status);
log.warn("请求失败,状态码为{},响应内容:{}", status, execute.body());
} catch (Exception e) {
log.error("请求阿里云API时发生异常URL{},请求体:{},异常信息:{}",
apiUrl, requestBody, e.getMessage(), e);
}
return null;
}
public static final String FREE_PIK = "https://api.freepik.com/v1/ai/beta/text-to-image/reimagine-flux";
public String sendFreepikPost( String requestBody){
// 发送POST请求 todo 异常处理
HttpResponse execute = HttpRequest.post(FREE_PIK)
public String sendFreepikPost(String requestBody) {
// 发送POST请求
try (HttpResponse execute = HttpRequest.post(FREE_PIK)
.header(Header.CONTENT_TYPE, "application/json")
.header("x-freepik-api-key", FREEPIK_API_KEY)
.body(requestBody)
.timeout(20000) // 设置超时时间20秒
.execute();
.execute()) {
int status = execute.getStatus();
if (status == 200){
if (status == 200) {
return execute.body();
}
log.warn("请求失败,状态码为 {}", status);
log.warn("请求失败,状态码为{},响应内容:{}", status, execute.body());
} catch (Exception e) {
log.error("请求Freepik API时发生异常请求体{},异常信息:{}",
requestBody, e.getMessage(), e);
}
return null;
}
public String sendAliYunGet(String fullUrl){
// 发送GET请求 todo 异常处理
HttpResponse httpResponse = HttpRequest.get(fullUrl)
public String sendAliYunGet(String fullUrl) {
// 发送GET请求
try (HttpResponse httpResponse = HttpRequest.get(fullUrl)
.header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY)
.timeout(20000) // 设置超时时间20秒
.execute();
.execute()) {
int status = httpResponse.getStatus();
if (status == 200){
if (status == 200) {
return httpResponse.body();
}else {
return null;
}
log.warn("请求失败,状态码为:{},响应内容:{}", status, httpResponse.body());
} catch (Exception e) {
log.error("请求阿里云API时发生异常URL{},异常信息:{}",
fullUrl, e.getMessage(), e);
}
return null;
}
/*public String sendFluxPost(String url, String requestBodyStr){

View File

@@ -380,12 +380,12 @@ public class AccountController {
return Response.success(accountService.subAccountImport(file));
}
/*@GetMapping("/send618Email")
@GetMapping("/send618Email")
@Operation(summary = "618邮件发送")
public Response<String> send618PromotionEmailTemp() {
accountService.send618PromotionEmailTemp();
return Response.success("success");
}*/
}
/*@GetMapping("/refreshCreditsMonthly")
@Operation(summary = "刷新子账号积分")

View File

@@ -176,25 +176,10 @@ public class GlobalAwardController {
}
@PostMapping("/contestants/export/files")
@ApiOperation(value = "导出参赛者文件为ZIP", notes = "根据参赛者编号范围导出PDF视频和信息文件为ZIP直接响应给浏览器")
public void exportContestantFiles(@ApiParam(value = "参赛者文件导出请求", required = true) @RequestBody ContestantExportRequest request, HttpServletResponse response) throws Exception {
byte[] zipData = globalAwardService.exportContestantFilesAsZip(request.getMinContestantNumber(), request.getMaxContestantNumber());
if (zipData.length == 0) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getWriter().write("No contestants found in the specified range.");
return;
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.zip\"");
response.setContentLength(zipData.length);
response.getOutputStream().write(zipData);
response.getOutputStream().flush();
}
@GetMapping("/contestants/count")
@ApiOperation(value = "查询参赛者总数", notes = "查询数据库中参赛者的总数量")
public Response<Long> getContestantCount() {
return Response.success(globalAwardService.getContestantCount());
@ApiOperation(value = "导出参赛者文件到本地", notes = "根据参赛者编号范围导出PDF视频文件到本地temp/uploads/contestants目录")
public Response<Integer> exportContestantFiles(@ApiParam(value = "参赛者文件导出请求", required = true) @RequestBody ContestantExportRequest request) throws Exception {
int exportedCount = globalAwardService.exportContestantFiles(request.getMinContestantNumber(), request.getMaxContestantNumber());
return Response.success(exportedCount);
}
}

View File

@@ -239,7 +239,7 @@ public interface AccountService extends IService<Account> {
Set<String> organizationNameSearch(String type, String name);
/*void send618PromotionEmailTemp();*/
void send618PromotionEmailTemp();
void checkEduAdminExpireStatus();

View File

@@ -33,18 +33,12 @@ public interface GlobalAwardService {
void saveContestantsToLocal() throws Exception;
/**
* 将参赛者文件打包为 ZIP 并返回字节数组(不落盘,直接响应给浏览器)
* 根据参赛者编号范围导出参赛者文件到本地目录
* @param minContestantNumber 最小参赛者编号
* @param maxContestantNumber 最大参赛者编号
* @return ZIP 文件的字节数组
* @return 导出的参赛者数量
*/
byte[] exportContestantFilesAsZip(Integer minContestantNumber, Integer maxContestantNumber) throws Exception;
/**
* 查询参赛者总数
* @return 参赛者数量
*/
long getContestantCount();
int exportContestantFiles(Integer minContestantNumber, Integer maxContestantNumber) throws Exception;
}

View File

@@ -3670,33 +3670,66 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
return new HashSet<>();
}
/* @Override
@Override
public void send618PromotionEmailTemp() {
QueryWrapper<TrialOrder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("is_deleted", 0);
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
List<TrialOrder> trialOrders = trialOrderMapper.selectList(queryWrapper);
queryWrapper.lambda()
.isNotNull(Account::getUserEmail)
.in(Account::getUserEmail, "xupei3360@163.com", "kimwong@code-create.com.hk", "kaicpang.pang@connect.polyu.hk", "chelseayu@code-create.com.hk")
.apply("LENGTH(TRIM(user_email)) > 0");
List<Account> accounts = baseMapper.selectList(queryWrapper);
// 按邮箱去重,保留每个邮箱的第一条记录
Map<String, Account> emailToAccountMap = new LinkedHashMap<>();
for (Account account : accounts) {
String email = account.getUserEmail();
if (!StringUtil.isNullOrEmpty(email)) {
email = email.trim().toLowerCase();
// 保留第一次出现的记录
emailToAccountMap.putIfAbsent(email, account);
}
}
List<String> uniqueEmails = new ArrayList<>(emailToAccountMap.keySet());
int total = uniqueEmails.size();
log.info("AiDA用户去重后邮件发送总量{}", total);
// 控制发送速率每发送20封后等待1秒控制QPS在20以内
int batchSize = 20;
long sleepMillis = 1000;
int successCount = 0;
int failCount = 0;
for (int i = 0; i < uniqueEmails.size(); i++) {
String email = uniqueEmails.get(i);
log.info("邮件发送进度 {}/{}", i + 1, total);
// List<Account> accountList = accountMapper.selectList(queryWrapper);
System.out.println(trialOrders);
int total = trialOrders.size();
log.info("试用用户总量:{}", total);
int current = 1;
for (TrialOrder trialOrder : trialOrders) {
log.info("邮件发送进度 {}/{}", current, total);
try {
if (!StringUtil.isNullOrEmpty(trialOrder.getCountry()) && trialOrder.getCountry().equals("China")) {
SendEmailUtil.send618PromotionEmailTemp(trialOrder.getEmail(), "CHINESE_SIMPLIFIED");
} else {
// 英文
SendEmailUtil.send618PromotionEmailTemp(trialOrder.getEmail(), "ENGLISH");
// 统一发送英文版本
// log.info("邮件地址 {}", email);
SendEmailUtil.send618PromotionEmailTemp(email, "ENGLISH");
successCount++;
} catch (Exception e) {
failCount++;
log.error("邮件发送失败,收件人:{},错误信息:{}", email, e.getMessage());
}
}catch (Exception e) {
log.info(e.getMessage());
// 控制发送速率,每发送 batchSize 封后等待
if ((i + 1) % batchSize == 0 && (i + 1) < uniqueEmails.size()) {
try {
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("邮件发送被中断");
break;
}
current ++;
}
}*/
}
log.info("邮件发送完成,成功:{},失败:{}", successCount, failCount);
}
@Override
public void checkEduAdminExpireStatus() {

View File

@@ -82,7 +82,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
// 邮件通知审批者
String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com";
String[] receiverEmail = {merchantEmail, developer};
String[] receiverEmail = {/*merchantEmail,*/ developer};
SendEmailUtil.affiliateEmailReminder(receiverEmail, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
// emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
}else {
@@ -442,7 +442,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com";
String[] receiverEmail = {merchantEmail, developer};
String[] receiverEmail = {/*merchantEmail,*/ developer};
// 邮件通知
SendEmailUtil.affiliateEmailReminder(receiverEmail, affiliateEmailParamsDTO, "summary");
// emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), affiliateEmailParamsDTO, "summary");

View File

@@ -524,7 +524,7 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
elementVO.setPrintBoardElements(printBoardElements);
if (!CollectionUtils.isEmpty(printBoardIds)) {
// 从数据库批量查询printBoard元素
printBoardElements.addAll(collectionElementMapper.selectBatchIds(printBoardIds));
printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
// 验证查询结果的完整性
if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) {
throw new BusinessException("get.printBoards.data.is.mismatch");

View File

@@ -587,7 +587,7 @@ public class EmailServiceImpl implements EmailService {
try {
String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com";
List<String> merchantReceiver = Arrays.asList(merchantEmail, developer);
List<String> merchantReceiver = Arrays.asList(/*merchantEmail,*/ developer);
String merchantSubject = null;
String merchantTemplate = null;
@@ -731,7 +731,7 @@ public class EmailServiceImpl implements EmailService {
jsonObject.put("quantity", quantity);
jsonObject.put("totalFee", amount);
sendEmail(Arrays.asList(merchantEmail, developerEmail), jsonObject, CREDITS_PURCHASE_MERCHANT, "New Credit Purchase Order", null, null);
sendEmail(Arrays.asList(/*merchantEmail,*/ developerEmail), jsonObject, CREDITS_PURCHASE_MERCHANT, "New Credit Purchase Order", null, null);
}
private final static String COMMON_EXCEPTION_REMINDER = "135279_common-exception-reminder.html";

View File

@@ -26,22 +26,24 @@ import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.FileOutputStream;
import java.time.format.DateTimeFormatter;
@Service
@Slf4j
@@ -263,9 +265,11 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
// r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
// r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
// 视频时长(秒)
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
// 视频大小、PDF 大小:以 MB 导出,保留两位小数
if (cst.getVideoSize() == null) {
r.createCell(ci++).setCellValue("");
} else {
@@ -328,9 +332,11 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
// r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
// r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
// 视频时长(秒)
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
// 视频大小、PDF 大小:以 MB 导出,保留两位小数
if (cst.getVideoSize() == null) {
r.createCell(ci++).setCellValue("");
} else {
@@ -474,7 +480,7 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
}
@Override
public byte[] exportContestantFilesAsZip(Integer minContestantNumber, Integer maxContestantNumber) throws Exception {
public int exportContestantFiles(Integer minContestantNumber, Integer maxContestantNumber) throws Exception {
if (minContestantNumber == null || maxContestantNumber == null) {
throw new BusinessException("minContestantNumber and maxContestantNumber are required.");
}
@@ -482,7 +488,7 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
throw new BusinessException("minContestantNumber cannot be greater than maxContestantNumber.");
}
// 1. 根据 contestantNumber 范围查询参赛者
// 1. 根据contestantNumber范围查询参赛者
QueryWrapper<Contestant> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.ge(Contestant::getContestantNumber, minContestantNumber)
@@ -492,13 +498,18 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
if (contestants.isEmpty()) {
log.info("No contestants found in range [{}, {}]", minContestantNumber, maxContestantNumber);
return new byte[0];
return 0;
}
// 2. 在内存中构建 ZIP
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(baos)) {
// 2. 创建基础目录
String baseDir = uploadDir + "/contestants";
Path basePath = Paths.get(baseDir).toAbsolutePath();
Files.createDirectories(basePath);
log.info("Base directory created: {}", basePath);
int exportedCount = 0;
// 3. 遍历每个参赛者,下载文件
for (Contestant contestant : contestants) {
Integer contestantNumber = contestant.getContestantNumber();
if (contestantNumber == null) {
@@ -506,98 +517,71 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
continue;
}
String dirPrefix = contestantNumber + "/";
// 创建参赛者文件夹
String contestantDir = baseDir + "/" + contestantNumber;
Path contestantPath = Paths.get(contestantDir);
Files.createDirectories(contestantPath);
// 添加 PDF 文件
// 下载PDF文件
String pdfPath = contestant.getPdfPath();
if (StringUtils.isNotBlank(pdfPath)) {
addMinioFileToZip(zos, pdfPath, dirPrefix + "design.pdf");
try {
String fileName = pdfPath.contains("/") ?
pdfPath.substring(pdfPath.lastIndexOf("/") + 1) : "design.pdf";
downloadFileFromMinio(pdfPath, contestantPath.toString(), "design.pdf");
log.info("Downloaded PDF for contestant {}", fileName);
} catch (Exception e) {
log.error("Failed to download PDF for contestant {}: {}", contestantNumber, e.getMessage());
}
}
// 添加视频文件
// 下载视频文件
String videoPath = contestant.getVideoPath();
if (StringUtils.isNotBlank(videoPath)) {
try {
// 根据路径判断视频格式
String fileName = videoPath.contains("/") ?
videoPath.substring(videoPath.lastIndexOf("/") + 1) : "video.mp4";
addMinioFileToZip(zos, videoPath, dirPrefix + fileName);
downloadFileFromMinio(videoPath, contestantPath.toString(), fileName);
log.info("Downloaded video for contestant {}", contestantNumber);
} catch (Exception e) {
log.error("Failed to download video for contestant {}: {}", contestantNumber, e.getMessage());
}
}
// 添加参赛者信息 txt 文件
StringBuilder sb = new StringBuilder();
sb.append("=== Contestant Information ===\n\n");
sb.append("ID: ").append(nullSafe(contestant.getId())).append("\n");
sb.append("Email: ").append(nullSafe(contestant.getEmail())).append("\n");
sb.append("Contestant Number: ").append(contestantNumber).append("\n");
sb.append("First Name: ").append(nullSafe(contestant.getFirstName())).append("\n");
sb.append("Last Name: ").append(nullSafe(contestant.getLastName())).append("\n");
sb.append("Gender: ").append(nullSafe(contestant.getGender())).append("\n");
sb.append("Occupation: ").append(nullSafe(contestant.getOccupation())).append("\n");
sb.append("Age: ").append(contestant.getAge() != null ? contestant.getAge() : "N/A").append("\n");
sb.append("Country/Region/City: ").append(nullSafe(contestant.getCountryRegionCity())).append("\n");
sb.append("Phone Number: ").append(nullSafe(contestant.getPhoneNumber())).append("\n");
sb.append("Design Title: ").append(nullSafe(contestant.getDesignTitle())).append("\n");
sb.append("Design Description: ").append(nullSafe(contestant.getDesignDescription())).append("\n");
sb.append("PDF Path: ").append(nullSafe(pdfPath)).append("\n");
sb.append("PDF Size (bytes): ").append(contestant.getPdfSize() != null ? contestant.getPdfSize() : "N/A").append("\n");
sb.append("Video Path: ").append(nullSafe(videoPath)).append("\n");
sb.append("Video Duration (seconds): ").append(contestant.getVideoDuration() != null ? contestant.getVideoDuration() : "N/A").append("\n");
sb.append("Video Size (bytes): ").append(contestant.getVideoSize() != null ? contestant.getVideoSize() : "N/A").append("\n");
sb.append("Created At: ").append(contestant.getCreatedAt() != null ? contestant.getCreatedAt() : "N/A").append("\n");
sb.append("Updated At: ").append(contestant.getUpdatedAt() != null ? contestant.getUpdatedAt() : "N/A").append("\n");
ZipEntry infoEntry = new ZipEntry(dirPrefix + "contestant_info.txt");
zos.putNextEntry(infoEntry);
zos.write(sb.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8));
zos.closeEntry();
log.info("Added contestant {} info to zip", contestantNumber);
exportedCount++;
}
zos.finish();
log.info("ZIP built for {} contestants, size: {} bytes", contestants.size(), baos.size());
return baos.toByteArray();
}
log.info("Exported {} contestants' files to {}", exportedCount, basePath);
return exportedCount;
}
/**
* MinIO 文件流式写入 ZIP不落盘
* @param zos ZIP 输出流
* @param minioPath MinIO 路径(格式: bucketName/objectPath
* @param entryName ZIP 条目名称
* MinIO下载文件到本地
* @param minioPath MinIO路径 (格式: bucketName/objectPath)
* @param localDir 本地目录
* @param fileName 本地文件名
*/
private void addMinioFileToZip(java.util.zip.ZipOutputStream zos, String minioPath, String entryName) {
private void downloadFileFromMinio(String minioPath, String localDir, String fileName) {
if (StringUtils.isBlank(minioPath)) {
return;
}
// 从路径中提取bucket名称和对象名称
int index = minioPath.indexOf("/");
if (index == -1) {
log.warn("Invalid MinIO path: {}", minioPath);
return;
}
String bucketName = minioPath.substring(0, index);
String objectName = minioPath.substring(index + 1);
try (InputStream in = minioUtil.download(bucketName, objectName)) {
ZipEntry entry = new ZipEntry(entryName);
zos.putNextEntry(entry);
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
zos.write(buffer, 0, bytesRead);
}
zos.closeEntry();
log.info("Added {} to zip ({} bytes)", entryName, entry.getSize());
} catch (Exception e) {
log.error("Failed to add {} to zip: {}", entryName, e.getMessage());
}
}
// 构建本地文件完整路径
Path localFilePath = Paths.get(localDir, fileName);
@Override
public long getContestantCount() {
return contestantMapper.selectCount(null);
}
private String nullSafe(String value) {
return value != null ? value : "N/A";
// 下载文件
minioUtil.downloadMinioObjectToLocal(bucketName, objectName, localFilePath.toString());
}
}

View File

@@ -1060,12 +1060,11 @@ public class StripeServiceImpl implements StripeService {
String periodEnd = DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_yyyy_MM_dd_HH_mm_ss);
qwPI.lambda().eq(PaymentInfo::getOrderNo, subscriptionInfo.getOrderNo())
.eq(PaymentInfo::getTradeState, "paid")
.between(PaymentInfo::getCreateTime, periodStart, periodEnd)
.orderByDesc(PaymentInfo::getId);
List<PaymentInfo> paymentInfos = paymentInfoMapper.selectList(qwPI);
if (paymentInfos.isEmpty()) {
log.info("不发送邮件原因【根据order_no:{},查询到的成功的paymentInfos为空】", orderNo);
log.info("不发送邮件原因【根据order_no:{},查询到的paymentInfos为空】", orderNo);
return false;
}
PaymentInfo paymentInfo = paymentInfos.get(0);

View File

@@ -905,15 +905,15 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
}
// 将构建好的结果对象添加到返回列表
// results.add(magicToolResultVO);
results.add(magicToolResultVO);
} else if (Objects.isNull(magicToolResultVO)) {
// 如果Redis中没有结果对象创建执行中状态的结果对象
magicToolResultVO = new MagicToolResultVO(taskId, "Executing");
// results.add(magicToolResultVO);
}/* else {
results.add(magicToolResultVO);
} else {
// 如果Redis中有结果对象但URL为空直接添加到返回列表
results.add(magicToolResultVO);
}*/
}
// 收集任务状态用于统计
if (!StringUtil.isNullOrEmpty(magicToolResultVO.getStatus())) collect.add(magicToolResultVO.getStatus());
@@ -1461,16 +1461,12 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
if (StringUtil.isNullOrEmpty(fluxResult)) {
toProductImageResult.setStatus("Fail");
toProductImageResultMapper.updateById(toProductImageResult);
if (toProductImageResult.getIsLike() != null && toProductImageResult.getIsLike() == 1) {
sortRank(toProductImageResult);
}
results.add(new MagicToolResultVO(taskId, "Fail"));
} else if (fluxResult.equals("Fail") || fluxResult.equals("Pending")) {
toProductImageResult.setStatus(fluxResult);
toProductImageResultMapper.updateById(toProductImageResult);
if (fluxResult.equals("Fail") && toProductImageResult.getIsLike() != null && toProductImageResult.getIsLike() == 1) {
sortRank(toProductImageResult);
}
results.add(new MagicToolResultVO(taskId, fluxResult));
} else {
results.add(processFluxResult(fluxResult, toProductImageResult, taskId, toProductImageRecord.getPrompt()));
@@ -2207,14 +2203,10 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
childCollectionQw.lambda().orderByAsc(CollectionSort::getSort);
List<CollectionSort> childSortList = collectionSortMapper.selectList(childCollectionQw);
List<AllCollectionVO> childList = new ArrayList<>();
// 收集需要删除的失败记录ID用于后续统一清理并重新排序
List<Long> failedSortIds = new ArrayList<>();
for (CollectionSort userLikeSort : childSortList) {
if (userLikeSort.getRelationType().equals(CollectionType.TO_PRODUCT_IMAGE.getValue())) {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())) {
failedSortIds.add(userLikeSort.getId());
log.info("【获取内容】TO_PRODUCT_IMAGE结果失败relationId={}即将从collection_sort中删除", userLikeSort.getRelationId());
continue;
}
toProductImageResult.setUrl(getMinioUrl(toProductImageResult.getUrl()));
@@ -2246,8 +2238,6 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
} else if (userLikeSort.getRelationType().equals(CollectionType.RELIGHT.getValue())) {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())) {
failedSortIds.add(userLikeSort.getId());
log.info("【获取内容】RELIGHT结果失败relationId={}即将从collection_sort中删除", userLikeSort.getRelationId());
continue;
}
toProductImageResult.setUrl(getMinioUrl(toProductImageResult.getUrl()));
@@ -2260,114 +2250,6 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
toProductImageResultVO.setPrompt(toProductImageRecord.getPrompt());
if (toProductImageResultVO.getElementType().equals("ProductElement")) {
ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(toProductElement.getUrl()));
} else if ((toProductImageResultVO.getElementType().equals("DesignOutfit"))) {
TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(tDesignPythonOutfit.getDesignUrl()));
} else {
ToProductImageResult toProductImageResult1 = toProductImageResultMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(toProductImageResult1.getUrl()));
}
toProductImageResultVO.setCollectionType(CollectionType.RELIGHT.getValue());
toProductImageResultVO.setSort(userLikeSort.getSort());
toProductImageResultVO.setUserLikeSortId(userLikeSort.getId());
toProductImageResultVO.setRelationType(userLikeSort.getRelationType());
toProductImageResultVO.setParentId(userLikeSort.getParentId());
childList.add(toProductImageResultVO);
} else if (userLikeSort.getRelationType().equals(CollectionType.POSE_TRANSFORM.getValue())) {
PoseTransformation item = poseTransformationMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(item.getTaskStatus(), item.getCreateTime())) {
failedSortIds.add(userLikeSort.getId());
log.info("【获取内容】POSE_TRANSFORM结果失败relationId={}即将从collection_sort中删除", userLikeSort.getRelationId());
continue;
}
PoseTransformationVO poseTransformationVO = new PoseTransformationVO();
poseTransformationVO.setId(item.getId());
poseTransformationVO.setTaskId(item.getUniqueId());
poseTransformationVO.setProductImage(getMinioUrl(item.getProductImage()));
poseTransformationVO.setLastFrameProductImage(getMinioUrl(item.getLastFrameProductImage()));
poseTransformationVO.setPrompt(item.getPrompt());
poseTransformationVO.setGifUrl(getMinioUrl(item.getGifUrl()));
poseTransformationVO.setVideoUrl(getMinioUrl(item.getVideoUrl()));
poseTransformationVO.setFirstFrameUrl(getMinioUrl(item.getFirstFrameUrl()));
poseTransformationVO.setIsLiked(item.getIsLiked());
poseTransformationVO.setCollectionType(CollectionType.POSE_TRANSFORM.getValue());
poseTransformationVO.setSort(userLikeSort.getSort());
poseTransformationVO.setUserLikeSortId(userLikeSort.getId());
poseTransformationVO.setRelationType(userLikeSort.getRelationType());
poseTransformationVO.setResultType(CollectionType.POSE_TRANSFORM.getValue());
poseTransformationVO.setParentId(userLikeSort.getParentId());
poseTransformationVO.setModelName(item.getModelName());
poseTransformationVO.setPoseId(item.getPoseId());
poseTransformationVO.setStatus(item.getTaskStatus());
childList.add(poseTransformationVO);
}
}
// 统一处理失败的记录从collection_sort表中删除失败的记录并重新排序
if (CollectionUtil.isNotEmpty(failedSortIds)) {
Long parentId = collectionSort.getId();
Long projectId = projectDTO.getId();
log.info("【获取内容】检测到{}条失败记录需要清理parentId={}, projectId={}", failedSortIds.size(), parentId, projectId);
for (Long failedSortId : failedSortIds) {
CollectionSort failedRecord = collectionSortMapper.selectById(failedSortId);
if (failedRecord != null) {
String relationType = failedRecord.getRelationType();
Long relationId = failedRecord.getRelationId();
collectionSortService.deleteCollectionSort(relationId, relationType, projectId, parentId);
log.info("【获取内容】已删除失败记录relationId={}, relationType={}", relationId, relationType);
}
}
// 重新查询子列表,获取更新后的排序
childSortList = collectionSortMapper.selectList(childCollectionQw);
// 重新构建childList使用更新后的sort值
childList = new ArrayList<>();
for (CollectionSort userLikeSort : childSortList) {
if (userLikeSort.getRelationType().equals(CollectionType.TO_PRODUCT_IMAGE.getValue())) {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())) {
continue;
}
toProductImageResult.setUrl(getMinioUrl(toProductImageResult.getUrl()));
ToProductImageResultVO toProductImageResultVO = CopyUtil.copyObject(toProductImageResult, ToProductImageResultVO.class);
ToProductImageRecord toProductImageRecord = toProductImageRecordMapper.selectById(toProductImageResult.getToProductImageRecordId());
if (Objects.isNull(toProductImageRecord)) {
continue;
}
toProductImageResultVO.setPrompt(toProductImageRecord.getPrompt());
if (toProductImageResultVO.getElementType().equals("ProductElement")) {
ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(toProductElement.getUrl()));
} else if ((toProductImageResultVO.getElementType().equals("DesignOutfit"))) {
TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(tDesignPythonOutfit.getDesignUrl()));
} else {
ToProductImageResult toProductImageResult1 = toProductImageResultMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(toProductImageResult1.getUrl()));
}
toProductImageResultVO.setCollectionType(CollectionType.TO_PRODUCT_IMAGE.getValue());
toProductImageResultVO.setSort(userLikeSort.getSort());
toProductImageResultVO.setUserLikeSortId(userLikeSort.getId());
toProductImageResultVO.setRelationType(userLikeSort.getRelationType());
toProductImageResultVO.setParentId(userLikeSort.getParentId());
childList.add(toProductImageResultVO);
} else if (userLikeSort.getRelationType().equals(CollectionType.RELIGHT.getValue())) {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())) {
continue;
}
toProductImageResult.setUrl(getMinioUrl(toProductImageResult.getUrl()));
ToProductImageResultVO toProductImageResultVO = CopyUtil.copyObject(toProductImageResult, ToProductImageResultVO.class);
ToProductImageRecord toProductImageRecord = toProductImageRecordMapper.selectById(toProductImageResult.getToProductImageRecordId());
if (Objects.isNull(toProductImageRecord)) {
continue;
}
toProductImageResultVO.setPrompt(toProductImageRecord.getPrompt());
if (toProductImageResultVO.getElementType().equals("ProductElement")) {
ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageResultVO.getElementId());
toProductImageResultVO.setSourceUrl(getMinioUrl(toProductElement.getUrl()));
@@ -2411,8 +2293,6 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
childList.add(poseTransformationVO);
}
}
log.info("【获取内容】失败记录清理完成重新排序后childList.size={}", childList.size());
}
o.setChildList(childList);
list.add(o);

View File

@@ -2,7 +2,7 @@
#spring.profiles.active=test
#<23><><EFBFBD><EFBFBD>application-prod<6F>ļ<EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
spring.profiles.active=prod
#spring.profiles.active=prod
#<23><><EFBFBD><EFBFBD>application-dev<65>ļ<EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
#spring.profiles.active=dev
spring.profiles.active=dev

View File

@@ -27,9 +27,9 @@ paypal.webhook_id=1D107312EX592781K
##### Stripe
# developer
#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
# dev 端点
#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
# local 端点
#stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm
@@ -43,8 +43,8 @@ paypal.webhook_id=1D107312EX592781K
#stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL
# kim - live
stripe.private-key=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
#stripe.private-key=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
# prod 端点
stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11
#stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11
# dev 端点
#stripe.webhook-sign-secret=whsec_cFUtjUOo8wnrIKZmt4GNvt7ZY1bOfrYr