# Mosquito System Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 统一鉴权与响应契约,补齐三端前端工程骨架,并让前后端可在同一契约下联调。 **Architecture:** 在后端引入 introspection 校验与缓存,统一 API 响应为 `ApiResponse`,并将鉴权策略按路由分层。前端三端共享组件库与 Design Tokens,使用一致的 API Client 与错误处理。 **Tech Stack:** Spring Boot 3, Java 17, Redis, Vite, Vue 3, TypeScript, Pinia, Vue Router, Tailwind CSS --- > 注意:根据项目指令,本计划不包含 git commit 步骤。 ### Task 1: 定义并落地 introspection 协议与缓存结构 **Files:** - Create: `src/main/java/com/mosquito/project/security/IntrospectionRequest.java` - Create: `src/main/java/com/mosquito/project/security/IntrospectionResponse.java` - Create: `src/main/java/com/mosquito/project/security/UserIntrospectionService.java` - Modify: `src/main/java/com/mosquito/project/config/AppConfig.java` - Modify: `src/main/resources/application.properties` **Step 1: Write the failing test** ```java // src/test/java/com/mosquito/project/security/UserIntrospectionServiceTest.java @Test void shouldReturnInactive_whenTokenInvalid() { UserIntrospectionService service = buildServiceWithMockResponse(false); var result = service.introspect("bad-token"); assertFalse(result.isActive()); } ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=UserIntrospectionServiceTest test` Expected: FAIL (class not found) **Step 3: Write minimal implementation** ```java public class IntrospectionResponse { private boolean active; private String userId; private String tenantId; private java.util.List roles; private java.util.List scopes; private long exp; private long iat; private String jti; // getters/setters } ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=UserIntrospectionServiceTest test` Expected: PASS ### Task 2: 实现 API Key + 用户态双重鉴权拦截器 **Files:** - Create: `src/main/java/com/mosquito/project/web/UserAuthInterceptor.java` - Modify: `src/main/java/com/mosquito/project/config/WebMvcConfig.java` - Modify: `src/main/java/com/mosquito/project/web/ApiKeyAuthInterceptor.java` **Step 1: Write the failing test** ```java // src/test/java/com/mosquito/project/web/UserAuthInterceptorTest.java @Test void shouldRejectRequest_whenMissingAuthorization() { var request = mockRequestWithoutAuth(); var response = new MockHttpServletResponse(); var result = interceptor.preHandle(request, response, new Object()); assertFalse(result); assertEquals(401, response.getStatus()); } ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=UserAuthInterceptorTest test` Expected: FAIL (class not found) **Step 3: Write minimal implementation** ```java public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } // call UserIntrospectionService return true; } ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=UserAuthInterceptorTest test` Expected: PASS ### Task 3: 路由分层鉴权策略 **Files:** - Modify: `src/main/java/com/mosquito/project/config/WebMvcConfig.java` **Step 1: Write the failing test** ```java // src/test/java/com/mosquito/project/config/WebMvcConfigTest.java @Test void shouldProtectMeEndpoints_withApiKeyAndUserAuth() { // verify interceptors order and path patterns } ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=WebMvcConfigTest test` Expected: FAIL **Step 3: Write minimal implementation** ```java registry.addInterceptor(apiKeyAuthInterceptor).addPathPatterns("/api/**"); registry.addInterceptor(userAuthInterceptor).addPathPatterns("/api/v1/me/**", "/api/v1/activities/**", "/api/v1/api-keys/**", "/api/v1/share/**"); registry.addInterceptor(apiKeyAuthInterceptor).excludePathPatterns("/r/**", "/actuator/**"); ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=WebMvcConfigTest test` Expected: PASS ### Task 4: 统一 API 响应为 ApiResponse **Files:** - Modify: `src/main/java/com/mosquito/project/controller/ActivityController.java` - Modify: `src/main/java/com/mosquito/project/controller/ApiKeyController.java` - Modify: `src/main/java/com/mosquito/project/controller/UserExperienceController.java` - Modify: `src/main/java/com/mosquito/project/controller/ShareTrackingController.java` - Modify: `src/main/java/com/mosquito/project/exception/GlobalExceptionHandler.java` **Step 1: Write the failing test** ```java // src/test/java/com/mosquito/project/controller/ActivityControllerContractTest.java @Test void shouldReturnApiResponseEnvelope() throws Exception { mockMvc.perform(get("/api/v1/activities/1")) .andExpect(jsonPath("$.code").value(200)) .andExpect(jsonPath("$.data").exists()); } ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=ActivityControllerContractTest test` Expected: FAIL **Step 3: Write minimal implementation** ```java return ResponseEntity.ok(ApiResponse.success(activity)); ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=ActivityControllerContractTest test` Expected: PASS ### Task 5: 排行榜分页与元数据 **Files:** - Modify: `src/main/java/com/mosquito/project/controller/ActivityController.java` - Modify: `src/main/java/com/mosquito/project/service/ActivityService.java` - Modify: `src/main/java/com/mosquito/project/persistence/repository/ActivityRepository.java` - Modify: `src/test/java/com/mosquito/project/controller/ActivityStatsAndGraphControllerTest.java` **Step 1: Write the failing test** ```java // add pagination meta assertion .andExpect(jsonPath("$.meta.pagination.total").value(3)) ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=ActivityStatsAndGraphControllerTest test` Expected: FAIL **Step 3: Write minimal implementation** ```java var data = list.subList(from, to); return ResponseEntity.ok(ApiResponse.paginated(data, page, size, list.size())); ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=ActivityStatsAndGraphControllerTest test` Expected: PASS ### Task 6: 更新 Java SDK 与前端 API Client **Files:** - Modify: `src/main/java/com/mosquito/project/sdk/ApiClient.java` - Modify: `src/main/java/com/mosquito/project/sdk/MosquitoClient.java` - Modify: `frontend/index.ts` - Modify: `frontend/components/MosquitoLeaderboard.vue` **Step 1: Write the failing test** ```java // src/test/java/com/mosquito/project/sdk/ApiClientTest.java @Test void shouldUnwrapApiResponse() { // response: { code: 200, data: {...} } } ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=ApiClientTest test` Expected: FAIL **Step 3: Write minimal implementation** ```java // ApiClient: parse ApiResponse, return data field ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=ApiClientTest test` Expected: PASS ### Task 7: H5 与管理端基础页面接通组件库 **Files:** - Create: `frontend/h5/src/views/ShareView.vue` - Create: `frontend/admin/src/views/ActivityListView.vue` - Modify: `frontend/h5/src/router/index.ts` - Modify: `frontend/admin/src/router/index.ts` **Step 1: Write the failing test** ```js // frontend/h5/src/tests/appRoutes.test.ts it('should render share page', () => { // mount router and assert route }) ``` **Step 2: Run test to verify it fails** Run: `npm --prefix "frontend/h5" run type-check` Expected: FAIL **Step 3: Write minimal implementation** ```vue ``` **Step 4: Run test to verify it passes** Run: `npm --prefix "frontend/h5" run type-check` Expected: PASS ### Task 8: 更新 API 文档与对外契约 **Files:** - Modify: `docs/api.md` - Modify: `README.md` **Step 1: Write the failing test** ```text # 手动校对:文档端点与控制器一致 ``` **Step 2: Run verification** Run: `rg "api/v1/me" "docs/api.md"` Expected: path consistent with controllers **Step 3: Apply updates** ```text - 错误响应改为 ApiResponse - /api/v1/me/poster -> /api/v1/me/poster/image|html|config ``` ### Task 9: 安全与配置校验 **Files:** - Modify: `src/main/java/com/mosquito/project/service/ApiKeyEncryptionService.java` - Modify: `src/main/resources/application-prod.yml` - Modify: `src/main/java/com/mosquito/project/config/CacheConfig.java` **Step 1: Write the failing test** ```java @Test void shouldFailStartup_whenEncryptionKeyDefault() { // assert illegal state } ``` **Step 2: Run test to verify it fails** Run: `mvn -Dtest=ApiKeyEncryptionServiceTest test` Expected: FAIL **Step 3: Write minimal implementation** ```java if (isDefaultKey(encryptionKey)) { throw new IllegalStateException("Encryption key must be set in production"); } ``` **Step 4: Run test to verify it passes** Run: `mvn -Dtest=ApiKeyEncryptionServiceTest test` Expected: PASS --- Plan complete and saved to `docs/plans/2026-01-26-mosquito-system-implementation-plan.md`. Two execution options: 1. Subagent-Driven (this session) 2. Parallel Session (separate) Which approach?