Skip to content

Commit 2919f78

Browse files
Merge branch 'dev' of https://github.com/TranHoan2004/LabVerse into feat/create-login-web
2 parents 2980cff + 16457c5 commit 2919f78

24 files changed

Lines changed: 3070 additions & 2797 deletions

File tree

services/AccountService/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@
8181
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
8282
</dependency>
8383

84+
<!-- OpenFeign Client -->
85+
<dependency>
86+
<groupId>org.springframework.cloud</groupId>
87+
<artifactId>spring-cloud-starter-openfeign</artifactId>
88+
</dependency>
89+
8490
<!-- Validation -->
8591
<dependency>
8692
<groupId>org.springframework.boot</groupId>

services/AccountService/src/main/java/com/se1853_jv/AccountServiceApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
55
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
6+
import org.springframework.cloud.openfeign.EnableFeignClients;
67

78
@SpringBootApplication
89
@EnableDiscoveryClient
10+
@EnableFeignClients
911
public class AccountServiceApplication {
1012

1113
public static void main(String[] args) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.se1853_jv.service;
2+
3+
import com.se1853_jv.dto.response.WrapperApiResponse;
4+
import org.springframework.cloud.openfeign.FeignClient;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
7+
@FeignClient(name = "GROUP-SERVICE", path = "/v1/api/collections")
8+
public interface GroupServiceClient {
9+
10+
@GetMapping("/internal/statistics")
11+
WrapperApiResponse getCollectionStatistics();
12+
}
13+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.se1853_jv.service;
2+
3+
import com.se1853_jv.dto.response.WrapperApiResponse;
4+
import org.springframework.cloud.openfeign.FeignClient;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
7+
@FeignClient(name = "PAPER-SERVICE", path = "/v1/api")
8+
public interface PaperServiceClient {
9+
10+
@GetMapping("/internal/api/papers/statistics")
11+
WrapperApiResponse getPaperStatistics();
12+
}
13+

services/AccountService/src/main/java/com/se1853_jv/service/impl/AdminServiceImpl.java

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,46 @@
1414
import com.se1853_jv.repository.TeamRepository;
1515
import com.se1853_jv.repository.UserRepository;
1616
import com.se1853_jv.service.AdminService;
17+
import com.se1853_jv.service.GroupServiceClient;
18+
import com.se1853_jv.service.PaperServiceClient;
1719
import com.se1853_jv.util.IdEncoder;
20+
import feign.FeignException;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
1823
import org.springframework.beans.factory.annotation.Autowired;
1924
import org.springframework.data.domain.Page;
2025
import org.springframework.data.domain.PageRequest;
2126
import org.springframework.data.domain.Pageable;
2227
import org.springframework.stereotype.Service;
2328
import org.springframework.transaction.annotation.Transactional;
2429

30+
import java.util.Map;
31+
2532
@Service
2633
public class AdminServiceImpl implements AdminService {
34+
35+
private static final Logger log = LoggerFactory.getLogger(AdminServiceImpl.class);
2736

2837
private final UserRepository userRepository;
2938
private final RoleRepository roleRepository;
3039
private final TeamRepository teamRepository;
3140
private final TeamMemberRepository teamMemberRepository;
41+
private final PaperServiceClient paperServiceClient;
42+
private final GroupServiceClient groupServiceClient;
3243

3344
@Autowired
3445
public AdminServiceImpl(UserRepository userRepository,
3546
RoleRepository roleRepository,
3647
TeamRepository teamRepository,
37-
TeamMemberRepository teamMemberRepository) {
48+
TeamMemberRepository teamMemberRepository,
49+
PaperServiceClient paperServiceClient,
50+
GroupServiceClient groupServiceClient) {
3851
this.userRepository = userRepository;
3952
this.roleRepository = roleRepository;
4053
this.teamRepository = teamRepository;
4154
this.teamMemberRepository = teamMemberRepository;
55+
this.paperServiceClient = paperServiceClient;
56+
this.groupServiceClient = groupServiceClient;
4257
}
4358

4459
@Override
@@ -168,10 +183,49 @@ public OverviewStatisticsResponse getOverviewStatistics() {
168183
Long publicTeams = teamRepository.countPublicTeams() != null ? teamRepository.countPublicTeams() : 0L;
169184
Long privateTeams = teamRepository.countPrivateTeams() != null ? teamRepository.countPrivateTeams() : 0L;
170185

171-
// Papers and collections would need to call other services
172-
Long totalPapers = 0L; // TODO: Call PaperService
173-
Long papersThisMonth = 0L; // TODO: Call PaperService
174-
Long totalCollections = 0L; // TODO: Call GroupService
186+
// Get papers statistics from PaperService
187+
Long totalPapers = 0L;
188+
Long papersThisMonth = 0L;
189+
try {
190+
var paperStatsResponse = paperServiceClient.getPaperStatistics();
191+
if (paperStatsResponse != null && paperStatsResponse.getData() != null) {
192+
Object data = paperStatsResponse.getData();
193+
if (data instanceof Map) {
194+
Map<String, Object> statsMap = (Map<String, Object>) data;
195+
if (statsMap.get("totalPapers") instanceof Number) {
196+
totalPapers = ((Number) statsMap.get("totalPapers")).longValue();
197+
}
198+
if (statsMap.get("papersThisMonth") instanceof Number) {
199+
papersThisMonth = ((Number) statsMap.get("papersThisMonth")).longValue();
200+
}
201+
}
202+
}
203+
} catch (FeignException e) {
204+
log.error("Error calling PaperService for statistics: {}", e.getMessage());
205+
} catch (Exception e) {
206+
log.error("Unexpected error getting paper statistics: {}", e.getMessage(), e);
207+
}
208+
209+
// Get collections statistics from GroupService
210+
Long totalCollections = 0L;
211+
try {
212+
var collectionStatsResponse = groupServiceClient.getCollectionStatistics();
213+
if (collectionStatsResponse != null && collectionStatsResponse.getData() != null) {
214+
Object data = collectionStatsResponse.getData();
215+
if (data instanceof Map) {
216+
Map<String, Object> statsMap = (Map<String, Object>) data;
217+
if (statsMap.get("totalCollections") instanceof Number) {
218+
totalCollections = ((Number) statsMap.get("totalCollections")).longValue();
219+
}
220+
}
221+
}
222+
} catch (FeignException e) {
223+
log.error("Error calling GroupService for statistics: {}", e.getMessage());
224+
} catch (Exception e) {
225+
log.error("Unexpected error getting collection statistics: {}", e.getMessage(), e);
226+
}
227+
228+
// Reading lists would need to call ReadingService
175229
Long totalReadingLists = 0L; // TODO: Call ReadingService
176230

177231
return new OverviewStatisticsResponse(

services/AccountService/src/main/resources/application.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
spring:
22
application:
3-
name: account-service
3+
name: ACCOUNT-SERVICE
44
profiles:
55
active: dev
66
jpa:

services/GroupService/src/main/java/com/se1853_jv/controller/CollectionController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.springframework.web.bind.annotation.*;
99
import jakarta.validation.Valid;
1010

11+
import java.util.Map;
12+
1113
@RestController
1214
@RequestMapping("/collections")
1315
@RequiredArgsConstructor
@@ -97,4 +99,10 @@ public ResponseEntity<WrapperApiResponse> recalculatePaperStatus(
9799
collectionService.recalculatePaperStatus(encodedCollectionId, encodedPaperId);
98100
return ResponseEntity.ok(WrapperApiResponse.success("Paper status recalculated successfully"));
99101
}
102+
103+
@GetMapping("/internal/statistics")
104+
public ResponseEntity<WrapperApiResponse> getStatistics() {
105+
long totalCollections = collectionService.getTotalCollectionsCount();
106+
return ResponseEntity.ok(WrapperApiResponse.success(Map.of("totalCollections", totalCollections)));
107+
}
100108
}

services/GroupService/src/main/java/com/se1853_jv/dto/response/CollectionResponse.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class CollectionResponse {
1212
private Long paperCount;
1313
private Long memberCount;
1414
private String creatorName;
15+
private String creatorEmail;
1516
private String creatorAvatarUrl;
1617
private AccessLevel currentUserAccessLevel; // Access level of the current user in this collection
1718

services/GroupService/src/main/java/com/se1853_jv/service/CollectionService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,11 @@ public interface CollectionService {
3737
* This is called after ReadingWorkflow is updated to keep collection status in sync
3838
*/
3939
void recalculatePaperStatus(String encodedCollectionId, String encodedPaperId);
40+
41+
/**
42+
* Get total count of collections
43+
* Used by admin service for statistics
44+
*/
45+
long getTotalCollectionsCount();
4046
}
4147

services/GroupService/src/main/java/com/se1853_jv/service/impl/CollectionServiceImpl.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,47 @@ public CollectionResponse createCollection(CollectionRequest request) {
5454
throw new IllegalArgumentException("User ID is required to create collection");
5555
}
5656

57+
// Verify user has PI role to create collection
58+
String userId = IdEncoder.decode(request.getUserId());
59+
try {
60+
String encodedUserId = IdEncoder.encode(userId);
61+
var userResponse = userServiceClient.getUserById(encodedUserId);
62+
63+
if (userResponse == null || userResponse.getData() == null) {
64+
throw new BadRequestException("User not found");
65+
}
66+
67+
Object data = userResponse.getData();
68+
String userRole = null;
69+
70+
// Handle different response types
71+
if (data instanceof Map) {
72+
Map<String, Object> userMap = (Map<String, Object>) data;
73+
userRole = (String) userMap.get("role");
74+
} else {
75+
// Try to use reflection or handle as UserResponse object
76+
try {
77+
java.lang.reflect.Method getRoleMethod = data.getClass().getMethod("getRole");
78+
Object roleObj = getRoleMethod.invoke(data);
79+
userRole = roleObj != null ? roleObj.toString() : null;
80+
} catch (Exception e) {
81+
log.warn("Cannot extract role from user response: {}", e.getMessage());
82+
}
83+
}
84+
85+
if (userRole == null || !"PI".equalsIgnoreCase(userRole.trim())) {
86+
throw new BadRequestException("Only users with PI role can create collections");
87+
}
88+
} catch (BadRequestException e) {
89+
throw e; // Re-throw BadRequestException as is
90+
} catch (FeignException.NotFound e) {
91+
log.error("User not found when creating collection: {}", userId);
92+
throw new BadRequestException("User not found");
93+
} catch (Exception e) {
94+
log.error("Error verifying user role when creating collection: {}", e.getMessage(), e);
95+
throw new BadRequestException("Unable to verify user permissions");
96+
}
97+
5798
// Kiểm tra trùng tên, nếu trùng thì thêm (1), (2)
5899
String baseName = request.getName().trim();
59100
String newName = baseName;
@@ -72,7 +113,7 @@ public CollectionResponse createCollection(CollectionRequest request) {
72113
Collection saved = collectionRepository.save(entity);
73114

74115
// Automatically add creator as author (isAuthor = true)
75-
String userId = IdEncoder.decode(request.getUserId());
116+
// Note: userId was already decoded above for role verification
76117
CollectionUserId compositeId = new CollectionUserId();
77118
compositeId.setCollectionId(saved.getId());
78119
compositeId.setMemberId(userId);
@@ -461,35 +502,43 @@ private void setCreatorInfo(CollectionResponse response, String collectionId) {
461502
if (data instanceof Map) {
462503
Map<String, Object> userMap = (Map<String, Object>) data;
463504
String fullName = (String) userMap.get("fullName");
505+
String email = (String) userMap.get("email");
464506
String avatarUrl = (String) userMap.get("avatarUrl");
465507
response.setCreatorName(fullName != null ? fullName : null);
508+
response.setCreatorEmail(email != null ? email : null);
466509
response.setCreatorAvatarUrl(avatarUrl != null ? avatarUrl : null);
467510
} else {
468511
// Try to use reflection or handle as UserResponse object
469512
log.warn("Unexpected user data type for creator ID {}: {}", creatorId, data.getClass().getName());
470513
response.setCreatorName(null);
514+
response.setCreatorEmail(null);
471515
response.setCreatorAvatarUrl(null);
472516
}
473517
} else {
474518
response.setCreatorName(null);
519+
response.setCreatorEmail(null);
475520
response.setCreatorAvatarUrl(null);
476521
}
477522
} catch (FeignException.NotFound e) {
478523
log.warn("User not found for creator ID {}: {}", creatorId, e.getMessage());
479524
response.setCreatorName(null);
525+
response.setCreatorEmail(null);
480526
response.setCreatorAvatarUrl(null);
481527
} catch (Exception e) {
482528
log.warn("Error fetching user info for creator ID {}: {}", creatorId, e.getMessage());
483529
response.setCreatorName(null);
530+
response.setCreatorEmail(null);
484531
response.setCreatorAvatarUrl(null);
485532
}
486533
} else {
487534
response.setCreatorName(null);
535+
response.setCreatorEmail(null);
488536
response.setCreatorAvatarUrl(null);
489537
}
490538
} catch (Exception e) {
491539
log.warn("Error setting creator info for collection {}: {}", collectionId, e.getMessage());
492540
response.setCreatorName(null);
541+
response.setCreatorEmail(null);
493542
response.setCreatorAvatarUrl(null);
494543
}
495544
}
@@ -827,6 +876,11 @@ public void recalculatePaperStatus(String encodedCollectionId, String encodedPap
827876
}
828877
}
829878

879+
@Override
880+
public long getTotalCollectionsCount() {
881+
return collectionRepository.count();
882+
}
883+
830884
private void storeToFirestore(CollectionResponse response) {
831885
try {
832886
Map<String, Object> data = new HashMap<>();

0 commit comments

Comments
 (0)