3 Commits 3df63d518c ... 3c9c8df4d5

Author SHA1 Message Date
  AAA123 3c9c8df4d5 Your commit message 10 months ago
  AAA123 84f599a546 Your commit message 10 months ago
  AAA123 4f2b44d550 Your commit message 10 months ago

+ 92 - 92
android/gradlew.bat

@@ -1,92 +1,92 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem      https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem  Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 2 - 2
angular.json

@@ -39,8 +39,8 @@
               "budgets": [
                 {
                   "type": "initial",
-                  "maximumWarning": "2mb",
-                  "maximumError": "5mb"
+                  "maximumWarning": "700kb",
+                  "maximumError": "1000kb"
                 },
                 {
                   "type": "anyComponentStyle",

File diff suppressed because it is too large
+ 177 - 465
package-lock.json


+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "@capacitor/splash-screen": "^6.0.3",
     "@capacitor/status-bar": "6.0.2",
     "@ionic/angular": "^8.4.1",
+    "@ionic/storage-angular": "^4.0.0",
     "ionicons": "^7.0.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",

+ 1 - 1
src/app/edit-profile/edit-profile.page.ts

@@ -29,7 +29,7 @@ export class EditProfilePage implements OnInit {
       phone: ['', [Validators.required, Validators.pattern('^[0-9]*$')]],
       gender: ['', [Validators.required]],
       birthday: ['', [Validators.required]],
-      userAvatar: ['../../assets/images/user-avatar.png', []] as [string | null, Validators[]] // 设置默认值并明确类型
+      userAvatar: ['../../assets/images/user.png', []] as [string | null, Validators[]] // 设置默认值并明确类型
     });
 
     // 预填充表单(如果有初始用户信息)

+ 64 - 23
src/app/services/user.service.ts

@@ -1,10 +1,13 @@
 import { Injectable } from '@angular/core';
-import { BehaviorSubject, Observable, of } from 'rxjs';
+import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
 import { tap, catchError } from 'rxjs/operators';
 import { HealthReport } from '../health-record/health-record.page';
+import { environment } from 'src/environments/environment';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
 
 // 定义用户信息接口
 export interface UserInfo {
+  id?: number; // 修改为 id 字段
   username: string;
   email?: string;
   phone?: string;
@@ -16,14 +19,20 @@ export interface UserInfo {
   medicalHistory?: string; // 添加 medicalHistory 字段 // 添加 reports 字段
 }
 
+export interface User {
+  username: string;
+  password: string;
+  // 可以根据需要添加更多的用户属性
+}
+
 @Injectable({
   providedIn: 'root'
 })
 export class UserService {
-         userInfoSubject = new BehaviorSubject<UserInfo | null>(null);
+  userInfoSubject = new BehaviorSubject<UserInfo | null>(null);
   public userInfo$ = this.userInfoSubject.asObservable();
 
-  constructor() {
+  constructor(private http: HttpClient) {
     this.loadUserInfo(); // 应用启动时加载本地存储的用户信息
   }
 
@@ -31,6 +40,7 @@ export class UserService {
     try {
       const currentUserInfo = this.userInfoSubject.value || {}; // 如果为空则提供一个空对象
       const updatedUserInfo: UserInfo = {
+        id: userInfo.id ?? (currentUserInfo as UserInfo).id,
         username: userInfo.username ?? (currentUserInfo as UserInfo).username ?? '', // 确保 username 不会是 undefined
         email: userInfo.email ?? (currentUserInfo as UserInfo).email,
         phone: userInfo.phone ?? (currentUserInfo as UserInfo).phone,
@@ -73,16 +83,17 @@ export class UserService {
       throw error;
     }
   }
-  
+
   private async loadUserInfo(): Promise<void> {
     try {
       const storedUserInfo = localStorage.getItem('userInfo');
       if (storedUserInfo) {
         const parsedUserInfo: UserInfo = JSON.parse(storedUserInfo);
-  
-        // 确保所有字段都有默认值
+        
+
         this.userInfoSubject.next({
           ...parsedUserInfo,
+          id: parsedUserInfo.id, // 确保 id 被正确传递
           username: parsedUserInfo.username || '',
           userAvatar: parsedUserInfo.userAvatar || '../../assets/images/user.png',
           userType: parsedUserInfo.userType || '普通用户',
@@ -90,28 +101,58 @@ export class UserService {
           phone: parsedUserInfo.phone || '',
           birthday: parsedUserInfo.birthday ?? null,
           email: parsedUserInfo.email || '',
-          reports: parsedUserInfo.reports || [] // 确保 reports 字段存在
-        });
-      } else {
-        // 设置默认用户信息,确保所有字段都有值
-        this.userInfoSubject.next({
-          username: '张三',
-          userAvatar: '../../assets/images/user.png',
-          userType: '普通用户',
-          gender: 'male',
-          phone: '123456789',
-          birthday: '2003-03-06',
-          email: '33333@333',
-          medicalHistory: '高血压',
-          reports: [ // 默认报告
-            { title: '年度体检', date: new Date(), details: '血压正常,血糖略高', type: 'annual' },
-            { title: '眼科检查', date: new Date('2024-01-01'), details: '视力良好,无异常', type: 'eye' }
-          ]
+          reports: parsedUserInfo.reports || [],
+          medicalHistory: parsedUserInfo.medicalHistory || ''
         });
       }
+      // else {
+      //   // 如果没有本地用户信息,尝试从服务器获取(假设有一个获取用户信息的API)
+      //   const userInfoResponse = await this.http.get<UserInfo>(`${environment.apiUrl}/getUserInfo`).toPromise();
+      //   this.saveUserInfoToLocalStorage(userInfoResponse);
+      //   this.userInfoSubject.next(userInfoResponse);
+      // }
     } catch (error) {
       console.error('Failed to parse userInfo from localStorage:', error);
       this.userInfoSubject.next(null);
     }
   }
+
+  loginUser(username: string, password: string): Observable<any> {
+    const loginUrl = `${environment.apiUrl}/api/login`;
+    return this.http.post(loginUrl, { username, password }, {
+      withCredentials: true,
+      headers: new HttpHeaders({
+        'Content-Type': 'application/json'
+      })
+    }).pipe(
+      tap(async (response: any) => {
+        await this.saveUserInfoToLocalStorage(response);
+        this.userInfoSubject.next(response);
+      }),
+      catchError(error => {
+        let errorMessage = '登录失败';
+        if (error.error instanceof ErrorEvent) {
+          errorMessage = `Error: ${error.error.message}`;
+        } else {
+          errorMessage = `Service returned code: ${error.status}, body was: ${error.message || error.statusText}`;
+        }
+        console.error('登录请求出错:', errorMessage);
+        return throwError(() => new Error(errorMessage));
+      })
+    );
+  }
+
+  registerNewUser(user: User): Observable<any> {
+    const registerUrl = `${environment.apiUrl}/api/register`;
+    return this.http.post(registerUrl, user).pipe(
+      tap(async (response: any) => {
+        await this.saveUserInfoToLocalStorage(response);
+        this.userInfoSubject.next(response);
+      }),
+      catchError(error => {
+        console.error('Registration failed:', error);
+        return throwError(() => new Error('Registration failed'));
+      })
+    );
+  }
 }

+ 7 - 18
src/app/tab1/tab1.page.html

@@ -6,11 +6,14 @@
       <ion-avatar (click)="openLoginModal()">
         <img [src]="userAvatar" alt="User Avatar" />
       </ion-avatar>
-      <ion-button fill="clear" (click)="openLoginModal()">
-        <ion-label>登录/注册</ion-label>
-      </ion-button>
+      <ng-container *ngIf="!isLoggedIn">
+        <!-- 如果用户未登录,显示登录/注册按钮 -->
+        <ion-button fill="clear" (click)="openLoginModal()">
+          <ion-label>登录/注册</ion-label>
+        </ion-button>
+      </ng-container>
     </ion-buttons>
-    <ion-title>
+    <ion-title >
       1分钟了解健康服务
     </ion-title>
     <ion-buttons slot="end">
@@ -121,10 +124,6 @@
       </div>
     </ng-container>
   </div>
-  <!-- <div class="controls">
-    <button (click)="prevSlide()">上一张</button>
-    <button (click)="nextSlide()">下一张</button>
-  </div> -->
 </div>
 
 
@@ -151,14 +150,4 @@
   </div>
 
 </div>
-
-<!-- 浮动操作按钮 -->
-<!-- <div class="floating-action-button" (click)="handleClick()">
-  <ion-icon name="add"></ion-icon>
-  <div class="label">发布</div>
-</div>
-<input type="file" id="fileInput" class="hidden-input" accept="image/*,.pdf" change="publishHealthInfo(this.files)">
-<script src="https://unpkg.com/ionicons@5.5.2/dist/ionicons.js"></script> -->
-  
-  <!-- 其他页面内容 -->
 </ion-content>

+ 1 - 1
src/app/tab1/tab1.page.scss

@@ -22,7 +22,7 @@ ion-toolbar {
   }
   
   ion-title {
-    font-size: 18px;
+    font-size: 15px;
     font-weight: bold;
     flex: 1; /* 使标题居中 */
     text-align: center;

+ 18 - 39
src/app/tab1/tab1.page.ts

@@ -1,14 +1,12 @@
 import { Component, AfterViewInit } from '@angular/core';
 import { Router } from '@angular/router';
 import { ModalController, NavController, ToastController } from '@ionic/angular';
-import { Observable } from 'rxjs';
-import { map, catchError } from 'rxjs/operators';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
-import { throwError } from 'rxjs';
+import { SplashService } from '../services/splash.service';
+import { HttpClient,} from '@angular/common/http';
 import { Subscription } from 'rxjs';
 import { UserService } from '../services/user.service';
-import {ElementRef, OnInit, Renderer2 } from '@angular/core';
-import { SplashService } from '../services/splash.service';
+import {ElementRef, Renderer2 } from '@angular/core';
+
 
 interface User {
   username: string;
@@ -88,6 +86,7 @@ export class Tab1Page implements AfterViewInit {
   fileToUpload: File | null = null;
   private splashSubscription!: Subscription;
   isLoggedIn: boolean = false;
+  
  
 
   constructor(
@@ -112,6 +111,15 @@ export class Tab1Page implements AfterViewInit {
         await this.openLoginModal();
       }
     });
+    
+    this.userService.userInfo$.subscribe(userInfo => {
+      if (userInfo) {
+        this.isLoggedIn = true;
+        this.userAvatar = userInfo.userAvatar || this.userAvatar; // 更新头像
+      } else {
+        this.isLoggedIn = false;
+      }
+    });
   }
 
   ionViewWillEnter() {
@@ -285,7 +293,7 @@ export class Tab1Page implements AfterViewInit {
       return;
     }
 
-    this.loginUser(formValue.username, formValue.password)
+    this.userService.loginUser(formValue.username, formValue.password)
       .subscribe(
         async (response) => {
           console.log('登录成功:', response);
@@ -318,9 +326,7 @@ export class Tab1Page implements AfterViewInit {
       return;
     }
 
-    console.log('注册用户:', JSON.stringify(this.user, null, 2));
-
-    this.registerNewUser(this.user)
+    this.userService.registerNewUser(this.user)
       .subscribe(
         async (response) => {
           console.log('注册成功:', response);
@@ -333,39 +339,12 @@ export class Tab1Page implements AfterViewInit {
           if (error.error && error.error.message.includes('cannot be null')) {
             await this.presentToast('密码不能为空,请输入密码', 'danger');
           } else {
-            await this.presentToast('注册失败,请稍后再试', 'danger');
+            await this.presentToast('用户名已存在,请稍后再试', 'danger');
           }
         }
       );
   }
-
-  private loginUser(username: string, password: string): Observable<any> {
-    const loginUrl = 'http://localhost:8080/api/login'; // 替换为你的登录 API 端点
-    return this.http.post(loginUrl, { username, password }, {
-      withCredentials: true,
-      headers: new HttpHeaders({
-        'Content-Type': 'application/json'
-      })
-    }).pipe(
-      map(response => response),
-      catchError(error => {
-        console.error('登录请求出错:', error);
-        return throwError(() => new Error('登录失败'));
-      })
-    );
-  }
-
-  private registerNewUser(user: User): Observable<any> {
-    const registerUrl = 'http://localhost:8080/api/register'; // 替换为你的注册 API 端点
-    return this.http.post(registerUrl, user)
-      .pipe(
-        map(response => response),
-        catchError(error => {
-          throw error;
-        })
-      );
-  }
-
+  
   onLearnMore() {
     this.navCtrl.navigateForward('/details');
   }

+ 0 - 7
src/app/tab2/tab2.page.html

@@ -1,4 +1,3 @@
-<!-- src/app/tab2/tab2.page.html -->
 <ion-header>
   <ion-toolbar color="primary">
     <ion-title class="center-title">AI 问诊</ion-title>
@@ -26,12 +25,6 @@
       </div>
     </ion-item>
   </ion-list>
-  
-  <!-- 显示连接状态 -->
-  <div *ngIf="!isSocketOpen" class="connection-status">
-    <ion-spinner name="crescent"></ion-spinner>
-    正在尝试连接...
-  </div>
 </ion-content>
 
 <ion-footer>

+ 52 - 288
src/app/tab2/tab2.page.ts

@@ -1,27 +1,7 @@
 import { Component, OnInit, OnDestroy } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { environment } from '../../environments/environment';
-import { v4 as uuidv4 } from 'uuid';
-
-export interface IServerMessage {
-  header?: {
-    code?: number;
-    message?: string;
-    sid?: string;
-    status?: number;
-    type?: 'heartbeat_ack' | 'normal'; // 在header中定义类型字段
-  };
-  payload?: {
-    type?: 'heartbeat_ack' | 'normal'; // 在payload中也定义类型字段
-    choices?: {
-      text?: Array<{
-        content: string;
-        role: 'user' | 'assistant';
-        index?: number;
-      }>;
-    };
-  };
-}
+import { UserService } from '../services/user.service';
 
 @Component({
   selector: 'app-tab2',
@@ -32,39 +12,35 @@ export class Tab2Page implements OnInit, OnDestroy {
   messages: any[] = [];
   userInput: string = '';
   isLoading: boolean = false;
-  private socket: WebSocket | null = null;
-  private messageBuffers: Map<string, { content: string[], status: number }> = new Map();
-  private activeSessions: Set<string> = new Set();
-  private heartbeatInterval: number = 30000; // 每30秒发送一次心跳
-  private heartbeatTimer: any;
-  private serverAcknowledgeTimeout: any;
+  isSocketOpen: boolean;
+  userId: number | null = null;
 
-  constructor(private http: HttpClient) {}
+  constructor(private http: HttpClient, private userService: UserService) {
+    this.isSocketOpen = false;
+  }
 
   ngOnInit() {
     this.sendMessageFromAI(this.getRandomWelcomeMessage());
-    this.initWebSocket();
+    this.userService.getUserInfo().subscribe(
+      userInfo => {
+        console.log('User Info:', userInfo);
+        if (userInfo && userInfo.id !== undefined) {
+          console.log('User ID:', userInfo.id);
+          this.userId = userInfo.id;
+        } else {
+          console.warn('No valid user info received');
+          this.sendMessageFromAI('无法获取有效的用户ID,请稍后再试');
+        }
+      },
+      error => {
+        console.error('Failed to get user info:', error);
+        this.sendMessageFromAI('系统遇到错误,请稍后再试');
+      }
+    );
   }
 
   ngOnDestroy() {
-    // 不要在这里手动关闭连接
-    // if (this.socket) {
-    //   if (this.activeSessions.size > 0) {
-    //     console.warn('There are active sessions, attempting graceful shutdown...');
-    //     setTimeout(() => {
-    //       if (this.socket) {
-    //         this.socket.close();
-    //       }
-    //     }, 5000); // 假设5秒足够让服务器处理完所有请求
-    //   } else {
-    //     console.log('No active sessions, closing WebSocket immediately.');
-    //     this.socket.close();
-    //   }
-    // }
-  }
-
-  get isSocketOpen(): boolean {
-    return Boolean(this.socket && this.socket.readyState === WebSocket.OPEN);
+    // 如果有其他资源需要清理,这里可以添加相应的代码
   }
 
   getRandomWelcomeMessage(): string {
@@ -78,221 +54,10 @@ export class Tab2Page implements OnInit, OnDestroy {
     return welcomeMessages[Math.floor(Math.random() * welcomeMessages.length)];
   }
 
-  async initWebSocket() {
-    try {
-      const response = await this.http.get(environment.apiUrl + '/api/v1/getWebSocketUrl', { responseType: 'text' }).toPromise();
-      const wsUrl = response as string;
-
-      if (!wsUrl || wsUrl.trim().length === 0) {
-        throw new Error('Received an empty or undefined WebSocket URL');
-      }
-
-      console.log('Fetched WebSocket URL:', wsUrl);
-
-      this.socket = new WebSocket(wsUrl);
-
-      this.socket.onopen = () => {
-        console.log('WebSocket connection opened');
-        this.startHeartbeat();
-      };
-
-      this.socket.onmessage = (event) => {
-        try {
-          const message: IServerMessage = JSON.parse(event.data);
-          console.log('Parsed message:', message);
-
-          if (message.header?.code === 0) {
-            // 检查心跳确认消息
-            if (message.header?.type === 'heartbeat_ack' || message.payload?.type === 'heartbeat_ack') {
-              this.resetHeartbeat();
-              return; // 如果是心跳确认消息,不再继续处理其他逻辑
-            }
-
-            // 继续处理正常消息...
-            if (
-              message.payload &&
-              message.payload.choices &&
-              Array.isArray(message.payload.choices.text)
-            ) {
-              const sid = message.header?.sid || ''; // 提供默认值
-              const status = message.header?.status || 0; // 提供默认值
-
-              // 初始化或获取当前对话的缓冲区
-              let buffer = this.messageBuffers.get(sid) || { content: [], status: 0 };
-              buffer.status = status;
-
-              // 累积所有非空文本内容,并去除前后空白字符
-              buffer.content.push(
-                ...message.payload.choices.text
-                  .filter(item => typeof item.content === 'string' && item.content.trim() !== '')
-                  .map(item => item.content.trim())
-              );
-
-              // 如果是最后一条消息,则合并并显示
-              if (buffer.status === 2) {
-                const combinedContent = buffer.content.join(' ');
-                const cleanedContent = combinedContent.replace(/\s+/g, ' ').trim();
-
-                this.sendMessageFromAI(cleanedContent); // 一次性发送合并后的内容
-                this.messageBuffers.delete(sid); // 清除已完成会话的缓冲区
-                this.activeSessions.delete(sid); // 从活跃会话中移除
-
-                // 检查是否还有活跃会话
-                if (this.activeSessions.size === 0) {
-                  this.isLoading = false;
-                }
-              } else {
-                // 更新缓冲区
-                this.messageBuffers.set(sid, buffer);
-              }
-            } else {
-              console.error('Invalid or unexpected message format:', message);
-              this.sendMessageFromAI('收到的消息格式无效,请检查服务器配置。');
-              this.isLoading = false;
-            }
-          } 
-        } catch (error) {
-          console.error('Error parsing message:', error);
-          this.sendMessageFromAI('抱歉,系统出现错误,请稍后再试。');
-          this.isLoading = false;
-        }
-      };
-
-      this.socket.onerror = (event) => {
-        console.error('WebSocket error observed:', event);
-        this.sendMessageFromAI('抱歉,系统出现错误,请稍后再试。');
-        this.isLoading = false;
-      };
-
-      this.socket.onclose = (event) => {
-        console.log('WebSocket connection closed:', event);
-        this.reconnectWebSocket(); // 尝试重新连接
-        this.isLoading = false;
-      };
-    } catch (error) {
-      console.error('Failed to fetch WebSocket URL:', error);
-      this.sendMessageFromAI('无法获取WebSocket连接信息,请检查网络设置。');
-    }
-  }
-
-  private startHeartbeat() {
-    this.stopHeartbeat(); // 确保只有一个心跳计时器运行
-
-    this.heartbeatTimer = setInterval(() => {
-      if (this.isSocketOpen) {
-        this.sendHeartbeat();
-      } else {
-        this.stopHeartbeat();
-      }
-    }, this.heartbeatInterval);
-
-    this.serverAcknowledgeTimeout = setTimeout(() => {
-      console.warn('No heartbeat acknowledgment received from the server.');
-      this.reconnectWebSocket(); // 尝试重新连接
-    }, this.heartbeatInterval * 2); // 设置两倍的心跳间隔作为等待服务器回应的时间
-  }
-
-  private sendHeartbeat() {
-    const heartbeatMessage = {
-      header: {
-        type: 'heartbeat'
-      },
-      payload: {}
-    };
-    console.log('Sending heartbeat...');
-    this.socket!.send(JSON.stringify(heartbeatMessage));
-  }
-
-  private resetHeartbeat() {
-    clearTimeout(this.serverAcknowledgeTimeout);
-    this.serverAcknowledgeTimeout = setTimeout(() => {
-      console.warn('No heartbeat acknowledgment received from the server.');
-      this.reconnectWebSocket(); // 尝试重新连接
-    }, this.heartbeatInterval * 2); // 再次设置两倍的心跳间隔作为等待服务器回应的时间
-  }
-
-  private stopHeartbeat() {
-    if (this.heartbeatTimer) {
-      clearInterval(this.heartbeatTimer);
-      this.heartbeatTimer = undefined;
-    }
-    if (this.serverAcknowledgeTimeout) {
-      clearTimeout(this.serverAcknowledgeTimeout);
-      this.serverAcknowledgeTimeout = undefined;
-    }
-  }
-
-  private reconnectWebSocket() {
-    console.log('Attempting to reconnect WebSocket...');
-    this.stopHeartbeat();
-    if (this.socket) {
-      this.socket.close();
-    }
-    this.initWebSocket();
-  }
-
-  private handleIncomingMessage(event: MessageEvent<string>) {
-    try {
-      const message: IServerMessage = JSON.parse(event.data);
-      console.log('Parsed message:', message);
-
-      if (
-        message.header &&
-        message.header.code === 0 &&
-        message.payload &&
-        message.payload.choices &&
-        Array.isArray(message.payload.choices.text)
-      ) {
-        const sid = message.header.sid || ''; // 提供默认值
-        const status = message.header.status || 0; // 提供默认值
-
-        // 初始化或获取当前对话的缓冲区
-        let buffer = this.messageBuffers.get(sid) || { content: [], status: 0 };
-        buffer.status = status;
-
-        // 累积所有非空文本内容,并去除前后空白字符
-        buffer.content.push(
-          ...message.payload.choices.text
-            .filter(item => typeof item.content === 'string' && item.content.trim() !== '')
-            .map(item => item.content.trim())
-        );
-
-        // 如果是最后一条消息,则合并并显示
-        if (buffer.status === 2) {
-          // 使用单个空格作为分隔符连接文本片段,或者根据需要选择其他分隔符
-          const combinedContent = buffer.content.join(' ');
-
-          // 去除最终结果中的多余空白字符
-          const cleanedContent = combinedContent.replace(/\s+/g, ' ').trim();
-
-          this.sendMessageFromAI(cleanedContent); // 一次性发送合并后的内容
-          this.messageBuffers.delete(sid); // 清除已完成会话的缓冲区
-          this.activeSessions.delete(sid); // 从活跃会话中移除
-
-          // 检查是否还有活跃会话
-          if (this.activeSessions.size === 0) {
-            this.isLoading = false;
-          }
-        } else {
-          // 更新缓冲区
-          this.messageBuffers.set(sid, buffer);
-        }
-      } else {
-        console.error('Invalid or unexpected message format:', message);
-        this.sendMessageFromAI('收到的消息格式无效,请检查服务器配置。');
-        this.isLoading = false;
-      }
-    } catch (error) {
-      console.error('Error parsing message:', error);
-      this.sendMessageFromAI('抱歉,系统出现错误,请稍后再试。');
-      this.isLoading = false;
-    }
-  }
-
-  sendMessage() {
+  async sendMessage() {
     const trimmedInput = this.userInput.trim();
     if (!trimmedInput) {
-      alert('请输入您的问题或消息'); 
+      alert('请输入您的问题或消息');
       return;
     }
 
@@ -300,36 +65,35 @@ export class Tab2Page implements OnInit, OnDestroy {
     this.isLoading = true;
     this.userInput = '';
 
-    if (this.isSocketOpen) {
-      const uid = localStorage.getItem('user_id') || uuidv4();
-      localStorage.setItem('user_id', uid);
+    try {
+      if (!this.userId) {
+        throw new Error('无法获取有效的用户ID');
+      }
 
-      const sendMessage = {
-        header: {
-          app_id: "c907b21b",
-          uid,
-        },
-        parameter: {
-          chat: {
-            domain: "generalv3.5",
-            temperature: 0.5,
-            max_tokens: 4096,
-          }
-        },
-        payload: {
-          message: {
-            text: [{
-              content: trimmedInput,
-              role: 'user'
-            }]
-          }
-        }
-      };
+      // 添加提示词
+      const promptPrefix = "你是个医生,回答病人的请求:";
+      const fullQuestion = `${promptPrefix}${trimmedInput}`;
+
+      // 使用 try-catch 包装 HTTP 请求
+      try {
+        const response = await this.http.get<string>(`${environment.apiUrl}/test/sendQuestion`, {
+          params: { id: this.userId.toString(), question: fullQuestion },
+          responseType: 'text' as 'json' // 指定响应类型为文本
+        }).toPromise();
 
-      this.socket!.send(JSON.stringify(sendMessage));
-      this.activeSessions.add(uid);
-    } else {
-      this.sendMessageFromAI('WebSocket 连接已断开,请稍后再试。');
+        if (response) {
+          this.sendMessageFromAI(response);
+        } else {
+          this.sendMessageFromAI('收到空回复,请尝试重新提问');
+        }
+      } catch (httpError) {
+        console.error('HTTP请求失败:', httpError);
+        this.sendMessageFromAI('网络请求失败,请检查网络连接或稍后再试');
+      }
+    } catch (error) {
+      console.error('发送消息时发生错误:', error);
+      this.sendMessageFromAI('系统遇到错误,请稍后再试');
+    } finally {
       this.isLoading = false;
     }
   }

+ 35 - 15
src/app/tab3/tab3.page.ts

@@ -3,6 +3,7 @@ import { NavController } from '@ionic/angular';
 import { UserService } from '../services/user.service';
 import { Subscription } from 'rxjs';
 
+
 @Component({
   selector: 'app-tab3',
   templateUrl: 'tab3.page.html',
@@ -11,7 +12,7 @@ import { Subscription } from 'rxjs';
 export class Tab3Page implements OnInit {
 
   username: string = '';
-  userAvatar: string | null = '../../assets/images/user.png'; // 允许 null
+  userAvatar: string | null = 'src/assets/images/user.png'; // 允许 null
   userType: string = '普通用户';
   previewImage: string | null = null;
   private userInfoSubscription?: Subscription;
@@ -41,31 +42,42 @@ export class Tab3Page implements OnInit {
   }
 
   private loadUserInfo() {
+    this.userAvatar = '/assets/images/user.png'; // 立即设置默认头像
+  
     // 取消之前的订阅(如果有)
     if (this.userInfoSubscription) {
       this.userInfoSubscription.unsubscribe();
     }
+  
     // 订阅最新的用户信息
     this.userInfoSubscription = this.userService.getUserInfo().subscribe(userInfo => {
-      if (userInfo) {
-        this.username = userInfo.username ?? this.username;
-        this.userAvatar = userInfo.userAvatar ?? null; // 确保预览图像是最新的头像或默认值
-        this.userType = userInfo.userType ?? '普通用户';
-        this.previewImage = userInfo.userAvatar ?? null; // 设置预览图像
-      } else {
-        this.userAvatar = '../../assets/images/user.png'; // 如果没有用户信息,使用默认头像
-        this.previewImage = null;
+      if (userInfo && userInfo.userAvatar) {
+        this.userAvatar = userInfo.userAvatar;
+        this.previewImage = userInfo.userAvatar;
       }
+      // 如果 userInfo 为 null 或 undefined,保持默认头像不变
     });
   }
 
   private checkStatuses() {
-    // 模拟检查各个功能区的状态
-    // 实际应用中应从服务器获取最新状态
-    this.hasUnreadHealthRecords = true;
-    this.hasPendingAppointments = true;
-    this.hasNewPromotions = true;
-    this.hasUnprocessedOrders = true;
+    // 尝试从 localStorage 加载状态
+  const savedStates = JSON.parse(localStorage.getItem('userStatuses') || '{}');
+
+  this.hasUnreadHealthRecords = savedStates.hasUnreadHealthRecords ?? false;
+  this.hasPendingAppointments = savedStates.hasPendingAppointments ?? false;
+  this.hasNewPromotions = savedStates.hasNewPromotions ?? false;
+  this.hasUnprocessedOrders = savedStates.hasUnprocessedOrders ?? false;
+  }
+
+  private saveStatuses() {
+    const userStatuses = {
+      hasUnreadHealthRecords: this.hasUnreadHealthRecords,
+      hasPendingAppointments: this.hasPendingAppointments,
+      hasNewPromotions: this.hasNewPromotions,
+      hasUnprocessedOrders: this.hasUnprocessedOrders
+    };
+  
+    localStorage.setItem('userStatuses', JSON.stringify(userStatuses));
   }
 
   editProfile() {
@@ -76,21 +88,29 @@ export class Tab3Page implements OnInit {
   viewHealthRecord() {
     console.log('查看/编辑健康档案');
     this.navCtrl.navigateForward('/health-record');
+    this.hasUnreadHealthRecords = false; // 标记为已读
+    this.saveStatuses(); // 保存状态
   }
 
   viewMedicalServices() {
     console.log('查看医疗服务');
     this.navCtrl.navigateForward('/medical-services');
+    this.hasPendingAppointments = false;
+    this.saveStatuses(); 
   }
 
   viewPointsAndCoupons() {
     console.log('查看积分与优惠');
     this.navCtrl.navigateForward('/points-and-coupons');
+    this.hasNewPromotions = false;
+    this.saveStatuses(); 
   }
 
   viewOrders() {
     console.log('查看订单管理');
     this.navCtrl.navigateForward('/orders');
+    this.hasUnprocessedOrders = false;
+    this.saveStatuses(); 
   }
 
   goToFaq() {

+ 18 - 0
src/deploy.ps1

@@ -0,0 +1,18 @@
+# 打包项目,携带应用前缀(index.html中相对路径将自动修复为/dev/jxnu/<学号>前缀)
+# /dev/ 项目测试版上传路径
+# /dev/jxnu/<学号> nova-crm项目预留路径
+set NODE_OPTIONS=–max_old_space_size=16000
+node ./node_modules/@angular/cli/bin/ng build --base-href="/dev/jxnu/202226701052/"
+
+# 清空旧文件目录
+obsutil rm obs://nova-cloud/dev/jxnu/202226701052 -r -f -i=XSUWJSVMZNHLWFAINRZ1 -k=P4TyfwfDovVNqz08tI1IXoLWXyEOSTKJRVlsGcV6 -e="obs.cn-south-1.myhuaweicloud.com"
+
+# 同步文件目录
+obsutil sync ./www obs://nova-cloud/dev/jxnu/202226701052  -i=XSUWJSVMZNHLWFAINRZ1 -k=P4TyfwfDovVNqz08tI1IXoLWXyEOSTKJRVlsGcV6 -e="obs.cn-south-1.myhuaweicloud.com" -acl=public-read
+
+
+# 授权公开可读
+obsutil chattri obs://nova-cloud/dev/jxnu/202226701052 -r -f -i=XSUWJSVMZNHLWFAINRZ1 -k=P4TyfwfDovVNqz08tI1IXoLWXyEOSTKJRVlsGcV6 -e="obs.cn-south-1.myhuaweicloud.com" -acl=public-read
+
+# 列举对象
+obsutil ls obs://nova-cloud/dev/jxnu/202226701052  -i=XSUWJSVMZNHLWFAINRZ1 -k=P4TyfwfDovVNqz08tI1IXoLWXyEOSTKJRVlsGcV6 -e="obs.cn-south-1.myhuaweicloud.com"

+ 1 - 1
src/environments/environment.prod.ts

@@ -1,4 +1,4 @@
 export const environment = {
   production: true,
-  apiUrl: 'https://your-production-api-url.com', // 生产环境下的API基础URL
+  apiUrl: 'http://113.44.251.16:8080', // 生产环境下的API基础URL
 };

+ 1 - 12
src/environments/environment.ts

@@ -1,17 +1,6 @@
-// This file can be replaced during build by using the `fileReplacements` array.
-// `ng build` replaces `environment.ts` with `environment.prod.ts`.
-// The list of file replacements can be found in `angular.json`.
 
 export const environment = {
   production: false,
-  apiUrl: 'http://localhost:8080' // 开发环境下的API基础URL
+  apiUrl: 'http://113.44.251.16:8080'
 };
 
-/*
- * For easier debugging in development mode, you can import the following file
- * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
- *
- * This import should be commented out in production mode because it will have a negative impact
- * on performance if an error is thrown.
- */
-// import 'zone.js/plugins/zone-error';  // Included with Angular CLI.

Some files were not shown because too many files changed in this diff