feat: 添加任务状态级联触发器,优化支付和做同款功能

主要更新:
- 添加 MySQL 触发器实现 task_status 表到其他表的状态级联
- 移除控制器中的多表状态检查代码
- 完善做同款功能,支持参数传递
- 支付宝 USD 转 CNY 汇率转换
- 修复状态枚举映射问题

注意: 触发器仅在 task_status 更新时触发,部分代码仍直接更新业务表
This commit is contained in:
AIGC Developer
2025-12-08 13:54:02 +08:00
parent 624d560fb4
commit 3c37006ebd
84 changed files with 5325 additions and 1668 deletions

View File

@@ -33,7 +33,7 @@
</nav>
<div class="sidebar-footer">
<div class="online-users">
{{ $t('nav.onlineUsers') }}: <span class="highlight">{{ onlineUsers }}</span>
{{ $t('nav.todayVisitors') }}: <span class="highlight">{{ onlineUsers }}</span>
</div>
<div class="system-uptime">
{{ $t('nav.systemUptime') }}: <span class="highlight">{{ systemUptime }}</span>
@@ -108,7 +108,7 @@
</div>
<div class="card-body">
<p class="price">${{ level.price || 0 }}{{ $t('systemSettings.perMonth') }}</p>
<p class="description">{{ level.description || $t('systemSettings.includesPoints', { points: level.resourcePoints || 0 }) }}</p>
<p class="description">{{ $t('systemSettings.includesPointsPerMonth', { points: level.resourcePoints || level.pointsBonus || 0 }) }}</p>
</div>
<div class="card-footer">
<el-button type="primary" @click="editLevel(level)">{{ $t('common.edit') }}</el-button>
@@ -360,26 +360,6 @@
>
<label for="monthly" class="radio-label">{{ $t('systemSettings.monthly') }}</label>
</div>
<div class="radio-option">
<input
type="radio"
id="quarterly"
v-model="editForm.validityPeriod"
value="quarterly"
class="radio-input"
>
<label for="quarterly" class="radio-label">{{ $t('systemSettings.quarterly') }}</label>
</div>
<div class="radio-option">
<input
type="radio"
id="yearly"
v-model="editForm.validityPeriod"
value="yearly"
class="radio-input"
>
<label for="yearly" class="radio-label">{{ $t('systemSettings.yearly') }}</label>
</div>
</div>
</div>
</el-form>
@@ -447,9 +427,10 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import {
Grid,
User,
@@ -467,13 +448,14 @@ import { getMembershipLevels, updateMembershipLevel } from '@/api/members'
import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
const router = useRouter()
const { t } = useI18n()
// 选项卡状态
const activeTab = ref('membership')
// 系统状态数据
const onlineUsers = ref('0/500')
const systemUptime = ref('加载中...')
const onlineUsers = ref('0')
const systemUptime = ref(t('nav.loading'))
// 会员收费标准相关
const membershipLevels = ref([])
@@ -486,18 +468,18 @@ const editForm = reactive({
level: '',
price: '',
resourcePoints: 0,
validityPeriod: 'quarterly'
validityPeriod: 'monthly'
})
const editRules = reactive({
level: [{ required: true, message: '请选择会员等级', trigger: 'change' }],
const editRules = computed(() => ({
level: [{ required: true, message: t('systemSettings.selectLevelRequired'), trigger: 'change' }],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' },
{ pattern: /^\d+(\.\d+)?$/, message: '请输入有效的数字', trigger: 'blur' }
{ required: true, message: t('systemSettings.enterPriceRequired'), trigger: 'blur' },
{ pattern: /^\d+(\.\d+)?$/, message: t('systemSettings.enterValidNumber'), trigger: 'blur' }
],
resourcePoints: [{ required: true, message: '请输入资源点数量', trigger: 'blur' }],
validityPeriod: [{ required: true, message: '请选择有效期', trigger: 'change' }]
})
resourcePoints: [{ required: true, message: t('systemSettings.enterResourcePointsRequired'), trigger: 'blur' }],
validityPeriod: [{ required: true, message: t('systemSettings.selectValidityRequired'), trigger: 'change' }]
}))
// 任务清理相关
const cleanupStats = ref(null)
@@ -512,12 +494,12 @@ const userCleanupForm = reactive({
username: ''
})
const userCleanupRules = reactive({
const userCleanupRules = computed(() => ({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 2, max: 50, message: '用户名长度在2到50个字符', trigger: 'blur' }
{ required: true, message: t('systemSettings.enterUsernameRequired'), trigger: 'blur' },
{ min: 2, max: 50, message: t('systemSettings.usernameLengthLimit'), trigger: 'blur' }
]
})
}))
const cleanupConfig = reactive({
retentionDays: 30,
@@ -595,7 +577,7 @@ const saveEdit = async () => {
price: parseFloat(editForm.price),
resourcePoints: parseInt(editForm.resourcePoints),
pointsBonus: parseInt(editForm.resourcePoints),
description: `包含${editForm.resourcePoints}资源点/月`
description: t('systemSettings.includesPointsPerMonth', { points: editForm.resourcePoints })
}
await updateMembershipLevel(editForm.id, updateData)
@@ -606,17 +588,17 @@ const saveEdit = async () => {
membershipLevels.value[index].price = parseFloat(editForm.price)
membershipLevels.value[index].pointsBonus = parseInt(editForm.resourcePoints)
membershipLevels.value[index].resourcePoints = parseInt(editForm.resourcePoints)
membershipLevels.value[index].description = `包含${editForm.resourcePoints}资源点/月`
membershipLevels.value[index].description = t('systemSettings.includesPointsPerMonth', { points: editForm.resourcePoints })
}
ElMessage.success('会员等级更新成功')
ElMessage.success(t('systemSettings.membershipUpdateSuccess'))
editDialogVisible.value = false
// 重新加载会员等级配置
await loadMembershipLevels()
} catch (error) {
console.error('更新会员等级失败:', error)
ElMessage.error('更新会员等级失败: ' + (error.response?.data?.message || error.message))
console.error('Update membership level failed:', error)
ElMessage.error(t('systemSettings.membershipUpdateFailed') + ': ' + (error.response?.data?.message || error.message))
}
}
@@ -649,31 +631,31 @@ const loadMembershipLevels = async () => {
price: level.price || 0,
resourcePoints: level.pointsBonus || 0,
pointsBonus: level.pointsBonus || 0,
description: level.description || `包含${level.pointsBonus || 0}资源点/月`
description: level.description || t('systemSettings.includesPointsPerMonth', { points: level.pointsBonus || 0 })
}))
console.log('会员等级配置加载成功:', membershipLevels.value)
console.log('Membership levels loaded:', membershipLevels.value)
} else {
// 如果没有数据,使用默认值
console.warn('数据库中没有会员等级数据,使用默认值')
console.warn('No membership data in database, using defaults')
membershipLevels.value = [
{ id: 1, name: '免费版会员', price: 0, resourcePoints: 200, description: '包含200资源点/月' },
{ id: 2, name: '标准版会员', price: 59, resourcePoints: 500, description: '包含500资源点/月' },
{ id: 3, name: '专业版会员', price: 250, resourcePoints: 2000, description: '包含2000资源点/月' }
{ id: 1, name: t('systemSettings.freeMembership'), price: 0, resourcePoints: 200, description: t('systemSettings.includesPointsPerMonth', { points: 200 }) },
{ id: 2, name: t('systemSettings.standardMembership'), price: 59, resourcePoints: 500, description: t('systemSettings.includesPointsPerMonth', { points: 500 }) },
{ id: 3, name: t('systemSettings.professionalMembership'), price: 250, resourcePoints: 2000, description: t('systemSettings.includesPointsPerMonth', { points: 2000 }) }
]
}
} catch (error) {
console.error('加载会员等级配置失败:', error)
console.error('错误详情:', error.response?.data || error.message)
console.error('Load membership config failed:', error)
console.error('Error details:', error.response?.data || error.message)
// 显示更详细的错误信息
const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || '未知错误'
ElMessage.warning(`加载会员等级配置失败: ${errorMessage},使用默认配置`)
const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('systemSettings.unknown')
ElMessage.warning(`${t('systemSettings.loadMembershipFailed')}: ${errorMessage}, ${t('systemSettings.usingDefaultConfig')}`)
// 使用默认值,确保页面可以正常显示
membershipLevels.value = [
{ id: 1, name: '免费版会员', price: 0, resourcePoints: 200, description: '包含200资源点/月' },
{ id: 2, name: '标准版会员', price: 59, resourcePoints: 500, description: '包含500资源点/月' },
{ id: 3, name: '专业版会员', price: 250, resourcePoints: 2000, description: '包含2000资源点/月' }
{ id: 1, name: t('systemSettings.freeMembership'), price: 0, resourcePoints: 200, description: t('systemSettings.includesPointsPerMonth', { points: 200 }) },
{ id: 2, name: t('systemSettings.standardMembership'), price: 59, resourcePoints: 500, description: t('systemSettings.includesPointsPerMonth', { points: 500 }) },
{ id: 3, name: t('systemSettings.professionalMembership'), price: 250, resourcePoints: 2000, description: t('systemSettings.includesPointsPerMonth', { points: 2000 }) }
]
} finally {
loadingLevels.value = false
@@ -691,10 +673,10 @@ const refreshStats = async () => {
try {
const response = await cleanupApi.getCleanupStats()
cleanupStats.value = response.data
ElMessage.success('统计信息刷新成功')
ElMessage.success(t('systemSettings.statsRefreshSuccess'))
} catch (error) {
console.error('获取统计信息失败:', error)
ElMessage.error('获取统计信息失败')
console.error('Get statistics failed:', error)
ElMessage.error(t('systemSettings.statsRefreshFailed'))
} finally {
loadingStats.value = false
}
@@ -705,13 +687,13 @@ const performFullCleanup = async () => {
loadingCleanup.value = true
try {
const response = await cleanupApi.performFullCleanup()
ElMessage.success('完整清理执行成功')
console.log('清理结果:', response.data)
ElMessage.success(t('systemSettings.fullCleanupSuccess'))
console.log('Cleanup result:', response.data)
// 刷新统计信息
await refreshStats()
} catch (error) {
console.error('执行完整清理失败:', error)
ElMessage.error('执行完整清理失败')
console.error('Execute full cleanup failed:', error)
ElMessage.error(t('systemSettings.fullCleanupFailed'))
} finally {
loadingCleanup.value = false
}
@@ -731,15 +713,15 @@ const performUserCleanup = async () => {
loadingUserCleanup.value = true
try {
const response = await cleanupApi.cleanupUserTasks(userCleanupForm.username)
ElMessage.success('用户任务清理成功')
console.log('清理结果:', response.data)
ElMessage.success(t('systemSettings.userCleanupSuccess'))
console.log('Cleanup result:', response.data)
// 刷新统计信息
await refreshStats()
// 关闭对话框
handleCloseUserCleanupDialog()
} catch (error) {
console.error('清理用户任务失败:', error)
ElMessage.error('清理用户任务失败')
console.error('Cleanup user tasks failed:', error)
ElMessage.error(t('systemSettings.userCleanupFailed'))
} finally {
loadingUserCleanup.value = false
}
@@ -761,17 +743,17 @@ const performUserCleanup_old = async () => {
if (response.ok) {
const result = await response.json()
ElMessage.success(`用户 ${userCleanupForm.username} 的任务清理完成`)
console.log('用户清理结果:', result)
ElMessage.success(t('systemSettings.userCleanupSuccess'))
console.log('User cleanup result:', result)
// 关闭对话框并刷新统计信息
handleCloseUserCleanupDialog()
await refreshStats()
} else {
ElMessage.error('清理用户任务失败')
ElMessage.error(t('systemSettings.userCleanupFailed'))
}
} catch (error) {
console.error('清理用户任务失败:', error)
ElMessage.error('清理用户任务失败')
console.error('Cleanup user tasks failed:', error)
ElMessage.error(t('systemSettings.userCleanupFailed'))
} finally {
loadingUserCleanup.value = false
}
@@ -783,10 +765,10 @@ const saveCleanupConfig = async () => {
// 这里可以添加保存配置的API调用
// 目前只是模拟保存
await new Promise(resolve => setTimeout(resolve, 1000))
ElMessage.success('清理配置保存成功')
ElMessage.success(t('systemSettings.configSaveSuccess'))
} catch (error) {
console.error('保存清理配置失败:', error)
ElMessage.error('保存清理配置失败')
console.error('Save cleanup config failed:', error)
ElMessage.error(t('systemSettings.configSaveFailed'))
} finally {
loadingConfig.value = false
}
@@ -795,7 +777,11 @@ const saveCleanupConfig = async () => {
// 加载AI模型设置
const loadAiModelSettings = async () => {
try {
const response = await fetch('/api/admin/settings')
const response = await fetch('/api/admin/settings', {
headers: {
'Authorization': `Bearer ${sessionStorage.getItem('token')}`
}
})
if (response.ok) {
const data = await response.json()
if (data.promptOptimizationModel) {
@@ -812,7 +798,7 @@ const loadAiModelSettings = async () => {
}
}
} catch (error) {
console.error('加载AI模型设置失败:', error)
console.error('Load AI model settings failed:', error)
}
}
@@ -823,7 +809,8 @@ const saveAiModelSettings = async () => {
const response = await fetch('/api/admin/settings', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
'Authorization': `Bearer ${sessionStorage.getItem('token')}`
},
body: JSON.stringify({
promptOptimizationModel: promptOptimizationModel.value,
@@ -833,13 +820,13 @@ const saveAiModelSettings = async () => {
})
})
if (response.ok) {
ElMessage.success('AI模型设置保存成功')
ElMessage.success(t('systemSettings.aiModelSaveSuccess'))
} else {
throw new Error('保存失败')
throw new Error('Save failed')
}
} catch (error) {
console.error('保存AI模型设置失败:', error)
ElMessage.error('保存AI模型设置失败')
console.error('Save AI model settings failed:', error)
ElMessage.error(t('systemSettings.aiModelSaveFailed'))
} finally {
savingAiModel.value = false
}
@@ -853,22 +840,26 @@ onMounted(() => {
loadAiModelSettings()
})
// 获取系统统计数据
// 获取系统统计数据(当天访问人数和系统运行时间)
const fetchSystemStats = async () => {
try {
// 临时使用计算值后续可以从API获取
// 计算在线用户数(这里简化处理)
const randomOnline = Math.floor(Math.random() * 50) + 10
onlineUsers.value = `${randomOnline}/500`
// 计算系统运行时间(基于当前时间简单模拟)
const hours = new Date().getHours()
const minutes = new Date().getMinutes()
systemUptime.value = `${hours}小时${minutes}`
const response = await fetch('/api/admin/online-stats', {
headers: {
'Authorization': `Bearer ${sessionStorage.getItem('token')}`
}
})
const data = await response.json()
if (data.success) {
onlineUsers.value = data.todayVisitors || 0
systemUptime.value = data.uptime || t('systemSettings.unknown')
} else {
onlineUsers.value = '0'
systemUptime.value = t('systemSettings.unknown')
}
} catch (error) {
console.error('获取系统统计失败:', error)
onlineUsers.value = '0/500'
systemUptime.value = '未知'
console.error('Get online stats failed:', error)
onlineUsers.value = '0'
systemUptime.value = t('systemSettings.unknown')
}
}
</script>
@@ -954,15 +945,15 @@ const fetchSystemStats = async () => {
.online-users,
.system-uptime {
font-size: 14px;
color: #64748b;
margin-bottom: 5px;
font-size: 13px;
color: #6b7280;
margin-bottom: 8px;
line-height: 1.5;
}
.highlight {
color: #333;
font-weight: bold;
color: #3b82f6;
font-weight: 600;
}
.main-content {