feat :init
108
docs/.i think u need 2 read this firstly
Normal file
@@ -0,0 +1,108 @@
|
||||
先改后缀为md到本地来看
|
||||
|
||||
**交接总览**
|
||||
|
||||
- 这份“后端打工人保命指南”专治历史误会与产品反转。
|
||||
- TL;DR:聊天功能服务的是小程序,不是后台;尽快把角色从`sys_template`切回正宗`sys_role`,并统一接口风格与命名。
|
||||
|
||||
**背景反转**
|
||||
|
||||
- 项目是标准的 Spring Boot 前后端分离。
|
||||
- 我一度以为“聊天要在后台界面实现”,后来才知道“聊天其实是给微信小程序用的”。
|
||||
- 现状:`ChatController`看起来像后台接口,实际上灵魂绑定了小程序;角色设计用的是`sys_template`而不是正统`sys_role`。
|
||||
|
||||
**现状雷区(请技术同学小心走位)**
|
||||
|
||||
- 角色体系割裂:接口里用`sys_template`当角色,脱离系统正统`sys_role`,后续权限/菜单/审计容易不一致。
|
||||
- 接口风格不统一:部分返回结构不走统一响应包装,出现“有的给`code/message`,有的直接给字符串”的尴尬混搭。
|
||||
- ChatController 与后台领域边界模糊:路由与命名既像后台,又服务小程序,后期联动容易误配。
|
||||
- 兼容层过度宽松:为“小程序能跑”做了魔改,继续叠加功能可能埋下小 bug(空指针、字段映射、默认值缺失)。
|
||||
- 文档误导:团队容易把聊天当“管理端功能”,实际是“C 端小程序功能”。
|
||||
|
||||
**立刻止损指南(快修清单)**
|
||||
|
||||
- 统一角色来源:移除`sys_template`在业务中的角色职能,回归`sys_role`为唯一运行时角色。
|
||||
- 归位聊天接口:将小程序相关接口归到明确的命名空间,如`/api/mp/chat`,清晰隔离后台路由。
|
||||
- 统一响应格式:所有接口走同一响应包装(如`{ code, message, data }`),避免“风格随缘”。
|
||||
- 清理文档与注释:删除或归档“聊天=后台”的旧叙事,明确“聊天=小程序”。
|
||||
|
||||
**推荐改造路线(分阶段稳妥推进)**
|
||||
|
||||
- 阶段一:信息梳理
|
||||
- 盘点`ChatController`的所有路由、入参、出参、依赖实体与服务。
|
||||
- 搜索全局对`sys_template`的引用,标记为“角色相关调用”。
|
||||
|
||||
- 阶段二:模型正名
|
||||
- 将角色相关逻辑替换为`sys_role`,在服务层聚合角色配置(人格提示词、上下文长度等)。
|
||||
- 如需保留“模板”概念,限定为“角色初始配置模板”,不可充当运行时角色。
|
||||
|
||||
- 阶段三:接口归位
|
||||
- 小程序路由统一到`/api/mp/chat/...`。
|
||||
- 后台保留管理接口到`/api/admin/...`。
|
||||
- 中间层封装统一响应与错误码,避免 Controller 内部自己拼 JSON。
|
||||
|
||||
- 阶段四:平滑迁移
|
||||
- 保留旧`sys_template`接口一版只读兼容层,打`@Deprecated`并在响应中附迁移提示。
|
||||
- 数据迁移:将现有模板绑定的“人格配置”迁移到`sys_role`,编写一次性 SQL/脚本。
|
||||
|
||||
- 阶段五:验证与发布
|
||||
- 编写集成测试:发送消息、拉取历史、选择角色/人格、历史分页、并发发送。
|
||||
- 预发布环境跑全回归;发布后观察日志与告警一周。
|
||||
|
||||
**命名与路由建议**
|
||||
|
||||
- 小程序聊天接口(示例)
|
||||
- `GET /api/mp/chat/roles`:获取可用`sys_role`人格列表
|
||||
- `POST /api/mp/chat/message`:发送消息
|
||||
- `GET /api/mp/chat/history`:拉取历史
|
||||
- `POST /api/mp/chat/role/select`:选择当前会话角色
|
||||
|
||||
- 后台管理接口(示例)
|
||||
- `GET /api/admin/roles`、`POST /api/admin/roles`:标准角色的增删改查
|
||||
- 明确禁止在后台接口使用`sys_template`做运行时角色
|
||||
|
||||
**数据迁移草案(示意)**
|
||||
|
||||
- 将“模板-人格映射”迁入`sys_role`:
|
||||
- 在`sys_role`增加必要字段以承载模板中的人格配置(如性格、提示词、上下文长度等)。
|
||||
- 编写一次性迁移脚本:将`sys_template`中的可运行配置复制/合并到`sys_role`对应记录。
|
||||
- 停止业务层对`sys_template`的运行时读取,只保留为“初始模板”与离线导出。
|
||||
|
||||
**统一响应示例(建议约定)**
|
||||
|
||||
- 成功:`{ "code": 0, "message": "ok", "data": { ... } }`
|
||||
- 失败:`{ "code": 1001, "message": "role not found" }`
|
||||
- 统一在服务层映射错误码,禁止字符串随缘返回。
|
||||
|
||||
**自检发布清单**
|
||||
|
||||
- 查找并删除/替换所有运行时对`sys_template`的使用。
|
||||
- 为`ChatController`写 5 个关键集成用例:成功、角色缺失、历史分页、并发发送、接口返回一致性。
|
||||
- 路由分区是否明确:`/api/mp/...`与`/api/admin/...`不串线。
|
||||
- 文档是否改写:README 与部署文档明确“聊天=小程序”。
|
||||
- 日志是否规范:关键链路打点,错误码落日志,方便灰度回滚。
|
||||
|
||||
**保命锦囊(恶搞版)**
|
||||
|
||||
- 若发现还有接口“口嫌体正直”用着`sys_template`:请高喊“模板是模板,角色是角色”,然后删。
|
||||
- 若有人想把聊天再接回后台:请温柔劝退,“这是给用户的小程序功能,不是管理员的自嗨面板”。
|
||||
- 若上线后出现“消息消失/人格错乱”:第一时间核对角色来源是否仍读取了`sys_template`。
|
||||
|
||||
**后续工作建议**
|
||||
|
||||
- 在`/src/main/java/com/xiaozhi/controller/`内核对`RoleController`与`ChatController`职责边界,避免领域耦合。
|
||||
- 将角色人格配置抽象到服务层,减少 Controller 内部拼装。
|
||||
- 完善测试与文档,新增“聊天接口 for 小程序”章节,删除一切让人误以为是“后台聊天”的描述。
|
||||
|
||||
祝后续同学下刀稳、上线稳、老板更稳。
|
||||
|
||||
**支付代码说明**
|
||||
|
||||
- 项目同样存在支付相关代码,但目前未实际使用、未测试、未上线。
|
||||
- 如需了解或启用,请先阅读以下文档:
|
||||
- `\root\xiaozhi-esp32-server-java\微信小程序会员绑定系统需求规格说明书.md`
|
||||
- `\root\xiaozhi-esp32-server-java\微信小程序支付上线配置修改清单.md`
|
||||
|
||||
2025年10月15日
|
||||
|
||||
如果觉得修改代码不如重写,那你可以看一下这个目录 `/root/xiaozhi-esp32-server-java-bak-before-change-git`
|
||||
309
docs/CENTOS_DEVELOPMENT.md
Normal file
@@ -0,0 +1,309 @@
|
||||
# 小智ESP32服务器CentOS部署文档
|
||||
|
||||
## 系统要求
|
||||
|
||||
- CentOS 7/8(推荐CentOS 8)
|
||||
- 最小化安装 + 开发工具(gcc, make等)
|
||||
- 至少2GB内存(推荐4GB)
|
||||
- 至少10GB磁盘空间
|
||||
|
||||
## 1. 环境准备
|
||||
|
||||
### 1.1 安装基础工具
|
||||
|
||||
```bash
|
||||
sudo yum install -y epel-release
|
||||
sudo yum install -y wget curl git vim unzip
|
||||
```
|
||||
|
||||
### 1.2 配置防火墙
|
||||
|
||||
```bash
|
||||
sudo firewall-cmd --permanent --add-port=8084/tcp
|
||||
sudo firewall-cmd --permanent --add-port=8091/tcp
|
||||
sudo firewall-cmd --permanent --add-port=3306/tcp
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
## 2. 安装Java JDK 8
|
||||
|
||||
```bash
|
||||
sudo yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
|
||||
```
|
||||
|
||||
验证安装:
|
||||
|
||||
```bash
|
||||
java -version
|
||||
```
|
||||
|
||||
## 3. 安装MySQL 5.7
|
||||
|
||||
```bash
|
||||
sudo yum localinstall -y https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
|
||||
sudo yum install -y mysql-community-server
|
||||
```
|
||||
|
||||
启动MySQL服务:
|
||||
|
||||
```bash
|
||||
sudo systemctl start mysqld
|
||||
sudo systemctl enable mysqld
|
||||
```
|
||||
|
||||
获取临时密码:
|
||||
|
||||
```bash
|
||||
sudo grep 'temporary password' /var/log/mysqld.log
|
||||
```
|
||||
|
||||
安全设置:
|
||||
|
||||
```bash
|
||||
sudo mysql_secure_installation
|
||||
```
|
||||
|
||||
## 4. 安装Maven
|
||||
|
||||
```bash
|
||||
sudo yum install -y maven
|
||||
```
|
||||
|
||||
验证安装:
|
||||
|
||||
```bash
|
||||
mvn -v
|
||||
```
|
||||
|
||||
## 5. 安装Node.js 16
|
||||
|
||||
```bash
|
||||
curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash -
|
||||
sudo yum install -y nodejs
|
||||
```
|
||||
|
||||
验证安装:
|
||||
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
|
||||
## 6. 安装FFmpeg
|
||||
|
||||
```bash
|
||||
sudo yum install -y ffmpeg ffmpeg-devel
|
||||
```
|
||||
|
||||
验证安装:
|
||||
|
||||
```bash
|
||||
ffmpeg -version
|
||||
```
|
||||
|
||||
## 7. 数据库配置
|
||||
|
||||
创建数据库和用户:
|
||||
|
||||
```sql
|
||||
mysql -u root -p
|
||||
CREATE DATABASE xiaozhi CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE USER 'xiaozhi'@'localhost' IDENTIFIED BY '123456';
|
||||
GRANT ALL PRIVILEGES ON xiaozhi.* TO 'xiaozhi'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
exit
|
||||
```
|
||||
|
||||
导入初始化脚本:
|
||||
|
||||
```bash
|
||||
mysql -u root -p xiaozhi < db/init.sql
|
||||
```
|
||||
|
||||
## 8. 下载Vosk语音识别模型
|
||||
|
||||
```bash
|
||||
wget https://alphacephei.com/vosk/models/vosk-model-cn-0.22.zip
|
||||
unzip vosk-model-cn-0.22.zip
|
||||
mkdir -p models
|
||||
mv vosk-model-cn-0.22 models/vosk-model
|
||||
```
|
||||
|
||||
## 9. 项目部署
|
||||
|
||||
### 9.1 克隆项目
|
||||
|
||||
```bash
|
||||
git clone https://github.com/joey-zhou/xiaozhi-esp32-server-java
|
||||
cd xiaozhi-esp32-server-java
|
||||
```
|
||||
|
||||
### 9.2 后端部署
|
||||
|
||||
```bash
|
||||
mvn clean package -DskipTests
|
||||
java -jar target/xiaozhi.server-1.0.jar &
|
||||
```
|
||||
|
||||
### 9.3 前端部署
|
||||
|
||||
```bash
|
||||
cd web
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 10. 配置系统服务(可选)
|
||||
|
||||
### 10.1 创建后端服务
|
||||
|
||||
编辑服务文件:
|
||||
|
||||
```bash
|
||||
sudo vim /etc/systemd/system/xiaozhi.service
|
||||
```
|
||||
|
||||
添加内容:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Xiaozhi ESP32 Server
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/path/to/xiaozhi-esp32-server-java
|
||||
ExecStart=/usr/bin/java -jar target/xiaozhi.server-1.0.jar
|
||||
SuccessExitStatus=143
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
启动服务:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl start xiaozhi
|
||||
sudo systemctl enable xiaozhi
|
||||
```
|
||||
|
||||
### 10.2 配置Nginx(可选)
|
||||
|
||||
```bash
|
||||
sudo yum install -y nginx
|
||||
sudo vim /etc/nginx/conf.d/xiaozhi.conf
|
||||
```
|
||||
|
||||
添加配置:
|
||||
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name your_domain_or_ip;
|
||||
|
||||
location / {
|
||||
root /path/to/xiaozhi-esp32-server-java/web/dist;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://localhost:8091;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
启动Nginx:
|
||||
|
||||
```bash
|
||||
sudo systemctl start nginx
|
||||
sudo systemctl enable nginx
|
||||
```
|
||||
|
||||
## 11. 访问系统
|
||||
|
||||
- 直接访问:`http://your_server_ip:8084`
|
||||
- 如果配置了Nginx:`http://your_domain_or_ip`
|
||||
- 默认管理员账号:admin/123456
|
||||
|
||||
## 常见问题解决
|
||||
|
||||
1. **MySQL初始化失败**
|
||||
|
||||
```bash
|
||||
sudo systemctl restart mysqld
|
||||
mysql_upgrade -u root -p
|
||||
```
|
||||
|
||||
2. **端口冲突**
|
||||
|
||||
```bash
|
||||
netstat -tulnp | grep 8084
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
3. **Node.js版本问题**
|
||||
|
||||
```bash
|
||||
sudo yum remove -y nodejs npm
|
||||
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
|
||||
sudo yum install -y nodejs
|
||||
```
|
||||
|
||||
4. **内存不足**
|
||||
|
||||
增加swap空间:
|
||||
|
||||
```bash
|
||||
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
|
||||
sudo mkswap /swapfile
|
||||
sudo swapon /swapfile
|
||||
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab
|
||||
```
|
||||
|
||||
5. **Vosk模型加载失败**
|
||||
|
||||
```bash
|
||||
chmod -R 755 models
|
||||
```
|
||||
|
||||
## 维护命令
|
||||
|
||||
- 查看后端日志:
|
||||
|
||||
```bash
|
||||
journalctl -u xiaozhi -f
|
||||
```
|
||||
|
||||
- 更新代码:
|
||||
|
||||
```bash
|
||||
git pull origin master
|
||||
mvn clean package -DskipTests
|
||||
sudo systemctl restart xiaozhi
|
||||
```
|
||||
|
||||
- 数据库备份:
|
||||
|
||||
```bash
|
||||
mysqldump -u root -p xiaozhi > xiaozhi_backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **生产环境建议:**
|
||||
- 修改默认密码
|
||||
- 配置HTTPS
|
||||
- 定期备份数据库
|
||||
|
||||
2. **性能优化:**
|
||||
|
||||
增加JVM内存:
|
||||
|
||||
```bash
|
||||
java -Xms512m -Xmx1024m -jar target/xiaozhi.server-1.0.jar
|
||||
```
|
||||
203
docs/DOCKER.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Docker 部署指南
|
||||
|
||||
本文档提供了使用Docker容器化部署Xiaozhi ESP32 Server Java项目的详细步骤。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 安装 [Docker](https://docs.docker.com/get-docker/)
|
||||
- 安装 [Docker Compose](https://docs.docker.com/compose/install/)
|
||||
- 确保以下端口在您的服务器上可用:
|
||||
- 13306 (MySQL)
|
||||
- 8084 (前端Node服务)
|
||||
- 8091 (后端Java服务)
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 1. 获取项目代码
|
||||
|
||||
```bash
|
||||
git clone https://github.com/joey-zhou/xiaozhi-esp32-server-java/
|
||||
cd xiaozhi-esp32-server-java
|
||||
```
|
||||
|
||||
### 2. 启动Docker容器
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
这将启动三个服务:
|
||||
- MySQL数据库 (端口13306)
|
||||
- 前端Node服务 (端口8084)
|
||||
- 后端Java服务 (端口8091)
|
||||
|
||||
首次启动可能需要一些时间,因为需要下载Docker镜像、构建应用程序并下载相关依赖。
|
||||
|
||||
### 3. 访问应用
|
||||
|
||||
- 前端界面: http://localhost:8084
|
||||
- 后端API: http://localhost:8091
|
||||
- WebSocket服务: ws://宿主机IP:8091/ws/xiaozhi/v1/
|
||||
|
||||
**注意**: 在ESP32设备连接时,需要使用宿主机的实际IP地址,而不是localhost。
|
||||
|
||||
### 4. 查看日志
|
||||
|
||||
```bash
|
||||
# 查看所有容器日志
|
||||
docker-compose logs
|
||||
|
||||
# 查看特定服务日志
|
||||
docker-compose logs server
|
||||
docker-compose logs node
|
||||
docker-compose logs mysql
|
||||
|
||||
# 实时查看日志
|
||||
docker-compose logs -f server
|
||||
```
|
||||
|
||||
### 5. 停止服务
|
||||
|
||||
```bash
|
||||
# 停止并保留容器
|
||||
docker-compose stop
|
||||
|
||||
# 停止并移除容器
|
||||
docker-compose down
|
||||
|
||||
# 停止并移除容器及卷(会删除所有数据)
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
可以通过设置环境变量来自定义部署:
|
||||
|
||||
- `VOSK_MODEL_SIZE`: 设置语音识别模型大小,默认为"small",可选值有"standard"(大模型下载慢,识别效果好),"small"(小模型下载快,识别效果差)。
|
||||
|
||||
例如,使用大型语音模型启动:
|
||||
|
||||
```bash
|
||||
VOSK_MODEL_SIZE=standard docker-compose up -d
|
||||
```
|
||||
|
||||
## 持久化数据
|
||||
|
||||
Docker部署方案已配置以下持久化卷:
|
||||
|
||||
- `mysql_data`: MySQL数据库数据
|
||||
- `maven_repo`: Maven仓库缓存
|
||||
- `vosk_models`: Vosk语音识别模型缓存
|
||||
|
||||
这些卷确保即使容器被删除,数据也不会丢失。要查看持久化卷的信息:
|
||||
|
||||
```bash
|
||||
docker volume ls | grep xiaozhi
|
||||
```
|
||||
|
||||
## 端口映射
|
||||
|
||||
默认端口映射如下:
|
||||
|
||||
- MySQL: 13306 -> 3306 (容器内)
|
||||
- 前端Node服务: 8084 -> 8084 (容器内)
|
||||
- 后端Java服务: 8091 -> 8091 (容器内)
|
||||
|
||||
如需修改端口映射,请编辑`docker-compose.yml`文件中的`ports`部分。例如,将前端端口从8084改为80:
|
||||
|
||||
```yaml
|
||||
node:
|
||||
ports:
|
||||
- "80:8084"
|
||||
```
|
||||
|
||||
## 系统要求
|
||||
|
||||
根据不同的使用场景,推荐的系统配置如下:
|
||||
|
||||
- **最低配置**:
|
||||
- 2核CPU
|
||||
- 2GB RAM
|
||||
- 10GB 存储空间
|
||||
- 该配置不适用于本地语音识别,需添加第三方识别 API
|
||||
|
||||
- **推荐配置**:
|
||||
- 2核CPU
|
||||
- 4GB RAM
|
||||
- 20GB+ 存储空间
|
||||
- 该配置适用于本地语音识别,但可能需要选择较小的 Vosk 模型以减少内存占用。
|
||||
|
||||
- **使用大型语音模型**:
|
||||
- 4核CPU
|
||||
- 8GB RAM
|
||||
- 30GB+ 存储空间
|
||||
- 该配置适用于本地语音识别,可使用较大的 Vosk 模型
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 1. 容器启动失败
|
||||
|
||||
检查容器状态:
|
||||
|
||||
```bash
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
查看失败容器的日志:
|
||||
|
||||
```bash
|
||||
docker-compose logs <service_name>
|
||||
```
|
||||
|
||||
### 2. 数据库连接问题
|
||||
|
||||
确保MySQL容器健康检查通过:
|
||||
|
||||
```bash
|
||||
docker-compose ps mysql
|
||||
```
|
||||
|
||||
如果状态不是"healthy",检查MySQL日志:
|
||||
|
||||
```bash
|
||||
docker-compose logs mysql
|
||||
```
|
||||
|
||||
常见问题包括:
|
||||
- 数据库初始化失败
|
||||
- 权限问题
|
||||
- 端口冲突
|
||||
|
||||
### 3. 重建容器
|
||||
|
||||
如果需要重新构建容器:
|
||||
|
||||
```bash
|
||||
docker-compose build --no-cache
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 4. WebSocket连接问题
|
||||
|
||||
如果ESP32设备无法连接到WebSocket服务,请检查:
|
||||
|
||||
1. 确保使用的是宿主机的实际IP地址,而不是localhost
|
||||
2. 确保8091端口已在防火墙中开放
|
||||
3. 检查服务器日志中是否有连接尝试记录
|
||||
|
||||
```bash
|
||||
docker-compose logs server
|
||||
```
|
||||
|
||||
## 更新应用
|
||||
|
||||
要更新到最新版本:
|
||||
|
||||
```bash
|
||||
# 拉取最新代码
|
||||
git pull
|
||||
|
||||
# 重新构建并启动容器
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
111
docs/FIRMWARE-BUILD.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 编译esp32固件
|
||||
|
||||
1. 下载`xiaozhi-esp32`
|
||||
项目,按照这个教程配置项目环境[《Windows搭建 ESP IDF 5.3.2开发环境以及编译小智》](https://icnynnzcwou8.feishu.cn/wiki/JEYDwTTALi5s2zkGlFGcDiRknXf)
|
||||
|
||||
2. 打开`xiaozhi-esp32/main/Kconfig.projbuild`文件,找到`WEBSOCKET_URL`的`default`的内容,把`wss://api.tenclass.net`
|
||||
改成你自己的地址,例如,我的接口地址是`ws://192.168.1.25:8091`,就把内容改成这个。
|
||||
|
||||
修改前:
|
||||
|
||||
```
|
||||
config WEBSOCKET_URL
|
||||
depends on CONNECTION_TYPE_WEBSOCKET
|
||||
string "Websocket URL"
|
||||
default "wss://api.tenclass.net/xiaozhi/v1/"
|
||||
help
|
||||
Communication with the server through websocket after wake up.
|
||||
```
|
||||
|
||||
修改后(示例):
|
||||
|
||||
```
|
||||
config WEBSOCKET_URL
|
||||
depends on CONNECTION_TYPE_WEBSOCKET
|
||||
string "Websocket URL"
|
||||
default "ws://192.168.5.167:8091/ws/xiaozhi/v1/"
|
||||
help
|
||||
Communication with the server through websocket after wake up.
|
||||
```
|
||||
|
||||
注意:你的地址是`ws://`开头,不是`wss://`开头,一定不要写错了。
|
||||
|
||||
注意:你的地址是`ws://`开头,不是`wss://`开头,一定不要写错了。
|
||||
|
||||
注意:你的地址是`ws://`开头,不是`wss://`开头,一定不要写错了。
|
||||
|
||||
**你也可以修改ota地址以提供更多设备信息服务**(修改ota地址后无法通过官方ota自动升级,可选项)
|
||||
|
||||
修改前:
|
||||
```
|
||||
config OTA_VERSION_URL
|
||||
string "OTA Version URL"
|
||||
default "https://api.tenclass.net/xiaozhi/ota/"
|
||||
help
|
||||
The application will access this URL to check for updates.
|
||||
```
|
||||
|
||||
修改后(示例):
|
||||
```
|
||||
config OTA_VERSION_URL
|
||||
string "OTA Version URL"
|
||||
default "http://192.168.5.167:8091/api/device/ota"
|
||||
help
|
||||
The application will access this URL to check for updates.
|
||||
```
|
||||
|
||||
3. 设置编译参数
|
||||
|
||||
```
|
||||
# 终端命令行进入xiaozhi-esp32的根目录
|
||||
cd xiaozhi-esp32
|
||||
# 例如我使用的板子是esp32s3,所以设置编译目标为esp32s3,如果你的板子是其他型号,请替换成对应的型号
|
||||
idf.py set-target esp32s3
|
||||
# 进入菜单配置
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||

|
||||
|
||||
进入菜单配置后,再进入`Xiaozhi Assistant`,将`CONNECTION_TYPE`设置为`Websocket`
|
||||
回退到主菜单,再进入`Xiaozhi Assistant`,将`BOARD_TYPE`设置你板子的具体型号
|
||||
保存退出,回到终端命令行。
|
||||
|
||||

|
||||
|
||||
4. 编译固件
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
如果是vscode安装的idf可以使用`F1`或者`ctrl+shift+p`,输入idf然后直接选择进行编译
|
||||
|
||||
还可以直接进行烧录不用接下来的操作
|
||||
|
||||
<img src="./images/vscode_idf.png" width="500px"/>
|
||||
|
||||
5. 打包bin固件
|
||||
|
||||
```
|
||||
cd scripts
|
||||
python release.py
|
||||
```
|
||||
|
||||
编译成功后,会在项目根目录下的`build`目录下生成固件文件`merged-binary.bin`。
|
||||
这个`merged-binary.bin`就是要烧录到硬件上的固件文件。
|
||||
|
||||
注意:如果执行到第二命令后,报了“zip”相关的错误,请忽略这个错误,只要`build`目录下生成固件文件`merged-binary.bin`
|
||||
,对你没有太大影响,请继续。
|
||||
|
||||
6. 烧录固件
|
||||
将esp32设备连接电脑,使用chrome浏览器,打开以下网址
|
||||
|
||||
```
|
||||
https://espressif.github.io/esp-launchpad/
|
||||
```
|
||||
|
||||
打开这个教程,[Flash工具/Web端烧录固件(无IDF开发环境)](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)。
|
||||
翻到:`方式二:ESP-Launchpad 浏览器WEB端烧录`,从`3. 烧录固件/下载到开发板`开始,按照教程操作。
|
||||
|
||||
烧录成功且联网成功后,通过唤醒词唤醒小智,留意server端输出的控制台信息。
|
||||
167
docs/WINDOWS_DEVELOPMENT.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Windows 部署小智ESP32服务器的详细步骤
|
||||
|
||||
## 系统要求确认
|
||||
- 确保您的Windows系统满足以下要求:
|
||||
- Windows 10或更高版本(建议使用最新版本)
|
||||
- 管理员权限
|
||||
|
||||
## 1. 安装Java JDK 8
|
||||
1. 访问Oracle官网下载JDK 8:[Oracle JDK 8下载](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html)
|
||||
备用下载地址:[CSDN下载](https://download.csdn.net/download/weixin_55629186/89045298)
|
||||
- 选择"Windows x64"版本下载(如`jdk-8u381-windows-x64.exe`)
|
||||
2. 运行安装程序,按向导完成安装
|
||||
3. 配置环境变量:
|
||||
- 右键"此电脑" → 属性 → 高级系统设置 → 环境变量
|
||||
- 在"系统变量"中新建:
|
||||
- 变量名:`JAVA_HOME`
|
||||
- 变量值:`C:\Program Files\Java\jdk1.8.0_381`(具体路径取决于您的安装版本)
|
||||
- 编辑"Path"变量,添加:`%JAVA_HOME%\bin`
|
||||
4. 验证安装:
|
||||
- 打开命令提示符(Win+R,输入`cmd`)
|
||||
- 输入:`java -version`
|
||||
- 应显示类似:`java version "1.8.0_381"`
|
||||
|
||||
## 2. 安装MySQL数据库
|
||||
1. 下载MySQL社区版:[MySQL下载](https://dev.mysql.com/downloads/installer/)
|
||||
2. 运行安装程序,选择"Custom"安装
|
||||
3. 选择安装:
|
||||
- MySQL Server
|
||||
- MySQL Workbench(可选,图形界面工具)
|
||||
4. 在配置步骤:
|
||||
- 设置root密码(建议使用复杂密码)
|
||||
- 记住您设置的密码
|
||||
5. 完成安装后,启动MySQL服务
|
||||
6. 配置变量
|
||||
- 找到MySQL的安装路径,默认路径通常是:`C:\Program Files\MySQL\MySQL Server 5.7\bin`
|
||||
- 如果不确定,可以在文件资源管理器中搜索 `mysql.exe` 的路径。
|
||||
7. 添加到环境变量
|
||||
- 右键 此电脑 → 属性 → 高级系统设置 → 环境变量。
|
||||
- 在 系统变量 中找到 Path,点击 编辑 → 新建,粘贴MySQL的bin路径(如上述路径)。
|
||||
- 保存后关闭所有窗口。
|
||||
8. 验证是否生效
|
||||
- 重新打开命令提示符(CMD),输入`mysql --version`
|
||||
- 如果显示版本信息(如 `mysql Ver 14.14 Distrib 5.7.43`),则配置成功。
|
||||
|
||||
## 3. 安装Maven
|
||||
1. 下载Maven:[Maven下载](https://maven.apache.org/download.cgi)
|
||||
- 选择"Binary zip archive"下载
|
||||
2. 解压到`C:\Program Files\apache-maven-3.9.4`(版本号可能不同)
|
||||
3. 配置环境变量:
|
||||
- 新建系统变量:
|
||||
- 变量名:`MAVEN_HOME`
|
||||
- 变量值:`C:\Program Files\apache-maven-3.9.4`
|
||||
- 编辑"Path"变量,添加:`%MAVEN_HOME%\bin`
|
||||
4. 验证安装:
|
||||
- 命令提示符输入:`mvn -v`
|
||||
- 应显示Maven版本信息
|
||||
|
||||
## 4. 安装Node.js和npm
|
||||
1. 下载Node.js LTS版本:[Node.js下载](https://nodejs.org/)
|
||||
2. 运行安装程序,按默认选项安装
|
||||
3. 安装完成后验证:
|
||||
- 命令提示符输入:
|
||||
- `node -v`
|
||||
- `npm -v`
|
||||
- 应显示版本信息
|
||||
|
||||
## 5. 安装FFmpeg(必需)
|
||||
1. 访问FFmpeg官网:[FFmpeg下载](https://ffmpeg.org/download.html)
|
||||
2. 选择"Windows builds from gyan.dev"链接
|
||||
3. 下载最新完整版(如`ffmpeg-git-full.7z`)
|
||||
4. 解压到`C:\Program Files\ffmpeg`(可以自定义路径)
|
||||
5. 配置环境变量:
|
||||
- 编辑"Path"变量,添加:`C:\Program Files\ffmpeg\bin`
|
||||
6. 验证安装:
|
||||
- 命令提示符输入:`ffmpeg -version`
|
||||
- 应显示FFmpeg版本信息
|
||||
|
||||
## 6. 数据库配置(详细Windows步骤)
|
||||
1. 打开命令提示符
|
||||
2. 登录MySQL(使用安装时设置的root密码):
|
||||
```bash
|
||||
mysql -u root -p
|
||||
```
|
||||
3. 创建数据库:
|
||||
```sql
|
||||
CREATE DATABASE xiaozhi;
|
||||
```
|
||||
4. 创建用户并授权:
|
||||
```sql
|
||||
CREATE USER 'xiaozhi'@'localhost' IDENTIFIED BY '123456';
|
||||
GRANT ALL PRIVILEGES ON xiaozhi.* TO 'xiaozhi'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
5. 初始化数据库:
|
||||
- 确保您已经克隆了项目代码
|
||||
- 在命令提示符中导航到项目目录下的`db`文件夹
|
||||
- 执行:
|
||||
```bash
|
||||
mysql -u root -p xiaozhi < init.sql
|
||||
```
|
||||
|
||||
## 7. Vosk语音识别模型安装(Windows)
|
||||
1. 下载中文模型:[Vosk模型](https://alphacephei.com/vosk/models)
|
||||
- 选择`vosk-model-cn-0.22`(或最新中文模型)
|
||||
2. 解压下载的zip文件
|
||||
3. 在项目根目录创建`models`文件夹(如果不存在)
|
||||
4. 将解压后的模型文件夹重命名为`vosk-model`并放入`models`目录
|
||||
5. 完整路径应为:`项目目录\models\vosk-model`
|
||||
|
||||
## 后端部署(Windows)
|
||||
1. 克隆项目(如果尚未克隆):
|
||||
```bash
|
||||
git clone https://github.com/joey-zhou/xiaozhi-esp32-server-java
|
||||
```
|
||||
2. 进入项目目录:
|
||||
```bash
|
||||
cd xiaozhi-esp32-server-java
|
||||
```
|
||||
3. 使用Maven构建:
|
||||
```bash
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
4. 运行后端服务:
|
||||
```bash
|
||||
java -jar target\xiaozhi.server-1.0.jar
|
||||
```
|
||||
|
||||
## 前端部署(Windows)
|
||||
1. 打开新的命令提示符窗口
|
||||
2. 导航到前端目录:
|
||||
```bash
|
||||
cd xiaozhi-esp32-server-java\web
|
||||
```
|
||||
3. 安装依赖:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
4. 运行开发服务器:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 访问系统
|
||||
1. 确保后端服务正在运行
|
||||
2. 确保前端开发服务器正在运行
|
||||
3. 打开浏览器访问:[http://localhost:8084](http://localhost:8084)
|
||||
4. 使用默认凭据登录:
|
||||
- 用户名:`admin`
|
||||
- 密码:`123456`
|
||||
|
||||
## Windows常见问题解决
|
||||
1. 端口冲突:
|
||||
- 如果8084端口被占用,可以:
|
||||
- 编辑`src\main\resources\application.properties`,修改`server.port`
|
||||
- 编辑前端`web`目录下的配置文件相应修改API地址
|
||||
2. FFmpeg找不到:
|
||||
- 确保已正确添加FFmpeg到PATH
|
||||
- 重启命令提示符窗口后重试
|
||||
3. MySQL连接问题:
|
||||
- 确保MySQL服务已启动(可在服务管理器中检查)
|
||||
- 检查`application.properties`中的数据库配置
|
||||
4. 缺少依赖:
|
||||
- 如果构建失败,尝试:
|
||||
```bash
|
||||
mvn clean install
|
||||
```
|
||||
- 确保网络连接正常,能访问Maven中央仓库
|
||||
187
docs/deploy-springboot.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# 小智ESP32服务器部署脚本说明
|
||||
|
||||
本项目提供两个自动化部署脚本,用于快速部署后端Java服务和前端Web应用。
|
||||
|
||||
## deploy-java.sh - 后端服务部署脚本
|
||||
|
||||
### 核心功能
|
||||
自动化部署Spring Boot后端服务,实现智能进程管理和无缝服务重启。
|
||||
|
||||
### 主要特性
|
||||
- **智能JAR文件发现**: 自动在`target`目录中查找最新的JAR文件(排除`.original`文件)
|
||||
- **灵活进程匹配**: 使用应用名称前缀(JAR文件名中"-"之前的部分)匹配进程,支持不同版本的应用
|
||||
- **优雅停止机制**: 先尝试正常终止进程,如果失败则强制杀死进程
|
||||
- **后台启动**: 使用`nohup`在后台启动服务,输出日志到`output.log`
|
||||
- **启动状态检查**: 自动验证服务是否成功启动
|
||||
|
||||
### 使用方法
|
||||
```bash
|
||||
# 给脚本执行权限
|
||||
chmod +x deploy-java.sh
|
||||
|
||||
# 执行部署
|
||||
./deploy-java.sh
|
||||
```
|
||||
|
||||
### 执行流程
|
||||
1. 在`target`目录中查找JAR文件
|
||||
2. 提取应用名称前缀用于进程匹配
|
||||
3. 查找并停止正在运行的相关进程
|
||||
4. 启动新的服务实例
|
||||
5. 验证服务启动状态
|
||||
|
||||
### 前置条件
|
||||
- 项目已通过`mvn clean package`构建完成
|
||||
- `target`目录中存在可执行的JAR文件
|
||||
|
||||
---
|
||||
|
||||
## deploy-web.sh - 前端应用部署脚本
|
||||
|
||||
### 核心功能
|
||||
自动化构建和部署Vue.js前端应用,实现安全的生产环境更新。
|
||||
|
||||
### 主要特性
|
||||
- **自动构建**: 执行`npm run build`构建生产版本
|
||||
- **安全备份**: 部署前自动备份现有的生产文件到`/tmp/dist_backup`
|
||||
- **无缝部署**: 将新构建的文件部署到`/var/www/html/dist`
|
||||
- **Nginx重载**: 自动测试和重载Nginx配置,确保服务连续性
|
||||
|
||||
### 使用方法
|
||||
```bash
|
||||
# 给脚本执行权限
|
||||
chmod +x deploy-web.sh
|
||||
|
||||
# 执行部署
|
||||
./deploy-web.sh
|
||||
```
|
||||
|
||||
### 执行流程
|
||||
1. 进入`web`目录
|
||||
2. 执行`npm run build`构建前端应用
|
||||
3. 备份现有的部署文件(如果存在)
|
||||
4. 将新构建的文件移动到生产目录
|
||||
5. 测试并重载Nginx配置
|
||||
|
||||
### 前置条件
|
||||
- Node.js和npm已正确安装
|
||||
- `web`目录中的依赖包已安装(`npm install`)
|
||||
- Nginx服务正在运行
|
||||
- 具有`/var/www/html`目录的写入权限
|
||||
|
||||
---
|
||||
|
||||
## stop-java.sh - 后端服务停止脚本
|
||||
|
||||
### 核心功能
|
||||
安全停止Spring Boot后端服务,提供优雅的服务关闭机制。
|
||||
|
||||
### 主要特性
|
||||
- **智能进程查找**: 自动查找target目录中的JAR文件,或通过xiaozhi关键字匹配进程
|
||||
- **优雅停止机制**: 先发送TERM信号优雅停止,失败后强制杀死进程
|
||||
- **PID文件清理**: 自动清理可能存在的PID文件
|
||||
- **详细状态反馈**: 提供完整的停止过程信息
|
||||
|
||||
### 使用方法
|
||||
```bash
|
||||
# 停止后端服务
|
||||
./stop-java.sh
|
||||
```
|
||||
|
||||
### 执行流程
|
||||
1. 查找target目录中的JAR文件
|
||||
2. 根据JAR文件名或xiaozhi关键字查找运行中的进程
|
||||
3. 优雅停止进程(发送TERM信号)
|
||||
4. 如果优雅停止失败,强制杀死进程
|
||||
5. 清理PID文件
|
||||
|
||||
---
|
||||
|
||||
## stop-web.sh - 前端服务停止脚本
|
||||
|
||||
### 核心功能
|
||||
停止Web前端服务,保持Nginx运行状态,提供服务维护页面。
|
||||
|
||||
### 主要特性
|
||||
- **安全备份**: 停止前自动备份现有的Web服务文件
|
||||
- **维护页面**: 自动创建友好的维护页面
|
||||
- **保持Nginx**: 不停止Nginx服务,只清理Web应用文件
|
||||
- **状态检查**: 检查Nginx运行状态并提供相应提示
|
||||
|
||||
### 使用方法
|
||||
```bash
|
||||
# 停止前端服务
|
||||
./stop-web.sh
|
||||
```
|
||||
|
||||
### 执行流程
|
||||
1. 检查Web服务目录是否存在
|
||||
2. 备份当前的Web服务文件到临时目录
|
||||
3. 清理Web服务目录中的所有文件
|
||||
4. 创建维护页面
|
||||
5. 检查Nginx状态(不停止Nginx)
|
||||
|
||||
---
|
||||
|
||||
## 快速部署
|
||||
|
||||
### 完整部署流程
|
||||
```bash
|
||||
# 1. 构建后端项目
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# 2. 部署后端服务
|
||||
./deploy-java.sh
|
||||
|
||||
# 3. 部署前端应用
|
||||
./deploy-web.sh
|
||||
```
|
||||
|
||||
### 服务管理
|
||||
```bash
|
||||
# 停止后端服务
|
||||
./stop-java.sh
|
||||
|
||||
# 停止前端服务(保持Nginx运行)
|
||||
./stop-web.sh
|
||||
|
||||
# 重新部署后端
|
||||
./stop-java.sh && ./deploy-java.sh
|
||||
|
||||
# 重新部署前端
|
||||
./stop-web.sh && ./deploy-web.sh
|
||||
```
|
||||
|
||||
### 服务验证
|
||||
```bash
|
||||
# 检查后端服务状态
|
||||
ps -ef | grep xiaozhi
|
||||
|
||||
# 检查前端部署
|
||||
ls -la /var/www/html/dist
|
||||
|
||||
# 查看后端服务日志
|
||||
tail -f output.log
|
||||
|
||||
# 检查Nginx状态
|
||||
nginx -t && systemctl status nginx
|
||||
```
|
||||
|
||||
### 故障排除
|
||||
```bash
|
||||
# 如果后端服务无法停止
|
||||
ps -ef | grep java | grep xiaozhi
|
||||
kill -9 <PID>
|
||||
|
||||
# 如果前端服务异常
|
||||
ls -la /tmp/web_backup_* # 查看备份文件
|
||||
cp -r /tmp/web_backup_<timestamp>/dist/* /var/www/html/dist/ # 恢复备份
|
||||
|
||||
# 检查端口占用
|
||||
netstat -tlnp | grep :8091 # 后端端口
|
||||
netstat -tlnp | grep :80 # 前端端口
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*最后更新: 2024年12月*
|
||||
BIN
docs/images/agent.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
docs/images/build_setting01.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
docs/images/build_setting02.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
docs/images/dashboard.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/images/device.jpg
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
docs/images/login.jpg
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
docs/images/message.jpg
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
docs/images/model.jpg
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
docs/images/role.jpg
Normal file
|
After Width: | Height: | Size: 283 KiB |
BIN
docs/images/user.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/images/voiceClone.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
docs/images/vscode_idf.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
docs/images/wechat_group.jpg
Normal file
|
After Width: | Height: | Size: 167 KiB |
82
docs/recruitment.md
Normal file
@@ -0,0 +1,82 @@
|
||||
|
||||
我们基于 [ ESP32] 硬件,开发了一套完整的Java版本服务端系统,为智能硬件设备提供强大的后端支持。
|
||||
|
||||
## 我们需要你!
|
||||
|
||||
### 🔧 后端开发工程师
|
||||
|
||||
**技术要求:**
|
||||
- 熟练掌握Java开发(Java 17+),有Spring Boot项目经验
|
||||
- 了解WebSocket、MQTT等实时通信协议
|
||||
- 熟悉MySQL数据库,有性能优化经验
|
||||
- 了解Docker容器化部署
|
||||
- 有AI/LLM集成经验者优先
|
||||
- 有音视频处理经验者优先
|
||||
|
||||
**你将负责:**
|
||||
- 开发和优化核心业务功能
|
||||
- 集成各类AI服务和语音识别/合成服务
|
||||
- 优化系统性能,处理高并发场景
|
||||
- 参与新功能的设计和实现
|
||||
|
||||
### 🎨 前端开发工程师
|
||||
|
||||
**技术要求:**
|
||||
- 熟练掌握Vue.js开发,有完整项目经验
|
||||
- 熟悉Ant Design Vue等UI组件库
|
||||
- 了解WebSocket实时通信
|
||||
- 有响应式设计和移动端适配经验
|
||||
- 有数据可视化经验者优先
|
||||
|
||||
**你将负责:**
|
||||
- 开发和优化管理后台界面
|
||||
- 实现设备管理、用户管理等功能模块
|
||||
- 优化用户体验,打造流畅的交互界面
|
||||
- 参与新功能的UI/UX设计
|
||||
|
||||
### 🛠️ 全栈开发工程师
|
||||
|
||||
如果你是一个全能型选手,能够同时处理前后端开发,那就更好了!我们特别欢迎能够独当一面的全栈工程师加入。
|
||||
|
||||
### 🤖 IoT/嵌入式开发工程师
|
||||
|
||||
**技术要求:**
|
||||
- 熟悉ESP32开发
|
||||
- 了解MQTT、WebSocket等物联网通信协议
|
||||
- 有音频处理经验
|
||||
- 熟悉C/C++开发
|
||||
|
||||
**你将负责:**
|
||||
- ESP32固件开发和优化
|
||||
- 硬件与服务端的通信协议设计
|
||||
- 音频采集和处理优化
|
||||
|
||||
|
||||
## 项目技术栈详情
|
||||
|
||||
### 后端技术栈
|
||||
```
|
||||
- Java 21 (LTS)
|
||||
- Spring Boot 3.3.0
|
||||
- Spring WebSocket & MQTT
|
||||
- MyBatis 3.0.3
|
||||
- MySQL + Redis
|
||||
- Docker & Docker Compose
|
||||
```
|
||||
|
||||
### AI与语音技术
|
||||
```
|
||||
- Spring AI 1.0.0
|
||||
- 语音识别:Vosk、阿里云、腾讯云、讯飞
|
||||
- 语音合成:Edge TTS、阿里云、火山引擎
|
||||
- LLM支持:OpenAI、智谱AI、讯飞星火、Ollama
|
||||
```
|
||||
|
||||
### 前端技术栈
|
||||
```
|
||||
- Vue.js 2.5.2
|
||||
- Ant Design Vue 1.7.8
|
||||
- WebSocket客户端
|
||||
- ECharts数据可视化
|
||||
```
|
||||
|
||||