202 lines
6.2 KiB
Markdown
202 lines
6.2 KiB
Markdown
# 赛程编排API冲突修复说明
|
||
|
||
## 问题描述
|
||
|
||
在实现赛程编排后端API时,发现项目中已经存在 `MartialScheduleArrangeController` 控制器,该控制器已经定义了相同的路径:
|
||
- `GET /martial/schedule/result`
|
||
- `POST /martial/schedule/save-and-lock`
|
||
|
||
这导致Spring Boot启动时报错:
|
||
```
|
||
Ambiguous mapping. Cannot map 'martialScheduleController' method to {POST [/martial/schedule/save-and-lock]}:
|
||
There is already 'martialScheduleArrangeController' bean method mapped.
|
||
```
|
||
|
||
## 解决方案
|
||
|
||
### 1. 删除重复的控制器端点
|
||
|
||
从新创建的 `MartialScheduleController` 中删除了冲突的3个端点:
|
||
- `/result`
|
||
- `/save-draft`
|
||
- `/save-and-lock`
|
||
|
||
保留原有的基础CRUD端点(detail, list, submit, remove)。
|
||
|
||
### 2. 更新现有控制器
|
||
|
||
修改 `MartialScheduleArrangeController`,使其使用新创建的Service和DTO:
|
||
|
||
**文件**: [MartialScheduleArrangeController.java](d:\workspace\31.比赛项目\project\martial-master\src\main\java\org\springblade\modules\martial\controller\MartialScheduleArrangeController.java)
|
||
|
||
#### 2.1 添加依赖注入
|
||
|
||
```java
|
||
private final IMartialScheduleArrangeService scheduleArrangeService;
|
||
private final IMartialScheduleService scheduleService; // 新增
|
||
```
|
||
|
||
#### 2.2 更新 GET /result 端点
|
||
|
||
**修改前**:
|
||
```java
|
||
public R<Map<String, Object>> getScheduleResult(@RequestParam Long competitionId) {
|
||
Map<String, Object> result = scheduleArrangeService.getScheduleResult(competitionId);
|
||
return R.data(result);
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```java
|
||
public R<ScheduleResultDTO> getScheduleResult(@RequestParam Long competitionId) {
|
||
ScheduleResultDTO result = scheduleService.getScheduleResult(competitionId);
|
||
return R.data(result);
|
||
}
|
||
```
|
||
|
||
**改进**:
|
||
- 使用结构化的DTO替代Map
|
||
- 返回类型更加明确
|
||
- 符合前端API规范
|
||
|
||
#### 2.3 新增 POST /save-draft 端点
|
||
|
||
```java
|
||
@PostMapping("/save-draft")
|
||
@Operation(summary = "保存编排草稿", description = "传入编排草稿数据")
|
||
public R saveDraftSchedule(@RequestBody SaveScheduleDraftDTO dto) {
|
||
try {
|
||
boolean success = scheduleService.saveDraftSchedule(dto);
|
||
return success ? R.success("草稿保存成功") : R.fail("草稿保存失败");
|
||
} catch (Exception e) {
|
||
log.error("保存编排草稿失败", e);
|
||
return R.fail("保存编排草稿失败: " + e.getMessage());
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2.4 更新 POST /save-and-lock 端点
|
||
|
||
**修改前**:
|
||
```java
|
||
public R saveAndLock(@RequestBody Map<String, Object> params) {
|
||
Long competitionId = Long.valueOf(String.valueOf(params.get("competitionId")));
|
||
scheduleArrangeService.saveAndLock(competitionId, userId);
|
||
return R.success("编排已保存并锁定");
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```java
|
||
public R saveAndLock(@RequestBody SaveScheduleDraftDTO dto) {
|
||
BladeUser user = AuthUtil.getUser();
|
||
String userId = user != null ? user.getUserName() : "system";
|
||
|
||
boolean success = scheduleService.saveAndLockSchedule(dto.getCompetitionId());
|
||
if (success) {
|
||
// 调用原有的锁定逻辑
|
||
scheduleArrangeService.saveAndLock(dto.getCompetitionId(), userId);
|
||
return R.success("编排已完成并锁定");
|
||
} else {
|
||
return R.fail("编排锁定失败");
|
||
}
|
||
}
|
||
```
|
||
|
||
**改进**:
|
||
1. 使用DTO替代Map,类型安全
|
||
2. 结合新旧两个Service的功能
|
||
3. 先更新参赛者状态,再执行原有的锁定逻辑
|
||
|
||
## 最终API结构
|
||
|
||
### MartialScheduleArrangeController
|
||
**基础路径**: `/martial/schedule`
|
||
|
||
| 方法 | 路径 | 功能 | 请求类型 | 响应类型 |
|
||
|------|------|------|----------|----------|
|
||
| GET | `/result` | 获取编排结果 | competitionId | ScheduleResultDTO |
|
||
| POST | `/save-draft` | 保存编排草稿 | SaveScheduleDraftDTO | R |
|
||
| POST | `/save-and-lock` | 完成编排并锁定 | SaveScheduleDraftDTO | R |
|
||
| POST | `/auto-arrange` | 手动触发自动编排 | Map | R |
|
||
|
||
### MartialScheduleController
|
||
**基础路径**: `/martial/schedule`
|
||
|
||
| 方法 | 路径 | 功能 | 请求类型 | 响应类型 |
|
||
|------|------|------|----------|----------|
|
||
| GET | `/detail` | 获取详情 | id | MartialSchedule |
|
||
| GET | `/list` | 分页列表 | MartialSchedule, Query | IPage |
|
||
| POST | `/submit` | 新增或修改 | MartialSchedule | R |
|
||
| POST | `/remove` | 删除 | ids | R |
|
||
|
||
## 字段冲突修复
|
||
|
||
### 问题
|
||
实体类 `MartialScheduleParticipant` 的 `status` 字段与基础类 `TenantEntity` 冲突。
|
||
|
||
### 解决方案
|
||
将 `status` 字段重命名为 `checkInStatus`(签到状态):
|
||
|
||
**文件**: [MartialScheduleParticipant.java:86-90](d:\workspace\31.比赛项目\project\martial-master\src\main\java\org\springblade\modules\martial\pojo\entity\MartialScheduleParticipant.java#L86-L90)
|
||
|
||
```java
|
||
/**
|
||
* 签到状态:未签到/已签到/异常
|
||
*/
|
||
@Schema(description = "签到状态:未签到/已签到/异常")
|
||
private String checkInStatus;
|
||
```
|
||
|
||
### 相应更新
|
||
|
||
**Service层** ([MartialScheduleServiceImpl.java](d:\workspace\31.比赛项目\project\martial-master\src\main\java\org\springblade\modules\martial\service\impl\MartialScheduleServiceImpl.java)):
|
||
|
||
1. **读取时**:
|
||
```java
|
||
dto.setStatus(p.getCheckInStatus() != null ? p.getCheckInStatus() : "未签到");
|
||
```
|
||
|
||
2. **保存时**:
|
||
```java
|
||
participant.setCheckInStatus(participantDTO.getStatus());
|
||
```
|
||
|
||
前端仍然使用 `status` 字段,在Service层进行映射转换。
|
||
|
||
## 数据库字段名建议
|
||
|
||
```sql
|
||
ALTER TABLE martial_schedule_participant
|
||
ADD COLUMN check_in_status VARCHAR(20) DEFAULT '未签到' COMMENT '签到状态:未签到/已签到/异常',
|
||
ADD COLUMN schedule_status VARCHAR(20) DEFAULT 'draft' COMMENT '编排状态:draft/completed';
|
||
```
|
||
|
||
## 前后端对接
|
||
|
||
前端API配置无需修改,仍然使用原有路径:
|
||
```javascript
|
||
// 获取赛程编排结果
|
||
GET /api/martial/schedule/result
|
||
|
||
// 保存编排草稿
|
||
POST /api/martial/schedule/save-draft
|
||
|
||
// 完成编排并锁定
|
||
POST /api/martial/schedule/save-and-lock
|
||
```
|
||
|
||
所有端点都通过 `MartialScheduleArrangeController` 处理。
|
||
|
||
## 总结
|
||
|
||
通过以下措施解决了API冲突问题:
|
||
|
||
1. ✅ 删除重复的控制器端点
|
||
2. ✅ 更新现有控制器使用新的DTO和Service
|
||
3. ✅ 修复字段名冲突
|
||
4. ✅ 保持前端API路径不变
|
||
5. ✅ 结合新旧Service功能,确保业务逻辑完整
|
||
|
||
现在系统可以正常启动,API端点清晰明确,没有冲突。
|