fix: 课程套餐管理详情修复 - Long 序列化为 String 避免 JS 精度丢失,修复 PUT 课程列表 JSON 格式
Made-with: Cursor
This commit is contained in:
parent
691e0248a2
commit
f5de4e613d
@ -3,7 +3,7 @@ import { http } from './index';
|
||||
// ==================== 套餐管理 ====================
|
||||
|
||||
export interface CoursePackage {
|
||||
id: number;
|
||||
id: number | string; // 后端 Long 序列化为 string,避免 JS 精度丢失
|
||||
name: string;
|
||||
description?: string;
|
||||
price: number;
|
||||
@ -22,7 +22,7 @@ export interface CoursePackage {
|
||||
}
|
||||
|
||||
export interface PackageCourse {
|
||||
id: number; // 课程 ID
|
||||
id: number | string; // 课程 ID,后端 Long 序列化为 string
|
||||
name: string; // 课程名称
|
||||
gradeLevel: string; // 适用年级
|
||||
sortOrder: number; // 排序号
|
||||
@ -48,8 +48,8 @@ export function getPackageList(params?: PackageListParams) {
|
||||
return http.get<{ list: CoursePackage[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/admin/packages', { params });
|
||||
}
|
||||
|
||||
// 获取套餐详情
|
||||
export function getPackageDetail(id: number) {
|
||||
// 获取套餐详情(id 支持 number | string,避免大整数精度丢失)
|
||||
export function getPackageDetail(id: number | string) {
|
||||
return http.get(`/v1/admin/packages/${id}`);
|
||||
}
|
||||
|
||||
@ -59,53 +59,54 @@ export function createPackage(data: CreatePackageData) {
|
||||
}
|
||||
|
||||
// 更新套餐
|
||||
export function updatePackage(id: number, data: Partial<CreatePackageData>) {
|
||||
export function updatePackage(id: number | string, data: Partial<CreatePackageData>) {
|
||||
return http.put(`/v1/admin/packages/${id}`, data);
|
||||
}
|
||||
|
||||
// 删除套餐
|
||||
export function deletePackage(id: number) {
|
||||
export function deletePackage(id: number | string) {
|
||||
return http.delete(`/v1/admin/packages/${id}`);
|
||||
}
|
||||
|
||||
// 设置套餐课程
|
||||
// 设置套餐课程(后端期望 JSON 数组 [courseId1, courseId2, ...])
|
||||
export function setPackageCourses(
|
||||
packageId: number,
|
||||
courses: { courseId: number; gradeLevel: string; sortOrder?: number }[],
|
||||
packageId: number | string,
|
||||
courses: { courseId: number; gradeLevel?: string; sortOrder?: number }[],
|
||||
) {
|
||||
return http.put(`/v1/admin/packages/${packageId}/courses`, { courses });
|
||||
const courseIds = courses.map((c) => c.courseId);
|
||||
return http.put(`/v1/admin/packages/${packageId}/courses`, courseIds);
|
||||
}
|
||||
|
||||
// 添加课程到套餐
|
||||
export function addCourseToPackage(
|
||||
packageId: number,
|
||||
packageId: number | string,
|
||||
data: { courseId: number; gradeLevel: string; sortOrder?: number },
|
||||
) {
|
||||
return http.post(`/v1/admin/packages/${packageId}/courses`, data);
|
||||
}
|
||||
|
||||
// 从套餐移除课程
|
||||
export function removeCourseFromPackage(packageId: number, courseId: number) {
|
||||
export function removeCourseFromPackage(packageId: number | string, courseId: number | string) {
|
||||
return http.delete(`/v1/admin/packages/${packageId}/courses/${courseId}`);
|
||||
}
|
||||
|
||||
// 提交审核
|
||||
export function submitPackage(id: number) {
|
||||
export function submitPackage(id: number | string) {
|
||||
return http.post(`/v1/admin/packages/${id}/submit`);
|
||||
}
|
||||
|
||||
// 审核套餐
|
||||
export function reviewPackage(id: number, data: { approved: boolean; comment?: string }) {
|
||||
export function reviewPackage(id: number | string, data: { approved: boolean; comment?: string }) {
|
||||
return http.post(`/v1/admin/packages/${id}/review`, data);
|
||||
}
|
||||
|
||||
// 发布套餐
|
||||
export function publishPackage(id: number) {
|
||||
export function publishPackage(id: number | string) {
|
||||
return http.post(`/v1/admin/packages/${id}/publish`);
|
||||
}
|
||||
|
||||
// 下架套餐
|
||||
export function offlinePackage(id: number) {
|
||||
export function offlinePackage(id: number | string) {
|
||||
return http.post(`/v1/admin/packages/${id}/offline`);
|
||||
}
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ const formatDate = (date?: string) => {
|
||||
const fetchData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const id = Number(route.params.id);
|
||||
const id = route.params.id as string;
|
||||
const res = await getPackageDetail(id);
|
||||
pkg.value = res;
|
||||
} catch (error) {
|
||||
@ -122,7 +122,7 @@ const handleEdit = () => {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await submitPackage(Number(route.params.id));
|
||||
await submitPackage(route.params.id as string);
|
||||
message.success('提交成功');
|
||||
fetchData();
|
||||
} catch (error) {
|
||||
@ -132,7 +132,7 @@ const handleSubmit = async () => {
|
||||
|
||||
const handlePublish = async () => {
|
||||
try {
|
||||
await publishPackage(Number(route.params.id));
|
||||
await publishPackage(route.params.id as string);
|
||||
message.success('发布成功');
|
||||
fetchData();
|
||||
} catch (error) {
|
||||
|
||||
@ -125,13 +125,13 @@ const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const isEdit = computed(() => !!route.params.id);
|
||||
const packageId = computed(() => Number(route.params.id));
|
||||
const packageId = computed(() => route.params.id as string | undefined);
|
||||
|
||||
const saving = ref(false);
|
||||
const loadingCourses = ref(false);
|
||||
const showCourseSelector = ref(false);
|
||||
const availableCourses = ref<any[]>([]);
|
||||
const selectedRowKeys = ref<number[]>([]);
|
||||
const selectedRowKeys = ref<(number | string)[]>([]);
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
@ -142,7 +142,7 @@ const form = reactive({
|
||||
gradeLevels: [] as string[],
|
||||
});
|
||||
|
||||
const selectedCourses = ref<{ courseId: number; gradeLevel: string; sortOrder: number; courseName: string }[]>([]);
|
||||
const selectedCourses = ref<{ courseId: number | string; gradeLevel: string; sortOrder: number; courseName: string }[]>([]);
|
||||
|
||||
const courseColumns = [
|
||||
{ title: '课程包', dataIndex: 'courseName', key: 'courseName' },
|
||||
@ -239,12 +239,13 @@ const handleSave = async () => {
|
||||
gradeLevels: form.gradeLevels,
|
||||
};
|
||||
|
||||
let id = packageId.value;
|
||||
let id: number | string;
|
||||
if (isEdit.value) {
|
||||
id = packageId.value!;
|
||||
await updatePackage(id, data);
|
||||
} else {
|
||||
const res = await createPackage(data) as any;
|
||||
id = res.id;
|
||||
id = res.id; // 后端 Long 序列化为 string
|
||||
}
|
||||
|
||||
// 保存课程关联
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.reading.platform.common.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Jackson 配置:将 Long 序列化为 String,避免前端 JavaScript 精度丢失。
|
||||
* JavaScript Number 只能安全表示 2^53 以内的整数,雪花 ID 等大整数会丢失精度。
|
||||
*/
|
||||
@Configuration
|
||||
public class JacksonConfig {
|
||||
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
|
||||
return builder -> {
|
||||
builder.serializerByType(Long.class, ToStringSerializer.instance);
|
||||
builder.serializerByType(Long.TYPE, ToStringSerializer.instance);
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user