Files
wenzi/docs/DEVELOPMENT_GUIDE.md

18 KiB
Raw Blame History

🛠️ 开发指南

版本: 1.0 更新时间: 2026-03-04

📋 目录

  1. 开发环境搭建
  2. 项目结构
  3. 开发规范
  4. 测试指南
  5. 调试技巧
  6. 贡献指南
  7. 常见问题

🚀 开发环境搭建

前置要求

工具 版本 说明
JDK 17+ 推荐使用OpenJDK 17或21
Maven 3.8+ 构建工具
PostgreSQL 12+ 数据库
Redis 6.0+ 缓存(可选,开发环境可用内存模式)
Git 2.30+ 版本控制
IDE - 推荐IntelliJ IDEA或VS Code

1. 克隆项目

git clone https://github.com/your-org/mosquito.git
cd mosquito

2. 安装依赖

# 安装PostgreSQLUbuntu/Debian
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

# 安装Redis可选
sudo apt-get install redis-server

# 或使用Docker
docker run -d --name mosquito-postgres -e POSTGRES_PASSWORD=dev_password -p 5432:5432 postgres:15
docker run -d --name mosquito-redis -p 6379:6379 redis:7-alpine

3. 配置数据库

# 创建数据库
sudo -u postgres psql -c "CREATE DATABASE mosquito_dev;"
sudo -u postgres psql -c "CREATE USER mosquito WITH PASSWORD 'dev_password';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE mosquito_dev TO mosquito;"

4. 配置开发环境

复制配置模板:

cp src/main/resources/application-dev.yml.example src/main/resources/application-dev.yml

编辑 application-dev.yml

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mosquito_dev
    username: mosquito
    password: dev_password

  data:
    redis:
      host: localhost
      port: 6379

app:
  api-key:
    encryption-key: dev_32_char_encryption_key_12

5. 构建项目

# 编译项目
mvn clean compile

# 运行测试
mvn test

# 打包
mvn package

6. 启动应用

# 使用Maven
mvn spring-boot:run -Dspring-boot.run.profiles=dev

# 或使用JAR
java -jar target/mosquito-1.0.0.jar --spring.profiles.active=dev

访问 http://localhost:8080/actuator/health 验证启动成功。

7. 工作区定期清理

为避免构建产物和测试产物污染仓库,建议在本地定期执行以下命令:

# 只检查(发现 target/frontend/*/dist 或测试产物即返回非零)
npm run clean:workspace:check

# 归档清理(将产物移动到 /tmp/mosquito-archives/<tag>
npm run clean:workspace:apply

# 历史日志归档(保留最近 1 天)
npm run logs:health:check
npm run logs:archive:check
npm run logs:archive:apply
npm run logs:archive:index

说明:

  • clean:workspace:check 对应 scripts/ci/clean-artifacts.sh --include-build-outputs --fail-on-found
  • clean:workspace:apply 默认使用归档模式,不直接删除文件,便于回溯。
  • logs:health:check 对应 scripts/ci/logs-health-check.sh,用于输出日志体积和历史候选文件数量(仅告警,不阻断)。
  • logs:archive:* 对应 scripts/ci/archive-logs.sh,将按时间戳命名的历史运行日志移动到 logs/archive/<tag>/
  • logs:archive:index 对应 scripts/ci/update-log-archive-index.sh,用于刷新 logs/archive/README.md 索引。
  • 若本地正在运行 spring-boot:runvitescripts/e2e_continuous_runner.shtargetfrontend/e2e/* 可能被立即重建。建议先停止后台进程再执行清理。

8. IDE配置

IntelliJ IDEA

  1. 导入项目:File > Open 选择项目根目录
  2. 配置JDKFile > Project Structure > Project SDK 选择JDK 17
  3. 启用注解处理:Settings > Build > Compiler > Annotation Processors 勾选 Enable annotation processing
  4. 配置运行配置:
    • Run > Edit Configurations
    • 添加 Spring Boot 配置
    • Main class: com.mosquito.project.MosquitoApplication
    • Active profiles: dev

VS Code

安装扩展:

  • Extension Pack for Java
  • Spring Boot Extension Pack
  • Lombok Annotations Support

配置 .vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "java",
      "name": "Spring Boot-MosquitoApplication",
      "request": "launch",
      "cwd": "${workspaceFolder}",
      "mainClass": "com.mosquito.project.MosquitoApplication",
      "projectName": "mosquito",
      "args": "--spring.profiles.active=dev",
      "envFile": "${workspaceFolder}/.env"
    }
  ]
}

📁 项目结构

mosquito/
├── src/
│   ├── main/
│   │   ├── java/com/mosquito/project/
│   │   │   ├── config/              # 配置类
│   │   │   │   ├── CacheConfig.java
│   │   │   │   ├── OpenApiConfig.java
│   │   │   │   └── WebMvcConfig.java
│   │   │   ├── controller/          # REST控制器
│   │   │   │   ├── ActivityController.java
│   │   │   │   ├── ApiKeyController.java
│   │   │   │   ├── ShareTrackingController.java
│   │   │   │   └── UserExperienceController.java
│   │   │   ├── dto/                 # 数据传输对象
│   │   │   │   ├── ApiResponse.java
│   │   │   │   ├── CreateActivityRequest.java
│   │   │   │   └── ActivityStatsResponse.java
│   │   │   ├── exception/           # 异常处理
│   │   │   │   ├── GlobalExceptionHandler.java
│   │   │   │   ├── BusinessException.java
│   │   │   │   └── ResourceNotFoundException.java
│   │   │   ├── persistence/         # 持久层
│   │   │   │   ├── entity/          # JPA实体
│   │   │   │   └── repository/      # JPA仓库
│   │   │   ├── service/             # 业务逻辑
│   │   │   │   ├── ActivityService.java
│   │   │   │   ├── ShortLinkService.java
│   │   │   │   └── ShareTrackingService.java
│   │   │   ├── security/            # 安全相关
│   │   │   │   └── UserIntrospectionService.java
│   │   │   ├── web/                 # Web层拦截器等
│   │   │   │   ├── ApiKeyAuthInterceptor.java
│   │   │   │   └── RateLimitInterceptor.java
│   │   │   └── MosquitoApplication.java
│   │   └── resources/
│   │       ├── db/migration/        # Flyway迁移脚本
│   │       ├── application.properties
│   │       ├── application-dev.yml
│   │       └── logback-spring.xml
│   └── test/
│       └── java/com/mosquito/project/
│           ├── controller/          # 控制器测试
│           ├── service/             # 服务测试
│           ├── integration/         # 集成测试
│           └── config/              # 测试配置
├── docs/                            # 文档
│   ├── api.md
│   ├── PRD.md
│   └── data-model.md
├── pom.xml
└── README.md

分层架构

┌─────────────────────────────────────┐
│         Controller Layer            │  REST API端点
│  (ActivityController, etc.)         │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│          Service Layer               │  业务逻辑
│  (ActivityService, etc.)             │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│       Repository Layer               │  数据访问
│  (JPA Repositories)                  │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│         Database Layer               │  PostgreSQL + Redis
└──────────────────────────────────────┘

📝 开发规范

代码风格

Java代码规范

  • 遵循Google Java Style Guide
  • 使用4个空格缩进
  • 类名使用PascalCase
  • 方法名和变量名使用camelCase
  • 常量使用UPPER_SNAKE_CASE

示例:

public class ActivityService {
    private static final int DEFAULT_PAGE_SIZE = 20;

    private final ActivityRepository activityRepository;

    public ActivityService(ActivityRepository activityRepository) {
        this.activityRepository = activityRepository;
    }

    public Activity createActivity(CreateActivityRequest request) {
        // 实现逻辑
    }
}

命名规范

Controller

  • 类名:XxxController
  • 方法名:动词开头,如 createActivity, getActivity, updateActivity

Service

  • 类名:XxxService
  • 方法名:业务动作,如 create, findById, update, delete

Repository

  • 类名:XxxRepository
  • 方法名遵循Spring Data JPA规范findByActivityId, existsByCode

DTO

  • 请求:XxxRequest
  • 响应:XxxResponse
  • 通用:XxxDto

注释规范

类注释:

/**
 * 活动管理服务
 *
 * 提供活动的创建、查询、更新和删除功能
 *
 * @author Your Name
 * @since 1.0.0
 */
public class ActivityService {
}

方法注释:

/**
 * 创建新活动
 *
 * @param request 活动创建请求
 * @return 创建的活动实体
 * @throws BusinessException 当活动名称重复时
 */
public Activity createActivity(CreateActivityRequest request) {
}

Git提交规范

遵循Conventional Commits规范

<type>(<scope>): <subject>

<body>

<footer>

类型type

  • feat: 新功能
  • fix: 修复bug
  • docs: 文档更新
  • style: 代码格式调整
  • refactor: 重构
  • test: 测试相关
  • chore: 构建/工具相关

示例:

feat(activity): add activity leaderboard endpoint

- Implement leaderboard query with pagination
- Add caching for leaderboard results
- Add integration tests

Closes #123

API设计规范

RESTful风格

GET    /api/v1/activities          # 获取活动列表
POST   /api/v1/activities          # 创建活动
GET    /api/v1/activities/{id}     # 获取单个活动
PUT    /api/v1/activities/{id}     # 更新活动
DELETE /api/v1/activities/{id}     # 删除活动

统一响应格式:

{
  "code": 200,
  "message": "Success",
  "data": { ... },
  "timestamp": "2026-03-04T10:00:00Z"
}

错误响应:

{
  "code": 400,
  "message": "Invalid request",
  "error": {
    "code": "VALIDATION_ERROR",
    "details": {
      "name": "Activity name is required"
    }
  },
  "timestamp": "2026-03-04T10:00:00Z"
}

🧪 测试指南

测试分层

测试金字塔:
    ┌─────────┐
    │   E2E   │  10% - 端到端测试
    ├─────────┤
    │  集成测试 │  30% - 集成测试
    ├─────────┤
    │  单元测试 │  60% - 单元测试
    └─────────┘

单元测试

Service层测试

@ExtendWith(MockitoExtension.class)
class ActivityServiceTest {

    @Mock
    private ActivityRepository activityRepository;

    @InjectMocks
    private ActivityService activityService;

    @Test
    @DisplayName("应成功创建活动")
    void shouldCreateActivity_whenValidRequest() {
        // Given
        CreateActivityRequest request = new CreateActivityRequest();
        request.setName("春季活动");

        ActivityEntity entity = new ActivityEntity();
        entity.setId(1L);
        entity.setName("春季活动");

        when(activityRepository.save(any())).thenReturn(entity);

        // When
        Activity result = activityService.create(request);

        // Then
        assertThat(result.getId()).isEqualTo(1L);
        assertThat(result.getName()).isEqualTo("春季活动");
        verify(activityRepository).save(any());
    }
}

集成测试

Controller集成测试

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class ActivityControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ActivityRepository activityRepository;

    @BeforeEach
    void setUp() {
        activityRepository.deleteAll();
    }

    @Test
    @DisplayName("应成功创建活动并返回201")
    void shouldCreateActivity() throws Exception {
        String requestBody = """
            {
              "name": "春季活动",
              "startTime": "2025-03-01T10:00:00+08:00",
              "endTime": "2025-03-31T23:59:59+08:00"
            }
            """;

        mockMvc.perform(post("/api/v1/activities")
                .contentType(MediaType.APPLICATION_JSON)
                .header("X-API-Key", "test-key")
                .content(requestBody))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.code").value(201))
            .andExpect(jsonPath("$.data.name").value("春季活动"));
    }
}

运行测试

# 运行所有测试
mvn test

# 运行单个测试类
mvn test -Dtest=ActivityServiceTest

# 运行单个测试方法
mvn test -Dtest=ActivityServiceTest#shouldCreateActivity_whenValidRequest

# 生成覆盖率报告
mvn clean test jacoco:report

# 查看覆盖率报告
open target/site/jacoco/index.html

测试覆盖率目标

指标 目标 当前
指令覆盖率 ≥80% 87%
分支覆盖率 ≥70% 66% 🟡
行覆盖率 ≥90% 93%

🐛 调试技巧

日志调试

启用调试日志:

logging:
  level:
    com.mosquito.project: DEBUG
    org.springframework.web: DEBUG
    org.hibernate.SQL: DEBUG

使用SLF4J日志

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ActivityService {
    private static final Logger log = LoggerFactory.getLogger(ActivityService.class);

    public Activity create(CreateActivityRequest request) {
        log.debug("Creating activity: {}", request.getName());
        // ...
        log.info("Activity created: id={}", activity.getId());
        return activity;
    }
}

远程调试

启动应用时启用远程调试:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 \
  -jar target/mosquito-1.0.0.jar

IntelliJ IDEA配置

  1. Run > Edit Configurations
  2. 添加 Remote JVM Debug
  3. Host: localhost, Port: 5005
  4. 点击Debug按钮连接

数据库调试

查看SQL语句

spring:
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true

使用H2 Console测试环境

spring:
  h2:
    console:
      enabled: true
      path: /h2-console

访问 http://localhost:8080/h2-console

Redis调试

使用Redis CLI

# 连接Redis
redis-cli

# 查看所有键
KEYS *

# 查看键值
GET rate_limit:api_key_123

# 查看TTL
TTL rate_limit:api_key_123

# 清空缓存
FLUSHDB

🤝 贡献指南

开发流程

  1. Fork项目
# Fork到你的GitHub账号
# 克隆你的Fork
git clone https://github.com/your-username/mosquito.git
cd mosquito
git remote add upstream https://github.com/your-org/mosquito.git
  1. 创建功能分支
git checkout -b feature/add-leaderboard
  1. 开发功能
  • 编写代码
  • 添加测试
  • 更新文档
  1. 提交代码
git add .
git commit -m "feat(activity): add leaderboard endpoint"
  1. 同步上游
git fetch upstream
git rebase upstream/main
  1. 推送分支
git push origin feature/add-leaderboard
  1. 创建Pull Request
  • 访问GitHub仓库
  • 点击 "New Pull Request"
  • 填写PR描述
  • 等待代码审查

Pull Request检查清单

  • 代码遵循项目规范
  • 添加了单元测试
  • 添加了集成测试
  • 测试覆盖率不降低
  • 更新了相关文档
  • 通过了CI/CD检查
  • 代码已经过自我审查
  • 提交信息遵循规范

代码审查标准

必须检查:

  • 功能是否正确实现
  • 是否有安全漏洞
  • 是否有性能问题
  • 测试是否充分
  • 代码是否可维护

建议检查:

  • 命名是否清晰
  • 注释是否充分
  • 是否有重复代码
  • 是否可以简化

常见问题

Q1: 数据库连接失败

问题: Connection refused: connect

解决:

# 检查PostgreSQL是否运行
sudo systemctl status postgresql

# 启动PostgreSQL
sudo systemctl start postgresql

# 检查连接
psql -U mosquito -h localhost -d mosquito_dev

Q2: Redis连接失败

问题: Unable to connect to Redis

解决:

# 检查Redis是否运行
sudo systemctl status redis

# 启动Redis
sudo systemctl start redis

# 测试连接
redis-cli ping

Q3: 测试失败

问题: TestContainers: Could not find a valid Docker environment

解决:

# 安装Docker
sudo apt-get install docker.io

# 启动Docker
sudo systemctl start docker

# 添加用户到docker组
sudo usermod -aG docker $USER

Q4: 端口被占用

问题: Port 8080 is already in use

解决:

# 查找占用端口的进程
lsof -i :8080

# 杀死进程
kill -9 <PID>

# 或使用其他端口
java -jar target/mosquito-1.0.0.jar --server.port=8081

Q5: Flyway迁移失败

问题: Flyway migration failed

解决:

# 查看迁移状态
mvn flyway:info

# 修复失败的迁移
mvn flyway:repair

# 清空数据库重新迁移(仅开发环境)
mvn flyway:clean flyway:migrate

📚 相关资源


文档版本: 1.0 最后更新: 2026-03-04