早上在思考今天的進度時,發現昨天有個地方做錯了,MAIL 那張表原先有個 STATUS 欄位預計用來存已讀跟未讀的狀態,但是要有這個狀態應該也是每個用戶要有一筆資料,這跟當初設計衝突了,原本是想說只存一筆站內信內容,就可以根據用戶類型發送給全部有這個角色的用戶,這部分是正確的沒錯,但沒有地方存用戶已讀未讀這封信件的狀態,所以今天就要來實現這個擴展。
今日的實作目標,預計要完成的進度:
首先我們先快速把 MAIL 表中用不到的欄位移除,另外還有更新下欄位名稱、類型,這樣關聯到新表時可讀性比較好:
ALTER TABLE `MAIL` DROP COLUMN STATUS;
ALTER TABLE `MAIL` RENAME COLUMN ID TO MAIL_ID;
ALTER TABLE `MAIL` RENAME COLUMN RECEIVER_ROLE TO RECEIVER_ROLE_ID;
ALTER TABLE `MAIL` CHANGE `RECEIVER_ROLE_ID` `RECEIVER_ROLE_ID` INT NOT NULL ;
接著創建一個新表 MAIL_INBOX 存放用戶的信件:
CREATE CORE_BASE.MAIL_INBOX(
ID BIGINT PRIMARY KEY AUTO_INCREMENT,
MAIL_ID BIGINT NOT NULL,
USER_ID INT NOT NULL,
STATUS TINYINT DEFAULT 0,
VERSION INT DEFAULT 0,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
來改寫一下業務邏輯,一樣是同一個 API 但分為兩個部分,站內信的保存跟用戶收件匣的信件保存,用戶收件匣可能會有大量的資料筆數,我們用 Kafka 異步處理這個 Task,雖然用 @Async 也是可以,但之後推送的流程可能也差不多是這樣,所以先保持這個架構,之後再看看。
@Override
@Transactional
public void sendMail(MailSendTO mailSendTO) {
var mail = new Mail();
var role = roleRepo.findByRoleType(mailSendTO.getReceiverRole())
.orElseThrow(() -> new BaseException(StatusCode.REQ_PARAM_ERR, "Role type does not exist"));
mail.setContent(mailSendTO.getContent());
mail.setReceiverRoleId(role.getRoleId());
mailRepo.saveAndFlush(mail);
var mailInboxTO = MailInboxTO.builder()
.mailId(mail.getMailId())
.roleId(mail.getReceiverRoleId())
.build();
kafkaSender.send(Topic.MAIL_INBOX, mailInboxTO);
}
mailService.saveUserMailInbox(mail);
開始進行用戶收件匣的信件保存。@KafkaListener(topics = Topic.MAIL_INBOX)
public void processMailNotification(String content) {
log.info("Receive topic [{}] and message=[{}]", Topic.MAIL_INBOX, content);
try {
var om = new ObjectMapper();
var mail = om.readValue(content, MailInboxTO.class);
mailService.saveUserMailInbox(mail);
} catch (Exception e) {
log.warn("Failed to process mail task", e);
}
}
@Override
@Transactional
public void saveUserMailInbox(MailInboxTO to) {
if (to.getMailId() == null || to.getRoleId() == null) {
log.info("Mail id can not be null");
return;
}
var userIds = userRoleRepo.findUserIdByRoleId(to.getRoleId());
var mailInboxes = new ArrayList<MailInbox>();
userIds.forEach(userId -> {
var mailInbox = new MailInbox();
mailInbox.setMailId(to.getMailId());
mailInbox.setUserId(userId);
mailInboxes.add(mailInbox);
});
log.info("Total {} mails inbox", mailInboxes.size());
mailInboxRepo.saveAll(mailInboxes);
}
今天先收尾到這,完成了表結構的優化,簡易基礎的 CRUD 對於小系統來說算夠用,反正之後會慢慢優化,以及測試效能瓶頸等等。效能測試這部分說實在我還很不熟,平時都是主管會提出效能優化要求,但為了往 Senior 這條路邁進,這是需要主動提升的素養,在倒數幾天一定要有所成長。
另外,今天其實還有查詢用戶收件匣的功能,想說留到明天實作,因為今天還有一點時間我想思考如何規劃 WebSocket 的推送功能,這部分我不太熟悉,睡前再溫習一下這部分,然後明天還可以用查看收件匣作為一個緩衝區,週六週日就能有更多時間可以來實作 WebSocket 的功能。