Files
wenzi/docs/DEVELOPMENT_GUIDE.md

797 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🛠️ 开发指南
> 版本: 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. 克隆项目
```bash
git clone https://github.com/your-org/mosquito.git
cd mosquito
```
### 2. 安装依赖
```bash
# 安装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. 配置数据库
```bash
# 创建数据库
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. 配置开发环境
复制配置模板:
```bash
cp src/main/resources/application-dev.yml.example src/main/resources/application-dev.yml
```
编辑 `application-dev.yml`
```yaml
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. 构建项目
```bash
# 编译项目
mvn clean compile
# 运行测试
mvn test
# 打包
mvn package
```
### 6. 启动应用
```bash
# 使用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. 工作区定期清理
为避免构建产物和测试产物污染仓库,建议在本地定期执行以下命令:
```bash
# 只检查(发现 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:run``vite``scripts/e2e_continuous_runner.sh``target``frontend/e2e/*` 可能被立即重建。建议先停止后台进程再执行清理。
### 8. IDE配置
**IntelliJ IDEA**
1. 导入项目:`File > Open` 选择项目根目录
2. 配置JDK`File > 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`
```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
**示例:**
```java
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`
### 注释规范
**类注释:**
```java
/**
* 活动管理服务
*
* 提供活动的创建、查询、更新和删除功能
*
* @author Your Name
* @since 1.0.0
*/
public class ActivityService {
}
```
**方法注释:**
```java
/**
* 创建新活动
*
* @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`: 构建/工具相关
**示例:**
```bash
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} # 删除活动
```
**统一响应格式:**
```json
{
"code": 200,
"message": "Success",
"data": { ... },
"timestamp": "2026-03-04T10:00:00Z"
}
```
**错误响应:**
```json
{
"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层测试**
```java
@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集成测试**
```java
@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("春季活动"));
}
}
```
### 运行测试
```bash
# 运行所有测试
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% ✅ |
## 🐛 调试技巧
### 日志调试
**启用调试日志:**
```yaml
logging:
level:
com.mosquito.project: DEBUG
org.springframework.web: DEBUG
org.hibernate.SQL: DEBUG
```
**使用SLF4J日志**
```java
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;
}
}
```
### 远程调试
**启动应用时启用远程调试:**
```bash
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语句**
```yaml
spring:
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
```
**使用H2 Console测试环境**
```yaml
spring:
h2:
console:
enabled: true
path: /h2-console
```
访问 `http://localhost:8080/h2-console`
### Redis调试
**使用Redis CLI**
```bash
# 连接Redis
redis-cli
# 查看所有键
KEYS *
# 查看键值
GET rate_limit:api_key_123
# 查看TTL
TTL rate_limit:api_key_123
# 清空缓存
FLUSHDB
```
## 🤝 贡献指南
### 开发流程
1. **Fork项目**
```bash
# 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
```
2. **创建功能分支**
```bash
git checkout -b feature/add-leaderboard
```
3. **开发功能**
- 编写代码
- 添加测试
- 更新文档
4. **提交代码**
```bash
git add .
git commit -m "feat(activity): add leaderboard endpoint"
```
5. **同步上游**
```bash
git fetch upstream
git rebase upstream/main
```
6. **推送分支**
```bash
git push origin feature/add-leaderboard
```
7. **创建Pull Request**
- 访问GitHub仓库
- 点击 "New Pull Request"
- 填写PR描述
- 等待代码审查
### Pull Request检查清单
- [ ] 代码遵循项目规范
- [ ] 添加了单元测试
- [ ] 添加了集成测试
- [ ] 测试覆盖率不降低
- [ ] 更新了相关文档
- [ ] 通过了CI/CD检查
- [ ] 代码已经过自我审查
- [ ] 提交信息遵循规范
### 代码审查标准
**必须检查:**
- 功能是否正确实现
- 是否有安全漏洞
- 是否有性能问题
- 测试是否充分
- 代码是否可维护
**建议检查:**
- 命名是否清晰
- 注释是否充分
- 是否有重复代码
- 是否可以简化
## ❓ 常见问题
### Q1: 数据库连接失败
**问题:** `Connection refused: connect`
**解决:**
```bash
# 检查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`
**解决:**
```bash
# 检查Redis是否运行
sudo systemctl status redis
# 启动Redis
sudo systemctl start redis
# 测试连接
redis-cli ping
```
### Q3: 测试失败
**问题:** `TestContainers: Could not find a valid Docker environment`
**解决:**
```bash
# 安装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`
**解决:**
```bash
# 查找占用端口的进程
lsof -i :8080
# 杀死进程
kill -9 <PID>
# 或使用其他端口
java -jar target/mosquito-1.0.0.jar --server.port=8081
```
### Q5: Flyway迁移失败
**问题:** `Flyway migration failed`
**解决:**
```bash
# 查看迁移状态
mvn flyway:info
# 修复失败的迁移
mvn flyway:repair
# 清空数据库重新迁移(仅开发环境)
mvn flyway:clean flyway:migrate
```
## 📚 相关资源
- [部署指南](./DEPLOYMENT_GUIDE.md) - 部署说明
- [配置指南](./CONFIGURATION_GUIDE.md) - 配置选项
- [API文档](./api.md) - API接口文档
- [API集成指南](./API_INTEGRATION_GUIDE.md) - API集成示例
---
**文档版本**: 1.0
**最后更新**: 2026-03-04