更新功能和文档: 增强支付系统、任务队列管理、用户作品管理等功能

This commit is contained in:
AIGC Developer
2025-10-29 10:16:03 +08:00
parent 8c55f9f376
commit 6f72386523
64 changed files with 1529 additions and 339 deletions

View File

@@ -286,3 +286,5 @@ grep "img2vid_abc123def456" logs/application.log
``` ```

View File

@@ -287,3 +287,5 @@ public TaskQueue addTextToVideoTask(String username, String taskId) {
5. **监控告警**: 监控积分冻结系统的运行状态 5. **监控告警**: 监控积分冻结系统的运行状态

View File

@@ -32,3 +32,5 @@ public class PasswordChecker {

View File

@@ -296,3 +296,5 @@ const startPolling = (taskId) => {
如有问题,请联系开发团队或查看系统日志获取详细错误信息。 如有问题,请联系开发团队或查看系统日志获取详细错误信息。

View File

@@ -279,3 +279,5 @@ POST /api/text-to-video/tasks/{id}/cancel # 取消任务
**文生视频API已成功实现并可以投入使用** 🎉 **文生视频API已成功实现并可以投入使用** 🎉

View File

@@ -166,3 +166,5 @@ const updateWork = async (workId, updateData) => {
5. **用户体验**: 提供友好的作品管理界面 5. **用户体验**: 提供友好的作品管理界面

View File

@@ -432,3 +432,5 @@ MIT License

View File

@@ -28,3 +28,5 @@ console.log('App.vue 加载成功')

View File

@@ -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)
} }

View File

@@ -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'

View 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'
})
}

View File

@@ -91,3 +91,5 @@

View 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>

View File

@@ -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('获取队列状态成功')

View File

@@ -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) => {

View File

@@ -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'

View File

@@ -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')
} }
// 重置筛选器 // 重置筛选器

View File

@@ -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')
} }
// 跳转到数据仪表盘 // 跳转到数据仪表盘

View File

@@ -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) => {

View File

@@ -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>

View File

@@ -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',

View File

@@ -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) => {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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测试控制器

View File

@@ -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);

View File

@@ -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<>();

View File

@@ -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;

View File

@@ -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控制器
*/ */

View File

@@ -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控制器
*/ */

View File

@@ -195,3 +195,5 @@ public class PointsFreezeRecord {
} }

View File

@@ -263,3 +263,5 @@ public class TaskQueue {
} }

View File

@@ -255,3 +255,5 @@ public class TaskStatus {
} }

View File

@@ -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

View File

@@ -63,3 +63,5 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {
} }

View File

@@ -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;
/** /**
* 用户作品仓库接口 * 用户作品仓库接口

View File

@@ -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;
/** /**
* 任务队列定时调度器 * 任务队列定时调度器

View File

@@ -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());

View File

@@ -34,3 +34,5 @@ public class PlainTextPasswordEncoder implements PasswordEncoder {

View File

@@ -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());
}
}
/** /**
* 处理支付宝异步通知 * 处理支付宝异步通知
*/ */

View File

@@ -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;
/** /**
* 图生视频服务类 * 图生视频服务类

View File

@@ -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);
// 不抛出异常,让前端处理 // 不抛出异常,让前端处理

View File

@@ -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;
}
}
/** /**
* 处理超时任务 * 处理超时任务
*/ */

View File

@@ -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;
/** /**
* 文生视频服务类 * 文生视频服务类

View File

@@ -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);

View File

@@ -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));

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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='任务队列表';

View File

@@ -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='积分冻结记录表';

View File

@@ -24,3 +24,5 @@ CREATE TABLE task_status (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务状态表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务状态表';

View File

@@ -570,3 +570,5 @@

View File

@@ -486,3 +486,5 @@

View File

@@ -525,3 +525,5 @@

View File

@@ -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;
/** /**
* 任务队列功能测试 * 任务队列功能测试

View File

@@ -25,3 +25,5 @@ echo 按任意键退出...
pause > nul pause > nul

View File

@@ -52,3 +52,5 @@ public class TestApiConnection {
} }

15
demo/test_api.py Normal file
View 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
View 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
View 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
View 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
View 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}")