Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.py]
indent_size = 4
max_line_length = 120

[*.{js,vue,json,yml,yaml,css,html}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dist-ssr/

# ===== Node =====
node_modules/
.pnpm-store/
.vite/
*.local

Expand All @@ -61,7 +62,12 @@ app-client/**/.gradle/
# ===== OS / 编辑器 =====
.DS_Store
.idea/
.vscode/
# 团队共享的 VS Code 调试/任务/插件配置入库,其余本地设置忽略
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
!.vscode/launch.json
!.vscode/tasks.json
*.log
.playwright-mcp/

Expand Down
Binary file not shown.
15 changes: 15 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.debugpy",
"charliermarsh.ruff",
"Vue.volar",
"ms-azuretools.vscode-docker",
"redhat.vscode-yaml",
"humao.rest-client",
"EditorConfig.EditorConfig",
"eamodio.gitlens"
],
"unwantedRecommendations": []
}
56 changes: 56 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "后端:FastAPI(调试)",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": ["app.main:app", "--reload", "--host", "127.0.0.1", "--port", "8000"],
"cwd": "${workspaceFolder}/backend",
"env": {
"CLOUD_DEVICE_BACKEND": "simulator",
"CLOUD_DEBUG": "true"
},
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "后端:pytest(全部)",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["tests", "-q"],
"cwd": "${workspaceFolder}/backend",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "后端:pytest(当前文件)",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["-q", "${file}"],
"cwd": "${workspaceFolder}/backend",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "前端:Vite Dev",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "dev", "--", "--host", "127.0.0.1"],
"cwd": "${workspaceFolder}/frontend",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
],
"compounds": [
{
"name": "全栈:后端 + 前端",
"configurations": ["后端:FastAPI(调试)", "前端:Vite Dev"],
"stopAll": true
}
]
}
49 changes: 49 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
// ===== Python / 后端 =====
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestArgs": ["tests"],
"python.testing.cwd": "${workspaceFolder}/backend",
"python.analysis.extraPaths": ["${workspaceFolder}/backend"],
"python.analysis.exclude": ["**/.venv/**", "**/.venv-local/**", "**/_attic/**"],
"ruff.configuration": "${workspaceFolder}/backend/pyproject.toml",
"ruff.lineLength": 120,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": false
},

// ===== 前端 =====
"[vue]": {
"editor.formatOnSave": false
},
"[javascript]": {
"editor.formatOnSave": false
},

// ===== 资源管理器 / 搜索噪音 =====
"files.exclude": {
"**/__pycache__": true,
"**/*.pyc": true,
"**/.ruff_cache": true,
"**/.pytest_cache": true,
"**/_test_*.db": true
},
"search.exclude": {
"**/node_modules": true,
"**/.venv": true,
"**/.venv-local": true,
"**/dist": true,
"**/_attic": true,
"**/.uploads": true,
"**/pnpm-lock.yaml": true
},
"files.watcherExclude": {
"**/node_modules/**": true,
"**/.venv*/**": true,
"**/dist/**": true
},

// ===== 终端 =====
"terminal.integrated.cwd": "${workspaceFolder}"
}
56 changes: 56 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "环境:一键安装依赖",
"type": "shell",
"command": "bash scripts/dev-setup.sh",
"windows": {
"command": "powershell -ExecutionPolicy Bypass -File scripts/dev-setup.ps1"
},
"problemMatcher": [],
"presentation": { "reveal": "always", "panel": "dedicated" }
},
{
"label": "启动:后端 + 前端",
"type": "shell",
"command": "./start-all.sh",
"windows": {
"command": "start-all.bat"
},
"problemMatcher": [],
"presentation": { "reveal": "always", "panel": "dedicated" }
},
{
"label": "测试:后端 pytest",
"type": "shell",
"command": "${workspaceFolder}/backend/.venv/bin/python",
"args": ["-m", "pytest", "tests", "-q"],
"options": { "cwd": "${workspaceFolder}/backend" },
"windows": {
"command": "${workspaceFolder}\\backend\\.venv\\Scripts\\python.exe",
"args": ["-m", "pytest", "tests", "-q"]
},
"group": { "kind": "test", "isDefault": true },
"problemMatcher": []
},
{
"label": "检查:ruff + compileall",
"type": "shell",
"command": "${workspaceFolder}/backend/.venv/bin/python",
"args": ["-m", "ruff", "check", "backend/app", "tools", "--config", "backend/pyproject.toml"],
"windows": {
"command": "${workspaceFolder}\\backend\\.venv\\Scripts\\python.exe",
"args": ["-m", "ruff", "check", "backend/app", "tools", "--config", "backend/pyproject.toml"]
},
"problemMatcher": []
},
{
"label": "构建:前端",
"type": "shell",
"command": "pnpm run build",
"options": { "cwd": "${workspaceFolder}/frontend" },
"problemMatcher": []
}
]
}
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
# 1) 后端
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install fastapi "uvicorn[standard]" "sqlalchemy[asyncio]" aiosqlite pydantic pydantic-settings PyJWT python-multipart httpx
pip install -r requirements-dev.txt # 版本已锁定,Python 3.11+
uvicorn app.main:app --reload # http://localhost:8000/docs

# 2) 前端(另开终端;用 pnpm,仓库锁文件是 pnpm-lock.yaml。Node 需 ≥ 22.13)
Expand All @@ -42,6 +42,11 @@ cd frontend && corepack enable && pnpm install && pnpm run dev # http://localhos
cd backend && source .venv/bin/activate && python -m tests.smoke
```

不想手动装环境?macOS / Linux 直接 `bash scripts/dev-setup.sh`,再 `bash start-all.sh`
(后端 :8000、前端 :5173 一起起,Ctrl+C 全停);Windows 用
`powershell -ExecutionPolicy Bypass -File scripts\dev-setup.ps1` + `start-all.bat`。
两个脚本都是幂等的,会自动建 `backend/.venv`、装依赖、从 `.env.example` 生成 `backend/.env`。

`tests/smoke.py` 会自动跑完登录→建 10 台→抽查指纹→预览→单机操控→批量同步→分组→脚本回放,全绿即主流程跑通。

> 首次部署(非临时演示)请先初始化 4 个角色的正式账号:
Expand All @@ -53,6 +58,17 @@ cd backend && source .venv/bin/activate && python -m tests.smoke
> **非破坏性**的 `python acceptance_live.py`:不建机不删机、只用现有在跑的设备核验,
> 跑完还原设备原来打开的网页;并会检出「有画面但设备其实已失联」的伪装情况。

## IDE 直接测试(VS Code,配置已入库)

仓库自带 `.vscode/` 共享配置,克隆后打开即可:

1. **插件**:首次打开会提示安装推荐插件(Python / Pylance / debugpy / Ruff / Vue Volar / Docker / YAML / REST Client / EditorConfig / GitLens),点「全部安装」即可。
2. **Python 解释器**:选 `backend/.venv/bin/python`(Windows 为 `backend\.venv\Scripts\python.exe`),测试面板会自动发现 `backend/tests`。
3. **一键任务**(`Ctrl+Shift+B` 或「终端 → 运行任务」):`环境:一键安装依赖` → `启动:后端 + 前端` → `测试:后端 pytest` / `检查:ruff + compileall` / `构建:前端`。
4. **F5 调试**:`后端:FastAPI(调试)`、`后端:pytest(当前文件)`、`前端:Vite Dev`,以及复合配置 `全栈:后端 + 前端`。

已实测(Python 3.11 + pnpm 10):`pytest tests -q` 115 项全过、`ruff check` 无告警、`pnpm run build` 成功。

## 一条命令起整套(Docker Compose)

```bash
Expand Down
24 changes: 12 additions & 12 deletions backend/app/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,25 +780,25 @@ def _svg_android_home(name: str, fp: dict) -> str:
week = "一二三四五六日"[now.weekday()]
date_text = f"{now.month} 月 {now.day} 日 · 周{week}"
parts = [
f'<defs><linearGradient id="awall" x1="0" y1="0" x2="1" y2="1">'
f'<stop offset="0%" stop-color="#123a6b"/>'
f'<stop offset="45%" stop-color="#0b2545"/>'
f'<stop offset="100%" stop-color="#061224"/></linearGradient>'
f'<radialGradient id="agl" cx="0.85" cy="0.12" r="0.5">'
f'<stop offset="0%" stop-color="#22d3ee" stop-opacity="0.45"/>'
f'<stop offset="100%" stop-color="#22d3ee" stop-opacity="0"/></radialGradient></defs>',
'<defs><linearGradient id="awall" x1="0" y1="0" x2="1" y2="1">'
'<stop offset="0%" stop-color="#123a6b"/>'
'<stop offset="45%" stop-color="#0b2545"/>'
'<stop offset="100%" stop-color="#061224"/></linearGradient>'
'<radialGradient id="agl" cx="0.85" cy="0.12" r="0.5">'
'<stop offset="0%" stop-color="#22d3ee" stop-opacity="0.45"/>'
'<stop offset="100%" stop-color="#22d3ee" stop-opacity="0"/></radialGradient></defs>',
f'<rect x="0" y="0" width="{W}" height="{H}" fill="url(#awall)"/>',
f'<rect x="0" y="0" width="{W}" height="{H}" fill="url(#agl)"/>',
'<circle cx="60" cy="690" r="150" fill="#3d7bff" opacity="0.10"/>',
'<circle cx="400" cy="520" r="110" fill="#22d3ee" opacity="0.08"/>',
_a_statusbar(),
# 日期与天气组件
f'<text x="24" y="70" font-size="26" fill="#f2f7ff" font-weight="600">{date_text}</text>',
f'<text x="24" y="96" font-size="13" fill="#9fb6d9">多云 · 24° · 空气质量 优</text>',
'<text x="24" y="96" font-size="13" fill="#9fb6d9">多云 · 24° · 空气质量 优</text>',
# 演示角标
f'<g><rect x="330" y="52" width="90" height="22" rx="11" fill="#f5a524" opacity="0.18"/>'
f'<rect x="330" y="52" width="90" height="22" rx="11" fill="none" stroke="#f5a524" stroke-opacity="0.55"/>'
f'<text x="375" y="67" font-size="11" fill="#ffcf7a" text-anchor="middle">演示模式 · SIM</text></g>',
'<g><rect x="330" y="52" width="90" height="22" rx="11" fill="#f5a524" opacity="0.18"/>'
'<rect x="330" y="52" width="90" height="22" rx="11" fill="none" stroke="#f5a524" stroke-opacity="0.55"/>'
'<text x="375" y="67" font-size="11" fill="#ffcf7a" text-anchor="middle">演示模式 · SIM</text></g>',
]

# 4 列 × 5 行图标网格
Expand All @@ -824,7 +824,7 @@ def _svg_android_home(name: str, fp: dict) -> str:
for x, glyph in zip(dock_x, _A_DOCK):
parts.append(_a_icon(x, dock_y + 10, 56, "#1f2b45" if glyph != "browser" else "#f8fafc", glyph))
# 手势条
parts.append(f'<rect x="150" y="800" width="140" height="5" rx="2.5" fill="#eef4ff" opacity="0.8"/>')
parts.append('<rect x="150" y="800" width="140" height="5" rx="2.5" fill="#eef4ff" opacity="0.8"/>')
return (
f'<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}" '
f'font-family="Roboto,Noto Sans CJK SC,-apple-system,Helvetica,Arial,sans-serif">'
Expand Down
1 change: 0 additions & 1 deletion backend/app/routers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from sqlalchemy.ext.asyncio import AsyncSession

from .. import auth
from ..config import settings
from ..database import get_db
from ..models import User
from ..rbac import ROLE_LEVELS, ROLE_LABELS, record_audit, require_role
Expand Down
2 changes: 1 addition & 1 deletion backend/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 开发/CI 依赖(不随运行时镜像安装)
-r requirements.txt
pytest==9.2.0
pytest==9.1.1
ruff==0.17.3
24 changes: 12 additions & 12 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# X86 云手机平台 —— 后端运行时依赖(精确锁定,2026-11-04 实测安装成功)
# X86 云手机平台 —— 后端运行时依赖(精确锁定,2026-11-04 在 Python 3.11 实测安装并跑通 115 项测试)
# 说明:密码哈希使用标准库 hashlib.pbkdf2,避免 bcrypt 原生编译依赖。
# 升级流程:改这里 → 在干净 venv 里 pip install -r requirements.txt → 跑 pytest tests -q。
fastapi==0.128.8
uvicorn[standard]==0.54.0
sqlalchemy[asyncio]==2.1.8
aiosqlite==0.22.1 # dev/测试默认库(SQLite)
asyncpg==0.32.0 # 生产库(PostgreSQL),按 DATABASE_URL 启用
pydantic==2.12.5
pydantic-settings==2.16.2
PyJWT==2.15.1
python-multipart==0.0.33
fastapi==0.141.1
uvicorn[standard]==0.44.0
sqlalchemy[asyncio]==2.0.51
aiosqlite==0.21.0 # dev/测试默认库(SQLite)
asyncpg==0.30.0 # 生产库(PostgreSQL),按 DATABASE_URL 启用
pydantic==2.13.4
pydantic-settings==2.14.2
PyJWT==2.13.0
python-multipart==0.0.21
httpx==0.28.1
python-dotenv==1.2.4 # tools/seed_rbac.py 读取 .env
python-dotenv==1.2.2 # tools/seed_rbac.py 读取 .env
# 可选:真实 Redroid 后端需要 Docker SDK(Simulator 模式无需安装)
# 注:该版本按 7.x 线锁定,Windows 上不安装(marker 排除),Linux 侧安装后请回归真机流程。
# 注:Windows 上不安装(marker 排除),Linux 侧安装后请回归真机流程。
docker==7.1.0 ; platform_system == "Linux"
6 changes: 6 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.9"
},
"pnpm": {
"onlyBuiltDependencies": [
"esbuild",
"vue-demi"
]
}
}
Loading
Loading