feat: 开发中...
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -39,3 +39,5 @@ out/
|
||||
node_modules/
|
||||
dist/
|
||||
*.d.ts
|
||||
|
||||
uploads/
|
@@ -13,6 +13,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
@@ -53,6 +54,18 @@ public class BlogExceptionHandler {
|
||||
return ResponseVO.failed(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(IOException.class)
|
||||
public ResponseVO<String> onIOException(IOException e) {
|
||||
if ("dev".equals(environment.getProperty("spring.profiles.active"))) {
|
||||
log.error("", e);
|
||||
StringWriter writer = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(writer));
|
||||
// StringWriter 不需要 close()
|
||||
return new ResponseVO<>(403, e.getMessage(), writer.toString());
|
||||
}
|
||||
return ResponseVO.failed(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseVO<String> onException(Exception e) {
|
||||
if ("dev".equals(environment.getProperty("spring.profiles.active"))) {
|
||||
@@ -64,4 +77,16 @@ public class BlogExceptionHandler {
|
||||
}
|
||||
return ResponseVO.failed(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Throwable.class)
|
||||
public ResponseVO<String> onThrowable(Throwable e) {
|
||||
if ("dev".equals(environment.getProperty("spring.profiles.active"))) {
|
||||
log.error("", e);
|
||||
StringWriter writer = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(writer));
|
||||
// StringWriter 不需要 close()
|
||||
return new ResponseVO<>(403, e.getMessage(), writer.toString());
|
||||
}
|
||||
return ResponseVO.failed(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,66 @@
|
||||
package cn.hamster3.application.blog.controller;
|
||||
|
||||
import cn.hamster3.application.blog.service.IAttachService;
|
||||
import cn.hamster3.application.blog.vo.PageableVO;
|
||||
import cn.hamster3.application.blog.vo.ResponseVO;
|
||||
import cn.hamster3.application.blog.vo.attach.AttachInfoResponseVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Tag(name = "AttachController", description = "附件相关接口")
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/v1/attach", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class AttachController {
|
||||
@Resource
|
||||
private IAttachService attachService;
|
||||
|
||||
@PostMapping("/")
|
||||
public ResponseVO<Void> createAttach(@RequestBody MultipartFile file) {
|
||||
return ResponseVO.success();
|
||||
@Operation(summary = "新建附件")
|
||||
public ResponseVO<Long> createAttach(@RequestParam MultipartFile file) throws IOException {
|
||||
return attachService.createAttach(file);
|
||||
}
|
||||
|
||||
@GetMapping("/{attachID}/")
|
||||
public ResponseVO<Void> getAttach(@PathVariable String attachID) {
|
||||
return ResponseVO.success();
|
||||
@Operation(summary = "获取附件信息")
|
||||
public ResponseVO<AttachInfoResponseVO> getAttachInfo(@PathVariable Long attachID) {
|
||||
return attachService.getAttachInfo(attachID);
|
||||
}
|
||||
|
||||
@GetMapping("/{attachID}/content")
|
||||
@Operation(summary = "获取附件内容")
|
||||
public ResponseEntity<InputStreamResource> getAttachContent(@PathVariable Long attachID) throws IOException {
|
||||
return attachService.getAttachContent(attachID);
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public ResponseVO<Void> getAttachList() {
|
||||
return ResponseVO.success();
|
||||
@Operation(summary = "获取附件列表")
|
||||
public ResponseVO<PageableVO<AttachInfoResponseVO>> getAttachList(
|
||||
@Parameter(description = "页码", example = "0") int page,
|
||||
@Parameter(description = "大小", example = "10") int size
|
||||
) {
|
||||
return attachService.getAttachList(PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
@PutMapping("/{attachID}/")
|
||||
public ResponseVO<Void> modifyAttach(@PathVariable String attachID, @RequestBody MultipartFile file) {
|
||||
return ResponseVO.success();
|
||||
@Operation(summary = "更新附件")
|
||||
public ResponseVO<Void> updateAttach(@PathVariable Long attachID, @RequestParam MultipartFile file) throws IOException {
|
||||
return attachService.updateAttach(attachID, file);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{attachID}/")
|
||||
public ResponseVO<Void> deleteAttach(@PathVariable String attachID) {
|
||||
return ResponseVO.success();
|
||||
@Operation(summary = "删除附件")
|
||||
public ResponseVO<Void> deleteAttach(@PathVariable Long attachID) {
|
||||
return attachService.deleteAttach(attachID);
|
||||
}
|
||||
|
||||
}
|
@@ -21,6 +21,14 @@ public class AttachEntity {
|
||||
@Column(name = "id", nullable = false, updatable = false)
|
||||
private Long id;
|
||||
|
||||
@Setter
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Setter
|
||||
@Column(name = "content_type", nullable = false)
|
||||
private String contentType;
|
||||
|
||||
@Setter
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
|
@@ -4,8 +4,6 @@ import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
@@ -32,10 +30,6 @@ public class BlogEntity {
|
||||
@Column(name = "abstracts", nullable = false, length = 512)
|
||||
private String abstracts;
|
||||
|
||||
@Column(name = "password", length = 60)
|
||||
@JdbcTypeCode(SqlTypes.VARCHAR)
|
||||
private String password;
|
||||
|
||||
@ToString.Exclude
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
|
@@ -3,20 +3,24 @@ package cn.hamster3.application.blog.entity.mapper;
|
||||
import cn.hamster3.application.blog.entity.AttachEntity;
|
||||
import cn.hamster3.application.blog.entity.UserEntity;
|
||||
import cn.hamster3.application.blog.vo.attach.AttachInfoResponseVO;
|
||||
import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING)
|
||||
public interface AttachMapper {
|
||||
AttachInfoResponseVO entityToInfoVO(AttachEntity entity);
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unused")
|
||||
default UUID map(@NotNull UserEntity value) {
|
||||
return value.getId();
|
||||
default UserInfoResponseVO mapToInfoVO(@NotNull UserEntity value) {
|
||||
return new UserInfoResponseVO(
|
||||
value.getId(),
|
||||
value.getEmail(),
|
||||
value.getNickname(),
|
||||
value.getRole(),
|
||||
value.getCreateTime(),
|
||||
value.getUpdateTime()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -10,20 +10,12 @@ import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING)
|
||||
public interface BlogMapper {
|
||||
BlogEntity voToEntity(BlogUpdateRequireVO vo);
|
||||
|
||||
BlogInfoResponseVO entityToInfoVO(BlogEntity entity);
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unused")
|
||||
default UUID mapToUUID(@NotNull UserEntity value) {
|
||||
return value.getId();
|
||||
}
|
||||
|
||||
default UserInfoResponseVO mapToInfoVO(@NotNull UserEntity value) {
|
||||
return new UserInfoResponseVO(
|
||||
value.getId(),
|
||||
|
@@ -5,11 +5,22 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AttachRepository extends JpaRepository<AttachEntity, Long>, JpaSpecificationExecutor<AttachEntity> {
|
||||
boolean existsByIdAndCreator_Id(Long id, UUID id1);
|
||||
@Query("select a from AttachEntity a where a.id = ?1")
|
||||
AttachEntity findByIdWithContent(Long id);
|
||||
|
||||
@Transactional
|
||||
@Modifying
|
||||
@Query("update AttachEntity a set a.data = ?1, a.contentType = ?2 where a.id = ?3")
|
||||
void updateDataAndContentTypeById(byte[] data, String contentType, Long id);
|
||||
|
||||
@Query("select a from AttachEntity a where a.creator.id = ?1 order by a.createTime DESC")
|
||||
Page<AttachEntity> findByCreator_IdOrderByCreateTimeDesc(UUID id, Pageable pageable);
|
||||
}
|
@@ -3,22 +3,26 @@ package cn.hamster3.application.blog.entity.repo;
|
||||
import cn.hamster3.application.blog.entity.BlogEntity;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.*;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BlogRepository extends JpaRepository<BlogEntity, Long>, JpaSpecificationExecutor<BlogEntity> {
|
||||
@Query("select b from BlogEntity b where b.creator.id = ?1 order by b.createTime DESC")
|
||||
Page<BlogEntity> findByCreator_IdOrderByCreateTimeDesc(UUID id, Pageable pageable);
|
||||
boolean existsByIdAndCreator_Id(Long id, UUID id1);
|
||||
|
||||
@EntityGraph(attributePaths = {"creator"})
|
||||
@Query("select b from BlogEntity b where b.id = ?1")
|
||||
BlogEntity findByIdWithCreator(Long id);
|
||||
|
||||
@EntityGraph(attributePaths = {"content"})
|
||||
@Query("select b from BlogEntity b where b.id = ?1")
|
||||
Optional<BlogEntity> findByIDWithContent(Long id);
|
||||
Optional<BlogEntity> findByIdWithContent(Long id);
|
||||
|
||||
@Query("select b from BlogEntity b where b.creator.id = ?1 order by b.createTime DESC")
|
||||
Page<BlogEntity> findByCreator_IdOrderByCreateTimeDesc(UUID id, Pageable pageable);
|
||||
|
||||
@Transactional
|
||||
@Modifying
|
||||
@Query("update BlogEntity b set b.title = ?1, b.abstracts = ?2, b.password = ?3, b.content = ?4 where b.id = ?5")
|
||||
void updateTitleAndAbstractsAndPasswordAndContentById(String title, String abstracts, String password, String content, Long id);
|
||||
}
|
@@ -1,4 +1,26 @@
|
||||
package cn.hamster3.application.blog.service;
|
||||
|
||||
import cn.hamster3.application.blog.vo.PageableVO;
|
||||
import cn.hamster3.application.blog.vo.ResponseVO;
|
||||
import cn.hamster3.application.blog.vo.attach.AttachInfoResponseVO;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IAttachService {
|
||||
@NotNull ResponseVO<Long> createAttach(@NotNull MultipartFile file) throws IOException;
|
||||
|
||||
@NotNull ResponseVO<AttachInfoResponseVO> getAttachInfo(@NotNull Long attachID);
|
||||
|
||||
@NotNull ResponseEntity<InputStreamResource> getAttachContent(@NotNull Long attachID) throws IOException;
|
||||
|
||||
@NotNull ResponseVO<PageableVO<AttachInfoResponseVO>> getAttachList(@NotNull Pageable pageable);
|
||||
|
||||
@NotNull ResponseVO<Void> updateAttach(@NotNull Long attachID, @NotNull MultipartFile file) throws IOException;
|
||||
|
||||
@NotNull ResponseVO<Void> deleteAttach(@NotNull Long attachID);
|
||||
}
|
||||
|
@@ -1,8 +1,155 @@
|
||||
package cn.hamster3.application.blog.service.impl;
|
||||
|
||||
import cn.hamster3.application.blog.config.security.BlogUser;
|
||||
import cn.hamster3.application.blog.entity.AttachEntity;
|
||||
import cn.hamster3.application.blog.entity.mapper.AttachMapper;
|
||||
import cn.hamster3.application.blog.entity.repo.AttachRepository;
|
||||
import cn.hamster3.application.blog.service.IAttachService;
|
||||
import cn.hamster3.application.blog.util.BlogUtils;
|
||||
import cn.hamster3.application.blog.vo.PageableVO;
|
||||
import cn.hamster3.application.blog.vo.ResponseVO;
|
||||
import cn.hamster3.application.blog.vo.attach.AttachInfoResponseVO;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AttachService implements IAttachService {
|
||||
private static final File ATTACH_FOLDER = new File("uploads");
|
||||
|
||||
static {
|
||||
if (ATTACH_FOLDER.mkdirs()) {
|
||||
log.info("attach folder created!");
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private AttachMapper attachMapper;
|
||||
@Resource
|
||||
private AttachRepository attachRepo;
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<Long> createAttach(@NotNull MultipartFile file) throws IOException {
|
||||
ResponseVO<Long> checked = BlogUtils.checkAuthorPermission();
|
||||
if (checked != null) {
|
||||
return checked;
|
||||
}
|
||||
log.info("prepare to save file: {}({} bytes)", file.getOriginalFilename(), file.getSize());
|
||||
AttachEntity attachEntity = new AttachEntity();
|
||||
attachEntity.setName(file.getOriginalFilename());
|
||||
attachEntity.setContentType(file.getContentType());
|
||||
attachEntity.setData(file.getBytes());
|
||||
attachEntity = attachRepo.save(attachEntity);
|
||||
|
||||
File localCacheFile = new File(ATTACH_FOLDER, String.valueOf(attachEntity.getId()));
|
||||
Files.copy(file.getInputStream(), localCacheFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
return ResponseVO.success(attachEntity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<AttachInfoResponseVO> getAttachInfo(@NotNull Long attachID) {
|
||||
AttachEntity entity = attachRepo.findById(attachID).orElse(null);
|
||||
if (entity == null) {
|
||||
return ResponseVO.notFound();
|
||||
}
|
||||
return ResponseVO.success(attachMapper.entityToInfoVO(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseEntity<InputStreamResource> getAttachContent(@NotNull Long attachID) throws IOException {
|
||||
File localCacheFile = new File(ATTACH_FOLDER, String.valueOf(attachID));
|
||||
AttachEntity entity;
|
||||
if (localCacheFile.exists()) {
|
||||
entity = attachRepo.findById(attachID).orElse(null);
|
||||
} else {
|
||||
entity = attachRepo.findByIdWithContent(attachID);
|
||||
}
|
||||
if (entity == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
if (!localCacheFile.exists()) {
|
||||
try (ByteArrayInputStream stream = new ByteArrayInputStream(entity.getData())) {
|
||||
Files.copy(stream, localCacheFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
InputStream stream = Files.newInputStream(localCacheFile.toPath());
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.header("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
.header("Content-Disposition", String.format("attachment; filename=\"%s\"", entity.getName()))
|
||||
.header("Pragma", "no-cache")
|
||||
.header("Expires", "0")
|
||||
.contentLength(localCacheFile.length())
|
||||
.contentType(MediaType.parseMediaType(entity.getContentType()))
|
||||
.body(new InputStreamResource(stream));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<PageableVO<AttachInfoResponseVO>> getAttachList(@NotNull Pageable pageable) {
|
||||
return PageableVO.success(attachRepo.findAll(pageable).map(o -> attachMapper.entityToInfoVO(o)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<Void> updateAttach(@NotNull Long attachID, @NotNull MultipartFile file) throws IOException {
|
||||
ResponseVO<Void> checked = BlogUtils.checkAuthorPermission();
|
||||
if (checked != null) {
|
||||
return checked;
|
||||
}
|
||||
if (!attachRepo.existsById(attachID)) {
|
||||
return ResponseVO.notFound();
|
||||
}
|
||||
attachRepo.updateDataAndContentTypeById(file.getBytes(), file.getContentType(), attachID);
|
||||
|
||||
File localCacheFile = new File(ATTACH_FOLDER, String.valueOf(attachID));
|
||||
Files.copy(file.getInputStream(), localCacheFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
log.info("written attach data to local cache file: {}", localCacheFile.getName());
|
||||
|
||||
return ResponseVO.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<Void> deleteAttach(@NotNull Long attachID) {
|
||||
BlogUser user = BlogUtils.getCurrentUser().orElse(null);
|
||||
if (user == null) {
|
||||
return ResponseVO.unauthorized();
|
||||
}
|
||||
switch (user.getRole()) {
|
||||
case AUTHOR -> {
|
||||
if (!attachRepo.existsByIdAndCreator_Id(attachID, user.getId())) {
|
||||
return ResponseVO.failed("该附件不存在或不属于你!");
|
||||
}
|
||||
}
|
||||
case ADMIN -> {
|
||||
if (!attachRepo.existsById(attachID)) {
|
||||
return ResponseVO.failed("该附件不存在!");
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
return ResponseVO.failed("你没有这个权限!");
|
||||
}
|
||||
}
|
||||
attachRepo.deleteById(attachID);
|
||||
|
||||
File localCacheFile = new File(ATTACH_FOLDER, String.valueOf(attachID));
|
||||
if (localCacheFile.delete()) {
|
||||
log.info("deleted local attach cache file: {}", localCacheFile.getName());
|
||||
}
|
||||
|
||||
return ResponseVO.success();
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,8 @@
|
||||
package cn.hamster3.application.blog.service.impl;
|
||||
|
||||
import cn.hamster3.application.blog.config.security.BlogUser;
|
||||
import cn.hamster3.application.blog.config.security.UserAuditorAware;
|
||||
import cn.hamster3.application.blog.constant.UserRole;
|
||||
import cn.hamster3.application.blog.entity.BlogEntity;
|
||||
import cn.hamster3.application.blog.entity.UserEntity;
|
||||
import cn.hamster3.application.blog.entity.mapper.BlogMapper;
|
||||
import cn.hamster3.application.blog.entity.repo.BlogRepository;
|
||||
import cn.hamster3.application.blog.service.IBlogService;
|
||||
@@ -26,16 +25,13 @@ public class BlogService implements IBlogService {
|
||||
@Resource
|
||||
private BlogRepository blogRepo;
|
||||
|
||||
@Resource
|
||||
private UserAuditorAware userAuditorAware;
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<Long> createBlog(@NotNull BlogUpdateRequireVO requireVO) {
|
||||
log.info("create blog vo: {}", requireVO);
|
||||
UserEntity user = userAuditorAware.getCurrentAuditor().orElse(null);
|
||||
if (user == null) {
|
||||
return ResponseVO.unauthorized();
|
||||
ResponseVO<Long> checked = BlogUtils.checkAuthorPermission();
|
||||
if (checked != null) {
|
||||
return checked;
|
||||
}
|
||||
log.info("create blog vo: {}", requireVO);
|
||||
BlogEntity entity = blogMapper.voToEntity(requireVO);
|
||||
entity = blogRepo.save(entity);
|
||||
return ResponseVO.success(entity.getId());
|
||||
@@ -43,7 +39,7 @@ public class BlogService implements IBlogService {
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<BlogInfoResponseVO> getBlogInfo(@NotNull Long blogID) {
|
||||
return blogRepo.findByIDWithContent(blogID)
|
||||
return blogRepo.findByIdWithContent(blogID)
|
||||
.map(o -> ResponseVO.success(blogMapper.entityToInfoVO(o)))
|
||||
.orElseThrow(() -> new IllegalArgumentException("未找到该文章!"));
|
||||
}
|
||||
@@ -55,30 +51,55 @@ public class BlogService implements IBlogService {
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<Void> updateBlog(@NotNull Long blogID, @NotNull BlogUpdateRequireVO requireVO) {
|
||||
if (!blogRepo.existsById(blogID)) {
|
||||
return ResponseVO.failed("该博文不存在!");
|
||||
}
|
||||
BlogUser user = BlogUtils.getCurrentUser().orElse(null);
|
||||
if (user == null) {
|
||||
return ResponseVO.unauthorized();
|
||||
}
|
||||
//todo 权限检查
|
||||
blogRepo.updateTitleAndAbstractsAndPasswordAndContentById(
|
||||
requireVO.getTitle(),
|
||||
requireVO.getAbstracts(),
|
||||
requireVO.getPassword(),
|
||||
requireVO.getContent(),
|
||||
blogID
|
||||
);
|
||||
log.info("update blog vo: {}", requireVO);
|
||||
BlogEntity blogEntity = blogRepo.findByIdWithCreator(blogID);
|
||||
if (blogEntity == null) {
|
||||
return ResponseVO.failed("该博文不存在!");
|
||||
}
|
||||
if (user.getRole() == UserRole.GUEST) {
|
||||
return ResponseVO.failed("你没有这个权限!");
|
||||
}
|
||||
if (user.getRole() != UserRole.ADMIN) {
|
||||
if (!blogEntity.getCreator().getId().equals(user.getId())) {
|
||||
return ResponseVO.failed("你没有这个权限!");
|
||||
}
|
||||
}
|
||||
blogEntity.setTitle(requireVO.getTitle());
|
||||
blogEntity.setAbstracts(requireVO.getAbstracts());
|
||||
blogEntity.setContent(requireVO.getContent());
|
||||
blogEntity.setTop(requireVO.getTop());
|
||||
blogEntity.setPublish(requireVO.getPublish());
|
||||
blogEntity.setTags(requireVO.getTags());
|
||||
blogRepo.save(blogEntity);
|
||||
return ResponseVO.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResponseVO<Void> removeBlog(@NotNull Long blogID) {
|
||||
BlogUser user = BlogUtils.getCurrentUser().orElse(null);
|
||||
if (user == null) {
|
||||
return ResponseVO.unauthorized();
|
||||
}
|
||||
switch (user.getRole()) {
|
||||
case AUTHOR -> {
|
||||
if (!blogRepo.existsByIdAndCreator_Id(blogID, user.getId())) {
|
||||
return ResponseVO.failed("该博文不存在或不属于你!");
|
||||
}
|
||||
}
|
||||
case ADMIN -> {
|
||||
if (!blogRepo.existsById(blogID)) {
|
||||
return ResponseVO.failed("该博文不存在!");
|
||||
}
|
||||
//todo 权限检查
|
||||
}
|
||||
default -> {
|
||||
return ResponseVO.failed("你没有这个权限!");
|
||||
}
|
||||
}
|
||||
|
||||
blogRepo.deleteById(blogID);
|
||||
return ResponseVO.success();
|
||||
}
|
||||
|
@@ -23,14 +23,9 @@ public class BlogUtils {
|
||||
public static Optional<BlogUser> getCurrentUser() {
|
||||
Authentication authentication = getCurrentAuthentication();
|
||||
if (authentication == null) {
|
||||
log.info("current user authentication: null");
|
||||
return Optional.empty();
|
||||
}
|
||||
log.info("current user authentication: {}", authentication);
|
||||
log.info("current user authentication getPrincipal: {}", authentication.getPrincipal());
|
||||
log.info("current user authentication getCredentials: {}", authentication.getCredentials());
|
||||
log.info("current user authentication getDetails: {}", authentication.getDetails());
|
||||
log.info("current user authentication getAuthorities: {}", authentication.getAuthorities());
|
||||
if (!authentication.isAuthenticated()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -45,14 +40,21 @@ public class BlogUtils {
|
||||
return getCurrentUser().map(BlogUser::getId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static UserRole getCurrentUserRole() {
|
||||
return getCurrentUser().map(BlogUser::getRole).orElse(UserRole.GUEST);
|
||||
@Nullable
|
||||
public static <T> ResponseVO<T> checkAuthorPermission() {
|
||||
BlogUser user = getCurrentUser().orElse(null);
|
||||
if (user == null) {
|
||||
return ResponseVO.unauthorized();
|
||||
}
|
||||
return switch (user.getRole()) {
|
||||
case AUTHOR, ADMIN -> null;
|
||||
default -> ResponseVO.failed("你没有这个权限!");
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T> ResponseVO<T> checkAdminPermission() {
|
||||
BlogUser user = BlogUtils.getCurrentUser().orElse(null);
|
||||
BlogUser user = getCurrentUser().orElse(null);
|
||||
if (user == null) {
|
||||
return ResponseVO.unauthorized();
|
||||
}
|
||||
|
@@ -1,19 +1,23 @@
|
||||
package cn.hamster3.application.blog.vo.attach;
|
||||
|
||||
import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AttachInfoResponseVO {
|
||||
@NotNull
|
||||
private Long id;
|
||||
@NotNull
|
||||
private UUID creator;
|
||||
private UserInfoResponseVO creator;
|
||||
@NotNull
|
||||
private UserInfoResponseVO updater;
|
||||
@NotNull
|
||||
private Date createTime;
|
||||
@NotNull
|
||||
|
@@ -4,11 +4,13 @@ import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BlogInfoResponseVO {
|
||||
@NotNull
|
||||
|
@@ -1,15 +1,16 @@
|
||||
package cn.hamster3.application.blog.vo.blog;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BlogUpdateRequireVO {
|
||||
@Length(max = 32, message = "标题长度不能超过 32 个字符!")
|
||||
@@ -19,10 +20,6 @@ public class BlogUpdateRequireVO {
|
||||
@Length(max = 512, message = "摘要长度不能超过 512 个字符!")
|
||||
private String abstracts;
|
||||
|
||||
@Nullable
|
||||
@Length(max = 16, message = "密码最大长度不能超过 16 个字符!")
|
||||
private String password;
|
||||
|
||||
@NotBlank(message = "博客文章内容不能为空!")
|
||||
private String content;
|
||||
|
||||
|
@@ -3,10 +3,12 @@ package cn.hamster3.application.blog.vo.setting;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SettingInfoResponseVO {
|
||||
@NotNull
|
||||
|
@@ -4,9 +4,11 @@ import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserCreateRequireVO {
|
||||
@Email(message = "邮箱配置不合法!")
|
||||
|
@@ -4,11 +4,13 @@ import cn.hamster3.application.blog.constant.UserRole;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserInfoResponseVO {
|
||||
@NotNull
|
||||
|
@@ -4,9 +4,12 @@ import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserLoginRequireVO {
|
||||
@Email(message = "邮箱配置不合法!")
|
||||
|
@@ -3,9 +3,11 @@ package cn.hamster3.application.blog.vo.user;
|
||||
import cn.hamster3.application.blog.constant.UserRole;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserUpdateRequireVO {
|
||||
private String email;
|
||||
|
@@ -1,12 +1,12 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
servlet:
|
||||
context-path: /
|
||||
multipart:
|
||||
enable: true
|
||||
max-file-size: 16M
|
||||
max-require-size: 32M
|
||||
spring:
|
||||
max-file-size: 16MB
|
||||
max-request-size: 32MB
|
||||
# 要使用的环境预设
|
||||
# prod: 生产环境
|
||||
# dev: 开发环境
|
||||
|
@@ -106,7 +106,7 @@ function handleCreated(editor: IDomEditor) {
|
||||
/>
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<el-main class="edit-form-side">
|
||||
<el-form :model="editBlog">
|
||||
<el-form-item label="博文标题">
|
||||
<el-input
|
||||
@@ -152,17 +152,15 @@ function handleCreated(editor: IDomEditor) {
|
||||
|
||||
.edit-content-side {
|
||||
height: 100%;
|
||||
width: 80%;
|
||||
width: 61.8%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-style: solid;
|
||||
border-width: 0 1px 0 1px;
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.edit-content-toolbar {
|
||||
height: 80px;
|
||||
border: 1px solid #ccc;
|
||||
border-top-width: 0;
|
||||
}
|
||||
.edit-content-scrollbar {
|
||||
height: calc(100% - 80px);
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
|
@@ -28,16 +28,22 @@ function deleteBlog() {
|
||||
<template>
|
||||
<el-container class="blog-container">
|
||||
<el-header class="blog-header">
|
||||
<el-row :gutter="20">
|
||||
<el-row :gutter="20" style="height: 100%; padding: 0; margin: 0">
|
||||
<el-col :span="4">
|
||||
<div style="display: flex; width: 100% height: 100%">
|
||||
<p>{{ blogInfo?.creator.nickname }}</p>
|
||||
<div style="flex-grow: 1"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<h1 class="blog-title">{{ blogInfo?.title }}</h1>
|
||||
</el-col>
|
||||
<el-col :span="4" v-if="globalStore.currentUserInfo?.role === 'ADMIN'">
|
||||
<div style="display: flex; width: 100% height: 100%">
|
||||
<div style="flex-grow: 1"></div>
|
||||
<el-button type="primary" @click="editBlog">编辑</el-button>
|
||||
<el-button type="danger" @click="deleteBlog">删除</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-header>
|
||||
@@ -62,6 +68,7 @@ function deleteBlog() {
|
||||
padding-bottom: 10px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.blog-author {
|
||||
|
Reference in New Issue
Block a user