refactor: establish standalone application boundary
@@ -0,0 +1,9 @@
|
||||
# 文档索引
|
||||
|
||||
- `product/小白复盘-完整产品规格说明书.md`:从零恢复产品时的完整功能与行为资产。
|
||||
- `maintenance/人工维护指南.md`:当前正式源码的启动、修改、验收、数据和回退流程。
|
||||
- `governance/`:架构决策、注册表治理和历次结构治理记录。
|
||||
- `migration/`:从旧根目录保真迁入`app/`的历史账本、证据和失败版本记录。
|
||||
|
||||
日常维护优先阅读根目录`AGENTS.md`、`ARCHITECTURE.md`和维护指南。`migration/`只用于审计与
|
||||
追溯,不参与应用启动、测试选择或运行时路径解析。
|
||||
@@ -0,0 +1,75 @@
|
||||
# ADR 0001: Govern as a Modular Monolith
|
||||
|
||||
Status: Accepted
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Context
|
||||
|
||||
The application is deployed on a LAN NAS as one Docker container with a Python HTTP process,
|
||||
SQLite, a build-free browser client, scheduled refresh work, external market providers, and
|
||||
LLM features. Product breadth has grown, while routing, persistence, frontend state, and CSS
|
||||
remain concentrated in a few large files.
|
||||
|
||||
The system may later become internet-facing, gain more features, replace SQLite, or move jobs
|
||||
to workers. It does not currently have load or team boundaries that justify distributed
|
||||
services.
|
||||
|
||||
## Decision
|
||||
|
||||
Retain one deployable application and introduce strict internal modules, ports, adapters,
|
||||
feature registries, data contracts, repository contracts, and regression gates.
|
||||
|
||||
The deployment remains:
|
||||
|
||||
```text
|
||||
one image + one application process + one persistent data volume + port 8765
|
||||
```
|
||||
|
||||
Internal code moves toward:
|
||||
|
||||
```text
|
||||
delivery -> application services -> ports -> infrastructure
|
||||
```
|
||||
|
||||
Compatibility facades permit incremental migration. No feature is rewritten solely to match
|
||||
the target directory structure.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Current NAS deployment stays simple.
|
||||
- Refactoring can proceed in reversible stages.
|
||||
- Feature ownership and account boundaries become visible.
|
||||
- Data, database, LLM, and job adapters can be replaced later.
|
||||
- A measured hotspot can be extracted without first untangling business logic.
|
||||
|
||||
### Costs
|
||||
|
||||
- The transition temporarily contains old and new entrypoints.
|
||||
- Boundary tests and registries require ongoing maintenance.
|
||||
- A single process remains a capacity and fault-isolation limit until infrastructure is
|
||||
deliberately extracted.
|
||||
|
||||
## Rejected Alternatives
|
||||
|
||||
### Immediate microservices
|
||||
|
||||
Rejected because they add network contracts, service discovery, deployment coordination,
|
||||
distributed tracing, and failure modes before load requires them.
|
||||
|
||||
### Full framework rewrite
|
||||
|
||||
Rejected because replacing the HTTP and frontend frameworks while moving boundaries would
|
||||
combine structural and behavioral risk.
|
||||
|
||||
### Continue patching flat modules
|
||||
|
||||
Rejected because current file size, direct provider creation, global page state, and CSS
|
||||
override layers already make regressions difficult to isolate.
|
||||
|
||||
## Revisit Conditions
|
||||
|
||||
Reconsider service extraction when one module has independently measured scaling needs,
|
||||
requires a separate availability boundary, or needs an independent release lifecycle.
|
||||
@@ -0,0 +1,317 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"captured_from": "governed source tree",
|
||||
"runtime": {
|
||||
"http_server": "http.server.ThreadingHTTPServer",
|
||||
"application_processes": 1,
|
||||
"database": "SQLite WAL",
|
||||
"frontend": "build-free HTML/CSS/JavaScript",
|
||||
"container_port": 8765
|
||||
},
|
||||
"counts": {
|
||||
"primary_pages": 16,
|
||||
"api_exact_paths": 53,
|
||||
"api_prefixes": 0,
|
||||
"api_patterns": 11,
|
||||
"database_tables": 34
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"id": "sentimentCycleView",
|
||||
"title": "情绪周期"
|
||||
},
|
||||
{
|
||||
"id": "limitPool",
|
||||
"title": "涨停池"
|
||||
},
|
||||
{
|
||||
"id": "brokenView",
|
||||
"title": "炸板池"
|
||||
},
|
||||
{
|
||||
"id": "downView",
|
||||
"title": "跌停板"
|
||||
},
|
||||
{
|
||||
"id": "yesterdayView",
|
||||
"title": "昨日涨停"
|
||||
},
|
||||
{
|
||||
"id": "performanceView",
|
||||
"title": "涨停表现"
|
||||
},
|
||||
{
|
||||
"id": "ladderView",
|
||||
"title": "市场天梯"
|
||||
},
|
||||
{
|
||||
"id": "rotationView",
|
||||
"title": "板块轮动"
|
||||
},
|
||||
{
|
||||
"id": "auctionView",
|
||||
"title": "集合竞价"
|
||||
},
|
||||
{
|
||||
"id": "themeLibraryView",
|
||||
"title": "题材库"
|
||||
},
|
||||
{
|
||||
"id": "popularityView",
|
||||
"title": "人气热榜"
|
||||
},
|
||||
{
|
||||
"id": "dragonView",
|
||||
"title": "龙虎榜"
|
||||
},
|
||||
{
|
||||
"id": "screenerView",
|
||||
"title": "智能选股"
|
||||
},
|
||||
{
|
||||
"id": "mentorView",
|
||||
"title": "问师"
|
||||
},
|
||||
{
|
||||
"id": "heavenView",
|
||||
"title": "问天"
|
||||
},
|
||||
{
|
||||
"id": "reviewWorkspaceView",
|
||||
"title": "我的复盘"
|
||||
}
|
||||
],
|
||||
"api": {
|
||||
"exact": [
|
||||
"/api/account/birth-profile",
|
||||
"/api/account/password",
|
||||
"/api/account/status",
|
||||
"/api/admin/membership",
|
||||
"/api/admin/refresh",
|
||||
"/api/admin/settings",
|
||||
"/api/admin/settings/test",
|
||||
"/api/alerts",
|
||||
"/api/alerts/read-all",
|
||||
"/api/assistant/chat",
|
||||
"/api/assistant/messages",
|
||||
"/api/auction",
|
||||
"/api/auth/login",
|
||||
"/api/auth/logout",
|
||||
"/api/auth/me",
|
||||
"/api/auth/register",
|
||||
"/api/backfill",
|
||||
"/api/chart/intraday",
|
||||
"/api/dashboard",
|
||||
"/api/dragon-tiger",
|
||||
"/api/dragon-tiger/profiles",
|
||||
"/api/health",
|
||||
"/api/heaven/hexagram",
|
||||
"/api/heaven/interpret",
|
||||
"/api/heaven/personal",
|
||||
"/api/heaven/readings",
|
||||
"/api/heaven/sector-phases",
|
||||
"/api/heaven/setup",
|
||||
"/api/mentors/chat",
|
||||
"/api/mentors/messages",
|
||||
"/api/mentors/preferences",
|
||||
"/api/mentors/setup",
|
||||
"/api/notes",
|
||||
"/api/popularity",
|
||||
"/api/realtime-aggregate/health",
|
||||
"/api/reasons",
|
||||
"/api/rotation/history",
|
||||
"/api/rotation/members",
|
||||
"/api/screener/compile",
|
||||
"/api/screener/run",
|
||||
"/api/screener/setup",
|
||||
"/api/screener/strategies",
|
||||
"/api/screener/sync",
|
||||
"/api/screener/tracking",
|
||||
"/api/screener/tracking/refresh",
|
||||
"/api/search",
|
||||
"/api/search/detail",
|
||||
"/api/seat-aliases",
|
||||
"/api/sentiment/history",
|
||||
"/api/themes",
|
||||
"/api/themes/detail",
|
||||
"/api/trades",
|
||||
"/api/watchlist"
|
||||
],
|
||||
"prefixes": [],
|
||||
"patterns": [
|
||||
"/api/alerts/(\\d+)",
|
||||
"/api/alerts/(\\d+)/read",
|
||||
"/api/heaven/readings/(\\d+)",
|
||||
"/api/heaven/sector-phases/(.+)",
|
||||
"/api/notes/(\\d+)",
|
||||
"/api/screener/strategies/(\\d+)",
|
||||
"/api/screener/tracking/(\\d+)",
|
||||
"/api/stock/(\\d{6})",
|
||||
"/api/stock/(\\d{6})/preview",
|
||||
"/api/trades/(\\d+)",
|
||||
"/api/watchlist/(\\d{6})"
|
||||
]
|
||||
},
|
||||
"database_tables": [
|
||||
"users",
|
||||
"user_sessions",
|
||||
"user_credentials",
|
||||
"user_birth_profiles",
|
||||
"system_settings",
|
||||
"llm_usage",
|
||||
"dashboard_snapshots",
|
||||
"sync_runs",
|
||||
"data_snapshots",
|
||||
"watchlist",
|
||||
"review_notes",
|
||||
"reason_overrides",
|
||||
"seat_aliases",
|
||||
"sector_phase_overrides",
|
||||
"stock_master",
|
||||
"daily_bars",
|
||||
"benchmark_bars",
|
||||
"daily_indicators",
|
||||
"fundamental_indicators",
|
||||
"moneyflow_daily",
|
||||
"auction_factors",
|
||||
"earnings_events",
|
||||
"popularity_factors",
|
||||
"lhb_institution_daily",
|
||||
"screener_strategies",
|
||||
"screener_runs",
|
||||
"mentor_messages",
|
||||
"mentor_preferences",
|
||||
"wencai_saved_queries",
|
||||
"strategy_tracks",
|
||||
"alerts",
|
||||
"trade_entries",
|
||||
"assistant_messages",
|
||||
"heaven_readings"
|
||||
],
|
||||
"background_job_methods": [
|
||||
"_background_refresh_tick",
|
||||
"_schedule_automatic_screeners",
|
||||
"_schedule_ifind_event_enrichment",
|
||||
"run_automatic_screeners"
|
||||
],
|
||||
"external_data_adapters": [
|
||||
{
|
||||
"provider": "tushare",
|
||||
"path": "tushare_client.py",
|
||||
"runtime_role": "primary deterministic market data"
|
||||
},
|
||||
{
|
||||
"provider": "ifind",
|
||||
"path": "ifind_client.py",
|
||||
"runtime_role": "realtime, charts, snapshots, enrichment"
|
||||
},
|
||||
{
|
||||
"provider": "eastmoney",
|
||||
"path": "chart_data_provider.py",
|
||||
"runtime_role": "display chart fallback"
|
||||
},
|
||||
{
|
||||
"provider": "eastmoney",
|
||||
"path": "realtime_aggregator.py",
|
||||
"runtime_role": "isolated realtime observation"
|
||||
},
|
||||
{
|
||||
"provider": "tencent",
|
||||
"path": "realtime_aggregator.py",
|
||||
"runtime_role": "index observation fallback"
|
||||
}
|
||||
],
|
||||
"llm_entrypoints": [
|
||||
{
|
||||
"function": "stream_with_mentor",
|
||||
"path": "mentor_agent.py"
|
||||
},
|
||||
{
|
||||
"function": "interpret_heaven",
|
||||
"path": "heaven_agent.py"
|
||||
},
|
||||
{
|
||||
"function": "stream_review_assistant",
|
||||
"path": "assistant_agent.py"
|
||||
},
|
||||
{
|
||||
"function": "compile_strategy_with_llm",
|
||||
"path": "llm_strategy.py"
|
||||
},
|
||||
{
|
||||
"function": "test_llm_connection",
|
||||
"path": "llm_strategy.py"
|
||||
}
|
||||
],
|
||||
"css_layers": [
|
||||
"/shared/tokens.css?v=20260729-1",
|
||||
"/styles.css",
|
||||
"/renovation.css?v=20260725-5",
|
||||
"/redesign-v2.css?v=20260728-1",
|
||||
"/design-system.css?v=20260728-4",
|
||||
"/theme.css?v=20260728-2",
|
||||
"/wentian-v2.css?v=20260728-7"
|
||||
],
|
||||
"code_hotspots": [
|
||||
{
|
||||
"path": "static/app.js",
|
||||
"bytes": 441313,
|
||||
"lines": 9283
|
||||
},
|
||||
{
|
||||
"path": "static/styles.css",
|
||||
"bytes": 361780,
|
||||
"lines": 15465
|
||||
},
|
||||
{
|
||||
"path": "server.py",
|
||||
"bytes": 263744,
|
||||
"lines": 5856
|
||||
},
|
||||
{
|
||||
"path": "static/redesign-v2.css",
|
||||
"bytes": 263539,
|
||||
"lines": 8570
|
||||
},
|
||||
{
|
||||
"path": "static/index.html",
|
||||
"bytes": 134831,
|
||||
"lines": 1890
|
||||
},
|
||||
{
|
||||
"path": "database.py",
|
||||
"bytes": 121546,
|
||||
"lines": 2839
|
||||
},
|
||||
{
|
||||
"path": "screener.py",
|
||||
"bytes": 108535,
|
||||
"lines": 2213
|
||||
},
|
||||
{
|
||||
"path": "tushare_client.py",
|
||||
"bytes": 94312,
|
||||
"lines": 2175
|
||||
},
|
||||
{
|
||||
"path": "static/renovation.css",
|
||||
"bytes": 83949,
|
||||
"lines": 1553
|
||||
},
|
||||
{
|
||||
"path": "static/wentian-v2.css",
|
||||
"bytes": 73222,
|
||||
"lines": 1084
|
||||
},
|
||||
{
|
||||
"path": "market_insights.py",
|
||||
"bytes": 58066,
|
||||
"lines": 1312
|
||||
},
|
||||
{
|
||||
"path": "static/theme.css",
|
||||
"bytes": 36427,
|
||||
"lines": 1253
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
# Application Architecture Standard
|
||||
|
||||
Status: Accepted
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## 1. Objective
|
||||
|
||||
The application remains a deployable modular monolith while its internal boundaries are made
|
||||
explicit. Governance must preserve current functionality, account isolation, visual behavior,
|
||||
and Docker simplicity. A structural refactor is not permission to change product behavior.
|
||||
|
||||
## 2. Target Topology
|
||||
|
||||
```text
|
||||
browser
|
||||
-> frontend shared API client
|
||||
-> backend HTTP controllers
|
||||
-> feature application services
|
||||
-> repositories / DataGateway / LLMGateway
|
||||
-> SQLite / market providers / model providers
|
||||
|
||||
background jobs
|
||||
-> the same feature application services
|
||||
-> the same repositories and gateways
|
||||
```
|
||||
|
||||
The browser and jobs are two delivery mechanisms. Neither owns business rules.
|
||||
|
||||
## 3. Target Source Layout
|
||||
|
||||
```text
|
||||
frontend/
|
||||
shared/
|
||||
tokens.css
|
||||
shell.js
|
||||
api.js
|
||||
state.js
|
||||
components/
|
||||
pages/<feature>/
|
||||
page.js
|
||||
desktop.css
|
||||
mobile.css
|
||||
pages.config.js
|
||||
|
||||
backend/
|
||||
bootstrap/
|
||||
http/
|
||||
router.py
|
||||
auth.py
|
||||
errors.py
|
||||
schemas/
|
||||
features/<feature>/
|
||||
routes.py
|
||||
service.py
|
||||
repository.py
|
||||
schemas.py
|
||||
data/
|
||||
gateway.py
|
||||
policy.py
|
||||
contracts.py
|
||||
providers/
|
||||
database/
|
||||
connection.py
|
||||
migrations/
|
||||
repositories/
|
||||
jobs/
|
||||
llm/
|
||||
gateway.py
|
||||
usage.py
|
||||
models.py
|
||||
prompts/
|
||||
features_config.py
|
||||
|
||||
tests/
|
||||
data/
|
||||
server.py
|
||||
```
|
||||
|
||||
The migration may use compatibility facades. Old modules are removed only after all callers
|
||||
move and regression gates pass.
|
||||
|
||||
## 4. Dependency Direction
|
||||
|
||||
Allowed direction:
|
||||
|
||||
```text
|
||||
HTTP / jobs -> feature services -> ports -> infrastructure adapters
|
||||
frontend pages -> shared components/state/API -> backend API
|
||||
```
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
1. Frontend code must not call external market or LLM providers directly.
|
||||
2. HTTP controllers must not contain scoring, screening, divination, or persistence logic.
|
||||
3. Feature services must not instantiate Tushare, iFinD, Eastmoney, Tencent, SQLite, or LLM
|
||||
clients directly.
|
||||
4. Provider adapters must not import feature services.
|
||||
5. Repositories must not call external providers.
|
||||
6. Features must not read another feature's tables directly; cross-feature work goes through
|
||||
an application service or declared read model.
|
||||
7. Background jobs must call the same services used by HTTP flows rather than duplicate
|
||||
calculations.
|
||||
|
||||
## 5. Feature Ownership
|
||||
|
||||
Each feature owns its routes, application service, schemas, repository interface, page module,
|
||||
tests, and documentation. Initial feature IDs are defined in `features.config.json` during
|
||||
Stage 04.
|
||||
|
||||
Shared code is allowed only when at least two features use the same stable behavior. A shared
|
||||
module must not branch on page names or feature IDs to emulate unrelated components.
|
||||
|
||||
## 6. HTTP Contract
|
||||
|
||||
- Public application APIs use `/api/<resource>` and UTF-8 JSON.
|
||||
- Controllers validate transport input, authorize the request, call one application service,
|
||||
and serialize the result.
|
||||
- Dates exposed to users use `YYYY-MM-DD`; provider-specific compact dates stay inside
|
||||
adapters.
|
||||
- Datetimes include an explicit timezone. Market time is interpreted as Asia/Shanghai.
|
||||
- Successful collections use `items`; pagination uses `page`, `page_size`, and `total` when
|
||||
required.
|
||||
- Errors use a stable `code`, a user-safe `message`, and a request correlation ID. Provider
|
||||
credentials and raw response bodies never appear in browser errors.
|
||||
- Existing response shapes remain compatible until a versioned migration is approved.
|
||||
- Backend authorization is authoritative. Hidden frontend controls are not a security rule.
|
||||
|
||||
## 7. Data Contract
|
||||
|
||||
Every calculation field declares:
|
||||
|
||||
- canonical field ID and Chinese display label;
|
||||
- entity and frequency;
|
||||
- type, unit, precision, timezone, and adjustment mode;
|
||||
- unique primary source and permitted fallback sources;
|
||||
- display-only or calculation-eligible status;
|
||||
- freshness and completeness thresholds;
|
||||
- missing-value behavior;
|
||||
- point-in-time availability rules;
|
||||
- owning dataset and persistence location.
|
||||
|
||||
Missing calculation data fails closed. A strategy may not silently replace a required field
|
||||
with a proxy and continue under the original strategy name. Display fallbacks cannot enter
|
||||
screening, scoring, backtesting, sentiment, or Wentian calculations unless explicitly approved
|
||||
for that canonical field.
|
||||
|
||||
One OHLC bar or factor history must not mix providers. Source changes are recorded with the
|
||||
stored observation and invalidate incompatible cached calculations.
|
||||
|
||||
## 8. Market Provider Policy
|
||||
|
||||
- Tushare is the default deterministic source for master data, calendar, daily bars,
|
||||
fundamentals, valuations, industry data, lists, and post-close datasets.
|
||||
- iFinD is the default source for licensed realtime snapshots, intraday data, dynamic auction
|
||||
observations, charts, and approved event enrichment.
|
||||
- Eastmoney and Tencent public endpoints are display or observation fallbacks only unless a
|
||||
field contract explicitly promotes them.
|
||||
- All provider traffic passes through `DataGateway`; provider classes only translate their own
|
||||
protocol into canonical contracts.
|
||||
- Retries, timeouts, quotas, cache TTL, circuit state, and provenance are centralized.
|
||||
|
||||
## 9. Persistence Standard
|
||||
|
||||
- SQLite WAL remains the current deployment database.
|
||||
- Schema changes use ordered migration files with an immutable version ID.
|
||||
- A migration is transactional where SQLite permits it and must be safe to run once.
|
||||
- Destructive migrations require a verified backup and explicit acceptance.
|
||||
- User-owned tables include `user_id`, a foreign key, an ownership index, and cross-account
|
||||
tests.
|
||||
- Repositories return domain-shaped records; controllers never execute SQL.
|
||||
- Financial and research data preserve announcement timestamps to prevent look-ahead use.
|
||||
- A future PostgreSQL adapter must satisfy the same repository contracts.
|
||||
|
||||
## 10. Background Jobs
|
||||
|
||||
- Jobs declare an ID, schedule, input date policy, dependencies, lock key, timeout, retry
|
||||
policy, and idempotency key.
|
||||
- Job runs persist start, completion, failure code, retry count, source coverage, and output
|
||||
version.
|
||||
- A process restart must not duplicate a completed post-close calculation.
|
||||
- Exceptions are logged and surfaced in system management; they are never silently discarded.
|
||||
- The current in-process runner may remain, but jobs cannot depend on thread-local request
|
||||
context. This permits later extraction to a worker without changing business services.
|
||||
|
||||
## 11. LLM Standard
|
||||
|
||||
All model calls pass through `LLMGateway`, which owns:
|
||||
|
||||
- membership authorization and feature availability;
|
||||
- quota reservation, settlement, and daily limits;
|
||||
- model-pool selection and fallback;
|
||||
- timeout, cancellation, retry, and streaming protocol;
|
||||
- prompt version, model, latency, token usage, and failure audit;
|
||||
- removal of secrets and engineering details from user-visible errors.
|
||||
|
||||
Skill evidence, source material, and data profiles remain separate from reusable prompt
|
||||
templates. Deterministic calculations happen before the LLM call and are not delegated to the
|
||||
model.
|
||||
|
||||
## 12. Frontend Standard
|
||||
|
||||
- `shared/api.js` is the only browser request exit.
|
||||
- `shared/shell.js` owns sidebar, topbar, market summary, status bar, global dialogs, and page
|
||||
mounting.
|
||||
- `pages.config.js` owns navigation metadata and page loading, not authorization truth.
|
||||
- A page module fetches data, owns page-local state, and composes shared components.
|
||||
- Global mutable page state and cross-page DOM queries are prohibited after migration.
|
||||
- Tables use shared shells with page-owned column schemas and formatters.
|
||||
- Opening, closing, focus management, and feedback for dialogs use one dialog service.
|
||||
|
||||
## 13. CSS and Design Tokens
|
||||
|
||||
The cascade order is fixed:
|
||||
|
||||
```text
|
||||
tokens -> reset/base -> shell/layout -> shared components -> page styles -> theme overrides
|
||||
```
|
||||
|
||||
- Literal colors, font sizes, and spacing are introduced through tokens first.
|
||||
- Page styles are scoped to the page root and cannot redefine the shell or another page.
|
||||
- A later file cannot be used indefinitely as a patch layer for an earlier file.
|
||||
- Shared components have one authoritative definition.
|
||||
- CSS is removed only after selector/reference scanning, light/dark screenshots, and full
|
||||
browser regression.
|
||||
|
||||
## 14. Mobile Standard
|
||||
|
||||
Mobile shares tokens, data, permissions, components, and state with desktop, but it may use a
|
||||
different composition and interaction model.
|
||||
|
||||
- Shared mobile shell rules live in `shared/mobile-shell.css`.
|
||||
- Each complex page may own `mobile.css` and a mobile view renderer.
|
||||
- One global all-page `mobile.css` override pile is prohibited.
|
||||
- Desktop tables may become summary lists and detail views on mobile.
|
||||
- Mobile acceptance is performed independently at 390x844 and 430x932.
|
||||
- Mobile changes must not alter approved desktop geometry.
|
||||
|
||||
## 15. Configuration and Secrets
|
||||
|
||||
- Environment variables seed deployment configuration; encrypted system settings hold managed
|
||||
runtime credentials.
|
||||
- Tokens, API keys, refresh tokens, model secrets, and encryption keys never enter Git, logs,
|
||||
API responses, or browser storage.
|
||||
- Feature flags, page metadata, field contracts, and permissions are versioned configuration,
|
||||
not ad hoc conditionals.
|
||||
|
||||
## 16. Observability
|
||||
|
||||
External calls record provider, operation, elapsed time, cache result, freshness, normalized
|
||||
error code, and correlation ID. Logs must not contain secrets or personal birth data.
|
||||
|
||||
Health checks distinguish process health, database health, provider degradation, background
|
||||
job health, and model availability. Provider degradation does not make the process health
|
||||
endpoint fail unless the application itself cannot serve stored data.
|
||||
|
||||
## 17. Change and Release Contract
|
||||
|
||||
Every governance phase must:
|
||||
|
||||
1. begin from a clean Git worktree;
|
||||
2. preserve the Stage 01 baseline contract;
|
||||
3. include migration or compatibility tests for changed boundaries;
|
||||
4. pass `python tools/verify_baseline.py`;
|
||||
5. run Playwright at phase boundaries affecting runtime or frontend behavior;
|
||||
6. document residual risk;
|
||||
7. create and push a dedicated rollback commit.
|
||||
|
||||
No phase combines framework replacement, visual redesign, and business-rule changes.
|
||||
|
||||
## 18. Public-Internet Evolution
|
||||
|
||||
The modular monolith remains the default. Reverse proxy/TLS, PostgreSQL, Redis, durable jobs,
|
||||
central secrets, monitoring, and rate limiting can replace infrastructure adapters later.
|
||||
Feature services and frontend contracts must not depend on whether those adapters are local or
|
||||
remote. A feature is extracted into a separate service only when measured load, independent
|
||||
release needs, or fault isolation justifies the operational cost.
|
||||
@@ -0,0 +1,401 @@
|
||||
# `app/`代码减法账本
|
||||
|
||||
> 基线:`xiaobai-preservation-complete-20260801`
|
||||
> 工作目录:只允许修改`webapp/app/`;原版根目录和冻结的`next/`只读
|
||||
> 目标:删除重复实现和历史补丁,不改变功能、视觉、交互、动画、计算、权限、API或数据行为
|
||||
|
||||
## 固定规则
|
||||
|
||||
1. 每批只处理一个明确边界,先证明重复或无消费者,再修改。
|
||||
2. 新共享实现必须在同一提交删除全部被替代实现;禁止只加一层包装。
|
||||
3. 运行代码总量原则上不得增加;测试和证据代码单独统计。
|
||||
4. 迁移期源码相等测试不得简单删除。发生已批准的结构重构时,必须替换成行为、错误语义和
|
||||
唯一所有权契约。
|
||||
5. 每批通过领域测试、全量候选测试、独立导出测试和受影响的浏览器流程后才建立Git检查点。
|
||||
6. CSS最后处理;没有逐页日间、夜间和多视口截图证据,不删除视觉规则。
|
||||
|
||||
## 批次记录
|
||||
|
||||
| 批次 | 边界 | 基线问题 | 目标 | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| CR-01 | LLM供应商传输 | 问师、问天、复盘助手和策略编译各自构造HTTP请求、解析响应和读取错误 | 只保留`backend/llm/transport.py`一个网络出口 | 已完成 |
|
||||
| CR-02 | HTTP精确POST委托 | 27个端点重复使用“比较路径、调用无参数处理器、返回”三行分支 | 用公开/受保护两张显式映射统一委托,同时保留复杂路由的原控制流 | 已完成 |
|
||||
| CR-03 | 股票市场后缀转换 | Tushare业务与iFinD图表各保留一份完全相同的沪深京代码转换函数 | 图表复用`bootstrap/config.py::tushare_code`,只保留一份函数体 | 已完成 |
|
||||
| CR-04 | 数值归一化策略 | 四个业务模块分别保留两组完全相同的数值转换函数体 | 由`backend/data/numbers.py`集中拥有两种既有语义,消费者保留原局部别名 | 已完成 |
|
||||
| CR-05 | 根级兼容入口 | 正式后端仍有五处通过迁移兼容模块反向导入规范实现 | 正式代码改用规范路径;兼容入口只服务原公开导入契约 | 已完成 |
|
||||
| CR-06 | 紧凑日期显示 | Tushare与选股引擎各保留一份完全相同的`YYYYMMDD`显示转换 | 由`bootstrap/config.py`拥有唯一格式策略,消费者保留原局部别名 | 已完成 |
|
||||
| CR-07 | 数据Provider组装 | 核查网关、容器和业务服务是否重复创建外部数据客户端 | 固化唯一创建位置及兼容例外,不改数据源语义 | 已完成 |
|
||||
| CR-08 | NDJSON流式传输 | 问师与复盘助手重复维护响应头、事件写入、完成、断线及关闭流程 | HTTP共享层拥有唯一流式连接生命周期 | 已完成 |
|
||||
| CR-09 | 应用服务门面 | 两个仅服务股池iFinD补全的方法仍错位在全局`DashboardService` | 原函数体机械归位到`PoolServiceMixin` | 已完成 |
|
||||
| CR-10 | Repository所有权 | 五行行业阶段覆盖的三项持久化方法仍错位在根级数据库门面 | 原函数体机械归位到问天Repository,兼容数据继续保留 | 已完成 |
|
||||
| CR-11 | 后台任务生命周期 | 导入应用即启动调度线程,启动早于端口绑定,停止只置位但不等待 | 运行时显式启停,每个Runner只拥有一个可等待的调度线程 | 已完成 |
|
||||
| CR-12 | CSS跨层精确重复 | 七层样式保留多次视觉改造形成的重复顶层规则,前层声明被后层逐字覆盖 | 只删除能够由CSSOM证明完全重复的前层规则,并建立浏览器级重复门禁 | 已完成 |
|
||||
| CR-13 | CSS跨文件嵌套精确重复 | 相同媒体上下文中的完整规则分散在不同样式文件,前层声明仍被后层完整重复 | 递归核对CSSOM上下文,只退休跨文件的精确副本 | 已完成 |
|
||||
| CR-14 | CSS同文件精确重复 | 同一文件、同一媒体上下文仍保留多轮视觉调整形成的完整重复规则 | 删除较早副本并把同文件重复纳入浏览器门禁 | 已完成 |
|
||||
|
||||
## CR-01验收口径
|
||||
|
||||
- 四个功能模块不得包含`urllib.request`或`/chat/completions`。
|
||||
- 非流式与流式请求的URL、鉴权、User-Agent、SSE累积和空响应行为保持不变。
|
||||
- 各功能原有错误类型和用户可见错误文案保持不变。
|
||||
- `LLMGateway`的会员、额度、主辅回退、首字后不中途换模型和审计规则保持不变。
|
||||
- 架构清单必须登记唯一传输入口;完整测试和真实主模型最小调用通过。
|
||||
|
||||
## CR-01结果
|
||||
|
||||
- 四个功能模块的运行代码由572行降至422行;新增唯一传输实现129行,生产代码净减少21行、
|
||||
约1.4 KB。行数不是主要收益,关键是5处`/chat/completions`请求只剩1处。
|
||||
- 三套重复HTTP错误正文解析合并为一套;各功能原有错误类型和用户可见文案由专项测试固定。
|
||||
- 迁移期4个“Agent文件逐字相等”断言没有直接删除,而是替换为共享传输唯一所有权、提示词模块
|
||||
归属、SSE行为和兼容模块对象契约。
|
||||
- 候选311项、纯`app/`导出248项、45项Playwright通过;24个JavaScript文件、架构/API注册表和
|
||||
SQLite完整性检查通过。
|
||||
- 使用候选数据库中加密保存的主模型完成真实非流式与流式最小调用,分别成功返回完整响应和
|
||||
4个流式分片。调用未输出密钥或模型正文。
|
||||
- 本批不修改页面、CSS、提示词、业务计算、会员额度、模型回退、数据库或部署。
|
||||
|
||||
回档基线为`xiaobai-preservation-complete-20260801`;本批检查点为
|
||||
`xiaobai-reduction-01-llm-transport-20260801`。
|
||||
|
||||
## CR-02验收口径
|
||||
|
||||
- 仅纳入没有路径参数、请求体解析或专属异常分支的精确POST端点;其余路由保持原样。
|
||||
- 公开注册/登录端点继续在鉴权前分发;受保护端点继续严格执行登录、CSRF、注册表权限、处理器。
|
||||
- 27个映射路径必须都由权威API注册表解析,处理方法必须真实存在,公开与受保护集合不得重叠。
|
||||
- API路径、功能归属、访问角色、状态码、错误正文和静态页面绕过鉴权行为保持不变。
|
||||
- API清单生成器必须结构化读取显式映射;架构清查复用API清单,不再维护第二套路由发现规则。
|
||||
|
||||
## CR-02结果
|
||||
|
||||
- 2个公开端点和25个受保护端点改为显式委托映射,原来的79行重复分支被43行映射、分发与调用替代;
|
||||
`backend/application.py`净减少36行,规范化源码约减少1.0 KB。
|
||||
- 带正则路径参数、请求体读取、查询参数转换或特殊异常语义的GET、POST、DELETE端点未改动。
|
||||
- 架构清查删除了自行扫描精确/正则API路径的第二套规则,改为消费权威`api.config.json`;API注册表的
|
||||
53个精确路径、11个正则路径、功能归属和权限均未变化。
|
||||
- 原版231项、候选315项、纯`app/`导出252项、45项Playwright通过;24个JavaScript文件、
|
||||
API/架构注册表、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改前端、CSS、业务计算、数据源、数据库结构、LLM、会员规则或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-01-llm-transport-20260801`;检查点为
|
||||
`xiaobai-reduction-02-http-dispatch-20260801`。
|
||||
|
||||
## CR-03验收口径
|
||||
|
||||
- 图表模块不再定义第二份市场后缀转换函数,仍保留原局部名称和两个调用点。
|
||||
- 深市、沪市、北交所的既有映射结果保持不变;不借本批修正或扩展代码规则。
|
||||
- 原图表文件除该函数外的所有顶层定义继续与原版AST逐项相等。
|
||||
- 原版`_stock_market_code`函数的参数和函数体必须与唯一共享实现AST相等,运行时别名必须指向
|
||||
同一个函数对象。
|
||||
|
||||
## CR-03结果
|
||||
|
||||
- 删除`backend/features/market/charts.py`中第二份10行定义,以1行导入别名复用共享实现,生产代码
|
||||
净减少9行;全仓后端只剩一份沪深京后缀转换函数体。
|
||||
- 未合并实时聚合、东方财富图表、iFinD和Tushare的HTTP传输;它们的缓存、错误、重试和降级语义
|
||||
不同,仅有外形相似,证据不足以安全抽象。
|
||||
- 原有迁移期整文件相等断言被等价范围断言、共享函数AST断言和唯一对象断言替代,没有降低门禁。
|
||||
- 原版231项、候选317项、纯`app/`导出254项、45项Playwright通过;API/架构注册表、
|
||||
24个JavaScript文件、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改图表请求、数据来源、缓存、时间范围、行情计算、前端、CSS、数据库或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-02-http-dispatch-20260801`;检查点为
|
||||
`xiaobai-reduction-03-market-symbol-20260801`。
|
||||
|
||||
## CR-04验收口径
|
||||
|
||||
- 只合并参数、函数体和运行结果完全一致的数值转换函数,不借本批改变任何业务计算或异常默认值。
|
||||
- `finite_number`继续拒绝`NaN`与正负无穷;`non_nan_number`继续只拒绝`NaN`并保留正负无穷。
|
||||
- Tushare与智能选股必须复用有限数策略;市场洞察与情绪引擎必须复用非NaN策略,并继续暴露原局部
|
||||
`_number`名称以保持兼容。
|
||||
- 实时行情和图表转换器的空值、默认值或参数签名语义不同,必须继续独立保留,不能因名称相同而合并。
|
||||
- 原版函数参数和函数体分别与共享实现AST相等;四个消费者的局部别名必须指向对应的唯一函数对象。
|
||||
|
||||
## CR-04结果
|
||||
|
||||
- 删除Tushare、智能选股、市场洞察和情绪引擎中的四份重复函数体,新建两种明确命名的共享策略;生产
|
||||
代码净减少约12行,全仓AST扫描不再发现完全相同的函数定义。
|
||||
- 将可复用的函数及模块AST契约归入测试辅助层,原有迁移保持性测试改为“未改范围保持相等、被替换
|
||||
函数与共享实现相等、运行时唯一对象”三重断言,没有降低门禁。
|
||||
- 实时行情`backend/data/realtime.py::_number`与图表`backend/features/market/charts.py::_number`
|
||||
被明确保留;它们不是本批重复实现,也未改变行为。
|
||||
- 58项定向测试、41项保持性/治理测试、原版231项、候选320项、纯`app/`导出257项和45项
|
||||
Playwright通过;24个JavaScript文件、API/架构注册表、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改前端、CSS、接口、数据来源、行情口径、选股条件、数据库、LLM、权限或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-03-market-symbol-20260801`;检查点为
|
||||
`xiaobai-reduction-04-numeric-normalization-20260801`。
|
||||
|
||||
## CR-05验收口径
|
||||
|
||||
- 逐项扫描根级Python入口、生产代码、测试、工具和动态导入;没有消费者或兼容责任的入口才能删除。
|
||||
- 规范后端不得经由`screener`、`advanced_strategies`、`tushare_client`或`server`兼容入口
|
||||
间接访问已经归位的实现。
|
||||
- 所有根级模块继续保持原导入名称、导出对象及模块对象身份,既有启动命令和第三方维护脚本不受影响。
|
||||
- `api_access`、选股Repository的惰性`sentiment_engine`导入及根级`database.py`属于已登记边界,
|
||||
分别留到HTTP、Repository阶段处理,不在本批跨边界修改。
|
||||
|
||||
## CR-05结果
|
||||
|
||||
- 审计确认21个根级兼容入口均有测试、工具、启动或原公开导入契约消费者,因此本批没有冒险删除文件。
|
||||
- 容器、策略编译器、选股引擎及数据同步命令的五处导入改为规范模块路径,正式代码不再通过四个根级
|
||||
兼容模块反向进入实现;运行代码行数未增加。
|
||||
- 特性边界测试取消选股引擎旧例外,并新增全后端兼容导入门禁;只允许两项已登记过渡边界,后续代码
|
||||
无法重新引入隐式根级依赖。
|
||||
- 候选321项、纯`app/`导出258项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改业务计算、策略公式、数据源、API、数据库、LLM、权限、前端或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-04-numeric-normalization-20260801`;检查点为
|
||||
`xiaobai-reduction-05-compatibility-boundaries-20260801`。
|
||||
|
||||
## CR-06验收口径
|
||||
|
||||
- 只合并参数、函数体和异常行为完全相同的日期/文本转换;名称相似但空值、未来日期、错误文案或
|
||||
输入格式不同的函数不得合并。
|
||||
- Tushare与选股引擎继续暴露局部`_display_date`名称,并分别指向唯一共享实现。
|
||||
- 原版两个`_display_date`函数必须分别与共享实现AST相等,所有原调用结果保持不变。
|
||||
- 市场洞察的日期显示函数会清理连字符并容忍空值,语义不同,必须继续独立保留。
|
||||
|
||||
## CR-06结果
|
||||
|
||||
- 删除Tushare与选股引擎内两个重复日期函数体,新增`display_compact_date`唯一策略;生产代码总
|
||||
行数不增加,重复函数体由两份降为一份。
|
||||
- 架构清单登记日期格式唯一所有权;保持性测试改为未改范围AST相等、共享函数AST相等和运行时
|
||||
对象身份三重契约,没有放宽原迁移门禁。
|
||||
- `normalize_date`、市场洞察日期显示、实时行情时间格式和会员日期边界因语义不同均原样保留。
|
||||
- 候选321项、纯`app/`导出258项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改日期输入规则、业务计算、选股结果、接口、数据库、数据源、LLM、权限或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-05-compatibility-boundaries-20260801`;检查点为
|
||||
`xiaobai-reduction-06-date-formatting-20260801`。
|
||||
|
||||
## CR-07验收口径
|
||||
|
||||
- iFinD、图表、实时观察器和Provider适配器必须只在`build_data_gateway`创建,并由容器共享。
|
||||
- Tushare必须继续通过实时Token供应器按需创建,不能为了减少对象数量缓存过期Token。
|
||||
- 市场服务中为原版隔离测试桩保留的一处`TushareClient(self.token)`是明确兼容例外,不得被误判为
|
||||
第二条正式数据链路。
|
||||
- 不得合并Tushare、iFinD、东方财富和腾讯的传输、缓存、重试或降级逻辑。
|
||||
|
||||
## CR-07结果
|
||||
|
||||
- 全后端构造点扫描确认iFinD、MarketChart、东方财富图表和实时观察器均只有网关一个创建位置;
|
||||
`ApplicationContainer`暴露的是同一对象引用,没有第二份客户端。
|
||||
- Tushare Provider使用动态Token供应器,市场服务只有一处已登记测试兼容回退;本批没有发现可安全
|
||||
删除的生产实现,因此不为追求行数强行修改运行代码。
|
||||
- 架构清单新增Provider创建所有权,自动测试会在未来出现第二个未登记构造点时失败。
|
||||
- 候选322项、纯`app/`导出259项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改请求频率、缓存、重试、Token更新、数据源选择、计算口径、API、数据库或前端。
|
||||
|
||||
本批基线为`xiaobai-reduction-06-date-formatting-20260801`;检查点为
|
||||
`xiaobai-reduction-07-provider-ownership-20260801`。
|
||||
|
||||
## CR-08验收口径
|
||||
|
||||
- 只合并NDJSON响应头、事件序列化、完成事件、业务错误事件、客户端断线和连接关闭这些传输行为。
|
||||
- 问师继续直接输出原事件字典;复盘助手继续把文本分片包装为`delta/content`事件。
|
||||
- 两个功能各自的业务异常类型、请求体错误状态码、错误正文和流创建时机保持不变。
|
||||
- `_write_stream_event`与连接生命周期必须只由`backend/http/handler.py`拥有,应用大类和功能HTTP
|
||||
模块不再保留第二份实现。
|
||||
|
||||
## CR-08结果
|
||||
|
||||
- 删除问师和复盘助手各16行重复流式控制流,并将应用大类中的10行事件写入方法归入HTTP共享层;
|
||||
新共享实现29行、两个调用适配共3行,生产代码净减少10行。
|
||||
- 新增专项测试固定四个响应头、中文NDJSON序列化、增量顺序、完成事件、业务错误事件和关闭状态。
|
||||
- 候选324项、纯`app/`导出261项及45项Playwright通过;24个JavaScript文件、API/架构注册表、
|
||||
Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改提示词、模型选择、会员计次、流式正文、前端解析、API路径、数据库或数据源。
|
||||
|
||||
本批基线为`xiaobai-reduction-07-provider-ownership-20260801`;检查点为
|
||||
`xiaobai-reduction-08-ndjson-transport-20260801`。
|
||||
|
||||
## CR-09验收口径
|
||||
|
||||
- 只有消费者全部属于单一领域、且能够按原函数体机械移动的方法才从应用门面移出。
|
||||
- `_ifind_field`与`_ifind_row_code`继续保持静态/类方法签名、字段优先级、大小写规则和代码正则。
|
||||
- 账号委托属于稳定公开门面;系统设置属于跨领域协调;后台刷新留到CR-11,本批均不得删除或重写。
|
||||
- 移动后`DashboardService`必须继续通过Mixin解析同名方法,调用点和返回值不变。
|
||||
|
||||
## CR-09结果
|
||||
|
||||
- 将iFinD字段匹配和股票代码提取两个方法从应用大类机械移动到股池服务,原版与迁移方法AST逐项
|
||||
相等;应用大类不再直接拥有股池专属实现。
|
||||
- 连同迁移期遗留空行,`backend/application.py`减少32行,股池服务增加24行,生产代码净减少8行。
|
||||
- 候选324项、纯`app/`导出261项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改字段匹配、涨跌停原因补全、接口、数据源、缓存、数据库、权限或前端。
|
||||
|
||||
本批基线为`xiaobai-reduction-08-ndjson-transport-20260801`;检查点为
|
||||
`xiaobai-reduction-09-service-facade-20260801`。
|
||||
|
||||
## CR-10验收口径
|
||||
|
||||
- 只移动调用方、数据表和业务含义均明确属于单一领域的方法;数据库连接、事务和返回值必须保持不变。
|
||||
- `list_sector_phase_overrides`、`save_sector_phase_override`与`delete_sector_phase_override`必须由
|
||||
`backend/features/heaven/repository.py`拥有,并继续通过`ReviewDatabase`的Mixin解析。
|
||||
- 不修改表结构、迁移顺序、时间格式、排序、冲突更新或删除结果语义。
|
||||
- `wencai_saved_queries`及其三个方法属于已登记的账户隔离兼容数据;即使前端入口已取消,也必须保留。
|
||||
|
||||
## CR-10结果
|
||||
|
||||
- 将五行行业阶段覆盖的查询、保存和删除三个方法从根级`database.py`机械移动到问天Repository;
|
||||
调用名称、SQL、事务边界、时间值和返回结果均未改变。
|
||||
- 根级数据库门面不再直接拥有问天领域的持久化实现,问财历史兼容表和方法完整保留,未扩大删除范围。
|
||||
- 34项Repository、问天、账户隔离、清理契约及迁移定向测试通过;候选324项、纯`app/`导出261项、
|
||||
24个JavaScript文件、API/架构注册表、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改页面、CSS、API、数据库结构、行情、数据源、业务计算、LLM、权限或后台任务。
|
||||
|
||||
本批基线为`xiaobai-reduction-09-service-facade-20260801`;检查点为
|
||||
`xiaobai-reduction-10-repository-ownership-20260801`。
|
||||
|
||||
## CR-11验收口径
|
||||
|
||||
- 导入`backend.application`或构造`DashboardService`不得启动后台调度;必须先成功绑定HTTP端口,
|
||||
再由运行时显式启动。
|
||||
- 同一`InProcessJobRunner`重复启动调度器必须返回同一活动线程,停止必须置位并在限定时间内等待退出,
|
||||
有序停止后允许重新启动。
|
||||
- 运行时关闭顺序固定为:停止调度、等待已提交任务、关闭HTTP服务器;迁移对比工具继续兼容原版入口。
|
||||
- 三个任务的注册定义、5秒刷新频率、3秒初始延迟、幂等键、锁、重试、业务函数和结果不得改变。
|
||||
- 声明的超时继续是目标与审计字段;Python线程不能安全强杀,本批不伪造硬取消能力。
|
||||
|
||||
## CR-11结果
|
||||
|
||||
- 删除`DashboardService`构造阶段的调度副作用,端口占用、模块导入和单元测试不再提前创建后台写线程;
|
||||
`backend/bootstrap/runtime.py`成为正式启动与停止所有者。
|
||||
- `InProcessJobRunner`集中持有调度停止事件和线程引用;重复启动幂等,停止可等待,原任务锁、持久化运行
|
||||
状态、成功幂等、失败记录和后续重试逻辑保持不变。
|
||||
- 新增语法树与运行顺序门禁,固定“构造不启动”“绑定后启动”“停止后关服”,并补齐重复启动与重启测试。
|
||||
- 候选328项、纯`app/`导出265项和45项Playwright通过;24个JavaScript文件、API/架构注册表、
|
||||
Git空白检查和SQLite完整性检查通过。
|
||||
- 本批没有可安全删除的重复任务实现;为补齐原先缺失的生命周期,生产代码净增加24行。增加内容仅为
|
||||
调度状态、幂等启停和运行时委托,不新增业务层、任务或兼容包装。
|
||||
- 本批不修改页面、CSS、API、数据源、刷新计算、自动选股条件、数据库结构、权限或LLM。
|
||||
|
||||
本批基线为`xiaobai-reduction-10-repository-ownership-20260801`;检查点为
|
||||
`xiaobai-reduction-11-background-jobs-20260801`。
|
||||
|
||||
## CR-11人工验收修正
|
||||
|
||||
- 2026-08-02人工验收发现本地页面可访问,但行情与LLM同时无法连接。第一原因是验收服务由受限
|
||||
自动化会话启动,子进程继承了禁止外部网络访问的权限;重新在主机正常网络权限下启动后恢复。
|
||||
- 随后的主模型连接测试暴露`LLMServiceMixin`机械迁移时遗漏`validate_text`导入,导致请求在真正
|
||||
访问模型前抛出`NameError`并关闭HTTP连接;恢复原依赖并增加保存模型连接探测的运行契约测试。
|
||||
- 网站自身实测`000001`返回Tushare日K 60根、分时242点;主模型
|
||||
`MiniMax-M2.7-highspeed`在2236毫秒内回复`OK`,证明服务进程的数据与LLM出网链路均已恢复。
|
||||
- 修正后候选329项、纯`app/`导出266项通过;本次只恢复缺失导入和测试,不修改模型配置、额度、
|
||||
提示词、回退策略、行情来源或计算逻辑。
|
||||
- 继续验收观势时发现模型已经成功返回,但问天服务在保存解势记录前关闭了HTTP连接。原因是原版
|
||||
`server.py`已有的`secrets`导入在机械拆分到问天服务时遗漏,生成非观气记录去重键时触发
|
||||
`NameError`。迁移版恢复该标准库依赖,并增加“模型成功返回后保存观势结果”的完整服务回归测试。
|
||||
- 使用`000001 平安银行`完成真实页面复测:六爻安全门6/6通过,解势结果正常返回并写入历史,
|
||||
`8797`错误日志为空。该修正不改变提示词、模型选择、额度、卦象计算、记录结构或前端行为。
|
||||
|
||||
本修正基线为`xiaobai-reduction-11-background-jobs-20260801`;检查点为
|
||||
`xiaobai-reduction-11-runtime-connectivity-fix-20260802`;后续解势修正检查点为
|
||||
`xiaobai-reduction-11-heaven-interpret-fix-20260802`。
|
||||
|
||||
## CR-12验收口径
|
||||
|
||||
- 本批只处理不同样式文件中、同为顶层、选择器与完整声明逐字等价的规则;媒体查询、伪状态、
|
||||
动画、问天隔离样式以及仅外形相似的规则不进入删除范围。
|
||||
- 删除的必须是较早加载的副本,较晚层继续提供完全相同的最终声明;样式加载顺序、变量、HTML、
|
||||
JavaScript和主题切换逻辑均不改变。
|
||||
- 迁移保真测试必须逐段登记允许退休的原始CSS文本,除登记片段外,其余源码继续与原版逐字符相等。
|
||||
- 浏览器必须验证跨层顶层精确重复为零,并通过日间、夜间、桌面、390px移动端及全站交互回归。
|
||||
|
||||
## CR-12结果
|
||||
|
||||
- 通过浏览器CSSOM扫描七层运行样式,共发现57组完整重复规则;本批只批准其中7组跨文件顶层重复,
|
||||
分别涉及工作区显示、折叠侧栏、板块轮动末列、选股概率值、摘要条两项声明及龙虎榜原因列。
|
||||
其余50组位于同文件或嵌套媒体条件等更复杂环境,证据不足,继续保留。
|
||||
- 删除7条较早加载的规则,三个生产CSS文件净减少19行、480字节;后层最终规则、选择器优先级与
|
||||
加载顺序均未改变,没有新增兼容覆盖或第二套样式实现。
|
||||
- 新增浏览器CSSOM门禁,任何两个样式层再次出现相同顶层选择器与完整声明都会失败;迁移保真门禁
|
||||
只允许已登记的7个精确源码片段退休,其他CSS差异仍会失败。
|
||||
- 真实`8797`页面复核情绪周期日间/夜间、龙虎榜和390×844移动端;三个受影响节点的计算样式
|
||||
与删除前一致,移动端无横向溢出。候选330项、CSS/前端契约33项、迁移对照63项及46项
|
||||
Playwright全部通过,24个JavaScript文件、API/架构注册表和SQLite完整性检查通过。
|
||||
- 本批不修改页面布局、颜色、字体、间距、响应式规则、主题、动画、业务功能、API、数据库或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-11-heaven-interpret-fix-20260802`;检查点为
|
||||
`xiaobai-reduction-12-css-exact-duplicates-20260802`。
|
||||
|
||||
## CR-13验收口径
|
||||
|
||||
- 本批只处理不同样式文件中、处于浏览器规范化后完全相同媒体条件下、选择器与完整CSSOM声明完全
|
||||
相同的规则;不同媒体上下文、同文件重复、近似声明、动画和问天隔离样式继续保留。
|
||||
- 删除的必须是较早加载的副本,较晚样式层继续提供相同声明;媒体条件、规则顺序、选择器优先级、
|
||||
变量、HTML、JavaScript和主题逻辑不得改变。
|
||||
- 保真门禁必须逐段登记允许退休的原始源码;浏览器门禁必须递归遍历嵌套规则,并只将同一上下文内
|
||||
跨文件的完整重复判为失败。
|
||||
- 390×844明暗主题、1000×800中等宽度和1000×600低高度断点必须保持原计算样式与视觉结果。
|
||||
|
||||
## CR-13结果
|
||||
|
||||
- 浏览器CSSOM确认22组跨文件嵌套重复:1组位于721-1279px媒体条件,12组位于720px移动端条件,
|
||||
9组位于720px或1023px低高度复合条件;全部删除较早层副本,后层规则原样保留。
|
||||
- `styles.css`减少65行,`redesign-v2.css`减少15行,生产CSS合计净减少80行、约1.9 KB;没有新增
|
||||
兼容覆盖、声明值、选择器或样式文件。
|
||||
- Playwright门禁由顶层扫描扩展为递归上下文扫描,修改后同一嵌套上下文的跨文件完整重复为0;同文件
|
||||
重复和不同上下文规则不在本批范围,未被误删。
|
||||
- 1000×800和1000×600修改前后截图逐字节一致;390×844明暗主题的关键显示、定位、间距、网格、
|
||||
溢出和导航状态一致,页面目视无差异。
|
||||
- 候选330项、CSS/前端契约33项、迁移对照63项及46项Playwright全部通过;24个JavaScript文件、
|
||||
API/架构注册表和SQLite完整性检查通过。
|
||||
- 本批不修改页面布局、颜色、字体、间距、主题、动画、业务功能、API、数据库、数据源、LLM或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-12-css-exact-duplicates-20260802`;检查点为
|
||||
`xiaobai-reduction-13-css-nested-duplicates-20260802`。
|
||||
|
||||
## CR-13后续产品修正:龙虎榜整页滚动
|
||||
|
||||
- 2026-08-02用户明确要求取消龙虎榜“当日操作明细单独纵向滚动”,改为龙虎榜主内容区整页纵向滚动,
|
||||
解决低分辨率下操作明细可视高度过小的问题;这是经批准的产品行为变化,不作为CSS去重处理。
|
||||
- 龙虎榜从桌面固定视口共享规则中独立出来;主内容区继续使用工作区高度并承担纵向滚动,游资卡片、
|
||||
操作明细和待归类席位按内容自然展开,宽操作表继续保留横向滚动。
|
||||
- 保真门禁以精确源码替换单独登记本次差异,其他CSS仍与原母版逐字符比较;未新增覆盖层或第二套规则。
|
||||
- 1366×768真实页面中主内容区为692px、内容高度为2620px,整页滚动可达;操作明细自身高度与内容
|
||||
高度一致,不再形成纵向小窗口,1180px宽表仍可横向滚动。
|
||||
- 本次不修改龙虎榜数据、筛选、搜索、游资卡牌、表格字段、游资档案、API、数据库或其他页面的
|
||||
滚动所有权。
|
||||
|
||||
本修正基线为`xiaobai-reduction-13-css-nested-duplicates-20260802`;检查点为
|
||||
`xiaobai-fix-dragon-page-scroll-20260802`。
|
||||
|
||||
## CR-14验收口径
|
||||
|
||||
- 只处理同一CSS文件、同一浏览器规范化媒体上下文中,选择器和完整CSSOM声明完全相同的规则;
|
||||
不同媒体上下文、近似声明、动画和问天隔离样式继续保留。
|
||||
- 每组只删除较早出现的副本并保留最后一份原规则;样式文件加载顺序、媒体条件、选择器优先级、变量、
|
||||
HTML、JavaScript和主题逻辑均不得改变。
|
||||
- 保真门禁必须精确登记原始片段及其出现/退休次数;浏览器门禁从“只拒绝跨文件重复”提升为
|
||||
“同一上下文内任何完整重复均拒绝”。
|
||||
- 1366×768桌面暗色关键页面和390×844移动端关键页面的尺寸、滚动范围及视觉结果必须保持不变,
|
||||
并通过全站Playwright回归。
|
||||
|
||||
## CR-14结果
|
||||
|
||||
- 浏览器CSSOM确认33组同文件精确重复,其中两个规则各出现三次;共退休35个较早副本:
|
||||
`styles.css`9个、`renovation.css`25个、`redesign-v2.css`1个,运行时同上下文完整重复降为0。
|
||||
- 三个生产CSS文件合计净减少97行、3272字节;未新增选择器、声明、覆盖层或样式文件,最后一份原规则
|
||||
及其媒体上下文全部保留。
|
||||
- 保真门禁新增精确出现次数与退休次数审计,除登记片段外继续与原母版逐字节比较;Playwright CSSOM
|
||||
门禁现会拒绝跨文件和同文件重复,后续不能重新堆回同类规则。
|
||||
- 1366×768暗色模式复核情绪周期、集合竞价、题材库、智能选股、问师和我的复盘;390×844复核
|
||||
情绪周期、集合竞价、智能选股和我的复盘。关键尺寸、滚动范围保持一致,移动端情绪周期截图逐像素一致,
|
||||
其余页面目视无差异且无横向溢出。
|
||||
- 候选330项、CSS/前端契约33项、迁移对照63项及46项Playwright全部通过;24个JavaScript文件、
|
||||
API/架构注册表和SQLite完整性检查通过。
|
||||
- 本批不修改页面布局、颜色、字体、间距、响应式行为、主题、动画、业务功能、API、数据库、数据源、
|
||||
LLM或部署。
|
||||
|
||||
本批基线为`xiaobai-fix-dragon-page-scroll-20260802`;检查点为
|
||||
`xiaobai-reduction-14-css-same-file-duplicates-20260802`。
|
||||
|
||||
## 人工验收记录
|
||||
|
||||
- 2026-08-01:用户检查CR-02与CR-03运行结果,确认未发现明显异常。本记录仅表示本轮可见功能与
|
||||
页面使用未发现明显回归,不替代后续批次各自的自动测试和人工抽查。
|
||||
@@ -0,0 +1,60 @@
|
||||
# Stage 01: Governance Baseline
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
Baseline commit: `0030bb8cc18c5aa7107dd8fd866355a6507bd6da`
|
||||
|
||||
## Purpose
|
||||
|
||||
This baseline freezes observable behavior before architecture governance begins. Later stages
|
||||
may move code and introduce internal contracts, but must not change API behavior, account
|
||||
boundaries, persisted user data, desktop presentation, or existing workflows unless a change
|
||||
is approved separately.
|
||||
|
||||
## Deployment Contract
|
||||
|
||||
- One Python process serves the API and static browser client.
|
||||
- One SQLite database is persisted at `data/review.db`.
|
||||
- Docker exposes `0.0.0.0:8765` and mounts only `./data` at `/app/data`.
|
||||
- Public market data is shared; private records are scoped by `user_id`.
|
||||
- Market-data and LLM credentials remain server-side.
|
||||
|
||||
## Verified Baseline
|
||||
|
||||
- Python: 169 tests passed.
|
||||
- Playwright: 45 tests passed at desktop and mobile viewports.
|
||||
- JavaScript entry points pass `node --check`.
|
||||
- `git diff --check` reports no patch errors.
|
||||
- SQLite `PRAGMA integrity_check` returns `ok`.
|
||||
- The database was backed up with the SQLite backup API to
|
||||
`data/backups/governance-stage-01-0030bb8.db` and the backup independently passed
|
||||
`PRAGMA integrity_check`. The backup is intentionally excluded from Git.
|
||||
|
||||
## Repeatable Verification
|
||||
|
||||
Run the fast baseline on every structural change:
|
||||
|
||||
```shell
|
||||
python tools/verify_baseline.py
|
||||
```
|
||||
|
||||
Run the browser suite at phase boundaries:
|
||||
|
||||
```shell
|
||||
python tools/verify_baseline.py --e2e
|
||||
```
|
||||
|
||||
## Regression Gates
|
||||
|
||||
1. API routes and response fields remain compatible until a versioned contract says otherwise.
|
||||
2. Existing SQLite files must upgrade without deleting or reassigning user-owned rows.
|
||||
3. A failed external data request must not synthesize market prices.
|
||||
4. Ordinary, member, and administrator permissions must remain distinct.
|
||||
5. Desktop visual refactors require 1080P and 4K comparison in both themes.
|
||||
6. Mobile work is isolated from the approved desktop shell and workflows.
|
||||
7. Old code is deleted only after its replacement is active and reference scans are clean.
|
||||
|
||||
## Rollback
|
||||
|
||||
Code can return to this point with Git commit `0030bb8`. Database rollback must use the
|
||||
matching backup above; a code rollback alone is not sufficient after a schema migration.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Stage 02: Architecture Inventory
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
The machine-readable inventory is `architecture-inventory.json`. Regenerate it with:
|
||||
|
||||
```shell
|
||||
python tools/build_architecture_inventory.py
|
||||
python tools/build_architecture_inventory.py --check
|
||||
```
|
||||
|
||||
## Current Shape
|
||||
|
||||
- The product is a single-container modular monolith with one Python process and SQLite WAL.
|
||||
- The browser is a build-free single-page application served by the same process.
|
||||
- The primary navigation exposes 16 workspaces.
|
||||
- The current database bootstrap defines 34 tables.
|
||||
- HTTP routing and service orchestration are concentrated in `server.py`.
|
||||
- Schema creation, inline migrations, and all persistence methods are concentrated in
|
||||
`database.py`.
|
||||
- The frontend loads six ordered CSS layers and one global application script.
|
||||
- Background refresh and automatic screener work execute as daemon threads in the HTTP
|
||||
process.
|
||||
|
||||
## Ownership Inventory
|
||||
|
||||
### Shared market and system data
|
||||
|
||||
Dashboard snapshots, synchronization runs, stock master data, daily bars, benchmark bars,
|
||||
daily indicators, fundamental indicators, money flow, auction factors, earnings events,
|
||||
popularity factors, institutional list data, public screener strategies, reason overrides,
|
||||
seat aliases, sector phase overrides, and encrypted system settings are shared.
|
||||
|
||||
### User-owned data
|
||||
|
||||
Sessions, personal credentials, birth profiles, watchlists, review notes, custom strategies,
|
||||
private screener runs, mentor messages and preferences, saved queries, tracking entries,
|
||||
alerts, trades, assistant messages, and heaven readings are user-scoped. Every repository
|
||||
extracted in later stages must preserve this boundary explicitly.
|
||||
|
||||
## Runtime Entrypoints
|
||||
|
||||
- HTTP and application bootstrap: `server.py`
|
||||
- Persistence and migrations: `database.py`
|
||||
- Access policy: `api_access.py`
|
||||
- Market providers: `tushare_client.py`, `ifind_client.py`, `chart_data_provider.py`,
|
||||
`realtime_aggregator.py`
|
||||
- Deterministic computation: `sentiment_engine.py`, `screener.py`, `advanced_strategies.py`,
|
||||
`market_insights.py`, `heaven_engine.py`
|
||||
- LLM calls: `mentor_agent.py`, `heaven_agent.py`, `assistant_agent.py`, `llm_strategy.py`
|
||||
- Browser shell and pages: `static/index.html`, `static/app.js`, and the ordered CSS files
|
||||
|
||||
## Primary Structural Risks
|
||||
|
||||
1. Request routing, use cases, scheduling, provider selection, serialization, and account
|
||||
context coexist in one service module.
|
||||
2. Database migrations cannot be reviewed independently from repository behavior.
|
||||
3. Several business paths instantiate provider clients directly, so source policy is not
|
||||
centrally enforceable.
|
||||
4. LLM authorization, quota accounting, model selection, streaming, and error handling are
|
||||
distributed across multiple adapters.
|
||||
5. Global DOM state and page behavior share one JavaScript file, increasing cross-page
|
||||
regression risk.
|
||||
6. CSS correctness depends on load order and late overrides rather than explicit layer
|
||||
ownership.
|
||||
7. In-process daemon jobs have no durable queue and some failure paths are not observable.
|
||||
|
||||
## Inventory Limits
|
||||
|
||||
The JSON inventory records source-level declarations and references. It does not claim that
|
||||
every declared endpoint is exercised or every table is populated. Runtime coverage and data
|
||||
quality are separate contracts introduced in later governance stages.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Stage 04: Governance Registries
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
Four versioned registries now describe the existing product before runtime decomposition:
|
||||
|
||||
1. `config/pages.config.json` registers all 16 primary workspaces.
|
||||
2. `config/features.config.json` registers 20 feature owners and their access/data scope.
|
||||
3. `config/api.config.json` registers 74 method/path combinations, including dynamic paths.
|
||||
4. `config/data-fields.config.json` registers 18 initial canonical data products and provider
|
||||
eligibility.
|
||||
|
||||
The API registry is generated from the current request handler so route drift fails tests. The
|
||||
other registries are curated contracts and may change only through an explicit product or data
|
||||
governance decision.
|
||||
|
||||
## Access Decisions Preserved
|
||||
|
||||
- Health, login, and registration are public.
|
||||
- Market views require an authenticated account.
|
||||
- Intelligent screening, Mentor, Wentian, and their history endpoints require membership.
|
||||
- My Review remains visible to authenticated users; LLM assistant endpoints remain member
|
||||
gated inside that workspace.
|
||||
- System management and shared knowledge modification remain administrator-only.
|
||||
- Frontend visibility does not grant API access.
|
||||
|
||||
## Data Decisions Preserved
|
||||
|
||||
- Tushare and licensed iFinD data may be calculation inputs when a dataset contract permits it.
|
||||
- Eastmoney and Tencent public endpoints are display-only in the initial registry.
|
||||
- Analyst consensus history and Level-2 microstructure remain blocked rather than silently
|
||||
approximated.
|
||||
- Chart fallbacks remain separate from calculation datasets.
|
||||
- The current unadjusted deterministic daily-bar behavior is recorded honestly; adjustment
|
||||
normalization is deferred to the data-quality phase rather than changed here.
|
||||
|
||||
## Transitional Rule
|
||||
|
||||
These files do not yet replace `server.py`, `api_access.py`, or the current frontend navigation.
|
||||
They are checked against those surfaces to prevent untracked drift. Later phases make each
|
||||
registry authoritative in one atomic migration with compatibility tests.
|
||||
|
||||
## Verification
|
||||
|
||||
`tests/test_governance_registries.py` verifies uniqueness, page coverage, current API coverage,
|
||||
explicit public routes, feature ownership, role validity, and provider eligibility.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Stage 05: Bootstrap and Dependency Assembly
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Environment and legacy LLM credential resolution moved to `backend/bootstrap/settings.py`.
|
||||
- Encryption-key initialization remains behavior-compatible and server-side.
|
||||
- Stable service construction moved to `backend/bootstrap/container.py`.
|
||||
- `DashboardService` keeps its compatibility attributes but receives them from one application
|
||||
container.
|
||||
- The iFinD client is instantiated once and shared by chart services.
|
||||
- HTTP routes, API payloads, background thread timing, database paths, and frontend assets are
|
||||
unchanged.
|
||||
|
||||
## Transitional Boundary
|
||||
|
||||
Some methods still construct Tushare clients directly. Stage 06 introduces `DataGateway` and
|
||||
migrates those provider creation paths without combining that work with bootstrap changes.
|
||||
|
||||
## Rollback
|
||||
|
||||
Reverting this stage restores inline service construction. No database or configuration
|
||||
migration is required.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Stage 06: Unified Data Gateway
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Added canonical provider and dataset contracts under `backend/data`.
|
||||
- Added `DataSourcePolicy`, loaded from the Stage 04 data registry.
|
||||
- Added provider adapters for Tushare and iFinD.
|
||||
- Added one `DataGateway` that owns Tushare creation, the shared iFinD client, chart routing,
|
||||
and isolated Eastmoney/Tencent realtime observation.
|
||||
- Replaced all real `DashboardService` Tushare construction paths with the gateway.
|
||||
- Kept one compatibility constructor for unit tests that instantiate an incomplete service with
|
||||
`__new__`; production instances never use it.
|
||||
- The Tushare token is supplied lazily, so administrator credential changes do not leave a
|
||||
stale client in memory.
|
||||
|
||||
## Enforcement Introduced
|
||||
|
||||
- Unregistered datasets fail.
|
||||
- Blocked datasets fail.
|
||||
- Public-web providers cannot be promoted to calculation inputs through a fallback call.
|
||||
- Display chart fallbacks remain distinct from deterministic calculation datasets.
|
||||
|
||||
## Deferred to Stage 07
|
||||
|
||||
Stage 06 centralizes provider access but does not yet attach freshness, coverage, unit, and
|
||||
point-in-time quality evidence to every returned observation. Stage 07 introduces those gates
|
||||
without changing provider routing again.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Stage 07: Data Quality and Provenance Gates
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Added a quality rule for every registered data product.
|
||||
- Added canonical Asia/Shanghai timestamp handling.
|
||||
- Added source and usage verification before quality acceptance.
|
||||
- Added maximum-age checks for realtime auction, intraday charts, and realtime indices.
|
||||
- Added minimum coverage thresholds for deterministic datasets.
|
||||
- Added unit profiles for prices, shares, currency, percentages, ratios, ranks, and timestamps.
|
||||
- Added adjustment checks that distinguish current unadjusted deterministic bars from iFinD
|
||||
forward-adjusted display charts.
|
||||
- Added announcement-date point-in-time checks for financial data.
|
||||
- Added explicit provider chains; a display fallback cannot silently become a calculation
|
||||
source.
|
||||
- Analyst consensus and Level-2 remain blocked until a qualified provider is registered.
|
||||
|
||||
## Fail-Closed Contract
|
||||
|
||||
A calculation evidence envelope is rejected when its source is unauthorized, its dataset is
|
||||
blocked, its timestamp is from the future, its realtime data is stale, its coverage is below
|
||||
the registered threshold, its units or adjustment mode differ, or its financial record was not
|
||||
available at the evaluation time.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Existing legacy provider response shapes remain unchanged in this stage. New governed feature
|
||||
paths must submit quality evidence through `DataGateway.require_quality`. Existing feature
|
||||
paths receive envelopes as they migrate behind domain services, avoiding a simultaneous
|
||||
rewrite of calculations and provider routing.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stage 08: Database Connection and Migration Foundation
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Centralized SQLite connection policy in `SQLiteConnectionFactory`.
|
||||
- Preserved WAL, foreign-key enforcement, row mapping, handle cleanup, and the existing
|
||||
20-second contention tolerance.
|
||||
- Added an ordered migration runner with immutable checksums and an applied-migration ledger.
|
||||
- Added savepoint rollback so a failed migration cannot be recorded or leave partial schema.
|
||||
- Adopted existing databases as version `0001` only after verifying the required legacy
|
||||
tables.
|
||||
- Kept the legacy idempotent bootstrap in place for compatibility with databases created by
|
||||
every previous application version.
|
||||
|
||||
## Forward Rule
|
||||
|
||||
All schema changes after this stage must be a new immutable module under
|
||||
`backend/database/migrations`. Editing an applied migration is rejected by checksum. A
|
||||
database containing a migration unknown to the running code is rejected rather than opened
|
||||
with an older schema interpretation.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
The historical inline bootstrap remains a compatibility facade during repository migration.
|
||||
It may be removed only after legacy upgrade fixtures cover every supported historical shape.
|
||||
No user table or row is rewritten in this stage.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Stage 09: Account-Scoped Repository Boundaries
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Added narrow repository ports for alerts, the trade journal, and strategy tracking.
|
||||
- Added SQLite adapters that expose only the persistence operations each service requires.
|
||||
- Required a positive account owner on every private read, write, update, and delete path.
|
||||
- Kept the shared automatic screener-run lookup explicit as the sole zero-owner read in the
|
||||
strategy tracking adapter.
|
||||
- Routed the application container through a repository bundle.
|
||||
- Removed direct alert and trade deletion/update calls from the HTTP-facing dashboard service.
|
||||
- Preserved structural compatibility for isolated tests and gradual extraction from the legacy
|
||||
database facade.
|
||||
|
||||
## Boundary
|
||||
|
||||
Application services depend on repository protocols. The SQLite implementation may later be
|
||||
replaced without changing those services. Repository adapters do not call market providers,
|
||||
and provider adapters do not access user tables.
|
||||
|
||||
## Residual Migration
|
||||
|
||||
The legacy `ReviewDatabase` still contains the SQL behind these adapters and remains the
|
||||
compatibility facade for features not yet extracted. Subsequent feature stages can move SQL
|
||||
behind the same ports one feature at a time after account-isolation tests pass.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stage 10: Feature Application Service Layout
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Created the governed `backend/features/<feature>` source boundary.
|
||||
- Moved alert behavior into the alerts feature.
|
||||
- Moved trade-journal behavior into the review feature.
|
||||
- Moved strategy-tracking behavior into the screener feature.
|
||||
- Changed the application container to import feature-owned services directly.
|
||||
- Reduced the former top-level service modules to compatibility exports, preserving existing
|
||||
imports without maintaining duplicate implementations.
|
||||
- Added dependency tests that prevent feature services from importing HTTP delivery code or
|
||||
concrete market-provider adapters.
|
||||
|
||||
## Template
|
||||
|
||||
Each migrated feature owns its application behavior and depends on repository or gateway
|
||||
ports. HTTP delivery and background jobs may invoke the service, but neither may become the
|
||||
owner of its rules. The same layout is now available for the remaining market, account,
|
||||
Mentor, Wentian, and screener services.
|
||||
|
||||
## Compatibility
|
||||
|
||||
No route, response shape, persisted data, service method, or top-level import path changes in
|
||||
this stage. Compatibility exports are removed only after all internal and external callers
|
||||
use the feature packages.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stage 11: HTTP Route, Access, and Error Governance
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Promoted `config/api.config.json` from a transitional inventory to the runtime route and
|
||||
access registry.
|
||||
- Added deterministic exact and regex route resolution with duplicate and regex validation.
|
||||
- Replaced hand-maintained member/admin path sets with the registered access contract.
|
||||
- Rejected unregistered API routes before business dispatch.
|
||||
- Added safe request correlation IDs to JSON responses and `X-Request-ID` headers.
|
||||
- Extended every legacy JSON error with stable `code`, `message`, and `request_id` fields while
|
||||
preserving the existing `error` field used by the browser.
|
||||
- Kept the current request handler and all route response bodies compatible while feature
|
||||
route modules are migrated incrementally.
|
||||
|
||||
## Runtime Authority
|
||||
|
||||
Changing or adding an API now requires one coherent change to the handler and API registry.
|
||||
The generated source inventory test prevents either side from drifting. Backend access remains
|
||||
authoritative; frontend visibility cannot grant a route.
|
||||
|
||||
## Residual Migration
|
||||
|
||||
Individual dispatch branches still live in the compatibility request handler. Feature-owned
|
||||
controllers will move behind the same registry in later stages without changing route identity,
|
||||
authorization, or error serialization.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Stage 12: Governed Background Jobs
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Registered market refresh, automatic screening, and iFinD event enrichment as versioned
|
||||
jobs with schedules, date policy, dependencies, locks, timeouts, retry limits, and output
|
||||
versions.
|
||||
- Added the `job_runs` migration and persistent run ledger.
|
||||
- Centralized worker and scheduler thread creation in `InProcessJobRunner`.
|
||||
- Added process-level lock keys and persistent idempotency keys.
|
||||
- Persisted successful, failed, and retried attempts with elapsed time and normalized error
|
||||
type.
|
||||
- Exposed recent job runs in administrator system status.
|
||||
- Limited retained completed history while preserving running jobs.
|
||||
- Kept existing feature services and the detailed market `sync_runs` audit unchanged.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The runner remains in-process, matching the current single-container deployment. Jobs call the
|
||||
same application methods as HTTP requests and do not depend on request-local account state.
|
||||
The persistent contract permits a later worker process without changing job identities or
|
||||
business calculations.
|
||||
|
||||
## Timeout Boundary
|
||||
|
||||
Timeouts are declared and elapsed time is recorded. Python threads cannot be terminated safely,
|
||||
so hard cancellation remains cooperative until jobs move to a durable worker process. Locks and
|
||||
idempotency prevent concurrent duplicate execution in the current single-process deployment.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Stage 13: Unified LLM Gateway
|
||||
|
||||
## Boundary
|
||||
|
||||
All runtime model calls now enter through `backend/llm/gateway.py`. Feature agents retain
|
||||
their deterministic context assembly, prompt content, and provider response parsing.
|
||||
|
||||
The gateway owns:
|
||||
|
||||
- membership and daily quota enforcement;
|
||||
- primary and fallback model selection;
|
||||
- fallback only before the first streamed delta;
|
||||
- stable user-visible availability and interruption errors;
|
||||
- one logical-call audit record with feature, model role, prompt version, latency, status,
|
||||
normalized error code, and token fields reserved for providers that report usage.
|
||||
|
||||
Administrator connection probes also cross the gateway boundary, but do not consume member
|
||||
quota or create usage records.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Mentor and review-assistant stream payloads are unchanged.
|
||||
- Heaven readings retain their primary/fallback notice and persistence behavior.
|
||||
- Strategy compilation still falls back to the deterministic local compiler when both model
|
||||
profiles are unavailable, while access and quota failures remain blocking.
|
||||
- Provider credentials and raw provider failures remain outside browser responses.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
The current OpenAI-compatible streaming providers do not consistently return token usage, so
|
||||
the audit schema records zero until transport adapters expose trustworthy token counts. Request
|
||||
cancellation remains bounded by the existing provider socket timeout.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Stage 14: Frontend Request and State Boundaries
|
||||
|
||||
## Request Boundary
|
||||
|
||||
`static/shared/api.js` is now the only application file allowed to call `fetch`. It owns:
|
||||
|
||||
- JSON serialization and response parsing;
|
||||
- CSRF attachment for mutating requests;
|
||||
- expired-session notification;
|
||||
- abort signals;
|
||||
- NDJSON stream decoding and normalized stream errors.
|
||||
|
||||
The mentor and review-assistant streams use the same client as ordinary API requests. Existing
|
||||
function signatures and page interactions remain unchanged.
|
||||
|
||||
## State Boundary
|
||||
|
||||
`static/shared/state.js` stores mutable state in explicit domains: session, market, entity
|
||||
details, review, screener, mentor, and heaven. A compatibility proxy retains the existing flat
|
||||
access syntax while rejecting unregistered fields. New page modules can request their owned
|
||||
domain without depending on another page's data.
|
||||
|
||||
This stage establishes a migration boundary rather than splitting the build-free monolith in a
|
||||
single high-risk edit. Page extraction can now proceed domain by domain with no contract change.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Automated checks require that:
|
||||
|
||||
- only `shared/api.js` contains browser `fetch` calls;
|
||||
- shared state and API scripts load before `app.js`;
|
||||
- application state is created through the shared state boundary.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
`static/app.js` still contains page renderers and event handlers in one file. The state domains
|
||||
make ownership explicit, but those functions should move into page modules only in later,
|
||||
independently verified stages.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Stage 15: Shared Frontend Shell and Page Registry
|
||||
|
||||
## Runtime Page Registry
|
||||
|
||||
`static/pages.config.js` is the build-free runtime representation of
|
||||
`config/pages.config.json`. It registers every primary workspace plus the internal strategy
|
||||
tracking workspace. Automated parity checks prevent titles, feature ownership, access labels,
|
||||
groups, default-page selection, and layout metadata from drifting between the two registries.
|
||||
|
||||
Legacy route aliases are resolved by the registry instead of page business code. The registry
|
||||
describes navigation and presentation metadata only; backend authorization remains
|
||||
authoritative.
|
||||
|
||||
## Shared Shell
|
||||
|
||||
`static/shared/shell.js` now owns:
|
||||
|
||||
- sidebar initialization, persistence, collapse state, and responsive control labels;
|
||||
- primary and mobile navigation binding and active-state synchronization;
|
||||
- workspace mounting, entry animation, URL state, and scroll reset;
|
||||
- header command-menu lifecycle;
|
||||
- market-summary expansion;
|
||||
- status-bar page titles and data dates;
|
||||
- the single-open-dialog lifecycle used by global application dialogs.
|
||||
|
||||
`static/app.js` retains feature-specific enter and leave behavior, such as stopping Wentian
|
||||
animations or loading auction data. It asks the shell to mount a registered page and no longer
|
||||
mutates global page geometry directly.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Existing DOM IDs, CSS classes, query parameters, page animations, and mobile navigation are
|
||||
unchanged.
|
||||
- `screenerTrackingView` continues to highlight the Intelligent Screener navigation item.
|
||||
- Existing function entry points remain as thin compatibility facades where feature code still
|
||||
calls status, dialog, or command-menu services.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
Feature renderers and feature event binding still share `static/app.js`. Their state ownership
|
||||
is now explicit and their shell dependencies are removed, so later page-module extraction can
|
||||
be performed one feature at a time instead of as a single rewrite.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Stage 16: CSS Tokens and Cascade Governance
|
||||
|
||||
## Scope
|
||||
|
||||
This stage changes CSS ownership, not visual design. Existing selectors, page geometry,
|
||||
responsive behavior, theme behavior, and the isolated Wentian visual layer remain intact.
|
||||
|
||||
## Canonical Token Layer
|
||||
|
||||
`static/shared/tokens.css` is the only owner of global application tokens. It is loaded before
|
||||
all application styles and follows a three-layer contract:
|
||||
|
||||
1. Primitive tokens contain raw palette, dimension, elevation, and motion values.
|
||||
2. Semantic tokens describe interface meaning such as canvas, surface, border, text, action,
|
||||
market direction, and warning states.
|
||||
3. Component tokens define shared card, control, shell, table, chart, and profile contracts.
|
||||
|
||||
Dark mode overrides semantic and compatibility values in the same file. It does not redefine
|
||||
component geometry. The `--wt-*` namespace remains owned by `wentian-v2.css` because Wentian
|
||||
has a deliberately isolated visual language.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The historical `--xb-*`, `--r2-*`, and flat variables such as `--blue`, `--line`, and `--up`
|
||||
remain compatibility aliases. Their effective light and dark values are unchanged. New CSS
|
||||
must use semantic or component tokens; compatibility names exist only so feature selectors can
|
||||
be migrated incrementally without a broad visual rewrite.
|
||||
|
||||
The stylesheet order is fixed as:
|
||||
|
||||
1. shared tokens;
|
||||
2. legacy/base styles;
|
||||
3. renovation and page redesign rules;
|
||||
4. canonical non-Wentian design-system rules;
|
||||
5. runtime theme selectors;
|
||||
6. isolated Wentian rules.
|
||||
|
||||
## Enforcement
|
||||
|
||||
`tests/test_css_governance.py` verifies the load order, the three token layers, single global
|
||||
ownership, compatibility coverage, and Wentian isolation. A new global `:root` token block in
|
||||
any legacy stylesheet fails the regression suite.
|
||||
|
||||
## Residual Migration
|
||||
|
||||
Page styles still contain literal values and historical variable references. Removing those is
|
||||
an incremental page-by-page task because blindly replacing them would risk changing already
|
||||
accepted visuals. Stage 16 establishes the ownership boundary that prevents new drift while
|
||||
leaving safe migration points for later stages.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Stage 17: Frontend Page Modules and Shared Components
|
||||
|
||||
## Scope
|
||||
|
||||
This stage establishes frontend feature ownership without changing page markup, API contracts,
|
||||
permissions, visual design, or user interaction. The build-free deployment model is retained.
|
||||
|
||||
## Page Module Runtime
|
||||
|
||||
`static/pages/runtime.js` owns the page lifecycle registry. Each feature registers its views in
|
||||
`static/pages/<feature>/page.js` and declares named enter and leave actions. The application
|
||||
injects the existing feature functions into that runtime, so page modules do not reach into
|
||||
another feature's state or DOM.
|
||||
|
||||
The lifecycle boundary now owns:
|
||||
|
||||
- page-specific data loading after a successful shell mount;
|
||||
- member-aware entry for Screener, Mentor, and Wentian;
|
||||
- auction timer cleanup when leaving Auction;
|
||||
- canvas, dust, and performance cleanup when leaving Wentian;
|
||||
- the internal Screener Tracking view's ownership relationship.
|
||||
|
||||
`openView` is now a generic coordinator. It validates the route, asks the page runtime to leave
|
||||
the previous page, mounts through the shared shell, and enters the next page. It contains no
|
||||
page-name branch chain.
|
||||
|
||||
## Shared Components
|
||||
|
||||
`static/shared/components.js` is the common rendering boundary for small, stable DOM patterns.
|
||||
The first migrated component is the empty state used across market rotation, themes,
|
||||
Dragon-Tiger, review, Screener, Wentian history, alerts, entity details, and administration.
|
||||
It centralizes escaping and class composition while preserving the exact existing markup.
|
||||
|
||||
Collection rendering and text assignment are exposed for later incremental migrations. They
|
||||
remain dependency-free and use `XiaobaiUI` for safe escaping.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Frontend boundary tests verify that:
|
||||
|
||||
- shared components load after UI primitives and before page code;
|
||||
- the page runtime loads before every feature registration and before `app.js`;
|
||||
- every public and internal workspace belongs to exactly one feature page module;
|
||||
- `openView` contains no feature-specific view comparisons;
|
||||
- shared empty-state rendering is used by multiple feature families;
|
||||
- provider requests still exit only through `shared/api.js`.
|
||||
|
||||
## Compatibility and Residual Risk
|
||||
|
||||
Feature renderers and event handlers still reside in `app.js`; moving them all at once would
|
||||
create a high-risk rewrite across already accepted workflows. The new lifecycle and component
|
||||
boundaries let those functions move feature by feature later without changing navigation or
|
||||
loading behavior. Dedicated mobile composition remains the next governance phase.
|
||||
@@ -0,0 +1,76 @@
|
||||
# 小白复盘人工维护指南
|
||||
|
||||
## 1. 正式边界
|
||||
|
||||
`app/`是唯一正式源码和运行目录。父目录旧程序与失败的`next/`不属于应用依赖,也不得作为后续
|
||||
实现来源。产品规格位于`docs/product/`,历史迁移证据位于`docs/migration/`。
|
||||
|
||||
## 2. 目录定位
|
||||
|
||||
```text
|
||||
server.py 进程入口
|
||||
backend/bootstrap/ 配置、依赖组装和启动
|
||||
backend/http/ 鉴权、响应和公共HTTP能力
|
||||
backend/features/ 按产品领域组织的服务、路由和Repository
|
||||
backend/data/ 数据网关、质量规则和供应商适配
|
||||
backend/database/ SQLite连接、迁移和Repository组合
|
||||
backend/jobs/ 后台任务、状态、锁与重试
|
||||
backend/llm/ 模型选择、鉴权、额度、流式和审计
|
||||
frontend/shared/ API、状态、Shell和公共组件
|
||||
frontend/pages/ 页面结构、行为和页面样式
|
||||
config/ 页面、功能、API、任务和数据字段注册表
|
||||
data/ 正式数据库和私有数据,不进入Git
|
||||
runtime/ 日志、PID、缓存和测试产物,不进入Git
|
||||
tests/ 单元、契约、边界和浏览器回归
|
||||
tools/ 启动、注册表生成和统一验收工具
|
||||
```
|
||||
|
||||
## 3. 本地启动
|
||||
|
||||
```powershell
|
||||
cd app
|
||||
python -m pip install -r requirements.txt
|
||||
powershell -ExecutionPolicy Bypass -File tools/start_local.ps1 -Port 8797
|
||||
```
|
||||
|
||||
日志、PID和Python缓存写入`runtime/`。前台启动可使用:
|
||||
|
||||
```powershell
|
||||
python server.py --host 127.0.0.1 --port 8797
|
||||
```
|
||||
|
||||
## 4. 修改流程
|
||||
|
||||
1. 阅读`AGENTS.md`、`ARCHITECTURE.md`、相关注册表和测试。
|
||||
2. 找到职责唯一所有者,不建立转发层或临时补丁文件。
|
||||
3. 保持API、数据库、权限、数据口径和用户可见行为兼容。
|
||||
4. 行情字段必须登记来源、时间、单位、复权、新鲜度和降级规则。
|
||||
5. 用户私有数据必须包含并按`user_id`隔离。
|
||||
6. 先运行领域测试,再运行统一验收,最后做真实浏览器检查。
|
||||
|
||||
## 5. 自动验收
|
||||
|
||||
```powershell
|
||||
python tools/verify_baseline.py
|
||||
python tools/verify_baseline.py --e2e
|
||||
```
|
||||
|
||||
统一验收覆盖全部Python测试、API与架构注册表、JavaScript语法、Git空白检查和SQLite只读完整性;
|
||||
`--e2e`额外运行Playwright。前端改动还需人工检查日间/夜间、1080P/4K、移动端、滚动、弹窗、
|
||||
图表、问师流式结果和问天动画。
|
||||
|
||||
## 6. 数据与密钥
|
||||
|
||||
- 正式数据库固定为`data/review.db`。
|
||||
- `.env`中的`APP_ENCRYPTION_KEY`必须与数据库成对备份。
|
||||
- 不要复制正在写入的SQLite文件;停服或使用SQLite backup API。
|
||||
- `.env`、Token、密码、数据库、私有Skill和运行日志不得提交Git或写入Docker镜像。
|
||||
- 同一时刻只允许一个正式实例写主库。
|
||||
|
||||
## 7. 部署与回退
|
||||
|
||||
Docker以当前目录为构建上下文,持久化挂载`./data:/app/data`。升级前保存当前Git提交、数据库一致性
|
||||
备份和`.env`;升级后验证健康、登录、最近交易日、私有数据、数据源、LLM和关键写入流程。
|
||||
|
||||
出现问题时先停止新进程,保存故障日志和数据库副本,再恢复上一Git提交及其成对数据库和`.env`。
|
||||
不要使用破坏性Git命令覆盖未提交数据。
|
||||
@@ -0,0 +1,13 @@
|
||||
# 迁移文档入口
|
||||
|
||||
后续恢复迁移工作时按以下顺序读取,禁止从历史阶段文档直接继续:
|
||||
|
||||
1. [`../../AGENTS.md`](../../AGENTS.md):仓库级不可违反约束。
|
||||
2. [`原版保真迁移总纲.md`](原版保真迁移总纲.md):当前唯一有效的迁移方法。
|
||||
3. [`保真迁移状态.json`](保真迁移状态.json):机器可读当前状态和下一步。
|
||||
4. [`保真迁移账本.md`](保真迁移账本.md):连续检查点、资产处置和决策记录。
|
||||
5. [`next失败冻结记录.md`](next失败冻结记录.md):失败实现的隔离边界。
|
||||
6. [`人工维护与本地切换指南.md`](人工维护与本地切换指南.md):迁移版目录、验证、数据边界、人工验收、切换与回退。
|
||||
7. [`evidence/slice-11/README.md`](evidence/slice-11/README.md):当前候选的试删审计和全量自动验收结果。
|
||||
|
||||
`重建迁移章程.md`及`next/`内阶段文档均是失败过程历史记录,不再指导后续实施。
|
||||
@@ -0,0 +1,34 @@
|
||||
# 切片00:原样可运行副本
|
||||
|
||||
## 范围
|
||||
|
||||
- 原版基线:`41329943c4878fc09ed82ec376eb93ab151e4092`
|
||||
- 目标目录:`app/`
|
||||
- 数据库:使用SQLite在线备份生成`app/data/review.db`,不写原版数据库。
|
||||
- 排除:冻结的`next/`、密钥、正式数据库、日志、缓存、构建产物。
|
||||
|
||||
## 自动证据
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| 原版与目标源文件SHA-256 | 628项一致,0项差异 |
|
||||
| Python测试 | 231项通过 |
|
||||
| Playwright测试 | 45项通过 |
|
||||
| 数据库结构 | 36张表一致 |
|
||||
| 数据库行数 | 2,830,708行一致 |
|
||||
| SQLite完整性 | `ok` |
|
||||
| 真实服务 | `http://127.0.0.1:8785/api/health`返回`ok` |
|
||||
| 真实登录 | 管理员账号登录成功,默认进入情绪周期 |
|
||||
|
||||
## 视觉证据
|
||||
|
||||
- `app-exact-light-1920x1080.png`
|
||||
- `app-exact-dark-1920x1080.png`
|
||||
- `app-exact-light-390x844.png`
|
||||
- `app-exact-dark-390x844.png`
|
||||
|
||||
四个视口均使用真实服务、数据库副本和原版前端资产。1920与390宽度下没有页面横向溢出。
|
||||
|
||||
## 结论
|
||||
|
||||
`app/`当前是原版的可独立运行副本,不包含来自`next/`的产品实现,也尚未进行业务拆分。
|
||||
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,57 @@
|
||||
# 切片 01:启动、HTTP、账号、会员与系统管理
|
||||
|
||||
> 基线:`4083dce`(切片 00 原样可运行副本)
|
||||
> 回档标签:`xiaobai-preservation-slice-01-20260731`
|
||||
> 结论:自动差分通过;前端运行资产未改动,仍等待最终全站人工验收
|
||||
|
||||
## 1. 本次范围
|
||||
|
||||
本切片只整理原版 `app/` 副本中的启动、通用 HTTP、账号、会员和系统管理实现。所有实现均从
|
||||
原文件机械移动,原导入面由兼容外壳保持;没有从冻结的 `next/` 复制产品代码,也没有重新设计
|
||||
页面、接口或数据结构。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 兼容方式 |
|
||||
|---|---|---|
|
||||
| `app/server.py` 的进程组装与完整应用服务 | `app/backend/application.py`、`app/backend/bootstrap/runtime.py` | `app/server.py` 继续导出原符号并保持原启动命令 |
|
||||
| `app/server.py` 的 Cookie、鉴权、CSRF、静态文件和 JSON 传输 | `app/backend/http/handler.py` | `RequestHandler` 组合 `HttpTransportMixin` |
|
||||
| `app/server.py` 的账号与会员 HTTP 方法 | `app/backend/features/accounts/http.py` | `RequestHandler` 组合 `AccountHttpMixin` |
|
||||
| `app/server.py` 的系统管理 HTTP 方法 | `app/backend/features/system/http.py` | `RequestHandler` 组合 `SystemHttpMixin` |
|
||||
| `app/server.py` 的账号、会员和出生信息业务方法 | `app/backend/features/accounts/service.py` | `DashboardService` 委托 `AccountService` |
|
||||
| `app/database.py` 的账号、会话、会员与用户凭据方法 | `app/backend/features/accounts/repository.py` | `ReviewDatabase` 组合 `AccountRepositoryMixin` |
|
||||
| `app/database.py` 的系统设置存取方法 | `app/backend/features/system/repository.py` | `ReviewDatabase` 组合 `SystemSettingsRepositoryMixin` |
|
||||
| `app/security.py` | `app/backend/features/accounts/security.py` | 根文件保留原导出 |
|
||||
| `app/app_config.py` | `app/backend/bootstrap/config.py` | 根文件保留原导出 |
|
||||
|
||||
## 2. 不变量与差分证据
|
||||
|
||||
- API:`config/api.config.json` 与 `app/config/api.config.json` 的 SHA-256 相同;路由路径、方法、
|
||||
访问角色、状态码和错误载荷由原注册表与全量测试继续约束。
|
||||
- 数据库:原版和迁移副本均为 36 张业务表、62 个 schema 对象,规范化 schema SHA-256 均为
|
||||
`0615a0423856d0eb02bccd81a071840bd50d6563da96d556cec49967ad8a5f4c`。
|
||||
- 账号边界:注册、登录、会话、密码、会员和出生信息使用临时数据库执行同一原版契约,5 项专项
|
||||
测试全部通过。
|
||||
- 前端:本切片没有修改 `app/static/`;`index.html`、`app.js`、`styles.css` 与根目录原版对应文件
|
||||
哈希相同。`app-light-1920x1080.png` 保存本切片运行截图。
|
||||
- 兼容:原 `python server.py` 命令以及测试和外部模块使用的 `server.DashboardService`、
|
||||
`server.RequestHandler`、`server.SERVICE`、`server.automatic_screener_jobs` 均保持可用。
|
||||
|
||||
## 3. 验证结果
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| `python -m unittest discover -s tests -q` | 236 项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_accounts -v` | 5 项通过 |
|
||||
| `npx playwright test --reporter=dot` | 45 项通过 |
|
||||
| `python -m compileall -q ...` | 通过 |
|
||||
| `git diff --check` | 通过(仅 Git 的 CRLF 提示) |
|
||||
|
||||
Playwright 在自行创建 Python 静态服务器时存在 Windows 子进程退出等待问题;验证时预先启动
|
||||
`127.0.0.1:8876` 静态服务器并由 Playwright 复用,45 项用例在 143 秒内正常返回退出码 0。
|
||||
|
||||
## 4. 保留与待处理
|
||||
|
||||
- `app/backend/application.py` 仍包含其余尚未迁移切片的原版实现,这是刻意保留,不是本切片遗漏。
|
||||
- `app/server.py`、`app/app_config.py`、`app/security.py` 和 `app/database.py` 的兼容面须等所有消费者
|
||||
完成归位后再评估;本阶段禁止删除。
|
||||
- 没有删除任何不确定代码,没有修改正式根目录数据库,也没有切换 Docker/NAS。
|
||||
|
||||
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,61 @@
|
||||
# 切片 02:公共行情、搜索、详情、图表与数据网关
|
||||
|
||||
> 基线:`4002f09`(切片 01)
|
||||
> 回档标签:`xiaobai-preservation-slice-02-20260731`
|
||||
> 结论:源码、API、数据库、真实页面和浏览器回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片从原版副本机械移动公共行情纵向链路,没有从 `next/` 取用代码,也没有修改计算逻辑。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 原位置兼容 |
|
||||
|---|---|---|
|
||||
| `app/backend/application.py` 的 30 个总览、搜索、详情、图表方法 | `app/backend/features/market/service.py` | `DashboardService` 继承 `MarketServiceMixin` |
|
||||
| `app/database.py` 的 11 个行情快照、搜索目录、同步记录方法 | `app/backend/features/market/repository.py` | `ReviewDatabase` 继承 `MarketRepositoryMixin` |
|
||||
| `app/tushare_client.py` | `app/backend/data/providers/tushare_client.py` | 根模块为同一模块对象的兼容别名 |
|
||||
| `app/ifind_client.py` | `app/backend/data/providers/ifind_client.py` | 根模块为同一模块对象的兼容别名 |
|
||||
| `app/realtime_aggregator.py` | `app/backend/data/realtime.py` | 根模块为同一模块对象的兼容别名 |
|
||||
| `app/chart_data_provider.py` | `app/backend/features/market/charts.py` | 根模块为同一模块对象的兼容别名 |
|
||||
|
||||
`app/tools/move_class_methods.py` 使用 Python AST 确定方法及装饰器的源码边界,只移动原文本片段。
|
||||
该工具会在缺失方法、目标标记不唯一或源码无法解析时停止,供后续切片继续复用。
|
||||
|
||||
## 2. 等价证据
|
||||
|
||||
- `test_preservation_slice_market.py` 对 30 个业务方法和 11 个 Repository 方法逐项执行无位置信息
|
||||
AST 比较,全部与根目录原版 `server.py`、`database.py` 完全相同。
|
||||
- Tushare、iFinD 和实时观察器文件与原版 SHA-256 完全相同;图表模块全部类和函数 AST 与原版相同,
|
||||
仅内部导入改为新规范位置。
|
||||
- 四个根级兼容模块与新模块共享同一类对象,旧导入和旧 monkeypatch 路径继续有效。
|
||||
- `config/api.config.json`、API路径、鉴权角色、错误结构和数据库 schema 未修改。
|
||||
- `app/static/` 未修改;七个核心 HTML/JS/CSS 文件哈希继续与原版相同。
|
||||
- `app-light-1280x720.png` 为真实 `8785` 服务完成载入后的日间模式截图,SHA-256 为
|
||||
`a7ee1b682f68c418d792727dbc4534d7494dae4f4811cdbba208bd86dcf25d10`。
|
||||
|
||||
## 3. 真实运行检查
|
||||
|
||||
- 迁移副本:`http://127.0.0.1:8785/`,管理员登录成功。
|
||||
- 总览:返回 2026-07-30 Tushare 已缓存行情,涨停 56 只。
|
||||
- 搜索:搜索“中国平安”返回 `601318`,点击后打开完整个股详情、日 K、资金流、事件逻辑和复盘笔记。
|
||||
- 页面:1280px 视口无横向溢出,数据加载状态正常,浏览器控制台 0 个错误。
|
||||
- 分时:迁移版与原版在当前本机网络环境均返回同一个 `Intraday chart request failed`,因此记录为
|
||||
既存外部接口状态,不是本切片差异;没有擅自增加降级或改变来源策略。
|
||||
|
||||
## 4. 自动验证
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| `python -m unittest discover -s tests -q` | 241 项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_market -q` | 6 项通过 |
|
||||
| 行情、图表、实时与数据库专项集合 | 61 项通过 |
|
||||
| `npx playwright test --reporter=dot` | 45 项通过 |
|
||||
| `python -m compileall -q ...` | 通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
## 5. 保留边界
|
||||
|
||||
- 情绪计算仍在 `application.py`,切片 03 再归位;行情服务只通过继承调用,没有复制。
|
||||
- 竞价、题材、人气、龙虎榜和问天对公共行情客户端的调用仍可通过兼容别名工作,待各自切片迁移。
|
||||
- 根级四个数据模块、`DashboardService` 和 `ReviewDatabase` 的兼容面在所有消费者完成迁移前保留。
|
||||
- 没有删除待定代码、没有改动根目录正式数据库、没有切换 Docker/NAS。
|
||||
|
||||
|
After Width: | Height: | Size: 92 KiB |
@@ -0,0 +1,66 @@
|
||||
# 切片 03:情绪周期、五类股池与涨停表现
|
||||
|
||||
> 基线:`a426432`(切片 02)
|
||||
> 回档标签:`xiaobai-preservation-slice-03-20260731`
|
||||
> 结论:源码、API、数据库、真实页面和浏览器回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片只移动原版副本中的真实实现,没有从 `next/` 取用代码,也没有改写情绪公式、股池数据、
|
||||
原因补全、表格、样式或交互。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 原位置兼容 |
|
||||
|---|---|---|
|
||||
| `app/backend/application.py` 的 2 个情绪服务方法 | `app/backend/features/sentiment/service.py` | `DashboardService` 继承 `SentimentServiceMixin` |
|
||||
| `app/backend/application.py` 的 6 个股池原因及事件补全方法 | `app/backend/features/pools/service.py` | `DashboardService` 继承 `PoolServiceMixin` |
|
||||
| `app/database.py` 的 2 个原因覆盖方法 | `app/backend/features/pools/repository.py` | `ReviewDatabase` 继承 `PoolRepositoryMixin` |
|
||||
| `app/sentiment_engine.py` | `app/backend/features/sentiment/engine.py` | 根模块为同一模块对象的兼容别名 |
|
||||
|
||||
五类股池、涨停梯队和涨停表现仍由切片 02 已归位的原 Tushare 总览实现生成,本切片没有建立第二套
|
||||
计算或数据来源。
|
||||
|
||||
## 2. 等价证据
|
||||
|
||||
- `test_preservation_slice_sentiment_pools.py` 对 8 个业务方法和 2 个 Repository 方法逐项执行无位置
|
||||
信息 AST 比较,全部与根目录原版 `server.py`、`database.py` 完全相同。
|
||||
- 新的情绪引擎文件与原版 `sentiment_engine.py` SHA-256 完全相同;根级兼容模块与新模块是同一模块对象。
|
||||
- 已归位的应用、行情服务、Tushare Provider、演示数据和选股模块直接导入新的唯一实现;Tushare
|
||||
Provider 仅调整该导入,其全部类和函数 AST 继续与原版一致。
|
||||
- 原版 `8784` 与迁移版 `8785` 在相同账号、日期和数据库副本上请求 `/api/dashboard` 与
|
||||
`/api/sentiment/history`,JSON 状态、字段、值和顺序完全相同。
|
||||
- 2026-07-30 的同请求结果均为:涨停 56、炸板 23、跌停 83、昨日涨停 81、情绪历史 20 日。
|
||||
- 原版和迁移版数据库均为 62 个 schema 对象,schema 哈希均为
|
||||
`17918327f8b919496e6630458293f9f777c7c24662625bb3fc0b64ff0a8fbeef`。
|
||||
- `config/api.config.json`、API 路径、鉴权和 `app/static/` 未修改。
|
||||
- `app-light-1920x1080.png` 是真实迁移服务载入完成后的情绪周期页面,SHA-256 为
|
||||
`e387417abbe0667e00875a8d4061b5546748ecf2452a692d06d078d516330dab`。
|
||||
|
||||
## 3. 真实运行检查
|
||||
|
||||
- 迁移副本:`http://127.0.0.1:8785/`,管理员会话与缓存行情载入正常。
|
||||
- 情绪周期:20 个连续交易日、当前阶段、评分构成和交易日明细均完整显示。
|
||||
- 股池:涨停池 56 行、炸板池 23 行、跌停池 83 行、昨日涨停 81 行。
|
||||
- 涨停表现:四档晋级率、市场宽度和今日结论均显示原版结果。
|
||||
- 1920×1080 下六个页面横向溢出均为 0;日间、夜间背景与面板状态正常;浏览器控制台无迁移错误。
|
||||
|
||||
## 4. 自动验证
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| `python -m unittest discover -s tests -q` | 248 项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_sentiment_pools -q` | 6 项通过 |
|
||||
| 情绪、总览、缓存、iFinD 与前端契约专项集合 | 48 项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45 项通过 |
|
||||
| `python -m compileall -q ...` | 通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
Windows 下由 Playwright 自行创建临时静态服务器时,45 项完成后子进程无法回收;改为预先启动同一个
|
||||
`8876` 静态服务器并让 Playwright 复用后,测试以零退出码正常结束,结果为 `45 passed (2.1m)`。
|
||||
|
||||
## 5. 保留边界
|
||||
|
||||
- 板块轮动仍调用情绪历史公共函数,待切片 04 与市场天梯一并归位。
|
||||
- 竞价、题材、人气和龙虎榜对股池数据的消费保持原调用路径,待切片 05 迁移。
|
||||
- 根级情绪引擎兼容模块、`DashboardService` 与 `ReviewDatabase` 兼容面继续保留;数据库内尚未迁移的
|
||||
选股统计方法仍走兼容别名,待切片 06 随完整方法一并归位。
|
||||
- 没有删除待定代码、没有改动根目录正式数据库、没有切换 Docker/NAS。
|
||||
|
After Width: | Height: | Size: 151 KiB |
@@ -0,0 +1,58 @@
|
||||
# 切片 04:市场天梯与板块轮动
|
||||
|
||||
> 基线:`b3555d2`(切片 03)
|
||||
> 回档标签:`xiaobai-preservation-slice-04-20260731`
|
||||
> 结论:源码、API、真实页面和浏览器回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片从原版副本机械移动板块轮动服务,没有从 `next/` 取用代码,也没有修改天梯、轮动的计算、
|
||||
排序、展开、配色、页面结构或交互。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 原位置兼容 |
|
||||
|---|---|---|
|
||||
| `app/backend/application.py` 的 2 个轮动方法 | `app/backend/features/rotation/service.py` | `DashboardService` 继承 `RotationServiceMixin` |
|
||||
| Tushare Provider 的天梯与轮动构造函数 | 保持 `app/backend/data/providers/tushare_client.py` | 切片 02 已归位的公共数据实现 |
|
||||
|
||||
市场天梯没有独立后端 API 或第二套计算,直接展示 `/api/dashboard` 中原 Tushare 实现生成的
|
||||
`ladders`;因此没有为目录形式建立空的天梯服务。
|
||||
|
||||
## 2. 等价证据
|
||||
|
||||
- `test_preservation_slice_ladder_rotation.py` 对 2 个轮动服务方法逐项执行无位置信息 AST 比较,
|
||||
全部与根目录原版 `server.py` 完全相同。
|
||||
- `_build_ladders` 与 `_build_sector_rotation` 两个原数据构造函数的 AST 与根目录原版完全相同。
|
||||
- 原版 `8784` 与迁移版 `8785` 在相同账号、日期和数据库副本上返回的天梯数据及 9 日轮动历史
|
||||
JSON 逐字段完全相同。
|
||||
- 成分股接口在当前外部网络状态下两版均返回 HTTP 400、`bad_request` 和相同的
|
||||
`该板块成分股暂不可用:Tushare request failed:`,没有改变错误或增加静默降级。
|
||||
- `config/api.config.json`、API 路径、鉴权、数据库 schema 和 `app/static/` 未修改。
|
||||
|
||||
## 3. 真实运行检查
|
||||
|
||||
- 市场天梯:8 个层级(含断层)、18 个首屏股票单元格、3 个结构分析模块正常;1920×1080 下
|
||||
页面宽度无溢出,首板展开入口保留。
|
||||
- 板块轮动:9 个交易日、每日 Top 12 共 108 个板块单元格、由远到近/由近到远两个排序入口正常;
|
||||
1920×1080 下页面宽度无溢出并保持全页滚动。
|
||||
- 日间模式页面控制台没有错误或警告。
|
||||
- `app-light-ladder-1920x1080.png` SHA-256:
|
||||
`9e57d18d92e745fd92131f7bf08f21faaaa745476dd942cdaa2a703b9a7a303a`。
|
||||
- `app-light-rotation-1920x1080.png` SHA-256:
|
||||
`03c092bb40bd0eb672136dff853abffc87e9790840107c2f22e6cf31d5f83c09`。
|
||||
|
||||
## 4. 自动验证
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| `python -m unittest discover -s tests -q` | 252 项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_ladder_rotation -q` | 4 项通过 |
|
||||
| 切片 02 至 04 与总览缓存专项集合 | 24 项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45 项通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
## 5. 保留边界
|
||||
|
||||
- 成分股接口依赖的日行情与因子持久化方法仍由原 `ReviewDatabase` 提供,因其同时服务智能选股,
|
||||
待切片 06 随完整共享职责归位。
|
||||
- 天梯和轮动前端资产保持原位,切片 10 再按页面职责归档;当前没有复制或改写。
|
||||
- 没有删除待定代码、没有改动根目录正式数据库、没有切换 Docker/NAS。
|
||||
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 198 KiB |
@@ -0,0 +1,69 @@
|
||||
# 切片 05:集合竞价、题材库、人气热榜与龙虎榜
|
||||
|
||||
> 基线:`814e757`(切片 04)
|
||||
> 回档标签:`xiaobai-preservation-slice-05-20260731`
|
||||
> 结论:源码、API、数据库、真实页面和全量回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片没有从`next/`取用代码,也没有重写计算、页面或接口。集合竞价、题材库和人气热榜原本
|
||||
共享`MarketInsightsService`,其中竞价候选会直接调用人气榜热度数据,因此整体移动到公共行情
|
||||
领域,避免拆出互相复制的实现;各页面入口仍按功能目录归位。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 兼容方式 |
|
||||
|---|---|---|
|
||||
| `app/market_insights.py` | `app/backend/features/market/insights.py` | 根级模块导出同一类对象 |
|
||||
| `DashboardService`竞价入口 | `app/backend/features/auction/service.py` | `AuctionServiceMixin` |
|
||||
| `DashboardService`题材入口 | `app/backend/features/themes/service.py` | `ThemeServiceMixin` |
|
||||
| `DashboardService`人气入口 | `app/backend/features/popularity/service.py` | `PopularityServiceMixin` |
|
||||
| `DashboardService`龙虎榜及游资档案 | `app/backend/features/dragon_tiger/service.py` | `DragonTigerServiceMixin` |
|
||||
| 竞价、人气、龙虎榜持久化方法 | 对应功能目录的`repository.py` | `ReviewDatabase`继承原接口 |
|
||||
|
||||
Tushare Provider 中`hot_money_profiles`与`dragon_tiger`继续保持切片02归位的唯一实现,没有为目录
|
||||
形式再制造一套数据构造逻辑。
|
||||
|
||||
## 2. 源码与接口等价
|
||||
|
||||
- `test_preservation_slice_market_insights.py`逐项比较22个市场洞察方法、8个页面服务方法、7个
|
||||
Repository方法和2个Tushare方法,全部与根目录原版无位置信息AST一致。
|
||||
- `DashboardService`与`ReviewDatabase`不再重复保留已移动方法;根级`market_insights`与新模块
|
||||
暴露同一个`MarketInsightsService`类对象。
|
||||
- 原版`8784`和迁移版`8785`使用同一数据库的独立副本,集合竞价、题材库、题材详情、人气热榜、
|
||||
龙虎榜、游资档案和席位别名共7个真实API状态码及JSON一致。
|
||||
- 题材详情在当前外部网络条件下两版均返回HTTP 400;差分只排除每次请求随机生成的
|
||||
`request_id`,错误码与错误内容仍完全一致。
|
||||
- 完整接口摘要见`api-diff.json`。
|
||||
|
||||
## 3. 数据库差分
|
||||
|
||||
- 两个副本均为62个schema对象,哈希均为
|
||||
`60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1`。
|
||||
- `auction_factors` 511914行、`popularity_factors` 232行、`lhb_institution_daily` 47行、
|
||||
`seat_aliases` 0行、`stock_master` 5535行均逐行一致。
|
||||
- 完整表计数与哈希见`database-diff.json`;运行数据库副本已在验收后删除,未提交凭据或正式数据。
|
||||
|
||||
## 4. 真实浏览器检查
|
||||
|
||||
- 1920×1080日间模式检查集合竞价、题材库、人气热榜和龙虎榜四页;均无横向溢出,控制台无
|
||||
错误或警告。
|
||||
- 集合竞价载入30行重点候选;题材库载入394个题材及选中题材成分股;人气热榜载入3个摘要
|
||||
模块和200行综合榜;龙虎榜按当前缓存显示既有不可用空态。
|
||||
- 四页HTML、主JS、CSS及各自页面JS与根目录原版字节哈希一致。
|
||||
- 截图SHA-256:
|
||||
- `app-light-auction-1920x1080.png`:`8065086c8f2b360aeb1004bd60429f3e2d7f1b8c872ec4a965e9a5d0c016b915`
|
||||
- `app-light-themes-1920x1080.png`:`80e1e41101497ee7213e1dadfaa4c9572a1c47b3495edd09e36745ccdb639402`
|
||||
- `app-light-popularity-1920x1080.png`:`614d6b7770f4e9ec72c059ab8a4129486df9eff5c4dd7e8279cc68ebcb80a36b`
|
||||
- `app-light-dragon-tiger-1920x1080.png`:`2f1e2ad3a9bc3874c73bae884fc8744177cef354c7176559da1453fab1f85993`
|
||||
|
||||
## 5. 自动验证与保留边界
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| `python -m unittest discover -s tests -q` | 258项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_market_insights -q` | 6项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45项通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
- 竞价、人气和龙虎榜因子同时服务切片06智能选股,迁移后仍由`ReviewDatabase`原方法名暴露。
|
||||
- 前端资产保持原位置,切片10再按页面职责归档;本切片没有改DOM、CSS、动画或交互。
|
||||
- 没有删除待定代码、没有修改根目录正式数据库、没有切换Docker/NAS。
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"endpoint": "/api/auction?trade_date=2026-07-29",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "523144cc14d876577b7d518cdf38fd2722b13a8f01ed5d2e20dcc38f6a2624ce",
|
||||
"migrated_sha256": "523144cc14d876577b7d518cdf38fd2722b13a8f01ed5d2e20dcc38f6a2624ce",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/themes?trade_date=2026-07-29",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "95c2ad418f18d877d94ec2a71fe6fafd5e329b069d87a9300f7fcac92d4ba5d1",
|
||||
"migrated_sha256": "95c2ad418f18d877d94ec2a71fe6fafd5e329b069d87a9300f7fcac92d4ba5d1",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/themes/detail?code=885001.TI&trade_date=2026-07-29",
|
||||
"original_status": 400,
|
||||
"migrated_status": 400,
|
||||
"original_sha256": "b11a3314b172d3ad6ba28d969fcd6a9a2a4b49e7d29ed804496a4dfecd2a364a",
|
||||
"migrated_sha256": "b11a3314b172d3ad6ba28d969fcd6a9a2a4b49e7d29ed804496a4dfecd2a364a",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/popularity?trade_date=2026-07-29",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "e62a93c41f7c3f95c3d47f8ccaedaa809563c8dd65c04dd54b164f73e6014e94",
|
||||
"migrated_sha256": "e62a93c41f7c3f95c3d47f8ccaedaa809563c8dd65c04dd54b164f73e6014e94",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/dragon-tiger?trade_date=2026-07-29",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "a3998b935377d5fd0673ec5b9214d1b0a64680d155c61e6cff49d0d5fcf0e843",
|
||||
"migrated_sha256": "a3998b935377d5fd0673ec5b9214d1b0a64680d155c61e6cff49d0d5fcf0e843",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/dragon-tiger/profiles",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "dfb1e534c018ec12fd8d8ee0e7fe0f234e73dc7715d7f0a948f74ef8d12d779a",
|
||||
"migrated_sha256": "dfb1e534c018ec12fd8d8ee0e7fe0f234e73dc7715d7f0a948f74ef8d12d779a",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/seat-aliases",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "2b0fb0a6b3e353c69158d61221c2200e4199d0d60dd0b9d99702a22eaa917a78",
|
||||
"migrated_sha256": "2b0fb0a6b3e353c69158d61221c2200e4199d0d60dd0b9d99702a22eaa917a78",
|
||||
"equal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 136 KiB |
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"schema": {
|
||||
"object_count": 62,
|
||||
"original_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"migrated_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"equal": true
|
||||
},
|
||||
"tables": [
|
||||
{
|
||||
"table": "auction_factors",
|
||||
"original_count": 511914,
|
||||
"migrated_count": 511914,
|
||||
"original_sha256": "3d0470787adaf7c4cf5f15f5ad9fa1d67c8fcd4807285ac264eeacdcf5054cd1",
|
||||
"migrated_sha256": "3d0470787adaf7c4cf5f15f5ad9fa1d67c8fcd4807285ac264eeacdcf5054cd1",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "popularity_factors",
|
||||
"original_count": 232,
|
||||
"migrated_count": 232,
|
||||
"original_sha256": "3f4c61a13ceaa1ed9ecb28f86241a8a478a6757d6a44b46f432f73c0226bba7f",
|
||||
"migrated_sha256": "3f4c61a13ceaa1ed9ecb28f86241a8a478a6757d6a44b46f432f73c0226bba7f",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "lhb_institution_daily",
|
||||
"original_count": 47,
|
||||
"migrated_count": 47,
|
||||
"original_sha256": "1f847eac34d2ba576b429da208667f3b67b591d36ee66800803605bbc447970c",
|
||||
"migrated_sha256": "1f847eac34d2ba576b429da208667f3b67b591d36ee66800803605bbc447970c",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "seat_aliases",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "stock_master",
|
||||
"original_count": 5535,
|
||||
"migrated_count": 5535,
|
||||
"original_sha256": "8656e2d189d3520433fb3552e17998e4bf17bcf6838403cddc6719282b23e792",
|
||||
"migrated_sha256": "8656e2d189d3520433fb3552e17998e4bf17bcf6838403cddc6719282b23e792",
|
||||
"equal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
# 切片 06:智能选股、自定义选股与策略持续跟踪
|
||||
|
||||
> 基线:`cf2aad2`(切片 05)
|
||||
> 回档标签:`xiaobai-preservation-slice-06-20260731`
|
||||
> 结论:源码、API、数据库、真实页面和全量回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片只机械移动原版选股实现,没有从`next/`取用代码,也没有改写策略、因子、权重、排序、
|
||||
盘后候选或跟踪逻辑。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 兼容方式 |
|
||||
|---|---|---|
|
||||
| `app/screener.py` | `app/backend/features/screener/engine.py` | 根级模块指向同一模块对象 |
|
||||
| `app/advanced_strategies.py` | `app/backend/features/screener/strategies.py` | 根级模块指向同一模块对象 |
|
||||
| `app/llm_strategy.py` | `app/backend/features/screener/compiler.py` | 根级模块指向同一模块对象 |
|
||||
| `DashboardService`选股入口 | `app/backend/features/screener/service.py` | `ScreenerServiceMixin` |
|
||||
| `ReviewDatabase`选股持久化方法 | `app/backend/features/screener/repository.py` | `ScreenerRepositoryMixin` |
|
||||
| 原策略持续跟踪服务 | `app/backend/features/screener/tracking.py` | 容器直接导入唯一实现 |
|
||||
|
||||
四个同时服务公共行情与选股的方法归入`MarketRepositoryMixin`,没有复制第二套实现:
|
||||
`upsert_stock_master`、`list_stock_master`、`upsert_daily_bars`、`daily_bars_for_date`。
|
||||
|
||||
## 2. 源码与接口等价
|
||||
|
||||
- `test_preservation_slice_screener.py`逐项验证选股引擎、29套策略定义、自然语言策略编译器、
|
||||
13个服务方法、25个选股持久化方法和跟踪服务与原版等价。
|
||||
- 三个根级兼容模块与正式模块暴露同一模块及类对象;原类不再重复保留已移动方法。
|
||||
- 原版与迁移版的`/api/screener/setup`、`/api/screener/tracking`和`/api/screener/run`
|
||||
状态码及业务JSON一致。
|
||||
- 差分仅规范化策略启动刷新和顺序执行必然变化的数组顺序与`updated_at`;其他字段仍逐项比较。
|
||||
- 请求与差分结果见`api-requests.json`和`api-diff.json`。
|
||||
|
||||
## 3. 数据库差分
|
||||
|
||||
- 两个临时副本均为62个schema对象,结构完全一致。
|
||||
- 13张关键表逐行一致,覆盖约143万条日线、77万条技术指标和51万条竞价因子。
|
||||
- `screener_runs`两边均为251行;会话和运行时间戳只在临时副本内规范化。
|
||||
- 正式`data/review.db`和`app/data/review.db`未写入测试策略、测试会话或差分时间戳。
|
||||
- 完整表计数与哈希见`database-diff.json`。
|
||||
|
||||
## 4. 真实浏览器检查
|
||||
|
||||
- 已登录迁移版真实服务,分别检查阶段选股、策略选股、自定义选股和策略持续跟踪。
|
||||
- 阶段选股显示退潮、置信度92%、匹配策略和262日因子就绪状态;策略选股载入29套策略。
|
||||
- 自定义选股保留因子权重、过滤条件、公式入口、手动执行和独立候选结果。
|
||||
- 跟踪页只展示手动加入的候选,批次、标的、T+1/T+3进度、胜率和移除操作均正常。
|
||||
- 各检查状态无横向溢出、无加载遮罩残留,浏览器控制台无错误。
|
||||
- 截图SHA-256:
|
||||
- `curated-screener.jpg`:`529ecd0947b34b42eb63d166c6804b0041e26fa362a171e4d0d7b584e073c05e`
|
||||
- `custom-screener.jpg`:`7f31f50d44bc63a488277be0a74232e2ac256779d0accda42b96cc1eef88cc6e`
|
||||
- `tracking.jpg`:`b19b1bc0cd4a98c29500917676bdcc4cc9a430a0c72dea8aff1110ad911db6e3`
|
||||
|
||||
## 5. 自动验证与保留边界
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| 原版`python -m unittest discover -s tests -q` | 231项通过 |
|
||||
| 迁移版`python -m unittest discover -s tests -q` | 265项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_screener -q` | 7项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45项通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
- Windows下由Playwright自行创建静态服务器时存在子进程不退出的测试基线问题;复用独立的8876
|
||||
测试服务器后45项用例在2.1分钟内通过并正常返回退出码0。
|
||||
- 选股引擎保留原版数据客户端依赖,边界测试将其与普通页面Service区分;后续只可在不改变行为且有
|
||||
独立差分证据时治理该依赖。
|
||||
- 前端资产保持原位置,切片10再按页面职责归档;本切片没有改DOM、CSS、动画或交互。
|
||||
- 没有删除待定代码、没有修改正式数据库、没有切换Docker/NAS。
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "盘后自动候选与策略库",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "e33a10e94accda4948c2af53887ea97736697f588ee1a92f561aa5c4bb28c2fd",
|
||||
"migrated_sha256": "e33a10e94accda4948c2af53887ea97736697f588ee1a92f561aa5c4bb28c2fd",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "策略持续跟踪",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/tracking?limit=12",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "f4476f53152b6a2ce01f2918da5999246cfd0ad7d0b71e80fdea8fa59c7456c2",
|
||||
"migrated_sha256": "f4476f53152b6a2ce01f2918da5999246cfd0ad7d0b71e80fdea8fa59c7456c2",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "自定义选股执行",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/screener/run",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "b3866e7d270aa222bd2bad0c0276b7e81ccd48e5f3a75941a32ef8e559a92c7b",
|
||||
"migrated_sha256": "b3866e7d270aa222bd2bad0c0276b7e81ccd48e5f3a75941a32ef8e559a92c7b",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
[
|
||||
{
|
||||
"name": "盘后自动候选与策略库",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/setup?trade_date=2026-07-30",
|
||||
"sort_lists": {
|
||||
"$.strategies": "id"
|
||||
},
|
||||
"exclude_paths": [
|
||||
"$.strategies[].updated_at"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "策略持续跟踪",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/tracking?limit=12"
|
||||
},
|
||||
{
|
||||
"name": "自定义选股执行",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/screener/run",
|
||||
"exclude_paths": [
|
||||
"$.result.meta.updated_at"
|
||||
],
|
||||
"payload": {
|
||||
"trade_date": "2026-07-30",
|
||||
"regime": "retreat",
|
||||
"strategy_name": "保真迁移差分策略",
|
||||
"mode": "quant",
|
||||
"run_backtest": false,
|
||||
"formula": {
|
||||
"meta": {
|
||||
"library": "quant",
|
||||
"category": "量化公式"
|
||||
},
|
||||
"universe": {
|
||||
"exclude_st": true,
|
||||
"listed_days_min": 120
|
||||
},
|
||||
"filters": [
|
||||
{
|
||||
"field": "pct_chg",
|
||||
"op": "between",
|
||||
"value": [-2, 2]
|
||||
}
|
||||
],
|
||||
"score": [
|
||||
{
|
||||
"field": "amount_billion",
|
||||
"weight": 1,
|
||||
"direction": "desc"
|
||||
}
|
||||
],
|
||||
"limit": 5,
|
||||
"min_score": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 89 KiB |
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"schema": {
|
||||
"object_count": 62,
|
||||
"original_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"migrated_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"equal": true
|
||||
},
|
||||
"tables": [
|
||||
{
|
||||
"table": "stock_master",
|
||||
"original_count": 5535,
|
||||
"migrated_count": 5535,
|
||||
"original_sha256": "8656e2d189d3520433fb3552e17998e4bf17bcf6838403cddc6719282b23e792",
|
||||
"migrated_sha256": "8656e2d189d3520433fb3552e17998e4bf17bcf6838403cddc6719282b23e792",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "daily_bars",
|
||||
"original_count": 1430501,
|
||||
"migrated_count": 1430501,
|
||||
"original_sha256": "d72a06755d92d6be95ab4f4c98a4718e973bee59d4f381b01acb963b6d7ae9e6",
|
||||
"migrated_sha256": "d72a06755d92d6be95ab4f4c98a4718e973bee59d4f381b01acb963b6d7ae9e6",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "benchmark_bars",
|
||||
"original_count": 261,
|
||||
"migrated_count": 261,
|
||||
"original_sha256": "691f7666dddcfdf7ebe8ba1d3e1ddd8f2d9670d1d7a06e740a929f3ef32ec1c9",
|
||||
"migrated_sha256": "691f7666dddcfdf7ebe8ba1d3e1ddd8f2d9670d1d7a06e740a929f3ef32ec1c9",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "daily_indicators",
|
||||
"original_count": 770238,
|
||||
"migrated_count": 770238,
|
||||
"original_sha256": "cd5a71c7d8de201ff2e3e58c64e2617a31d247911732fbddf81acb025d31fde9",
|
||||
"migrated_sha256": "cd5a71c7d8de201ff2e3e58c64e2617a31d247911732fbddf81acb025d31fde9",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "fundamental_indicators",
|
||||
"original_count": 49490,
|
||||
"migrated_count": 49490,
|
||||
"original_sha256": "4b6859256063d3a0238ba3cbbbe2842f1278cf88d44b0f6114f1af978b8bfa7d",
|
||||
"migrated_sha256": "4b6859256063d3a0238ba3cbbbe2842f1278cf88d44b0f6114f1af978b8bfa7d",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "moneyflow_daily",
|
||||
"original_count": 36368,
|
||||
"migrated_count": 36368,
|
||||
"original_sha256": "f7591dec150c7e0094568e3c46d77fc54e14a803223e15940dc54af1017bd83d",
|
||||
"migrated_sha256": "f7591dec150c7e0094568e3c46d77fc54e14a803223e15940dc54af1017bd83d",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "earnings_events",
|
||||
"original_count": 580,
|
||||
"migrated_count": 580,
|
||||
"original_sha256": "ebac03fe9ef491a6d6d2b5bc8f5ee08b609fa0ebe5992087fa067216490a6b9d",
|
||||
"migrated_sha256": "ebac03fe9ef491a6d6d2b5bc8f5ee08b609fa0ebe5992087fa067216490a6b9d",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "auction_factors",
|
||||
"original_count": 511914,
|
||||
"migrated_count": 511914,
|
||||
"original_sha256": "3d0470787adaf7c4cf5f15f5ad9fa1d67c8fcd4807285ac264eeacdcf5054cd1",
|
||||
"migrated_sha256": "3d0470787adaf7c4cf5f15f5ad9fa1d67c8fcd4807285ac264eeacdcf5054cd1",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "popularity_factors",
|
||||
"original_count": 232,
|
||||
"migrated_count": 232,
|
||||
"original_sha256": "3f4c61a13ceaa1ed9ecb28f86241a8a478a6757d6a44b46f432f73c0226bba7f",
|
||||
"migrated_sha256": "3f4c61a13ceaa1ed9ecb28f86241a8a478a6757d6a44b46f432f73c0226bba7f",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "lhb_institution_daily",
|
||||
"original_count": 47,
|
||||
"migrated_count": 47,
|
||||
"original_sha256": "1f847eac34d2ba576b429da208667f3b67b591d36ee66800803605bbc447970c",
|
||||
"migrated_sha256": "1f847eac34d2ba576b429da208667f3b67b591d36ee66800803605bbc447970c",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "screener_strategies",
|
||||
"original_count": 36,
|
||||
"migrated_count": 36,
|
||||
"original_sha256": "e58859dbd478272d71bf85b6bc4ba45e609baccba74bb818b044d93bafa07312",
|
||||
"migrated_sha256": "e58859dbd478272d71bf85b6bc4ba45e609baccba74bb818b044d93bafa07312",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "screener_runs",
|
||||
"original_count": 251,
|
||||
"migrated_count": 251,
|
||||
"original_sha256": "117b0b95a362c91ddd70ca2def1e62e96e0ade2494ba4302bab5154c8d384498",
|
||||
"migrated_sha256": "117b0b95a362c91ddd70ca2def1e62e96e0ade2494ba4302bab5154c8d384498",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "strategy_tracks",
|
||||
"original_count": 16,
|
||||
"migrated_count": 16,
|
||||
"original_sha256": "f0639b1386b69c92fd815f929864b3b2457a6bf4ca515bee2dca87e9ea29e8c4",
|
||||
"migrated_sha256": "f0639b1386b69c92fd815f929864b3b2457a6bf4ca515bee2dca87e9ea29e8c4",
|
||||
"equal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1,71 @@
|
||||
# 切片 07:问师、模型 Skill 与 LLM 流式链路
|
||||
|
||||
> 基线:`4bab921`(切片 06)
|
||||
> 回档标签:`xiaobai-preservation-slice-07-20260731`
|
||||
> 结论:源码、API、数据库、Skill 资产、真实页面和全量回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片只移动原版问师、Skill 注册、模型访问与流式协议,没有从`next/`取用代码,也没有改写
|
||||
提示词、数据侧重、模型排序、权限、计次、回退或流式去重逻辑。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 兼容方式 |
|
||||
|---|---|---|
|
||||
| `app/mentor_agent.py` | `app/backend/features/mentor/agent.py` | 根级模块指向同一模块对象 |
|
||||
| `DashboardService`问师方法 | `app/backend/features/mentor/service.py` | `MentorServiceMixin` |
|
||||
| 问师消息和偏好持久化 | `app/backend/features/mentor/repository.py` | `MentorRepositoryMixin` |
|
||||
| 问师流式HTTP方法 | `app/backend/features/mentor/http.py` | `MentorHttpMixin` |
|
||||
| `app/llm_stream.py` | `app/backend/llm/stream.py` | 根级模块指向同一模块对象 |
|
||||
| `DashboardService`模型访问方法 | `app/backend/llm/service.py` | `LLMServiceMixin` |
|
||||
| LLM调用审计持久化 | `app/backend/llm/repository.py` | `LLMAuditRepositoryMixin` |
|
||||
| 旧个人模型HTTP兼容入口 | `app/backend/llm/http.py` | `LLMHttpMixin` |
|
||||
|
||||
模型池的增删、主辅模型选择及会员每日额度仍由系统管理负责;`backend/llm`只负责解析当前可用模型、
|
||||
鉴权、计次、首段前回退、流式传输和调用审计,避免复制第二套系统设置逻辑。
|
||||
|
||||
## 2. 源码与 Skill 等价
|
||||
|
||||
- `mentor_agent.py`和`llm_stream.py`与原版文件SHA-256一致,根级兼容模块与正式模块为同一模块对象。
|
||||
- 8个问师服务方法、5个问师Repository方法、2个LLM审计Repository方法与原版无位置信息AST一致。
|
||||
- 21个LLM服务方法中19个与原版AST一致;`_platform_usage_today`及按用户查询的辅助方法保留切片01
|
||||
已验证的用户边界适配,使会员管理可在不切换请求上下文的情况下显示每位用户当日用量。
|
||||
- 问师流式和旧模型HTTP方法仅把原全局`SERVICE`改为Mixin的`self.application_service`,响应状态、
|
||||
Content-Type、NDJSON事件、异常和断连处理不变。
|
||||
- 公开`游资skills`共190个文件,原版与迁移版逐路径、逐SHA-256比较,差异为0。
|
||||
- 私有“小白”Skill仍位于Git忽略的`data/private-mentor-skills`,未复制到公开目录或证据文件。
|
||||
|
||||
## 3. API与数据库差分
|
||||
|
||||
- 原版`8786`和迁移版`8787`使用同一数据库的独立副本。
|
||||
- `/api/mentors/setup`和按用户、模型、日期读取消息的API状态码及业务JSON完全一致。
|
||||
- 两版均为62个schema对象;`mentor_messages` 28行、`mentor_preferences` 45行、`llm_usage` 72行、
|
||||
`system_settings` 1行、`users` 3行,均逐行一致。
|
||||
- 接口证据见`api-requests.json`、`api-diff.json`,数据库证据见`database-diff.json`。
|
||||
- 差分只在系统临时目录的数据库副本上运行,登录会话和后台任务运行数据未纳入业务表比较;临时副本
|
||||
已在验收后删除,正式数据库未写入测试消息或LLM调用记录。
|
||||
|
||||
## 4. 真实浏览器检查
|
||||
|
||||
- 迁移版真实服务载入24个思维模型,管理员可见私有“小白”,公开模型的A/B/C标签、简介和置顶按钮正常。
|
||||
- A级筛选显示13个模型;全部、A级、B级、C级、搜索、整理、清空对话、建议问题、输入框和发送按钮均存在。
|
||||
- 页面宽度与1280像素视口一致,无横向溢出;浏览器控制台无错误。
|
||||
- 浏览器不调用真实外部模型,防止模型容量和网络波动污染迁移结论;流式首段回退、输出后禁止切模、
|
||||
完整快照去重和空响应处理由`test_llm_gateway`、`test_mentor_stream`与`test_llm_stream`覆盖。
|
||||
- 截图SHA-256:
|
||||
- `mentor-all.jpg`:`9cda4d6896da6932b8e9db014eed4882359287d6999e2181af7d945ea6272bd9`
|
||||
- `mentor-a-filter.jpg`:`5dca958d5edf7ab11767474a3c4405d4cb96029f7a1efb35fa8155c61c863984`
|
||||
|
||||
## 5. 自动验证与保留边界
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| 原版`python -m unittest discover -s tests -q` | 231项通过 |
|
||||
| 迁移版`python -m unittest discover -s tests -q` | 273项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_mentor_llm -q` | 8项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45项通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
- `assistant_agent.py`属于切片09复盘助手,本切片不提前移动。
|
||||
- 问天的模型调用属于切片08,本切片只复用统一LLM网关,不移动问天业务。
|
||||
- 前端DOM、页面JS、CSS和移动端行为未改动,统一归档延至切片10。
|
||||
- 没有删除待定代码、没有修改正式数据库、没有切换Docker/NAS。
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "mentor setup and public skill catalog",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "3a5bd83b58e849757664bbfe8cf54e704bc9a7682333deb8297212c488aa3ddc",
|
||||
"migrated_sha256": "3a5bd83b58e849757664bbfe8cf54e704bc9a7682333deb8297212c488aa3ddc",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "mentor messages scoped by user mentor and date",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/messages?mentor_id=kobe92-perspective&trade_date=20260730",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"migrated_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"name": "mentor setup and public skill catalog",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/setup?trade_date=2026-07-30",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "mentor messages scoped by user mentor and date",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/messages?mentor_id=kobe92-perspective&trade_date=20260730",
|
||||
"payload": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"schema": {
|
||||
"object_count": 62,
|
||||
"original_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"migrated_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"equal": true
|
||||
},
|
||||
"tables": [
|
||||
{
|
||||
"table": "mentor_messages",
|
||||
"original_count": 28,
|
||||
"migrated_count": 28,
|
||||
"original_sha256": "b906b776d575ff6fe459b2aa0e8b396267feed36d3d85f7edd7947b284482aeb",
|
||||
"migrated_sha256": "b906b776d575ff6fe459b2aa0e8b396267feed36d3d85f7edd7947b284482aeb",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "mentor_preferences",
|
||||
"original_count": 45,
|
||||
"migrated_count": 45,
|
||||
"original_sha256": "c19c7d05e6745e4f1787d1eed5f39d7e555ec24f8e490b585c19158edc2505ec",
|
||||
"migrated_sha256": "c19c7d05e6745e4f1787d1eed5f39d7e555ec24f8e490b585c19158edc2505ec",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "llm_usage",
|
||||
"original_count": 72,
|
||||
"migrated_count": 72,
|
||||
"original_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"migrated_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "system_settings",
|
||||
"original_count": 1,
|
||||
"migrated_count": 1,
|
||||
"original_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"migrated_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "users",
|
||||
"original_count": 3,
|
||||
"migrated_count": 3,
|
||||
"original_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"migrated_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"equal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,69 @@
|
||||
# 切片 08:问天、观势、观气与观心
|
||||
|
||||
> 基线:`2919229`(切片 07)
|
||||
> 回档标签:`xiaobai-preservation-slice-08-20260731`
|
||||
> 结论:源码、API、数据库、真实页面、动画与全量回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片只移动原版问天 Agent、历法/卦象引擎、服务、持久化与三个 HTTP 入口,没有从
|
||||
`next/`取用代码,也没有改写六爻、安全门、五运六气、个人合参、铜钱起卦、历史去重或
|
||||
LLM 解读逻辑。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 兼容方式 |
|
||||
|---|---|---|
|
||||
| `app/heaven_agent.py` | `app/backend/features/heaven/agent.py` | 根级模块指向同一模块对象 |
|
||||
| `app/heaven_engine.py` | `app/backend/features/heaven/engine.py` | 根级模块指向同一模块对象 |
|
||||
| `DashboardService`问天方法 | `app/backend/features/heaven/service.py` | `HeavenServiceMixin` |
|
||||
| 问天历史持久化 | `app/backend/features/heaven/repository.py` | `HeavenRepositoryMixin` |
|
||||
| 三个问天 POST 入口 | `app/backend/features/heaven/http.py` | `HeavenHttpMixin` |
|
||||
|
||||
历法引擎搬迁后仍从`app/vendor`及`app/data/iching_zh.json`读取原资源;这是唯一资源路径适配,
|
||||
顶层函数与类定义未变。旧测试依赖的根级模块继续有效。
|
||||
|
||||
## 2. 源码等价
|
||||
|
||||
- `heaven_agent.py`与原版文件 SHA-256 一致。
|
||||
- `heaven_engine.py`所有顶层函数和类与原版无位置信息 AST 一致。
|
||||
- 20个问天服务方法中19个与原版 AST 一致;`_heaven_reading_identity`仅将旧巨型类名
|
||||
`DashboardService`替换为已归位的`MarketServiceMixin`静态日期格式化方法。
|
||||
- 5个问天 Repository 方法与原版 AST 一致。
|
||||
- 三个 HTTP 方法只把全局`SERVICE`改为 Mixin 的`self.application_service`,状态码、字段和异常语义不变。
|
||||
- `DashboardService`、`ReviewDatabase`与`RequestHandler`不再重复保留已迁移实现。
|
||||
|
||||
## 3. API 与数据库差分
|
||||
|
||||
- 原版`8788`和迁移版`8789`使用同一正式数据库的两个临时副本。
|
||||
- 固定日期的问天初始化、三类历史读取、六爻成卦和个人五行计算共6个接口,状态码与业务 JSON 完全一致。
|
||||
- 未调用真实`/api/heaven/interpret`,避免外部模型波动及测试记录写入;提示词和调用链由源码等价与单元测试覆盖。
|
||||
- 两版均为62个 schema 对象;`heaven_readings`、`users`、`system_settings`、`llm_usage`、
|
||||
`user_birth_profiles`和`sector_phase_overrides`逐行一致。
|
||||
- 差分只在系统临时目录副本运行,正式数据库未写入问天记录或 LLM 用量。
|
||||
|
||||
## 4. 真实浏览器与动画检查
|
||||
|
||||
- 1920×1080视口逐页检查观势、观气、观心,无横向溢出,页面滚动边界可用。
|
||||
- 观势保留星空、三才六爻与八卦待载入场景;观气生成100个星点,五运六气环动画名为`wt-spin`,
|
||||
五行行业默认折叠;观心呼吸波纹、香火燃烧和阶段文字均持续运行。
|
||||
- 夜间模式问天背景为`rgb(11, 17, 32)`,全页无控制台错误。
|
||||
- 没有触发真实解势、解运或解卦调用,加载动画的静态资产及脚本未改动。
|
||||
- 截图 SHA-256:
|
||||
- `heaven-trend-1080.png`:`9e70d9cbfd741e74d3c4511c0e76ef8e625a6a31fd5d6bde014103f807bcc76e`
|
||||
- `heaven-fortune-1080.png`:`e4cdf0e0fc1d9325724654a781febed1d509e49ddccecb3355803b5e9f1d2f5c`
|
||||
- `heaven-heart-breathing-1080.png`:`3493a59b9032c1a8a1ba7da609b499c736170d2c7947b3aa0ac4c8b95d981ff5`
|
||||
- `heaven-heart-dark-1080.png`:`3d346fea6b593fa4e43037f5fc68eda1a3f3446a223c2022d9084922f646a088`
|
||||
|
||||
## 5. 自动验证与保留边界
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| 原版`python -m unittest discover -s tests -q` | 231项通过 |
|
||||
| 迁移版`python -m unittest discover -s tests -q` | 280项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_heaven -q` | 7项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45项通过(2.2分钟) |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
- 复盘助手、交易日志、自选、笔记与提醒属于切片09,本切片不提前移动。
|
||||
- 前端 DOM、页面 JS、CSS和移动端行为未改动,统一归档延至切片10。
|
||||
- `static/heaven-loading.js`是否失效仍不确定,继续保留到切片11试删。
|
||||
- 没有删除待定代码、没有修改正式数据库、没有测试或切换 Docker/NAS。
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "heaven setup awaiting stock selection",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/setup?trade_date=2026-07-29",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "b72f2e972c3993717d639d1ccc68a9f1fb716a5846823a485a05458103465b92",
|
||||
"migrated_sha256": "b72f2e972c3993717d639d1ccc68a9f1fb716a5846823a485a05458103465b92",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "trend interpretation history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=trend&limit=20",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "2aa83ea177bd5cb2351ac4f3a60fdcd17e02cfc14b5753e157e515f03fc2c6c1",
|
||||
"migrated_sha256": "2aa83ea177bd5cb2351ac4f3a60fdcd17e02cfc14b5753e157e515f03fc2c6c1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "fortune interpretation history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=fortune&limit=20",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "e60debaad54c58ad34fc7f025bfeaba7d2349aee2531b3f507c6e1968dbdcbf0",
|
||||
"migrated_sha256": "e60debaad54c58ad34fc7f025bfeaba7d2349aee2531b3f507c6e1968dbdcbf0",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "heart interpretation history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=heart&limit=20",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "1fe13531909c83c645b4fbb6740f46f2590adb346ab6e43414023967bb51b072",
|
||||
"migrated_sha256": "1fe13531909c83c645b4fbb6740f46f2590adb346ab6e43414023967bb51b072",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "six-line hexagram calculation",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/heaven/hexagram",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "9773a9f288ea5fbe063b778fc47c613972fd7747f8fc6f80333316f33923c960",
|
||||
"migrated_sha256": "9773a9f288ea5fbe063b778fc47c613972fd7747f8fc6f80333316f33923c960",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "personal five-phase calculation",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/heaven/personal",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "3b179ea4e3e0b43d12e929b4057a51264772fadcf41e5c87573dc5d18873bc9e",
|
||||
"migrated_sha256": "3b179ea4e3e0b43d12e929b4057a51264772fadcf41e5c87573dc5d18873bc9e",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"name": "heaven setup awaiting stock selection",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/setup?trade_date=2026-07-29",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "trend interpretation history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=trend&limit=20",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "fortune interpretation history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=fortune&limit=20",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "heart interpretation history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=heart&limit=20",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "six-line hexagram calculation",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/heaven/hexagram",
|
||||
"payload": {
|
||||
"lines": [7, 8, 9, 6, 7, 8]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "personal five-phase calculation",
|
||||
"method": "POST",
|
||||
"endpoint": "/api/heaven/personal",
|
||||
"payload": {
|
||||
"trade_date": "2026-07-29"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"schema": {
|
||||
"object_count": 62,
|
||||
"original_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"migrated_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"equal": true
|
||||
},
|
||||
"tables": [
|
||||
{
|
||||
"table": "heaven_readings",
|
||||
"original_count": 31,
|
||||
"migrated_count": 31,
|
||||
"original_sha256": "e6cfc86f010fb182d7522bb99e5d268e826f93896035172120df51a011590951",
|
||||
"migrated_sha256": "e6cfc86f010fb182d7522bb99e5d268e826f93896035172120df51a011590951",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "users",
|
||||
"original_count": 3,
|
||||
"migrated_count": 3,
|
||||
"original_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"migrated_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "system_settings",
|
||||
"original_count": 1,
|
||||
"migrated_count": 1,
|
||||
"original_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"migrated_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "llm_usage",
|
||||
"original_count": 72,
|
||||
"migrated_count": 72,
|
||||
"original_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"migrated_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "user_birth_profiles",
|
||||
"original_count": 1,
|
||||
"migrated_count": 1,
|
||||
"original_sha256": "2a74d4b2edd3a730a46f17fb6ab3926575c42662c1d968643b0d54586ab68b13",
|
||||
"migrated_sha256": "2a74d4b2edd3a730a46f17fb6ab3926575c42662c1d968643b0d54586ab68b13",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "sector_phase_overrides",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,60 @@
|
||||
# 切片 09:我的复盘、自选、笔记、交易日志、提醒与复盘助手
|
||||
|
||||
> 基线:`b3df070`(切片 08)
|
||||
> 回档标签:`xiaobai-preservation-slice-09-20260731`
|
||||
> 结论:源码、API、数据库、账户隔离、真实页面及全量回归通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片只机械移动原版复盘、提醒和复盘助手实现,没有从`next/`取用代码,也没有修改自选、笔记、交易日志、提醒、会员灰化、LLM 上下文或流式协议。
|
||||
|
||||
| 原位置 | 新的唯一实现位置 | 兼容方式 |
|
||||
|---|---|---|
|
||||
| `assistant_agent.py` | `app/backend/features/review/agent.py` | 根级模块指向同一模块对象 |
|
||||
| `DashboardService`提醒方法 | `app/backend/features/alerts/facade.py` | `AlertServiceMixin` |
|
||||
| `DashboardService`复盘方法 | `app/backend/features/review/service.py` | `ReviewServiceMixin` |
|
||||
| `ReviewDatabase`提醒方法 | `app/backend/features/alerts/repository.py` | `AlertRepositoryMixin` |
|
||||
| `ReviewDatabase`复盘方法 | `app/backend/features/review/repository.py` | `ReviewRepositoryMixin` |
|
||||
| 五个 POST/流式入口 | `app/backend/features/alerts/http.py`、`review/http.py` | HTTP Mixin |
|
||||
|
||||
原版已经存在的`AlertService`、`TradeJournalService`、Repository Port 与 SQLite Adapter 继续保留为唯一底层实现。本切片没有制造第二套提醒或交易日志服务。
|
||||
|
||||
## 2. 源码等价与必要适配
|
||||
|
||||
- `assistant_agent.py`与正式 Agent 文件 SHA-256 一致,根级兼容模块与正式 Agent 是同一模块对象。
|
||||
- 5 个提醒编排方法、8 个复盘编排方法、6 个提醒 Repository 方法和 13 个复盘 Repository 方法均与原版无位置信息 AST 一致。
|
||||
- 五个 HTTP 方法只把全局`SERVICE`改为 Mixin 的`self.application_service`;状态码、字段、异常和流式事件写法不变。
|
||||
- `backend.features.alerts`和`backend.features.review`仅对旧底层服务采用延迟导出,避免`database -> feature package -> sqlite adapter -> database`初始化循环;外部`from ... import AlertService/TradeJournalService`接口不变。
|
||||
- 公共`RequestHandler._write_stream_event`继续保留,因为问师和复盘助手共同使用,且现有测试直接覆盖;统一传输层留到切片 10。
|
||||
|
||||
## 3. API 与数据库差分
|
||||
|
||||
- 原版`8790`和迁移版`8791`使用正式数据库的两个临时副本,未写正式数据库。
|
||||
- 固定日期下自选、全部复盘笔记、交易日志、提醒中心及复盘助手历史共 5 个鉴权接口,状态码与业务 JSON 完全一致。
|
||||
- 两版均为 62 个 schema 对象;`watchlist`、`review_notes`、`trade_entries`、`alerts`、`assistant_messages`、`users`、`system_settings`和`llm_usage`逐行一致。
|
||||
- 真实浏览器在迁移版临时副本中新增一条交易记录,弹窗正确关闭并显示短提示“交易记录已保存”;没有写正式数据库。
|
||||
- 未调用真实`/api/assistant/chat`,避免外部模型波动和 LLM 用量写入;Agent、流式累积与权限路径由源码等价和单元测试覆盖。
|
||||
|
||||
## 4. 真实浏览器检查
|
||||
|
||||
- 1920×1080 检查我的复盘完整布局,自选 6 只、交易日志、每日复盘三个独立输入框和最近复盘均可访问,无横向溢出。
|
||||
- 添加自选与交易日志弹窗居中可用;交易日志保存后没有出现超长空弹窗。
|
||||
- 提醒中心正常展示筛选、新建提醒和提醒记录。
|
||||
- 管理员会员账号显示完整可用的复盘助手;临时普通账号显示相同界面、顶部“复盘助手仅对会员开放”,下方快捷问题、输入框和发送按钮均灰化禁用。
|
||||
- 临时普通账号自选和交易日志均为空,证明用户私有数据没有串到管理员账号。
|
||||
- 夜间模式背景为`rgb(18, 20, 22)`,1920×1080 页面无横向溢出,控制台无错误。
|
||||
- 截图`review-workspace-night-1920x1080.png` SHA-256:`bc803305a391930a0aafd68062bab6de86eca14612799eebfda220cf2ad0798e`。
|
||||
|
||||
## 5. 自动验证与保留边界
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| 原版`python -m unittest discover -s tests -q` | 231 项通过 |
|
||||
| 迁移版`python -m unittest discover -s tests -q` | 287 项通过 |
|
||||
| `python -m unittest tests.test_preservation_slice_review_alerts -q` | 7 项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45 项通过(2.2 分钟) |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
- 前端 DOM、页面 JS、CSS 与移动端行为未改动,统一归位延至切片 10。
|
||||
- `demo_data.py`、`static/heaven-loading.js`、五个疑似无引用前端函数和`wencai_saved_queries`继续保留到切片 11 试删。
|
||||
- 没有修改`next/`、正式数据库、Docker/NAS 或`8765`服务。
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "review watchlist",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/watchlist?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "29c001cc6859312fd90082d0fb83818d263827e28ec21157b94c415b71c45078",
|
||||
"migrated_sha256": "29c001cc6859312fd90082d0fb83818d263827e28ec21157b94c415b71c45078",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "all review notes",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/notes?scope=all",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "a2a1f08610d017de0e46080a1268f72b734eeb80f9cada311d7e03eb1416656a",
|
||||
"migrated_sha256": "a2a1f08610d017de0e46080a1268f72b734eeb80f9cada311d7e03eb1416656a",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "trade journal through fixed date",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/trades?end_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "77287b34046e22748e94e61ead841e157f6b63f08de1b2fe61f5d715b2e989a4",
|
||||
"migrated_sha256": "77287b34046e22748e94e61ead841e157f6b63f08de1b2fe61f5d715b2e989a4",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "alert center through fixed date",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/alerts?status=all&as_of=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "8807772f2b611ea8b95033aa19cae84f08b08e92d76fff8e3cd5b79966ea04f4",
|
||||
"migrated_sha256": "8807772f2b611ea8b95033aa19cae84f08b08e92d76fff8e3cd5b79966ea04f4",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "review assistant history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/assistant/messages",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"migrated_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"name": "review watchlist",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/watchlist?trade_date=2026-07-30",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "all review notes",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/notes?scope=all",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "trade journal through fixed date",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/trades?end_date=2026-07-30",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "alert center through fixed date",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/alerts?status=all&as_of=2026-07-30",
|
||||
"payload": null
|
||||
},
|
||||
{
|
||||
"name": "review assistant history",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/assistant/messages",
|
||||
"payload": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"schema": {
|
||||
"object_count": 62,
|
||||
"original_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"migrated_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"equal": true
|
||||
},
|
||||
"tables": [
|
||||
{
|
||||
"table": "watchlist",
|
||||
"original_count": 6,
|
||||
"migrated_count": 6,
|
||||
"original_sha256": "070161f5cdcc33addf289f11cb0ae54bfc0d669a39426fc33398b64512dba2fe",
|
||||
"migrated_sha256": "070161f5cdcc33addf289f11cb0ae54bfc0d669a39426fc33398b64512dba2fe",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "review_notes",
|
||||
"original_count": 3,
|
||||
"migrated_count": 3,
|
||||
"original_sha256": "86ed485c2ce922e3434f8ac742d38431ef430b5c22024cacb6f64f0e8e71ee98",
|
||||
"migrated_sha256": "86ed485c2ce922e3434f8ac742d38431ef430b5c22024cacb6f64f0e8e71ee98",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "trade_entries",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "alerts",
|
||||
"original_count": 2,
|
||||
"migrated_count": 2,
|
||||
"original_sha256": "37b46fb4a754cf9c6179b1be88eed7f5a5398ddfc87160f41ded622ffc313c88",
|
||||
"migrated_sha256": "37b46fb4a754cf9c6179b1be88eed7f5a5398ddfc87160f41ded622ffc313c88",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "assistant_messages",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "users",
|
||||
"original_count": 3,
|
||||
"migrated_count": 3,
|
||||
"original_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"migrated_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "system_settings",
|
||||
"original_count": 1,
|
||||
"migrated_count": 1,
|
||||
"original_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"migrated_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"table": "llm_usage",
|
||||
"original_count": 72,
|
||||
"migrated_count": 72,
|
||||
"original_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"migrated_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"equal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 98 KiB |
@@ -0,0 +1,70 @@
|
||||
# 切片 10:前端 Shell、页面、样式与移动端职责归位
|
||||
|
||||
> 基线:`38de3de`(切片 09)
|
||||
> 回档标签:`xiaobai-preservation-slice-10-20260731`
|
||||
> 结论:源码、静态资产、API、数据库、桌面/移动页面及动画差分通过;最终视觉仍等待全站人工验收
|
||||
|
||||
## 1. 原实现归位
|
||||
|
||||
本切片只移动和拆分原版前端源码,没有从`next/`取用代码,也没有重写页面、样式、动画、交互或响应式规则。
|
||||
|
||||
| 原位置 | 新位置 | 处理方式 |
|
||||
|---|---|---|
|
||||
| `static/index.html` | `app/frontend/index.html` | 原 DOM 骨架,仅调整静态资源路径和拆分脚本加载顺序 |
|
||||
| `static/app.js` | `app/frontend/app.js`、`pages/*/page.js`、`pages/market/runtime.js`、`shared/export.js` | 按连续源码行机械拆分 |
|
||||
| `static/styles.css`等公共样式 | `app/frontend/styles/` | 字节级移动 |
|
||||
| `static/wentian-v2.css` | `app/frontend/pages/heaven/page.css` | 字节级移动 |
|
||||
| `static/ui-core.js` | `app/frontend/shared/ui-core.js` | 字节级移动 |
|
||||
| `static/heaven-loading-v2.js` | `app/frontend/pages/heaven/loading-v2.js` | 字节级移动 |
|
||||
| `static/pages/`、`shared/`、`vendor/` | `app/frontend/`同职责目录 | 字节级移动 |
|
||||
|
||||
`app/backend/bootstrap/config.py`现在只把静态根目录从`app/static/`切换到`app/frontend/`。原`app/static/`已不再存在,避免形成两套可被误改的前端实现。
|
||||
|
||||
## 2. 源码与静态资产等价
|
||||
|
||||
- 原`static/app.js`共 9,283 行,SHA-256 为`c020d0bc68f8a96b352a0ffa62c913a0ea3f5ca0afc41f70969f833e447166a6`。
|
||||
- 所有拆分文件中的保真源码片段按原行号重组后,SHA-256仍为同一值,9,283行无遗漏、无重排、无内容变化。
|
||||
- 七层原版样式、问天样式、加载动画、共享脚本、页面注册脚本及Lucide供应商文件在移动后保持字节一致。
|
||||
- `index.html`的差异仅限资源新路径和两个机械拆分脚本入口;反向替换后与原版逐字符一致。
|
||||
- 浏览器请求仍只有`shared/api.js`一个`fetch`出口。
|
||||
- 完整行号映射见`frontend-source-map.json`。该映射继续作为切片10的历史证据;一次性拆分工具在人工验收和当前所有者契约建立后已退役,不再进入维护工具链。
|
||||
|
||||
## 3. API 与数据库差分
|
||||
|
||||
- 同一管理员账号和同一固定日期下,对原版与迁移版执行21个全站只读请求。
|
||||
- 请求覆盖账号、行情、情绪、轮动、竞价、题材、人气、龙虎榜、选股、问师、问天、复盘与提醒;状态码和规范化业务JSON全部一致。
|
||||
- 两个数据库副本均包含62个schema对象,schema哈希一致。
|
||||
- 21张关键表的记录数、顺序和规范化内容逐行一致;`screener_strategies`仅排除每次初始化可能更新的`updated_at`。
|
||||
- 完整结果见`api-requests.json`、`api-diff.json`和`database-diff.json`,两份差分文件的`all_equal`均为`true`。
|
||||
|
||||
## 4. 真实浏览器差分
|
||||
|
||||
- 1920×1080日间模式检查情绪周期、智能选股、观势、观气、观心介绍及观心呼吸。
|
||||
- 1920×1080夜间模式检查情绪周期的背景、字体、表格、几何和横向溢出。
|
||||
- 390×844检查情绪周期与观势:移动Shell、底部五入口、页面纵向滚动、无横向溢出及问天特效均一致。
|
||||
- 观势星空节点90个、观气100个、观心110个;三页八卦节点均为2个。
|
||||
- 观势与观气保留`wt-tw`和`wt-spin`动画;观心呼吸保留`heart-incense-burn`、`heart-incense-glow`及动态波纹。
|
||||
- 观气五行行业为5组且默认折叠;观心介绍到呼吸流程可正常进入。
|
||||
- 原版和迁移版检查流程均未产生新增控制台error或warn。
|
||||
- 动画帧、焦点框和动态状态文字属于采样瞬时状态,因此截图文件哈希不要求相同;可见布局几何、计算样式、节点、动画名称和交互结果必须一致,本次均通过。
|
||||
- 两个服务使用相同主机名、不同端口时会共享并覆盖登录Cookie;曾导致迁移版切页被误判为失效。逐服务重新登录后行为一致,该问题属于并行验收环境限制,不是产品回归。
|
||||
- 2026-08-01像素复核发现`frontend-migrated-auction-light-1920x1080`实际截取了登录状态失效页,
|
||||
不能证明集合竞价视觉等价,文件已重命名为`INVALID-login-session`。集合竞价仍有切片05真实页面、
|
||||
本切片源码/样式保真、API及Playwright证据,但最终视觉明确留待人工验收,不以替代证据冒充
|
||||
本切片截图差分。
|
||||
- 机器可读记录见`browser-acceptance.json`,截图均保存在本目录。
|
||||
|
||||
## 5. 自动验证与保留边界
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| 前端专项测试 | 85项通过 |
|
||||
| 原版`python -m unittest discover -s tests -q` | 231项通过 |
|
||||
| 迁移版`python -m unittest discover -s tests -q` | 294项通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45项通过(2.3分钟) |
|
||||
| 全部拆分后JavaScript执行`node --check` | 通过 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
- `demo_data.py`、`frontend/heaven-loading.js`、五个疑似无引用前端函数和`wencai_saved_queries`继续保留到切片11逐项审计。
|
||||
- 没有修改`next/`、正式数据库、Docker/NAS或`8765`服务。
|
||||
- 自动差分只能证明已检查范围一致;依据迁移总纲,未经用户最终人工确认不得宣称全站视觉已经完全等价。
|
||||
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "当前账号",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/auth/me",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "33e0c08463d5f14a142840f894d92985135c6ce2bee22c93c4dde34dc433cf5b",
|
||||
"migrated_sha256": "33e0c08463d5f14a142840f894d92985135c6ce2bee22c93c4dde34dc433cf5b",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "账号状态",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/account/status",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "a1f81967a58dae0947191fe1a7465c87e70403339e36897329de94c541272e05",
|
||||
"migrated_sha256": "a1f81967a58dae0947191fe1a7465c87e70403339e36897329de94c541272e05",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "全市场总览",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/dashboard?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "565730fb75cec6f6758321074dfeba7bff19db6611476d511f9bc7257a5870f5",
|
||||
"migrated_sha256": "565730fb75cec6f6758321074dfeba7bff19db6611476d511f9bc7257a5870f5",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "情绪周期历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/sentiment/history?trade_date=2026-07-30&limit=9",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "1a577fb859a2fd06749169e1d0f08f78b51c5217b55506c7903487a956b6192b",
|
||||
"migrated_sha256": "1a577fb859a2fd06749169e1d0f08f78b51c5217b55506c7903487a956b6192b",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "板块轮动历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/rotation/history?trade_date=2026-07-30&limit=9",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eaf3ac41f479c0f57f161546f197c1321721b4a8390486a916232fe6ea1d3d37",
|
||||
"migrated_sha256": "eaf3ac41f479c0f57f161546f197c1321721b4a8390486a916232fe6ea1d3d37",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "集合竞价",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/auction?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "bd75b100485c0afa0cdddc73d1880160eea8c0b8c91f556245c665d591285ed6",
|
||||
"migrated_sha256": "bd75b100485c0afa0cdddc73d1880160eea8c0b8c91f556245c665d591285ed6",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "题材库",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/themes?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "f45e218d9bff5191445fe45b03fc62f3d9c440b8353404ae4b54901c20929552",
|
||||
"migrated_sha256": "f45e218d9bff5191445fe45b03fc62f3d9c440b8353404ae4b54901c20929552",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "人气热榜",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/popularity?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "69987a80f02ac02bf9ff49b3a0a99782385f1933c643c9e3dbb5a75a1e20dd29",
|
||||
"migrated_sha256": "69987a80f02ac02bf9ff49b3a0a99782385f1933c643c9e3dbb5a75a1e20dd29",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "龙虎榜",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/dragon-tiger?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "173baf4e1f0b56678aef3f74276f29af49271ea34c89c0c18283bfe27f5cf2a6",
|
||||
"migrated_sha256": "173baf4e1f0b56678aef3f74276f29af49271ea34c89c0c18283bfe27f5cf2a6",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "游资档案",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/dragon-tiger/profiles",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "dfb1e534c018ec12fd8d8ee0e7fe0f234e73dc7715d7f0a948f74ef8d12d779a",
|
||||
"migrated_sha256": "dfb1e534c018ec12fd8d8ee0e7fe0f234e73dc7715d7f0a948f74ef8d12d779a",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "智能选股工作区",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "e33a10e94accda4948c2af53887ea97736697f588ee1a92f561aa5c4bb28c2fd",
|
||||
"migrated_sha256": "e33a10e94accda4948c2af53887ea97736697f588ee1a92f561aa5c4bb28c2fd",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "策略持续跟踪",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/tracking?limit=12",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "f4476f53152b6a2ce01f2918da5999246cfd0ad7d0b71e80fdea8fa59c7456c2",
|
||||
"migrated_sha256": "f4476f53152b6a2ce01f2918da5999246cfd0ad7d0b71e80fdea8fa59c7456c2",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问师模型库",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "0ff7794fe45f49ab980221fa855b64c70d5c9b83c78ef86edc041e713490b626",
|
||||
"migrated_sha256": "0ff7794fe45f49ab980221fa855b64c70d5c9b83c78ef86edc041e713490b626",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问师历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/messages?mentor_id=kobe92-perspective&trade_date=20260730",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"migrated_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问天初始化",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "c814c2004c7dd672c397887725af000c150693a50900bb0ce1aa94599af1a3c8",
|
||||
"migrated_sha256": "c814c2004c7dd672c397887725af000c150693a50900bb0ce1aa94599af1a3c8",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问天历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=trend&limit=20",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "2aa83ea177bd5cb2351ac4f3a60fdcd17e02cfc14b5753e157e515f03fc2c6c1",
|
||||
"migrated_sha256": "2aa83ea177bd5cb2351ac4f3a60fdcd17e02cfc14b5753e157e515f03fc2c6c1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "自选追踪",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/watchlist?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "29c001cc6859312fd90082d0fb83818d263827e28ec21157b94c415b71c45078",
|
||||
"migrated_sha256": "29c001cc6859312fd90082d0fb83818d263827e28ec21157b94c415b71c45078",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "全部复盘笔记",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/notes?scope=all",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "a2a1f08610d017de0e46080a1268f72b734eeb80f9cada311d7e03eb1416656a",
|
||||
"migrated_sha256": "a2a1f08610d017de0e46080a1268f72b734eeb80f9cada311d7e03eb1416656a",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "交易日志",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/trades?end_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "77287b34046e22748e94e61ead841e157f6b63f08de1b2fe61f5d715b2e989a4",
|
||||
"migrated_sha256": "77287b34046e22748e94e61ead841e157f6b63f08de1b2fe61f5d715b2e989a4",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "提醒中心",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/alerts?status=all&as_of=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "495a1f83ed661913807899e0955ef1b89193c30bc9a5be0587f7f03d60411d95",
|
||||
"migrated_sha256": "495a1f83ed661913807899e0955ef1b89193c30bc9a5be0587f7f03d60411d95",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "复盘助手历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/assistant/messages",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"migrated_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
[
|
||||
{"name": "当前账号", "method": "GET", "endpoint": "/api/auth/me", "exclude_paths": ["$.csrf_token"]},
|
||||
{"name": "账号状态", "method": "GET", "endpoint": "/api/account/status"},
|
||||
{"name": "全市场总览", "method": "GET", "endpoint": "/api/dashboard?trade_date=2026-07-30"},
|
||||
{"name": "情绪周期历史", "method": "GET", "endpoint": "/api/sentiment/history?trade_date=2026-07-30&limit=9"},
|
||||
{"name": "板块轮动历史", "method": "GET", "endpoint": "/api/rotation/history?trade_date=2026-07-30&limit=9"},
|
||||
{"name": "集合竞价", "method": "GET", "endpoint": "/api/auction?trade_date=2026-07-30", "exclude_paths": ["$.meta.updated_at"]},
|
||||
{"name": "题材库", "method": "GET", "endpoint": "/api/themes?trade_date=2026-07-30"},
|
||||
{"name": "人气热榜", "method": "GET", "endpoint": "/api/popularity?trade_date=2026-07-30"},
|
||||
{"name": "龙虎榜", "method": "GET", "endpoint": "/api/dragon-tiger?trade_date=2026-07-30"},
|
||||
{"name": "游资档案", "method": "GET", "endpoint": "/api/dragon-tiger/profiles"},
|
||||
{
|
||||
"name": "智能选股工作区",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/setup?trade_date=2026-07-30",
|
||||
"sort_lists": {"$.strategies": "id"},
|
||||
"exclude_paths": ["$.strategies[].updated_at"]
|
||||
},
|
||||
{"name": "策略持续跟踪", "method": "GET", "endpoint": "/api/screener/tracking?limit=12"},
|
||||
{"name": "问师模型库", "method": "GET", "endpoint": "/api/mentors/setup?trade_date=2026-07-30"},
|
||||
{"name": "问师历史", "method": "GET", "endpoint": "/api/mentors/messages?mentor_id=kobe92-perspective&trade_date=20260730"},
|
||||
{"name": "问天初始化", "method": "GET", "endpoint": "/api/heaven/setup?trade_date=2026-07-30"},
|
||||
{"name": "问天历史", "method": "GET", "endpoint": "/api/heaven/readings?mode=trend&limit=20"},
|
||||
{"name": "自选追踪", "method": "GET", "endpoint": "/api/watchlist?trade_date=2026-07-30"},
|
||||
{"name": "全部复盘笔记", "method": "GET", "endpoint": "/api/notes?scope=all"},
|
||||
{"name": "交易日志", "method": "GET", "endpoint": "/api/trades?end_date=2026-07-30"},
|
||||
{"name": "提醒中心", "method": "GET", "endpoint": "/api/alerts?status=all&as_of=2026-07-30"},
|
||||
{"name": "复盘助手历史", "method": "GET", "endpoint": "/api/assistant/messages"}
|
||||
]
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"tested_at": "2026-07-31T12:00:00+08:00",
|
||||
"result": "passed_with_documented_evidence_gap",
|
||||
"environments": {
|
||||
"original": "temporary_original_runtime",
|
||||
"migrated": "temporary_app_runtime",
|
||||
"account": "administrator",
|
||||
"database": "isolated_copies"
|
||||
},
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"theme": "light",
|
||||
"views": [
|
||||
"sentiment",
|
||||
"screener",
|
||||
"heaven_trend",
|
||||
"heaven_fortune",
|
||||
"heaven_heart_intro",
|
||||
"heaven_heart_breath"
|
||||
],
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"theme": "dark",
|
||||
"views": [
|
||||
"sentiment"
|
||||
],
|
||||
"equal": true
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844,
|
||||
"theme": "light",
|
||||
"views": [
|
||||
"sentiment",
|
||||
"heaven_trend"
|
||||
],
|
||||
"equal": true
|
||||
}
|
||||
],
|
||||
"invalid_captures": [
|
||||
{
|
||||
"view": "auction",
|
||||
"file": "frontend-migrated-auction-light-1920x1080.INVALID-login-session.png",
|
||||
"reason": "The migrated capture shows an expired login session and is not visual-equivalence evidence.",
|
||||
"replacement_claim": "No replacement screenshot claim; final visual acceptance remains manual."
|
||||
}
|
||||
],
|
||||
"heaven_animation_contract": {
|
||||
"trend_star_nodes": 90,
|
||||
"fortune_star_nodes": 100,
|
||||
"heart_star_nodes": 110,
|
||||
"bagua_nodes_per_view": 2,
|
||||
"trend_and_fortune_animations": [
|
||||
"wt-tw",
|
||||
"wt-spin"
|
||||
],
|
||||
"heart_breath_animations": [
|
||||
"heart-incense-burn",
|
||||
"heart-incense-glow"
|
||||
],
|
||||
"fortune_industry_groups": 5,
|
||||
"fortune_industries_collapsed_by_default": true,
|
||||
"equal": true
|
||||
},
|
||||
"mobile_contract": {
|
||||
"primary_navigation_items": 5,
|
||||
"horizontal_overflow": false,
|
||||
"vertical_page_scroll": true,
|
||||
"heaven_trend_geometry": {
|
||||
"active": {
|
||||
"height": 723,
|
||||
"width": 374,
|
||||
"x": 8,
|
||||
"y": 50
|
||||
},
|
||||
"header": {
|
||||
"width": 390,
|
||||
"height": 50
|
||||
},
|
||||
"bottom_navigation": {
|
||||
"width": 390,
|
||||
"height": 58
|
||||
}
|
||||
},
|
||||
"equal": true
|
||||
},
|
||||
"console": {
|
||||
"new_errors": 0,
|
||||
"new_warnings": 0
|
||||
},
|
||||
"screenshot_policy": "Dynamic animation frames, focus outlines, and live status text may change pixel hashes. Acceptance compares visible geometry, computed styles, DOM state, animation names, interaction results, and overflow behavior.",
|
||||
"known_test_environment_constraint": "Original and migrated services on the same hostname share cookies across ports. Each service must be logged in separately immediately before comparison.",
|
||||
"all_equal": true,
|
||||
"manual_acceptance_required": true
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"schema": {
|
||||
"object_count": 62,
|
||||
"original_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"migrated_sha256": "60a4e044f0ddb6502e44b45f65bedc1a0a31d4bd596f386d4ccfe153a6c8ddd1",
|
||||
"equal": true
|
||||
},
|
||||
"tables": [
|
||||
{
|
||||
"table": "users",
|
||||
"original_count": 3,
|
||||
"migrated_count": 3,
|
||||
"original_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"migrated_sha256": "4a0f135bef8ebae454693d3f40e1157d84d814d18d849f33e2e760ed1d8a7f89",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "system_settings",
|
||||
"original_count": 1,
|
||||
"migrated_count": 1,
|
||||
"original_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"migrated_sha256": "86d333a5c7feaf7111cd79b2db29513326a9b0d3781607f78bf633579e2ca8e9",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "watchlist",
|
||||
"original_count": 6,
|
||||
"migrated_count": 6,
|
||||
"original_sha256": "070161f5cdcc33addf289f11cb0ae54bfc0d669a39426fc33398b64512dba2fe",
|
||||
"migrated_sha256": "070161f5cdcc33addf289f11cb0ae54bfc0d669a39426fc33398b64512dba2fe",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "review_notes",
|
||||
"original_count": 3,
|
||||
"migrated_count": 3,
|
||||
"original_sha256": "86ed485c2ce922e3434f8ac742d38431ef430b5c22024cacb6f64f0e8e71ee98",
|
||||
"migrated_sha256": "86ed485c2ce922e3434f8ac742d38431ef430b5c22024cacb6f64f0e8e71ee98",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "trade_entries",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "alerts",
|
||||
"original_count": 2,
|
||||
"migrated_count": 2,
|
||||
"original_sha256": "40b1ee5678b32f29ae356c1b30958e66138d34afd2aa6b8364fa02c23df66553",
|
||||
"migrated_sha256": "40b1ee5678b32f29ae356c1b30958e66138d34afd2aa6b8364fa02c23df66553",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "assistant_messages",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "mentor_messages",
|
||||
"original_count": 28,
|
||||
"migrated_count": 28,
|
||||
"original_sha256": "b906b776d575ff6fe459b2aa0e8b396267feed36d3d85f7edd7947b284482aeb",
|
||||
"migrated_sha256": "b906b776d575ff6fe459b2aa0e8b396267feed36d3d85f7edd7947b284482aeb",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "mentor_preferences",
|
||||
"original_count": 45,
|
||||
"migrated_count": 45,
|
||||
"original_sha256": "c19c7d05e6745e4f1787d1eed5f39d7e555ec24f8e490b585c19158edc2505ec",
|
||||
"migrated_sha256": "c19c7d05e6745e4f1787d1eed5f39d7e555ec24f8e490b585c19158edc2505ec",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "heaven_readings",
|
||||
"original_count": 31,
|
||||
"migrated_count": 31,
|
||||
"original_sha256": "e6cfc86f010fb182d7522bb99e5d268e826f93896035172120df51a011590951",
|
||||
"migrated_sha256": "e6cfc86f010fb182d7522bb99e5d268e826f93896035172120df51a011590951",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "user_birth_profiles",
|
||||
"original_count": 1,
|
||||
"migrated_count": 1,
|
||||
"original_sha256": "2a74d4b2edd3a730a46f17fb6ab3926575c42662c1d968643b0d54586ab68b13",
|
||||
"migrated_sha256": "2a74d4b2edd3a730a46f17fb6ab3926575c42662c1d968643b0d54586ab68b13",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "sector_phase_overrides",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "screener_strategies",
|
||||
"original_count": 36,
|
||||
"migrated_count": 36,
|
||||
"original_sha256": "872846dcb746446bda37723e63db737a2a0cf0540eb6a1da264012248887f4c5",
|
||||
"migrated_sha256": "872846dcb746446bda37723e63db737a2a0cf0540eb6a1da264012248887f4c5",
|
||||
"equal": true,
|
||||
"excluded_columns": [
|
||||
"updated_at"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "screener_runs",
|
||||
"original_count": 250,
|
||||
"migrated_count": 250,
|
||||
"original_sha256": "9f58ff2f85e5b85878153f7694161620d77cb9b0f351754f2974a8a0cc8ad8fb",
|
||||
"migrated_sha256": "9f58ff2f85e5b85878153f7694161620d77cb9b0f351754f2974a8a0cc8ad8fb",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "strategy_tracks",
|
||||
"original_count": 16,
|
||||
"migrated_count": 16,
|
||||
"original_sha256": "f0639b1386b69c92fd815f929864b3b2457a6bf4ca515bee2dca87e9ea29e8c4",
|
||||
"migrated_sha256": "f0639b1386b69c92fd815f929864b3b2457a6bf4ca515bee2dca87e9ea29e8c4",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "auction_factors",
|
||||
"original_count": 511914,
|
||||
"migrated_count": 511914,
|
||||
"original_sha256": "3d0470787adaf7c4cf5f15f5ad9fa1d67c8fcd4807285ac264eeacdcf5054cd1",
|
||||
"migrated_sha256": "3d0470787adaf7c4cf5f15f5ad9fa1d67c8fcd4807285ac264eeacdcf5054cd1",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "popularity_factors",
|
||||
"original_count": 232,
|
||||
"migrated_count": 232,
|
||||
"original_sha256": "3f4c61a13ceaa1ed9ecb28f86241a8a478a6757d6a44b46f432f73c0226bba7f",
|
||||
"migrated_sha256": "3f4c61a13ceaa1ed9ecb28f86241a8a478a6757d6a44b46f432f73c0226bba7f",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "seat_aliases",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "reason_overrides",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "llm_usage",
|
||||
"original_count": 72,
|
||||
"migrated_count": 72,
|
||||
"original_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"migrated_sha256": "03a070316a125cef904bbfb2bb06b06e792242d5713142e70354402c741393c3",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
},
|
||||
{
|
||||
"table": "wencai_saved_queries",
|
||||
"original_count": 0,
|
||||
"migrated_count": 0,
|
||||
"original_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"migrated_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
|
||||
"equal": true,
|
||||
"excluded_columns": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 91 KiB |
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"source": "static/app.js",
|
||||
"source_sha256": "c020d0bc68f8a96b352a0ffa62c913a0ea3f5ca0afc41f70969f833e447166a6",
|
||||
"source_line_count": 9283,
|
||||
"reassembled_sha256": "c020d0bc68f8a96b352a0ffa62c913a0ea3f5ca0afc41f70969f833e447166a6",
|
||||
"all_source_lines_preserved": true,
|
||||
"core": {
|
||||
"target": "app/frontend/app.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 1,
|
||||
"end": 1206
|
||||
},
|
||||
{
|
||||
"start": 3471,
|
||||
"end": 3489
|
||||
},
|
||||
{
|
||||
"start": 8427,
|
||||
"end": 8904
|
||||
},
|
||||
{
|
||||
"start": 9052,
|
||||
"end": 9283
|
||||
}
|
||||
]
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"target": "app/frontend/pages/sentiment/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 1207,
|
||||
"end": 1516,
|
||||
"sha256": "70a884362b3f97ceb28e2f3a83a70ce26e61c79e23bfeee62b301b9068f2b69c"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/pools/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 1517,
|
||||
"end": 1915,
|
||||
"sha256": "ff8623609c1ef6eb793deaef2b627746727a4768962711d1712e35833b4f58c3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/market/runtime.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 1916,
|
||||
"end": 1957,
|
||||
"sha256": "703dafd0417926df8572a885ffa32a79ee16346019f0e14fcee9e03ad1f1baf7"
|
||||
},
|
||||
{
|
||||
"start": 6893,
|
||||
"end": 7719,
|
||||
"sha256": "a670986ac2099a181f5f348cd48bc4777da0d4709b3ac79239067ff5666261fb"
|
||||
},
|
||||
{
|
||||
"start": 7969,
|
||||
"end": 8426,
|
||||
"sha256": "467409c2cfc06ee96bb7f422be55f79920c3a9936229b2234dd8f78b537e7f26"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/rotation/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 1958,
|
||||
"end": 2124,
|
||||
"sha256": "33f536213db33be0934527f13527712a9d521b5f89a5784b42887c5bf8631f2f"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/ladder/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 2125,
|
||||
"end": 2216,
|
||||
"sha256": "2be84071921910acbec2328f1f10dd8565420ea03a1930ff255040bf99d679a8"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/auction/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 2217,
|
||||
"end": 2481,
|
||||
"sha256": "7f16901ae8769b9570693211022538feb02f2cda4bffe6fa95ba6ce60c10b012"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/themes/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 2482,
|
||||
"end": 2583,
|
||||
"sha256": "c134d762bda669483f0c094efae9281919b68b2ffb83c9457670bc9013ce1515"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/popularity/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 2584,
|
||||
"end": 2659,
|
||||
"sha256": "db72e42385e4a4bd4314ab471b27c1bb804f13650ee463f0843d783b0ba50bff"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/dragon-tiger/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 2660,
|
||||
"end": 3028,
|
||||
"sha256": "b33ccad80ce997bddd75e4025eec35aa246fa5e9f5ca9ccd5f17e587885274cd"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/review/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 3029,
|
||||
"end": 3470,
|
||||
"sha256": "10dba1966b32941cb28ac46d1cb1b20ae74ea271fd7be22a23b46d290ece80ef"
|
||||
},
|
||||
{
|
||||
"start": 7720,
|
||||
"end": 7968,
|
||||
"sha256": "3af64079f997fabbf0ff4023088eb62f94c0091a823bd6d65c1f9e485d1c5ad8"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/screener/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 3490,
|
||||
"end": 4336,
|
||||
"sha256": "76dfdd5610453f0abb5899baba9d751b5f597450ec656bf10580ce91447a68b9"
|
||||
},
|
||||
{
|
||||
"start": 6668,
|
||||
"end": 6892,
|
||||
"sha256": "a7d0fa98b07516169a8e46c2ae2f6efbaeab651887c479919210835f0877533e"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/mentor/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 4337,
|
||||
"end": 4827,
|
||||
"sha256": "b2067c062fb6cb539dd6402074d02f1ae65903957106d7ebf8d63bc5487f244c"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/pages/heaven/page.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 4828,
|
||||
"end": 6667,
|
||||
"sha256": "57bd47c239c4ee9302c7cd7a1f86a5d0932df6065148a34bafe0c49bef644cb1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "app/frontend/shared/export.js",
|
||||
"ranges": [
|
||||
{
|
||||
"start": 8905,
|
||||
"end": 9051,
|
||||
"sha256": "17b867189087615a4414da4f9db3862306e05cb527c25c794beb9c4c74b583ab"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
# 切片 11:不确定代码审计、全量验收与交接准备
|
||||
|
||||
> 基线:`dec3cd12365df5e7d4c391369f14c457cf56d7d9`
|
||||
> 候选回档标签:`xiaobai-preservation-slice-11-candidate-20260731`
|
||||
> 严格审计候选标签:`xiaobai-preservation-slice-11-audit-candidate-20260731`
|
||||
> 跨日复验候选标签:`xiaobai-preservation-slice-11-audit-candidate-20260801`
|
||||
> 独立维护候选标签:`xiaobai-preservation-slice-11-audit-candidate-standalone-20260801`
|
||||
> 最终完成标签:`xiaobai-preservation-complete-20260801`
|
||||
> 当前结论:自动与用户人工验收均已完成;本地迁移交付完成,尚未执行正式切换、Docker或NAS部署
|
||||
|
||||
## 1. 本切片做了什么
|
||||
|
||||
本切片不增加功能、不调整视觉,也不继续拆分业务代码。它逐项审计切片10保留的待定资产,
|
||||
把有完整证据的无效实现放入可单独回档的试删候选,并为人工接管和后续切换准备文档。
|
||||
|
||||
经试删和人工验收后确认废弃:
|
||||
|
||||
- `app/demo_data.py`:没有运行导入、动态注册、数据库或配置责任的旧演示数据构造器。
|
||||
- `app/frontend/heaven-loading.js`:页面未加载的旧问天动画;正式入口继续使用
|
||||
`frontend/pages/heaven/loading-v2.js`。
|
||||
- 五个只有定义、没有消费者的前端函数:`commonReviewColumns`、`outcomeClass`、
|
||||
`screenerResultMatchesSelection`、`selectRegime`、`showHeartRitualCurtain`。
|
||||
|
||||
明确保留:
|
||||
|
||||
- `wencai_saved_queries`表及三个Repository方法。它们承担历史数据库兼容和用户隔离责任,
|
||||
当前库为空不能证明其他部署库也为空。
|
||||
- 与试删函数相邻但证据不足的DOM、状态字段和CSS。没有为追求行数继续连带删除。
|
||||
|
||||
逐项指纹、引用扫描、责任判断和恢复位置见`uncertain-code-audit.md`。
|
||||
|
||||
## 2. 自动验证结果
|
||||
|
||||
| 验证 | 结果 |
|
||||
|---|---:|
|
||||
| 原版`python -m unittest discover -s tests -q` | 231项通过 |
|
||||
| 迁移版`python -m unittest discover -s tests -q` | 305项通过 |
|
||||
| 历史切片与前端/清理专项复核 | 通过 |
|
||||
| 迁移版JavaScript语法检查 | 24个文件通过 |
|
||||
| `npx.cmd playwright test --reporter=dot` | 45项通过(1.6分钟) |
|
||||
| 固定只读API差分 | 21个端点全部一致 |
|
||||
| 数据库差分 | 62个schema对象、21张关键表全部一致 |
|
||||
| `git diff --check` | 通过 |
|
||||
|
||||
2026-08-01 01:19(Asia/Shanghai)跨日复验时,原版与迁移版共用的实时详情测试暴露出
|
||||
测试夹具依赖运行日的问题:夹具在周六动态生成“今日”,而业务正确拒绝在非交易日合并实时
|
||||
行情。两版测试夹具同步固定到明确的工作日盘中/盘前时点,产品代码与行情日期规则未改。
|
||||
修复后统一验收命令再次通过302项测试、24个JavaScript文件、SQLite完整性和45项Playwright
|
||||
(2.0分钟)。
|
||||
|
||||
早期切片的五个前端源码测试原本要求与未试删的`static/app.js`逐字符一致。本切片验收时曾通过
|
||||
行号回放机制限制未登记改动。完成全站人工验收及前端所有权治理后,该过渡机制已退役;当前由
|
||||
运行时注册表、唯一所有者、DOM/API契约、JavaScript语法检查和Playwright行为回归持续守门。
|
||||
|
||||
完整API和数据库结果分别见`api-diff.json`与`database-diff.json`,二者`all_equal`均为`true`。
|
||||
|
||||
严格完成审计另外验证了候选维护工具本身:API注册表和候选架构清单均可生成并执行
|
||||
`--check`;24个前端脚本全部由统一命令发现;迁移期写入工具必须显式指定输出或`--apply`。
|
||||
Windows下Playwright托管Python静态服务器会在45项执行后不退出,统一验证器现改为显式启动、
|
||||
探活和停止8876;最终同一命令正常退出并明确报告`45 passed`。四项维护工具契约测试使迁移版
|
||||
总数从298增加到302。逐项目标矩阵见`completion-audit.md`。
|
||||
|
||||
后续独立性审计把仅含`app/`的Git导出放到系统临时目录:运行模块全部解析到导出内部,统一
|
||||
维护命令通过242项候选自有测试、两个注册表、24个JavaScript文件和SQLite完整性检查,并明确
|
||||
跳过不存在的Git工作区。正式仓库继续执行全部保真差分,最终为305项Python测试和45项
|
||||
Playwright通过。架构热点字节数已改为换行归一化后的UTF-8大小,Windows CRLF与Git/Linux LF
|
||||
不会再制造假差异。
|
||||
|
||||
## 3. 真实浏览器验收
|
||||
|
||||
- 桌面端复核情绪周期、智能选股三个工作区、问天日间/夜间和加载资源;控制台无新增
|
||||
error或warn,页面无横向溢出。
|
||||
- 问天只加载`/pages/heaven/loading-v2.js`,未请求已试删的`/heaven-loading.js`。
|
||||
- 观势、观气、观心星空与八卦节点、原动画名称和可见行为保持不变。
|
||||
- `390x844`逐一点击涨停池、智能选股、问师、问天、我的复盘五个移动入口;底部导航固定,
|
||||
页面均可完整纵向滚动且没有文档级横向溢出。
|
||||
- 观势、观气、观心三页均复核;观气滚动到底后“个人合参”和“五行对应行业”可达。
|
||||
- 同账号、同数据库快照、同主题和同视口下,原版与迁移版问天布局和计算样式一致。
|
||||
|
||||
机器可读记录见`browser-acceptance.json`。截图和更广的桌面/移动基线继续沿用切片10证据目录。
|
||||
切片10截图的事后像素复核见`screenshot-pixel-audit.json`;九组有效截图平均色差低于0.3/255,
|
||||
集合竞价候选图因登录状态失效被明确排除,未作为自动截图证据。2026-08-01用户随后在`8797`
|
||||
真实登录状态下逐页检查全部页面并测试全部功能,确认视觉与功能迁移成功;所见问题几乎都
|
||||
属于原版遗留问题,未发现阻止验收的迁移回归。完整人工结论见`manual-acceptance.md`。
|
||||
|
||||
## 4. 数据与部署边界
|
||||
|
||||
- API/数据库比较使用两个隔离副本:`slice11-final-original`与`slice11-final-migrated`。
|
||||
- 没有写入根目录正式`data/review.db`,没有修改`.env`或私有Skill。
|
||||
- 没有占用`8765`,没有执行Docker构建、NAS测试或服务器切换。
|
||||
- `app/`已经完成本地迁移验收;原版根目录仍是当前部署基线和切换前回档来源。
|
||||
- `next/`保持冻结,没有作为代码、样式、测试或文档来源。
|
||||
|
||||
## 5. 回档与最终确认
|
||||
|
||||
2026-08-01用户完成全部页面和功能人工验收,确认迁移在视觉和功能上成功,并确认人工检查中
|
||||
发现的问题几乎都属于原版遗留问题。切片11的2个文件和5个无消费者函数据此从“候选试删”改为
|
||||
“确认废弃”;恢复标签继续保留作历史兼容应急,不代表删除结论仍待裁决。
|
||||
|
||||
本地迁移收尾已执行:
|
||||
|
||||
1. 试删候选状态改为“确认废弃”。
|
||||
2. 建立最终完成标签并推送Gitea。
|
||||
3. `app/`作为后续结构治理的唯一开发目录。
|
||||
|
||||
正式数据库、原版部署、Docker和NAS仍未切换;切换时间与根目录清理必须由用户另行批准。
|
||||
|
||||
人工维护、验证、切换和回退步骤见`docs/migration/人工维护与本地切换指南.md`。
|
||||
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"all_equal": true,
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "当前账号",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/auth/me",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "33e0c08463d5f14a142840f894d92985135c6ce2bee22c93c4dde34dc433cf5b",
|
||||
"migrated_sha256": "33e0c08463d5f14a142840f894d92985135c6ce2bee22c93c4dde34dc433cf5b",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "账号状态",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/account/status",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "0ac1b88affa62671f14ba9ea0bd42155952fdc2e03dba3573c232bb452733bc6",
|
||||
"migrated_sha256": "0ac1b88affa62671f14ba9ea0bd42155952fdc2e03dba3573c232bb452733bc6",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "全市场总览",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/dashboard?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "565730fb75cec6f6758321074dfeba7bff19db6611476d511f9bc7257a5870f5",
|
||||
"migrated_sha256": "565730fb75cec6f6758321074dfeba7bff19db6611476d511f9bc7257a5870f5",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "情绪周期历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/sentiment/history?trade_date=2026-07-30&limit=9",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "1a577fb859a2fd06749169e1d0f08f78b51c5217b55506c7903487a956b6192b",
|
||||
"migrated_sha256": "1a577fb859a2fd06749169e1d0f08f78b51c5217b55506c7903487a956b6192b",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "板块轮动历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/rotation/history?trade_date=2026-07-30&limit=9",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eaf3ac41f479c0f57f161546f197c1321721b4a8390486a916232fe6ea1d3d37",
|
||||
"migrated_sha256": "eaf3ac41f479c0f57f161546f197c1321721b4a8390486a916232fe6ea1d3d37",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "集合竞价",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/auction?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "bd75b100485c0afa0cdddc73d1880160eea8c0b8c91f556245c665d591285ed6",
|
||||
"migrated_sha256": "bd75b100485c0afa0cdddc73d1880160eea8c0b8c91f556245c665d591285ed6",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "题材库",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/themes?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "f45e218d9bff5191445fe45b03fc62f3d9c440b8353404ae4b54901c20929552",
|
||||
"migrated_sha256": "f45e218d9bff5191445fe45b03fc62f3d9c440b8353404ae4b54901c20929552",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "人气热榜",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/popularity?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "69987a80f02ac02bf9ff49b3a0a99782385f1933c643c9e3dbb5a75a1e20dd29",
|
||||
"migrated_sha256": "69987a80f02ac02bf9ff49b3a0a99782385f1933c643c9e3dbb5a75a1e20dd29",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "龙虎榜",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/dragon-tiger?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "1d6936e33aea2bb4258d1d4d007385626d0c638fc290b66271f00649dba59358",
|
||||
"migrated_sha256": "1d6936e33aea2bb4258d1d4d007385626d0c638fc290b66271f00649dba59358",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "游资档案",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/dragon-tiger/profiles",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "dfb1e534c018ec12fd8d8ee0e7fe0f234e73dc7715d7f0a948f74ef8d12d779a",
|
||||
"migrated_sha256": "dfb1e534c018ec12fd8d8ee0e7fe0f234e73dc7715d7f0a948f74ef8d12d779a",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "智能选股工作区",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "e33a10e94accda4948c2af53887ea97736697f588ee1a92f561aa5c4bb28c2fd",
|
||||
"migrated_sha256": "e33a10e94accda4948c2af53887ea97736697f588ee1a92f561aa5c4bb28c2fd",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "策略持续跟踪",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/screener/tracking?limit=12",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "f4476f53152b6a2ce01f2918da5999246cfd0ad7d0b71e80fdea8fa59c7456c2",
|
||||
"migrated_sha256": "f4476f53152b6a2ce01f2918da5999246cfd0ad7d0b71e80fdea8fa59c7456c2",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问师模型库",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "0ff7794fe45f49ab980221fa855b64c70d5c9b83c78ef86edc041e713490b626",
|
||||
"migrated_sha256": "0ff7794fe45f49ab980221fa855b64c70d5c9b83c78ef86edc041e713490b626",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问师历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/mentors/messages?mentor_id=kobe92-perspective&trade_date=20260730",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"migrated_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问天初始化",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/setup?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "c814c2004c7dd672c397887725af000c150693a50900bb0ce1aa94599af1a3c8",
|
||||
"migrated_sha256": "c814c2004c7dd672c397887725af000c150693a50900bb0ce1aa94599af1a3c8",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "问天历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/heaven/readings?mode=trend&limit=20",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "2aa83ea177bd5cb2351ac4f3a60fdcd17e02cfc14b5753e157e515f03fc2c6c1",
|
||||
"migrated_sha256": "2aa83ea177bd5cb2351ac4f3a60fdcd17e02cfc14b5753e157e515f03fc2c6c1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "自选追踪",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/watchlist?trade_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "29c001cc6859312fd90082d0fb83818d263827e28ec21157b94c415b71c45078",
|
||||
"migrated_sha256": "29c001cc6859312fd90082d0fb83818d263827e28ec21157b94c415b71c45078",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "全部复盘笔记",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/notes?scope=all",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "a2a1f08610d017de0e46080a1268f72b734eeb80f9cada311d7e03eb1416656a",
|
||||
"migrated_sha256": "a2a1f08610d017de0e46080a1268f72b734eeb80f9cada311d7e03eb1416656a",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "交易日志",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/trades?end_date=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "77287b34046e22748e94e61ead841e157f6b63f08de1b2fe61f5d715b2e989a4",
|
||||
"migrated_sha256": "77287b34046e22748e94e61ead841e157f6b63f08de1b2fe61f5d715b2e989a4",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "提醒中心",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/alerts?status=all&as_of=2026-07-30",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "b611e936ada5d547c728e33e811630282d1eecd7c1ab1517e22b34902eda032a",
|
||||
"migrated_sha256": "b611e936ada5d547c728e33e811630282d1eecd7c1ab1517e22b34902eda032a",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
},
|
||||
{
|
||||
"name": "复盘助手历史",
|
||||
"method": "GET",
|
||||
"endpoint": "/api/assistant/messages",
|
||||
"original_status": 200,
|
||||
"migrated_status": 200,
|
||||
"original_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"migrated_sha256": "eef46741adfc3a9f76294d3b78f37a45f113092ac9d44ee77c7a038a88ff09a1",
|
||||
"equal": true,
|
||||
"first_difference": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"tested_at": "2026-07-31T16:10:00+08:00",
|
||||
"runtime": {
|
||||
"url": "http://127.0.0.1:8797/",
|
||||
"root": "app",
|
||||
"database": "app/data/backups/slice11-final-migrated/review.db",
|
||||
"production_data_modified": false
|
||||
},
|
||||
"overall": true,
|
||||
"desktop": {
|
||||
"checked": true,
|
||||
"views": [
|
||||
"sentimentCycleView",
|
||||
"screenerView",
|
||||
"heavenView"
|
||||
],
|
||||
"themes": [
|
||||
"light",
|
||||
"dark"
|
||||
],
|
||||
"console_errors": 0,
|
||||
"console_warnings": 0,
|
||||
"horizontal_overflow": false
|
||||
},
|
||||
"mobile": {
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"fixed_navigation": {
|
||||
"selector": ".module-nav",
|
||||
"position": "fixed",
|
||||
"height": 58,
|
||||
"bottom_offset": 0,
|
||||
"primary_tab_count": 5
|
||||
},
|
||||
"views": [
|
||||
{
|
||||
"id": "limitPool",
|
||||
"document_height": 1607,
|
||||
"horizontal_overflow": false,
|
||||
"vertical_content_reachable": true
|
||||
},
|
||||
{
|
||||
"id": "screenerView",
|
||||
"document_height": 1555,
|
||||
"horizontal_overflow": false,
|
||||
"vertical_content_reachable": true
|
||||
},
|
||||
{
|
||||
"id": "mentorView",
|
||||
"document_height": 961,
|
||||
"horizontal_overflow": false,
|
||||
"vertical_content_reachable": true
|
||||
},
|
||||
{
|
||||
"id": "heavenView",
|
||||
"document_height": 844,
|
||||
"horizontal_overflow": false,
|
||||
"vertical_content_reachable": true
|
||||
},
|
||||
{
|
||||
"id": "reviewWorkspaceView",
|
||||
"document_height": 1475,
|
||||
"horizontal_overflow": false,
|
||||
"vertical_content_reachable": true
|
||||
}
|
||||
],
|
||||
"heaven_panels": [
|
||||
{
|
||||
"id": "trend",
|
||||
"document_height": 844,
|
||||
"vertical_content_reachable": true
|
||||
},
|
||||
{
|
||||
"id": "fortune",
|
||||
"document_height": 1730,
|
||||
"vertical_content_reachable": true,
|
||||
"bottom_content_checked": "五行对应行业"
|
||||
},
|
||||
{
|
||||
"id": "heart",
|
||||
"document_height": 1156,
|
||||
"vertical_content_reachable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"preservation_comparison": {
|
||||
"same_account": true,
|
||||
"same_database_snapshot": true,
|
||||
"same_theme": "dark",
|
||||
"same_mobile_viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"original_and_migrated_layout_equal": true,
|
||||
"theme_toggle_icon_note": "原版和迁移版都保留切换后图标需重载才同步的既有行为;相同加载序列下结果一致。"
|
||||
},
|
||||
"manual_acceptance_required": true
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# 保真迁移完成度逐项审计
|
||||
|
||||
> 审计对象:`webapp/app/`
|
||||
> 审计基线:`xiaobai-preservation-slice-11-candidate-20260731`
|
||||
> 结论口径:自动证据通过不替代用户人工验收,也不授权部署切换
|
||||
|
||||
本审计回答的不是“测试是否为绿色”,而是迁移总纲、批准目录、产品表面和人工维护目标是否
|
||||
分别有可复验的证据。状态只使用:`自动闭环`、`人工闭环`、`明确保留`和`未执行`。
|
||||
|
||||
## 1. 目标与证据矩阵
|
||||
|
||||
| 要求 | 当前实现 | 主要证据 | 状态 |
|
||||
|---|---|---|---|
|
||||
| 原版是唯一产品/视觉基线 | 所有切片从根目录原版复制或机械移动;规格书不覆盖真实行为 | 总纲;切片00-11源码映射 | 自动闭环 |
|
||||
| `next/`完全隔离 | `next/`未进入候选导入、资产、测试或部署路径;切片10后Git差分为0 | `next失败冻结记录.md`;Git路径审计 | 自动闭环 |
|
||||
| 批准的模块化单体目录 | `backend/{bootstrap,http,features,data,database,jobs,llm}`、`frontend/{shared,pages,styles,vendor}`、`config/data/tests/tools`均已归位 | `app/config/architecture-inventory.json`;切片01-10 | 自动闭环 |
|
||||
| 不制造第二套产品实现 | 根入口是兼容外壳;业务方法从原类机械移动,专项测试逐符号比较AST/哈希 | 切片01-10 preservation tests | 自动闭环 |
|
||||
| 16个主工作区及内部能力完整 | 页面注册表有16个主工作区;策略跟踪、搜索、详情、提醒、账户和系统管理保留 | `pages.config.json`;前端边界测试;Playwright | 自动闭环 |
|
||||
| API路径、权限、错误语义不变 | 53个精确路径和11个正则路径有唯一所有者;鉴权在后端执行 | `api.config.json`;HTTP/权限测试;21端点差分 | 自动闭环 |
|
||||
| 数据源有统一边界和质量规则 | 18个数据集登记来源、用途、单位、新鲜度、覆盖率、复权和失败关闭规则 | data registries;DataGateway/quality tests | 自动闭环 |
|
||||
| 数据库无损、用户隔离不变 | 36张表由初始schema和有序migration管理;62个schema对象、21张关键表差分相同 | 数据库差分;migration/repository/account-boundary tests | 自动闭环 |
|
||||
| 后台任务可追踪和重试 | 3个任务登记调度、锁、超时、重试和输出版本;运行状态持久化 | `jobs.config.json`;job runner tests | 自动闭环 |
|
||||
| LLM唯一治理边界 | 问师、问天、复盘助手和策略编译经统一网关执行会员、额度、模型回退、流式和审计规则 | 切片07/09;LLM gateway/stream tests | 自动闭环 |
|
||||
| 前端唯一请求出口、Shell和页面职责 | 只有`frontend/shared/api.js`调用业务数据`fetch`;Shell、状态、弹窗、页面生命周期及页面模块已归位 | frontend boundary tests;运行时注册表;Playwright | 自动闭环 |
|
||||
| CSS、主题、动画和移动行为不改写 | 原七层CSS和问天动画按字节/源码移动;令牌层与加载顺序受测试保护;用户逐页确认无迁移视觉回归 | CSS governance;切片10/11浏览器证据;`manual-acceptance.md` | 人工闭环 |
|
||||
| 不确定代码先记录再试删 | 2个文件和5个无消费者函数经自动与人工验收确认废弃;历史表兼容责任继续保留 | `uncertain-code-audit.md`;cleanup contract;`manual-acceptance.md` | 人工闭环 |
|
||||
| 可由人工维护者复验 | 候选README/架构说明、工具分类、API/架构生成器、统一验证命令均可从`app/`独立运行;系统临时目录导出实测242项候选测试通过 | `app/tools/README.md`;maintenance tool tests;独立导出复验 | 自动闭环 |
|
||||
| 正式数据库与部署不受影响 | 验收只使用隔离SQLite副本和非8765端口 | 各切片README;运行记录 | 自动闭环 |
|
||||
| Docker/NAS和正式入口切换 | 用户已明确本轮不做NAS Docker测试;人工验收前禁止切换 | 迁移状态和切换指南 | 未执行 |
|
||||
|
||||
## 2. 结构减法的可量化结果
|
||||
|
||||
- 根`server.py`从原版约5,851行降为25行稳定入口;实现归入启动、HTTP和产品领域模块。
|
||||
- 根`database.py`从原版2,839行降为746行,只保留初始schema、组合入口及有明确历史兼容责任的
|
||||
方法;各领域查询已移入对应Repository。
|
||||
- 20个其他根Python兼容模块均为1-15行导出/模块别名,不含第二套实现。
|
||||
- 原9,283行前端运行时按原连续源码范围拆入共享层和13个页面目录;该行号映射作为迁移历史
|
||||
证据保留,日常维护改由当前模块所有权和行为回归约束。
|
||||
- 已确认没有消费者的`demo_data.py`、旧问天加载文件和5个函数进入可单项恢复的试删候选;
|
||||
`wencai_saved_queries`因旧数据库兼容和账号隔离继续保留。
|
||||
|
||||
大文件不等于自动可删。筛选引擎、Tushare适配器、问天确定性计算和原CSS仍是单一有效实现;
|
||||
在没有更细的同输入差分与人工视觉确认前继续拆分,反而违反“整理原件而非重拍”的迁移约束。
|
||||
它们已登记在`config/architecture-inventory.json`的`code_hotspots`,供后续正常维护按领域处理。
|
||||
|
||||
## 3. 本次严格审计发现并关闭的缺口
|
||||
|
||||
候选版初次自动验收后,三个维护工具仍带有旧目录假设:基线验证器引用不存在的`static/`,
|
||||
架构清单写向不存在的候选文档目录,原样清单生成器假设`app/app`且会因试删文件直接异常。
|
||||
这些问题不改变产品页面,却会让接管者无法按指南复验,因此不能视为可维护性完成。
|
||||
|
||||
关闭方式:
|
||||
|
||||
1. 基线验证器改为检查候选全量测试、两个生成注册表、`frontend/`下全部JavaScript、Git差异和
|
||||
测试数据库完整性。
|
||||
2. 候选架构清单固定生成到`config/architecture-inventory.json`,从正式候选路径统计16页、
|
||||
64类路由匹配、36张表、供应商/LLM入口、CSS层和热点文件。
|
||||
3. 原样资产清单、方法搬运和前端拆分明确标为迁移期工具;可能写文件的操作必须显式传路径或
|
||||
`--apply`,默认帮助路径不改文件。
|
||||
4. 新增维护工具契约测试,保证生成文件新鲜、所有工具`--help`可用,旧`static/`路径不会回归。
|
||||
5. 跨到2026-08-01周六后,实时详情测试中用运行日构造的“固定时间”不再代表交易日;原版和
|
||||
候选测试夹具同步固定到明确工作日,消除跨午夜/周末不确定性。产品实现与交易日规则未改,
|
||||
随后统一验收再次通过302项Python测试和45项Playwright。
|
||||
6. 独立导出`app/`后成功启动健康接口,236项非迁移业务/前端测试通过;同时发现六项日常前端
|
||||
契约经辅助函数隐式读取旧`static/app.js`。完成人工验收后,日常契约已改为装配当前注册脚本;
|
||||
只有确有价值的静态资产、页面前缀及后端AST差分继续读取原版,避免历史源码回放成为第二套维护对象。
|
||||
7. 对切片10十组截图重新做像素审计,九组平均色差均低于0.3/255;集合竞价候选图实际为登录
|
||||
失效页,已明确标为无效并撤销其截图证明力。集合竞价最终视觉继续列为人工验收项。
|
||||
8. 统一验证器现在按环境选择完整保真套件或候选自有套件;系统临时目录中的纯`app/`导出通过
|
||||
242项测试、注册表、24个JavaScript文件和SQLite检查。架构热点大小按归一化UTF-8计算,
|
||||
CRLF/LF不再导致清单失效;正式仓库最终通过305项测试和45项Playwright。
|
||||
|
||||
## 4. 最终人工裁决与部署边界
|
||||
|
||||
2026-08-01用户在隔离端口`8797`浏览全部页面并测试全部功能,确认视觉与功能迁移成功;发现的
|
||||
问题几乎都属于原版遗留问题,未发现阻止验收的迁移回归。该结论已经关闭以下迁移裁决:
|
||||
|
||||
1. 切片11试删候选确认为废弃,恢复标签继续保留。
|
||||
2. 页面视觉、功能、交互和动画的人工验收完成。
|
||||
3. 允许建立本地迁移完成标签并推送Gitea。
|
||||
|
||||
该结论不授权切换正式数据库、Docker或NAS,也不授权删除根目录原版、数据库备份或冻结的
|
||||
`next/`记录。部署切换继续以`人工维护与本地切换指南.md`为准,并须单独获得用户批准。
|
||||