星野录(博客系统)测试报告

article/2025/6/8 10:32:53

目录

一.  项目背景

二、项目功能

三、测试计划

1. 功能测试

1.1 测试用例

1.2 执行测试部分操作截图

2. 使用selenium进行自动化测试

2.1 添加相关依赖

2.2 登录页面测试

3.3 注册页面测试

 3.4 博客列表页面测试

3.5 博客详情页测试

 3.6 博客编辑页面测试

3.7 个人主页测试

3. 使用jmeter进行性能测试

3.1 jmeter基础配置

3.2 页面接口测试

3.2.1 测试博客登录页接口

3.2.2 测试博客列表接口

3.2.3 测试博客详情页接口

3.3 性能测试


一.  项目背景

  1. 基于SpringBoot框架开发的个人博客系统,采用数据库存储数据并部署于云服务器。前端包含六个核心页面:登录页、注册页、个人主页、博客列表页、博客详情页和博客编辑页。系统实现了完整的用户功能体系,包括:账号注册登录、个人信息管理、博客撰写编辑、账号注销及强制登录验证等功能。

  2. 该系统为个人用户提供了简洁高效的博客记录平台,支持完整的内容展示功能。每篇博客的时间戳、标题、正文及作者信息均可清晰查阅,满足用户的基本写作和阅读需求。


二、项目功能

该个人博客系统主要实现了以下几个功能:登录、注册、修改个人信息、注销、编写博客以及删除博客等功能。

1.登录功能:输入用户名以及密码,登录成功后就会跳转到列表页面。登录页面的右上角有主页和写博客两个按钮,但是在未登录情况下按下均只会跳转到登录页面。

2.注册功能:在登录页面点击注册按钮后跳转到注册页面,在注册页面输入用户名、密码、头像、gitee地址,用户名要求必须是纯字符并且长度满足4~16位之间,密码要求6位~12位之间,头像必须上传,gitee地址可以不输入。

3.修改个人信息功能:登录成功后进入了博客主页面,此时点击头像进入个人信息页面,可点击编辑个人信息对用户名、密码、gitee地址以及头像都可以进行修改。

4.注销功能:登录成功后,点击右上角的注销按钮,可退出当前用户的登录跳转到登录页。

5.编写博客功能:登录后,点击右上角的写博客,进入博客编辑页面,支持使用MarkDown格式,编写完成后可以点击发布,后续也可以对发布的博客进行修改。

6.删除博客功能:可以将当前登录用户编写的博客进行删除操作。


三、测试计划

1. 功能测试

1.1 测试用例

1.2 执行测试部分操作截图

1)正常登录,跳转到博客列表页

2)注册成功,并使用注册的账号进行登录

3)对个人信息进行修改

4)编写博客,并发布

5)对博客进行修改、删除

6)注销,点击后返回到登录页

2. 使用selenium进行自动化测试

自动化测试代码地址:自动化测试: 自动化测试代码 - Gitee.com

下面将展示UI自动化测试的部分相关代码

2.1 添加相关依赖

<dependencies><!--驱动管理--><dependency><groupId>io.github.bonigarcia</groupId><artifactId>webdrivermanager</artifactId><version>5.8.0</version><scope>test</scope></dependency><!--安装selenium库--><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.0.0</version></dependency><!--屏幕截图--><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency></dependencies>

2.2 登录页面测试

/*** 页面可以正常显示*/public void checkPageRight() throws IOException {// 检擦菜单driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)"));// 检查登录框driver.findElement(By.cssSelector("#username"));// 账号输入框driver.findElement(By.cssSelector("#password"));// 密码输入框driver.findElement(By.cssSelector("#submit"));// 提交按钮driver.findElement(By.cssSelector("#register"));// 注册按钮screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());}
/*** 成功登录*/public void loginSuccess() throws IOException {// 先清空框中的内容driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");// 账号输入框driver.findElement(By.cssSelector("#password")).sendKeys("123456");// 密码输入框driver.findElement(By.cssSelector("#submit")).click();// 提交按钮// 跳转页面后检查新页面的元素是否存在来判断是否跳转成功driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)"));// 检查注销按钮是否存在driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a"));// 检查查看全文按钮是否存在screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());}
/*** 异常登录* 举例:账号正确,密码错误*/public void loginFail() throws IOException {driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");// 账号输入框driver.findElement(By.cssSelector("#password")).sendKeys("1234567");// 密码输入框driver.findElement(By.cssSelector("#submit")).click();// 提交按钮// 显示等待,等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());// 处理弹出的窗口,等窗口出现后才能处理,否则会报错Alert alert = driver.switchTo().alert();alert.accept();screenShot(Thread.currentThread().getStackTrace()[1].getMethodName());}

3.3 注册页面测试

    /***注册成功*/public void checkRegisterSuc() throws InterruptedException {// 一、注册// 满足4-16位driver.findElement(By.cssSelector("#username")).sendKeys("xiaoliuTest");// 满足至少6位driver.findElement(By.cssSelector("#password")).sendKeys("123456");// 上传头像driver.findElement(By.cssSelector("#avatar")).sendKeys("C:\\Users\\22350\\Desktop\\OIP-C.jpg");driver.findElement(By.cssSelector("body > div.container-register > div > form > button")).click();Thread.sleep(2000);// 对弹窗点击确定wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();// 二、测试是否注册成功driver.findElement(By.cssSelector("#username")).sendKeys("xiaoliuTest");// 账号输入框driver.findElement(By.cssSelector("#password")).sendKeys("123456");// 密码输入框driver.findElement(By.cssSelector("#submit")).click();// 提交按钮Thread.sleep(2000);// 跳转页面后检查新页面的元素是否存在来判断是否跳转成功driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)"));// 检查注销按钮是否存在driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a"));// 检查查看全文按钮是否存在}
    /*** 注册失败*/public void checkRegisterFail() throws InterruptedException {// 一、注册(用户名不符合)// 用户名少于4位driver.findElement(By.cssSelector("#username")).sendKeys("Tes");// 满足至少6位driver.findElement(By.cssSelector("#password")).sendKeys("123456");// 上传头像driver.findElement(By.cssSelector("#avatar")).sendKeys("C:\\Users\\22350\\Desktop\\OIP-C.jpg");driver.findElement(By.cssSelector("body > div.container-register > div > form > button")).click();Thread.sleep(2000);// 对弹窗点击确定wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();}

 3.4 博客列表页面测试

public void checkList() throws InterruptedException {// 检查元素的文本内容是否存在String title = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > div.title")).getText();String time = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > div.date")).getText();String content = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > div.desc")).getText();String button = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a")).getText();// 使用断言来判断assert !title.isEmpty();assert !time.isEmpty();assert !content.isEmpty();assert button.equals("查看全文>>");// 检查“查看全文”按钮是否可以跳转driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a")).click();// 显示等待,等待跳转后的页面的相应文本加载WebElement element = driver.findElement(By.cssSelector("body > div.container > div.right > div > div.title"));wait.until(ExpectedConditions.textToBePresentInElement(element, title));// 跳转后检查 跳转后的标题是否和跳转前的标题一致String AfterJumpTitle = driver.findElement(By.cssSelector("body > div.container > div.right > div > div.title")).getText();assert title.equals(AfterJumpTitle);// 将当前的url传给父类的detail,供util的子类使用detailUrl = driver.getCurrentUrl();// 将个人主页地址传给父类driver.findElement(By.cssSelector("#userAvatar")).click();loginIdUrl = driver.getCurrentUrl();}

3.5 博客详情页测试

    /*** 检查博客详情的内容*/public void checkPage() {// 标题driver.findElement(By.cssSelector("body > div.container > div.right > div > div.title"));// 时间driver.findElement(By.cssSelector("body > div.container > div.right > div > div.date"));// 内容driver.findElement(By.cssSelector("#detail"));// 编辑按钮driver.findElement(By.cssSelector("body > div.container > div.right > div > div.operating > button:nth-child(1)"));// 删除按钮driver.findElement(By.cssSelector("body > div.container > div.right > div > div.operating > button:nth-child(2)"));}
    /*** 检擦编辑按钮*/public void checkDetailEdit() throws InterruptedException {// 初始标题String title = driver.findElement(By.cssSelector("body > div.container > div.right > div > div.title")).getText();// 点击编辑按钮driver.findElement(By.cssSelector("body > div.container > div.right > div > div.operating > button:nth-child(1)")).click();// 给更新的标题名后加上时间,可以防止更新的标题相同SimpleDateFormat simp = new SimpleDateFormat("HHmmssSS");String titleTime = simp.format(System.currentTimeMillis());Thread.sleep(100);// 强制等待driver.findElement(By.cssSelector("#title")).clear();// 输入更改后的标题driver.findElement(By.cssSelector("#title")).sendKeys("自动化测试文章" + titleTime);// 点击提交按钮driver.findElement(By.cssSelector("#submit")).click();// 等待弹窗出现wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();// 显示等待文本加载完毕wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("body > div.container > div.right > div > div.title")));// 获取更新后的标题String afterEditTitle = driver.findElement(By.cssSelector("body > div.container > div.right > div > div.title")).getText();// 如果不相同则更新成功assert !title.equals(afterEditTitle);System.out.println("更新前的标题:"+title);System.out.println("更新后的标题:"+afterEditTitle);}
    /*** 检查博客删除按钮*/public void checkDetailDel() {// 跳转到博客详情页driver.get(detailUrl);driver.findElement(By.cssSelector("body > div.container > div.right > div > div.operating > button:nth-child(2)")).click();wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();// 确认alert.accept();}

 3.6 博客编辑页面测试

    /*** 博客提交成功*/public void checkSubmitSuc() throws InterruptedException {// 一、编写并发送一篇新文章// 给更新的标题名后加上时间,可以防止标题相同SimpleDateFormat simp = new SimpleDateFormat("HHmmssSS");String titleTime = simp.format(System.currentTimeMillis());Thread.sleep(1000);String beforeTitle = "自动化创建的博客"+titleTime;driver.findElement(By.cssSelector("#title")).sendKeys(beforeTitle);wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#title")));WebElement ele = driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));// 使用鼠标键盘操作不可互动的元素new Actions(driver).click(ele).sendKeys("自动化输入内容").perform();Thread.sleep(3000);driver.findElement(By.cssSelector("#submit")).click();// 确认发表博客成功弹窗wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();// 二、校验 (新发送的博客位于最后面)// 先获取所有的博客List<WebElement>  elements = driver.findElements(By.cssSelector("body > div.container > div.right > div"));// 获取最后一个博客String afterTitle = driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child("+elements.size()+") > div.title")).getText();assert beforeTitle.equals(afterTitle);}
    /*** 博客提交失败,有内容,没有标题*/public void checkSubmitFailNoTitle() throws InterruptedException {// 一、编写并发送一篇新文章// 标题为空driver.findElement(By.cssSelector("#title"));// 使用鼠标键盘操作不可互动的元素WebElement ele = driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));new Actions(driver).click(ele).sendKeys("自动化输入内容").perform();Thread.sleep(1000);driver.findElement(By.cssSelector("#submit")).click();// 发送失败弹窗wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();}
    /*** 博客提交失败,有标题,没有内容*/public void checkSubmitFailNoContent() throws InterruptedException {// 给更新的标题名后加上时间,可以防止标题相同SimpleDateFormat simp = new SimpleDateFormat("HHmmssSS");String titleTime = simp.format(System.currentTimeMillis());Thread.sleep(1000);String beforeTitle = "自动化创建的博客"+titleTime;driver.findElement(By.cssSelector("#title")).sendKeys(beforeTitle);wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#title")));// 将内容清空WebElement ele = driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));new Actions(driver).doubleClick(ele).click(ele).sendKeys(Keys.DELETE).perform();Thread.sleep(1000);driver.findElement(By.cssSelector("#submit")).click();// 发送失败弹窗wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();}

3.7 个人主页测试

    /*** 检查页面*/public void checkPersonPage() throws InterruptedException {Thread.sleep(1000);// 文章标是否存在driver.findElement(By.cssSelector("body > div.container > div.right > div.tab-buttons > button.tab-btn.active"));// 点击个人信息按钮driver.findElement(By.cssSelector("body > div.container > div.right > div.tab-buttons > button:nth-child(2)")).click();Thread.sleep(1000);// 再点击文章按钮driver.findElement(By.cssSelector("body > div.container > div.right > div.tab-buttons > button:nth-child(1)")).click();Thread.sleep(1000);}
    /*** 测试编辑个人信息功能(修改用户名)*/public void checkPersonEdit() throws InterruptedException {// 点击个人信息按钮driver.findElement(By.cssSelector("body > div.container > div.right > div.tab-buttons > button:nth-child(2)")).click();Thread.sleep(1000);driver.findElement(By.cssSelector("#editProfileBtn")).click();// 更改前的用户名String beforeName = driver.findElement(By.cssSelector("body > div.container > div.left > div > h3")).getText();driver.findElement(By.cssSelector("#editUsername")).clear();// 更改后的用户名String afterName = "zhangsana";driver.findElement(By.cssSelector("#editUsername")).sendKeys(afterName);// 点击保存按钮driver.findElement(By.cssSelector("body > div.container > div.right > div.action-btns > button.save-btn")).click();wait.until(ExpectedConditions.alertIsPresent());Alert alert = driver.switchTo().alert();alert.accept();Thread.sleep(1000);String temp = driver.findElement(By.cssSelector("body > div.container > div.left > div > h3")).getText();assert !beforeName.equals(temp);}

3. 使用jmeter进行性能测试

3.1 jmeter基础配置

1)打开jmeter后,首先创建一个线程组:

 2)设置HTTP请求默认值:

3)添加JSON断言:

要求测试的返回结果状态必须是SUCCESS

比如:

只有code的值位SUCCESS才显示这次的接口请求测试成功

以下演示部分测试

3.2 页面接口测试

3.2.1 测试博客登录页接口

进行登录接口的测试:

测试结果:

3.2.2 测试博客列表接口

结果返回都正确

3.2.3 测试博客详情页接口

3.3 性能测试

聚合报告:

相应时间:

吞吐量:

生成性能测试报告:


http://www.hkcw.cn/article/TBYrWkEVhe.shtml

相关文章

WPF技术体系与现代化样式

目录 ​​1 WPF技术架构解析​​ ​​1.1 技术演进与定位​​ ​​1.2 核心机制对比​​ ​​2 样式与资源系统​​ ​​2.1 资源(Resource)定义与作用域​​ ​​2.2 样式(Style)与触发器​​ ​​3 开发环境配置(.NET 8)​​ ​​3.1 安装流程​​ ​​3.2 项目结…

智能快递地址解析接口如何用PHP调用?

一、什么是智能快递地址解析接口 随着互联网技术的普及和电子商务的迅猛发展&#xff0c;网购已成为现代人日常生活的重要组成部分。然而&#xff0c;在这个便捷的背后&#xff0c;一个看似不起眼却影响深远的问题正悄然浮现——用户填写的快递地址格式混乱、信息不全甚至错漏…

Day11

1. HTTP常见状态码有哪些&#xff1f; 1xx 类状态码属于提示信息&#xff0c;是协议处理中的一种中间状态&#xff0c;实际用的比较少。2xx 类状态码表示服务器成功处理了客户端的请求。3xx 类状态码表示客户端请求的资源发生了变动&#xff0c;需要客户端用新的 URL 重新发送请…

Windows 下部署 SUNA 项目:虚拟环境尝试与最终方案

#工作记录 #回顾总结 本文记录了在 Windows 系统上&#xff0c;通过 PyCharm 图形界面&#xff08;尽量减少命令行操作&#xff09;部署 SUNA 项目时&#xff0c;针对不同虚拟环境方案的尝试过程、遇到的问题以及最终选择的可行方案&#xff0c;并补充了整体部署思路与推荐。…

Mycat的监控

参考资料&#xff1a; 参考视频 参考博客 Mysql分库分表&#xff08;基于Mycat&#xff09;的基本部署 MySQL垂直分库&#xff08;基于MyCat&#xff09; Mysql水平分表&#xff08;基于Mycat&#xff09;及常用分片规则 视频参考资料及安装包&#xff1a; https://pan.b…

安科电动机保护器通过ModbusRTU转profinet网关与PLC通讯

安科电动机保护器通过ModbusRTU转profinet网关与PLC通讯 在工业自动化领域&#xff0c;设备间的通信和数据交互至关重要。Modbus作为一种常用的通讯协议&#xff0c;广泛应用于各种工业现场&#xff1b;而Profinet则凭借其高效、实时性&#xff0c;在工业以太网通讯中占据重要…

CLion社区免费后,使用CLion开发STM32相关工具资源汇总与入门教程

Clion下载与配置 Clion推出社区免费&#xff0c;就是需要注册一个账号使用&#xff0c;大家就不用去找破解版版本了&#xff0c;jetbrains家的IDEA用过的都说好&#xff0c;这里嵌入式领域也推荐使用。 CLion官网下载地址 安装没有什么特别&#xff0c;下一步就好。 启动登录…

【快见刊】2025年应用材料、机械与制造工程国际会议(ICAMMME 2025)

2025年应用材料、机械与制造工程国际会议&#xff08;MMME 2025&#xff09;将在美丽的六朝古都南京隆重召开。南京&#xff0c;这座兼具现代都市风貌与深厚历史文化底蕴的城市&#xff0c;将以其独特的魅力迎接来自世界各地的应用材料、机械与制造工程领域的专家、学者及业界精…

TopCode之最大子数组和

题目链接 53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 解法1: 暴力(一个循环用来固定,一个用来找最大的子数组O(n^2),每次往后拓展一个元素就判断是否是最长的),枚举出每一种情况, 然后不断更新最大的 解法二: dp 1> dp的含义: dp[i]记…

SAP是什么?SAP概述

SAP 概述 一、核心定义 ‌SAP‌&#xff08;Systems, Applications & Products in Data Processing&#xff09;是德国 SAP 公司开发的集成化 ‌ERP&#xff08;企业资源规划&#xff09;‌ 系统&#xff0c;通过模块化架构整合企业财务、供应链、生产等核心业务流程&…

Fooocus 一款好用的文生图软件,4G显存可跑

Fooocus 是一款对标**Midjourney**的图像生成软件&#xff0c;并且可以本地部署&#xff0c;目前star数量已经来到了45.2k&#xff0c;说明非常受大家欢迎的。 今天我们来体验下。 首先贴上git地址 https://github.com/lllyasviel/Fooocus 看下软件对硬件的要求 针对不同的显…

【AI Study】第三天,Python基础 - NumPy(1)

学习计划&#xff1a;AI Study&#xff0c;学习计划源码地址&#xff1a;https://github.com/co-n00b/AI-Study.git 2025-05-23 一、NumPy简介 NumPy的官方文档并没有明确指出其发音,但根据英语单词的常规发音规则NumPy可以读作/ˈnʌmpaɪ/&#xff0c;也可以读作/ˈnʌmpi/…

电工基础【4】点动接线实操

06 接线实操 (接线实操) 1、接线实操软件下载和安装 2、点动点路图接线实操 3、接线的基本工艺和基本工具 -----小记----- 斯沃数控机床仿真(SSCNC Machine Simulation 软件)示例图 斯沃数控机床仿真软件(电气) v1.3 安装版(附安装教程) https://www.jb51.net/softs/776239.…

QT 5.9.2+VTK8.0实现等高线绘制

项目下载链接&#xff1a;QT5.9.2VTK8.0实现等高线绘制资源-CSDN文库 示例如下&#xff1a; 主要代码如下&#xff1a; #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkPo…

【C++】特殊类设计

1.设计一个不能被拷贝的类 C98中&#xff1a; 将拷贝构造函数和赋值运算符重载只声明不定义&#xff0c;将访问权限设置私有 原因&#xff1a; 设置成私有&#xff1a;如果只声明没有设置成private&#xff0c;用户自己如果在类外定义了&#xff0c;就可以不能禁止拷贝了只声…

Oracle 用户/权限/角色管理

1. 用户 1.1. 用户的创建和删除 1.1.1. 创建用户 create user user identified {by password | externally} [ default tablespace tablespace ] [ temporary tablespace tablespace ] [ quota {integer [k | m ] | unlimited } on tablespace [ quota {integer [k | m ] | …

机器人现可完全破解验证码:未来安全技术何去何从?

引言 随着计算机视觉技术的飞速发展&#xff0c;机器学习模型现已能够100%可靠地解决Google的视觉reCAPTCHAv2验证码。这标志着一个时代的结束——自2000年代初以来&#xff0c;CAPTCHA&#xff08;"全自动区分计算机与人类的图灵测试"的缩写&#xff09;一直是区分…

AI赋能SEO关键词革新

内容概要 随着人工智能技术与搜索引擎优化的深度融合&#xff0c;传统关键词策略正经历系统性变革。基于智能语义分析与深度学习算法&#xff0c;AI能够实时解析海量搜索数据&#xff0c;构建动态更新的关键词库&#xff0c;突破人工筛选的局限性。通过自然语言处理技术&#…

深入解析Java11核心新特性

文章目录 前言一、标准化HTTP Client&#xff1a;告别HttpURLConnection1.1 HttpURLConnection 的痛点1.2 标准化 HTTP Client 的解决方案1.3 实战应用指南1.4 总结 二、局部变量类型推断增强&#xff1a;Lambda中的var2.1 解决的问题&#xff1a;类型声明的一致性困境2.2 实现…

香橙派3B学习笔记6:基本的Bash脚本学习_UTF-8格式问题

今日学习基本的linux 的一些 bash 脚本 ssh &#xff1a; orangepi本地ip 密码 &#xff1a; orangepi 操作系统发行版&#xff1a; 基于 Ubuntu 20.04.6 LTS&#xff08;Focal Fossa&#xff09;的定制版本&#xff0c;专门为 Orange Pi 设备优化。PRETTY_NAME"Orange P…