feat: 导入 CapacityReport 初始源码

This commit is contained in:
Nixevol
2026-09-24 06:16:41 +08:00
commit 89cca70430
134 changed files with 38799 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
# Docker 编排:一键构建并运行整套系统(应用 + MySQL + sftpgo)
`docker-compose.yml` 把整套系统编排为三个互相连通的服务,开箱即用。
| 服务 | 容器名 | 端口(宿主:容器) | 说明 |
| --- | --- | --- | --- |
| capacity-app | capacity-report-app | 9081:9081 | 应用(前端 + 后端),数据落在 `/data` 卷 |
| capacity-mysql | capacity-mysql | 13306:3306 | MySQL 8,自动建 `CapacityReport` 与 `celldata` 两库 |
| sftpgo | capacity-sftpgo | 2022:2022(SFTP)、18080:8080(Web) | 文件服务,供应用拉取数据 |
> 应用镜像 `capacity-report-app:latest` 由本仓库根的 `Dockerfile` 构建;MySQL、sftpgo 为公共镜像。
---
## 前置要求
- 已安装 Docker(含 Docker Compose v2,命令为 `docker compose`)。
- 构建应用镜像需要先产出前端 `frontend/dist`(下方步骤会做)。
- 联网构建需要能拉取 `python:3.13.11-slim`、`mysql:8.0.44`、`drakkan/sftpgo:v2.7.1`;
内网离线见「方式 B」。
---
## 方式 A:联网机器,一键构建并启动
```bash
# 1) 构建前端(产出 frontend/dist,镜像会拷贝它)
cd ../frontend
npm ci && npm run build
cd ../packaging
# 2) 构建应用镜像并启动整套系统
docker compose build
docker compose up -d
# 3) 查看状态 / 日志
docker compose ps
docker compose logs -f capacity-app
```
启动后访问 `http://localhost:9081`(默认登录 `root` / `Capacity`)。
> 也可以用脚本一步构建并导出离线包:在仓库根执行 `python scripts/build_docker.py`,
> 产物在 `dist/docker/`(含 `capacity-report-app-latest.tar` + `docker-compose.yml` + `mysql/` + `sftpgo/` + 默认脚本)。
---
## 方式 B:内网离线部署(无外网)
在有网机器准备好以下文件,拷贝到内网机器:
1. **基础/公共镜像**(python、mysql、sftpgo)——可用离线镜像目录方案:
- 有网机器:`docker pull` 三个镜像后 `docker save -o xxx.tar`,连同导入脚本放一个目录。
- 内网机器:在该目录执行 `python load_images.py` 逐个 `docker load -i ./xxx.tar`。
- (示例:本项目已在 `F:\CR\env\images` 准备了 `mysql_8.0.44.tar`、`sftpgo_v2.7.1.tar`、`python_3.13.11-slim.tar` 与 `load_images.py`。)
2. **应用镜像**:在有网机器 `docker compose build` 后
`docker save capacity-report-app:latest -o capacity-report-app.tar`,拷到内网 `docker load -i ./capacity-report-app.tar`。
- 或直接用 `python scripts/build_docker.py` 生成 `dist/docker/` 整包带走。
3. **编排文件**:本目录的 `docker-compose.yml` + `mysql/` + `sftpgo/`。
内网机器导入镜像后,在编排文件目录执行:
```bash
docker compose up -d # 镜像已在本地,不会联网拉取
```
> 若内网机器**不便构建**应用镜像,务必走「先在有网机器 build 出 `capacity-report-app:latest` 再 save」,
> 这样内网仅需 `docker load` + `docker compose up -d`,无需 python 基础镜像与构建。
---
## 默认账号 / 连接
| 项 | 值 |
| --- | --- |
| 应用登录 | `root` / `Capacity` |
| MySQL | `root` / `gmcc123`,库 `CapacityReport`、`celldata`(容器内主机名 `capacity-mysql`) |
| SFTP 用户 | `capacity` / `capacity123`(home `/srv/sftpgo/data/capacity`) |
| sftpgo Web | `admin` / `gmcc123`(http://localhost:18080) |
应用**首次启动**时,`entrypoint` 会按 `docker-compose.yml` 里的环境变量把 `Configure.json` 的
「数据库 / SFTP 连接」自动改写为容器服务(主机名用 `capacity-mysql` / `sftpgo`);
之后你在界面里的修改不会被覆盖。要改默认密码等,编辑 compose 的环境变量与 `sftpgo/sftpgo-init.json`。
---
## 上传数据
通过 SFTP(`localhost:2022`,`capacity`/`capacity123`)或 sftpgo Web(`:18080`)上传到 `capacity` 用户目录:
- 容量数据 → `/CapacityReportData`(对应 `RemoteData.remote_dir`,结构 `4G/ 5G/ RJ/`)。
- CellData → `/网优日常优化数据文档/...`(对应 `CellData.scan_paths`)。
> 用仓库 `test/make_test_data.py` 生成的小样本可直接传上来做快速测试。
> 上传后在「数据处理」页执行处理;首次会自动建表(`db_init/` 前置检查)。
---
## 数据卷(持久化)
| 卷 | 内容 |
| --- | --- |
| `capacity-data` | 应用 `/data`:Configure.json、ReportScript.sql、CellData.sql、db_init、历史、缓存 |
| `capacity-mysql-data` | MySQL 数据 |
| `sftpgo-config` / `sftpgo-data` | sftpgo 状态与上传的文件 |
---
## 常用运维
```bash
docker compose ps # 状态
docker compose logs -f capacity-app # 应用日志
docker compose restart capacity-app # 重启应用
docker compose down # 停止(保留数据卷)
docker compose down -v # 停止并删除数据卷(彻底重置,慎用)
# 改了代码 / 前端后更新应用:
cd ../frontend && npm run build && cd ../packaging
docker compose build capacity-app && docker compose up -d capacity-app
```
> 重置应用侧连接:`Configure.json` 仅在首次播种时按环境变量初始化;如需重新按 env 生成,
> 删除 `capacity-data` 卷(`docker volume rm packaging_capacity-data`)后重新 `up -d`。
+99
View File
@@ -0,0 +1,99 @@
# -*- mode: python ; coding: utf-8 -*-
import os
from pathlib import Path
project_root = Path.cwd()
onefile = os.environ.get("CAPAREPORT_ONEFILE") == "1"
hiddenimports = [
"uvicorn.logging",
"uvicorn.loops.auto",
"uvicorn.loops.asyncio",
"uvicorn.protocols.http.auto",
"uvicorn.protocols.http.h11_impl",
"uvicorn.protocols.websockets.auto",
"uvicorn.protocols.websockets.websockets_impl",
"uvicorn.lifespan.on",
"pymysql",
"paramiko",
"openpyxl",
"chardet",
]
excludes = [
"pytest",
"pandas.tests",
"numpy.tests",
"openpyxl.tests",
"matplotlib",
"IPython",
"notebook",
"tkinter",
]
a = Analysis(
[str(project_root / "app" / "main.py")],
pathex=[str(project_root)],
binaries=[],
datas=[],
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=excludes,
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
if onefile:
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
exclude_binaries=False,
name="capareport-server",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
else:
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="capareport-server",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name="capareport-server",
)
@@ -0,0 +1,67 @@
services:
web:
image: capacityrepost-web:baaea9a
container_name: capacityrepost-web
restart: unless-stopped
ports:
- "19082:9081"
volumes:
- capacityrepost-web-data:/data
environment:
TZ: Asia/Shanghai
PYTHONUNBUFFERED: "1"
DB_HOST: capacityrepost-mysql
DB_PORT: "3306"
DB_USER: root
DB_PASSWD: gmcc123
MAIN_DB_NAME: CapacityReport
CELLDATA_DB_NAME: celldata
depends_on:
mysql:
condition: service_healthy
networks:
- capacityrepost-network
mysql:
image: capacity-mysql:8.0.44
container_name: capacityrepost-mysql
restart: unless-stopped
expose:
- "3306"
volumes:
- capacityrepost-mysql-data:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d:ro
- ./mysql/init:/docker-entrypoint-initdb.d:ro
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: gmcc123
MYSQL_DATABASE: CapacityReport
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --local-infile=ON
- --secure-file-priv=
- --max_allowed_packet=256M
- --innodb_buffer_pool_size=512M
- --innodb_log_file_size=128M
- --innodb_flush_log_at_trx_commit=2
- --sync_binlog=0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pgmcc123"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
networks:
- capacityrepost-network
networks:
capacityrepost-network:
name: capacityrepost-network
driver: bridge
volumes:
capacityrepost-web-data:
name: capacityrepost-web-data
capacityrepost-mysql-data:
name: capacityrepost-mysql-data
+105
View File
@@ -0,0 +1,105 @@
# 一键编排:CapacityReport 应用 + MySQL + sftpgo
# 构建前先在仓库根构建前端:cd frontend && npm ci && npm run build(产出 frontend/dist)
# 然后在本目录:docker compose build && docker compose up -d
services:
capacity-app:
build:
context: ..
dockerfile: Dockerfile
image: capacity-report-app:latest
container_name: capacity-report-app
restart: unless-stopped
ports:
- "9081:9081"
volumes:
- capacity-data:/data
environment:
- TZ=Asia/Shanghai
- PYTHONUNBUFFERED=1
# 首次启动按以下变量初始化 Configure.json 的连接(用户后续在界面改动不被覆盖)
- DB_HOST=capacity-mysql
- DB_PORT=3306
- DB_USER=root
- DB_PASSWD=gmcc123
- MAIN_DB_NAME=CapacityReport
- CELLDATA_DB_NAME=celldata
- SFTP_HOST=sftpgo
- SFTP_PORT=2022
- SFTP_USER=capacity
- SFTP_PASSWD=capacity123
depends_on:
capacity-mysql:
condition: service_healthy
sftpgo:
condition: service_started
networks:
- capacity-network
capacity-mysql:
image: mysql:8.0.44
container_name: capacity-mysql
restart: unless-stopped
ports:
- "13306:3306"
volumes:
- capacity-mysql-data:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d:ro
- ./mysql/init:/docker-entrypoint-initdb.d:ro
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=gmcc123
- MYSQL_DATABASE=CapacityReport
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --local-infile=ON
- --secure-file-priv=
- --max_allowed_packet=256M
- --innodb_buffer_pool_size=512M
- --innodb_log_file_size=128M
- --innodb_flush_log_at_trx_commit=2
- --sync_binlog=0
networks:
- capacity-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pgmcc123"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
sftpgo:
image: drakkan/sftpgo:v2.7.1
container_name: capacity-sftpgo
restart: unless-stopped
ports:
- "2022:2022" # SFTP(上传容量/CellData 数据)
- "18080:8080" # Web 管理后台
environment:
- TZ=Asia/Shanghai
# Web 后台管理员
- SFTPGO_DEFAULT_ADMIN_USERNAME=admin
- SFTPGO_DEFAULT_ADMIN_PASSWORD=gmcc123
# 启动时导入 SFTP 用户(capacity/capacity123);MODE=1 保留文件、每次启动确保用户存在
- SFTPGO_LOADDATA_FROM=/sftpgo-init.json
- SFTPGO_LOADDATA_MODE=1
volumes:
- sftpgo-config:/var/lib/sftpgo
- sftpgo-data:/srv/sftpgo/data
- ./sftpgo/sftpgo-init.json:/sftpgo-init.json:ro
networks:
- capacity-network
networks:
capacity-network:
driver: bridge
volumes:
capacity-mysql-data:
driver: local
capacity-data:
driver: local
sftpgo-config:
driver: local
sftpgo-data:
driver: local
+29
View File
@@ -0,0 +1,29 @@
# MySQL settings for CapacityReport
[mysqld]
# Charset
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# LOAD DATA LOCAL INFILE
local_infile = ON
secure_file_priv = ""
# Performance
max_allowed_packet = 256M
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M
innodb_flush_log_at_trx_commit = 2
sync_binlog = 0
# Connections
max_connections = 200
wait_timeout = 28800
interactive_timeout = 28800
[mysql]
local_infile = ON
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
+14
View File
@@ -0,0 +1,14 @@
-- MySQL initialization for CapacityReport.
-- This file runs only when the MySQL data directory is empty.
-- 建两个库:CapacityReport(主仓库/容量结果)与 celldata(CellData 小区/扇区)。
-- 仅建库;具体表由应用前置检查(app/db_init.py + db_init/)按需创建。
CREATE DATABASE IF NOT EXISTS CapacityReport
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS celldata
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
SELECT 'CapacityReport & celldata databases initialized with utf8mb4 charset' AS status;
+13
View File
@@ -0,0 +1,13 @@
{
"users": [
{
"username": "capacity",
"password": "capacity123",
"status": 1,
"home_dir": "/srv/sftpgo/data/capacity",
"permissions": {
"/": ["*"]
}
}
]
}