rename com.imyeyu.server to com.imyeyu.api
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
package com.imyeyu.api.modules.common.task;
|
||||
|
||||
import freemarker.template.Template;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||
import com.imyeyu.api.modules.blog.service.CommentRemindQueueService;
|
||||
import com.imyeyu.api.modules.common.bean.EmailException;
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueueLog;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
||||
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import com.imyeyu.utils.Text;
|
||||
import com.imyeyu.utils.Time;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 邮件推送任务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 14:10
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@RequiredArgsConstructor
|
||||
public class EmailTask implements TimiJava {
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String env;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String sendUser;
|
||||
|
||||
private final UserService userService;
|
||||
private final JavaMailSender mailSender;
|
||||
private final EmailQueueService service;
|
||||
private final CommentReplyService commentReplyService;
|
||||
private final CommentRemindQueueService commentRemindQueueService;
|
||||
|
||||
private final Redis<String, Long> redisUserEmailVerify;
|
||||
private final Redis<String, Long> redisUserResetPWVerify;
|
||||
|
||||
private final FreeMarkerConfigurer freeMarkerConfigurer;
|
||||
|
||||
@Scheduled(fixedRate = Time.S * 8)
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void traverseQueue() {
|
||||
List<EmailQueue> emailQueueList = service.listAll();
|
||||
if (TimiJava.isNotEmpty(emailQueueList)) {
|
||||
for (EmailQueue emailQueue : emailQueueList) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (emailQueue.getSendAt() < now) {
|
||||
log.info(emailQueue.getUUID() + " 邮件推送:" + emailQueue.getBizType() + "." +emailQueue.getBizId());
|
||||
|
||||
EmailQueueLog emailQueueLog = new EmailQueueLog();
|
||||
emailQueueLog.setUUID(emailQueue.getUUID());
|
||||
emailQueueLog.setBizType(emailQueue.getBizType());
|
||||
emailQueueLog.setBizId(emailQueue.getBizId());
|
||||
emailQueueLog.setSendAt(emailQueue.getSendAt());
|
||||
try {
|
||||
String sendTo = switch (emailQueue.getBizType()) {
|
||||
case REPLY_REMINAD -> sendEmail4ReplyRemind(emailQueue);
|
||||
case EMAIL_VERIFY -> sendEmail4EmailVerify(emailQueue);
|
||||
case RESET_PASSWORD -> sendEmail4ResetPassword(emailQueue);
|
||||
};
|
||||
emailQueueLog.setSendTo(sendTo);
|
||||
emailQueueLog.setIsSent(true);
|
||||
emailQueueLog.setCreatedAt(System.currentTimeMillis());
|
||||
log.info(emailQueue.getUUID() + " 邮件 " + emailQueueLog.getSendTo() + " 推送成功:" + (emailQueueLog.getCreatedAt() - now) + " ms");
|
||||
} catch (EmailException e) {
|
||||
emailQueueLog.setIsSent(false);
|
||||
log.error(emailQueue.getUUID() + " 邮件 " + e.getEmail() + " 推送中止:", e.getMessage());
|
||||
emailQueueLog.setExceptionMsg(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
emailQueueLog.setIsSent(false);
|
||||
log.error(emailQueue.getUUID() + " 邮件 " + emailQueueLog.getSendTo() + " 推送异常", e);
|
||||
emailQueueLog.setExceptionMsg(e.getMessage().substring(0,Math.min(200, e.getMessage().length())) + "...");
|
||||
} finally {
|
||||
emailQueueLog.setCreatedAt(now);
|
||||
service.addLog(emailQueueLog);
|
||||
service.destroy(emailQueue.getUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*
|
||||
* @param to 目标邮箱
|
||||
* @param subject 标题
|
||||
* @param html HTML 字符串
|
||||
* @throws Exception 发送异常
|
||||
*/
|
||||
private void sendEmail(String to, String subject, String html) throws Exception {
|
||||
if (env.contains("dev")) {
|
||||
log.info("skip send email in debug environment");
|
||||
log.info("send title: {}", subject);
|
||||
log.info("send to: {}", to);
|
||||
log.info("send detail: \n{}", html);
|
||||
return;
|
||||
}
|
||||
MimeMessage message = mailSender.createMimeMessage();
|
||||
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||||
helper.setFrom(sendUser);
|
||||
helper.setTo(to);
|
||||
helper.setSubject(subject);
|
||||
helper.setText(html, true);
|
||||
mailSender.send(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱验证
|
||||
*
|
||||
* @param emailQueue 邮件队列
|
||||
* @return 发送目标
|
||||
* @throws Exception 服务异常
|
||||
*/
|
||||
private String sendEmail4EmailVerify(EmailQueue emailQueue) throws Exception {
|
||||
User user = userService.get(emailQueue.getBizId()); //.withData();
|
||||
|
||||
String key = Text.randomString(64);
|
||||
redisUserEmailVerify.set(key, user.getId(), 600L);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
|
||||
model.put("user", user);
|
||||
model.put("url", "https://www.imyeyu.net/user/space/%s?action=EMAIL_VERIFY&key=%s".formatted(user.getId(), key));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("EmailVerify.ftl");
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
sendEmail(user.getEmail(), "Hey! 请继续完成 夜雨博客 的邮箱验证!", html);
|
||||
return user.getEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复提醒邮件
|
||||
*
|
||||
* @param emailQueue 邮件队列
|
||||
* @return 发送目标
|
||||
* @throws Exception 服务异常
|
||||
*/
|
||||
private String sendEmail4ReplyRemind(EmailQueue emailQueue) throws Exception {
|
||||
User user = userService.get(emailQueue.getBizId()); // TODO .withData();
|
||||
List<CommentRemindQueue> reminds = commentRemindQueueService.listByUserId(emailQueue.getBizId());
|
||||
if (reminds.isEmpty()) {
|
||||
throw new EmailException(TimiCode.RESULT_NULL, "没有需要提醒的回复", user.getEmail());
|
||||
}
|
||||
// 回查数据
|
||||
for (CommentRemindQueue remind : reminds) {
|
||||
// 回复
|
||||
CommentReply reply = commentReplyService.get(remind.getReplyId());
|
||||
CommentReplyView replyView = new CommentReplyView();
|
||||
BeanUtils.copyProperties(reply, replyView);
|
||||
remind.setReply(replyView);
|
||||
if (TimiJava.isNotEmpty(remind.getReply().getSenderId())) {
|
||||
// 发送者
|
||||
remind.getReply().setSender(userService.view(remind.getReply().getSenderId()));
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("user", user);
|
||||
model.put("reminds", reminds);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("ReplyRemind.ftl");
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
sendEmail(user.getEmail(), "Hey! 你在 夜雨博客 的评论收到新回复", html);
|
||||
// 移除回复提醒队列
|
||||
commentRemindQueueService.destroyByUserId(emailQueue.getBizId());
|
||||
return user.getEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码验证
|
||||
*
|
||||
* @param emailQueue 邮件队列
|
||||
* @return 发送目标
|
||||
* @throws Exception 服务异常
|
||||
*/
|
||||
private String sendEmail4ResetPassword(EmailQueue emailQueue) throws Exception {
|
||||
User user = userService.get(emailQueue.getBizId()); // TODO .withData();
|
||||
|
||||
String key = Text.randomString(64);
|
||||
redisUserResetPWVerify.set(key, user.getId(), 600L);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("user", user);
|
||||
model.put("url", "https://www.imyeyu.net/user/pw-reset?key=" + key);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("ResetPassword.ftl");
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
sendEmail(user.getEmail(), "Hey! 请继续完成 夜雨博客 的重置密码!", html);
|
||||
return user.getEmail();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.imyeyu.api.modules.common.task;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.network.FormMap;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
||||
import com.imyeyu.api.modules.common.service.MultilingualService;
|
||||
import com.imyeyu.api.modules.common.service.SettingService;
|
||||
import com.imyeyu.utils.Digest;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hc.client5.http.fluent.Request;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2025-05-31 11:06
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@RequiredArgsConstructor
|
||||
public class MultilingualTranslateTask {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-05-31 11:08
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum BaiduLanguage {
|
||||
|
||||
ZH(Language.zh_CN),
|
||||
EN(Language.en_US),
|
||||
JP(Language.ja_JP),
|
||||
KOR(Language.ko_KR),
|
||||
RU(Language.ru_RU),
|
||||
DE(Language.de_DE),
|
||||
CHT(Language.zh_TW);
|
||||
|
||||
/** 标准映射 */
|
||||
final Language language;
|
||||
|
||||
/**
|
||||
* 获取排除语言列表
|
||||
*
|
||||
* @param baiduLanguage 排除语言
|
||||
* @return 语言列表
|
||||
*/
|
||||
static List<BaiduLanguage> valuesWithout(BaiduLanguage... baiduLanguage) {
|
||||
Set<BaiduLanguage> outList = Set.of(baiduLanguage);
|
||||
|
||||
List<BaiduLanguage> result = new ArrayList<>();
|
||||
BaiduLanguage[] values = values();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (!outList.contains(values[i])) {
|
||||
result.add(values[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private final SettingService settingService;
|
||||
private final MultilingualService service;
|
||||
|
||||
@Scheduled(fixedRate = Time.M * 10)
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
public void handle() {
|
||||
try {
|
||||
List<Multilingual> list = service.listByNotTranslate();
|
||||
|
||||
Map<String, Multilingual> cnMap = new HashMap<>();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int j = 0; j < Math.min(list.size() - i, 20); j++, i++) {
|
||||
Multilingual multilingual = list.get(i);
|
||||
sb.append(multilingual.getZhCN()).append("\r\n");
|
||||
cnMap.put(multilingual.getZhCN(), multilingual);
|
||||
}
|
||||
i--;
|
||||
List<BaiduLanguage> languageList = BaiduLanguage.valuesWithout(BaiduLanguage.ZH);
|
||||
for (int j = 0; j < languageList.size(); j++) {
|
||||
Map<String, String> result = doTranslate(sb.toString(), languageList.get(j));
|
||||
for (Map.Entry<String, String> item : result.entrySet()) {
|
||||
Multilingual multilingual = cnMap.get(item.getKey());
|
||||
Language lang = languageList.get(j).language;
|
||||
String value = multilingual.getValue(lang);
|
||||
if (TimiJava.isEmpty(value)) {
|
||||
Ref.setFieldValue(multilingual, lang.toString().replace("_", ""), item.getValue());
|
||||
}
|
||||
service.update(multilingual);
|
||||
}
|
||||
}
|
||||
wait(1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文本翻译
|
||||
*
|
||||
* @param text 原文本
|
||||
* @param to 目标语言
|
||||
* @return Map<原数据,翻译结果>
|
||||
* @throws Exception 翻译异常
|
||||
*/
|
||||
private synchronized Map<String, String> doTranslate(String text, BaiduLanguage to) throws Exception {
|
||||
String random = String.valueOf(Time.now());
|
||||
|
||||
String appId = settingService.getAsString(SettingKey.MULTILINGUAL_TRANSLATE_APP_ID);
|
||||
String key = settingService.getAsString(SettingKey.MULTILINGUAL_TRANSLATE_KEY);
|
||||
|
||||
FormMap<String, Object> args = new FormMap<>();
|
||||
args.put("q", text);
|
||||
args.put("from", BaiduLanguage.ZH.toString().toLowerCase());
|
||||
args.put("to", to.toString().toLowerCase());
|
||||
args.put("appid", appId);
|
||||
args.put("salt", random);
|
||||
args.put("sign", Digest.md5(appId + text + random + key));
|
||||
|
||||
String response = Request.post(settingService.getAsString(SettingKey.MULTILINGUAL_TRANSLATE_API))
|
||||
.bodyForm(args.build())
|
||||
.execute()
|
||||
.returnContent()
|
||||
.asString();
|
||||
JsonObject jo = JsonParser.parseString(response).getAsJsonObject();
|
||||
if (jo.has("error_code")) {
|
||||
System.err.println(jo);
|
||||
throw new TimiException(TimiCode.ERROR, jo.get("error_msg").getAsString());
|
||||
}
|
||||
JsonArray ja = jo.get("trans_result").getAsJsonArray();
|
||||
|
||||
JsonObject resultJO;
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (int i = 0; i < ja.size(); i++) {
|
||||
resultJO = ja.get(i).getAsJsonObject();
|
||||
result.put(resultJO.get("src").getAsString(), resultJO.get("dst").getAsString());
|
||||
}
|
||||
wait(200);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user