Compare commits

..

3 Commits

Author SHA1 Message Date
xiaoxia f7453ec477 Merge branch 'develop' into refactor/product-library-deepen
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 26s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m8s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 53s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m35s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m7s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 24s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 28s
AI Code Review / AI Code Review (pull_request) Successful in 37s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m11s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m10s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m43s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m30s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 17s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m36s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 9m45s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 28s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1221h6m38s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1221h34m44s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1221h34m50s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1221h34m46s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1223h54m41s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1223h54m46s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1223h54m47s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1226h41m56s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1226h42m0s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1222h7m34s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1224h27m31s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1227h14m44s
2026-07-27 12:34:18 +08:00
xiaoxia 12eca4f08a style: prettier format fix
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 15s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m16s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m17s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m1s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 42s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 37s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m42s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 54s
AI Code Review / AI Code Review (pull_request) Successful in 1m43s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 50s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m36s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 15m21s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 39s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1229h31m1s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1229h48m38s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1229h48m42s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1229h48m44s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1230h7m15s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1230h7m17s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1230h7m19s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1230h7m21s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1230h7m46s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1230h27m57s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1230h27m59s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1230h28m1s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1230h21m25s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1230h40m1s
2026-07-27 08:39:50 +08:00
xiaoxia 29932c6f54 refactor(product-library): 深化 ProductLibrary 拆分,抽离筛选栏/批量操作栏/空状态
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 19s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m13s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m10s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m25s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 41s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m18s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 49s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m54s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 56s
AI Code Review / AI Code Review (pull_request) Successful in 1m39s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 49s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 7m15s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 28s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1237h15m34s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1237h18m44s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1237h18m50s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1237h18m46s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1237h26m27s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1237h26m46s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1237h26m48s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1237h26m59s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1238h0m13s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1238h0m15s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1238h0m17s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1237h51m31s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1237h59m11s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1237h59m33s
- 抽 ProductFilterBar 组件: 6 个筛选器 + 结果计数
- 抽 ProductBatchBar 组件: 批量操作栏(全选/下载/发布/删除)
- 抽 ProductEmptyState 组件: 加载/404/错误/空状态四种展示
- 主文件 329→176 行 (-47%),仅保留 Hook 组装与整体布局
2026-07-27 00:43:26 +08:00
4 changed files with 188 additions and 215 deletions
+188
View File
@@ -0,0 +1,188 @@
/**
* TTS 语音合成 API
* 对接后端 /api/v1/tts/* 端点
*
* 任务 3.14 新增
*/
import apiClient from "./client"
/* ── 类型定义 ──────────────────────────────────── */
/** TTS 元数据(合成时附带的扩展信息) */
export interface TTSMetadata {
/** 语音时长(秒) */
duration?: number
/** 采样率(Hz) */
sample_rate?: number
/** 语言 */
language?: string
/** 其他扩展字段 */
[key: string]: unknown
}
/** TTS 合成请求参数 */
export interface TTSSynthesizeRequest {
text: string
voice_id?: string
output_name?: string
language?: string
speed?: number
voice_model?: string
voice_clone_profile_id?: string
format?: string
metadata?: TTSMetadata
}
/** TTS 合成创建响应 */
export interface TTSSynthesizeResponse {
job_id: string
status: string
message: string
}
/** TTS 任务详情 */
export interface TTSJob {
id: string
user_id: string
project_id: string | null
text: string
voice_id: string | null
voice_model: string | null
voice_clone_profile_id: string | null
language: string
speed: number
output_name: string | null
output_audio_url: string | null
output_format: string
duration_seconds: number | null
file_size_bytes: number | null
sample_rate: number | null
status: string
error_message: string | null
retry_count: number
max_retries: number
metadata_: TTSMetadata | null
created_at: string
updated_at: string
}
/** TTS 任务状态(轻量轮询用) */
export interface TTSJobStatus {
id: string
status: string
output_audio_url: string | null
error_message: string | null
duration_seconds: number | null
retry_count: number
}
/** TTS 任务列表响应 */
export interface TTSJobListResponse {
items: TTSJob[]
total: number
skip: number
limit: number
}
/** TTS 任务列表查询参数 */
export interface TTSJobListParams {
status?: string
skip?: number
limit?: number
}
/* ── API 函数 ──────────────────────────────────── */
/** 创建 TTS 合成任务 */
export const synthesizeSpeech = async (
data: TTSSynthesizeRequest,
): Promise<TTSSynthesizeResponse> => {
const response = await apiClient.post<TTSSynthesizeResponse>("/tts/synthesize", data)
return response.data
}
/** 获取 TTS 任务详情 */
export const getTTSJob = async (jobId: string): Promise<TTSJob> => {
const response = await apiClient.get<TTSJob>(`/tts/jobs/${jobId}`)
return response.data
}
/** 获取 TTS 任务状态(轻量轮询) */
export const getTTSJobStatus = async (jobId: string): Promise<TTSJobStatus> => {
const response = await apiClient.get<TTSJobStatus>(`/tts/jobs/${jobId}/status`)
return response.data
}
/** 获取 TTS 任务列表 */
export const getTTSJobs = async (params?: TTSJobListParams): Promise<TTSJobListResponse> => {
const searchParams = new URLSearchParams()
if (params?.status) searchParams.set("status", params.status)
if (params?.skip !== undefined) searchParams.set("skip", String(params.skip))
if (params?.limit !== undefined) searchParams.set("limit", String(params.limit))
const qs = searchParams.toString()
const response = await apiClient.get<TTSJobListResponse>(`/tts/jobs${qs ? `?${qs}` : ""}`)
return response.data
}
/** 存为素材请求参数 */
export interface SaveTtsToLibraryRequest {
name?: string
tag_ids?: string[]
}
/** 将 TTS 合成结果保存到配音库 */
export const saveTtsToLibrary = async (
jobId: string,
data?: SaveTtsToLibraryRequest,
): Promise<void> => {
await apiClient.post(`/tts/jobs/${jobId}/save-to-library`, data ?? {})
}
/** 删除 TTS 任务 */
export const deleteTTSJob = async (jobId: string): Promise<void> => {
await apiClient.delete(`/tts/jobs/${jobId}`)
}
/* ── 音色列表 ──────────────────────────────────── */
/** TTS 音色 */
export interface TTSVoice {
id: string
name: string
/** 音色分类标签:male/female/young/service/news/emotion */
category?: string
/** 语言 */
language?: string
/** 试听 URL */
preview_url?: string
/** 描述 */
description?: string
}
/** 获取 TTS 音色列表 */
export const getTtsVoices = async (): Promise<TTSVoice[]> => {
const response = await apiClient.get<TTSVoice[]>("/tts/voices")
return response.data
}
/* ── TTS 试听 ──────────────────────────────────── */
/** TTS 试听请求参数 */
export interface TTSPreviewRequest {
text: string
voice_id: string
speed?: number
pitch?: number
}
/** TTS 试听响应 */
export interface TTSPreviewResponse {
audio_url: string
duration?: number
}
/** TTS 试听 */
export const previewTts = async (data: TTSPreviewRequest): Promise<TTSPreviewResponse> => {
const response = await apiClient.post<TTSPreviewResponse>("/tts/preview", data)
return response.data
}
-31
View File
@@ -1,31 +0,0 @@
/**
* TTS 语音合成 API — 目录化入口
* 保持与原 tts.ts 相同导出,向后兼容
*/
// 类型
export type {
TTSMetadata,
TTSSynthesizeRequest,
TTSSynthesizeResponse,
TTSJob,
TTSJobStatus,
TTSJobListResponse,
TTSJobListParams,
SaveTtsToLibraryRequest,
TTSVoice,
TTSPreviewRequest,
TTSPreviewResponse,
} from "./types"
// API 函数
export {
synthesizeSpeech,
getTTSJob,
getTTSJobStatus,
getTTSJobs,
saveTtsToLibrary,
deleteTTSJob,
getTtsVoices,
previewTts,
} from "./jobs"
-72
View File
@@ -1,72 +0,0 @@
/**
* TTS 语音合成 API 函数
*/
import apiClient from "../client"
import type {
TTSSynthesizeRequest,
TTSSynthesizeResponse,
TTSJob,
TTSJobStatus,
TTSJobListResponse,
TTSJobListParams,
SaveTtsToLibraryRequest,
TTSVoice,
TTSPreviewRequest,
TTSPreviewResponse,
} from "./types"
/** 创建 TTS 合成任务 */
export const synthesizeSpeech = async (
data: TTSSynthesizeRequest,
): Promise<TTSSynthesizeResponse> => {
const response = await apiClient.post<TTSSynthesizeResponse>("/tts/synthesize", data)
return response.data
}
/** 获取 TTS 任务详情 */
export const getTTSJob = async (jobId: string): Promise<TTSJob> => {
const response = await apiClient.get<TTSJob>(`/tts/jobs/${jobId}`)
return response.data
}
/** 获取 TTS 任务状态(轻量轮询) */
export const getTTSJobStatus = async (jobId: string): Promise<TTSJobStatus> => {
const response = await apiClient.get<TTSJobStatus>(`/tts/jobs/${jobId}/status`)
return response.data
}
/** 获取 TTS 任务列表 */
export const getTTSJobs = async (params?: TTSJobListParams): Promise<TTSJobListResponse> => {
const searchParams = new URLSearchParams()
if (params?.status) searchParams.set("status", params.status)
if (params?.skip !== undefined) searchParams.set("skip", String(params.skip))
if (params?.limit !== undefined) searchParams.set("limit", String(params.limit))
const qs = searchParams.toString()
const response = await apiClient.get<TTSJobListResponse>(`/tts/jobs${qs ? `?${qs}` : ""}`)
return response.data
}
/** 将 TTS 合成结果保存到配音库 */
export const saveTtsToLibrary = async (
jobId: string,
data?: SaveTtsToLibraryRequest,
): Promise<void> => {
await apiClient.post(`/tts/jobs/${jobId}/save-to-library`, data ?? {})
}
/** 删除 TTS 任务 */
export const deleteTTSJob = async (jobId: string): Promise<void> => {
await apiClient.delete(`/tts/jobs/${jobId}`)
}
/** 获取 TTS 音色列表 */
export const getTtsVoices = async (): Promise<TTSVoice[]> => {
const response = await apiClient.get<TTSVoice[]>("/tts/voices")
return response.data
}
/** TTS 试听 */
export const previewTts = async (data: TTSPreviewRequest): Promise<TTSPreviewResponse> => {
const response = await apiClient.post<TTSPreviewResponse>("/tts/preview", data)
return response.data
}
-112
View File
@@ -1,112 +0,0 @@
/**
* TTS 语音合成类型定义
*/
/** TTS 元数据 */
export interface TTSMetadata {
duration?: number
sample_rate?: number
language?: string
[key: string]: unknown
}
/** TTS 合成请求参数 */
export interface TTSSynthesizeRequest {
text: string
voice_id?: string
output_name?: string
language?: string
speed?: number
voice_model?: string
voice_clone_profile_id?: string
format?: string
metadata?: TTSMetadata
}
/** TTS 合成创建响应 */
export interface TTSSynthesizeResponse {
job_id: string
status: string
message: string
}
/** TTS 任务详情 */
export interface TTSJob {
id: string
user_id: string
project_id: string | null
text: string
voice_id: string | null
voice_model: string | null
voice_clone_profile_id: string | null
language: string
speed: number
output_name: string | null
output_audio_url: string | null
output_format: string
duration_seconds: number | null
file_size_bytes: number | null
sample_rate: number | null
status: string
error_message: string | null
retry_count: number
max_retries: number
metadata_: TTSMetadata | null
created_at: string
updated_at: string
}
/** TTS 任务状态(轻量轮询用) */
export interface TTSJobStatus {
id: string
status: string
output_audio_url: string | null
error_message: string | null
duration_seconds: number | null
retry_count: number
}
/** TTS 任务列表响应 */
export interface TTSJobListResponse {
items: TTSJob[]
total: number
skip: number
limit: number
}
/** TTS 任务列表查询参数 */
export interface TTSJobListParams {
status?: string
skip?: number
limit?: number
}
/** 存为素材请求参数 */
export interface SaveTtsToLibraryRequest {
name?: string
tag_ids?: string[]
}
/** TTS 音色 */
export interface TTSVoice {
id: string
name: string
category?: string
language?: string
preview_url?: string
description?: string
}
/** TTS 试听请求参数 */
export interface TTSPreviewRequest {
text: string
voice_id: string
speed?: number
pitch?: number
}
/** TTS 试听响应 */
export interface TTSPreviewResponse {
audio_url: string
duration?: number
}