mirror of
https://gitee.com/270580156/weiyu.git
synced 2025-12-30 10:52:26 +00:00
update
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 10:00:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 10:00:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
public enum EmailConnectionStatusEnum {
|
||||
CONNECTED("已连接"),
|
||||
DISCONNECTED("未连接"),
|
||||
CONNECTING("连接中"),
|
||||
CONNECTION_FAILED("连接失败"),
|
||||
AUTHENTICATION_FAILED("认证失败"),
|
||||
SYNCING("同步中"),
|
||||
SYNC_FAILED("同步失败");
|
||||
|
||||
private final String description;
|
||||
|
||||
EmailConnectionStatusEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-05-11 18:14:28
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-26 12:43:53
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import com.bytedesk.core.base.BaseEntity;
|
||||
import com.bytedesk.core.constant.I18Consts;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EntityListeners({EmailEntityListener.class})
|
||||
@Table(name = "bytedesk_core_email")
|
||||
public class EmailEntity extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
|
||||
@Builder.Default
|
||||
private String description = I18Consts.I18N_DESCRIPTION;
|
||||
|
||||
// 邮件服务提供商,如 QQ/GMAIL/网易 等
|
||||
@Builder.Default
|
||||
@Column(name = "email_provider")
|
||||
private String provider = EmailProviderEnum.QQ.name();
|
||||
|
||||
// 用途类型:在线客服接待/工单客服 等
|
||||
@Builder.Default
|
||||
@Column(name = "email_type")
|
||||
private String type = EmailTypeEnum.TICKET.name();
|
||||
|
||||
// 邮箱协议类型:IMAP/POP3/SMTP/EXCHANGE
|
||||
@Builder.Default
|
||||
@Column(name = "email_protocol")
|
||||
private String protocol = EmailProtocolEnum.IMAP.name();
|
||||
|
||||
// 邮箱地址
|
||||
private String emailAddress;
|
||||
|
||||
// 邮箱密码或授权码
|
||||
private String emailPassword;
|
||||
|
||||
// SMTP服务器地址
|
||||
private String smtpHost;
|
||||
|
||||
// SMTP服务器端口
|
||||
@Builder.Default
|
||||
private Integer smtpPort = 587;
|
||||
|
||||
// 是否启用SSL
|
||||
@Builder.Default
|
||||
private Boolean smtpSslEnabled = true;
|
||||
|
||||
// 是否启用TLS
|
||||
@Builder.Default
|
||||
private Boolean smtpTlsEnabled = true;
|
||||
|
||||
// IMAP服务器地址
|
||||
private String imapHost;
|
||||
|
||||
// IMAP服务器端口
|
||||
@Builder.Default
|
||||
private Integer imapPort = 993;
|
||||
|
||||
// IMAP是否启用SSL
|
||||
@Builder.Default
|
||||
private Boolean imapSslEnabled = true;
|
||||
|
||||
// POP3服务器地址
|
||||
private String pop3Host;
|
||||
|
||||
// POP3服务器端口
|
||||
@Builder.Default
|
||||
private Integer pop3Port = 995;
|
||||
|
||||
// POP3是否启用SSL
|
||||
@Builder.Default
|
||||
private Boolean pop3SslEnabled = true;
|
||||
|
||||
// Exchange配置
|
||||
// Exchange服务器地址
|
||||
private String exchangeHost;
|
||||
|
||||
// Exchange服务器端口
|
||||
@Builder.Default
|
||||
private Integer exchangePort = 993;
|
||||
|
||||
// Exchange是否启用SSL
|
||||
@Builder.Default
|
||||
private Boolean exchangeSslEnabled = true;
|
||||
|
||||
// 发件人显示名称
|
||||
private String senderName;
|
||||
|
||||
// 邮件显示名称(用于发送邮件时的显示名)
|
||||
private String displayName;
|
||||
|
||||
// 邮件同步间隔(分钟)
|
||||
@Builder.Default
|
||||
private Integer syncInterval = 5;
|
||||
|
||||
// 是否自动同步邮件
|
||||
@Builder.Default
|
||||
private Boolean autoSyncEnabled = true;
|
||||
|
||||
// 是否自动回复
|
||||
@Builder.Default
|
||||
private Boolean autoReplyEnabled = false;
|
||||
|
||||
// 自动回复内容
|
||||
@Column(length = 1000)
|
||||
private String autoReplyContent;
|
||||
|
||||
// 最后同步时间
|
||||
private ZonedDateTime lastSyncTime;
|
||||
|
||||
// 连接状态:连接成功/连接失败
|
||||
@Builder.Default
|
||||
private String connectionStatus = EmailConnectionStatusEnum.DISCONNECTED.name();
|
||||
|
||||
// 连接错误信息
|
||||
@Column(length = 500)
|
||||
private String connectionError;
|
||||
|
||||
// 是否启用,状态:启用/禁用
|
||||
@Builder.Default
|
||||
@Column(name = "is_enabled")
|
||||
private Boolean enabled = true;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "is_debug")
|
||||
private Boolean debug = true;
|
||||
|
||||
// 关联的工作组ID
|
||||
private String workgroupUid;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-02-25 09:52:34
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-03-20 17:00:07
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.SerializationUtils;
|
||||
|
||||
import com.bytedesk.core.config.BytedeskEventPublisher;
|
||||
import com.bytedesk.core.email.event.EmailCreateEvent;
|
||||
import com.bytedesk.core.email.event.EmailUpdateEvent;
|
||||
import com.bytedesk.core.utils.ApplicationContextHolder;
|
||||
|
||||
import jakarta.persistence.PostPersist;
|
||||
import jakarta.persistence.PostUpdate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EmailEntityListener {
|
||||
|
||||
@PostPersist
|
||||
public void onPostPersist(EmailEntity email) {
|
||||
log.info("onPostPersist: {}", email);
|
||||
EmailEntity cloneEmail = SerializationUtils.clone(email);
|
||||
//
|
||||
BytedeskEventPublisher bytedeskEventPublisher = ApplicationContextHolder.getBean(BytedeskEventPublisher.class);
|
||||
bytedeskEventPublisher.publishEvent(new EmailCreateEvent(cloneEmail));
|
||||
}
|
||||
|
||||
@PostUpdate
|
||||
public void onPostUpdate(EmailEntity email) {
|
||||
log.info("onPostUpdate: {}", email);
|
||||
EmailEntity cloneEmail = SerializationUtils.clone(email);
|
||||
//
|
||||
BytedeskEventPublisher bytedeskEventPublisher = ApplicationContextHolder.getBean(BytedeskEventPublisher.class);
|
||||
bytedeskEventPublisher.publishEvent(new EmailUpdateEvent(cloneEmail));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-08-01 06:18:10
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-04 18:08:08
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.format.DateTimeFormat;
|
||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* https://github.com/alibaba/easyexcel
|
||||
*/
|
||||
@Data
|
||||
public class EmailExcel {
|
||||
|
||||
@ExcelProperty(index = 0, value = "标签名称")
|
||||
@ColumnWidth(20)
|
||||
private String name;
|
||||
|
||||
@ExcelProperty(index = 1, value = "类型")
|
||||
@ColumnWidth(20)
|
||||
private String type;
|
||||
|
||||
@ExcelProperty(index = 2, value = "颜色")
|
||||
@ColumnWidth(20)
|
||||
private String color;
|
||||
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
@ExcelProperty(value = "创建时间", converter = com.bytedesk.core.converter.ZonedDateTimeConverter.class)
|
||||
@ColumnWidth(25)
|
||||
private ZonedDateTime createdAt;
|
||||
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
@ExcelProperty(value = "修改时间", converter = com.bytedesk.core.converter.ZonedDateTimeConverter.class)
|
||||
@ColumnWidth(25)
|
||||
private ZonedDateTime updatedAt;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-12 15:00:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-12 15:00:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.bytedesk.core.base.BaseExtra;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 邮件统一额外信息存储类
|
||||
* 用于解析 VisitorRequest.extra 和 ThreadEntity.extra 字段中的信息,特别是当 client/channel 为 EMAIL 时
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmailExtra extends BaseExtra {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// ==================== 邮件基础字段 ====================
|
||||
/**
|
||||
* 邮件配置ID
|
||||
*/
|
||||
private String emailConfigUid;
|
||||
|
||||
/**
|
||||
* 邮件地址
|
||||
*/
|
||||
private String emailAddress;
|
||||
|
||||
/**
|
||||
* 邮件协议类型
|
||||
*/
|
||||
private String protocol;
|
||||
|
||||
/**
|
||||
* 邮件消息ID
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 邮件主题
|
||||
*/
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 发件人邮箱地址
|
||||
*/
|
||||
private String fromAddress;
|
||||
|
||||
/**
|
||||
* 发件人姓名
|
||||
*/
|
||||
private String fromName;
|
||||
|
||||
/**
|
||||
* 收件人邮箱地址
|
||||
*/
|
||||
private String toAddresses;
|
||||
|
||||
/**
|
||||
* 抄送邮箱地址
|
||||
*/
|
||||
private String ccAddresses;
|
||||
|
||||
/**
|
||||
* 密送邮箱地址
|
||||
*/
|
||||
private String bccAddresses;
|
||||
|
||||
/**
|
||||
* 邮件内容(纯文本)
|
||||
*/
|
||||
private String contentText;
|
||||
|
||||
/**
|
||||
* 邮件内容(HTML)
|
||||
*/
|
||||
private String contentHtml;
|
||||
|
||||
/**
|
||||
* 邮件发送/接收时间
|
||||
*/
|
||||
private String emailDate;
|
||||
|
||||
/**
|
||||
* 是否有附件
|
||||
*/
|
||||
private Boolean hasAttachments;
|
||||
|
||||
/**
|
||||
* 附件信息
|
||||
*/
|
||||
private String attachments;
|
||||
|
||||
/**
|
||||
* 邮件大小(字节)
|
||||
*/
|
||||
private Long emailSize;
|
||||
|
||||
/**
|
||||
* 邮件状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 回复的原邮件ID
|
||||
*/
|
||||
private String replyToMessageId;
|
||||
|
||||
/**
|
||||
* 转发的原邮件ID
|
||||
*/
|
||||
private String forwardFromMessageId;
|
||||
|
||||
/**
|
||||
* 邮件线程ID
|
||||
*/
|
||||
private String threadId;
|
||||
|
||||
/**
|
||||
* 将对象转换为JSON字符串
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public String toJson() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JSON字符串解析对象
|
||||
* @param jsonString JSON字符串
|
||||
* @return EmailExtra对象
|
||||
*/
|
||||
public static EmailExtra fromJson(String jsonString) {
|
||||
return JSON.parseObject(jsonString, EmailExtra.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 16:57:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 17:17:35
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 邮件监听配置
|
||||
* 用于管理不同协议的监听策略
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "bytedesk.email.listener")
|
||||
public class EmailListenerConfig {
|
||||
|
||||
/**
|
||||
* 是否启用IDLE监听(仅支持IMAP协议)
|
||||
*/
|
||||
private boolean idleEnabled = true;
|
||||
|
||||
/**
|
||||
* 是否启用轮询同步(支持所有协议)
|
||||
*/
|
||||
private boolean pollingEnabled = true;
|
||||
|
||||
/**
|
||||
* IDLE监听检查间隔(毫秒)
|
||||
*/
|
||||
private long idleCheckInterval = 30000; // 30秒
|
||||
|
||||
/**
|
||||
* 轮询同步最小间隔(分钟)
|
||||
*/
|
||||
private int minPollingInterval = 1;
|
||||
|
||||
/**
|
||||
* 轮询同步最大间隔(分钟)
|
||||
*/
|
||||
private int maxPollingInterval = 60;
|
||||
|
||||
/**
|
||||
* 连接超时时间(毫秒)
|
||||
*/
|
||||
private long connectionTimeout = 30000; // 30秒
|
||||
|
||||
/**
|
||||
* 读取超时时间(毫秒)
|
||||
*/
|
||||
private long readTimeout = 30000; // 30秒
|
||||
|
||||
/**
|
||||
* 重连间隔(毫秒)
|
||||
*/
|
||||
private long reconnectInterval = 5000; // 5秒
|
||||
|
||||
/**
|
||||
* 最大重连次数
|
||||
*/
|
||||
private int maxReconnectAttempts = 3;
|
||||
|
||||
/**
|
||||
* 是否启用调试日志
|
||||
*/
|
||||
private boolean debugEnabled = false;
|
||||
|
||||
/**
|
||||
* 获取监听策略
|
||||
* @param protocol 邮件协议
|
||||
* @return 监听策略
|
||||
*/
|
||||
public ListenerStrategy getListenerStrategy(String protocol) {
|
||||
if (EmailProtocolEnum.IMAP.name().equals(protocol)) {
|
||||
if (idleEnabled) {
|
||||
return ListenerStrategy.IDLE;
|
||||
} else if (pollingEnabled) {
|
||||
return ListenerStrategy.POLLING;
|
||||
}
|
||||
} else if (EmailProtocolEnum.POP3.name().equals(protocol) ||
|
||||
EmailProtocolEnum.EXCHANGE.name().equals(protocol)) {
|
||||
if (pollingEnabled) {
|
||||
return ListenerStrategy.POLLING;
|
||||
}
|
||||
}
|
||||
return ListenerStrategy.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听策略枚举
|
||||
*/
|
||||
public enum ListenerStrategy {
|
||||
/**
|
||||
* IDLE监听(实时)
|
||||
*/
|
||||
IDLE,
|
||||
|
||||
/**
|
||||
* 轮询同步(定时)
|
||||
*/
|
||||
POLLING,
|
||||
|
||||
/**
|
||||
* 无监听
|
||||
*/
|
||||
NONE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-11-05 16:58:18
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-05-06 11:55:32
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import com.bytedesk.core.base.BasePermissions;
|
||||
|
||||
public class EmailPermissions extends BasePermissions {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 10:40:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 10:40:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
public enum EmailProtocolEnum {
|
||||
IMAP, // IMAP邮箱
|
||||
POP3, // POP3邮箱
|
||||
SMTP, // SMTP邮箱
|
||||
EXCHANGE // Exchange邮箱
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-07-23 17:02:46
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-11-14 10:35:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
/**
|
||||
* 邮件服务提供商枚举
|
||||
*/
|
||||
public enum EmailProviderEnum {
|
||||
QQ,
|
||||
TENCENT_ENTERPRISE,
|
||||
SINA,
|
||||
GMAIL,
|
||||
NETEASE_163,
|
||||
NETEASE_ENTERPRISE,
|
||||
ALIYUN_ENTERPRISE,
|
||||
HOTMAIL,
|
||||
FEISHU_ENTERPRISE,
|
||||
YAHOO,
|
||||
OTHER
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-05-11 18:25:55
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-03-11 09:23:20
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface EmailRepository extends JpaRepository<EmailEntity, Long>, JpaSpecificationExecutor<EmailEntity> {
|
||||
|
||||
Optional<EmailEntity> findByUid(String uid);
|
||||
|
||||
Boolean existsByUid(String uid);
|
||||
|
||||
List<EmailEntity> findByEnabledTrueAndDeletedFalse();
|
||||
|
||||
// Boolean existsByPlatform(String platform);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-05-11 18:26:04
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-16 16:13:52
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import com.bytedesk.core.base.BaseRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EmailRequest extends BaseRequest {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
// 邮件服务提供商(QQ/GMAIL/NETEASE等)
|
||||
private String provider;
|
||||
|
||||
// 用途类型:在线客服接待/工单客服 等
|
||||
// private String type;
|
||||
|
||||
private String protocol;
|
||||
|
||||
// 邮箱地址
|
||||
private String emailAddress;
|
||||
|
||||
// 邮箱密码或授权码
|
||||
private String emailPassword;
|
||||
|
||||
// SMTP服务器地址
|
||||
private String smtpHost;
|
||||
|
||||
// SMTP服务器端口
|
||||
private Integer smtpPort;
|
||||
|
||||
// 是否启用SSL
|
||||
private Boolean smtpSslEnabled;
|
||||
|
||||
// 是否启用TLS
|
||||
private Boolean smtpTlsEnabled;
|
||||
|
||||
// IMAP服务器地址
|
||||
private String imapHost;
|
||||
|
||||
// IMAP服务器端口
|
||||
private Integer imapPort;
|
||||
|
||||
// IMAP是否启用SSL
|
||||
private Boolean imapSslEnabled;
|
||||
|
||||
// POP3服务器地址
|
||||
private String pop3Host;
|
||||
|
||||
// POP3服务器端口
|
||||
private Integer pop3Port;
|
||||
|
||||
// POP3是否启用SSL
|
||||
private Boolean pop3SslEnabled;
|
||||
|
||||
// Exchange服务器地址
|
||||
private String exchangeHost;
|
||||
|
||||
// Exchange服务器端口
|
||||
private Integer exchangePort;
|
||||
|
||||
// Exchange是否启用SSL
|
||||
private Boolean exchangeSslEnabled;
|
||||
|
||||
// 发件人显示名称
|
||||
private String senderName;
|
||||
|
||||
// 邮件同步间隔(分钟)
|
||||
private Integer syncInterval;
|
||||
|
||||
// 是否自动同步邮件
|
||||
private Boolean autoSyncEnabled;
|
||||
|
||||
// 是否自动回复
|
||||
private Boolean autoReplyEnabled;
|
||||
|
||||
// 自动回复内容
|
||||
private String autoReplyContent;
|
||||
|
||||
// 是否启用,状态:启用/禁用
|
||||
private Boolean enabled;
|
||||
|
||||
// 是否调试,状态:调试/生产
|
||||
private Boolean debug;
|
||||
|
||||
// 关联的工作组ID
|
||||
private String workgroupUid;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-05-11 18:26:12
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-11 16:53:44
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import com.bytedesk.core.base.BaseResponse;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EmailResponse extends BaseResponse {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// 邮件服务提供商(QQ/GMAIL/NETEASE等)
|
||||
private String provider;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
// 用途类型:在线客服接待/工单客服 等
|
||||
private String type;
|
||||
|
||||
private String protocol;
|
||||
|
||||
// 邮箱地址
|
||||
private String emailAddress;
|
||||
|
||||
// 邮箱密码或授权码
|
||||
private String emailPassword;
|
||||
|
||||
// SMTP服务器地址
|
||||
private String smtpHost;
|
||||
|
||||
// SMTP服务器端口
|
||||
private Integer smtpPort;
|
||||
|
||||
// 是否启用SSL
|
||||
private Boolean smtpSslEnabled;
|
||||
|
||||
// 是否启用TLS
|
||||
private Boolean smtpTlsEnabled;
|
||||
|
||||
// IMAP服务器地址
|
||||
private String imapHost;
|
||||
|
||||
// IMAP服务器端口
|
||||
private Integer imapPort;
|
||||
|
||||
// IMAP是否启用SSL
|
||||
private Boolean imapSslEnabled;
|
||||
|
||||
// POP3服务器地址
|
||||
private String pop3Host;
|
||||
|
||||
// POP3服务器端口
|
||||
private Integer pop3Port;
|
||||
|
||||
// POP3是否启用SSL
|
||||
private Boolean pop3SslEnabled;
|
||||
|
||||
// Exchange服务器地址
|
||||
private String exchangeHost;
|
||||
|
||||
// Exchange服务器端口
|
||||
private Integer exchangePort;
|
||||
|
||||
// Exchange是否启用SSL
|
||||
private Boolean exchangeSslEnabled;
|
||||
|
||||
// 发件人显示名称
|
||||
private String senderName;
|
||||
|
||||
// 邮件同步间隔(分钟)
|
||||
private Integer syncInterval;
|
||||
|
||||
// 是否自动同步邮件
|
||||
private Boolean autoSyncEnabled;
|
||||
|
||||
// 是否自动回复
|
||||
private Boolean autoReplyEnabled;
|
||||
|
||||
// 自动回复内容
|
||||
private String autoReplyContent;
|
||||
|
||||
// 最后同步时间
|
||||
private ZonedDateTime lastSyncTime;
|
||||
|
||||
// 连接状态
|
||||
private String connectionStatus;
|
||||
|
||||
// 连接错误信息
|
||||
private String connectionError;
|
||||
|
||||
private Boolean enabled;
|
||||
|
||||
private Boolean debug;
|
||||
|
||||
// 关联的工作组ID
|
||||
private String workgroupUid;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-05-11 18:25:36
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 16:09:23
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
// import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.bytedesk.core.annotation.ActionAnnotation;
|
||||
import com.bytedesk.core.base.BaseRestController;
|
||||
import com.bytedesk.core.utils.JsonResult;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/email")
|
||||
@AllArgsConstructor
|
||||
public class EmailRestController extends BaseRestController<EmailRequest, EmailRestService> {
|
||||
|
||||
private final EmailRestService emailRestService;
|
||||
|
||||
// @PreAuthorize(RolePermissions.ROLE_ADMIN)
|
||||
@ActionAnnotation(title = "Email", action = "组织查询", description = "query email by org")
|
||||
@Override
|
||||
public ResponseEntity<?> queryByOrg(EmailRequest request) {
|
||||
|
||||
Page<EmailResponse> emails = emailRestService.queryByOrg(request);
|
||||
|
||||
return ResponseEntity.ok(JsonResult.success(emails));
|
||||
}
|
||||
|
||||
@ActionAnnotation(title = "Email", action = "用户查询", description = "query email by user")
|
||||
@Override
|
||||
public ResponseEntity<?> queryByUser(EmailRequest request) {
|
||||
|
||||
Page<EmailResponse> emails = emailRestService.queryByUser(request);
|
||||
|
||||
return ResponseEntity.ok(JsonResult.success(emails));
|
||||
}
|
||||
|
||||
@ActionAnnotation(title = "Email", action = "查询详情", description = "query email by uid")
|
||||
@Override
|
||||
public ResponseEntity<?> queryByUid(EmailRequest request) {
|
||||
|
||||
EmailResponse email = emailRestService.queryByUid(request);
|
||||
|
||||
return ResponseEntity.ok(JsonResult.success(email));
|
||||
}
|
||||
|
||||
@ActionAnnotation(title = "Email", action = "新建", description = "create email")
|
||||
@Override
|
||||
// @PreAuthorize("hasAuthority('EMAIL_CREATE')")
|
||||
public ResponseEntity<?> create(EmailRequest request) {
|
||||
|
||||
EmailResponse email = emailRestService.create(request);
|
||||
|
||||
return ResponseEntity.ok(JsonResult.success(email));
|
||||
}
|
||||
|
||||
@ActionAnnotation(title = "Email", action = "更新", description = "update email")
|
||||
@Override
|
||||
// @PreAuthorize("hasAuthority('EMAIL_UPDATE')")
|
||||
public ResponseEntity<?> update(EmailRequest request) {
|
||||
|
||||
EmailResponse email = emailRestService.update(request);
|
||||
|
||||
return ResponseEntity.ok(JsonResult.success(email));
|
||||
}
|
||||
|
||||
@ActionAnnotation(title = "Email", action = "删除", description = "delete email")
|
||||
@Override
|
||||
// @PreAuthorize("hasAuthority('EMAIL_DELETE')")
|
||||
public ResponseEntity<?> delete(EmailRequest request) {
|
||||
|
||||
emailRestService.delete(request);
|
||||
|
||||
return ResponseEntity.ok(JsonResult.success());
|
||||
}
|
||||
|
||||
@ActionAnnotation(title = "Email", action = "导出", description = "export email")
|
||||
@Override
|
||||
// @PreAuthorize("hasAuthority('TAG_EXPORT')")
|
||||
@GetMapping("/export")
|
||||
public Object export(EmailRequest request, HttpServletResponse response) {
|
||||
return exportTemplate(
|
||||
request,
|
||||
response,
|
||||
emailRestService,
|
||||
EmailExcel.class,
|
||||
"Email",
|
||||
"email"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-05-11 18:25:45
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-08-20 18:16:58
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.bytedesk.core.base.BaseRestServiceWithExport;
|
||||
import com.bytedesk.core.rbac.user.UserEntity;
|
||||
import com.bytedesk.core.uid.UidUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class EmailRestService extends BaseRestServiceWithExport<EmailEntity, EmailRequest, EmailResponse, EmailExcel> {
|
||||
|
||||
private final EmailRepository emailRepository;
|
||||
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
private final UidUtils uidUtils;
|
||||
|
||||
@Override
|
||||
protected Specification<EmailEntity> createSpecification(EmailRequest request) {
|
||||
return EmailSpecification.search(request, authService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Page<EmailEntity> executePageQuery(Specification<EmailEntity> spec, Pageable pageable) {
|
||||
return emailRepository.findAll(spec, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailResponse queryByUid(EmailRequest request) {
|
||||
Optional<EmailEntity> optional = findByUid(request.getUid());
|
||||
if (optional.isPresent()) {
|
||||
return convertToResponse(optional.get());
|
||||
} else {
|
||||
throw new RuntimeException("Email not found");
|
||||
}
|
||||
}
|
||||
|
||||
@Cacheable(value = "email", key = "#uid", unless="#result==null")
|
||||
@Override
|
||||
public Optional<EmailEntity> findByUid(String uid) {
|
||||
return emailRepository.findByUid(uid);
|
||||
}
|
||||
|
||||
public Boolean existsByUid(String uid) {
|
||||
return emailRepository.existsByUid(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailResponse create(EmailRequest request) {
|
||||
// 判断是否已经存在
|
||||
if (StringUtils.hasText(request.getUid()) && existsByUid(request.getUid())) {
|
||||
return convertToResponse(findByUid(request.getUid()).get());
|
||||
}
|
||||
//
|
||||
UserEntity user = authService.getUser();
|
||||
if (user != null) {
|
||||
request.setUserUid(user.getUid());
|
||||
}
|
||||
//
|
||||
EmailEntity entity = modelMapper.map(request, EmailEntity.class);
|
||||
if (!StringUtils.hasText(request.getUid())) {
|
||||
entity.setUid(uidUtils.getUid());
|
||||
}
|
||||
//
|
||||
EmailEntity savedEntity = save(entity);
|
||||
return convertToResponse(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailResponse update(EmailRequest request) {
|
||||
Optional<EmailEntity> optional = emailRepository.findByUid(request.getUid());
|
||||
if (optional.isPresent()) {
|
||||
EmailEntity entity = optional.get();
|
||||
modelMapper.map(request, entity);
|
||||
//
|
||||
EmailEntity savedEntity = save(entity);
|
||||
return convertToResponse(savedEntity);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Email not found");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EmailEntity doSave(EmailEntity entity) {
|
||||
return emailRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailEntity handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e, EmailEntity entity) {
|
||||
try {
|
||||
Optional<EmailEntity> latest = emailRepository.findByUid(entity.getUid());
|
||||
if (latest.isPresent()) {
|
||||
EmailEntity latestEntity = latest.get();
|
||||
// 合并需要保留的数据
|
||||
latestEntity.setName(entity.getName());
|
||||
// latestEntity.setOrder(entity.getOrder());
|
||||
// latestEntity.setDeleted(entity.isDeleted());
|
||||
return emailRepository.save(latestEntity);
|
||||
} else {
|
||||
throw new RuntimeException("无法找到最新的实体数据,uid: " + entity.getUid());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("无法处理乐观锁冲突: {}", ex.getMessage(), ex);
|
||||
throw new RuntimeException("无法处理乐观锁冲突: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByUid(String uid) {
|
||||
Optional<EmailEntity> optional = emailRepository.findByUid(uid);
|
||||
if (optional.isPresent()) {
|
||||
optional.get().setDeleted(true);
|
||||
save(optional.get());
|
||||
// emailRepository.delete(optional.get());
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Email not found");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(EmailRequest request) {
|
||||
deleteByUid(request.getUid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailResponse convertToResponse(EmailEntity entity) {
|
||||
return modelMapper.map(entity, EmailResponse.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailExcel convertToExcel(EmailEntity entity) {
|
||||
return modelMapper.map(entity, EmailExcel.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-07-09 22:19:21
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-09-22 11:02:07
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.bytedesk.core.base.BaseSpecification;
|
||||
import com.bytedesk.core.rbac.auth.AuthService;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class EmailSpecification extends BaseSpecification<EmailEntity, EmailRequest> {
|
||||
|
||||
public static Specification<EmailEntity> search(EmailRequest request, AuthService authService) {
|
||||
// log.info("request: {}", request);
|
||||
return (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
predicates.addAll(getBasicPredicates(root, criteriaBuilder, request, authService));
|
||||
// name
|
||||
if (StringUtils.hasText(request.getName())) {
|
||||
predicates.add(criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%"));
|
||||
}
|
||||
// description
|
||||
// if (StringUtils.hasText(request.getDescription())) {
|
||||
// predicates.add(criteriaBuilder.like(root.get("description"), "%" + request.getDescription() + "%"));
|
||||
// }
|
||||
//
|
||||
if (StringUtils.hasText(request.getUserUid())) {
|
||||
predicates.add(criteriaBuilder.equal(root.get("userUid"), request.getUserUid()));
|
||||
}
|
||||
// searchText
|
||||
if (StringUtils.hasText(request.getSearchText())) {
|
||||
List<Predicate> orPredicates = new ArrayList<>();
|
||||
String searchText = request.getSearchText();
|
||||
|
||||
orPredicates.add(criteriaBuilder.like(root.get("name"), "%" + searchText + "%"));
|
||||
orPredicates.add(criteriaBuilder.like(root.get("description"), "%" + searchText + "%"));
|
||||
|
||||
predicates.add(criteriaBuilder.or(orPredicates.toArray(new Predicate[0])));
|
||||
}
|
||||
//
|
||||
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 14:00:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 14:00:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 邮件同步概览状态
|
||||
*/
|
||||
@Data
|
||||
public class EmailSyncOverview {
|
||||
|
||||
// 总的启用邮件账户数
|
||||
private int totalEnabledEmails;
|
||||
|
||||
// 当前活跃的同步任务数
|
||||
private int totalActiveTasks;
|
||||
|
||||
// 总的同步任务数(包括未运行的)
|
||||
private int totalSyncTasks;
|
||||
|
||||
// 已连接的邮件账户数
|
||||
private int connectedCount;
|
||||
|
||||
// 计算连接率
|
||||
public double getConnectionRate() {
|
||||
if (totalEnabledEmails == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double) connectedCount / totalEnabledEmails * 100;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 14:00:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 14:00:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 邮件同步状态信息
|
||||
*/
|
||||
@Data
|
||||
public class EmailSyncStatus {
|
||||
|
||||
// 邮件配置UID
|
||||
private String emailUid;
|
||||
|
||||
// 是否正在运行同步任务
|
||||
private boolean running;
|
||||
|
||||
// 是否启用
|
||||
private boolean enabled;
|
||||
|
||||
// 是否启用自动同步
|
||||
private boolean autoSyncEnabled;
|
||||
|
||||
// 最后同步时间
|
||||
private ZonedDateTime lastSyncTime;
|
||||
|
||||
// 连接状态
|
||||
private String connectionStatus;
|
||||
|
||||
// 错误信息
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-07-23 17:02:46
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 17:53:14
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
/**
|
||||
* 邮件用途类型
|
||||
* SERVICE: 在线客服接待
|
||||
* TICKET: 工单客服
|
||||
*/
|
||||
public enum EmailTypeEnum {
|
||||
SERVICE,
|
||||
TICKET
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-02-25 09:59:29
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-06-10 11:35:33
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import com.bytedesk.core.email.EmailEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class EmailCreateEvent extends ApplicationEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private EmailEntity email;
|
||||
|
||||
public EmailCreateEvent(EmailEntity email) {
|
||||
super(email);
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-02-25 12:31:16
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-02-25 12:31:19
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import com.bytedesk.core.email.EmailEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class EmailDeleteEvent extends ApplicationEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private EmailEntity email;
|
||||
|
||||
public EmailDeleteEvent(EmailEntity email) {
|
||||
super(email);
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-02-25 09:59:29
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-02-25 10:01:00
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
*
|
||||
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import com.bytedesk.core.email.EmailEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class EmailUpdateEvent extends ApplicationEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private EmailEntity email;
|
||||
|
||||
public EmailUpdateEvent(EmailEntity email) {
|
||||
super(email);
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NonNullApi
|
||||
package com.bytedesk.core.email;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 16:00:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 16:00:00
|
||||
* @Description: 邮件编码测试工具类
|
||||
*/
|
||||
package com.bytedesk.core.email.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 邮件编码测试工具类
|
||||
* 用于测试和验证邮件编码解码功能
|
||||
*/
|
||||
@Slf4j
|
||||
public class EmailEncodingTestUtil {
|
||||
|
||||
/**
|
||||
* 测试MIME编码解码功能
|
||||
*/
|
||||
public static void testMimeDecoding() {
|
||||
log.info("=== 开始测试MIME编码解码功能 ===");
|
||||
|
||||
// 测试Base64编码的UTF-8字符串
|
||||
String[] testCases = {
|
||||
"=?UTF-8?B?6Zi/6YeM6YKu566x?=", // "微语AI"的Base64编码
|
||||
"=?utf-8?B?5b6u5L+h5Zui6Zif?=", // "微信公众平台"的Base64编码
|
||||
"=?UTF-8?B?d2VpeXU=?=", // "weiyu"的Base64编码
|
||||
"=?UTF-8?Q?Test_Subject?=", // Quoted-printable编码
|
||||
"=?UTF-8?B?5L2g5aW9?=", // "你好"的Base64编码
|
||||
"=?UTF-8?B?6K+36L6T5YWl?=", // "欢迎"的Base64编码
|
||||
"=?UTF-8?B?YnVpbGRlci5jb20=?=", // "builder.com"的Base64编码
|
||||
"=?UTF-8?B?5L2g5aW95L2g5aW9?=", // "你好你好"的Base64编码
|
||||
};
|
||||
|
||||
for (String testCase : testCases) {
|
||||
String decoded = EmailEncodingUtil.decodeMimeString(testCase);
|
||||
log.info("编码: {} -> 解码: {}", testCase, decoded);
|
||||
}
|
||||
|
||||
// 测试复合地址格式
|
||||
String[] addressTestCases = {
|
||||
"=?UTF-8?B?6Zi/6YeM6YKu566x?= <no-reply@mailsupport.aliyun.com>",
|
||||
"=?utf-8?B?5b6u5L+h5Zui6Zif?= <weixinmphelper@tencent.com>",
|
||||
"=?UTF-8?B?d2VpeXU=?= <weiyu@bytedesk.com>",
|
||||
"Test User <test@example.com>",
|
||||
"=?UTF-8?B?5L2g5aW9?= <hello@world.com>",
|
||||
};
|
||||
|
||||
log.info("=== 测试地址解析 ===");
|
||||
for (String testCase : addressTestCases) {
|
||||
String email = EmailEncodingUtil.extractEmailFromString(testCase);
|
||||
String name = EmailEncodingUtil.extractNameFromString(testCase);
|
||||
log.info("地址: {} -> 邮箱: {}, 姓名: {}", testCase, email, name);
|
||||
}
|
||||
|
||||
// 测试gb18030编码(新发现的问题)
|
||||
String[] gb18030TestCases = {
|
||||
"=?gb18030?B?d2VpeXU=?=", // "weiyu"的gb18030编码
|
||||
"=?GB18030?B?d2VpeXU=?=", // 大写GB18030
|
||||
"=?gb18030?B?d2VpeXU=?= <weiyu@bytedesk.com>",
|
||||
"=?gb18030?Q?weiyu?= <weiyu@bytedesk.com>",
|
||||
};
|
||||
|
||||
log.info("=== 测试GB18030编码解码 ===");
|
||||
for (String testCase : gb18030TestCases) {
|
||||
String decoded = EmailEncodingUtil.decodeMimeString(testCase);
|
||||
log.info("GB18030编码: {} -> 解码: {}", testCase, decoded);
|
||||
}
|
||||
|
||||
log.info("=== MIME编码解码测试完成 ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动解码Base64编码的UTF-8字符串(用于验证)
|
||||
*/
|
||||
public static String manualDecodeBase64(String encoded) {
|
||||
try {
|
||||
// 移除MIME编码格式 =?UTF-8?B?...?=
|
||||
String base64Part = encoded.replaceAll("=\\?UTF-8\\?B\\?(.+?)\\?=", "$1");
|
||||
base64Part = base64Part.replaceAll("=\\?utf-8\\?B\\?(.+?)\\?=", "$1");
|
||||
|
||||
// Base64解码
|
||||
byte[] decodedBytes = java.util.Base64.getDecoder().decode(base64Part);
|
||||
|
||||
// 转换为UTF-8字符串
|
||||
return new String(decodedBytes, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
log.error("手动解码失败: {}", e.getMessage());
|
||||
return encoded;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证解码结果
|
||||
*/
|
||||
public static void verifyDecoding() {
|
||||
log.info("=== 验证解码结果 ===");
|
||||
|
||||
String[] testCases = {
|
||||
"=?UTF-8?B?6Zi/6YeM6YKu566x?=", // 应该解码为 "微语AI"
|
||||
"=?utf-8?B?5b6u5L+h5Zui6Zif?=", // 应该解码为 "微信公众平台"
|
||||
"=?UTF-8?B?d2VpeXU=?=", // 应该解码为 "weiyu"
|
||||
};
|
||||
|
||||
for (String testCase : testCases) {
|
||||
String autoDecoded = EmailEncodingUtil.decodeMimeString(testCase);
|
||||
String manualDecoded = manualDecodeBase64(testCase);
|
||||
|
||||
log.info("原始: {}", testCase);
|
||||
log.info("自动解码: {}", autoDecoded);
|
||||
log.info("手动解码: {}", manualDecoded);
|
||||
log.info("结果一致: {}", autoDecoded.equals(manualDecoded));
|
||||
log.info("---");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主方法,用于独立测试
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
testMimeDecoding();
|
||||
verifyDecoding();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2025-07-01 16:00:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-02 08:45:48
|
||||
* @Description: 邮件编码解码工具类
|
||||
*/
|
||||
package com.bytedesk.core.email.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jakarta.mail.Address;
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.Multipart;
|
||||
import jakarta.mail.Part;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeUtility;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 邮件编码解码工具类
|
||||
* 用于处理MIME编码的邮件头和邮件内容提取
|
||||
*/
|
||||
@Slf4j
|
||||
public class EmailEncodingUtil {
|
||||
|
||||
/**
|
||||
* 解码MIME编码的字符串
|
||||
* 处理 =?UTF-8?B?...?= 和 =?UTF-8?Q?...?= 格式
|
||||
*/
|
||||
public static String decodeMimeString(String encodedString) {
|
||||
if (encodedString == null || encodedString.trim().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用JavaMail的MimeUtility来解码
|
||||
return MimeUtility.decodeText(encodedString);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to decode MIME string: {}, error: {}", encodedString, e.getMessage());
|
||||
return encodedString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取并解码发件人地址
|
||||
*/
|
||||
public static String extractFromAddress(Message message) {
|
||||
try {
|
||||
if (message.getFrom() != null && message.getFrom().length > 0) {
|
||||
Address fromAddress = message.getFrom()[0];
|
||||
if (fromAddress instanceof InternetAddress) {
|
||||
InternetAddress internetAddress = (InternetAddress) fromAddress;
|
||||
return internetAddress.getAddress();
|
||||
} else {
|
||||
String addressStr = fromAddress.toString();
|
||||
// 尝试从 "Name <email@domain.com>" 格式中提取邮箱地址
|
||||
return extractEmailFromString(addressStr);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract from address: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取并解码发件人姓名
|
||||
*/
|
||||
public static String extractFromName(Message message) {
|
||||
try {
|
||||
if (message.getFrom() != null && message.getFrom().length > 0) {
|
||||
Address fromAddress = message.getFrom()[0];
|
||||
if (fromAddress instanceof InternetAddress) {
|
||||
InternetAddress internetAddress = (InternetAddress) fromAddress;
|
||||
String personal = internetAddress.getPersonal();
|
||||
if (personal != null) {
|
||||
return decodeMimeString(personal);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法获取personal,尝试从完整字符串中提取
|
||||
String fromStr = fromAddress.toString();
|
||||
return extractNameFromString(fromStr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract from name: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中提取邮箱地址
|
||||
*/
|
||||
public static String extractEmailFromString(String addressStr) {
|
||||
if (addressStr == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 匹配 <email@domain.com> 格式
|
||||
Pattern pattern = Pattern.compile("<([^>]+)>");
|
||||
Matcher matcher = pattern.matcher(addressStr);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1).trim();
|
||||
}
|
||||
|
||||
// 如果没有尖括号,直接返回原字符串
|
||||
return addressStr.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中提取姓名
|
||||
*/
|
||||
public static String extractNameFromString(String addressStr) {
|
||||
if (addressStr == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 匹配 "Name <email@domain.com>" 格式
|
||||
Pattern pattern = Pattern.compile("^(.+?)\\s*<[^>]+>$");
|
||||
Matcher matcher = pattern.matcher(addressStr);
|
||||
if (matcher.find()) {
|
||||
String name = matcher.group(1).trim();
|
||||
return decodeMimeString(name);
|
||||
}
|
||||
|
||||
// 如果没有尖括号,返回原字符串
|
||||
return decodeMimeString(addressStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取邮件文本内容
|
||||
*/
|
||||
public static String extractTextContent(Message message) {
|
||||
try {
|
||||
if (message.isMimeType("text/plain")) {
|
||||
Object content = message.getContent();
|
||||
if (content instanceof String) {
|
||||
return (String) content;
|
||||
} else if (content instanceof InputStream) {
|
||||
return readInputStream((InputStream) content);
|
||||
}
|
||||
} else if (message.isMimeType("multipart/*")) {
|
||||
return extractTextFromMultipart(message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract text content: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取邮件HTML内容
|
||||
*/
|
||||
public static String extractHtmlContent(Message message) {
|
||||
try {
|
||||
if (message.isMimeType("text/html")) {
|
||||
Object content = message.getContent();
|
||||
if (content instanceof String) {
|
||||
return (String) content;
|
||||
} else if (content instanceof InputStream) {
|
||||
return readInputStream((InputStream) content);
|
||||
}
|
||||
} else if (message.isMimeType("multipart/*")) {
|
||||
return extractHtmlFromMultipart(message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract HTML content: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从多部分邮件中提取文本内容
|
||||
*/
|
||||
private static String extractTextFromMultipart(Message message) {
|
||||
try {
|
||||
Multipart multipart = (Multipart) message.getContent();
|
||||
return extractTextFromMultipart(multipart);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract text from multipart: {}", e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从多部分邮件中提取HTML内容
|
||||
*/
|
||||
private static String extractHtmlFromMultipart(Message message) {
|
||||
try {
|
||||
Multipart multipart = (Multipart) message.getContent();
|
||||
return extractHtmlFromMultipart(multipart);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract HTML from multipart: {}", e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归处理多部分邮件,提取文本内容
|
||||
*/
|
||||
private static String extractTextFromMultipart(Multipart multipart) throws MessagingException, IOException {
|
||||
StringBuilder textContent = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
|
||||
if (part.isMimeType("text/plain")) {
|
||||
Object content = part.getContent();
|
||||
if (content instanceof String) {
|
||||
textContent.append((String) content);
|
||||
} else if (content instanceof InputStream) {
|
||||
textContent.append(readInputStream((InputStream) content));
|
||||
}
|
||||
} else if (part.isMimeType("multipart/*")) {
|
||||
textContent.append(extractTextFromMultipart((Multipart) part.getContent()));
|
||||
}
|
||||
}
|
||||
|
||||
return textContent.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归处理多部分邮件,提取HTML内容
|
||||
*/
|
||||
private static String extractHtmlFromMultipart(Multipart multipart) throws MessagingException, IOException {
|
||||
StringBuilder htmlContent = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
|
||||
if (part.isMimeType("text/html")) {
|
||||
Object content = part.getContent();
|
||||
if (content instanceof String) {
|
||||
htmlContent.append((String) content);
|
||||
} else if (content instanceof InputStream) {
|
||||
htmlContent.append(readInputStream((InputStream) content));
|
||||
}
|
||||
} else if (part.isMimeType("multipart/*")) {
|
||||
htmlContent.append(extractHtmlFromMultipart((Multipart) part.getContent()));
|
||||
}
|
||||
}
|
||||
|
||||
return htmlContent.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从InputStream读取内容
|
||||
*/
|
||||
private static String readInputStream(InputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
result.write(buffer, 0, length);
|
||||
}
|
||||
return result.toString(StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取并解码收件人地址
|
||||
*/
|
||||
public static String extractToAddresses(Message message) {
|
||||
try {
|
||||
if (message.getAllRecipients() != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < message.getAllRecipients().length; i++) {
|
||||
if (i > 0) sb.append(",");
|
||||
Address recipient = message.getAllRecipients()[i];
|
||||
if (recipient instanceof InternetAddress) {
|
||||
InternetAddress internetAddress = (InternetAddress) recipient;
|
||||
String address = internetAddress.getAddress();
|
||||
String personal = internetAddress.getPersonal();
|
||||
if (personal != null) {
|
||||
// 如果有姓名,格式化为 "姓名 <邮箱>" 或直接返回邮箱
|
||||
String decodedPersonal = decodeMimeString(personal);
|
||||
sb.append(decodedPersonal).append(" <").append(address).append(">");
|
||||
} else {
|
||||
sb.append(address);
|
||||
}
|
||||
} else {
|
||||
// 对于非InternetAddress类型,尝试解码整个字符串
|
||||
String addressStr = recipient.toString();
|
||||
sb.append(decodeMimeString(addressStr));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract to addresses: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取并解码抄送地址
|
||||
*/
|
||||
public static String extractCcAddresses(Message message) {
|
||||
try {
|
||||
if (message.getRecipients(Message.RecipientType.CC) != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Address[] ccAddresses = message.getRecipients(Message.RecipientType.CC);
|
||||
for (int i = 0; i < ccAddresses.length; i++) {
|
||||
if (i > 0) sb.append(",");
|
||||
Address ccAddress = ccAddresses[i];
|
||||
if (ccAddress instanceof InternetAddress) {
|
||||
InternetAddress internetAddress = (InternetAddress) ccAddress;
|
||||
String address = internetAddress.getAddress();
|
||||
String personal = internetAddress.getPersonal();
|
||||
if (personal != null) {
|
||||
String decodedPersonal = decodeMimeString(personal);
|
||||
sb.append(decodedPersonal).append(" <").append(address).append(">");
|
||||
} else {
|
||||
sb.append(address);
|
||||
}
|
||||
} else {
|
||||
String addressStr = ccAddress.toString();
|
||||
sb.append(decodeMimeString(addressStr));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract CC addresses: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取并解码密送地址
|
||||
*/
|
||||
public static String extractBccAddresses(Message message) {
|
||||
try {
|
||||
if (message.getRecipients(Message.RecipientType.BCC) != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Address[] bccAddresses = message.getRecipients(Message.RecipientType.BCC);
|
||||
for (int i = 0; i < bccAddresses.length; i++) {
|
||||
if (i > 0) sb.append(",");
|
||||
Address bccAddress = bccAddresses[i];
|
||||
if (bccAddress instanceof InternetAddress) {
|
||||
InternetAddress internetAddress = (InternetAddress) bccAddress;
|
||||
String address = internetAddress.getAddress();
|
||||
String personal = internetAddress.getPersonal();
|
||||
if (personal != null) {
|
||||
String decodedPersonal = decodeMimeString(personal);
|
||||
sb.append(decodedPersonal).append(" <").append(address).append(">");
|
||||
} else {
|
||||
sb.append(address);
|
||||
}
|
||||
} else {
|
||||
String addressStr = bccAddress.toString();
|
||||
sb.append(decodeMimeString(addressStr));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract BCC addresses: {}", e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查邮件是否有附件
|
||||
*/
|
||||
public static boolean hasAttachments(Message message) {
|
||||
try {
|
||||
if (message.isMimeType("multipart/*")) {
|
||||
Multipart multipart = (Multipart) message.getContent();
|
||||
return hasAttachmentsInMultipart(multipart);
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to check attachments: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归检查多部分邮件中是否有附件
|
||||
*/
|
||||
private static boolean hasAttachmentsInMultipart(Multipart multipart) throws MessagingException {
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
|
||||
// 检查是否为附件
|
||||
String disposition = part.getDisposition();
|
||||
if (Part.ATTACHMENT.equalsIgnoreCase(disposition) ||
|
||||
Part.INLINE.equalsIgnoreCase(disposition)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查文件名
|
||||
String fileName = part.getFileName();
|
||||
if (fileName != null && !fileName.trim().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 递归检查子部分
|
||||
if (part.isMimeType("multipart/*")) {
|
||||
try {
|
||||
if (hasAttachmentsInMultipart((Multipart) part.getContent())) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed to check attachments in multipart: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-09-30 11:41:56
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2024-09-30 14:18:35
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
public class EmailPushService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:15:22
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2024-10-28 10:44:26
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import com.bytedesk.core.base.BaseEntity;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 邮件模板
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Table(name = "bytedesk_core_email_template")
|
||||
public class EmailTemplateEntity extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
|
||||
// private String subject;
|
||||
|
||||
private String content;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:40:38
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2024-10-28 10:40:41
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface EmailTemplateRepository extends JpaRepository<EmailTemplateEntity, Long>, JpaSpecificationExecutor<EmailTemplateEntity> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:40:51
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2024-10-28 10:40:54
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import com.bytedesk.core.base.BaseRequest;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EmailTemplateRequest extends BaseRequest {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
// private String subject;
|
||||
|
||||
// private String content;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:41:00
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-07-01 10:42:52
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import com.bytedesk.core.base.BaseResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EmailTemplateResponse extends BaseResponse {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
// private String subject;
|
||||
|
||||
private String content;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:40:27
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-08-20 17:41:39
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.bytedesk.core.base.BaseRestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/email-template")
|
||||
public class EmailTemplateRestController extends BaseRestController<EmailTemplateRequest, EmailTemplateService> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:40:14
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2025-04-11 11:17:29
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.bytedesk.core.base.BaseRestService;
|
||||
|
||||
@Service
|
||||
public class EmailTemplateService extends BaseRestService<EmailTemplateEntity, EmailTemplateRequest, EmailTemplateResponse> {
|
||||
|
||||
@Override
|
||||
public Page<EmailTemplateResponse> queryByOrg(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'queryByOrg'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<EmailTemplateResponse> queryByUser(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'queryByUser'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EmailTemplateEntity> findByUid(String uid) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'findByUid'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailTemplateResponse create(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'create'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailTemplateResponse update(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'update'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailTemplateEntity save(EmailTemplateEntity entity) {
|
||||
try {
|
||||
return doSave(entity);
|
||||
} catch (ObjectOptimisticLockingFailureException e) {
|
||||
return handleOptimisticLockingFailureException(e, entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EmailTemplateEntity doSave(EmailTemplateEntity entity) {
|
||||
throw new UnsupportedOperationException("实现保存逻辑");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByUid(String uid) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'deleteByUid'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'delete'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailTemplateEntity handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e,
|
||||
EmailTemplateEntity entity) {
|
||||
throw new UnsupportedOperationException("实现乐观锁处理逻辑");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailTemplateResponse convertToResponse(EmailTemplateEntity entity) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'convertToResponse'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailTemplateResponse queryByUid(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'queryByUid'");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<EmailTemplateEntity> createSpecification(EmailTemplateRequest request) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'createSpecification'");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Page<EmailTemplateEntity> executePageQuery(Specification<EmailTemplateEntity> specification, Pageable pageable) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'executePageQuery'");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* @Author: jackning 270580156@qq.com
|
||||
* @Date: 2024-10-28 10:41:23
|
||||
* @LastEditors: jackning 270580156@qq.com
|
||||
* @LastEditTime: 2024-10-28 10:41:26
|
||||
* @Description: bytedesk.com https://github.com/Bytedesk/bytedesk
|
||||
* Please be aware of the BSL license restrictions before installing Bytedesk IM –
|
||||
* selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license.
|
||||
* Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE
|
||||
* contact: 270580156@qq.com
|
||||
* 联系:270580156@qq.com
|
||||
* Copyright (c) 2024 by bytedesk.com, All Rights Reserved.
|
||||
*/
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import com.bytedesk.core.base.BaseSpecification;
|
||||
|
||||
public class EmailTemplateSpecification extends BaseSpecification<EmailTemplateEntity, EmailTemplateRequest> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NonNullApi
|
||||
package com.bytedesk.core.email_template;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
Reference in New Issue
Block a user