feat: 完成小说漫剧历史记录模块开发
This commit is contained in:
@@ -56,3 +56,11 @@ export const getNovelComicHistory = (params = {}) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除历史记录
|
||||||
|
export const deleteNovelComicHistory = (id) => {
|
||||||
|
return request({
|
||||||
|
url: `/novel-comic/history/${id}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const getMyWorksByType = (workType, params = {}) => {
|
|||||||
return api.get('/works/my-works', {
|
return api.get('/works/my-works', {
|
||||||
params: {
|
params: {
|
||||||
page: params.page || 0,
|
page: params.page || 0,
|
||||||
size: params.size || 1000,
|
size: params.size || 20,
|
||||||
includeProcessing: true,
|
includeProcessing: true,
|
||||||
workType: workType // TEXT_TO_VIDEO, IMAGE_TO_VIDEO, STORYBOARD_VIDEO, STORYBOARD_IMAGE
|
workType: workType // TEXT_TO_VIDEO, IMAGE_TO_VIDEO, STORYBOARD_VIDEO, STORYBOARD_IMAGE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ const isVerticalVideo = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const pageSize = ref(100)
|
const pageSize = ref(20)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const hasMore = ref(true)
|
const hasMore = ref(true)
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
@@ -502,10 +502,10 @@ const setupVirtualObserver = () => {
|
|||||||
if (changed) visibleItemIds.value = next
|
if (changed) visibleItemIds.value = next
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
root: contentAreaRef.value,
|
root: contentAreaRef.value,
|
||||||
rootMargin: '75% 0px', // 上下 75% 视口高度的缓冲区
|
rootMargin: '50% 0px', // 上下 50% 视口高度的缓冲区
|
||||||
threshold: 0
|
threshold: 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 观察已注册的元素
|
// 观察已注册的元素
|
||||||
@@ -665,6 +665,8 @@ const loadList = async () => {
|
|||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
const data = response.data.data || []
|
const data = response.data.data || []
|
||||||
|
const totalPages = response.data.totalPages || 0
|
||||||
|
const currentPage = response.data.currentPage || 0
|
||||||
|
|
||||||
// 转换数据格式
|
// 转换数据格式
|
||||||
const transformedData = data
|
const transformedData = data
|
||||||
@@ -673,7 +675,11 @@ const loadList = async () => {
|
|||||||
|
|
||||||
if (page.value === 1) items.value = []
|
if (page.value === 1) items.value = []
|
||||||
items.value = items.value.concat(transformedData)
|
items.value = items.value.concat(transformedData)
|
||||||
hasMore.value = data.length === pageSize.value
|
|
||||||
|
// 使用后端返回的总页数来判断是否还有更多数据
|
||||||
|
hasMore.value = currentPage < totalPages - 1
|
||||||
|
|
||||||
|
console.log('[MyWorks] 分页信息 - 当前页:', currentPage, '总页数:', totalPages, '是否有更多:', hasMore.value)
|
||||||
|
|
||||||
// 检查是否有处理中的任务,如果有则启动轮询
|
// 检查是否有处理中的任务,如果有则启动轮询
|
||||||
checkAndStartPolling()
|
checkAndStartPolling()
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
<div class="left-panel-content">
|
<div class="left-panel-content">
|
||||||
<CreationTabs active="novel-comic" />
|
<CreationTabs active="novel-comic" />
|
||||||
|
|
||||||
<div class="form-card">
|
<div>
|
||||||
<h3 class="form-card-title">创作设置</h3>
|
<h3 class="form-section-title">创作设置</h3>
|
||||||
|
|
||||||
<!-- 主题(选填) -->
|
<!-- 主题(选填) -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
v-model:value="form.theme"
|
v-model:value="form.theme"
|
||||||
placeholder="例如:都市奇幻、校园恋爱、科幻冒险..."
|
placeholder="例如:都市奇幻、校园恋爱、科幻冒险..."
|
||||||
:maxlength="100"
|
:maxlength="100"
|
||||||
show-count
|
@input="checkThemeLength"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
placeholder="描述故事发生的世界观、时代背景、环境设定等..."
|
placeholder="描述故事发生的世界观、时代背景、环境设定等..."
|
||||||
:rows="4"
|
:rows="4"
|
||||||
:maxlength="2000"
|
:maxlength="2000"
|
||||||
show-count
|
@input="checkStoryBackgroundLength"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
placeholder="输入完整的故事文案/剧本内容,AI 将基于此生成漫剧..."
|
placeholder="输入完整的故事文案/剧本内容,AI 将基于此生成漫剧..."
|
||||||
:rows="8"
|
:rows="8"
|
||||||
:maxlength="10000"
|
:maxlength="10000"
|
||||||
show-count
|
@input="checkStoryScriptLength"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -163,35 +163,64 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 历史记录 -->
|
<!-- 历史记录 -->
|
||||||
<div class="history-card" v-if="historyList.length > 0">
|
<div class="history-card">
|
||||||
<h3 class="form-card-title">历史记录</h3>
|
<h3 class="form-card-title">历史记录</h3>
|
||||||
<div class="history-list">
|
<div class="history-list" v-if="historyList.length > 0">
|
||||||
<div
|
<div
|
||||||
v-for="item in historyList"
|
v-for="item in historyList"
|
||||||
:key="item.id || item.taskId"
|
:key="item.id || item.taskId"
|
||||||
class="history-item"
|
class="history-item"
|
||||||
@click="viewHistoryItem(item)"
|
|
||||||
>
|
>
|
||||||
<div class="history-item-top">
|
<div class="history-item-top">
|
||||||
<span class="history-status" :class="getHistoryStatusClass(item.status)">
|
<span class="history-status" :class="getHistoryStatusClass(item.status)">
|
||||||
{{ getHistoryStatusText(item.status) }}
|
{{ getHistoryStatusText(item.status) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="history-date">{{ formatDate(item.createdAt) }}</span>
|
<span class="history-date">{{ formatFriendlyDate(item.createdAt) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="history-theme" v-if="item.theme">{{ item.theme }}</div>
|
<div class="history-theme" v-if="item.theme">{{ item.theme }}</div>
|
||||||
<div class="history-preview-text">
|
<div class="history-preview-text">
|
||||||
{{ truncateText(item.storyBackground || item.storyScript || '', 80) }}
|
{{ truncateText(item.storyBackground || item.storyScript || '', 80) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 展开详情 -->
|
||||||
|
<div class="history-expand" v-if="expandedItemId === item.id">
|
||||||
|
<div class="history-detail-section">
|
||||||
|
<div class="history-detail-label">故事背景</div>
|
||||||
|
<div class="history-detail-content">{{ item.storyBackground || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="history-detail-section">
|
||||||
|
<div class="history-detail-label">故事文案</div>
|
||||||
|
<div class="history-detail-content">{{ item.storyScript || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="history-item-actions">
|
<div class="history-item-actions">
|
||||||
|
<a-button size="small" @click.stop="toggleExpand(item)">
|
||||||
|
{{ expandedItemId === item.id ? '收起' : '展开' }}
|
||||||
|
</a-button>
|
||||||
<a-button size="small" @click.stop="reuseHistory(item)">复用设置</a-button>
|
<a-button size="small" @click.stop="reuseHistory(item)">复用设置</a-button>
|
||||||
<a-button size="small" type="primary" v-if="item.status === 'SUCCESS' || item.status === 'COMPLETED'" @click.stop="viewHistoryItem(item)">
|
<a-button size="small" type="primary" v-if="item.status === 'SUCCESS' || item.status === 'COMPLETED'" @click.stop="viewHistoryItem(item)">
|
||||||
<CopyOutlined /> 查看结果
|
<CopyOutlined /> 查看结果
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button size="small" danger @click.stop="handleDeleteHistory(item)">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div class="history-empty" v-else-if="!loadingHistory">
|
||||||
|
<div class="empty-illustration">📝</div>
|
||||||
|
<p class="empty-title">暂无历史记录</p>
|
||||||
|
<p class="empty-desc">完成创作后,记录将显示在这里</p>
|
||||||
|
</div>
|
||||||
|
<!-- 加载中 -->
|
||||||
|
<div class="history-loading" v-else>
|
||||||
|
<LoadingOutlined class="is-loading" />
|
||||||
|
<span>加载中...</span>
|
||||||
|
</div>
|
||||||
<!-- 加载更多 -->
|
<!-- 加载更多 -->
|
||||||
<div class="history-load-more" v-if="hasMoreHistory">
|
<div class="history-load-more" v-if="hasMoreHistory && historyList.length > 0">
|
||||||
<a-button size="small" :loading="loadingHistory" @click="loadMoreHistory">加载更多</a-button>
|
<a-button size="small" :loading="loadingHistory" @click="loadMoreHistory">加载更多</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -225,11 +254,11 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { LoadingOutlined, CopyOutlined, CustomerServiceOutlined, CloseOutlined } from '@ant-design/icons-vue'
|
import { LoadingOutlined, CopyOutlined, CustomerServiceOutlined, CloseOutlined } from '@ant-design/icons-vue'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { createNovelComicTask, getNovelComicTaskStatus, getNovelComicHistory } from '@/api/novelComic'
|
import { createNovelComicTask, getNovelComicTaskStatus, getNovelComicHistory, deleteNovelComicHistory } from '@/api/novelComic'
|
||||||
import { getUserSubscriptionInfo } from '@/api/payments'
|
import { getUserSubscriptionInfo } from '@/api/payments'
|
||||||
import CreationHeader from '@/components/CreationHeader.vue'
|
import CreationHeader from '@/components/CreationHeader.vue'
|
||||||
import CreationTabs from '@/components/CreationTabs.vue'
|
import CreationTabs from '@/components/CreationTabs.vue'
|
||||||
@@ -362,6 +391,8 @@ const handleSubmit = async () => {
|
|||||||
// 异步任务:开始轮询状态
|
// 异步任务:开始轮询状态
|
||||||
polling.value = true
|
polling.value = true
|
||||||
startPolling(taskId)
|
startPolling(taskId)
|
||||||
|
// 刷新历史记录
|
||||||
|
loadHistory()
|
||||||
} else {
|
} else {
|
||||||
taskResult.value = {
|
taskResult.value = {
|
||||||
status: 'FAILED',
|
status: 'FAILED',
|
||||||
@@ -469,11 +500,35 @@ const resetForm = () => {
|
|||||||
taskResult.value = null
|
taskResult.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 字数限制检查
|
||||||
|
const THEME_MAX_LENGTH = 100
|
||||||
|
const STORY_BACKGROUND_MAX_LENGTH = 2000
|
||||||
|
const STORY_SCRIPT_MAX_LENGTH = 10000
|
||||||
|
|
||||||
|
const checkThemeLength = (e) => {
|
||||||
|
if (e.target.value.length >= THEME_MAX_LENGTH) {
|
||||||
|
message.warning(`主题最多 ${THEME_MAX_LENGTH} 个字`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkStoryBackgroundLength = (e) => {
|
||||||
|
if (e.target.value.length >= STORY_BACKGROUND_MAX_LENGTH) {
|
||||||
|
message.warning(`故事背景最多 ${STORY_BACKGROUND_MAX_LENGTH} 个字`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkStoryScriptLength = (e) => {
|
||||||
|
if (e.target.value.length >= STORY_SCRIPT_MAX_LENGTH) {
|
||||||
|
message.warning(`故事文案最多 ${STORY_SCRIPT_MAX_LENGTH} 个字`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 历史记录 =====
|
// ===== 历史记录 =====
|
||||||
const historyList = ref([])
|
const historyList = ref([])
|
||||||
const loadingHistory = ref(false)
|
const loadingHistory = ref(false)
|
||||||
const historyPage = ref(0)
|
const historyPage = ref(0)
|
||||||
const hasMoreHistory = ref(false)
|
const hasMoreHistory = ref(false)
|
||||||
|
const expandedItemId = ref(null)
|
||||||
|
|
||||||
const loadHistory = async (page = 0) => {
|
const loadHistory = async (page = 0) => {
|
||||||
loadingHistory.value = true
|
loadingHistory.value = true
|
||||||
@@ -488,7 +543,6 @@ const loadHistory = async (page = 0) => {
|
|||||||
historyList.value.push(...items)
|
historyList.value.push(...items)
|
||||||
}
|
}
|
||||||
historyPage.value = page
|
historyPage.value = page
|
||||||
// 判断是否还有更多
|
|
||||||
const totalElements = data.data.totalElements || 0
|
const totalElements = data.data.totalElements || 0
|
||||||
hasMoreHistory.value = historyList.value.length < totalElements
|
hasMoreHistory.value = historyList.value.length < totalElements
|
||||||
}
|
}
|
||||||
@@ -530,11 +584,47 @@ const formatDate = (dateStr) => {
|
|||||||
return `${month}-${day} ${hour}:${min}`
|
return `${month}-${day} ${hour}:${min}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatFriendlyDate = (dateStr) => {
|
||||||
|
if (!dateStr) return ''
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
const now = new Date()
|
||||||
|
const diff = now - date
|
||||||
|
const oneDay = 24 * 60 * 60 * 1000
|
||||||
|
const oneHour = 60 * 60 * 1000
|
||||||
|
const oneMinute = 60 * 1000
|
||||||
|
|
||||||
|
if (diff < oneMinute) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (diff < oneHour) {
|
||||||
|
const minutes = Math.floor(diff / oneMinute)
|
||||||
|
return `${minutes} 分钟前`
|
||||||
|
} else if (diff < oneDay) {
|
||||||
|
const hours = Math.floor(diff / oneHour)
|
||||||
|
return `${hours} 小时前`
|
||||||
|
} else if (diff < 2 * oneDay) {
|
||||||
|
return '昨天'
|
||||||
|
} else if (diff < 7 * oneDay) {
|
||||||
|
const days = Math.floor(diff / oneDay)
|
||||||
|
return `${days} 天前`
|
||||||
|
} else {
|
||||||
|
return formatDate(dateStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const truncateText = (text, maxLen) => {
|
const truncateText = (text, maxLen) => {
|
||||||
if (!text) return '暂无描述'
|
if (!text) return '暂无描述'
|
||||||
return text.length > maxLen ? text.slice(0, maxLen) + '...' : text
|
return text.length > maxLen ? text.slice(0, maxLen) + '...' : text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 展开/收起历史记录
|
||||||
|
const toggleExpand = (item) => {
|
||||||
|
if (expandedItemId.value === item.id) {
|
||||||
|
expandedItemId.value = null
|
||||||
|
} else {
|
||||||
|
expandedItemId.value = item.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 查看历史结果
|
// 查看历史结果
|
||||||
const viewHistoryItem = (item) => {
|
const viewHistoryItem = (item) => {
|
||||||
const s = (item.status || '').toUpperCase()
|
const s = (item.status || '').toUpperCase()
|
||||||
@@ -560,6 +650,34 @@ const reuseHistory = (item) => {
|
|||||||
message.success('已填充历史设置')
|
message.success('已填充历史设置')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除历史记录
|
||||||
|
const handleDeleteHistory = (item) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: `确定要删除这条历史记录吗?`,
|
||||||
|
okText: '删除',
|
||||||
|
cancelText: '取消',
|
||||||
|
okType: 'danger',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
const response = await deleteNovelComicHistory(item.id)
|
||||||
|
if (response.data?.success) {
|
||||||
|
message.success('删除成功')
|
||||||
|
historyList.value = historyList.value.filter(i => i.id !== item.id)
|
||||||
|
if (expandedItemId.value === item.id) {
|
||||||
|
expandedItemId.value = null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error(response.data?.message || '删除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除历史记录失败:', error)
|
||||||
|
message.error('删除失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkMembership()
|
checkMembership()
|
||||||
loadHistory()
|
loadHistory()
|
||||||
@@ -640,7 +758,6 @@ onMounted(() => {
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-card,
|
|
||||||
.result-card {
|
.result-card {
|
||||||
background: var(--bg-surface);
|
background: var(--bg-surface);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
@@ -649,7 +766,7 @@ onMounted(() => {
|
|||||||
box-shadow: 0 2px 12px rgba(124, 58, 237, 0.04);
|
box-shadow: 0 2px 12px rgba(124, 58, 237, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-card-title {
|
.form-section-title {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0 0 24px 0;
|
margin: 0 0 24px 0;
|
||||||
@@ -658,7 +775,7 @@ onMounted(() => {
|
|||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-card-title::after {
|
.form-section-title::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -1101,6 +1218,75 @@ onMounted(() => {
|
|||||||
.history-item-actions {
|
.history-item-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 展开区域 */
|
||||||
|
.history-expand {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px dashed var(--border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-detail-section {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-detail-section:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-detail-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-detail-content {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.6;
|
||||||
|
background: var(--bg-base);
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态和加载中 */
|
||||||
|
.history-empty,
|
||||||
|
.history-loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-empty .empty-illustration,
|
||||||
|
.history-loading .empty-illustration {
|
||||||
|
font-size: 48px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-empty .empty-title,
|
||||||
|
.history-loading .empty-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-empty .empty-desc,
|
||||||
|
.history-loading .empty-desc {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-loading .is-loading {
|
||||||
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-load-more {
|
.history-load-more {
|
||||||
|
|||||||
@@ -265,6 +265,46 @@ public class NovelComicApiController {
|
|||||||
return ResponseEntity.ok(response);
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除历史记录
|
||||||
|
* 不需要事务,不需要先查询,直接删除并通过影响行数判断
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/history/{id}")
|
||||||
|
public ResponseEntity<Map<String, Object>> deleteHistory(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestHeader("Authorization") String token) {
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String username = extractUsernameFromToken(token);
|
||||||
|
if (username == null) {
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "用户未登录");
|
||||||
|
return ResponseEntity.status(401).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleted = novelComicTaskRepository.deleteByIdAndUsername(id, username);
|
||||||
|
|
||||||
|
if (deleted > 0) {
|
||||||
|
logger.info("删除历史记录成功: id={}, user={}", id, username);
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "删除成功");
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} else {
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "记录不存在或无权限删除");
|
||||||
|
return ResponseEntity.status(404).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("删除历史记录异常: id={}", id, e);
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "删除失败: " + e.getMessage());
|
||||||
|
return ResponseEntity.status(500).body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新数据库中的任务记录
|
* 更新数据库中的任务记录
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class UserWorkApiController {
|
|||||||
public ResponseEntity<Map<String, Object>> getMyWorks(
|
public ResponseEntity<Map<String, Object>> getMyWorks(
|
||||||
@RequestHeader(value = "Authorization", required = false) String token,
|
@RequestHeader(value = "Authorization", required = false) String token,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "1000") int size,
|
@RequestParam(defaultValue = "20") int size,
|
||||||
@RequestParam(defaultValue = "true") boolean includeProcessing,
|
@RequestParam(defaultValue = "true") boolean includeProcessing,
|
||||||
@RequestParam(required = false) String workType) {
|
@RequestParam(required = false) String workType) {
|
||||||
|
|
||||||
@@ -110,9 +110,10 @@ public class UserWorkApiController {
|
|||||||
return ResponseEntity.status(401).body(response);
|
return ResponseEntity.status(401).body(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 输入验证 - 移除size上限限制
|
// 输入验证 - 设置合理的size上限
|
||||||
if (page < 0) page = 0;
|
if (page < 0) page = 0;
|
||||||
if (size <= 0) size = 1000; // 不设上限,默认1000条
|
if (size <= 0) size = 20;
|
||||||
|
if (size > 100) size = 100; // 设置上限,最多100条
|
||||||
|
|
||||||
// 解析作品类型
|
// 解析作品类型
|
||||||
CommonTaskType filterType = null;
|
CommonTaskType filterType = null;
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ 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;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import com.example.demo.model.NovelComicTask;
|
import com.example.demo.model.NovelComicTask;
|
||||||
@@ -14,5 +17,11 @@ public interface NovelComicTaskRepository extends JpaRepository<NovelComicTask,
|
|||||||
|
|
||||||
Optional<NovelComicTask> findByTaskId(String taskId);
|
Optional<NovelComicTask> findByTaskId(String taskId);
|
||||||
|
|
||||||
|
Optional<NovelComicTask> findByIdAndUsername(Long id, String username);
|
||||||
|
|
||||||
Page<NovelComicTask> findByUsernameOrderByCreatedAtDesc(String username, Pageable pageable);
|
Page<NovelComicTask> findByUsernameOrderByCreatedAtDesc(String username, Pageable pageable);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("DELETE FROM NovelComicTask t WHERE t.id = :id AND t.username = :username")
|
||||||
|
int deleteByIdAndUsername(@Param("id") Long id, @Param("username") String username);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user