import request from "@/utils/request"; import { getDemoAnswerKey, getStudentDemoSession, isStudentDemo } from "@/utils/studentDemo"; const newProductSurveyStepOneAnswers = { "selling-point-1": "核心层", "selling-point-2": "有形层", "selling-point-3": "核心层", "selling-point-4": "核心层", "selling-point-5": "有形层", "selling-point-6": "有形层", "selling-point-7": "有形层", "selling-point-8": "延伸层", "selling-point-9": "延伸层", "selling-point-10": "延伸层", "selling-point-11": "有形层", }; function demoResult(taskKey) { try { return JSON.parse(sessionStorage.getItem(getDemoAnswerKey(taskKey)) || "null"); } catch (error) { return null; } } export function getStudentTrainingAnswer(taskKey, params) { if (isStudentDemo()) return Promise.resolve({ code: 200, data: demoResult(taskKey) }); return request({ url: `/api/student-training-answers/${taskKey}`, method: "get", params, }); } export function saveStudentTrainingAnswer(taskKey, data) { if (isStudentDemo()) { const answer = { ...data, teachingClassId: getStudentDemoSession()?.userInfo?.demoTeachingClassId }; sessionStorage.setItem(getDemoAnswerKey(taskKey), JSON.stringify(answer)); return Promise.resolve({ code: 200, data: answer }); } return request({ url: `/api/student-training-answers/${taskKey}`, method: "post", data, headers: { repeatSubmit: false }, }); } export function deleteStudentTrainingAnswer(taskKey, params) { if (isStudentDemo()) { sessionStorage.removeItem(getDemoAnswerKey(taskKey)); return Promise.resolve({ code: 200 }); } return request({ url: `/api/student-training-answers/${taskKey}`, method: "delete", params, }); } export function uploadStudentTrainingFile(data) { if (isStudentDemo()) return Promise.reject(new Error("演示模式不上传文件")); return request({ url: "/common/upload", method: "post", data, headers: { "Content-Type": "multipart/form-data", repeatSubmit: false, }, }); } export function checkNewProductSurveyStepOne(items) { if (isStudentDemo()) { const results = Array.isArray(items) ? items.map((item) => { const correct = newProductSurveyStepOneAnswers[item?.id] === item?.category; return { id: item?.id, correct, message: correct ? "分类正确" : "分类有误,请重新选择", }; }) : []; const correctCount = results.filter((item) => item.correct).length; return Promise.resolve({ code: 200, data: { items: results, correctCount, totalCount: results.length, allCorrect: results.length === Object.keys(newProductSurveyStepOneAnswers).length && correctCount === results.length, }, }); } return request({ url: "/api/student-training-answers/new-product-survey/step-1/check", method: "post", data: { items }, headers: { repeatSubmit: false }, }); } export function checkMarketOpportunitySelectionStepOne(items) { const expected = { "search-count": "市场规模", "demand-supply-ratio": "竞争强度", "transaction-amount": "市场规模", "transaction-growth": "市场潜力", "category-transaction-growth": "市场潜力", "online-products": "竞争强度", "online-merchants": "竞争强度", "organic-traffic-ratio": "运营难度", }; if (isStudentDemo()) { const list = Array.isArray(items) ? items : []; const incorrect = list.find((item) => expected[item?.id] !== item?.category); if (list.length !== Object.keys(expected).length || incorrect) return Promise.reject(new Error("指标分类有误,请根据提示重新选择")); return Promise.resolve({ code: 200, data: { correctCount: Object.keys(expected).length, totalCount: Object.keys(expected).length, allCorrect: true, message: "校验通过" } }); } return request({ url: "/api/student-training-answers/market-opportunity-selection/step-1/check", method: "post", data: { items }, headers: { repeatSubmit: false } }); } export function checkMarketOpportunitySelectionStepTwo(payload) { const weights = Array.isArray(payload?.weights) ? payload.weights : []; const expectedIds = ["search-count", "transaction-amount", "transaction-growth", "category-transaction-growth", "demand-supply-ratio", "online-products", "online-merchants", "organic-traffic-ratio"]; if (isStudentDemo()) { const ids = new Set(weights.map((item) => item?.id)); const total = weights.reduce((sum, item) => sum + Number(item?.weight || 0), 0); if (weights.length !== expectedIds.length || expectedIds.some((id) => !ids.has(id)) || weights.some((item) => !Number.isInteger(Number(item?.weight)) || Number(item.weight) < 1 || Number(item.weight) > 100) || total !== 100) { return Promise.reject(new Error("二级指标权重需为 1 至 100 的整数,且合计必须为 100%")); } return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); } return request({ url: "/api/student-training-answers/market-opportunity-selection/step-2/validate", method: "post", data: { weights }, headers: { repeatSubmit: false } }); } export function checkMarketOpportunitySelectionStepThree(payload) { const scores = Array.isArray(payload?.scores) ? payload.scores : []; const expectedIds = ["smart-fitness-mirror", "smart-bathroom-mirror", "smart-beauty-mirror"]; if (isStudentDemo()) { const ids = new Set(scores.map((item) => item?.id)); if (scores.length !== expectedIds.length || expectedIds.some((id) => !ids.has(id)) || scores.some((item) => !Number.isFinite(Number(item?.score)) || Number(item.score) < 0 || Number(item.score) > 100)) { return Promise.reject(new Error("请为三类产品填写 0 至 100 分的综合得分")); } return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); } return request({ url: "/api/student-training-answers/market-opportunity-selection/step-3/validate", method: "post", data: { scores }, headers: { repeatSubmit: false } }); } export function checkMarketOpportunitySelectionStepFour(payload) { const visualizationUrl = String(payload?.visualizationUrl || "").trim(); if (isStudentDemo()) { if (!visualizationUrl) return Promise.reject(new Error("请上传数据可视化结果图")); return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); } return request({ url: "/api/student-training-answers/market-opportunity-selection/step-4/validate", method: "post", data: { visualizationUrl }, headers: { repeatSubmit: false } }); } function validateNewProductSurveyStepTwoDemo(form) { const value = form || {}; const images = Array.isArray(value.images) ? value.images : []; if (!['京东', '天猫', '抖音电商'].includes(String(value.platform || '').trim())) { throw new Error('请选择调查平台'); } if (!String(value.scene || '').trim()) { throw new Error('请选择调查场景'); } if (value.scene === '其他场景' && !String(value.customScene || '').trim()) { throw new Error('请填写自定义调查场景'); } if (!String(value.keyword || '').trim()) { throw new Error('请填写搜索关键词'); } if (!String(value.productName || '').trim()) { throw new Error('请填写产品名称'); } if (images.length < 1 || images.length > 5 || images.some((image) => !String(image?.url || '').trim())) { throw new Error('请上传 1 至 5 张产品图片'); } return { valid: true, message: '校验通过' }; } export function checkNewProductSurveyStepTwo(form) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateNewProductSurveyStepTwoDemo(form) }); } catch (error) { return Promise.reject(error); } } return request({ url: '/api/student-training-answers/new-product-survey/step-2/validate', method: 'post', data: { ...form, imageUrls: (form?.images || []).map((image) => image?.url) }, headers: { repeatSubmit: false }, }); } function validateNewProductSurveyStepThreeDemo(payload) { const expectedDimensions = ["核心层", "有形层", "延伸层"]; const rows = Array.isArray(payload?.rows) ? payload.rows : []; if (rows.length !== expectedDimensions.length) { throw new Error("请完成核心层、有形层、延伸层分析"); } const dimensions = rows.map((row) => String(row?.dimension || "").trim()); if (new Set(dimensions).size !== expectedDimensions.length || expectedDimensions.some((dimension) => !dimensions.includes(dimension))) { throw new Error("分析维度不完整或重复"); } const invalidRow = rows.find((row) => { const points = Array.isArray(row?.points) ? row.points : []; return points.map((point) => String(point || "").trim()).filter(Boolean).length < 2; }); if (invalidRow) { throw new Error(`${invalidRow.dimension || "该层"}至少填写 2 条分析要点`); } return { valid: true, message: "校验通过" }; } export function checkNewProductSurveyStepThree(payload) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateNewProductSurveyStepThreeDemo(payload) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/new-product-survey/step-3/validate", method: "post", data: payload, headers: { repeatSubmit: false }, }); } function validateNewProductSurveyStepFourDemo(form) { const value = form || {}; const fields = [ ["coreValue", "核心层"], ["tangibleValue", "有形层"], ["extendedValue", "延伸层"], ]; for (const [field, label] of fields) { if (!String(value[field] || "").trim()) { throw new Error(`请填写${label}AI应用价值分析`); } } return { valid: true, message: "校验通过" }; } export function checkNewProductSurveyStepFour(form) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateNewProductSurveyStepFourDemo(form) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/new-product-survey/step-4/validate", method: "post", data: form, headers: { repeatSubmit: false }, }); } const productDevelopmentFactorsStepOneDimensions = ["用户需求真实性", "产品体验闭环", "生态与兼容"]; function validateProductDevelopmentFactorsStepOneDemo(payload) { const factors = Array.isArray(payload?.factors) ? payload.factors : []; if (factors.length !== productDevelopmentFactorsStepOneDimensions.length) { throw new Error("请完成用户需求真实性、产品体验闭环和生态与兼容分析"); } const dimensions = factors.map((factor) => String(factor?.dimension || "").trim()); if (new Set(dimensions).size !== productDevelopmentFactorsStepOneDimensions.length || productDevelopmentFactorsStepOneDimensions.some((dimension) => !dimensions.includes(dimension))) { throw new Error("提交的分析维度不完整或重复"); } const incompleteFactor = factors.find((factor) => !String(factor?.successAnalysis || "").trim() || !String(factor?.failedAnalysis || "").trim()); if (incompleteFactor) { throw new Error(`请完成${incompleteFactor.dimension || "该项"}的成功产品和失败产品分析`); } return { valid: true, message: "校验通过" }; } export function checkProductDevelopmentFactorsStepOne(payload) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepOneDemo(payload) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/product-development-factors/step-1/validate", method: "post", data: payload, headers: { repeatSubmit: false }, }); } const productDevelopmentFactorsStepTwoDimensions = ["技术应用", "合规与认证"]; function validateProductDevelopmentFactorsStepTwoDemo(payload) { const factors = Array.isArray(payload?.factors) ? payload.factors : []; const dimensions = factors.map((factor) => String(factor?.dimension || "").trim()); if (factors.length !== 2 || new Set(dimensions).size !== 2 || productDevelopmentFactorsStepTwoDimensions.some((dimension) => !dimensions.includes(dimension))) throw new Error("请完成技术应用和合规与认证分析"); const incomplete = factors.find((factor) => !String(factor?.successAnalysis || "").trim() || !String(factor?.failedAnalysis || "").trim()); if (incomplete) throw new Error(`请完成${incomplete.dimension || "该项"}的成功产品和失败产品分析`); return { valid: true, message: "校验通过" }; } export function checkProductDevelopmentFactorsStepTwo(payload) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepTwoDemo(payload) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/product-development-factors/step-2/validate", method: "post", data: payload, headers: { repeatSubmit: false } }); } const productDevelopmentFactorsStepThreeDimensions = ["供应链与成本控制", "商业模式", "市场反馈"]; function validateProductDevelopmentFactorsStepThreeDemo(payload) { const factors = Array.isArray(payload?.factors) ? payload.factors : []; const dimensions = factors.map((factor) => String(factor?.dimension || "").trim()); if (factors.length !== productDevelopmentFactorsStepThreeDimensions.length || new Set(dimensions).size !== productDevelopmentFactorsStepThreeDimensions.length || productDevelopmentFactorsStepThreeDimensions.some((dimension) => !dimensions.includes(dimension))) { throw new Error("请完成供应链与成本控制、商业模式和市场反馈分析"); } const incomplete = factors.find((factor) => !String(factor?.successAnalysis || "").trim() || !String(factor?.failedAnalysis || "").trim()); if (incomplete) throw new Error(`请完成${incomplete.dimension || "该项"}的成功产品和失败产品分析`); return { valid: true, message: "校验通过" }; } export function checkProductDevelopmentFactorsStepThree(payload) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepThreeDemo(payload) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/product-development-factors/step-3/validate", method: "post", data: payload, headers: { repeatSubmit: false } }); } export function checkProductDevelopmentFactorsStepFour(payload) { const value = payload || {}; if (isStudentDemo()) { if (!String(value.successFactors || "").trim()) return Promise.reject(new Error("请填写产品成功的关键因素")); if (!String(value.failedFactors || "").trim()) return Promise.reject(new Error("请填写产品失败的关键因素")); return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); } return request({ url: "/api/student-training-answers/product-development-factors/step-4/validate", method: "post", data: payload, headers: { repeatSubmit: false } }); } function validateBusinessFeasibilityStepOneDemo(payload) { const value = payload || {}; const overview = value.projectOverview || {}; const requiredOverview = ["projectBackground", "productPositioning", "projectGoals", "macroEnvironmentConclusion", "competitiveLandscapeConclusion"]; if (requiredOverview.some((key) => !String(overview[key] || "").trim())) throw new Error("请完整填写项目概述"); const market = value.marketAnalysis || {}; const numericFields = ["annualMarketSize", "monthlyGrowthRate", "topBrandMarketShare", "marketConcentrationCr3"]; if (numericFields.some((key) => !Number.isFinite(Number(market[key])) || Number(market[key]) < 0) || !String(market.competitorsPriceRange || "").trim()) throw new Error("请完整填写市场规模与竞争格局分析"); const users = Array.isArray(value.targetUsers) ? value.targetUsers : []; if (users.length !== 3 || users.some((user) => !String(user?.userGroup || "").trim() || !String(user?.scenario || "").trim() || !String(user?.coreNeed || "").trim() || !String(user?.spendingPower || "").trim())) throw new Error("请完整填写 3 类目标用户群体分析"); const models = Array.isArray(value.businessModels) ? value.businessModels : []; if (models.length !== 2 || models.some((model) => !String(model?.module || "").trim() || !String(model?.strategy || "").trim() || !String(model?.explanation || "").trim())) throw new Error("请完整填写商业模式梳理"); const costIds = ["hardware-bom", "manufacturing", "rd-amortization", "channel-marketing", "logistics-warehouse"]; const costs = Array.isArray(value.costs) ? value.costs : []; if (costs.length !== costIds.length || costIds.some((id) => !costs.some((cost) => cost?.id === id && Number.isFinite(Number(cost?.amount)) && Number(cost.amount) >= 0))) throw new Error("请填写全部成本项目"); const totalCost = costs.reduce((total, cost) => total + Number(cost.amount), 0); if (totalCost <= 0 || !Number.isFinite(Number(value.targetPrice)) || Number(value.targetPrice) < totalCost) throw new Error("目标售价需不低于单位总成本,且单位总成本必须大于 0"); const riskTypes = ["技术风险", "市场风险", "供应链风险", "资金风险"]; const risks = Array.isArray(value.risks) ? value.risks : []; if (risks.length !== riskTypes.length || riskTypes.some((type) => !risks.some((risk) => risk?.type === type && String(risk?.description || "").trim() && ["高", "中", "低"].includes(risk?.impact) && String(risk?.response || "").trim()))) throw new Error("请完整填写风险描述、影响程度和应对措施"); const unitGrossProfit = Number(value.targetPrice) - totalCost; return { valid: true, message: "校验通过", unitTotalCost: totalCost.toFixed(2), unitGrossProfit: unitGrossProfit.toFixed(2), grossMargin: (unitGrossProfit / totalCost * 100).toFixed(2) }; } export function checkBusinessFeasibilityStepOne(payload) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateBusinessFeasibilityStepOneDemo(payload) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/business-feasibility/step-1/validate", method: "post", data: payload, headers: { repeatSubmit: false } }); }