feat(upload): 组内作品唯一 + 小组编号目录 + 按小组打包下载(R190)
需求(管理人员反馈):
① 本科组/教师组同样遵循「小组内只要有一人上传作品,其他人就不能再上传」;
② 作品按「小组编号」建目录存放并保留原始文件名,管理端下载时按小组打包。
后端落地:
- 组锁:新增 getTeamUploadState/assertTeamUploadAllowed,按 (group, teamUid) 取全组
提交记录,**最早提交者为归属人**;其他人上传与分片合并两条路径都返 6006
(data 带 teamLocked/teamOwnerName/teamNumber/folderName)。删除本人作品后自动解锁。
⛔ 必须先按 group 过滤再认 teamUid(team_uid 仅组内唯一)。
- 落盘:works/<group>/<小组编号>/<原始文件名>。目录名走新增 lib/team_folder.js
(唯一 SoT:净化 Windows 非法字符/控制字符/首尾点/设备保留名,限长 60)。
撞名三态:不存在→原名;同一聚合+同一 file_type→原名原子覆盖;否则 (2)(3)。
DB file_path 仍为相对路径 ⇒ 存量平铺文件免迁移。
- 直传与分片合并收敛到同一套 prepareWorkTarget + commitWorkFile:先写同目录临时名
(.upload_*/.merge_*),再 renameSync;失败一律 safeCleanup 后抛。
- status 接口新增 teamLocked/canUpload/teamOwnerName/isTeamOwner/teamFiles/memberCount。
- 管理端:works 列表返回 teamUid/teamNumber/teamFolder(走 resolveTeamLabels 既有口径);
新增 GET /api/admin/team-zip/:group?teamUid= 单组打包(仅本科/教师,其余 400,包内平铺);
batch-download 包内改为 <小组编号>/<原始文件名>。两者共用 _buildZip。
验证:真接口 45/45 + 变异 M1(locked=false ⇒ 15 条断言红,还原后复绿);
已收编为门禁第 58 步。
This commit is contained in:
@@ -160,6 +160,10 @@ module.exports = app => {
|
||||
router.get('/api/admin/competition-item', adminAuth, controller.admin.competitionItemInfo);
|
||||
router.put('/api/admin/competition-item', adminAuth, controller.admin.updateCompetitionItemInfo);
|
||||
router.get('/api/admin/batch-download/:group', adminAuth, controller.admin.batchDownload);
|
||||
// 单小组作品压缩包(R190):zip 本身即小组目录,包内平铺该组文件。
|
||||
// ⚠️ 必须注册在 `/api/admin/works/:group` 之前不是硬要求(路径段数不同,不冲突),
|
||||
// 但保持「同一业务相邻登记」,避免后来者以为是两条无关通道。
|
||||
router.get('/api/admin/team-zip/:group', adminAuth, controller.admin.teamZip);
|
||||
router.get('/api/admin/works/:group', adminAuth, controller.admin.works);
|
||||
|
||||
// 时间设置
|
||||
|
||||
Reference in New Issue
Block a user