fix(email): 统一注册成功邮件模板风格及调用逻辑

- 重写 register-success.html,视觉风格与邮箱验证码邮件对齐
- TenantServiceImpl 开通邮件改为发送 HTML 模板邮件,添加 sendTime 字段
- 修改 EmailTemplateUtil.sendRegisterSuccessEmail 方法签名及实现,传递正确模板变量
- 更新 EmailTestController 中的测试接口参数,支持新模板数据
- 修复 MainController 注册成功邮件调用用错参数导致空字段问题,使用兜底值避免编译错误
- 备注:register-success.html 为首次启用,旧逻辑未使用此模板仅发送纯文本邮件
- 测试方式:通过 POST /api/email-test/register-success?email=xxx 预览邮件渲染效果
This commit is contained in:
2026-08-10 21:56:46 +08:00
parent c585647dec
commit 58972885ad
6 changed files with 143 additions and 210 deletions
@@ -29,10 +29,12 @@ public class EmailTestController extends BaseController {
public ApiResult<?> testRegisterSuccessEmail(@RequestParam String email) {
try {
emailTemplateUtil.sendRegisterSuccessEmail(
"测试用户",
"13800138000",
"TestPassword123",
email,
"广西河马科技有限公司",
"https://site-10654.sitelnk.cn",
"https://site.websoft.top",
email,
"F!huhtL3fqD8",
email,
getTenantId()
);
return success("注册成功邮件发送成功");
@@ -1425,7 +1425,7 @@ public class MainController extends BaseController {
tokenExpireTime, configProperties.getTokenKey());
// 发送邮件通知
if(ObjectUtil.isNotEmpty(addUser) && email != null){
emailTemplateUtil.sendRegisterSuccessEmail(username, phone, password, email, addUser.getTenantId());
emailTemplateUtil.sendRegisterSuccessEmail(username, "https://websoft.top", "https://websoft.top", email, password, email, addUser.getTenantId());
}
return success("注册成功", new LoginResult(access_token, addUser));
}
@@ -648,22 +648,23 @@ import java.util.stream.Collectors;
final String siteUrl = "https://" + sitePrefix + "-" + company.getTid() + "." + siteDomain;
final String adminUrl = "https://" + adminHost;
final String title = "您的企业官网已开通成功";
final StringBuilder content = new StringBuilder();
content.append("尊敬的用户:\r\n\r\n");
content.append("恭喜!您的企业官网「").append(company.getShortName()).append("」已开通成功。\r\n\r\n");
content.append("您可以使用以下信息登录管理后台,继续完善站点内容:\r\n\r\n");
content.append("• 官网地址:").append(siteUrl).append("\r\n");
content.append("• 后台地址:").append(adminUrl).append("\r\n");
content.append("• 登录账号:").append(company.getEmail()).append("\r\n");
content.append("• 登录密码:").append(company.getPassword()).append("\r\n\r\n");
content.append("安全提示:\r\n");
content.append("1. 请妥善保管登录密码,不要通过邮件、聊天工具发送给他人。\r\n");
content.append("2. 建议首次登录后立即进入「个人中心 > 修改密码」重置密码。\r\n\r\n");
content.append("点击登录后台:").append(adminUrl).append("\r\n\r\n");
content.append("如有疑问,请联系客服或访问帮助中心。");
// 发送邮件通知
// 发送 HTML 邮件通知(模板风格与验证码邮件统一)
if (company.getEmail() != null) {
emailRecordService.sendEmail(title, content.toString(), company.getEmail(), company.getTid());
final Map<String, Object> data = new HashMap<>(8);
data.put("siteName", company.getShortName());
data.put("siteUrl", siteUrl);
data.put("adminUrl", adminUrl);
data.put("account", company.getEmail());
data.put("password", company.getPassword());
data.put("sendTime", DateUtil.now());
try {
emailRecordService.sendHtmlEmail(title, "register-success.html", data, new String[]{company.getEmail()});
} catch (Exception e) {
// HTML 邮件发送失败时降级为纯文本
String plain = "恭喜!您的企业官网「" + company.getShortName() + "」已开通成功\r\n官网地址:" + siteUrl
+ "\r\n后台地址:" + adminUrl + "\r\n登录账号:" + company.getEmail() + "\r\n登录密码:" + company.getPassword();
emailRecordService.sendEmail(title, plain, company.getEmail(), company.getTid());
}
}
return company;
}
@@ -30,22 +30,23 @@ public class EmailTemplateUtil {
* @param email 邮箱
* @param tenantId 租户ID
*/
public void sendRegisterSuccessEmail(String username, String phone, String password, String email, Integer tenantId) {
public void sendRegisterSuccessEmail(String siteName, String siteUrl, String adminUrl, String account, String password, String email, Integer tenantId) {
try {
String title = "恭喜!您的账号已注册成功";
Map<String, Object> data = new HashMap<>();
data.put("username", username);
data.put("phone", phone);
String title = "您的企业官网已开通成功";
Map<String, Object> data = new HashMap<>(8);
data.put("siteName", siteName);
data.put("siteUrl", siteUrl);
data.put("adminUrl", adminUrl);
data.put("account", account);
data.put("password", password);
if (email != null && !email.trim().isEmpty()) {
data.put("email", email);
}
data.put("sendTime", DateUtil.now());
emailRecordService.sendHtmlEmail(title, "register-success.html", data, new String[]{email});
} catch (Exception e) {
// 如果HTML邮件发送失败,降级为文本邮件
String content = "恭喜!您的WebSoft账号已注册成功\r\n用户名:" + username + "\r\n手机号码:" + phone + "\r\n密码" + password;
emailRecordService.sendEmail("恭喜!您的WebSoft账号已注册成功", content, email, tenantId);
String content = "恭喜!您的企业官网「" + siteName + "」已开通成功\r\n官网地址" + siteUrl
+ "\r\n后台地址:" + adminUrl + "\r\n登录账号:" + account + "\r\n登录密码:" + password;
emailRecordService.sendEmail("您的企业官网已开通成功", content, email, tenantId);
}
}
@@ -14,16 +14,10 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.wrap { width: 100%; background: #ffffff; padding: 40px 0 36px; }
.wrap { width: 100%; background: #ffffff; padding: 40px 0 30px; }
.container { max-width: 680px; margin: 0 auto; padding: 0 22px; }
.brand { text-align: center; margin-bottom: 28px; }
.brand-logo {
height: 36px;
width: auto;
display: inline-block;
}
.brand-logo { height: 36px; width: auto; display: inline-block; }
.page-title {
text-align: center;
font-size: 40px;
@@ -41,40 +35,39 @@
margin: 0 auto 30px;
max-width: 560px;
}
.lede strong { font-weight: 600; }
/* 一张大资料卡,左"APPLE 账户"右"站点信息" */
.info-card {
.card {
background-color: #f5f5f7;
border-radius: 18px;
padding: 28px 32px 24px;
padding: 28px 32px;
margin: 0 0 22px;
}
.info-card-title {
.card-caption {
font-size: 13px;
line-height: 1.3;
color: #6e6e73;
margin-bottom: 14px;
font-weight: 400;
margin-bottom: 8px;
letter-spacing: -0.008em;
}
.info-cols {
display: table;
width: 100%;
border-spacing: 0;
.card-title {
font-size: 22px;
font-weight: 600;
letter-spacing: -0.022em;
color: #1d1d1f;
margin-bottom: 6px;
}
.info-col {
display: table-cell;
vertical-align: top;
width: 50%;
padding-right: 18px;
}
.info-col + .info-col {
border-left: 1px solid #d2d2d7;
padding-left: 22px;
padding-right: 0;
.card-sub {
font-size: 14px;
color: #6e6e73;
line-height: 1.5;
}
/* 登录信息字段列表 */
.field-list { margin-top: 16px; }
.field-row {
padding: 8px 0;
padding: 12px 0;
border-top: 1px solid #d2d2d7;
}
.field-row:first-child { border-top: none; padding-top: 4px; }
.field-label {
font-size: 12px;
color: #6e6e73;
@@ -87,122 +80,65 @@
word-break: break-all;
line-height: 1.4;
}
.field-value a {
color: #06c;
text-decoration: none;
}
.field-value a { color: #06c; text-decoration: none; }
.field-value a:hover { text-decoration: underline; }
.field-value.mono {
font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
font-family: 'SF Mono', 'Menlo', 'Consolas', 'Courier New', monospace;
font-size: 14px;
letter-spacing: 0.5px;
}
/* 安全提示卡片 */
.tips-card {
background-color: #f5f5f7;
border-radius: 18px;
padding: 22px 32px;
margin: 0 0 22px;
/* 安全提示 */
.tips { margin-top: 6px; font-size: 14px; color: #1d1d1f; line-height: 1.7; }
.tips div { padding-left: 14px; position: relative; }
.tips div:before {
content: "•"; position: absolute; left: 0; color: #6e6e73;
}
.tips-caption {
font-size: 13px;
color: #6e6e73;
margin-bottom: 10px;
letter-spacing: -0.008em;
}
.tips-list {
list-style: none;
font-size: 14px;
color: #1d1d1f;
line-height: 1.7;
}
.tips-list li {
padding-left: 14px;
position: relative;
}
.tips-list li:before {
content: "•";
position: absolute;
left: 0;
color: #6e6e73;
}
/* CTA 文字链接(仿 Apple 的"管理账户"链接样式) */
.cta {
text-align: center;
margin: 32px 0 6px;
}
.cta a {
font-size: 17px;
color: #06c;
/* 登录按钮 */
.btn-wrap { text-align: center; margin: 30px 0 8px; }
.btn {
display: inline-block;
background-color: #1d1d1f;
color: #ffffff;
font-size: 15px;
font-weight: 600;
letter-spacing: -0.01em;
padding: 13px 36px;
border-radius: 980px;
text-decoration: none;
letter-spacing: -0.022em;
}
.cta a:hover { text-decoration: underline; }
.cta-sub {
.btn:hover { opacity: 0.9; }
/* 时间戳 */
.send-time {
text-align: center;
font-size: 13px;
font-size: 12px;
color: #6e6e73;
margin-top: 6px;
margin-top: 18px;
}
/* 帮助条 */
.help {
text-align: center;
font-size: 13px;
color: #6e6e73;
margin: 26px auto 0;
max-width: 560px;
line-height: 1.6;
}
.help a { color: #06c; text-decoration: none; }
.help a:hover { text-decoration: underline; }
/* 底部 */
.footer {
text-align: center;
margin-top: 44px;
padding-top: 22px;
margin-top: 40px;
padding-top: 24px;
border-top: 1px solid #d2d2d7;
}
.footer-brand {
font-size: 14px;
font-weight: 600;
color: #1d1d1f;
margin-bottom: 10px;
margin-bottom: 12px;
letter-spacing: -0.016em;
}
.footer-links {
font-size: 13px;
line-height: 1.8;
}
.footer-links a {
color: #6e6e73;
margin: 0 14px;
text-decoration: none;
}
.footer-links { font-size: 13px; color: #6e6e73; line-height: 1.8; }
.footer-links a { color: #6e6e73; margin: 0 14px; text-decoration: none; }
.footer-links a:hover { color: #1d1d1f; text-decoration: underline; }
.copyright {
font-size: 12px;
color: #6e6e73;
margin-top: 14px;
line-height: 1.4;
}
.copyright { font-size: 12px; color: #6e6e73; margin-top: 16px; line-height: 1.4; }
.copyright a { color: #6e6e73; }
@media (max-width: 600px) {
.page-title { font-size: 30px; }
.lede { font-size: 16px; }
.card { padding: 22px 22px; border-radius: 16px; }
.container { padding: 0 16px; }
.info-card, .tips-card { padding: 22px 22px; border-radius: 16px; }
.info-cols { display: block; }
.info-col { display: block; width: 100%; padding: 0; }
.info-col + .info-col {
border-left: none;
border-top: 1px solid #d2d2d7;
padding: 12px 0 0;
margin-top: 12px;
}
}
</style>
</head>
@@ -216,68 +152,58 @@
<!-- Title + Lede -->
<h1 class="page-title">开通成功</h1>
<p class="lede">
您好!您的企业官网「<strong>${siteName!}</strong>」已开通成功。
</p>
<p class="lede">恭喜!您的企业官网已开通成功,可使用下方信息登录管理后台。</p>
<!-- 资料大卡:左站点 / 右账号 -->
<div class="info-card">
<div class="info-card-title">账户信息</div>
<div class="info-cols">
<div class="info-col">
<div class="field-row">
<div class="field-label">官网地址</div>
<div class="field-value">
<a href="${siteUrl!}">${siteUrl!}</a>
</div>
</div>
<div class="field-row">
<div class="field-label">后台地址</div>
<div class="field-value">
<a href="${adminUrl!}">${adminUrl!}</a>
</div>
</div>
<!-- 站点信息 + 登录信息主卡 -->
<div class="card">
<div class="card-caption">站点信息</div>
<div class="card-title">企业官网「${siteName!}」</div>
<div class="card-sub">请妥善保管登录信息,首次登录后建议立即修改密码。</div>
<div class="field-list">
<div class="field-row">
<div class="field-label">官网地址</div>
<div class="field-value"><a href="${siteUrl!}">${siteUrl!}</a></div>
</div>
<div class="info-col">
<div class="field-row">
<div class="field-label">登录账号</div>
<div class="field-value">${account!}</div>
</div>
<div class="field-row">
<div class="field-label">登录密码</div>
<div class="field-value mono">${password!}</div>
</div>
<div class="field-row">
<div class="field-label">后台地址</div>
<div class="field-value"><a href="${adminUrl!}">${adminUrl!}</a></div>
</div>
<div class="field-row">
<div class="field-label">登录账号</div>
<div class="field-value">${account!}</div>
</div>
<div class="field-row">
<div class="field-label">登录密码</div>
<div class="field-value mono">${password!}</div>
</div>
</div>
</div>
<!-- 安全提示 -->
<div class="tips-card">
<div class="tips-caption">安全提示</div>
<ul class="tips-list">
<li>请妥善保管登录密码,不要通过邮件、聊天工具发送给他人。</li>
<li>建议首次登录后立即进入「个人中心 > 修改密码」重置密码。</li>
<li>如发现可疑登录或账号被盗,请立即联系客服并修改密码。</li>
</ul>
<div class="card">
<div class="card-caption">安全提示</div>
<div class="tips">
<div>请妥善保管登录密码,不要通过邮件、聊天工具发送给他人。</div>
<div>建议首次登录后立即进入「个人中心 &gt; 修改密码」重置密码。</div>
<div>如发现可疑登录或账号被盗,请立即联系客服并修改密码。</div>
</div>
</div>
<!-- CTA -->
<div class="cta">
<a href="${adminUrl!}">点击登录后台 →</a>
<!-- 登录按钮 -->
<div class="btn-wrap">
<a href="${adminUrl!}" class="btn">登录管理后台</a>
</div>
<div class="cta-sub">如按钮未生效,请将上面的后台地址复制到浏览器打开。</div>
<!-- 帮助 -->
<p class="help">
如有疑问,请联系客服或访问 <a href="${helpUrl!'https://websoft.top/help'}">帮助中心</a>
</p>
<!-- 时间戳 -->
<div class="send-time">发送时间:${sendTime!}</div>
<!-- Footer -->
<div class="footer">
<div class="footer-brand">WebSoft</div>
<div class="footer-links">
<a href="https://websoft.top">官方网站</a>
<a href="https://websoft.top/help">帮助中心</a>
<a href="${helpUrl!'https://websoft.top/help'}">帮助中心</a>
<a href="https://websoft.top/contact">联系我们</a>
<a href="https://websoft.top/settings">账户设置</a>
</div>