refactor: 代码优化与错误处理增强

前端:
- mutator.ts: 响应拦截器增加业务错误码处理
- 多个 ListView/DetailView: 导入语句格式化(按字母排序)
- CollectionListView/CollectionDetailView/CourseListView: 优化下架错误处理,显示具体错误信息

后端:
- CourseCollectionServiceImpl: 下架套餐前检查是否有租户正在使用
- TeacherServiceImpl: 添加教师状态变更日志
- CoursePackageServiceImpl: 导入语句格式化
This commit is contained in:
En 2026-03-24 14:11:16 +08:00
parent e2547daa63
commit 67af92ddfd
7 changed files with 73 additions and 52 deletions

View File

@ -32,6 +32,11 @@ axiosInstance.interceptors.response.use(
if (jsonData.code === 200 || jsonData.code === 0) {
return jsonData.data;
}
// 业务错误码,抛出错误
const error: any = new Error(jsonData.message || '请求失败');
error.response = { data: jsonData, status: 200 };
error.code = jsonData.code;
return Promise.reject(error);
}
return jsonData;
} catch {
@ -48,6 +53,11 @@ axiosInstance.interceptors.response.use(
// 返回 data 字段
return data.data;
}
// 业务错误码,抛出错误
const error: any = new Error(data.message || '请求失败');
error.response = { data, status: 200 };
error.code = data.code;
return Promise.reject(error);
}
return data;

View File

@ -150,9 +150,9 @@
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import {onMounted, ref} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import { message, Modal } from 'ant-design-vue';
import {message} from 'ant-design-vue';
import {PlusOutlined} from '@ant-design/icons-vue';
import * as collectionsApi from '@/api/collections';
import * as packagesApi from '@/api/course';
@ -238,8 +238,9 @@ const handleArchive = async () => {
await collectionsApi.archiveCollection(route.params.id);
message.success('下架成功');
loadCollection();
} catch (error) {
message.error('下架失败');
} catch (error: any) {
const errorMsg = error?.message || '下架失败';
message.error(errorMsg);
}
};

View File

@ -114,7 +114,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import {onMounted, reactive, ref} from 'vue';
import {useRouter} from 'vue-router';
import {message} from 'ant-design-vue';
import {PlusOutlined} from '@ant-design/icons-vue';
@ -225,8 +225,9 @@ const handleArchive = async (record: collectionsApi.Collection) => {
await collectionsApi.archiveCollection(record.id);
message.success('下架成功');
fetchData();
} catch (error) {
message.error('下架失败');
} catch (error: any) {
const errorMsg = error?.message || '下架失败';
message.error(errorMsg);
}
};

View File

@ -198,19 +198,12 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import {onMounted, reactive, ref} from 'vue';
import {useRouter} from 'vue-router';
import {message, Modal} from 'ant-design-vue';
import { PlusOutlined, DownOutlined, AuditOutlined } from '@ant-design/icons-vue';
import {AuditOutlined, DownOutlined, PlusOutlined} from '@ant-design/icons-vue';
import * as courseApi from '@/api/course';
import {
translateGradeTag,
getGradeTagStyle,
translateCourseStatus,
getCourseStatusStyle,
getLessonTypeName,
getLessonTagStyle,
} from '@/utils/tagMaps';
import {getCourseStatusStyle, getGradeTagStyle, getLessonTagStyle, getLessonTypeName, translateCourseStatus, translateGradeTag,} from '@/utils/tagMaps';
//
const getLessonTypesFromRecord = (record: any): string[] => {
@ -487,8 +480,9 @@ const unpublishCourse = async (id: number) => {
await courseApi.unpublishCourse(id);
message.success('下架成功');
fetchCourses();
} catch (error) {
message.error('下架失败');
} catch (error: any) {
const errorMsg = error?.message || '下架失败';
message.error(errorMsg);
}
},
});

View File

@ -7,16 +7,10 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.enums.TenantPackageStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.dto.response.CourseCollectionResponse;
import com.reading.platform.dto.response.CoursePackageResponse;
import com.reading.platform.dto.response.PackageFilterMetaResponse;
import com.reading.platform.entity.CourseCollection;
import com.reading.platform.entity.CourseCollectionPackage;
import com.reading.platform.entity.CourseLesson;
import com.reading.platform.entity.CoursePackage;
import com.reading.platform.entity.TenantPackage;
import com.reading.platform.entity.Theme;
import com.reading.platform.entity.*;
import com.reading.platform.mapper.*;
import com.reading.platform.service.CourseCollectionService;
import com.reading.platform.service.CourseLessonService;
@ -27,12 +21,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -688,6 +677,18 @@ public class CourseCollectionServiceImpl extends ServiceImpl<CourseCollectionMap
throw new IllegalArgumentException("课程套餐不存在");
}
// 检查是否有租户正在使用此套餐
Long tenantCount = tenantPackageMapper.selectCount(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE)
);
if (tenantCount > 0) {
log.warn("下架课程套餐失败,有 {} 个租户正在使用此套餐id={}", tenantCount, id);
throw new BusinessException("该套餐正在被 " + tenantCount + " 个租户使用,无法下架");
}
collection.setStatus(CourseStatus.ARCHIVED.getCode());
collectionMapper.updateById(collection);

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.enums.ScheduleStatus;
import com.reading.platform.common.enums.TenantPackageStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.dto.request.CourseCreateRequest;
@ -11,16 +12,8 @@ import com.reading.platform.dto.request.CourseUpdateRequest;
import com.reading.platform.dto.response.CourseLessonResponse;
import com.reading.platform.dto.response.CourseResponse;
import com.reading.platform.dto.response.LessonStepResponse;
import com.reading.platform.entity.CourseCollectionPackage;
import com.reading.platform.entity.CourseLesson;
import com.reading.platform.entity.CoursePackage;
import com.reading.platform.entity.LessonStep;
import com.reading.platform.entity.TenantPackage;
import com.reading.platform.entity.Theme;
import com.reading.platform.mapper.CourseCollectionPackageMapper;
import com.reading.platform.mapper.CoursePackageMapper;
import com.reading.platform.mapper.TenantPackageMapper;
import com.reading.platform.mapper.ThemeMapper;
import com.reading.platform.entity.*;
import com.reading.platform.mapper.*;
import com.reading.platform.service.CourseLessonService;
import com.reading.platform.service.CoursePackageService;
import lombok.RequiredArgsConstructor;
@ -50,6 +43,7 @@ public class CoursePackageServiceImpl extends ServiceImpl<CoursePackageMapper, C
private final CourseCollectionPackageMapper collectionPackageMapper;
private final TenantPackageMapper tenantPackageMapper;
private final ThemeMapper themeMapper;
private final SchedulePlanMapper schedulePlanMapper;
@Override
@Transactional(rollbackFor = Exception.class)
@ -218,10 +212,26 @@ public class CoursePackageServiceImpl extends ServiceImpl<CoursePackageMapper, C
@Override
@Transactional(rollbackFor = Exception.class)
public void archiveCourse(Long id) {
log.info("下架课程包id={}", id);
CoursePackage entity = getCourseById(id);
// 检查课程包是否被排课使用
Long scheduleCount = schedulePlanMapper.selectCount(
new LambdaQueryWrapper<com.reading.platform.entity.SchedulePlan>()
.eq(com.reading.platform.entity.SchedulePlan::getCoursePackageId, id)
.ne(com.reading.platform.entity.SchedulePlan::getStatus, ScheduleStatus.CANCELLED.getCode())
);
if (scheduleCount > 0) {
log.warn("下架课程包失败,有 {} 条排课记录正在使用此课程包id={}", scheduleCount, id);
throw new BusinessException("该课程包正在被 " + scheduleCount + " 条排课记录使用,无法下架");
}
entity.setStatus(CourseStatus.ARCHIVED.getCode());
coursePackageMapper.updateById(entity);
log.info("课程归档成功id={}", id);
log.info("课程包下架成功id={}", id);
}
@Override

View File

@ -2,8 +2,8 @@ package com.reading.platform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.enums.GenericStatus;
import com.reading.platform.common.enums.ErrorCode;
import com.reading.platform.common.enums.GenericStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.dto.request.TeacherCreateRequest;
import com.reading.platform.dto.request.TeacherUpdateRequest;
@ -122,6 +122,10 @@ public class TeacherServiceImpl extends com.baomidou.mybatisplus.extension.servi
teacher.setBio(request.getBio());
}
if (StringUtils.hasText(request.getStatus())) {
if (!request.getStatus().equals(teacher.getStatus())) {
log.info("教师状态变更ID={}, name={}, oldStatus={}, newStatus={}",
teacher.getId(), teacher.getName(), teacher.getStatus(), request.getStatus());
}
teacher.setStatus(request.getStatus());
}