/** * order-manager.js - 订单核心管理模块 * 编号: ZT-YYYYMMDD-NNN, 子任务: -XX * 状态: submitted → reviewing → accepted(开发中) → testing → completed → delivered * 归档: 已完成/已交付订单可移入历史 */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const DATA_FILE = path.join(__dirname, 'data', 'orders.json'); const COUNTER_FILE = path.join(__dirname, 'data', 'counter.json'); const HISTORY_FILE = path.join(__dirname, 'data', 'history.json'); fs.mkdirSync(path.dirname(DATA_FILE), { recursive: true }); function loadOrders() { try { return JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8')); } catch { return []; } } function saveOrders(orders) { fs.writeFileSync(DATA_FILE, JSON.stringify(orders, null, 2)); } function loadCounter() { try { return JSON.parse(fs.readFileSync(COUNTER_FILE, 'utf-8')); } catch { return {}; } } function saveCounter(c) { fs.writeFileSync(COUNTER_FILE, JSON.stringify(c, null, 2)); } function loadHistory() { try { return JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf-8')); } catch { return []; } } function saveHistory(h) { fs.writeFileSync(HISTORY_FILE, JSON.stringify(h, null, 2)); } /** 生成编号 */ export function nextOrderNumber() { const counter = loadCounter(); const today = new Date().toISOString().slice(0, 10).replace(/-/g, ''); counter[today] = (counter[today] || 0) + 1; saveCounter(counter); return `ZT-${today}-${String(counter[today]).padStart(3, '0')}`; } /** 生成子编号 */ export function nextSubNumber(parentId) { const orders = loadOrders(); const o = orders.find(x => x.id === parentId); if (!o) return null; return `${parentId}-${String((o.subs || []).length + 1).padStart(2, '0')}`; } /** 创建订单 */ export function createOrder({ name, contact, contactType, requirements, budget, expectedDays, source }) { const now = new Date().toISOString(); const id = nextOrderNumber(); const order = { id, name: name || '匿名客户', contact, contactType: contactType || '微信', requirements: requirements || '', budget: budget || '', expectedDays: expectedDays || 0, expectedDelivery: '', progress: 0, progressNote: '等待接单', source: source || 'web', status: 'submitted', subs: [], comments: [], timeline: [{ at: now, action: 'created', detail: '订单已提交' }], flagged: false, flagReason: '', archived: false, createdAt: now, updatedAt: now, }; const orders = loadOrders(); orders.push(order); saveOrders(orders); return order; } /** 获取未归档订单 */ export function getActiveOrders() { return loadOrders().filter(o => !o.archived).sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); } /** 获取已归档订单(历史) */ export function getArchivedOrders() { return loadHistory().sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt)); } /** 获取全部(含归档) */ export function getAllOrders() { return [...loadOrders(), ...loadHistory()].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); } /** 搜索 */ export function findOrder(query) { const all = getAllOrders(); return all.filter(o => o.id.toLowerCase().includes(query.toLowerCase()) || (o.contact || '').includes(query) || (o.name || '').includes(query) ); } /** 按 ID 获取(查活跃+历史) */ export function getOrder(id) { return loadOrders().find(o => o.id === id) || loadHistory().find(o => o.id === id) || null; } /** 更新状态 */ export function updateStatus(id, status) { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; const valid = ['submitted', 'reviewing', 'accepted', 'in-progress', 'testing', 'completed', 'delivered']; if (!valid.includes(status)) return null; const now = new Date().toISOString(); order.status = status; order.updatedAt = now; const labels = { submitted:'已提交', reviewing:'审核中', accepted:'已接单', 'in-progress':'制作中', testing:'测试中', completed:'已完成', delivered:'已交付' }; order.timeline.push({ at: now, action: 'status', detail: `状态 → ${labels[status] || status}` }); // 接单时算预计交付日 if (status === 'accepted' && order.expectedDays > 0) { const d = new Date(); d.setDate(d.getDate() + order.expectedDays); order.expectedDelivery = d.toISOString().slice(0, 10); order.progressNote = `预计 ${order.expectedDays} 天内交付`; } saveOrders(orders); return order; } /** 更新进度百分比 + 说明 */ export function updateProgress(id, progress, note = '') { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; const now = new Date().toISOString(); order.progress = Math.max(0, Math.min(100, progress)); order.progressNote = note || order.progressNote; order.updatedAt = now; order.timeline.push({ at: now, action: 'progress', detail: `进度更新: ${order.progress}% - ${order.progressNote}` }); saveOrders(orders); return order; } /** 设定预期天数(接单时) */ export function setExpectedDays(id, days) { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; order.expectedDays = days; const d = new Date(); d.setDate(d.getDate() + days); order.expectedDelivery = d.toISOString().slice(0, 10); order.progressNote = `预计 ${days} 天内交付`; order.updatedAt = new Date().toISOString(); order.timeline.push({ at: order.updatedAt, action: 'estimate', detail: `设定预期 ${days} 天交付` }); saveOrders(orders); return order; } /** 交付(标记为已交付) */ export function deliverOrder(id) { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; const now = new Date().toISOString(); order.status = 'delivered'; order.progress = 100; order.progressNote = '已完成并交付'; order.updatedAt = now; order.timeline.push({ at: now, action: 'deliver', detail: '订单已交付给客户' }); saveOrders(orders); return order; } /** 归档订单(移入历史) */ export function archiveOrder(id) { const orders = loadOrders(); const idx = orders.findIndex(o => o.id === id); if (idx === -1) return null; const order = orders[idx]; order.archived = true; order.updatedAt = new Date().toISOString(); order.timeline.push({ at: order.updatedAt, action: 'archive', detail: '订单已归档' }); const history = loadHistory(); history.push(order); saveHistory(history); orders.splice(idx, 1); saveOrders(orders); return order; } /** 子任务管理 */ export function addSubTask(id, { title, description }) { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; const subId = nextSubNumber(id); const now = new Date().toISOString(); const sub = { subId, title: title || '', description: description || '', status: 'pending', createdAt: now }; order.subs = order.subs || []; order.subs.push(sub); order.updatedAt = now; order.timeline.push({ at: now, action: 'subtask', detail: `添加子任务: ${subId} - ${title}` }); saveOrders(orders); return sub; } export function updateSubStatus(id, subId, status) { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; const sub = order.subs?.find(s => s.subId === subId); if (!sub) return null; sub.status = status; sub.updatedAt = new Date().toISOString(); order.updatedAt = sub.updatedAt; order.timeline.push({ at: sub.updatedAt, action: 'subtask', detail: `子任务 ${subId} → ${status}` }); saveOrders(orders); return sub; } /** 评论 */ export function addComment(id, { author, content, side }) { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; const now = new Date().toISOString(); const comment = { id: `c-${Date.now()}`, author: author || '客户', content: content || '', side: side || 'client', createdAt: now }; order.comments = order.comments || []; order.comments.push(comment); order.updatedAt = now; order.timeline.push({ at: now, action: 'comment', detail: `${side === 'dev' ? '开发者' : '客户'}留言` }); saveOrders(orders); return comment; } /** 标记可疑 */ export function flagOrder(id, flagged, reason = '') { const orders = loadOrders(); const order = orders.find(o => o.id === id); if (!order) return null; order.flagged = flagged; order.flagReason = reason; order.updatedAt = new Date().toISOString(); order.timeline.push({ at: order.updatedAt, action: 'flag', detail: flagged ? `标记可疑: ${reason}` : '解除可疑标记' }); saveOrders(orders); return order; } /** 统计 */ export function getFlaggedOrders() { return loadOrders().filter(o => o.flagged); } export function getRecentOrders(min = 60) { const cutoff = Date.now() - min * 60000; return loadOrders().filter(o => new Date(o.createdAt).getTime() > cutoff); } export function getPendingCount() { return loadOrders().filter(o => !o.archived && ['submitted', 'reviewing'].includes(o.status)).length; } /** 删除订单 */ export function deleteOrder(id) { let orders = loadOrders(); const idx = orders.findIndex(o => o.id === id); if (idx !== -1) { orders.splice(idx, 1); saveOrders(orders); return true; } let history = loadHistory(); const hidx = history.findIndex(o => o.id === id); if (hidx !== -1) { history.splice(hidx, 1); saveHistory(history); return true; } return false; }