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