mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-05-08 11:47:28 +08:00
Add DAU statistics module
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.openisle.config;
|
package com.openisle.config;
|
||||||
|
|
||||||
import com.openisle.service.JwtService;
|
import com.openisle.service.JwtService;
|
||||||
|
import com.openisle.service.UserVisitService;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -38,6 +39,7 @@ public class SecurityConfig {
|
|||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final AccessDeniedHandler customAccessDeniedHandler;
|
private final AccessDeniedHandler customAccessDeniedHandler;
|
||||||
|
private final UserVisitService userVisitService;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
@@ -103,7 +105,8 @@ public class SecurityConfig {
|
|||||||
.requestMatchers("/api/admin/**").hasAuthority("ADMIN")
|
.requestMatchers("/api/admin/**").hasAuthority("ADMIN")
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||||
|
.addFilterAfter(userVisitFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,4 +153,18 @@ public class SecurityConfig {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OncePerRequestFilter userVisitFilter() {
|
||||||
|
return new OncePerRequestFilter() {
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
var auth = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (auth != null && auth.isAuthenticated() && !(auth instanceof org.springframework.security.authentication.AnonymousAuthenticationToken)) {
|
||||||
|
userVisitService.recordVisit(auth.getName());
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/main/java/com/openisle/controller/StatController.java
Normal file
26
src/main/java/com/openisle/controller/StatController.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.openisle.controller;
|
||||||
|
|
||||||
|
import com.openisle.service.UserVisitService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/stats")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StatController {
|
||||||
|
private final UserVisitService userVisitService;
|
||||||
|
|
||||||
|
@GetMapping("/dau")
|
||||||
|
public Map<String, Long> dau(@RequestParam(value = "date", required = false)
|
||||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {
|
||||||
|
long count = userVisitService.countDau(date);
|
||||||
|
return Map.of("dau", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,4 +10,5 @@ import java.util.Optional;
|
|||||||
public interface UserVisitRepository extends JpaRepository<UserVisit, Long> {
|
public interface UserVisitRepository extends JpaRepository<UserVisit, Long> {
|
||||||
Optional<UserVisit> findByUserAndVisitDate(User user, LocalDate visitDate);
|
Optional<UserVisit> findByUserAndVisitDate(User user, LocalDate visitDate);
|
||||||
long countByUser(User user);
|
long countByUser(User user);
|
||||||
|
long countByVisitDate(LocalDate visitDate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,4 +32,9 @@ public class UserVisitService {
|
|||||||
.orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found"));
|
.orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found"));
|
||||||
return userVisitRepository.countByUser(user);
|
return userVisitRepository.countByUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long countDau(LocalDate date) {
|
||||||
|
LocalDate d = date != null ? date : LocalDate.now();
|
||||||
|
return userVisitRepository.countByVisitDate(d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.openisle.config.CustomAccessDeniedHandler;
|
|||||||
import com.openisle.config.SecurityConfig;
|
import com.openisle.config.SecurityConfig;
|
||||||
import com.openisle.service.JwtService;
|
import com.openisle.service.JwtService;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
|
import com.openisle.service.UserVisitService;
|
||||||
import com.openisle.model.Role;
|
import com.openisle.model.Role;
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -31,6 +32,8 @@ class AdminControllerTest {
|
|||||||
private JwtService jwtService;
|
private JwtService jwtService;
|
||||||
@MockBean
|
@MockBean
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
@MockBean
|
||||||
|
private UserVisitService userVisitService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void adminHelloReturnsMessage() throws Exception {
|
void adminHelloReturnsMessage() throws Exception {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.openisle.config.CustomAccessDeniedHandler;
|
|||||||
import com.openisle.config.SecurityConfig;
|
import com.openisle.config.SecurityConfig;
|
||||||
import com.openisle.service.JwtService;
|
import com.openisle.service.JwtService;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
|
import com.openisle.service.UserVisitService;
|
||||||
import com.openisle.model.Role;
|
import com.openisle.model.Role;
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -31,6 +32,8 @@ class HelloControllerTest {
|
|||||||
private JwtService jwtService;
|
private JwtService jwtService;
|
||||||
@MockBean
|
@MockBean
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
@MockBean
|
||||||
|
private UserVisitService userVisitService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void helloReturnsMessage() throws Exception {
|
void helloReturnsMessage() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.openisle.controller;
|
||||||
|
|
||||||
|
import com.openisle.config.CustomAccessDeniedHandler;
|
||||||
|
import com.openisle.config.SecurityConfig;
|
||||||
|
import com.openisle.service.JwtService;
|
||||||
|
import com.openisle.repository.UserRepository;
|
||||||
|
import com.openisle.service.UserVisitService;
|
||||||
|
import com.openisle.model.Role;
|
||||||
|
import com.openisle.model.User;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
@WebMvcTest(StatController.class)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@Import({SecurityConfig.class, CustomAccessDeniedHandler.class})
|
||||||
|
class StatControllerTest {
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private JwtService jwtService;
|
||||||
|
@MockBean
|
||||||
|
private UserRepository userRepository;
|
||||||
|
@MockBean
|
||||||
|
private UserVisitService userVisitService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dauReturnsCount() throws Exception {
|
||||||
|
Mockito.when(jwtService.validateAndGetSubject("token")).thenReturn("user");
|
||||||
|
User user = new User();
|
||||||
|
user.setUsername("user");
|
||||||
|
user.setPassword("p");
|
||||||
|
user.setEmail("u@example.com");
|
||||||
|
user.setRole(Role.USER);
|
||||||
|
Mockito.when(userRepository.findByUsername("user")).thenReturn(Optional.of(user));
|
||||||
|
Mockito.when(userVisitService.countDau(Mockito.any())).thenReturn(3L);
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/stats/dau").header("Authorization", "Bearer token"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.dau").value(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user