mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-22 22:21:09 +08:00
Merge pull request #205 from nagisa77/codex/add-stat-module-for-dau-tracking
Add DAU stats filter and endpoint
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.openisle.config;
|
||||
|
||||
import com.openisle.service.JwtService;
|
||||
import com.openisle.service.UserVisitService;
|
||||
import com.openisle.repository.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -38,6 +39,7 @@ public class SecurityConfig {
|
||||
private final JwtService jwtService;
|
||||
private final UserRepository userRepository;
|
||||
private final AccessDeniedHandler customAccessDeniedHandler;
|
||||
private final UserVisitService userVisitService;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
@@ -103,7 +105,8 @@ public class SecurityConfig {
|
||||
.requestMatchers("/api/admin/**").hasAuthority("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterAfter(userVisitFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
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> {
|
||||
Optional<UserVisit> findByUserAndVisitDate(User user, LocalDate visitDate);
|
||||
long countByUser(User user);
|
||||
long countByVisitDate(LocalDate visitDate);
|
||||
}
|
||||
|
||||
@@ -32,4 +32,9 @@ public class UserVisitService {
|
||||
.orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found"));
|
||||
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.service.JwtService;
|
||||
import com.openisle.repository.UserRepository;
|
||||
import com.openisle.service.UserVisitService;
|
||||
import com.openisle.model.Role;
|
||||
import com.openisle.model.User;
|
||||
import java.util.Optional;
|
||||
@@ -31,6 +32,8 @@ class AdminControllerTest {
|
||||
private JwtService jwtService;
|
||||
@MockBean
|
||||
private UserRepository userRepository;
|
||||
@MockBean
|
||||
private UserVisitService userVisitService;
|
||||
|
||||
@Test
|
||||
void adminHelloReturnsMessage() throws Exception {
|
||||
|
||||
@@ -11,6 +11,7 @@ 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 java.util.Optional;
|
||||
@@ -31,6 +32,8 @@ class HelloControllerTest {
|
||||
private JwtService jwtService;
|
||||
@MockBean
|
||||
private UserRepository userRepository;
|
||||
@MockBean
|
||||
private UserVisitService userVisitService;
|
||||
|
||||
@Test
|
||||
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