diff --git a/entry/src/main/ets/common/ClassRoomService.ets b/entry/src/main/ets/common/ClassRoomService.ets index d6e6881..04fb2a9 100644 --- a/entry/src/main/ets/common/ClassRoomService.ets +++ b/entry/src/main/ets/common/ClassRoomService.ets @@ -7,6 +7,7 @@ import logManager, { LogCategory, LogEventType } from './logtext'; import settingsService from './SettingsService'; import { DatabaseService } from './DatabaseService'; import http from '@ohos.net.http'; +import { JSON } from '@kit.ArkTS'; // 课堂会话状态 export enum ClassSessionStatus { @@ -52,8 +53,8 @@ export class ClassSessionModel { questions: QuestionModel[] = []; // 课堂题目列表 messages: MessageModel[] = []; // 课堂消息列表 - constructor(sessionId: string = '', - className: string = '', + constructor(sessionId: string = '', + className: string = '', classCode: string = '', teacherAccount: string = '', teacherName: string = '') { @@ -96,8 +97,8 @@ export class MessageModel { content: string = ''; // 消息内容 timestamp: Date = new Date(); // 发送时间 - constructor(senderId: string = '', - senderName: string = '', + constructor(senderId: string = '', + senderName: string = '', senderRole: SenderRole, content: string = '') { this.id = Date.now().toString(36) + Math.random().toString(36).substring(2); @@ -107,6 +108,22 @@ export class MessageModel { this.content = content; this.timestamp = new Date(); } + + static fromJSON(jsons: string): MessageModel { + let json = JSON.parse(jsons) as Map + const msg = new MessageModel( + json["id"] as string, + json["senderId"] as string, + json["senderName"] as SenderRole, + json["content"] as string + ); + msg.timestamp = new Date(); + return msg; + } + + public useTime(){ + this.timestamp = new Date() + } } // 题目模型 @@ -122,8 +139,8 @@ export class QuestionModel { answers: QuestionAnswer[] = []; // 学生答案列表 classCode: string = ''; // 关联的课堂暗号 - constructor(title: string = '', - options: QuestionOption[] = [], + constructor(title: string = '', + options: QuestionOption[] = [], correctOption: number = 0, duration: number = 30, classCode: string = '') { @@ -146,8 +163,8 @@ export class QuestionAnswer { timestamp: Date = new Date(); // 提交时间 isCorrect: boolean = false; // 是否正确 - constructor(studentAccount: string = '', - studentName: string = '', + constructor(studentAccount: string = '', + studentName: string = '', selectedOption: number = 0, correctOption: number = 0) { this.studentAccount = studentAccount; @@ -181,7 +198,7 @@ export interface HttpRequestOptions { method: number; // http.RequestMethod enum value header?: object; extraData?: string; - connectTimeout?: number; + connectTimeout?: number; readTimeout?: number; } @@ -210,7 +227,7 @@ export interface CreateSessionRequest { } export interface JoinSessionRequest { - classCode: string; + classCode: string; studentAccount: string; studentName: string; } @@ -263,54 +280,54 @@ const TIAN_CHANNEL_TAG = 'tianChannel'; // 自定义TAG用于筛选日志 export class ClassRoomService { private static instance: ClassRoomService; private dbService: DatabaseService = DatabaseService.getInstance(); - + // 当前课堂会话 private currentSession: ClassSessionModel | null = null; private currentClassCode: string = ''; - + // 回调函数集合 private eventCallbacks: Map> = new Map(); - + // 是否已连接 private connected: boolean = false; - + // 是否为教师 private isTeacher: boolean = false; - + // 消息轮询定时器ID private pollTimerId: number = -1; - + // 最后接收的消息ID private lastMessageId: string = ''; - + // 最后接收的题目ID private lastQuestionId: string = ''; - + private constructor() { // 初始化 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, 'ClassRoom Service Initialized'); } - + public static getInstance(): ClassRoomService { if (!ClassRoomService.instance) { ClassRoomService.instance = new ClassRoomService(); } return ClassRoomService.instance; } - + // 创建HTTP请求客户端 private createHttpClient(): http.HttpRequest { let httpRequest = http.createHttp(); return httpRequest; } - + // 发起HTTP POST请求 private async postRequest(endpoint: string, data: object): Promise { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `开始POST请求: ${endpoint}`); try { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `创建HTTP客户端`); const httpRequest = this.createHttpClient(); - + // 记录请求数据摘要 let dataStr = ""; try { @@ -319,7 +336,7 @@ export class ClassRoomService { dataStr = "无法序列化数据"; } hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `请求数据(摘要): ${dataStr}`); - + hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `发送请求到: ${CHANNEL_API_CONFIG.baseUrl}/${endpoint}`); const response = await httpRequest.request( `${CHANNEL_API_CONFIG.baseUrl}/${endpoint}`, @@ -333,12 +350,12 @@ export class ClassRoomService { readTimeout: CHANNEL_API_CONFIG.timeout } ); - + hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `请求完成,销毁HTTP客户端`); httpRequest.destroy(); - + hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `响应状态码: ${response.responseCode}`); - + if (response.responseCode === 200) { try { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `开始解析响应JSON`); @@ -356,7 +373,7 @@ export class ClassRoomService { return errorResponse; } } - + hilog.warn(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `HTTP错误状态码: ${response.responseCode}`); const errorResponse: ChannelApiResponse = { success: false, @@ -367,10 +384,10 @@ export class ClassRoomService { // 详细记录网络错误 const errorMsg = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : 'No stack trace'; - + hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `网络请求错误: ${errorMsg}`); hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `错误堆栈: ${errorStack}`); - + logManager.error(LogCategory.NETWORK, `Network request error: ${error}`); const errorResponse: ChannelApiResponse = { success: false, @@ -379,12 +396,12 @@ export class ClassRoomService { return errorResponse; } } - + // 发起HTTP GET请求 private async getRequest(endpoint: string): Promise { try { const httpRequest = this.createHttpClient(); - + const response = await httpRequest.request( `${CHANNEL_API_CONFIG.baseUrl}/${endpoint}`, { @@ -393,9 +410,9 @@ export class ClassRoomService { readTimeout: CHANNEL_API_CONFIG.timeout } ); - + httpRequest.destroy(); - + if (response.responseCode === 200) { const jsonString: string = response.result ? response.result.toString() : '{}'; try { @@ -410,7 +427,7 @@ export class ClassRoomService { return errorResponse; } } - + const errorResponse: ChannelApiResponse = { success: false, message: `HTTP error: ${response.responseCode}` @@ -425,15 +442,15 @@ export class ClassRoomService { return errorResponse; } } - + // 检查当前用户是否为教师 public checkIsTeacher(): boolean { const currentAccount = settingsService.getCurrentAccount(); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `检查用户 ${currentAccount} 是否为教师`); - + const category = this.dbService.getUserCategory(currentAccount); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `用户类别: ${category}`); - + // 首先通过数据库类别判断 if (category === 'teacher') { this.isTeacher = true; @@ -445,23 +462,23 @@ export class ClassRoomService { this.isTeacher = currentAccount.includes('0'); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `通过账号判断用户身份为${this.isTeacher ? '教师' : '学生'}`); } - - logManager.info(LogCategory.USER, LogEventType.SYSTEM_INFO, + + logManager.info(LogCategory.USER, LogEventType.SYSTEM_INFO, `用户 ${currentAccount} 的角色为: ${this.isTeacher ? '教师' : '学生'}`); return this.isTeacher; } - + // 启动消息轮询 private startPolling(): void { if (this.pollTimerId !== -1) { clearInterval(this.pollTimerId); } - + this.pollTimerId = setInterval(() => { this.pollForUpdates(); }, CHANNEL_API_CONFIG.pollInterval); } - + // 停止消息轮询 private stopPolling(): void { if (this.pollTimerId !== -1) { @@ -469,24 +486,24 @@ export class ClassRoomService { this.pollTimerId = -1; } } - + // 轮询更新 private async pollForUpdates(): Promise { if (!this.connected || !this.currentClassCode) { return; } - + try { // 查询新消息 const messagesResponse = await this.getRequest(`messages/${this.currentClassCode}?since=${this.lastMessageId}`); - + if (messagesResponse.success && messagesResponse.data && Array.isArray(messagesResponse.data)) { const messages = messagesResponse.data as MessageModel[]; - + if (messages.length > 0) { // 更新最后接收的消息ID this.lastMessageId = messages[messages.length - 1].id; - + // 将消息添加到当前会话 if (this.currentSession) { messages.forEach(message => { @@ -499,9 +516,9 @@ export class ClassRoomService { this.currentSession?.messages.push(message); // 确保消息被添加后立即通知UI this.notifyEvent(WebSocketEventType.SEND_MESSAGE, message); - + // 记录接收到的新消息(来自他人) - logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `收到新消息 - ID: ${message.id}, 发送者: ${message.senderName}, 内容: ${message.content}`); } } @@ -509,28 +526,28 @@ export class ClassRoomService { } } } - + // 查询新题目 const questionsResponse = await this.getRequest(`questions/${this.currentClassCode}?since=${this.lastQuestionId}`); - + if (questionsResponse.success && questionsResponse.data && Array.isArray(questionsResponse.data)) { const questions = questionsResponse.data as QuestionModel[]; - + if (questions.length > 0) { // 更新最后接收的题目ID this.lastQuestionId = questions[questions.length - 1].questionId; - + // 处理题目 questions.forEach(question => { if (this.currentSession) { // 检查题目是否已存在 const existingQuestionIndex = this.currentSession.questions.findIndex(q => q.questionId === question.questionId); - + if (existingQuestionIndex === -1) { // 新题目 this.currentSession.questions.push(question); this.notifyEvent(WebSocketEventType.PUBLISH_QUESTION, question); - + // 如果题目处于活动状态且有结束时间,设置倒计时 if (question.status === QuestionStatus.ACTIVE && question.duration > 0) { setTimeout(() => { @@ -541,10 +558,10 @@ export class ClassRoomService { // 更新已有题目 const oldQuestion = this.currentSession.questions[existingQuestionIndex]; const oldStatus = oldQuestion.status; - + // 更新题目 this.currentSession.questions[existingQuestionIndex] = question; - + // 如果状态从活动变为结束,通知题目结束 if (oldStatus === QuestionStatus.ACTIVE && question.status === QuestionStatus.ENDED) { this.notifyEvent(WebSocketEventType.END_QUESTION, question); @@ -554,13 +571,13 @@ export class ClassRoomService { }); } } - + // 查询课堂状态 const sessionResponse = await this.getRequest(`session/${this.currentClassCode}`); - + if (sessionResponse.success && sessionResponse.data) { const session = sessionResponse.data as ClassSessionModel; - + // 检查课堂是否已结束 if (session.status === ClassSessionStatus.ENDED && this.currentSession?.status === ClassSessionStatus.ACTIVE) { // 更新当前会话状态 @@ -568,10 +585,10 @@ export class ClassRoomService { this.currentSession.status = ClassSessionStatus.ENDED; this.currentSession.endTime = session.endTime ? new Date(session.endTime) : new Date(); } - + // 通知课堂结束 this.notifyEvent(WebSocketEventType.END_CLASS, session); - + // 如果不是教师,停止轮询 if (!this.isTeacher) { this.disconnect(); @@ -582,33 +599,33 @@ export class ClassRoomService { logManager.error(LogCategory.NETWORK, `Polling error: ${error}`); } } - + // 检查题目状态 private async checkQuestionStatus(questionId: string): Promise { try { const response = await this.getRequest(`question/${questionId}`); - + if (response.success && response.data) { const question = response.data as QuestionModel; - + // 如果题目仍处于活动状态,结束题目 if (question.status === QuestionStatus.ACTIVE && this.currentSession) { // 查找并更新本地题目 const questionIndex = this.currentSession.questions.findIndex(q => q.questionId === questionId); - + if (questionIndex !== -1) { // 更新题目状态 this.currentSession.questions[questionIndex].status = QuestionStatus.ENDED; this.currentSession.questions[questionIndex].endTime = new Date(); - + // 提交题目状态更新 const request: EndQuestionRequest = { questionId: questionId, classCode: this.currentClassCode }; - + await this.postRequest('endQuestion', request); - + // 通知题目结束 this.notifyEvent(WebSocketEventType.END_QUESTION, this.currentSession.questions[questionIndex]); } @@ -618,15 +635,15 @@ export class ClassRoomService { logManager.error(LogCategory.CLASS, `Error checking question status: ${error}`); } } - + // 连接服务 public async connect(): Promise { if (this.connected) return true; - + try { // 验证服务器连接 const response = await this.getRequest('ping'); - + if (response.success) { this.connected = true; const connectionEvent: EventConnection = { status: 'connected' }; @@ -642,23 +659,23 @@ export class ClassRoomService { return false; } } - + // 断开连接 public disconnect(): void { if (!this.connected) return; - + // 停止轮询 this.stopPolling(); - + this.connected = false; this.notifyEvent('connection', { status: 'disconnected' }); logManager.info(LogCategory.NETWORK, LogEventType.SYSTEM_INFO, 'Channel service disconnected'); - + // 清空当前会话 this.currentSession = null; this.currentClassCode = ''; } - + // 创建新课堂(教师) public async createClassSession(className: string, classCode: string): Promise { try { @@ -669,23 +686,23 @@ export class ClassRoomService { teacherAccount: settingsService.getCurrentAccount(), teacherName: settingsService.getUserNickname() }; - + const response = await this.postRequest('createSession', reqData); - + if (response.success && response.data) { // 设置当前会话 this.currentSession = response.data as ClassSessionModel; this.currentClassCode = classCode; this.isTeacher = true; this.connected = true; - + // 开始消息轮询 this.startPolling(); - + // 通知连接状态 const connectionEvent: EventConnection = { status: 'connected' }; this.notifyEvent(WebSocketEventType.JOIN_CLASS, connectionEvent); - + return this.currentSession; } else { logManager.error(LogCategory.CLASS, `Create session failed: ${response.message}`); @@ -696,7 +713,7 @@ export class ClassRoomService { return null; } } - + // 加入课堂(学生) public async joinClassSession(classCode: string): Promise { try { @@ -706,23 +723,23 @@ export class ClassRoomService { studentAccount: settingsService.getCurrentAccount(), studentName: settingsService.getUserNickname() }; - + const response = await this.postRequest('joinSession', reqData); - + if (response.success && response.data) { // 设置当前会话 this.currentSession = response.data as ClassSessionModel; this.currentClassCode = classCode; this.isTeacher = false; this.connected = true; - + // 开始消息轮询 this.startPolling(); - + // 通知连接状态 const connectionEvent: EventConnection = { status: 'connected' }; this.notifyEvent(WebSocketEventType.JOIN_CLASS, connectionEvent); - + return true; } else { logManager.error(LogCategory.CLASS, `Join session failed: ${response.message}`); @@ -733,11 +750,11 @@ export class ClassRoomService { return false; } } - + // 发送消息 public async sendMessage(content: string): Promise { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `========= 开始发送消息 =========`); - + // 检查前提条件 if (!this.currentSession) { hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `发送失败: currentSession为空`); @@ -751,52 +768,52 @@ export class ClassRoomService { hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `发送失败: currentClassCode为空`); return false; } - + if (!this.currentSession || !this.connected || !this.currentClassCode) { logManager.error(LogCategory.CLASS, `Cannot send message: No active session or not connected`); return false; } - + // Debug message: Log session and connection information hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `会话ID: ${this.currentSession.sessionId}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `课堂码: ${this.currentClassCode}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `连接状态: ${this.connected}`); - + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `[DEBUG] Message sending attempt - Session ID: ${this.currentSession.sessionId}, Class Code: ${this.currentClassCode}, Connection status: ${this.connected}`); - + // 获取用户信息 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `获取用户信息`); const currentAccount = settingsService.getCurrentAccount(); const senderName = this.dbService.getUserNickname(currentAccount) || currentAccount; const role = this.isTeacher ? SenderRole.TEACHER : SenderRole.STUDENT; - + hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `用户账号: ${currentAccount}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `用户名称: ${senderName}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `用户角色: ${role}`); - + // Debug message: Log user information logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `[DEBUG] Message sender info - Account: ${currentAccount}, Name: ${senderName}, Role: ${role}`); - + // 创建消息对象 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `创建消息对象`); try { // 单独记录消息创建过程 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `消息内容: ${content}`); - + const message = new MessageModel(currentAccount, senderName, role, content); - + hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `消息创建成功,ID: ${message.id}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `消息时间戳: ${message.timestamp.toISOString()}`); - + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `Creating message: ID=${message.id}, Content=${content}, Sender=${senderName}, Role=${role}`); - + // 构建请求 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `构建发送请求`); const request: SendMessageRequest = { classCode: this.currentClassCode, message: message }; - + // 打印请求对象,但避免过长内容 let requestStr = ""; try { @@ -805,49 +822,49 @@ export class ClassRoomService { } catch (e) { hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `无法序列化请求: ${e}`); } - + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `发送消息请求: classCode=${this.currentClassCode}, messageId=${message.id}`); - + // 发送请求 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `开始发送POST请求到sendMessage`); const response = await this.postRequest('sendMessage', request); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `请求完成,响应success: ${response.success}`); - + // 详细记录响应 if (response.message) { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `响应消息: ${response.message}`); } if (response.data) { try { - const dataStr = JSON.stringify(response.data).substring(0, 100) + + const dataStr = JSON.stringify(response.data).substring(0, 100) + (JSON.stringify(response.data).length > 100 ? '...' : ''); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `响应数据(摘要): ${dataStr}`); } catch (e) { hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `无法序列化响应数据: ${e}`); } } - + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `服务器响应: success=${response.success}, message=${response.message || "无"}`); - + if (response.success) { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `消息发送成功,更新lastMessageId: ${message.id}`); // 不再将消息添加到currentSession,因为UI层已经添加了本地消息 - // (已删除): this.currentSession.messages.push(message); - + // (已删除): this.currentSession.messages.push(message); + // 不需要再次通知UI,因为UI层已经添加了本地消息 // (已删除): this.notifyEvent(WebSocketEventType.SEND_MESSAGE, message); - + // 只添加到lastMessageId,确保轮询时不重复获取 this.lastMessageId = message.id; - + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `Message sent successfully: ${message.id}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `========= 消息发送成功 =========`); - + return true; } else { // Enhanced error logging with response details hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `消息发送失败: ${response.message || '未知错误'}`); - logManager.error(LogCategory.CLASS, `Send message failed: ${response.message || 'Unknown error'}, Response data: ${JSON.stringify(response.data || {})}`); + logManager.error(LogCategory.CLASS, `Send message failed: ${response.message || 'Unknown error'}, Response data: ${JSON.stringify(response.data)}`); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `========= 消息发送失败 =========`); return false; } diff --git a/entry/src/main/ets/common/DatabaseService.ets b/entry/src/main/ets/common/DatabaseService.ets index 5ea4a72..36740ad 100644 --- a/entry/src/main/ets/common/DatabaseService.ets +++ b/entry/src/main/ets/common/DatabaseService.ets @@ -14,13 +14,13 @@ export class UserModel { phone: string = ''; photo: string = ''; category: string = ''; - - constructor(account: string = '', - nickname: string = '', - email: string = '', - phone: string = '', - photo: string = '', - category: string = '') { + + constructor(account: string = '', + nickname: string = '', + email: string = '', + phone: string = '', + photo: string = '', + category: string = '') { this.account = account; this.nickname = nickname; this.email = email; @@ -51,13 +51,16 @@ export interface UserInfo { export interface ApiConfig { baseUrl: string; timeout: number; + websocketDomain: string } // 定义API服务配置 const API_CONFIG: ApiConfig = { // 修改为用户实际使用的服务器地址 baseUrl: 'http://139.155.155.67:2342/api', - timeout: 10000 // 10秒超时 + timeout: 10000, // 10秒超时 + websocketDomain: "139.155.155.67:4233", + // websocketDomain: "198.18.0.1:8080", }; // 修正JSON响应类型处理 @@ -79,23 +82,26 @@ export class DatabaseService { // 缓存用户数据,减少API调用 private userCache: Map = new Map(); // 缓存验证结果 - private authCache: Map = new Map(); - + private authCache: Map = new Map(); // Callbacks for user data changes private userDataChangeCallbacks: (() => void)[] = []; - + private constructor() { // 初始化缓存 hilog.info(0, 'ClassMG', 'Database Service Initialized'); } - + public static getInstance(): DatabaseService { if (!DatabaseService.instance) { DatabaseService.instance = new DatabaseService(); } return DatabaseService.instance; } - + + public static getApiConfig():ApiConfig{ + return API_CONFIG; + } + // 创建HTTP请求客户端 private createHttpClient(): http.HttpRequest { let httpRequest = http.createHttp(); @@ -104,27 +110,27 @@ export class DatabaseService { }); return httpRequest; } - + // 执行登录验证 public async validateUser(account: string, password: string): Promise { try { // 生成缓存键 const cacheKey = `${account}:${password}`; - + // 检查缓存 if (this.authCache.has(cacheKey)) { const cachedResult = this.authCache.get(cacheKey); return cachedResult === true; // 确保返回布尔值 } - + // 创建HTTP客户端 const httpRequest = this.createHttpClient(); - + // 准备登录数据 let loginData = {} as Record; loginData.account = account; loginData.password = password; - + // 发送登录请求 const response = await httpRequest.request( `${API_CONFIG.baseUrl}/login`, @@ -138,21 +144,21 @@ export class DatabaseService { readTimeout: API_CONFIG.timeout } ); - + // 释放HTTP客户端 httpRequest.destroy(); - + // 检查响应 if (response.responseCode === 200) { // 使用显式类型转换处理JSON结果 const jsonString: string = response.result ? response.result.toString() : '{}'; try { const result: ApiResponse = JSON.parse(jsonString) as ApiResponse; - + // 缓存结果 const isValid = result.success === true; this.authCache.set(cacheKey, isValid); - + // 如果登录成功且有用户数据,缓存用户信息 if (isValid && result.user) { const user = new UserModel( @@ -165,28 +171,28 @@ export class DatabaseService { ); this.userCache.set(account, user); } - + return isValid; } catch (parseError) { const errorMsg: string = parseError instanceof Error ? parseError.message : String(parseError); hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`); } } - + // 默认返回false return false; } catch (error) { hilog.error(0, 'ClassMG', `Error validating user: ${error instanceof Error ? error.message : String(error)}`); - + // 临时降级到模拟验证 - 仅用于开发/测试 -/* if (account === '2' || account === '9222' || account === '0') { - return password === '1'; - } - */ + /* if (account === '2' || account === '9222' || account === '0') { + return password === '1'; + } + */ return false; } } - + // 获取用户类别 public getUserCategory(account: string): string { // 检查缓存 @@ -194,17 +200,17 @@ export class DatabaseService { const user = this.userCache.get(account); return user ? user.category : ''; } - + // 降级逻辑 - 当API不可用时 if (account.includes('2')) { return 'student'; } else if (account.includes('0')) { return 'teacher'; } - + return ''; } - + // 获取用户昵称 public getUserNickname(account: string): string { // 检查缓存 @@ -212,15 +218,21 @@ export class DatabaseService { const user = this.userCache.get(account); return user ? user.nickname : ''; } - + // 默认值 - 仅用于开发/测试 - if (account === '2') return '张三'; - if (account === '9222') return '李华'; - if (account === '0') return '教师demo'; - + if (account === '2') { + return '张三'; + } + if (account === '9222') { + return '李华'; + } + if (account === '0') { + return '教师demo'; + } + return ''; } - + // 异步获取用户信息 public async getUserByAccountAsync(account: string): Promise { try { @@ -229,10 +241,10 @@ export class DatabaseService { const cachedUser = this.userCache.get(account); return cachedUser || null; } - + // 创建HTTP客户端 const httpRequest = this.createHttpClient(); - + // 发送获取用户信息请求 const response = await httpRequest.request( `${API_CONFIG.baseUrl}/user/${account}`, @@ -242,17 +254,17 @@ export class DatabaseService { readTimeout: API_CONFIG.timeout } ); - + // 释放HTTP客户端 httpRequest.destroy(); - + // 检查响应 if (response.responseCode === 200) { // 使用显式类型转换处理JSON结果 const jsonString: string = response.result ? response.result.toString() : '{}'; try { const result: ApiResponse = JSON.parse(jsonString) as ApiResponse; - + if (result.success && result.user) { const user = new UserModel( result.user.account, @@ -262,10 +274,10 @@ export class DatabaseService { result.user.photo, result.user.category ); - + // 更新缓存 this.userCache.set(account, user); - + return user; } } catch (parseError) { @@ -273,24 +285,24 @@ export class DatabaseService { hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`); } } - + return null; } catch (error) { hilog.error(0, 'ClassMG', `Error getting user: ${error instanceof Error ? error.message : String(error)}`); - + // 降级到模拟数据 - 仅用于开发/测试 -/* if (account === '2') { - return new UserModel('2', '张三', 'student@qq.com', '17267383831', '', 'student'); - } else if (account === '9222') { - return new UserModel('9222', '李华', 'student123@qq.com', '12345678901', '', 'student'); - } else if (account === '0') { - return new UserModel('0', '教师demo', 'teach@qq.com', '', '', 'teacher'); - }*/ - + /* if (account === '2') { + return new UserModel('2', '张三', 'student@qq.com', '17267383831', '', 'student'); + } else if (account === '9222') { + return new UserModel('9222', '李华', 'student123@qq.com', '12345678901', '', 'student'); + } else if (account === '0') { + return new UserModel('0', '教师demo', 'teach@qq.com', '', '', 'teacher'); + }*/ + return null; } } - + // 为了兼容性保留的同步方法 - 使用缓存 public getUserByAccount(account: string): UserModel | null { // 检查缓存 @@ -298,7 +310,7 @@ export class DatabaseService { const cachedUser = this.userCache.get(account); return cachedUser || null; } - + // 如果缓存中没有,返回null并触发异步加载 this.getUserByAccountAsync(account) .then((user: UserModel | null) => { @@ -310,10 +322,10 @@ export class DatabaseService { const errorMsg: string = error instanceof Error ? error.message : String(error); hilog.error(0, 'ClassMG', `Error in async user fetch: ${errorMsg}`); }); - + return null; } - + // 更新用户邮箱 - 连接API public async updateUserEmail(account: string, newEmail: string): Promise { try { @@ -321,14 +333,14 @@ export class DatabaseService { if (!this.validateEmailFormat(newEmail)) { return false; } - + // 创建HTTP客户端 const httpRequest = this.createHttpClient(); - + // 准备更新数据 let updateData = {} as Record; updateData.email = newEmail; - + // 发送更新请求 const response = await httpRequest.request( `${API_CONFIG.baseUrl}/user/${account}/email`, @@ -342,17 +354,17 @@ export class DatabaseService { readTimeout: API_CONFIG.timeout } ); - + // 释放HTTP客户端 httpRequest.destroy(); - + // 检查响应 if (response.responseCode === 200) { // 使用显式类型转换处理JSON结果 const jsonString: string = response.result ? response.result.toString() : '{}'; try { const result: ApiResponse = JSON.parse(jsonString) as ApiResponse; - + if (result.success) { // 更新本地缓存 if (this.userCache.has(account)) { @@ -362,10 +374,10 @@ export class DatabaseService { this.userCache.set(account, user); } } - + // 通知更新 this.notifyUserDataChange(); - + return true; } } catch (parseError) { @@ -373,12 +385,12 @@ export class DatabaseService { hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`); } } - + return false; } catch (error) { const errorMessage: string = error instanceof Error ? error.message : String(error); hilog.error(0, 'ClassMG', `Error updating user email: ${errorMessage}`); - + // 降级到本地更新 - 仅用于开发/测试 if (this.userCache.has(account)) { const user = this.userCache.get(account); @@ -389,41 +401,41 @@ export class DatabaseService { return true; } } - + return false; } } - + // 注册数据变化回调 public registerUserDataChangeCallback(callback: () => void): void { this.userDataChangeCallbacks.push(callback); } - + // 通知所有回调 private notifyUserDataChange(): void { this.userDataChangeCallbacks.forEach(callback => { callback(); }); } - + // 验证邮箱格式 public validateEmailFormat(email: string): boolean { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } - + // 清除缓存 public clearCache(): void { this.userCache.clear(); this.authCache.clear(); } - + // 注册用户 public async registerUser(userData: RegisterUserData): Promise { try { // 创建HTTP客户端 const httpRequest = this.createHttpClient(); - + // 发送注册请求 const response = await httpRequest.request( `${API_CONFIG.baseUrl}/register`, @@ -437,10 +449,10 @@ export class DatabaseService { readTimeout: API_CONFIG.timeout } ); - + // 释放HTTP客户端 httpRequest.destroy(); - + // 检查响应 if (response.responseCode === 200) { // 使用显式类型转换处理JSON结果 @@ -453,7 +465,7 @@ export class DatabaseService { hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`); } } - + return false; } catch (error) { hilog.error(0, 'ClassMG', `Error registering user: ${error instanceof Error ? error.message : String(error)}`); diff --git a/entry/src/main/ets/pages/ClassLivePage.ets b/entry/src/main/ets/pages/ClassLivePage.ets index 04c70df..fad91ae 100644 --- a/entry/src/main/ets/pages/ClassLivePage.ets +++ b/entry/src/main/ets/pages/ClassLivePage.ets @@ -20,6 +20,9 @@ import { import logManager, { LogCategory, LogEventType } from '../common/logtext'; import http from '@ohos.net.http'; import hilog from '@ohos.hilog'; +import WebSocketMessage from '../util/WebsocketMessage'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { DatabaseService } from '../common/DatabaseService'; // 添加hilog和tianChannel常量 const TIAN_CHANNEL_DOMAIN_ID = 0x00201; // 自定义域ID @@ -133,8 +136,11 @@ struct ClassLivePage { }; // 用于服务实例 private classRoomService: ClassRoomService = ClassRoomService.getInstance(); + wsClient: WebSocketMessage = new WebSocketMessage(DatabaseService.getApiConfig().websocketDomain, "111111") aboutToAppear() { + this.wsClient.connect(); + // 注册语言变化的回调 settingsService.registerLanguageChangeCallback(() => { this.texts = settingsService.getTextResources(); @@ -170,6 +176,19 @@ struct ClassLivePage { this.registerEventListeners(); logManager.info(LogCategory.CLASS, LogEventType.PAGE_APPEAR, 'ClassLivePage'); + + + this.wsClient.setOnMessage((data) => { + hilog.debug(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, "收到消息: " + data.toString()); + try { + let j: MessageModel = MessageModel.fromJSON(data.toString()) //JSON.parse(data.toString()) as MessageModel + this.messages.push(j) + hilog.debug(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, "渲染: " + j); + // this.messages.push(data.toString()) + } catch (e) { + hilog.debug(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, "消息解析失败: " + e); + } + }); } // 注册事件监听 @@ -177,6 +196,7 @@ struct ClassLivePage { // 接收新消息 this.messageCallback = (data: EventData) => { const message = data as MessageModel; + message.useTime() if (message && message.id) { // 检查消息是否已经存在(防止重复) const existingMessage = this.messages.find(m => m.id === message.id); diff --git a/entry/src/main/ets/util/WebsocketMessage.ets b/entry/src/main/ets/util/WebsocketMessage.ets new file mode 100644 index 0000000..8a42904 --- /dev/null +++ b/entry/src/main/ets/util/WebsocketMessage.ets @@ -0,0 +1,81 @@ +import { webSocket } from "@kit.NetworkKit"; +import { BusinessError } from "@kit.BasicServicesKit"; +import { hilog } from "@kit.PerformanceAnalysisKit"; + +const DOMAIN: number = 0 +const TAG: string = "websocket message" + +export default class WebSocketMessage { + private ws = webSocket.createWebSocket(); + private url: string; + private room: string; + private onMessageCallback: (data: string | ArrayBuffer) => void = () => { + }; + + constructor(url: string, room: string) { + this.url = url; + this.room = room; + } + + public connect(): void { + this.ws.on('open', (err: BusinessError, value: Object) => { + hilog.debug(DOMAIN, TAG, "on open, status: " + JSON.stringify(value)); + this.ws.send("Hello, server!", (err: BusinessError, success: boolean) => { + if (!err) { + hilog.debug(DOMAIN, TAG, "Message sent successfully"); + } else { + hilog.debug(DOMAIN, TAG, "Failed to send the message. Err: " + JSON.stringify(err)); + } + }); + }); + + this.ws.on('message', (err: BusinessError, value: string | ArrayBuffer) => { + hilog.debug(DOMAIN, TAG, "on message, message: " + value); + if (value === 'bye') { + this.close(); + } + this.onMessageCallback(value); + }); + + this.ws.on('close', (err: BusinessError, value: webSocket.CloseResult) => { + hilog.debug(DOMAIN, TAG, "on close, code: " + value.code + ", reason: " + value.reason); + }); + + this.ws.on('error', (err: BusinessError) => { + hilog.debug(DOMAIN, TAG, "on error, error: " + JSON.stringify(err)); + }); + + const wsUrl = `ws://${this.url}/ws/room?room=${this.room}`; + this.ws.connect(wsUrl, (err: BusinessError, connected: boolean) => { + if (!err) { + hilog.debug(DOMAIN, TAG, "Connected successfully"); + } else { + hilog.debug(DOMAIN, TAG, "Connection failed. Err: " + JSON.stringify(err)); + } + }); + } + + public setOnMessage(callback: (data: string | ArrayBuffer) => void): void { + this.onMessageCallback = callback; + } + + public sendMessage(message: string): void { + this.ws.send(message, (err: BusinessError, success: boolean) => { + if (!err) { + hilog.debug(DOMAIN, TAG, "Message sent: " + message); + } else { + hilog.debug(DOMAIN, TAG, "Send failed: " + JSON.stringify(err)); + } + }); + } + + public close(): void { + this.ws.close((err: BusinessError, success: boolean) => { + if (!err) { + hilog.debug(DOMAIN, TAG, "Connection closed successfully"); + } else { + hilog.debug(DOMAIN, TAG, "Failed to close the connection. Err: " + JSON.stringify(err)); + } + }); + } +}