| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317 |
- import { Injectable } from '@angular/core';
- import { FmodeParse } from 'fmode-ng/core';
- import { FmodeChatCompletion, completionJSON } from 'fmode-ng/core/agent/chat/completion';
- const Parse = FmodeParse.with('nova');
- /**
- * 室内设计AI分析服务
- * 使用豆包1.6模型进行设计分析
- */
- @Injectable({
- providedIn: 'root'
- })
- export class DesignAnalysisAIService {
-
- // AI模型配置(豆包1.6)
- private readonly AI_MODEL = 'fmode-1.6-cn';
- constructor() {}
- /**
- * 分析参考图片,识别场景类型和设计维度
- */
- async analyzeReferenceImages(options: {
- images: string[];
- textDescription?: string;
- spaceType?: string;
- conversationHistory?: Array<{ role: string; content: string }>;
- deepThinking?: boolean;
- onProgressChange?: (progress: string) => void;
- onContentStream?: (content: string) => void; // 新增:流式内容回调
- loading?: any;
- }): Promise<any> {
- return new Promise(async (resolve, reject) => {
- try {
- // 构建详细的分析提示词
- const prompt = this.buildAnalysisPrompt(
- options.spaceType,
- options.textDescription,
- options.conversationHistory,
- options.deepThinking
- );
- options.onProgressChange?.('正在识别场景和分析设计维度...');
- // 🔥 直接使用图片URL,不转base64(参考ai-k12-daofa的实现)
- console.log('📸 准备传入图片URL到AI模型...');
- console.log('📸 图片URL列表:', options.images);
- // 日志输出,帮助调试
- console.log('🤖 调用豆包1.6模型进行vision分析...');
- console.log('📸 图片数量:', options.images.length);
- console.log('📝 提示词长度:', prompt.length, '字符');
- console.log('🏠 空间类型:', options.spaceType);
- console.log('💬 对话历史:', options.conversationHistory?.length || 0, '条');
-
- // 检查提示词长度(建议不超过10000字符)
- if (prompt.length > 10000) {
- console.warn('⚠️ 提示词过长,可能导致API调用失败');
- }
- // 🔥 使用completionJSON进行vision分析(严格参考ai-k12-daofa的成功实现)
- console.log('🚀 开始调用completionJSON进行vision分析...');
-
- // 定义JSON schema(与提示词中的JSON格式完全一致)
- const outputSchema = `{
- "quickSummary": {
- "colorTone": "色彩基调(如: 暖色调、木色和暖灰色结合)",
- "mainMaterials": "主要材质(如: 软装以木作为主、黑色皮革沙发)",
- "atmosphere": "整体氛围(如: 温暖、舒适、生活气息浓厚)"
- },
- "spaceType": "空间类型(如:客餐厅一体化、主卧、玄关等)",
- "spacePositioning": "空间定位与场景属性的详细分析",
- "layout": "空间布局与动线的详细分析",
- "hardDecoration": "硬装系统细节的详细分析(顶面、墙面、地面、门窗)",
- "colorAnalysis": "色调精准分析(主色调、辅助色、色调关系)",
- "materials": "材质应用解析(自然材质、现代材质、材质对比)",
- "form": "形体与比例分析(空间形体、家具形体、造型细节)",
- "style": "风格与氛围营造(风格识别、氛围手法)",
- "suggestions": "专业优化建议(居住适配、细节优化、落地可行性)",
- "summary": "简洁摘要(格式: 空间类型 | 风格 | 色调 | 氛围)"
- }`;
-
- // 流式内容累积
- let streamContent = '';
-
- try {
- // 🔥 关键:使用completionJSON + vision: true + images (URL数组)
- console.log('📤 发送给AI的提示词:', prompt);
- console.log('📤 JSON Schema:', outputSchema);
- console.log('📤 图片URL:', options.images);
-
- const result = await completionJSON(
- prompt,
- outputSchema, // 🔥 关键:提供JSON schema
- (content) => {
- // 流式回调(模拟)
- console.log('📥 AI流式响应:', typeof content, content);
- if (content && options.onContentStream) {
- streamContent = content;
- // 🔥 关键修复:将JSON转为易读的中文格式化文本
- let displayText: string;
- let jsonObject: any = null;
-
- // 1. 尝试获取JSON对象
- if (typeof content === 'string') {
- // 检查是否是JSON字符串
- const trimmed = content.trim();
- if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
- console.log('🔍 检测到JSON格式字符串,尝试解析...');
- try {
- jsonObject = JSON.parse(trimmed);
- console.log('✅ JSON解析成功,完整对象');
- } catch (e) {
- // 🔥 关键修复:JSON不完整时,提取已完整的字段并格式化显示
- console.log('⚠️ JSON解析失败,尝试提取部分字段...');
-
- // 尝试从不完整的JSON中提取已完成的字段
- jsonObject = this.extractPartialJSON(trimmed);
-
- if (jsonObject && Object.keys(jsonObject).length > 0) {
- console.log('✅ 成功提取部分字段:', Object.keys(jsonObject).join(', '));
- } else {
- // 完全无法提取,显示提示
- displayText = '🔄 正在生成分析结果...';
- console.log('⚠️ 无法提取有效字段,等待更多数据');
- }
- }
- } else {
- // 普通文本,直接显示
- displayText = content;
- }
- } else if (typeof content === 'object') {
- jsonObject = content;
- console.log('📦 收到JSON对象');
- }
-
- // 2. 如果是JSON对象,进行格式化
- if (jsonObject) {
- console.log('🎨 开始格式化JSON对象...');
- displayText = this.formatJSONToText(jsonObject);
-
- // 如果格式化失败或内容过短,使用后备方案
- if (!displayText || displayText.trim().length < 50) {
- console.log('⚠️ formatJSONToText结果过短,使用fallback...');
- displayText = this.fallbackFormatJSON(jsonObject);
- }
-
- // 最后兜底:美化JSON
- if (!displayText || displayText.trim().length < 20) {
- console.log('⚠️ fallback也失败,使用beautifyJSON...');
- displayText = this.beautifyJSON(jsonObject);
- }
-
- console.log('✅ 格式化完成,长度:', displayText.length);
- }
-
- // 3. 如果还没有displayText,使用默认值
- if (!displayText) {
- displayText = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
- }
-
- options.onContentStream(displayText);
- options.onProgressChange?.(`正在分析,已接收 ${displayText.length} 字符...`);
- }
- },
- 2, // 重试次数
- {
- model: this.AI_MODEL,
- vision: true, // 🔥 关键:启用vision
- images: options.images, // 🔥 关键:直接传URL数组
- max_tokens: 8000,
- temperature: 0.3 // 🔥 降低随机性,提高一致性(0.0-1.0,越低越确定)
- }
- );
-
- console.log('📥 AI最终返回结果:', result);
- console.log('📥 返回结果类型:', typeof result);
- // 获取最终内容(result应该就是JSON对象)
- const analysisResult = result;
-
- console.log('✅ AI分析完成,返回JSON对象:', analysisResult);
- console.log('📝 AI返回JSON预览:', JSON.stringify(analysisResult).substring(0, 500));
- // 验证返回的JSON结构
- if (!analysisResult || typeof analysisResult !== 'object') {
- console.error('❌ AI返回格式错误,不是JSON对象');
- reject(new Error('AI返回格式异常,请重试'));
- return;
- }
- // 检查必要字段
- if (!analysisResult.spaceType || !analysisResult.spacePositioning) {
- console.error('❌ AI返回JSON缺少必要字段');
- console.error('🔍 AI返回的完整对象:', analysisResult);
- reject(new Error('AI分析结果不完整,请重试'));
- return;
- }
-
- // 解析JSON结果
- const analysisData = this.parseJSONAnalysis(analysisResult);
-
- console.log('📊 解析后的分析数据:', analysisData);
- // 🔥 关键:在最后发送完整的格式化内容
- if (options.onContentStream && analysisData.formattedContent) {
- console.log('📤 发送最终格式化内容到UI...');
- options.onContentStream(analysisData.formattedContent);
- }
- resolve(analysisData);
- } catch (err: any) {
- console.error('❌ completionJSON失败,详细错误:', err);
- console.error('❌ 错误类型:', err?.constructor?.name);
- console.error('❌ 错误消息:', err?.message);
-
- // 🔥 关键:如果completionJSON失败,尝试使用FmodeChatCompletion作为备选方案
- if (err?.message?.includes('JSON') || err?.message?.includes('格式')) {
- console.warn('⚠️ completionJSON解析失败,尝试使用FmodeChatCompletion备选方案...');
-
- try {
- // 使用FmodeChatCompletion获取纯文本响应
- const textPrompt = this.buildTextAnalysisPrompt(options.spaceType, options.textDescription);
- const messageList = [{
- role: 'user',
- content: textPrompt,
- images: options.images
- }];
-
- const completion = new FmodeChatCompletion(messageList, {
- model: this.AI_MODEL,
- max_tokens: 8000
- });
-
- let fullContent = '';
- const subscription = completion.sendCompletion({
- isDirect: true,
- }).subscribe({
- next: (message: any) => {
- const content = message?.content || '';
- if (content) {
- fullContent = content;
- options.onContentStream?.(content);
- }
-
- if (message?.complete && fullContent) {
- console.log('✅ FmodeChatCompletion备选方案成功,内容长度:', fullContent.length);
- const analysisData = this.parseAnalysisContent(fullContent);
- resolve(analysisData);
- subscription?.unsubscribe();
- }
- },
- error: (err2: any) => {
- console.error('❌ FmodeChatCompletion备选方案也失败:', err2);
- reject(new Error('AI分析失败,请稍后重试'));
- subscription?.unsubscribe();
- }
- });
-
- return; // 使用备选方案,不继续执行下面的reject
- } catch (fallbackErr: any) {
- console.error('❌ 备选方案失败:', fallbackErr);
- }
- }
-
- // 根据错误类型提供更具体的错误信息
- let errorMessage = 'AI分析失败';
- if (err?.message?.includes('500')) {
- errorMessage = 'AI服务暂时不可用(服务器错误),请稍后重试';
- } else if (err?.message?.includes('timeout')) {
- errorMessage = 'AI分析超时,请减少图片数量或简化需求后重试';
- } else if (err?.message?.includes('token')) {
- errorMessage = '提示词过长,请简化描述或减少对话历史';
- } else if (err?.message) {
- errorMessage = `AI分析失败: ${err.message}`;
- }
-
- reject(new Error(errorMessage));
- }
- } catch (error: any) {
- reject(new Error('分析失败: ' + error.message));
- }
- });
- }
- /**
- * 构建纯文本分析提示词(用于FmodeChatCompletion备选方案)
- */
- private buildTextAnalysisPrompt(spaceType?: string, textDescription?: string): string {
- let prompt = `请对图片中的室内设计进行专业分析,从以下8个维度详细展开:
- 一、空间定位与场景属性
- 二、空间布局与动线
- 三、硬装系统细节
- 四、色调精准分析
- 五、材质应用解析
- 六、形体与比例
- 七、风格与氛围营造
- 八、专业优化建议
- 要求:
- 1. 基于图片实际视觉内容进行分析
- 2. 每个维度2-4个段落,每段3-5行
- 3. 使用专业的室内设计术语
- 4. 不使用Markdown符号,使用纯文本格式`;
- if (spaceType) {
- prompt += `\n5. 空间类型参考: ${spaceType}`;
- }
- if (textDescription) {
- prompt += `\n6. 客户需求参考: ${textDescription}`;
- }
- return prompt;
- }
- /**
- * 检测用户是否在进行单维度询问(而非完整分析)
- */
- private detectSingleDimensionQuery(userInput: string): boolean {
- const lowerInput = userInput.toLowerCase();
-
- // 🔥 单维度询问关键词
- const singleDimensionKeywords = [
- // 询问词
- '什么', '如何', '怎么', '哪些', '是否', '有没有',
- // 具体维度
- '色彩', '色调', '颜色', '配色',
- '材质', '材料', '用料',
- '布局', '动线', '空间',
- '灯光', '照明',
- '风格', '氛围',
- '尺寸', '大小', '面积',
- '建议', '优化', '改进',
- // 疑问形式
- '?', '?'
- ];
-
- // 🔥 完整分析关键词(如果包含这些,说明要完整分析)
- const fullAnalysisKeywords = [
- '完整', '全面', '详细分析', '整体分析',
- '重新分析', '再分析一次', '重新生成',
- '全部', '所有维度', '各个方面'
- ];
-
- // 如果包含完整分析关键词,返回false(不是单维度)
- for (const keyword of fullAnalysisKeywords) {
- if (lowerInput.includes(keyword)) {
- return false;
- }
- }
-
- // 如果包含单维度关键词,返回true
- for (const keyword of singleDimensionKeywords) {
- if (lowerInput.includes(keyword)) {
- return true;
- }
- }
-
- // 🔥 如果用户输入很短(<20字),可能是简单询问
- if (userInput.length < 20) {
- return true;
- }
-
- // 默认:如果用户输入很长(>50字),可能是要求完整分析
- return userInput.length < 50;
- }
- /**
- * 构建AI分析提示词(JSON格式输出,兼容completionJSON)
- * 参考ai-k12-daofa的简洁提示词风格
- */
- private buildAnalysisPrompt(spaceType?: string, textDescription?: string, conversationHistory?: Array<{ role: string; content: string }>, deepThinking?: boolean): string {
- // 🔥 全面优化的提示词:支持多种风格,精准识别材质和色调,支持重新分析和单维度问答
- const hasPreviousAnalysis = conversationHistory && conversationHistory.length > 0;
-
- // 🔥 检测用户是否在进行单维度询问
- const isSingleDimensionQuery = hasPreviousAnalysis && textDescription && this.detectSingleDimensionQuery(textDescription);
-
- let prompt = `你是一位专业的室内设计分析师,请仔细观察图片中的室内设计细节`;
-
- // 🔥 如果是单维度询问,使用对话模式
- if (isSingleDimensionQuery) {
- prompt += `,并针对用户的具体问题进行专业回答。
- 【重要说明 - 单维度问答模式】
- • 用户正在进行特定维度的询问(如色彩、材质、布局等)
- • 请直接针对用户的问题进行详细、专业的文字回答
- • 不需要输出完整的JSON结构分析报告
- • 使用自然流畅的语言,就像设计师之间的专业交流
- • 回答应该详细、准确,包含具体的色号、材质名称等专业术语
- 【用户当前问题】:${textDescription}
- 请针对这个问题,给出专业、详细的回答:`;
-
- return prompt;
- }
-
- // 🔥 否则,使用完整分析模式
- prompt += `,并按以下JSON格式输出专业分析:`;
-
- // 🔥 如果有对话历史,说明这是用户提出修正意见后的重新分析
- if (hasPreviousAnalysis) {
- prompt += `
- 【重要说明 - 完整重新分析】
- • 用户已经看过之前的分析,并提出了修正意见或新的要求
- • 请基于用户的反馈,重新生成一份完整的、修正后的分析报告
- • 不要继续之前的分析内容,而是输出一份全新的、完整的JSON分析结果
- • 特别关注用户提到的色调、材质、风格等修正意见,确保新的分析符合用户期望
- 【分析要求】`;
- }
-
- prompt += `
- {
- "quickSummary": {
- "colorTone": "色彩基调(如: 暖白法式偏女性向,象牙白护墙板+浅灰地面+米色木地板,软装点缀豆沙紫/湖蓝色)",
- "mainMaterials": "主要材质(如: 象牙白护墙板+线条装饰、大理石纹理台面、黑色雕刻家具、水晶灯、香槟金灯具、豆沙紫/湖蓝色软装)",
- "atmosphere": "整体氛围(如: 柔暖、精致、优雅、女性向、明亮通透)"
- },
- "spaceType": "空间类型(如:客餐厅一体化、主卧、玄关等)",
- "spacePositioning": "空间定位与场景属性的详细分析",
- "layout": "空间布局与动线的详细分析",
- "hardDecoration": "硬装系统细节的详细分析(顶面、墙面、地面、门窗)",
- "colorAnalysis": "色调精准分析",
- "materials": "材质应用解析",
- "form": "形体与比例分析(空间形体、家具形体、造型细节)",
- "style": "风格与氛围营造",
- "suggestions": "专业优化建议(居住适配、细节优化、落地可行性)",
- "summary": "简洁摘要(格式: 空间类型 | 风格 | 色调 | 氛围)"
- }
- 【核心分析原则 - 保证一致性】
- • 🔥 **客观描述优先**:基于图片中可见的元素进行描述,避免过度解读
- • 🔥 **细节精准识别**:准确识别护墙板、线条、大理石纹理、金属材质等细节
- • 🔥 **色调精准定位**:使用精确的色彩描述(淡奶灰、暖白、米色、木色等)
- • 🔥 **风格准确判断**:根据硬装+软装+色调综合判断(现代法式、侘寂、轻奢等)
- • 🔥 **保持分析一致性**:相同的视觉元素应该得出相同的结论
- 【关键分析要求】
- 0. **快速总结 (quickSummary)** - 🔥 优先级最高:
-
- • 色彩基调(colorTone):精准描述主色调组合
-
- 【现代法式女性向示例】
- - "暖白法式偏女性向,象牙白护墙板+浅灰地面+米色木地板,软装点缀豆沙紫/湖蓝色"
- - "浅色调为主,暖白色/米色为基底,黑色家具+香槟金灯具点缀"
- - "暖白象牙色主导,大理石灰白褐纹呼应,彩色软装增添柔美"
-
- 【侘寂风格示例】
- - "暖色调,木色和暖灰色结合"
- - "自然暖棕+柔和灰,低饱和度舒适系"
-
- 【轻奢风格示例】
- - "高级灰+香槟金,大理石质感"
- - "冷灰主导+金属光泽,精致轻奢"
-
- ⚠️ 必须明确:
- - 冷暖倾向(暖白/冷白/中性)
- - 主色调名称(象牙白/暖白/淡奶灰)
- - 关键配色(软装彩色/黑色对比/金属点缀)
-
- • 主要材质(mainMaterials):按重要性列举关键材质
-
- 【现代法式女性向示例】
- - "象牙白护墙板+线条装饰、大理石纹理台面、黑色雕刻家具、水晶灯、香槟金灯具"
- - "白色岩板、浅灰地砖、米色木地板、豆沙紫/湖蓝色软装、绿植装饰"
-
- 【侘寂风格示例】
- - "木质软装为主、黑色皮革沙发、藤编家具、混凝土墙面"
-
- ⚠️ 优先识别:
- - 硬装:护墙板、线条、大理石/岩板、木地板、瓷砖
- - 软装:家具材质(木质/皮革/布艺)、灯具(水晶/金属)
- - 装饰:花瓶、烛台、绿植、艺术品
-
- • 整体氛围(atmosphere):3-5个关键词
-
- 【现代法式女性向示例】
- - "柔暖、精致、优雅、女性向、浪漫"
- - "明亮通透、轻盈柔美、精致细腻"
-
- 【侘寂风格示例】
- - "温暖、舒适、质朴、生活气息"
-
- ⚠️ 氛围词必须与色调、材质相呼应:
- - 暖白+浅色 = 明亮、通透、柔和
- - 彩色软装 = 女性向、浪漫、柔美
- - 黑色对比+金属 = 精致、优雅、轻奢感
- 1. **色调精准分析 (colorAnalysis)** - 🔥 核心重点:
-
- • 🎨 **硬装基础色精准识别**:
- - **暖白色/象牙白**:偏米色的白色,带微黄调(NCS S 0502-Y50R)
- * 应用:护墙板、墙面涂料、顶面
- * 特征:柔和、温润、不刺眼
-
- - **浅灰色系**:
- * 淡奶灰色:带微黄调/微粉调的浅灰色(NCS S 0502-Y、S 0502-R)
- * 灰蓝色:带微蓝调的浅灰色(如背景墙)
- * 浅灰地面:大理石/瓷砖的自然灰色
-
- - **米色/奶咖色**:
- * 木地板:浅橡木色、枫木色
- * 暖米色墙面:带黄调的浅米色
-
- - **大理石纹理色**:
- * 白色基底+褐色/金色纹理(桌面、台面)
- * 白色基底+蓝绿色纹理(装饰性大理石)
- * 灰白色带细褐纹(地面)
-
- • 🎀 **软装点缀色精准识别** - 女性向关键:
- - **豆沙紫/紫罗兰色**:柔和的紫色调沙发/椅子
- - **湖蓝色/Tiffany蓝**:清新的蓝绿色调椅子/装饰
- - **香槟金/玫瑰金**:金属灯具、装饰件
- - **黑色**:雕刻家具、烛台、电器(形成优雅对比)
- - **绿植色**:自然绿色,点缀生机
-
- • 🔢 **色彩比例量化分析**:
- 【现代法式女性向示例】
- - 主色调(70%):暖白色/象牙白护墙板+墙面
- - 辅助色(20%):浅灰色地面+米色木地板
- - 点缀色(10%):豆沙紫+湖蓝色软装 + 黑色家具 + 香槟金灯具
-
- 【侘寂风格示例】
- - 主色调(60%):暖灰色墙面
- - 辅助色(30%):木色家具
- - 点缀色(10%):黑色皮革
-
- • 🌡️ **冷暖定位精准判断**:
- - **暖色系**:木色、米色、暖灰、奶咖、淡粉、豆沙紫
- - **中性偏暖**:暖白/象牙白、淡奶灰(带微黄调)
- - **清新偏冷**:湖蓝色、Tiffany蓝、灰蓝色
- - **冷色系**:纯灰、冷白、深蓝灰
- - **中性色**:黑色(优雅对比)、金色(精致点缀)
-
- • 🎯 **色调协调性分析** - 重要:
- - 大面积浅色(暖白+浅灰)营造明亮通透感
- - 软装彩色(豆沙紫+湖蓝)增加女性柔美感
- - 黑色家具形成优雅对比,提升精致度
- - 金色饰品点缀轻奢感
- - 自然绿植平衡色彩,增加生机
-
- • ⚠️ **避免模糊词汇,使用精准描述**:
- ❌ "中性灰棕色系" → ✅ "暖白法式,浅色调为主"
- ❌ "浅色系" → ✅ "象牙白护墙板+浅灰地面+米色木地板"
- ❌ "彩色点缀" → ✅ "豆沙紫沙发+湖蓝色椅子+香槟金灯具"
- 2. **材质应用解析 (materials)** - 全面细致:
-
- • 🏗️ **硬装材质全面识别**:
-
- 【墙面系统】
- * **护墙板**(法式关键特征):
- - 颜色:象牙白/暖白/淡奶灰
- - 工艺:凸起线条装饰、方框造型、哑光质感
- - 细节:线条宽度、阴影层次、接缝工艺
-
- * **线条装饰**(法式精髓):
- - 顶角线/腰线/门框线
- - 造型:简约直线/曲线/雕花
- - 材质:石膏/PU/实木
-
- * **涂料墙面**:
- - 暖白色/米色/浅灰色乳胶漆
- - 质感:哑光/丝光
-
- * **大理石/岩板背景墙**:
- - 白色岩板(电视墙/装饰墙)
- - 纹理大理石(背景装饰)
-
- 【地面系统】
- * **大理石地砖**:
- - 浅灰色柔哑面地砖(主要区域)
- - 纹理:细密灰白纹理/大理石自然纹
- - 拼接:直铺/对角铺/拼花
-
- * **木地板**:
- - 浅色木地板(米色/浅橡木色)
- - 工艺:直拼/人字拼/鱼骨拼
- - 质感:哑光/半哑光
-
- 【顶面系统】
- * 石膏线装饰/隐藏式灯带
- * 平顶+局部造型
- * 无主灯设计(筒灯+吊灯组合)
-
- 【门窗系统】
- * 拱门造型(法式经典元素)
- * 门框线条装饰
-
- • 🛋️ **软装材质细节描述** - 女性向重点:
-
- 【家具材质】
- * **桌子**:
- - 大理石圆桌(灰白褐纹/蓝绿纹理)
- - 黑色雕刻底座(手工雕花纹理)
-
- * **沙发/椅子**:
- - 豆沙紫布艺沙发(丝绒/天鹅绒质感)
- - 湖蓝色椅子(皮革/布艺)
- - 曲线造型、包裹感强
-
- * **柜子**:
- - 白色浮雕柜(立体雕花装饰)
- - 储物功能+装饰性
-
- 【灯具材质】
- * **水晶灯**:
- - 透明水晶台灯(切面反光)
- - 白色灯罩(布艺/丝绸质感)
-
- * **金属灯**:
- - 香槟金/玫瑰金树枝造型灯
- - 艺术造型、雕塑感
-
- 【装饰品材质】
- * **花瓶**:
- - 白色陶瓷花瓶(哑光质感)
- - 造型:圆润/曲线/传统
-
- * **烛台**:
- - 黑色烛台(亮面烤漆/陶瓷)
- - 组合摆放,形成节奏感
-
- * **绿植**:
- - 自然枝条(枯枝/绿叶)
- - 点缀生机、柔化空间
-
- * **墙面装饰**:
- - 蝴蝶/鸟类装饰(白色/金色)
- - 艺术挂画
-
- • 🔍 **材质质感精准描述** - 触觉与视觉:
-
- - **护墙板**:哑光漆面,肌理感,立体阴影
- - **大理石**:
- * 柔哑面(不反光,温润触感)
- * 天然纹理(褐色/金色/蓝绿色不规则纹路)
- * 质感层次(深浅交错,自然过渡)
-
- - **木质**:自然木纹,温润触感,哑光/半哑光
- - **金属**:
- * 黑色雕刻(手工痕迹,哑光质感)
- * 香槟金(微光泽,细腻拉丝)
-
- - **水晶/玻璃**:透明清澈,切面反光,精致感
- - **布艺**:丝绒柔软,天鹅绒光泽,包裹感
- - **陶瓷**:哑光白瓷,温润细腻,手工感
- 3. **风格与氛围营造 (style)** - 综合判断:
-
- • 🎭 **风格准确识别** - 基于硬装+软装+色调综合判断:
-
- 【现代法式】关键特征:
- - 硬装:淡奶灰/暖白/象牙白护墙板 + 线条装饰 + 大理石地面 + 拱门造型
- - 软装:水晶灯/金属灯 + 精致家具 + 花艺绿植装饰
- - 色调:暖白/象牙白为主 + 米色/浅灰辅助 + 黑色家具对比
- - 氛围:柔暖、精致、优雅、明亮通透
-
- 【现代法式·女性向】附加特征:
- - 软装彩色:豆沙紫/湖蓝色/淡粉色沙发/椅子
- - 装饰细节:蝴蝶/鸟类墙饰、曲线造型、浮雕柜
- - 灯具选择:水晶台灯、香槟金/玫瑰金灯具
- - 色彩搭配:浅色基底+柔和彩色点缀
- - 氛围升级:女性向、浪漫、轻盈柔美、精致细腻
-
- 【温润侘寂】关键特征:
- - 硬装:暖灰色墙面 + 木地板 + 简约造型
- - 软装:木质家具为主 + 藤编/皮革 + 自然装饰
- - 色调:木色 + 暖灰色 + 低饱和度
- - 氛围:温暖、舒适、质朴、生活气息
-
- 【现代轻奢】关键特征:
- - 硬装:大理石 + 金属线条 + 高级灰
- - 软装:轻奢家具 + 金属饰品 + 艺术挂画
- - 色调:高级灰 + 香槟金/玫瑰金 + 白色
- - 氛围:精致、时尚、轻奢、品质感
-
- • 💫 **氛围判断依据**:
- - 柔暖精致:淡奶灰+暖白+水晶灯+护墙板
- - 温暖舒适:木色+暖灰+木质软装+自然光
- - 清冷克制:纯灰+冷白+极简家具+留白
- - 女性向/浪漫:淡粉色点缀+曲线造型+精致细节
-
- • ⚠️ **避免混淆**:
- - 护墙板+水晶灯+大理石 = 法式,而非侘寂
- - 木质+混凝土+暖灰 = 侘寂,而非法式
- 4. **专业优化建议 (suggestions)**:
- • 🏠 **居住适配** - 基于风格特征提建议:
- - 法式风格:补充淡粉色软装(女儿房)、台盆柜+梳妆台一体化设计
- - 侘寂风格:木质模块化收纳、暖灰色地毯、生活化装饰
- • 🔧 **细节优化**:
- - 材质统一性(护墙板色调协调、大理石纹理呼应)
- - 色彩过渡(淡奶灰→米色→木色的渐变)
- - 隐形工程(筒射灯预埋无边框、空调风口预埋无边框)
- • ✅ **落地可行性**:
- - 材料选择(护墙板材质、大理石品类、木地板工艺)
- - 施工注意事项(拼花对缝、线条安装、灯光预埋)
- 5. **基础要求 - 保证分析质量**:
- • ✅ 基于图片**实际可见元素**进行分析,严禁臆测或模板化
- • ✅ 每个字段提供**详细描述**(200-400字)
- • ✅ 使用**专业室内设计术语**(护墙板、线条、大理石纹理、柔哑面等)
- • ✅ **不提及品牌**,仅描述材质、色调、形态、氛围
- • ✅ **保持客观中立**,避免过度解读或情感化描述
- • ✅ **同一视觉元素=同一结论**,确保分析一致性`;
- // 添加空间类型提示
- if (spaceType) {
- prompt += `\n\n【空间类型参考】: ${spaceType}`;
- }
- // 添加客户需求提示
- if (textDescription) {
- prompt += `\n\n【客户核心需求】: ${textDescription}\n请特别关注客户需求中提到的色调、材质、氛围要求,确保分析结果与需求高度契合`;
- }
-
- // 🔥 强化分析质量要求
- if (hasPreviousAnalysis) {
- // 如果是重新分析,强调要结合用户反馈
- prompt += `\n\n【重要提醒 - 重新分析要点】
- • 仔细阅读用户的修正意见和反馈,理解用户的真实需求
- • 重新审视图片,基于用户指出的方向进行调整
- • 输出一份完整的、修正后的JSON分析报告
- • 使用精准的专业术语(如"淡奶灰色护墙板"而非"灰色墙面")
- • 如果用户指出了色调、材质、风格的具体要求,必须在新报告中体现
- • 保持分析的专业性和完整性,不要只修改某一部分`;
- } else {
- // 如果是首次分析,强调一致性
- prompt += `\n\n【重要提醒 - 保证分析质量】
- • 基于图片中客观可见的元素进行分析,避免主观臆测
- • 使用精准的专业术语(如"淡奶灰色护墙板"而非"灰色墙面")
- • 材质、色调、风格的判断应该保持逻辑一致性
- • 避免使用模糊或可变的描述词汇
- • 确保分析的完整性和专业性`;
- }
-
- return prompt;
- }
- /**
- * 解析JSON格式的AI分析结果(新方法,处理completionJSON返回的JSON对象)
- */
- private parseJSONAnalysis(jsonResult: any): any {
- console.log('📝 [parseJSONAnalysis] 开始解析JSON分析结果...');
- console.log('🔍 [parseJSONAnalysis] JSON对象:', JSON.stringify(jsonResult).substring(0, 300));
- // 将JSON字段转换为易读的格式化文本
- let formattedContent = this.formatJSONToText(jsonResult);
-
- // 🔥 关键:如果formattedContent为空或过短,说明JSON可能没有标准字段
- if (!formattedContent || formattedContent.trim().length < 50) {
- console.warn('⚠️ [parseJSONAnalysis] 格式化内容过短,尝试后备方案...');
- formattedContent = this.fallbackFormatJSON(jsonResult);
- }
-
- // 🔥 最终校验:如果还是为空,使用原始JSON的美化版本
- if (!formattedContent || formattedContent.trim().length < 20) {
- console.warn('⚠️ [parseJSONAnalysis] 后备方案也失败,使用JSON美化版本...');
- formattedContent = this.beautifyJSON(jsonResult);
- }
-
- console.log('✅ [parseJSONAnalysis] 最终格式化内容长度:', formattedContent.length);
- console.log('📝 [parseJSONAnalysis] 内容预览:', formattedContent.substring(0, 200));
- return {
- rawContent: JSON.stringify(jsonResult, null, 2), // 原始JSON
- formattedContent: formattedContent, // 格式化文本(确保有内容)
- structuredData: {
- quickSummary: jsonResult.quickSummary || null, // 🔥 快速总结
- spacePositioning: jsonResult.spacePositioning || '',
- layout: jsonResult.layout || '',
- hardDecoration: jsonResult.hardDecoration || '',
- colorAnalysis: jsonResult.colorAnalysis || '',
- materials: jsonResult.materials || '',
- form: jsonResult.form || '',
- style: jsonResult.style || '',
- suggestions: jsonResult.suggestions || ''
- },
- spaceType: jsonResult.spaceType || '',
- summary: jsonResult.summary || '',
- hasContent: true,
- timestamp: new Date().toISOString()
- };
- }
- /**
- * 美化JSON显示(当所有格式化方法都失败时的最后手段)
- */
- private beautifyJSON(jsonResult: any): string {
- const lines: string[] = [];
-
- for (const [key, value] of Object.entries(jsonResult)) {
- if (value && typeof value === 'string' && value.trim().length > 0) {
- // 将驼峰命名转换为中文标题
- const chineseTitle = this.getChineseTitleForKey(key);
- lines.push(`【${chineseTitle}】\n${value}\n`);
- }
- }
-
- return lines.join('\n') || '分析结果为空,请重新分析';
- }
- /**
- * 将JSON字段名转换为中文标题
- */
- private getChineseTitleForKey(key: string): string {
- const titleMap: { [key: string]: string } = {
- 'spaceType': '空间类型',
- 'spacePositioning': '空间定位与场景属性',
- 'layout': '空间布局与动线',
- 'hardDecoration': '硬装系统细节',
- 'colorAnalysis': '色调精准分析',
- 'materials': '材质应用解析',
- 'form': '形体与比例',
- 'style': '风格与氛围营造',
- 'suggestions': '专业优化建议',
- 'summary': '设计概要'
- };
-
- return titleMap[key] || key;
- }
- /**
- * 将JSON结果转换为易读的文本格式
- */
- private formatJSONToText(jsonResult: any): string {
- console.log('🔄 [formatJSONToText] 开始格式化JSON结果...');
- console.log('🔍 [formatJSONToText] JSON字段数量:', Object.keys(jsonResult).length);
-
- const sections = [];
-
- // 🔥 关键:确保每个字段都被处理,即使内容为空也显示标题
- if (jsonResult.spacePositioning) {
- sections.push(`一、空间定位与场景属性\n\n${jsonResult.spacePositioning}\n`);
- }
- if (jsonResult.layout) {
- sections.push(`二、空间布局与动线\n\n${jsonResult.layout}\n`);
- }
- if (jsonResult.hardDecoration) {
- sections.push(`三、硬装系统细节\n\n${jsonResult.hardDecoration}\n`);
- }
- if (jsonResult.colorAnalysis) {
- sections.push(`四、色调精准分析\n\n${jsonResult.colorAnalysis}\n`);
- }
- if (jsonResult.materials) {
- sections.push(`五、材质应用解析\n\n${jsonResult.materials}\n`);
- }
- if (jsonResult.form) {
- sections.push(`六、形体与比例\n\n${jsonResult.form}\n`);
- }
- if (jsonResult.style) {
- sections.push(`七、风格与氛围营造\n\n${jsonResult.style}\n`);
- }
- if (jsonResult.suggestions) {
- sections.push(`八、专业优化建议\n\n${jsonResult.suggestions}\n`);
- }
-
- const formattedText = sections.join('\n');
- console.log('✅ [formatJSONToText] 格式化完成,长度:', formattedText.length);
- console.log('📝 [formatJSONToText] 内容预览:', formattedText.substring(0, 200));
-
- // 🔥 后备机制:如果格式化结果为空,尝试从JSON直接生成文本
- if (!formattedText || formattedText.trim().length === 0) {
- console.warn('⚠️ [formatJSONToText] 格式化结果为空,使用后备方案...');
- return this.fallbackFormatJSON(jsonResult);
- }
-
- return formattedText;
- }
- /**
- * 从不完整的JSON字符串中提取已完成的字段
- * 🔥 流式传输专用:实时提取部分字段
- */
- private extractPartialJSON(jsonString: string): any {
- console.log('🔧 [extractPartialJSON] 开始提取部分JSON字段...');
- const result: any = {};
-
- // 定义所有可能的字段
- const fields = [
- 'spaceType', 'spacePositioning', 'layout', 'hardDecoration',
- 'colorAnalysis', 'materials', 'form', 'style', 'suggestions', 'summary'
- ];
-
- // 使用正则表达式提取每个字段的完整值
- for (const field of fields) {
- // 匹配 "fieldName": "value" 或 "fieldName": "value...(可能不完整)
- const regex = new RegExp(`"${field}"\\s*:\\s*"([^"]*(?:"[^"]*)*)"`, 'g');
- const match = regex.exec(jsonString);
-
- if (match && match[1]) {
- // 提取到完整的字段值
- result[field] = match[1];
- console.log(`✅ 提取字段 ${field}:`, match[1].substring(0, 50) + '...');
- } else {
- // 尝试提取不完整的值(到字符串末尾)
- const partialRegex = new RegExp(`"${field}"\\s*:\\s*"([^"]*?)(?:"|$)`, 's');
- const partialMatch = partialRegex.exec(jsonString);
-
- if (partialMatch && partialMatch[1] && partialMatch[1].length > 20) {
- // 只有当值足够长时才提取(避免只有几个字符的情况)
- result[field] = partialMatch[1] + '...';
- console.log(`⚠️ 提取不完整字段 ${field}:`, partialMatch[1].substring(0, 50) + '...');
- }
- }
- }
-
- const extractedCount = Object.keys(result).length;
- console.log(`✅ [extractPartialJSON] 提取了 ${extractedCount} 个字段`);
-
- return extractedCount > 0 ? result : null;
- }
- /**
- * 后备格式化方法:当主要方法失败时使用
- */
- private fallbackFormatJSON(jsonResult: any): string {
- const lines: string[] = [];
-
- // 遍历JSON对象的所有字段
- const fieldMap: { [key: string]: string } = {
- 'spaceType': '空间类型',
- 'spacePositioning': '一、空间定位与场景属性',
- 'layout': '二、空间布局与动线',
- 'hardDecoration': '三、硬装系统细节',
- 'colorAnalysis': '四、色调精准分析',
- 'materials': '五、材质应用解析',
- 'form': '六、形体与比例',
- 'style': '七、风格与氛围营造',
- 'suggestions': '八、专业优化建议',
- 'summary': '设计概要'
- };
-
- for (const [key, title] of Object.entries(fieldMap)) {
- if (jsonResult[key] && typeof jsonResult[key] === 'string' && jsonResult[key].trim().length > 0) {
- if (key === 'spaceType' || key === 'summary') {
- lines.push(`${title}:${jsonResult[key]}\n`);
- } else {
- lines.push(`${title}\n\n${jsonResult[key]}\n`);
- }
- }
- }
-
- const result = lines.join('\n');
- console.log('✅ [fallbackFormatJSON] 后备格式化完成,长度:', result.length);
- return result || '暂无分析内容';
- }
- /**
- * 解析AI分析内容(优化版:格式化处理,确保结构清晰)
- * @deprecated 使用parseJSONAnalysis代替
- */
- private parseAnalysisContent(content: string): any {
- console.log('📝 AI返回的原始内容长度:', content.length);
- console.log('📝 AI返回的内容预览:', content.substring(0, 500));
- if (!content || content.length < 50) {
- console.warn('⚠️ AI返回内容过短或为空');
- return {
- rawContent: content,
- formattedContent: content,
- hasContent: false,
- timestamp: new Date().toISOString()
- };
- }
- // 格式化处理:优化段落、间距、结构
- const formattedContent = this.formatAnalysisContent(content);
- // 提取结构化信息
- const structuredData = this.extractStructuredInfo(content);
- return {
- rawContent: content, // 原始AI输出
- formattedContent: formattedContent, // 格式化后的内容
- structuredData: structuredData, // 结构化数据(维度分段)
- hasContent: content.length > 50,
- timestamp: new Date().toISOString()
- };
- }
- /**
- * 格式化分析内容:优化排版、段落、间距
- */
- private formatAnalysisContent(content: string): string {
- let formatted = content;
- // 1. 统一维度标题格式(确保维度标题前后有空行)
- const dimensionPattern = /([一二三四五六七八九十]、[^\n]+)/g;
- formatted = formatted.replace(dimensionPattern, '\n\n$1\n');
- // 2. 处理过长段落:如果段落超过300字,尝试在句号处换行
- const paragraphs = formatted.split('\n');
- const processedParagraphs = paragraphs.map(para => {
- if (para.trim().length > 300) {
- // 在句号、问号、感叹号后添加换行,但保持在段落内
- return para.replace(/([。!?])(?=[^。!?\n]{50,})/g, '$1\n');
- }
- return para;
- });
- formatted = processedParagraphs.join('\n');
- // 3. 清理多余空行(超过2个连续空行压缩为2个)
- formatted = formatted.replace(/\n{3,}/g, '\n\n');
- // 4. 确保维度之间有明确的空行分隔
- formatted = formatted.replace(/(一、|二、|三、|四、|五、|六、|七、|八、)/g, '\n\n$1');
- // 5. 移除开头和结尾的多余空行
- formatted = formatted.trim();
- // 6. 确保每个维度内部段落之间有适当间距
- formatted = formatted.replace(/([。!?])\s*\n(?=[^\n一二三四五六七八])/g, '$1\n\n');
- // 7. 最后清理:确保格式整洁
- formatted = formatted.replace(/\n{3,}/g, '\n\n');
- return formatted;
- }
- /**
- * 提取结构化信息:将内容按维度分段
- */
- private extractStructuredInfo(content: string): any {
- const dimensions: any = {
- spacePositioning: '', // 空间定位与场景属性
- layout: '', // 空间布局与动线
- hardDecoration: '', // 硬装系统细节
- colorAnalysis: '', // 色调精准分析
- materials: '', // 材质应用解析
- form: '', // 形体与比例
- style: '', // 风格与氛围营造
- suggestions: '' // 专业优化建议
- };
- // 按维度标题分割内容
- const dimensionRegex = /([一二三四五六七八]、[^\n]+)\n+([\s\S]*?)(?=\n[一二三四五六七八]、|$)/g;
- let match;
- while ((match = dimensionRegex.exec(content)) !== null) {
- const title = match[1].trim();
- const contentText = match[2].trim();
- // 根据标题关键词匹配到对应维度
- if (title.includes('空间定位') || title.includes('场景属性')) {
- dimensions.spacePositioning = contentText;
- } else if (title.includes('布局') || title.includes('动线')) {
- dimensions.layout = contentText;
- } else if (title.includes('硬装') || title.includes('系统细节')) {
- dimensions.hardDecoration = contentText;
- } else if (title.includes('色调') || title.includes('色彩')) {
- dimensions.colorAnalysis = contentText;
- } else if (title.includes('材质')) {
- dimensions.materials = contentText;
- } else if (title.includes('形体') || title.includes('比例')) {
- dimensions.form = contentText;
- } else if (title.includes('风格') || title.includes('氛围')) {
- dimensions.style = contentText;
- } else if (title.includes('建议') || title.includes('优化')) {
- dimensions.suggestions = contentText;
- }
- }
- return dimensions;
- }
- /**
- * 生成简洁摘要:提取关键信息,适合客服和设计师快速查看
- */
- generateBriefSummary(analysisData: any): string {
- if (!analysisData || !analysisData.rawContent) {
- return '暂无分析内容';
- }
- const content = analysisData.rawContent;
- const summary: string[] = [];
- // 1. 提取空间类型
- const spaceTypeMatch = content.match(/(?:这是|空间为|属于).*?([^\n]{2,20}?(?:空间|客厅|餐厅|卧室|厨房|卫生间|玄关|书房))/);
- if (spaceTypeMatch) {
- summary.push(spaceTypeMatch[1].trim());
- }
- // 2. 提取风格关键词(优化:区分温润vs清冷侘寂)
- const styleKeywords = ['现代', '法式', '简约', '极简', '轻奢', '新中式', '日式', '北欧', '工业风', '台式', '温润侘寂', '侘寂', '美式', '混搭'];
- const foundStyles: string[] = [];
- styleKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundStyles.includes(keyword)) {
- foundStyles.push(keyword);
- }
- });
- if (foundStyles.length > 0) {
- summary.push(foundStyles.slice(0, 2).join('+'));
- }
- // 3. 提取色调关键词(优化:优先识别暖灰色、木色)
- const colorKeywords = [
- '暖灰色', '暖灰', '木色', '木棕', '原木色', '暖棕', '胡桃木色', // 暖色调优先
- '暖色系', '暖调', '米白', '奶白', '米色',
- '冷色系', '冷调', '高级灰', '纯灰' // 冷色调在后
- ];
- const foundColors: string[] = [];
- colorKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundColors.includes(keyword)) {
- foundColors.push(keyword);
- }
- });
- if (foundColors.length > 0) {
- summary.push(foundColors.slice(0, 3).join('、'));
- }
- // 4. 提取氛围关键词(优化:优先识别温暖、舒适、生活气息)
- const moodKeywords = [
- '温暖', '舒适', '生活气息', '温馨', '质朴', // 温暖氛围优先
- '精致', '高级', '优雅', '松弛', '静谧', '时尚',
- '女性向', '男性向', '亲子', '清冷' // 清冷在后
- ];
- const foundMoods: string[] = [];
- moodKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundMoods.includes(keyword)) {
- foundMoods.push(keyword);
- }
- });
- if (foundMoods.length > 0) {
- summary.push(foundMoods.slice(0, 3).join('、'));
- }
- // 5. 提取关键材质(优化:优先识别木材、皮革)
- const materialKeywords = [
- '木材', '木质', '实木', '胡桃木', '橡木', '柚木', // 木材优先
- '皮革', '黑色皮革', // 皮革
- '大理石', '瓷砖', '混凝土', '护墙板', '布艺', '金属', '玻璃', '藤编'
- ];
- const foundMaterials: string[] = [];
- materialKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundMaterials.includes(keyword)) {
- foundMaterials.push(keyword);
- }
- });
- if (foundMaterials.length > 0) {
- summary.push('主要材质:' + foundMaterials.slice(0, 4).join('、'));
- }
- return summary.length > 0 ? summary.join(' | ') : '整体设计基于图片实际内容分析';
- }
- /**
- * 生成客服标注格式:提取客户要求的关键点
- */
- generateCustomerServiceNotes(analysisData: any, customerRequirements?: string): string {
- if (!analysisData) {
- return '暂无标注内容';
- }
- const notes: string[] = [];
-
- // 优先使用JSON格式的structuredData,否则使用rawContent
- const structuredData = analysisData.structuredData;
- const rawContent = analysisData.rawContent || '';
- // 1. 客户要求(如果有)
- if (customerRequirements) {
- notes.push(`【客户要求】\n${customerRequirements}`);
- }
- // 2. 空间类型识别
- let spaceType = '';
- if (structuredData?.spaceType) {
- spaceType = structuredData.spaceType;
- } else {
- // 从rawContent提取
- const spaceMatch = rawContent.match(/(?:这是|空间为|属于).*?([^\n]{2,20}?(?:空间|客厅|餐厅|卧室|厨房|卫生间|玄关|书房|客餐厅一体化|三室两厅|两室一厅))/);
- if (spaceMatch) spaceType = spaceMatch[1].trim();
- }
- if (spaceType) {
- notes.push(`【空间类型】\n${spaceType}`);
- }
- // 3. 风格定位(从结构化数据或rawContent提取)
- let styleInfo = '';
- if (structuredData?.style) {
- // 提取风格关键词
- const styleKeywords = ['现代', '法式', '简约', '极简', '轻奢', '新中式', '日式', '北欧', '工业风', '侘寂', '美式', '台式'];
- const foundStyles: string[] = [];
- styleKeywords.forEach(keyword => {
- if (structuredData.style.includes(keyword) && !foundStyles.includes(keyword)) {
- foundStyles.push(keyword);
- }
- });
- if (foundStyles.length > 0) {
- styleInfo = foundStyles.join('+') + '风格';
- }
-
- // 提取氛围描述
- const moodMatch = structuredData.style.match(/(?:氛围|营造|呈现).*?([^\n。]{5,30}?(?:温馨|舒适|精致|高级|松弛|静谧|优雅|时尚))/);
- if (moodMatch) {
- styleInfo += `,${moodMatch[1].trim()}`;
- }
- } else if (rawContent) {
- // 从rawContent提取风格
- const styleMatch = rawContent.match(/(?:风格|呈现|属于).*?([^\n。]{5,40}?(?:风格|法式|现代|简约|极简))/);
- if (styleMatch) styleInfo = styleMatch[1].trim();
- }
- if (styleInfo) {
- notes.push(`【风格定位】\n${styleInfo}`);
- }
- // 4. 色调要求(从色彩分析提取)
- let colorInfo = '';
- if (structuredData?.colorAnalysis) {
- // 提取主色调
- const mainColorMatch = structuredData.colorAnalysis.match(/主色调[::]\s*([^\n。]{5,50})/);
- if (mainColorMatch) {
- colorInfo = `主色调:${mainColorMatch[1].trim()}`;
- }
-
- // 提取辅助色
- const subColorMatch = structuredData.colorAnalysis.match(/辅助色[::]\s*([^\n。]{5,50})/);
- if (subColorMatch) {
- colorInfo += `\n辅助色:${subColorMatch[1].trim()}`;
- }
- } else if (rawContent) {
- // 从rawContent提取色调
- const colorMatch = rawContent.match(/(?:色调|色彩|主色)[::]\s*([^\n。]{5,50})/);
- if (colorMatch) colorInfo = colorMatch[1].trim();
- }
- if (colorInfo) {
- notes.push(`【色调要求】\n${colorInfo}`);
- }
- // 5. 材质要求(从硬装和材质维度提取)
- const materials: string[] = [];
- if (structuredData?.hardDecoration) {
- // 提取地面材质
- const floorMatch = structuredData.hardDecoration.match(/地面[::]\s*([^\n。]{5,40})/);
- if (floorMatch) materials.push(`地面:${floorMatch[1].trim()}`);
-
- // 提取墙面材质
- const wallMatch = structuredData.hardDecoration.match(/墙面[::]\s*([^\n。]{5,40})/);
- if (wallMatch) materials.push(`墙面:${wallMatch[1].trim()}`);
-
- // 提取顶面材质
- const ceilingMatch = structuredData.hardDecoration.match(/顶面[::]\s*([^\n。]{5,40})/);
- if (ceilingMatch) materials.push(`顶面:${ceilingMatch[1].trim()}`);
- }
-
- // 从材质维度补充
- if (structuredData?.materials && materials.length < 2) {
- const materialMatch = structuredData.materials.match(/(?:主要材质|材质应用)[::]\s*([^\n。]{10,60})/);
- if (materialMatch) materials.push(materialMatch[1].trim());
- }
-
- if (materials.length > 0) {
- notes.push(`【材质要求】\n${materials.join('\n')}`);
- }
- // 6. 空间布局要点
- if (structuredData?.layout) {
- const layoutMatch = structuredData.layout.match(/(?:布局特点|空间关系)[::]\s*([^\n。]{10,60})/);
- if (layoutMatch) {
- notes.push(`【布局要点】\n${layoutMatch[1].trim()}`);
- }
- }
- // 7. 施工注意事项(从优化建议提取)
- if (structuredData?.suggestions) {
- const attentionPoints: string[] = [];
-
- // 提取落地可行性
- const feasibilityMatch = structuredData.suggestions.match(/落地可行性[::]\s*([^\n。]{10,80})/);
- if (feasibilityMatch) attentionPoints.push(feasibilityMatch[1].trim());
-
- // 提取细节优化
- const detailMatch = structuredData.suggestions.match(/细节优化[::]\s*([^\n。]{10,80})/);
- if (detailMatch) attentionPoints.push(detailMatch[1].trim());
-
- if (attentionPoints.length > 0) {
- notes.push(`【施工注意】\n${attentionPoints.join('\n')}`);
- }
- }
- // 8. 品质要求(固定添加)
- notes.push(`【品质要求】\n新客户,需严格把控施工品质和材料质量`);
- return notes.length > 0 ? notes.join('\n\n') : '请根据分析内容补充具体要求';
- }
- /**
- * 生成客户报告(此方法保留以便后续使用)
- */
- async generateClientReport(options: {
- analysisData: any;
- spaceName: string;
- onContentChange?: (content: string) => void;
- loading?: any;
- }): Promise<string> {
- return new Promise(async (resolve, reject) => {
- try {
- // 使用格式化后的内容
- const content = options.analysisData?.formattedContent || options.analysisData?.rawContent || '暂无报告内容';
- resolve(content);
- } catch (error: any) {
- reject(new Error('生成报告失败: ' + error.message));
- }
- });
- }
- }
|