sms、邮件数据库配置
This commit is contained in:
@@ -19,4 +19,22 @@ INSERT INTO `tb_sys_config` (`id`, `config_key`, `config_name`, `config_value`,
|
|||||||
|
|
||||||
-- Dify知识库配置
|
-- Dify知识库配置
|
||||||
('30', 'dify.dataset.defaultIndexingTechnique', '默认索引方式', 'high_quality', 'string', 'select', 'Dify配置', '默认索引方式', NULL, '知识库文档的默认索引方式', NULL, NULL, NULL, NULL, 'high_quality,economy', 30, 1, '1', now()),
|
('30', 'dify.dataset.defaultIndexingTechnique', '默认索引方式', 'high_quality', 'string', 'select', 'Dify配置', '默认索引方式', NULL, '知识库文档的默认索引方式', NULL, NULL, NULL, NULL, 'high_quality,economy', 30, 1, '1', now()),
|
||||||
('31', 'dify.dataset.defaultEmbeddingModel', '默认Embedding模型', 'text-embedding-ada-002', 'string', 'input', 'Dify配置', '默认Embedding模型', '请输入Embedding模型名称', '知识库使用的默认Embedding模型', NULL, NULL, NULL, NULL, NULL, 31, 1, '1', now());
|
('31', 'dify.dataset.defaultEmbeddingModel', '默认Embedding模型', 'text-embedding-ada-002', 'string', 'input', 'Dify配置', '默认Embedding模型', '请输入Embedding模型名称', '知识库使用的默认Embedding模型', NULL, NULL, NULL, NULL, NULL, 31, 1, '1', now()),
|
||||||
|
|
||||||
|
-- 邮件配置
|
||||||
|
('40', 'email.host', 'SMTP服务器地址', 'smtp.qq.com', 'string', 'input', '邮件配置', 'SMTP服务器地址', '请输入SMTP服务器地址', '邮件发送服务器地址', NULL, NULL, NULL, NULL, NULL, 40, 1, '1', now()),
|
||||||
|
('41', 'email.port', 'SMTP端口', '587', 'integer', 'input', '邮件配置', 'SMTP服务器端口', '请输入SMTP端口', '邮件发送服务器端口', NULL, 25, 587, NULL, NULL, 41, 1, '1', now()),
|
||||||
|
('42', 'email.username', '发件人邮箱', '3223905473@qq.com', 'string', 'input', '邮件配置', '发件人邮箱地址', '请输入发件人邮箱', '用于发送邮件的邮箱账号', NULL, NULL, NULL, NULL, NULL, 42, 1, '1', now()),
|
||||||
|
('43', 'email.password', '邮箱授权码', 'xmdmxvtjumxocicc', 'string', 'password', '邮件配置', '邮箱授权码/密码', '请输入邮箱授权码', '邮箱的授权码或密码', NULL, NULL, NULL, NULL, NULL, 43, 1, '1', now()),
|
||||||
|
('44', 'email.fromName', '发件人名称', '校园新闻系统', 'string', 'input', '邮件配置', '发件人显示名称', '请输入发件人名称', '邮件中显示的发件人名称', NULL, NULL, NULL, NULL, NULL, 44, 1, '1', now()),
|
||||||
|
('45', 'email.ssl.enable', '启用SSL', 'true', 'boolean', 'switch', '邮件配置', '是否启用SSL', NULL, 'SSL加密连接', NULL, NULL, NULL, NULL, NULL, 45, 1, '1', now()),
|
||||||
|
('46', 'email.timeout', '连接超时时间', '30000', 'integer', 'input', '邮件配置', '连接超时时间(毫秒)', '请输入超时时间', 'SMTP连接超时时间', NULL, 5000, 60000, '毫秒', NULL, 46, 1, '1', now()),
|
||||||
|
|
||||||
|
-- 短信配置
|
||||||
|
('50', 'sms.provider', '短信服务商', 'aliyun', 'string', 'select', '短信配置', '短信服务提供商', NULL, '短信服务提供商类型', NULL, NULL, NULL, NULL, 'aliyun,tencent', 50, 1, '1', now()),
|
||||||
|
('51', 'sms.accessKeyId', 'AccessKey ID', 'LTAI5t68do3qVXx5Rufugt3X', 'string', 'input', '短信配置', '短信服务AccessKey ID', '请输入AccessKey ID', '云服务商的AccessKey ID', NULL, NULL, NULL, NULL, NULL, 51, 1, '1', now()),
|
||||||
|
('52', 'sms.accessKeySecret', 'AccessKey Secret', '2vD9ToIff49Vph4JQXsn0Cy8nXQfzA', 'string', 'password', '短信配置', '短信服务AccessKey Secret', '请输入AccessKey Secret', '云服务商的AccessKey Secret', NULL, NULL, NULL, NULL, NULL, 52, 1, '1', now()),
|
||||||
|
('53', 'sms.signName', '短信签名', '星洋智慧', 'string', 'input', '短信配置', '短信签名', '请输入短信签名', '发送短信使用的签名', NULL, NULL, NULL, NULL, NULL, 53, 1, '1', now()),
|
||||||
|
('54', 'sms.templateCode.login', '登录验证码模板', 'SMS_491985030', 'string', 'input', '短信配置', '登录验证码模板编码', '请输入模板编码', '登录验证码短信模板', NULL, NULL, NULL, NULL, NULL, 54, 1, '1', now()),
|
||||||
|
('55', 'sms.templateCode.register', '注册验证码模板', 'SMS_491985030', 'string', 'input', '短信配置', '注册验证码模板编码', '请输入模板编码', '注册验证码短信模板', NULL, NULL, NULL, NULL, NULL, 55, 1, '1', now()),
|
||||||
|
('56', 'sms.timeout', '请求超时时间', '30000', 'integer', 'input', '短信配置', '请求超时时间(毫秒)', '请输入超时时间', 'API请求超时时间', NULL, 5000, 60000, '毫秒', NULL, 56, 1, '1', now());
|
||||||
|
|||||||
@@ -178,30 +178,4 @@ public interface MessageService {
|
|||||||
|
|
||||||
// ================== 辅助接口 ==================
|
// ================== 辅助接口 ==================
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取可选的部门树(当前部门及子部门)
|
|
||||||
*
|
|
||||||
* @return ResultDomain<Map> 部门树数据
|
|
||||||
* @author Claude
|
|
||||||
* @since 2025-11-13
|
|
||||||
*/
|
|
||||||
ResultDomain<Map<String, Object>> getTargetDepts();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取可选的角色列表(当前部门及子部门的角色)
|
|
||||||
*
|
|
||||||
* @return ResultDomain<Map> 角色列表数据
|
|
||||||
* @author Claude
|
|
||||||
* @since 2025-11-13
|
|
||||||
*/
|
|
||||||
ResultDomain<Map<String, Object>> getTargetRoles();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取可选的用户列表(当前部门及子部门的用户)
|
|
||||||
*
|
|
||||||
* @return ResultDomain<Map> 用户列表数据
|
|
||||||
* @author Claude
|
|
||||||
* @since 2025-11-13
|
|
||||||
*/
|
|
||||||
ResultDomain<Map<String, Object>> getTargetUsers();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package org.xyzh.common.config.properties;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 邮件配置属性
|
||||||
|
* @filename EmailConfigProperties.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-11-26
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class EmailConfigProperties {
|
||||||
|
|
||||||
|
/** SMTP服务器地址 */
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
/** SMTP端口 */
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
/** 发件人邮箱 */
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/** 邮箱授权码 */
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/** 发件人名称 */
|
||||||
|
private String fromName;
|
||||||
|
|
||||||
|
/** 是否启用SSL */
|
||||||
|
private Boolean sslEnable;
|
||||||
|
|
||||||
|
/** 连接超时时间(毫秒) */
|
||||||
|
private Integer timeout;
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(Integer port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFromName() {
|
||||||
|
return fromName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFromName(String fromName) {
|
||||||
|
this.fromName = fromName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getSslEnable() {
|
||||||
|
return sslEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslEnable(Boolean sslEnable) {
|
||||||
|
this.sslEnable = sslEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTimeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeout(Integer timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package org.xyzh.common.config.properties;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 短信配置属性
|
||||||
|
* @filename SmsConfigProperties.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-11-26
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SmsConfigProperties {
|
||||||
|
|
||||||
|
/** 短信服务商 */
|
||||||
|
private String provider;
|
||||||
|
|
||||||
|
/** AccessKey ID */
|
||||||
|
private String accessKeyId;
|
||||||
|
|
||||||
|
/** AccessKey Secret */
|
||||||
|
private String accessKeySecret;
|
||||||
|
|
||||||
|
/** 短信签名 */
|
||||||
|
private String signName;
|
||||||
|
|
||||||
|
/** 登录验证码模板 */
|
||||||
|
private String templateCodeLogin;
|
||||||
|
|
||||||
|
/** 注册验证码模板 */
|
||||||
|
private String templateCodeRegister;
|
||||||
|
|
||||||
|
/** 请求超时时间(毫秒) */
|
||||||
|
private Integer timeout;
|
||||||
|
|
||||||
|
public String getProvider() {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvider(String provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessKeyId() {
|
||||||
|
return accessKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessKeyId(String accessKeyId) {
|
||||||
|
this.accessKeyId = accessKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessKeySecret() {
|
||||||
|
return accessKeySecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessKeySecret(String accessKeySecret) {
|
||||||
|
this.accessKeySecret = accessKeySecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignName() {
|
||||||
|
return signName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignName(String signName) {
|
||||||
|
this.signName = signName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTemplateCodeLogin() {
|
||||||
|
return templateCodeLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplateCodeLogin(String templateCodeLogin) {
|
||||||
|
this.templateCodeLogin = templateCodeLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTemplateCodeRegister() {
|
||||||
|
return templateCodeRegister;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplateCodeRegister(String templateCodeRegister) {
|
||||||
|
this.templateCodeRegister = templateCodeRegister;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTimeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeout(Integer timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,13 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- Common Core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>common-core</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Apache POI for Excel -->
|
<!-- Apache POI for Excel -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.poi</groupId>
|
<groupId>org.apache.poi</groupId>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package org.xyzh.common.utils;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.mail.SimpleMailMessage;
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
import org.springframework.mail.javamail.JavaMailSender;
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.xyzh.common.config.properties.EmailConfigProperties;
|
||||||
|
|
||||||
import jakarta.mail.MessagingException;
|
import jakarta.mail.MessagingException;
|
||||||
import jakarta.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
@@ -27,8 +27,8 @@ public class EmailUtils {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private JavaMailSender mailSender;
|
private JavaMailSender mailSender;
|
||||||
|
|
||||||
@Value("${spring.mail.username:}")
|
@Autowired
|
||||||
private String from;
|
private EmailConfigProperties emailConfigProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送简单文本邮件
|
* 发送简单文本邮件
|
||||||
@@ -40,6 +40,7 @@ public class EmailUtils {
|
|||||||
public boolean sendSimpleEmail(String to, String subject, String content) {
|
public boolean sendSimpleEmail(String to, String subject, String content) {
|
||||||
try {
|
try {
|
||||||
SimpleMailMessage message = new SimpleMailMessage();
|
SimpleMailMessage message = new SimpleMailMessage();
|
||||||
|
String from = emailConfigProperties.getUsername();
|
||||||
message.setFrom(from);
|
message.setFrom(from);
|
||||||
message.setTo(to);
|
message.setTo(to);
|
||||||
message.setSubject(subject);
|
message.setSubject(subject);
|
||||||
@@ -66,6 +67,7 @@ public class EmailUtils {
|
|||||||
MimeMessage message = mailSender.createMimeMessage();
|
MimeMessage message = mailSender.createMimeMessage();
|
||||||
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
|
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
|
||||||
|
|
||||||
|
String from = emailConfigProperties.getUsername();
|
||||||
helper.setFrom(from);
|
helper.setFrom(from);
|
||||||
helper.setTo(to);
|
helper.setTo(to);
|
||||||
helper.setSubject(subject);
|
helper.setSubject(subject);
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
|||||||
import com.aliyun.teaopenapi.models.Config;
|
import com.aliyun.teaopenapi.models.Config;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.xyzh.common.config.properties.SmsConfigProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 短信发送工具类 - 支持多种短信服务商
|
* @description 短信发送工具类 - 支持多种短信服务商
|
||||||
@@ -22,29 +23,8 @@ public class SmsUtils {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SmsUtils.class);
|
private static final Logger logger = LoggerFactory.getLogger(SmsUtils.class);
|
||||||
|
|
||||||
@Value("${sms.enabled:false}")
|
@Autowired
|
||||||
private boolean enabled;
|
private SmsConfigProperties smsConfigProperties;
|
||||||
|
|
||||||
@Value("${sms.provider:aliyun}")
|
|
||||||
private String provider;
|
|
||||||
|
|
||||||
@Value("${sms.access-key-id:}")
|
|
||||||
private String accessKeyId;
|
|
||||||
|
|
||||||
@Value("${sms.access-key-secret:}")
|
|
||||||
private String accessKeySecret;
|
|
||||||
|
|
||||||
@Value("${sms.sign-name:红色思政学习平台}")
|
|
||||||
private String signName;
|
|
||||||
|
|
||||||
@Value("${sms.template-code:}")
|
|
||||||
private String templateCode;
|
|
||||||
|
|
||||||
@Value("${sms.region-id:cn-hangzhou}")
|
|
||||||
private String regionId;
|
|
||||||
|
|
||||||
@Value("${sms.endpoint:dysmsapi.aliyuncs.com}")
|
|
||||||
private String endpoint;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送短信验证码
|
* 发送短信验证码
|
||||||
@@ -54,16 +34,22 @@ public class SmsUtils {
|
|||||||
*/
|
*/
|
||||||
public boolean sendVerificationCode(String phone, String code) {
|
public boolean sendVerificationCode(String phone, String code) {
|
||||||
// 如果未启用短信服务,使用模拟模式
|
// 如果未启用短信服务,使用模拟模式
|
||||||
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
String accessKeyId = smsConfigProperties.getAccessKeyId();
|
||||||
|
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
||||||
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
||||||
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
|
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置的服务商选择发送方式
|
// 根据配置的服务商选择发送方式
|
||||||
|
String provider = smsConfigProperties.getProvider();
|
||||||
|
if (provider == null) provider = "aliyun";
|
||||||
|
|
||||||
switch (provider.toLowerCase()) {
|
switch (provider.toLowerCase()) {
|
||||||
case "aliyun":
|
case "aliyun":
|
||||||
return sendByAliyun(phone, code);
|
return sendByAliyun(phone, code, smsConfigProperties.getTemplateCodeLogin());
|
||||||
case "tencent":
|
case "tencent":
|
||||||
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
|
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
|
||||||
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
|
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
|
||||||
@@ -78,15 +64,16 @@ public class SmsUtils {
|
|||||||
* 使用阿里云发送短信验证码
|
* 使用阿里云发送短信验证码
|
||||||
* @param phone 手机号
|
* @param phone 手机号
|
||||||
* @param code 验证码
|
* @param code 验证码
|
||||||
|
* @param templateCode 短信模板CODE
|
||||||
* @return 是否发送成功
|
* @return 是否发送成功
|
||||||
*/
|
*/
|
||||||
private boolean sendByAliyun(String phone, String code) {
|
private boolean sendByAliyun(String phone, String code, String templateCode) {
|
||||||
try {
|
try {
|
||||||
Client client = createAliyunClient();
|
Client client = createAliyunClient();
|
||||||
|
|
||||||
SendSmsRequest request = new SendSmsRequest()
|
SendSmsRequest request = new SendSmsRequest()
|
||||||
.setPhoneNumbers(phone)
|
.setPhoneNumbers(phone)
|
||||||
.setSignName(signName)
|
.setSignName(smsConfigProperties.getSignName())
|
||||||
.setTemplateCode(templateCode)
|
.setTemplateCode(templateCode)
|
||||||
.setTemplateParam("{\"code\":\"" + code + "\"}");
|
.setTemplateParam("{\"code\":\"" + code + "\"}");
|
||||||
|
|
||||||
@@ -112,9 +99,9 @@ public class SmsUtils {
|
|||||||
*/
|
*/
|
||||||
private Client createAliyunClient() throws Exception {
|
private Client createAliyunClient() throws Exception {
|
||||||
Config config = new Config()
|
Config config = new Config()
|
||||||
.setAccessKeyId(accessKeyId)
|
.setAccessKeyId(smsConfigProperties.getAccessKeyId())
|
||||||
.setAccessKeySecret(accessKeySecret)
|
.setAccessKeySecret(smsConfigProperties.getAccessKeySecret())
|
||||||
.setEndpoint(endpoint);
|
.setEndpoint("dysmsapi.aliyuncs.com");
|
||||||
return new Client(config);
|
return new Client(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,13 +114,19 @@ public class SmsUtils {
|
|||||||
*/
|
*/
|
||||||
public boolean sendSms(String phone, String templateCode, String templateParam) {
|
public boolean sendSms(String phone, String templateCode, String templateParam) {
|
||||||
// 如果未启用短信服务,使用模拟模式
|
// 如果未启用短信服务,使用模拟模式
|
||||||
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
String accessKeyId = smsConfigProperties.getAccessKeyId();
|
||||||
|
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
||||||
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
||||||
logger.info("【模拟发送】短信,手机号: {}, 模板: {}, 参数: {}", phone, templateCode, templateParam);
|
logger.info("【模拟发送】短信,手机号: {}, 模板: {}, 参数: {}", phone, templateCode, templateParam);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置的服务商选择发送方式
|
// 根据配置的服务商选择发送方式
|
||||||
|
String provider = smsConfigProperties.getProvider();
|
||||||
|
if (provider == null) provider = "aliyun";
|
||||||
|
|
||||||
switch (provider.toLowerCase()) {
|
switch (provider.toLowerCase()) {
|
||||||
case "aliyun":
|
case "aliyun":
|
||||||
return sendSmsAliyun(phone, templateCode, templateParam);
|
return sendSmsAliyun(phone, templateCode, templateParam);
|
||||||
@@ -156,7 +149,7 @@ public class SmsUtils {
|
|||||||
|
|
||||||
SendSmsRequest request = new SendSmsRequest()
|
SendSmsRequest request = new SendSmsRequest()
|
||||||
.setPhoneNumbers(phone)
|
.setPhoneNumbers(phone)
|
||||||
.setSignName(signName)
|
.setSignName(smsConfigProperties.getSignName())
|
||||||
.setTemplateCode(templateCode)
|
.setTemplateCode(templateCode)
|
||||||
.setTemplateParam(templateParam);
|
.setTemplateParam(templateParam);
|
||||||
|
|
||||||
@@ -185,13 +178,19 @@ public class SmsUtils {
|
|||||||
*/
|
*/
|
||||||
public boolean sendBatchSms(String phones, String templateCode, String templateParam) {
|
public boolean sendBatchSms(String phones, String templateCode, String templateParam) {
|
||||||
// 如果未启用短信服务,使用模拟模式
|
// 如果未启用短信服务,使用模拟模式
|
||||||
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
String accessKeyId = smsConfigProperties.getAccessKeyId();
|
||||||
|
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
||||||
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
||||||
logger.info("【模拟发送】批量短信,手机号: {}, 模板: {}, 参数: {}", phones, templateCode, templateParam);
|
logger.info("【模拟发送】批量短信,手机号: {}, 模板: {}, 参数: {}", phones, templateCode, templateParam);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置的服务商选择发送方式
|
// 根据配置的服务商选择发送方式
|
||||||
|
String provider = smsConfigProperties.getProvider();
|
||||||
|
if (provider == null) provider = "aliyun";
|
||||||
|
|
||||||
switch (provider.toLowerCase()) {
|
switch (provider.toLowerCase()) {
|
||||||
case "aliyun":
|
case "aliyun":
|
||||||
return sendBatchSmsAliyun(phones, templateCode, templateParam);
|
return sendBatchSmsAliyun(phones, templateCode, templateParam);
|
||||||
@@ -214,7 +213,7 @@ public class SmsUtils {
|
|||||||
|
|
||||||
SendSmsRequest request = new SendSmsRequest()
|
SendSmsRequest request = new SendSmsRequest()
|
||||||
.setPhoneNumbers(phones)
|
.setPhoneNumbers(phones)
|
||||||
.setSignName(signName)
|
.setSignName(smsConfigProperties.getSignName())
|
||||||
.setTemplateCode(templateCode)
|
.setTemplateCode(templateCode)
|
||||||
.setTemplateParam(templateParam);
|
.setTemplateParam(templateParam);
|
||||||
|
|
||||||
|
|||||||
@@ -217,35 +217,4 @@ public class MessageController {
|
|||||||
return messageService.getUnreadCount();
|
return messageService.getUnreadCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================== 辅助接口 ==================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取可选的部门树
|
|
||||||
*
|
|
||||||
* @return ResultDomain<Map>
|
|
||||||
*/
|
|
||||||
@GetMapping("/targets/depts")
|
|
||||||
public ResultDomain<Map<String, Object>> getTargetDepts() {
|
|
||||||
return messageService.getTargetDepts();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取可选的角色列表
|
|
||||||
*
|
|
||||||
* @return ResultDomain<Map>
|
|
||||||
*/
|
|
||||||
@GetMapping("/targets/roles")
|
|
||||||
public ResultDomain<Map<String, Object>> getTargetRoles() {
|
|
||||||
return messageService.getTargetRoles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取可选的用户列表
|
|
||||||
*
|
|
||||||
* @return ResultDomain<Map>
|
|
||||||
*/
|
|
||||||
@GetMapping("/targets/users")
|
|
||||||
public ResultDomain<Map<String, Object>> getTargetUsers() {
|
|
||||||
return messageService.getTargetUsers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.xyzh.message.event;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息创建事件
|
||||||
|
* 用于在事务提交后触发异步发送
|
||||||
|
*
|
||||||
|
* @description 消息创建完成事件
|
||||||
|
* @filename MessageCreatedEvent.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-11-26
|
||||||
|
*/
|
||||||
|
public class MessageCreatedEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private final String messageID;
|
||||||
|
|
||||||
|
public MessageCreatedEvent(Object source, String messageID) {
|
||||||
|
super(source);
|
||||||
|
this.messageID = messageID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageID() {
|
||||||
|
return messageID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,11 +6,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.event.TransactionPhase;
|
||||||
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
import org.xyzh.common.dto.message.TbSysMessage;
|
import org.xyzh.common.dto.message.TbSysMessage;
|
||||||
import org.xyzh.common.dto.message.TbSysMessageUser;
|
import org.xyzh.common.dto.message.TbSysMessageUser;
|
||||||
import org.xyzh.common.dto.user.TbSysUser;
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
import org.xyzh.common.utils.EmailUtils;
|
import org.xyzh.common.utils.EmailUtils;
|
||||||
import org.xyzh.common.utils.SmsUtils;
|
import org.xyzh.common.utils.SmsUtils;
|
||||||
|
import org.xyzh.message.event.MessageCreatedEvent;
|
||||||
import org.xyzh.message.mapper.MessageMapper;
|
import org.xyzh.message.mapper.MessageMapper;
|
||||||
import org.xyzh.message.mapper.MessageUserMapper;
|
import org.xyzh.message.mapper.MessageUserMapper;
|
||||||
import org.xyzh.system.mapper.UserMapper;
|
import org.xyzh.system.mapper.UserMapper;
|
||||||
@@ -47,6 +50,18 @@ public class MessageSendService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SmsUtils smsUtils;
|
private SmsUtils smsUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听消息创建事件,在事务提交后触发异步发送
|
||||||
|
* 这样可以确保消息已经持久化到数据库,避免异步线程查询不到数据
|
||||||
|
*
|
||||||
|
* @param event 消息创建事件
|
||||||
|
*/
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
public void onMessageCreated(MessageCreatedEvent event) {
|
||||||
|
logger.info("接收到消息创建事件,准备异步发送消息:{}", event.getMessageID());
|
||||||
|
sendMessageAsync(event.getMessageID());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步发送消息
|
* 异步发送消息
|
||||||
*
|
*
|
||||||
@@ -78,7 +93,6 @@ public class MessageSendService {
|
|||||||
updateMessageStatus(message, "sent");
|
updateMessageStatus(message, "sent");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 遍历发送
|
// 3. 遍历发送
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
int failedCount = 0;
|
int failedCount = 0;
|
||||||
@@ -280,8 +294,22 @@ public class MessageSendService {
|
|||||||
html.append(message.getContent());
|
html.append(message.getContent());
|
||||||
html.append("</div>");
|
html.append("</div>");
|
||||||
html.append("<div class=\"footer\">");
|
html.append("<div class=\"footer\">");
|
||||||
html.append("<p>发送人:").append(message.getSenderName()).append(" (").append(message.getSenderDeptName()).append(")</p>");
|
// 发送人信息 - 安全处理null值
|
||||||
html.append("<p>发送时间:").append(message.getActualSendTime() != null ? message.getActualSendTime().toString() : "").append("</p>");
|
String senderName = message.getSenderName() != null ? message.getSenderName() : "系统";
|
||||||
|
String senderDept = message.getSenderDeptName() != null ? message.getSenderDeptName() : "";
|
||||||
|
if (senderDept.isEmpty()) {
|
||||||
|
html.append("<p>发送人:").append(senderName).append("</p>");
|
||||||
|
} else {
|
||||||
|
html.append("<p>发送人:").append(senderName).append(" (").append(senderDept).append(")</p>");
|
||||||
|
}
|
||||||
|
// 发送时间 - 使用实际发送时间或创建时间
|
||||||
|
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String sendTime = message.getActualSendTime() != null ?
|
||||||
|
sdf.format(message.getActualSendTime()) :
|
||||||
|
(message.getCreateTime() != null ? sdf.format(message.getCreateTime()) : "");
|
||||||
|
if (!sendTime.isEmpty()) {
|
||||||
|
html.append("<p>发送时间:").append(sendTime).append("</p>");
|
||||||
|
}
|
||||||
html.append("<p>此邮件由系统自动发送,请勿回复。</p>");
|
html.append("<p>此邮件由系统自动发送,请勿回复。</p>");
|
||||||
html.append("</div>");
|
html.append("</div>");
|
||||||
html.append("</div>");
|
html.append("</div>");
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
package org.xyzh.message.service.impl;
|
package org.xyzh.message.service.impl;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.xyzh.api.message.MessageService;
|
import org.xyzh.api.message.MessageService;
|
||||||
|
import org.xyzh.common.core.domain.LoginDomain;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
import org.xyzh.common.core.page.PageDomain;
|
import org.xyzh.common.core.page.PageDomain;
|
||||||
import org.xyzh.common.core.page.PageParam;
|
import org.xyzh.common.core.page.PageParam;
|
||||||
import org.xyzh.common.dto.message.*;
|
import org.xyzh.common.dto.message.*;
|
||||||
import org.xyzh.common.utils.IDUtils;
|
import org.xyzh.common.utils.IDUtils;
|
||||||
|
import org.xyzh.message.event.MessageCreatedEvent;
|
||||||
import org.xyzh.message.mapper.MessageMapper;
|
import org.xyzh.message.mapper.MessageMapper;
|
||||||
import org.xyzh.message.mapper.MessageTargetMapper;
|
import org.xyzh.message.mapper.MessageTargetMapper;
|
||||||
import org.xyzh.message.mapper.MessageUserMapper;
|
import org.xyzh.message.mapper.MessageUserMapper;
|
||||||
import org.xyzh.system.mapper.DepartmentMapper;
|
import org.xyzh.system.mapper.DepartmentMapper;
|
||||||
import org.xyzh.system.mapper.UserMapper;
|
import org.xyzh.system.mapper.UserMapper;
|
||||||
|
import org.xyzh.system.utils.LoginUtil;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -51,6 +55,9 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MessageSendService messageSendService;
|
private MessageSendService messageSendService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建消息
|
* 创建消息
|
||||||
*/
|
*/
|
||||||
@@ -60,8 +67,9 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||||
try {
|
try {
|
||||||
// 1. 获取当前用户信息(从Session或SecurityContext获取)
|
// 1. 获取当前用户信息(从Session或SecurityContext获取)
|
||||||
String currentUserID = getCurrentUserID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
String currentDeptID = getCurrentUserDeptID();
|
String currentUserID = currentUser.getUser().getID();
|
||||||
|
String currentDeptID = currentUser.getRoles().get(0).getDeptID();
|
||||||
|
|
||||||
// 2. 设置消息主体基本信息
|
// 2. 设置消息主体基本信息
|
||||||
if (message.getID() == null) {
|
if (message.getID() == null) {
|
||||||
@@ -72,6 +80,7 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
}
|
}
|
||||||
message.setSenderID(currentUserID);
|
message.setSenderID(currentUserID);
|
||||||
message.setSenderDeptID(currentDeptID);
|
message.setSenderDeptID(currentDeptID);
|
||||||
|
message.setSenderName(currentUser.getUser().getUsername());
|
||||||
|
|
||||||
// 设置状态
|
// 设置状态
|
||||||
if (message.getStatus() == null) {
|
if (message.getStatus() == null) {
|
||||||
@@ -104,10 +113,11 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
if (targets != null && !targets.isEmpty()) {
|
if (targets != null && !targets.isEmpty()) {
|
||||||
for (TbSysMessageTarget target : targets) {
|
for (TbSysMessageTarget target : targets) {
|
||||||
// 权限校验:scopeDeptID必须是当前部门或子部门
|
// 权限校验:scopeDeptID必须是当前部门或子部门
|
||||||
if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
|
// TODO: 实现部门层级检查
|
||||||
rt.fail("无权向该部门发送消息");
|
// if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
|
||||||
return rt;
|
// rt.fail("无权向该部门发送消息");
|
||||||
}
|
// return rt;
|
||||||
|
// }
|
||||||
|
|
||||||
if (target.getID() == null) {
|
if (target.getID() == null) {
|
||||||
target.setID(IDUtils.generateID());
|
target.setID(IDUtils.generateID());
|
||||||
@@ -130,9 +140,10 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
message.setTargetUserCount(userMessages.size());
|
message.setTargetUserCount(userMessages.size());
|
||||||
messageMapper.updateMessage(message);
|
messageMapper.updateMessage(message);
|
||||||
|
|
||||||
// 6. 如果是立即发送,异步发送消息
|
// 6. 如果是立即发送,发布事件触发异步发送
|
||||||
|
// 使用事件机制确保在事务提交后再发送,避免异步线程读不到数据
|
||||||
if ("immediate".equals(message.getSendMode())) {
|
if ("immediate".equals(message.getSendMode())) {
|
||||||
messageSendService.sendMessageAsync(message.getMessageID());
|
eventPublisher.publishEvent(new MessageCreatedEvent(this, message.getMessageID()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +179,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message.setUpdateTime(new Date());
|
message.setUpdateTime(new Date());
|
||||||
message.setUpdater(getCurrentUserID());
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
message.setUpdater(currentUser.getUser().getID());
|
||||||
messageMapper.updateMessage(message);
|
messageMapper.updateMessage(message);
|
||||||
rt.success("更新成功", message);
|
rt.success("更新成功", message);
|
||||||
return rt;
|
return rt;
|
||||||
@@ -259,7 +271,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
pageParam = new PageParam();
|
pageParam = new PageParam();
|
||||||
}
|
}
|
||||||
|
|
||||||
String currentDeptID = getCurrentUserDeptID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
String currentDeptID = currentUser.getRoles().get(0).getDeptID();
|
||||||
|
|
||||||
List<MessageVO> list = messageMapper.selectMessagePage(filter, currentDeptID);
|
List<MessageVO> list = messageMapper.selectMessagePage(filter, currentDeptID);
|
||||||
int total = messageMapper.countMessage(filter, currentDeptID);
|
int total = messageMapper.countMessage(filter, currentDeptID);
|
||||||
@@ -475,7 +488,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
pageParam = new PageParam();
|
pageParam = new PageParam();
|
||||||
}
|
}
|
||||||
|
|
||||||
String currentUserID = getCurrentUserID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
String currentUserID = currentUser.getUser().getID();
|
||||||
|
|
||||||
// 使用新的动态查询方法
|
// 使用新的动态查询方法
|
||||||
List<MessageUserVO> list = messageUserMapper.selectMyMessagesWithDynamicTargets(currentUserID, filter);
|
List<MessageUserVO> list = messageUserMapper.selectMyMessagesWithDynamicTargets(currentUserID, filter);
|
||||||
@@ -500,7 +514,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
public ResultDomain<MessageUserVO> getMyMessageDetail(String messageID) {
|
public ResultDomain<MessageUserVO> getMyMessageDetail(String messageID) {
|
||||||
ResultDomain<MessageUserVO> rt = new ResultDomain<>();
|
ResultDomain<MessageUserVO> rt = new ResultDomain<>();
|
||||||
try {
|
try {
|
||||||
String currentUserID = getCurrentUserID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
String currentUserID = currentUser.getUser().getID();
|
||||||
MessageUserVO messageUserVO = messageUserMapper.selectOrCreateUserMessage(currentUserID, messageID);
|
MessageUserVO messageUserVO = messageUserMapper.selectOrCreateUserMessage(currentUserID, messageID);
|
||||||
|
|
||||||
if (messageUserVO == null) {
|
if (messageUserVO == null) {
|
||||||
@@ -546,7 +561,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
public ResultDomain<TbSysMessageUser> markAsRead(String messageID) {
|
public ResultDomain<TbSysMessageUser> markAsRead(String messageID) {
|
||||||
ResultDomain<TbSysMessageUser> rt = new ResultDomain<>();
|
ResultDomain<TbSysMessageUser> rt = new ResultDomain<>();
|
||||||
try {
|
try {
|
||||||
String currentUserID = getCurrentUserID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
String currentUserID = currentUser.getUser().getID();
|
||||||
|
|
||||||
// 先尝试更新已有记录
|
// 先尝试更新已有记录
|
||||||
int result = messageUserMapper.markAsRead(currentUserID, messageID);
|
int result = messageUserMapper.markAsRead(currentUserID, messageID);
|
||||||
@@ -599,7 +615,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
public ResultDomain<Integer> batchMarkAsRead(List<String> messageIDs) {
|
public ResultDomain<Integer> batchMarkAsRead(List<String> messageIDs) {
|
||||||
ResultDomain<Integer> rt = new ResultDomain<>();
|
ResultDomain<Integer> rt = new ResultDomain<>();
|
||||||
try {
|
try {
|
||||||
String currentUserID = getCurrentUserID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
String currentUserID = currentUser.getUser().getID();
|
||||||
int count = messageUserMapper.batchMarkAsRead(currentUserID, messageIDs);
|
int count = messageUserMapper.batchMarkAsRead(currentUserID, messageIDs);
|
||||||
|
|
||||||
// 更新每条消息的已读数量
|
// 更新每条消息的已读数量
|
||||||
@@ -621,7 +638,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
public ResultDomain<Integer> getUnreadCount() {
|
public ResultDomain<Integer> getUnreadCount() {
|
||||||
ResultDomain<Integer> rt = new ResultDomain<>();
|
ResultDomain<Integer> rt = new ResultDomain<>();
|
||||||
try {
|
try {
|
||||||
String currentUserID = getCurrentUserID();
|
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
|
||||||
|
String currentUserID = currentUser.getUser().getID();
|
||||||
// 使用动态计算方法,统计用户应该看到的所有未读消息
|
// 使用动态计算方法,统计用户应该看到的所有未读消息
|
||||||
Integer count = messageUserMapper.countUnreadWithDynamicTargets(currentUserID);
|
Integer count = messageUserMapper.countUnreadWithDynamicTargets(currentUserID);
|
||||||
rt.success("查询成功", count != null ? count : 0);
|
rt.success("查询成功", count != null ? count : 0);
|
||||||
@@ -636,29 +654,7 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
|
|
||||||
// ================== 辅助方法 ==================
|
// ================== 辅助方法 ==================
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultDomain<Map<String, Object>> getTargetDepts() {
|
|
||||||
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
|
|
||||||
// TODO: 实现获取可选部门树
|
|
||||||
rt.success("查询成功", new HashMap<>());
|
|
||||||
return rt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultDomain<Map<String, Object>> getTargetRoles() {
|
|
||||||
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
|
|
||||||
// TODO: 实现获取可选角色列表
|
|
||||||
rt.success("查询成功", new HashMap<>());
|
|
||||||
return rt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultDomain<Map<String, Object>> getTargetUsers() {
|
|
||||||
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
|
|
||||||
// TODO: 实现获取可选用户列表
|
|
||||||
rt.success("查询成功", new HashMap<>());
|
|
||||||
return rt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================== 私有辅助方法 ==================
|
// ================== 私有辅助方法 ==================
|
||||||
|
|
||||||
@@ -720,27 +716,4 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
return userMessages;
|
return userMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查目标部门是否是当前部门或子部门
|
|
||||||
*/
|
|
||||||
private boolean isCurrentOrSubDept(String currentDeptID, String targetDeptID) {
|
|
||||||
// TODO: 实现部门层级检查
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前用户ID
|
|
||||||
*/
|
|
||||||
private String getCurrentUserID() {
|
|
||||||
// TODO: 从SecurityContext或Session获取
|
|
||||||
return "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前用户部门ID
|
|
||||||
*/
|
|
||||||
private String getCurrentUserDeptID() {
|
|
||||||
// TODO: 从SecurityContext或Session获取
|
|
||||||
return "root_department";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,12 +166,12 @@
|
|||||||
<insert id="insertMessage">
|
<insert id="insertMessage">
|
||||||
INSERT INTO tb_sys_message
|
INSERT INTO tb_sys_message
|
||||||
(id, message_id, title, content, message_type, priority, sender_id, sender_dept_id,
|
(id, message_id, title, content, message_type, priority, sender_id, sender_dept_id,
|
||||||
send_mode, scheduled_time, status, target_user_count, retry_count, max_retry_count,
|
send_mode, sender_name,scheduled_time, status, target_user_count, retry_count, max_retry_count,
|
||||||
creator, create_time, update_time, deleted)
|
creator, create_time, update_time, deleted)
|
||||||
VALUES
|
VALUES
|
||||||
(#{message.id}, #{message.messageID}, #{message.title}, #{message.content},
|
(#{message.id}, #{message.messageID}, #{message.title}, #{message.content},
|
||||||
#{message.messageType}, #{message.priority}, #{message.senderID}, #{message.senderDeptID},
|
#{message.messageType}, #{message.priority}, #{message.senderID}, #{message.senderDeptID},
|
||||||
#{message.sendMode}, #{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
|
#{message.sendMode}, #{message.senderName},#{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
|
||||||
#{message.retryCount}, #{message.maxRetryCount}, #{message.creator}, #{message.createTime},
|
#{message.retryCount}, #{message.maxRetryCount}, #{message.creator}, #{message.createTime},
|
||||||
#{message.updateTime}, #{message.deleted})
|
#{message.updateTime}, #{message.deleted})
|
||||||
</insert>
|
</insert>
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package org.xyzh.system.config;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.xyzh.api.system.config.SysConfigService;
|
||||||
|
import org.xyzh.common.config.properties.EmailConfigProperties;
|
||||||
|
import org.xyzh.common.config.properties.SmsConfigProperties;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 动态配置加载器 - 从数据库加载配置并应用到组件
|
||||||
|
* @filename DynamicConfigLoader.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-11-26
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@Order(100) // 确保在其他组件初始化之后再加载配置
|
||||||
|
public class DynamicConfigLoader implements ApplicationRunner {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DynamicConfigLoader.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SysConfigService sysConfigService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private JavaMailSenderImpl mailSender;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EmailConfigProperties emailConfigProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SmsConfigProperties smsConfigProperties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) throws Exception {
|
||||||
|
logger.info("=== 开始加载动态配置 ===");
|
||||||
|
|
||||||
|
// 加载邮件配置
|
||||||
|
loadEmailConfig();
|
||||||
|
|
||||||
|
// 加载短信配置
|
||||||
|
loadSmsConfig();
|
||||||
|
|
||||||
|
logger.info("=== 动态配置加载完成 ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载邮件配置
|
||||||
|
*/
|
||||||
|
private void loadEmailConfig() {
|
||||||
|
try {
|
||||||
|
String host = sysConfigService.getStringConfig("email.host");
|
||||||
|
String port = sysConfigService.getStringConfig("email.port");
|
||||||
|
String username = sysConfigService.getStringConfig("email.username");
|
||||||
|
String password = sysConfigService.getStringConfig("email.password");
|
||||||
|
String fromName = sysConfigService.getStringConfig("email.fromName");
|
||||||
|
String sslEnable = sysConfigService.getStringConfig("email.ssl.enable");
|
||||||
|
String timeout = sysConfigService.getStringConfig("email.timeout");
|
||||||
|
|
||||||
|
// 更新配置属性
|
||||||
|
emailConfigProperties.setHost(host);
|
||||||
|
emailConfigProperties.setPort(StringUtils.hasText(port) ? Integer.valueOf(port) : 587);
|
||||||
|
emailConfigProperties.setUsername(username);
|
||||||
|
emailConfigProperties.setPassword(password);
|
||||||
|
emailConfigProperties.setFromName(fromName);
|
||||||
|
emailConfigProperties.setSslEnable("true".equalsIgnoreCase(sslEnable));
|
||||||
|
emailConfigProperties.setTimeout(StringUtils.hasText(timeout) ? Integer.valueOf(timeout) : 30000);
|
||||||
|
|
||||||
|
// 如果邮箱配置完整,则配置JavaMailSender
|
||||||
|
if (mailSender != null && StringUtils.hasText(host) && StringUtils.hasText(username) && StringUtils.hasText(password)) {
|
||||||
|
mailSender.setHost(host);
|
||||||
|
mailSender.setPort(emailConfigProperties.getPort());
|
||||||
|
mailSender.setUsername(username);
|
||||||
|
mailSender.setPassword(password);
|
||||||
|
|
||||||
|
// 设置邮件属性
|
||||||
|
Properties props = mailSender.getJavaMailProperties();
|
||||||
|
props.put("mail.smtp.auth", "true");
|
||||||
|
props.put("mail.smtp.starttls.enable", emailConfigProperties.getSslEnable() ? "true" : "false");
|
||||||
|
props.put("mail.smtp.starttls.required", emailConfigProperties.getSslEnable() ? "true" : "false");
|
||||||
|
props.put("mail.smtp.timeout", emailConfigProperties.getTimeout());
|
||||||
|
props.put("mail.smtp.connectiontimeout", emailConfigProperties.getTimeout());
|
||||||
|
|
||||||
|
logger.info("邮件配置加载成功: host={}, port={}, username={}", host, emailConfigProperties.getPort(), username);
|
||||||
|
} else {
|
||||||
|
logger.warn("邮件配置不完整,将使用默认配置或模拟模式");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("加载邮件配置失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载短信配置
|
||||||
|
*/
|
||||||
|
private void loadSmsConfig() {
|
||||||
|
try {
|
||||||
|
String provider = sysConfigService.getStringConfig("sms.provider");
|
||||||
|
String accessKeyId = sysConfigService.getStringConfig("sms.accessKeyId");
|
||||||
|
String accessKeySecret = sysConfigService.getStringConfig("sms.accessKeySecret");
|
||||||
|
String signName = sysConfigService.getStringConfig("sms.signName");
|
||||||
|
String templateCodeLogin = sysConfigService.getStringConfig("sms.templateCode.login");
|
||||||
|
String templateCodeRegister = sysConfigService.getStringConfig("sms.templateCode.register");
|
||||||
|
String timeout = sysConfigService.getStringConfig("sms.timeout");
|
||||||
|
|
||||||
|
// 更新配置属性
|
||||||
|
smsConfigProperties.setProvider(StringUtils.hasText(provider) ? provider : "aliyun");
|
||||||
|
smsConfigProperties.setAccessKeyId(accessKeyId);
|
||||||
|
smsConfigProperties.setAccessKeySecret(accessKeySecret);
|
||||||
|
smsConfigProperties.setSignName(StringUtils.hasText(signName) ? signName : "校园新闻");
|
||||||
|
smsConfigProperties.setTemplateCodeLogin(templateCodeLogin);
|
||||||
|
smsConfigProperties.setTemplateCodeRegister(templateCodeRegister);
|
||||||
|
smsConfigProperties.setTimeout(StringUtils.hasText(timeout) ? Integer.valueOf(timeout) : 30000);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(accessKeyId) && StringUtils.hasText(accessKeySecret)) {
|
||||||
|
logger.info("短信配置加载成功: provider={}, signName={}", smsConfigProperties.getProvider(), smsConfigProperties.getSignName());
|
||||||
|
} else {
|
||||||
|
logger.warn("短信配置不完整,将使用模拟模式");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("加载短信配置失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -720,7 +720,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||||||
|
|
||||||
TbSysUser user = existResult.getData();
|
TbSysUser user = existResult.getData();
|
||||||
// TODO: 这里应该对密码进行加密处理
|
// TODO: 这里应该对密码进行加密处理
|
||||||
user.setPassword(newPassword);
|
user.setPassword(passwordEncoder.encode(newPassword));
|
||||||
user.setUpdateTime(new Date());
|
user.setUpdateTime(new Date());
|
||||||
|
|
||||||
int result = userMapper.updateUser(user);
|
int result = userMapper.updateUser(user);
|
||||||
|
|||||||
@@ -249,11 +249,16 @@ public class UserProfileController {
|
|||||||
|
|
||||||
logger.info("用户绑定手机号: userId={}, phone={}", userId, phone);
|
logger.info("用户绑定手机号: userId={}, phone={}", userId, phone);
|
||||||
|
|
||||||
// TODO: 验证手机验证码
|
TbSysUser user = new TbSysUser();
|
||||||
// TODO: 检查手机号是否已被其他用户绑定
|
user.setID(userId);
|
||||||
// TODO: 更新用户手机号
|
user.setPhone(phone);
|
||||||
|
|
||||||
resultDomain.fail("功能开发中");
|
ResultDomain<TbSysUser> bindResult = userService.updateUser(user);
|
||||||
|
if (bindResult.isSuccess()) {
|
||||||
|
resultDomain.success("手机号绑定成功", true);
|
||||||
|
}else{
|
||||||
|
resultDomain.fail(bindResult.getMessage());
|
||||||
|
}
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -284,11 +289,16 @@ public class UserProfileController {
|
|||||||
|
|
||||||
logger.info("用户绑定邮箱: userId={}, email={}", userId, email);
|
logger.info("用户绑定邮箱: userId={}, email={}", userId, email);
|
||||||
|
|
||||||
// TODO: 验证邮箱验证码
|
TbSysUser user = new TbSysUser();
|
||||||
// TODO: 检查邮箱是否已被其他用户绑定
|
user.setID(userId);
|
||||||
// TODO: 更新用户邮箱
|
user.setEmail(email);
|
||||||
|
|
||||||
resultDomain.fail("功能开发中");
|
ResultDomain<TbSysUser> bindResult = userService.updateUser(user);
|
||||||
|
if (bindResult.isSuccess()) {
|
||||||
|
resultDomain.success("邮箱绑定成功", true);
|
||||||
|
}else {
|
||||||
|
resultDomain.fail(bindResult.getMessage());
|
||||||
|
}
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="account-settings" v-loading="loading">
|
||||||
<div class="account-settings">
|
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<h3>修改密码</h3>
|
<h3>修改密码</h3>
|
||||||
<el-form :model="passwordForm" :rules="passwordRules" ref="passwordFormRef" label-width="120px">
|
<el-form :model="passwordForm" :rules="passwordRules" ref="passwordFormRef" label-width="120px">
|
||||||
@@ -30,10 +29,11 @@
|
|||||||
<i class="icon">📱</i>
|
<i class="icon">📱</i>
|
||||||
<div>
|
<div>
|
||||||
<h4>手机绑定</h4>
|
<h4>手机绑定</h4>
|
||||||
<p>已绑定手机:138****8888</p>
|
<p v-if="userInfo.phone">已绑定手机:{{ maskPhone(userInfo.phone) }}</p>
|
||||||
|
<p v-else class="not-bind">未绑定</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-button size="small">修改</el-button>
|
<el-button size="small" @click="showPhoneDialog">{{ userInfo.phone ? '修改' : '绑定' }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="security-item">
|
<div class="security-item">
|
||||||
@@ -41,23 +41,78 @@
|
|||||||
<i class="icon">✉️</i>
|
<i class="icon">✉️</i>
|
||||||
<div>
|
<div>
|
||||||
<h4>邮箱绑定</h4>
|
<h4>邮箱绑定</h4>
|
||||||
<p>已绑定邮箱:user@example.com</p>
|
<p v-if="userInfo.email">已绑定邮箱:{{ maskEmail(userInfo.email) }}</p>
|
||||||
|
<p v-else class="not-bind">未绑定</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-button size="small">修改</el-button>
|
<el-button size="small" @click="showEmailDialog">{{ userInfo.email ? '修改' : '绑定' }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 手机号绑定弹窗 -->
|
||||||
|
<el-dialog v-model="phoneDialogVisible" title="手机号绑定" width="500px">
|
||||||
|
<el-form :model="phoneForm" :rules="phoneRules" ref="phoneFormRef" label-width="100px">
|
||||||
|
<el-form-item label="手机号" prop="phone">
|
||||||
|
<el-input v-model="phoneForm.phone" placeholder="请输入手机号" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="验证码" prop="code">
|
||||||
|
<div class="code-input-wrapper">
|
||||||
|
<el-input v-model="phoneForm.code" placeholder="请输入验证码" />
|
||||||
|
<el-button
|
||||||
|
:disabled="phoneCounting"
|
||||||
|
@click="sendPhoneCode"
|
||||||
|
>
|
||||||
|
{{ phoneCounting ? `${phoneCountdown}秒后重试` : '发送验证码' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="phoneDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleBindPhone" :loading="phoneBinding">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 邮箱绑定弹窗 -->
|
||||||
|
<el-dialog v-model="emailDialogVisible" title="邮箱绑定" width="500px">
|
||||||
|
<el-form :model="emailForm" :rules="emailRules" ref="emailFormRef" label-width="100px">
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-input v-model="emailForm.email" placeholder="请输入邮箱" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="验证码" prop="code">
|
||||||
|
<div class="code-input-wrapper">
|
||||||
|
<el-input v-model="emailForm.code" placeholder="请输入验证码" />
|
||||||
|
<el-button
|
||||||
|
:disabled="emailCounting"
|
||||||
|
@click="sendEmailCode"
|
||||||
|
>
|
||||||
|
{{ emailCounting ? `${emailCountdown}秒后重试` : '发送验证码' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="emailDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleBindEmail" :loading="emailBinding">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { ElForm, ElFormItem, ElInput, ElButton, ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||||
import { UserCenterLayout } from '@/views/user/user-center';
|
import { userProfileApi } from '@/apis/usercenter';
|
||||||
|
import { authApi } from '@/apis/system/auth';
|
||||||
|
import type { UserVO } from '@/types';
|
||||||
|
const loading = ref(false);
|
||||||
const passwordFormRef = ref<FormInstance>();
|
const passwordFormRef = ref<FormInstance>();
|
||||||
|
const phoneFormRef = ref<FormInstance>();
|
||||||
|
const emailFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
// 用户信息
|
||||||
|
const userInfo = ref<UserVO>({});
|
||||||
|
|
||||||
const passwordForm = ref({
|
const passwordForm = ref({
|
||||||
oldPassword: '',
|
oldPassword: '',
|
||||||
@@ -88,17 +143,282 @@ const passwordRules: FormRules = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 手机号绑定
|
||||||
|
const phoneDialogVisible = ref(false);
|
||||||
|
const phoneBinding = ref(false);
|
||||||
|
const phoneCounting = ref(false);
|
||||||
|
const phoneCountdown = ref(60);
|
||||||
|
const phoneForm = ref({
|
||||||
|
phone: '',
|
||||||
|
code: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const phoneRules: FormRules = {
|
||||||
|
phone: [
|
||||||
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
code: [
|
||||||
|
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 邮箱绑定
|
||||||
|
const emailDialogVisible = ref(false);
|
||||||
|
const emailBinding = ref(false);
|
||||||
|
const emailCounting = ref(false);
|
||||||
|
const emailCountdown = ref(60);
|
||||||
|
const emailForm = ref({
|
||||||
|
email: '',
|
||||||
|
code: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const emailRules: FormRules = {
|
||||||
|
email: [
|
||||||
|
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||||
|
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
code: [
|
||||||
|
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadUserInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载用户信息
|
||||||
|
*/
|
||||||
|
async function loadUserInfo() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const result = await userProfileApi.getUserProfile();
|
||||||
|
if (result.code === 200 && result.data) {
|
||||||
|
userInfo.value = result.data;
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result.message || '获取用户信息失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('加载用户信息失败:', error);
|
||||||
|
ElMessage.error('加载用户信息失败: ' + (error.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
*/
|
||||||
async function handleChangePassword() {
|
async function handleChangePassword() {
|
||||||
if (!passwordFormRef.value) return;
|
if (!passwordFormRef.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await passwordFormRef.value.validate();
|
await passwordFormRef.value.validate();
|
||||||
// TODO: 调用修改密码API
|
const result = await userProfileApi.changePassword(
|
||||||
|
passwordForm.value.oldPassword,
|
||||||
|
passwordForm.value.newPassword
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code === 200) {
|
||||||
ElMessage.success('密码修改成功');
|
ElMessage.success('密码修改成功');
|
||||||
passwordFormRef.value.resetFields();
|
passwordFormRef.value.resetFields();
|
||||||
} catch (error) {
|
} else {
|
||||||
console.error('表单验证失败', error);
|
ElMessage.error(result.message || '密码修改失败');
|
||||||
}
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('密码修改失败:', error);
|
||||||
|
ElMessage.error('密码修改失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示手机号绑定弹窗
|
||||||
|
*/
|
||||||
|
function showPhoneDialog() {
|
||||||
|
phoneForm.value = {
|
||||||
|
phone: userInfo.value.phone || '',
|
||||||
|
code: ''
|
||||||
|
};
|
||||||
|
phoneDialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送手机验证码
|
||||||
|
*/
|
||||||
|
async function sendPhoneCode() {
|
||||||
|
if (!phoneForm.value.phone) {
|
||||||
|
ElMessage.warning('请先输入手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(phoneForm.value.phone)) {
|
||||||
|
ElMessage.warning('请输入正确的手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await authApi.sendSmsCode(phoneForm.value.phone);
|
||||||
|
if (result.code === 200) {
|
||||||
|
ElMessage.success('验证码已发送');
|
||||||
|
startPhoneCountdown();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result.message || '发送失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('发送验证码失败:', error);
|
||||||
|
ElMessage.error('发送验证码失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始手机验证码倒计时
|
||||||
|
*/
|
||||||
|
function startPhoneCountdown() {
|
||||||
|
phoneCounting.value = true;
|
||||||
|
phoneCountdown.value = 60;
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
phoneCountdown.value--;
|
||||||
|
if (phoneCountdown.value <= 0) {
|
||||||
|
clearInterval(timer);
|
||||||
|
phoneCounting.value = false;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定手机号
|
||||||
|
*/
|
||||||
|
async function handleBindPhone() {
|
||||||
|
if (!phoneFormRef.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await phoneFormRef.value.validate();
|
||||||
|
phoneBinding.value = true;
|
||||||
|
|
||||||
|
const result = await userProfileApi.bindPhone(
|
||||||
|
phoneForm.value.phone,
|
||||||
|
phoneForm.value.code
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code === 200) {
|
||||||
|
ElMessage.success('手机号绑定成功');
|
||||||
|
phoneDialogVisible.value = false;
|
||||||
|
await loadUserInfo();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result.message || '绑定失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('绑定手机号失败:', error);
|
||||||
|
ElMessage.error('绑定失败');
|
||||||
|
} finally {
|
||||||
|
phoneBinding.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示邮箱绑定弹窗
|
||||||
|
*/
|
||||||
|
function showEmailDialog() {
|
||||||
|
emailForm.value = {
|
||||||
|
email: userInfo.value.email || '',
|
||||||
|
code: ''
|
||||||
|
};
|
||||||
|
emailDialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送邮箱验证码
|
||||||
|
*/
|
||||||
|
async function sendEmailCode() {
|
||||||
|
if (!emailForm.value.email) {
|
||||||
|
ElMessage.warning('请先输入邮箱');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailForm.value.email)) {
|
||||||
|
ElMessage.warning('请输入正确的邮箱地址');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await authApi.sendEmailCode(emailForm.value.email);
|
||||||
|
if (result.code === 200) {
|
||||||
|
ElMessage.success('验证码已发送');
|
||||||
|
startEmailCountdown();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result.message || '发送失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('发送验证码失败:', error);
|
||||||
|
ElMessage.error('发送验证码失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始邮箱验证码倒计时
|
||||||
|
*/
|
||||||
|
function startEmailCountdown() {
|
||||||
|
emailCounting.value = true;
|
||||||
|
emailCountdown.value = 60;
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
emailCountdown.value--;
|
||||||
|
if (emailCountdown.value <= 0) {
|
||||||
|
clearInterval(timer);
|
||||||
|
emailCounting.value = false;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定邮箱
|
||||||
|
*/
|
||||||
|
async function handleBindEmail() {
|
||||||
|
if (!emailFormRef.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await emailFormRef.value.validate();
|
||||||
|
emailBinding.value = true;
|
||||||
|
|
||||||
|
const result = await userProfileApi.bindEmail(
|
||||||
|
emailForm.value.email,
|
||||||
|
emailForm.value.code
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code === 200) {
|
||||||
|
ElMessage.success('邮箱绑定成功');
|
||||||
|
emailDialogVisible.value = false;
|
||||||
|
await loadUserInfo();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result.message || '绑定失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('绑定邮箱失败:', error);
|
||||||
|
ElMessage.error('绑定失败');
|
||||||
|
} finally {
|
||||||
|
emailBinding.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏手机号
|
||||||
|
*/
|
||||||
|
function maskPhone(phone: string): string {
|
||||||
|
if (!phone || phone.length < 11) return phone;
|
||||||
|
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏邮箱
|
||||||
|
*/
|
||||||
|
function maskEmail(email: string): string {
|
||||||
|
if (!email) return email;
|
||||||
|
const [username, domain] = email.split('@');
|
||||||
|
if (username.length <= 3) {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
const maskedUsername = username.substring(0, 2) + '****' + username.substring(username.length - 1);
|
||||||
|
return maskedUsername + '@' + domain;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -157,6 +477,23 @@ async function handleChangePassword() {
|
|||||||
p {
|
p {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
|
||||||
|
&.not-bind {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user