guest-reception-system/plugins/mockServer.ts

125 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import type { Plugin } from "vite";
import type { IncomingMessage, ServerResponse } from "node:http";
/*
* 开发用 mock 中间件:后端未就绪时拦截招生辅助数据接口,返回 Admin.NET 统一响应结构。
* 仅在 dev serve 生效apply: "serve"),由 vite.config 根据 VITE_USE_MOCK 决定是否启用。
* 后端就绪后把 .env 的 VITE_USE_MOCK 置空/false 即可。
*/
// value 为后端整数枚举(见 docs/api.md 10.8),前端按整数判断学段。
const CONSULT_STAGES = [
{ label: "高中", value: 1, sort: 1, disabled: false },
{ label: "职教高考", value: 2, sort: 2, disabled: false },
{ label: "综合高中", value: 3, sort: 3, disabled: false },
{ label: "小学", value: 4, sort: 4, disabled: false },
{ label: "初中", value: 5, sort: 5, disabled: false },
{ label: "国际/出国", value: 6, sort: 6, disabled: false },
{ label: "复读", value: 7, sort: 7, disabled: false },
];
const REGION_SCHOOLS: Record<string, string[]> = {
: ["济南市历城第二中学", "济南稼轩学校", "济南市历城区洪家楼高级中学"],
: [
"济南协和双语实验学校(初中部)",
"济南市安生学校(初中部)",
"济南高新区海川中学(民办)",
"济南高新区瀚阳学校(初中部)",
"济南高新区凤凰路学校",
"济南高新区奥体中路学校",
"济南高新区伯乐实验学校",
"济南市育英中学",
"山东省济南第三中学",
],
: ["山东省济南第九中学", "济南市槐荫中学", "济南市济微中学"],
: ["山东省济南第十一中学", "济南市天桥区泺口实验学校"],
: ["山东省平阴县第一中学", "平阴县实验高级中学"],
: ["济南市长清第一中学", "济南市长清中学"],
: ["山东省章丘第四中学", "济南市章丘区实验中学"],
: ["山东省济北中学", "济阳区第一中学"],
};
// 给「市中区」补一些学校,方便测试 VanList 滚动分页(超过 1 页)。
for (let i = 1; i <= 15; i++) {
REGION_SCHOOLS["市中区"].push(`济南市中区实验学校(分校${i}`);
}
const REGION_ORDER = Object.keys(REGION_SCHOOLS);
const REGIONS = REGION_ORDER.map((region, index) => ({
region,
label: region,
schoolCount: REGION_SCHOOLS[region].length,
sort: index + 1,
}));
let autoId = 0;
const ALL_SCHOOLS = REGION_ORDER.flatMap((region) => REGION_SCHOOLS[region].map((schoolName) => ({ id: ++autoId, region, schoolName })));
function sendResult(res: ServerResponse, result: unknown): void {
res.setHeader("Content-Type", "application/json; charset=utf-8");
res.statusCode = 200;
res.end(JSON.stringify({ code: 200, type: "success", message: "", result, extras: null, time: new Date().toISOString() }));
}
function readJson(req: IncomingMessage): Promise<Record<string, unknown>> {
return new Promise((resolve) => {
let raw = "";
req.on("data", (chunk) => (raw += chunk));
req.on("end", () => {
try {
resolve(raw ? JSON.parse(raw) : {});
} catch {
resolve({});
}
});
});
}
const delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
export function mockServer(): Plugin {
return {
name: "admission-mock-server",
apply: "serve",
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
const path = (req.url || "").split("?")[0];
const method = req.method || "GET";
const handler =
method === "GET" && path === "/api/admission/options/consult-stages"
? () => sendResult(res, CONSULT_STAGES)
: method === "GET" && path === "/api/admission/school/regions"
? () => sendResult(res, REGIONS)
: method === "POST" && path === "/api/admission/school/search"
? async () => {
const body = await readJson(req);
const page = Math.max(1, Number(body.page) || 1);
const pageSize = Math.min(100, Math.max(1, Number(body.pageSize) || 20));
let list = ALL_SCHOOLS;
if (body.region) {
list = list.filter((school) => school.region === body.region);
}
if (body.keyword) {
list = list.filter((school) => school.schoolName.includes(String(body.keyword)));
}
const total = list.length;
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const items = list.slice((page - 1) * pageSize, page * pageSize);
sendResult(res, { page, pageSize, total, totalPages, items, hasPrevPage: page > 1, hasNextPage: page < totalPages });
}
: null;
if (!handler) {
next();
return;
}
await delay(250); // 模拟网络延迟
await handler();
});
},
};
}