Files

191 lines
9.7 KiB
Markdown
Raw Permalink Normal View History

# 03 · 架构设计
> 用户端(uni-app 多端) + 管理后台(Web) + 腾讯云 VPS 后端(Go·域名·certbot HTTPS) + 本地优先同步。
## 一、整体架构图
```
用户端(unibest / uni-app · Vue3) 后端(腾讯云 VPS · Docker · HTTPS)
┌─────────────────────────────┐ ┌────────────────────────────┐
│ ① 每日打卡 │ │ 域名 + Nginx + certbot │
│ ② 目标进度 & 净资产曲线 │ HTTPS │ Go + Gin + GORM │
│ ③ FIRE 测算器 │ ─REST→ │ ├─ JWT 单用户鉴权 │
│ ④ 持仓 & 分红跟踪 │ ←同步─ │ ├─ API: 打卡/快照/持仓/设置 │
│ 本地缓存(离线优先) │ │ └─ SQLite(纯Go驱动·可备份) │
└─────────────────────────────┘ └────────────────────────────┘
│ 由同一个 Go 进程托管 ▲
├─ 浏览器 → 网页 / PWA │ html/template
├─ 云打包 → Android (.apk/.aab) │ 渲染(共用DB)
└─ 云打包 → iOS (.ipa,需苹果账号) ┌─────────────────────────┐
│ 管理后台(Web · 电脑端) │
│ Gin html/template + │
│ HTMX + Alpine + Tabler │
│ 无构建·数据增删改查/报表 │
└─────────────────────────┘
```
## 二、前端结构
- **页面**:四大模块各一主页面 + 设置页,普通 vue 页面(不碰 nvue)。
- **图表**:统一用 qiun-data-charts,图表页避免 nvue,数据做抽稀。
- **状态/数据层**:本地优先,所有读写先走"存储抽象层",再异步同步到后端。
### 存储抽象层(关键设计)
各端存储能力不同,必须封装统一接口,内部用条件编译分端实现:
| 端 | 底层实现 | 注意 |
|---|---|---|
| H5 | IndexedDB(大数据)/ localStorage | localStorage ~5MB 上限 |
| App | uni.storage / 原生 SQLite 插件 | 历史快照/流水量大时用 SQLite |
| 小程序 | uni.storage | 总量约 10MB,存流水易爆 |
```
interface Store {
get(key), set(key, val), query(table, filter), bulkSave(...)
}
// #ifdef H5 → IndexedDB 实现
// #ifdef APP-PLUS → SQLite/uni.storage 实现
// #ifdef MP → uni.storage 实现
```
## 三、后端结构(腾讯云 VPS · Go)
- **技术栈**:**Go + Gin(Web)+ GORM(ORM)+ SQLite**(纯 Go 驱动 `modernc.org/sqlite`,免 CGO,编译/部署最省心)+ **JWT** 鉴权。
- **部署**:编译为**单个静态二进制**,Docker 化部署到腾讯云 VPS;前置 **Nginx 反向代理**,由 **certbot(Let's Encrypt)** 签发并自动续期 HTTPS。
- **域名**:使用用户已有域名,解析到 VPS 公网 IP。
- **数据表(初版)**:`checkins`(打卡)、`snapshots`(资产快照)、`holdings`(持仓)、`settings`(参数)、`users`(单用户)。
- **接口**:标准 REST(增删改查 + 拉取增量),全程 HTTPS;用户端与管理后台**共用同一套 API**。
- **备份**:SQLite 单文件,定时备份到对象存储/本地。
> 为什么用 Go 而非 Node:单二进制、内存占用小、Docker 镜像可压到几 MB,**更适合常驻的小 VPS 自托管**;前后端经 REST/JSON 解耦,后端语言对 uni-app 前端无影响。
## 三-B、管理后台(Web 管理端 · 无构建)
- **定位**:电脑端网页,给本人用来**增删改查数据、查看报表、调整参数**(如目标本金、提取率、收益率假设)。
- **技术(无构建)**:**Gin html/template 服务端渲染 + HTMX(局部刷新)+ Alpine.js(小交互)+ Tabler 后台模板(Bootstrap5,自带登录/侧边栏/仪表盘/表格/表单皮肤)+ Chart.js(图表)**。全部走 `<script>`/`<link>` 引入,**不需要 Node 工具链、不需要 build**。
- **骨架**:直接采用 **Tabler** 现成页面(登录、侧边栏布局、仪表盘卡片、表格表单),相当于 admin 端的 unibest。
- **托管**:由**同一个 Go 进程**直接渲染并提供(路由如 `/admin/*`);模板与静态资源用 `//go:embed` 打进二进制 → 部署仍是单文件。
- **数据**:与用户端**共用同一个 Go 后端与 SQLite**(HTMX 路由返回 HTML 片段,不另起服务)。
- **鉴权**:JWT/会话,单用户。
- **范围**:单人自管,不做多用户/复杂权限。
> 为什么不用 Vue3+Element Plus:那需要独立 build 流水线和 `admin/` 子项目。管理端本质是 CRUD+报表,用 HTMX 超媒体方案**零构建、Go 一把托管**,仓库更简单、部署仍单二进制。
## 四、同步策略(刻意从简)
单用户场景不追求复杂冲突合并:
1. **本地优先**:所有操作先写本地、立即可用、离线可用。
2. **时间戳 + 后写覆盖**:每条记录带 `updatedAt`,同步时以较新者为准。
3. **后端为历史数据权威源**:本地仅作缓存,换设备从后端拉全量。
> 不使用 uniCloud(绑定 DCloud 云,非自托管),坚持自建 VPS 后端。
## 五、各端后端连通性注意
> 已有「域名 + certbot 正经 HTTPS」后,App / H5 的后端要求**全部满足**;小程序只剩"域名备案 + 配置合法域名白名单"一步。
| 端 | 对后端要求 | 当前状态(VPS + 域名 + certbot) |
|---|---|---|
| H5 | 需处理 CORS | ✅ 同域部署或配置 CORS 即可 |
| App | 正经 HTTPS | ✅ certbot 证书满足,不再有自签被拒问题 |
| 小程序 | 强制 HTTPS + **备案域名** + 合法域名白名单 | ⚠️ HTTPS 已满足;仅需**域名 ICP 备案**(腾讯云可办)+ 小程序后台配 request 合法域名 |
## 六、HTTPS 与安全(腾讯云 VPS)
- **HTTPS**:域名解析到 VPS → Nginx 占用 80/443 → `certbot --nginx` 签发证书 → 自动续期(`certbot renew` cron/timer)。
- **安全加固(后端已暴露公网,务必做)**:
- 仅开放必要端口(80/443/SSH),其余用腾讯云安全组 + 防火墙关闭;
- 强 Token/密码鉴权,接口加速率限制,**强制 HTTPS(HTTP 301 跳转)**;
- SSH 改密钥登录、装 fail2ban;
- SQLite 定时备份;财务数据在公网,敏感字段可考虑加密存储。
## 七、部署形态
- **开发期**:WSL 内 `pnpm dev:h5` 跑 unibest 前端实时预览;`go run` 起 Go 后端(同时托管管理后台 `/admin`,改模板刷新即见,无需 build)。
- **自用期**:Go 后端二进制 Docker 跑在**腾讯云 VPS**(Nginx + certbot HTTPS),管理后台由 Go 进程自身托管;用户端 H5 静态文件由 Nginx 同域托管;手机装 PWA。
- **App 期**:HBuilderX 云打包出 Android/iOS。
## 八、WSL2 开发环境配置
> 在 WSL2 Ubuntu 中开发,Windows 浏览器中预览——这是本项目的标准开发模式。
### 原理
WSL2 会自动将内部端口转发到 Windows 的 `localhost`,所以 WSL2 里启动的 dev server,Windows 浏览器直接用 `http://localhost:端口` 访问,热更新也正常生效。
### ⚠️ app/ 必须放在 WSL2 原生文件系统
pnpm 在 `/mnt/d/`(Windows NTFS 挂载)上做原子重命名时会报 `EACCES` 权限错误,导致 `pnpm install` 失败。
**正确路径**:`~/fire_goal/app/`(WSL2 原生 ext4)
```bash
# 开发时进入 WSL2 原生目录
cd ~/fire_goal/app
pnpm install # ✅ 无权限问题
pnpm dev:h5
```
Windows 侧(HBuilderX 云打包)通过以下路径访问:
```
\\wsl$\Ubuntu\home\<用户名>\fire_goal\app
```
> docs/ 和 research/ 仍在 `/mnt/d/opc_project/fire_goal/`,用 git 管理;app/ 在 WSL2 原生 fs 单独开发。
### 必须的 Vite 配置
Vite 默认只监听 `127.0.0.1`(WSL2 内部),需改为监听所有网卡,否则 Windows 浏览器无法访问。
在 `app/vite.config.ts` 中添加:
```ts
server: {
host: '0.0.0.0', // 允许 WSL2 外部(Windows)访问
port: 5173,
},
```
或临时用命令行参数(不改配置时):
```bash
pnpm dev:h5 --host
```
### 访问方式
| 场景 | 地址 |
|---|---|
| Windows 浏览器调试 | `http://localhost:5173` |
| 手机扫码调试(同 WiFi) | `http://<Windows局域网IP>:5173`(`ipconfig` 查 WiFi 的 IPv4) |
> 注意:手机调试时用的是 **Windows 的局域网 IP**,不是 WSL2 的内部 IP(`172.x.x.x`)。
### 开发流程示意
```
WSL2 Ubuntu
├─ pnpm dev:h5 --host → Vite 监听 0.0.0.0:5173
└─ go run . → Go 后端监听 0.0.0.0:8080
↓ WSL2 自动转发
Windows
├─ Chrome localhost:5173 ✅ 前端热更新正常
└─ 接口请求 localhost:8080 ✅ 后端 API 正常
```
## 九、代码仓库结构(建议)
```
fire_goal/
├─ app/ # unibest 用户端(uni-app,需 build 出 H5/App)
├─ server/ # Go 后端(Gin + GORM + SQLite)
│ ├─ templates/ # 管理后台 html/template(无构建)
│ └─ static/ # htmx/alpine/tabler/chart.js 等,//go:embed 进二进制
├─ deploy/ # Dockerfile / docker-compose / Nginx / certbot 脚本
└─ docs/ # 本文档目录
```
> 管理后台不再是独立子项目,已并入 `server/`(模板 + 静态资源),与后端同进程、同部署。