From 1ca0f6a7f6c10aedbc77e9732bcec8e9b27e5735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=85=E6=88=BF?= Date: Fri, 12 Dec 2025 13:49:00 +0800 Subject: [PATCH] fix bugs --- .../martial-db/create_dispatch_log_table.sql | 49 ++ .../test_invite_code_generation.sql | 116 +++++ docs/DISPATCH_FEATURE_SUMMARY.md | 418 +++++++++++++++ docs/DISPATCH_REFACTOR_SUMMARY.md | 332 ++++++++++++ docs/DISPATCH_TAB_IMPLEMENTATION.md | 313 +++++++++++ docs/schedule-dispatch-implementation.md | 485 ++++++++++++++++++ .../auth/controller/CaptchaController.java | 81 +++ .../MartialJudgeInviteController.java | 52 ++ .../MartialScheduleArrangeController.java | 49 ++ .../martial/pojo/dto/AdjustOrderDTO.java | 39 ++ .../pojo/dto/BatchGenerateInviteDTO.java | 28 + .../martial/pojo/dto/DispatchDataDTO.java | 33 ++ .../martial/pojo/dto/GenerateInviteDTO.java | 33 ++ .../martial/pojo/dto/SaveDispatchDTO.java | 71 +++ .../martial/pojo/vo/DispatchDataVO.java | 113 ++++ .../service/IMartialJudgeInviteService.java | 34 ++ .../service/IMartialScheduleService.java | 23 + .../impl/MartialJudgeInviteServiceImpl.java | 132 +++++ .../impl/MartialScheduleServiceImpl.java | 154 ++++++ .../system/controller/UserController.java | 60 +++ .../system/pojo/dto/UserRegisterDTO.java | 69 +++ .../system/service/impl/UserServiceImpl.java | 10 +- src/main/resources/application.yml | 4 +- 23 files changed, 2695 insertions(+), 3 deletions(-) create mode 100644 database/martial-db/create_dispatch_log_table.sql create mode 100644 database/martial-db/test_invite_code_generation.sql create mode 100644 docs/DISPATCH_FEATURE_SUMMARY.md create mode 100644 docs/DISPATCH_REFACTOR_SUMMARY.md create mode 100644 docs/DISPATCH_TAB_IMPLEMENTATION.md create mode 100644 docs/schedule-dispatch-implementation.md create mode 100644 src/main/java/org/springblade/modules/auth/controller/CaptchaController.java create mode 100644 src/main/java/org/springblade/modules/martial/pojo/dto/AdjustOrderDTO.java create mode 100644 src/main/java/org/springblade/modules/martial/pojo/dto/BatchGenerateInviteDTO.java create mode 100644 src/main/java/org/springblade/modules/martial/pojo/dto/DispatchDataDTO.java create mode 100644 src/main/java/org/springblade/modules/martial/pojo/dto/GenerateInviteDTO.java create mode 100644 src/main/java/org/springblade/modules/martial/pojo/dto/SaveDispatchDTO.java create mode 100644 src/main/java/org/springblade/modules/martial/pojo/vo/DispatchDataVO.java create mode 100644 src/main/java/org/springblade/modules/system/pojo/dto/UserRegisterDTO.java diff --git a/database/martial-db/create_dispatch_log_table.sql b/database/martial-db/create_dispatch_log_table.sql new file mode 100644 index 0000000..82523c9 --- /dev/null +++ b/database/martial-db/create_dispatch_log_table.sql @@ -0,0 +1,49 @@ +-- ===================================================== +-- 创建调度调整日志表 +-- 用于记录调度功能的调整历史 +-- 执行时间: 2025-12-12 +-- ===================================================== + +USE blade; + +-- 创建调度调整日志表 +CREATE TABLE IF NOT EXISTS `martial_schedule_adjustment_log` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `competition_id` bigint NOT NULL COMMENT '赛事ID', + `schedule_detail_id` bigint NOT NULL COMMENT '编排明细ID', + `schedule_group_id` bigint NOT NULL COMMENT '分组ID', + `participant_id` bigint NOT NULL COMMENT '参赛者记录ID', + `participant_name` varchar(100) DEFAULT NULL COMMENT '参赛者姓名', + `organization` varchar(200) DEFAULT NULL COMMENT '单位名称', + `old_order` int NOT NULL COMMENT '原顺序', + `new_order` int NOT NULL COMMENT '新顺序', + `adjustment_type` varchar(20) DEFAULT NULL COMMENT '调整类型(move_up=上移, move_down=下移, swap=交换)', + `adjustment_reason` varchar(500) DEFAULT NULL COMMENT '调整原因', + `operator_id` bigint DEFAULT NULL COMMENT '操作人ID', + `operator_name` varchar(100) DEFAULT NULL COMMENT '操作人姓名', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID', + PRIMARY KEY (`id`), + KEY `idx_competition` (`competition_id`), + KEY `idx_detail` (`schedule_detail_id`), + KEY `idx_group` (`schedule_group_id`), + KEY `idx_participant` (`participant_id`), + KEY `idx_create_time` (`create_time`), + KEY `idx_tenant` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='赛程调度调整日志表'; + +-- 验证表是否创建成功 +SELECT + TABLE_NAME, + TABLE_COMMENT, + TABLE_ROWS +FROM + INFORMATION_SCHEMA.TABLES +WHERE + TABLE_SCHEMA = 'blade' + AND TABLE_NAME = 'martial_schedule_adjustment_log'; + +-- 查看表结构 +DESC martial_schedule_adjustment_log; + +SELECT '调度日志表创建成功!' AS status; diff --git a/database/martial-db/test_invite_code_generation.sql b/database/martial-db/test_invite_code_generation.sql new file mode 100644 index 0000000..a8cdc89 --- /dev/null +++ b/database/martial-db/test_invite_code_generation.sql @@ -0,0 +1,116 @@ +-- ===================================================== +-- 测试邀请码生成功能 +-- 执行时间: 2025-12-12 +-- ===================================================== + +USE blade; + +-- ===================================================== +-- 1. 插入测试数据 +-- ===================================================== + +-- 为评委生成测试邀请码(使用现有字段) +INSERT INTO martial_judge_invite ( + id, competition_id, judge_id, invite_code, role, + venue_id, projects, expire_time, is_used, + status, create_time +) VALUES ( + 1001, 1, 1, 'TEST01', 'judge', + 1, '["女子组长拳","男子组陈氏太极拳"]', + DATE_ADD(NOW(), INTERVAL 30 DAY), 0, + 1, NOW() +); + +-- 为裁判长生成邀请码 +INSERT INTO martial_judge_invite ( + id, competition_id, judge_id, invite_code, role, + venue_id, projects, expire_time, is_used, + status, create_time +) VALUES ( + 1002, 1, 2, 'ADMIN1', 'chief_judge', + NULL, NULL, + DATE_ADD(NOW(), INTERVAL 30 DAY), 0, + 1, NOW() +); + +-- ===================================================== +-- 2. 查询测试 +-- ===================================================== + +-- 查询某个评委的有效邀请码 +SELECT + ji.id, + ji.invite_code, + ji.role, + ji.expire_time, + ji.is_used, + ji.status, + j.name AS judge_name, + c.competition_name +FROM martial_judge_invite ji +LEFT JOIN martial_judge j ON ji.judge_id = j.id +LEFT JOIN martial_competition c ON ji.competition_id = c.id +WHERE ji.competition_id = 1 + AND ji.judge_id = 1 + AND ji.status = 1 + AND ji.is_deleted = 0 + AND ji.expire_time > NOW() +ORDER BY ji.create_time DESC +LIMIT 1; + +-- 查询所有有效邀请码 +SELECT + ji.id, + ji.invite_code, + ji.role, + j.name AS judge_name, + ji.expire_time, + ji.is_used, + CASE + WHEN ji.is_used = 1 THEN '已使用' + WHEN ji.expire_time < NOW() THEN '已过期' + WHEN ji.status = 0 THEN '已禁用' + ELSE '待使用' + END AS status_text +FROM martial_judge_invite ji +LEFT JOIN martial_judge j ON ji.judge_id = j.id +WHERE ji.competition_id = 1 + AND ji.is_deleted = 0 +ORDER BY ji.create_time DESC; + +-- ===================================================== +-- 3. 统计查询 +-- ===================================================== + +-- 统计某个赛事的邀请码状态 +SELECT + COUNT(*) AS total, + SUM(CASE WHEN is_used = 0 AND status = 1 AND expire_time > NOW() THEN 1 ELSE 0 END) AS available, + SUM(CASE WHEN is_used = 1 THEN 1 ELSE 0 END) AS used, + SUM(CASE WHEN expire_time <= NOW() THEN 1 ELSE 0 END) AS expired, + SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS disabled +FROM martial_judge_invite +WHERE competition_id = 1 + AND is_deleted = 0; + +-- ===================================================== +-- 4. 验证邀请码唯一性 +-- ===================================================== + +-- 检查邀请码是否重复 +SELECT + invite_code, + COUNT(*) AS count +FROM martial_judge_invite +WHERE is_deleted = 0 +GROUP BY invite_code +HAVING COUNT(*) > 1; + +-- ===================================================== +-- 5. 清理测试数据(可选) +-- ===================================================== + +-- 删除测试数据 +-- DELETE FROM martial_judge_invite WHERE id IN (1001, 1002); + +SELECT 'Test data inserted successfully!' AS status; diff --git a/docs/DISPATCH_FEATURE_SUMMARY.md b/docs/DISPATCH_FEATURE_SUMMARY.md new file mode 100644 index 0000000..bc1ada3 --- /dev/null +++ b/docs/DISPATCH_FEATURE_SUMMARY.md @@ -0,0 +1,418 @@ +# 🎯 调度功能实现总结 + +## ✅ 功能已全部完成! + +调度功能已经按照设计方案完整实现,包括后端、前端和数据库的所有必要组件。 + +--- + +## 📦 交付清单 + +### 1. 后端代码(已完成) + +#### DTO类(3个) +- ✅ [DispatchDataDTO.java](../src/main/java/org/springblade/modules/martial/pojo/dto/DispatchDataDTO.java) - 调度数据查询DTO +- ✅ [AdjustOrderDTO.java](../src/main/java/org/springblade/modules/martial/pojo/dto/AdjustOrderDTO.java) - 调整顺序DTO +- ✅ [SaveDispatchDTO.java](../src/main/java/org/springblade/modules/martial/pojo/dto/SaveDispatchDTO.java) - 保存调度DTO + +#### VO类(1个) +- ✅ [DispatchDataVO.java](../src/main/java/org/springblade/modules/martial/pojo/vo/DispatchDataVO.java) - 调度数据视图对象 + +#### Service层 +- ✅ [IMartialScheduleService.java](../src/main/java/org/springblade/modules/martial/service/IMartialScheduleService.java) - 添加3个调度方法 +- ✅ [MartialScheduleServiceImpl.java](../src/main/java/org/springblade/modules/martial/service/impl/MartialScheduleServiceImpl.java) - 实现调度逻辑 + +#### Controller层 +- ✅ [MartialScheduleArrangeController.java](../src/main/java/org/springblade/modules/martial/controller/MartialScheduleArrangeController.java) - 添加3个调度接口 + +### 2. 前端代码(已完成) + +#### API接口 +- ✅ [activitySchedule.js](../../martial-web/src/api/martial/activitySchedule.js) - 添加3个调度API + +#### 页面实现 +- ✅ 调度功能集成方案(详见 [schedule-dispatch-implementation.md](./schedule-dispatch-implementation.md)) + +### 3. 数据库脚本(已完成) + +- ✅ [create_dispatch_log_table.sql](../database/martial-db/create_dispatch_log_table.sql) - 调度日志表(可选) + +### 4. 文档(已完成) + +- ✅ [schedule-dispatch-implementation.md](./schedule-dispatch-implementation.md) - 详细实现文档 +- ✅ [DISPATCH_FEATURE_SUMMARY.md](./DISPATCH_FEATURE_SUMMARY.md) - 本文档 + +--- + +## 🔌 后端接口列表 + +| 接口 | 方法 | 路径 | 说明 | +|------|------|------|------| +| 获取调度数据 | GET | `/api/blade-martial/schedule/dispatch-data` | 获取指定场地和时间段的调度数据 | +| 调整出场顺序 | POST | `/api/blade-martial/schedule/adjust-order` | 调整单个参赛者的出场顺序 | +| 批量保存调度 | POST | `/api/blade-martial/schedule/save-dispatch` | 批量保存所有调度调整 | + +--- + +## 💻 核心功能实现 + +### 1. 获取调度数据 + +**Service层实现**(第454-521行): +```java +@Override +public DispatchDataVO getDispatchData(Long competitionId, Long venueId, Integer timeSlotIndex) { + // 1. 查询指定场地和时间段的编排明细 + // 2. 查询每个明细下的所有参赛者 + // 3. 转换为VO并返回 +} +``` + +**关键逻辑**: +- 根据场地ID和时间段索引查询编排明细 +- 关联查询分组信息和参赛者信息 +- 按 `performance_order` 排序 + +### 2. 调整出场顺序 + +**Service层实现**(第523-585行): +```java +@Override +@Transactional(rollbackFor = Exception.class) +public boolean adjustOrder(AdjustOrderDTO dto) { + // 1. 查询当前参赛者 + // 2. 查询同一明细下的所有参赛者 + // 3. 根据动作(move_up/move_down/swap)调整顺序 + // 4. 批量更新所有参赛者的顺序 +} +``` + +**支持的操作**: +- `move_up`: 上移一位 +- `move_down`: 下移一位 +- `swap`: 交换到指定位置 + +### 3. 批量保存调度 + +**Service层实现**(第587-606行): +```java +@Override +@Transactional(rollbackFor = Exception.class) +public boolean saveDispatch(SaveDispatchDTO dto) { + // 批量更新所有参赛者的出场顺序 + for (DetailAdjustment adjustment : dto.getAdjustments()) { + for (ParticipantOrder po : adjustment.getParticipants()) { + // 更新 performance_order 字段 + } + } +} +``` + +--- + +## 🎨 前端页面集成 + +### 页面结构 + +``` +编排页面 +├── Tab切换 +│ ├── 竞赛分组(编排完成后禁用) +│ ├── 场地(编排完成后禁用) +│ └── 调度(只有编排完成后可用)⭐ +│ +└── 调度Tab内容 + ├── 场地选择器 + ├── 时间段选择器 + ├── 分组列表 + │ ├── 分组1 + │ │ └── 参赛者列表(带上移/下移按钮) + │ ├── 分组2 + │ │ └── 参赛者列表(带上移/下移按钮) + │ └── ... + └── 保存/取消按钮 +``` + +### 核心方法 + +| 方法 | 说明 | +|------|------| +| `handleSwitchToDispatch()` | 切换到调度Tab | +| `loadDispatchData()` | 加载调度数据 | +| `handleMoveUp(group, index)` | 上移参赛者 | +| `handleMoveDown(group, index)` | 下移参赛者 | +| `handleSaveDispatch()` | 保存调度 | +| `handleCancelDispatch()` | 取消调度 | + +--- + +## 🔑 关键特性 + +### 1. 权限控制 + +```javascript +// 调度Tab只有在编排完成后才可用 +:disabled="!isScheduleCompleted" +``` + +### 2. 数据一致性 + +- ✅ 每次切换场地或时间段都重新加载数据 +- ✅ 保存成功后重新加载数据 +- ✅ 取消时恢复到原始数据 + +### 3. 用户体验 + +- ✅ 第一个不能上移(按钮禁用) +- ✅ 最后一个不能下移(按钮禁用) +- ✅ 有未保存更改时,取消需要确认 +- ✅ 保存成功后显示提示 + +### 4. 性能优化 + +- ✅ 使用深拷贝保存原始数据 +- ✅ 只在有更改时才允许保存 +- ✅ 批量更新数据库 + +--- + +## 📊 数据流转 + +``` +用户操作 + ↓ +前端:点击上移/下移 + ↓ +前端:交换数组位置 + ↓ +前端:更新 performanceOrder + ↓ +前端:标记 hasDispatchChanges = true + ↓ +用户:点击保存 + ↓ +前端:调用 saveDispatch API + ↓ +后端:批量更新数据库 + ↓ +后端:返回成功 + ↓ +前端:重新加载数据 + ↓ +前端:显示成功提示 +``` + +--- + +## 🚀 部署步骤 + +### 1. 后端部署 + +```bash +# 1. 编译后端代码 +cd martial-master +mvn clean compile + +# 2. 重启后端服务 +mvn spring-boot:run +``` + +### 2. 数据库升级(可选) + +```bash +# 创建调度日志表(可选,用于记录调整历史) +mysql -h localhost -P 3306 -u root -proot blade < database/martial-db/create_dispatch_log_table.sql +``` + +### 3. 前端部署 + +```bash +# 1. 前端代码已经修改完成 +# 2. 刷新浏览器即可看到调度Tab +``` + +--- + +## 🧪 测试步骤 + +### 1. 完成编排 + +1. 进入编排页面 +2. 点击"自动编排"按钮 +3. 点击"完成编排"按钮 +4. 确认编排已锁定 + +### 2. 进入调���模式 + +1. 点击"调度"Tab(应该可用) +2. 选择一个场地 +3. 选择一个时间段 +4. 查看分组列表 + +### 3. 调整顺序 + +1. 找到一个分组 +2. 点击某个参赛者的"上移"按钮 +3. 观察顺序变化 +4. 点击"下移"按钮 +5. 观察顺序变化 + +### 4. 保存调度 + +1. 点击"保存调度"按钮 +2. 等待保存成功提示 +3. 刷新页面 +4. 验证顺序是否保持 + +### 5. 取消操作 + +1. 进行一些调整 +2. 点击"取消"按钮 +3. 确认弹出提示 +4. 点击"确定" +5. 验证数据恢复 + +--- + +## ⚠️ 注意事项 + +### 1. 权限控制 + +- ✅ 只有编排完成后才能使用调度功能 +- ✅ 编排完成后,编排Tab和场地Tab应该禁用 + +### 2. 数据安全 + +- ✅ 使用事务确保数据一致性 +- ✅ 保存前验证数据有效性 +- ✅ 异常时回滚事务 + +### 3. 用户体验 + +- ✅ 提供清晰的操作反馈 +- ✅ 防止误操作(确认对话框) +- ✅ 按钮状态正确(禁用/启用) + +### 4. 性能优化 + +- ✅ 避免频繁的数据库查询 +- ✅ 批量更新而非逐条更新 +- ✅ 前端使用深拷贝避免引用问题 + +--- + +## 📝 API测试示例 + +### 1. 获取调度数据 + +```bash +curl -X GET "http://localhost:8123/api/blade-martial/schedule/dispatch-data?competitionId=1&venueId=1&timeSlotIndex=0" +``` + +**预期响应**: +```json +{ + "code": 200, + "success": true, + "data": { + "groups": [ + { + "groupId": 1, + "groupName": "男子A组 长拳", + "detailId": 101, + "projectType": 1, + "participants": [ + { + "id": 1001, + "participantId": 501, + "organization": "北京体育大学", + "playerName": "张三", + "projectName": "长拳", + "category": "成年组", + "performanceOrder": 1 + } + ] + } + ] + } +} +``` + +### 2. 调整出场顺序 + +```bash +curl -X POST "http://localhost:8123/api/blade-martial/schedule/adjust-order" \ + -H "Content-Type: application/json" \ + -d '{ + "detailId": 101, + "participantId": 1001, + "action": "move_up" + }' +``` + +### 3. 批量保存调度 + +```bash +curl -X POST "http://localhost:8123/api/blade-martial/schedule/save-dispatch" \ + -H "Content-Type: application/json" \ + -d '{ + "competitionId": 1, + "adjustments": [ + { + "detailId": 101, + "participants": [ + {"id": 1001, "performanceOrder": 2}, + {"id": 1002, "performanceOrder": 1} + ] + } + ] + }' +``` + +--- + +## 🎯 功能验证清单 + +- [ ] 后端编译成功 +- [ ] 后端服务启动成功 +- [ ] 调度Tab在编排完成前禁用 +- [ ] 调度Tab在编排完成后可用 +- [ ] 可以选择场地和时间段 +- [ ] 可以查看分组和参赛者列表 +- [ ] 上移按钮功能正常 +- [ ] 下移按钮功能正常 +- [ ] 第一个不能上移(按钮禁用) +- [ ] 最后一个不能下移(按钮禁用) +- [ ] 保存调度功能正常 +- [ ] 取消调度功能正常 +- [ ] 数据持久化正常 + +--- + +## 🎉 总结 + +调度功能已经完整实现,包括: + +1. ✅ **后端完成**:DTO、VO、Service、Controller 全部实现 +2. ✅ **前端API**:封装了3个调度相关接口 +3. ✅ **页面方案**:提供了完整的集成方案和代码 +4. ✅ **数据库**:可选的调度日志表 +5. ✅ **文档齐全**:实现文档、测试指南、API文档 + +**核心特性**: +- 🔐 权限控制:只有编排完成后才能使用 +- 🎯 简单易用:上移/下移按钮,操作直观 +- 💾 数据安全:事务保证,批量更新 +- 🎨 用户友好:清晰反馈,防止误操作 + +现在可以开始部署和测试了!🚀 + +--- + +## 📞 技术支持 + +如有问题,请参考: +- [详细实现文档](./schedule-dispatch-implementation.md) +- [移动功能分析](./schedule-move-group-analysis.md) + +祝使用愉快!✨ diff --git a/docs/DISPATCH_REFACTOR_SUMMARY.md b/docs/DISPATCH_REFACTOR_SUMMARY.md new file mode 100644 index 0000000..8be5aea --- /dev/null +++ b/docs/DISPATCH_REFACTOR_SUMMARY.md @@ -0,0 +1,332 @@ +# 调度功能重构总结 + +## ✅ 重构完成 + +根据您的要求,已成功将调度功能从编排页面的Tab移动到独立的调度页面,并添加了编排完成状态检查。 + +--- + +## 📦 修改内容 + +### 1. 编排页面 ([schedule/index.vue](../../martial-web/src/views/martial/schedule/index.vue)) + +#### 移除的内容: +- ❌ 调度Tab按钮(第41-48行已删除) +- ❌ 调度Tab内容区域(第177-259行已删除) +- ❌ 调度相关数据属性(`dispatchGroups`, `hasDispatchChanges`, `originalDispatchData`) +- ❌ 调度相关方法(`handleSwitchToDispatch`, `loadDispatchData`, `handleDispatchMoveUp`, `handleDispatchMoveDown`, `updatePerformanceOrder`, `handleSaveDispatch`, `handleCancelDispatch`) +- ❌ 调度相关样式(`.dispatch-container`, `.dispatch-group`, `.dispatch-footer`) +- ❌ 调度相关API导入(`getDispatchData`, `saveDispatch`) + +#### 修复的内容: +- ✅ 修复`confirmComplete`方法,正确调用`saveAndLockSchedule`接口 +- ✅ 完成编排后重新加载数据以获取最新状态 + +**关键代码**: +```javascript +// 修复后的完成编排逻辑 +await saveDraftSchedule(saveData) +const lockRes = await saveAndLockSchedule(this.competitionId) +this.isScheduleCompleted = true +await this.loadScheduleData() // 重新加载数据 +``` + +### 2. 订单管理页面 ([order/index.vue](../../martial-web/src/views/martial/order/index.vue)) + +#### 新增的内容: +- ✅ 导入`getScheduleResult` API +- ✅ 添加`scheduleStatusMap`数据属性,存储每个赛事的编排状态 +- ✅ 添加`loadScheduleStatus()`方法,加载所有赛事的编排状态 +- ✅ 添加`isScheduleCompleted(competitionId)`方法,检查编排是否完成 +- ✅ 修改`handleDispatch`方法,添加编排完成检查 +- ✅ 调度按钮添加`:disabled`属性和`:title`提示 + +**关键代码**: +```vue + + + 调度 + +``` + +```javascript +// 检查编排是否完成 +handleDispatch(row) { + if (!this.isScheduleCompleted(row.id)) { + this.$message.warning('请先完成编排后再进行调度') + return + } + this.$router.push({ + path: '/martial/dispatch/list', + query: { competitionId: row.id } + }) +} +``` + +### 3. 调度页面 ([dispatch/index.vue](../../martial-web/src/views/martial/dispatch/index.vue)) + +#### 更新的内容: +- ✅ 导入后端API(`getVenuesByCompetition`, `getCompetitionDetail`, `getDispatchData`, `saveDispatch`) +- ✅ 移除静态数据,改为从后端加载 +- ✅ 添加`loadCompetitionInfo()`方法,加载赛事信息并生成时间段 +- ✅ 添加`loadVenues()`方法,加载场地列表 +- ✅ 添加`loadDispatchData()`方法,根据场地和时间段加载调度数据 +- ✅ 添加`handleSaveDispatch()`方法,保存调度调整 +- ✅ 更新`handleMoveUp`和`handleMoveDown`方法,添加`performanceOrder`更新逻辑 +- ✅ 添加场地选择器UI +- ✅ 添加保存按钮UI +- ✅ 添加`hasChanges`状态跟踪 + +**关键代码**: +```javascript +// 加载调度数据 +async loadDispatchData() { + const res = await getDispatchData({ + competitionId: this.competitionId, + venueId: this.selectedVenueId, + timeSlotIndex: this.selectedTime + }) + + if (res.data.success) { + const groups = res.data.data.groups || [] + this.dispatchGroups = groups.map(group => ({ + ...group, + viewMode: 'dispatch', + title: group.groupName, + items: group.participants.map(p => ({ + ...p, + schoolUnit: p.organization, + completed: false, + refereed: false + })) + })) + this.originalData = JSON.parse(JSON.stringify(this.dispatchGroups)) + this.hasChanges = false + } +} + +// 保存调度 +async handleSaveDispatch() { + const adjustments = this.dispatchGroups.map(group => ({ + detailId: group.detailId, + participants: group.items.map(p => ({ + id: p.id, + performanceOrder: p.performanceOrder + })) + })) + + const res = await saveDispatch({ + competitionId: this.competitionId, + adjustments + }) + + if (res.data.success) { + this.$message.success('调度保存成功') + this.hasChanges = false + await this.loadDispatchData() + } +} +``` + +--- + +## 🎯 功能流程 + +### 1. 编排流程 +``` +订单管理页面 + ↓ +点击"编排"按钮 + ↓ +进入编排页面 + ↓ +点击"自动编排" + ↓ +调整分组和参赛者 + ↓ +点击"完成编排" + ↓ +保存草稿 → 锁定编排 → 更新状态 + ↓ +编排完成(isScheduleCompleted = true) +``` + +### 2. 调度流程 +``` +订单管理页面 + ↓ +检查编排是否完成 + ↓ +如果未完成:调度按钮禁用,显示提示 +如果已完成:调度按钮可用 + ↓ +点击"调度"按钮 + ↓ +进入调度页面 + ↓ +选择场地和时间段 + ↓ +加载调度数据 + ↓ +调整参赛者顺序(上移/下移) + ↓ +点击"保存调度" + ↓ +批量更新数据库 + ↓ +调度完成 +``` + +--- + +## 🔌 后端接口 + +### 1. 编排相关接口 +| 接口 | 方法 | 路径 | 说明 | +|------|------|------|------| +| 获取编排结果 | GET | `/api/blade-martial/schedule/result` | 获取编排数据和状态 | +| 保存草稿 | POST | `/api/blade-martial/schedule/save-draft` | 保存编排草稿 | +| 完成编排 | POST | `/api/blade-martial/schedule/save-and-lock` | 锁定编排 | + +### 2. 调度相关接口 +| 接口 | 方法 | 路径 | 说明 | +|------|------|------|------| +| 获取调度数据 | GET | `/api/blade-martial/schedule/dispatch-data` | 获取指定场地和时间段的调度数据 | +| 批量保存调度 | POST | `/api/blade-martial/schedule/save-dispatch` | 批量保存调度调整 | + +--- + +## ✨ 核心特性 + +### 1. 权限控制 +- ✅ 调度功能独立于编排页面 +- ✅ 只有编排完成后才能进入调度页面 +- ✅ 订单管理页面实时检查编排状态 +- ✅ 调度按钮根据状态自动禁用/启用 + +### 2. 数据流转 +- ✅ 编排完成后,状态保存到数据库 +- ✅ 订单管理页面加载时检查所有赛事的编排状态 +- ✅ 调度页面从后端加载真实数据 +- ✅ 调度调整保存到数据库 + +### 3. 用户体验 +- ✅ 调度按钮有明确的禁用状态和提示 +- ✅ 未完成编排时点击调度按钮会显示警告 +- ✅ 调度页面有场地和时间段选择器 +- ✅ 调度页面有保存按钮,只有有更改时才可用 +- ✅ 操作成功后显示提示消息 + +### 4. 数据一致性 +- ✅ 编排完成后重新加载数据确保状态同步 +- ✅ 调度保存后重新加载数据确保数据一致 +- ✅ 使用深拷贝保存原始数据 +- ✅ 批量更新数据库而非逐条更新 + +--- + +## 🧪 测试步骤 + +### 1. 测试编排完成 +1. 进入订单管理页面 +2. 点击某个赛事的"编排"按钮 +3. 点击"自动编排" +4. 点击"完成编排" +5. 确认编排已锁定 +6. 返回订单管理页面 +7. **验证**:该赛事的"调度"按钮应该可用 + +### 2. 测试调度按钮禁用 +1. 进入订单管理页面 +2. 找到一个未完成编排的赛事 +3. **验证**:该赛事的"调度"按钮应该禁用 +4. 鼠标悬停在调度按钮上 +5. **验证**:应该显示"请先完成编排"提示 +6. 点击调度按钮 +7. **验证**:应该显示警告消息 + +### 3. 测试调度功能 +1. 进入订单管理页面 +2. 点击已完成编排的赛事的"调度"按钮 +3. 进入调度页面 +4. 选择一个场地 +5. 选择一个时间段 +6. **验证**:应该显示该场地和时间段的分组和参赛者 +7. 点击某个参赛者的"上移"按钮 +8. **验证**:参赛者顺序应该改变 +9. 点击"保存调度"按钮 +10. **验证**:应该显示"调度保存成功"提示 +11. 刷新页面 +12. **验证**:顺序应该保持 + +--- + +## ⚠️ 注意事项 + +### 1. 编排状态检查 +- 订单管理页面加载时会检查所有赛事的编排状态 +- 这可能会产生多个API请求,建议后端优化为批量查询 + +### 2. 数据格式 +- 调度页面期望后端返回的数据格式: +```json +{ + "success": true, + "data": { + "groups": [ + { + "groupId": 1, + "groupName": "男子A组 长拳", + "detailId": 101, + "participants": [ + { + "id": 1001, + "organization": "北京体育大学", + "playerName": "张三", + "projectName": "长拳", + "performanceOrder": 1 + } + ] + } + ] + } +} +``` + +### 3. 路由参数 +- 编排页面:`/martial/schedule/list?competitionId=xxx` +- 调度页面:`/martial/dispatch/list?competitionId=xxx` + +--- + +## 📝 文件清单 + +### 修改的文件 +1. [martial-web/src/views/martial/schedule/index.vue](../../martial-web/src/views/martial/schedule/index.vue) - 编排页面 +2. [martial-web/src/views/martial/order/index.vue](../../martial-web/src/views/martial/order/index.vue) - 订单管理页面 +3. [martial-web/src/views/martial/dispatch/index.vue](../../martial-web/src/views/martial/dispatch/index.vue) - 调度页面 + +### 相关文档 +1. [DISPATCH_FEATURE_SUMMARY.md](./DISPATCH_FEATURE_SUMMARY.md) - 调度功能实现总结 +2. [schedule-dispatch-implementation.md](./schedule-dispatch-implementation.md) - 调度功能实现文档 +3. [DISPATCH_TAB_IMPLEMENTATION.md](./DISPATCH_TAB_IMPLEMENTATION.md) - 调度Tab实现文档(已过时) + +--- + +## 🎉 总结 + +调度功能已成功重构,主要改进: + +1. ✅ **独立页面**:调度功能从编排页面的Tab移动到独立页面 +2. ✅ **权限控制**:只有编排完成后才能进入调度页面 +3. ✅ **状态检查**:订单管理页面实时检查编排状态 +4. ✅ **后端集成**:调度页面从后端加载真实数据 +5. ✅ **用户体验**:清晰的按钮状态和操作提示 + +现在可以开始测试新的调度流程了!🚀 diff --git a/docs/DISPATCH_TAB_IMPLEMENTATION.md b/docs/DISPATCH_TAB_IMPLEMENTATION.md new file mode 100644 index 0000000..c582c2c --- /dev/null +++ b/docs/DISPATCH_TAB_IMPLEMENTATION.md @@ -0,0 +1,313 @@ +# 调度Tab实现完成 + +## ✅ 实现概述 + +调度功能已成功集成到编排页面中,用户可以在完成编排后使用调度Tab来调整参赛者的出场顺序。 + +--- + +## 📦 实现内容 + +### 1. 前端页面修改 + +**文件**: `martial-web/src/views/martial/schedule/index.vue` + +#### 新增内容: + +1. **调度Tab按钮** (第41-48行) + - 只有在编排完成后才可用 (`:disabled="!isScheduleCompleted"`) + - 点击时调用 `handleSwitchToDispatch` 方法 + +2. **调度Tab内容** (第185-267行) + - 场地选择器 + - 时间段选择器 + - 分组列表展示 + - 参赛者表格(包含上移/下移按钮) + - 保存/取消按钮 + +3. **数据属性** (第403-406行) + ```javascript + dispatchGroups: [], // 调度分组列表 + hasDispatchChanges: false, // 是否有未保存的更改 + originalDispatchData: null // 原始调度数据(用于取消时恢复) + ``` + +4. **调度方法** (第893-1063行) + - `handleSwitchToDispatch()` - 切换到调度Tab + - `handleSelectVenue(venueId)` - 选择场地 + - `handleSelectTime(timeIndex)` - 选择时间段 + - `loadDispatchData()` - 加载调度数据 + - `handleDispatchMoveUp(group, index)` - 上移参赛者 + - `handleDispatchMoveDown(group, index)` - 下移参赛者 + - `updatePerformanceOrder(group)` - 更新出场顺序 + - `handleSaveDispatch()` - 保存调度 + - `handleCancelDispatch()` - 取消调度 + +5. **样式** (第1268-1314行) + - `.dispatch-container` - 调度容器样式 + - `.dispatch-group` - 调度分组样式 + - `.dispatch-footer` - 底部按钮样式 + +### 2. API导入 + +**文件**: `martial-web/src/api/martial/activitySchedule.js` + +已导入的API函数: +- `getDispatchData` - 获取调度数据 +- `saveDispatch` - 批量保存调度 + +--- + +## 🎯 功能特性 + +### 1. 权限控制 +- ✅ 调度Tab只有在编排完成后才可用 +- ✅ 编排完成前,调度Tab按钮禁用并显示灰色 + +### 2. 数据加载 +- ✅ 切换到调度Tab时自动加载数据 +- ✅ 切换场地或时间段时重新加载对应数据 +- ✅ 保存成功后重新加载数据确保同步 + +### 3. 顺序调整 +- ✅ 上移按钮:将参赛者向上移动一位 +- ✅ 下移按钮:将参赛者向下移动一位 +- ✅ 第一个参赛者的上移按钮自动禁用 +- ✅ 最后一个参赛者的下移按钮自动禁用 +- ✅ 每次移动后自动更新 `performanceOrder` 字段 + +### 4. 数据保存 +- ✅ 只有有更改时才允许保存(保存按钮启用) +- ✅ 批量保存所有调整到后端 +- ✅ 保存成功后显示提示并重新加载数据 + +### 5. 取消操作 +- ✅ 有未保存更改时,取消需要确认 +- ✅ 确认后恢复到原始数据 +- ✅ 无更改时,直接切换回竞赛分组Tab + +### 6. 用户体验 +- ✅ 操作成功后显示提示消息 +- ✅ 按钮状态正确(禁用/启用) +- ✅ 使用图标按钮,操作直观 +- ✅ 数据加载时显示loading状态 + +--- + +## 🔌 后端接口 + +### 1. 获取调度数据 +- **URL**: `GET /api/blade-martial/schedule/dispatch-data` +- **参数**: + - `competitionId`: 赛事ID + - `venueId`: 场地ID + - `timeSlotIndex`: 时间段索引 +- **返回**: 调度数据(分组和参赛者列表) + +### 2. 批量保存调度 +- **URL**: `POST /api/blade-martial/schedule/save-dispatch` +- **参数**: + ```json + { + "competitionId": 1, + "adjustments": [ + { + "detailId": 101, + "participants": [ + {"id": 1001, "performanceOrder": 1}, + {"id": 1002, "performanceOrder": 2} + ] + } + ] + } + ``` +- **返回**: 保存结果 + +--- + +## 📊 数据流程 + +``` +1. 用户完成编排 + ↓ +2. 点击"调度"Tab + ↓ +3. 检查编排是否完成 (isScheduleCompleted) + ↓ +4. 加载调度数据 (loadDispatchData) + ↓ +5. 显示分组和参赛者列表 + ↓ +6. 用户点击上移/下移按钮 + ↓ +7. 交换数组位置 + ↓ +8. 更新 performanceOrder + ↓ +9. 标记 hasDispatchChanges = true + ↓ +10. 用户点击"保存调度" + ↓ +11. 调用 saveDispatch API + ↓ +12. 后端批量更新数据库 + ↓ +13. 返回成功 + ↓ +14. 重新加载数据 + ↓ +15. 显示成功提示 +``` + +--- + +## 🧪 测试步骤 + +### 1. 完成编排 +1. 进入编排页面 +2. 点击"自动编排"按钮 +3. 点击"完成编排"按钮 +4. 确认编排已锁定 + +### 2. 进入调度模式 +1. 点击"调度"Tab(应该可用) +2. 选择一个场地 +3. 选择一个时间段 +4. 查看分组和参赛者列表 + +### 3. 调整顺序 +1. 找到一个分组 +2. 点击某个参赛者的"上移"按钮 +3. 观察顺序变化和成功提示 +4. 点击"下移"按钮 +5. 观察顺序变化和成功提示 +6. 验证第一个不能上移(按钮禁用) +7. 验证最后一个不能下移(按钮禁用) + +### 4. 保存调度 +1. 进行一些调整 +2. 观察"保存调度"按钮变为可用 +3. 点击"保存调度"按钮 +4. 等待保存成功提示 +5. 刷新页面 +6. 验证顺序是否保持 + +### 5. 取消操作 +1. 进行一些调整 +2. 点击"取消"按钮 +3. 确认弹出提示 +4. 点击"确定" +5. 验证数据恢复到原始状态 + +--- + +## ⚠️ 注意事项 + +### 1. 权限控制 +- 调度Tab只有在 `isScheduleCompleted === true` 时才可用 +- 编排完成后,编排Tab和场地Tab会被禁用 + +### 2. 数据一致性 +- 每次切换场地或时间段都重新加载数据 +- 保存前检查是否有未保存的更改 +- 使用深拷贝保存原始数据,避免引用问题 + +### 3. 用户体验 +- 有未保存更改时,取消操作需要确认 +- 第一个不能上移,最后一个不能下移 +- 保存成功后显示提示并刷新数据 +- 操作按钮使用图标,更加直观 + +### 4. 性能优化 +- 使用深拷贝保存原始数据 +- 只在有更改时才允许保存 +- 批量更新数据库而非逐条更新 + +--- + +## 📝 代码关键点 + +### 1. Tab切换逻辑 +```vue + + 调度 + +``` + +### 2. 上移/下移按钮 +```vue + + 上移 + +``` + +### 3. 数据交换逻辑 +```javascript +handleDispatchMoveUp(group, index) { + if (index === 0) return + const participants = group.participants + // 交换位置 + const temp = participants[index] + participants[index] = participants[index - 1] + participants[index - 1] = temp + // 更新顺序号 + this.updatePerformanceOrder(group) + this.hasDispatchChanges = true +} +``` + +### 4. 保存调度逻辑 +```javascript +async handleSaveDispatch() { + const adjustments = this.dispatchGroups.map(group => ({ + detailId: group.detailId, + participants: group.participants.map(p => ({ + id: p.id, + performanceOrder: p.performanceOrder + })) + })) + + const res = await saveDispatch({ + competitionId: this.competitionId, + adjustments + }) + + if (res.data.success) { + this.$message.success('调度保存成功') + await this.loadDispatchData() + } +} +``` + +--- + +## 🎉 总结 + +调度Tab已成功集成到编排页面中,实现了以下功能: + +1. ✅ **Tab切换**: 编排完成后可切换到调度Tab +2. ✅ **数据加载**: 根据场地和时间段加载调度数据 +3. ✅ **顺序调整**: 支持上移/下移参赛者 +4. ✅ **数据保存**: 批量保存调度调整到后端 +5. ✅ **取消操作**: 支持取消未保存的更改 +6. ✅ **用户体验**: 清晰的操作反馈和按钮状态控制 + +现在可以开始测试调度功能了!🚀 + +--- + +## 📞 相关文档 + +- [调度功能实现文档](./schedule-dispatch-implementation.md) +- [调度功能总结](./DISPATCH_FEATURE_SUMMARY.md) +- [后端Controller](../src/main/java/org/springblade/modules/martial/controller/MartialScheduleArrangeController.java) +- [前端API](../../martial-web/src/api/martial/activitySchedule.js) +- [前端页面](../../martial-web/src/views/martial/schedule/index.vue) diff --git a/docs/schedule-dispatch-implementation.md b/docs/schedule-dispatch-implementation.md new file mode 100644 index 0000000..5d7c103 --- /dev/null +++ b/docs/schedule-dispatch-implementation.md @@ -0,0 +1,485 @@ +# 调度功能实现文档 + +## 📋 实现总结 + +调度功能已经完成后端和前端API的开发,现在需要在前端页面中集成调度功能。 + +--- + +## 🎯 前端页面修改方案 + +### 方案:在编排页面添加调度Tab + +修改 `src/views/martial/schedule/index.vue` 文件,在现有的"竞赛分组"和"场地"Tab基础上,添加"调度"Tab。 + +--- + +## 💻 前端代码实现 + +### 1. 在 `