zhizhi/modules/orders/server.js

235 lines
7.7 KiB
JavaScript
Raw Permalink Normal View History

/**
* server.js - AI 接单系统主服务
* 支持 Docker 部署垃圾过滤环境变量配置
*/
import http from 'http';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { CONFIG, validateConfig } from './config.js';
import {
createOrder, getActiveOrders, getArchivedOrders, getAllOrders, findOrder, getOrder,
updateStatus, addSubTask, updateSubStatus, updateProgress, setExpectedDays,
deliverOrder, archiveOrder, addComment, getRecentOrders, getPendingCount, deleteOrder, flagOrder
} from './order-manager.js';
import { analyzeRequirement, generateConfirmation } from './ai-intake.js';
import { filterOrder, getRateStatus } from './spam-filter.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// 启动检查
const configWarnings = validateConfig();
if (configWarnings.length) configWarnings.forEach(w => console.log(w));
// 路由表
const routes = {
'/' : serveStatic('submit.html', 'text/html'),
'/submit' : serveStatic('submit.html', 'text/html'),
'/admin' : serveStatic('admin.html', 'text/html'),
'/track' : serveStatic('track.html', 'text/html'),
'/style.css' : serveStatic('style.css', 'text/css'),
'/api/orders' : apiOrders,
'/api/orders/active': apiActiveOrders,
'/api/orders/archived': apiArchivedOrders,
'/api/orders/search': apiSearchOrders,
'/api/orders/pending': apiPendingCount,
'/api/orders/recent': apiRecentOrders,
'/api/orders/analyze': apiAnalyze,
'/api/health' : apiHealth,
};
function serveStatic(file, mime) {
return (req, res) => {
const p = path.join(__dirname, 'public', file);
try {
let data = fs.readFileSync(p, 'utf-8');
// 注入站点名称
data = data.replace(/__SITE_NAME__/g, CONFIG.siteName);
data = data.replace(/__ADMIN_TOKEN__/g, CONFIG.adminToken);
res.writeHead(200, { 'Content-Type': mime });
res.end(data);
} catch {
res.writeHead(404);
res.end('Not found');
}
};
}
function json(res, data, code = 200) {
res.writeHead(code, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify(data));
}
function readBody(req) {
return new Promise(resolve => {
let d = '';
req.on('data', c => d += c);
req.on('end', () => { try { resolve(JSON.parse(d)); } catch { resolve({}); } });
});
}
function parseUrl(req) {
return new URL(req.url, `http://localhost:${CONFIG.port}`);
}
function getClientIp(req) {
return req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.socket?.remoteAddress || '0.0.0.0';
}
// --- API Handlers ---
async function apiOrders(req, res) {
const url = parseUrl(req);
const parts = url.pathname.split('/');
const id = parts.length >= 4 ? parts[3] : null;
const ip = getClientIp(req);
// Special sub-paths - redirect to specific handlers
if (id === 'active') return apiActiveOrders(req, res);
if (id === 'archived') return apiArchivedOrders(req, res);
if (id === 'search') return apiSearchOrders(req, res);
if (id === 'pending') return apiPendingCount(req, res);
if (id === 'recent') return apiRecentOrders(req, res);
if (id === 'analyze') return apiAnalyze(req, res);
// GET
if (req.method === 'GET') {
if (id) {
const order = getOrder(id);
if (!order) return json(res, { error: '订单不存在' }, 404);
return json(res, order);
}
return json(res, getAllOrders());
}
// POST — 创建订单(含垃圾过滤)
if (req.method === 'POST') {
const body = await readBody(req);
if (!body.requirements || !body.contact) {
return json(res, { error: '需求和联系方式不能为空' }, 400);
}
// 垃圾过滤三道关
const security = filterOrder(body, ip);
if (!security.allowed) {
return json(res, {
error: '提交被拒绝',
issues: security.issues,
flagged: true,
}, 429);
}
const order = createOrder({
name: body.name,
contact: body.contact,
contactType: body.contactType || '微信',
requirements: body.requirements,
budget: body.budget || '',
expectedDays: parseInt(body.expectedDays) || 0,
source: body.source || 'web',
});
// 如果被标记为可疑,自动标记
if (security.flagged) {
flagOrder(order.id, true, security.flagReasons?.join('; ') || '系统自动标记');
addComment(order.id, {
author: '系统', content: `⚠️ 此订单被自动标记为可疑: ${security.flagReasons?.join('、') || '内容异常'}。请审核。`, side: 'dev'
});
}
// AI 确认(异步)
try {
const msg = await generateConfirmation(order);
addComment(order.id, { author: '系统', content: msg, side: 'dev' });
} catch {}
return json(res, {
success: true,
order,
flagged: security.flagged,
flagReasons: security.flagged ? security.flagReasons : undefined,
}, 201);
}
// PUT
if (req.method === 'PUT') {
const body = await readBody(req);
let result;
if (body.action === 'status') result = updateStatus(id, body.status);
else if (body.action === 'subtask') result = addSubTask(id, body);
else if (body.action === 'sub-status') result = updateSubStatus(id, body.subId, body.status);
else if (body.action === 'comment') result = addComment(id, body);
else if (body.action === 'flag') result = flagOrder(id, body.flagged, body.reason);
else if (body.action === 'progress') result = updateProgress(id, body.progress, body.note);
else if (body.action === 'set-expected') result = setExpectedDays(id, body.days);
else if (body.action === 'deliver') result = deliverOrder(id);
else if (body.action === 'archive') result = archiveOrder(id);
if (!result) return json(res, { error: '操作失败' }, 400);
return json(res, { success: true, data: result });
}
// DELETE
if (req.method === 'DELETE') {
const ok = deleteOrder(id);
return json(res, { success: ok });
}
}
async function apiSearchOrders(req, res) {
const q = parseUrl(req).searchParams.get('q') || '';
json(res, findOrder(q));
}
function apiPendingCount(req, res) {
json(res, { count: getPendingCount() });
}
function apiActiveOrders(req, res) {
json(res, getActiveOrders());
}
function apiArchivedOrders(req, res) {
json(res, getArchivedOrders());
}
function apiRecentOrders(req, res) {
json(res, getRecentOrders(120));
}
async function apiAnalyze(req, res) {
const body = await readBody(req);
if (!body.text) return json(res, { error: '缺少需求文本' }, 400);
try {
const analysis = await analyzeRequirement(body.text);
json(res, analysis || { error: '分析失败' });
} catch (e) {
json(res, { error: e.message }, 500);
}
}
function apiHealth(req, res) {
json(res, {
status: 'ok',
site: CONFIG.siteName,
version: '1.0.0',
aiEnabled: !!CONFIG.deepseekKey && CONFIG.deepseekKey !== 'your-api-key-here',
docker: !!process.env.DOCKER,
});
}
// Server
const server = http.createServer((req, res) => {
const pathname = parseUrl(req).pathname;
if (pathname.startsWith('/api/orders')) return apiOrders(req, res);
const handler = routes[pathname];
if (handler) handler(req, res);
else { res.writeHead(404); res.end('Not found'); }
});
server.listen(CONFIG.port, '0.0.0.0', () => {
console.log(`🧰 zhiqiu-order-system v1.0`);
console.log(` 📋 客户提交: http://0.0.0.0:${CONFIG.port}/submit`);
console.log(` 🔧 管理面板: http://0.0.0.0:${CONFIG.port}/admin`);
console.log(` 🔍 订单追踪: http://0.0.0.0:${CONFIG.port}/track`);
console.log(` 🤖 AI 辅助: ${CONFIG.deepseekKey && CONFIG.deepseekKey !== 'your-api-key-here' ? '已启用' : '未配置'}`);
});