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 @@
-
+
+
+
新建套餐
-
+
草稿
- 待审核
- 已通过
已发布
已下架
- 已驳回
-
+
{{ formatPrice(record.price) }}
@@ -62,11 +49,7 @@
发布
-
+
发布
@@ -89,11 +72,7 @@
发布
-
+
发布
@@ -115,9 +94,13 @@
查看
+ 编辑
重新发布
+
+ 删除
+
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