forked from eesast/web
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'eesast:master' into master
- Loading branch information
Showing
47 changed files
with
8,233 additions
and
3,756 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
title: 赛事代码管理 | ||
description: 网站开发接口文档 | ||
permalink: /code | ||
--- | ||
|
||
> 要点:代码跟队伍走不跟角色走,编译跟代码走不跟比赛走,比赛跟角色走不跟代码走 | ||
### 流程描述(分工) | ||
|
||
- 代码提交 | ||
1. 选手在代码管理页面点击上传/拖入文件上传`AI.cpp`或`AI.py` | ||
2. 前端识别编程语言,并在数据库`contest_team_code`表插入新行,返回得`code_id` | ||
3. 前端根据`${code_id}.${lang}`重命名文件并上传至`cos`,上传路径见[COS存储桶访问路径约定](https://eesast.github.io/web/cos) | ||
4. 若语言为解释型语言(`py`),则前端更改数据库`compile_status`为`No Need`(可与第二步合并) | ||
5. 若语言为编译型语言(`cpp`),则前端向后端发请求`/code/compile-start`(见后),使后端开始编译代码 | ||
6. 后端下载`cos`上的代码文件,在服务器上启动编译`docker`,并在数据库中更新`compile_status`为`Compiling` | ||
7. `docker`完成编译后,请求后端`/code/compile-finish`路由(见后)。若编译成功无报错,后端在数据库中更新`compile_status`为`Completed`;若编译出错,后端在数据库中更新`compile_status`为`Failed` | ||
8. 后端将`docker`生成的可执行文件`${code_id}`与`${code_id}.log`上传至`cos`,同代码文件夹 | ||
9. 前端通过`subscription`实时更新`compile_status` | ||
- 代码重命名 | ||
- 前端页面上和数据库中的`code_name`是代码文件上传的原名,仅作展示和下载时的命名之用,与后端和`cos`没有关系。用户可以修改这个名字来做版本管理,仅需前端修改数据库`contest_team_code`表即可。 | ||
- 编译日志获取 | ||
- 前端直接下载`cos`上对应`${code_id}.log` | ||
- 代码删除 | ||
1. 用户在前端点击删除某份代码文件后,前端先删除`cos`上的`${code_id}.${lang}`、`${code_id}.log`和`${code_id}`可执行文件 | ||
2. 删除成功后,前端再修改数据库删除行,最后提示删除成功 | ||
- 角色代码选择 | ||
- 选手可以选择当时当刻进行天梯和比赛使用的代码,选择好后更新数据库`contest_team_player`表 | ||
|
||
### 后端路由路径 | ||
|
||
- `/code/compile-start`:下载代码文件并启动编译镜像。 | ||
- 请求方法:`POST` | ||
- 请求:`body`中有`{code_id: uuid}` | ||
- 响应:`200`:`200 OK: Create container success` | ||
- 错误: | ||
- `422`:`422 Unprocessable Entity: Missing credentials`(请求缺失参数) | ||
- `404`:`404 Not Found: Code unavailable`(无法成功下载代码) | ||
- `400`:`400 Bad Request: Interpreted language do not require compilation.` | ||
- `400`:`400 Bad Request: Unsupported language.` | ||
- `401`:`401 Unauthorized: User not in team.` | ||
- `401`:`401 Unauthorized: User and code not in the same team.` | ||
- `409`:`409 Confilct: Code already in compilation`(代码正在或已编译) | ||
- `500`:`undefined`(其他内部错误,返回报错信息) | ||
- `/code/compile-finish`:代码完成编译的`hook`,在`docker`结束前调用。更新编译状态并保存可执行文件和`log`。 | ||
- 请求方法:`POST` | ||
- 请求:`body`中有`{compile_status: string}`,且将启动docker时传入的`token`传回 | ||
- 响应:`200`:`200 OK: Update compile status success` | ||
- 错误: | ||
- `422`:`422 Unprocessable Entity: Missing credentials`(请求缺失参数) | ||
- `400`:`400 Bad Request: Invalid compile status.` | ||
- `401`:`401 Unauthorized: Missing token.` | ||
- `401`:`401 Unauthorized: Token expired or invalid.` | ||
- `500`:`undefined`(返回报错信息) | ||
|
||
### 与赛事组的约定 | ||
|
||
1. 编译代码的`docker`每次启动只编译一份代码,且只需考虑编译型语言(如`cpp`)的代码 | ||
2. `docker`启动时代码文件绑定在`/usr/local/code`文件夹下,编译产生的可执行文件和`log`请保存到`/usr/local/output`文件夹(命名与代码文件前缀相同) | ||
3. `docker`启动时会设置环境变量`URL`,`TOKEN`,`LANG`(目前默认是 `cpp`),编译完成后需要请求`URL`(实际上是`/code/compile-finish`),请求时需要在`header`中加上`TOKEN`,请求的`body`中需包括代码编译的状态`compile_status: string`,取值为`Success`或`Failed` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
--- | ||
title: 天梯和比赛逻辑 | ||
description: 网站开发接口文档 | ||
permalink: /contest | ||
--- | ||
|
||
> 在开启队式之旅前,请先完成队伍创建、代码上传和角色分配流程。 | ||
> | ||
> 目标: | ||
> | ||
> - 数据库`room` 管理移交给后端执行,避免让前端鉴权 | ||
> - 架构的可扩展性和动态性,支持比赛队伍数可变、每个队伍的角色数可变、每支队伍的代码数可变、每个角色可选的技能可变、比赛的地图可变 | ||
## 天梯逻辑 | ||
|
||
### 流程描述 | ||
|
||
1. 选手在完成队伍和代码准备后,打开`天梯试炼`,向心仪的队伍`开战!`。 | ||
1. 谨防战争狂。同一队伍不能同时打多于6场的比赛。前端需要从数据库上查询比赛状态,若有相同配置的比赛已经在开战,则给用户发出提示信息:`已有一场相同的比赛正在开战。是否继续?`。 | ||
2. 后端也要检查,限制一支队伍的开战频率。 | ||
2. 前端向后端通信,后端首先对队伍进行检查——代码和队伍是否准备完成,若队伍角色未分配代码,或代码未编译,则报错。 | ||
1. 后端首先要检查数据库上的代码编译状态和角色代码分配状态,都正常的情况下再继续下一步。 | ||
2. 选手代码的编译文件在`cos`中。后端需要从`cos`上临时下载队伍的代码或编译文件到后端服务器上。后端服务器存储空间有限,需要定期清理下载的队伍代码和文件。后端服务器与`docker`服务器之间通过`NFS`进行文件共享,因此`docker`服务器自动同步了队伍文件。(备注:建议提前服务器之间组内网减少流量费。) | ||
3. 后端在数据表`contest_room`中创建 `room`,更新`status`为`Waiting`,并在`contest_room_team`中绑定`room`和`team`,这场比赛入队`docker_queue`,返回创建是否成功的结果。 | ||
4. 创建`room`后,后端与 `docker` 服务器通信,创建比赛`docker`,开启比赛。 | ||
1. 比赛状态显示。后端创建 `docker` 分为两步:【第一步】是将比赛放入队列`docker_queue`尾,此时`room` -> `status` 为 `Waiting`;【第二步】是`docker_cron` 定时程序从队列中抽取队首的比赛进行,如果比赛启动成功,此时`room` -> `status`为`Running`。前端应当根据`status`显示比赛状态。 | ||
2. 比赛期间,用户可通过特定端口观看直播。后端在上面所述启动比赛的【第二步】时分配好一个端口。如果端口数量不足,则不启动比赛。如果成功分配端口并启动比赛,则应更新数据库`contest_room`表中的`port`字段,此时`status`字段已经更新为`Running`,则前端可以查看`port`字段并提供直播观看接口。 | ||
5. `docker` 服务器结束比赛后向后端通信,后端更新数据库,更新`contest_room`表中的`status`为`Finished`,更新`contest_room_team`表中的`score`字段,为这场比赛的每个队伍记录分数,更新比赛结果,并更新`contest_team`表中的天梯积分`arena_score`。后端将比赛回放文件上传至 `cos`。具体路径参考[COS存储桶访问路径](https://eesast.github.io/web/cos)。 | ||
6. 比赛结束后,前端提供下载回放接口。前端按照[COS存储桶访问路径](https://eesast.github.io/web/cos)中约定的路径从`cos`下载对应的文件。 | ||
|
||
### 接口描述 | ||
|
||
新版天梯接口的前缀为`/arena`。 | ||
|
||
- `/arena/create`:创建比赛。数据库中插入`room`,并将比赛加入队列中。 | ||
- 请求方法:`POST` | ||
- 请求:`body`中有`{contest_name: string, team_label_bind: TeamLabelBind[], map_id: uuid}`,其中`contest_name`是数据库中的`name`、用于确定用于编译的镜像,`team_ids`为参加比赛的队伍 id,`labels`表示队伍的标签(例如Student Team/Tricker Team),`map_id`代表选择的地图id。`TOKEN`中包含用户的`uuid`。 | ||
- 响应:`200`: `Arena created!`。 | ||
- 工作过程: | ||
1. 鉴权。检查登录状态,及用户是否在队伍中。 | ||
2. 限制开战频率。同一队伍不能同时打多于6场的比赛。 | ||
3. 接下来检查代码和队伍是否准备完成,若队伍角色未分配代码,或代码未编译,则报错。 | ||
4. 后端需要从`cos`上临时下载队伍的代码或编译文件到服务器上。文件路径参考[COS存储桶访问路径](https://eesast.github.io/web/cos)。服务器存储空间有限,需要定期清理下载的队伍代码和文件。如果`cos`上找不到对应的编译文件,则报错。 | ||
5. 后端在数据表`contest_room`中创建 `room`,更新`status`为`Waiting`,并在`contest_room_team`中绑定`room`和`team`,并返回创建是否成功的结果,以及`room_id`。 | ||
6. 后端将比赛数据存入`docker_queue`中,等待`docker_cron`发起比赛。 | ||
- 错误: | ||
- `401`:`401 Unauthorized: Missing token`(未登录) | ||
- `403`:`403 Forbidden: User not in team`(用户不在队伍中) | ||
- `403`:`403 Forbidden: Team player not assigned `(队伍角色未分配代码) | ||
- `403`:`403 Forbidden: Team code not compiled`(代码未编译) | ||
- `404`:`404 Not Found: Team code not found in cos `(`cos`上找不到文件) | ||
- `422`:`422 Unprocessable Entity: Missing credentials`(请求缺失参数) | ||
- `423`:`423 Locked: Request arena too frequently`(比赛次数过多) | ||
- `500`:`undefined`(其他内部错误) | ||
- `/arena/finish`:`docker`服务器比赛结束的`hook`。更新比赛结果,更新天梯分数。 | ||
- 请求方法:`POST` | ||
- 请求:`{result: ContestResult[]}`。`TOKEN`包含的信息:`{room_id: uuid}` | ||
- 响应:`200`:`Update OK!` | ||
- 错误:`500`:`undefined`,返回报错信息 | ||
|
||
注:此外,后端需要在`docker_cron`中更新数据库比赛状态和端口信息。 | ||
|
||
## 比赛逻辑 | ||
|
||
### 流程描述 | ||
|
||
比赛的流程就简洁多了。 | ||
|
||
1. 管理员用户发起比赛。前端管理员点击启动之后,前端数据库`contest_round`里插一行,然后请求后端路由。 | ||
2. 后端获取所有队伍数据。检查队伍代码是否完整,角色是否分配,如果不完整则跳过此队伍。 | ||
3. 后端对于正常队伍开启循环赛,将比赛加入`docker_queue`中。 | ||
4. `docker`比赛结束后向后端通信,后端在数据表上更新比赛分数`competition_score`。 | ||
|
||
### 接口描述 | ||
|
||
新版比赛接口的前缀为`/competition`。 | ||
|
||
- `/competition/create`:管理员专用。后端可以按`contest_round`表中的信息设置所有队伍之间的完整比赛,全部队伍的比赛合起来称为一个`round`,对应一个`round_id`。设置`room`发起对战的流程跟天梯逻辑一致,只需要在`contest_room`里额外加`round_id`标识即可。 | ||
- 请求方法:`POST` | ||
- 请求:`{round_id: uuid}`。`TOKEN`包含`user_uuid`。 | ||
- 响应:`200`:`Competition Created!` | ||
- 错误: | ||
- `422`:`422 Unprocessable Entity: Missing credentials`(请求缺失参数) | ||
- `500`:`undefined`,返回报错信息 | ||
- `/competition/assign`:管理员专用。发起`round`中某一场特定的比赛,然后将比赛加入队列中。设置`room`发起对战的过程跟天梯逻辑一致,只需要在`contest_room`里额外加`round_id`标识即可。 | ||
- 请求方法:`POST` | ||
- 请求:`{team_label_bind: TeamLabelBind[], map_id: uuid, round_id: uuid}`。`TOKEN`包含`user_uuid`。 | ||
- 响应:`200`:`Competition Created!` | ||
- 错误: | ||
- `422`:`422 Unprocessable Entity: Missing credentials`(请求缺失参数) | ||
- `500`:`undefined`,返回报错信息 | ||
- `/competition/finish`:`docker`服务器比赛结束的`hook`。更新比赛结果。 | ||
- 请求方法:`POST` | ||
- 请求:`{result: ContestResult[]}` 。`TOKEN`包含的信息:`{room_id: uuid}` | ||
- 响应:`200`:`Update OK!` | ||
- 错误:`500`:`undefined`,返回报错信息 | ||
|
||
## 与赛事组的约定 | ||
|
||
1. 一场比赛对应一个`docker`容器。 | ||
2. 队式应当关注上面的`/arena/finish`和`/competition/finish`路由参数信息。`docker`镜像启动时会设置环境变量`URL`(对应`/arena/finish`和`/competition/finish`)和`TOKEN`,编译完成后需要请求`URL`,请求时需要在header中加上`TOKEN`,在body中加上每个队的分数`result`。 | ||
3. `docker`目录绑定:回放文件在`/usr/local/playback`下,地图文件在`/usr/local/map`下,队伍代码应在`/usr/local/team<xxx>`下,具体格式根据比赛规则商定。 | ||
|
||
## 附录 | ||
|
||
数据结构定义 | ||
|
||
```javascript | ||
interface ContestResult { | ||
team_id: number; | ||
score: number; | ||
}; | ||
|
||
interface TeamLabelBind { | ||
team_id: uuid; | ||
label: string; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
--- | ||
title: COS存储桶访问路径 | ||
description: 网站开发接口文档 | ||
permalink: /cos | ||
--- | ||
|
||
# COS存储访问路径 | ||
|
||
前端访问相关文件时,需先向后端 `/static` 下对应的**同名路由**请求COS访问权限,请求所需参数可到对应文档中寻找或直接查看API接口。 | ||
|
||
### 比赛相关文件 | ||
|
||
下方 `${name}` 指代比赛名称,形如 `THUAI7`,对应数据库 `contest` 表中的 `name` | ||
|
||
- 代码:`${name}/code/${team_id}/${code_id}.${suffix}` | ||
- 编译后的可执行文件:`${name}/code/${team_id}/${code_id}` | ||
- 编译产生的日志文件:`${name}/code/${team_id}/${code_id}.log` | ||
- 公告文件:`${name}/notice/${notice_id}/${filename}` | ||
- 天梯回放:`${name}/arena/${room_id}/${filename}` | ||
- (选择性实现) 后台比赛回放:`${name}/competition/${round_id}/${filename}` | ||
- (选择性实现) 地图:`${name}/map/${filename}` | ||
|
||
### Info页面相关文件 | ||
|
||
- 新生导师谈话记录:`chat_record/${application_id}/${filename}` | ||
- 公告文件:`upload/${filename}` | ||
|
||
### 公开静态资源文件 | ||
|
||
这部分文件前端可直接通过 `https://static.eesast.com/` 解析到存储桶访问。 | ||
|
||
- 展示图片:`public/images/${filename}` | ||
- WebGL资源:`public/WebGL/${name}/${filename}` | ||
- 其他可公开文件:`public/files/${filename}` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
- [用户逻辑](https://eesast.github.io/web/user) | ||
- 代码提交逻辑(待补充) | ||
- 天梯逻辑(待补充) | ||
- 静态资源管理(待补充) | ||
- 新生导师逻辑(待补充) | ||
- [用户逻辑](https://eesast.github.io/web/user) | ||
- [COS存储桶访问路径约定](https://eesast.github.io/web/cos) | ||
- [赛事代码管理流程](https://eesast.github.io/web/code) | ||
- [天梯和比赛逻辑](https://eesast.github.io/web/contest) | ||
- 新生导师逻辑(待补充) |
Oops, something went wrong.