<!-- Generated by Trae Work -->
|
<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<title>考试阅卷系统 - 华为云部署新手教程</title>
|
<style>
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
body {
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
background: #f5f7fa;
|
color: #333;
|
line-height: 1.8;
|
}
|
.container {
|
max-width: 960px;
|
margin: 0 auto;
|
padding: 40px 20px;
|
}
|
h1 {
|
font-size: 32px;
|
color: #1a1a2e;
|
margin-bottom: 10px;
|
text-align: center;
|
}
|
.subtitle {
|
text-align: center;
|
color: #666;
|
margin-bottom: 40px;
|
font-size: 16px;
|
}
|
.toc {
|
background: #fff;
|
border-radius: 12px;
|
padding: 24px 32px;
|
margin-bottom: 32px;
|
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
}
|
.toc h2 {
|
font-size: 18px;
|
margin-bottom: 12px;
|
color: #1a1a2e;
|
}
|
.toc ol {
|
padding-left: 24px;
|
}
|
.toc li {
|
margin: 6px 0;
|
}
|
.toc a {
|
color: #0052d9;
|
text-decoration: none;
|
}
|
.toc a:hover {
|
text-decoration: underline;
|
}
|
.section {
|
background: #fff;
|
border-radius: 12px;
|
padding: 32px;
|
margin-bottom: 24px;
|
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
}
|
.section h2 {
|
font-size: 24px;
|
color: #1a1a2e;
|
margin-bottom: 16px;
|
padding-bottom: 12px;
|
border-bottom: 2px solid #e8eaed;
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
}
|
.section h2 .step-num {
|
background: linear-gradient(135deg, #0052d9, #36cffb);
|
color: #fff;
|
width: 36px;
|
height: 36px;
|
border-radius: 50%;
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
font-size: 16px;
|
font-weight: bold;
|
}
|
.section h3 {
|
font-size: 18px;
|
color: #1a1a2e;
|
margin: 20px 0 10px;
|
}
|
.section p {
|
margin: 10px 0;
|
color: #444;
|
}
|
.section ul, .section ol {
|
padding-left: 24px;
|
margin: 10px 0;
|
}
|
.section li {
|
margin: 6px 0;
|
color: #444;
|
}
|
.code-block {
|
background: #1e1e2e;
|
color: #cdd6f4;
|
padding: 16px 20px;
|
border-radius: 8px;
|
margin: 12px 0;
|
font-family: "Fira Code", "Consolas", monospace;
|
font-size: 14px;
|
overflow-x: auto;
|
line-height: 1.6;
|
}
|
.code-block .comment { color: #6c7086; }
|
.code-block .cmd { color: #a6e3a1; }
|
.code-block .var { color: #f9e2af; }
|
.tip {
|
background: #ecf5ff;
|
border-left: 4px solid #409eff;
|
padding: 12px 16px;
|
border-radius: 4px;
|
margin: 12px 0;
|
color: #333;
|
}
|
.tip::before {
|
content: "💡 提示:";
|
font-weight: bold;
|
color: #409eff;
|
}
|
.warning {
|
background: #fdf6ec;
|
border-left: 4px solid #e6a23c;
|
padding: 12px 16px;
|
border-radius: 4px;
|
margin: 12px 0;
|
color: #333;
|
}
|
.warning::before {
|
content: "⚠️ 注意:";
|
font-weight: bold;
|
color: #e6a23c;
|
}
|
.success {
|
background: #f0f9eb;
|
border-left: 4px solid #67c23a;
|
padding: 12px 16px;
|
border-radius: 4px;
|
margin: 12px 0;
|
color: #333;
|
}
|
.success::before {
|
content: "✅ 成功:";
|
font-weight: bold;
|
color: #67c23a;
|
}
|
.table-wrapper {
|
overflow-x: auto;
|
margin: 16px 0;
|
}
|
table {
|
width: 100%;
|
border-collapse: collapse;
|
font-size: 14px;
|
}
|
th, td {
|
border: 1px solid #e8eaed;
|
padding: 10px 14px;
|
text-align: left;
|
}
|
th {
|
background: #f5f7fa;
|
font-weight: 600;
|
color: #1a1a2e;
|
}
|
tr:nth-child(even) {
|
background: #fafbfc;
|
}
|
.highlight {
|
background: #fffbe6;
|
padding: 2px 6px;
|
border-radius: 3px;
|
font-family: monospace;
|
font-size: 13px;
|
}
|
.architecture {
|
background: #f8f9fc;
|
border-radius: 8px;
|
padding: 24px;
|
margin: 16px 0;
|
text-align: center;
|
}
|
.arch-box {
|
display: inline-block;
|
background: #fff;
|
border: 2px solid #0052d9;
|
color: #0052d9;
|
padding: 12px 24px;
|
border-radius: 8px;
|
margin: 8px;
|
font-weight: 600;
|
}
|
.arch-arrow {
|
display: inline-block;
|
color: #999;
|
font-size: 24px;
|
margin: 0 8px;
|
vertical-align: middle;
|
}
|
.faq-item {
|
border: 1px solid #e8eaed;
|
border-radius: 8px;
|
margin: 10px 0;
|
overflow: hidden;
|
}
|
.faq-q {
|
background: #f5f7fa;
|
padding: 12px 16px;
|
font-weight: 600;
|
color: #1a1a2e;
|
}
|
.faq-a {
|
padding: 12px 16px;
|
color: #555;
|
}
|
.footer {
|
text-align: center;
|
color: #999;
|
padding: 20px;
|
font-size: 14px;
|
}
|
</style>
|
</head>
|
<body>
|
<div class="container">
|
<h1>🚀 考试阅卷系统 - 华为云部署新手教程</h1>
|
<p class="subtitle">从购买服务器到上线运行,一步步带你完成部署</p>
|
|
<div class="toc">
|
<h2>📑 目录</h2>
|
<ol>
|
<li><a href="#overview">项目概览与部署方案</a></li>
|
<li><a href="#step1">第一步:购买华为云 ECS 服务器</a></li>
|
<li><a href="#step2">第二步:配置安全组(开放端口)</a></li>
|
<li><a href="#step3">第三步:远程登录服务器</a></li>
|
<li><a href="#step4">第四步:安装运行环境</a></li>
|
<li><a href="#step5">第五步:上传项目代码</a></li>
|
<li><a href="#step6">第六步:安装项目依赖</a></li>
|
<li><a href="#step7">第七步:初始化数据库与管理员</a></li>
|
<li><a href="#step8">第八步:启动服务并验证</a></li>
|
<li><a href="#step9">第九步:配置开机自启(systemd)</a></li>
|
<li><a href="#step10">第十步:绑定域名(可选)</a></li>
|
<li><a href="#faq">常见问题 FAQ</a></li>
|
</ol>
|
</div>
|
|
<!-- 0. 项目概览 -->
|
<div class="section" id="overview">
|
<h2><span class="step-num">📌</span> 项目概览与部署方案</h2>
|
|
<h3>项目技术栈</h3>
|
<div class="table-wrapper">
|
<table>
|
<tr><th>层级</th><th>技术</th><th>端口</th></tr>
|
<tr><td>前端</td><td>React + TypeScript + Vite + Ant Design</td><td>5174(生产)</td></tr>
|
<tr><td>后端</td><td>Python FastAPI + SQLAlchemy + Uvicorn</td><td>8010</td></tr>
|
<tr><td>数据库</td><td>SQLite(文件型,无需单独部署)</td><td>-</td></tr>
|
</table>
|
</div>
|
|
<h3>部署架构</h3>
|
<div class="architecture">
|
<div class="arch-box">用户浏览器</div>
|
<div class="arch-arrow">→</div>
|
<div class="arch-box">ECS 云服务器</div>
|
<div style="margin-top:12px;">
|
<div class="arch-box" style="border-color:#67c23a;color:#67c23a;">前端 :5174</div>
|
<div class="arch-arrow">→</div>
|
<div class="arch-box" style="border-color:#e6a23c;color:#e6a23a;">后端 :8010</div>
|
<div class="arch-arrow">→</div>
|
<div class="arch-box" style="border-color:#909399;color:#909399;">SQLite 文件</div>
|
</div>
|
</div>
|
|
<h3>为什么选 ECS?</h3>
|
<ul>
|
<li><strong>最简单</strong>:一台服务器搞定全部,适合新手</li>
|
<li><strong>成本低</strong>:SQLite 不需要额外买数据库</li>
|
<li><strong>灵活</strong>:后续可以随时升级配置</li>
|
</ul>
|
|
<div class="tip">
|
本教程选择 <strong>华为云弹性云服务器 ECS + Ubuntu 22.04</strong> 操作系统,这是最通用、文档最多的组合。
|
</div>
|
</div>
|
|
<!-- 第一步:购买 ECS -->
|
<div class="section" id="step1">
|
<h2><span class="step-num">1</span> 购买华为云 ECS 服务器</h2>
|
|
<h3>1.1 注册华为云账号</h3>
|
<p>访问 <a href="https://www.huaweicloud.com/" target="_blank">华为云官网</a>,点击右上角「注册」,按照提示完成注册和实名认证。</p>
|
|
<h3>1.2 购买 ECS</h3>
|
<ol>
|
<li>登录后,在顶部搜索框输入「弹性云服务器 ECS」,点击进入</li>
|
<li>点击「购买弹性云服务器」</li>
|
<li>按照以下配置选择:</li>
|
</ol>
|
|
<div class="table-wrapper">
|
<table>
|
<tr><th>配置项</th><th>推荐选择</th><th>说明</th></tr>
|
<tr><td>计费模式</td><td>按需计费 / 包年包月</td><td>新手先用按需计费试试,稳定后转包月更便宜</td></tr>
|
<tr><td>区域</td><td>选离你最近的(如:北京四、上海一)</td><td>影响访问速度</td></tr>
|
<tr><td>CPU 架构</td><td>x86 计算</td><td>兼容性最好</td></tr>
|
<tr><td>规格</td><td>通用计算型 | s6.large.2(2核4G)</td><td>💡 最低 2核2G 也能跑,但推荐 2核4G 更流畅</td></tr>
|
<tr><td>镜像</td><td>公共镜像 → Ubuntu → Ubuntu 22.04 64bit</td><td>本教程基于此系统</td></tr>
|
<tr><td>系统盘</td><td>通用型 SSD,40GB</td><td>足够用了</td></tr>
|
<tr><td>带宽</td><td>按流量计费,5Mbit/s</td><td>小流量场景省钱</td></tr>
|
</table>
|
</div>
|
|
<h3>1.3 设置登录凭证</h3>
|
<p>在「高级配置」中设置登录方式:</p>
|
<ul>
|
<li><strong>密码方式(推荐新手)</strong>:设置 root 用户密码,请牢记!</li>
|
<li>密钥对方式:更安全,但需要生成密钥对</li>
|
</ul>
|
|
<div class="warning">
|
请务必记住你的服务器 <strong>公网 IP 地址</strong> 和 <strong>root 密码</strong>,后面会反复用到。
|
</div>
|
|
<p>确认配置无误后,点击「立即购买」→ 「去支付」,等待一两分钟服务器就创建好了。</p>
|
</div>
|
|
<!-- 第二步:配置安全组 -->
|
<div class="section" id="step2">
|
<h2><span class="step-num">2</span> 配置安全组(开放端口)</h2>
|
|
<p>华为云默认只开放了 22 端口(SSH 登录用),我们需要手动开放项目要用的端口。</p>
|
|
<h3>2.1 进入安全组配置</h3>
|
<ol>
|
<li>在 ECS 控制台,找到你的服务器,点击服务器名称进入详情</li>
|
<li>找到「安全组」标签页</li>
|
<li>点击安全组名称(如 default)进入配置</li>
|
</ol>
|
|
<h3>2.2 添加入方向规则</h3>
|
<p>点击「添加规则」,依次添加以下规则:</p>
|
|
<div class="table-wrapper">
|
<table>
|
<tr><th>优先级</th><th>策略</th><th>协议端口</th><th>类型</th><th>源地址</th><th>说明</th></tr>
|
<tr><td>1</td><td>允许</td><td>TCP:22</td><td>IPv4</td><td>0.0.0.0/0</td><td>SSH 远程登录</td></tr>
|
<tr><td>1</td><td>允许</td><td>TCP:5174</td><td>IPv4</td><td>0.0.0.0/0</td><td>前端访问端口</td></tr>
|
<tr><td>1</td><td>允许</td><td>TCP:8010</td><td>IPv4</td><td>0.0.0.0/0</td><td>后端 API 端口</td></tr>
|
<tr><td>1</td><td>允许</td><td>TCP:80</td><td>IPv4</td><td>0.0.0.0/0</td><td>HTTP(后面绑域名用)</td></tr>
|
<tr><td>1</td><td>允许</td><td>TCP:443</td><td>IPv4</td><td>0.0.0.0/0</td><td>HTTPS(后面绑域名用)</td></tr>
|
</table>
|
</div>
|
|
<div class="tip">
|
源地址填 <span class="highlight">0.0.0.0/0</span> 表示允许所有 IP 访问。如果只有你自己用,可以填你电脑的公网 IP,更安全。
|
</div>
|
</div>
|
|
<!-- 第三步:登录服务器 -->
|
<div class="section" id="step3">
|
<h2><span class="step-num">3</span> 远程登录服务器</h2>
|
|
<h3>Windows 用户:使用 PowerShell</h3>
|
<p>按下 <kbd>Win</kbd> + <kbd>X</kbd>,选择「Windows PowerShell」或「终端」,输入:</p>
|
<div class="code-block">
|
<span class="cmd">ssh</span> root@你的服务器公网IP
|
<span class="comment"># 例如:ssh root@123.45.67.89</span>
|
</div>
|
<p>第一次连接会提示「是否继续连接」,输入 <span class="highlight">yes</span> 回车,然后输入密码(密码不会显示,输完直接回车)。</p>
|
|
<h3>Mac 用户:使用终端</h3>
|
<p>打开「终端」App,同样输入:</p>
|
<div class="code-block"><span class="cmd">ssh</span> root@你的服务器公网IP</div>
|
|
<div class="success">
|
登录成功后,你会看到类似 <span class="highlight">root@ecs-xxx:~#</span> 的提示符,说明已经进入服务器了!
|
</div>
|
|
<div class="tip">
|
也可以使用华为云控制台的「远程登录」功能(VNC),在 ECS 详情页点击「远程登录」按钮即可。
|
</div>
|
</div>
|
|
<!-- 第四步:安装环境 -->
|
<div class="section" id="step4">
|
<h2><span class="step-num">4</span> 安装运行环境</h2>
|
|
<p>登录服务器后,依次执行以下命令。每输完一行按回车执行,等待完成再继续。</p>
|
|
<h3>4.1 更新系统包</h3>
|
<div class="code-block">
|
<span class="cmd">apt</span> update && <span class="cmd">apt</span> upgrade -y
|
</div>
|
<p>这一步可能需要几分钟,耐心等待。</p>
|
|
<h3>4.2 安装 Python 3 和 pip</h3>
|
<div class="code-block">
|
<span class="cmd">apt</span> install -y python3 python3-venv python3-pip git
|
</div>
|
<p>验证安装:</p>
|
<div class="code-block">
|
<span class="cmd">python3</span> --version
|
<span class="comment"># 输出类似 Python 3.10.x 就对了</span>
|
</div>
|
|
<h3>4.3 安装 Node.js</h3>
|
<p>前端构建需要 Node.js,我们安装 LTS 版本:</p>
|
<div class="code-block">
|
<span class="comment"># 下载 Node.js 安装脚本</span>
|
<span class="cmd">curl</span> -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
|
<span class="comment"># 安装 Node.js</span>
|
<span class="cmd">apt</span> install -y nodejs
|
|
<span class="comment"># 验证安装</span>
|
<span class="cmd">node</span> --version
|
<span class="cmd">npm</span> --version
|
<span class="comment"># 输出 v20.x.x 和 10.x.x 就对了</span>
|
</div>
|
|
<div class="success">
|
环境安装完成!现在有了 Python、Node.js 和 Git,可以部署项目了。
|
</div>
|
</div>
|
|
<!-- 第五步:上传代码 -->
|
<div class="section" id="step5">
|
<h2><span class="step-num">5</span> 上传项目代码到服务器</h2>
|
|
<p>有两种方式上传代码,选择你方便的一种:</p>
|
|
<h3>方式一:Git 克隆(推荐,如果代码在 Git 仓库)</h3>
|
<p>如果你的代码已经推送到 Gitee / GitHub / GitLab 等平台:</p>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt
|
<span class="cmd">git</span> clone 你的仓库地址 score-query
|
<span class="comment"># 例如:git clone https://gitee.com/你的用户名/score-query.git</span>
|
|
<span class="cmd">cd</span> score-query
|
<span class="cmd">ls</span>
|
<span class="comment"># 应该能看到 backend、frontend、scripts 等目录</span>
|
</div>
|
|
<h3>方式二:本地上传(适合代码在本地电脑)</h3>
|
<p><strong>Windows 用户:</strong>下载 <a href="https://winscp.net/" target="_blank">WinSCP</a> 或使用 <a href="https://www.putty.org/" target="_blank">PuTTY</a> 的 pscp</p>
|
<p><strong>Mac 用户:</strong>在本地终端执行:</p>
|
<div class="code-block">
|
<span class="cmd">scp</span> -r /本地项目路径 root@你的服务器IP:/opt/score-query
|
</div>
|
|
<div class="tip">
|
如果你用的是 VS Code,可以安装「Remote - SSH」插件,直接在 VS Code 里编辑服务器上的文件,非常方便!
|
</div>
|
</div>
|
|
<!-- 第六步:安装依赖 -->
|
<div class="section" id="step6">
|
<h2><span class="step-num">6</span> 安装项目依赖</h2>
|
|
<p>假设项目在 <span class="highlight">/opt/score-query</span> 目录下:</p>
|
|
<h3>6.1 安装后端 Python 依赖</h3>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query/backend
|
|
<span class="comment"># 创建虚拟环境(推荐,隔离依赖)</span>
|
<span class="cmd">python3</span> -m venv .venv
|
|
<span class="comment"># 激活虚拟环境并安装依赖</span>
|
<span class="cmd">source</span> .venv/bin/activate
|
<span class="cmd">pip</span> install -r requirements.txt
|
|
<span class="comment"># 等待安装完成,验证一下</span>
|
<span class="cmd">python</span> -c "import fastapi; print('FastAPI 版本:', fastapi.__version__)"
|
</div>
|
|
<h3>6.2 安装前端 Node.js 依赖</h3>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query/frontend
|
|
<span class="comment"># 安装依赖,可能需要几分钟</span>
|
<span class="cmd">npm</span> install
|
|
<span class="comment"># 验证</span>
|
<span class="cmd">npm</span> run build
|
<span class="comment"># 如果能成功构建出 dist 目录就说明没问题</span>
|
</div>
|
|
<div class="warning">
|
如果 <span class="highlight">npm install</span> 很慢,可以切换到国内镜像:<br>
|
<span class="highlight">npm config set registry https://registry.npmmirror.com</span>
|
</div>
|
</div>
|
|
<!-- 第七步:初始化 -->
|
<div class="section" id="step7">
|
<h2><span class="step-num">7</span> 初始化数据库与管理员</h2>
|
|
<h3>7.1 创建数据目录</h3>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query/backend
|
<span class="cmd">mkdir</span> -p data uploads
|
</div>
|
|
<h3>7.2 初始化管理员账号</h3>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query/backend
|
<span class="cmd">source</span> .venv/bin/activate
|
<span class="cmd">python</span> scripts/seed_admin.py
|
</div>
|
|
<div class="success">
|
默认账号:<br>
|
管理员:<span class="highlight">admin</span> / 密码:<span class="highlight">admin123</span><br>
|
阅卷员:<span class="highlight">grader</span> / 密码:<span class="highlight">grader123</span><br>
|
<strong>上线后请务必修改默认密码!</strong>
|
</div>
|
</div>
|
|
<!-- 第八步:启动服务 -->
|
<div class="section" id="step8">
|
<h2><span class="step-num">8</span> 启动服务并验证</h2>
|
|
<h3>8.1 使用项目自带脚本启动</h3>
|
<p>项目已经提供了方便的启动脚本:</p>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query
|
|
<span class="comment"># 给脚本添加执行权限(首次需要)</span>
|
<span class="cmd">chmod</span> +x scripts/*.sh
|
|
<span class="comment"># 启动全部服务(生产模式)</span>
|
./scripts/start.sh
|
</div>
|
|
<p>启动成功后,你会看到类似输出:</p>
|
<div class="code-block">
|
后端已启动 → http://127.0.0.1:8010
|
前端生产 → http://0.0.0.0:5174
|
</div>
|
|
<h3>8.2 验证服务是否正常</h3>
|
<p>检查进程是否在运行:</p>
|
<div class="code-block">
|
./scripts/status.sh
|
<span class="comment"># 或</span>
|
<span class="cmd">ps</span> aux | grep uvicorn
|
<span class="cmd">ps</span> aux | grep vite
|
</div>
|
|
<p>检查端口是否监听:</p>
|
<div class="code-block">
|
<span class="cmd">ss</span> -tlnp | grep -E '5174|8010'
|
</div>
|
|
<h3>8.3 在浏览器访问</h3>
|
<p>打开浏览器,访问:</p>
|
<div class="code-block">
|
http://你的服务器公网IP:5174
|
</div>
|
|
<div class="success">
|
如果能看到登录页面,输入 <span class="highlight">admin / admin123</span> 能成功登录,那么恭喜你,部署成功了!🎉
|
</div>
|
|
<h3>8.4 常用命令</h3>
|
<div class="table-wrapper">
|
<table>
|
<tr><th>命令</th><th>作用</th></tr>
|
<tr><td><span class="highlight">./scripts/start.sh</span></td><td>启动全部服务(生产模式)</td></tr>
|
<tr><td><span class="highlight">./scripts/stop.sh</span></td><td>停止全部服务</td></tr>
|
<tr><td><span class="highlight">./scripts/restart.sh</span></td><td>重启全部服务</td></tr>
|
<tr><td><span class="highlight">./scripts/status.sh</span></td><td>查看服务状态</td></tr>
|
<tr><td><span class="highlight">./scripts/build-frontend.sh</span></td><td>重新构建前端</td></tr>
|
</table>
|
</div>
|
|
<h3>8.5 查看日志</h3>
|
<div class="code-block">
|
<span class="comment"># 后端日志</span>
|
<span class="cmd">tail</span> -f logs/backend.log
|
|
<span class="comment"># 前端日志</span>
|
<span class="cmd">tail</span> -f logs/frontend.log
|
</div>
|
<p>按 <kbd>Ctrl</kbd> + <kbd>C</kbd> 退出日志查看。</p>
|
</div>
|
|
<!-- 第九步:开机自启 -->
|
<div class="section" id="step9">
|
<h2><span class="step-num">9</span> 配置开机自启(强烈推荐)</h2>
|
|
<p>前面的方式启动后,如果服务器重启,服务不会自动恢复。我们配置 systemd 服务来实现开机自启。</p>
|
|
<h3>9.1 创建后端服务</h3>
|
<div class="code-block">
|
<span class="cmd">cat</span> > /etc/systemd/system/score-backend.service << 'EOF'
|
[Unit]
|
Description=Score Query Backend (FastAPI)
|
After=network.target
|
|
[Service]
|
Type=simple
|
User=root
|
WorkingDirectory=/opt/score-query/backend
|
ExecStart=/opt/score-query/backend/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8010
|
Restart=always
|
RestartSec=5
|
Environment=PYTHONUNBUFFERED=1
|
|
[Install]
|
WantedBy=multi-user.target
|
EOF
|
</div>
|
|
<h3>9.2 创建前端服务</h3>
|
<div class="code-block">
|
<span class="cmd">cat</span> > /etc/systemd/system/score-frontend.service << 'EOF'
|
[Unit]
|
Description=Score Query Frontend (Vite Preview)
|
After=network.target
|
|
[Service]
|
Type=simple
|
User=root
|
WorkingDirectory=/opt/score-query/frontend
|
ExecStart=/usr/bin/npm run preview
|
Restart=always
|
RestartSec=5
|
|
[Install]
|
WantedBy=multi-user.target
|
EOF
|
</div>
|
|
<h3>9.3 启用并启动服务</h3>
|
<div class="code-block">
|
<span class="comment"># 先停止之前的脚本启动的服务</span>
|
<span class="cmd">cd</span> /opt/score-query && ./scripts/stop.sh
|
|
<span class="comment"># 重新加载 systemd 配置</span>
|
<span class="cmd">systemctl</span> daemon-reload
|
|
<span class="comment"># 设置开机自启</span>
|
<span class="cmd">systemctl</span> enable score-backend score-frontend
|
|
<span class="comment"># 启动服务</span>
|
<span class="cmd">systemctl</span> start score-backend score-frontend
|
|
<span class="comment"># 查看状态</span>
|
<span class="cmd">systemctl</span> status score-backend
|
<span class="cmd">systemctl</span> status score-frontend
|
</div>
|
|
<div class="success">
|
看到 <span class="highlight">active (running)</span> 就说明配置成功了!服务器重启后服务会自动启动。
|
</div>
|
|
<h3>9.4 systemd 常用命令</h3>
|
<div class="table-wrapper">
|
<table>
|
<tr><th>命令</th><th>作用</th></tr>
|
<tr><td>systemctl start 服务名</td><td>启动服务</td></tr>
|
<tr><td>systemctl stop 服务名</td><td>停止服务</td></tr>
|
<tr><td>systemctl restart 服务名</td><td>重启服务</td></tr>
|
<tr><td>systemctl status 服务名</td><td>查看服务状态</td></tr>
|
<tr><td>journalctl -u 服务名 -f</td><td>查看服务日志</td></tr>
|
</table>
|
</div>
|
</div>
|
|
<!-- 第十步:绑定域名 -->
|
<div class="section" id="step10">
|
<h2><span class="step-num">10</span> 绑定域名(可选进阶)</h2>
|
|
<p>用 IP 访问不太方便,也不专业。我们可以绑定一个域名。</p>
|
|
<h3>10.1 购买域名</h3>
|
<p>在华为云「域名注册」服务购买一个域名,例如 <span class="highlight">yourdomain.com</span>。</p>
|
|
<h3>10.2 配置 DNS 解析</h3>
|
<ol>
|
<li>进入「云解析服务 DNS」</li>
|
<li>找到你的域名,点击「解析」</li>
|
<li>添加记录:</li>
|
</ol>
|
<div class="table-wrapper">
|
<table>
|
<tr><th>主机记录</th><th>类型</th><th>值</th></tr>
|
<tr><td>grader(或 www)</td><td>A</td><td>你的服务器公网IP</td></tr>
|
</table>
|
</div>
|
<p>等待几分钟解析生效。</p>
|
|
<h3>10.3 修改 CORS 配置</h3>
|
<p>因为项目有 CORS 跨域限制,需要把你的域名加到允许列表中。创建环境变量配置:</p>
|
<div class="code-block">
|
<span class="cmd">cat</span> > /opt/score-query/.env << 'EOF'
|
CORS_ORIGINS="http://grader.yourdomain.com,https://grader.yourdomain.com,http://localhost:5174"
|
JWT_SECRET="这里改成一个随机的长字符串,比如 mashuang123456!@#"
|
EOF
|
</div>
|
|
<p>然后修改 systemd 服务加载这个环境变量:</p>
|
<div class="code-block">
|
<span class="cmd">sed</span> -i '/ExecStart/i EnvironmentFile=/opt/score-query/.env' /etc/systemd/system/score-backend.service
|
<span class="cmd">systemctl</span> daemon-reload
|
<span class="cmd">systemctl</span> restart score-backend
|
</div>
|
|
<h3>10.4 修改前端 vite 配置中的 allowedHosts</h3>
|
<p>编辑 <span class="highlight">/opt/score-query/frontend/vite.config.ts</span>,把你的域名加到 <span class="highlight">allowedHosts</span> 数组中,然后重新构建:</p>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query/frontend
|
<span class="cmd">npm</span> run build
|
<span class="cmd">systemctl</span> restart score-frontend
|
</div>
|
|
<h3>10.5 配置 Nginx 反向代理(推荐)</h3>
|
<p>用 Nginx 做反向代理,可以直接用 80 端口访问,不需要加端口号:</p>
|
|
<div class="code-block">
|
<span class="comment"># 安装 Nginx</span>
|
<span class="cmd">apt</span> install -y nginx
|
|
<span class="comment"># 创建配置文件</span>
|
<span class="cmd">cat</span> > /etc/nginx/sites-available/score-query << 'EOF'
|
server {
|
listen 80;
|
server_name grader.yourdomain.com;
|
|
# 前端静态文件(也可以继续用 vite preview,这里演示直接由 Nginx 托管)
|
location / {
|
proxy_pass http://127.0.0.1:5174;
|
proxy_set_header Host $host;
|
proxy_set_header X-Real-IP $remote_addr;
|
}
|
}
|
EOF
|
|
<span class="comment"># 启用站点</span>
|
<span class="cmd">ln</span> -s /etc/nginx/sites-available/score-query /etc/nginx/sites-enabled/
|
<span class="cmd">rm</span> /etc/nginx/sites-enabled/default
|
|
<span class="comment"># 测试配置</span>
|
<span class="cmd">nginx</span> -t
|
|
<span class="comment"># 重载 Nginx</span>
|
<span class="cmd">systemctl</span> reload nginx
|
</div>
|
|
<p>现在就可以用 <span class="highlight">http://grader.yourdomain.com</span> 直接访问了!</p>
|
|
<div class="tip">
|
如果需要 HTTPS,可以用 Let's Encrypt 免费证书,安装 certbot 后执行:<br>
|
<span class="highlight">apt install -y certbot python3-certbot-nginx && certbot --nginx</span>
|
</div>
|
</div>
|
|
<!-- FAQ -->
|
<div class="section" id="faq">
|
<h2><span class="step-num">❓</span> 常见问题 FAQ</h2>
|
|
<div class="faq-item">
|
<div class="faq-q">Q1: 浏览器访问不了怎么办?</div>
|
<div class="faq-a">
|
<p>按以下顺序排查:</p>
|
<ol>
|
<li>确认安全组是否开放了 5174 端口</li>
|
<li>确认服务是否在运行:<span class="highlight">systemctl status score-backend score-frontend</span></li>
|
<li>在服务器本地测试:<span class="highlight">curl http://127.0.0.1:5174</span>,能返回 HTML 说明服务正常</li>
|
<li>如果本地通、外网不通,基本是安全组或防火墙的问题</li>
|
<li>检查系统防火墙:<span class="highlight">ufw status</span>,如果开了就 <span class="highlight">ufw allow 5174</span></li>
|
</ol>
|
</div>
|
</div>
|
|
<div class="faq-item">
|
<div class="faq-q">Q2: 前端页面打开了,但登录/接口报错?</div>
|
<div class="faq-a">
|
<p>大概率是后端没启动或跨域配置问题:</p>
|
<ol>
|
<li>确认后端在运行:<span class="highlight">systemctl status score-backend</span></li>
|
<li>本地测试后端:<span class="highlight">curl http://127.0.0.1:8010/docs</span>,能看到 Swagger 文档说明正常</li>
|
<li>如果是跨域错误(浏览器 F12 控制台有 CORS 字样),检查 CORS_ORIGINS 配置是否包含你的访问地址</li>
|
</ol>
|
</div>
|
</div>
|
|
<div class="faq-item">
|
<div class="faq-q">Q3: SQLite 数据库文件在哪?怎么备份?</div>
|
<div class="faq-a">
|
<p>数据库文件在 <span class="highlight">/opt/score-query/backend/data/app.db</span>,直接复制这个文件就是备份。建议定期备份:</p>
|
<div class="code-block"><span class="cmd">cp</span> /opt/score-query/backend/data/app.db /opt/score-query/backend/data/app.db.bak.$(date +%Y%m%d)</div>
|
</div>
|
</div>
|
|
<div class="faq-item">
|
<div class="faq-q">Q4: 修改了代码怎么更新?</div>
|
<div class="faq-a">
|
<p>分两种情况:</p>
|
<ul>
|
<li><strong>改了前端代码:</strong>重新构建 → 重启前端
|
<div class="code-block"><span class="cmd">cd</span> /opt/score-query/frontend && npm run build && systemctl restart score-frontend</div>
|
</li>
|
<li><strong>改了后端代码:</strong>直接重启后端
|
<div class="code-block"><span class="cmd">systemctl</span> restart score-backend</div>
|
</li>
|
</ul>
|
</div>
|
</div>
|
|
<div class="faq-item">
|
<div class="faq-q">Q5: 服务器太卡了怎么办?</div>
|
<div class="faq-a">
|
<p>可以在华为云控制台「变更规格」升级配置。也可以优化:</p>
|
<ul>
|
<li>用 Nginx 直接托管前端静态文件,关掉 vite preview(节省内存)</li>
|
<li>开启 gzip 压缩</li>
|
<li>如果数据量大,可以考虑把 SQLite 换成 MySQL</li>
|
</ul>
|
</div>
|
</div>
|
|
<div class="faq-item">
|
<div class="faq-q">Q6: 忘记管理员密码怎么办?</div>
|
<div class="faq-a">
|
<p>重新运行 seed_admin.py 脚本会重置默认账号密码:</p>
|
<div class="code-block">
|
<span class="cmd">cd</span> /opt/score-query/backend
|
<span class="cmd">source</span> .venv/bin/activate
|
<span class="cmd">python</span> scripts/seed_admin.py
|
</div>
|
</div>
|
</div>
|
|
<div class="faq-item">
|
<div class="faq-q">Q7: 用脚本启动还是 systemd 启动?</div>
|
<div class="faq-a">
|
<p>推荐用 <strong>systemd</strong>,因为:</p>
|
<ul>
|
<li>开机自动启动,服务器重启不怕</li>
|
<li>进程崩溃自动重启</li>
|
<li>统一的日志管理(journalctl)</li>
|
<li>更专业、更稳定</li>
|
</ul>
|
<p>脚本方式适合临时调试使用。</p>
|
</div>
|
</div>
|
</div>
|
|
<div class="footer">
|
<p>📚 考试阅卷系统华为云部署教程 | 祝你部署顺利!🎉</p>
|
</div>
|
</div>
|
</body>
|
</html>
|