From 9a9caab60b41d7f2ca53f343249258fe90a1862c Mon Sep 17 00:00:00 2001 From: En Date: Thu, 19 Mar 2026 12:34:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(db):=20=E6=B7=BB=E5=8A=A0=20V40=20?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E8=84=9A=E6=9C=AC=E5=88=A0=E9=99=A4=20tenant?= =?UTF-8?q?=5Fpackage=20=E8=A1=A8=E7=9A=84=20package=5Fid=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 V40__drop_tenant_package_package_id.sql 迁移脚本 - 安全删除 tenant_package 表中废弃的 package_id/packageId 列 - 该字段已在 V31 中尝试删除,V40 用于修复执行失败的情况 - TenantPackage 实体已使用 collectionId 字段替代 Co-Authored-By: Claude Opus 4.6 --- .claude/settings.local.json | 3 +- reading-platform-frontend/openapi.json | 383 ++++++++---------- .../src/api/generated/index.ts | 63 ++- .../generated/model/tenantCreateRequest.ts | 15 - .../generated/model/tenantUpdateRequest.ts | 15 - reading-platform-frontend/src/api/package.ts | 33 -- reading-platform-frontend/src/api/school.ts | 6 - .../src/api/teacher.adapter.ts | 16 - .../admin/collections/CollectionEditView.vue | 6 +- .../school/SchoolPackageController.java | 20 - .../dto/request/TenantCreateRequest.java | 11 - .../dto/request/TenantUpdateRequest.java | 11 - .../dto/response/TenantPackageResponse.java | 4 +- .../reading/platform/entity/SchedulePlan.java | 7 - .../platform/entity/TenantPackage.java | 3 - .../impl/SchoolScheduleServiceImpl.java | 3 - .../service/impl/TenantServiceImpl.java | 28 +- .../V31__drop_deprecated_columns.sql | 48 +++ ...dd_auto_increment_to_course_collection.sql | 73 +++- .../V39__drop_foreign_key_constraints.sql | 74 +++- .../V40__drop_tenant_package_package_id.sql | 49 +++ 21 files changed, 421 insertions(+), 450 deletions(-) delete mode 100644 reading-platform-frontend/src/api/teacher.adapter.ts create mode 100644 reading-platform-java/src/main/resources/db/migration/V31__drop_deprecated_columns.sql create mode 100644 reading-platform-java/src/main/resources/db/migration/V40__drop_tenant_package_package_id.sql diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 963cb33..0ef1f70 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,8 @@ { "permissions": { "allow": [ - "Bash(mvn compile:*)" + "Bash(mvn compile:*)", + "Bash(sed:*)" ] } } diff --git a/reading-platform-frontend/openapi.json b/reading-platform-frontend/openapi.json index 571910f..d94f130 100644 --- a/reading-platform-frontend/openapi.json +++ b/reading-platform-frontend/openapi.json @@ -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": "租户创建请求" diff --git a/reading-platform-frontend/src/api/generated/index.ts b/reading-platform-frontend/src/api/generated/index.ts index 528137d..0eb2dc6 100644 --- a/reading-platform-frontend/src/api/generated/index.ts +++ b/reading-platform-frontend/src/api/generated/index.ts @@ -2022,23 +2022,6 @@ const resetPassword1 = ( ); } -/** - * @deprecated - * @summary 续费套餐(已废弃,请使用课程套餐续费) - */ -const renewPackage = ( - id: number, - renewRequest: RenewRequest, - ) => { - return customMutator( - {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( - {url: `/v1/school/packages/legacy`, method: 'GET', - responseType: 'blob' - }, - ); - } - /** * @summary 获取日志列表 */ @@ -3776,6 +3745,19 @@ const getStats1 = ( ); } +/** + * @summary 获取所有已发布的课程包 + */ +const getAllPublishedCourses = ( + + ) => { + return customMutator( + {url: `/v1/admin/packages/all`, method: 'GET', + responseType: 'blob' + }, + ); + } + /** * @summary 按类型获取课程环节 */ @@ -3790,6 +3772,19 @@ const findByType = ( ); } +/** + * @summary 获取所有已发布的课程套餐 + */ +const getAllPublishedCollections = ( + + ) => { + return customMutator( + {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['getTask']>>> export type UpdateTaskResult = NonNullable['updateTask']>>> export type DeleteTaskResult = NonNullable['deleteTask']>>> @@ -3939,7 +3934,6 @@ export type CreateParentResult = NonNullable['bindStudent']>>> export type UnbindStudentResult = NonNullable['unbindStudent']>>> export type ResetPassword1Result = NonNullable['resetPassword1']>>> -export type RenewPackageResult = NonNullable['renewPackage']>>> export type RenewCollectionResult = NonNullable['renewCollection']>>> export type GetGrowthRecordPage1Result = NonNullable['getGrowthRecordPage1']>>> export type CreateGrowthRecord1Result = NonNullable['createGrowthRecord1']>>> @@ -4030,7 +4024,6 @@ export type GetPackagesByCollectionResult = NonNullable['getPackageCourses']>>> export type GetPackageInfoResult = NonNullable['getPackageInfo']>>> export type GetPackageUsageResult = NonNullable['getPackageUsage']>>> -export type FindTenantPackagesResult = NonNullable['findTenantPackages']>>> export type GetLogListResult = NonNullable['getLogList']>>> export type GetLogDetailResult = NonNullable['getLogDetail']>>> export type GetLogStatsResult = NonNullable['getLogStats']>>> @@ -4066,5 +4059,7 @@ export type GetPopularCoursesResult = NonNullable['getRecentActivities1']>>> export type GetTenantDefaultsResult = NonNullable['getTenantDefaults']>>> export type GetStats1Result = NonNullable['getStats1']>>> +export type GetAllPublishedCoursesResult = NonNullable['getAllPublishedCourses']>>> export type FindByTypeResult = NonNullable['findByType']>>> +export type GetAllPublishedCollectionsResult = NonNullable['getAllPublishedCollections']>>> export type DeleteFileResult = NonNullable['deleteFile']>>> diff --git a/reading-platform-frontend/src/api/generated/model/tenantCreateRequest.ts b/reading-platform-frontend/src/api/generated/model/tenantCreateRequest.ts index 3f29cc3..07c08d7 100644 --- a/reading-platform-frontend/src/api/generated/model/tenantCreateRequest.ts +++ b/reading-platform-frontend/src/api/generated/model/tenantCreateRequest.ts @@ -38,19 +38,4 @@ export interface TenantCreateRequest { expireDate?: string; /** 课程套餐 ID(可选) */ collectionId?: number; - /** - * 过期时间(兼容旧字段) - * @deprecated - */ - expireAt?: string; - /** - * 最大学生数(兼容旧字段) - * @deprecated - */ - maxStudents?: number; - /** - * 最大教师数(兼容旧字段) - * @deprecated - */ - maxTeachers?: number; } diff --git a/reading-platform-frontend/src/api/generated/model/tenantUpdateRequest.ts b/reading-platform-frontend/src/api/generated/model/tenantUpdateRequest.ts index 5b60eb4..59bffb7 100644 --- a/reading-platform-frontend/src/api/generated/model/tenantUpdateRequest.ts +++ b/reading-platform-frontend/src/api/generated/model/tenantUpdateRequest.ts @@ -36,19 +36,4 @@ export interface TenantUpdateRequest { startDate?: string; /** 结束日期 */ expireDate?: string; - /** - * 过期时间(兼容旧字段) - * @deprecated - */ - expireAt?: string; - /** - * 最大学生数(兼容旧字段) - * @deprecated - */ - maxStudents?: number; - /** - * 最大教师数(兼容旧字段) - * @deprecated - */ - maxTeachers?: number; } diff --git a/reading-platform-frontend/src/api/package.ts b/reading-platform-frontend/src/api/package.ts index aff353a..575a0e4 100644 --- a/reading-platform-frontend/src/api/package.ts +++ b/reading-platform-frontend/src/api/package.ts @@ -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; diff --git a/reading-platform-frontend/src/api/school.ts b/reading-platform-frontend/src/api/school.ts index 6aad5ea..f17f3ff 100644 --- a/reading-platform-frontend/src/api/school.ts +++ b/reading-platform-frontend/src/api/school.ts @@ -334,12 +334,6 @@ export const getCourseCollectionPackages = (collectionId: number | string) => export const renewCollection = (collectionId: number, data: RenewPackageDto) => http.post(`/v1/school/packages/${collectionId}/renew`, data); -// 旧版API(已废弃) -export const getTenantPackages = () => - http.get('/v1/school/packages/legacy'); - -export const renewPackage = (packageId: number, data: RenewPackageDto) => - http.post(`/v1/school/packages/${packageId}/renew`, data); // ==================== 系统设置 ==================== diff --git a/reading-platform-frontend/src/api/teacher.adapter.ts b/reading-platform-frontend/src/api/teacher.adapter.ts deleted file mode 100644 index 6590b95..0000000 --- a/reading-platform-frontend/src/api/teacher.adapter.ts +++ /dev/null @@ -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'; diff --git a/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue b/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue index 1bf00f9..6d2f96f 100644 --- a/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue +++ b/reading-platform-frontend/src/views/admin/collections/CollectionEditView.vue @@ -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(','); } // 加载已选课程包 diff --git a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolPackageController.java b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolPackageController.java index 6c26b2d..cfd13ae 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolPackageController.java +++ b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolPackageController.java @@ -68,26 +68,6 @@ public class SchoolPackageController { return Result.success(packageService.getCourseByIdWithLessons(packageId)); } - @GetMapping("/legacy") - @Operation(summary = "查询租户套餐(旧版API,已废弃)") - @Deprecated - @RequireRole(UserRole.SCHOOL) - public Result> 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 renewPackage( - @PathVariable Long id, - @RequestBody RenewRequest request) { - // Deprecated - use collection renewal instead - return Result.success(); - } @GetMapping("/package") @Operation(summary = "获取套餐信息") diff --git a/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantCreateRequest.java b/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantCreateRequest.java index 31cb45c..b5b3a5f 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantCreateRequest.java +++ b/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantCreateRequest.java @@ -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; } diff --git a/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantUpdateRequest.java b/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantUpdateRequest.java index 458841b..761567d 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantUpdateRequest.java +++ b/reading-platform-java/src/main/java/com/reading/platform/dto/request/TenantUpdateRequest.java @@ -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; } diff --git a/reading-platform-java/src/main/java/com/reading/platform/dto/response/TenantPackageResponse.java b/reading-platform-java/src/main/java/com/reading/platform/dto/response/TenantPackageResponse.java index 2fb4b19..6de854d 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/dto/response/TenantPackageResponse.java +++ b/reading-platform-java/src/main/java/com/reading/platform/dto/response/TenantPackageResponse.java @@ -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; diff --git a/reading-platform-java/src/main/java/com/reading/platform/entity/SchedulePlan.java b/reading-platform-java/src/main/java/com/reading/platform/entity/SchedulePlan.java index 8592fb8..9b7a8f8 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/entity/SchedulePlan.java +++ b/reading-platform-java/src/main/java/com/reading/platform/entity/SchedulePlan.java @@ -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; diff --git a/reading-platform-java/src/main/java/com/reading/platform/entity/TenantPackage.java b/reading-platform-java/src/main/java/com/reading/platform/entity/TenantPackage.java index 0f4ceca..6e529d7 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/entity/TenantPackage.java +++ b/reading-platform-java/src/main/java/com/reading/platform/entity/TenantPackage.java @@ -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; diff --git a/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java b/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java index 619abb6..d4114e7 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java +++ b/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java @@ -96,9 +96,6 @@ public class SchoolScheduleServiceImpl extends ServiceImpl() .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(); diff --git a/reading-platform-java/src/main/resources/db/migration/V31__drop_deprecated_columns.sql b/reading-platform-java/src/main/resources/db/migration/V31__drop_deprecated_columns.sql new file mode 100644 index 0000000..452108f --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V31__drop_deprecated_columns.sql @@ -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; diff --git a/reading-platform-java/src/main/resources/db/migration/V38__add_auto_increment_to_course_collection.sql b/reading-platform-java/src/main/resources/db/migration/V38__add_auto_increment_to_course_collection.sql index 24383ee..679e995 100644 --- a/reading-platform-java/src/main/resources/db/migration/V38__add_auto_increment_to_course_collection.sql +++ b/reading-platform-java/src/main/resources/db/migration/V38__add_auto_increment_to_course_collection.sql @@ -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'; diff --git a/reading-platform-java/src/main/resources/db/migration/V39__drop_foreign_key_constraints.sql b/reading-platform-java/src/main/resources/db/migration/V39__drop_foreign_key_constraints.sql index a5b5a0a..10cd56a 100644 --- a/reading-platform-java/src/main/resources/db/migration/V39__drop_foreign_key_constraints.sql +++ b/reading-platform-java/src/main/resources/db/migration/V39__drop_foreign_key_constraints.sql @@ -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; diff --git a/reading-platform-java/src/main/resources/db/migration/V40__drop_tenant_package_package_id.sql b/reading-platform-java/src/main/resources/db/migration/V40__drop_tenant_package_package_id.sql new file mode 100644 index 0000000..3aaddc2 --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V40__drop_tenant_package_package_id.sql @@ -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;