Skip to content

Commit ef67bf5

Browse files
committed
Added Announcement enpoints, services and controllers
1 parent 46b0968 commit ef67bf5

10 files changed

Lines changed: 299 additions & 8 deletions

src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
5151
.requestMatchers("/api/auth/**").permitAll()
5252
.requestMatchers("/api/health").permitAll()
5353
.requestMatchers("/api/admin/health").permitAll()
54-
.requestMatchers("/api/facial/health").permitAll() // ML service health check
55-
.requestMatchers("/api/call-analysis/health").permitAll() // ML service health check
56-
54+
.requestMatchers("/api/facial/health").permitAll() // ML service health check
55+
.requestMatchers("/api/call-analysis/health").permitAll() // ML service health check
56+
5757
// ML Service endpoints
5858
.requestMatchers("/api/call-analysis/**").hasRole("Investigator")
5959
.requestMatchers("/api/facial/register").hasAnyRole("Investigator", "OIC")
@@ -63,7 +63,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
6363
// Criminal CRUD (direct DB, no Python)
6464
.requestMatchers("/api/criminals/**").hasAnyRole("Investigator", "OIC")
6565
.requestMatchers("/api/criminals").hasAnyRole("Investigator", "OIC")
66-
66+
6767
.requestMatchers("/api/database/**").permitAll()
6868
.requestMatchers("/api/test").permitAll()
6969
.requestMatchers("/api/debug/**").permitAll() // 🔍 Debug endpoints
@@ -90,9 +90,14 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
9090

9191
// Admin/OIC/Investigator routes (officer data, locations, users)
9292
.requestMatchers("/api/users/field-officers").hasAnyRole("Admin", "OIC", "Investigator")
93-
.requestMatchers("/api/admin/officers/*/locations/**").hasAnyRole("Admin", "OIC", "Investigator")
93+
.requestMatchers("/api/admin/officers/*/locations/**")
94+
.hasAnyRole("Admin", "OIC", "Investigator")
9495
.requestMatchers("/api/admin/**").hasAnyRole("OIC", "Admin")
9596

97+
// Officer announcements
98+
.requestMatchers("/api/announcements").hasAnyRole("OIC", "FieldOfficer")
99+
.requestMatchers("/api/announcements/**").hasRole("OIC")
100+
96101
.anyRequest().authenticated())
97102
.exceptionHandling(exception -> exception
98103
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
@@ -107,7 +112,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
107112
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
108113

109114
return http.build();
110-
}
115+
}
111116

112117
@Bean
113118
public AuthenticationProvider authenticationProvider() {
@@ -136,8 +141,7 @@ public CorsConfigurationSource corsConfigurationSource() {
136141
configuration.setAllowedOriginPatterns(List.of("*"));
137142
} else {
138143
configuration.setAllowedOrigins(
139-
Arrays.asList(corsAllowedOrigins.split(","))
140-
);
144+
Arrays.asList(corsAllowedOrigins.split(",")));
141145
}
142146
configuration.setAllowedMethods(Arrays.asList(
143147
"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.crimeLink.analyzer.controller;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.DeleteMapping;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
import org.springframework.web.bind.annotation.PatchMapping;
11+
import org.springframework.web.bind.annotation.PathVariable;
12+
import org.springframework.web.bind.annotation.PostMapping;
13+
import org.springframework.web.bind.annotation.PutMapping;
14+
import org.springframework.web.bind.annotation.RequestBody;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.RestController;
17+
18+
import com.crimeLink.analyzer.entity.OfficerAnnouncement;
19+
import com.crimeLink.analyzer.enums.AnnouncementStatus;
20+
import com.crimeLink.analyzer.service.OfficerAnnouncementService;
21+
22+
import lombok.RequiredArgsConstructor;
23+
24+
@RestController
25+
@RequestMapping("/api/announcements")
26+
@RequiredArgsConstructor
27+
public class OfficerAnnouncementController {
28+
private final OfficerAnnouncementService announcementService;
29+
30+
@PostMapping
31+
public ResponseEntity<OfficerAnnouncement> createAnnouncement(
32+
@RequestBody OfficerAnnouncement announcement) {
33+
OfficerAnnouncement created = announcementService.createAnnouncement(announcement);
34+
return ResponseEntity.status(HttpStatus.CREATED).body(created);
35+
}
36+
37+
@GetMapping
38+
public ResponseEntity<List<OfficerAnnouncement>> getAllAnnouncements() {
39+
return ResponseEntity.ok(announcementService.getAllAnnouncements());
40+
}
41+
42+
@GetMapping("/{id}")
43+
public ResponseEntity<OfficerAnnouncement> getAnnouncementById(@PathVariable Long id) {
44+
return ResponseEntity.ok(announcementService.getAnnouncementById(id));
45+
}
46+
47+
@PutMapping("/{id}")
48+
public ResponseEntity<OfficerAnnouncement> updateAnnouncement(
49+
@PathVariable Long id,
50+
@RequestBody OfficerAnnouncement updatedData) {
51+
return ResponseEntity.ok(announcementService.updateAnnouncement(id, updatedData));
52+
}
53+
54+
@PatchMapping("/{id}/status")
55+
public ResponseEntity<OfficerAnnouncement> changeStatus(
56+
@PathVariable Long id,
57+
@RequestBody Map<String, String> body) {
58+
String statusValue = body.get("status");
59+
if (statusValue == null || statusValue.isBlank()) {
60+
return ResponseEntity.badRequest().build();
61+
}
62+
try {
63+
AnnouncementStatus newStatus = AnnouncementStatus.valueOf(statusValue.toUpperCase());
64+
return ResponseEntity.ok(announcementService.changeStatus(id, newStatus));
65+
} catch (IllegalArgumentException e) {
66+
return ResponseEntity.badRequest().build();
67+
}
68+
}
69+
70+
@DeleteMapping("/{id}")
71+
public ResponseEntity<Void> deleteAnnouncement(@PathVariable Long id) {
72+
announcementService.deleteAnnouncement(id);
73+
return ResponseEntity.noContent().build();
74+
}
75+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.crimeLink.analyzer.dto;
2+
3+
import java.util.Date;
4+
5+
import com.crimeLink.analyzer.enums.AnnouncementStatus;
6+
import com.crimeLink.analyzer.enums.AnnouncementTags;
7+
8+
import lombok.AllArgsConstructor;
9+
import lombok.Data;
10+
import lombok.NoArgsConstructor;
11+
12+
@Data
13+
@AllArgsConstructor
14+
@NoArgsConstructor
15+
public class OfficerAnnouncementDTO {
16+
private Long id;
17+
private String title;
18+
private String message;
19+
private Date date;
20+
private AnnouncementTags tag;
21+
private AnnouncementStatus status;
22+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.crimeLink.analyzer.entity;
2+
3+
import java.util.Date;
4+
5+
import com.crimeLink.analyzer.enums.AnnouncementStatus;
6+
import com.crimeLink.analyzer.enums.AnnouncementTags;
7+
8+
import jakarta.persistence.Column;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.EnumType;
11+
import jakarta.persistence.Enumerated;
12+
import jakarta.persistence.GeneratedValue;
13+
import jakarta.persistence.GenerationType;
14+
import jakarta.persistence.Id;
15+
import jakarta.persistence.Table;
16+
import jakarta.persistence.Temporal;
17+
import jakarta.persistence.TemporalType;
18+
import lombok.AllArgsConstructor;
19+
import lombok.Data;
20+
import lombok.NoArgsConstructor;
21+
22+
@Entity
23+
@Data
24+
@Table(name = "officer_announcement")
25+
@AllArgsConstructor
26+
@NoArgsConstructor
27+
public class OfficerAnnouncement {
28+
29+
@Id
30+
@GeneratedValue(strategy = GenerationType.IDENTITY)
31+
private Long id;
32+
33+
@Column(nullable = false)
34+
private String title;
35+
36+
@Column(nullable = false, columnDefinition = "TEXT")
37+
private String message;
38+
39+
@Temporal(TemporalType.TIMESTAMP)
40+
@Column(nullable = false, updatable = false)
41+
private Date date;
42+
43+
@Enumerated(EnumType.STRING)
44+
@Column(nullable = false)
45+
private AnnouncementTags tag;
46+
47+
@Enumerated(EnumType.STRING)
48+
@Column(nullable = false)
49+
private AnnouncementStatus status;
50+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.crimeLink.analyzer.enums;
2+
3+
public enum AnnouncementStatus {
4+
ACTIVE, INACTIVE, ARCHIVED
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.crimeLink.analyzer.enums;
2+
3+
public enum AnnouncementTags {
4+
GENERAL, ALERT, UPDATE, EVENT
5+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.crimeLink.analyzer.mapper;
2+
3+
import com.crimeLink.analyzer.dto.OfficerAnnouncementDTO;
4+
import com.crimeLink.analyzer.entity.OfficerAnnouncement;
5+
6+
public class OfficerAnnouncementMapper {
7+
public static OfficerAnnouncementDTO mapToDTO(OfficerAnnouncement entity) {
8+
if (entity == null) {
9+
return null;
10+
}
11+
OfficerAnnouncementDTO dto = new OfficerAnnouncementDTO();
12+
dto.setId(entity.getId());
13+
dto.setTitle(entity.getTitle());
14+
dto.setMessage(entity.getMessage());
15+
dto.setDate(entity.getDate());
16+
dto.setTag(entity.getTag());
17+
dto.setStatus(entity.getStatus());
18+
return dto;
19+
}
20+
21+
public static OfficerAnnouncement mapToEntity(OfficerAnnouncementDTO dto) {
22+
if (dto == null) {
23+
return null;
24+
}
25+
OfficerAnnouncement entity = new OfficerAnnouncement();
26+
entity.setId(dto.getId());
27+
entity.setTitle(dto.getTitle());
28+
entity.setMessage(dto.getMessage());
29+
entity.setDate(dto.getDate());
30+
entity.setTag(dto.getTag());
31+
entity.setStatus(dto.getStatus());
32+
return entity;
33+
}
34+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.crimeLink.analyzer.repository;
2+
3+
import java.util.List;
4+
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.stereotype.Repository;
7+
8+
import com.crimeLink.analyzer.entity.OfficerAnnouncement;
9+
import com.crimeLink.analyzer.enums.AnnouncementStatus;
10+
import com.crimeLink.analyzer.enums.AnnouncementTags;
11+
12+
@Repository
13+
public interface OfficerAnnouncementRepository extends JpaRepository<OfficerAnnouncement, Long> {
14+
List<OfficerAnnouncement> findByStatus(AnnouncementStatus status);
15+
16+
List<OfficerAnnouncement> findByTag(AnnouncementTags tag);
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.crimeLink.analyzer.service;
2+
3+
import java.util.List;
4+
5+
import com.crimeLink.analyzer.entity.OfficerAnnouncement;
6+
import com.crimeLink.analyzer.enums.AnnouncementStatus;
7+
8+
public interface OfficerAnnouncementService {
9+
public OfficerAnnouncement createAnnouncement(OfficerAnnouncement announcement);
10+
11+
public List<OfficerAnnouncement> getAllAnnouncements();
12+
13+
public OfficerAnnouncement getAnnouncementById(Long id);
14+
15+
public OfficerAnnouncement updateAnnouncement(Long id, OfficerAnnouncement announcement);
16+
17+
public OfficerAnnouncement changeStatus(Long id, AnnouncementStatus status);
18+
19+
public void deleteAnnouncement(Long id);
20+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.crimeLink.analyzer.service.impl;
2+
3+
import java.util.Date;
4+
import java.util.List;
5+
6+
import org.springframework.stereotype.Service;
7+
8+
import com.crimeLink.analyzer.entity.OfficerAnnouncement;
9+
import com.crimeLink.analyzer.enums.AnnouncementStatus;
10+
import com.crimeLink.analyzer.repository.OfficerAnnouncementRepository;
11+
import com.crimeLink.analyzer.service.OfficerAnnouncementService;
12+
13+
import lombok.RequiredArgsConstructor;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class OfficerAnnouncementServiceImpl implements OfficerAnnouncementService {
18+
19+
private final OfficerAnnouncementRepository announcementRepository;
20+
21+
public OfficerAnnouncement createAnnouncement(OfficerAnnouncement announcement) {
22+
announcement.setId(null);
23+
announcement.setDate(new Date());
24+
if (announcement.getStatus() == null) {
25+
announcement.setStatus(AnnouncementStatus.ACTIVE);
26+
}
27+
return announcementRepository.save(announcement);
28+
}
29+
30+
public List<OfficerAnnouncement> getAllAnnouncements() {
31+
return announcementRepository.findAll();
32+
}
33+
34+
public OfficerAnnouncement getAnnouncementById(Long id) {
35+
return announcementRepository.findById(id).orElseThrow(() -> new RuntimeException("Announcement not found"));
36+
}
37+
38+
public OfficerAnnouncement updateAnnouncement(Long id, OfficerAnnouncement announcement) {
39+
OfficerAnnouncement existing = getAnnouncementById(id);
40+
existing.setTitle(announcement.getTitle());
41+
existing.setMessage(announcement.getMessage());
42+
existing.setTag(announcement.getTag());
43+
existing.setStatus(announcement.getStatus());
44+
return announcementRepository.save(existing);
45+
}
46+
47+
public OfficerAnnouncement changeStatus(Long id, AnnouncementStatus status) {
48+
OfficerAnnouncement existing = getAnnouncementById(id);
49+
existing.setStatus(status);
50+
return announcementRepository.save(existing);
51+
}
52+
53+
public void deleteAnnouncement(Long id) {
54+
if(!announcementRepository.existsById(id)) {
55+
throw new RuntimeException("Announcement not found");
56+
}
57+
announcementRepository.deleteById(id);
58+
}
59+
}

0 commit comments

Comments
 (0)