Skip to content

Commit 4242526

Browse files
committed
feat: 动态绑定验证码通知模板
1 parent 95eac00 commit 4242526

9 files changed

Lines changed: 241 additions & 35 deletions

File tree

src/main/java/io/github/opensabre/sysadmin/captcha/enums/BusinessScenario.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,25 @@ public enum BusinessScenario {
1313
* Login with image captcha scenario
1414
* Used for graphical verification during user login process
1515
*/
16-
LOGIN_IMAGE("LOGIN_IMAGE", CaptchaType.IMAGE, null, "登录时图形验证码", 4, 300, 1, -1, 100),
16+
LOGIN_IMAGE("LOGIN_IMAGE", CaptchaType.IMAGE, null, null, "登录时图形验证码", 4, 300, 1, -1, 100),
1717

1818
/**
1919
* Registration with image captcha scenario
2020
* Used for graphical verification during user registration process
2121
*/
22-
REGISTER_IMAGE("REGISTER_IMAGE", CaptchaType.IMAGE, null, "注册时图形验证码", 4, 60, 3, 60, 50),
22+
REGISTER_IMAGE("REGISTER_IMAGE", CaptchaType.IMAGE, null, null, "注册时图形验证码", 4, 60, 3, 60, 50),
2323

2424
/**
2525
* Login with SMS captcha scenario
2626
* Used for SMS verification during user login process
2727
*/
28-
LOGIN_SMS("LOGIN_SMS", CaptchaType.SMS, "CAPTCHA", "登录时短信验证码", 6, 60, 2, -1, 100),
28+
LOGIN_SMS("LOGIN_SMS", CaptchaType.SMS, "CAPTCHA", "NOTIFY_TPL_LOGIN_SMS", "登录时短信验证码", 6, 60, 2, -1, 100),
2929

3030
/**
3131
* Login with Email captcha scenario
3232
* Used for Email verification during user login process
3333
*/
34-
LOGIN_EMAIL("LOGIN_EMAIL", CaptchaType.EMAIL, "CAPTCHA", "登录时邮箱验证码", 6, 300, 3, 60, 100);
34+
LOGIN_EMAIL("LOGIN_EMAIL", CaptchaType.EMAIL, "CAPTCHA", "NOTIFY_TPL_LOGIN_EMAIL", "登录时邮箱验证码", 6, 300, 3, 60, 100);
3535
/**
3636
* Unique code identifying the business scenario
3737
*/
@@ -47,6 +47,11 @@ public enum BusinessScenario {
4747
*/
4848
private final String templateCode;
4949

50+
/**
51+
* 通知中心模板ID
52+
*/
53+
private final String notificationTemplateId;
54+
5055
/**
5156
* Human-readable description of the scenario
5257
*/
@@ -88,12 +93,13 @@ public enum BusinessScenario {
8893
* @param captchaExpireTime Expiration time in seconds
8994
* @param captchaAttempts Maximum verification attempts
9095
*/
91-
BusinessScenario(String code, CaptchaType type, String templateCode, String description,
96+
BusinessScenario(String code, CaptchaType type, String templateCode, String notificationTemplateId, String description,
9297
int captchaLength, int captchaExpireTime,
9398
int captchaAttempts, int minInterval, int maxLimitCount) {
9499
this.code = code;
95100
this.type = type;
96101
this.templateCode = templateCode;
102+
this.notificationTemplateId = notificationTemplateId;
97103
this.description = description;
98104
this.captchaLength = captchaLength;
99105
this.captchaExpireTime = captchaExpireTime;

src/main/java/io/github/opensabre/sysadmin/captcha/model/po/CaptchaScene.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public class CaptchaScene extends BasePo {
2929

3030
private String templateCode;
3131

32+
private String notificationTemplateId;
33+
3234
private String description;
3335

3436
private int captchaLength;
@@ -52,6 +54,7 @@ public static CaptchaScene from(BusinessScenario scenario) {
5254
.sceneName(scenario.getDescription())
5355
.captchaType(scenario.getType())
5456
.templateCode(scenario.getTemplateCode())
57+
.notificationTemplateId(scenario.getNotificationTemplateId())
5558
.description(scenario.getDescription())
5659
.captchaLength(scenario.getCaptchaLength())
5760
.captchaExpireTime(scenario.getCaptchaExpireTime())
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.github.opensabre.sysadmin.captcha.service;
2+
3+
import io.github.opensabre.sysadmin.captcha.enums.CaptchaType;
4+
import io.github.opensabre.sysadmin.captcha.model.po.CaptchaScene;
5+
import io.github.opensabre.sysadmin.notification.enums.NotificationSendStatus;
6+
import io.github.opensabre.sysadmin.notification.enums.NotificationType;
7+
import io.github.opensabre.sysadmin.notification.model.form.NotificationSendForm;
8+
import io.github.opensabre.sysadmin.notification.model.po.NotificationTemplateConfig;
9+
import io.github.opensabre.sysadmin.notification.model.vo.NotificationSendResponse;
10+
import io.github.opensabre.sysadmin.notification.service.INotificationTemplateConfigService;
11+
import io.github.opensabre.sysadmin.notification.service.NotificationServiceManager;
12+
import org.apache.commons.lang3.StringUtils;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.util.Assert;
15+
16+
import java.util.Map;
17+
18+
@Service
19+
public class CaptchaNotificationSender {
20+
21+
private final INotificationTemplateConfigService notificationTemplateConfigService;
22+
23+
private final NotificationServiceManager notificationServiceManager;
24+
25+
public CaptchaNotificationSender(INotificationTemplateConfigService notificationTemplateConfigService,
26+
NotificationServiceManager notificationServiceManager) {
27+
this.notificationTemplateConfigService = notificationTemplateConfigService;
28+
this.notificationServiceManager = notificationServiceManager;
29+
}
30+
31+
public String sendCaptcha(CaptchaScene scene, String target, String code) {
32+
Assert.notNull(scene, "Captcha scene must not be null");
33+
Assert.isTrue(StringUtils.isNotBlank(scene.getNotificationTemplateId()),
34+
"Captcha scene notification template must not be blank: " + scene.getSceneCode());
35+
36+
NotificationTemplateConfig template = notificationTemplateConfigService.getFormData(scene.getNotificationTemplateId());
37+
Assert.notNull(template, "Notification template not found: " + scene.getNotificationTemplateId());
38+
Assert.isTrue(Boolean.TRUE.equals(template.getEnabled()),
39+
"Notification template disabled: " + scene.getNotificationTemplateId());
40+
Assert.isTrue(matchesChannel(scene.getCaptchaType(), template.getChannel()),
41+
"Notification template channel does not match captcha type: " + scene.getSceneCode());
42+
43+
NotificationSendResponse response = notificationServiceManager.sendNotification(new NotificationSendForm(
44+
target,
45+
template.getSceneCode(),
46+
template.getChannel(),
47+
Map.of(
48+
"code", code,
49+
"minutes", String.valueOf(scene.getCaptchaExpireTime() / 60)
50+
)
51+
));
52+
Assert.isTrue(response.getStatus() == NotificationSendStatus.SUCCESS,
53+
"Captcha notification send failed: " + response.getFailureReason());
54+
return response.getMessageId();
55+
}
56+
57+
private boolean matchesChannel(CaptchaType captchaType, NotificationType channel) {
58+
return (captchaType == CaptchaType.SMS && channel == NotificationType.SMS)
59+
|| (captchaType == CaptchaType.EMAIL && channel == NotificationType.EMAIL);
60+
}
61+
}

src/main/java/io/github/opensabre/sysadmin/captcha/service/impl/EmailCaptchaService.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
import io.github.opensabre.sysadmin.captcha.model.po.ClientInfo;
55
import io.github.opensabre.sysadmin.captcha.model.po.CaptchaScene;
66
import io.github.opensabre.sysadmin.captcha.model.vo.CaptchaVo;
7-
import io.github.opensabre.sysadmin.notification.enums.NotificationTemplate;
8-
import io.github.opensabre.sysadmin.notification.enums.NotificationType;
9-
import io.github.opensabre.sysadmin.notification.service.NotificationServiceManager;
7+
import io.github.opensabre.sysadmin.captcha.service.CaptchaNotificationSender;
108
import lombok.extern.slf4j.Slf4j;
119
import org.springframework.beans.factory.annotation.Autowired;
1210
import org.springframework.stereotype.Service;
@@ -18,12 +16,12 @@
1816
@Service
1917
public class EmailCaptchaService extends CaptchaService {
2018

21-
private final NotificationServiceManager notificationServiceManager;
19+
private final CaptchaNotificationSender captchaNotificationSender;
2220

2321
@Autowired
24-
public EmailCaptchaService(EmailCaptchaGenerator captchaGenerator, NotificationServiceManager notificationServiceManager) {
22+
public EmailCaptchaService(EmailCaptchaGenerator captchaGenerator, CaptchaNotificationSender captchaNotificationSender) {
2523
super(captchaGenerator);
26-
this.notificationServiceManager = notificationServiceManager;
24+
this.captchaNotificationSender = captchaNotificationSender;
2725
}
2826

2927
@Override
@@ -36,15 +34,11 @@ protected void beforeGenerateCaptcha(String businessKey, CaptchaScene scenario,
3634

3735
@Override
3836
protected CaptchaVo afterGenerateCaptcha(CaptchaInfo captchaInfo) {
39-
// Send Email
40-
String messageId = notificationServiceManager.sendNotification(
37+
String messageId = captchaNotificationSender.sendCaptcha(
38+
captchaInfo.getCaptchaScene(),
4139
captchaInfo.getBusinessKey(),
42-
NotificationTemplate.CAPTCHA,
43-
captchaInfo.getCode(),
44-
captchaInfo.getCaptchaScene().getCaptchaExpireTime() / 60
45-
);
40+
captchaInfo.getCode());
4641
log.info("Email sent successfully, messageId: {}", messageId);
47-
// CaptchaVo
4842
return CaptchaVo.builder()
4943
.captchaId(captchaInfo.getCaptchaId())
5044
.expireTime(captchaInfo.getCaptchaScene().getCaptchaExpireTime())

src/main/java/io/github/opensabre/sysadmin/captcha/service/impl/SmsCaptchaService.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,24 @@
44
import io.github.opensabre.sysadmin.captcha.model.po.ClientInfo;
55
import io.github.opensabre.sysadmin.captcha.model.po.CaptchaScene;
66
import io.github.opensabre.sysadmin.captcha.model.vo.CaptchaVo;
7-
import io.github.opensabre.sysadmin.notification.enums.NotificationTemplate;
8-
import io.github.opensabre.sysadmin.notification.service.INotificationService;
9-
import jakarta.annotation.Resource;
7+
import io.github.opensabre.sysadmin.captcha.service.CaptchaNotificationSender;
108
import lombok.extern.slf4j.Slf4j;
119
import org.springframework.beans.factory.annotation.Autowired;
1210
import org.springframework.stereotype.Service;
1311

14-
import java.util.Map;
15-
1612
/**
1713
* 短信验证码服务
1814
*/
1915
@Slf4j
2016
@Service
2117
public class SmsCaptchaService extends CaptchaService {
2218

23-
private final INotificationService notificationService;
19+
private final CaptchaNotificationSender captchaNotificationSender;
2420

2521
@Autowired
26-
public SmsCaptchaService(SmsCaptchaGenerator captchaGenerator, INotificationService smsNotificationService) {
22+
public SmsCaptchaService(SmsCaptchaGenerator captchaGenerator, CaptchaNotificationSender captchaNotificationSender) {
2723
super(captchaGenerator);
28-
this.notificationService = smsNotificationService;
24+
this.captchaNotificationSender = captchaNotificationSender;
2925
}
3026

3127
@Override
@@ -38,11 +34,11 @@ protected void beforeGenerateCaptcha(String businessKey, CaptchaScene scenario,
3834

3935
@Override
4036
protected CaptchaVo afterGenerateCaptcha(CaptchaInfo captchaInfo) {
41-
// Send Sms
42-
NotificationTemplate template = NotificationTemplate.valueOf(captchaInfo.getCaptchaScene().getTemplateCode());
43-
String messageId = notificationService.send(captchaInfo.getBusinessKey(), template, captchaInfo.getCode(), 5);
37+
String messageId = captchaNotificationSender.sendCaptcha(
38+
captchaInfo.getCaptchaScene(),
39+
captchaInfo.getBusinessKey(),
40+
captchaInfo.getCode());
4441
log.info("Sms sent successfully, messageId: {}", messageId);
45-
// CaptchaVo
4642
return CaptchaVo.builder()
4743
.captchaId(captchaInfo.getCaptchaId())
4844
.expireTime(captchaInfo.getCaptchaScene().getCaptchaExpireTime())

src/main/resources/db/os-base-sysadmin-db.sql

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ DROP DATABASE IF EXISTS os_base_sysadmin;
44
CREATE DATABASE os_base_sysadmin DEFAULT CHARSET utf8mb4;
55
USE os_base_sysadmin;
66

7-
INSERT INTO `base_sys_captcha_scene` (`id`, `scene_code`, `scene_name`, `captcha_type`, `template_code`, `description`, `captcha_length`, `captcha_expire_time`, `captcha_attempts`, `min_interval`, `max_limit_count`, `enabled`, `created_by`, `updated_by`)
7+
INSERT INTO `base_sys_captcha_scene` (`id`, `scene_code`, `scene_name`, `captcha_type`, `template_code`, `notification_template_id`, `description`, `captcha_length`, `captcha_expire_time`, `captcha_attempts`, `min_interval`, `max_limit_count`, `enabled`, `created_by`, `updated_by`)
88
VALUES
9-
('LOGIN_IMAGE', 'LOGIN_IMAGE', '登录时图形验证码', 'IMAGE', NULL, '登录时图形验证码', 4, 300, 1, 60, 100, 1, 'system', 'system'),
10-
('REGISTER_IMAGE', 'REGISTER_IMAGE', '注册时图形验证码', 'IMAGE', NULL, '注册时图形验证码', 4, 60, 3, 60, 50, 1, 'system', 'system'),
11-
('LOGIN_SMS', 'LOGIN_SMS', '登录时短信验证码', 'SMS', 'CAPTCHA', '登录时短信验证码', 6, 60, 2, 60, 100, 1, 'system', 'system'),
12-
('LOGIN_EMAIL', 'LOGIN_EMAIL', '登录时邮箱验证码', 'EMAIL', 'CAPTCHA', '登录时邮箱验证码', 6, 300, 3, 60, 100, 1, 'system', 'system');
9+
('LOGIN_IMAGE', 'LOGIN_IMAGE', '登录时图形验证码', 'IMAGE', NULL, NULL, '登录时图形验证码', 4, 300, 1, 60, 100, 1, 'system', 'system'),
10+
('REGISTER_IMAGE', 'REGISTER_IMAGE', '注册时图形验证码', 'IMAGE', NULL, NULL, '注册时图形验证码', 4, 60, 3, 60, 50, 1, 'system', 'system'),
11+
('LOGIN_SMS', 'LOGIN_SMS', '登录时短信验证码', 'SMS', 'CAPTCHA', 'NOTIFY_TPL_LOGIN_SMS', '登录时短信验证码', 6, 60, 2, 60, 100, 1, 'system', 'system'),
12+
('LOGIN_EMAIL', 'LOGIN_EMAIL', '登录时邮箱验证码', 'EMAIL', 'CAPTCHA', 'NOTIFY_TPL_LOGIN_EMAIL', '登录时邮箱验证码', 6, 300, 3, 60, 100, 1, 'system', 'system');
1313

1414
INSERT INTO `base_sys_notification_scene` (`id`, `scene_code`, `scene_name`, `description`, `enabled`, `created_by`, `updated_by`)
1515
VALUES

src/main/resources/db/os-base-sysadmin-ddl.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ CREATE TABLE IF NOT EXISTS `base_sys_captcha_scene` (
3434
`scene_name` varchar(128) NOT NULL COMMENT '场景名称',
3535
`captcha_type` varchar(32) NOT NULL COMMENT '验证码类型',
3636
`template_code` varchar(64) DEFAULT NULL COMMENT '消息模板编码',
37+
`notification_template_id` varchar(64) DEFAULT NULL COMMENT '通知模板ID',
3738
`description` varchar(255) DEFAULT NULL COMMENT '描述',
3839
`captcha_length` int NOT NULL DEFAULT 4 COMMENT '验证码长度',
3940
`captcha_expire_time` int NOT NULL DEFAULT 300 COMMENT '过期时间(秒)',

0 commit comments

Comments
 (0)