# UAT 备忘录（SDK + MCP + 信息导入）

- 日期: 2026-02-07
- 环境: 本地开发环境（`http://127.0.0.1:8000`）
- 执行人: Codex（模拟用户验收）
- 范围: MCP/SDK 接入可用性 + 信息导入专项（宏观数据、文档 JSON/CSV）

## 一、结论摘要

1. MCP 接入主链路可用，`tests/acceptance/test_mcp_server.py` 通过（9/9）。
2. SDK 接入可用但存在缺陷，`tests/acceptance/test_sdk_connection.py` 在有效 Token 下通过 5 项、跳过 3 项、失败 2 项。
3. 信息导入链路核心能力可用：宏观数据导入、文档 JSON/CSV 导入均成功写库。
4. 发现 2 个高优先级问题影响管理端体验与 SDK 稳定性（见“问题清单”）。

## 二、执行记录

### 1) MCP 验收

- 命令: `pytest -q tests/acceptance/test_mcp_server.py`
- 结果: 9 passed
- 说明: 工具注册、资源读取、prompt 获取、SDK in MCP 上下文初始化均可执行。

### 2) SDK 验收

- 前置: 为 `admin` 生成并使用 API Token（`AGOMTRADEPRO_API_TOKEN`）
- 命令: `pytest -q tests/acceptance/test_sdk_connection.py`
- 结果: 5 passed, 3 skipped, 2 failed
- 失败点:
  - `test_check_signal_eligibility`: 接口连续 500 导致 SDK 重试后失败
  - `test_regime_history`: SDK 解析返回结构时出现 `'list' object has no attribute 'get'`

### 3) 信息导入专项 UAT（模拟用户调用）

执行了 10 个导入相关用例（HTTP 真实请求）：

- 通过 8 项:
  - `macro_create_record_valid`
  - `macro_verify_created_record`
  - `macro_create_record_invalid_payload_rejected`（返回 500，功能上拒绝了非法数据）
  - `macro_quick_sync`
  - `docs_import_missing_file`
  - `docs_import_json`
  - `docs_import_csv_update`
  - `docs_import_invalid_format`
- 未通过 2 项:
  - `admin_login_for_docs_import`（登录后访问文档管理页触发 500）
  - `docs_verify_after_import`（登录后访问文档详情页触发 500）

数据库复核：
- 文档 `slug=uat-doc-json` 已存在，且内容已更新为 `csv import content v2`，说明导入写库成功。

## 三、问题清单（按优先级）

### P1: 登录态访问文档管理/详情页 500

- 现象:
  - `GET /admin/docs/manage/` -> 500
  - `GET /docs/uat-doc-json/`（登录态）-> 500
- 错误:
  - `NoReverseMatch: Reverse for 'token_management' not found`
- 初步定位:
  - `core/templates/base.html` 使用了 `account:token_management` / `account:user_management`
  - 但 `core/urls.py` 当前对 account 的 include 未声明 namespace（`path('account/', include('apps.account.interface.urls'))`）
- 影响:
  - 管理员登录后进入文档相关页面失败，阻断后台文档运营流程。

### P1: SDK `signal.check_eligibility` 稳定失败

- 现象:
  - SDK 调用 `signal/api/check_eligibility/` 连续返回 500，触发重试后失败。
- 影响:
  - 基于 SDK 的资格校验流程不可用。

### P2: SDK `regime.history` 返回结构兼容问题

- 现象:
  - `regime.history()` 解析时假设响应对象支持 `.get()`，实际返回 list，导致异常。
- 影响:
  - SDK 在部分环境下无法读取历史 Regime。

### P3: 宏观数据创建接口对非法参数返回 500（可优化为 400）

- 现象:
  - `/macro/api/record/create/` 缺少 `code` 时由 DB 约束抛错，返回 500。
- 影响:
  - 用户输入错误时提示不友好；不利于前端做精确提示。

## 四、导入能力验收结论

- 宏观数据导入: 通过（手动新增 + 快速同步均可）
- 文档导入（JSON/CSV）: 导入能力本身通过（创建/更新成功，数据已落库）
- 文档导入后可视化验证: 受 P1 路由问题影响，登录态页面验证失败

## 五、建议的修复顺序

1. 修复 account URL namespace 与模板反向解析不一致问题（先恢复文档管理可用性）。
2. 修复 `signal.check_eligibility` 后端 500（查看接口日志和输入校验链路）。
3. 修复 SDK `regime.history` 对 list/dict 的兼容解析。
4. 将宏观创建接口参数错误改为 400 级业务错误返回。

## 六、备注

- 本轮会话级 MCP 资源列表为空（工具层 `list_mcp_resources` 返回空），但项目内 MCP 测试脚本可通过。
- 本次验证以 2026-02-07 本地实时执行结果为准。

## 七、修复后复测（2026-02-07）

已完成修复并复测通过：

1. `core/urls.py`
- 为 `account` 路由补齐 namespace（`account:*` 反向解析恢复）。
- 结果：`/admin/docs/manage/`、`/docs/uat-doc-json/` 登录态访问均返回 200，不再出现 `NoReverseMatch`。

2. `sdk/agomtradepro/modules/regime.py`
- `history()` 增加对 list/dict 两种响应结构兼容解析。
- 结果：SDK `regime_history` 用例通过。

3. `apps/signal/interface/api_views.py`
- 修复 `check_eligibility` 调用参数错误（原逻辑会触发 500）。
- 改为基于 `asset_code` 推断 `asset_class` 后执行准入检查，并返回 SDK 兼容字段。
- 结果：`/signal/api/check_eligibility/` 返回 200，SDK 用例通过。

4. `apps/macro/interface/views/table_api.py`
- `api_create_record` 增加必填参数和日期格式校验，非法请求返回 400。
- 结果：缺少 `code` 的创建请求由 500 改为 400。

复测结果：
- `pytest -q tests/acceptance/test_sdk_connection.py` -> `7 passed, 3 skipped`
- 文档页检查：
  - `GET /admin/docs/manage/` -> 200
  - `GET /docs/uat-doc-json/` -> 200

## 八、MCP 持仓/交易/资金流水导入导出专项复测（2026-02-07）

目标：验证 CLI（Codex/Claude Code）经 MCP 对关键持仓数据执行“读/导入/导出”主链路可用。

前置：
- Django 服务健康检查：`GET /health/` -> 200
- 使用 `admin` Token 作为 MCP 凭证
- 开启 RBAC：`AGOMTRADEPRO_MCP_ENFORCE_RBAC=true`

执行结果：

1. 账户工具基础读取
- `list_portfolios(limit=5)`：通过（返回默认组合）
- 期间发现并修复：
  - `sdk/agomtradepro/modules/account.py` 账户模块基础路径由 `/api/account` 修正为 `/account/api`
  - `sdk/agomtradepro_mcp/tools/account_tools.py` 的 `list_portfolios/get_portfolio/get_positions/create_position` 改为直连后端真实接口，避免 SDK 结构差异导致的 404/字段异常

2. 导入导出主链路（角色：`investment_manager`）
- 组合：`portfolio_id=1`
- 测试资产：`UATMCP163933`
- `import_positions_json`：dry-run 通过；真实写入通过；`export_positions_json` 可检索到资产
- `import_transactions_json`：dry-run 通过；真实写入通过；`export_transactions_json` 可检索到记录
- `import_capital_flows_json`：dry-run 通过；真实写入通过；`export_capital_flows_json` 可检索到记录
- `export_account_bundle_json`：通过，返回计数：
  - `positions=3`
  - `transactions=2`
  - `capital_flows=2`

3. RBAC 权限验证（角色矩阵）
- 新增单测：`sdk/tests/test_mcp/test_rbac.py`（20 条用例）
- 结果：`20 passed`
- 关键在线验证：
  - 角色 `read_only` 调用 `import_positions_json` 被拒绝（符合预期，write 权限被拦截）

4. 回归验证
- `pytest -q tests/acceptance/test_mcp_server.py` -> `9 passed`

结论：
- MCP 已可覆盖关键账户数据的读取、导入、导出和打包导出。
- RBAC 已覆盖管理员、所有者、分析师、投资经理、交易员、风控、只读用户 7 类角色，并在工具/资源/prompt 三层生效。

## 九、SDK 兼容性修复（2026-02-07）

问题：
- `client.account.get_portfolios()` 触发 `KeyError: 'cash'`，导致 SDK 验收剩余 1 项失败。

修复：
- 文件：`sdk/agomtradepro/modules/account.py`
1. 账户 API 前缀统一到后端实际路径：`/account/api`
2. `Portfolio` 解析兼容 `cash` 缺失（默认 `0.0`）
3. `Position` 解析兼容后端字段：
   - `shares` -> `quantity`
   - `unrealized_pnl` -> `profit_loss`
4. `create_position/update_position` 请求体改为后端实际字段（`portfolio/shares/avg_cost/current_price`）

复测：
- `pytest -q tests/acceptance/test_sdk_connection.py` -> `8 passed, 2 skipped`
- `pytest -q tests/acceptance/test_mcp_server.py` -> `9 passed`

## 十、系统 RBAC 与 MCP 对齐（2026-02-07）

目标：让“系统用户角色”与 “MCP RBAC”使用同一套角色语义与权限矩阵，避免双轨配置漂移。

实施内容：

1. 后端统一 RBAC 模型与矩阵
- 新增统一 RBAC 逻辑：`apps/account/application/rbac.py`
- 角色：`admin/owner/analyst/investment_manager/trader/risk/read_only`
- 新增账户角色字段：`AccountProfileModel.rbac_role`
  - 模型：`apps/account/infrastructure/models.py`
  - 迁移：`apps/account/migrations/0013_accountprofilemodel_rbac_role.py`
  - 数据迁移策略：`superuser -> admin`，其余用户 -> `owner`

2. 系统侧权限接入统一角色
- 管理员页面权限由“仅 superuser”升级为“系统 RBAC admin”判定：
  - `apps/account/interface/views.py` (`is_admin_user -> is_system_admin`)
- 用户注册时同步初始化 `rbac_role`：
  - 首个自动管理员：`admin`
  - 其余用户：`owner`
- 新增管理员设置用户角色接口：
  - `POST /account/admin/users/<user_id>/role/`
  - 路由：`apps/account/interface/urls.py`

3. Account API 按 RBAC 矩阵限权
- 新增 DRF 权限类：`apps/account/interface/permissions.py`
- 交易域接口（组合/持仓/交易/资金流水）接入 `TradingPermission`
- 账户配置与健康检查接入 `GeneralPermission`
- 结果：系统 API 写操作将按角色矩阵限制，不再只依赖“是否已登录”

4. MCP 与系统角色自动对齐
- MCP RBAC 默认从后端用户资料读取 `rbac_role`（`account/api/profile/`）
- `AGOMTRADEPRO_MCP_ROLE` 仍可作为强制覆盖
- 文档更新：`docs/mcp/mcp_guide.md`

5. 验证结果
- `pytest -q sdk/tests/test_mcp/test_rbac.py` -> `22 passed`
- `pytest -q tests/acceptance/test_sdk_connection.py` -> `8 passed, 2 skipped`
- `pytest -q tests/acceptance/test_mcp_server.py` -> `9 passed`
- 迁移执行：`python manage.py migrate` -> `account.0013 ... OK`

结论：
- 系统用户角色与 MCP RBAC 已对齐到同一角色体系。
- 后端角色变更可直接驱动 MCP 权限，无需重复维护两套角色配置。

