feat: 优化
This commit is contained in:
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
|
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
|
||||||
import { Empty } from 'ant-design-vue'
|
import { Empty, message } from 'ant-design-vue'
|
||||||
import { SoundOutlined } from '@ant-design/icons-vue'
|
import { SoundOutlined } from '@ant-design/icons-vue'
|
||||||
import { useVoiceCopyStore } from '@/stores/voiceCopy'
|
import { useVoiceCopyStore } from '@/stores/voiceCopy'
|
||||||
import { useTTS, TTS_PROVIDERS } from '@/composables/useTTS'
|
import { useTTS, TTS_PROVIDERS } from '@/composables/useTTS'
|
||||||
@@ -205,6 +205,14 @@ const initPlayer = (url) => {
|
|||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
try {
|
try {
|
||||||
|
// 检查容器是否存在
|
||||||
|
if (!playerContainer.value) {
|
||||||
|
message.error('播放器容器未就绪')
|
||||||
|
isPlayerInitializing.value = false
|
||||||
|
audioUrl.value = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
player = new APlayer({
|
player = new APlayer({
|
||||||
container: playerContainer.value,
|
container: playerContainer.value,
|
||||||
autoplay: true,
|
autoplay: true,
|
||||||
@@ -225,6 +233,7 @@ const initPlayer = (url) => {
|
|||||||
|
|
||||||
player.on('error', (e) => {
|
player.on('error', (e) => {
|
||||||
console.error('APlayer 播放错误:', e)
|
console.error('APlayer 播放错误:', e)
|
||||||
|
message.error('音频播放失败,请重试')
|
||||||
})
|
})
|
||||||
|
|
||||||
player.on('canplay', () => {
|
player.on('canplay', () => {
|
||||||
@@ -232,7 +241,9 @@ const initPlayer = (url) => {
|
|||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('APlayer 初始化失败:', e)
|
console.error('APlayer 初始化失败:', e)
|
||||||
|
message.error('播放器初始化失败,请重试')
|
||||||
isPlayerInitializing.value = false
|
isPlayerInitializing.value = false
|
||||||
|
audioUrl.value = ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ref, reactive } from 'vue'
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { MaterialService } from '@/api/material'
|
import { MaterialService } from '@/api/material'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import { OSS_ORIGINAL, isDev } from '@gold/config/api'
|
||||||
|
|
||||||
// GB转字节常量
|
// GB转字节常量
|
||||||
const GB_TO_BYTES = 1073741824
|
const GB_TO_BYTES = 1073741824
|
||||||
@@ -106,11 +107,12 @@ export function useUpload() {
|
|||||||
reject(new Error('网络错误,上传失败'))
|
reject(new Error('网络错误,上传失败'))
|
||||||
})
|
})
|
||||||
|
|
||||||
// 发起PUT请求 - 使用代理路径
|
// 发起PUT请求
|
||||||
const uploadUrl = presignedData.presignedUrl.replace(
|
// 开发环境:使用 /oss 代理避免CORS问题
|
||||||
'https://muye-ai-chat.oss-cn-hangzhou.aliyuncs.com',
|
// 生产环境:直接使用OSS原始域名(需要OSS配置CORS)
|
||||||
'/oss'
|
const uploadUrl = isDev()
|
||||||
)
|
? presignedData.presignedUrl.replace(OSS_ORIGINAL, '/oss')
|
||||||
|
: presignedData.presignedUrl
|
||||||
xhr.open('PUT', uploadUrl)
|
xhr.open('PUT', uploadUrl)
|
||||||
if (presignedData.headers && presignedData.headers['Content-Type']) {
|
if (presignedData.headers && presignedData.headers['Content-Type']) {
|
||||||
xhr.setRequestHeader('Content-Type', presignedData.headers['Content-Type'])
|
xhr.setRequestHeader('Content-Type', presignedData.headers['Content-Type'])
|
||||||
|
|||||||
@@ -406,10 +406,16 @@ const handleSaveGroupName = async (group) => {
|
|||||||
const handleDeleteGroup = async (group, event) => {
|
const handleDeleteGroup = async (group, event) => {
|
||||||
event?.stopPropagation?.()
|
event?.stopPropagation?.()
|
||||||
|
|
||||||
|
// 校验:分组内还有文件时不允许删除
|
||||||
|
if (group.fileCount > 0) {
|
||||||
|
message.warning(`分组「${group.name}」内还有 ${group.fileCount} 个文件,请先删除文件后再删除分组`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const confirmed = await new Promise((resolve) => {
|
const confirmed = await new Promise((resolve) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '删除分组',
|
title: '删除分组',
|
||||||
content: `确定要删除分组「${group.name}」吗?删除后该分组下的所有文件将被移动到默认分组。此操作不可恢复。`,
|
content: `确定要删除分组「${group.name}」吗?此操作不可恢复。`,
|
||||||
okText: '确认删除',
|
okText: '确认删除',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
okType: 'danger',
|
okType: 'danger',
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
'/oss': {
|
'/oss': {
|
||||||
target: 'https://muye-ai-chat.oss-cn-hangzhou.aliyuncs.com',
|
target: 'https://oss.muyetools.cn',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/oss/, ''),
|
rewrite: (path: string) => path.replace(/^\/oss/, ''),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -38,6 +38,21 @@ export const API_BASE = {
|
|||||||
AI_APP: `${BASE_URL}/api/ai`,
|
AI_APP: `${BASE_URL}/api/ai`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OSS 原始域名(用于上传预签名URL代理)
|
||||||
|
*/
|
||||||
|
export const OSS_ORIGINAL = 'https://muye-ai-chat.oss-cn-hangzhou.aliyuncs.com'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为开发环境
|
||||||
|
*/
|
||||||
|
export const isDev = () => {
|
||||||
|
if (typeof import.meta !== 'undefined' && import.meta.env) {
|
||||||
|
return import.meta.env.DEV
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取完整的 API 路径
|
* 获取完整的 API 路径
|
||||||
* @param {string} module - 模块名称 (如 'ADMIN_AI', 'APP_MEMBER')
|
* @param {string} module - 模块名称 (如 'ADMIN_AI', 'APP_MEMBER')
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String presignGetUrl(String url, Integer expirationSeconds) {
|
public String presignGetUrl(String url, Integer expirationSeconds) {
|
||||||
// 1. 将 url 转换为 path
|
// 1. 将 url 转换为 path(支持多种URL格式)
|
||||||
String path = StrUtil.removePrefix(url, config.getDomain() + "/");
|
String path = extractPathFromUrl(url);
|
||||||
path = HttpUtils.removeUrlQuery(path);
|
path = HttpUtils.removeUrlQuery(path);
|
||||||
String decodedPath = URLUtil.decode(path, StandardCharsets.UTF_8);
|
String decodedPath = URLUtil.decode(path, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
@@ -138,6 +138,37 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
|||||||
return signedUrl.toString();
|
return signedUrl.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从URL中提取相对路径
|
||||||
|
* 支持多种URL格式:
|
||||||
|
* 1. CDN域名:https://oss.muyetools.cn/path/to/file
|
||||||
|
* 2. OSS原始域名:https://bucket.oss-region.aliyuncs.com/path/to/file
|
||||||
|
* 3. 相对路径:path/to/file
|
||||||
|
*/
|
||||||
|
private String extractPathFromUrl(String url) {
|
||||||
|
if (StrUtil.isEmpty(url)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 尝试移除配置的域名前缀
|
||||||
|
String path = StrUtil.removePrefix(url, config.getDomain() + "/");
|
||||||
|
if (!StrUtil.equals(url, path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 如果不是以配置域名开头,检查是否是完整URL
|
||||||
|
if (url.contains("://")) {
|
||||||
|
// 提取域名后的路径
|
||||||
|
int pathStart = url.indexOf("/", url.indexOf("://") + 3);
|
||||||
|
if (pathStart > 0) {
|
||||||
|
return url.substring(pathStart + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 已经是相对路径,直接返回
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于 bucket + endpoint 构建访问的 Domain 地址
|
* 基于 bucket + endpoint 构建访问的 Domain 地址
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -151,9 +151,8 @@ public class BatchProduceAlignment {
|
|||||||
String dateDir = java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
|
String dateDir = java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
String outputMediaPath = mixDirectory + "/" + dateDir + "/" + targetFileName + ".mp4";
|
String outputMediaPath = mixDirectory + "/" + dateDir + "/" + targetFileName + ".mp4";
|
||||||
|
|
||||||
// 使用默认的阿里云OSS endpoint格式
|
// ICE写入必须使用OSS原始域名(不能是CDN域名,因为ICE需要写权限)
|
||||||
String bucketEndpoint = "https://" + properties.getBucket() + ".oss-" + properties.getRegionId() + ".aliyuncs.com";
|
String outputMediaUrl = properties.getOssWriteUrl(outputMediaPath);
|
||||||
String outputMediaUrl = bucketEndpoint + "/" + outputMediaPath;
|
|
||||||
|
|
||||||
// ICE需要将处理结果写入到该URL,签名URL会导致写入失败
|
// ICE需要将处理结果写入到该URL,签名URL会导致写入失败
|
||||||
int width = 720;
|
int width = 720;
|
||||||
@@ -374,8 +373,8 @@ public class BatchProduceAlignment {
|
|||||||
String dateDir = java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
|
String dateDir = java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
String outputMediaPath = mixDirectory + "/" + dateDir + "/" + targetFileName + ".mp4";
|
String outputMediaPath = mixDirectory + "/" + dateDir + "/" + targetFileName + ".mp4";
|
||||||
|
|
||||||
String bucketEndpoint = "https://" + properties.getBucket() + ".oss-" + properties.getRegionId() + ".aliyuncs.com";
|
// ICE写入必须使用OSS原始域名(不能是CDN域名,因为ICE需要写权限)
|
||||||
String outputMediaUrl = bucketEndpoint + "/" + outputMediaPath;
|
String outputMediaUrl = properties.getOssWriteUrl(outputMediaPath);
|
||||||
|
|
||||||
int width = 720;
|
int width = 720;
|
||||||
int height = 1280;
|
int height = 1280;
|
||||||
|
|||||||
@@ -53,4 +53,11 @@ public class IceProperties {
|
|||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled && StrUtil.isNotBlank(accessKeyId) && StrUtil.isNotBlank(accessKeySecret);
|
return enabled && StrUtil.isNotBlank(accessKeyId) && StrUtil.isNotBlank(accessKeySecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取ICE写入用的OSS URL(必须使用原始域名)
|
||||||
|
*/
|
||||||
|
public String getOssWriteUrl(String path) {
|
||||||
|
return "https://" + bucket + ".oss-" + regionId + ".aliyuncs.com/" + path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -551,24 +551,6 @@ public class MixTaskServiceImpl implements MixTaskService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 从OSS URL中提取相对路径
|
|
||||||
*/
|
|
||||||
private String extractPathFromUrl(String ossUrl) {
|
|
||||||
if (StrUtil.isEmpty(ossUrl)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找 ".aliyuncs.com/" 的位置
|
|
||||||
int index = ossUrl.indexOf(".aliyuncs.com/");
|
|
||||||
if (index == -1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取 "/" 后的路径
|
|
||||||
return ossUrl.substring(index + ".aliyuncs.com/".length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将DO分页结果转换为VO分页结果,消除冗余代码
|
* 将DO分页结果转换为VO分页结果,消除冗余代码
|
||||||
* 优化点:
|
* 优化点:
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ yudao:
|
|||||||
region-id: cn-hangzhou
|
region-id: cn-hangzhou
|
||||||
bucket: muye-ai-chat
|
bucket: muye-ai-chat
|
||||||
enabled: true
|
enabled: true
|
||||||
|
oss-domain: https://oss.muyetools.cn # CDN加速域名(用于文件访问)
|
||||||
captcha:
|
captcha:
|
||||||
enable: false # 关闭图片验证码,方便登录等接口的测试
|
enable: false # 关闭图片验证码,方便登录等接口的测试
|
||||||
security:
|
security:
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ yudao:
|
|||||||
region-id: cn-hangzhou
|
region-id: cn-hangzhou
|
||||||
bucket: muye-ai-chat
|
bucket: muye-ai-chat
|
||||||
enabled: true
|
enabled: true
|
||||||
|
oss-domain: https://oss.muyetools.cn # CDN加速域名(用于文件访问)
|
||||||
dify:
|
dify:
|
||||||
api-url: http://127.0.0.1:8088 # Dify API 地址,请根据实际情况修改
|
api-url: http://127.0.0.1:8088 # Dify API 地址,请根据实际情况修改
|
||||||
timeout: 240 # 请求超时时间(秒)
|
timeout: 240 # 请求超时时间(秒)
|
||||||
|
|||||||
Reference in New Issue
Block a user