task-116: 后台管理新增/导入表单弹窗化 + 分组管理独立菜单与UI优化 + 轻量进度端点
Build Backend JAR / build (push) Has been cancelled
Build Backend JAR / build (push) Has been cancelled
- 后台管理页(admin)所有面板的新增/导入表单改为弹窗操作,原有字段 ID 全部保留、提交逻辑不变;导入删除入口不再触发二次确认拦截 - 分组管理升级为独立菜单(V102 + schema initializer),移除 5 个面板内的管理分组按钮;分组列表改为蓝白主题、增加权限分组横幅 - Python 侧 group-manage 权限守卫(_ensure_backend_menu_access 补充 group-manage) - 引入 V101(biz_task_file_job 复合索引)+ 新增 TaskProgressLight/TaskFileJob 轻量端点与进度聚合作 - 前端 progress-light / page-separated-loads / dispatch-guard 共享模块及单元测试
This commit is contained in:
@@ -33,6 +33,7 @@ export const API_ENDPOINTS = {
|
||||
fileActivate: '/api/publish/tasks/{taskId}/files/{fileId}/activate',
|
||||
items: '/api/publish/tasks/{taskId}/items',
|
||||
progressBatch: '/api/publish/tasks/progress/batch',
|
||||
progressLight: '/api/publish/tasks/progress/light',
|
||||
result: '/api/publish/tasks/{taskId}/result',
|
||||
dashboard: '/api/publish/dashboard',
|
||||
history: '/api/publish/history',
|
||||
@@ -48,6 +49,7 @@ export const API_ENDPOINTS = {
|
||||
resultDownload: '/api/delete-brand/results/{resultId}/download',
|
||||
resultSubmit: '/api/delete-brand/tasks/{taskId}/result',
|
||||
progressBatch: '/api/delete-brand/tasks/progress/batch',
|
||||
progressLight: '/api/delete-brand/tasks/progress/light',
|
||||
},
|
||||
productRisk: {
|
||||
candidates: '/api/product-risk-resolve/candidates',
|
||||
@@ -62,6 +64,7 @@ export const API_ENDPOINTS = {
|
||||
taskDelete: '/api/product-risk-resolve/tasks/{taskId}',
|
||||
taskBatch: '/api/product-risk-resolve/tasks/batch',
|
||||
progressBatch: '/api/product-risk-resolve/tasks/progress/batch',
|
||||
progressLight: '/api/product-risk-resolve/tasks/progress/light',
|
||||
pendingShopResult: '/api/product-risk-resolve/pending-shop-result',
|
||||
resultDownload: '/api/product-risk-resolve/results/{resultId}/download',
|
||||
},
|
||||
@@ -80,6 +83,7 @@ export const API_ENDPOINTS = {
|
||||
stageFinished: '/api/shop-match/tasks/{taskId}/stage-finished',
|
||||
taskBatch: '/api/shop-match/tasks/batch',
|
||||
progressBatch: '/api/shop-match/tasks/progress/batch',
|
||||
progressLight: '/api/shop-match/tasks/progress/light',
|
||||
skipAsins: '/api/shop-match/tasks/{taskId}/skip-asins/paginated',
|
||||
resultDownload: '/api/shop-match/results/{resultId}/download',
|
||||
},
|
||||
@@ -95,6 +99,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/patrol-delete/history',
|
||||
historyDelete: '/api/patrol-delete/history/{resultId}',
|
||||
progressBatch: '/api/patrol-delete/tasks/progress/batch',
|
||||
progressLight: '/api/patrol-delete/tasks/progress/light',
|
||||
taskCreate: '/api/patrol-delete/tasks',
|
||||
taskDelete: '/api/patrol-delete/tasks/{taskId}',
|
||||
taskResult: '/api/patrol-delete/tasks/{taskId}/result',
|
||||
@@ -109,6 +114,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/query-asin/history',
|
||||
historyDelete: '/api/query-asin/history/{resultId}',
|
||||
progressBatch: '/api/query-asin/tasks/progress/batch',
|
||||
progressLight: '/api/query-asin/tasks/progress/light',
|
||||
taskCreate: '/api/query-asin/tasks',
|
||||
taskDelete: '/api/query-asin/tasks/{taskId}',
|
||||
taskResult: '/api/query-asin/tasks/{taskId}/result',
|
||||
@@ -124,6 +130,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/shop-data-crawl/history',
|
||||
historyDelete: '/api/shop-data-crawl/history/{resultId}',
|
||||
progressBatch: '/api/shop-data-crawl/tasks/progress/batch',
|
||||
progressLight: '/api/shop-data-crawl/tasks/progress/light',
|
||||
taskCreate: '/api/shop-data-crawl/tasks',
|
||||
taskDelete: '/api/shop-data-crawl/tasks/{taskId}',
|
||||
resultDownload: '/api/shop-data-crawl/results/{resultId}/download',
|
||||
@@ -138,6 +145,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/withdraw/history',
|
||||
historyDelete: '/api/withdraw/history/{resultId}',
|
||||
progressBatch: '/api/withdraw/tasks/progress/batch',
|
||||
progressLight: '/api/withdraw/tasks/progress/light',
|
||||
taskCreate: '/api/withdraw/tasks',
|
||||
taskDelete: '/api/withdraw/tasks/{taskId}',
|
||||
taskResult: '/api/withdraw/tasks/{taskId}/result',
|
||||
@@ -153,6 +161,7 @@ export const API_ENDPOINTS = {
|
||||
taskDelete: '/api/price-track/tasks/{taskId}',
|
||||
tasksBatch: '/api/price-track/tasks/batch',
|
||||
progressBatch: '/api/price-track/tasks/progress/batch',
|
||||
progressLight: '/api/price-track/tasks/progress/light',
|
||||
loopRuns: '/api/price-track/loop-runs',
|
||||
loopRunDetail: '/api/price-track/loop-runs/{loopRunId}',
|
||||
loopRunStop: '/api/price-track/loop-runs/{loopRunId}/stop',
|
||||
@@ -173,6 +182,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/similar-asin/history',
|
||||
historyDelete: '/api/similar-asin/history/{resultId}',
|
||||
progressBatch: '/api/similar-asin/tasks/progress/batch',
|
||||
progressLight: '/api/similar-asin/tasks/progress/light',
|
||||
taskActivate: '/api/similar-asin/tasks/{taskId}/activate',
|
||||
taskDelete: '/api/similar-asin/tasks/{taskId}',
|
||||
resultDownload: '/api/similar-asin/results/{resultId}/download',
|
||||
@@ -185,6 +195,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/appearance-patent/history',
|
||||
historyDelete: '/api/appearance-patent/history/{resultId}',
|
||||
progressBatch: '/api/appearance-patent/tasks/progress/batch',
|
||||
progressLight: '/api/appearance-patent/tasks/progress/light',
|
||||
taskActivate: '/api/appearance-patent/tasks/{taskId}/activate',
|
||||
taskDelete: '/api/appearance-patent/tasks/{taskId}',
|
||||
resultDownload: '/api/appearance-patent/results/{resultId}/download',
|
||||
@@ -196,6 +207,7 @@ export const API_ENDPOINTS = {
|
||||
history: '/api/collect-data/history',
|
||||
historyDelete: '/api/collect-data/history/{resultId}',
|
||||
progressBatch: '/api/collect-data/tasks/progress/batch',
|
||||
progressLight: '/api/collect-data/tasks/progress/light',
|
||||
taskActivate: '/api/collect-data/tasks/{taskId}/activate',
|
||||
taskDelete: '/api/collect-data/tasks/{taskId}',
|
||||
fail: '/api/collect-data/tasks/{taskId}/fail',
|
||||
|
||||
@@ -14,6 +14,7 @@ export * from "./types/modules/image-video.ts";
|
||||
export * from "./types/modules/brand.ts";
|
||||
export * from "./types/modules/permission.ts";
|
||||
export * from "./types/modules/digital-human.ts";
|
||||
export * from "./progress-light.ts";
|
||||
export * from "./upload.ts";
|
||||
export * from "./download-url.ts";
|
||||
export { JAVA_API_PREFIX } from "./url.ts";
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { post, unwrapJavaResponse, type JavaApiResponse } from './http.ts'
|
||||
import { buildJavaUrl } from './url.ts'
|
||||
import { API_ENDPOINTS } from './endpoints.ts'
|
||||
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from '../task-progress-config.ts'
|
||||
import { createTaskProgressRequestCache } from '../task-progress-request-cache.ts'
|
||||
import type { TaskProgressBatchVo } from './types/task.ts'
|
||||
|
||||
/**
|
||||
* 共享 progress/light 轻量进度端点(任务 118)。
|
||||
*
|
||||
* 后端 12 个模块已提供 /tasks/progress/light(Task 109-113),本模块按模块名
|
||||
* 统一路由到对应端点,把轻量响应归一为现有 TaskProgressBatchVo 结构
|
||||
* (items + missingTaskIds,missingTaskIds 缺失时补空数组),页面可渐进切换:
|
||||
* useLight=false 时返回空结构不发起请求(调用方自行回退旧 progress/batch 端点)。
|
||||
*
|
||||
* 与各模块 progress/batch 函数一致:同参数请求共享 TTL 缓存与 in-flight 合并,
|
||||
* 进度类请求默认 10s 超时。
|
||||
*/
|
||||
export const PROGRESS_LIGHT_MODULES = [
|
||||
'publish',
|
||||
'deleteBrand',
|
||||
'productRisk',
|
||||
'shopMatch',
|
||||
'patrolDelete',
|
||||
'queryAsin',
|
||||
'shopDataCrawl',
|
||||
'withdraw',
|
||||
'priceTrack',
|
||||
'similarAsin',
|
||||
'appearancePatent',
|
||||
'collectData',
|
||||
] as const
|
||||
|
||||
export type ProgressLightModule = (typeof PROGRESS_LIGHT_MODULES)[number]
|
||||
|
||||
/** 轻量进度项(与后端 TaskProgressLightVo 白名单一致) */
|
||||
export interface TaskProgressLightItem {
|
||||
taskId?: number
|
||||
status?: string
|
||||
statusCode?: string | null
|
||||
fileStatus?: string | null
|
||||
fileError?: string | null
|
||||
fileReady?: boolean | null
|
||||
updatedAt?: string | null
|
||||
}
|
||||
|
||||
export interface GetModuleProgressLightOptions {
|
||||
/** 是否走 light 端点;false 时不发起请求返回空结构(调用方回退旧端点),默认 true */
|
||||
useLight?: boolean
|
||||
/** 跳过缓存强制请求 */
|
||||
force?: boolean
|
||||
}
|
||||
|
||||
const progressLightCache = createTaskProgressRequestCache<TaskProgressBatchVo<TaskProgressLightItem>>({
|
||||
ttlMs: () => getTaskProgressCacheTtlMs(),
|
||||
maxEntries: 100,
|
||||
maxInflight: 16,
|
||||
})
|
||||
|
||||
function normalizeTaskIds(taskIds: number[]) {
|
||||
return Array.from(
|
||||
new Set(
|
||||
(taskIds || [])
|
||||
.map((taskId) => Number(taskId))
|
||||
.filter((taskId) => Number.isFinite(taskId) && taskId > 0),
|
||||
),
|
||||
).sort((left, right) => left - right)
|
||||
}
|
||||
|
||||
export function getModuleProgressLight(
|
||||
module: ProgressLightModule,
|
||||
taskIds: number[],
|
||||
options: GetModuleProgressLightOptions = {},
|
||||
): Promise<TaskProgressBatchVo<TaskProgressLightItem>> {
|
||||
if (options.useLight === false) {
|
||||
return Promise.resolve({ items: [], missingTaskIds: [] })
|
||||
}
|
||||
const normalizedTaskIds = normalizeTaskIds(taskIds)
|
||||
if (!normalizedTaskIds.length) {
|
||||
return Promise.resolve({ items: [], missingTaskIds: [] })
|
||||
}
|
||||
const path = buildJavaUrl(API_ENDPOINTS[module].progressLight)
|
||||
const cacheKey = `${path}::${normalizedTaskIds.join(",")}`
|
||||
const cached = progressLightCache.get(cacheKey)
|
||||
if (!options.force && cached !== undefined) {
|
||||
return Promise.resolve(cached)
|
||||
}
|
||||
const inflight = progressLightCache.getInflight(cacheKey)
|
||||
if (!options.force && inflight) {
|
||||
return inflight
|
||||
}
|
||||
const requestPromise = unwrapJavaResponse(
|
||||
post<JavaApiResponse<TaskProgressBatchVo<TaskProgressLightItem>>, { taskIds: number[] }>(
|
||||
path,
|
||||
{ taskIds: normalizedTaskIds },
|
||||
{ timeout: getTaskProgressTimeoutMs() },
|
||||
),
|
||||
)
|
||||
.then((data) => {
|
||||
const normalized = {
|
||||
items: data?.items || [],
|
||||
missingTaskIds: data?.missingTaskIds || [],
|
||||
}
|
||||
progressLightCache.set(cacheKey, normalized)
|
||||
return normalized
|
||||
})
|
||||
.finally(() => {
|
||||
progressLightCache.endInflight(cacheKey)
|
||||
})
|
||||
progressLightCache.startInflight(cacheKey, requestPromise)
|
||||
return requestPromise
|
||||
}
|
||||
Reference in New Issue
Block a user