From 03138c8a053d81403bea302c3d5b3515b3fe4d81 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Thu, 12 Jun 2025 12:59:12 +0800 Subject: [PATCH] Add Mail function --- .../modules/common/mail/entity/MailSendLog.kt | 40 ++++++++++++ .../common/mail/entity/MailSendLogLine.kt | 24 +++++++ .../mail/entity/MailSendLogLineRepository.kt | 8 +++ .../mail/entity/MailSendLogRepository.kt | 8 +++ .../common/mail/entity/MailTemplate.kt | 48 ++++++++++++++ .../mail/entity/MailTemplateRepository.kt | 12 ++++ .../common/mail/enums/MailTemplateType.kt | 5 ++ .../mail/service/MailReminderService.kt | 27 +------- .../common/mail/service/MailService.kt | 36 +++-------- .../mail/service/MailTemplateService.kt | 62 +++++++++++++++++++ .../modules/common/mail/web/MailController.kt | 8 ++- .../common/mail/web/MailTemplateController.kt | 25 ++++++++ .../common/mail/web/models/MailSaveRequest.kt | 3 +- .../mail/web/models/MailTemplateRequest.kt | 19 ++++++ .../01_create_mail_template.sql | 24 +++++++ .../02_create_mail_send_log.sql | 38 ++++++++++++ 16 files changed, 329 insertions(+), 58 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLog.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLine.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLineRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplate.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/enums/MailTemplateType.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailTemplateRequest.kt create mode 100644 src/main/resources/db/changelog/changes/20250609_01_cyril/01_create_mail_template.sql create mode 100644 src/main/resources/db/changelog/changes/20250609_01_cyril/02_create_mail_send_log.sql diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLog.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLog.kt new file mode 100644 index 0000000..6691b89 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLog.kt @@ -0,0 +1,40 @@ +package com.ffii.fpsms.modules.common.mail.entity + +import com.ffii.core.entity.BaseEntity +import jakarta.persistence.* +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size +import java.time.LocalDateTime + +@Entity +@Table(name = "mail_send_log") +open class MailSendLog : BaseEntity() { + + @NotNull + @ManyToOne + @JoinColumn(name = "tempId", nullable = false) + open var temp: MailSendLog? = null + + @Size(max = 255) + @NotNull + @Column(name = "tempDesc", nullable = false) + open var tempDesc: String? = null + + @Size(max = 255) + @Column(name = "senderMail") + open var senderMail: String? = null + + @Column(name = "subject") + open var subject: String? = null + + @Lob + @Column(name = "content") + open var content: String? = null + + @Column(name = "sendDate") + open var sendDate: LocalDateTime? = null + + @Lob + @Column(name = "errorMessage") + open var errorMessage: String? = null +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLine.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLine.kt new file mode 100644 index 0000000..c7974a7 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLine.kt @@ -0,0 +1,24 @@ +package com.ffii.fpsms.modules.common.mail.entity + +import com.ffii.core.entity.BaseEntity +import jakarta.persistence.* +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size + +@Entity +@Table(name = "mail_send_log_line") +open class MailSendLogLine : BaseEntity() { + + @NotNull + @ManyToOne + @JoinColumn(name = "sendMailLogId", nullable = false) + open var sendMailLog: MailSendLog? = null + + @Size(max = 255) + @Column(name = "toMail") + open var toMail: String? = null + + @Lob + @Column(name = "errorMessage") + open var errorMessage: String? = null +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLineRepository.kt new file mode 100644 index 0000000..98c2675 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogLineRepository.kt @@ -0,0 +1,8 @@ +package com.ffii.fpsms.modules.common.mail.entity + +import com.ffii.core.support.AbstractRepository +import org.springframework.stereotype.Repository + +@Repository +interface MailSendLogLineRepository : AbstractRepository { +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogRepository.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogRepository.kt new file mode 100644 index 0000000..6025e55 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailSendLogRepository.kt @@ -0,0 +1,8 @@ +package com.ffii.fpsms.modules.common.mail.entity + +import com.ffii.core.support.AbstractRepository +import org.springframework.stereotype.Repository + +@Repository +interface MailSendLogRepository : AbstractRepository { +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplate.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplate.kt new file mode 100644 index 0000000..853caf1 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplate.kt @@ -0,0 +1,48 @@ +package com.ffii.fpsms.modules.common.mail.entity + +import com.ffii.core.entity.BaseEntity +import jakarta.persistence.* +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size + +@Entity +@Table(name = "mail_template") +open class MailTemplate : BaseEntity() { + @Column(name = "`to`") + open var to: String? = null + + @Column(name = "cc") + open var cc: String? = null + + @Size(max = 255) + @NotNull + @Column(name = "code", nullable = false) + open var code: String? = null + + @Size(max = 255) + @NotNull + @Column(name = "description", nullable = false) + open var description: String? = null + + @Size(max = 30) + @Column(name = "type", length = 30) + open var type: String? = null + + @Size(max = 255) + @Column(name = "params") + open var params: String? = null + + @Column(name = "subjectCht") + open var subjectCht: String? = null + + @Column(name = "subjectEng") + open var subjectEng: String? = null + + @Lob + @Column(name = "contentCht") + open var contentCht: String? = null + + @Lob + @Column(name = "contentEng") + open var contentEng: String? = null +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt new file mode 100644 index 0000000..8287e36 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt @@ -0,0 +1,12 @@ +package com.ffii.fpsms.modules.common.mail.entity + +import com.ffii.core.support.AbstractRepository +import org.springframework.stereotype.Repository +import java.io.Serializable + +@Repository +interface MailTemplateRepository : AbstractRepository { + fun findAllByDeletedIsFalse(): List + + fun findByIdAndDeletedIsFalse(id: Serializable): MailTemplate? +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/enums/MailTemplateType.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/enums/MailTemplateType.kt new file mode 100644 index 0000000..cd10613 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/enums/MailTemplateType.kt @@ -0,0 +1,5 @@ +package com.ffii.fpsms.modules.common.mail.enums + +enum class MailTemplateType(val value: String) { + PURCHASE_ORDER("po"), +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailReminderService.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailReminderService.kt index 2ced6f8..286bf2e 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailReminderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailReminderService.kt @@ -23,43 +23,20 @@ open class MailReminderService( val settingsService: SettingsService, ) { protected val logger: Log = LogFactory.getLog(javaClass) - private val FULLTIME = "FT" - private val PARTTIME = "PT" private val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") private fun isSettingsConfigValid(): Boolean { try { val username = settingsService.findByName(SettingNames.MAIL_SMTP_USERNAME).orElseThrow().value - val pw = settingsService.findByName(SettingNames.MAIL_SMTP_USERNAME).orElseThrow().value + val pw = settingsService.findByName(SettingNames.MAIL_SMTP_PASSWORD).orElseThrow().value return !username.isNullOrEmpty() && !pw.isNullOrEmpty() } catch (e: Error) { return false } } - private fun createHTMLTable(naughtyList: MutableList): String { - val tableStarter = StringBuilder(" ") - val header = StringBuilder( - " " - + " " - + " " - + " " - + " " - ) - tableStarter.append(header) - for (per in naughtyList) { - tableStarter.append( - " " - + " " - + " " - + " " - + " " - ) - } - val footer = StringBuilder("
StaffIdNameMissing Dates
${per.staffId}${per.name}${per.missingDates.joinToString(", ")}
") - return tableStarter.toString() - } open fun createEmailRequest(content: String, emailTo: List) { + if (!isSettingsConfigValid()) return; // val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value // val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value // val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailService.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailService.kt index aec186d..9c20522 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailService.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailService.kt @@ -5,7 +5,7 @@ import com.ffii.core.utils.LocaleUtils import com.ffii.fpsms.modules.common.ErrorCodes import com.ffii.fpsms.modules.common.SettingNames import com.ffii.fpsms.modules.common.mail.pojo.MailRequest -import com.ffii.fpsms.modules.common.mail.web.models.MailSave +import com.ffii.fpsms.modules.common.mail.web.models.MailSaveRequest import com.ffii.fpsms.modules.settings.entity.Settings import com.ffii.fpsms.modules.settings.entity.SettingsRepository import com.ffii.fpsms.modules.settings.service.SettingsService @@ -32,6 +32,7 @@ open class MailService( private val settingsService: SettingsService, private val passwordEncoder: PasswordEncoder, private val settingsRepository: SettingsRepository, + private val mailTemplateService: MailTemplateService, ) { protected val logger: Log = LogFactory.getLog(javaClass) @@ -171,45 +172,22 @@ open class MailService( asyncSend(mutableListOf(mailRequest)) } - open fun saveMail(mailSave: MailSave): List { + open fun saveMail(request: MailSaveRequest): List { // ------------------ save mail settings ------------------ // val settings = mutableListOf() - mailSave.settings.forEach { setting -> + request.settings.forEach { setting -> val tempSetting = settingsRepository.findById(setting.id).orElseThrow() settings += tempSetting.apply { value = setting.value } } - // ------------------ save timesheet mail details ------------------ // -// val mailCc = settingsRepository.findByName("TIMESHEET.mail.cc").orElseThrow() -// -// settings += mailCc.apply { -// value = mailSave.template.cc -// } -// -// val mailBcc = settingsRepository.findByName("TIMESHEET.mail.bcc").orElseThrow() -// -// settings += mailBcc.apply { -// value = mailSave.template.bcc -// } -// -// val mailSubject = settingsRepository.findByName("TIMESHEET.mail.subject").orElseThrow() -// -// settings += mailSubject.apply { -// value = mailSave.template.subject -// } -// -// val mailTemplate = settingsRepository.findByName("TIMESHEET.mail.template").orElseThrow() -// -// settings += mailTemplate.apply { -// value = mailSave.template.template -// } - - // ------------------ save all ------------------ // settingsRepository.saveAll(settings) + // ------------------ save mail templates' details ------------------ // + mailTemplateService.saveMailTemplates(request.templates) + return settings } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt new file mode 100644 index 0000000..ffb1ea0 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt @@ -0,0 +1,62 @@ +package com.ffii.fpsms.modules.common.mail.service + +import com.ffii.fpsms.modules.common.mail.entity.MailTemplate +import com.ffii.fpsms.modules.common.mail.entity.MailTemplateRepository +import com.ffii.fpsms.modules.common.mail.web.models.MailTemplateRequest +import org.springframework.stereotype.Service + +@Service +open class MailTemplateService( + private val mailTemplateRepository: MailTemplateRepository, +) { + fun allMailTemplates(): List { + return mailTemplateRepository.findAllByDeletedIsFalse(); + } + + fun findById(id: Long): MailTemplate? { + return mailTemplateRepository.findByIdAndDeletedIsFalse(id); + } + + fun saveMailTemplate(request: MailTemplateRequest): MailTemplate { + val mailTemplate = request.id?.let { findById(it) } ?: MailTemplate() + + mailTemplate.apply { + to = request.to + cc = request.cc + code = request.code + description = request.description + type = request.type + params = request.params + subjectCht = request.subjectCht + subjectEng = request.subjectEng + contentCht = request.contentCht + contentEng = request.contentEng + } + + val response = mailTemplateRepository.saveAndFlush(mailTemplate) + + return response + } + + fun saveMailTemplates(requests: List) { + val mailTemplates: List = mutableListOf() + requests.forEach { request -> + val mailTemplate = request.id?.let { findById(it) } ?: MailTemplate() + + mailTemplate.apply { + to = request.to + cc = request.cc + code = request.code + description = request.description + type = request.type + params = request.params + subjectCht = request.subjectCht + subjectEng = request.subjectEng + contentCht = request.contentCht + contentEng = request.contentEng + }.let { mailTemplates.plus(it) } + } + + mailTemplateRepository.saveAll(mailTemplates) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailController.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailController.kt index cd34758..ee26477 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailController.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailController.kt @@ -1,8 +1,10 @@ package com.ffii.fpsms.modules.common.mail.web +import com.ffii.fpsms.modules.common.mail.entity.MailTemplate import com.ffii.fpsms.modules.common.mail.service.MailReminderService import com.ffii.fpsms.modules.common.mail.service.MailService -import com.ffii.fpsms.modules.common.mail.web.models.MailSave +import com.ffii.fpsms.modules.common.mail.service.MailTemplateService +import com.ffii.fpsms.modules.common.mail.web.models.MailSaveRequest import com.ffii.fpsms.modules.common.mail.web.models.Setting import com.ffii.fpsms.modules.settings.entity.Settings import com.ffii.fpsms.modules.settings.service.SettingsService @@ -16,7 +18,7 @@ import java.util.Date class MailController( private val settingsService: SettingsService, private val mailService: MailService, - private val mailReminderService: MailReminderService, + private val mailReminderService: MailReminderService ) { @GetMapping("/setting") fun mailSetting(): List { @@ -29,7 +31,7 @@ class MailController( } @PostMapping("/save") - fun saveMail(@Valid @RequestBody mailSave: MailSave): List { + fun saveMail(@Valid @RequestBody mailSave: MailSaveRequest): List { return mailService.saveMail(mailSave) } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt new file mode 100644 index 0000000..f37cb33 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt @@ -0,0 +1,25 @@ +package com.ffii.fpsms.modules.common.mail.web + +import com.ffii.fpsms.modules.common.mail.entity.MailTemplate +import com.ffii.fpsms.modules.common.mail.web.models.MailTemplateRequest +import com.ffii.fpsms.modules.common.mail.service.MailTemplateService +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RequestMapping("/mailTemplates") +@RestController +class MailTemplateController( + private val mailTemplateService: MailTemplateService, +) { + @GetMapping + fun allMailTemplates(): List { + return mailTemplateService.allMailTemplates(); + } + + @PostMapping("/save") + fun saveMailTemplate(request: MailTemplateRequest): MailTemplate { + return mailTemplateService.saveMailTemplate(request); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailSaveRequest.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailSaveRequest.kt index 6331cb7..3ea2f8b 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailSaveRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailSaveRequest.kt @@ -15,7 +15,8 @@ data class Template ( val template: String?, ) -data class MailSave ( +data class MailSaveRequest ( val settings: List, + val templates: List, // val template: Template ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailTemplateRequest.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailTemplateRequest.kt new file mode 100644 index 0000000..e9288f1 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/MailTemplateRequest.kt @@ -0,0 +1,19 @@ +package com.ffii.fpsms.modules.common.mail.web.models + +import jakarta.validation.constraints.NotNull + +data class MailTemplateRequest( + val id: Long?, + val to: String?, + val cc: String?, + @field:NotNull(message = "code cannot be null") + val code: String, + @field:NotNull(message = "description cannot be null") + val description: String?, + val type: String?, + val params: String?, + val subjectCht: String?, + val subjectEng: String?, + val contentCht: String?, + val contentEng: String?, +) \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250609_01_cyril/01_create_mail_template.sql b/src/main/resources/db/changelog/changes/20250609_01_cyril/01_create_mail_template.sql new file mode 100644 index 0000000..e17d917 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250609_01_cyril/01_create_mail_template.sql @@ -0,0 +1,24 @@ +-- liquibase formatted sql +-- changeset cyril:create mail template + +CREATE TABLE `mail_template` +( + `id` INT NOT NULL AUTO_INCREMENT, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` VARCHAR(30) NULL DEFAULT NULL, + `version` INT NOT NULL DEFAULT '0', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` VARCHAR(30) NULL DEFAULT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT '0', + `to` VARCHAR(255) NULL, + `cc` VARCHAR(255) NULL, + `code` VARCHAR(255) NOT NULL, + `description` VARCHAR(255) NOT NULL, + `type` VARCHAR(30) NULL, + `params` VARCHAR(255) NULL, + `subjectCht` VARCHAR(255) NULL, + `subjectEng` VARCHAR(255) NULL, + `contentCht` TEXT NULL, + `contentEng` TEXT NULL, + CONSTRAINT pk_mail_template PRIMARY KEY (id) +); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250609_01_cyril/02_create_mail_send_log.sql b/src/main/resources/db/changelog/changes/20250609_01_cyril/02_create_mail_send_log.sql new file mode 100644 index 0000000..e21d931 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250609_01_cyril/02_create_mail_send_log.sql @@ -0,0 +1,38 @@ +-- liquibase formatted sql +-- changeset cyril:create send mail log + +CREATE TABLE `mail_send_log` +( + `id` INT NOT NULL AUTO_INCREMENT, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` VARCHAR(30) NULL DEFAULT NULL, + `version` INT NOT NULL DEFAULT '0', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` VARCHAR(30) NULL DEFAULT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT '0', + `tempId` INT NOT NULL, + `tempDesc` VARCHAR(255) NOT NULL, + `senderMail` VARCHAR(255) NULL, + `subject` VARCHAR(255) NULL, + `content` TEXT NULL, + `sendDate` DATETIME NULL, + `errorMessage` TEXT NULL, + CONSTRAINT pk_mail_send_log PRIMARY KEY (id), + CONSTRAINT `FK_MAIL_SEND_LOG_ON_TEMPID` FOREIGN KEY (`tempId`) REFERENCES `mail_send_log` (`id`) +); + +CREATE TABLE `mail_send_log_line` +( + `id` INT NOT NULL AUTO_INCREMENT, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` VARCHAR(30) NULL DEFAULT NULL, + `version` INT NOT NULL DEFAULT '0', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` VARCHAR(30) NULL DEFAULT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT '0', + `sendMailLogId` INT NOT NULL, + `toMail` VARCHAR(255) NULL, + `errorMessage` TEXT NULL, + CONSTRAINT pk_mail_send_log_line PRIMARY KEY (id), + CONSTRAINT `FK_MAIL_SEND_LOG_LINE_ON_SENDMAILLOGID` FOREIGN KEY (`sendMailLogId`) REFERENCES `mail_send_log` (`id`) +); \ No newline at end of file