feat: 课程套餐与课程包管理优化
- 课程套餐:适用年级从已选课程包自动同步,年级列改为只读 - 课程套餐:草稿/已驳回状态添加发布按钮 - 课程包:已发布状态不显示编辑按钮,编辑页增加状态校验 - 套餐创建:年级列从课程包读取,不可编辑 Made-with: Cursor
This commit is contained in:
parent
134cc6a075
commit
289fcbee52
@ -32,11 +32,12 @@
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="适用年级" name="gradeLevels">
|
||||
<a-checkbox-group v-model:value="formState.gradeLevels">
|
||||
<a-checkbox-group v-model:value="formState.gradeLevels" disabled>
|
||||
<a-checkbox value="小班">小班</a-checkbox>
|
||||
<a-checkbox value="中班">中班</a-checkbox>
|
||||
<a-checkbox value="大班">大班</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
<div v-if="formState.gradeLevels.length === 0" class="grade-hint">根据已选课程包自动填充</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-divider>课程包配置</a-divider>
|
||||
@ -55,11 +56,8 @@
|
||||
<a-input-number v-model:value="record.sortOrder" :min="0" size="small" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'gradeLevel'">
|
||||
<a-select v-model:value="record.gradeLevel" size="small" style="width: 100px">
|
||||
<a-select-option value="小班">小班</a-select-option>
|
||||
<a-select-option value="中班">中班</a-select-option>
|
||||
<a-select-option value="大班">大班</a-select-option>
|
||||
</a-select>
|
||||
<a-tag v-for="g in (record.gradeLevels || [])" :key="g">{{ g }}</a-tag>
|
||||
<span v-if="!record.gradeLevels?.length">-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-button type="link" size="small" danger @click="removePackage(index)">移除</a-button>
|
||||
@ -110,7 +108,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
@ -140,7 +138,7 @@ const formState = ref({
|
||||
gradeLevels: [] as string[],
|
||||
});
|
||||
|
||||
const selectedPackages = ref<{ packageId: number | string; gradeLevel: string; sortOrder: number; packageName: string }[]>([]);
|
||||
const selectedPackages = ref<{ packageId: number | string; gradeLevels: string[]; sortOrder: number; packageName: string }[]>([]);
|
||||
|
||||
const formRules = {
|
||||
name: [{ required: true, message: '请输入套餐名称' }],
|
||||
@ -149,7 +147,7 @@ const formRules = {
|
||||
|
||||
const packageColumns = [
|
||||
{ title: '课程包', dataIndex: 'packageName', key: 'packageName' },
|
||||
{ title: '年级', dataIndex: 'gradeLevel', key: 'gradeLevel', width: 120 },
|
||||
{ title: '年级', dataIndex: 'gradeLevels', key: 'gradeLevel', width: 120 },
|
||||
{ title: '排序', dataIndex: 'sortOrder', key: 'sortOrder', width: 80 },
|
||||
{ title: '操作', key: 'action', width: 80 },
|
||||
];
|
||||
@ -176,6 +174,15 @@ const parseGradeTags = (tags: string | string[]) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 从已选课程包同步适用年级到表单(年级列只读,从课程包数据读取)
|
||||
const syncGradeLevelsFromPackages = () => {
|
||||
const grades = [...new Set(selectedPackages.value.flatMap((p) => p.gradeLevels || []).filter(Boolean))];
|
||||
formState.value.gradeLevels = grades;
|
||||
};
|
||||
|
||||
// 监听已选课程包变化(添加、移除、修改年级),自动同步适用年级
|
||||
watch(() => selectedPackages.value, syncGradeLevelsFromPackages, { deep: true });
|
||||
|
||||
const fetchCollectionDetail = async () => {
|
||||
if (!isEdit.value) return;
|
||||
|
||||
@ -186,17 +193,13 @@ const fetchCollectionDetail = async () => {
|
||||
formState.value.price = (detail.price || 0) / 100; // 分转元
|
||||
formState.value.discountPrice = detail.discountPrice ? detail.discountPrice / 100 : null;
|
||||
formState.value.discountType = detail.discountType || null;
|
||||
// 解析年级,后端是逗号分隔字符串,前端是数组
|
||||
if (detail.gradeLevels) {
|
||||
formState.value.gradeLevels = detail.gradeLevels.split(',');
|
||||
}
|
||||
|
||||
// 加载已选课程包
|
||||
// 加载已选课程包(年级从课程包读取,不可编辑;适用年级自动同步)
|
||||
selectedPackages.value = (detail.packages || []).map((p: any) => ({
|
||||
packageId: p.id,
|
||||
packageName: p.name,
|
||||
gradeLevel: p.gradeLevels?.[0] || '小班',
|
||||
sortOrder: p.sortOrder,
|
||||
gradeLevels: Array.isArray(p.gradeLevels) ? p.gradeLevels : (p.gradeLevels ? [p.gradeLevels] : []),
|
||||
sortOrder: p.sortOrder ?? 0,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取套餐详情失败:', error);
|
||||
@ -221,12 +224,15 @@ const handleAddPackages = () => {
|
||||
const existingIds = new Set(selectedPackages.value.map((p) => p.packageId));
|
||||
const newPackages = availablePackages.value
|
||||
.filter((p) => selectedRowKeys.value.includes(p.id) && !existingIds.has(p.id))
|
||||
.map((p) => ({
|
||||
packageId: p.id,
|
||||
packageName: p.name,
|
||||
gradeLevel: parseGradeTags(p.gradeTags)?.[0] || '小班',
|
||||
sortOrder: selectedPackages.value.length,
|
||||
}));
|
||||
.map((p) => {
|
||||
const tags = parseGradeTags(p.gradeTags);
|
||||
return {
|
||||
packageId: p.id,
|
||||
packageName: p.name,
|
||||
gradeLevels: Array.isArray(tags) && tags.length > 0 ? tags : ['小班'],
|
||||
sortOrder: selectedPackages.value.length,
|
||||
};
|
||||
});
|
||||
|
||||
selectedPackages.value.push(...newPackages);
|
||||
selectedRowKeys.value = [];
|
||||
@ -268,12 +274,10 @@ const handleSubmit = async () => {
|
||||
id = res.id;
|
||||
}
|
||||
|
||||
// 保存课程包关联
|
||||
// 保存课程包关联(按 sortOrder 排序)
|
||||
if (selectedPackages.value.length > 0) {
|
||||
await setCollectionPackages(
|
||||
id,
|
||||
selectedPackages.value.map((p) => p.packageId),
|
||||
);
|
||||
const sorted = [...selectedPackages.value].sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
await setCollectionPackages(id, sorted.map((p) => p.packageId));
|
||||
}
|
||||
|
||||
message.success(isEdit.value ? '套餐更新成功' : '套餐创建成功');
|
||||
@ -308,4 +312,10 @@ onMounted(() => {
|
||||
.package-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grade-hint {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -57,6 +57,18 @@
|
||||
<template v-if="record.status === 'DRAFT'">
|
||||
<a-button type="link" size="small" @click="handleView(record)">查看</a-button>
|
||||
<a-button type="link" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-tooltip v-if="(record.packageCount || 0) === 0" title="请先添加至少一个课程包">
|
||||
<span>
|
||||
<a-button type="primary" size="small" disabled>发布</a-button>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-popconfirm
|
||||
v-else
|
||||
title="确定要发布此套餐吗?发布后学校端将可以查看。"
|
||||
@confirm="handlePublish(record)"
|
||||
>
|
||||
<a-button type="primary" size="small">发布</a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm title="确定要删除吗?" @confirm="handleDelete(record)">
|
||||
<a-button type="link" size="small" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
@ -72,6 +84,18 @@
|
||||
<template v-else-if="record.status === 'REJECTED'">
|
||||
<a-button type="link" size="small" @click="handleView(record)">查看</a-button>
|
||||
<a-button type="link" size="small" @click="handleEdit(record)">修改</a-button>
|
||||
<a-tooltip v-if="(record.packageCount || 0) === 0" title="请先添加至少一个课程包">
|
||||
<span>
|
||||
<a-button type="primary" size="small" disabled>发布</a-button>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-popconfirm
|
||||
v-else
|
||||
title="确定要发布此套餐吗?发布后学校端将可以查看。"
|
||||
@confirm="handlePublish(record)"
|
||||
>
|
||||
<a-button type="primary" size="small">发布</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
|
||||
<!-- 已通过状态 -->
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<a-button @click="editCourse">
|
||||
<a-button v-if="course.status !== 'PUBLISHED'" @click="editCourse">
|
||||
<EditOutlined /> 编辑
|
||||
</a-button>
|
||||
<a-button @click="viewStats">
|
||||
|
||||
@ -218,6 +218,13 @@ const fetchCourseDetail = async () => {
|
||||
const res = await getCourse(courseId.value) as any;
|
||||
const course = res.data || res;
|
||||
|
||||
// 已发布的课程包不允许编辑
|
||||
if (course?.status === 'PUBLISHED') {
|
||||
message.warning('已发布的课程包不允许编辑,请使用「迭代版本」创建新版本');
|
||||
router.push(`/admin/packages/${courseId.value}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 基本信息
|
||||
formData.basic.name = course.name;
|
||||
formData.basic.themeId = course.themeId;
|
||||
|
||||
@ -39,11 +39,12 @@
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="适用年级" name="gradeLevels">
|
||||
<a-select v-model:value="form.gradeLevels" mode="multiple" placeholder="请选择适用年级" style="width: 300px">
|
||||
<a-select-option value="小班">小班</a-select-option>
|
||||
<a-select-option value="中班">中班</a-select-option>
|
||||
<a-select-option value="大班">大班</a-select-option>
|
||||
</a-select>
|
||||
<a-checkbox-group v-model:value="form.gradeLevels" disabled>
|
||||
<a-checkbox value="小班">小班</a-checkbox>
|
||||
<a-checkbox value="中班">中班</a-checkbox>
|
||||
<a-checkbox value="大班">大班</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
<div v-if="form.gradeLevels.length === 0" class="grade-hint">根据已选课程包自动填充</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-divider>课程包配置</a-divider>
|
||||
@ -62,11 +63,8 @@
|
||||
<a-input-number v-model:value="record.sortOrder" :min="0" size="small" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'gradeLevel'">
|
||||
<a-select v-model:value="record.gradeLevel" size="small" style="width: 100px">
|
||||
<a-select-option value="小班">小班</a-select-option>
|
||||
<a-select-option value="中班">中班</a-select-option>
|
||||
<a-select-option value="大班">大班</a-select-option>
|
||||
</a-select>
|
||||
<a-tag v-for="g in (record.gradeLevels || [])" :key="g">{{ g }}</a-tag>
|
||||
<span v-if="!record.gradeLevels?.length">-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-button type="link" size="small" danger @click="removePackage(index)">移除</a-button>
|
||||
@ -115,7 +113,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
@ -145,18 +143,23 @@ const form = reactive({
|
||||
gradeLevels: [] as string[],
|
||||
});
|
||||
|
||||
const selectedPackages = ref<{ packageId: number | string; gradeLevel: string; sortOrder: number; packageName: string }[]>([]);
|
||||
const selectedPackages = ref<{ packageId: number | string; gradeLevels: string[]; sortOrder: number; packageName: string }[]>([]);
|
||||
|
||||
// 表单校验规则
|
||||
// 表单校验规则(适用年级由已选课程包自动填充)
|
||||
const formRules = {
|
||||
name: [{ required: true, message: '请输入套餐名称' }],
|
||||
price: [{ required: true, message: '请输入价格' }],
|
||||
gradeLevels: [{ required: true, message: '请选择适用年级' }],
|
||||
gradeLevels: [{
|
||||
validator: () => {
|
||||
if (selectedPackages.value.length === 0) return Promise.reject('请先添加至少一个课程包');
|
||||
return Promise.resolve();
|
||||
},
|
||||
}],
|
||||
};
|
||||
|
||||
const packageColumns = [
|
||||
{ title: '课程包', dataIndex: 'packageName', key: 'packageName' },
|
||||
{ title: '年级', dataIndex: 'gradeLevel', key: 'gradeLevel', width: 120 },
|
||||
{ title: '年级', dataIndex: 'gradeLevels', key: 'gradeLevel', width: 120 },
|
||||
{ title: '排序', dataIndex: 'sortOrder', key: 'sortOrder', width: 80 },
|
||||
{ title: '操作', key: 'action', width: 80 },
|
||||
];
|
||||
@ -174,7 +177,8 @@ const rowSelection = computed(() => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const parseGradeTags = (tags: string) => {
|
||||
const parseGradeTags = (tags: string | string[]) => {
|
||||
if (Array.isArray(tags)) return tags;
|
||||
try {
|
||||
return JSON.parse(tags || '[]');
|
||||
} catch {
|
||||
@ -182,6 +186,15 @@ const parseGradeTags = (tags: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 从已选课程包同步适用年级到表单(年级列只读,从课程包数据读取)
|
||||
const syncGradeLevelsFromPackages = () => {
|
||||
const grades = [...new Set(selectedPackages.value.flatMap((p) => p.gradeLevels || []).filter(Boolean))];
|
||||
form.gradeLevels = grades;
|
||||
};
|
||||
|
||||
// 监听已选课程包变化(添加、移除、修改年级),自动同步适用年级
|
||||
watch(() => selectedPackages.value, syncGradeLevelsFromPackages, { deep: true });
|
||||
|
||||
const fetchPackageDetail = async () => {
|
||||
if (!isEdit.value) return;
|
||||
|
||||
@ -192,13 +205,13 @@ const fetchPackageDetail = async () => {
|
||||
form.price = pkg.price / 100;
|
||||
form.discountPrice = pkg.discountPrice ? pkg.discountPrice / 100 : undefined;
|
||||
form.discountType = pkg.discountType;
|
||||
form.gradeLevels = JSON.parse(pkg.gradeLevels || '[]');
|
||||
|
||||
// 加载已选课程包(年级从课程包读取,不可编辑;适用年级自动同步)
|
||||
selectedPackages.value = (pkg.packages || []).map((p: any) => ({
|
||||
packageId: p.id,
|
||||
packageName: p.name,
|
||||
gradeLevel: p.gradeLevels?.[0] || '小班',
|
||||
sortOrder: p.sortOrder,
|
||||
gradeLevels: Array.isArray(p.gradeLevels) ? p.gradeLevels : (p.gradeLevels ? [p.gradeLevels] : []),
|
||||
sortOrder: p.sortOrder ?? 0,
|
||||
}));
|
||||
} catch (error) {
|
||||
message.error('获取套餐详情失败');
|
||||
@ -222,12 +235,15 @@ const handleAddPackages = () => {
|
||||
const existingIds = new Set(selectedPackages.value.map((p) => p.packageId));
|
||||
const newPackages = availablePackages.value
|
||||
.filter((p) => selectedRowKeys.value.includes(p.id) && !existingIds.has(p.id))
|
||||
.map((p) => ({
|
||||
packageId: p.id,
|
||||
packageName: p.name,
|
||||
gradeLevel: parseGradeTags(p.gradeTags)?.[0] || '小班',
|
||||
sortOrder: selectedPackages.value.length,
|
||||
}));
|
||||
.map((p) => {
|
||||
const tags = parseGradeTags(p.gradeTags);
|
||||
return {
|
||||
packageId: p.id,
|
||||
packageName: p.name,
|
||||
gradeLevels: Array.isArray(tags) && tags.length > 0 ? tags : ['小班'],
|
||||
sortOrder: selectedPackages.value.length,
|
||||
};
|
||||
});
|
||||
|
||||
selectedPackages.value.push(...newPackages);
|
||||
selectedRowKeys.value = [];
|
||||
@ -267,12 +283,10 @@ const handleSave = async () => {
|
||||
id = res.id; // 后端 Long 序列化为 string
|
||||
}
|
||||
|
||||
// 保存课程包关联
|
||||
// 保存课程包关联(按 sortOrder 排序)
|
||||
if (selectedPackages.value.length > 0) {
|
||||
await setCollectionPackages(
|
||||
id,
|
||||
selectedPackages.value.map((p) => p.packageId),
|
||||
);
|
||||
const sorted = [...selectedPackages.value].sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
await setCollectionPackages(id, sorted.map((p) => p.packageId));
|
||||
}
|
||||
|
||||
message.success('保存成功');
|
||||
@ -303,4 +317,10 @@ onMounted(() => {
|
||||
.package-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grade-hint {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -296,24 +296,24 @@ public class CoursePackageServiceImpl extends ServiceImpl<CoursePackageMapper, C
|
||||
entity.setIntroMethods(request.getIntroMethods());
|
||||
entity.setIntroEvaluation(request.getIntroEvaluation());
|
||||
entity.setIntroNotes(request.getIntroNotes());
|
||||
entity.setScheduleRefData(request.getScheduleRefData());
|
||||
entity.setScheduleRefData(emptyToNull(request.getScheduleRefData()));
|
||||
entity.setEnvironmentConstruction(request.getEnvironmentConstruction());
|
||||
entity.setThemeId(request.getThemeId());
|
||||
entity.setPictureBookName(request.getPictureBookName());
|
||||
entity.setEbookPaths(request.getEbookPaths());
|
||||
entity.setAudioPaths(request.getAudioPaths());
|
||||
entity.setVideoPaths(request.getVideoPaths());
|
||||
entity.setOtherResources(request.getOtherResources());
|
||||
entity.setEbookPaths(emptyToNull(request.getEbookPaths()));
|
||||
entity.setAudioPaths(emptyToNull(request.getAudioPaths()));
|
||||
entity.setVideoPaths(emptyToNull(request.getVideoPaths()));
|
||||
entity.setOtherResources(emptyToNull(request.getOtherResources()));
|
||||
entity.setPptPath(request.getPptPath());
|
||||
entity.setPptName(request.getPptName());
|
||||
entity.setPosterPaths(request.getPosterPaths());
|
||||
entity.setTools(request.getTools());
|
||||
entity.setStudentMaterials(request.getStudentMaterials());
|
||||
entity.setLessonPlanData(request.getLessonPlanData());
|
||||
entity.setActivitiesData(request.getActivitiesData());
|
||||
entity.setAssessmentData(request.getAssessmentData());
|
||||
entity.setGradeTags(request.getGradeTags());
|
||||
entity.setDomainTags(request.getDomainTags());
|
||||
entity.setLessonPlanData(emptyToNull(request.getLessonPlanData()));
|
||||
entity.setActivitiesData(emptyToNull(request.getActivitiesData()));
|
||||
entity.setAssessmentData(emptyToNull(request.getAssessmentData()));
|
||||
entity.setGradeTags(emptyToNull(request.getGradeTags()));
|
||||
entity.setDomainTags(emptyToNull(request.getDomainTags()));
|
||||
entity.setHasCollectiveLesson(request.getHasCollectiveLesson() != null && request.getHasCollectiveLesson() ? 1 : 0);
|
||||
return entity;
|
||||
}
|
||||
@ -339,27 +339,34 @@ public class CoursePackageServiceImpl extends ServiceImpl<CoursePackageMapper, C
|
||||
if (request.getIntroMethods() != null) entity.setIntroMethods(request.getIntroMethods());
|
||||
if (request.getIntroEvaluation() != null) entity.setIntroEvaluation(request.getIntroEvaluation());
|
||||
if (request.getIntroNotes() != null) entity.setIntroNotes(request.getIntroNotes());
|
||||
if (request.getScheduleRefData() != null) entity.setScheduleRefData(request.getScheduleRefData());
|
||||
if (request.getScheduleRefData() != null) entity.setScheduleRefData(emptyToNull(request.getScheduleRefData()));
|
||||
if (request.getEnvironmentConstruction() != null) entity.setEnvironmentConstruction(request.getEnvironmentConstruction());
|
||||
if (request.getThemeId() != null) entity.setThemeId(request.getThemeId());
|
||||
if (request.getPictureBookName() != null) entity.setPictureBookName(request.getPictureBookName());
|
||||
if (request.getEbookPaths() != null) entity.setEbookPaths(request.getEbookPaths());
|
||||
if (request.getAudioPaths() != null) entity.setAudioPaths(request.getAudioPaths());
|
||||
if (request.getVideoPaths() != null) entity.setVideoPaths(request.getVideoPaths());
|
||||
if (request.getOtherResources() != null) entity.setOtherResources(request.getOtherResources());
|
||||
if (request.getEbookPaths() != null) entity.setEbookPaths(emptyToNull(request.getEbookPaths()));
|
||||
if (request.getAudioPaths() != null) entity.setAudioPaths(emptyToNull(request.getAudioPaths()));
|
||||
if (request.getVideoPaths() != null) entity.setVideoPaths(emptyToNull(request.getVideoPaths()));
|
||||
if (request.getOtherResources() != null) entity.setOtherResources(emptyToNull(request.getOtherResources()));
|
||||
if (request.getPptPath() != null) entity.setPptPath(request.getPptPath());
|
||||
if (request.getPptName() != null) entity.setPptName(request.getPptName());
|
||||
if (request.getPosterPaths() != null) entity.setPosterPaths(request.getPosterPaths());
|
||||
if (request.getTools() != null) entity.setTools(request.getTools());
|
||||
if (request.getStudentMaterials() != null) entity.setStudentMaterials(request.getStudentMaterials());
|
||||
if (request.getLessonPlanData() != null) entity.setLessonPlanData(request.getLessonPlanData());
|
||||
if (request.getActivitiesData() != null) entity.setActivitiesData(request.getActivitiesData());
|
||||
if (request.getAssessmentData() != null) entity.setAssessmentData(request.getAssessmentData());
|
||||
if (request.getGradeTags() != null) entity.setGradeTags(request.getGradeTags());
|
||||
if (request.getDomainTags() != null) entity.setDomainTags(request.getDomainTags());
|
||||
if (request.getLessonPlanData() != null) entity.setLessonPlanData(emptyToNull(request.getLessonPlanData()));
|
||||
if (request.getActivitiesData() != null) entity.setActivitiesData(emptyToNull(request.getActivitiesData()));
|
||||
if (request.getAssessmentData() != null) entity.setAssessmentData(emptyToNull(request.getAssessmentData()));
|
||||
if (request.getGradeTags() != null) entity.setGradeTags(emptyToNull(request.getGradeTags()));
|
||||
if (request.getDomainTags() != null) entity.setDomainTags(emptyToNull(request.getDomainTags()));
|
||||
if (request.getHasCollectiveLesson() != null) entity.setHasCollectiveLesson(request.getHasCollectiveLesson() ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将空字符串转为 null,避免 MySQL JSON 列报错 "The document is empty"
|
||||
*/
|
||||
private static String emptyToNull(String value) {
|
||||
return StringUtils.hasText(value) ? value : null;
|
||||
}
|
||||
|
||||
private CourseLessonResponse toLessonResponse(CourseLesson lesson) {
|
||||
List<LessonStep> steps = courseLessonService.findSteps(lesson.getId());
|
||||
List<LessonStepResponse> stepResponses = steps.stream()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user