feat: 开发中...

This commit is contained in:
2023-04-06 04:49:23 +08:00
parent bb336c3c17
commit d8b1f6f959
24 changed files with 378 additions and 98 deletions

2
.gitignore vendored
View File

@@ -39,3 +39,5 @@ out/
node_modules/
dist/
*.d.ts
uploads/

View File

@@ -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());
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()
);
}
}

View File

@@ -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(),

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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) {
if (!blogRepo.existsById(blogID)) {
return ResponseVO.failed("该博文不存在!");
BlogUser user = BlogUtils.getCurrentUser().orElse(null);
if (user == null) {
return ResponseVO.unauthorized();
}
//todo 权限检查
switch (user.getRole()) {
case AUTHOR -> {
if (!blogRepo.existsByIdAndCreator_Id(blogID, user.getId())) {
return ResponseVO.failed("该博文不存在或不属于你!");
}
}
case ADMIN -> {
if (!blogRepo.existsById(blogID)) {
return ResponseVO.failed("该博文不存在!");
}
}
default -> {
return ResponseVO.failed("你没有这个权限!");
}
}
blogRepo.deleteById(blogID);
return ResponseVO.success();
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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 = "邮箱配置不合法!")

View File

@@ -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

View File

@@ -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 = "邮箱配置不合法!")

View File

@@ -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;

View File

@@ -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: 开发环境

View File

@@ -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>

View File

@@ -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">
<p>{{ blogInfo?.creator.nickname }}</p>
<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'">
<el-button type="primary" @click="editBlog">编辑</el-button>
<el-button type="danger" @click="deleteBlog">删除</el-button>
<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 {