diff --git a/reading-platform-frontend/src/api/package.ts b/reading-platform-frontend/src/api/package.ts index 64e6739..7a0a034 100644 --- a/reading-platform-frontend/src/api/package.ts +++ b/reading-platform-frontend/src/api/package.ts @@ -1,36 +1,33 @@ import { http } from "./index"; -import { - readingApi, - UnwrapResult, - ApiResultOf, - GetPackages1Result, -} from "./client"; -// ==================== 套餐管理 ==================== +// ==================== 课程包管理 ==================== +/** + * 课程包(已移除商业字段,如 price, status 等) + * 注意:商业字段已移至 ProductBundle + */ export interface CoursePackage { - id: number; + id: string; name: string; description?: string; - price: number; - discountPrice?: number; - discountType?: string; - gradeLevels: string[]; - status: string; + coverUrl?: string; courseCount: number; - tenantCount: number; + isSystem: number; createdAt: string; - publishedAt?: string; - courses?: PackageCourse[]; + updatedAt: string; + packageCourses?: PackageCourse[]; } +/** + * 课程包 - 课程关联 + */ export interface PackageCourse { - packageId: number; - courseId: number; - gradeLevel: string; + id: string; + coursePackageId: string; + courseId: string; sortOrder: number; - course: { - id: number; + course?: { + id: string; name: string; coverImagePath?: string; duration?: number; @@ -39,122 +36,31 @@ export interface PackageCourse { } export interface PackageListParams { - status?: string; page?: number; pageSize?: number; + keyword?: string; } export interface CreatePackageData { name: string; description?: string; - price: number; - discountPrice?: number; - discountType?: string; - gradeLevels: string[]; + coverUrl?: string; } -type AdminPackageResult = UnwrapResult>; - -// 获取套餐列表(管理员端) -export const getPackageList = readingApi.getPackages1; -// 获取套餐详情(管理员端) -export function getPackageDetail(id: number): Promise { - return readingApi.getPackage1(id).then((res) => res.data as any); +export interface UpdatePackageData { + name?: string; + description?: string; + coverUrl?: string; } -// 创建套餐(管理员端) -export function createPackage( - data: CreatePackageData, -): Promise { - return readingApi.createPackage(data as any).then((res) => res.data as any); +// 获取课程包列表(通用) +export function getPackages(params: PackageListParams) { + return http.get("/api/v1/admin/packages", { params }); } -// 更新套餐(管理员端) -export function updatePackage( - id: number, - data: Partial, -): Promise { - return readingApi - .updatePackage(id, data as any) - .then((res) => res.data as any); -} +// ==================== 学校端课程包 ==================== -// 删除套餐(管理员端) -export function deletePackage(id: number): Promise { - return readingApi.deletePackage(id).then(() => undefined); -} - -// 设置套餐课程(仍使用自定义 HTTP 接口) -export function setPackageCourses( - packageId: number, - courses: { courseId: number; gradeLevel: string; sortOrder?: number }[], -) { - return http.put(`/api/v1/admin/packages/${packageId}/courses`, { courses }); -} - -// 添加课程到套餐(仍使用自定义 HTTP 接口) -export function addCourseToPackage( - packageId: number, - data: { courseId: number; gradeLevel: string; sortOrder?: number }, -) { - return http.post(`/api/v1/admin/packages/${packageId}/courses`, data); -} - -// 从套餐移除课程(仍使用自定义 HTTP 接口) -export function removeCourseFromPackage(packageId: number, courseId: number) { - return http.delete(`/api/v1/admin/packages/${packageId}/courses/${courseId}`); -} - -// 提交审核 -export function submitPackage(id: number): Promise { - return readingApi.submitPackage(id).then(() => undefined); -} - -// 审核套餐 -export function reviewPackage( - id: number, - data: { approved: boolean; comment?: string }, -): Promise { - return readingApi - .reviewPackage(id, { - approved: data.approved, - comment: data.comment, - } as any) - .then(() => undefined); -} - -// 发布套餐 -export function publishPackage(id: number): Promise { - return readingApi.publishPackage(id).then(() => undefined); -} - -// 下架套餐 -export function offlinePackage(id: number): Promise { - return readingApi.offlinePackage(id).then(() => undefined); -} - -// ==================== 学校端套餐 ==================== - -export interface TenantPackage { - id: number; - tenantId: number; - packageId: number; - startDate: string; - endDate: string; - status: string; - pricePaid: number; - package: CoursePackage; -} - -// 获取学校已授权套餐 +// 获取学校已授权课程包 export function getTenantPackages() { return http.get("/api/v1/school/packages"); } - -// 续订套餐 -export function renewPackage( - packageId: number, - data: { endDate: string; pricePaid?: number }, -) { - return http.post(`/api/v1/school/packages/${packageId}/renew`, data); -} diff --git a/reading-platform-frontend/src/api/productBundle.ts b/reading-platform-frontend/src/api/productBundle.ts new file mode 100644 index 0000000..3165cff --- /dev/null +++ b/reading-platform-frontend/src/api/productBundle.ts @@ -0,0 +1,327 @@ +import { http } from "./index"; + +// ==================== 类型定义 ==================== + +/** + * 产品套餐 + */ +export interface ProductBundle { + id: string; + name: string; + description?: string; + coverUrl?: string; + price: number; // 单位:分 + discountPrice?: number; // 单位:分 + discountType?: string; // PERCENT-折扣 / FIXED-立减 + gradeLevels: string[]; + status: string; // DRAFT, PENDING_REVIEW, APPROVED, REJECTED, PUBLISHED, OFFLINE + coursePackageCount: number; + tenantCount: number; + publishedAt?: string; + isSystem: number; + createdAt: string; + updatedAt: string; + bundleCoursePackages?: BundleCoursePackage[]; +} + +/** + * 套餐 - 课程包关联 + */ +export interface BundleCoursePackage { + id: string; + productBundleId: string; + coursePackageId: string; + gradeLevel?: string; + sortOrder: number; + coursePackage?: CoursePackageSimple; +} + +/** + * 课程包(简化版) + */ +export interface CoursePackageSimple { + id: string; + name: string; + description?: string; + coverUrl?: string; + courseCount?: number; +} + +/** + * 学校套餐购买记录 + */ +export interface TenantProductBundle { + id: string; + tenantId: string; + productBundleId: string; + productId: string; + startDate: string; + endDate: string; + status: string; // ACTIVE, EXPIRED, SUSPENDED + pricePaid: number; // 单位:分 + purchaseTime?: string; + renewedFrom?: string; + createdAt: string; + updatedAt: string; + productBundle?: ProductBundle; +} + +// ==================== 请求参数 ==================== + +export interface BundleListParams { + page?: number; + pageSize?: number; + keyword?: string; + status?: string; +} + +export interface CreateBundleData { + name: string; + description?: string; + coverUrl?: string; + price: number; + discountPrice?: number; + discountType?: string; + gradeLevels: string[]; +} + +export interface ReviewBundleData { + approved: boolean; + comment?: string; +} + +// ==================== 管理员端 - 套餐管理 ==================== + +/** + * 获取套餐列表(分页) + */ +export function getBundleList(params: BundleListParams) { + return http.get("/api/v1/admin/bundles", { params }); +} + +/** + * 获取套餐详情 + */ +export function getBundleDetail(id: string): Promise { + return http.get(`/api/v1/admin/bundles/${id}`); +} + +/** + * 创建套餐 + */ +export function createBundle(data: CreateBundleData): Promise { + return http.post("/api/v1/admin/bundles", data); +} + +/** + * 更新套餐 + */ +export function updateBundle(id: string, data: Partial): Promise { + return http.put(`/api/v1/admin/bundles/${id}`, data); +} + +/** + * 删除套餐 + */ +export function deleteBundle(id: string): Promise { + return http.delete(`/api/v1/admin/bundles/${id}`); +} + +/** + * 提交套餐审核 + */ +export function submitBundle(id: string): Promise { + return http.post(`/api/v1/admin/bundles/${id}/submit`); +} + +/** + * 审核套餐(通过或拒绝) + */ +export function reviewBundle(id: string, data: ReviewBundleData): Promise { + return http.post(`/api/v1/admin/bundles/${id}/review`, data); +} + +/** + * 发布套餐 + */ +export function publishBundle(id: string): Promise { + return http.post(`/api/v1/admin/bundles/${id}/publish`); +} + +/** + * 下架套餐 + */ +export function offlineBundle(id: string): Promise { + return http.post(`/api/v1/admin/bundles/${id}/offline`); +} + +/** + * 获取系统套餐列表 + */ +export function getSystemBundles(params: { page?: number; pageSize?: number; gradeLevel?: string }) { + return http.get("/api/v1/admin/bundles/system", { params }); +} + +// ==================== 管理员端 - 套餐课程包关联管理 ==================== + +/** + * 获取套餐包含的课程包列表 + */ +export function getBundleCoursePackages(bundleId: string) { + return http.get(`/api/v1/admin/bundles/${bundleId}/packages`); +} + +/** + * 添加课程包到套餐 + */ +export function addBundleCoursePackage( + bundleId: string, + data: { coursePackageId: string; gradeLevel?: string } +) { + return http.post(`/api/v1/admin/bundles/${bundleId}/packages`, data); +} + +/** + * 从套餐移除课程包 + */ +export function removeBundleCoursePackage(bundleId: string, id: string): Promise { + return http.delete(`/api/v1/admin/bundles/${bundleId}/packages/${id}`); +} + +/** + * 更新课程包排序 + */ +export function updateBundleCoursePackageSort( + bundleId: string, + id: string, + sortOrder: number +): Promise { + return http.put(`/api/v1/admin/bundles/${bundleId}/packages/${id}/sort`, { sortOrder }); +} + +/** + * 批量添加课程包到套餐 + */ +export function batchAddBundleCoursePackages( + bundleId: string, + coursePackageIds: string[] +): Promise { + return http.post(`/api/v1/admin/bundles/${bundleId}/packages/batch`, coursePackageIds); +} + +// ==================== 管理员端 - 课程包课程关联管理 ==================== + +/** + * 获取课程包包含的课程列表 + */ +export function getPackageCourses(packageId: string) { + return http.get(`/api/v1/admin/packages/${packageId}/courses`); +} + +/** + * 添加课程到课程包 + */ +export function addPackageCourse( + packageId: string, + data: { courseId: string } +) { + return http.post(`/api/v1/admin/packages/${packageId}/courses`, data); +} + +/** + * 从课程包移除课程 + */ +export function removePackageCourse(packageId: string, id: string): Promise { + return http.delete(`/api/v1/admin/packages/${packageId}/courses/${id}`); +} + +/** + * 更新课程排序 + */ +export function updatePackageCourseSort( + packageId: string, + id: string, + sortOrder: number +): Promise { + return http.put(`/api/v1/admin/packages/${packageId}/courses/${id}/sort`, { sortOrder }); +} + +/** + * 批量添加课程到课程包 + */ +export function batchAddPackageCourses( + packageId: string, + courseIds: string[] +): Promise { + return http.post(`/api/v1/admin/packages/${packageId}/courses/batch`, courseIds); +} + +// ==================== 管理员端 - 学校套餐购买记录管理 ==================== + +/** + * 获取学校套餐购买记录列表(分页) + */ +export function getTenantBundleList(params: { + page?: number; + pageSize?: number; + tenantId?: string; + status?: string; +}) { + return http.get("/api/v1/admin/tenant-bundles", { params }); +} + +/** + * 获取购买记录详情 + */ +export function getTenantBundleDetail(id: string) { + return http.get(`/api/v1/admin/tenant-bundles/${id}`); +} + +/** + * 获取学校的套餐列表 + */ +export function getTenantBundlesByTenantId(tenantId: string) { + return http.get(`/api/v1/admin/tenant-bundles/tenant/${tenantId}`); +} + +/** + * 获取学校当前有效的套餐 + */ +export function getActiveTenantBundle(tenantId: string) { + return http.get(`/api/v1/admin/tenant-bundles/tenant/${tenantId}/active`); +} + +/** + * 创建购买记录 + */ +export function createTenantBundle(data: Partial) { + return http.post("/api/v1/admin/tenant-bundles", data); +} + +/** + * 更新购买记录 + */ +export function updateTenantBundle(id: string, data: Partial) { + return http.put(`/api/v1/admin/tenant-bundles/${id}`, data); +} + +/** + * 激活套餐 + */ +export function activateTenantBundle(id: string): Promise { + return http.post(`/api/v1/admin/tenant-bundles/${id}/activate`); +} + +/** + * 过期套餐 + */ +export function expireTenantBundle(id: string): Promise { + return http.post(`/api/v1/admin/tenant-bundles/${id}/expire`); +} + +/** + * 暂停套餐 + */ +export function suspendTenantBundle(id: string): Promise { + return http.post(`/api/v1/admin/tenant-bundles/${id}/suspend`); +} diff --git a/reading-platform-frontend/src/views/admin/LayoutView.vue b/reading-platform-frontend/src/views/admin/LayoutView.vue index 29e18fa..7208a76 100644 --- a/reading-platform-frontend/src/views/admin/LayoutView.vue +++ b/reading-platform-frontend/src/views/admin/LayoutView.vue @@ -36,7 +36,7 @@ 课程包管理 - + @@ -162,8 +162,8 @@ watch( (path) => { if (path.startsWith('/admin/courses')) { selectedKeys.value = ['courses']; - } else if (path.startsWith('/admin/packages')) { - selectedKeys.value = ['packages']; + } else if (path.startsWith('/admin/bundles')) { + selectedKeys.value = ['bundles']; } else if (path.startsWith('/admin/themes')) { selectedKeys.value = ['themes']; } else if (path.startsWith('/admin/tenants')) { @@ -184,7 +184,7 @@ const handleMenuSelect = ({ key }: { key: string | number }) => { const routeMap: Record = { dashboard: '/admin/dashboard', courses: '/admin/courses', - packages: '/admin/packages', + bundles: '/admin/bundles', themes: '/admin/themes', tenants: '/admin/tenants', resources: '/admin/resources', diff --git a/reading-platform-frontend/src/views/admin/packages/PackageDetailView.vue b/reading-platform-frontend/src/views/admin/packages/PackageDetailView.vue index 30ded1d..299f122 100644 --- a/reading-platform-frontend/src/views/admin/packages/PackageDetailView.vue +++ b/reading-platform-frontend/src/views/admin/packages/PackageDetailView.vue @@ -1,58 +1,57 @@