Skip to content

Commit 226257e

Browse files
authored
Merge pull request #48 from arosha-w/Isira
Added Announcement enpoints, services and controllers
2 parents 46b0968 + aec6b6f commit 226257e

19 files changed

Lines changed: 375 additions & 12 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+
}

src/main/java/com/crimeLink/analyzer/controller/WeaponController.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
import com.crimeLink.analyzer.entity.Weapon;
77
import com.crimeLink.analyzer.service.WeaponService;
88
import lombok.RequiredArgsConstructor;
9+
import org.springframework.http.MediaType;
910
import org.springframework.http.HttpStatus;
1011
import org.springframework.http.ResponseEntity;
1112
import org.springframework.web.bind.annotation.*;
13+
import org.springframework.web.multipart.MultipartFile;
1214

1315
import java.util.HashMap;
1416
import java.util.List;
@@ -94,6 +96,25 @@ public List<Weapon> getWeaponsIssuedToOfficer(@PathVariable Integer officerId) {
9496
return weaponService.getWeaponsIssuedToOfficer(officerId);
9597
}
9698

99+
@PostMapping(value = "/{serialNumber}/upload-photo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
100+
public ResponseEntity<?> uploadWeaponPhoto(
101+
@PathVariable String serialNumber,
102+
@RequestParam("photo") MultipartFile photo) {
103+
try {
104+
String imageUrl = weaponService.uploadWeaponPhoto(serialNumber, photo);
105+
Map<String, Object> response = new HashMap<>();
106+
response.put("serialNumber", serialNumber);
107+
response.put("imageUrl", imageUrl);
108+
return ResponseEntity.ok(response);
109+
} catch (RuntimeException e) {
110+
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
111+
.body(createErrorResponse(e.getMessage()));
112+
} catch (Exception e) {
113+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
114+
.body(createErrorResponse("Failed to upload weapon photo"));
115+
}
116+
}
117+
97118
private Map<String, String> createErrorResponse(String message) {
98119
Map<String, String> response = new HashMap<>();
99120
response.put("error", message);
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+
}

src/main/java/com/crimeLink/analyzer/dto/WeaponAddDTO.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ public class WeaponAddDTO {
77
private String serialNumber;
88
private String weaponType;
99
private String remarks;
10+
private String imageUrl;
1011
}

src/main/java/com/crimeLink/analyzer/dto/WeaponResponseDTO.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class WeaponResponseDTO {
1212
private String weaponType;
1313
private String status;
1414
private String remarks;
15+
private String imageUrl;
1516

1617
// Issue details (if issued)
1718
private OfficerDTO issuedTo;

src/main/java/com/crimeLink/analyzer/dto/WeaponUpdateDTO.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ public class WeaponUpdateDTO {
88
private String weaponType;
99
private WeaponStatus status;
1010
private String remarks;
11+
private String imageUrl;
1112

1213
}
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+
}

src/main/java/com/crimeLink/analyzer/entity/Weapon.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public class Weapon {
3838

3939
private String remarks;
4040

41+
private String imageUrl;
42+
4143
@PrePersist
4244
void onCreate() {
4345
registerDate = LocalDateTime.now();
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+
}

0 commit comments

Comments
 (0)