publication added newly

This commit is contained in:
2025-11-06 14:52:45 +05:30
parent 311ca61dea
commit a9cc0a9122
18 changed files with 2118 additions and 21 deletions

View File

@ -0,0 +1,140 @@
package net.shyshkin.study.fullstack.supportportal.backend.resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.shyshkin.study.fullstack.supportportal.backend.domain.HttpResponse;
import net.shyshkin.study.fullstack.supportportal.backend.domain.Publication;
import net.shyshkin.study.fullstack.supportportal.backend.service.PublicationService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/publications")
@RequiredArgsConstructor
@Slf4j
@CrossOrigin
public class PublicationResource {
private final PublicationService publicationService;
@PostMapping
@PreAuthorize("hasAnyAuthority('user:create')")
public ResponseEntity<Publication> addPublication(
@RequestParam String title,
@RequestParam String authors,
@RequestParam Integer year,
@RequestParam String journal,
@RequestParam(required = false) String doi,
@RequestParam(required = false) String category,
@RequestParam(required = false) String abstractText,
@RequestParam(required = false) String publicationDate,
@RequestParam(required = false) String keywords,
@RequestParam(required = false) Integer displayOrder) {
log.info("Adding new publication: {}", title);
Publication publication = publicationService.addPublication(
title, authors, year, journal, doi, category, abstractText, publicationDate, keywords, displayOrder
);
return ResponseEntity.status(HttpStatus.CREATED).body(publication);
}
@PutMapping("/{id}")
@PreAuthorize("hasAnyAuthority('user:update')")
public ResponseEntity<Publication> updatePublication(
@PathVariable Long id,
@RequestParam String title,
@RequestParam String authors,
@RequestParam Integer year,
@RequestParam String journal,
@RequestParam(required = false) String doi,
@RequestParam(required = false) String category,
@RequestParam(required = false) String abstractText,
@RequestParam(required = false) String publicationDate,
@RequestParam(required = false) String keywords,
@RequestParam(required = false) Integer displayOrder) {
log.info("Updating publication with id: {}", id);
Publication publication = publicationService.updatePublication(
id, title, authors, year, journal, doi, category, abstractText, publicationDate, keywords, displayOrder
);
return ResponseEntity.ok(publication);
}
@GetMapping("/active")
public ResponseEntity<List<Publication>> getActivePublications() {
log.info("Getting active publications");
List<Publication> publications = publicationService.getActivePublications();
return ResponseEntity.ok(publications);
}
@GetMapping("/{id}")
public ResponseEntity<Publication> getPublicationById(@PathVariable Long id) {
log.info("Getting publication with id: {}", id);
Publication publication = publicationService.getPublicationById(id);
return ResponseEntity.ok(publication);
}
@GetMapping
@PreAuthorize("hasAnyAuthority('user:read')")
public ResponseEntity<List<Publication>> getAllPublications() {
log.info("Getting all publications");
List<Publication> publications = publicationService.getAllPublications();
return ResponseEntity.ok(publications);
}
@GetMapping("/category/{category}")
public ResponseEntity<List<Publication>> getPublicationsByCategory(@PathVariable String category) {
log.info("Getting publications by category: {}", category);
List<Publication> publications = publicationService.getPublicationsByCategory(category);
return ResponseEntity.ok(publications);
}
@GetMapping("/year/{year}")
public ResponseEntity<List<Publication>> getPublicationsByYear(@PathVariable Integer year) {
log.info("Getting publications by year: {}", year);
List<Publication> publications = publicationService.getPublicationsByYear(year);
return ResponseEntity.ok(publications);
}
@DeleteMapping("/{id}")
@PreAuthorize("hasAnyAuthority('user:delete')")
public ResponseEntity<HttpResponse> deletePublication(@PathVariable Long id) {
log.info("Deleting publication with id: {}", id);
publicationService.deletePublication(id);
return ResponseEntity.ok(
HttpResponse.builder()
.httpStatusCode(HttpStatus.OK.value())
.httpStatus(HttpStatus.OK)
.reason(HttpStatus.OK.getReasonPhrase())
.message("Publication deleted successfully")
.build()
);
}
@PutMapping("/{id}/toggle-active")
@PreAuthorize("hasAnyAuthority('user:update')")
public ResponseEntity<Publication> toggleActiveStatus(@PathVariable Long id) {
log.info("Toggling active status for publication with id: {}", id);
Publication publication = publicationService.toggleActiveStatus(id);
return ResponseEntity.ok(publication);
}
@PutMapping("/reorder")
@PreAuthorize("hasAnyAuthority('user:update')")
public ResponseEntity<HttpResponse> reorderPublications(@RequestBody List<Long> orderedIds) {
log.info("Reordering publications");
publicationService.reorderPublications(orderedIds);
return ResponseEntity.ok(
HttpResponse.builder()
.httpStatusCode(HttpStatus.OK.value())
.httpStatus(HttpStatus.OK)
.reason(HttpStatus.OK.getReasonPhrase())
.message("Publications reordered successfully")
.build()
);
}
}

View File

@ -0,0 +1,74 @@
package net.shyshkin.study.fullstack.supportportal.backend.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "publications")
public class Publication implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, updatable = false)
private Long id;
@Column(columnDefinition = "TEXT", nullable = false)
private String title;
@Column(columnDefinition = "TEXT")
private String authors; // Stored as comma-separated string
@Column(nullable = false)
private Integer year;
@Column(columnDefinition = "TEXT", nullable = false)
private String journal;
@Column(columnDefinition = "TEXT")
private String doi;
@Column(length = 100)
private String category;
@Column(columnDefinition = "LONGTEXT")
private String abstractText;
@Column(length = 100)
private String publicationDate;
@Column(columnDefinition = "TEXT")
private String keywords; // Stored as comma-separated string
@JsonProperty("isActive")
@Column(name = "is_active")
private boolean isActive;
@Column(name = "display_order")
private Integer displayOrder;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
@PrePersist
protected void onCreate() {
createdDate = new Date();
lastModified = new Date();
}
@PreUpdate
protected void onUpdate() {
lastModified = new Date();
}
}

View File

@ -0,0 +1,8 @@
package net.shyshkin.study.fullstack.supportportal.backend.exception.domain;
public class PublicationNotFoundException extends RuntimeException {
public PublicationNotFoundException(String message) {
super(message);
}
}

View File

@ -0,0 +1,22 @@
package net.shyshkin.study.fullstack.supportportal.backend.repository;
import net.shyshkin.study.fullstack.supportportal.backend.domain.Publication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface PublicationRepository extends JpaRepository<Publication, Long> {
@Query("SELECT p FROM Publication p WHERE p.isActive = true ORDER BY p.year DESC, p.displayOrder ASC, p.id DESC")
List<Publication> findByIsActiveTrueOrderByYearDesc();
@Query("SELECT p FROM Publication p ORDER BY p.year DESC, p.displayOrder ASC, p.id DESC")
List<Publication> findAllOrderByYearDesc();
List<Publication> findByCategory(String category);
List<Publication> findByYear(Integer year);
}

View File

@ -0,0 +1,33 @@
package net.shyshkin.study.fullstack.supportportal.backend.service;
import net.shyshkin.study.fullstack.supportportal.backend.domain.Publication;
import java.util.List;
public interface PublicationService {
Publication addPublication(String title, String authors, Integer year, String journal,
String doi, String category, String abstractText,
String publicationDate, String keywords, Integer displayOrder);
Publication updatePublication(Long id, String title, String authors, Integer year,
String journal, String doi, String category,
String abstractText, String publicationDate,
String keywords, Integer displayOrder);
List<Publication> getActivePublications();
Publication getPublicationById(Long id);
List<Publication> getAllPublications();
List<Publication> getPublicationsByCategory(String category);
List<Publication> getPublicationsByYear(Integer year);
void deletePublication(Long id);
Publication toggleActiveStatus(Long id);
void reorderPublications(List<Long> orderedIds);
}

View File

@ -0,0 +1,129 @@
package net.shyshkin.study.fullstack.supportportal.backend.service.impl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.shyshkin.study.fullstack.supportportal.backend.domain.Publication;
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.PublicationNotFoundException;
import net.shyshkin.study.fullstack.supportportal.backend.repository.PublicationRepository;
import net.shyshkin.study.fullstack.supportportal.backend.service.PublicationService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class PublicationServiceImpl implements PublicationService {
private final PublicationRepository publicationRepository;
@Override
public Publication addPublication(String title, String authors, Integer year, String journal,
String doi, String category, String abstractText,
String publicationDate, String keywords, Integer displayOrder) {
log.info("Adding new publication: {}", title);
Publication publication = new Publication();
publication.setTitle(title);
publication.setAuthors(authors);
publication.setYear(year);
publication.setJournal(journal);
publication.setDoi(doi);
publication.setCategory(category);
publication.setAbstractText(abstractText);
publication.setPublicationDate(publicationDate);
publication.setKeywords(keywords);
publication.setDisplayOrder(displayOrder != null ? displayOrder : 0);
publication.setActive(true);
return publicationRepository.save(publication);
}
@Override
public Publication updatePublication(Long id, String title, String authors, Integer year,
String journal, String doi, String category,
String abstractText, String publicationDate,
String keywords, Integer displayOrder) {
log.info("Updating publication with id: {}", id);
Publication publication = getPublicationById(id);
publication.setTitle(title);
publication.setAuthors(authors);
publication.setYear(year);
publication.setJournal(journal);
publication.setDoi(doi);
publication.setCategory(category);
publication.setAbstractText(abstractText);
publication.setPublicationDate(publicationDate);
publication.setKeywords(keywords);
if (displayOrder != null) {
publication.setDisplayOrder(displayOrder);
}
return publicationRepository.save(publication);
}
@Override
@Transactional(readOnly = true)
public List<Publication> getActivePublications() {
return publicationRepository.findByIsActiveTrueOrderByYearDesc();
}
@Override
@Transactional(readOnly = true)
public Publication getPublicationById(Long id) {
return publicationRepository.findById(id)
.orElseThrow(() -> new PublicationNotFoundException("Publication not found with id: " + id));
}
@Override
@Transactional(readOnly = true)
public List<Publication> getAllPublications() {
return publicationRepository.findAllOrderByYearDesc();
}
@Override
@Transactional(readOnly = true)
public List<Publication> getPublicationsByCategory(String category) {
return publicationRepository.findByCategory(category);
}
@Override
@Transactional(readOnly = true)
public List<Publication> getPublicationsByYear(Integer year) {
return publicationRepository.findByYear(year);
}
@Override
public void deletePublication(Long id) {
log.info("Deleting publication with id: {}", id);
Publication publication = getPublicationById(id);
publicationRepository.delete(publication);
}
@Override
public Publication toggleActiveStatus(Long id) {
log.info("Toggling active status for publication with id: {}", id);
Publication publication = getPublicationById(id);
publication.setActive(!publication.isActive());
return publicationRepository.save(publication);
}
@Override
public void reorderPublications(List<Long> orderedIds) {
log.info("Reordering {} publications", orderedIds.size());
for (int i = 0; i < orderedIds.size(); i++) {
Long id = orderedIds.get(i);
Publication publication = getPublicationById(id);
publication.setDisplayOrder(i);
publicationRepository.save(publication);
}
}
}

View File

@ -51,7 +51,7 @@ file:
app:
base-url: ${APP_BASE_URL:http://localhost:8080}
# Fixed public URLs with correct wildcard patterns
public-urls: /user/login,/user/register,/user/*/profile-image,/user/*/profile-image/**,/professors,/professors/**,/api/posts,/api/posts/*,/api/posts/posted,/api/posts/tag/*,/api/posts/tags/count,/api/files/**,/uploads/**,/professor/**,/api/events,/api/events/*,/api/public/**,/api/jobs/active,/api/job-applications/**,/api/job-applications/resume/**,/api/courses/active,/api/courses/*,/api/course-applications,/api/upcoming-events/active,/api/milestones,/api/milestones/**,/api/testimonials,/api/testimonials/**,/hero/image/**,/hero/active/**,/hero/**,/service-tiles/active,/service-tiles/active/**
public-urls: /user/login,/user/register,/user/*/profile-image,/user/*/profile-image/**,/professors,/professors/**,/api/posts,/api/posts/*,/api/posts/posted,/api/posts/tag/*,/api/posts/tags/count,/api/files/**,/uploads/**,/professor/**,/api/events,/api/events/*,/api/public/**,/api/jobs/active,/api/job-applications/**,/api/job-applications/resume/**,/api/courses/active,/api/courses/*,/api/course-applications,/api/upcoming-events/active,/api/milestones,/api/milestones/**,/api/testimonials,/api/testimonials/**,/hero/image/**,/hero/active/**,/hero/**,/service-tiles/active,/service-tiles/active/**,/publications/active/**,/publications/*/**,/publications/category/**,/publications/year/**
cors:
allowed-origins: http://localhost:4200,http://localhost:3000,https://maincmc.rootxwire.com,https://dashboard.cmctrauma.com,https://www.dashboard.cmctrauma.com,https://cmctrauma.com,https://www.cmctrauma.com,https://cmcbackend.rootxwire.com,https://cmcadminfrontend.rootxwire.com
jwt: