更新功能和文档: 增强支付系统、任务队列管理、用户作品管理等功能
This commit is contained in:
@@ -286,3 +286,5 @@ grep "img2vid_abc123def456" logs/application.log
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -287,3 +287,5 @@ public TaskQueue addTextToVideoTask(String username, String taskId) {
|
|||||||
5. **监控告警**: 监控积分冻结系统的运行状态
|
5. **监控告警**: 监控积分冻结系统的运行状态
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,3 +32,5 @@ public class PasswordChecker {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -296,3 +296,5 @@ const startPolling = (taskId) => {
|
|||||||
如有问题,请联系开发团队或查看系统日志获取详细错误信息。
|
如有问题,请联系开发团队或查看系统日志获取详细错误信息。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -279,3 +279,5 @@ POST /api/text-to-video/tasks/{id}/cancel # 取消任务
|
|||||||
**文生视频API已成功实现并可以投入使用!** 🎉
|
**文生视频API已成功实现并可以投入使用!** 🎉
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -166,3 +166,5 @@ const updateWork = async (workId, updateData) => {
|
|||||||
5. **用户体验**: 提供友好的作品管理界面
|
5. **用户体验**: 提供友好的作品管理界面
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -432,3 +432,5 @@ MIT License
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,3 +28,5 @@ console.log('App.vue 加载成功')
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ export const login = (credentials) => {
|
|||||||
return api.post('/auth/login', credentials)
|
return api.post('/auth/login', credentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 邮箱验证码登录
|
||||||
|
export const loginWithEmail = (credentials) => {
|
||||||
|
return api.post('/auth/login/email', credentials)
|
||||||
|
}
|
||||||
|
|
||||||
export const register = (userData) => {
|
export const register = (userData) => {
|
||||||
return api.post('/auth/register', userData)
|
return api.post('/auth/register', userData)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const cleanupApi = {
|
|||||||
// 获取清理统计信息(原始fetch方式,用于测试)
|
// 获取清理统计信息(原始fetch方式,用于测试)
|
||||||
async getCleanupStatsRaw() {
|
async getCleanupStatsRaw() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/cleanup/cleanup-stats')
|
const response = await fetch('http://localhost:8080/api/cleanup/cleanup-stats')
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return await response.json()
|
return await response.json()
|
||||||
} else {
|
} else {
|
||||||
@@ -44,7 +44,7 @@ export const cleanupApi = {
|
|||||||
// 执行完整清理(原始fetch方式,用于测试)
|
// 执行完整清理(原始fetch方式,用于测试)
|
||||||
async performFullCleanupRaw() {
|
async performFullCleanupRaw() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/cleanup/full-cleanup', {
|
const response = await fetch('http://localhost:8080/api/cleanup/full-cleanup', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
|||||||
58
demo/frontend/src/api/userWorks.js
Normal file
58
demo/frontend/src/api/userWorks.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import request from './request'
|
||||||
|
|
||||||
|
// 获取我的作品列表
|
||||||
|
export const getMyWorks = (params = {}) => {
|
||||||
|
return request({
|
||||||
|
url: '/works/my-works',
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
page: params.page || 0,
|
||||||
|
size: params.size || 10
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取作品详情
|
||||||
|
export const getWorkDetail = (workId) => {
|
||||||
|
return request({
|
||||||
|
url: `/works/${workId}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除作品
|
||||||
|
export const deleteWork = (workId) => {
|
||||||
|
return request({
|
||||||
|
url: `/works/${workId}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除作品
|
||||||
|
export const batchDeleteWorks = (workIds) => {
|
||||||
|
return request({
|
||||||
|
url: '/works/batch-delete',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
workIds: workIds
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新作品信息
|
||||||
|
export const updateWork = (workId, data) => {
|
||||||
|
return request({
|
||||||
|
url: `/works/${workId}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取作品统计信息
|
||||||
|
export const getWorkStats = () => {
|
||||||
|
return request({
|
||||||
|
url: '/works/stats',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@@ -91,3 +91,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
502
demo/frontend/src/components/PaymentModal.vue
Normal file
502
demo/frontend/src/components/PaymentModal.vue
Normal file
@@ -0,0 +1,502 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="title"
|
||||||
|
width="500px"
|
||||||
|
class="payment-modal"
|
||||||
|
:modal="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="true"
|
||||||
|
@close="handleClose"
|
||||||
|
center
|
||||||
|
>
|
||||||
|
<div class="payment-content">
|
||||||
|
<!-- 支付方式选择 -->
|
||||||
|
<div class="payment-methods">
|
||||||
|
<div
|
||||||
|
class="payment-method"
|
||||||
|
:class="{ active: selectedMethod === 'alipay' }"
|
||||||
|
@click="selectMethod('alipay')"
|
||||||
|
>
|
||||||
|
<div class="method-icon alipay-icon">
|
||||||
|
<el-icon><CreditCard /></el-icon>
|
||||||
|
</div>
|
||||||
|
<span>Alipay扫码支付</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="payment-method"
|
||||||
|
:class="{ active: selectedMethod === 'paypal' }"
|
||||||
|
@click="selectMethod('paypal')"
|
||||||
|
>
|
||||||
|
<div class="method-icon paypal-icon">
|
||||||
|
<el-icon><CreditCard /></el-icon>
|
||||||
|
</div>
|
||||||
|
<span>PayPal支付</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 金额显示 -->
|
||||||
|
<div class="amount-section">
|
||||||
|
<div class="amount-label">金额</div>
|
||||||
|
<div class="amount-value">${{ amount }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 二维码区域 -->
|
||||||
|
<div class="qr-section">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img id="qr-code-img" style="display: none; width: 200px; height: 200px;" alt="支付二维码" />
|
||||||
|
<div class="qr-placeholder">
|
||||||
|
<div class="qr-grid">
|
||||||
|
<div class="qr-dot" v-for="i in 64" :key="i"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="qr-tip">支付前请阅读《XX 付费服务协议》</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付提示 -->
|
||||||
|
<div class="action-section">
|
||||||
|
<div class="pay-tip">
|
||||||
|
<p>请使用支付宝扫描上方二维码完成支付</p>
|
||||||
|
<p class="tip-small">支付完成后页面将自动更新</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部链接 -->
|
||||||
|
<div class="footer-link">
|
||||||
|
<a href="#" @click.prevent="showAgreement">《XX 付费服务协议》</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { CreditCard } from '@element-plus/icons-vue'
|
||||||
|
import { createPayment, createAlipayPayment } from '@/api/payments'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '标准版会员'
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '32.00'
|
||||||
|
},
|
||||||
|
orderId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'pay-success', 'pay-error'])
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const selectedMethod = ref('alipay')
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 监听 modelValue 变化
|
||||||
|
watch(() => props.modelValue, (newVal) => {
|
||||||
|
visible.value = newVal
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 visible 变化
|
||||||
|
watch(visible, (newVal) => {
|
||||||
|
emit('update:modelValue', newVal)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 选择支付方式
|
||||||
|
const selectMethod = (method) => {
|
||||||
|
selectedMethod.value = method
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理支付
|
||||||
|
const handlePay = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
ElMessage.info('正在创建支付订单...')
|
||||||
|
|
||||||
|
// 创建支付订单数据
|
||||||
|
const paymentData = {
|
||||||
|
orderId: props.orderId,
|
||||||
|
amount: props.amount.toString(),
|
||||||
|
method: selectedMethod.value.toUpperCase(),
|
||||||
|
description: `${props.title} - ${selectedMethod.value === 'alipay' ? '支付宝' : 'PayPal'}支付`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('=== 开始支付流程 ===')
|
||||||
|
console.log('支付数据:', paymentData)
|
||||||
|
|
||||||
|
// 先创建支付记录
|
||||||
|
console.log('1. 创建支付订单...')
|
||||||
|
const createResponse = await createPayment(paymentData)
|
||||||
|
console.log('创建支付订单响应:', createResponse)
|
||||||
|
|
||||||
|
if (createResponse.data && createResponse.data.success) {
|
||||||
|
const paymentId = createResponse.data.data.id
|
||||||
|
console.log('2. 支付订单创建成功,ID:', paymentId)
|
||||||
|
|
||||||
|
if (selectedMethod.value === 'alipay') {
|
||||||
|
ElMessage.info('正在生成支付宝二维码...')
|
||||||
|
|
||||||
|
console.log('3. 创建支付宝支付...')
|
||||||
|
// 创建支付宝支付
|
||||||
|
const alipayResponse = await createAlipayPayment({ paymentId })
|
||||||
|
console.log('支付宝支付响应:', alipayResponse)
|
||||||
|
console.log('支付宝支付响应数据:', alipayResponse.data)
|
||||||
|
console.log('支付宝支付响应数据详情:', JSON.stringify(alipayResponse.data, null, 2))
|
||||||
|
|
||||||
|
if (alipayResponse.data && alipayResponse.data.success) {
|
||||||
|
// 显示二维码
|
||||||
|
const qrCode = alipayResponse.data.data.qrCode
|
||||||
|
console.log('4. 支付宝二维码:', qrCode)
|
||||||
|
|
||||||
|
// 更新二维码显示
|
||||||
|
const qrCodeElement = document.querySelector('#qr-code-img')
|
||||||
|
if (qrCodeElement) {
|
||||||
|
qrCodeElement.src = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrCode)}`
|
||||||
|
qrCodeElement.style.display = 'block'
|
||||||
|
console.log('5. 二维码图片已设置')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏模拟二维码
|
||||||
|
const qrPlaceholder = document.querySelector('.qr-placeholder')
|
||||||
|
if (qrPlaceholder) {
|
||||||
|
qrPlaceholder.style.display = 'none'
|
||||||
|
console.log('6. 模拟二维码已隐藏')
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage.success('二维码已生成,请使用支付宝扫码支付')
|
||||||
|
console.log('=== 支付流程完成 ===')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.error('支付宝响应失败:', alipayResponse)
|
||||||
|
ElMessage.error(alipayResponse.data?.message || '生成二维码失败')
|
||||||
|
emit('pay-error', new Error(alipayResponse.data?.message || '生成二维码失败'))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// PayPal支付处理
|
||||||
|
ElMessage.info('PayPal支付功能开发中...')
|
||||||
|
emit('pay-error', new Error('PayPal支付功能暂未开放'))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.error('创建支付订单失败:', createResponse)
|
||||||
|
ElMessage.error(createResponse.data?.message || '创建支付订单失败')
|
||||||
|
emit('pay-error', new Error(createResponse.data?.message || '创建支付订单失败'))
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('=== 支付流程出错 ===')
|
||||||
|
console.error('错误详情:', error)
|
||||||
|
console.error('错误响应:', error.response)
|
||||||
|
console.error('错误状态:', error.response?.status)
|
||||||
|
console.error('错误数据:', error.response?.data)
|
||||||
|
|
||||||
|
ElMessage.error(`支付失败:${error.message || '请重试'}`)
|
||||||
|
emit('pay-error', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭模态框
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示协议
|
||||||
|
const showAgreement = () => {
|
||||||
|
ElMessage.info('服务协议页面')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.payment-modal {
|
||||||
|
background: #0a0a0a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal :deep(.el-dialog) {
|
||||||
|
background: #0a0a0a;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal :deep(.el-dialog__header) {
|
||||||
|
background: #0a0a0a;
|
||||||
|
border-bottom: 1px solid #1a1a1a;
|
||||||
|
padding: 20px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal :deep(.el-dialog__title) {
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal :deep(.el-dialog__headerbtn) {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal :deep(.el-dialog__headerbtn:hover) {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-content {
|
||||||
|
padding: 24px;
|
||||||
|
background: #0a0a0a;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 支付方式选择 */
|
||||||
|
.payment-methods {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-method {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: #1a1a1a;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-method:hover {
|
||||||
|
background: #2a2a2a;
|
||||||
|
box-shadow: 0 4px 16px rgba(74, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-method.active {
|
||||||
|
background: linear-gradient(135deg, #4a9eff 0%, #3a8bdf 100%);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 16px rgba(74, 158, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #1677FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-method.active .method-icon {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-method span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 金额显示 */
|
||||||
|
.amount-section {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #e0e0e0;
|
||||||
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 二维码区域 */
|
||||||
|
.qr-section {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
margin: 0 auto 16px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-placeholder {
|
||||||
|
width: 180px;
|
||||||
|
height: 180px;
|
||||||
|
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-placeholder::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border: 3px solid #4a9eff;
|
||||||
|
border-radius: 8px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
opacity: 0.6;
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.6;
|
||||||
|
transform: translate(-50%, -50%) scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: translate(-50%, -50%) scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-placeholder::after {
|
||||||
|
content: '扫码支付';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, calc(-50% + 40px));
|
||||||
|
color: #4a9eff;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮 */
|
||||||
|
.action-section {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
background: #4a9eff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-button:hover {
|
||||||
|
background: #3a8bdf;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-button:disabled {
|
||||||
|
background: #666;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-button:disabled:hover {
|
||||||
|
background: #666;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 支付提示 */
|
||||||
|
.pay-tip {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-tip p {
|
||||||
|
margin: 8px 0;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-small {
|
||||||
|
color: #999 !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部链接 */
|
||||||
|
.footer-link {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-link a {
|
||||||
|
color: #4a9eff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-link a:hover {
|
||||||
|
color: #3a8bdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.payment-modal :deep(.el-dialog) {
|
||||||
|
width: 90%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-methods {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
width: 160px;
|
||||||
|
height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-placeholder {
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-value {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -167,7 +167,7 @@ const testGetStats = async () => {
|
|||||||
statsError.value = null
|
statsError.value = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/cleanup/cleanup-stats', {
|
const response = await fetch('http://localhost:8080/api/cleanup/cleanup-stats', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...getAuthHeaders()
|
...getAuthHeaders()
|
||||||
@@ -193,7 +193,7 @@ const testFullCleanup = async () => {
|
|||||||
cleanupError.value = null
|
cleanupError.value = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/cleanup/full-cleanup', {
|
const response = await fetch('http://localhost:8080/api/cleanup/full-cleanup', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -252,7 +252,7 @@ const testQueueStatus = async () => {
|
|||||||
queueError.value = null
|
queueError.value = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/diagnostic/queue-status')
|
const response = await fetch('http://localhost:8080/api/diagnostic/queue-status')
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
queueResult.value = await response.json()
|
queueResult.value = await response.json()
|
||||||
ElMessage.success('获取队列状态成功')
|
ElMessage.success('获取队列状态成功')
|
||||||
|
|||||||
@@ -201,11 +201,11 @@ const goToMyWorks = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTextToVideo = () => {
|
const goToTextToVideo = () => {
|
||||||
router.push('/text-to-video')
|
router.push('/text-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToStoryboardVideo = () => {
|
const goToStoryboardVideo = () => {
|
||||||
router.push('/storyboard-video')
|
router.push('/storyboard-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToCreate = (work) => {
|
const goToCreate = (work) => {
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const getEmailCode = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用后端API发送邮箱验证码
|
// 调用后端API发送邮箱验证码
|
||||||
const response = await fetch('/api/verification/email/send', {
|
const response = await fetch('http://localhost:8080/api/verification/email/send', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -176,7 +176,7 @@ const getEmailCode = async () => {
|
|||||||
|
|
||||||
// 开发模式:将验证码同步到后端
|
// 开发模式:将验证码同步到后端
|
||||||
try {
|
try {
|
||||||
await fetch('/api/verification/email/dev-set', {
|
await fetch('http://localhost:8080/api/verification/email/dev-set', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -234,7 +234,7 @@ const handleLogin = async () => {
|
|||||||
|
|
||||||
// 邮箱验证码登录
|
// 邮箱验证码登录
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/auth/login/email', {
|
const response = await fetch('http://localhost:8080/api/auth/login/email', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ import { ref, onMounted, computed } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Star, User, Compass, Document, VideoPlay, Picture, Film, Bell, Setting, Search } from '@element-plus/icons-vue'
|
import { Star, User, Compass, Document, VideoPlay, Picture, Film, Bell, Setting, Search } from '@element-plus/icons-vue'
|
||||||
|
import { getMyWorks } from '@/api/userWorks'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -343,12 +344,20 @@ const items = ref([])
|
|||||||
const loadList = async () => {
|
const loadList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/user-works')
|
const response = await getMyWorks({
|
||||||
const data = response.data.data || []
|
page: page.value - 1, // 后端使用0-based分页
|
||||||
|
size: pageSize.value
|
||||||
|
})
|
||||||
|
|
||||||
if (page.value === 1) items.value = []
|
if (response.data.success) {
|
||||||
items.value = items.value.concat(data)
|
const data = response.data.data || []
|
||||||
hasMore.value = data.length < pageSize.value
|
|
||||||
|
if (page.value === 1) items.value = []
|
||||||
|
items.value = items.value.concat(data)
|
||||||
|
hasMore.value = data.length === pageSize.value
|
||||||
|
} else {
|
||||||
|
throw new Error(response.data.message || '获取作品列表失败')
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载作品列表失败:', error)
|
console.error('加载作品列表失败:', error)
|
||||||
ElMessage.error('加载作品列表失败')
|
ElMessage.error('加载作品列表失败')
|
||||||
@@ -512,18 +521,18 @@ const goToSubscription = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTextToVideo = () => {
|
const goToTextToVideo = () => {
|
||||||
console.log('导航到文生视频')
|
console.log('导航到文生视频创作')
|
||||||
router.push('/text-to-video')
|
router.push('/text-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToImageToVideo = () => {
|
const goToImageToVideo = () => {
|
||||||
console.log('导航到图生视频')
|
console.log('导航到图生视频创作')
|
||||||
router.push('/image-to-video')
|
router.push('/image-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToStoryboardVideo = () => {
|
const goToStoryboardVideo = () => {
|
||||||
console.log('导航到分镜视频')
|
console.log('导航到分镜视频创作')
|
||||||
router.push('/storyboard-video')
|
router.push('/storyboard-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置筛选器
|
// 重置筛选器
|
||||||
|
|||||||
@@ -179,15 +179,15 @@ const goToMyWorks = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTextToVideo = () => {
|
const goToTextToVideo = () => {
|
||||||
router.push('/text-to-video')
|
router.push('/text-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToImageToVideo = () => {
|
const goToImageToVideo = () => {
|
||||||
router.push('/image-to-video')
|
router.push('/image-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToStoryboardVideo = () => {
|
const goToStoryboardVideo = () => {
|
||||||
router.push('/storyboard-video')
|
router.push('/storyboard-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到数据仪表盘
|
// 跳转到数据仪表盘
|
||||||
|
|||||||
@@ -201,11 +201,11 @@ const goToMyWorks = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTextToVideo = () => {
|
const goToTextToVideo = () => {
|
||||||
router.push('/text-to-video')
|
router.push('/text-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToImageToVideo = () => {
|
const goToImageToVideo = () => {
|
||||||
router.push('/image-to-video')
|
router.push('/image-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToCreate = (work) => {
|
const goToCreate = (work) => {
|
||||||
|
|||||||
@@ -129,7 +129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="package-price">$59/月</div>
|
<div class="package-price">$59/月</div>
|
||||||
<div class="points-box">每月200积分</div>
|
<div class="points-box">每月200积分</div>
|
||||||
<button class="package-button subscribe">立即订阅</button>
|
<button class="package-button subscribe" @click.stop="handleSubscribe('standard')">立即订阅</button>
|
||||||
<div class="package-features">
|
<div class="package-features">
|
||||||
<div class="feature-item">
|
<div class="feature-item">
|
||||||
<el-icon class="check-icon"><Check /></el-icon>
|
<el-icon class="check-icon"><Check /></el-icon>
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="package-price">$259/月</div>
|
<div class="package-price">$259/月</div>
|
||||||
<div class="points-box">每月1000积分</div>
|
<div class="points-box">每月1000积分</div>
|
||||||
<button class="package-button premium">立即订阅</button>
|
<button class="package-button premium" @click.stop="handleSubscribe('premium')">立即订阅</button>
|
||||||
<div class="package-features">
|
<div class="package-features">
|
||||||
<div class="feature-item">
|
<div class="feature-item">
|
||||||
<el-icon class="check-icon"><Check /></el-icon>
|
<el-icon class="check-icon"><Check /></el-icon>
|
||||||
@@ -226,12 +226,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 支付模态框 -->
|
||||||
|
<PaymentModal
|
||||||
|
v-model="paymentModalVisible"
|
||||||
|
:title="currentPaymentData.title"
|
||||||
|
:amount="currentPaymentData.amount"
|
||||||
|
:order-id="currentPaymentData.orderId"
|
||||||
|
@pay-success="handlePaymentSuccess"
|
||||||
|
@pay-error="handlePaymentError"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import MyWorks from '@/views/MyWorks.vue'
|
import MyWorks from '@/views/MyWorks.vue'
|
||||||
|
import PaymentModal from '@/components/PaymentModal.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { createPayment, createAlipayPayment } from '@/api/payments'
|
||||||
import {
|
import {
|
||||||
User,
|
User,
|
||||||
Document,
|
Document,
|
||||||
@@ -257,19 +270,21 @@ const goToMyWorks = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTextToVideo = () => {
|
const goToTextToVideo = () => {
|
||||||
router.push('/text-to-video')
|
router.push('/text-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToImageToVideo = () => {
|
const goToImageToVideo = () => {
|
||||||
router.push('/image-to-video')
|
router.push('/image-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToStoryboardVideo = () => {
|
const goToStoryboardVideo = () => {
|
||||||
router.push('/storyboard-video')
|
router.push('/storyboard-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 订单模态框相关
|
// 订单模态框相关
|
||||||
const orderDialogVisible = ref(false)
|
const orderDialogVisible = ref(false)
|
||||||
|
const paymentModalVisible = ref(false)
|
||||||
|
const currentPaymentData = ref({})
|
||||||
const orders = ref([
|
const orders = ref([
|
||||||
{
|
{
|
||||||
id: 'ORD-2024-001',
|
id: 'ORD-2024-001',
|
||||||
@@ -327,6 +342,216 @@ const selectedPlan = ref('free')
|
|||||||
const selectPlan = (plan) => {
|
const selectPlan = (plan) => {
|
||||||
selectedPlan.value = plan
|
selectedPlan.value = plan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理订阅
|
||||||
|
const handleSubscribe = async (planType) => {
|
||||||
|
console.log('handleSubscribe 被调用,planType:', planType)
|
||||||
|
try {
|
||||||
|
console.log('开始处理订阅...')
|
||||||
|
const planInfo = getPlanInfo(planType)
|
||||||
|
console.log('获取到的套餐信息:', planInfo)
|
||||||
|
|
||||||
|
// 设置支付数据
|
||||||
|
currentPaymentData.value = {
|
||||||
|
title: `${planInfo.name}会员`,
|
||||||
|
amount: planInfo.price,
|
||||||
|
orderId: `SUB_${planType}_${Date.now()}`,
|
||||||
|
planType: planType,
|
||||||
|
planInfo: planInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('支付数据设置完成:', currentPaymentData.value)
|
||||||
|
|
||||||
|
// 显示支付模态框
|
||||||
|
paymentModalVisible.value = true
|
||||||
|
console.log('支付模态框应该已显示')
|
||||||
|
|
||||||
|
// 直接生成二维码
|
||||||
|
ElMessage.info('正在生成支付二维码...')
|
||||||
|
await generateQRCode(planType, planInfo)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('订阅处理失败:', error)
|
||||||
|
ElMessage.error('订阅处理失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成二维码
|
||||||
|
const generateQRCode = async (planType, planInfo) => {
|
||||||
|
try {
|
||||||
|
console.log('=== 开始生成二维码 ===')
|
||||||
|
|
||||||
|
// 创建支付订单数据
|
||||||
|
const paymentData = {
|
||||||
|
orderId: `SUB_${planType}_${Date.now()}`,
|
||||||
|
amount: planInfo.price.toString(),
|
||||||
|
method: 'ALIPAY',
|
||||||
|
description: `${planInfo.name}会员 - 支付宝支付`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('1. 创建支付订单,数据:', paymentData)
|
||||||
|
const createResponse = await createPayment(paymentData)
|
||||||
|
console.log('创建支付订单响应:', createResponse)
|
||||||
|
|
||||||
|
if (createResponse.data && createResponse.data.success) {
|
||||||
|
const paymentId = createResponse.data.data.id
|
||||||
|
console.log('2. 支付订单创建成功,ID:', paymentId)
|
||||||
|
|
||||||
|
console.log('3. 创建支付宝支付...')
|
||||||
|
const alipayResponse = await createAlipayPayment({ paymentId })
|
||||||
|
console.log('支付宝支付响应:', alipayResponse)
|
||||||
|
|
||||||
|
if (alipayResponse.data && alipayResponse.data.success) {
|
||||||
|
const qrCode = alipayResponse.data.data.qrCode
|
||||||
|
console.log('4. 支付宝二维码:', qrCode)
|
||||||
|
|
||||||
|
if (qrCode) {
|
||||||
|
// 更新二维码显示
|
||||||
|
const qrCodeElement = document.querySelector('#qr-code-img')
|
||||||
|
if (qrCodeElement) {
|
||||||
|
qrCodeElement.src = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrCode)}`
|
||||||
|
qrCodeElement.style.display = 'block'
|
||||||
|
console.log('5. 二维码图片已设置')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏模拟二维码
|
||||||
|
const qrPlaceholder = document.querySelector('.qr-placeholder')
|
||||||
|
if (qrPlaceholder) {
|
||||||
|
qrPlaceholder.style.display = 'none'
|
||||||
|
console.log('6. 模拟二维码已隐藏')
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage.success('二维码已生成,请使用支付宝扫码支付')
|
||||||
|
console.log('=== 二维码生成完成 ===')
|
||||||
|
} else {
|
||||||
|
ElMessage.error('二维码生成失败:二维码为空')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('支付宝响应失败:', alipayResponse)
|
||||||
|
ElMessage.error(alipayResponse.data?.message || '生成二维码失败')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('创建支付订单失败:', createResponse)
|
||||||
|
ElMessage.error(createResponse.data?.message || '创建支付订单失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('=== 二维码生成出错 ===')
|
||||||
|
console.error('错误详情:', error)
|
||||||
|
ElMessage.error(`二维码生成失败:${error.message || '请重试'}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取套餐信息
|
||||||
|
const getPlanInfo = (planType) => {
|
||||||
|
const plans = {
|
||||||
|
standard: {
|
||||||
|
name: '标准版',
|
||||||
|
price: 59,
|
||||||
|
points: 200,
|
||||||
|
description: '标准版订阅 - 每月200积分'
|
||||||
|
},
|
||||||
|
premium: {
|
||||||
|
name: '专业版',
|
||||||
|
price: 259,
|
||||||
|
points: 1000,
|
||||||
|
description: '专业版订阅 - 每月1000积分'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plans[planType]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付成功处理
|
||||||
|
const handlePaymentSuccess = async (paymentData) => {
|
||||||
|
try {
|
||||||
|
ElMessage.success('支付成功!正在处理订单...')
|
||||||
|
|
||||||
|
// 这里可以添加支付成功后的处理逻辑
|
||||||
|
// 比如更新用户状态、发送确认邮件等
|
||||||
|
|
||||||
|
// 关闭支付模态框
|
||||||
|
paymentModalVisible.value = false
|
||||||
|
|
||||||
|
// 刷新页面或更新用户信息
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload()
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('支付成功处理失败:', error)
|
||||||
|
ElMessage.error('支付成功但处理订单失败,请联系客服')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付失败处理
|
||||||
|
const handlePaymentError = (error) => {
|
||||||
|
console.error('支付失败:', error)
|
||||||
|
ElMessage.error('支付失败,请重试')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建订阅订单
|
||||||
|
const createSubscriptionOrder = async (planType, planInfo) => {
|
||||||
|
try {
|
||||||
|
ElMessage.info('正在创建订单...')
|
||||||
|
|
||||||
|
// 生成订单号
|
||||||
|
const orderId = `SUB_${planType}_${Date.now()}`
|
||||||
|
|
||||||
|
// 创建支付订单数据
|
||||||
|
const paymentData = {
|
||||||
|
orderId: orderId,
|
||||||
|
amount: planInfo.price.toString(),
|
||||||
|
method: 'ALIPAY',
|
||||||
|
description: planInfo.description,
|
||||||
|
orderType: 'SUBSCRIPTION',
|
||||||
|
planType: planType
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先创建支付记录
|
||||||
|
const createResponse = await createPayment(paymentData)
|
||||||
|
|
||||||
|
if (createResponse.data.success) {
|
||||||
|
const paymentId = createResponse.data.data.id
|
||||||
|
|
||||||
|
// 然后创建支付宝支付
|
||||||
|
const alipayResponse = await createAlipayPayment({ paymentId })
|
||||||
|
|
||||||
|
if (alipayResponse.data.success) {
|
||||||
|
// 跳转到支付宝支付页面
|
||||||
|
const paymentForm = alipayResponse.data.data
|
||||||
|
// 创建临时div来显示支付表单
|
||||||
|
const tempDiv = document.createElement('div')
|
||||||
|
tempDiv.innerHTML = paymentForm
|
||||||
|
document.body.appendChild(tempDiv)
|
||||||
|
|
||||||
|
// 自动提交表单
|
||||||
|
const form = tempDiv.querySelector('form')
|
||||||
|
if (form) {
|
||||||
|
form.submit()
|
||||||
|
} else {
|
||||||
|
ElMessage.error('支付页面加载失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理临时元素
|
||||||
|
setTimeout(() => {
|
||||||
|
if (document.body.contains(tempDiv)) {
|
||||||
|
document.body.removeChild(tempDiv)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ElMessage.error(alipayResponse.data.message || '创建支付宝支付失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ElMessage.error(createResponse.data.message || '创建支付订单失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建支付订单失败:', error)
|
||||||
|
ElMessage.error('创建支付订单失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -507,7 +507,7 @@ const getAuthHeaders = () => {
|
|||||||
const refreshStats = async () => {
|
const refreshStats = async () => {
|
||||||
loadingStats.value = true
|
loadingStats.value = true
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/cleanup/cleanup-stats', {
|
const response = await fetch('http://localhost:8080/api/cleanup/cleanup-stats', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...getAuthHeaders()
|
...getAuthHeaders()
|
||||||
@@ -530,7 +530,7 @@ const refreshStats = async () => {
|
|||||||
const performFullCleanup = async () => {
|
const performFullCleanup = async () => {
|
||||||
loadingCleanup.value = true
|
loadingCleanup.value = true
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/cleanup/full-cleanup', {
|
const response = await fetch('http://localhost:8080/api/cleanup/full-cleanup', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
@@ -201,11 +201,11 @@ const goToMyWorks = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToImageToVideo = () => {
|
const goToImageToVideo = () => {
|
||||||
router.push('/image-to-video')
|
router.push('/image-to-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToStoryboardVideo = () => {
|
const goToStoryboardVideo = () => {
|
||||||
router.push('/storyboard-video')
|
router.push('/storyboard-video/create')
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToCreate = (work) => {
|
const goToCreate = (work) => {
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ public class SecurityConfig {
|
|||||||
"http://localhost:3000",
|
"http://localhost:3000",
|
||||||
"http://127.0.0.1:3000",
|
"http://127.0.0.1:3000",
|
||||||
"http://localhost:5173",
|
"http://localhost:5173",
|
||||||
"http://127.0.0.1:5173",
|
"http://127.0.0.1:5173"
|
||||||
"https://*.ngrok.io",
|
|
||||||
"https://*.ngrok-free.app"
|
|
||||||
));
|
));
|
||||||
configuration.setAllowedOriginPatterns(Arrays.asList(
|
configuration.setAllowedOriginPatterns(Arrays.asList(
|
||||||
"https://*.ngrok.io",
|
"https://*.ngrok.io",
|
||||||
@@ -103,6 +101,7 @@ public class SecurityConfig {
|
|||||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||||
configuration.setAllowCredentials(true);
|
configuration.setAllowCredentials(true);
|
||||||
configuration.setExposedHeaders(Arrays.asList("Authorization"));
|
configuration.setExposedHeaders(Arrays.asList("Authorization"));
|
||||||
|
configuration.setMaxAge(3600L); // 预检请求缓存时间
|
||||||
|
|
||||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
source.registerCorsConfiguration("/**", configuration);
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.util.Locale;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
package com.example.demo.controller;
|
package com.example.demo.controller;
|
||||||
|
|
||||||
import com.example.demo.service.ApiResponseHandler;
|
import java.util.HashMap;
|
||||||
import com.example.demo.util.JwtUtils;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import com.example.demo.service.ApiResponseHandler;
|
||||||
import java.util.Map;
|
import com.example.demo.util.JwtUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API测试控制器
|
* API测试控制器
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
package com.example.demo.controller;
|
package com.example.demo.controller;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import com.example.demo.model.User;
|
import com.example.demo.model.User;
|
||||||
import com.example.demo.service.UserService;
|
import com.example.demo.service.UserService;
|
||||||
import com.example.demo.service.VerificationCodeService;
|
import com.example.demo.service.VerificationCodeService;
|
||||||
import com.example.demo.util.JwtUtils;
|
import com.example.demo.util.JwtUtils;
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/auth")
|
@RequestMapping("/api/auth")
|
||||||
@@ -114,11 +121,23 @@ public class AuthApiController {
|
|||||||
try {
|
try {
|
||||||
// 从邮箱生成用户名(去掉@符号及后面的部分)
|
// 从邮箱生成用户名(去掉@符号及后面的部分)
|
||||||
String username = email.split("@")[0];
|
String username = email.split("@")[0];
|
||||||
|
logger.info("邮箱验证码登录 - 原始邮箱: '{}', 生成的用户名: '{}'", email, username);
|
||||||
|
|
||||||
|
// 确保用户名长度不超过50个字符
|
||||||
|
if (username.length() > 50) {
|
||||||
|
username = username.substring(0, 50);
|
||||||
|
logger.info("邮箱验证码登录 - 用户名过长,截断为: '{}'", username);
|
||||||
|
}
|
||||||
// 确保用户名唯一
|
// 确保用户名唯一
|
||||||
String originalUsername = username;
|
String originalUsername = username;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
while (userService.findByUsername(username) != null) {
|
while (userService.findByUsernameOrNull(username) != null) {
|
||||||
username = originalUsername + counter;
|
// 如果用户名过长,需要重新截断
|
||||||
|
String newUsername = originalUsername + counter;
|
||||||
|
if (newUsername.length() > 50) {
|
||||||
|
newUsername = newUsername.substring(0, 50);
|
||||||
|
}
|
||||||
|
username = newUsername;
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +164,7 @@ public class AuthApiController {
|
|||||||
|
|
||||||
// 生成JWT Token
|
// 生成JWT Token
|
||||||
String token = jwtUtils.generateToken(user.getUsername(), user.getRole(), user.getId());
|
String token = jwtUtils.generateToken(user.getUsername(), user.getRole(), user.getId());
|
||||||
|
logger.info("邮箱验证码登录 - 生成JWT token,用户名: '{}', 用户ID: {}", user.getUsername(), user.getId());
|
||||||
|
|
||||||
Map<String, Object> body = new HashMap<>();
|
Map<String, Object> body = new HashMap<>();
|
||||||
body.put("success", true);
|
body.put("success", true);
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public class PaymentApiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String orderId = (String) paymentData.get("orderId");
|
String orderId = (String) paymentData.get("orderId");
|
||||||
String amountStr = (String) paymentData.get("amount");
|
String amountStr = paymentData.get("amount") != null ? paymentData.get("amount").toString() : null;
|
||||||
String method = (String) paymentData.get("method");
|
String method = (String) paymentData.get("method");
|
||||||
|
|
||||||
if (orderId == null || amountStr == null || method == null) {
|
if (orderId == null || amountStr == null || method == null) {
|
||||||
@@ -306,7 +306,7 @@ public class PaymentApiController {
|
|||||||
.body(createErrorResponse("请先登录后再创建支付记录"));
|
.body(createErrorResponse("请先登录后再创建支付记录"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String amountStr = (String) paymentData.get("amount");
|
String amountStr = paymentData.get("amount") != null ? paymentData.get("amount").toString() : null;
|
||||||
String method = (String) paymentData.get("method");
|
String method = (String) paymentData.get("method");
|
||||||
|
|
||||||
if (amountStr == null || method == null) {
|
if (amountStr == null || method == null) {
|
||||||
@@ -373,31 +373,42 @@ public class PaymentApiController {
|
|||||||
@RequestBody Map<String, Object> paymentData,
|
@RequestBody Map<String, Object> paymentData,
|
||||||
Authentication authentication) {
|
Authentication authentication) {
|
||||||
try {
|
try {
|
||||||
|
logger.info("收到创建支付宝支付请求,数据:{}", paymentData);
|
||||||
|
|
||||||
String username;
|
String username;
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
username = authentication.getName();
|
username = authentication.getName();
|
||||||
|
logger.info("用户已认证:{}", username);
|
||||||
} else {
|
} else {
|
||||||
|
logger.warn("用户未认证,拒绝支付请求");
|
||||||
return ResponseEntity.badRequest()
|
return ResponseEntity.badRequest()
|
||||||
.body(createErrorResponse("请先登录后再创建支付"));
|
.body(createErrorResponse("请先登录后再创建支付"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Long paymentId = Long.valueOf(paymentData.get("paymentId").toString());
|
Long paymentId = Long.valueOf(paymentData.get("paymentId").toString());
|
||||||
|
logger.info("查找支付记录,ID:{}", paymentId);
|
||||||
|
|
||||||
Payment payment = paymentService.findById(paymentId)
|
Payment payment = paymentService.findById(paymentId)
|
||||||
.orElseThrow(() -> new RuntimeException("支付记录不存在"));
|
.orElseThrow(() -> new RuntimeException("支付记录不存在"));
|
||||||
|
|
||||||
// 检查权限
|
// 检查权限
|
||||||
if (!payment.getUser().getUsername().equals(username)) {
|
if (!payment.getUser().getUsername().equals(username)) {
|
||||||
|
logger.warn("用户{}无权限操作支付记录{}", username, paymentId);
|
||||||
return ResponseEntity.status(403)
|
return ResponseEntity.status(403)
|
||||||
.body(createErrorResponse("无权限操作此支付记录"));
|
.body(createErrorResponse("无权限操作此支付记录"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info("开始调用支付宝服务创建支付,订单号:{},金额:{}", payment.getOrderId(), payment.getAmount());
|
||||||
|
|
||||||
// 调用支付宝接口创建支付
|
// 调用支付宝接口创建支付
|
||||||
String paymentUrl = alipayService.createPayment(payment);
|
Map<String, Object> paymentResult = alipayService.createPayment(payment);
|
||||||
|
|
||||||
|
logger.info("支付宝二维码生成成功,订单号:{}", payment.getOrderId());
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
response.put("success", true);
|
response.put("success", true);
|
||||||
response.put("message", "支付宝支付创建成功");
|
response.put("message", "支付宝二维码生成成功");
|
||||||
response.put("data", Map.of("paymentUrl", paymentUrl));
|
response.put("data", paymentResult);
|
||||||
|
|
||||||
return ResponseEntity.ok(response);
|
return ResponseEntity.ok(response);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -449,47 +460,7 @@ public class PaymentApiController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝异步通知
|
|
||||||
*/
|
|
||||||
@PostMapping("/alipay/notify")
|
|
||||||
public ResponseEntity<String> handleAlipayNotify(@RequestParam Map<String, String> params) {
|
|
||||||
try {
|
|
||||||
logger.info("收到支付宝异步通知:{}", params);
|
|
||||||
|
|
||||||
boolean success = alipayService.handleNotify(params);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
return ResponseEntity.ok("success");
|
|
||||||
} else {
|
|
||||||
return ResponseEntity.ok("fail");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("处理支付宝异步通知失败:", e);
|
|
||||||
return ResponseEntity.ok("fail");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝同步返回
|
|
||||||
*/
|
|
||||||
@GetMapping("/alipay/return")
|
|
||||||
public ResponseEntity<String> handleAlipayReturn(@RequestParam Map<String, String> params) {
|
|
||||||
try {
|
|
||||||
logger.info("收到支付宝同步返回:{}", params);
|
|
||||||
|
|
||||||
boolean success = alipayService.handleReturn(params);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
return ResponseEntity.ok("支付成功,正在跳转...");
|
|
||||||
} else {
|
|
||||||
return ResponseEntity.ok("支付验证失败");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("处理支付宝同步返回失败:", e);
|
|
||||||
return ResponseEntity.ok("支付处理异常");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> createErrorResponse(String message) {
|
private Map<String, Object> createErrorResponse(String message) {
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
package com.example.demo.controller;
|
package com.example.demo.controller;
|
||||||
|
|
||||||
import com.example.demo.model.Payment;
|
import java.util.List;
|
||||||
import com.example.demo.model.PaymentMethod;
|
import java.util.Map;
|
||||||
import com.example.demo.model.PaymentStatus;
|
|
||||||
import com.example.demo.model.User;
|
|
||||||
import com.example.demo.service.AlipayService;
|
|
||||||
import com.example.demo.service.PayPalService;
|
|
||||||
import com.example.demo.service.PaymentService;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -16,10 +10,23 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
import java.util.List;
|
import com.example.demo.model.Payment;
|
||||||
import java.util.Map;
|
import com.example.demo.model.PaymentMethod;
|
||||||
|
import com.example.demo.model.User;
|
||||||
|
import com.example.demo.service.AlipayService;
|
||||||
|
import com.example.demo.service.PayPalService;
|
||||||
|
import com.example.demo.service.PaymentService;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/payment")
|
@RequestMapping("/payment")
|
||||||
@@ -73,8 +80,12 @@ public class PaymentController {
|
|||||||
// 根据支付方式创建支付
|
// 根据支付方式创建支付
|
||||||
String redirectUrl;
|
String redirectUrl;
|
||||||
if (payment.getPaymentMethod() == PaymentMethod.ALIPAY) {
|
if (payment.getPaymentMethod() == PaymentMethod.ALIPAY) {
|
||||||
redirectUrl = alipayService.createPayment(payment);
|
Map<String, Object> paymentResult = alipayService.createPayment(payment);
|
||||||
return "redirect:" + redirectUrl;
|
if (paymentResult.containsKey("qrCode")) {
|
||||||
|
// 对于二维码支付,重定向到支付页面显示二维码
|
||||||
|
return "redirect:/payment/qr?qrCode=" + paymentResult.get("qrCode");
|
||||||
|
}
|
||||||
|
return "redirect:/payment/error";
|
||||||
} else if (payment.getPaymentMethod() == PaymentMethod.PAYPAL) {
|
} else if (payment.getPaymentMethod() == PaymentMethod.PAYPAL) {
|
||||||
redirectUrl = payPalService.createPayment(payment);
|
redirectUrl = payPalService.createPayment(payment);
|
||||||
return "redirect:" + redirectUrl;
|
return "redirect:" + redirectUrl;
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
package com.example.demo.controller;
|
package com.example.demo.controller;
|
||||||
|
|
||||||
import com.example.demo.model.TaskQueue;
|
|
||||||
import com.example.demo.service.TaskQueueService;
|
|
||||||
import com.example.demo.util.JwtUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.example.demo.model.TaskQueue;
|
||||||
|
import com.example.demo.service.TaskQueueService;
|
||||||
|
import com.example.demo.util.JwtUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务队列API控制器
|
* 任务队列API控制器
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
package com.example.demo.controller;
|
package com.example.demo.controller;
|
||||||
|
|
||||||
import com.example.demo.model.TextToVideoTask;
|
|
||||||
import com.example.demo.service.TextToVideoService;
|
|
||||||
import com.example.demo.util.JwtUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.example.demo.model.TextToVideoTask;
|
||||||
|
import com.example.demo.service.TextToVideoService;
|
||||||
|
import com.example.demo.util.JwtUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文生视频API控制器
|
* 文生视频API控制器
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -195,3 +195,5 @@ public class PointsFreezeRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -263,3 +263,5 @@ public class TaskQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -255,3 +255,5 @@ public class TaskStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package com.example.demo.model;
|
package com.example.demo.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.GenerationType;
|
import jakarta.persistence.GenerationType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import jakarta.persistence.PrePersist;
|
import jakarta.persistence.PrePersist;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
import jakarta.validation.constraints.Email;
|
import jakarta.validation.constraints.Email;
|
||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "users")
|
@Table(name = "users")
|
||||||
@@ -31,9 +32,7 @@ public class User {
|
|||||||
@Column(nullable = false, unique = true, length = 100)
|
@Column(nullable = false, unique = true, length = 100)
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@NotBlank
|
@Column(nullable = true)
|
||||||
@Size(min = 6, max = 100)
|
|
||||||
@Column(nullable = false)
|
|
||||||
private String passwordHash;
|
private String passwordHash;
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
|
|||||||
@@ -63,3 +63,5 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.example.demo.repository;
|
package com.example.demo.repository;
|
||||||
|
|
||||||
import com.example.demo.model.UserWork;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
@@ -9,9 +12,7 @@ import org.springframework.data.jpa.repository.Query;
|
|||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import com.example.demo.model.UserWork;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户作品仓库接口
|
* 用户作品仓库接口
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.example.demo.scheduler;
|
package com.example.demo.scheduler;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -8,7 +10,6 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import com.example.demo.service.TaskCleanupService;
|
import com.example.demo.service.TaskCleanupService;
|
||||||
import com.example.demo.service.TaskQueueService;
|
import com.example.demo.service.TaskQueueService;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务队列定时调度器
|
* 任务队列定时调度器
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
package com.example.demo.security;
|
package com.example.demo.security;
|
||||||
|
|
||||||
import com.example.demo.model.User;
|
import java.io.IOException;
|
||||||
import com.example.demo.service.UserService;
|
import java.util.ArrayList;
|
||||||
import com.example.demo.util.JwtUtils;
|
import java.util.List;
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
import org.slf4j.Logger;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import org.slf4j.LoggerFactory;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.example.demo.model.User;
|
||||||
import java.util.ArrayList;
|
import com.example.demo.service.UserService;
|
||||||
import java.util.List;
|
import com.example.demo.util.JwtUtils;
|
||||||
import java.util.Map;
|
|
||||||
import org.slf4j.Logger;
|
import jakarta.servlet.FilterChain;
|
||||||
import org.slf4j.LoggerFactory;
|
import jakarta.servlet.ServletException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
@@ -41,7 +41,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||||
@NonNull FilterChain filterChain) throws ServletException, IOException {
|
@NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
|
||||||
logger.debug("JWT过滤器处理请求: {}", request.getRequestURI());
|
logger.info("JWT过滤器处理请求: {}", request.getRequestURI());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String authHeader = request.getHeader("Authorization");
|
String authHeader = request.getHeader("Authorization");
|
||||||
@@ -50,13 +50,14 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
String token = jwtUtils.extractTokenFromHeader(authHeader);
|
String token = jwtUtils.extractTokenFromHeader(authHeader);
|
||||||
logger.debug("提取的token: {}", token != null ? "存在" : "不存在");
|
logger.debug("提取的token: {}", token != null ? "存在" : "不存在");
|
||||||
|
|
||||||
if (token != null) {
|
if (token != null && !token.equals("null") && !token.trim().isEmpty()) {
|
||||||
String username = jwtUtils.getUsernameFromToken(token);
|
String username = jwtUtils.getUsernameFromToken(token);
|
||||||
logger.debug("从token获取用户名: {}", username);
|
logger.debug("从token获取用户名: {}", username);
|
||||||
|
|
||||||
if (username != null && jwtUtils.validateToken(token, username)) {
|
if (username != null && jwtUtils.validateToken(token, username)) {
|
||||||
|
logger.info("JWT认证 - 从token获取的用户名: '{}'", username);
|
||||||
User user = userService.findByUsername(username);
|
User user = userService.findByUsername(username);
|
||||||
logger.debug("查找用户: {}", user != null ? user.getUsername() : "未找到");
|
logger.info("JWT认证 - 数据库查找结果: {}", user != null ? "找到用户 '" + user.getUsername() + "'" : "未找到用户");
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
// 创建用户权限列表
|
// 创建用户权限列表
|
||||||
@@ -64,7 +65,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
authorities.add(new SimpleGrantedAuthority(user.getRole()));
|
authorities.add(new SimpleGrantedAuthority(user.getRole()));
|
||||||
|
|
||||||
UsernamePasswordAuthenticationToken authToken =
|
UsernamePasswordAuthenticationToken authToken =
|
||||||
new UsernamePasswordAuthenticationToken(user, null, authorities);
|
new UsernamePasswordAuthenticationToken(user.getUsername(), null, authorities);
|
||||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
logger.debug("JWT认证成功,用户: {}, 角色: {}", username, user.getRole());
|
logger.debug("JWT认证成功,用户: {}, 角色: {}", username, user.getRole());
|
||||||
|
|||||||
@@ -34,3 +34,5 @@ public class PlainTextPasswordEncoder implements PasswordEncoder {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
package com.example.demo.service;
|
package com.example.demo.service;
|
||||||
|
|
||||||
import com.alipay.api.AlipayApiException;
|
import java.time.LocalDateTime;
|
||||||
import com.alipay.api.AlipayClient;
|
import java.util.HashMap;
|
||||||
import com.alipay.api.DefaultAlipayClient;
|
import java.util.Map;
|
||||||
import com.alipay.api.domain.AlipayTradePagePayModel;
|
import java.util.UUID;
|
||||||
import com.alipay.api.internal.util.AlipaySignature;
|
|
||||||
import com.alipay.api.request.AlipayTradePagePayRequest;
|
|
||||||
import com.alipay.api.response.AlipayTradePagePayResponse;
|
|
||||||
import com.example.demo.model.Payment;
|
|
||||||
import com.example.demo.model.PaymentMethod;
|
|
||||||
import com.example.demo.model.PaymentStatus;
|
|
||||||
import com.example.demo.repository.PaymentRepository;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import com.alipay.api.AlipayClient;
|
||||||
import java.util.Map;
|
import com.alipay.api.DefaultAlipayClient;
|
||||||
import java.util.UUID;
|
import com.alipay.api.domain.AlipayTradePrecreateModel;
|
||||||
|
import com.alipay.api.internal.util.AlipaySignature;
|
||||||
|
import com.alipay.api.request.AlipayTradePrecreateRequest;
|
||||||
|
import com.alipay.api.response.AlipayTradePrecreateResponse;
|
||||||
|
import com.example.demo.model.Payment;
|
||||||
|
import com.example.demo.model.PaymentMethod;
|
||||||
|
import com.example.demo.model.PaymentStatus;
|
||||||
|
import com.example.demo.repository.PaymentRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AlipayService {
|
public class AlipayService {
|
||||||
@@ -56,60 +57,117 @@ public class AlipayService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建支付宝支付订单
|
* 创建支付宝支付订单并生成二维码
|
||||||
*/
|
*/
|
||||||
public String createPayment(Payment payment) {
|
public Map<String, Object> createPayment(Payment payment) {
|
||||||
try {
|
try {
|
||||||
|
logger.info("开始创建支付宝支付订单,订单号:{},金额:{}", payment.getOrderId(), payment.getAmount());
|
||||||
|
|
||||||
// 设置支付状态
|
// 设置支付状态
|
||||||
payment.setStatus(PaymentStatus.PENDING);
|
payment.setStatus(PaymentStatus.PENDING);
|
||||||
payment.setPaymentMethod(PaymentMethod.ALIPAY);
|
payment.setPaymentMethod(PaymentMethod.ALIPAY);
|
||||||
payment.setOrderId(generateOrderId());
|
if (payment.getOrderId() == null || payment.getOrderId().isEmpty()) {
|
||||||
|
payment.setOrderId(generateOrderId());
|
||||||
|
}
|
||||||
payment.setCallbackUrl(notifyUrl);
|
payment.setCallbackUrl(notifyUrl);
|
||||||
payment.setReturnUrl(returnUrl);
|
payment.setReturnUrl(returnUrl);
|
||||||
|
|
||||||
// 保存支付记录
|
// 保存支付记录
|
||||||
paymentRepository.save(payment);
|
paymentRepository.save(payment);
|
||||||
|
logger.info("支付记录已保存,ID:{}", payment.getId());
|
||||||
|
|
||||||
// 创建支付宝客户端
|
// 调用真实的支付宝API
|
||||||
AlipayClient alipayClient = new DefaultAlipayClient(
|
return callRealAlipayAPI(payment);
|
||||||
gatewayUrl, appId, privateKey, "json", charset, publicKey, signType);
|
|
||||||
|
|
||||||
// 设置请求参数
|
} catch (Exception e) {
|
||||||
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
|
logger.error("创建支付订单时发生异常,订单号:{},错误:{}", payment.getOrderId(), e.getMessage(), e);
|
||||||
request.setReturnUrl(returnUrl);
|
|
||||||
request.setNotifyUrl(notifyUrl);
|
|
||||||
|
|
||||||
// 设置业务参数
|
|
||||||
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
|
|
||||||
model.setOutTradeNo(payment.getOrderId());
|
|
||||||
model.setTotalAmount(payment.getAmount().toString());
|
|
||||||
model.setSubject(payment.getDescription() != null ? payment.getDescription() : "商品支付");
|
|
||||||
model.setBody(payment.getDescription());
|
|
||||||
model.setProductCode("FAST_INSTANT_TRADE_PAY");
|
|
||||||
|
|
||||||
request.setBizModel(model);
|
|
||||||
|
|
||||||
// 调用API
|
|
||||||
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
|
|
||||||
|
|
||||||
if (response.isSuccess()) {
|
|
||||||
logger.info("支付宝支付订单创建成功,订单号:{}", payment.getOrderId());
|
|
||||||
return response.getBody();
|
|
||||||
} else {
|
|
||||||
logger.error("支付宝支付订单创建失败:{}", response.getMsg());
|
|
||||||
payment.setStatus(PaymentStatus.FAILED);
|
|
||||||
paymentRepository.save(payment);
|
|
||||||
throw new RuntimeException("支付订单创建失败:" + response.getMsg());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
logger.error("支付宝API调用异常:", e);
|
|
||||||
payment.setStatus(PaymentStatus.FAILED);
|
payment.setStatus(PaymentStatus.FAILED);
|
||||||
paymentRepository.save(payment);
|
paymentRepository.save(payment);
|
||||||
throw new RuntimeException("支付服务异常:" + e.getMessage());
|
throw new RuntimeException("支付服务异常:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用真实的支付宝API
|
||||||
|
*/
|
||||||
|
private Map<String, Object> callRealAlipayAPI(Payment payment) throws Exception {
|
||||||
|
// 创建支付宝客户端,增加超时时间
|
||||||
|
AlipayClient alipayClient = new DefaultAlipayClient(
|
||||||
|
gatewayUrl, appId, privateKey, "json", charset, publicKey, signType);
|
||||||
|
|
||||||
|
// 设置连接和读取超时时间(30秒)
|
||||||
|
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");
|
||||||
|
System.setProperty("sun.net.client.defaultReadTimeout", "30000");
|
||||||
|
|
||||||
|
// 使用预创建API生成二维码
|
||||||
|
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
|
||||||
|
request.setNotifyUrl(notifyUrl);
|
||||||
|
|
||||||
|
// 设置业务参数
|
||||||
|
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
|
||||||
|
model.setOutTradeNo(payment.getOrderId());
|
||||||
|
model.setTotalAmount(payment.getAmount().toString());
|
||||||
|
model.setSubject(payment.getDescription() != null ? payment.getDescription() : "AIGC会员订阅");
|
||||||
|
model.setBody(payment.getDescription() != null ? payment.getDescription() : "AIGC平台会员订阅服务");
|
||||||
|
|
||||||
|
request.setBizModel(model);
|
||||||
|
|
||||||
|
logger.info("调用支付宝预创建API,订单号:{},金额:{},商品名称:{}",
|
||||||
|
model.getOutTradeNo(), model.getTotalAmount(), model.getSubject());
|
||||||
|
|
||||||
|
// 调用API,增加重试机制
|
||||||
|
AlipayTradePrecreateResponse response = null;
|
||||||
|
int maxRetries = 3;
|
||||||
|
int retryCount = 0;
|
||||||
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
try {
|
||||||
|
logger.info("正在调用支付宝API... (第{}次尝试)", retryCount + 1);
|
||||||
|
response = alipayClient.execute(request);
|
||||||
|
break; // 成功则跳出循环
|
||||||
|
} catch (Exception e) {
|
||||||
|
retryCount++;
|
||||||
|
logger.warn("支付宝API调用失败,第{}次重试,错误:{}", retryCount, e.getMessage());
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
logger.error("支付宝API调用失败,已达到最大重试次数");
|
||||||
|
throw new RuntimeException("支付宝API调用失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000); // 等待2秒后重试
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new RuntimeException("重试被中断", ie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("支付宝API响应状态:{}", response.isSuccess());
|
||||||
|
logger.info("支付宝API响应码:{}", response.getCode());
|
||||||
|
logger.info("支付宝API响应消息:{}", response.getMsg());
|
||||||
|
logger.info("支付宝API响应子码:{}", response.getSubCode());
|
||||||
|
logger.info("支付宝API响应子消息:{}", response.getSubMsg());
|
||||||
|
|
||||||
|
if (response.isSuccess()) {
|
||||||
|
String qrCode = response.getQrCode();
|
||||||
|
logger.info("支付宝二维码生成成功,订单号:{},二维码:{}", payment.getOrderId(), qrCode);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("qrCode", qrCode);
|
||||||
|
result.put("outTradeNo", response.getOutTradeNo());
|
||||||
|
result.put("success", true);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
logger.error("支付宝二维码生成失败,订单号:{},错误信息:{},子码:{},子消息:{}",
|
||||||
|
payment.getOrderId(), response.getMsg(), response.getSubCode(), response.getSubMsg());
|
||||||
|
payment.setStatus(PaymentStatus.FAILED);
|
||||||
|
paymentRepository.save(payment);
|
||||||
|
throw new RuntimeException("二维码生成失败:" + response.getMsg() + " - " + response.getSubMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理支付宝异步通知
|
* 处理支付宝异步通知
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
package com.example.demo.service;
|
package com.example.demo.service;
|
||||||
|
|
||||||
import com.example.demo.model.ImageToVideoTask;
|
import java.io.IOException;
|
||||||
import com.example.demo.repository.ImageToVideoTaskRepository;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -14,15 +22,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.example.demo.model.ImageToVideoTask;
|
||||||
import java.nio.file.Files;
|
import com.example.demo.repository.ImageToVideoTaskRepository;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图生视频服务类
|
* 图生视频服务类
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
package com.example.demo.service;
|
package com.example.demo.service;
|
||||||
|
|
||||||
import com.example.demo.model.*;
|
|
||||||
import com.example.demo.repository.PaymentRepository;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -15,6 +7,22 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.example.demo.model.Order;
|
||||||
|
import com.example.demo.model.OrderItem;
|
||||||
|
import com.example.demo.model.OrderStatus;
|
||||||
|
import com.example.demo.model.OrderType;
|
||||||
|
import com.example.demo.model.Payment;
|
||||||
|
import com.example.demo.model.PaymentMethod;
|
||||||
|
import com.example.demo.model.PaymentStatus;
|
||||||
|
import com.example.demo.model.User;
|
||||||
|
import com.example.demo.repository.PaymentRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional
|
||||||
public class PaymentService {
|
public class PaymentService {
|
||||||
@@ -291,11 +299,15 @@ public class PaymentService {
|
|||||||
*/
|
*/
|
||||||
public Payment createPayment(String username, String orderId, String amountStr, String method) {
|
public Payment createPayment(String username, String orderId, String amountStr, String method) {
|
||||||
try {
|
try {
|
||||||
logger.info("创建支付 - 用户名: {}, 订单ID: {}, 金额: {}, 支付方式: {}", username, orderId, amountStr, method);
|
logger.info("创建支付 - 用户名: '{}', 订单ID: {}, 金额: {}, 支付方式: {}", username, orderId, amountStr, method);
|
||||||
|
|
||||||
User user = userService.findByUsername(username);
|
User user;
|
||||||
if (user == null) {
|
try {
|
||||||
logger.error("用户不存在: {}", username);
|
logger.info("PaymentService - 尝试查找用户: '{}'", username);
|
||||||
|
user = userService.findByUsername(username);
|
||||||
|
logger.info("PaymentService - 用户查找结果: {}", user != null ? "找到用户 '" + user.getUsername() + "'" : "未找到用户");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("PaymentService - 用户查找异常: {}", username, e);
|
||||||
// 如果是匿名用户,创建一个临时用户记录
|
// 如果是匿名用户,创建一个临时用户记录
|
||||||
if (username.startsWith("anonymous_")) {
|
if (username.startsWith("anonymous_")) {
|
||||||
user = createAnonymousUser(username);
|
user = createAnonymousUser(username);
|
||||||
@@ -315,6 +327,7 @@ public class PaymentService {
|
|||||||
payment.setUser(user);
|
payment.setUser(user);
|
||||||
payment.setOrderId(orderId);
|
payment.setOrderId(orderId);
|
||||||
payment.setAmount(amount);
|
payment.setAmount(amount);
|
||||||
|
payment.setCurrency("CNY"); // 设置默认货币为人民币
|
||||||
payment.setPaymentMethod(paymentMethod);
|
payment.setPaymentMethod(paymentMethod);
|
||||||
payment.setStatus(PaymentStatus.PENDING);
|
payment.setStatus(PaymentStatus.PENDING);
|
||||||
payment.setCreatedAt(LocalDateTime.now());
|
payment.setCreatedAt(LocalDateTime.now());
|
||||||
@@ -325,10 +338,12 @@ public class PaymentService {
|
|||||||
// 根据支付方式调用相应的支付服务
|
// 根据支付方式调用相应的支付服务
|
||||||
if (paymentMethod == PaymentMethod.ALIPAY) {
|
if (paymentMethod == PaymentMethod.ALIPAY) {
|
||||||
try {
|
try {
|
||||||
String paymentUrl = alipayService.createPayment(savedPayment);
|
Map<String, Object> paymentResult = alipayService.createPayment(savedPayment);
|
||||||
savedPayment.setPaymentUrl(paymentUrl);
|
if (paymentResult.containsKey("qrCode")) {
|
||||||
|
savedPayment.setPaymentUrl(paymentResult.get("qrCode").toString());
|
||||||
|
}
|
||||||
save(savedPayment);
|
save(savedPayment);
|
||||||
logger.info("支付宝支付URL生成成功: {}", paymentUrl);
|
logger.info("支付宝二维码生成成功: {}", paymentResult.get("qrCode"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("调用支付宝支付接口失败:", e);
|
logger.error("调用支付宝支付接口失败:", e);
|
||||||
// 不抛出异常,让前端处理
|
// 不抛出异常,让前端处理
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ public class TaskStatusPollingService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用外部API查询状态
|
// 调用外部API查询状态
|
||||||
HttpResponse<String> response = Unirest.post(apiBaseUrl + "/v1/videos")
|
HttpResponse<String> response = Unirest.get(apiBaseUrl + "/v1/videos")
|
||||||
.header("Authorization", "Bearer " + apiKey)
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
.field("task_id", task.getExternalTaskId())
|
.queryString("task_id", task.getExternalTaskId())
|
||||||
.asString();
|
.asString();
|
||||||
|
|
||||||
if (response.getStatus() == 200) {
|
if (response.getStatus() == 200) {
|
||||||
@@ -113,19 +113,41 @@ public class TaskStatusPollingService {
|
|||||||
*/
|
*/
|
||||||
private void updateTaskStatus(TaskStatus task, JsonNode responseJson) {
|
private void updateTaskStatus(TaskStatus task, JsonNode responseJson) {
|
||||||
try {
|
try {
|
||||||
|
// 检查base_resp状态
|
||||||
|
JsonNode baseResp = responseJson.path("base_resp");
|
||||||
|
if (!baseResp.isMissingNode() && baseResp.path("status_code").asInt() != 0) {
|
||||||
|
String errorMsg = baseResp.path("status_msg").asText("Unknown error");
|
||||||
|
task.markAsFailed(errorMsg);
|
||||||
|
logger.warn("API返回错误: taskId={}, error={}", task.getTaskId(), errorMsg);
|
||||||
|
taskStatusRepository.save(task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String status = responseJson.path("status").asText();
|
String status = responseJson.path("status").asText();
|
||||||
int progress = responseJson.path("progress").asInt(0);
|
int progress = responseJson.path("progress").asInt(0);
|
||||||
String resultUrl = responseJson.path("result_url").asText();
|
String resultUrl = null;
|
||||||
String errorMessage = responseJson.path("error_message").asText();
|
String errorMessage = responseJson.path("error_message").asText();
|
||||||
|
|
||||||
task.incrementPollCount();
|
task.incrementPollCount();
|
||||||
task.setProgress(progress);
|
task.setProgress(progress);
|
||||||
|
|
||||||
switch (status.toLowerCase()) {
|
switch (status.toLowerCase()) {
|
||||||
case "completed":
|
|
||||||
case "success":
|
case "success":
|
||||||
task.markAsCompleted(resultUrl);
|
// 获取file_id并获取视频URL
|
||||||
logger.info("任务完成: taskId={}, resultUrl={}", task.getTaskId(), resultUrl);
|
String fileId = responseJson.path("file_id").asText();
|
||||||
|
if (!fileId.isEmpty()) {
|
||||||
|
resultUrl = getVideoUrlFromFileId(fileId);
|
||||||
|
if (resultUrl != null) {
|
||||||
|
task.markAsCompleted(resultUrl);
|
||||||
|
logger.info("任务完成: taskId={}, resultUrl={}", task.getTaskId(), resultUrl);
|
||||||
|
} else {
|
||||||
|
task.markAsFailed("无法获取视频URL");
|
||||||
|
logger.warn("任务完成但无法获取视频URL: taskId={}, fileId={}", task.getTaskId(), fileId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
task.markAsFailed("任务完成但未返回文件ID");
|
||||||
|
logger.warn("任务完成但未返回文件ID: taskId={}", task.getTaskId());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "failed":
|
case "failed":
|
||||||
@@ -136,6 +158,7 @@ public class TaskStatusPollingService {
|
|||||||
|
|
||||||
case "processing":
|
case "processing":
|
||||||
case "in_progress":
|
case "in_progress":
|
||||||
|
case "pending":
|
||||||
task.setStatus(TaskStatus.Status.PROCESSING);
|
task.setStatus(TaskStatus.Status.PROCESSING);
|
||||||
logger.info("任务处理中: taskId={}, progress={}%", task.getTaskId(), progress);
|
logger.info("任务处理中: taskId={}, progress={}%", task.getTaskId(), progress);
|
||||||
break;
|
break;
|
||||||
@@ -152,6 +175,38 @@ public class TaskStatusPollingService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据file_id获取视频URL
|
||||||
|
*/
|
||||||
|
private String getVideoUrlFromFileId(String fileId) {
|
||||||
|
try {
|
||||||
|
HttpResponse<String> response = Unirest.get(apiBaseUrl + "/minimax/v1/files/retrieve")
|
||||||
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
|
.queryString("file_id", fileId)
|
||||||
|
.asString();
|
||||||
|
|
||||||
|
if (response.getStatus() == 200) {
|
||||||
|
JsonNode responseJson = objectMapper.readTree(response.getBody());
|
||||||
|
JsonNode fileNode = responseJson.path("file");
|
||||||
|
if (!fileNode.isMissingNode()) {
|
||||||
|
String downloadUrl = fileNode.path("download_url").asText();
|
||||||
|
if (!downloadUrl.isEmpty()) {
|
||||||
|
logger.info("成功获取视频URL: fileId={}, url={}", fileId, downloadUrl);
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn("获取视频URL失败: fileId={}, status={}, response={}",
|
||||||
|
fileId, response.getStatus(), response.getBody());
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取视频URL时发生错误: fileId={}, error={}", fileId, e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理超时任务
|
* 处理超时任务
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.example.demo.service;
|
package com.example.demo.service;
|
||||||
|
|
||||||
import com.example.demo.model.TextToVideoTask;
|
import java.time.LocalDateTime;
|
||||||
import com.example.demo.repository.TextToVideoTaskRepository;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -13,11 +17,8 @@ import org.springframework.scheduling.annotation.Async;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import com.example.demo.model.TextToVideoTask;
|
||||||
import java.util.List;
|
import com.example.demo.repository.TextToVideoTaskRepository;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文生视频服务类
|
* 文生视频服务类
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ public class UserService {
|
|||||||
return userRepository.findByUsername(username).orElseThrow(() -> new IllegalArgumentException("用户不存在"));
|
return userRepository.findByUsername(username).orElseThrow(() -> new IllegalArgumentException("用户不存在"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public User findByUsernameOrNull(String username) {
|
||||||
|
return userRepository.findByUsername(username).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public User create(String username, String email, String rawPassword) {
|
public User create(String username, String email, String rawPassword) {
|
||||||
return register(username, email, rawPassword);
|
return register(username, email, rawPassword);
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package com.example.demo.service;
|
package com.example.demo.service;
|
||||||
|
|
||||||
import com.example.demo.model.User;
|
import java.time.LocalDateTime;
|
||||||
import com.example.demo.model.UserWork;
|
import java.util.HashMap;
|
||||||
import com.example.demo.model.TextToVideoTask;
|
import java.util.Map;
|
||||||
import com.example.demo.model.ImageToVideoTask;
|
import java.util.Optional;
|
||||||
import com.example.demo.repository.UserWorkRepository;
|
|
||||||
import com.example.demo.repository.TextToVideoTaskRepository;
|
|
||||||
import com.example.demo.repository.ImageToVideoTaskRepository;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -16,10 +14,13 @@ import org.springframework.data.domain.Pageable;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import com.example.demo.model.ImageToVideoTask;
|
||||||
import java.util.HashMap;
|
import com.example.demo.model.TextToVideoTask;
|
||||||
import java.util.Map;
|
import com.example.demo.model.User;
|
||||||
import java.util.Optional;
|
import com.example.demo.model.UserWork;
|
||||||
|
import com.example.demo.repository.ImageToVideoTaskRepository;
|
||||||
|
import com.example.demo.repository.TextToVideoTaskRepository;
|
||||||
|
import com.example.demo.repository.UserWorkRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户作品服务类
|
* 用户作品服务类
|
||||||
@@ -314,13 +315,33 @@ public class UserWorkService {
|
|||||||
*/
|
*/
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Map<String, Object> getUserWorkStats(String username) {
|
public Map<String, Object> getUserWorkStats(String username) {
|
||||||
Object[] stats = userWorkRepository.getUserWorkStats(username);
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("completedCount", stats[0] != null ? stats[0] : 0);
|
|
||||||
result.put("processingCount", stats[1] != null ? stats[1] : 0);
|
try {
|
||||||
result.put("failedCount", stats[2] != null ? stats[2] : 0);
|
Object[] stats = userWorkRepository.getUserWorkStats(username);
|
||||||
result.put("totalPointsCost", stats[3] != null ? stats[3] : 0);
|
|
||||||
|
// 安全地处理查询结果
|
||||||
|
if (stats != null && stats.length >= 4) {
|
||||||
|
result.put("completedCount", stats[0] != null ? stats[0] : 0);
|
||||||
|
result.put("processingCount", stats[1] != null ? stats[1] : 0);
|
||||||
|
result.put("failedCount", stats[2] != null ? stats[2] : 0);
|
||||||
|
result.put("totalPointsCost", stats[3] != null ? stats[3] : 0);
|
||||||
|
} else {
|
||||||
|
// 如果查询结果格式不正确,使用默认值
|
||||||
|
result.put("completedCount", 0);
|
||||||
|
result.put("processingCount", 0);
|
||||||
|
result.put("failedCount", 0);
|
||||||
|
result.put("totalPointsCost", 0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果查询失败,使用默认值
|
||||||
|
logger.warn("获取用户作品统计失败,使用默认值: " + e.getMessage());
|
||||||
|
result.put("completedCount", 0);
|
||||||
|
result.put("processingCount", 0);
|
||||||
|
result.put("failedCount", 0);
|
||||||
|
result.put("totalPointsCost", 0);
|
||||||
|
}
|
||||||
|
|
||||||
result.put("totalCount", userWorkRepository.countByUsername(username));
|
result.put("totalCount", userWorkRepository.countByUsername(username));
|
||||||
result.put("publicCount", userWorkRepository.countPublicWorksByUsername(username));
|
result.put("publicCount", userWorkRepository.countPublicWorksByUsername(username));
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
package com.example.demo.util;
|
package com.example.demo.util;
|
||||||
|
|
||||||
import io.jsonwebtoken.*;
|
|
||||||
import io.jsonwebtoken.security.Keys;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtUtils {
|
public class JwtUtils {
|
||||||
|
|
||||||
@@ -120,17 +124,7 @@ public class JwtUtils {
|
|||||||
* 获取签名密钥
|
* 获取签名密钥
|
||||||
*/
|
*/
|
||||||
private SecretKey getSigningKey() {
|
private SecretKey getSigningKey() {
|
||||||
// 确保密钥长度至少为256位(32字节)
|
|
||||||
byte[] keyBytes = secret.getBytes();
|
byte[] keyBytes = secret.getBytes();
|
||||||
if (keyBytes.length < 32) {
|
|
||||||
// 如果密钥太短,使用SHA-256哈希扩展
|
|
||||||
try {
|
|
||||||
java.security.MessageDigest sha = java.security.MessageDigest.getInstance("SHA-256");
|
|
||||||
keyBytes = sha.digest(keyBytes);
|
|
||||||
} catch (java.security.NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException("SHA-256 algorithm not available", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Keys.hmacShaKeyFor(keyBytes);
|
return Keys.hmacShaKeyFor(keyBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,59 +1,40 @@
|
|||||||
# Server Configuration
|
# 数据库配置
|
||||||
server.port=8080
|
spring.datasource.url=jdbc:mysql://localhost:3306/aigc_platform?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
spring.datasource.username=root
|
||||||
# MySQL DataSource (DEV) - 使用环境变量
|
spring.datasource.password=177615
|
||||||
spring.datasource.url=${DB_URL:jdbc:mysql://localhost:3306/aigc?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true}
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
|
|
||||||
spring.datasource.username=${DB_USERNAME:root}
|
|
||||||
spring.datasource.password=${DB_PASSWORD:177615}
|
|
||||||
|
|
||||||
# 数据库连接池配置
|
|
||||||
spring.datasource.hikari.maximum-pool-size=20
|
|
||||||
spring.datasource.hikari.minimum-idle=5
|
|
||||||
spring.datasource.hikari.idle-timeout=300000
|
|
||||||
spring.datasource.hikari.max-lifetime=1200000
|
|
||||||
spring.datasource.hikari.connection-timeout=20000
|
|
||||||
spring.datasource.hikari.leak-detection-threshold=60000
|
|
||||||
|
|
||||||
|
# JPA配置
|
||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.jpa.show-sql=true
|
spring.jpa.show-sql=true
|
||||||
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||||||
spring.jpa.properties.hibernate.format_sql=true
|
spring.jpa.properties.hibernate.format_sql=true
|
||||||
|
|
||||||
# 初始化脚本仅在开发环境开启(与JPA DDL冲突,暂时禁用)
|
# 服务器配置
|
||||||
# spring.sql.init.mode=always
|
server.port=8080
|
||||||
# spring.sql.init.platform=mysql
|
|
||||||
|
# 日志配置
|
||||||
|
logging.level.com.example.demo=DEBUG
|
||||||
|
logging.level.org.springframework.security=DEBUG
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
jwt.secret=mySecretKey123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
jwt.expiration=86400000
|
||||||
|
|
||||||
|
# 腾讯云SES配置
|
||||||
|
tencent.ses.secret-id=AKIDz8krbsJ5yKBZQpn74WFkmLPx3gnPhESA
|
||||||
|
tencent.ses.secret-key=Gu5t9xGARNpqDXcd3I4WZkFpAALPVZ7pKbNScfFsj
|
||||||
|
tencent.ses.region=ap-beijing
|
||||||
|
tencent.ses.from-email=noreply@vionow.com
|
||||||
|
tencent.ses.from-name=AIGC平台
|
||||||
|
|
||||||
# 支付宝配置 (开发环境 - 沙箱测试)
|
# 支付宝配置 (开发环境 - 沙箱测试)
|
||||||
# 请替换为您的实际配置
|
# 请替换为您的实际配置
|
||||||
alipay.app-id=您的APPID
|
alipay.app-id=9021000157616562
|
||||||
alipay.private-key=您的应用私钥
|
alipay.private-key=MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCH7wPeptkJlJuoKwDqxvfJJLTOAWVkHa/TLh+wiy1tEtmwcrOwEU3GuqfkUlhij71WJIZi8KBytCwbax1QGZA/oLXvTCGJJrYrsEL624X5gGCCPKWwHRDhewsQ5W8jFxaaMXxth8GKlSW61PZD2cOQClRVEm2xnWFZ+6/7WBI7082g7ayzGCD2eowXsJyWyuEBCUSbHXkSgxVhqj5wUGIXhr8ly+pdUlJmDX5K8UG2rjJYx+0AU5UZJbOAND7d3iyDsOulHDvth50t8MOWDnDCVJ2aAgUB5FZKtOFxOmzNTsMjvzYldFztF0khbypeeMVL2cxgioIgTvjBkUwd55hZAgMBAAECggEAUjk3pARUoEDt7skkYt87nsW/QCUECY0Tf7AUpxtovON8Hgkju8qbuyvIxokwwV2k72hkiZB33Soyy9r8/iiYYoR5yGfKmUV7R+30df03ivYmamD48BCE138v8GZ31Ufv+hEY7MADSCpzihGrbNtaOdSlslfVVmyWKHHfvy9EyD6yHJGYswLpHXC/QX1TuLRRxk6Uup8qENOG/6zjGWMfxoRZFwTt80ml1mKy32YZGyJqDaQpJcdYwAHOPcnJl1emw4E+oVjiLyksl643npuTkgnZXs1iWcWSS8ojF1w/0kVDzcNh9toLg+HDuQlIHOis01VQ7lYcG4oiMOnhX1QHIQKBgQC9fgBuILjBhuCI9fHvLRdzoNC9heD54YK7xGvEV/mv90k8xcmNx+Yg5C57ASaMRtOq3b7muPiCv5wOtMT4tUCcMIwSrTNlcBM6EoTagnaGfpzOMaHGMXO4vbaw+MIynHnvXFj1rZjG1lzkV/9K36LAaHD9ZKVJaBQ9mK+0CIq/3QKBgQC3pL5GbvXj6/4ahTraXzNDQQpPGVgbHxcOioEXL4ibaOPC58puTW8HDbRvVuhl/4EEOBRVX81BSgkN8XHwTSiZdih2iOqByg+o9kixs7nlFn3Iw9BBP2/g+Wqiyi2N+9g17kfWXXVOKYz/eMXLBeOo4KhQE9wqNGyZldYzX2ywrQKBgApJmvBfqmgnUG1fHOFlS06lvm9ro0ktqxFSmp8wP4gEHt/DxSuDXMUQXk2jRFp9ReSS4VhZVnSSvoA15DO0c2uHXzNsX8v0B7cxZjEOwCyRFyZCn4vJB4VSF2cIOlLRF/Wcx9+eqxqwbJ6hAGUqOwXDJc879ZVEp0So03EsvYupAoGAAnI+Wp/VxLB7FQ1bSFdmTmoKYh1bUBks7HOp3o4yiqduCUWfK7L6XKSxF56Xv+wUYuMAWlbJXCpJTpc9xk6w0MKDLXkLbqkrZjvJohxbyJJxIICDQKtAqUWJRxvcWXzWV3mSGWfrTRw+lZSdReQRMUm01EQ/dYx3OeCGFu8Zeo0CgYAlH5YSYdJxZSoDCJeoTrkxUlFoOg8UQ7SrsaLYLwpwcwpuiWJaTrg6jwFocj+XhjQ9RtRbSBHz2wKSLdl+pXbTbqECKk85zMFl6zG3etXtTJU/dD750Ty4i8zt3+JGhvglPrQBY1CfItgml2oXa/VUVMnLCUS0WSZuPRmPYZD8dg==
|
||||||
alipay.public-key=支付宝公钥
|
alipay.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAksEwzuR3ASrKtTzaANqdQYKOoD44itA1TWG/6onvQr8PHNEMgcguLuJNrdeuT2PDg23byzZ9qKfEM2D5U4zbpt0/uCYLfZQyAnAWWyMvnKPoSIgrtBjnxYK6HE6fuQV3geJTcZxvP/z8dGZB0V0s6a53rzbKSLh0p4w0hWfVXlQihq3Xh4vSKB+ojdhEkIblhpWPT42NPbjVNdwPzIhUGpRy3/nsgNqVBu+ZacQ5/rCvzXU1RE0allBbjcvjymKQTS7bAE0i1Mgo1eX8njvElsfQUv5P7xQdrvZagqtIuTdP19cmsSNGdIC9Z5Po3j0z3KWPR7MrKgDuJfzkWtJR4wIDAQAB
|
||||||
alipay.gateway-url=https://openapi.alipaydev.com/gateway.do
|
alipay.gateway-url=https://openapi.alipaydev.com/gateway.do
|
||||||
alipay.charset=UTF-8
|
alipay.charset=UTF-8
|
||||||
alipay.sign-type=RSA2
|
alipay.sign-type=RSA2
|
||||||
alipay.notify-url=http://您的域名:8080/api/payments/alipay/notify
|
alipay.notify-url=https://curtly-aphorismatic-ginger.ngrok-free.dev/api/payments/alipay/notify
|
||||||
alipay.return-url=http://您的域名:8080/api/payments/alipay/return
|
alipay.return-url=https://curtly-aphorismatic-ginger.ngrok-free.dev/api/payments/alipay/return
|
||||||
|
|
||||||
# PayPal配置 (开发环境 - 沙箱模式)
|
|
||||||
paypal.client-id=your_paypal_sandbox_client_id
|
|
||||||
paypal.client-secret=your_paypal_sandbox_client_secret
|
|
||||||
paypal.mode=sandbox
|
|
||||||
paypal.return-url=http://localhost:8080/api/payments/paypal/return
|
|
||||||
paypal.cancel-url=http://localhost:8080/api/payments/paypal/cancel
|
|
||||||
|
|
||||||
# JWT配置 - 使用环境变量
|
|
||||||
jwt.secret=${JWT_SECRET:aigc-demo-secret-key-for-jwt-token-generation-very-long-secret-key}
|
|
||||||
jwt.expiration=${JWT_EXPIRATION:604800000}
|
|
||||||
|
|
||||||
# AI API配置
|
|
||||||
ai.api.base-url=http://116.62.4.26:8081
|
|
||||||
ai.api.key=ak_5f13ec469e6047d5b8155c3cc91350e2
|
|
||||||
|
|
||||||
# 任务清理配置
|
|
||||||
task.cleanup.retention-days=30
|
|
||||||
task.cleanup.archive-retention-days=365
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -22,3 +22,5 @@ CREATE TABLE IF NOT EXISTS task_queue (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务队列表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务队列表';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,3 +21,5 @@ CREATE TABLE IF NOT EXISTS points_freeze_records (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='积分冻结记录表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='积分冻结记录表';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,3 +24,5 @@ CREATE TABLE task_status (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务状态表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务状态表';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -570,3 +570,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -486,3 +486,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -525,3 +525,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package com.example.demo.test;
|
package com.example.demo.test;
|
||||||
|
|
||||||
import com.example.demo.model.TaskQueue;
|
import java.util.List;
|
||||||
import com.example.demo.service.TaskQueueService;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
import java.util.List;
|
import com.example.demo.model.TaskQueue;
|
||||||
|
import com.example.demo.service.TaskQueueService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务队列功能测试
|
* 任务队列功能测试
|
||||||
|
|||||||
@@ -25,3 +25,5 @@ echo 按任意键退出...
|
|||||||
pause > nul
|
pause > nul
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -52,3 +52,5 @@ public class TestApiConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
demo/test_api.py
Normal file
15
demo/test_api.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
# 测试作品列表API
|
||||||
|
url = "http://localhost:8080/api/works/my-works"
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwidXNlcklkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE3NjE2MzYyNDQsImV4cCI6MTc2MTcyMjY0NH0.qZxHDkgSoSRvmMHBFfRdzZYtC55eCKba3VN07lTsFzKXn1hYbupv7boBJDKNOUrRYaH5ougHLFTI5xm059434g"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers, params={"page": 0, "size": 10})
|
||||||
|
print(f"Status Code: {response.status_code}")
|
||||||
|
print(f"Response: {response.text}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
23
demo/test_jwt.py
Normal file
23
demo/test_jwt.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
# 测试JWT解析
|
||||||
|
token = "eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwidXNlcklkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE3NjE2MzYyNDQsImV4cCI6MTc2MTcyMjY0NH0.qZxHDkgSoSRvmMHBFfRdzZYtC55eCKba3VN07lTsFzKXn1hYbupv7boBJDKNOUrRYaH5ougHLFTI5xm059434g"
|
||||||
|
|
||||||
|
# 解码JWT payload
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
|
||||||
|
# JWT由三部分组成:header.payload.signature
|
||||||
|
parts = token.split('.')
|
||||||
|
if len(parts) == 3:
|
||||||
|
# 解码payload (第二部分)
|
||||||
|
payload = parts[1]
|
||||||
|
# 添加padding
|
||||||
|
payload += '=' * (4 - len(payload) % 4)
|
||||||
|
decoded = base64.urlsafe_b64decode(payload)
|
||||||
|
payload_data = json.loads(decoded.decode('utf-8'))
|
||||||
|
print("JWT Payload:", payload_data)
|
||||||
|
else:
|
||||||
|
print("Invalid JWT format")
|
||||||
|
|
||||||
59
demo/test_payment.py
Normal file
59
demo/test_payment.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
# 测试支付API
|
||||||
|
def test_payment_api():
|
||||||
|
# 首先创建支付记录
|
||||||
|
create_payment_url = "http://localhost:8080/api/payments/create"
|
||||||
|
create_payment_data = {
|
||||||
|
"amount": 59.00,
|
||||||
|
"description": "标准版会员 - 支付宝支付",
|
||||||
|
"paymentMethod": "ALIPAY"
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwidXNlcklkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE3NjE2MzYyNDQsImV4cCI6MTc2MTcyMjY0NH0.qZxHDkgSoSRvmMHBFfRdzZYtC55eCKba3VN07lTsFzKXn1hYbupv7boBJDKNOUrRYaH5ougHLFTI5xm059434g",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("1. 创建支付记录...")
|
||||||
|
response = requests.post(create_payment_url, json=create_payment_data, headers=headers)
|
||||||
|
print(f"创建支付记录状态码: {response.status_code}")
|
||||||
|
print(f"创建支付记录响应: {response.text}")
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
payment_data = response.json()
|
||||||
|
if payment_data.get('success'):
|
||||||
|
payment_id = payment_data['data']['id']
|
||||||
|
print(f"支付记录创建成功,ID: {payment_id}")
|
||||||
|
|
||||||
|
# 然后创建支付宝支付
|
||||||
|
alipay_url = "http://localhost:8080/api/payments/alipay/create"
|
||||||
|
alipay_data = {"paymentId": payment_id}
|
||||||
|
|
||||||
|
print("2. 创建支付宝支付...")
|
||||||
|
alipay_response = requests.post(alipay_url, json=alipay_data, headers=headers)
|
||||||
|
print(f"支付宝支付状态码: {alipay_response.status_code}")
|
||||||
|
print(f"支付宝支付响应: {alipay_response.text}")
|
||||||
|
|
||||||
|
if alipay_response.status_code == 200:
|
||||||
|
alipay_data = alipay_response.json()
|
||||||
|
if alipay_data.get('success'):
|
||||||
|
qr_code = alipay_data['data'].get('qrCode')
|
||||||
|
print(f"支付宝二维码生成成功: {qr_code}")
|
||||||
|
else:
|
||||||
|
print(f"支付宝支付失败: {alipay_data.get('message')}")
|
||||||
|
else:
|
||||||
|
print(f"支付宝支付请求失败: {alipay_response.text}")
|
||||||
|
else:
|
||||||
|
print(f"支付记录创建失败: {payment_data.get('message')}")
|
||||||
|
else:
|
||||||
|
print(f"创建支付记录请求失败: {response.text}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"测试失败: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_payment_api()
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 286 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 286 KiB |
80
test_alipay_connection.py
Normal file
80
test_alipay_connection.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import requests
|
||||||
|
import time
|
||||||
|
|
||||||
|
def test_alipay_connection():
|
||||||
|
"""测试支付宝沙箱API连接"""
|
||||||
|
print("=== 测试支付宝沙箱API连接 ===")
|
||||||
|
|
||||||
|
# 支付宝沙箱网关地址
|
||||||
|
gateway_url = "https://openapi.alipaydev.com/gateway.do"
|
||||||
|
|
||||||
|
# 测试连接
|
||||||
|
try:
|
||||||
|
print(f"正在测试连接到: {gateway_url}")
|
||||||
|
response = requests.get(gateway_url, timeout=10)
|
||||||
|
print(f"连接成功! 状态码: {response.status_code}")
|
||||||
|
print(f"响应头: {dict(response.headers)}")
|
||||||
|
return True
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
print("❌ 连接超时 - 可能是网络问题")
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
print(f"❌ 连接错误: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 其他错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_alternative_gateway():
|
||||||
|
"""测试备用网关"""
|
||||||
|
print("\n=== 测试备用网关 ===")
|
||||||
|
|
||||||
|
# 尝试不同的网关地址
|
||||||
|
gateways = [
|
||||||
|
"https://openapi.alipaydev.com/gateway.do",
|
||||||
|
"https://openapi.alipay.com/gateway.do",
|
||||||
|
"https://mapi.alipay.com/gateway.do"
|
||||||
|
]
|
||||||
|
|
||||||
|
for gateway in gateways:
|
||||||
|
try:
|
||||||
|
print(f"测试网关: {gateway}")
|
||||||
|
response = requests.get(gateway, timeout=5)
|
||||||
|
print(f"✅ {gateway} - 状态码: {response.status_code}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ {gateway} - 错误: {e}")
|
||||||
|
|
||||||
|
def test_network_connectivity():
|
||||||
|
"""测试基本网络连接"""
|
||||||
|
print("\n=== 测试基本网络连接 ===")
|
||||||
|
|
||||||
|
test_urls = [
|
||||||
|
"https://www.baidu.com",
|
||||||
|
"https://www.google.com",
|
||||||
|
"https://openapi.alipaydev.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
for url in test_urls:
|
||||||
|
try:
|
||||||
|
response = requests.get(url, timeout=5)
|
||||||
|
print(f"✅ {url} - 状态码: {response.status_code}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ {url} - 错误: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("开始网络连接测试...")
|
||||||
|
|
||||||
|
# 测试基本网络连接
|
||||||
|
test_network_connectivity()
|
||||||
|
|
||||||
|
# 测试支付宝连接
|
||||||
|
alipay_ok = test_alipay_connection()
|
||||||
|
|
||||||
|
# 测试备用网关
|
||||||
|
test_alternative_gateway()
|
||||||
|
|
||||||
|
if alipay_ok:
|
||||||
|
print("\n✅ 支付宝沙箱API连接正常")
|
||||||
|
else:
|
||||||
|
print("\n❌ 支付宝沙箱API连接有问题,建议检查网络或使用模拟支付")
|
||||||
|
|
||||||
15
test_api.py
Normal file
15
test_api.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
# 测试作品列表API
|
||||||
|
url = "http://localhost:8080/api/works/my-works"
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwidXNlcklkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE3NjE2MzYyNDQsImV4cCI6MTc2MTcyMjY0NH0.qZxHDkgSoSRvmMHBFfRdzZYtC55eCKba3VN07lTsFzKXn1hYbupv7boBJDKNOUrRYaH5ougHLFTI5xm059434g"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers, params={"page": 0, "size": 10})
|
||||||
|
print(f"Status Code: {response.status_code}")
|
||||||
|
print(f"Response: {response.text}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
Reference in New Issue
Block a user