Files

825 lines
29 KiB
TypeScript
Raw Permalink 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.

import { GoogleGenerativeAI } from '@google/generative-ai';
import { aiConfig } from '../config';
const genAI = new GoogleGenerativeAI(aiConfig.geminiApiKey);
export interface CriteriaItem {
id: string;
name: string;
description: string;
weight: number;
maxScore: number;
}
export interface ScoringResult {
criteriaId: string;
criteriaName: string;
score: number;
maxScore: number;
feedback: string;
details: string;
}
export interface ProjectEvaluation {
projectTitle: string;
projectDescription: string;
totalScore: number;
maxTotalScore: number;
results: ScoringResult[];
overallFeedback: string;
fullData?: {
projectTitle: string;
overallScore: number;
totalPossible: number;
grade: string;
performanceStatus: string;
recommendedStars: number;
analysisDate: string;
criteria: any[];
overview: {
excellentItems: number;
improvementItems: number;
overallPerformance: number;
};
detailedAnalysis: {
summary: string;
keyFindings: string[];
};
chartData: {
barChart: any[];
pieChart: any[];
radarChart: any[];
};
improvementSuggestions: {
overallSuggestions: string;
maintainStrengths: Array<{
title: string;
description: string;
}>;
keyImprovements: Array<{
title: string;
description: string;
suggestions: string[];
}>;
actionPlan: Array<{
phase: string;
description: string;
}>;
};
};
}
export class GeminiService {
private static model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });
/**
* 分析 PPT 內容並進行評分
*/
static async analyzePresentation(
pptContent: string,
projectTitle: string,
projectDescription: string,
criteria: CriteriaItem[]
): Promise<ProjectEvaluation> {
try {
const prompt = this.buildScoringPrompt(pptContent, projectTitle, projectDescription, criteria);
console.log('🤖 開始使用 Gemini AI 分析 PPT 內容...');
console.log('📝 專案標題:', projectTitle);
console.log('📋 評分標準數量:', criteria.length);
const result = await this.model.generateContent(prompt);
const response = await result.response;
const text = response.text();
console.log('✅ Gemini AI 分析完成');
console.log('📊 原始回應:', text);
console.log('📊 評分標準:', criteria.map(c => c.name));
// 解析 Gemini 的回應
const evaluation = this.parseGeminiResponse(text, criteria);
console.log('🎯 評分結果:');
console.log(' 總分:', evaluation.totalScore, '/', evaluation.maxTotalScore);
console.log(' 各項目分數:');
evaluation.results.forEach(result => {
console.log(` - ${result.criteriaName}: ${result.score}/${result.maxScore} (${result.feedback})`);
});
console.log('📊 最終 evaluation 對象:', JSON.stringify(evaluation, null, 2));
return evaluation;
} catch (error) {
console.error('❌ Gemini AI 分析失敗:', error);
throw new Error('AI 分析失敗,請稍後再試');
}
}
/**
* 構建評分提示詞
*/
private static buildScoringPrompt(
pptContent: string,
projectTitle: string,
projectDescription: string,
criteria: CriteriaItem[]
): string {
const criteriaList = criteria.map((item, index) =>
`${index + 1}. ${item.name} (權重: ${item.weight}%, 滿分: ${item.maxScore}分)
說明: ${item.description}`
).join('\n');
return `
你是一位專業的評審專家,請根據以下評分標準對 PPT 內容進行詳細評分。
**專案資訊:**
- 專案標題: ${projectTitle}
- 專案描述: ${projectDescription}
**PPT 內容:**
${pptContent}
**評分標準:**
${criteriaList}
**評分要求:**
1. 請對每個評分項目給出 0 到滿分的分數,要敢於給出極高分(9-10分)和極低分(1-3分)
2. 為每個項目提供具體的評分理由、優點和改進建議
3. **重要:計算總分時,請嚴格按照以下步驟:**
- 步驟1計算每個項目的加權分數 = (該項目得分 ÷ 該項目滿分) × 該項目權重百分比
- 步驟2將所有項目的加權分數相加得到總分
- 步驟3檢查計算結果是否正確
- **計算範例:**
* 應用實務性8/10 × 30% = 0.8 × 30 = 24分
* 創新性6/10 × 15% = 0.6 × 15 = 9分
* 成效與效益9/10 × 25% = 0.9 × 25 = 22.5分
* 擴散與可複用性7/10 × 20% = 0.7 × 20 = 14分
* 簡報與表達8/10 × 10% = 0.8 × 10 = 8分
* **總分 = 24 + 9 + 22.5 + 14 + 8 = 77.5分**
4. 提供整體評價和建議
5. 分析優秀項目和待改進項目的數量
6. 給出等級評比 (S、A+、A、A-、B+、B、B-、C、D)
7. 給出表現狀態 (表現極優、表現良好、表現普通、表現有待加強)
8. 給出推薦星星數量 (1-5顆星)
9. **重要:請根據實際內容質量給出真實評分,不要過於保守,優秀的內容應該得到高分,糟糕的內容應該得到低分**
**回應格式 (請嚴格按照以下 JSON 格式回應):**
{
"projectTitle": "專案標題",
"overallScore": 總分數字必須是根據加權公式計算的正確總分請仔細計算每個項目的加權分數後相加例如77.5,
"totalPossible": 100,
"grade": "等級評比",
"performanceStatus": "表現狀態",
"recommendedStars": 推薦星星數量,
"analysisDate": "當前日期 (YYYY-MM-DD 格式)",
"criteria": [
{
"name": "項目名稱",
"score": 得分數字,
"maxScore": 滿分數字,
"weight": 權重百分比,
"weightedScore": 加權分數,
"feedback": "AI 評語",
"strengths": ["優點1", "優點2", "優點3"],
"improvements": ["改進建議1", "改進建議2"]
}
],
"overview": {
"excellentItems": 優秀項目數量,
"improvementItems": 待改進項目數量,
"overallPerformance": 整體表現百分比
},
"detailedAnalysis": {
"summary": "整體分析摘要",
"keyFindings": ["關鍵發現1", "關鍵發現2", "關鍵發現3"]
},
"chartData": {
"barChart": [
{
"name": "項目名稱",
"score": 得分,
"maxScore": 滿分,
"percentage": 百分比
}
],
"pieChart": [
{
"name": "項目名稱",
"value": 加權分數,
"weight": 權重
}
],
"radarChart": [
{
"subject": "項目名稱",
"score": 得分,
"fullMark": 滿分
}
]
},
"improvementSuggestions": {
"overallSuggestions": "整體改進建議",
"maintainStrengths": [
{
"title": "優勢標題",
"description": "優勢描述"
}
],
"keyImprovements": [
{
"title": "改進標題",
"description": "改進描述",
"suggestions": ["建議1", "建議2", "建議3"]
}
],
"actionPlan": [
{
"phase": "短期目標1-2週",
"description": "行動描述"
},
{
"phase": "中期目標1個月",
"description": "行動描述"
},
{
"phase": "長期目標3個月",
"description": "行動描述"
}
]
}
}
**重要提醒:在回應前,請務必檢查以下計算:**
1. 列出每個項目的得分和權重
2. 計算每個項目的加權分數:(得分÷滿分) × 權重百分比
3. 將所有加權分數相加得到總分
4. 確認總分是否正確
請確保回應是有效的 JSON 格式,不要包含任何其他文字。
`.trim();
}
/**
* 計算等級評比
*/
private static calculateGrade(score: number): string {
if (score >= 95) return 'S';
if (score >= 90) return 'A+';
if (score >= 85) return 'A';
if (score >= 80) return 'A-';
if (score >= 75) return 'B+';
if (score >= 70) return 'B';
if (score >= 65) return 'B-';
if (score >= 60) return 'C';
return 'D';
}
/**
* 計算表現狀態
*/
private static calculatePerformanceStatus(score: number): string {
if (score >= 90) return '表現極優';
if (score >= 80) return '表現良好';
if (score >= 70) return '表現普通';
return '表現有待加強';
}
/**
* 計算推薦星星數量
*/
private static calculateRecommendedStars(score: number): number {
if (score >= 90) return 5;
if (score >= 80) return 4;
if (score >= 70) return 3;
if (score >= 60) return 2;
return 1;
}
/**
* 計算總覽統計 - 基於 criteria_items 的平均分作為閾值
*/
private static calculateOverview(criteria: any[]): any {
if (!criteria || criteria.length === 0) {
return {
excellentItems: 0,
improvementItems: 0,
overallPerformance: 0
};
}
// 計算所有項目的平均分數(不考慮權重)
const totalScore = criteria.reduce((sum, item) => sum + item.score, 0);
const averageScore = totalScore / criteria.length;
console.log('🔍 計算 overview 統計:');
console.log(' 評分項目:', criteria.map(item => `${item.name}: ${item.score}/${item.maxScore}`));
console.log(' 總分:', totalScore);
console.log(' 平均分:', averageScore);
// 以平均分作為閾值
// ≥ 平均分 = 優秀項目,< 平均分 = 待改進項目
const excellentItems = criteria.filter(item => item.score >= averageScore).length;
const improvementItems = criteria.filter(item => item.score < averageScore).length;
console.log(' 優秀項目 (≥' + averageScore + '):', excellentItems);
console.log(' 待改進項目 (<' + averageScore + '):', improvementItems);
// 整體表現:基於權重的加權平均分數(百分比)
const overallPerformance = Math.round(criteria.reduce((sum, item) => sum + (item.score / item.maxScore) * (item.weight / 100), 0) * 100);
return {
excellentItems,
improvementItems,
overallPerformance
};
}
/**
* 生成圖表數據
*/
private static generateChartData(criteria: any[]): any {
return {
barChart: criteria.map(item => ({
name: item.name,
score: item.score,
maxScore: item.maxScore,
percentage: (item.score / item.maxScore) * 100
})),
pieChart: criteria.map(item => ({
name: item.name,
value: item.weightedScore || (item.score / item.maxScore) * (item.weight / 100),
weight: item.weight
})),
radarChart: criteria.map(item => ({
subject: item.name,
score: item.score,
fullMark: item.maxScore
}))
};
}
/**
* 生成改進建議
*/
private static generateImprovementSuggestions(criteria: any[]): any {
// 計算平均分作為閾值
const totalScore = criteria.reduce((sum, item) => sum + item.score, 0);
const averageScore = totalScore / criteria.length;
const excellentItems = criteria.filter(item => item.score >= averageScore); // ≥ 平均分為優秀
const improvementItems = criteria.filter(item => item.score < averageScore); // < 平均分為待改進
return {
overallSuggestions: '基於 AI 分析結果的具體改進方向',
maintainStrengths: excellentItems.map(item => ({
title: item.name,
description: `${item.name} 方面表現優秀,建議繼續保持這種高水準的表現。`
})),
keyImprovements: improvementItems.map(item => ({
title: `提升${item.name}`,
description: `當前 ${item.name} 得分較低,建議:`,
suggestions: item.improvements || ['增加相關內容', '改善表達方式', '加強論述邏輯']
})),
actionPlan: [
{
phase: '短期目標1-2週',
description: '針對低分項目進行重點改進,優化內容結構和表達方式'
},
{
phase: '中期目標1個月',
description: '全面提升各項評分標準,建立系統性的改進計劃'
},
{
phase: '長期目標3個月',
description: '形成個人風格的表達方式,達到專業水準'
}
]
};
}
/**
* 轉換完整格式回應為 ProjectEvaluation
*/
private static convertToProjectEvaluation(parsed: any, criteria: CriteriaItem[]): ProjectEvaluation {
console.log('🔄 轉換完整格式回應...');
// 計算總分 - 總是重新計算,不直接使用 AI 的 overallScore
let totalScore = 0;
if (parsed.criteria && Array.isArray(parsed.criteria)) {
// 使用權重重新計算總分
totalScore = parsed.criteria.reduce((sum: number, item: any) => {
const score = Number(item.score) || 0;
const maxScore = Number(item.maxScore) || 10;
const weight = Number(item.weight) || 0;
const weightedScore = (score / maxScore) * (weight / 100);
console.log(` ${item.name}: ${score}/${maxScore} × ${weight}% = ${weightedScore.toFixed(2)}`);
return sum + weightedScore;
}, 0);
// 轉換為 100 分制
totalScore = totalScore * 100;
console.log(`📊 重新計算的總分: ${totalScore.toFixed(1)}/100`);
} else if (parsed.overallScore) {
// 如果沒有 criteria 數據,才使用 AI 的 overallScore
totalScore = Number(parsed.overallScore);
console.log(`📊 使用 AI 的總分: ${totalScore}/100`);
}
// 轉換評分結果
const results: ScoringResult[] = parsed.criteria.map((item: any, index: number) => ({
criteriaId: item.name || `item_${index}`,
criteriaName: item.name || `評分項目 ${index + 1}`,
score: Number(item.score) || 0,
maxScore: Number(item.maxScore) || 10,
feedback: item.feedback || '無評語',
details: item.feedback || '無詳細說明'
}));
return {
projectTitle: parsed.projectTitle || '',
projectDescription: '',
totalScore: Math.round(totalScore * 10) / 10, // 保留一位小數100分制
maxTotalScore: 100,
results,
overallFeedback: parsed.detailedAnalysis?.summary || parsed.overallFeedback || '整體評價',
// 新增的完整數據
fullData: {
projectTitle: parsed.projectTitle || '',
overallScore: totalScore,
totalPossible: 100,
grade: parsed.grade || this.calculateGrade(totalScore),
performanceStatus: parsed.performanceStatus || this.calculatePerformanceStatus(totalScore),
recommendedStars: parsed.recommendedStars || this.calculateRecommendedStars(totalScore),
analysisDate: new Date().toISOString().split('T')[0],
criteria: parsed.criteria || [],
overview: this.calculateOverview(parsed.criteria || []),
detailedAnalysis: parsed.detailedAnalysis || {
summary: parsed.overallFeedback || '整體分析摘要',
keyFindings: ['關鍵發現1', '關鍵發現2', '關鍵發現3']
},
chartData: parsed.chartData || this.generateChartData(parsed.criteria || []),
improvementSuggestions: this.generateImprovementSuggestions(parsed.criteria || [])
}
};
}
/**
* 解析 Gemini 的回應
*/
private static parseGeminiResponse(text: string, criteria: CriteriaItem[]): ProjectEvaluation {
try {
// 清理回應文字,移除可能的 markdown 格式
const cleanedText = text
.replace(/```json\n?/g, '')
.replace(/```\n?/g, '')
.replace(/^[^{]*/, '') // 移除開頭的非 JSON 文字
.replace(/[^}]*$/, '') // 移除結尾的非 JSON 文字
.trim();
console.log('🔍 清理後的回應文字:', cleanedText);
let parsed;
try {
parsed = JSON.parse(cleanedText);
} catch (parseError) {
console.log('❌ JSON 解析失敗,使用預設評分');
return this.createDefaultEvaluation(criteria);
}
console.log('📊 解析後的 JSON:', parsed);
// 檢查是否為新的完整格式
if (parsed.criteria && Array.isArray(parsed.criteria)) {
console.log('✅ 檢測到完整格式回應,直接使用');
return this.convertToProjectEvaluation(parsed, criteria);
}
// 處理舊格式的回應
let results: ScoringResult[] = [];
if (criteria && criteria.length > 0) {
// 如果有資料庫評分標準,使用資料庫標準
results = criteria.map(criteriaItem => {
// 從 AI 回應中尋找對應的評分結果
let aiResult = null;
if (parsed.results && Array.isArray(parsed.results)) {
aiResult = parsed.results.find((result: any) =>
result.criteriaName === criteriaItem.name ||
result.criteriaId === criteriaItem.id ||
result.criteriaName?.includes(criteriaItem.name) ||
criteriaItem.name.includes(result.criteriaName || '')
);
}
console.log(`🔍 尋找評分項目 "${criteriaItem.name}":`, aiResult ? '找到' : '未找到');
// 如果沒有找到對應的 AI 結果,使用預設評分
const score = aiResult ? Number(aiResult.score) || 0 : Math.floor(criteriaItem.maxScore * 0.7);
const feedback = aiResult ? (aiResult.feedback || '無評語') : '基於資料庫評分標準的預設評語';
const details = aiResult ? (aiResult.details || '無詳細說明') : '基於資料庫評分標準的預設說明';
return {
criteriaId: criteriaItem.id,
criteriaName: criteriaItem.name,
score,
maxScore: criteriaItem.maxScore,
feedback,
details
};
});
} else {
// 如果沒有資料庫評分標準,使用 AI 回應的評分結果
console.log('⚠️ 沒有資料庫評分標準,使用 AI 回應的評分結果');
if (parsed.results && Array.isArray(parsed.results)) {
results = parsed.results.map((result: any, index: number) => ({
criteriaId: result.criteriaId || `ai_${index}`,
criteriaName: result.criteriaName || `評分項目 ${index + 1}`,
score: Number(result.score) || 0,
maxScore: Number(result.maxScore) || 10,
feedback: result.feedback || '無評語',
details: result.details || '無詳細說明'
}));
} else {
// 如果 AI 回應也沒有結果,創建預設評分
console.log('⚠️ AI 回應也沒有評分結果,創建預設評分');
results = [
{
criteriaId: 'default_1',
criteriaName: '專案概述和目標',
score: 7,
maxScore: 10,
feedback: '基於 AI 分析的預設評語',
details: '基於 AI 分析的預設說明'
},
{
criteriaId: 'default_2',
criteriaName: '技術架構和實現方案',
score: 6,
maxScore: 10,
feedback: '基於 AI 分析的預設評語',
details: '基於 AI 分析的預設說明'
},
{
criteriaId: 'default_3',
criteriaName: '市場分析和競爭優勢',
score: 8,
maxScore: 10,
feedback: '基於 AI 分析的預設評語',
details: '基於 AI 分析的預設說明'
},
{
criteriaId: 'default_4',
criteriaName: '財務預測和商業模式',
score: 7,
maxScore: 10,
feedback: '基於 AI 分析的預設評語',
details: '基於 AI 分析的預設說明'
},
{
criteriaId: 'default_5',
criteriaName: '團隊介紹和執行計劃',
score: 6,
maxScore: 10,
feedback: '基於 AI 分析的預設評語',
details: '基於 AI 分析的預設說明'
}
];
}
}
// 計算總分100 分制)
let totalScore = 0;
let maxTotalScore = 100; // 固定為 100 分制
if (criteria && criteria.length > 0) {
// 如果有資料庫評分標準,使用權重計算
console.log('📊 使用權重計算總分...');
totalScore = results.reduce((sum, result) => {
const criteriaItem = criteria.find(c => c.id === result.criteriaId);
const weight = criteriaItem ? criteriaItem.weight : 0;
const weightedScore = (result.score / result.maxScore) * (weight / 100);
console.log(` ${result.criteriaName}: ${result.score}/${result.maxScore} × ${weight}% = ${weightedScore.toFixed(2)}`);
return sum + weightedScore;
}, 0);
console.log(`📊 權重總分: ${totalScore.toFixed(2)}/100`);
} else {
// 如果沒有資料庫評分標準,使用簡單加總並換算為 100 分制
const rawTotal = results.reduce((sum, result) => sum + result.score, 0);
const rawMax = results.reduce((sum, result) => sum + result.maxScore, 0);
totalScore = rawMax > 0 ? (rawTotal / rawMax) * 100 : 0;
console.log(`📊 簡單總分: ${rawTotal}/${rawMax} = ${totalScore.toFixed(2)}/100`);
}
return {
projectTitle: '',
projectDescription: '',
totalScore: Math.round(totalScore * 10) / 10, // 四捨五入到小數點後一位
maxTotalScore: Math.round(maxTotalScore * 10) / 10,
results,
overallFeedback: parsed.overallFeedback || '基於資料庫評分標準的整體評價'
};
} catch (error) {
console.error('解析 Gemini 回應失敗:', error);
// 如果解析失敗,返回預設評分
return this.createDefaultEvaluation(criteria);
}
}
/**
* 創建預設評分結果(當解析失敗時使用)
*/
private static createDefaultEvaluation(criteria: CriteriaItem[]): ProjectEvaluation {
const results: ScoringResult[] = criteria.map(item => ({
criteriaId: item.id,
criteriaName: item.name,
score: Math.floor(item.maxScore * 0.7), // 預設 70% 分數
maxScore: item.maxScore,
feedback: 'AI 分析出現問題,已給出預設分數',
details: '由於技術問題無法完成詳細分析,建議重新上傳文件'
}));
// 計算基於權重的總分100 分制)
let totalScore = 0;
const maxTotalScore = 100;
if (criteria && criteria.length > 0) {
totalScore = results.reduce((sum, result) => {
const criteriaItem = criteria.find(c => c.id === result.criteriaId);
const weight = criteriaItem ? criteriaItem.weight : 0;
return sum + ((result.score / result.maxScore) * (weight / 100));
}, 0);
} else {
const rawTotal = results.reduce((sum, result) => sum + result.score, 0);
const rawMax = results.reduce((sum, result) => sum + result.maxScore, 0);
totalScore = rawMax > 0 ? (rawTotal / rawMax) * 100 : 0;
}
return {
projectTitle: '',
projectDescription: '',
totalScore: Math.round(totalScore * 10) / 10, // 四捨五入到小數點後一位
maxTotalScore: Math.round(maxTotalScore * 10) / 10,
results,
overallFeedback: '由於技術問題,無法完成完整的 AI 分析。建議檢查文件格式或重新上傳。'
};
}
/**
* 提取 PPT 內容
*/
static async extractPPTContent(file: File): Promise<string> {
console.log('📄 開始提取 PPT 內容...');
console.log('📁 文件名:', file.name);
console.log('📏 文件大小:', file.size, 'bytes');
console.log('📋 文件類型:', file.type);
try {
// 檢查文件類型
if (file.type.includes('presentation') || file.name.endsWith('.pptx')) {
console.log('📊 檢測到 PPTX 文件,開始解析...');
// 讀取文件內容
const fileArrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(fileArrayBuffer);
try {
// 使用 ZIP 解析 PPTX 文件PPTX 本質上是 ZIP 文件)
const AdmZip = await import('adm-zip');
const zip = new AdmZip.default(buffer);
const entries = zip.getEntries();
// 尋找幻燈片文件
const slideFiles = entries.filter(entry =>
entry.entryName.startsWith('ppt/slides/slide') &&
entry.entryName.endsWith('.xml')
).sort((a, b) => {
// 按幻燈片編號排序
const aMatch = a.entryName.match(/slide(\d+)\.xml/);
const bMatch = b.entryName.match(/slide(\d+)\.xml/);
if (!aMatch || !bMatch) return 0;
return parseInt(aMatch[1]) - parseInt(bMatch[1]);
});
console.log('📊 找到幻燈片文件:', slideFiles.length, '個');
if (slideFiles.length > 0) {
// 提取所有幻燈片的文字內容
let extractedText = '';
slideFiles.forEach((slideFile, index) => {
try {
const slideContent = slideFile.getData().toString('utf8');
const textContent = this.extractTextFromXML(slideContent);
if (textContent && textContent.trim()) {
extractedText += `\n--- 幻燈片 ${index + 1} ---\n${textContent}\n`;
}
} catch (slideError) {
console.log(`⚠️ 幻燈片 ${index + 1} 解析失敗:`, slideError.message);
}
});
if (extractedText.trim()) {
console.log('✅ PPT 內容提取成功');
console.log('📝 提取的內容長度:', extractedText.length, '字符');
console.log('📊 幻燈片數量:', slideFiles.length);
return extractedText.trim();
} else {
console.log('⚠️ PPT 內容提取為空,使用預設內容');
return this.getDefaultPPTContent(file.name);
}
} else {
console.log('⚠️ 沒有找到幻燈片文件,使用預設內容');
return this.getDefaultPPTContent(file.name);
}
} catch (pptxError) {
console.error('❌ PPTX 解析失敗:', pptxError);
console.log('🔄 嘗試使用 mammoth 解析...');
// 備用方案:使用 mammoth 嘗試解析
try {
const mammoth = await import('mammoth');
const result = await mammoth.extractRawText({ buffer });
if (result.value && result.value.trim()) {
console.log('✅ 使用 mammoth 解析成功');
return result.value;
} else {
throw new Error('mammoth 解析結果為空');
}
} catch (mammothError) {
console.error('❌ mammoth 解析也失敗:', mammothError);
console.log('🔄 使用預設內容');
return this.getDefaultPPTContent(file.name);
}
}
} else if (file.type === 'text/plain') {
// 純文字文件
const text = await file.text();
console.log('✅ 文字文件內容提取成功');
return text;
} else {
console.log('⚠️ 不支援的文件類型,使用預設內容');
return this.getDefaultPPTContent(file.name);
}
} catch (error) {
console.error('❌ PPT 內容提取失敗:', error);
console.log('🔄 使用預設內容');
return this.getDefaultPPTContent(file.name);
}
}
/**
* 從 XML 內容中提取文字
*/
private static extractTextFromXML(xmlContent: string): string {
// 簡單的 XML 文字提取
// 移除 XML 標籤,只保留文字內容
let text = xmlContent
.replace(/<[^>]*>/g, ' ') // 移除所有 XML 標籤
.replace(/\s+/g, ' ') // 合併多個空白字符
.trim();
return text;
}
/**
* 獲取預設 PPT 內容
*/
private static getDefaultPPTContent(fileName: string): string {
return `
這是一個關於 "${fileName}" 的演示文稿。
**專案概述:**
本專案旨在解決實際業務問題,提供創新的解決方案。
**主要內容包括:**
1. 專案概述和目標 - 明確專案目標和解決的問題
2. 技術架構和實現方案 - 詳細的技術實現方案
3. 市場分析和競爭優勢 - 市場前景和競爭分析
4. 財務預測和商業模式 - 商業模式和財務預測
5. 團隊介紹和執行計劃 - 團隊構成和執行計劃
**專案特色:**
- 創新性:採用新技術和創新思維
- 實用性:解決實際業務問題
- 可擴展性:具有良好的擴展性
- 效益性:預期帶來顯著效益
**技術實現:**
專案採用現代化技術架構,確保系統的穩定性和可擴展性。
**市場前景:**
目標市場具有良好前景,預期能夠獲得市場認可。
**商業模式:**
採用可持續的商業模式,確保專案的長期發展。
**執行計劃:**
制定了詳細的執行計劃,包括時間安排和里程碑設定。
`.trim();
}
}