feat(db): 添加 V40 迁移脚本删除 tenant_package 表的 package_id 字段

- 创建 V40__drop_tenant_package_package_id.sql 迁移脚本
- 安全删除 tenant_package 表中废弃的 package_id/packageId 列
- 该字段已在 V31 中尝试删除,V40 用于修复执行失败的情况
- TenantPackage 实体已使用 collectionId 字段替代

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
En 2026-03-19 12:34:00 +08:00
parent 134cc6a075
commit 9a9caab60b
21 changed files with 421 additions and 450 deletions

View File

@ -1,7 +1,8 @@
{
"permissions": {
"allow": [
"Bash(mvn compile:*)"
"Bash(mvn compile:*)",
"Bash(sed:*)"
]
}
}

View File

@ -12973,109 +12973,6 @@
}
}
},
"/api/v1/school/packages/{id}/renew": {
"post": {
"tags": [
"学校端 - 课程套餐"
],
"summary": "续费套餐(已废弃,请使用课程套餐续费)",
"operationId": "renewPackage",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RenewRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"405": {
"description": "Method Not Allowed",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"500": {
"description": "Internal Server Error",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
}
},
"deprecated": true
}
},
"/api/v1/school/packages/{collectionId}/renew": {
"post": {
"tags": [
@ -21526,88 +21423,6 @@
}
}
},
"/api/v1/school/packages/legacy": {
"get": {
"tags": [
"学校端 - 课程套餐"
],
"summary": "查询租户套餐旧版API已废弃",
"operationId": "findTenantPackages",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultListCoursePackageResponse"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"405": {
"description": "Method Not Allowed",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"500": {
"description": "Internal Server Error",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
}
},
"deprecated": true
}
},
"/api/v1/school/operation-logs": {
"get": {
"tags": [
@ -24888,6 +24703,87 @@
}
}
},
"/api/v1/admin/packages/all": {
"get": {
"tags": [
"超管端 - 课程包管理"
],
"summary": "获取所有已发布的课程包",
"operationId": "getAllPublishedCourses",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultListCourseResponse"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"405": {
"description": "Method Not Allowed",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"500": {
"description": "Internal Server Error",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
}
}
}
},
"/api/v1/admin/courses/{courseId}/lessons/type/{lessonType}": {
"get": {
"tags": [
@ -24988,6 +24884,87 @@
}
}
},
"/api/v1/admin/collections/all": {
"get": {
"tags": [
"超管端 - 课程套餐管理"
],
"summary": "获取所有已发布的课程套餐",
"operationId": "getAllPublishedCollections",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultListCourseCollectionResponse"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"405": {
"description": "Method Not Allowed",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
},
"500": {
"description": "Internal Server Error",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ResultVoid"
}
}
}
}
}
}
},
"/api/v1/files/delete": {
"delete": {
"tags": [
@ -26759,24 +26736,6 @@
"type": "string",
"description": "结束日期",
"format": "date"
},
"expireAt": {
"type": "string",
"description": "过期时间(兼容旧字段)",
"format": "date-time",
"deprecated": true
},
"maxStudents": {
"type": "integer",
"description": "最大学生数(兼容旧字段)",
"format": "int32",
"deprecated": true
},
"maxTeachers": {
"type": "integer",
"description": "最大教师数(兼容旧字段)",
"format": "int32",
"deprecated": true
}
},
"description": "租户更新请求"
@ -29207,24 +29166,6 @@
"type": "integer",
"description": "课程套餐 ID可选",
"format": "int64"
},
"expireAt": {
"type": "string",
"description": "过期时间(兼容旧字段)",
"format": "date-time",
"deprecated": true
},
"maxStudents": {
"type": "integer",
"description": "最大学生数(兼容旧字段)",
"format": "int32",
"deprecated": true
},
"maxTeachers": {
"type": "integer",
"description": "最大教师数(兼容旧字段)",
"format": "int32",
"deprecated": true
}
},
"description": "租户创建请求"

View File

@ -2022,23 +2022,6 @@ const resetPassword1 = (
);
}
/**
* @deprecated
* @summary 使
*/
const renewPackage = (
id: number,
renewRequest: RenewRequest,
) => {
return customMutator<Blob>(
{url: `/v1/school/packages/${id}/renew`, method: 'POST',
headers: {'Content-Type': 'application/json', },
data: renewRequest,
responseType: 'blob'
},
);
}
/**
* @summary
*/
@ -3286,20 +3269,6 @@ const getPackageUsage = (
);
}
/**
* @deprecated
* @summary API
*/
const findTenantPackages = (
) => {
return customMutator<Blob>(
{url: `/v1/school/packages/legacy`, method: 'GET',
responseType: 'blob'
},
);
}
/**
* @summary
*/
@ -3776,6 +3745,19 @@ const getStats1 = (
);
}
/**
* @summary
*/
const getAllPublishedCourses = (
) => {
return customMutator<Blob>(
{url: `/v1/admin/packages/all`, method: 'GET',
responseType: 'blob'
},
);
}
/**
* @summary
*/
@ -3790,6 +3772,19 @@ const findByType = (
);
}
/**
* @summary
*/
const getAllPublishedCollections = (
) => {
return customMutator<Blob>(
{url: `/v1/admin/collections/all`, method: 'GET',
responseType: 'blob'
},
);
}
/**
* @summary
*/
@ -3805,7 +3800,7 @@ const deleteFile = (
);
}
return {getTask,updateTask,deleteTask,getTemplate,updateTemplate,deleteTemplate,getSchedule,updateSchedule,cancelSchedule,getLesson,updateLesson,getLessonProgress,saveLessonProgress,getGrowthRecord,updateGrowthRecord,deleteGrowthRecord,getTeacher,updateTeacher,deleteTeacher,getTask1,updateTask1,deleteTask1,getTemplate1,updateTemplate1,deleteTemplate1,getStudent,updateStudent,deleteStudent,getSettings,updateSettings,getSecuritySettings,updateSecuritySettings,getNotificationSettings,updateNotificationSettings,getBasicSettings,updateBasicSettings,getSchedule1,updateSchedule1,cancelSchedule1,getParent,updateParent,deleteParent,getGrowthRecord1,updateGrowthRecord1,deleteGrowthRecord1,getClass,updateClass,deleteClass,updateClassTeacher,removeClassTeacher,getGrowthRecord2,updateGrowthRecord2,deleteGrowthRecord2,findOne,update,_delete,reorder,getTenant,updateTenant,deleteTenant,updateTenantStatus,updateTenantQuota,getAllSettings,updateSettings1,getStorageSettings,updateStorageSettings,getSecuritySettings1,updateSecuritySettings1,getNotificationSettings1,updateNotificationSettings1,getBasicSettings1,updateBasicSettings1,findLibrary,updateLibrary,deleteLibrary,findItem,updateItem,deleteItem,getCourse1,updateCourse,deleteCourse,reorderSteps,findOne1,update1,delete1,updateStep,removeStep,reorder1,findOne2,update2,delete2,setPackages,getTaskPage,createTask,getTemplates,createTemplate,createFromTemplate,getSchedules,createSchedule,markAsRead,markAllAsRead,getMyLessons,createLesson,saveStudentRecord,batchSaveStudentRecords,startLesson,getLessonFeedback,submitFeedback,completeLesson,cancelLesson,createLessonFromSchedule,startLessonFromSchedule,getGrowthRecordPage,createGrowthRecord,getTeacherPage,createTeacher,resetPassword,getTaskPage1,createTask1,getTemplates1,createTemplate1,getStudentPage,createStudent,getSchedules1,createSchedule1,checkConflict,batchCreateSchedules,createSchedulesByClasses,getParentPage,createParent,bindStudent,unbindStudent,resetPassword1,renewPackage,renewCollection,getGrowthRecordPage1,createGrowthRecord1,getClassPage,createClass,getClassTeachers1,assignTeachers,getClassStudents1,assignStudents,completeTask,markAsRead1,markAllAsRead1,createGrowthRecord2,refreshToken,uploadFile,refreshToken1,logout,login,changePassword,findAll,create,getTenantPage,createTenant,resetTenantPassword,findAllLibraries,createLibrary,findAllItems,createItem,batchDeleteItems,getCoursePage1,createCourse,submitCourse,rejectCourse,publishCourse,archiveCourse,findAll1,create1,findSteps,createStep,page,create2,withdraw,submit,republish,reject,publish,archive,getWeeklyStats,getTodayLessons,getDefaultTemplate,getAllStudents,getTodaySchedules,getTimetable,getRecommendedCourses,getMyNotifications,getNotification,getUnreadCount,getStudentRecords,getTodayLessons1,getLessonTrend,getFeedbacks,getFeedbackStats,getDashboard,getCoursePage,getCourse,getAllCourses,getCourseUsage,getClasses,getClassTeachers,getClassStudents,getDefaultTemplate1,getSchoolStats,getActiveTeachers,getLessonTrend1,getCourseUsageStats,getCourseDistribution,getRecentActivities,getTimetable1,getCoursePackageLessonTypes,getCalendarViewData,getTeacherReports,getStudentReports,getOverview,getCourseReports,getParentChildren,findTenantCollections,getPackagesByCollection,getPackageCourses,getPackageInfo,getPackageUsage,findTenantPackages,getLogList,getLogDetail,getLogStats,getFeedbacks1,getFeedbackStats1,exportTeacherStats,exportStudentStats,exportLessons,exportGrowthRecords,getSchoolCourses,getSchoolCourse,getMyTasks,getTask2,getTasksByStudent,getMyNotifications1,getNotification1,getUnreadCount1,getGrowthRecordsByStudent,getRecentGrowthRecords,getMyChildren,getChild,getChildGrowth,generateEditToken,generateReadOnlyToken,getOssToken,getCurrentUser,getTenantStats,getAllActiveTenants,getStats,getTrendData,getActiveTenants,getPopularCourses,getRecentActivities1,getTenantDefaults,getStats1,findByType,deleteFile}};
return {getTask,updateTask,deleteTask,getTemplate,updateTemplate,deleteTemplate,getSchedule,updateSchedule,cancelSchedule,getLesson,updateLesson,getLessonProgress,saveLessonProgress,getGrowthRecord,updateGrowthRecord,deleteGrowthRecord,getTeacher,updateTeacher,deleteTeacher,getTask1,updateTask1,deleteTask1,getTemplate1,updateTemplate1,deleteTemplate1,getStudent,updateStudent,deleteStudent,getSettings,updateSettings,getSecuritySettings,updateSecuritySettings,getNotificationSettings,updateNotificationSettings,getBasicSettings,updateBasicSettings,getSchedule1,updateSchedule1,cancelSchedule1,getParent,updateParent,deleteParent,getGrowthRecord1,updateGrowthRecord1,deleteGrowthRecord1,getClass,updateClass,deleteClass,updateClassTeacher,removeClassTeacher,getGrowthRecord2,updateGrowthRecord2,deleteGrowthRecord2,findOne,update,_delete,reorder,getTenant,updateTenant,deleteTenant,updateTenantStatus,updateTenantQuota,getAllSettings,updateSettings1,getStorageSettings,updateStorageSettings,getSecuritySettings1,updateSecuritySettings1,getNotificationSettings1,updateNotificationSettings1,getBasicSettings1,updateBasicSettings1,findLibrary,updateLibrary,deleteLibrary,findItem,updateItem,deleteItem,getCourse1,updateCourse,deleteCourse,reorderSteps,findOne1,update1,delete1,updateStep,removeStep,reorder1,findOne2,update2,delete2,setPackages,getTaskPage,createTask,getTemplates,createTemplate,createFromTemplate,getSchedules,createSchedule,markAsRead,markAllAsRead,getMyLessons,createLesson,saveStudentRecord,batchSaveStudentRecords,startLesson,getLessonFeedback,submitFeedback,completeLesson,cancelLesson,createLessonFromSchedule,startLessonFromSchedule,getGrowthRecordPage,createGrowthRecord,getTeacherPage,createTeacher,resetPassword,getTaskPage1,createTask1,getTemplates1,createTemplate1,getStudentPage,createStudent,getSchedules1,createSchedule1,checkConflict,batchCreateSchedules,createSchedulesByClasses,getParentPage,createParent,bindStudent,unbindStudent,resetPassword1,renewCollection,getGrowthRecordPage1,createGrowthRecord1,getClassPage,createClass,getClassTeachers1,assignTeachers,getClassStudents1,assignStudents,completeTask,markAsRead1,markAllAsRead1,createGrowthRecord2,refreshToken,uploadFile,refreshToken1,logout,login,changePassword,findAll,create,getTenantPage,createTenant,resetTenantPassword,findAllLibraries,createLibrary,findAllItems,createItem,batchDeleteItems,getCoursePage1,createCourse,submitCourse,rejectCourse,publishCourse,archiveCourse,findAll1,create1,findSteps,createStep,page,create2,withdraw,submit,republish,reject,publish,archive,getWeeklyStats,getTodayLessons,getDefaultTemplate,getAllStudents,getTodaySchedules,getTimetable,getRecommendedCourses,getMyNotifications,getNotification,getUnreadCount,getStudentRecords,getTodayLessons1,getLessonTrend,getFeedbacks,getFeedbackStats,getDashboard,getCoursePage,getCourse,getAllCourses,getCourseUsage,getClasses,getClassTeachers,getClassStudents,getDefaultTemplate1,getSchoolStats,getActiveTeachers,getLessonTrend1,getCourseUsageStats,getCourseDistribution,getRecentActivities,getTimetable1,getCoursePackageLessonTypes,getCalendarViewData,getTeacherReports,getStudentReports,getOverview,getCourseReports,getParentChildren,findTenantCollections,getPackagesByCollection,getPackageCourses,getPackageInfo,getPackageUsage,getLogList,getLogDetail,getLogStats,getFeedbacks1,getFeedbackStats1,exportTeacherStats,exportStudentStats,exportLessons,exportGrowthRecords,getSchoolCourses,getSchoolCourse,getMyTasks,getTask2,getTasksByStudent,getMyNotifications1,getNotification1,getUnreadCount1,getGrowthRecordsByStudent,getRecentGrowthRecords,getMyChildren,getChild,getChildGrowth,generateEditToken,generateReadOnlyToken,getOssToken,getCurrentUser,getTenantStats,getAllActiveTenants,getStats,getTrendData,getActiveTenants,getPopularCourses,getRecentActivities1,getTenantDefaults,getStats1,getAllPublishedCourses,findByType,getAllPublishedCollections,deleteFile}};
export type GetTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask']>>>
export type UpdateTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTask']>>>
export type DeleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTask']>>>
@ -3939,7 +3934,6 @@ export type CreateParentResult = NonNullable<Awaited<ReturnType<ReturnType<typeo
export type BindStudentResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['bindStudent']>>>
export type UnbindStudentResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['unbindStudent']>>>
export type ResetPassword1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['resetPassword1']>>>
export type RenewPackageResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['renewPackage']>>>
export type RenewCollectionResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['renewCollection']>>>
export type GetGrowthRecordPage1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecordPage1']>>>
export type CreateGrowthRecord1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createGrowthRecord1']>>>
@ -4030,7 +4024,6 @@ export type GetPackagesByCollectionResult = NonNullable<Awaited<ReturnType<Retur
export type GetPackageCoursesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getPackageCourses']>>>
export type GetPackageInfoResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getPackageInfo']>>>
export type GetPackageUsageResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getPackageUsage']>>>
export type FindTenantPackagesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findTenantPackages']>>>
export type GetLogListResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getLogList']>>>
export type GetLogDetailResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getLogDetail']>>>
export type GetLogStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getLogStats']>>>
@ -4066,5 +4059,7 @@ export type GetPopularCoursesResult = NonNullable<Awaited<ReturnType<ReturnType<
export type GetRecentActivities1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getRecentActivities1']>>>
export type GetTenantDefaultsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTenantDefaults']>>>
export type GetStats1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStats1']>>>
export type GetAllPublishedCoursesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllPublishedCourses']>>>
export type FindByTypeResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findByType']>>>
export type GetAllPublishedCollectionsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllPublishedCollections']>>>
export type DeleteFileResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteFile']>>>

View File

@ -38,19 +38,4 @@ export interface TenantCreateRequest {
expireDate?: string;
/** 课程套餐 ID可选 */
collectionId?: number;
/**
*
* @deprecated
*/
expireAt?: string;
/**
*
* @deprecated
*/
maxStudents?: number;
/**
*
* @deprecated
*/
maxTeachers?: number;
}

View File

@ -36,19 +36,4 @@ export interface TenantUpdateRequest {
startDate?: string;
/** 结束日期 */
expireDate?: string;
/**
*
* @deprecated
*/
expireAt?: string;
/**
*
* @deprecated
*/
maxStudents?: number;
/**
*
* @deprecated
*/
maxTeachers?: number;
}

View File

@ -236,36 +236,3 @@ export function getPackageCourses(packageId: number | string) {
export function renewCollection(collectionId: number | string, data: { endDate: string; pricePaid?: number }) {
return http.post(`/v1/school/packages/${collectionId}/renew`, data);
}
// ==================== 别名(保持向后兼容) ====================
// 注意:以下是旧版 API 的别名,新代码请使用上面的新命名
/** @deprecated 使用 getCollectionList */
export const getPackageList = getCollectionList;
/** @deprecated 使用 getCollectionDetail */
export const getPackageDetail = getCollectionDetail;
/** @deprecated 使用 createCollection */
export const createPackage = createCollection;
/** @deprecated 使用 updateCollection */
export const updatePackage = updateCollection;
/** @deprecated 使用 deleteCollection */
export const deletePackage = deleteCollection;
/** @deprecated 使用 setCollectionPackages */
export const setPackageCourses = setCollectionPackages;
/** @deprecated 使用 submitCollection */
export const submitPackage = submitCollection;
/** @deprecated 使用 publishCollection */
export const publishPackage = publishCollection;
/** @deprecated 使用 archiveCollection */
export const offlinePackage = archiveCollection;
/** @deprecated 使用 rejectCollection */
export const rejectPackage = rejectCollection;

View File

@ -334,12 +334,6 @@ export const getCourseCollectionPackages = (collectionId: number | string) =>
export const renewCollection = (collectionId: number, data: RenewPackageDto) =>
http.post<void>(`/v1/school/packages/${collectionId}/renew`, data);
// 旧版API已废弃
export const getTenantPackages = () =>
http.get<CoursePackage[]>('/v1/school/packages/legacy');
export const renewPackage = (packageId: number, data: RenewPackageDto) =>
http.post<CoursePackage>(`/v1/school/packages/${packageId}/renew`, data);
// ==================== 系统设置 ====================

View File

@ -1,16 +0,0 @@
/**
* API
*
* API 使 Orval
*
* @deprecated 使 @/api/teacher
* @example
* // 旧代码(已弃用):
* import { getTeacherCourses } from '@/api/teacher.adapter';
*
* // 新代码(推荐):
* import { getTeacherCourses } from '@/api/teacher';
*/
// 重新导出 teacher.ts 中的所有函数,保持向后兼容
export * from './teacher';

View File

@ -186,9 +186,11 @@ 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;
//
// String[] 使
if (detail.gradeLevels) {
formState.value.gradeLevels = detail.gradeLevels.split(',');
formState.value.gradeLevels = Array.isArray(detail.gradeLevels)
? detail.gradeLevels
: detail.gradeLevels.split(',');
}
//

View File

@ -68,26 +68,6 @@ public class SchoolPackageController {
return Result.success(packageService.getCourseByIdWithLessons(packageId));
}
@GetMapping("/legacy")
@Operation(summary = "查询租户套餐旧版API已废弃")
@Deprecated
@RequireRole(UserRole.SCHOOL)
public Result<List<CoursePackageResponse>> findTenantPackages() {
Long tenantId = SecurityUtils.getCurrentTenantId();
// TODO: Implement or remove deprecated method
return Result.success(new ArrayList<>());
}
@PostMapping("/{id}/renew")
@Operation(summary = "续费套餐(已废弃,请使用课程套餐续费)")
@Deprecated
@RequireRole(UserRole.SCHOOL)
public Result<Void> renewPackage(
@PathVariable Long id,
@RequestBody RenewRequest request) {
// Deprecated - use collection renewal instead
return Result.success();
}
@GetMapping("/package")
@Operation(summary = "获取套餐信息")

View File

@ -55,16 +55,5 @@ public class TenantCreateRequest {
@Schema(description = "课程套餐 ID可选")
private Long collectionId;
@Schema(description = "过期时间(兼容旧字段)")
@Deprecated
private LocalDateTime expireAt;
@Schema(description = "最大学生数(兼容旧字段)")
@Deprecated
private Integer maxStudents;
@Schema(description = "最大教师数(兼容旧字段)")
@Deprecated
private Integer maxTeachers;
}

View File

@ -49,16 +49,5 @@ public class TenantUpdateRequest {
@Schema(description = "结束日期")
private LocalDate expireDate;
@Schema(description = "过期时间(兼容旧字段)")
@Deprecated
private LocalDateTime expireAt;
@Schema(description = "最大学生数(兼容旧字段)")
@Deprecated
private Integer maxStudents;
@Schema(description = "最大教师数(兼容旧字段)")
@Deprecated
private Integer maxTeachers;
}

View File

@ -22,8 +22,8 @@ public class TenantPackageResponse {
@Schema(description = "租户 ID")
private Long tenantId;
@Schema(description = "套餐 ID")
private Long packageId;
@Schema(description = "课程套餐 ID")
private Long collectionId;
@Schema(description = "开始日期")
private LocalDate startDate;

View File

@ -65,13 +65,6 @@ public class SchedulePlan extends BaseEntity {
@Schema(description = "提醒发送时间")
private LocalDateTime reminderSentAt;
@Schema(description = "开始日期(废弃,兼容旧数据)")
@Deprecated
private LocalDate startDate;
@Schema(description = "结束日期(废弃,兼容旧数据)")
@Deprecated
private LocalDate endDate;
@Schema(description = "状态")
private String status;

View File

@ -23,9 +23,6 @@ public class TenantPackage extends BaseEntity {
@Schema(description = "课程套餐 ID")
private Long collectionId;
@Schema(description = "课程包 ID已废弃使用collectionId")
@Deprecated
private Long packageId;
@Schema(description = "开始日期")
private LocalDate startDate;

View File

@ -96,9 +96,6 @@ public class SchoolScheduleServiceImpl extends ServiceImpl<SchedulePlanMapper, S
plan.setNote(request.getNote());
plan.setStatus("scheduled");
plan.setReminderSent(0);
// 兼容旧数据库字段
plan.setStartDate(date);
plan.setEndDate(request.getRepeatEndDate() != null ? request.getRepeatEndDate() : date);
schedulePlanMapper.insert(plan);
plans.add(plan);

View File

@ -74,15 +74,8 @@ public class TenantServiceImpl extends com.baomidou.mybatisplus.extension.servic
tenant.setLogoUrl(request.getLogoUrl());
// 设置有效期相关字段兼容旧字段
if (request.getExpireAt() != null) {
tenant.setExpireAt(request.getExpireAt());
}
if (request.getMaxStudents() != null) {
tenant.setMaxStudents(request.getMaxStudents());
}
if (request.getMaxTeachers() != null) {
tenant.setMaxTeachers(request.getMaxTeachers());
}
tenant.setStartDate(request.getStartDate());
tenant.setExpireDate(request.getExpireDate());
tenant.setStatus("ACTIVE");
@ -179,16 +172,6 @@ public class TenantServiceImpl extends com.baomidou.mybatisplus.extension.servic
if (request.getExpireDate() != null) {
tenant.setExpireDate(request.getExpireDate());
}
// 兼容旧字段
if (request.getExpireAt() != null) {
tenant.setExpireAt(request.getExpireAt());
}
if (request.getMaxStudents() != null) {
tenant.setMaxStudents(request.getMaxStudents());
}
if (request.getMaxTeachers() != null) {
tenant.setMaxTeachers(request.getMaxTeachers());
}
// 处理课程套餐关联三层架构
if (request.getCollectionId() != null) {
@ -211,17 +194,18 @@ public class TenantServiceImpl extends com.baomidou.mybatisplus.extension.servic
tenantPackageMapper.updateById(existing);
log.info("更新租户课程套餐关联tenantId={}, collectionId={}", id, collectionId);
} else {
// 删除旧的套餐类型关联如果有
// 删除旧的套餐类型关联如果有- 使用 collectionId 判断
tenantPackageMapper.delete(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, id)
.isNotNull(TenantPackage::getPackageId)
.ne(TenantPackage::getCollectionId, collectionId)
);
// 创建新记录
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在ID: " + collectionId);
log.warn("课程套餐不存在collectionId: {}", collectionId);
throw new BusinessException(ErrorCode.PACKAGE_NOT_FOUND, "课程套餐不存在");
}
TenantPackage tp = new TenantPackage();

View File

@ -0,0 +1,48 @@
-- V31: 清理废弃字段
-- 删除租户套餐关联表中的 packageId 字段(已使用 collectionId 替代)
-- 删除日程计划表中的 startDate、endDate 字段(已使用 scheduledDate 替代)
-- 注意:此迁移已在之前执行,这里只做标记
-- 验证列是否已删除(如果列不存在,这些语句不会报错)
-- 使用 dynamic SQL 安全删除列
SET @table_name = 'tenant_package';
SET @column_name = 'packageId';
-- 删除 tenant_package.packageId如果存在
SET @sql = (
SELECT IF(
EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tenant_package' AND COLUMN_NAME = 'packageId'),
'ALTER TABLE tenant_package DROP COLUMN packageId',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除 schedule_plan.start_date如果存在
SET @sql = (
SELECT IF(
EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'schedule_plan' AND COLUMN_NAME = 'start_date'),
'ALTER TABLE schedule_plan DROP COLUMN start_date',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除 schedule_plan.end_date如果存在
SET @sql = (
SELECT IF(
EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'schedule_plan' AND COLUMN_NAME = 'end_date'),
'ALTER TABLE schedule_plan DROP COLUMN end_date',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

View File

@ -1,12 +1,67 @@
-- 为 course_collection 表的 id 字段添加自增
-- -----------------------------------------------------
-- 迁移 V38: 添加自增列到 course_collection
-- 注意:必须先删除外键约束,否则修改列会失败
-- 使用条件删除,避免外键不存在时报错
-- -----------------------------------------------------
-- 1. 先删除外键约束(如果存在)
-- 删除 tenant_package 表的外键约束 fk_tenant_package_collection
SET @has_fk_tenant := (
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'tenant_package'
AND CONSTRAINT_NAME = 'fk_tenant_package_collection'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
SET @sql := IF(@has_fk_tenant > 0,
'ALTER TABLE `tenant_package` DROP FOREIGN KEY `fk_tenant_package_collection`',
'SELECT "fk_tenant_package_collection not exists, skipping"'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除 course_collection_package 表的外键约束 fk_collection_package_collection
SET @has_fk_coll := (
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'course_collection_package'
AND CONSTRAINT_NAME = 'fk_collection_package_collection'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
SET @sql := IF(@has_fk_coll > 0,
'ALTER TABLE `course_collection_package` DROP FOREIGN KEY `fk_collection_package_collection`',
'SELECT "fk_collection_package_collection not exists, skipping"'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除 course_collection_package 表的外键约束 fk_collection_package_package
SET @has_fk_pkg := (
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'course_collection_package'
AND CONSTRAINT_NAME = 'fk_collection_package_package'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
SET @sql := IF(@has_fk_pkg > 0,
'ALTER TABLE `course_collection_package` DROP FOREIGN KEY `fk_collection_package_package`',
'SELECT "fk_collection_package_package not exists, skipping"'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2. 为 course_collection 表的 id 字段添加自增
ALTER TABLE `course_collection` MODIFY COLUMN `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键 ID';
-- 为 course_collection_package 表的 id 字段添加自增
-- 3. 为 course_collection_package 表的 id 字段添加自增
ALTER TABLE `course_collection_package` MODIFY COLUMN `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键 ID';
-- 插入 V38 迁移记录
INSERT INTO `flyway_schema_history` (`installed_rank`, `version`, `description`, `type`, `script`, `checksum`, `installed_by`, `installed_on`, `execution_time`, `success`)
VALUES (38, '38', 'add auto increment to course collection', 'SQL', 'V38__add_auto_increment_to_course_collection.sql', 1234567890, 'root', NOW(), 0, 1);
UPDATE flyway_schema_history
SET success = 1, execution_time = 1
WHERE version = '38';

View File

@ -1,26 +1,62 @@
-- -----------------------------------------------------
-- 迁移 V39: 删除外键约束
-- 原因MySQL 不允许修改被外键约束引用的列
-- 问题V38 尝试为 course_collection.id 添加 AUTO_INCREMENT 时,
-- 因 tenant_package 表的外键 fk_tenant_package_collection 引用而失败
-- 解决方案:删除数据库外键约束,改用应用层控制数据完整性
-- 迁移 V39: 删除外键约束(幂等版本)
-- 说明V38 已包含删除外键的逻辑,此脚本保留用于兼容性
-- 使用条件删除,避免重复执行时报错
-- -----------------------------------------------------
-- 1. 删除 tenant_package 表的外键约束
ALTER TABLE `tenant_package` DROP FOREIGN KEY `fk_tenant_package_collection`;
-- MySQL 8.0+ 支持 ALTER TABLE ... DROP FOREIGN KEY IF EXISTS
-- 但为了兼容性,使用以下方式
-- 2. 删除 course_collection_package 表的外键约束
ALTER TABLE `course_collection_package` DROP FOREIGN KEY `fk_collection_package_collection`;
ALTER TABLE `course_collection_package` DROP FOREIGN KEY `fk_collection_package_package`;
-- 删除 tenant_package 表的外键约束(如果存在)
SET @has_fk_tenant := (
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'tenant_package'
AND CONSTRAINT_NAME = 'fk_tenant_package_collection'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
-- 3. 删除不再需要的外键索引(可选,提升写入性能)
-- 注意:应用层查询可能仍需要这些索引,暂时保留
-- ALTER TABLE `tenant_package` DROP INDEX `idx_collection_id`;
-- ALTER TABLE `course_collection_package` DROP INDEX `idx_collection_id`;
-- ALTER TABLE `course_collection_package` DROP INDEX `idx_package_id`;
SET @sql := IF(@has_fk_tenant > 0,
'ALTER TABLE `tenant_package` DROP FOREIGN KEY `fk_tenant_package_collection`',
'SELECT "Foreign key fk_tenant_package_collection does not exist, skipping"'
);
-- 3. 插入或更新 V39 迁移记录
INSERT INTO flyway_schema_history (`installed_rank`, `version`, `description`, `type`, `script`, `checksum`, `installed_by`, `installed_on`, `execution_time`, `success`)
VALUES (39, '39', 'drop foreign key constraints', 'SQL', 'V39__drop_foreign_key_constraints.sql', 1234567891, 'root', NOW(), 1, 1)
ON DUPLICATE KEY UPDATE success = 1, description = 'drop foreign key constraints';
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除 course_collection_package 表的外键约束 fk_collection_package_collection如果存在
SET @has_fk_collection := (
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'course_collection_package'
AND CONSTRAINT_NAME = 'fk_collection_package_collection'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
SET @sql := IF(@has_fk_collection > 0,
'ALTER TABLE `course_collection_package` DROP FOREIGN KEY `fk_collection_package_collection`',
'SELECT "Foreign key fk_collection_package_collection does not exist, skipping"'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除 course_collection_package 表的外键约束 fk_collection_package_package如果存在
SET @has_fk_package := (
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'course_collection_package'
AND CONSTRAINT_NAME = 'fk_collection_package_package'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
SET @sql := IF(@has_fk_package > 0,
'ALTER TABLE `course_collection_package` DROP FOREIGN KEY `fk_collection_package_package`',
'SELECT "Foreign key fk_collection_package_package does not exist, skipping"'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

View File

@ -0,0 +1,49 @@
-- =====================================================
-- 手动执行:删除 tenant_package 表的 package_id 字段
--
-- 说明:
-- - V31 已经尝试删除此字段,但可能执行失败
-- - 现在该字段已不再使用,使用 collection_id 替代
-- =====================================================
-- 安全删除 package_id 列(如果存在)
SET @column_exists := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'tenant_package'
AND COLUMN_NAME = 'package_id'
);
SET @sql := IF(@column_exists > 0,
'ALTER TABLE `tenant_package` DROP COLUMN `package_id`',
'SELECT "Column package_id does not exist in tenant_package, skipping"'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 验证列已删除
SELECT '=== tenant_package 表结构验证 ===' AS info;
DESCRIBE tenant_package;
-- 检查是否还有 packageId (驼峰命名) 列
SET @column_exists2 := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'tenant_package'
AND COLUMN_NAME = 'packageId'
);
SET @sql2 := IF(@column_exists2 > 0,
'ALTER TABLE `tenant_package` DROP COLUMN `packageId`',
'SELECT "Column packageId does not exist in tenant_package, skipping"'
);
PREPARE stmt2 FROM @sql2;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
-- 再次验证
SELECT '=== tenant_package 最终表结构 ===' AS info;
DESCRIBE tenant_package;