290 lines
9.0 KiB
Markdown
290 lines
9.0 KiB
Markdown
# junboV2 项目上下文(Claude 参考文档)
|
||
|
||
> 本文档用于为 Claude AI 助手提供项目背景和关键决策上下文
|
||
|
||
## 项目概况
|
||
|
||
**项目名称**: junboV2
|
||
**类型**: 企业管理系统
|
||
**技术栈**: Spring Boot 2.7.18 + Spring Data JPA + SQLite + Lombok
|
||
**构建工具**: Gradle 7.6.3
|
||
**Java 版本**: Java 8 (1.8)
|
||
**JDK**: 使用系统 JAVA_HOME 环境变量指定的 JDK
|
||
|
||
### 业务领域
|
||
|
||
- **organization** - 组织架构(员工、部门、部门关系)
|
||
- **attendance** - 考勤管理(签到、请假、工作日历)
|
||
- **scrum** - Scrum 迭代(Sprint、Story、Story Member)
|
||
- **kpa** - KPA 绩效(KPA 记录、员工得分)
|
||
- **incentive** - 激励管理(激励记录)
|
||
|
||
## 包结构(Package by Feature)
|
||
|
||
```
|
||
info.panli.junbo/
|
||
├── JunboApplication.java # Spring Boot 入口
|
||
├── api/ # REST Controllers(横切层)
|
||
├── infrastructure/ # 基础设施(横切层)
|
||
│ ├── config/ # 配置类
|
||
│ └── importer/ # 数据导入接口
|
||
├── organization/ # 【核心领域】组织架构
|
||
│ ├── entity/ # JPA 实体
|
||
│ ├── repository/ # Spring Data Repository
|
||
│ └── importer/ # 数据导入服务
|
||
├── attendance/ # 【业务领域】考勤
|
||
│ ├── entity/
|
||
│ ├── repository/
|
||
│ ├── importer/
|
||
│ ├── bo/, dao/, po/ # 旧代码(保留,标记 @Deprecated)
|
||
│ └── service/
|
||
├── scrum/ # 【业务领域】迭代
|
||
│ ├── entity/
|
||
│ ├── repository/
|
||
│ ├── service/
|
||
│ └── bo/, dao/, po/ # 旧代码(保留)
|
||
├── kpa/ # 【业务领域】KPA
|
||
│ ├── entity/
|
||
│ ├── repository/
|
||
│ ├── service/
|
||
│ └── bo/, dao/, po/ # 旧代码(保留)
|
||
└── incentive/ # 【业务领域】激励
|
||
├── entity/
|
||
├── repository/
|
||
├── service/
|
||
└── bo/, dao/, po/ # 旧代码(保留)
|
||
```
|
||
|
||
## 关键架构决策
|
||
|
||
### 1. 跨领域依赖规则
|
||
|
||
✅ **允许**:其他领域 → `organization` (组织架构是基础领域)
|
||
❌ **禁止**:平级业务领域之间直接依赖(如 `attendance` ↔ `scrum`)
|
||
|
||
```java
|
||
// ✅ 正确示例
|
||
package info.panli.junbo.attendance.entity;
|
||
import info.panli.junbo.organization.entity.EmployeeEntity;
|
||
|
||
@Entity
|
||
public class SignRecordEntity {
|
||
@ManyToOne
|
||
private EmployeeEntity employee; // 引用组织领域
|
||
}
|
||
```
|
||
|
||
### 2. Importer 职责边界(重要)
|
||
|
||
**原则**: Importer 只负责数据去重,不负责业务逻辑验证
|
||
|
||
| 职责类型 | 定义 | 示例 | 处理者 |
|
||
|---------|------|------|--------|
|
||
| **数据去重** | 完全相同的记录 | 同员工同日期同时间的签到 | ✅ Importer |
|
||
| **业务冲突** | 逻辑上的矛盾 | 两次请假时间重叠 | ❌ Service 层 |
|
||
|
||
**Importer 应该做的**:
|
||
- 读取外部数据源(Excel/YAML)
|
||
- 检查记录是否完全相同(主键/唯一索引)
|
||
- 跳过重复记录,插入新记录
|
||
|
||
**Importer 不应该做的**:
|
||
- 业务规则验证(时间合理性、余额检查)
|
||
- 数据冲突检测(时间重叠、状态机校验)
|
||
- 复杂计算和统计
|
||
|
||
### 3. 数据访问层演进
|
||
|
||
| 层次 | 状态 | 使用范围 |
|
||
|------|------|---------|
|
||
| **Entity + Repository** | ✅ 当前主力 | 数据持久化、CRUD |
|
||
| **bo/dao/po** | ⚠️ 遗留代码 | 旧功能、Excel 导入 |
|
||
|
||
新功能应使用 `Entity + Repository`,旧代码暂时保留标记 `@Deprecated`。
|
||
|
||
## 常见操作指南
|
||
|
||
### 编译和测试
|
||
|
||
```bash
|
||
# 编译(使用系统 JAVA_HOME)
|
||
./gradlew compileJava
|
||
|
||
# 运行测试
|
||
./gradlew test
|
||
|
||
# 构建
|
||
./gradlew build
|
||
```
|
||
|
||
### 添加新实体
|
||
|
||
1. 在对应领域的 `entity/` 创建 JPA 实体
|
||
2. 在 `repository/` 创建 Repository 接口
|
||
3. 如需导入功能,在 `importer/` 创建 ImportService
|
||
4. 更新 `JunboApplication.java` 的 `@EnableJpaRepositories`(如果是新领域)
|
||
|
||
### 跨领域引用
|
||
|
||
```java
|
||
// ✅ 正确:依赖 organization
|
||
import info.panli.junbo.organization.entity.EmployeeEntity;
|
||
import info.panli.junbo.organization.repository.EmployeeRepository;
|
||
|
||
// ❌ 错误:旧的 sqlite 包(已删除)
|
||
import info.panli.junbo.sqlite.entity.EmployeeEntity;
|
||
|
||
// ❌ 错误:平级领域互相依赖
|
||
import info.panli.junbo.scrum.entity.SprintEntity; // 在 attendance 中
|
||
```
|
||
|
||
### 创建数据导入服务
|
||
|
||
```java
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class XxxImportService {
|
||
|
||
private final XxxRepository repository;
|
||
private final EmployeeRepository employeeRepository;
|
||
|
||
@Transactional
|
||
public ImportResult importFromExcel(String filePath) {
|
||
// 1. 读取外部数据
|
||
List<XxxBo> records = readExcel(filePath);
|
||
|
||
int inserted = 0, skipped = 0;
|
||
|
||
for (XxxBo bo : records) {
|
||
// 2. 查找员工(如果需要)
|
||
EmployeeEntity employee = employeeRepository
|
||
.findByNameOrAlias(bo.getEmployeeName())
|
||
.orElse(null);
|
||
if (employee == null) {
|
||
continue; // 找不到员工,跳过
|
||
}
|
||
|
||
// 3. 检查是否已存在(数据去重)
|
||
boolean exists = repository.existsByEmployeeAndDate(
|
||
employee, bo.getDate()
|
||
);
|
||
if (exists) {
|
||
skipped++;
|
||
continue;
|
||
}
|
||
|
||
// 4. 保存新记录
|
||
XxxEntity entity = convert(bo, employee);
|
||
repository.save(entity);
|
||
inserted++;
|
||
}
|
||
|
||
return ImportResult.builder()
|
||
.insertedCount(inserted)
|
||
.skippedCount(skipped)
|
||
.build();
|
||
}
|
||
|
||
// ❌ 不要在这里做业务逻辑验证!
|
||
}
|
||
```
|
||
|
||
## 技术细节
|
||
|
||
### JPA 实体命名规范
|
||
|
||
- 实体类:`XxxEntity`(如 `EmployeeEntity`)
|
||
- Repository:`XxxRepository`(如 `EmployeeRepository`)
|
||
- Service:`XxxService`(如 `KpaService`)
|
||
- Importer:`XxxImportService`(如 `SignRecordImportService`)
|
||
|
||
### Spring Data JPA Repository 扫描
|
||
|
||
在 `JunboApplication.java` 中配置:
|
||
|
||
```java
|
||
@EnableJpaRepositories(basePackages = {
|
||
"info.panli.junbo.organization.repository",
|
||
"info.panli.junbo.attendance.repository",
|
||
"info.panli.junbo.scrum.repository",
|
||
"info.panli.junbo.kpa.repository",
|
||
"info.panli.junbo.incentive.repository"
|
||
})
|
||
```
|
||
|
||
### 数据库
|
||
|
||
- **类型**: SQLite
|
||
- **配置**: `application.properties` 或 `application.yml`
|
||
- **初始化**: `DataInitializer.java`(自动从 YAML 导入初始数据)
|
||
|
||
## 项目历史变更
|
||
|
||
### 2026-02-06: 包结构重构
|
||
|
||
**背景**: 原有代码按领域组织(bo/dao/po),新增 SQLite 代码按层组织(entity/repository/controller),架构不一致。
|
||
|
||
**方案**: 将所有 SQLite 相关代码迁移到领域包,采用 Package by Feature 架构。
|
||
|
||
**变更**:
|
||
- ❌ 删除 `info.panli.junbo.sqlite.*` 包(47 个文件)
|
||
- ✅ 创建 `organization/`, `attendance/`, `scrum/`, `kpa/`, `incentive/` 领域包
|
||
- ✅ 创建 `api/`, `infrastructure/` 横切包
|
||
- ✅ 更新所有 import 语句
|
||
|
||
**结果**:
|
||
- ✅ 编译通过
|
||
- ✅ 96.4% 测试通过(534/554)
|
||
- ✅ 无包引用错误
|
||
|
||
## 常见问题
|
||
|
||
### Q: 为什么 bo/dao/po 代码还在?
|
||
|
||
A: 旧功能依赖这些代码,暂时保留标记 `@Deprecated`,后续逐步迁移。
|
||
|
||
### Q: 添加新功能应该用哪一套?
|
||
|
||
A: 使用 `Entity + Repository + Service`,不要再用 bo/dao/po。
|
||
|
||
### Q: 如何处理跨领域的业务逻辑?
|
||
|
||
A: 通过 Service 层协调,或使用 Spring 事件机制解耦。
|
||
|
||
### Q: Importer 和 Service 的边界是什么?
|
||
|
||
A: Importer 只做数据去重导入,Service 做业务逻辑验证和计算。
|
||
|
||
### Q: 为什么项目使用 Java 8?
|
||
|
||
A: 项目使用 Spring Boot 2.7.18,与 Java 8 兼容性最佳。确保系统 JAVA_HOME 指向 JDK 8。
|
||
|
||
## 后续计划
|
||
|
||
1. ✅ 包结构重构(已完成)
|
||
2. ⏳ 完善 Service 层业务逻辑
|
||
3. ⏳ 补充单元测试和集成测试
|
||
4. ⏳ 引入 DTO 层(Controller ↔ Service)
|
||
5. ⏳ 添加领域事件机制
|
||
6. ⏳ 逐步移除 bo/dao/po 旧代码
|
||
|
||
## 参考资料
|
||
|
||
- [包结构重构设计方案](docs/plans/2026-02-05-package-restructure-design.md)
|
||
- [测试工具包使用指南](docs/testing-toolkit-guide.md)
|
||
- [JUnit 5 迁移完成报告](docs/junit5-jacoco-completion-report.md)
|
||
- [项目健康度报告](docs/health-report-2026-02-06.md)
|
||
- [测试覆盖率报告](docs/test-coverage-report-2026-02-06.md)
|
||
- [测试修复进度报告](docs/test-fixes-progress-2026-02-06.md)
|
||
- [测试提升总结](docs/test-improvement-summary-2026-02-06.md)
|
||
- [测试最终总结](docs/test-final-summary-2026-02-06.md) ⭐ 最新
|
||
- Spring Data JPA: https://spring.io/projects/spring-data-jpa
|
||
- Package by Feature: https://phauer.com/2020/package-by-feature/
|
||
- JUnit 5: https://junit.org/junit5/docs/current/user-guide/
|
||
|
||
---
|
||
|
||
**文档版本**: 1.0
|
||
**最后更新**: 2026-02-06
|
||
**维护者**: User & Claude
|