# 赛程编排功能实施完成文档

## 📋 实施概述

**实施日期**: 2025-12-08
**版本**: v1.0
**状态**: ✅ 核心功能已完成

---

## 1. 已完成的功能

### ✅ 1.1 数据库表创建

创建了两张核心数据库表:

**文件位置**: `doc/create_schedule_tables.sql`

#### martial_schedule (赛程安排表)
- 存储分组的基本信息
- 包含场地分配、时间段分配
- 支持草稿和发布状态

#### martial_schedule_detail (赛程明细表)
- 存储每个分组中的参赛人员详情
- 记录实际比赛时间
- 支持比赛进度跟踪

**执行方式**:
```bash
# 方式1: 通过数据库客户端导入并执行
# 方式2: 命令行执行
mysql -u root -p martial_competition < doc/create_schedule_tables.sql
```

### ✅ 1.2 前端赛程编排页面完善

**文件位置**: `src/views/martial/schedule/index.vue`

#### 核心算法实现:

1. **时间段自动生成** (generateTimeSlots方法)
   - 根据赛事开始/结束时间自动生成
   - 上午场: 08:30-12:00
   - 下午场: 13:30-17:30
   - 支持多天赛程

2. **智能自动分组** (autoGroupParticipants方法)
   - ✅ 集体项目优先(type=2)
   - ✅ 个人项目在后(type=1)
   - ✅ 集体项目按"单位+项目"分组
   - ✅ 个人项目按"项目+组别"分组,每组最多30人
   - ✅ 自动生成分组名称和编号

3. **场地自动分配** (autoAssignVenues方法)
   - ✅ 负载均衡算法
   - ✅ 优先分配时长长的分组
   - ✅ 选择当前负载最小的场地
   - ✅ 均匀分布,避免某个场地过载

4. **分组名称编辑**
   - ✅ 双击分组名称进入编辑模式
   - ✅ Enter保存,失焦保存
   - ✅ 实时更新显示

5. **拖拽移动分组**
   - ✅ 使用vuedraggable组件
   - ✅ 支持在场地间拖拽移动
   - ✅ 支持场地内排序

---

## 2. 功能使用流程

### 2.1 基本操作流程

```
1. 进入赛事管理 → 选择赛事 → 点击"编排"按钮
   ↓
2. 系统自动加载:
   - 赛事信息
   - 时间段列表 (根据赛事时间自动生成)
   - 场地列表
   - 所有参赛者数据
   ↓
3. 点击"自动编排"按钮
   ↓
4. 系统自动完成:
   - 按集体/个人分类参赛者
   - 智能分组 (集体按单位+项目, 个人按项目+组别)
   - 自动分配场地 (负载均衡)
   ↓
5. 手动调整 (可选):
   - 双击分组名称修改
   - 拖拽分组到其他场地
   - 调整分组内选手顺序
   - 选择场地下拉菜单移动分组
   ↓
6. 保存编排 / 完成编排
```

### 2.2 时间段切换

- 点击页面顶部的时间段按钮
- 可查看不同时间段的分组安排
- 每个时间段独立管理分组

### 2.3 场地视图

- 切换到"场地"Tab
- 查看每个场地的分组分布
- 统计每个场地的预计时长

---

## 3. 核心算法说明

### 3.1 自动分组算法

```javascript
autoGroupParticipants(participants) {
  // 1. 分离集体(type=2)和个人(type=1)
  const teamProjects = participants.filter(p => p.type === 2)
  const individualProjects = participants.filter(p => p.type === 1)

  // 2. 集体项目: 按"organization_projectId"分组
  // 3. 个人项目: 按"projectId_category"分组,每组最多30人
  // 4. 返回: [集体分组, 个人分组]
}
```

**特点**:
- 集体项目同单位同项目的选手分在一组
- 个人项目同项目同组别的选手分在一组
- 个人项目超过30人自动拆分为A组、B组、C组...

### 3.2 场地分配算法 (贪心 + 负载均衡)

```javascript
autoAssignVenues(groups) {
  // 1. 初始化场地负载为0
  // 2. 分组按预计时长降序排序
  // 3. 贪心策略:
  //    - 找当前负载最小的场地
  //    - 分配分组到该场地
  //    - 更新场地负载
}
```

**特点**:
- 先分配时间长的分组,后分配时间短的
- 总是选择负载最轻的场地
- 确保各场地负载均衡

---

## 4. 测试用例

### 4.1 使用1000个参赛者测试

**前提条件**:
1. 已执行 `test-data/batch_create_1000_participants.sql`
2. 赛事ID=200: "郑州协会全国运动大赛"
3. 包含10个项目、5个场地、1000个参赛者

**测试步骤**:

#### 测试1: 自动编排功能
```
1. 进入赛事编排页面 (competitionId=200)
2. 点击"自动编排"按钮
3. 预期结果:
   - 自动生成分组 (集体项目在前,个人项目在后)
   - 每个分组自动分配场地
   - 场地负载均衡
   - 显示成功提示
```

#### 测试2: 分组名称编辑
```
1. 双击某个分组名称
2. 修改名称 (如: "少林寺武术学校 - 集体拳术表演" → "少林组")
3. 按Enter保存
4. 预期结果: 名称更新成功
```

#### 测试3: 场地切换
```
1. 点击某个分组的"选择场地"下拉菜单
2. 选择其他场地
3. 预期结果: 分组移动到新场地
```

#### 测试4: 时间段切换
```
1. 点击不同的时间段按钮
2. 预期结果: 显示对应时间段的分组列表
```

#### 测试5: 场地视图
```
1. 切换到"场地"Tab
2. 预期结果:
   - 显示每个场地的分组列表
   - 显示每个分组的预计时长
   - 统计汇总正确
```

### 4.2 边界测试

| 测试项 | 操作 | 预期结果 |
|--------|------|---------|
| 无参赛者 | 点击自动编排 | 提示"没有未分组的参赛者" |
| 无场地 | 点击自动分配场地 | 提示"请先配置场地信息" |
| 空分组名称 | 保存空名称 | 保持原名称 |
| 大量参赛者 | 1000人自动编排 | 正常处理,性能良好 |

---

## 5. 性能优化

### 5.1 已实现的优化

1. **项目信息缓存**
   - 使用Map缓存项目详情
   - 避免重复查询相同项目

2. **批量处理**
   - 一次性加载所有参赛者
   - 批量分组和分配

3. **算法优化**
   - 使用Map进行分组,时间复杂度O(n)
   - 负载均衡算法,时间复杂度O(n*m), n=分组数, m=场地数

### 5.2 未来可优化

1. **虚拟滚动**: 分组数量>100时使用虚拟滚动
2. **防抖保存**: 拖拽操作延迟保存
3. **懒加载**: 只加载当前时间段数据

---

## 6. 数据流转

```
用户操作
  ↓
前端Vue页面 (src/views/martial/schedule/index.vue)
  ↓
调用API (src/api/martial/...)
  ↓
后端接口 (待开发)
  ↓
数据库表 (martial_schedule, martial_schedule_detail)
```

---

## 7. 待开发功能

### 7.1 后端API接口

需要创建以下接口 (参考文档第5章):

1. **GET /api/martial/schedule/time-slots**
   - 获取时间段列表

2. **POST /api/martial/schedule/auto-group**
   - 自动生成分组

3. **PUT /api/martial/schedule/group/{groupId}/name**
   - 更新分组名称

4. **POST /api/martial/schedule/save**
   - 保存编排结果 (草稿)

5. **POST /api/martial/schedule/publish**
   - 发布编排 (status=1)

6. **POST /api/martial/schedule/auto-assign-venues**
   - 自动分配场地

7. **GET /api/martial/schedule/list**
   - 获取已保存的编排

### 7.2 功能增强

1. **保存草稿**: 将编排数据保存到数据库
2. **加载已保存编排**: 恢复之前的编排状态
3. **发布编排**: 确认完成后发布
4. **导出功能**: 导出Excel/PDF格式的赛程表
5. **打印功能**: 打印秩序册

---

## 8. 文件清单

### 已创建/修改的文件

```
✅ doc/schedule-system-analysis.md            # 系统设计文档 (1200+行)
✅ doc/create_schedule_tables.sql             # 数据库表创建SQL
✅ doc/schedule-feature-implementation.md     # 本文档
✅ src/views/martial/schedule/index.vue       # 前端编排页面 (已完善)
✅ test-data/batch_create_1000_participants.sql  # 测试数据
✅ doc/batch-create-participants-guide.md     # 测试数据使用指南
```

### 待创建的文件 (后端)

```
❌ backend/api/schedule.js         # 赛程编排API接口
❌ backend/service/schedule.js     # 赛程编排业务逻辑
❌ backend/mapper/schedule.xml     # 赛程数据访问SQL
```

---

## 9. 技术栈

| 技术 | 版本 | 用途 |
|------|------|------|
| Vue 3 | - | 前端框架 |
| Element Plus | - | UI组件库 |
| vuedraggable | - | 拖拽功能 |
| MySQL | 5.7+ | 数据库 |
| SpringBoot | 2.x | 后端框架(待开发) |

---

## 10. 常见问题

### Q1: 如何确定参赛者的项目类型?
**A**: 通过查询 `martial_project` 表的 `type` 字段:
- `type=1`: 个人项目
- `type=2`: 集体项目

### Q2: 个人项目为什么每组最多30人?
**A**: 这是为了避免单组人数过多,比赛时间过长。可以在代码中修改 `maxPerGroup` 变量。

### Q3: 如何自定义场地分配策略?
**A**: 修改 `autoAssignVenues` 方法中的分配逻辑,可以考虑:
- 场地容量限制
- 项目类型匹配 (如集体项目分配到大场地)
- 时间段容量限制

### Q4: 分组编号规则是什么?
**A**: GROUP_001, GROUP_002, ... 按生成顺序递增,集体项目编号在前。

---

## 11. 下一步计划

### 阶段1: 后端接口开发 (优先级: 高)
- [ ] 创建赛程编排相关API接口
- [ ] 实现数据持久化
- [ ] 实现加载已保存编排

### 阶段2: 功能完善 (优先级: 中)
- [ ] 保存草稿功能
- [ ] 发布编排功能
- [ ] 撤销/重做功能

### 阶段3: 导出功能 (优先级: 中)
- [ ] 导出Excel格式赛程表
- [ ] 导出PDF格式秩序册
- [ ] 二维码生成(选手扫码查看)

### 阶段4: 优化和扩展 (优先级: 低)
- [ ] 性能优化(虚拟滚动、懒加载)
- [ ] 冲突检测(同一选手多项目)
- [ ] 可视化增强(甘特图、热力图)

---

## 12. 联系与反馈

如有问题或建议,请记录在项目Issue中。

---

**文档维护**:
- 创建人: Claude Code
- 创建日期: 2025-12-08
- 版本: v1.0
- 最后更新: 2025-12-08
