Files
training/website/src/services/ops.js
T
Pine 97054a9699 feat: 培训业务 Web(Vite+React)
- user 端:课程/活动/报名/测评/政策/调研
- 后端已并入 server-core/app/training,/api/* 走 opc.pinesound.cn
2026-08-23 22:32:42 +08:00

119 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 运营 / 管理 API 服务(ops service
* -------------------------------------------------------------
* 管理端点(/pine 后台)需要登录 token;公开端点(当期排期)无需。
* 对接 server/mock-api.mjs。
*/
const API_BASE = 'https://opc.pinesound.cn'; // 云超服 FastAPI 后端
const TOKEN_KEY = 'pine_token';
import { cached } from './cache';
function authHeaders() {
const t = localStorage.getItem(TOKEN_KEY);
return { 'Content-Type': 'application/json', ...(t ? { Authorization: `Bearer ${t}` } : {}) };
}
async function api(method, path, body) {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: authHeaders(),
body: body === undefined ? undefined : JSON.stringify(body)
});
const data = await res.json().catch(() => ({}));
if (!res.ok || data.ok === false) throw new Error(data.error || '请求失败');
return data;
}
/* ---------- 后台管理 API(需登录) ---------- */
export const ops = {
stats: () => api('GET', '/api/ops/stats'),
bookings: () => api('GET', '/api/bookings'),
updateBooking: (id, status) => api('PATCH', `/api/bookings/${id}`, { status }),
updateBookingAudit: (id, auditStatus) => api('PATCH', `/api/bookings/${id}`, { auditStatus }),
deleteBooking: (id) => api('DELETE', `/api/bookings/${id}`),
bookAudit: (audit) => api('GET', `/api/bookings?audit=${audit}`),
events: () => api('GET', '/api/events'),
createEvent: (data) => api('POST', '/api/events', data),
updateEvent: (id, data) => api('PUT', `/api/events/${id}`, data),
deleteEvent: (id) => api('DELETE', `/api/events/${id}`),
tests: () => api('GET', '/api/tests'),
policyLogs: () => api('GET', '/api/policy-logs'),
planLogs: () => api('GET', '/api/plan-logs'),
surveyLogs: () => api('GET', '/api/survey-logs')
};
/* ---------- 公开:上报政策测评 / 启动流程结果(失败不影响用户) ---------- */
export async function reportPolicyLog(data) {
try { await api('POST', '/api/policy-logs', data); } catch { /* noop */ }
}
export async function reportPlanLog(data) {
try { await api('POST', '/api/plan-logs', data); } catch { /* noop */ }
}
/* 公开:上报 OPC 创业伙伴调研(匿名可提交;失败不影响用户) */
export async function reportSurveyLog(data) {
try { await api('POST', '/api/survey-logs', data); } catch { /* noop */ }
}
/* 管理端:查看调研记录 */
export async function getSurveyLogs() {
return api('GET', '/api/survey-logs');
}
/* ---------- 公开:当期排期(无后端时返回 null,前端降级静态排期) ---------- */
export async function getCurrentEvents() {
return cached('events:current', 30000, async () => {
try {
const res = await fetch(`${API_BASE}/api/events?current=1`);
const data = await res.json().catch(() => ({}));
if (!res.ok || !data.ok) return null;
return data;
} catch {
return null;
}
});
}
/* ---------- 图像上传(管理端,multipart ---------- */
export async function uploadImage(file) {
const token = localStorage.getItem(TOKEN_KEY);
const fd = new FormData();
fd.append('file', file);
const res = await fetch(`${API_BASE}/api/upload`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: fd
});
const data = await res.json().catch(() => ({}));
if (!res.ok || !data.ok) throw new Error(data.error || '上传失败');
return data; // { ok, url }
}
/* ---------- 公开:政策测评(题目下发 + 结果生成,后端驱动) ---------- */
export async function getPolicyQuestions() {
return cached('policy:questions', 30000, async () => {
try { const r = await api('GET', '/api/policy/questions'); return r.questions || []; } catch { return []; }
});
}
export async function calcPolicy(answers) {
try { const r = await api('POST', '/api/policy/calculate', { answers }); return r.result; } catch { return null; }
}
/* ---------- 公开:启动流程(配置 + 生成,后端驱动) ---------- */
export async function getPlanConfig() {
return cached('plan:config', 30000, async () => {
try { const r = await api('GET', '/api/plan/config'); return r; } catch { return { regions: [], status: [] }; }
});
}
export async function genPlan(opt) {
try { const r = await api('POST', '/api/plan/generate', opt); return r.plan || []; } catch { return []; }
}
/* ---------- 公开:调研(题目下发 + 提交,后端驱动) ---------- */
export async function getSurveyQuestions() {
return cached('survey:questions', 30000, async () => {
try { const r = await api('GET', '/api/survey/questions'); return r; } catch { return null; }
});
}
export async function submitSurvey(data) {
try { return await api('POST', '/api/survey/submit', data); } catch { return null; }
}