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 {
|
export interface CoursePackage {
|
||||||
id: number;
|
id: number | string; // 后端 Long 序列化为 string,避免 JS 精度丢失
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
price: number;
|
price: number;
|
||||||
@ -22,7 +22,7 @@ export interface CoursePackage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PackageCourse {
|
export interface PackageCourse {
|
||||||
id: number; // 课程 ID
|
id: number | string; // 课程 ID,后端 Long 序列化为 string
|
||||||
name: string; // 课程名称
|
name: string; // 课程名称
|
||||||
gradeLevel: string; // 适用年级
|
gradeLevel: string; // 适用年级
|
||||||
sortOrder: number; // 排序号
|
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 });
|
return http.get<{ list: CoursePackage[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/admin/packages', { params });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取套餐详情
|
// 获取套餐详情(id 支持 number | string,避免大整数精度丢失)
|
||||||
export function getPackageDetail(id: number) {
|
export function getPackageDetail(id: number | string) {
|
||||||
return http.get(`/v1/admin/packages/${id}`);
|
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);
|
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}`);
|
return http.delete(`/v1/admin/packages/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置套餐课程
|
// 设置套餐课程(后端期望 JSON 数组 [courseId1, courseId2, ...])
|
||||||
export function setPackageCourses(
|
export function setPackageCourses(
|
||||||
packageId: number,
|
packageId: number | string,
|
||||||
courses: { courseId: number; gradeLevel: string; sortOrder?: number }[],
|
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(
|
export function addCourseToPackage(
|
||||||
packageId: number,
|
packageId: number | string,
|
||||||
data: { courseId: number; gradeLevel: string; sortOrder?: number },
|
data: { courseId: number; gradeLevel: string; sortOrder?: number },
|
||||||
) {
|
) {
|
||||||
return http.post(`/v1/admin/packages/${packageId}/courses`, data);
|
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}`);
|
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`);
|
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);
|
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`);
|
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`);
|
return http.post(`/v1/admin/packages/${id}/offline`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -106,7 +106,7 @@ const formatDate = (date?: string) => {
|
|||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const id = Number(route.params.id);
|
const id = route.params.id as string;
|
||||||
const res = await getPackageDetail(id);
|
const res = await getPackageDetail(id);
|
||||||
pkg.value = res;
|
pkg.value = res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -122,7 +122,7 @@ const handleEdit = () => {
|
|||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
await submitPackage(Number(route.params.id));
|
await submitPackage(route.params.id as string);
|
||||||
message.success('提交成功');
|
message.success('提交成功');
|
||||||
fetchData();
|
fetchData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -132,7 +132,7 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
const handlePublish = async () => {
|
const handlePublish = async () => {
|
||||||
try {
|
try {
|
||||||
await publishPackage(Number(route.params.id));
|
await publishPackage(route.params.id as string);
|
||||||
message.success('发布成功');
|
message.success('发布成功');
|
||||||
fetchData();
|
fetchData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -125,13 +125,13 @@ const router = useRouter();
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const isEdit = computed(() => !!route.params.id);
|
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 saving = ref(false);
|
||||||
const loadingCourses = ref(false);
|
const loadingCourses = ref(false);
|
||||||
const showCourseSelector = ref(false);
|
const showCourseSelector = ref(false);
|
||||||
const availableCourses = ref<any[]>([]);
|
const availableCourses = ref<any[]>([]);
|
||||||
const selectedRowKeys = ref<number[]>([]);
|
const selectedRowKeys = ref<(number | string)[]>([]);
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
@ -142,7 +142,7 @@ const form = reactive({
|
|||||||
gradeLevels: [] as string[],
|
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 = [
|
const courseColumns = [
|
||||||
{ title: '课程包', dataIndex: 'courseName', key: 'courseName' },
|
{ title: '课程包', dataIndex: 'courseName', key: 'courseName' },
|
||||||
@ -239,12 +239,13 @@ const handleSave = async () => {
|
|||||||
gradeLevels: form.gradeLevels,
|
gradeLevels: form.gradeLevels,
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = packageId.value;
|
let id: number | string;
|
||||||
if (isEdit.value) {
|
if (isEdit.value) {
|
||||||
|
id = packageId.value!;
|
||||||
await updatePackage(id, data);
|
await updatePackage(id, data);
|
||||||
} else {
|
} else {
|
||||||
const res = await createPackage(data) as any;
|
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