From 8f5a0fcdda704b2db1e469fb13ac0d6d81a41a63 Mon Sep 17 00:00:00 2001 From: zhonghua Date: Thu, 19 Mar 2026 11:37:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A5=97=E9=A4=90=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8E=20gradeLevels/=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=8C=85=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gradeLevels: 后端 JSON 解析修复,前端兼容错误格式 - 筛选下拉: 状态选项按业务流程排序 - 已下架: 列表与详情页增加编辑、删除操作 - 课程包关联: 前后端去重,修复 uk_collection_package 唯一约束冲突 Made-with: Cursor --- .../src/api/collections.ts | 18 ++++++-- .../collections/CollectionDetailView.vue | 22 ++++++---- .../admin/collections/CollectionEditView.vue | 7 +++- .../admin/collections/CollectionListView.vue | 41 ++++++------------- .../views/admin/packages/PackageEditView.vue | 7 +++- .../views/admin/packages/PackageListView.vue | 15 ++++++- .../service/CourseCollectionService.java | 36 ++++++++++++---- 7 files changed, 93 insertions(+), 53 deletions(-) diff --git a/reading-platform-frontend/src/api/collections.ts b/reading-platform-frontend/src/api/collections.ts index d4f213b..95a9be8 100644 --- a/reading-platform-frontend/src/api/collections.ts +++ b/reading-platform-frontend/src/api/collections.ts @@ -202,9 +202,21 @@ export function getCollectionStatusInfo(status: string) { return COLLECTION_STATUS_MAP[status] || { label: status, color: 'default' }; } -// 解析适用年级 -export function parseGradeLevels(gradeLevels: string | string[]): string[] { - if (Array.isArray(gradeLevels)) return gradeLevels; +// 解析适用年级(统一处理列表展示与创建时的格式) +export function parseGradeLevels(gradeLevels: string | string[] | undefined): string[] { + if (!gradeLevels) return []; + if (Array.isArray(gradeLevels)) { + if (gradeLevels.length === 0) return []; + // 兼容后端错误格式:["[\"小班\"", " \"中班\""] -> 拼接后解析 + if (gradeLevels[0]?.toString().startsWith('[')) { + try { + return JSON.parse(gradeLevels.join('')); + } catch { + return []; + } + } + return gradeLevels; + } try { return JSON.parse(gradeLevels || '[]'); } catch { diff --git a/reading-platform-frontend/src/views/admin/collections/CollectionDetailView.vue b/reading-platform-frontend/src/views/admin/collections/CollectionDetailView.vue index 84f7772..68e56c8 100644 --- a/reading-platform-frontend/src/views/admin/collections/CollectionDetailView.vue +++ b/reading-platform-frontend/src/views/admin/collections/CollectionDetailView.vue @@ -31,10 +31,14 @@ - + @@ -284,10 +288,12 @@ const handleAddPackage = async () => { addingPackage.value = true; try { - // 获取当前课程包列表 - const currentPackageIds = collection.value?.packages?.map(p => p.id) || []; - // 添加新的课程包ID - const newPackageIds = [...currentPackageIds, selectedPackageId.value]; + // 获取当前课程包列表(去重) + const currentPackageIds = [...new Set(collection.value?.packages?.map(p => p.id) || [])]; + // 添加新的课程包ID(避免重复) + const newPackageIds = currentPackageIds.includes(selectedPackageId.value) + ? currentPackageIds + : [...currentPackageIds, selectedPackageId.value]; await collectionsApi.setCollectionPackages(route.params.id, newPackageIds); message.success('添加成功'); @@ -304,8 +310,8 @@ const handleAddPackage = async () => { // 移除课程包 const handleRemovePackage = async (packageId: number) => { try { - // 获取当前课程包列表,移除指定的课程包 - const currentPackageIds = collection.value?.packages?.map(p => p.id) || []; + // 获取当前课程包列表(去重),移除指定的课程包 + const currentPackageIds = [...new Set(collection.value?.packages?.map(p => p.id) || [])]; const newPackageIds = currentPackageIds.filter(id => id !== packageId); await collectionsApi.setCollectionPackages(route.params.id, newPackageIds); diff --git a/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue b/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue index 736f3bb..150d766 100644 --- a/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue +++ b/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue @@ -274,10 +274,13 @@ const handleSubmit = async () => { id = res.id; } - // 保存课程包关联(按 sortOrder 排序) + // 保存课程包关联(按 sortOrder 排序,去重避免唯一约束冲突) if (selectedPackages.value.length > 0) { const sorted = [...selectedPackages.value].sort((a, b) => a.sortOrder - b.sortOrder); - await setCollectionPackages(id, sorted.map((p) => p.packageId)); + const packageIds = [...new Map(sorted.map((p) => [p.packageId, p])).values()] + .sort((a, b) => a.sortOrder - b.sortOrder) + .map((p) => p.packageId); + await setCollectionPackages(id, packageIds); } message.success(isEdit.value ? '套餐更新成功' : '套餐创建成功'); diff --git a/reading-platform-frontend/src/views/admin/collections/CollectionListView.vue b/reading-platform-frontend/src/views/admin/collections/CollectionListView.vue index c9e0e42..c5e1a0a 100644 --- a/reading-platform-frontend/src/views/admin/collections/CollectionListView.vue +++ b/reading-platform-frontend/src/views/admin/collections/CollectionListView.vue @@ -6,38 +6,25 @@
- + 草稿 - 待审核 - 已通过 已发布 已下架 - 已驳回
- + diff --git a/reading-platform-frontend/src/views/admin/packages/PackageEditView.vue b/reading-platform-frontend/src/views/admin/packages/PackageEditView.vue index 01a89ca..275219b 100644 --- a/reading-platform-frontend/src/views/admin/packages/PackageEditView.vue +++ b/reading-platform-frontend/src/views/admin/packages/PackageEditView.vue @@ -283,10 +283,13 @@ const handleSave = async () => { id = res.id; // 后端 Long 序列化为 string } - // 保存课程包关联(按 sortOrder 排序) + // 保存课程包关联(按 sortOrder 排序,去重避免唯一约束冲突) if (selectedPackages.value.length > 0) { const sorted = [...selectedPackages.value].sort((a, b) => a.sortOrder - b.sortOrder); - await setCollectionPackages(id, sorted.map((p) => p.packageId)); + const packageIds = [...new Map(sorted.map((p) => [p.packageId, p])).values()] + .sort((a, b) => a.sortOrder - b.sortOrder) + .map((p) => p.packageId); + await setCollectionPackages(id, packageIds); } message.success('保存成功'); diff --git a/reading-platform-frontend/src/views/admin/packages/PackageListView.vue b/reading-platform-frontend/src/views/admin/packages/PackageListView.vue index 1b820bd..8ed0dc6 100644 --- a/reading-platform-frontend/src/views/admin/packages/PackageListView.vue +++ b/reading-platform-frontend/src/views/admin/packages/PackageListView.vue @@ -169,8 +169,19 @@ const statusTexts: Record = { const getStatusColor = (status: string) => statusColors[status] || 'default'; const getStatusText = (status: string) => statusTexts[status] || status; -const parseGradeLevels = (gradeLevels: string | string[]) => { - if (Array.isArray(gradeLevels)) return gradeLevels; +const parseGradeLevels = (gradeLevels: string | string[] | undefined): string[] => { + if (!gradeLevels) return []; + if (Array.isArray(gradeLevels)) { + if (gradeLevels.length === 0) return []; + if (gradeLevels[0]?.toString().startsWith('[')) { + try { + return JSON.parse(gradeLevels.join('')); + } catch { + return []; + } + } + return gradeLevels; + } try { return JSON.parse(gradeLevels || '[]'); } catch { diff --git a/reading-platform-java/src/main/java/com/reading/platform/service/CourseCollectionService.java b/reading-platform-java/src/main/java/com/reading/platform/service/CourseCollectionService.java index 751b06d..07f6b78 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/service/CourseCollectionService.java +++ b/reading-platform-java/src/main/java/com/reading/platform/service/CourseCollectionService.java @@ -222,13 +222,16 @@ public class CourseCollectionService extends ServiceImpl distinctIds = packageIds.stream().distinct().collect(Collectors.toList()); + // 验证课程包是否存在(应用层外键约束) - if (!packageIds.isEmpty()) { + if (!distinctIds.isEmpty()) { List packages = packageMapper.selectList( new LambdaQueryWrapper() - .in(CoursePackage::getId, packageIds) + .in(CoursePackage::getId, distinctIds) ); - if (packages.size() != packageIds.size()) { + if (packages.size() != distinctIds.size()) { throw new BusinessException("存在无效的课程包 ID"); } } @@ -240,16 +243,16 @@ public class CourseCollectionService extends ServiceImpl