interest-search.component.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. import { Component, OnInit, ViewChild } from '@angular/core';
  2. import {
  3. IonTextarea, IonCheckbox, IonList, IonButton, IonContent, IonHeader, IonInput, IonTitle,
  4. IonToolbar, IonItem, IonLabel, IonRadioGroup, IonRadio, IonDatetimeButton, IonDatetime,
  5. IonModal, IonAlert, IonBackButton, IonButtons,
  6. IonIcon,
  7. IonSelectOption,
  8. IonSelect,
  9. IonSpinner
  10. } from '@ionic/angular/standalone';
  11. import { CloudQuery, CloudObject, Pointer } from '../../lib/ncloud'; // 确保路径正确
  12. import { CommonModule, DatePipe } from '@angular/common'; // 导入 CommonModule
  13. import { FormsModule } from '@angular/forms'; // 导入 FormsModule
  14. import { FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
  15. import { AlertController } from '@ionic/angular';
  16. import { CloudUser } from '../../lib/ncloud';
  17. import { UserService } from '../lib/user.service';
  18. import { subscriptionLogsToBeFn } from 'rxjs/internal/testing/TestScheduler';
  19. // 定义接口以确保类型安全
  20. interface Questionnaire {
  21. objectId: string;
  22. createdAt: string;
  23. QuestionnaireId: string;
  24. title: string;
  25. status: string;
  26. questions: string[]; // 修改为字符串数组
  27. }
  28. interface Question {
  29. objectId: string;
  30. createdAt: string;
  31. QuestionId: string;
  32. questionnaireId: string; // 修改为字符串
  33. questionText: string;
  34. options: string[]; // 组
  35. }
  36. interface Option {
  37. objectId: string;
  38. createdAt: string;
  39. OptionId: string;
  40. questionId: string; // 修改为字符串
  41. optionText: string;
  42. isSelected: boolean;
  43. }
  44. interface QuestionnaireResult {
  45. objectId: string;
  46. createdAt: string;
  47. QuestionnaireResultId: string;
  48. userId: Pointer;
  49. questionnaireId: Pointer;
  50. answers: Pointer[];
  51. }
  52. interface UserInterestProfile {
  53. objectId: string;
  54. createdAt: string;
  55. userId: String;
  56. QuestionnaireId: String;
  57. interestTags: String[];
  58. content: String;
  59. }
  60. interface QuestionWithOptions extends Question {
  61. optionsData: Option[];
  62. }
  63. @Component({
  64. selector: 'app-interest-search',
  65. templateUrl: './interest-search.component.html',
  66. styleUrls: ['./interest-search.component.scss'],
  67. standalone: true,
  68. imports: [IonTextarea, IonCheckbox, IonList, IonButton, IonContent, IonHeader, IonInput,
  69. IonTitle, IonToolbar, IonItem, IonLabel, IonRadioGroup, IonRadio, IonDatetimeButton,
  70. IonDatetime, IonModal, CommonModule, FormsModule, IonDatetime, IonModal, IonAlert,
  71. IonBackButton, IonButtons, MarkdownPreviewModule, IonIcon, IonSelectOption, IonSelect,
  72. IonSpinner,
  73. ]
  74. })
  75. export class InterestSearchComponent implements OnInit {
  76. //初始化当前用户
  77. currentUser: CloudUser | null = null; // 声明 currentUser 属性,类型为 CloudUser 或 null
  78. // 固定字段
  79. name: string = '';
  80. birthday: string = '';
  81. maxDate = new Date().toISOString().split('T')[0];
  82. minDate = '1900-01-01';
  83. //记录当前问卷的Id
  84. currentQuestionnaireId: string = '';
  85. // 动态问卷数据
  86. questionnaire: Questionnaire | null = null;
  87. questionsWithOptions: QuestionWithOptions[] = [];
  88. answers: { [questionId: string]: string } = {}; // 存储用户答案
  89. //新增AI分析部分的变量
  90. aiAnalysisResult: { interestTags: string[], content: string } | null = null; // AI 分析结果
  91. isComplete: boolean = false; // 定义完成状态属性,用来标记是否补全完成
  92. @ViewChild(IonModal) modal!: IonModal; // 引入 IonModal 以控制其打开和关闭
  93. modalIsOpen: boolean = false; // 使用 isOpen 控制 Modal 的显示状态
  94. modalContent: string = ''; // 保存弹窗的内容
  95. gender: string = '';
  96. age: number | null = null;
  97. occupation: string = '';
  98. @ViewChild(IonDatetime) datetime!: IonDatetime;
  99. isAnalyzing: boolean = false; // 添加分析状态标志
  100. // 修改问卷ID数组
  101. questionnaires = ['q1', 'q2', 'q3']; // 确保这些ID在数据库中存在
  102. constructor(private userService: UserService) {
  103. //this.currentUser = new CloudUser();
  104. }
  105. // 定义方法,用于获取 <ion-datetime> 组件选择的值
  106. onDateTimeChange(event: any) {
  107. if (event && event.detail && event.detail.value) {
  108. // console.log(event.detail)
  109. // console.log(event.detail.value)
  110. this.birthday = event.detail.value.split('T')[0]; // 只保留日期部分
  111. // 计算年龄
  112. const birthDate = new Date(this.birthday);
  113. const today = new Date();
  114. let age = today.getFullYear() - birthDate.getFullYear();
  115. const monthDiff = today.getMonth() - birthDate.getMonth();
  116. if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
  117. age--;
  118. }
  119. this.age = age;
  120. console.log('Selected date:', this.birthday);
  121. console.log('Calculated age:', this.age);
  122. }
  123. }
  124. alertButtons = ['确定'];
  125. async ngOnInit() {
  126. try {
  127. // 在组件初始化时获取当前用户信息
  128. this.loadCurrentUser();
  129. console.log("加载当前用户信息");
  130. // 重置所有状态
  131. this.resetAllData();
  132. console.log("刷新所有数据");
  133. // 加载新问卷
  134. this.currentQuestionnaireId = this.getRandomQuestionnaire();
  135. await this.loadQuestionnaireData(this.currentQuestionnaireId);
  136. console.log("加载新问卷");
  137. } catch (error) {
  138. console.error('初始化问卷失败:', error);
  139. const toast = document.createElement('ion-toast');
  140. toast.message = '加载问卷失败,请重试';
  141. toast.duration = 2000;
  142. toast.position = 'top';
  143. toast.color = 'danger';
  144. document.body.appendChild(toast);
  145. await toast.present();
  146. }
  147. }
  148. // 添加重置所有数据的方法
  149. private resetAllData() {
  150. // 重置基本信息
  151. this.name = '';
  152. this.gender = '';
  153. this.age = null;
  154. this.birthday = '';
  155. this.occupation = '';
  156. // 重置问卷相关数据
  157. this.answers = {};
  158. this.questionsWithOptions = [];
  159. this.questionnaire = null;
  160. // 重置其他状态
  161. this.modalIsOpen = false;
  162. this.modalContent = '';
  163. this.isAnalyzing = false;
  164. this.aiAnalysisResult = null;
  165. }
  166. // 保留这个新的 getRandomQuestionnaire 方法
  167. getRandomQuestionnaire(): string {
  168. // 随机选择一个问卷ID
  169. const randomIndex = Math.floor(Math.random() * this.questionnaires.length);
  170. const selectedId = this.questionnaires[randomIndex];
  171. console.log('随机选择的问卷索引:', randomIndex, '问卷ID:', selectedId);
  172. return selectedId;
  173. }
  174. // 保留这个新的 loadQuestionnaireData 方法
  175. async loadQuestionnaireData(questionnaireId: string) {
  176. try {
  177. console.log('开始加载问卷:', questionnaireId);
  178. // 清空现有数据
  179. this.questionsWithOptions = [];
  180. this.answers = {};
  181. this.questionnaire = null;
  182. const questionnaireQuery = new CloudQuery("Questionnaire");
  183. questionnaireQuery.equalTo("QuestionnaireId", questionnaireId);
  184. const questionnaireObj = await questionnaireQuery.first();
  185. if (questionnaireObj) {
  186. const questionnaireData = questionnaireObj.data as Questionnaire;
  187. this.questionnaire = {
  188. ...questionnaireData,
  189. objectId: String(questionnaireObj.id)
  190. };
  191. console.log("加载到的问卷数据:", this.questionnaire);
  192. if (this.questionnaire.questions && this.questionnaire.questions.length > 0) {
  193. await this.loadQuestions(this.questionnaire.questions);
  194. } else {
  195. throw new Error('问卷中没有问题');
  196. }
  197. } else {
  198. throw new Error(`未找到 QuestionnaireId 为 ${questionnaireId} 的问卷`);
  199. }
  200. } catch (error) {
  201. console.error("加载问卷数据时出错:", error);
  202. throw error;
  203. }
  204. }
  205. // 保留这个新的 saveProgress 方法
  206. async saveProgress() {
  207. try {
  208. const surveyProgress = new CloudObject("SurveyProgress");
  209. surveyProgress.set({
  210. userId: { __type: "Pointer", className: "_User", objectId: "user1" },
  211. questionnaireId: this.questionnaire?.QuestionnaireId, // 保存当前问卷ID
  212. answers: this.answers,
  213. personalInfo: {
  214. name: this.name,
  215. gender: this.gender,
  216. age: this.age,
  217. birthday: this.birthday,
  218. occupation: this.occupation
  219. },
  220. lastUpdated: new Date(),
  221. isCompleted: false
  222. });
  223. await surveyProgress.save();
  224. const toast = document.createElement('ion-toast');
  225. toast.message = '进度保存成功';
  226. toast.duration = 2000;
  227. toast.position = 'top';
  228. toast.color = 'success';
  229. document.body.appendChild(toast);
  230. await toast.present();
  231. } catch (error) {
  232. console.error('保存进度失败:', error);
  233. const toast = document.createElement('ion-toast');
  234. toast.message = '保存失败,请重试';
  235. toast.duration = 2000;
  236. toast.position = 'top';
  237. toast.color = 'danger';
  238. document.body.appendChild(toast);
  239. await toast.present();
  240. }
  241. }
  242. async loadQuestions(questionIds: string[]) {
  243. this.questionsWithOptions = []; // 初始化问题列表
  244. for (const questionId of questionIds) {
  245. try {
  246. const questionQuery = new CloudQuery("Question");
  247. questionQuery.equalTo("QuestionId", questionId);
  248. const questionObj = await questionQuery.first();
  249. if (questionObj) {
  250. const question = questionObj.data as Question;
  251. // 异步加载选项并立即显示问题
  252. this.questionsWithOptions.push({ ...question, optionsData: [] });
  253. this.loadOptions(question.options).then((options) => {
  254. const index = this.questionsWithOptions.findIndex(
  255. (q) => q.QuestionId === question.QuestionId
  256. );
  257. if (index !== -1) {
  258. this.questionsWithOptions[index].optionsData = options;
  259. }
  260. });
  261. // 可选:每加载一个题,立即触发渲染
  262. console.log("已加载问题:", question);
  263. }
  264. } catch (error) {
  265. console.error(`加载问题 ID ${questionId} 时出错:`, error);
  266. }
  267. }
  268. }
  269. async loadOptions(optionIds: string[]): Promise<Option[]> {
  270. try {
  271. if (!optionIds || optionIds.length === 0) return [];
  272. const optionQuery = new CloudQuery("Option");
  273. optionQuery.containedIn("OptionId", optionIds); // 批量查询
  274. const optionObjs = await optionQuery.find();
  275. return optionObjs.map((optionObj: any) => optionObj.data as Option);
  276. } catch (error) {
  277. console.error("加载选项时出错:", error);
  278. return [];
  279. }
  280. }
  281. // 保存功能(可选)
  282. async save() {
  283. try {
  284. // 实现保存逻辑,例如保存到本地存储或发送到后台
  285. console.log("保存的答案:", this.answers);
  286. console.log("姓名:", this.name);
  287. console.log("生日:", this.birthday);
  288. } catch (error) {
  289. console.error("保存答案时出错:", error);
  290. }
  291. }
  292. // 提交功能
  293. async submit() {
  294. try {
  295. if (!this.questionnaire) {
  296. console.error("未加载问卷数据。");
  297. return;
  298. }
  299. // 显示加载状态
  300. this.isAnalyzing = true;
  301. this.modalIsOpen = true;
  302. // 创建一个数组保存选的 OptionId
  303. const answersArray: string[] = [];
  304. // 遍历每个问题,获取用户选择的选��
  305. for (const question of this.questionsWithOptions) {
  306. const selectedOptionId = this.answers[question.QuestionId];
  307. if (selectedOptionId) {
  308. // 将选中的 OptionId 存入 answersArray
  309. answersArray.push(selectedOptionId);
  310. }
  311. }
  312. // 创建一个新的 QuestionnaireResult 对象
  313. const questionnaireResult = new CloudObject("QuestionnaireResult");
  314. // 设置 QuestionnaireResult 的属性
  315. questionnaireResult.set({
  316. QuestionnaireResultId: `qr_${new Date().getTime()}`, // 生成唯一的 QuestionnaireResultId
  317. userId: { __type: "Pointer", className: "_User", objectId: "user1" }, // 替换为实际的用户ID
  318. // 使用 Pointer 类型的引用方式来设置 questionnaireId
  319. questionnaireId: { __type: "Pointer", className: "Questionnaire", objectId: this.questionnaire.objectId },
  320. answers: answersArray // 将选中的 OptionId 数组存入 answers 字段
  321. });
  322. // 保存 QuestionnaireResult 对象
  323. await questionnaireResult.save();
  324. console.log("问卷提交成功。");
  325. // 构建用于 AI 模型分析提示词
  326. const aiPrompt = this.createAiPrompt(answersArray);
  327. // 调用 AI 模型分析,强制等待结果
  328. const aiResponse = await this.callAiModel(aiPrompt);
  329. // 如果 AI 响应有效,执行以下逻辑
  330. if (aiResponse) {
  331. this.aiAnalysisResult = aiResponse; // 保存 AI 响应结果
  332. await this.saveAnalysisResult(aiResponse); // 保存到数据库
  333. // 准备显示结果
  334. this.modalContent = this.formatContent(
  335. JSON.stringify({
  336. interestTags: this.aiAnalysisResult.interestTags,
  337. content: this.aiAnalysisResult.content
  338. })
  339. );
  340. // 关闭加载状态
  341. this.isAnalyzing = false;
  342. }
  343. } catch (error) {
  344. console.error("提交问卷时出错:", error);
  345. this.isAnalyzing = false; // 确保出错时也关闭加载状态
  346. this.modalIsOpen = false;
  347. // 显示错误提示
  348. const toast = document.createElement('ion-toast');
  349. toast.message = '分析失败,请重试';
  350. toast.duration = 2000;
  351. toast.position = 'top';
  352. toast.color = 'danger';
  353. document.body.appendChild(toast);
  354. await toast.present();
  355. }
  356. }
  357. // 生成 AI 模型的提示词
  358. createAiPrompt(answersArray: string[]): string {
  359. const questionTexts = this.questionsWithOptions.map(q => q.questionText);
  360. const optionTexts = answersArray.map(optionId => {
  361. // 找到对应的 Option,并提取其 optionText
  362. const option = this.questionsWithOptions
  363. .flatMap(q => q.optionsData) // 从每个问题的 optionsData 获取 Option 对象
  364. .find(o => o.OptionId === optionId);
  365. return option ? option.optionText : ''; // 返回选项文本
  366. });
  367. return `
  368. 您是一名专业的兴趣分析师,请根据用户填写的问卷内容以及选项分析用户的兴趣并且生成以下格式的响应:
  369. {
  370. "interestTags": ["标签1", "标签2", "标签3", "标签4"], // 生成用户最感兴趣的四个标签(如:书法、绘画、摄影)
  371. "content": "标签描述" // 针对每个标签生成简洁、生动的描述,帮助用户更清楚了解兴趣特点。描述可包括用户行为、倾向和相关建议。
  372. }
  373. 请根据以下信息进行分析:
  374. 问题:${questionTexts.join(',')}
  375. 选项:${optionTexts.join(',')}
  376. 意:
  377. - 仅选择用户**最感兴趣**的四个标签。
  378. - 生成的描述需要具体、生动,反映用户的兴趣深度或行为倾向。
  379. - 请忽略与用户兴趣无关的内容。
  380. - 标签和描述应通俗易懂,适合用户直接阅读。
  381. `;
  382. }
  383. async callAiModel(prompt: string): Promise<{ interestTags: string[], content: string }> {
  384. try {
  385. const completion = new FmodeChatCompletion([
  386. { role: "system", content: "您是一个专业的兴趣分析助手。" },
  387. { role: "user", content: prompt }
  388. ]);
  389. let fullContent = '';
  390. let count = 0;
  391. return new Promise((resolve, reject) => {
  392. completion.sendCompletion().subscribe({
  393. next: (message: any) => {
  394. if (message.content) {
  395. try {
  396. console.log('Received content:', message.content);
  397. fullContent = message.content;
  398. // 判断消息是否完成
  399. if (message?.complete) {
  400. this.isComplete = true;
  401. }
  402. // 如果消息完成且内容符合 JSON 格式,则解析
  403. if (this.isComplete) {
  404. const cleanedContent = fullContent.trim();
  405. // 检查是否有效的 JSON 格式
  406. if (cleanedContent.startsWith('{') && cleanedContent.endsWith('}')) {
  407. try {
  408. // 清理掉换行符和多余空格
  409. let finalContent = cleanedContent.replace(/[\r\n]+/g, ''); // 去掉换行符
  410. finalContent = finalContent.replace(/\s+/g, ' '); // 去掉多余的空格
  411. console.log(finalContent);
  412. // 解析 JSON
  413. const parsedResponse = JSON.parse(finalContent);
  414. // 如果解析成功并且格式正确
  415. if (parsedResponse && parsedResponse.interestTags && parsedResponse.content) {
  416. const { interestTags, content } = parsedResponse;
  417. // 如果 content 是对象类型,转化成字符串
  418. let contentStr = '';
  419. if (typeof content === 'string') {
  420. contentStr = content; // 如果已经是字符串,直接使用
  421. } else if (typeof content === 'object') {
  422. // 如果对,转换为 JSON 字符串
  423. contentStr = JSON.stringify(content, null, 2); // 美化 JSON 字符串格式
  424. count = 0;
  425. this.isComplete = false;
  426. }
  427. resolve({
  428. interestTags: Array.isArray(interestTags) ? interestTags : [],
  429. content: contentStr
  430. });
  431. } else {
  432. reject(new Error("AI 返回的内容格式不正确"));
  433. }
  434. } catch (err) {
  435. console.log(fullContent);
  436. console.error("解析 AI 响应失败:", err);
  437. reject(new Error("解 AI 响应失败"));
  438. }
  439. } else {
  440. reject(new Error("返回的内容不是有效的 JSON 格式"));
  441. }
  442. }
  443. } catch (err) {
  444. console.error("处理消息时出错:", err);
  445. reject(new Error("处理消息时出错"));
  446. }
  447. } else {
  448. if (count !== 0) {
  449. console.error("AI 返回的消息为空");
  450. reject(new Error("AI 返回的消息为空"));
  451. }
  452. count = 1;
  453. }
  454. },
  455. error: (err) => {
  456. console.error("AI 模型调用失败:", err);
  457. reject(new Error("AI 模型调用失败"));
  458. },
  459. complete: () => {
  460. // 可以在这里处理完成后的操作
  461. console.log("AI 请求完成");
  462. }
  463. });
  464. });
  465. } catch (error) {
  466. console.error("AI 模型调用失败:", error);
  467. throw new Error("AI 模型调用失败");
  468. }
  469. }
  470. // 格式化 AI 响应内容
  471. formatContent(content: string): string {
  472. try {
  473. const contentObj = JSON.parse(content);
  474. // 构建兴趣标签 HTML
  475. const tagsHtml = contentObj.interestTags.map((tag: string) => `
  476. <div class="tag-item">
  477. <div class="tag-icon">
  478. <ion-icon name="star"></ion-icon>
  479. </div>
  480. <span>${tag}</span>
  481. </div>
  482. `).join('');
  483. // 处理分析内容,将内容按句号分段并添加样式
  484. const contentText = contentObj.content;
  485. const paragraphs = contentText
  486. .replace(/\"/g, '') // 移除转义字符如 \"
  487. .replace(/\n/g, '') // 移除换行符 \n
  488. .replace(/,/g, '') // 移除,
  489. .replace(/{/g, '') // 移除{
  490. .replace(/}/g, '') // 移除}
  491. /// 在每个句号 "。" 后插入换行 <br />
  492. .split('。')
  493. .filter((p: string) => p.trim())
  494. .map((p: string) => `
  495. <div class="analysis-item">
  496. <div class="bullet-point"></div>
  497. <p>${p}。</p>
  498. </div>
  499. `)
  500. .join('');
  501. // 返回完整的 HTML 结构
  502. return `
  503. <div class="analysis-result">
  504. <div class="result-section tags-section">
  505. <div class="section-title">
  506. <ion-icon name="ribbon"></ion-icon>
  507. <h2>您的兴趣标签</h2>
  508. </div>
  509. <div class="tags-wrapper">
  510. ${tagsHtml}
  511. </div>
  512. </div>
  513. <div class="result-section report-section">
  514. <div class="section-title">
  515. <ion-icon name="document-text"></ion-icon>
  516. <h2>个性化分析报告</h2>
  517. </div>
  518. <div class="content-wrapper">
  519. ${paragraphs}
  520. </div>
  521. </div>
  522. </div>
  523. `;
  524. } catch (error) {
  525. console.error('格式化 AI 响应时出错:', error);
  526. return '分析结果格式错误。';
  527. }
  528. }
  529. // 显示分析结果
  530. showAnalysisResult() {
  531. if (this.aiAnalysisResult) {
  532. this.modalContent = this.formatContent(
  533. JSON.stringify({
  534. interestTags: this.aiAnalysisResult.interestTags,
  535. content: this.aiAnalysisResult.content
  536. })
  537. );
  538. this.modalIsOpen = true; // 打开 Modal
  539. }
  540. }
  541. // 关闭 Modal
  542. closeModal() {
  543. if (!this.isAnalyzing) { // 只有在不是分析状态时才允许关闭
  544. this.modalIsOpen = false;
  545. }
  546. }
  547. /*
  548. // 加载当前用户
  549. private async loadCurrentUser() {
  550. // 你可以通过 sessionToken 来检查用户是否已经登录
  551. try {
  552. const user = await this.currentUser?.current();
  553. if (user) {
  554. console.log('当前用户:', user);
  555. this.currentUser = user;
  556. } else {
  557. console.log('用户未登录');
  558. this.currentUser = null;
  559. }
  560. } catch (error) {
  561. console.error('获取当前用户失败:', error);
  562. this.currentUser = null;
  563. }
  564. }*/
  565. // 加载当前用户
  566. private loadCurrentUser() {
  567. this.currentUser = this.userService.getCurrentUser();
  568. if (this.currentUser) {
  569. console.log('当前用户:', this.currentUser);
  570. } else {
  571. console.log('用户未登录');
  572. }
  573. }
  574. // 保存 AI 分析结果到数据库
  575. async saveAnalysisResult(aiResponse: { interestTags: string[], content: string }) {
  576. try {
  577. // 获取当前用户对象
  578. if (!this.currentUser || !this.currentUser.id) {
  579. console.log("用户未登录或没有用户ID");
  580. return;
  581. }
  582. // 获取当前用户的 objectId
  583. const userObjectId = this.currentUser.id;
  584. if (!userObjectId) {
  585. console.log("无法获取用户的 objectId");
  586. return;
  587. }
  588. // 创建并保存用户兴趣分析数据
  589. const userInterestProfile = new CloudObject("UserInterestProfile");
  590. userInterestProfile.set({
  591. userId: { __type: "Pointer", className: "_User", objectId: userObjectId }, // 使用 currentUser 的 objectId
  592. QuestionnaireId: this.questionnaire?.QuestionnaireId,
  593. interestTags: aiResponse.interestTags,
  594. content: aiResponse.content
  595. });
  596. await userInterestProfile.save();
  597. console.log("分析结果保存");
  598. } catch (error) {
  599. console.error('保存分析结果时出错:', error);
  600. }
  601. }
  602. async cancelDate() {
  603. await this.datetime.cancel(true);
  604. }
  605. async confirmDate() {
  606. await this.datetime.confirm(true);
  607. }
  608. async loadSavedProgress() {
  609. try {
  610. const progressQuery = new CloudQuery("SurveyProgress");
  611. progressQuery.equalTo("userId", { __type: "Pointer", className: "_User", objectId: "user1" });
  612. // progressQuery.order("-lastUpdated"); // 使用 order 方法,负号表示降序
  613. const savedProgress = await progressQuery.first();
  614. if (savedProgress) {
  615. const progressData = savedProgress.data;
  616. // 恢复个人信息
  617. this.name = progressData['personalInfo']['name'];
  618. this.gender = progressData['personalInfo']['gender'];
  619. this.age = progressData['personalInfo']['age'];
  620. this.birthday = progressData['personalInfo']['birthday'];
  621. this.occupation = progressData['personalInfo']['occupation'];
  622. // 恢复答案
  623. this.answers = progressData['answers'];
  624. console.log('已加载保存的进度');
  625. }
  626. } catch (error) {
  627. console.error('加载保存的进度失败:', error);
  628. }
  629. }
  630. // 修改刷新问卷的方法
  631. async refreshQuestionnaire() {
  632. try {
  633. /*
  634. // 显示加载提示
  635. const loadingToast = document.createElement('ion-toast');
  636. loadingToast.message = '正在更新问卷...';
  637. loadingToast.duration = 1000;
  638. loadingToast.position = 'top';
  639. document.body.appendChild(loadingToast);
  640. await loadingToast.present();*/
  641. // 重置状态
  642. this.answers = {};
  643. this.questionsWithOptions = [];
  644. this.questionnaire = null;
  645. //当前的问卷Id
  646. let randomQuestionnaireId: string;
  647. // 重新加载其他的问卷
  648. do {
  649. randomQuestionnaireId = this.getRandomQuestionnaire(); // 获取随机的问卷ID
  650. console.log(`随机生成的问卷ID:${randomQuestionnaireId}`);
  651. } while (randomQuestionnaireId === this.currentQuestionnaireId); // 如果相同则继续生成
  652. this.currentQuestionnaireId = randomQuestionnaireId;
  653. console.log(`现在的问卷ID:${this.currentQuestionnaireId}`)
  654. await this.loadQuestionnaireData(randomQuestionnaireId);
  655. /*
  656. // 显示成功提示
  657. const toast = document.createElement('ion-toast');
  658. toast.message = '问卷已更新';
  659. toast.duration = 2000;
  660. toast.position = 'top';
  661. toast.color = 'success';
  662. document.body.appendChild(toast);
  663. toast.present().catch((err) => console.error('展示 toast 时出错:', err));*/
  664. } catch (error) {
  665. console.error('更新问卷失败:', error);
  666. const toast = document.createElement('ion-toast');
  667. toast.message = '更新问卷失败,请重试';
  668. toast.duration = 2000;
  669. toast.position = 'top';
  670. toast.color = 'danger';
  671. document.body.appendChild(toast);
  672. await toast.present();
  673. }
  674. }
  675. }