修复聊天闪退bug,添加对接聊天服务器

This commit is contained in:
mcmy 2025-04-21 16:09:31 +08:00
parent 3f5dd9ddaf
commit a676fe8cd0
4 changed files with 324 additions and 194 deletions

View File

@ -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<string,object>
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<string, Array<EventCallback>> = 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<ChannelApiResponse> {
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<ChannelApiResponse> {
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<void> {
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<void> {
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<boolean> {
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<ClassSessionModel | null> {
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<boolean> {
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<boolean> {
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;
}

View File

@ -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<string, UserModel> = new Map();
// 缓存验证结果
private authCache: Map<string, boolean> = new Map();
private authCache: Map<string, boolean> = 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<boolean> {
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<string, string>;
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<UserModel | null> {
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<boolean> {
try {
@ -321,14 +333,14 @@ export class DatabaseService {
if (!this.validateEmailFormat(newEmail)) {
return false;
}
// 创建HTTP客户端
const httpRequest = this.createHttpClient();
// 准备更新数据
let updateData = {} as Record<string, string>;
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<boolean> {
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)}`);

View File

@ -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);

View File

@ -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));
}
});
}
}