feat(教程管理): 后台可上传教程包,工具台按最新上传下载

- admin-vue 新增「记录与版本 → 教程管理」页(menuKey admin_tutorial):
  zip 浏览器直传 MinIO(presign → PUT 带进度 → confirm 落库)+ 列表/下载/删除,
  最新一条标「当前生效」;补操作指引与 align-tutorial 守卫测试。
- frontend-vue 工具台「立即下载教程」进页面取 /api/tutorial/latest(最新上传优先),
  接口异常或无记录回退历史固定直链,按钮不失效。
- Java 侧 tutorial 模块与 V119 迁移已在 dc6e8924 提交并上线(2026-09-13)。

vue-tsc + 两端单测(工具台 695 / 后台 1617)通过;生产已实测
presign→PUT→confirm→latest 切换→删除回落全链路。
This commit is contained in:
2026-09-13 23:48:24 +08:00
parent 14a723ab1c
commit 9ffd68bae5
13 changed files with 722 additions and 7 deletions
+3
View File
@@ -262,6 +262,9 @@ export const API_ENDPOINTS = {
delete: '/api/digital-human/versions/{version}',
downloadUrl: '/api/digital-human/versions/{version}/download-url',
},
tutorial: {
latest: '/api/tutorial/latest',
},
} as const
export type ApiEndpointGroup = {
+1
View File
@@ -0,0 +1 @@
export * from './types/modules/tutorial'
@@ -0,0 +1,31 @@
import { get, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
import { buildJavaUrl } from '../../url.ts'
import { API_ENDPOINTS } from '../../endpoints.ts'
/** 工具台首页「立即下载教程」的最新教程包(后台「教程管理」上传,以最新上传为主)。 */
export interface LatestTutorialPackage {
fileName: string
fileUrl: string
fileSize: number
createdAt: string
}
/** 读取最新教程包;后端无记录时返回 null(调用方回退到历史固定直链)。 */
export async function fetchLatestTutorialPackage(): Promise<LatestTutorialPackage | null> {
const payload = await unwrapJavaResponse(
get<JavaApiResponse<Record<string, unknown>>>(buildJavaUrl(API_ENDPOINTS.tutorial.latest)),
)
if (!payload || typeof payload !== 'object') {
return null
}
const fileUrl = typeof payload.file_url === 'string' ? payload.file_url.trim() : ''
if (!fileUrl) {
return null
}
return {
fileName: typeof payload.file_name === 'string' ? payload.file_name.trim() : '',
fileUrl,
fileSize: typeof payload.file_size === 'number' ? payload.file_size : 0,
createdAt: typeof payload.created_at === 'string' ? payload.created_at : '',
}
}