First running version

This commit is contained in:
qingjie.du
2026-03-30 17:39:13 +09:00
parent 5ffea3d849
commit bce2a5672c
67 changed files with 16503 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
/**
* Mock Service Server — port 5100
*
* Simulates external/internal enterprise systems for the reference implementation.
* MCPlet handlers call these endpoints; replacing with real services requires only
* updating the mockServiceUrl in reference.yaml.
*/
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dataDir = path.join(__dirname, 'data');
const PORT = 5100;
// ---- Data loaders ----
function loadJson<T>(filename: string): T {
return JSON.parse(fs.readFileSync(path.join(dataDir, filename), 'utf-8')) as T;
}
interface Customer {
customerId: string;
name: string;
email: string;
cancelTendency: string;
cancelTendencyReason: string;
reservations: string[];
}
interface Reservation {
reservationId: string;
customerId: string;
customerName: string;
date: string;
time: string;
partySize: number;
status: string;
notes: string;
}
interface SentEmail {
to: string;
subject: string;
body: string;
sentAt: string;
}
interface SentPost {
platform: string;
content: string;
postedAt: string;
}
// In-memory log for mock sends
const sentEmails: SentEmail[] = [];
const sentPosts: SentPost[] = [];
// ---- Route handlers ----
function handleCrmCustomers(url: URL, res: http.ServerResponse): void {
const filter = url.searchParams.get('filter');
const customers = loadJson<Customer[]>('customers.json');
const result = filter === 'rain_cancel_tendency'
? customers.filter((c) => c.cancelTendency === 'high')
: customers;
sendJson(res, 200, { customers: result, total: result.length });
}
function handleCrmReservations(_url: URL, res: http.ServerResponse): void {
// Fixed mock: return all reservations regardless of date query
const reservations = loadJson<Reservation[]>('reservations.json');
sendJson(res, 200, { reservations, total: reservations.length });
}
function handleErpInventory(url: URL, res: http.ServerResponse): void {
const item = url.searchParams.get('item');
const data = loadJson<{ items: Array<{ category: string }> }>('inventory.json');
const items = item
? data.items.filter((i) => i.category === item || i.category.includes(item))
: data.items;
sendJson(res, 200, { items, total: items.length });
}
function handleWeatherForecast(_url: URL, res: http.ServerResponse): void {
// Fixed mock: always return rainy forecast regardless of date query
const data = loadJson<{ location: string; forecasts: Array<{ date: string }> }>('weather.json');
sendJson(res, 200, { location: data.location, forecasts: data.forecasts });
}
function handleSiteStats(_url: URL, res: http.ServerResponse): void {
sendJson(res, 200, {
site: 'EPARK',
period: 'last_7_days',
pageViews: 12480,
uniqueVisitors: 3920,
reservationPageViews: 2150,
conversionRate: 0.18,
updatedAt: new Date().toISOString(),
});
}
async function handleEmailSend(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
const body = await parseJsonBody<{ to: string; subject: string; body: string }>(req);
const entry: SentEmail = { ...body, sentAt: new Date().toISOString() };
sentEmails.push(entry);
console.log(`[mock-email] Sent to ${body.to}: ${body.subject}`);
sendJson(res, 200, { ok: true, messageId: `mock-${Date.now()}`, sentAt: entry.sentAt });
}
async function handleSnsPost(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
const body = await parseJsonBody<{ platform: string; content: string }>(req);
const entry: SentPost = { ...body, postedAt: new Date().toISOString() };
sentPosts.push(entry);
console.log(`[mock-sns] Posted to ${body.platform}: ${body.content.slice(0, 50)}...`);
sendJson(res, 200, { ok: true, postId: `mock-${Date.now()}`, postedAt: entry.postedAt });
}
function handleSentEmails(_url: URL, res: http.ServerResponse): void {
sendJson(res, 200, { sentEmails, total: sentEmails.length });
}
function handleSentPosts(_url: URL, res: http.ServerResponse): void {
sendJson(res, 200, { sentPosts, total: sentPosts.length });
}
// ---- Router ----
const server = http.createServer((req, res) => {
const url = new URL(req.url ?? '/', `http://localhost:${PORT}`);
const method = req.method ?? 'GET';
try {
if (method === 'GET' && url.pathname === '/crm/customers') {
handleCrmCustomers(url, res); return;
}
if (method === 'GET' && url.pathname === '/crm/reservations') {
handleCrmReservations(url, res); return;
}
if (method === 'GET' && url.pathname === '/erp/inventory') {
handleErpInventory(url, res); return;
}
if (method === 'GET' && url.pathname === '/weather/forecast') {
handleWeatherForecast(url, res); return;
}
if (method === 'GET' && url.pathname === '/site/stats') {
handleSiteStats(url, res); return;
}
if (method === 'POST' && url.pathname === '/email/send') {
void handleEmailSend(req, res); return;
}
if (method === 'POST' && url.pathname === '/sns/post') {
void handleSnsPost(req, res); return;
}
if (method === 'GET' && url.pathname === '/debug/emails') {
handleSentEmails(url, res); return;
}
if (method === 'GET' && url.pathname === '/debug/posts') {
handleSentPosts(url, res); return;
}
sendJson(res, 404, { error: `Not found: ${method} ${url.pathname}` });
} catch (err) {
sendJson(res, 500, { error: (err as Error).message });
}
});
server.listen(PORT, () => {
console.log(`[mock-services] Listening on http://localhost:${PORT}`);
console.log('[mock-services] Endpoints:');
console.log(' GET /crm/customers?filter=rain_cancel_tendency');
console.log(' GET /crm/reservations?date=YYYY-MM-DD');
console.log(' GET /erp/inventory?item=dessert');
console.log(' GET /weather/forecast?date=YYYY-MM-DD');
console.log(' GET /site/stats');
console.log(' POST /email/send');
console.log(' POST /sns/post');
console.log(' GET /debug/emails (sent email log)');
console.log(' GET /debug/posts (sent SNS post log)');
});
// ---- Helpers ----
function sendJson(res: http.ServerResponse, status: number, body: unknown): void {
res.writeHead(status, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(body, null, 2));
}
function parseJsonBody<T>(req: http.IncomingMessage): Promise<T> {
return new Promise((resolve, reject) => {
let data = '';
req.on('data', (chunk: Buffer) => { data += chunk.toString(); });
req.on('end', () => {
try { resolve(JSON.parse(data) as T); }
catch { reject(new Error('Invalid JSON')); }
});
req.on('error', reject);
});
}