feat: 完善代理商端业务逻辑与前后端框架
主要更新: - 更新代理商端文档,明确项目由品牌方分配流程 - 新增Brief配置详情页(已配置)设计稿 - 完善工作台紧急待办中品牌新任务功能 - 整理Pencil设计文件中代理商端页面顺序 - 新增后端FastAPI框架及核心API - 新增前端Next.js页面和组件库 - 添加.gitignore排除构建和缓存文件 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
d52509d630
commit
e4959d584f
@@ -0,0 +1,203 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { api } from '@/lib/api'
|
||||
import type {
|
||||
VideoReviewRequest,
|
||||
ReviewTask,
|
||||
TaskStatus,
|
||||
} from '@/types/review'
|
||||
|
||||
interface UseReviewOptions {
|
||||
pollingInterval?: number
|
||||
onComplete?: (result: ReviewTask) => void
|
||||
onError?: (error: Error) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频审核 Hook
|
||||
*/
|
||||
export function useReview(options: UseReviewOptions = {}) {
|
||||
const { pollingInterval = 2000, onComplete, onError } = options
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [isPolling, setIsPolling] = useState(false)
|
||||
const [task, setTask] = useState<ReviewTask | null>(null)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
/**
|
||||
* 提交审核
|
||||
*/
|
||||
const submitReview = useCallback(async (data: VideoReviewRequest) => {
|
||||
setIsSubmitting(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const response = await api.submitVideoReview(data)
|
||||
setTask({
|
||||
reviewId: response.reviewId,
|
||||
status: response.status,
|
||||
createdAt: new Date().toISOString(),
|
||||
})
|
||||
return response.reviewId
|
||||
} catch (err) {
|
||||
const error = err instanceof Error ? err : new Error('提交失败')
|
||||
setError(error)
|
||||
onError?.(error)
|
||||
throw error
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}, [onError])
|
||||
|
||||
/**
|
||||
* 查询进度
|
||||
*/
|
||||
const fetchProgress = useCallback(async (reviewId: string) => {
|
||||
try {
|
||||
const progress = await api.getReviewProgress(reviewId)
|
||||
setTask((prev) => ({
|
||||
...prev,
|
||||
reviewId: progress.reviewId,
|
||||
status: progress.status,
|
||||
progress: progress.progress,
|
||||
currentStep: progress.currentStep,
|
||||
createdAt: prev?.createdAt || new Date().toISOString(),
|
||||
}))
|
||||
return progress
|
||||
} catch (err) {
|
||||
const error = err instanceof Error ? err : new Error('查询失败')
|
||||
setError(error)
|
||||
throw error
|
||||
}
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* 查询结果
|
||||
*/
|
||||
const fetchResult = useCallback(async (reviewId: string) => {
|
||||
try {
|
||||
const result = await api.getReviewResult(reviewId)
|
||||
const updatedTask: ReviewTask = {
|
||||
reviewId: result.reviewId,
|
||||
status: result.status,
|
||||
score: result.score,
|
||||
summary: result.summary,
|
||||
violations: result.violations,
|
||||
softWarnings: result.softWarnings,
|
||||
createdAt: task?.createdAt || new Date().toISOString(),
|
||||
completedAt: new Date().toISOString(),
|
||||
}
|
||||
setTask(updatedTask)
|
||||
return updatedTask
|
||||
} catch (err) {
|
||||
const error = err instanceof Error ? err : new Error('查询失败')
|
||||
setError(error)
|
||||
throw error
|
||||
}
|
||||
}, [task?.createdAt])
|
||||
|
||||
/**
|
||||
* 开始轮询进度
|
||||
*/
|
||||
const startPolling = useCallback((reviewId: string) => {
|
||||
setIsPolling(true)
|
||||
|
||||
const poll = async () => {
|
||||
try {
|
||||
const progress = await fetchProgress(reviewId)
|
||||
|
||||
if (progress.status === 'completed') {
|
||||
setIsPolling(false)
|
||||
const result = await fetchResult(reviewId)
|
||||
onComplete?.(result)
|
||||
} else if (progress.status === 'failed') {
|
||||
setIsPolling(false)
|
||||
const error = new Error('审核失败')
|
||||
setError(error)
|
||||
onError?.(error)
|
||||
}
|
||||
} catch (err) {
|
||||
// 继续轮询,忽略单次错误
|
||||
console.error('Polling error:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const intervalId = setInterval(poll, pollingInterval)
|
||||
poll() // 立即执行一次
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId)
|
||||
setIsPolling(false)
|
||||
}
|
||||
}, [fetchProgress, fetchResult, pollingInterval, onComplete, onError])
|
||||
|
||||
/**
|
||||
* 停止轮询
|
||||
*/
|
||||
const stopPolling = useCallback(() => {
|
||||
setIsPolling(false)
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* 重置状态
|
||||
*/
|
||||
const reset = useCallback(() => {
|
||||
setTask(null)
|
||||
setError(null)
|
||||
setIsSubmitting(false)
|
||||
setIsPolling(false)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
task,
|
||||
error,
|
||||
isSubmitting,
|
||||
isPolling,
|
||||
submitReview,
|
||||
fetchProgress,
|
||||
fetchResult,
|
||||
startPolling,
|
||||
stopPolling,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核结果 Hook (单次查询)
|
||||
*/
|
||||
export function useReviewResult(reviewId: string | null) {
|
||||
const [task, setTask] = useState<ReviewTask | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!reviewId) return
|
||||
|
||||
const fetchResult = async () => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const result = await api.getReviewResult(reviewId)
|
||||
setTask({
|
||||
reviewId: result.reviewId,
|
||||
status: result.status,
|
||||
score: result.score,
|
||||
summary: result.summary,
|
||||
violations: result.violations,
|
||||
softWarnings: result.softWarnings,
|
||||
createdAt: new Date().toISOString(),
|
||||
})
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err : new Error('查询失败'))
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchResult()
|
||||
}, [reviewId])
|
||||
|
||||
return { task, isLoading, error }
|
||||
}
|
||||
Reference in New Issue
Block a user