修复已知问题

This commit is contained in:
cc 2025-04-01 19:49:15 +08:00
parent fa666d934e
commit f1e7589344
8 changed files with 1399 additions and 0 deletions

328
DATABASE_SETUP.md Normal file
View File

@ -0,0 +1,328 @@
# 智慧教室管理系统数据库与API配置指南
本文档详细介绍如何在宝塔面板上配置MySQL数据库和Node.js API服务以支持智慧教室管理系统的HarmonyOS应用。
## 一、宝塔面板安装与配置
### 1.1 安装宝塔面板
如果尚未安装宝塔面板,请参考[宝塔官方文档](https://www.bt.cn/bbs/thread-19376-1-1.html)进行安装。
### 1.2 安装必要软件
在宝塔面板中安装以下软件:
- Nginx (用于Web服务器)
- MySQL (用于数据库)
- PHP (如需使用phpMyAdmin)
- Node.js (用于API服务)
## 二、MySQL数据库配置
### 2.1 创建数据库
1. 在宝塔面板左侧找到"数据库"选项
2. 点击"添加数据库"
3. 填写以下信息:
- 数据库名称:`hongm`
- 用户名:`hongm`
- 密码:`JsKJeG7CX2WnyArt` (或自定义安全密码)
- 访问权限:选择"所有人" (开发环境)或指定IP (生产环境)
4. 点击"提交"创建数据库
### 2.2 导入数据结构
1. 在数据库列表找到刚创建的`hongm`数据库
2. 点击"管理"进入phpMyAdmin
3. 选择"SQL"标签
4. 复制并执行以下SQL语句
```sql
-- 创建用户信息表
CREATE TABLE IF NOT EXISTS UserText (
account VARCHAR(20) PRIMARY KEY,
nickname VARCHAR(50) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
photo VARCHAR(255) DEFAULT NULL,
category VARCHAR(20) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建用户密码表
CREATE TABLE IF NOT EXISTS UserPassword (
account VARCHAR(20) PRIMARY KEY,
password VARCHAR(50) NOT NULL,
FOREIGN KEY (account) REFERENCES UserText(account)
);
-- 插入示例用户数据
INSERT INTO UserText (account, nickname, email, phone, category) VALUES
('2', '张三', 'student@qq.com', '17267383831', 'student'),
('9222', '李华', 'student123@qq.com', '12345678901', 'student'),
('0', '教师demo', 'teach@qq.com', NULL, 'teacher');
-- 插入密码(所有密码都是'1')
INSERT INTO UserPassword (account, password) VALUES
('2', '1'),
('9222', '1'),
('0', '1');
```
## 三、Node.js API服务配置
### 3.1 创建网站
1. 在宝塔面板左侧找到"网站"选项
2. 点击"添加站点"
3. 填写以下信息:
- 域名:`api.yourdomain.com` (或您的服务器IP地址)
- 备注:`智慧教室管理系统API服务`
- PHP版本纯静态
4. 点击"提交"创建网站
### 3.2 上传API服务代码
1. 在网站目录中创建`app.js`文件
2. 将以下代码复制到`app.js`中:
```javascript
const express = require('express');
const mysql = require('mysql2');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
// 启用CORS和JSON解析
app.use(cors());
app.use(bodyParser.json());
// 数据库连接配置
const dbConfig = {
host: 'localhost',
port: 3306, // MySQL默认端口根据您的配置可能需要修改
user: 'hongm',
password: 'JsKJeG7CX2WnyArt',
database: 'hongm'
};
// 创建数据库连接池
const pool = mysql.createPool(dbConfig);
const promisePool = pool.promise();
// 验证用户登录API
app.post('/api/login', async (req, res) => {
try {
const { account, password } = req.body;
// 查询密码
const [passwordRows] = await promisePool.query(
'SELECT password FROM UserPassword WHERE account = ?',
[account]
);
if (passwordRows.length === 0) {
return res.status(401).json({ success: false, message: '账号不存在' });
}
const storedPassword = passwordRows[0].password;
if (password !== storedPassword) {
return res.status(401).json({ success: false, message: '密码错误' });
}
// 查询用户信息
const [userRows] = await promisePool.query(
'SELECT * FROM UserText WHERE account = ?',
[account]
);
if (userRows.length === 0) {
return res.status(401).json({ success: false, message: '用户信息不存在' });
}
const user = userRows[0];
// 返回成功登录信息和用户数据
res.json({
success: true,
message: '登录成功',
user: {
account: user.account,
nickname: user.nickname,
email: user.email,
phone: user.phone,
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
category: user.category
}
});
} catch (error) {
console.error('登录错误:', error);
res.status(500).json({ success: false, message: '服务器错误' });
}
});
// 获取用户信息API
app.get('/api/user/:account', async (req, res) => {
try {
const { account } = req.params;
// 查询用户信息
const [userRows] = await promisePool.query(
'SELECT * FROM UserText WHERE account = ?',
[account]
);
if (userRows.length === 0) {
return res.status(404).json({ success: false, message: '用户不存在' });
}
const user = userRows[0];
// 返回用户数据
res.json({
success: true,
user: {
account: user.account,
nickname: user.nickname,
email: user.email,
phone: user.phone,
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
category: user.category
}
});
} catch (error) {
console.error('获取用户信息错误:', error);
res.status(500).json({ success: false, message: '服务器错误' });
}
});
// 更新用户邮箱API
app.put('/api/user/:account/email', async (req, res) => {
try {
const { account } = req.params;
const { email } = req.body;
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.status(400).json({ success: false, message: '邮箱格式无效' });
}
// 更新邮箱
const [result] = await promisePool.query(
'UPDATE UserText SET email = ? WHERE account = ?',
[email, account]
);
if (result.affectedRows === 0) {
return res.status(404).json({ success: false, message: '用户不存在' });
}
res.json({ success: true, message: '邮箱更新成功' });
} catch (error) {
console.error('更新邮箱错误:', error);
res.status(500).json({ success: false, message: '服务器错误' });
}
});
// 启动服务器
app.listen(port, () => {
console.log(`API服务运行在端口 ${port}`);
});
```
### 3.3 安装依赖并启动服务
1. 通过SSH连接到服务器进入网站目录
```bash
cd /www/wwwroot/your_website_directory
```
2. 初始化项目并安装依赖:
```bash
npm init -y
npm install express mysql2 cors body-parser
```
3. 安装PM2并启动服务
```bash
npm install -g pm2
pm2 start app.js --name "classmg-api"
pm2 startup
pm2 save
```
### 3.4 配置Nginx反向代理 (可选)
为了使API更安全和易于访问可以配置Nginx反向代理
1. 在宝塔面板中,找到您的网站,点击"设置"
2. 点击"反向代理",添加新的反向代理
3. 填写以下信息:
- 名称:`API反向代理`
- 目标URL`http://127.0.0.1:3000`
- 发送域名:勾选
4. 点击"提交"
现在您可以通过 `http://your_domain/api/` 访问API服务。
### 3.5 配置防火墙
确保服务器防火墙允许访问3000端口(如果直接使用Node.js)或80/443端口(如果使用Nginx反向代理)。
## 四、HarmonyOS应用配置
### 4.1 修改API地址
在HarmonyOS应用的`DatabaseService.ets`文件中确保API地址正确配置
```typescript
// 定义API服务配置
const API_CONFIG: ApiConfig = {
// 修改为您的实际服务器地址
baseUrl: 'http://your_server_ip:3000/api',
timeout: 10000
};
```
### 4.2 构建并测试应用
1. 在DevEco Studio中构建应用
2. 在模拟器或真机上测试登录功能
3. 尝试使用示例账号登录:
- 账号:`2``9222``0`
- 密码:`1`
## 五、生产环境安全建议
1. 启用HTTPS为API服务配置SSL证书
2. 实现更安全的密码存储:使用加盐哈希而不是明文存储
3. 添加JWT认证实现令牌机制进行会话管理
4. 限制请求频率:防止暴力破解攻击
5. 日志记录记录所有API调用以便审计
6. 定期备份数据库:防止数据丢失
## 六、故障排除
### 6.1 API连接问题
如果应用无法连接到API
1. 检查服务器IP和端口是否正确
2. 确认防火墙设置允许连接
3. 验证API服务是否正在运行 (`pm2 status`)
4. 检查服务器日志 (`pm2 logs classmg-api`)
### 6.2 数据库问题
如果API服务无法连接到数据库
1. 确认数据库用户名和密码正确
2. 检查数据库用户权限
3. 验证数据库服务是否正在运行
### 6.3 降级模式
应用内已实现降级模式当无法连接到API服务时将使用本地模拟数据
- 账号:`2``9222``0`
- 密码:`1`

81
README.md Normal file
View File

@ -0,0 +1,81 @@
# 智慧教室管理系统
## 项目概述
智慧教室管理系统是一款基于HarmonyOS/ArkTS开发的应用旨在提供智能化的教室管理解决方案。系统包含多个功能模块用于管理教室状态、课程信息和用户权限等。
## 功能特点
- **用户管理**: 支持教师和学生两种用户类型,提供差异化的功能体验
- **教室监控**: 实时监控教室温度、湿度、人数等数据
- **课程数据**: 展示课程信息、教师信息和学生出勤情况
- **个人设置**: 用户可以修改个人信息和系统偏好设置
## 技术架构
- **前端**: HarmonyOS/ArkTS
- **后端**: MySQL数据库
- **通信**: HTTP API
## 系统要求
- HarmonyOS设备
- 网络连接
- 支持HarmonyOS 3.0及以上版本
## 数据库配置
系统使用MySQL数据库存储用户信息和系统数据。数据库配置如下:
- **数据库地址**: 139.155.155.67:25342
- **数据库名**: hongm
- **用户名**: hongm
- **密码**: JsKJeG7CX2WnyArt
### 数据库初始化
导入`database_setup.sql`文件以创建必要的表和初始数据。主要的数据表包括:
- **UserText**: 存储用户个人信息
- **UserPassword**: 存储用户登录信息
## 使用指南
### 登录系统
1. 在登录页面输入您的账号和密码(默认密码: 1
2. 系统会根据账号自动识别用户类型:
- 账号包含"2"的识别为学生
- 账号包含"0"的识别为教师
3. 登录成功后将显示过渡页面,然后进入系统主页
### 系统导航
系统包含三个主要页面:
- **首页**: 显示教室监控和综合上课数据
- **上课**: 提供课程相关功能
- **设置**: 管理个人信息和系统设置
### 个人信息设置
在设置页面,用户可以查看和修改个人信息:
- **头像**: 显示用户头像
- **账号**: 显示当前登录账号(不可修改)
- **昵称**: 显示用户昵称(不可修改)
- **邮箱**: 可修改,需符合邮箱格式
- **电话**: 显示联系电话(不可修改)
## 开发信息
- **开发团队**: 922213102班鸿蒙第一组
- **版本**: 1.0.0
## 备注
- 用户密码默认为"1"
- 系统现有示例用户:
- 学生: 账号"2",昵称"张三"
- 学生: 账号"9222",昵称"李华"
- 教师: 账号"0",昵称"教师demo"

255
api-app.js Normal file
View File

@ -0,0 +1,255 @@
const express = require('express');
const mysql = require('mysql2');
const cors = require('cors');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');
// 创建Express应用
const app = express();
const port = 3000;
// 启用CORS和JSON解析
app.use(cors());
app.use(bodyParser.json());
// 创建logs目录(如果不存在)
const logsDir = path.join(__dirname, 'logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir);
}
// 简单日志函数
function logToFile(message) {
const logFile = path.join(logsDir, 'api.log');
const timestamp = new Date().toISOString();
fs.appendFileSync(logFile, `${timestamp} - ${message}\n`);
console.log(`${timestamp} - ${message}`);
}
// 数据库连接配置
const dbConfig = {
host: 'localhost',
port: 25342, // 修改为正确的MySQL端口
user: 'hongm', // 替换为您的数据库用户名
password: 'hongm', // 替换为您的数据库密码
database: 'hongm',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
};
// 创建数据库连接池
const pool = mysql.createPool(dbConfig);
const promisePool = pool.promise();
// 检查数据库连接
async function checkDbConnection() {
try {
const connection = await promisePool.getConnection();
logToFile('数据库连接成功');
connection.release();
return true;
} catch (error) {
logToFile(`数据库连接失败: ${error.message}`);
return false;
}
}
// 在启动时测试数据库连接
checkDbConnection();
// 创建一个简单的首页响应
app.get('/', (req, res) => {
res.send('智能教室管理系统API服务正在运行');
});
// 创建一个图片目录路由
app.use('/images', express.static(path.join(__dirname, 'images')));
// API: 用户登录验证
app.post('/api/login', async (req, res) => {
try {
logToFile(`收到登录请求: ${JSON.stringify(req.body)}`);
const { account, password } = req.body;
if (!account || !password) {
return res.status(400).json({
success: false,
message: '账号和密码不能为空'
});
}
// 查询密码
const [passwordRows] = await promisePool.query(
'SELECT password FROM UserPassword WHERE account = ?',
[account]
);
if (passwordRows.length === 0) {
logToFile(`登录失败: 账号 ${account} 不存在`);
return res.status(401).json({
success: false,
message: '账号不存在'
});
}
const storedPassword = passwordRows[0].password;
if (password !== storedPassword) {
logToFile(`登录失败: 账号 ${account} 密码错误`);
return res.status(401).json({
success: false,
message: '密码错误'
});
}
// 查询用户信息
const [userRows] = await promisePool.query(
'SELECT * FROM UserText WHERE account = ?',
[account]
);
if (userRows.length === 0) {
logToFile(`登录异常: 账号 ${account} 存在但用户信息丢失`);
return res.status(500).json({
success: false,
message: '用户信息不存在'
});
}
const user = userRows[0];
logToFile(`登录成功: 账号 ${account}, 用户名 ${user.nickname}`);
// 返回成功登录信息和用户数据
res.json({
success: true,
message: '登录成功',
user: {
account: user.account,
nickname: user.nickname,
email: user.email || '',
phone: user.phone || '',
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
category: user.category
}
});
} catch (error) {
logToFile(`登录错误: ${error.message}`);
res.status(500).json({
success: false,
message: '服务器错误',
error: error.message
});
}
});
// API: 获取用户信息
app.get('/api/user/:account', async (req, res) => {
try {
const { account } = req.params;
logToFile(`获取用户信息请求: 账号 ${account}`);
// 查询用户信息
const [userRows] = await promisePool.query(
'SELECT * FROM UserText WHERE account = ?',
[account]
);
if (userRows.length === 0) {
logToFile(`获取用户信息失败: 账号 ${account} 不存在`);
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
const user = userRows[0];
logToFile(`获取用户信息成功: 账号 ${account}, 用户名 ${user.nickname}`);
// 返回用户数据
res.json({
success: true,
user: {
account: user.account,
nickname: user.nickname,
email: user.email || '',
phone: user.phone || '',
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
category: user.category
}
});
} catch (error) {
logToFile(`获取用户信息错误: ${error.message}`);
res.status(500).json({
success: false,
message: '服务器错误',
error: error.message
});
}
});
// API: 更新用户邮箱
app.put('/api/user/:account/email', async (req, res) => {
try {
const { account } = req.params;
const { email } = req.body;
logToFile(`更新用户邮箱请求: 账号 ${account}, 新邮箱 ${email}`);
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
logToFile(`更新邮箱失败: 邮箱格式无效 ${email}`);
return res.status(400).json({
success: false,
message: '邮箱格式无效'
});
}
// 检查用户是否存在
const [userCheck] = await promisePool.query(
'SELECT account FROM UserText WHERE account = ?',
[account]
);
if (userCheck.length === 0) {
logToFile(`更新邮箱失败: 账号 ${account} 不存在`);
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 更新邮箱
const [result] = await promisePool.query(
'UPDATE UserText SET email = ? WHERE account = ?',
[email, account]
);
if (result.affectedRows === 0) {
logToFile(`更新邮箱失败: 账号 ${account} 无法更新`);
return res.status(500).json({
success: false,
message: '更新邮箱失败'
});
}
logToFile(`更新邮箱成功: 账号 ${account}, 新邮箱 ${email}`);
res.json({
success: true,
message: '邮箱更新成功'
});
} catch (error) {
logToFile(`更新邮箱错误: ${error.message}`);
res.status(500).json({
success: false,
message: '服务器错误',
error: error.message
});
}
});
// 启动服务器
app.listen(port, () => {
logToFile(`API服务运行在端口 ${port}`);
});

16
api-package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "classmg-api",
"version": "1.0.0",
"description": "智能教室管理系统API服务",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
},
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mysql2": "^3.6.1"
}
}

11
build-script.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
echo "Cleaning project..."
rm -rf build/
rm -rf build-profile/
echo "Building project..."
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node /Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js --mode module -p module=entry@default -p product=default -p requiredDeviceType=phone clean
echo "Rebuilding project..."
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node /Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js --mode module -p module=entry@default -p product=default -p requiredDeviceType=phone assembleHap --analyze=normal --parallel --incremental --daemon

33
database_setup.sql Normal file
View File

@ -0,0 +1,33 @@
-- Create the UserText table to store user information
CREATE TABLE IF NOT EXISTS UserText (
account VARCHAR(20) PRIMARY KEY,
nickname VARCHAR(50) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
photo VARCHAR(255) DEFAULT NULL,
category VARCHAR(20) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create the UserPassword table to store user account and password
CREATE TABLE IF NOT EXISTS UserPassword (
account VARCHAR(20) PRIMARY KEY,
password VARCHAR(50) NOT NULL,
FOREIGN KEY (account) REFERENCES UserText(account)
);
-- Insert sample users as specified in the requirements
-- Student users
INSERT INTO UserText (account, nickname, email, phone, category) VALUES
('2', '张三', 'student@qq.com', '17267383831', 'student'),
('9222', '李华', 'student123@qq.com', '12345678901', 'student');
-- Teacher user
INSERT INTO UserText (account, nickname, email, phone, category) VALUES
('0', '教师demo', 'teach@qq.com', NULL, 'teacher');
-- Insert passwords (all passwords are '1')
INSERT INTO UserPassword (account, password) VALUES
('2', '1'),
('9222', '1'),
('0', '1');

View File

@ -0,0 +1,415 @@
import { hilog } from '@kit.PerformanceAnalysisKit';
import prompt from '@ohos.promptAction';
import http from '@ohos.net.http';
// User model for storing user information from database
export class UserModel {
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;
this.phone = phone;
this.photo = photo || 'http://139.155.155.67:2342/images/default_avatar.png'; // Default photo URL
this.category = category;
}
}
// API响应接口定义
export interface ApiResponse {
success: boolean;
message?: string;
user?: UserInfo;
}
// User info returned from API
export interface UserInfo {
account: string;
nickname: string;
email: string;
phone: string;
photo?: string;
category: string;
}
// API服务配置
export interface ApiConfig {
baseUrl: string;
timeout: number;
}
// 定义API服务配置
const API_CONFIG: ApiConfig = {
// 修改为用户实际使用的服务器地址
baseUrl: 'http://139.155.155.67:2342/api',
timeout: 10000 // 10秒超时
};
// 修正JSON响应类型处理
export type JsonResponse = string | object | ArrayBuffer;
// 真实API实现 - 连接到后端服务
export class DatabaseService {
private static instance: DatabaseService;
// 缓存用户数据减少API调用
private userCache: Map<string, UserModel> = 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;
}
// 创建HTTP请求客户端
private createHttpClient(): http.HttpRequest {
let httpRequest = http.createHttp();
httpRequest.on('headersReceive', (header) => {
hilog.debug(0, 'ClassMG', `Headers received: ${JSON.stringify(header)}`);
});
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`,
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify(loginData),
connectTimeout: API_CONFIG.timeout,
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(
result.user.account,
result.user.nickname,
result.user.email,
result.user.phone,
result.user.photo,
result.user.category
);
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';
}
return false;
}
}
// 获取用户类别
public getUserCategory(account: string): string {
// 检查缓存
if (this.userCache.has(account)) {
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 {
// 检查缓存
if (this.userCache.has(account)) {
const user = this.userCache.get(account);
return user ? user.nickname : '';
}
// 默认值 - 仅用于开发/测试
if (account === '2') return '张三';
if (account === '9222') return '李华';
if (account === '0') return '教师demo';
return '';
}
// 异步获取用户信息
public async getUserByAccountAsync(account: string): Promise<UserModel | null> {
try {
// 检查缓存
if (this.userCache.has(account)) {
const cachedUser = this.userCache.get(account);
return cachedUser || null;
}
// 创建HTTP客户端
const httpRequest = this.createHttpClient();
// 发送获取用户信息请求
const response = await httpRequest.request(
`${API_CONFIG.baseUrl}/user/${account}`,
{
method: http.RequestMethod.GET,
connectTimeout: API_CONFIG.timeout,
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,
result.user.nickname,
result.user.email,
result.user.phone,
result.user.photo,
result.user.category
);
// 更新缓存
this.userCache.set(account, user);
return user;
}
} catch (parseError) {
const errorMsg: string = parseError instanceof Error ? parseError.message : String(parseError);
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');
}
return null;
}
}
// 为了兼容性保留的同步方法 - 使用缓存
public getUserByAccount(account: string): UserModel | null {
// 检查缓存
if (this.userCache.has(account)) {
const cachedUser = this.userCache.get(account);
return cachedUser || null;
}
// 如果缓存中没有,返回默认模拟数据
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');
}
// 触发异步加载并返回null
this.getUserByAccountAsync(account)
.then((user: UserModel | null) => {
if (user) {
this.notifyUserDataChange();
}
})
.catch((error: Error | string | Object) => {
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 {
// 验证邮箱格式
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`,
{
method: http.RequestMethod.PUT,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify(updateData),
connectTimeout: API_CONFIG.timeout,
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)) {
const user = this.userCache.get(account);
if (user) {
user.email = newEmail;
this.userCache.set(account, user);
}
}
// 通知更新
this.notifyUserDataChange();
return true;
}
} catch (parseError) {
const errorMsg: string = parseError instanceof Error ? parseError.message : String(parseError);
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);
if (user) {
user.email = newEmail;
this.userCache.set(account, user);
this.notifyUserDataChange();
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();
}
}

View File

@ -0,0 +1,260 @@
import { router } from '@kit.ArkUI';
import settingsService from '../common/SettingsService';
import logManager, { LogCategory, LogEventType } from '../common/logtext';
// 粒子信息类型
class ParticleInfo {
x: number = 0;
y: number = 0;
size: number = 0;
opacity: number = 0;
speed: number = 0;
color: string = '#ffffff';
constructor(x: number = 0,
y: number = 0,
size: number = 0,
opacity: number = 0,
speed: number = 0,
color: string = '#ffffff') {
this.x = x;
this.y = y;
this.size = size;
this.opacity = opacity;
this.speed = speed;
this.color = color;
}
}
@Entry
@Component
struct TransitionPage {
@State userName: string = '';
@State welcomeMessage: string = '欢迎使用智慧教室管理系统';
@State contentOpacity: number = 0;
@State scaleValue: number = 0.8;
@State rotationAngle: number = -10;
@State translateYValue: number = 100;
// 为粒子动画定义状态
@State particles: ParticleInfo[] = [];
private particleCount: number = 30; // 降低粒子数量避免性能问题
private particleTimer: number = 0;
private bgColorStop: number = 0;
@State backgroundGradient: string = 'linear-gradient(45deg, #4c71f2, #42a7f0)';
// 创建粒子
private createParticles() {
let newParticles: ParticleInfo[] = [];
for (let i = 0; i < this.particleCount; i++) {
let particle = new ParticleInfo(
Math.random() * 100, // x位置 (百分比)
Math.random() * 100, // y位置 (百分比)
Math.random() * 8 + 2, // 粒子大小
Math.random() * 0.6 + 0.1, // 初始透明度
Math.random() * 0.5 + 0.1, // 移动速度
i % 3 === 0 ? '#ffffff' : (i % 3 === 1 ? '#aaccff' : '#77aaff') // 随机颜色
);
newParticles.push(particle);
}
this.particles = newParticles;
}
// 更新粒子动画
private updateParticles() {
this.particleTimer = setInterval(() => {
let updatedParticles: ParticleInfo[] = [];
for (let i = 0; i < this.particles.length; i++) {
let p = this.particles[i];
// 更新位置
let newY = p.y - p.speed;
if (newY < -5) {
newY = 105; // 当粒子移出屏幕顶部时,从底部重新进入
p.x = Math.random() * 100;
}
let newOpacity = p.opacity + (Math.random() * 0.1 - 0.05);
// 确保透明度在合理范围内
if (newOpacity < 0.1) newOpacity = 0.1;
if (newOpacity > 0.8) newOpacity = 0.8;
// 创建更新后的粒子对象
let updatedParticle = new ParticleInfo(
p.x,
newY,
p.size,
newOpacity,
p.speed,
p.color
);
updatedParticles.push(updatedParticle);
}
this.particles = updatedParticles;
// 更新背景渐变
this.bgColorStop = (this.bgColorStop + 0.005) % 1;
const hue1 = Math.floor((220 + Math.sin(this.bgColorStop * Math.PI * 2) * 20) % 360);
const hue2 = Math.floor((190 + Math.cos(this.bgColorStop * Math.PI * 2) * 20) % 360);
this.backgroundGradient = `linear-gradient(45deg, hsl(${hue1}, 70%, 60%), hsl(${hue2}, 70%, 60%))`;
}, 50);
}
aboutToAppear() {
// 设置用户名称
this.userName = settingsService.getUserNickname();
// 创建并开始粒子动画
this.createParticles();
this.updateParticles();
// 第一阶段动画:元素进入
animateTo({ duration: 1000, curve: Curve.Ease }, () => {
this.contentOpacity = 1;
this.scaleValue = 1;
this.rotationAngle = 0;
this.translateYValue = 0;
});
// 3秒后开始退出动画
setTimeout(() => {
// 第二阶段动画:放大效果
animateTo({ duration: 300, curve: Curve.EaseIn }, () => {
this.scaleValue = 1.1;
this.rotationAngle = 0;
});
// 第三阶段动画:淡出效果
setTimeout(() => {
animateTo({ duration: 700, curve: Curve.EaseOut }, () => {
this.contentOpacity = 0;
this.scaleValue = 0.5;
this.translateYValue = -50;
});
// 等待动画结束后跳转
setTimeout(() => {
// 停止粒子动画定时器
clearInterval(this.particleTimer);
router.pushUrl({
url: 'pages/HomePage'
});
}, 700);
}, 300);
}, 3000);
// 记录过渡
this.Log_Event('aboutToAppear');
}
aboutToDisappear() {
// 清除粒子动画定时器
if (this.particleTimer) {
clearInterval(this.particleTimer);
}
this.Log_Event('aboutToDisappear');
}
build() {
Column() {
// 粒子动画层
Stack() {
// 渐变背景
Column()
.width('100%')
.height('100%')
.backgroundImage(this.backgroundGradient)
// 粒子效果
ForEach(this.particles, (item: ParticleInfo) => {
Circle()
.fill(item.color)
.width(item.size)
.height(item.size)
.position({ x: `${item.x}%`, y: `${item.y}%` })
.opacity(item.opacity)
})
// 内容层
Column() {
Image('http://139.155.155.67:2342/images/default_avatar.png')
.width(120)
.height(120)
.borderRadius(60)
.margin({ bottom: 40 })
Text(this.userName)
.fontSize(42)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.margin({ bottom: 20 })
Text(this.welcomeMessage)
.fontSize(26)
.fontColor('rgba(255, 255, 255, 0.9)')
Row()
.width(150)
.height(4)
.backgroundColor('#FFFFFF')
.borderRadius(2)
.margin({ top: 30 })
.opacity(0.7)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
.opacity(this.contentOpacity)
.scale({ x: this.scaleValue, y: this.scaleValue })
.rotate({ angle: this.rotationAngle })
.translate({ y: this.translateYValue })
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
}
// 页面生命周期方法
onPageShow(): void {
this.Log_Event('onPageShow');
}
onPageHide(): void {
this.Log_Event('onPageHide');
}
onBackPress(): boolean {
this.Log_Event('onBackPress');
return true; // 防止返回
}
// 日志记录方法
public Log_Event(eventName: string): void {
// 根据事件名称选择合适的日志事件类型
switch (eventName) {
case 'aboutToAppear':
logManager.info(LogCategory.USER, LogEventType.PAGE_APPEAR, 'TransitionPage');
break;
case 'aboutToDisappear':
logManager.info(LogCategory.USER, LogEventType.PAGE_DISAPPEAR, 'TransitionPage');
break;
case 'onPageShow':
logManager.info(LogCategory.USER, LogEventType.PAGE_SHOW, 'TransitionPage');
break;
case 'onPageHide':
logManager.info(LogCategory.USER, LogEventType.PAGE_HIDE, 'TransitionPage');
break;
case 'onBackPress':
logManager.info(LogCategory.USER, LogEventType.PAGE_BACK, 'TransitionPage');
break;
default:
logManager.info(LogCategory.USER, LogEventType.SYSTEM_INFO, `TransitionPage: ${eventName}`);
}
}
}