fix: 课程包管理审核页面优化
- 修复 list/items 数据格式转换,解决暂无数据问题
- 新增课程驳回接口 POST /api/v1/admin/courses/{id}/reject
- 审核页仅返回待审核和已驳回(reviewOnly),排除已通过
- 修复状态筛选、自动检查列逻辑
- 后端支持 status 和 reviewOnly 参数
Made-with: Cursor
This commit is contained in:
parent
3fba64958c
commit
691e0248a2
@ -1,4 +1,5 @@
|
||||
import { getReadingPlatformAPI } from './generated';
|
||||
import { axios } from './generated/mutator';
|
||||
|
||||
// 创建 API 实例
|
||||
const api = getReadingPlatformAPI();
|
||||
@ -11,6 +12,8 @@ export interface CourseQueryParams {
|
||||
grade?: string;
|
||||
status?: string;
|
||||
keyword?: string;
|
||||
/** 审核管理页专用:仅返回待审核和已驳回,排除已通过 */
|
||||
reviewOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface Course {
|
||||
@ -110,12 +113,30 @@ export interface ValidationWarning {
|
||||
|
||||
// 转换查询参数类型
|
||||
const toFindAllParams = (params: CourseQueryParams): any => ({
|
||||
pageNum: params.pageNum ?? 1, // 默认值为 1
|
||||
pageSize: params.pageSize ?? 10, // 默认值为 10
|
||||
pageNum: params.pageNum ?? 1,
|
||||
pageSize: params.pageSize ?? 10,
|
||||
keyword: params.keyword,
|
||||
category: params.grade, // grade 映射到 category
|
||||
category: params.grade,
|
||||
status: params.status || undefined,
|
||||
reviewOnly: params.reviewOnly,
|
||||
});
|
||||
|
||||
// 后端 PageResult 返回 list,前端统一转为 items
|
||||
function normalizePageResult(raw: any): {
|
||||
items: Course[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
} {
|
||||
const list = raw?.list ?? raw?.items ?? [];
|
||||
return {
|
||||
items: Array.isArray(list) ? list : [],
|
||||
total: raw?.total ?? 0,
|
||||
page: raw?.pageNum ?? raw?.page ?? 1,
|
||||
pageSize: raw?.pageSize ?? 10,
|
||||
};
|
||||
}
|
||||
|
||||
// 获取课程包列表
|
||||
export function getCourses(params: CourseQueryParams): Promise<{
|
||||
items: Course[];
|
||||
@ -123,18 +144,17 @@ export function getCourses(params: CourseQueryParams): Promise<{
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}> {
|
||||
return api.getCoursePage1(toFindAllParams(params)) as any;
|
||||
return api.getCoursePage1(toFindAllParams(params)).then(normalizePageResult) as any;
|
||||
}
|
||||
|
||||
// 获取审核列表 (使用相同的列表接口,前端过滤状态)
|
||||
// 获取审核列表(仅返回待审核和已驳回,不含已通过)
|
||||
export function getReviewList(params: CourseQueryParams): Promise<{
|
||||
items: Course[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}> {
|
||||
// 注意:后端可能没有单独的审核列表接口,这里返回所有数据让前端过滤
|
||||
return api.getCoursePage1(toFindAllParams(params)) as any;
|
||||
return api.getCoursePage1(toFindAllParams({ ...params, reviewOnly: true })).then(normalizePageResult) as any;
|
||||
}
|
||||
|
||||
// 获取课程包详情
|
||||
@ -177,9 +197,15 @@ export function approveCourse(id: number, data: { checklist?: any; comment?: str
|
||||
return api.publishCourse(id) as any;
|
||||
}
|
||||
|
||||
// 审核驳回
|
||||
// 审核驳回(课程专用,调用 POST /api/v1/admin/courses/{id}/reject)
|
||||
export function rejectCourse(id: number, data: { checklist?: any; comment: string }): Promise<any> {
|
||||
return api.review(id, data) as any;
|
||||
return axios.post(`/api/v1/admin/courses/${id}/reject`, { comment: data.comment }).then((res: any) => {
|
||||
const body = res?.data;
|
||||
if (body && typeof body === 'object' && 'code' in body && body.code !== 200 && body.code !== 0) {
|
||||
throw new Error(body.message || '驳回失败');
|
||||
}
|
||||
return body?.data;
|
||||
});
|
||||
}
|
||||
|
||||
// 直接发布(超级管理员)
|
||||
|
||||
@ -4,25 +4,15 @@
|
||||
<a-row :gutter="16" align="middle">
|
||||
<a-col :span="16">
|
||||
<a-space>
|
||||
<a-select
|
||||
v-model:value="filters.grade"
|
||||
placeholder="年级"
|
||||
style="width: 120px"
|
||||
allow-clear
|
||||
@change="fetchCourses"
|
||||
>
|
||||
<a-select v-model:value="filters.grade" placeholder="年级" style="width: 120px" allow-clear
|
||||
@change="fetchCourses">
|
||||
<a-select-option value="small">小班</a-select-option>
|
||||
<a-select-option value="middle">中班</a-select-option>
|
||||
<a-select-option value="big">大班</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select
|
||||
v-model:value="filters.status"
|
||||
placeholder="状态"
|
||||
style="width: 120px"
|
||||
allow-clear
|
||||
@change="fetchCourses"
|
||||
>
|
||||
<a-select v-model:value="filters.status" placeholder="状态" style="width: 120px" allow-clear
|
||||
@change="fetchCourses">
|
||||
<a-select-option value="DRAFT">草稿</a-select-option>
|
||||
<a-select-option value="PENDING">审核中</a-select-option>
|
||||
<a-select-option value="REJECTED">已驳回</a-select-option>
|
||||
@ -30,13 +20,8 @@
|
||||
<a-select-option value="ARCHIVED">已下架</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-input-search
|
||||
v-model:value="filters.keyword"
|
||||
placeholder="搜索课程包名称"
|
||||
style="width: 250px"
|
||||
@search="fetchCourses"
|
||||
@blur="fetchCourses"
|
||||
/>
|
||||
<a-input-search v-model:value="filters.keyword" placeholder="搜索课程包名称" style="width: 250px"
|
||||
@search="fetchCourses" @blur="fetchCourses" />
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col :span="8" style="text-align: right;">
|
||||
@ -53,25 +38,15 @@
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="courses"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<a-table :columns="columns" :data-source="courses" :loading="loading" :pagination="pagination"
|
||||
@change="handleTableChange" row-key="id">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<div class="course-name">
|
||||
<a @click="viewCourse(record.id)">{{ record.name }}</a>
|
||||
<div class="course-tags">
|
||||
<a-tag
|
||||
v-for="grade in parseGradeTags(record.gradeTags)"
|
||||
:key="grade"
|
||||
size="small"
|
||||
:style="getGradeTagStyle(grade)"
|
||||
>
|
||||
<a-tag v-for="grade in parseGradeTags(record.gradeTags)" :key="grade" size="small"
|
||||
:style="getGradeTagStyle(grade)">
|
||||
{{ grade }}
|
||||
</a-tag>
|
||||
</div>
|
||||
@ -111,7 +86,8 @@
|
||||
<a-button size="small" @click="editCourse(record.id)">编辑</a-button>
|
||||
<a-dropdown>
|
||||
<a-button type="primary" size="small">
|
||||
发布 <DownOutlined />
|
||||
发布
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="({ key }) => handlePublishAction(String(key), record)">
|
||||
@ -120,10 +96,7 @@
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-popconfirm
|
||||
title="确定删除此课程包吗?"
|
||||
@confirm="deleteCourseHandler(record.id)"
|
||||
>
|
||||
<a-popconfirm title="确定删除此课程包吗?" @confirm="deleteCourseHandler(record.id)">
|
||||
<a-button size="small" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
@ -150,7 +123,8 @@
|
||||
<a-button size="small" @click="viewStats(record.id)">数据</a-button>
|
||||
<a-dropdown>
|
||||
<a-button size="small">
|
||||
更多 <DownOutlined />
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="({ key }) => handleMoreAction(String(key), record)">
|
||||
@ -166,10 +140,7 @@
|
||||
<a-button size="small" @click="viewCourse(record.id)">查看</a-button>
|
||||
<a-button size="small" @click="viewStats(record.id)">数据</a-button>
|
||||
<a-button type="primary" size="small" @click="republishCourse(record.id)">重新发布</a-button>
|
||||
<a-popconfirm
|
||||
title="确定删除此课程包吗?删除后无法恢复"
|
||||
@confirm="deleteCourseHandler(record.id)"
|
||||
>
|
||||
<a-popconfirm title="确定删除此课程包吗?删除后无法恢复" @confirm="deleteCourseHandler(record.id)">
|
||||
<a-button size="small" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
@ -179,18 +150,9 @@
|
||||
</a-table>
|
||||
|
||||
<!-- 提交审核弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="submitModalVisible"
|
||||
title="提交审核"
|
||||
:confirmLoading="submitting"
|
||||
@ok="confirmSubmit"
|
||||
>
|
||||
<a-alert
|
||||
v-if="validationResult && !validationResult.valid"
|
||||
type="error"
|
||||
:message="`发现 ${validationResult.errors.length} 个问题需要修复`"
|
||||
style="margin-bottom: 16px;"
|
||||
>
|
||||
<a-modal v-model:open="submitModalVisible" title="提交审核" :confirmLoading="submitting" @ok="confirmSubmit">
|
||||
<a-alert v-if="validationResult && !validationResult.valid" type="error"
|
||||
:message="`发现 ${validationResult.errors.length} 个问题需要修复`" style="margin-bottom: 16px;">
|
||||
<template #description>
|
||||
<ul style="margin: 0; padding-left: 20px;">
|
||||
<li v-for="error in validationResult?.errors" :key="error.code">
|
||||
@ -200,12 +162,8 @@
|
||||
</template>
|
||||
</a-alert>
|
||||
|
||||
<a-alert
|
||||
v-if="validationResult && validationResult.warnings.length > 0"
|
||||
type="warning"
|
||||
:message="`有 ${validationResult.warnings.length} 条建议`"
|
||||
style="margin-bottom: 16px;"
|
||||
>
|
||||
<a-alert v-if="validationResult && validationResult.warnings.length > 0" type="warning"
|
||||
:message="`有 ${validationResult.warnings.length} 条建议`" style="margin-bottom: 16px;">
|
||||
<template #description>
|
||||
<ul style="margin: 0; padding-left: 20px;">
|
||||
<li v-for="warning in validationResult?.warnings" :key="warning.code">
|
||||
@ -290,8 +248,8 @@ const fetchCourses = async () => {
|
||||
});
|
||||
console.log('📡 课程列表 API 响应:', data);
|
||||
|
||||
// 后端返回 PageResult 格式:{ list, total, pageNum, pageSize, pages }
|
||||
courses.value = (data as any).list || [];
|
||||
// API 已统一返回 { items, total, page, pageSize }
|
||||
courses.value = (data as any).items || [];
|
||||
pagination.total = (data as any).total || 0;
|
||||
console.log('📊 课程列表数据:', courses.value, '总数:', pagination.total);
|
||||
} catch (error) {
|
||||
@ -303,7 +261,7 @@ const fetchCourses = async () => {
|
||||
|
||||
const fetchPendingCount = async () => {
|
||||
try {
|
||||
const data = await courseApi.getReviewList({ pageSize: 1 });
|
||||
const data = await courseApi.getReviewList({ pageSize: 1, status: 'PENDING', });
|
||||
pendingCount.value = data.total || 0;
|
||||
} catch (error) {
|
||||
console.error('获取待审核数量失败:', error);
|
||||
|
||||
@ -1,20 +1,10 @@
|
||||
<template>
|
||||
<div class="course-review">
|
||||
<div class="page-header">
|
||||
<a-page-header
|
||||
title="审核管理"
|
||||
sub-title="审核待发布的课程包"
|
||||
@back="$router.back()"
|
||||
>
|
||||
<a-page-header title="审核管理" sub-title="审核待发布的课程包" @back="$router.back()">
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-select
|
||||
v-model:value="filters.status"
|
||||
placeholder="状态筛选"
|
||||
style="width: 120px"
|
||||
allow-clear
|
||||
@change="fetchCourses"
|
||||
>
|
||||
<a-select v-model:value="filters.status" placeholder="全部状态" style="width: 120px" @change="fetchCourses">
|
||||
<a-select-option value="PENDING">待审核</a-select-option>
|
||||
<a-select-option value="REJECTED">已驳回</a-select-option>
|
||||
</a-select>
|
||||
@ -26,25 +16,15 @@
|
||||
</a-page-header>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="courses"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<a-table :columns="columns" :data-source="courses" :loading="loading" :pagination="pagination"
|
||||
@change="handleTableChange" row-key="id">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<div class="course-name">
|
||||
<a @click="showReviewModal(record)">{{ record.name }}</a>
|
||||
<div class="course-tags">
|
||||
<a-tag
|
||||
v-for="grade in parseGradeTags(record.gradeTags)"
|
||||
:key="grade"
|
||||
size="small"
|
||||
:style="getGradeTagStyle(grade)"
|
||||
>
|
||||
<a-tag v-for="grade in parseGradeTags(record.gradeTags)" :key="grade" size="small"
|
||||
:style="getGradeTagStyle(grade)">
|
||||
{{ grade }}
|
||||
</a-tag>
|
||||
</div>
|
||||
@ -52,8 +32,8 @@
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 'PENDING' ? 'processing' : 'error'">
|
||||
{{ record.status === 'PENDING' ? '待审核' : '已驳回' }}
|
||||
<a-tag :color="record.status === 'PENDING' || record.status === 'pending' ? 'processing' : 'error'">
|
||||
{{ record.status === 'PENDING' || record.status === 'pending' ? '待审核' : '已驳回' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
@ -63,7 +43,10 @@
|
||||
|
||||
<template v-else-if="column.key === 'autoCheck'">
|
||||
<a-space>
|
||||
<a-tag v-if="record.validationPassed" color="success">
|
||||
<a-tag v-if="record.status === 'REJECTED'" color="default">
|
||||
已驳回
|
||||
</a-tag>
|
||||
<a-tag v-else-if="record.validationPassed" color="success">
|
||||
<CheckOutlined /> 通过
|
||||
</a-tag>
|
||||
<a-tag v-else color="warning">
|
||||
@ -86,14 +69,8 @@
|
||||
</a-table>
|
||||
|
||||
<!-- 审核弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="reviewModalVisible"
|
||||
:title="`审核: ${currentCourse?.name || ''}`"
|
||||
width="800px"
|
||||
:confirmLoading="reviewing"
|
||||
@ok="submitReview"
|
||||
@cancel="closeReviewModal"
|
||||
>
|
||||
<a-modal v-model:open="reviewModalVisible" :title="`审核: ${currentCourse?.name || ''}`" width="800px"
|
||||
:confirmLoading="reviewing" @ok="submitReview" @cancel="closeReviewModal">
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button @click="closeReviewModal">取消</a-button>
|
||||
@ -109,12 +86,8 @@
|
||||
<a-spin :spinning="loadingDetail">
|
||||
<div v-if="currentCourse" class="review-content">
|
||||
<!-- 自动检查项 -->
|
||||
<a-alert
|
||||
v-if="validationResult"
|
||||
:type="validationResult.valid ? 'success' : 'warning'"
|
||||
:message="validationResult.valid ? '自动检查通过' : '自动检查有警告'"
|
||||
style="margin-bottom: 16px;"
|
||||
>
|
||||
<a-alert v-if="validationResult" :type="validationResult.valid ? 'success' : 'warning'"
|
||||
:message="validationResult.valid ? '自动检查通过' : '自动检查有警告'" style="margin-bottom: 16px;">
|
||||
<template #description>
|
||||
<div v-if="validationResult.errors.length > 0">
|
||||
<strong>错误:</strong>
|
||||
@ -168,11 +141,8 @@
|
||||
<!-- 审核意见 -->
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="审核意见" required>
|
||||
<a-textarea
|
||||
v-model:value="reviewComment"
|
||||
placeholder="请输入审核意见(驳回时必填,通过时可选)"
|
||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||
/>
|
||||
<a-textarea v-model:value="reviewComment" placeholder="请输入审核意见(驳回时必填,通过时可选)"
|
||||
:auto-size="{ minRows: 3, maxRows: 6 }" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
@ -180,11 +150,7 @@
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看驳回原因弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="rejectReasonVisible"
|
||||
title="驳回原因"
|
||||
:footer="null"
|
||||
>
|
||||
<a-modal v-model:open="rejectReasonVisible" title="驳回原因" :footer="null">
|
||||
<a-alert type="error" :message="rejectReasonCourse?.reviewComment" show-icon />
|
||||
</a-modal>
|
||||
</div>
|
||||
@ -204,8 +170,8 @@ const loading = ref(false);
|
||||
const loadingDetail = ref(false);
|
||||
const courses = ref<any[]>([]);
|
||||
|
||||
const filters = reactive({
|
||||
status: 'PENDING' as string | undefined,
|
||||
const filters = reactive<{ status?: string }>({
|
||||
status: 'PENDING', // 默认只显示待审核
|
||||
});
|
||||
|
||||
const pagination = reactive({
|
||||
@ -249,7 +215,8 @@ const fetchCourses = async () => {
|
||||
|
||||
courses.value = (data.items || []).map((item: any) => ({
|
||||
...item,
|
||||
validationPassed: true, // 能提交审核的都已通过基本验证
|
||||
// 已驳回的课程不显示「通过」,待审核的默认通过(能提交的已过基本验证)
|
||||
validationPassed: item.status !== 'REJECTED',
|
||||
}));
|
||||
pagination.total = data.total || 0;
|
||||
} catch (error) {
|
||||
|
||||
@ -52,9 +52,11 @@ public class AdminCourseController {
|
||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String category) {
|
||||
log.info("查询课程列表,pageNum={}, pageSize={}, keyword={}, category={}", pageNum, pageSize, keyword, category);
|
||||
Page<Course> page = courseService.getSystemCoursePage(pageNum, pageSize, keyword, category);
|
||||
@RequestParam(required = false) String category,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false, defaultValue = "false") Boolean reviewOnly) {
|
||||
log.info("查询课程列表,pageNum={}, pageSize={}, keyword={}, category={}, status={}, reviewOnly={}", pageNum, pageSize, keyword, category, status, reviewOnly);
|
||||
Page<Course> page = courseService.getSystemCoursePage(pageNum, pageSize, keyword, category, status, Boolean.TRUE.equals(reviewOnly));
|
||||
PageResult<Course> result = PageResult.of(page);
|
||||
log.info("课程列表查询结果,total={}, list={}", result.getTotal(), result.getList().size());
|
||||
return Result.success(result);
|
||||
@ -81,4 +83,12 @@ public class AdminCourseController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "Reject course (审核驳回)")
|
||||
@PostMapping("/{id}/reject")
|
||||
public Result<Void> rejectCourse(@PathVariable Long id, @RequestBody java.util.Map<String, String> body) {
|
||||
String comment = body != null ? body.get("comment") : null;
|
||||
courseService.rejectCourse(id, comment != null ? comment : "已驳回");
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ public interface CourseService extends com.baomidou.mybatisplus.extension.servic
|
||||
|
||||
Page<Course> getCoursePage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String category, String status);
|
||||
|
||||
Page<Course> getSystemCoursePage(Integer pageNum, Integer pageSize, String keyword, String category);
|
||||
Page<Course> getSystemCoursePage(Integer pageNum, Integer pageSize, String keyword, String category, String status, boolean reviewOnly);
|
||||
|
||||
void deleteCourse(Long id);
|
||||
|
||||
@ -36,6 +36,11 @@ public interface CourseService extends com.baomidou.mybatisplus.extension.servic
|
||||
|
||||
void archiveCourse(Long id);
|
||||
|
||||
/**
|
||||
* 审核驳回课程
|
||||
*/
|
||||
void rejectCourse(Long id, String comment);
|
||||
|
||||
List<Course> getCoursesByTenantId(Long tenantId);
|
||||
|
||||
}
|
||||
|
||||
@ -345,14 +345,19 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Course> getSystemCoursePage(Integer pageNum, Integer pageSize, String keyword, String category) {
|
||||
log.info("查询系统课程列表,pageNum={}, pageSize={}, keyword={}, category={}", pageNum, pageSize, keyword, category);
|
||||
public Page<Course> getSystemCoursePage(Integer pageNum, Integer pageSize, String keyword, String category, String status, boolean reviewOnly) {
|
||||
log.info("查询系统课程列表,pageNum={}, pageSize={}, keyword={}, category={}, status={}, reviewOnly={}", pageNum, pageSize, keyword, category, status, reviewOnly);
|
||||
Page<Course> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<Course> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 只过滤系统课程,不过滤状态(超管需要看到所有状态的课程,包括草稿)
|
||||
// 只过滤系统课程
|
||||
wrapper.eq(Course::getIsSystem, 1);
|
||||
|
||||
// 审核管理页:仅返回待审核和已驳回,排除已通过/已发布
|
||||
if (reviewOnly) {
|
||||
wrapper.in(Course::getStatus, List.of("PENDING", "pending", "REJECTED", "rejected"));
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
wrapper.and(w -> w
|
||||
.like(Course::getName, keyword)
|
||||
@ -363,6 +368,9 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
||||
if (StringUtils.hasText(category)) {
|
||||
wrapper.eq(Course::getCategory, category);
|
||||
}
|
||||
if (StringUtils.hasText(status)) {
|
||||
wrapper.eq(Course::getStatus, status);
|
||||
}
|
||||
wrapper.orderByDesc(Course::getCreatedAt);
|
||||
|
||||
Page<Course> result = courseMapper.selectPage(page, wrapper);
|
||||
@ -397,6 +405,21 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
||||
log.info("课程归档成功:id={}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void rejectCourse(Long id, String comment) {
|
||||
Course course = getCourseById(id);
|
||||
if (!"PENDING".equalsIgnoreCase(course.getStatus()) && !"pending".equals(course.getStatus())) {
|
||||
log.warn("课程状态非待审核,无法驳回:id={}, status={}", id, course.getStatus());
|
||||
}
|
||||
course.setStatus("REJECTED");
|
||||
course.setReviewComment(comment);
|
||||
course.setReviewedAt(LocalDateTime.now());
|
||||
course.setReviewedBy(1L); // TODO: 从 SecurityUtils 获取当前用户
|
||||
courseMapper.updateById(course);
|
||||
log.info("课程驳回成功:id={}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Course> getCoursesByTenantId(Long tenantId) {
|
||||
return courseMapper.selectList(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user