Skip to content

Commit

Permalink
feat: mail notify and app link to (#706)
Browse files Browse the repository at this point in the history
* feat: 添加剧集更新邮件通知

* feat: 添加app跳转页面

* docs: update CHANGELOG.MD
  • Loading branch information
chivehao authored Oct 18, 2024
1 parent bea9102 commit b10fdaa
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 13 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

更新日志文档,版本顺序从新到旧,最新版本在最前(上)面。

# 0.17.3

## 新特性

- 添加剧集更新邮件通知,配合蜜柑计划插件进行番剧更新通知
- 添加app条目跳转页面

# 0.17.2

## 问题修复
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.17.2
version=0.17.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package run.ikaros.server.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import reactor.core.publisher.Mono;
import run.ikaros.api.infra.properties.IkarosProperties;
import run.ikaros.api.infra.utils.StringUtils;
import run.ikaros.server.core.subject.service.SubjectService;

@Controller
@RequestMapping("/app/link")
public class AppLinkController {
private final SubjectService subjectService;
private final IkarosProperties ikarosProperties;

public AppLinkController(SubjectService subjectService, IkarosProperties ikarosProperties) {
this.subjectService = subjectService;
this.ikarosProperties = ikarosProperties;
}

/**
* app 链接跳转页面.
*/
@GetMapping("/subject/{id}")
public Mono<String> index(Model model, @PathVariable("id") Long subjectId) {
return subjectService.findById(subjectId)
.flatMap(subject -> {
String name = StringUtils.isBlank(subject.getNameCn())
? subject.getName() : subject.getNameCn();
model.addAttribute("subject", subject);
model.addAttribute("subjectName", name);
String cover = subject.getCover();
if (StringUtils.isNotBlank(cover) && !cover.startsWith("http")) {
cover = ikarosProperties.getExternalUrl() + cover;
}
subject.setCover(cover);
model.addAttribute("subjectCover", cover);
String subjectUrl = "ikaros://app/subject/" + subject.getId();
model.addAttribute("subjectUrl", subjectUrl);
return Mono.just("temp/app-link-to");
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package run.ikaros.server.core.episode;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.Context;
import reactor.core.publisher.Mono;
import run.ikaros.api.infra.properties.IkarosProperties;
import run.ikaros.api.infra.utils.StringUtils;
import run.ikaros.server.core.attachment.event.EpisodeAttachmentUpdateEvent;
import run.ikaros.server.core.notify.NotifyService;
import run.ikaros.server.store.entity.AttachmentEntity;
import run.ikaros.server.store.entity.EpisodeEntity;
import run.ikaros.server.store.entity.SubjectEntity;
import run.ikaros.server.store.repository.AttachmentRepository;
import run.ikaros.server.store.repository.EpisodeRepository;
import run.ikaros.server.store.repository.SubjectRepository;

@Slf4j
@Component
public class EpisodeUpdateListener {
private final AttachmentRepository attachmentRepository;
private final EpisodeRepository episodeRepository;
private final SubjectRepository subjectRepository;

private final NotifyService notifyService;

private final IkarosProperties ikarosProperties;

/**
* Construct.
*/
public EpisodeUpdateListener(AttachmentRepository attachmentRepository,
EpisodeRepository episodeRepository,
SubjectRepository subjectRepository,
NotifyService notifyService, IkarosProperties ikarosProperties) {
this.attachmentRepository = attachmentRepository;
this.episodeRepository = episodeRepository;
this.subjectRepository = subjectRepository;
this.notifyService = notifyService;
this.ikarosProperties = ikarosProperties;
}

/**
* 如果需要发送通知则进行发送.
*/
@EventListener(EpisodeAttachmentUpdateEvent.class)
public Mono<Void> onEpisodeAttachmentUpdateEvent(EpisodeAttachmentUpdateEvent event) {
if (!event.getNotify()) {
return Mono.empty();
}
final Long attachmentId = event.getAttachmentId();
final Long episodeId = event.getEpisodeId();
return Mono.just(new Context())
.flatMap(context -> attachmentRepository.findById(attachmentId)
.map(entity -> {
context.setVariable("attachment", entity);
return context;
}))
.flatMap(context -> episodeRepository.findById(episodeId)
.map(entity -> {
String name = StringUtils.isEmpty(entity.getNameCn())
? entity.getName() : entity.getNameCn();
context.setVariable("episodeName", name);
context.setVariable("episode", entity);
return context;
}))
.flatMap(context -> {
Object episode = context.getVariable("episode");

if (!(episode instanceof EpisodeEntity episodeEntity)) {
return Mono.empty();
}
Long subjectId = episodeEntity.getSubjectId();
return subjectRepository.findById(subjectId)
.map(entity -> {
String cover = entity.getCover();
if (StringUtils.isNotBlank(cover) && !cover.startsWith("http")) {
cover = ikarosProperties.getExternalUrl() + cover;
}
entity.setCover(cover);
String name = StringUtils.isBlank(entity.getNameCn())
? entity.getName() : entity.getNameCn();
String subjectUrl = ikarosProperties.getExternalUrl()
+ "/app/link/subject/" + subjectId;

context.setVariable("subjectName", name);
context.setVariable("subjectCover", cover);
context.setVariable("subject", entity);
context.setVariable("subjectUrl", subjectUrl);
return context;
});
})
.flatMap(context -> {
Object episode = context.getVariable("episode");

if (!(episode instanceof EpisodeEntity episodeEntity)) {
return Mono.empty();
}
Object attachment = context.getVariable("attachment");

if (!(attachment instanceof AttachmentEntity attachmentEntity)) {
return Mono.empty();
}

Object subject = context.getVariable("subject");

if (!(subject instanceof SubjectEntity subjectEntity)) {
return Mono.empty();
}

StringBuilder sb = new StringBuilder("番剧《");
sb.append(StringUtils.isBlank(subjectEntity.getNameCn())
? subjectEntity.getName() : subjectEntity.getNameCn())
.append("》第")
.append(episodeEntity.getSequence())
.append("集 [")
.append(StringUtils.isBlank(episodeEntity.getNameCn())
? episodeEntity.getName() : episodeEntity.getNameCn())
.append("] 更新了");

return notifyService.send(sb.toString(), "mail/anime_update", context);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package run.ikaros.server.core.notify;

import org.thymeleaf.context.Context;
import reactor.core.publisher.Mono;

public interface NotifyService {
Mono<Void> sendMail(String title, String context);

Mono<Void> send(String title, String template, Context context);

Mono<Void> testMail();
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ public Mono<Void> sendMail(String title, String context) {
}
}

@Override
public Mono<Void> send(String title, String template, Context context) {
Assert.hasText(title, "title is empty");
Assert.notNull(context, "context must not null");
String receiveAddress = mailService.getMailConfig().getReceiveAddress();
Assert.hasText(receiveAddress, "receive address is empty");
MailRequest mailRequest = new MailRequest();
mailRequest.setTitle(title);
mailRequest.setAddress(receiveAddress);
return mailService.send(mailRequest, template, context);
}

@Override
public Mono<Void> testMail() {
String receiveAddress = mailService.getMailConfig().getReceiveAddress();
Expand Down
24 changes: 12 additions & 12 deletions server/src/main/resources/templates/mail/anime_update.html
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">

<body>
<div style="background: #fff;">
<div style="width:100%;max-width:720px;text-align: left;margin: 0 auto;padding-top: 20px;padding-bottom: 80px">
<div style="border-radius: 5px;border:1px solid #eee;overflow: hidden;">
<h1 style="color:#fff;background: #3798e8;font-size:24px;font-weight:normal;padding-left:40px;margin:0">
番剧更新: 《<strong th:text="${title}"></strong>
番剧更新: 《<strong th:text="${subjectName}"></strong>
</h1>
<div style="background:#fff;padding:20px 32px 40px;">
<h2>封面:</h2>
<div align="center">
<img style="border-radius:5px;" th:src="${coverImgUrl}" alt="URL无法访问"></img>
<img style="border-radius:5px;" th:src="${subjectCover}" alt="URL无法访问"></img>
</div>
<h2>剧集更新详情:</h2>
<span th:if="${not #strings.isEmpty(epTitle)}">
<span style="color: #6e6e6e;font-size:13px;line-height:24px;">剧集标题:<span th:text="${epTitle}" /></span>
<span th:if="${not #strings.isEmpty(episodeName)}">
<span style="color: #6e6e6e;font-size:13px;line-height:24px;">剧集标题:<span th:text="${episodeName}" /></span>
</span>
<br />
<span style="color: #6e6e6e;font-size:13px;line-height:24px;">剧集序号:<span th:text="${epSeq}" /></span>
<span style="color: #6e6e6e;font-size:13px;line-height:24px;">剧集序号:<span th:text="${episode.sequence}" /></span>
<br />
<span style="color: #6e6e6e;font-size:13px;line-height:24px;">剧集文件名:<span th:text="${epUrlFileName}" /></span>
<span style="color: #6e6e6e;font-size:13px;line-height:24px;">剧集文件名:<span th:text="${attachment.name}" /></span>


<span th:if="${not #strings.isEmpty(epIntroduction)}">
<span th:if="${not #strings.isEmpty(episode.description)}">
<h2>剧集描述:</h2>
<p th:text="${epIntroduction}" style="color: #6e6e6e;font-size:13px;line-height:24px;padding:10px 20px;background:#f8f8f8;margin:0"></p>
<p th:text="${episode.description}" style="color: #6e6e6e;font-size:13px;line-height:24px;padding:10px 20px;background:#f8f8f8;margin:0"></p>
</span>

<!-- <p style="color: #6e6e6e;font-size:13px;line-height:24px;">放送进度:<span th:text="${updateEpsCount}"></span>/<span th:text="${allEpsCount}"></span></p>-->
<h2>番剧简介:</h2>
<p th:text="${introduction}" style="color: #6e6e6e;font-size:13px;line-height:24px;padding:10px 20px;background:#f8f8f8;margin:0">
<p th:text="${subject.summary}" style="color: #6e6e6e;font-size:13px;line-height:24px;padding:10px 20px;background:#f8f8f8;margin:0">
</p>
<!-- <strong>详细:</strong>-->
<!-- <p style="color: #6e6e6e;font-size:13px;line-height:24px;">您可以点击&nbsp;<a th:href="${infoUrl}">跳转到动漫页面</a></p>-->
<strong>前往app观看</strong>
<p style="color: #6e6e6e;font-size:13px;line-height:24px;">您可以点击&nbsp;<a th:href="${subjectUrl}">跳转到app条目页面</a></p>
</div>
</div>
</div>
Expand Down
33 changes: 33 additions & 0 deletions server/src/main/resources/templates/temp/app-link-to.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Ikaros app link temp page</title>
</head>
<body>
<div style="background: #fff;">
<div style="width:100%;max-width:720px;text-align: left;margin: 0 auto;padding-top: 20px;padding-bottom: 80px">
<div style="border-radius: 5px;border:1px solid #eee;overflow: hidden;">
<h1 style="color:#fff;background: #3798e8;font-size:24px;font-weight:normal;padding-left:40px;margin:0">
番剧: 《<strong th:text="${subjectName}"></strong>
</h1>
<div style="background:#fff;padding:20px 32px 40px;">
<strong>前往app观看:</strong>
<p style="color: #6e6e6e;font-size:13px;line-height:24px;">点击&nbsp;<a th:href="${subjectUrl}">跳转到app条目页面</a></p>

<!-- <p style="color: #6e6e6e;font-size:13px;line-height:24px;">放送进度:<span th:text="${updateEpsCount}"></span>/<span th:text="${allEpsCount}"></span></p>-->
<h2>番剧简介:</h2>
<p th:text="${subject.summary}" style="color: #6e6e6e;font-size:13px;line-height:24px;padding:10px 20px;background:#f8f8f8;margin:0">
</p>

<h2>封面:</h2>
<div align="center">
<img style="border-radius:5px;width: 100%;" th:src="${subjectCover}" alt="URL无法访问"></img>
</div>
</div>
</div>
</div>
</div>

</body>
</html>

0 comments on commit b10fdaa

Please sign in to comment.