From 4cb6c4eab731927688f752496945edf9eb9facf6 Mon Sep 17 00:00:00 2001 From: tech Date: Thu, 7 Aug 2025 16:57:41 +0800 Subject: [PATCH] refactor: restructure project to modern Python layout, separate src, tests, docs, examples --- .gitignore | 190 +++++++++++++++++ .python-version | 1 + PROJECT_STRUCTURE.md | 204 ++++++++++++++++++ README.md | 168 +++++++++++++++ docs/README.md | 50 +++++ docs/USAGE.md | 221 +++++++++++++++++++ docs/progress-tracker.md | 284 +++++++++++++++++++++++++ docs/requirements.md | 232 ++++++++++++++++++++ docs/workplan.md | 246 +++++++++++++++++++++ examples/basic_usage.py | 49 +++++ main.py | 12 ++ pyproject.toml | 111 ++++++++++ src/config_man/__init__.py | 10 + src/config_man/cli/__init__.py | 9 + src/config_man/cli/main.py | 109 ++++++++++ src/config_man/core/__init__.py | 11 + src/config_man/core/config_manager.py | 119 +++++++++++ src/config_man/core/display_manager.py | 109 ++++++++++ src/config_man/core/view_command.py | 142 +++++++++++++ src/config_man/utils/__init__.py | 10 + src/config_man/utils/crypto.py | 23 ++ src/config_man/utils/mock_rclone.py | 153 +++++++++++++ tests/__init__.py | 5 + tests/fixtures/__init__.py | 5 + tests/fixtures/test_configs.py | 97 +++++++++ tests/integration/__init__.py | 5 + tests/unit/__init__.py | 5 + 27 files changed, 2580 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 PROJECT_STRUCTURE.md create mode 100644 README.md create mode 100644 docs/README.md create mode 100644 docs/USAGE.md create mode 100644 docs/progress-tracker.md create mode 100644 docs/requirements.md create mode 100644 docs/workplan.md create mode 100644 examples/basic_usage.py create mode 100644 main.py create mode 100644 pyproject.toml create mode 100644 src/config_man/__init__.py create mode 100644 src/config_man/cli/__init__.py create mode 100644 src/config_man/cli/main.py create mode 100644 src/config_man/core/__init__.py create mode 100644 src/config_man/core/config_manager.py create mode 100644 src/config_man/core/display_manager.py create mode 100644 src/config_man/core/view_command.py create mode 100644 src/config_man/utils/__init__.py create mode 100644 src/config_man/utils/crypto.py create mode 100644 src/config_man/utils/mock_rclone.py create mode 100644 tests/__init__.py create mode 100644 tests/fixtures/__init__.py create mode 100644 tests/fixtures/test_configs.py create mode 100644 tests/integration/__init__.py create mode 100644 tests/unit/__init__.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c78ef3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,190 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be added to the global gitignore or merged into this project gitignore. For a PyCharm +# project, it is recommended to include the following files in version control: +# - .idea/modules.xml +# - .idea/*.iml +# - .idea/misc.xml +# - .idea/vcs.xml +# - .idea/workspace.xml +# - .idea/tasks.xml +# - .idea/usage.statistics.xml +# - .idea/shelf.xml +# - .idea/dictionaries +# - .idea/shelf +# - .idea/workspace.xml +# - .idea/tasks.xml +# - .idea/usage.statistics.xml +# - .idea/shelf.xml +# - .idea/dictionaries +# - .idea/shelf +.idea/ + +# VS Code +.vscode/ + +# macOS +.DS_Store + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Config-Man specific +test_data/ +*.log diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..81fd80d --- /dev/null +++ b/PROJECT_STRUCTURE.md @@ -0,0 +1,204 @@ +# Config-Man 项目结构 + +## 概述 + +Config-Man 项目采用现代Python项目标准结构,将源代码、测试、文档和示例分开组织,便于维护和扩展。 + +## 目录结构 + +``` +config-man/ +├── src/ # 源代码目录 +│ └── config_man/ # 主包 +│ ├── __init__.py # 包初始化文件 +│ ├── cli/ # CLI模块 +│ │ ├── __init__.py +│ │ └── main.py # CLI主程序 +│ ├── core/ # 核心功能模块 +│ │ ├── __init__.py +│ │ ├── config_manager.py # 配置管理器 +│ │ ├── display_manager.py # 显示管理器 +│ │ └── view_command.py # 查看命令处理器 +│ └── utils/ # 工具模块 +│ ├── __init__.py +│ ├── crypto.py # 加密解密工具 +│ └── mock_rclone.py # 模拟rclone环境 +├── tests/ # 测试目录 +│ ├── __init__.py +│ ├── unit/ # 单元测试 +│ │ └── __init__.py +│ ├── integration/ # 集成测试 +│ │ └── __init__.py +│ └── fixtures/ # 测试工具和数据 +│ ├── __init__.py +│ ├── test_configs.py # 测试配置生成器 +│ └── test_data/ # 测试数据目录 +├── docs/ # 文档目录 +│ ├── README.md # 文档说明 +│ ├── USAGE.md # 使用说明 +│ ├── progress-tracker.md # 进度跟踪 +│ ├── requirements.md # 需求文档 +│ └── workplan.md # 工作计划 +├── examples/ # 示例目录 +│ └── basic_usage.py # 基本使用示例 +├── main.py # 主入口文件 +├── pyproject.toml # 项目配置文件 +├── README.md # 项目主文档 +├── .gitignore # Git忽略文件 +└── PROJECT_STRUCTURE.md # 项目结构说明(本文件) +``` + +## 模块说明 + +### 源代码模块 (src/config_man/) + +#### CLI模块 (cli/) +- **main.py**: CLI主程序,包含所有命令行命令的定义 +- 使用Click框架实现命令行界面 +- 支持view、suggest-update、force-update等命令 + +#### 核心模块 (core/) +- **config_manager.py**: 配置管理器,负责文件下载、解密、比较等核心功能 +- **display_manager.py**: 显示管理器,负责格式化输出和表格显示 +- **view_command.py**: 查看命令处理器,实现查看功能的核心逻辑 + +#### 工具模块 (utils/) +- **crypto.py**: 加密解密工具,提供DES加密解密功能 +- **mock_rclone.py**: 模拟rclone环境,用于测试和开发 + +### 测试模块 (tests/) + +#### 单元测试 (unit/) +- 测试各个模块的独立功能 +- 测试边界条件和错误处理 + +#### 集成测试 (integration/) +- 测试模块间的集成功能 +- 测试完整的工作流程 + +#### 测试工具 (fixtures/) +- **test_configs.py**: 测试配置生成器 +- **test_data/**: 测试数据目录,包含模拟的配置文件 + +### 文档模块 (docs/) +- 包含所有项目文档 +- 使用说明、需求文档、工作计划等 + +### 示例模块 (examples/) +- 提供使用示例和代码示例 +- 帮助用户快速上手 + +## 设计原则 + +### 1. 分离关注点 +- **源代码**: 只包含业务逻辑 +- **测试**: 独立的测试代码和数据 +- **文档**: 完整的使用和开发文档 +- **示例**: 实际的使用示例 + +### 2. 模块化设计 +- 每个模块职责单一 +- 模块间依赖关系清晰 +- 便于独立测试和维护 + +### 3. 可扩展性 +- 新功能可以轻松添加到对应模块 +- 测试可以独立添加 +- 文档结构清晰,便于更新 + +### 4. 标准化 +- 遵循Python项目标准结构 +- 使用现代Python工具链 +- 支持pip安装和开发模式 + +## 开发工作流 + +### 1. 开发新功能 +```bash +# 在src/config_man/对应模块中添加代码 +# 在tests/对应目录中添加测试 +# 在docs/中添加文档 +# 在examples/中添加示例 +``` + +### 2. 运行测试 +```bash +# 运行所有测试 +pytest + +# 运行单元测试 +pytest tests/unit/ + +# 运行集成测试 +pytest tests/integration/ + +# 生成覆盖率报告 +pytest --cov=src/config_man +``` + +### 3. 代码质量检查 +```bash +# 代码格式化 +black src/ tests/ + +# 类型检查 +mypy src/ + +# 代码检查 +flake8 src/ tests/ +``` + +### 4. 安装和发布 +```bash +# 开发模式安装 +pip install -e . + +# 构建包 +python -m build + +# 发布到PyPI +twine upload dist/ +``` + +## 最佳实践 + +### 1. 导入路径 +- 使用相对导入在包内部 +- 使用绝对导入从外部访问 +- 避免循环导入 + +### 2. 测试组织 +- 单元测试测试独立功能 +- 集成测试测试模块协作 +- 使用fixtures提供测试数据 + +### 3. 文档维护 +- 代码和文档同步更新 +- 提供完整的使用示例 +- 保持文档结构清晰 + +### 4. 版本控制 +- 合理的.gitignore配置 +- 清晰的提交信息 +- 版本号管理 + +## 扩展指南 + +### 添加新功能 +1. 在`src/config_man/core/`中添加核心逻辑 +2. 在`src/config_man/cli/main.py`中添加CLI命令 +3. 在`tests/`中添加对应测试 +4. 在`docs/`中更新文档 +5. 在`examples/`中添加使用示例 + +### 添加新工具 +1. 在`src/config_man/utils/`中添加工具函数 +2. 在`tests/unit/`中添加单元测试 +3. 在`docs/`中添加使用说明 + +### 添加新测试 +1. 根据测试类型选择`tests/unit/`或`tests/integration/` +2. 使用`tests/fixtures/`中的测试数据 +3. 遵循pytest最佳实践 + +这种项目结构确保了代码的可维护性、可测试性和可扩展性,符合现代Python项目的标准。 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3caddd4 --- /dev/null +++ b/README.md @@ -0,0 +1,168 @@ +# Config-Man CLI 工具 + +## 项目概述 + +Config-Man 是一个Python CLI工具,用于管理存储在对象存储中的加密静态资源配置文件。该工具支持多版本管理,通过Cloudflare CDN进行分发,主要用于游戏应用的配置管理。 + +### 项目目标 +- 简化多版本配置文件的查看和管理 +- 支持版本升级策略(建议更新和强制更新) +- 确保配置文件的加密存储和CDN缓存同步 +- 提供直观的配置对比功能 + +## 系统架构 + +### 存储架构 +``` +对象存储服务器 +├── ab/ +│ ├── 0_29/ +│ │ ├── androidconfig.json (加密) +│ │ └── iosconfig.json (加密) +│ ├── 0_30/ +│ │ ├── androidconfig.json (加密) +│ │ └── iosconfig.json (加密) +│ └── 1_00/ +│ ├── androidconfig.json (加密) +│ └── iosconfig.json (加密) +``` + +### 技术栈 +- **开发语言**: Python 3.13+ +- **CLI框架**: Click 或 Typer +- **存储同步**: rclone +- **CDN服务**: Cloudflare +- **文件加密**: AES-256 +- **配置格式**: JSON + +## 功能说明 + +### 功能1: 查看配置文件 + +对比显示对象存储和CDN中的配置内容,支持Android和iOS平台。 + +```bash +config-man view --version <版本号> +``` + +**参数说明**: +- `--version`: 版本号,格式为 0.29, 0.30, 0.31 等 + +**功能特性**: +- 同时显示Android和iOS平台的配置文件 +- 对比对象存储中的内容和CDN中的内容 +- 使用表格格式漂亮地展示配置差异 +- 支持JSON格式化输出 + +### 功能2: 建议更新 + +在目标版本的配置文件中添加 `Ver_New` 属性,提示用户有新版本可用。 + +```bash +config-man suggest-update --platform <平台> --target-version <目标版本> --update-version <更新版本> +``` + +**参数说明**: +- `--platform`: 平台类型,支持 android 或 ios +- `--target-version`: 目标版本号,如 0.29 +- `--update-version`: 更新版本号,如 0.30 + +### 功能3: 强制更新 + +直接更新目标版本的 `Ver` 属性,强制用户升级到新版本。 + +```bash +config-man force-update --platform <平台> --target-version <目标版本> --update-version <更新版本> +``` + +**参数说明**: +- `--platform`: 平台类型,支持 android 或 ios +- `--target-version`: 目标版本号,如 0.29 +- `--update-version`: 更新版本号,如 0.30 + +## 配置文件结构 + +### 配置项说明 +```json +{ + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.28.2ece920695", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==" +} +``` + +### 配置项分类 +- **API端点**: EventApiURL, FuncUrl +- **版本信息**: Ver, Ver_New +- **应用商店链接**: Google_Play_URL, APP_Store_URL +- **实时通信**: RTMPid, RTMServerEndpoint, RTMHmacSecret +- **功能配置**: PlayFabTitle, HeartBeat, FuncKey + +## 技术要求 + +### 安全要求 +- 配置文件在对象存储中加密存储 +- 支持AES-256加密算法 +- 加密密钥安全管理 +- 操作日志记录 + +### 性能要求 +- 配置文件查看响应时间 < 3秒 +- 配置文件修改响应时间 < 5秒 +- CDN缓存刷新响应时间 < 10秒 + +### 兼容性要求 +- 支持Python 3.13+ +- 支持Windows、macOS、Linux +- 支持rclone命令行工具 + +## 安装和使用 + +### 安装依赖 +```bash +pip install -r requirements.txt +``` + +### 配置环境 +```bash +# 配置rclone +rclone config + +# 配置Cloudflare API +export CLOUDFLARE_API_TOKEN=your_token +``` + +### 使用示例 +```bash +# 查看配置 +config-man view --version 0.30 + +# 建议更新 +config-man suggest-update --platform android --target-version 0.29 --update-version 0.30 + +# 强制更新 +config-man force-update --platform ios --target-version 0.29 --update-version 0.30 +``` + +## 错误处理 + +### 常见错误场景 +- 版本号不存在 +- 平台类型错误 +- 网络连接失败 +- 文件加密/解密失败 +- CDN刷新失败 + +### 错误处理策略 +- 提供清晰的错误信息 +- 支持重试机制 +- 记录详细的操作日志 +- 提供回滚功能 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b57e94a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,50 @@ +# Config-Man 文档 + +本目录包含 Config-Man 项目的所有文档。 + +## 文档结构 + +- `README.md` - 项目主文档 +- `USAGE.md` - 使用说明文档 +- `progress-tracker.md` - 进度跟踪文档 +- `requirements.md` - 需求文档 +- `workplan.md` - 工作计划文档 + +## 快速开始 + +1. 安装项目: + ```bash + pip install -e . + ``` + +2. 查看帮助: + ```bash + python main.py --help + ``` + +3. 使用查看功能: + ```bash + python main.py view --version 0.30 --mock + ``` + +## 开发指南 + +1. 安装开发依赖: + ```bash + pip install -e ".[dev]" + ``` + +2. 运行测试: + ```bash + pytest + ``` + +3. 代码格式化: + ```bash + black src/ tests/ + ``` + +4. 类型检查: + ```bash + mypy src/ + ``` \ No newline at end of file diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..bc787ef --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,221 @@ +# Config-Man 查看功能使用说明 + +## 功能概述 + +Config-Man 的查看功能允许您对比显示对象存储和CDN中的配置内容,支持Android和iOS平台。 + +## 安装依赖 + +首先安装项目依赖: + +```bash +pip install -e . +``` + +## 基本用法 + +### 1. 查看配置文件 + +```bash +# 查看版本 0.30 的配置文件 +python main.py view --version 0.30 + +# 以JSON格式输出 +python main.py view --version 0.30 --json + +# 使用模拟环境进行测试 +python main.py view --version 0.30 --mock +``` + +### 2. 查看帮助信息 + +```bash +# 查看主命令帮助 +python main.py --help + +# 查看view命令帮助 +python main.py view --help +``` + +## 参数说明 + +### view 命令参数 + +- `--version`: 版本号,格式为 0.29, 0.30, 0.31 等(必需) +- `--json`: 以JSON格式输出(可选) +- `--storage-path`: 对象存储路径前缀,默认为 'ab'(可选) +- `--cdn-url`: CDN基础URL(可选) +- `--mock`: 使用模拟环境进行测试(可选) + +## 输出格式 + +### 表格格式(默认) + +``` +版本: 0.30 | 平台: Android +┌─────────────────┬─────────────────────┬─────────────────────┬──────────┐ +│ 配置项 │ 对象存储内容 │ CDN内容 │ 状态 │ +├─────────────────┼─────────────────────┼─────────────────────┼──────────┤ +│ Ver │ 0.30.2ghi789 │ 0.30.2ghi789 │ ✅ 一致 │ +│ EventApiURL │ https://n3backend. │ https://n3backend. │ ✅ 一致 │ +│ NewFeature │ enabled │ N/A │ ❌ 不同 │ +│ ... │ ... │ ... │ ... │ +└─────────────────┴─────────────────────┴─────────────────────┴──────────┘ +``` + +### JSON格式 + +```json +{ + "version": "0.30", + "platform": "android", + "storage_config": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.30.2ghi789", + "NewFeature": "enabled" + }, + "cdn_config": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.30.2ghi789" + } +} +``` + +## 模拟环境测试 + +为了方便测试,项目提供了模拟环境: + +```bash +# 使用模拟环境测试 +python main.py view --version 0.30 --mock +``` + +模拟环境会: +1. 自动创建测试配置文件 +2. 模拟rclone命令 +3. 提供完整的测试数据 + +## 错误处理 + +### 常见错误 + +1. **版本号格式错误** + ``` + 错误: 无效的版本号格式: 1.2.3 + ``` + +2. **rclone未安装** + ``` + 错误: rclone 未安装或不可用。请先安装 rclone。 + ``` + +3. **文件不存在** + ``` + 警告: 无法从对象存储下载 ab/0_30/androidconfig.json: 文件不存在 + ``` + +4. **网络连接失败** + ``` + 警告: 无法从CDN获取 https://cdn.example.com/ab/0_30/androidconfig.json + ``` + +## 配置说明 + +### 配置文件结构 + +配置文件包含以下主要字段: + +- **EventApiURL**: API端点URL +- **Ver**: 版本信息 +- **PlayFabTitle**: PlayFab标题 +- **Google_Play_URL**: Google Play商店链接 +- **APP_Store_URL**: App Store链接 +- **HeartBeat**: 心跳间隔 +- **RTMPid**: RTM进程ID +- **RTMServerEndpoint**: RTM服务器端点 +- **RTMHmacSecret**: RTM HMAC密钥 +- **FuncUrl**: 功能URL +- **FuncKey**: 功能密钥 + +### 版本号转换 + +版本号会自动转换为存储路径格式: +- `0.29` → `0_29` +- `0.30` → `0_30` +- `1.00` → `1_00` + +## 高级用法 + +### 自定义存储路径 + +```bash +python main.py view --version 0.30 --storage-path custom_path +``` + +### 指定CDN URL + +```bash +python main.py view --version 0.30 --cdn-url https://cdn.example.com +``` + +### 组合使用 + +```bash +python main.py view --version 0.30 --json --mock --storage-path test_data/ab +``` + +## 故障排除 + +### 1. 安装问题 + +确保安装了所有依赖: +```bash +pip install click pycryptodome tabulate requests rich +``` + +### 2. rclone配置 + +如果使用真实环境,需要配置rclone: +```bash +rclone config +``` + +### 3. 权限问题 + +确保有足够的权限访问对象存储和CDN。 + +### 4. 网络问题 + +检查网络连接和防火墙设置。 + +## 开发说明 + +### 项目结构 + +``` +config-man/ +├── main.py # 主程序入口 +├── appconfig_tool.py # 加密解密工具 +├── config_manager.py # 配置管理器 +├── display_manager.py # 显示管理器 +├── view_command.py # 查看命令处理器 +├── mock_rclone.py # 模拟rclone环境 +└── test_configs.py # 测试配置生成器 +``` + +### 扩展功能 + +要添加新的查看功能,可以: + +1. 在 `config_manager.py` 中添加新的方法 +2. 在 `display_manager.py` 中添加新的显示格式 +3. 在 `view_command.py` 中添加新的处理逻辑 +4. 在 `main.py` 中添加新的CLI命令 + +## 联系支持 + +如果遇到问题,请检查: +1. 版本号格式是否正确 +2. 网络连接是否正常 +3. rclone配置是否正确 +4. 依赖是否完整安装 \ No newline at end of file diff --git a/docs/progress-tracker.md b/docs/progress-tracker.md new file mode 100644 index 0000000..23abab1 --- /dev/null +++ b/docs/progress-tracker.md @@ -0,0 +1,284 @@ +# Config-Man CLI 工具进度追踪 + +## 项目状态概览 + +- **项目名称**: Config-Man CLI 工具 +- **当前阶段**: 项目初始化 +- **开始日期**: [待定] +- **预计结束日期**: [待定] +- **实际进度**: 0% +- **剩余时间**: [待定] + +## 阶段进度追踪 + +### 阶段1: 项目初始化 (1-2天) +**状态**: 🔄 进行中 +**进度**: 0% +**开始时间**: [待定] +**预计完成**: [待定] + +#### 任务1.1: 环境搭建 +- [ ] 创建Python虚拟环境 +- [ ] 安装项目依赖 +- [ ] 配置开发环境 +- [ ] 设置代码仓库 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **实际时间**: [待定] + +#### 任务1.2: 项目结构设计 +- [ ] 设计项目目录结构 +- [ ] 创建基础模块 +- [ ] 配置CLI框架 +- [ ] 设置配置文件管理 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **实际时间**: [待定] + +#### 任务1.3: 配置文件管理 +- [ ] 实现配置文件加密/解密功能 +- [ ] 实现对象存储连接 +- [ ] 实现rclone集成 +- [ ] 实现基础文件操作 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +### 阶段2: 核心功能开发 (3-4天) +**状态**: ⏳ 待开始 +**进度**: 0% +**开始时间**: [待定] +**预计完成**: [待定] + +#### 任务2.1: 查看配置文件功能 +- [ ] 实现版本号转换逻辑 (0.29 → 0_29) +- [ ] 实现文件下载和解密 +- [ ] 实现CDN内容获取 +- [ ] 实现配置对比显示 +- [ ] 实现格式化输出 (表格格式) +- [ ] 实现Android和iOS平台同时显示 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1.5天 +- **实际时间**: [待定] + +#### 任务2.2: 建议更新功能 +- [ ] 实现配置文件读取和解析 +- [ ] 实现Ver_New属性添加逻辑 +- [ ] 实现文件加密和上传 +- [ ] 实现CDN缓存刷新 +- [ ] 实现错误处理和回滚 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +#### 任务2.3: 强制更新功能 +- [ ] 实现Ver属性更新逻辑 +- [ ] 实现文件加密和上传 +- [ ] 实现CDN缓存刷新 +- [ ] 实现错误处理和回滚 +- [ ] 实现安全确认机制 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +#### 任务2.4: CLI框架完善 +- [ ] 实现命令行参数解析 +- [ ] 实现帮助信息 +- [ ] 实现错误提示 +- [ ] 实现日志记录 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **实际时间**: [待定] + +### 阶段3: 集成测试 (2-3天) +**状态**: ⏳ 待开始 +**进度**: 0% +**开始时间**: [待定] +**预计完成**: [待定] + +#### 任务3.1: 单元测试 +- [ ] 编写配置文件处理测试 +- [ ] 编写加密解密测试 +- [ ] 编写CLI命令测试 +- [ ] 编写版本号转换测试 +- [ ] 编写JSON处理测试 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +#### 任务3.2: 集成测试 +- [ ] 测试对象存储连接 +- [ ] 测试CDN缓存刷新 +- [ ] 测试完整工作流程 +- [ ] 测试多版本场景 +- [ ] 测试多平台场景 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +#### 任务3.3: 错误处理测试 +- [ ] 测试网络错误处理 +- [ ] 测试文件不存在错误 +- [ ] 测试参数验证错误 +- [ ] 测试加密解密错误 +- [ ] 测试CDN刷新错误 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +### 阶段4: 文档和部署 (1-2天) +**状态**: ⏳ 待开始 +**进度**: 0% +**开始时间**: [待定] +**预计完成**: [待定] + +#### 任务4.1: 文档编写 +- [ ] 编写用户使用手册 +- [ ] 编写API文档 +- [ ] 编写部署指南 +- [ ] 编写故障排除指南 +- [ ] 更新README文档 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +#### 任务4.2: 部署准备 +- [ ] 创建安装脚本 +- [ ] 配置CI/CD流程 +- [ ] 准备发布包 +- [ ] 创建配置文件模板 +- [ ] 设置环境变量配置 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 1天 +- **实际时间**: [待定] + +### 阶段5: 验收测试 (1天) +**状态**: ⏳ 待开始 +**进度**: 0% +**开始时间**: [待定] +**预计完成**: [待定] + +#### 任务5.1: 功能验收 +- [ ] 验证所有CLI命令 +- [ ] 验证配置文件管理 +- [ ] 验证CDN缓存刷新 +- [ ] 验证错误处理 +- [ ] 验证文档完整性 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **实际时间**: [待定] + +#### 任务5.2: 性能验收 +- [ ] 测试响应时间 +- [ ] 测试并发处理 +- [ ] 测试错误恢复 +- [ ] 测试内存使用 +- [ ] 测试CPU使用 +- **状态**: ⏳ 待开始 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **实际时间**: [待定] + +## 里程碑追踪 + +### 里程碑1: 项目初始化完成 +- **状态**: ⏳ 待开始 +- **计划时间**: 第2天 +- **实际时间**: [待定] +- **交付物**: 基础框架和配置文件管理功能 +- **验收标准**: 可以成功操作加密的配置文件 + +### 里程碑2: 核心功能完成 +- **状态**: ⏳ 待开始 +- **计划时间**: 第6天 +- **实际时间**: [待定] +- **交付物**: 三个主要CLI功能 +- **验收标准**: 所有命令正常工作 + +### 里程碑3: 测试完成 +- **状态**: ⏳ 待开始 +- **计划时间**: 第9天 +- **实际时间**: [待定] +- **交付物**: 测试报告和修复的问题 +- **验收标准**: 测试覆盖率 > 80%,无严重bug + +### 里程碑4: 项目交付 +- **状态**: ⏳ 待开始 +- **计划时间**: 第12天 +- **实际时间**: [待定] +- **交付物**: 完整的CLI工具和文档 +- **验收标准**: 满足所有需求,可以投入使用 + +## 问题追踪 + +### 已解决问题 +| 问题ID | 问题描述 | 解决方案 | 解决时间 | 负责人 | +|--------|----------|----------|----------|--------| +| - | - | - | - | - | + +### 待解决问题 +| 问题ID | 问题描述 | 优先级 | 状态 | 负责人 | 预计解决时间 | +|--------|----------|--------|------|--------|--------------| +| - | - | - | - | - | - | + +### 风险追踪 +| 风险ID | 风险描述 | 影响程度 | 缓解措施 | 状态 | 负责人 | +|--------|----------|----------|----------|------|--------| +| R001 | 对象存储API集成复杂性 | 高 | 使用rclone作为统一接口 | 监控中 | [待定] | +| R002 | CDN缓存刷新API限制 | 中 | 实现重试机制和错误处理 | 监控中 | [待定] | +| R003 | CLI框架学习成本 | 低 | 选择文档完善的框架 | 监控中 | [待定] | + +## 团队工作负载 + +### 本周工作负载 +| 团队成员 | 当前任务 | 预计完成时间 | 工作负载 | 备注 | +|----------|----------|--------------|----------|------| +| [待定] | - | - | - | - | + +### 下周工作计划 +| 团队成员 | 计划任务 | 预计开始时间 | 预计完成时间 | 备注 | +|----------|----------|--------------|--------------|------| +| [待定] | - | - | - | - | + +## 质量指标 + +### 代码质量 +- **测试覆盖率**: 0% (目标: >80%) +- **代码审查**: 0% (目标: 100%) +- **静态分析**: 0% (目标: 100%) + +### 性能指标 +- **配置文件查看响应时间**: [待测试] (目标: <3秒) +- **配置文件修改响应时间**: [待测试] (目标: <5秒) +- **CDN缓存刷新响应时间**: [待测试] (目标: <10秒) + +### 文档质量 +- **用户手册**: 0% (目标: 100%) +- **API文档**: 0% (目标: 100%) +- **部署指南**: 0% (目标: 100%) + +## 更新日志 + +### 2024年[待定] +- 创建项目进度追踪文档 +- 初始化所有任务状态为待开始 +- 设置里程碑和验收标准 + +--- + +**最后更新**: [待定] +**更新人**: [待定] +**下次更新**: [待定] \ No newline at end of file diff --git a/docs/requirements.md b/docs/requirements.md new file mode 100644 index 0000000..000ea0c --- /dev/null +++ b/docs/requirements.md @@ -0,0 +1,232 @@ +# Config-Man CLI 工具需求文档 + +## 1. 项目概述 + +### 1.1 项目背景 +开发一个Python CLI工具,用于管理存储在对象存储中的加密静态资源配置文件。该工具支持多版本管理,通过Cloudflare CDN进行分发,主要用于游戏应用的配置管理。 + +### 1.2 项目目标 +- 简化多版本配置文件的查看和管理 +- 支持版本升级策略(建议更新和强制更新) +- 确保配置文件的加密存储和CDN缓存同步 +- 提供直观的配置对比功能 + +## 2. 系统架构 + +### 2.1 存储架构 +``` +对象存储服务器 +├── ab/ +│ ├── 0_29/ +│ │ ├── androidconfig.json (加密) +│ │ └── iosconfig.json (加密) +│ ├── 0_30/ +│ │ ├── androidconfig.json (加密) +│ │ └── iosconfig.json (加密) +│ └── 1_00/ +│ ├── androidconfig.json (加密) +│ └── iosconfig.json (加密) +``` + +### 2.2 技术栈 +- **开发语言**: Python 3.13+ +- **CLI框架**: Click 或 Typer +- **存储同步**: rclone +- **CDN服务**: Cloudflare +- **文件加密**: AES-256 +- **配置格式**: JSON + +## 3. 功能需求 + +### 3.1 功能1: 查看配置文件 + +#### 3.1.1 功能描述 +对比显示对象存储和CDN中的配置内容,支持Android和iOS平台。 + +#### 3.1.2 命令行格式 +```bash +config-man view --version <版本号> +``` + +#### 3.1.3 参数说明 +- `--version`: 版本号,格式为 0.29, 0.30, 0.31 等 + +#### 3.1.4 功能特性 +- 同时显示Android和iOS平台的配置文件 +- 对比对象存储中的内容和CDN中的内容 +- 使用表格格式漂亮地展示配置差异 +- 支持JSON格式化输出 + +#### 3.1.5 输出示例 +``` +版本: 0.30 +平台: Android +┌─────────────────┬─────────────────────┬─────────────────────┐ +│ 配置项 │ 对象存储内容 │ CDN内容 │ +├─────────────────┼─────────────────────┼─────────────────────┤ +│ Ver │ 0.30.1abc123 │ 0.30.1abc123 │ +│ EventApiURL │ https://... │ https://... │ +│ PlayFabTitle │ B066F │ B066F │ +│ ... │ ... │ ... │ +└─────────────────┴─────────────────────┴─────────────────────┘ + +平台: iOS +┌─────────────────┬─────────────────────┬─────────────────────┐ +│ 配置项 │ 对象存储内容 │ CDN内容 │ +├─────────────────┼─────────────────────┼─────────────────────┤ +│ Ver │ 0.30.1def456 │ 0.30.1def456 │ +│ ... │ ... │ ... │ +└─────────────────┴─────────────────────┴─────────────────────┘ +``` + +### 3.2 功能2: 建议更新 + +#### 3.2.1 功能描述 +在目标版本的配置文件中添加 `Ver_New` 属性,提示用户有新版本可用。 + +#### 3.2.2 命令行格式 +```bash +config-man suggest-update --platform <平台> --target-version <目标版本> --update-version <更新版本> +``` + +#### 3.2.3 参数说明 +- `--platform`: 平台类型,支持 android 或 ios +- `--target-version`: 目标版本号,如 0.29 +- `--update-version`: 更新版本号,如 0.30 + +#### 3.2.4 功能特性 +- 在目标版本配置中添加 `Ver_New` 属性 +- `Ver_New` 的值设为更新版本的 `Ver` 属性值 +- 自动加密并上传到对象存储 +- 自动刷新CDN缓存 + +#### 3.2.5 配置变更示例 +```json +// 修改前 +{ + "Ver": "0.29.1abc123", + "EventApiURL": "https://n3backend.azurewebsites.net/", + "PlayFabTitle": "B066F" +} + +// 修改后 +{ + "Ver": "0.29.1abc123", + "Ver_New": "0.30.1def456", + "EventApiURL": "https://n3backend.azurewebsites.net/", + "PlayFabTitle": "B066F" +} +``` + +### 3.3 功能3: 强制更新 + +#### 3.3.1 功能描述 +直接更新目标版本的 `Ver` 属性,强制用户升级到新版本。 + +#### 3.3.2 命令行格式 +```bash +config-man force-update --platform <平台> --target-version <目标版本> --update-version <更新版本> +``` + +#### 3.3.3 参数说明 +- `--platform`: 平台类型,支持 android 或 ios +- `--target-version`: 目标版本号,如 0.29 +- `--update-version`: 更新版本号,如 0.30 + +#### 3.3.4 功能特性 +- 将目标版本的 `Ver` 属性值改为更新版本的 `Ver` 属性值 +- 自动加密并上传到对象存储 +- 自动刷新CDN缓存 + +#### 3.3.5 配置变更示例 +```json +// 修改前 +{ + "Ver": "0.29.1abc123", + "EventApiURL": "https://n3backend.azurewebsites.net/", + "PlayFabTitle": "B066F" +} + +// 修改后 +{ + "Ver": "0.30.1def456", + "EventApiURL": "https://n3backend.azurewebsites.net/", + "PlayFabTitle": "B066F" +} +``` + +## 4. 配置文件结构 + +### 4.1 配置项说明 +```json +{ + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.28.2ece920695", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==" +} +``` + +### 4.2 配置项分类 +- **API端点**: EventApiURL, FuncUrl +- **版本信息**: Ver, Ver_New +- **应用商店链接**: Google_Play_URL, APP_Store_URL +- **实时通信**: RTMPid, RTMServerEndpoint, RTMHmacSecret +- **功能配置**: PlayFabTitle, HeartBeat, FuncKey + +## 5. 技术要求 + +### 5.1 安全要求 +- 配置文件在对象存储中加密存储 +- 支持AES-256加密算法 +- 加密密钥安全管理 +- 操作日志记录 + +### 5.2 性能要求 +- 配置文件查看响应时间 < 3秒 +- 配置文件修改响应时间 < 5秒 +- CDN缓存刷新响应时间 < 10秒 + +### 5.3 兼容性要求 +- 支持Python 3.13+ +- 支持Windows、macOS、Linux +- 支持rclone命令行工具 + +## 6. 错误处理 + +### 6.1 常见错误场景 +- 版本号不存在 +- 平台类型错误 +- 网络连接失败 +- 文件加密/解密失败 +- CDN刷新失败 + +### 6.2 错误处理策略 +- 提供清晰的错误信息 +- 支持重试机制 +- 记录详细的操作日志 +- 提供回滚功能 + +## 7. 非功能性需求 + +### 7.1 可用性 +- 提供清晰的命令行帮助信息 +- 支持命令自动补全 +- 提供详细的错误提示 + +### 7.2 可维护性 +- 模块化设计 +- 完善的日志记录 +- 清晰的代码注释 + +### 7.3 可扩展性 +- 支持新的配置文件格式 +- 支持新的存储后端 +- 支持新的CDN服务商 \ No newline at end of file diff --git a/docs/workplan.md b/docs/workplan.md new file mode 100644 index 0000000..dd0755d --- /dev/null +++ b/docs/workplan.md @@ -0,0 +1,246 @@ +# Config-Man CLI 工具工作计划 + +## 项目概览 + +- **项目名称**: Config-Man CLI 工具 +- **预计工期**: 8-12天 +- **开始日期**: [待定] +- **结束日期**: [待定] +- **项目经理**: [待定] + +## 阶段1: 项目初始化 (1-2天) + +### 任务1.1: 环境搭建 +- [ ] 创建Python虚拟环境 +- [ ] 安装项目依赖 +- [ ] 配置开发环境 +- [ ] 设置代码仓库 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **完成标准**: 开发环境可以正常运行 + +### 任务1.2: 项目结构设计 +- [ ] 设计项目目录结构 +- [ ] 创建基础模块 +- [ ] 配置CLI框架 +- [ ] 设置配置文件管理 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **完成标准**: 项目结构清晰,基础框架可用 + +### 任务1.3: 配置文件管理 +- [ ] 实现配置文件加密/解密功能 +- [ ] 实现对象存储连接 +- [ ] 实现rclone集成 +- [ ] 实现基础文件操作 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: 可以成功下载、解密、加密、上传配置文件 + +## 阶段2: 核心功能开发 (3-4天) + +### 任务2.1: 查看配置文件功能 +- [ ] 实现版本号转换逻辑 (0.29 → 0_29) +- [ ] 实现文件下载和解密 +- [ ] 实现CDN内容获取 +- [ ] 实现配置对比显示 +- [ ] 实现格式化输出 (表格格式) +- [ ] 实现Android和iOS平台同时显示 +- **负责人**: [待定] +- **预计时间**: 1.5天 +- **完成标准**: `config-man view --version 0.30` 命令正常工作 + +### 任务2.2: 建议更新功能 +- [ ] 实现配置文件读取和解析 +- [ ] 实现Ver_New属性添加逻辑 +- [ ] 实现文件加密和上传 +- [ ] 实现CDN缓存刷新 +- [ ] 实现错误处理和回滚 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: `config-man suggest-update` 命令正常工作 + +### 任务2.3: 强制更新功能 +- [ ] 实现Ver属性更新逻辑 +- [ ] 实现文件加密和上传 +- [ ] 实现CDN缓存刷新 +- [ ] 实现错误处理和回滚 +- [ ] 实现安全确认机制 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: `config-man force-update` 命令正常工作 + +### 任务2.4: CLI框架完善 +- [ ] 实现命令行参数解析 +- [ ] 实现帮助信息 +- [ ] 实现错误提示 +- [ ] 实现日志记录 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **完成标准**: CLI界面友好,错误提示清晰 + +## 阶段3: 集成测试 (2-3天) + +### 任务3.1: 单元测试 +- [ ] 编写配置文件处理测试 +- [ ] 编写加密解密测试 +- [ ] 编写CLI命令测试 +- [ ] 编写版本号转换测试 +- [ ] 编写JSON处理测试 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: 测试覆盖率 > 80% + +### 任务3.2: 集成测试 +- [ ] 测试对象存储连接 +- [ ] 测试CDN缓存刷新 +- [ ] 测试完整工作流程 +- [ ] 测试多版本场景 +- [ ] 测试多平台场景 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: 所有功能在真实环境中正常工作 + +### 任务3.3: 错误处理测试 +- [ ] 测试网络错误处理 +- [ ] 测试文件不存在错误 +- [ ] 测试参数验证错误 +- [ ] 测试加密解密错误 +- [ ] 测试CDN刷新错误 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: 错误处理完善,用户体验良好 + +## 阶段4: 文档和部署 (1-2天) + +### 任务4.1: 文档编写 +- [ ] 编写用户使用手册 +- [ ] 编写API文档 +- [ ] 编写部署指南 +- [ ] 编写故障排除指南 +- [ ] 更新README文档 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: 文档完整,易于理解 + +### 任务4.2: 部署准备 +- [ ] 创建安装脚本 +- [ ] 配置CI/CD流程 +- [ ] 准备发布包 +- [ ] 创建配置文件模板 +- [ ] 设置环境变量配置 +- **负责人**: [待定] +- **预计时间**: 1天 +- **完成标准**: 可以一键部署和安装 + +## 阶段5: 验收测试 (1天) + +### 任务5.1: 功能验收 +- [ ] 验证所有CLI命令 +- [ ] 验证配置文件管理 +- [ ] 验证CDN缓存刷新 +- [ ] 验证错误处理 +- [ ] 验证文档完整性 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **完成标准**: 所有功能按需求正常工作 + +### 任务5.2: 性能验收 +- [ ] 测试响应时间 +- [ ] 测试并发处理 +- [ ] 测试错误恢复 +- [ ] 测试内存使用 +- [ ] 测试CPU使用 +- **负责人**: [待定] +- **预计时间**: 0.5天 +- **完成标准**: 性能指标达到要求 + +## 关键里程碑 + +### 里程碑1: 项目初始化完成 +- **时间**: 第2天 +- **交付物**: 基础框架和配置文件管理功能 +- **验收标准**: 可以成功操作加密的配置文件 + +### 里程碑2: 核心功能完成 +- **时间**: 第6天 +- **交付物**: 三个主要CLI功能 +- **验收标准**: 所有命令正常工作 + +### 里程碑3: 测试完成 +- **时间**: 第9天 +- **交付物**: 测试报告和修复的问题 +- **验收标准**: 测试覆盖率 > 80%,无严重bug + +### 里程碑4: 项目交付 +- **时间**: 第12天 +- **交付物**: 完整的CLI工具和文档 +- **验收标准**: 满足所有需求,可以投入使用 + +## 风险评估 + +### 高风险 +- **对象存储API集成复杂性** + - 风险描述: 不同对象存储服务商的API差异较大 + - 缓解措施: 使用rclone作为统一接口,降低集成复杂度 + - 影响: 可能延长开发时间1-2天 + +### 中风险 +- **CDN缓存刷新API限制** + - 风险描述: Cloudflare API可能有调用频率限制 + - 缓解措施: 实现重试机制和错误处理 + - 影响: 可能影响用户体验 + +### 低风险 +- **CLI框架学习成本** + - 风险描述: 团队成员可能需要学习Click或Typer框架 + - 缓解措施: 选择文档完善的框架,提供学习资源 + - 影响: 轻微影响开发进度 + +## 成功标准 + +### 功能标准 +- [ ] 所有三个CLI功能正常工作 +- [ ] 配置文件正确加密存储 +- [ ] CDN缓存正确刷新 +- [ ] 错误处理完善 +- [ ] 文档完整清晰 + +### 性能标准 +- [ ] 配置文件查看响应时间 < 3秒 +- [ ] 配置文件修改响应时间 < 5秒 +- [ ] CDN缓存刷新响应时间 < 10秒 +- [ ] 内存使用 < 100MB +- [ ] CPU使用 < 10% + +### 质量标准 +- [ ] 代码测试覆盖率 > 80% +- [ ] 无严重bug +- [ ] 代码注释完整 +- [ ] 符合Python编码规范 + +## 团队分工 + +### 开发团队 +- **主开发**: [待定] - 负责核心功能开发 +- **测试工程师**: [待定] - 负责测试和验收 +- **文档工程师**: [待定] - 负责文档编写 + +### 支持团队 +- **项目经理**: [待定] - 负责项目管理和进度跟踪 +- **技术顾问**: [待定] - 负责技术指导和问题解决 + +## 沟通计划 + +### 日常沟通 +- **每日站会**: 每天上午9:00,15分钟 +- **周报**: 每周五下午,总结本周进展和下周计划 + +### 里程碑沟通 +- **里程碑评审**: 每个里程碑完成后进行评审 +- **风险汇报**: 发现风险时立即汇报 + +### 文档更新 +- **进度更新**: 每周更新工作计划进度 +- **问题记录**: 及时记录和跟踪问题 +- **经验总结**: 项目结束后进行经验总结 \ No newline at end of file diff --git a/examples/basic_usage.py b/examples/basic_usage.py new file mode 100644 index 0000000..d16f651 --- /dev/null +++ b/examples/basic_usage.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +""" +Config-Man 基本使用示例 + +演示如何使用 Config-Man CLI 工具的基本功能。 +""" + +import sys +import os + +# 添加项目根目录到路径 +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from config_man.core import ViewCommand +from config_man.utils import patch_rclone + + +def main(): + """基本使用示例""" + print("Config-Man 基本使用示例") + print("=" * 50) + + # 启用模拟环境 + patch_rclone() + + # 创建查看命令处理器 + view_cmd = ViewCommand() + + # 查看版本 0.30 的配置 + print("\n1. 查看版本 0.30 的配置:") + success = view_cmd.execute("0.30", json_format=False) + + if success: + print("✅ 查看成功") + else: + print("❌ 查看失败") + + # 查看版本 0.29 的配置(JSON格式) + print("\n2. 查看版本 0.29 的配置(JSON格式):") + success = view_cmd.execute("0.29", json_format=True) + + if success: + print("✅ 查看成功") + else: + print("❌ 查看失败") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..cc7e1db --- /dev/null +++ b/main.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +""" +Config-Man CLI 工具入口点 + +用于管理存储在对象存储中的加密静态资源配置文件。 +支持多版本管理,通过Cloudflare CDN进行分发。 +""" + +from config_man.cli.main import main + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a50edf4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,111 @@ +[project] +name = "config-man" +version = "0.1.0" +description = "CLI tool for managing encrypted configuration files" +readme = "README.md" +requires-python = ">=3.13" +authors = [ + {name = "Config-Man Team", email = "team@config-man.com"} +] +license = {text = "MIT"} +keywords = ["cli", "config", "encryption", "cdn", "storage"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: System :: Systems Administration", +] +dependencies = [ + "click>=8.0.0", + "pycryptodome>=3.19.0", + "tabulate>=0.9.0", + "requests>=2.31.0", + "rich>=13.0.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", + "black>=23.0.0", + "flake8>=6.0.0", + "mypy>=1.0.0", +] +test = [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", +] + +[project.scripts] +config-man = "config_man.cli.main:main" + +[project.urls] +Homepage = "https://github.com/config-man/config-man" +Documentation = "https://config-man.readthedocs.io" +Repository = "https://github.com/config-man/config-man" +Issues = "https://github.com/config-man/config-man/issues" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/config_man"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = [ + "--strict-markers", + "--strict-config", + "--cov=src/config_man", + "--cov-report=term-missing", + "--cov-report=html", +] + +[tool.black] +line-length = 88 +target-version = ['py313'] +include = '\.pyi?$' +extend-exclude = ''' +/( + # directories + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | build + | dist +)/ +''' + +[tool.mypy] +python_version = "3.13" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +disallow_untyped_decorators = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_no_return = true +warn_unreachable = true +strict_equality = true + +[[tool.mypy.overrides]] +module = [ + "click.*", + "rich.*", + "requests.*", + "Crypto.*", +] +ignore_missing_imports = true diff --git a/src/config_man/__init__.py b/src/config_man/__init__.py new file mode 100644 index 0000000..ace5040 --- /dev/null +++ b/src/config_man/__init__.py @@ -0,0 +1,10 @@ +""" +Config-Man CLI 工具 + +用于管理存储在对象存储中的加密静态资源配置文件。 +支持多版本管理,通过Cloudflare CDN进行分发。 +""" + +__version__ = "0.1.0" +__author__ = "Config-Man Team" +__description__ = "CLI tool for managing encrypted configuration files" \ No newline at end of file diff --git a/src/config_man/cli/__init__.py b/src/config_man/cli/__init__.py new file mode 100644 index 0000000..37c4d27 --- /dev/null +++ b/src/config_man/cli/__init__.py @@ -0,0 +1,9 @@ +""" +CLI 模块 + +包含命令行界面相关的功能。 +""" + +from .main import cli, main + +__all__ = ["cli", "main"] \ No newline at end of file diff --git a/src/config_man/cli/main.py b/src/config_man/cli/main.py new file mode 100644 index 0000000..86c452f --- /dev/null +++ b/src/config_man/cli/main.py @@ -0,0 +1,109 @@ +import click +import os +from ..core.view_command import ViewCommand +from ..utils.mock_rclone import patch_rclone + + +@click.group() +@click.version_option(version="0.1.0") +def cli(): + """Config-Man CLI 工具 + + 用于管理存储在对象存储中的加密静态资源配置文件。 + 支持多版本管理,通过Cloudflare CDN进行分发。 + """ + pass + + +@cli.command() +@click.option('--version', required=True, help='版本号,格式为 0.29, 0.30, 0.31 等') +@click.option('--json', 'json_format', is_flag=True, help='以JSON格式输出') +@click.option('--storage-path', default='ab', help='对象存储路径前缀') +@click.option('--cdn-url', default='', help='CDN基础URL') +@click.option('--mock', is_flag=True, help='使用模拟环境进行测试') +def view(version, json_format, storage_path, cdn_url, mock): + """查看配置文件 + + 对比显示对象存储和CDN中的配置内容,支持Android和iOS平台。 + + 示例: + config-man view --version 0.30 + config-man view --version 0.30 --json + config-man view --version 0.30 --mock + """ + try: + # 如果使用模拟环境,启用rclone模拟 + if mock: + patch_rclone() + click.echo("使用模拟环境进行测试...") + else: + # 检查rclone是否可用 + if not _check_rclone(): + click.echo("错误: rclone 未安装或不可用。请先安装 rclone。") + return 1 + + # 创建查看命令处理器 + view_cmd = ViewCommand(storage_path, cdn_url) + + # 执行查看命令 + success = view_cmd.execute(version, json_format) + + return 0 if success else 1 + + except Exception as e: + click.echo(f"错误: {str(e)}") + return 1 + + +@cli.command() +@click.option('--platform', required=True, type=click.Choice(['android', 'ios']), + help='平台类型') +@click.option('--target-version', required=True, help='目标版本号') +@click.option('--update-version', required=True, help='更新版本号') +def suggest_update(platform, target_version, update_version): + """建议更新 + + 在目标版本的配置文件中添加 Ver_New 属性,提示用户有新版本可用。 + + 示例: + config-man suggest-update --platform android --target-version 0.29 --update-version 0.30 + """ + click.echo("建议更新功能尚未实现") + return 1 + + +@cli.command() +@click.option('--platform', required=True, type=click.Choice(['android', 'ios']), + help='平台类型') +@click.option('--target-version', required=True, help='目标版本号') +@click.option('--update-version', required=True, help='更新版本号') +def force_update(platform, target_version, update_version): + """强制更新 + + 直接更新目标版本的 Ver 属性,强制用户升级到新版本。 + + 示例: + config-man force-update --platform ios --target-version 0.29 --update-version 0.30 + """ + click.echo("强制更新功能尚未实现") + return 1 + + +def _check_rclone(): + """检查rclone是否可用""" + import subprocess + try: + result = subprocess.run(['rclone', 'version'], + capture_output=True, text=True, timeout=5) + return result.returncode == 0 + except (subprocess.TimeoutExpired, FileNotFoundError): + return False + + +def main(): + """主函数""" + cli() + + +if __name__ == "__main__": + main() diff --git a/src/config_man/core/__init__.py b/src/config_man/core/__init__.py new file mode 100644 index 0000000..3b2bda1 --- /dev/null +++ b/src/config_man/core/__init__.py @@ -0,0 +1,11 @@ +""" +核心功能模块 + +包含配置管理、加密解密、显示管理等核心功能。 +""" + +from .config_manager import ConfigManager +from .display_manager import DisplayManager +from .view_command import ViewCommand + +__all__ = ["ConfigManager", "DisplayManager", "ViewCommand"] \ No newline at end of file diff --git a/src/config_man/core/config_manager.py b/src/config_man/core/config_manager.py new file mode 100644 index 0000000..89140b1 --- /dev/null +++ b/src/config_man/core/config_manager.py @@ -0,0 +1,119 @@ +import json +import os +import subprocess +import requests +from typing import Dict, Optional, Tuple +from ..utils.crypto import encrypt, decrypt + + +class ConfigManager: + """配置文件管理器""" + + def __init__(self, storage_path: str = "ab", cdn_base_url: str = ""): + self.storage_path = storage_path + self.cdn_base_url = cdn_base_url + + def version_to_path(self, version: str) -> str: + """将版本号转换为存储路径格式""" + # 0.29 -> 0_29 + return version.replace('.', '_') + + def get_config_path(self, version: str, platform: str) -> str: + """获取配置文件在对象存储中的路径""" + version_path = self.version_to_path(version) + filename = f"{platform}config.json" + return f"{self.storage_path}/{version_path}/{filename}" + + def get_cdn_url(self, version: str, platform: str) -> str: + """获取配置文件在CDN中的URL""" + if not self.cdn_base_url: + return "" + version_path = self.version_to_path(version) + filename = f"{platform}config.json" + return f"{self.cdn_base_url}/{self.storage_path}/{version_path}/{filename}" + + def download_from_storage(self, version: str, platform: str) -> Optional[str]: + """从对象存储下载并解密配置文件""" + try: + config_path = self.get_config_path(version, platform) + + # 使用rclone下载文件 + result = subprocess.run( + ["rclone", "cat", config_path], + capture_output=True, + text=True, + timeout=30 + ) + + if result.returncode != 0: + print(f"警告: 无法从对象存储下载 {config_path}: {result.stderr}") + return None + + # 解密文件内容 + encrypted_content = result.stdout.strip() + if not encrypted_content: + print(f"警告: 文件 {config_path} 为空") + return None + + decrypted_content = decrypt(encrypted_content) + return decrypted_content + + except subprocess.TimeoutExpired: + print(f"错误: 下载 {config_path} 超时") + return None + except Exception as e: + print(f"错误: 下载 {config_path} 失败: {str(e)}") + return None + + def get_from_cdn(self, version: str, platform: str) -> Optional[str]: + """从CDN获取配置文件内容""" + try: + cdn_url = self.get_cdn_url(version, platform) + if not cdn_url: + print(f"警告: CDN URL未配置,跳过CDN内容获取") + return None + + response = requests.get(cdn_url, timeout=10) + response.raise_for_status() + + # CDN中的内容应该是解密后的JSON + return response.text + + except requests.RequestException as e: + print(f"警告: 无法从CDN获取 {cdn_url}: {str(e)}") + return None + except Exception as e: + print(f"错误: 获取CDN内容失败: {str(e)}") + return None + + def parse_config(self, content: str) -> Optional[Dict]: + """解析配置文件内容""" + try: + return json.loads(content) + except json.JSONDecodeError as e: + print(f"错误: 配置文件格式错误: {str(e)}") + return None + + def compare_configs(self, storage_config: Dict, cdn_config: Dict) -> Dict: + """比较存储配置和CDN配置的差异""" + all_keys = set(storage_config.keys()) | set(cdn_config.keys()) + differences = {} + + for key in sorted(all_keys): + storage_value = storage_config.get(key, "N/A") + cdn_value = cdn_config.get(key, "N/A") + + if storage_value != cdn_value: + differences[key] = { + "storage": storage_value, + "cdn": cdn_value, + "different": True + } + else: + differences[key] = { + "storage": storage_value, + "cdn": cdn_value, + "different": False + } + + return differences \ No newline at end of file diff --git a/src/config_man/core/display_manager.py b/src/config_man/core/display_manager.py new file mode 100644 index 0000000..3a2671c --- /dev/null +++ b/src/config_man/core/display_manager.py @@ -0,0 +1,109 @@ +from typing import Dict, List +from rich.console import Console +from rich.table import Table +from rich.panel import Panel +from rich.text import Text +import json + + +class DisplayManager: + """显示管理器""" + + def __init__(self): + self.console = Console() + + def display_config_comparison(self, version: str, platform: str, + storage_config: Dict, cdn_config: Dict, + differences: Dict): + """显示配置对比结果""" + + # 创建标题 + title = f"版本: {version} | 平台: {platform.title()}" + self.console.print(f"\n[bold blue]{title}[/bold blue]") + + # 创建表格 + table = Table(show_header=True, header_style="bold magenta") + table.add_column("配置项", style="cyan", width=20) + table.add_column("对象存储内容", style="green", width=30) + table.add_column("CDN内容", style="yellow", width=30) + table.add_column("状态", style="red", width=10) + + # 添加数据行 + for key, data in differences.items(): + storage_value = str(data["storage"]) + cdn_value = str(data["cdn"]) + status = "❌ 不同" if data["different"] else "✅ 一致" + + # 截断过长的值 + if len(storage_value) > 25: + storage_value = storage_value[:22] + "..." + if len(cdn_value) > 25: + cdn_value = cdn_value[:22] + "..." + + table.add_row(key, storage_value, cdn_value, status) + + self.console.print(table) + + def display_json_format(self, version: str, platform: str, + storage_config: Dict, cdn_config: Dict): + """以JSON格式显示配置""" + + title = f"版本: {version} | 平台: {platform.title()}" + self.console.print(f"\n[bold blue]{title}[/bold blue]") + + # 创建对比JSON + comparison = { + "version": version, + "platform": platform, + "storage_config": storage_config, + "cdn_config": cdn_config + } + + # 格式化JSON输出 + json_str = json.dumps(comparison, indent=2, ensure_ascii=False) + + # 使用面板显示 + panel = Panel( + json_str, + title="JSON格式", + border_style="blue" + ) + self.console.print(panel) + + def display_summary(self, version: str, results: Dict): + """显示总结信息""" + + self.console.print(f"\n[bold green]配置查看总结[/bold green]") + self.console.print(f"版本: {version}") + + for platform in ["android", "ios"]: + if platform in results: + result = results[platform] + if result["success"]: + storage_keys = len(result["storage_config"]) + cdn_keys = len(result["cdn_config"]) + differences = sum(1 for data in result["differences"].values() + if data["different"]) + + status = "✅ 正常" if differences == 0 else f"⚠️ {differences} 项不同" + + self.console.print( + f" {platform.title()}: {status} " + f"(存储: {storage_keys}项, CDN: {cdn_keys}项)" + ) + else: + self.console.print(f" {platform.title()}: ❌ 失败") + else: + self.console.print(f" {platform.title()}: ⚠️ 未检查") + + def display_error(self, message: str): + """显示错误信息""" + self.console.print(f"[bold red]错误: {message}[/bold red]") + + def display_warning(self, message: str): + """显示警告信息""" + self.console.print(f"[bold yellow]警告: {message}[/bold yellow]") + + def display_info(self, message: str): + """显示信息""" + self.console.print(f"[bold blue]信息: {message}[/bold blue]") \ No newline at end of file diff --git a/src/config_man/core/view_command.py b/src/config_man/core/view_command.py new file mode 100644 index 0000000..0d83cbe --- /dev/null +++ b/src/config_man/core/view_command.py @@ -0,0 +1,142 @@ +from typing import Dict, Optional +from .config_manager import ConfigManager +from .display_manager import DisplayManager + + +class ViewCommand: + """查看命令处理器""" + + def __init__(self, storage_path: str = "ab", cdn_base_url: str = ""): + self.config_manager = ConfigManager(storage_path, cdn_base_url) + self.display_manager = DisplayManager() + + def execute(self, version: str, json_format: bool = False) -> bool: + """执行查看命令""" + + # 验证版本号格式 + if not self._validate_version(version): + self.display_manager.display_error(f"无效的版本号格式: {version}") + return False + + self.display_manager.display_info(f"正在查看版本 {version} 的配置文件...") + + results = {} + success = True + + # 检查Android和iOS平台 + for platform in ["android", "ios"]: + result = self._check_platform_config(version, platform) + results[platform] = result + + if not result["success"]: + success = False + + # 显示结果 + if json_format: + self._display_json_results(version, results) + else: + self._display_table_results(version, results) + + # 显示总结 + self.display_manager.display_summary(version, results) + + return success + + def _validate_version(self, version: str) -> bool: + """验证版本号格式""" + try: + # 检查版本号格式 (如 0.29, 0.30, 1.00) + parts = version.split('.') + if len(parts) != 2: + return False + + major, minor = parts + if not major.isdigit() or not minor.isdigit(): + return False + + return True + except: + return False + + def _check_platform_config(self, version: str, platform: str) -> Dict: + """检查指定平台的配置""" + result = { + "success": False, + "storage_config": {}, + "cdn_config": {}, + "differences": {}, + "error": None + } + + try: + # 从对象存储获取配置 + storage_content = self.config_manager.download_from_storage(version, platform) + if storage_content is None: + result["error"] = f"无法从对象存储获取 {platform} 配置" + return result + + storage_config = self.config_manager.parse_config(storage_content) + if storage_config is None: + result["error"] = f"解析对象存储中的 {platform} 配置失败" + return result + + result["storage_config"] = storage_config + + # 从CDN获取配置 + cdn_content = self.config_manager.get_from_cdn(version, platform) + if cdn_content is None: + # CDN获取失败,使用空配置 + result["cdn_config"] = {} + result["differences"] = self.config_manager.compare_configs( + storage_config, {} + ) + else: + cdn_config = self.config_manager.parse_config(cdn_content) + if cdn_config is None: + result["error"] = f"解析CDN中的 {platform} 配置失败" + return result + + result["cdn_config"] = cdn_config + result["differences"] = self.config_manager.compare_configs( + storage_config, cdn_config + ) + + result["success"] = True + + except Exception as e: + result["error"] = f"检查 {platform} 配置时发生错误: {str(e)}" + + return result + + def _display_table_results(self, version: str, results: Dict): + """以表格格式显示结果""" + for platform in ["android", "ios"]: + if platform in results: + result = results[platform] + if result["success"]: + self.display_manager.display_config_comparison( + version, platform, + result["storage_config"], + result["cdn_config"], + result["differences"] + ) + else: + self.display_manager.display_error( + f"{platform.title()} 配置检查失败: {result['error']}" + ) + + def _display_json_results(self, version: str, results: Dict): + """以JSON格式显示结果""" + for platform in ["android", "ios"]: + if platform in results: + result = results[platform] + if result["success"]: + self.display_manager.display_json_format( + version, platform, + result["storage_config"], + result["cdn_config"] + ) + else: + self.display_manager.display_error( + f"{platform.title()} 配置检查失败: {result['error']}" + ) \ No newline at end of file diff --git a/src/config_man/utils/__init__.py b/src/config_man/utils/__init__.py new file mode 100644 index 0000000..162f065 --- /dev/null +++ b/src/config_man/utils/__init__.py @@ -0,0 +1,10 @@ +""" +工具模块 + +包含加密解密、模拟环境等工具功能。 +""" + +from .crypto import encrypt, decrypt +from .mock_rclone import MockRclone, patch_rclone + +__all__ = ["encrypt", "decrypt", "MockRclone", "patch_rclone"] \ No newline at end of file diff --git a/src/config_man/utils/crypto.py b/src/config_man/utils/crypto.py new file mode 100644 index 0000000..90e58bb --- /dev/null +++ b/src/config_man/utils/crypto.py @@ -0,0 +1,23 @@ +import json +import os +from Crypto.Cipher import DES +from Crypto.Util.Padding import pad,unpad +import base64 + +def encrypt(text): + key = 'tbambooz'.encode('ascii') + des = DES.new(key, DES.MODE_CBC, IV=key) + padded_text = pad(text.encode('utf-8'), DES.block_size) + encrypted_text = des.encrypt(padded_text) + return base64.b64encode(encrypted_text).decode('utf-8') + + +def decrypt(encrypted_text): + key = 'tbambooz' + key = key.encode('ascii') + des = DES.new(key, DES.MODE_CBC, IV=key) + decoded_text = base64.b64decode(encrypted_text) + decrypted_text = des.decrypt(decoded_text) + unpadded_text = unpad(decrypted_text, DES.block_size) + return unpadded_text.decode('utf-8') + diff --git a/src/config_man/utils/mock_rclone.py b/src/config_man/utils/mock_rclone.py new file mode 100644 index 0000000..e6ae8aa --- /dev/null +++ b/src/config_man/utils/mock_rclone.py @@ -0,0 +1,153 @@ +import os +import json +import subprocess +from .crypto import encrypt, decrypt + + +class MockRclone: + """模拟rclone环境,用于测试""" + + def __init__(self, test_data_path: str = "test_data"): + self.test_data_path = test_data_path + self._setup_mock_environment() + + def _setup_mock_environment(self): + """设置模拟环境""" + # 确保测试数据目录存在 + os.makedirs(self.test_data_path, exist_ok=True) + + # 创建测试配置文件 + self._create_test_configs() + + def _create_test_configs(self): + """创建测试配置文件""" + test_configs = { + "0.29": { + "android": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.29.1abc123", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==" + }, + "ios": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.29.1def456", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==" + } + }, + "0.30": { + "android": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.30.2ghi789", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==", + "NewFeature": "enabled" + }, + "ios": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.30.2jkl012", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==", + "NewFeature": "enabled" + } + } + } + + # 创建测试目录结构 + for version, platforms in test_configs.items(): + version_path = f"{self.test_data_path}/ab/{version.replace('.', '_')}" + os.makedirs(version_path, exist_ok=True) + + for platform, config in platforms.items(): + filename = f"{platform}config.json" + filepath = os.path.join(version_path, filename) + + # 加密配置并保存 + config_json = json.dumps(config, indent=2, ensure_ascii=False) + encrypted_config = encrypt(config_json) + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(encrypted_config) + + print(f"创建测试文件: {filepath}") + + def mock_rclone_cat(self, path: str) -> str: + """模拟rclone cat命令""" + # 将路径转换为本地文件路径 + local_path = os.path.join(self.test_data_path, path) + + if not os.path.exists(local_path): + raise FileNotFoundError(f"文件不存在: {path}") + + with open(local_path, 'r', encoding='utf-8') as f: + return f.read().strip() + + +def patch_rclone(): + """修补rclone调用,使用模拟环境""" + import subprocess + + original_run = subprocess.run + + def mock_run(cmd, *args, **kwargs): + if len(cmd) >= 2 and cmd[0] == 'rclone' and cmd[1] == 'cat': + # 模拟rclone cat命令 + mock_rclone = MockRclone() + try: + content = mock_rclone.mock_rclone_cat(cmd[2]) + # 返回模拟的subprocess结果 + from types import SimpleNamespace + result = SimpleNamespace() + result.returncode = 0 + result.stdout = content + result.stderr = "" + return result + except FileNotFoundError as e: + # 返回错误结果 + from types import SimpleNamespace + result = SimpleNamespace() + result.returncode = 1 + result.stdout = "" + result.stderr = str(e) + return result + else: + # 对于其他命令,使用原始实现 + return original_run(cmd, *args, **kwargs) + + subprocess.run = mock_run + print("已启用rclone模拟环境") + + +if __name__ == "__main__": + # 创建测试环境 + mock = MockRclone() + print("模拟rclone环境设置完成!") \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..da58017 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,5 @@ +""" +测试包 + +包含单元测试、集成测试和测试工具。 +""" \ No newline at end of file diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 0000000..b976304 --- /dev/null +++ b/tests/fixtures/__init__.py @@ -0,0 +1,5 @@ +""" +测试工具 + +包含测试数据、模拟环境和测试工具。 +""" \ No newline at end of file diff --git a/tests/fixtures/test_configs.py b/tests/fixtures/test_configs.py new file mode 100644 index 0000000..ef66432 --- /dev/null +++ b/tests/fixtures/test_configs.py @@ -0,0 +1,97 @@ +import json +import os +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'src')) + +from config_man.utils.crypto import encrypt + + +def create_test_configs(): + """创建测试配置文件""" + + # 测试配置数据 + test_configs = { + "0.29": { + "android": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.29.1abc123", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==" + }, + "ios": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.29.1def456", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==" + } + }, + "0.30": { + "android": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.30.2ghi789", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==", + "NewFeature": "enabled" + }, + "ios": { + "EventApiURL": "https://n3backend.azurewebsites.net/", + "Ver": "0.30.2jkl012", + "PlayFabTitle": "B066F", + "Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft", + "APP_Store_URL": "https://apps.apple.com/us/app/id6505145935", + "HeartBeat": "60", + "RTMPid": "80000586", + "RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321", + "RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358", + "FuncUrl": "https://leaderboardcreate.azurewebsites.net", + "FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==", + "NewFeature": "enabled" + } + } + } + + # 创建测试目录结构 + for version, platforms in test_configs.items(): + version_path = f"test_data/ab/{version.replace('.', '_')}" + os.makedirs(version_path, exist_ok=True) + + for platform, config in platforms.items(): + filename = f"{platform}config.json" + filepath = os.path.join(version_path, filename) + + # 加密配置并保存 + config_json = json.dumps(config, indent=2, ensure_ascii=False) + encrypted_config = encrypt(config_json) + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(encrypted_config) + + print(f"创建测试文件: {filepath}") + + print("测试配置文件创建完成!") + + +if __name__ == "__main__": + create_test_configs() \ No newline at end of file diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..81d7b46 --- /dev/null +++ b/tests/integration/__init__.py @@ -0,0 +1,5 @@ +""" +集成测试 + +测试模块间的集成功能。 +""" \ No newline at end of file diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..97b2608 --- /dev/null +++ b/tests/unit/__init__.py @@ -0,0 +1,5 @@ +""" +单元测试 + +测试各个模块的独立功能。 +""" \ No newline at end of file