Files
wenzi/docs/plans/2026-01-26-mosquito-system-implementation-plan.md
Your Name 91a0b77f7a test(cache): 修复CacheConfigTest边界值测试
- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl
- 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE
- 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查
- 所有1266个测试用例通过
- 覆盖率: 指令81.89%, 行88.48%, 分支51.55%

docs: 添加项目状态报告
- 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态
- 包含质量指标、已完成功能、待办事项和技术债务
2026-03-02 13:31:54 +08:00

9.2 KiB
Raw Permalink Blame History

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

// 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

public class IntrospectionResponse {
    private boolean active;
    private String userId;
    private String tenantId;
    private java.util.List<String> roles;
    private java.util.List<String> 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

// 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

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

// 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

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

// 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

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

// 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

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

// 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

// ApiClient: parse ApiResponse<T>, 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

// 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

<MosquitoShareButton :activity-id="1" :user-id="1" />

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

# 手动校对:文档端点与控制器一致

Step 2: Run verification

Run: rg "api/v1/me" "docs/api.md" Expected: path consistent with controllers

Step 3: Apply updates

- 错误响应改为 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

@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

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?