perf: 支持邮件模版设置

This commit is contained in:
xiaojunnuo
2025-12-14 01:36:20 +08:00
parent 437d956cad
commit a6c0d2c6f1
31 changed files with 703 additions and 214 deletions
@@ -1,14 +1,10 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { cache, isDev, randomNumber, simpleNanoId } from '@certd/basic';
import { SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { SmsServiceFactory } from '../sms/factory.js';
import { AccessService, AccessSysGetter, CodeErrorException, SysSettingsService } from '@certd/lib-server';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { ISmsService } from '../sms/api.js';
import { CodeErrorException } from '@certd/lib-server';
import { EmailService } from './email-service.js';
import { AccessService } from '@certd/lib-server';
import { AccessSysGetter } from '@certd/lib-server';
import { isComm } from '@certd/plus-core';
import { SmsServiceFactory } from '../sms/factory.js';
import { CaptchaService } from "./captcha-service.js";
import { EmailService } from './email-service.js';
// {data: '<svg.../svg>', text: 'abcd'}
/**
@@ -84,8 +80,6 @@ export class CodeService {
async sendEmailCode(
email: string,
opts?: {
title?: string,
content?: string,
duration?: number,
verificationType?: string,
verificationCodeLength?: number,
@@ -96,32 +90,27 @@ export class CodeService {
}
let siteTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
if (siteInfo) {
siteTitle = siteInfo.title || siteTitle;
}
}
const verificationCodeLength = Math.floor(Math.max(Math.min(opts?.verificationCodeLength || 4, 8), 4));
const duration = Math.floor(Math.max(Math.min(opts?.duration || 5, 15), 1));
const code = randomNumber(verificationCodeLength);
const templateData = {
code, duration, siteTitle
code, duration,
title: "验证码",
content:`您的验证码是${code},请勿泄露`,
notificationType: "registerCode"
}
const titleTemplate = opts?.title?
const title = `${siteTitle}${!!opts?.title ? opts.title : '验证码'}`;
const content = !!opts.content ? this.compile(opts.content)(templateData) : `您的验证码是${code},请勿泄露`;
await this.emailService.send({
subject: title,
content: content,
receivers: [email],
if (opts?.verificationType === 'forgotPassword') {
templateData.title = '找回密码';
templateData.notificationType = "forgotPassword"
}
await this.emailService.sendByTemplate({
type: templateData.notificationType,
data: templateData,
email:{
receivers: [email],
},
});
const key = this.buildEmailCodeKey(email,opts?.verificationType);
@@ -1,5 +1,5 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import type { EmailSend } from '@certd/pipeline';
import type { EmailSend, EmailSendByTemplateReq } from '@certd/pipeline';
import { IEmailService } from '@certd/pipeline';
import { logger } from '@certd/basic';
@@ -8,9 +8,11 @@ import { isComm, isPlus } from '@certd/plus-core';
import nodemailer from 'nodemailer';
import { SendMailOptions } from 'nodemailer';
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
import { PlusService, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { AddonService, PlusService, SysEmailConf, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { getEmailSettings } from '../../sys/settings/fix.js';
import { UserEmailSetting } from "../../mine/service/models.js";
import { AddonGetterService } from '../../pipeline/service/addon-getter-service.js';
import { EmailContent, ITemplateProvider } from '../../../plugins/plugin-template/api.js';
export type EmailConfig = {
host: string;
@@ -38,6 +40,12 @@ export class EmailService implements IEmailService {
@Inject()
plusService: PlusService;
@Inject()
addonGetterService: AddonGetterService;
@Inject()
addonService: AddonService
async sendByPlus(email: EmailSend) {
if (!isPlus()) {
throw new Error('plus not enabled');
@@ -49,7 +57,6 @@ export class EmailService implements IEmailService {
* content: string;
* receivers: string[];
*/
await this.plusService.sendEmail(email);
}
@@ -62,26 +69,6 @@ export class EmailService implements IEmailService {
throw new Error('收件人不能为空');
}
const emailConf = await getEmailSettings(this.sysSettingsService, this.settingsService);
if (!emailConf.host && emailConf.usePlus == null) {
if (isPlus()) {
//自动使用plus发邮件
return await this.sendByPlus(email);
}
throw new Error('邮件服务器还未设置');
}
if (emailConf.usePlus && isPlus()) {
return await this.sendByPlus(email);
}
await this.sendByCustom(emailConf, email);
logger.info('sendEmail complete: ', email);
}
private async sendByCustom(emailConfig: EmailConfig, email: EmailSend) {
const transporter = nodemailer.createTransport(emailConfig);
let sysTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
@@ -93,10 +80,34 @@ export class EmailService implements IEmailService {
if (!subject.includes(`${sysTitle}`)) {
subject = `${sysTitle}${subject}`;
}
email.subject = subject;
const emailConf = await getEmailSettings(this.sysSettingsService, this.settingsService);
if (!emailConf.host && emailConf.usePlus == null) {
if (isPlus()) {
//自动使用plus发邮件
return await this.sendByPlus(email);
}
throw new Error('邮件服务器还未设置');
}
if (emailConf.usePlus && isPlus()) {
return await this.sendByPlus(email);
}
await this.sendByCustom(emailConf, email, sysTitle);
logger.info('sendEmail complete: ', email);
}
private async sendByCustom(emailConfig: EmailConfig, email: EmailSend, sysTitle: string) {
const transporter = nodemailer.createTransport(emailConfig);
const mailOptions = {
from: `${sysTitle} <${emailConfig.sender}>`,
to: email.receivers.join(', '), // list of receivers
subject: subject,
subject: email.subject,
text: email.content,
html: email.html,
attachments: email.attachments,
@@ -105,30 +116,72 @@ export class EmailService implements IEmailService {
}
async test(userId: number, receiver: string) {
await this.send({
receivers: [receiver],
subject: '测试邮件,from certd',
content: '测试邮件,from certd',
await this.sendByTemplate({
type:"common",
data:{
title: '测试邮件,from certd',
content: '测试邮件,from certd',
},
email: {
receivers: [receiver],
},
});
}
async list(userId: any) {
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,UserEmailSetting)
return userEmailSetting.list;
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId, UserEmailSetting)
return userEmailSetting.list;
}
async delete(userId: any, email: string) {
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,UserEmailSetting)
userEmailSetting.list = userEmailSetting.list.filter(item=>item !== email);
await this.settingsService.saveSetting(userId,userEmailSetting)
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId, UserEmailSetting)
userEmailSetting.list = userEmailSetting.list.filter(item => item !== email);
await this.settingsService.saveSetting(userId, userEmailSetting)
}
async add(userId: any, email: string) {
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,UserEmailSetting)
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId, UserEmailSetting)
//如果已存在
if(userEmailSetting.list.includes(email)){
if (userEmailSetting.list.includes(email)) {
return
}
userEmailSetting.list.unshift(email)
await this.settingsService.saveSetting(userId,userEmailSetting)
await this.settingsService.saveSetting(userId, userEmailSetting)
}
async sendByTemplate(req: EmailSendByTemplateReq) {
const emailConf = await this.sysSettingsService.getSetting<SysEmailConf>(SysEmailConf);
const template = emailConf?.templates?.[req.type]
let content = null
if (template && template.addonId) {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getAddonById(template.addonId, true, 0)
if (addon) {
content = await addon.buildContent({ data: req.data })
}
}
if (!content) {
//看看有没有通用模版
if (emailConf?.templates?.common && emailConf?.templates?.common.addonId) {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getAddonById(emailConf.templates.common.addonId, true, 0)
if (addon) {
content = await addon.buildContent({ data: req.data })
}
}
}
// 没有找到模版,使用默认模版
if (!content) {
try {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getBlank(req.type, "默认")
content = await addon.buildDefaultContent({ data: req.data })
} catch (e) {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getBlank("common", "默认")
content = await addon.buildDefaultContent({ data: req.data })
}
}
return await this.send({
...req.email,
...content
})
}
}