diff --git a/src/main/java/org/cmh/backend/NewsManagement/controller/FileController.java b/src/main/java/org/cmh/backend/NewsManagement/controller/FileController.java new file mode 100644 index 0000000..b48660a --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/controller/FileController.java @@ -0,0 +1,65 @@ +package org.cmh.backend.NewsManagement.controller; + +import org.cmh.backend.NewsManagement.dto.UploadFileResponse; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +@RestController +@CrossOrigin // 如果前端和后端不在同一个域名或端口下,需要启用跨域 +public class FileController { + + private static final String UPLOAD_DIR = "uploads/"; + + @PostMapping("/news/uploadPic") + public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { + if (file.isEmpty()) { + return new ResponseEntity<>(new UploadFileResponse("文件不能为空", null), HttpStatus.BAD_REQUEST); + } + + try { + // 确保上传目录存在 + Path uploadDirPath = Paths.get(UPLOAD_DIR); + if (!Files.exists(uploadDirPath)) { + Files.createDirectories(uploadDirPath); + } + + // 生成文件路径 + byte[] bytes = file.getBytes(); + Path path = Paths.get(UPLOAD_DIR + file.getOriginalFilename()); + Files.write(path, bytes); + + // 返回成功信息 + return new ResponseEntity<>(new UploadFileResponse("文件上传成功", "/api/news/files/" + file.getOriginalFilename()), HttpStatus.OK); + } catch (IOException e) { + return new ResponseEntity<>(new UploadFileResponse("文件上传失败", null), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @GetMapping("/news/files/{filename}") + public ResponseEntity getFile(@PathVariable String filename) { + try { + Path filePath = Paths.get(UPLOAD_DIR).resolve(filename).normalize(); + Resource resource = new UrlResource(filePath.toUri()); + + if (resource.exists() && resource.isReadable()) { + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } catch (Exception e) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/controller/NewsController.java b/src/main/java/org/cmh/backend/NewsManagement/controller/NewsController.java new file mode 100644 index 0000000..c149c4b --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/controller/NewsController.java @@ -0,0 +1,104 @@ +package org.cmh.backend.NewsManagement.controller; + +import jakarta.persistence.EntityNotFoundException; +import org.cmh.backend.NewsManagement.dto.GetNewsListResponse; +import org.cmh.backend.NewsManagement.dto.MessageResponse; +import org.cmh.backend.NewsManagement.dto.NewsRequest; +import org.cmh.backend.NewsManagement.dto.SearchNewsRequest; +import org.cmh.backend.NewsManagement.model.News; +import org.cmh.backend.NewsManagement.service.NewsService; +import org.cmh.backend.Utils.JwtUtil; +import org.cmh.backend.Utils.JwtVerify; +import org.cmh.backend.authentication.model.UserHS; +import org.cmh.backend.authentication.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/news") +public class NewsController { + @Autowired + private NewsService newsService; + + @Autowired + private UserService userService; + + @GetMapping + @JwtVerify + public ResponseEntity getNewsByRange(@RequestParam Integer start, @RequestParam Integer end, @RequestParam String token) { + String username = JwtUtil.extractUsername(token); + UserHS user = userService.getUserByUsername(username); + // TODO:完善用户权限 + if (user.getSuperAdmin()) { + + } else { + + } + + if (start >= end) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + long newsCount = newsService.getNewsCount(); + return new ResponseEntity<>(new GetNewsListResponse(newsCount, newsService.getNewsByRange(start, end, user)), HttpStatus.OK); + } + + @PostMapping("/search") + @JwtVerify + public ResponseEntity searchNews(@RequestBody SearchNewsRequest request) { + List newsList = newsService.searchNews(request); + long newsCount = newsList.size(); + return new ResponseEntity<>(new GetNewsListResponse(newsCount, newsList), HttpStatus.OK); + } + + @GetMapping("/{id}") + @JwtVerify + public ResponseEntity getNewsPage(@PathVariable Long id, @RequestParam String token) { + try { + return new ResponseEntity<>(newsService.getNewsById(id), HttpStatus.OK); + } catch (EntityNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @PostMapping + @JwtVerify + public ResponseEntity createNews(@RequestBody NewsRequest request) { + try { + newsService.createNews(request); + } catch (DataIntegrityViolationException e) { + return new ResponseEntity<>(new MessageResponse("创建失败,文章已存在或缺少字段"), HttpStatus.BAD_REQUEST); + } catch (Exception e) { + return new ResponseEntity<>(new MessageResponse("创建失败:" + e.getMessage()), HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(new MessageResponse("创建成功"), HttpStatus.CREATED); + } + + @PutMapping("/{id}") + @JwtVerify + public ResponseEntity updateNews(@PathVariable Long id, @RequestBody NewsRequest request) { + try { + newsService.updateNews(id, request); + } catch (DataIntegrityViolationException e) { + return new ResponseEntity<>(new MessageResponse("修改失败,新标题已存在或缺少字段"), HttpStatus.BAD_REQUEST); + } catch (Exception e) { + return new ResponseEntity<>(new MessageResponse("创建失败:" + e.getMessage()), HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(new MessageResponse("修改成功"), HttpStatus.OK); + } + + @DeleteMapping("/{id}") + @JwtVerify + public ResponseEntity deleteNews(@PathVariable Long id, @RequestParam String token) { + try { + newsService.deleteNews(id); + } catch (EntityNotFoundException e) { + return new ResponseEntity<>(new MessageResponse("删除失败,文章不存在"), HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(new MessageResponse("删除成功"), HttpStatus.OK); + } +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/dto/GetNewsListResponse.java b/src/main/java/org/cmh/backend/NewsManagement/dto/GetNewsListResponse.java new file mode 100644 index 0000000..4c1f6c3 --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/dto/GetNewsListResponse.java @@ -0,0 +1,17 @@ +package org.cmh.backend.NewsManagement.dto; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.cmh.backend.NewsManagement.model.News; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +public class GetNewsListResponse { + Long newsCount; + List newsList; +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/dto/MessageResponse.java b/src/main/java/org/cmh/backend/NewsManagement/dto/MessageResponse.java new file mode 100644 index 0000000..b777601 --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/dto/MessageResponse.java @@ -0,0 +1,12 @@ +package org.cmh.backend.NewsManagement.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class MessageResponse { + String message; +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/dto/NewsRequest.java b/src/main/java/org/cmh/backend/NewsManagement/dto/NewsRequest.java new file mode 100644 index 0000000..9852ff5 --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/dto/NewsRequest.java @@ -0,0 +1,16 @@ +package org.cmh.backend.NewsManagement.dto; + +import lombok.Getter; +import lombok.Setter; +import org.cmh.backend.Utils.JwtRequest; + +@Getter +@Setter +public class NewsRequest extends JwtRequest { + private String title; + private String summary; + private String content; + private String imagePath; + private String author; + private String tenant; +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/dto/SearchNewsRequest.java b/src/main/java/org/cmh/backend/NewsManagement/dto/SearchNewsRequest.java new file mode 100644 index 0000000..7d632fd --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/dto/SearchNewsRequest.java @@ -0,0 +1,15 @@ +package org.cmh.backend.NewsManagement.dto; + +import lombok.Getter; +import lombok.Setter; +import org.cmh.backend.Utils.JwtRequest; + +@Getter +@Setter +public class SearchNewsRequest extends JwtRequest { + private String author; + private String title; + private String imagePath; + private String summary; + private String sortBy; +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/dto/UploadFileResponse.java b/src/main/java/org/cmh/backend/NewsManagement/dto/UploadFileResponse.java new file mode 100644 index 0000000..b2eba96 --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/dto/UploadFileResponse.java @@ -0,0 +1,16 @@ +package org.cmh.backend.NewsManagement.dto; + + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class UploadFileResponse extends MessageResponse { + private String url; + + public UploadFileResponse(String message, String url) { + super(message); + this.url = url; + } +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/model/News.java b/src/main/java/org/cmh/backend/NewsManagement/model/News.java new file mode 100644 index 0000000..64edf3f --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/model/News.java @@ -0,0 +1,36 @@ +package org.cmh.backend.NewsManagement.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; + +import java.time.LocalDateTime; + +@Setter +@Getter +@Entity +public class News { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String title; + + @Column(nullable = false) + private String summary; + + @Column(nullable = false) + private String content; + + private String imagePath; + + @Column(nullable = false) + private String author; + + @CreationTimestamp + private LocalDateTime createdAt; + // TODO:添加外键绑定 + private String tenant; +} diff --git a/src/main/java/org/cmh/backend/NewsManagement/repository/NewsRepository.java b/src/main/java/org/cmh/backend/NewsManagement/repository/NewsRepository.java new file mode 100644 index 0000000..d5e7884 --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/repository/NewsRepository.java @@ -0,0 +1,23 @@ +package org.cmh.backend.NewsManagement.repository; + +import org.cmh.backend.NewsManagement.model.News; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface NewsRepository extends JpaRepository { + Page findAllByOrderByIdDesc(Pageable pageable); + + Page findByTenantOrderByIdDesc(String tenant, Pageable pageable); + + List findByTitleContainingOrSummaryContainingOrAuthorContainingOrImagePathContainingAndTenantEquals( + String title, + String summary, + String author, + String imagePath, + String tenant); + + List findByTitleContainingOrSummaryContainingOrAuthorContainingOrImagePathContaining(String title, String summary, String author, String imagePath); +} \ No newline at end of file diff --git a/src/main/java/org/cmh/backend/NewsManagement/service/NewsService.java b/src/main/java/org/cmh/backend/NewsManagement/service/NewsService.java new file mode 100644 index 0000000..43d7c99 --- /dev/null +++ b/src/main/java/org/cmh/backend/NewsManagement/service/NewsService.java @@ -0,0 +1,175 @@ +package org.cmh.backend.NewsManagement.service; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityNotFoundException; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import org.cmh.backend.NewsManagement.dto.NewsRequest; +import org.cmh.backend.NewsManagement.dto.SearchNewsRequest; +import org.cmh.backend.NewsManagement.model.News; +import org.cmh.backend.NewsManagement.repository.NewsRepository; +import org.cmh.backend.Utils.JwtUtil; +import org.cmh.backend.authentication.model.UserHS; +import org.cmh.backend.authentication.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class NewsService { + @Autowired + private NewsRepository newsRepository; + + @Autowired + private UserService userService; + + @PersistenceContext + private EntityManager entityManager; + + public List getNewsByRange(int start, int end, UserHS user) { + if (start < 0 || end <= start) { + throw new IllegalArgumentException("Invalid start or end range"); + } + + int pageSize = end - start; // 计算每页的大小 + int startPageNumber = start / pageSize; // 计算起始页码 + int endPageNumber = (end - 1) / pageSize; // 计算结束页码 + + List result = new ArrayList<>(); + + for (int pageNumber = startPageNumber; pageNumber <= endPageNumber; pageNumber++) { + Pageable pageable = PageRequest.of(pageNumber, pageSize); + Page newsPage = null; + if (user.getSuperAdmin()) { + newsPage = newsRepository.findAllByOrderByIdDesc(pageable); + } else { + newsPage = newsRepository.findByTenantOrderByIdDesc(user.getUsername(), pageable); + } + + if (newsPage.hasContent()) { + result.addAll(newsPage.getContent()); + } else { + break; // 如果没有更多内容,提前退出 + } + } + + int startIndex = start % pageSize; + int endIndex = startIndex + (end - start); + + if (endIndex > result.size()) { + endIndex = result.size(); + } + + return result.subList(startIndex, endIndex); + } + + + public void createNews(NewsRequest request) { + News news = new News(); + news.setTitle(request.getTitle()); + news.setSummary(request.getSummary()); + news.setContent(request.getContent()); + news.setAuthor(request.getAuthor()); + news.setImagePath(request.getImagePath()); + news.setTenant(request.getTenant()); + newsRepository.save(news); + } + + public void updateNews(Long id, NewsRequest request) { + News news = newsRepository.findById(id).orElse(null); + if (news != null) { + news.setTitle(request.getTitle()); + news.setSummary(request.getSummary()); + news.setContent(request.getContent()); + news.setAuthor(request.getAuthor()); + news.setImagePath(request.getImagePath()); + news.setTenant(request.getTenant()); + newsRepository.save(news); + } + } + + public void deleteNews(Long id) { + if (!newsRepository.existsById(id)) { + throw new EntityNotFoundException(); + } + newsRepository.deleteById(id); + } + + public News getNewsById(Long id) { + if (!newsRepository.existsById(id)) { + throw new EntityNotFoundException(); + } + return newsRepository.findById(id).orElse(null); + } + + public long getNewsCount() { + return newsRepository.count(); + } + + // TODO:完善用户权限 +// public List searchNews(SearchNewsRequest request) { +// String username = JwtUtil.extractUsername(request.getToken()); +// UserHS user = userService.getUserByUsername(username); +// if (user.getSuperAdmin()) { +// return newsRepository.findByTitleContainingOrSummaryContainingOrAuthorContainingOrImagePathContaining( +// request.getTitle(), +// request.getSummary(), +// request.getAuthor(), +// request.getImagePath() +// ); +// } +// return newsRepository.findByTitleContainingOrSummaryContainingOrAuthorContainingOrImagePathContainingAndTenantEquals( +// request.getTitle(), +// request.getSummary(), +// request.getAuthor(), +// request.getImagePath(), +// user.getUsername() +// ); +// } + public List searchNews(SearchNewsRequest request) { + String username = JwtUtil.extractUsername(request.getToken()); + UserHS user = userService.getUserByUsername(username); + + // Create a list to hold predicates + List predicates = new ArrayList<>(); + // Create the query + CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(News.class); + Root root = criteriaQuery.from(News.class); + + // Build the query conditionally based on non-empty fields + if (!request.getTitle().isEmpty()) { + predicates.add(criteriaBuilder.like(root.get("title"), "%" + request.getTitle() + "%")); + } + if (!request.getSummary().isEmpty()) { + predicates.add(criteriaBuilder.like(root.get("summary"), "%" + request.getSummary() + "%")); + } + if (!request.getAuthor().isEmpty()) { + predicates.add(criteriaBuilder.like(root.get("author"), "%" + request.getAuthor() + "%")); + } + if (!request.getImagePath().isEmpty()) { + predicates.add(criteriaBuilder.like(root.get("imagePath"), "%" + request.getImagePath() + "%")); + } + + if (user.getSuperAdmin()) { + // Combine predicates with OR + criteriaQuery.where(criteriaBuilder.or(predicates.toArray(new Predicate[0]))); + } else { + // Combine predicates with OR and add tenant condition + Predicate tenantPredicate = criteriaBuilder.equal(root.get("tenant"), user.getUsername()); + predicates.add(tenantPredicate); + criteriaQuery.where(criteriaBuilder.and(predicates.toArray(new Predicate[0]))); + } + + // Execute the query + return entityManager.createQuery(criteriaQuery).getResultList(); + } +}