mirror of https://github.com/halo-dev/halo
Add base post services
parent
afdf94b898
commit
bd2eebf1cd
|
@ -113,8 +113,10 @@ public class AdminController {
|
||||||
adminService.updateApplicationConfig(content);
|
adminService.updateApplicationConfig(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/spring/restart")
|
@PostMapping(value = {"halo/restart", "spring/restart"})
|
||||||
|
@ApiOperation("Restart halo server")
|
||||||
public void restartApplication() {
|
public void restartApplication() {
|
||||||
Application.restart();
|
Application.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,5 +88,4 @@ public class MainController {
|
||||||
response.sendRedirect(favicon);
|
response.sendRedirect(favicon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,6 @@ public class CommonController extends AbstractErrorController {
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public String handleError(HttpServletRequest request, HttpServletResponse response, Model model) {
|
public String handleError(HttpServletRequest request, HttpServletResponse response, Model model) {
|
||||||
HttpStatus status = getStatus(request);
|
|
||||||
|
|
||||||
log.error("Request URL: [{}], URI: [{}], Request Method: [{}], IP: [{}]",
|
log.error("Request URL: [{}], URI: [{}], Request Method: [{}], IP: [{}]",
|
||||||
request.getRequestURL(),
|
request.getRequestURL(),
|
||||||
request.getRequestURI(),
|
request.getRequestURI(),
|
||||||
|
@ -82,6 +80,8 @@ public class CommonController extends AbstractErrorController {
|
||||||
|
|
||||||
log.debug("Error detail: [{}]", errorDetail);
|
log.debug("Error detail: [{}]", errorDetail);
|
||||||
|
|
||||||
|
HttpStatus status = getStatus(request);
|
||||||
|
|
||||||
if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
|
if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
|
||||||
return contentInternalError();
|
return contentInternalError();
|
||||||
} else if (status.equals(HttpStatus.NOT_FOUND)) {
|
} else if (status.equals(HttpStatus.NOT_FOUND)) {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package run.halo.app.event.post;
|
package run.halo.app.event.post;
|
||||||
|
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import run.halo.app.utils.ServiceUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post visit event.
|
* Post visit event.
|
||||||
*
|
*
|
||||||
|
@ -14,7 +18,8 @@ public class PostVisitEvent extends AbstractVisitEvent {
|
||||||
* @param source the object on which the event initially occurred (never {@code null})
|
* @param source the object on which the event initially occurred (never {@code null})
|
||||||
* @param postId post id must not be null
|
* @param postId post id must not be null
|
||||||
*/
|
*/
|
||||||
public PostVisitEvent(Object source, Integer postId) {
|
public PostVisitEvent(Object source, @NonNull Integer postId) {
|
||||||
super(source, postId);
|
super(source, postId);
|
||||||
|
Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ResourceUtils;
|
import org.springframework.util.ResourceUtils;
|
||||||
import run.halo.app.config.properties.HaloProperties;
|
import run.halo.app.config.properties.HaloProperties;
|
||||||
import run.halo.app.model.properties.PrimaryProperties;
|
import run.halo.app.model.properties.PrimaryProperties;
|
||||||
|
@ -15,6 +17,7 @@ import run.halo.app.service.ThemeService;
|
||||||
import run.halo.app.service.UserService;
|
import run.halo.app.service.UserService;
|
||||||
import run.halo.app.utils.FileUtils;
|
import run.halo.app.utils.FileUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -76,8 +79,9 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
||||||
Path source;
|
Path source;
|
||||||
|
|
||||||
if ("jar".equalsIgnoreCase(themeUri.getScheme())) {
|
if ("jar".equalsIgnoreCase(themeUri.getScheme())) {
|
||||||
|
|
||||||
// Create new file system for jar
|
// Create new file system for jar
|
||||||
FileSystem fileSystem = FileSystems.newFileSystem(themeUri, Collections.emptyMap());
|
FileSystem fileSystem = getFileSystem(themeUri);
|
||||||
source = fileSystem.getPath("/BOOT-INF/classes/" + ThemeService.THEME_FOLDER);
|
source = fileSystem.getPath("/BOOT-INF/classes/" + ThemeService.THEME_FOLDER);
|
||||||
} else {
|
} else {
|
||||||
source = Paths.get(themeUri);
|
source = Paths.get(themeUri);
|
||||||
|
@ -98,4 +102,18 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private FileSystem getFileSystem(@NonNull URI uri) throws IOException {
|
||||||
|
Assert.notNull(uri, "Uri must not be null");
|
||||||
|
|
||||||
|
FileSystem fileSystem;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileSystem = FileSystems.getFileSystem(uri);
|
||||||
|
} catch (FileSystemNotFoundException e) {
|
||||||
|
fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileSystem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package run.halo.app.model.params;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post content param.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostContentParam {
|
||||||
|
private String content;
|
||||||
|
}
|
|
@ -142,4 +142,22 @@ public interface BasePostRepository<POST extends BasePost> extends BaseRepositor
|
||||||
@Query("update BasePost p set p.likes = p.likes + :likes where p.id = :postId")
|
@Query("update BasePost p set p.likes = p.likes + :likes where p.id = :postId")
|
||||||
int updateLikes(@Param("likes") long likes, @Param("postId") @NonNull Integer postId);
|
int updateLikes(@Param("likes") long likes, @Param("postId") @NonNull Integer postId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates post original content/
|
||||||
|
*
|
||||||
|
* @param content content could be blank but disallow to be null
|
||||||
|
* @param postId post id must not be null
|
||||||
|
* @return updated rows
|
||||||
|
*/
|
||||||
|
@Modifying
|
||||||
|
@Query("update BasePost p set p.originalContent = :content where p.id = :postId")
|
||||||
|
int updateOriginalContent(@Param("content") @NonNull String content, @Param("postId") @NonNull Integer postId);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("update BasePost p set p.status = :status where p.id = :postId")
|
||||||
|
int updateStatus(@Param("status") @NonNull PostStatus status, @Param("postId") @NonNull Integer postId);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("update BasePost p set p.formatContent = :formatContent where p.id = :postId")
|
||||||
|
int updateFormatContent(@Param("formatContent") @NonNull String formatContent, @Param("postId") @NonNull Integer postId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import run.halo.app.model.dto.post.BasePostDetailDTO;
|
||||||
import run.halo.app.model.dto.post.BasePostMinimalDTO;
|
import run.halo.app.model.dto.post.BasePostMinimalDTO;
|
||||||
import run.halo.app.model.dto.post.BasePostSimpleDTO;
|
import run.halo.app.model.dto.post.BasePostSimpleDTO;
|
||||||
import run.halo.app.model.entity.BasePost;
|
import run.halo.app.model.entity.BasePost;
|
||||||
|
import run.halo.app.model.entity.Post;
|
||||||
import run.halo.app.model.enums.PostStatus;
|
import run.halo.app.model.enums.PostStatus;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -215,4 +216,24 @@ public interface BasePostService<POST extends BasePost> extends CrudService<POST
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
BasePostDetailDTO convertToDetail(@NonNull POST post);
|
BasePostDetailDTO convertToDetail(@NonNull POST post);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates draft content.
|
||||||
|
*
|
||||||
|
* @param content draft content could be blank
|
||||||
|
* @param postId post id must not be null
|
||||||
|
* @return updated post
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
POST updateDraftContent(@Nullable String content, @NonNull Integer postId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates post status.
|
||||||
|
*
|
||||||
|
* @param status post status must not be null
|
||||||
|
* @param postId post id must not be null
|
||||||
|
* @return updated post
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
POST updateStatus(@NonNull PostStatus status, @NonNull Integer postId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,14 +112,7 @@ public abstract class BaseMetaServiceImpl<META extends BaseMeta> extends Abstrac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> convertToMap(List<META> metas) {
|
public Map<String, Object> convertToMap(List<META> metas) {
|
||||||
if (CollectionUtils.isEmpty(metas)) {
|
return ServiceUtils.convertToMap(metas, META::getKey, META::getValue);
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
metas.forEach(meta -> result.put(meta.getKey(), meta.getValue()));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.util.CollectionUtils;
|
||||||
import run.halo.app.exception.AlreadyExistsException;
|
import run.halo.app.exception.AlreadyExistsException;
|
||||||
import run.halo.app.exception.BadRequestException;
|
import run.halo.app.exception.BadRequestException;
|
||||||
import run.halo.app.exception.NotFoundException;
|
import run.halo.app.exception.NotFoundException;
|
||||||
|
import run.halo.app.exception.ServiceException;
|
||||||
import run.halo.app.model.dto.post.BasePostDetailDTO;
|
import run.halo.app.model.dto.post.BasePostDetailDTO;
|
||||||
import run.halo.app.model.dto.post.BasePostMinimalDTO;
|
import run.halo.app.model.dto.post.BasePostMinimalDTO;
|
||||||
import run.halo.app.model.dto.post.BasePostSimpleDTO;
|
import run.halo.app.model.dto.post.BasePostSimpleDTO;
|
||||||
|
@ -320,6 +321,65 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
|
||||||
return new BasePostDetailDTO().convertFrom(post);
|
return new BasePostDetailDTO().convertFrom(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public POST updateDraftContent(String content, Integer postId) {
|
||||||
|
Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty");
|
||||||
|
|
||||||
|
if (content == null) {
|
||||||
|
content = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
POST post = getById(postId);
|
||||||
|
|
||||||
|
if (!StringUtils.equals(content, post.getOriginalContent())) {
|
||||||
|
// If content is different with database, then update database
|
||||||
|
int updatedRows = basePostRepository.updateOriginalContent(content, postId);
|
||||||
|
if (updatedRows != 1) {
|
||||||
|
throw new ServiceException("Failed to update original content of post with id " + postId);
|
||||||
|
}
|
||||||
|
// Set the content
|
||||||
|
post.setOriginalContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public POST updateStatus(PostStatus status, Integer postId) {
|
||||||
|
Assert.notNull(status, "Post status must not be null");
|
||||||
|
Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty");
|
||||||
|
|
||||||
|
// Get post
|
||||||
|
POST post = getById(postId);
|
||||||
|
|
||||||
|
if (!status.equals(post.getStatus())) {
|
||||||
|
// Update post
|
||||||
|
int updatedRows = basePostRepository.updateStatus(status, postId);
|
||||||
|
if (updatedRows != 1) {
|
||||||
|
throw new ServiceException("Failed to update post status of post with id " + postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
post.setStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync content
|
||||||
|
if (PostStatus.PUBLISHED.equals(status)) {
|
||||||
|
// If publish this post, then convert the formatted content
|
||||||
|
String formatContent = MarkdownUtils.renderHtml(post.getOriginalContent());
|
||||||
|
int updatedRows = basePostRepository.updateFormatContent(formatContent, postId);
|
||||||
|
|
||||||
|
if (updatedRows != 1) {
|
||||||
|
throw new ServiceException("Failed to update post format content of post with id " + postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
post.setFormatContent(formatContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public POST create(POST post) {
|
public POST create(POST post) {
|
||||||
// Check title
|
// Check title
|
||||||
|
|
|
@ -67,7 +67,7 @@ public class MarkdownUtils {
|
||||||
/**
|
/**
|
||||||
* Render Markdown content
|
* Render Markdown content
|
||||||
*
|
*
|
||||||
* @param content content
|
* @param markdown content
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
public static String renderHtml(String markdown) {
|
public static String renderHtml(String markdown) {
|
||||||
|
@ -108,7 +108,7 @@ public class MarkdownUtils {
|
||||||
/**
|
/**
|
||||||
* Get front-matter
|
* Get front-matter
|
||||||
*
|
*
|
||||||
* @param content content
|
* @param markdown markdown
|
||||||
* @return Map
|
* @return Map
|
||||||
*/
|
*/
|
||||||
public static Map<String, List<String>> getFrontMatter(String markdown) {
|
public static Map<String, List<String>> getFrontMatter(String markdown) {
|
||||||
|
|
|
@ -99,8 +99,9 @@ public class ServiceUtils {
|
||||||
* @return a map which key from list data and value is data
|
* @return a map which key from list data and value is data
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static <ID, D, V> Map<ID, V> convertToMap(Collection<D> list, Function<D, ID> keyFunction, Function<D, V> valueFunction) {
|
public static <ID, D, V> Map<ID, V> convertToMap(@Nullable Collection<D> list, @NonNull Function<D, ID> keyFunction, @NonNull Function<D, V> valueFunction) {
|
||||||
Assert.notNull(keyFunction, "mapping function must not be null");
|
Assert.notNull(keyFunction, "Key function must not be null");
|
||||||
|
Assert.notNull(valueFunction, "Value function must not be null");
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(list)) {
|
if (CollectionUtils.isEmpty(list)) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
|
|
|
@ -14,10 +14,10 @@ spring:
|
||||||
type: com.zaxxer.hikari.HikariDataSource
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
|
|
||||||
# H2 Database 配置
|
# H2 Database 配置
|
||||||
driver-class-name: org.h2.Driver
|
# driver-class-name: org.h2.Driver
|
||||||
url: jdbc:h2:file:~/halo-test/db/halo
|
# url: jdbc:h2:file:~/halo-test/db/halo
|
||||||
username: admin
|
# username: admin
|
||||||
password: 123456
|
# password: 123456
|
||||||
|
|
||||||
# MySQL 配置
|
# MySQL 配置
|
||||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
|
|
@ -7,13 +7,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@ActiveProfiles("dev")
|
@ActiveProfiles("test")
|
||||||
public class PostServiceImplTest {
|
public class PostServiceImplTest {
|
||||||
|
|
||||||
String standardMdContent = "---\n" +
|
private String standardMdContent = "---\n" +
|
||||||
"title: springfox-swagger2配置成功但无法访问/swagger-ui.html\n" +
|
"title: springfox-swagger2配置成功但无法访问/swagger-ui.html\n" +
|
||||||
"tags:\n" +
|
"tags:\n" +
|
||||||
" - spring boot\n" +
|
" - spring boot\n" +
|
||||||
|
@ -26,7 +27,7 @@ public class PostServiceImplTest {
|
||||||
"\n" +
|
"\n" +
|
||||||
"在前后端分离项目中,通常需要用到 API 文档,springfox 开发的 **[SpringFox](https://github.com/springfox/springfox)** 可以实现自动化 json API 文档。";
|
"在前后端分离项目中,通常需要用到 API 文档,springfox 开发的 **[SpringFox](https://github.com/springfox/springfox)** 可以实现自动化 json API 文档。";
|
||||||
|
|
||||||
String nonStandardMdContent = "---\n" +
|
private String nonStandardMdContent = "---\n" +
|
||||||
"title: Basic concepts of JPA\n" +
|
"title: Basic concepts of JPA\n" +
|
||||||
"date: 2018-08-03 11:57:00\n" +
|
"date: 2018-08-03 11:57:00\n" +
|
||||||
"tags: ['spring', 'jpa', 'database', 'concept']\n" +
|
"tags: ['spring', 'jpa', 'database', 'concept']\n" +
|
||||||
|
@ -45,6 +46,7 @@ public class PostServiceImplTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Transactional
|
||||||
public void markdownImportTest() {
|
public void markdownImportTest() {
|
||||||
postService.importMarkdown(standardMdContent, "standard");
|
postService.importMarkdown(standardMdContent, "standard");
|
||||||
postService.importMarkdown(nonStandardMdContent, "nonStandard");
|
postService.importMarkdown(nonStandardMdContent, "nonStandard");
|
||||||
|
|
Loading…
Reference in New Issue