98 lines
4.1 KiB
Python
98 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import date
|
|
from http import HTTPStatus
|
|
|
|
from backend.bootstrap.config import normalize_date, validate_stock_code, validate_text
|
|
from backend.features.review.agent import ReviewAssistantError
|
|
|
|
|
|
class ReviewHttpMixin:
|
|
def save_trade_entry(self) -> None:
|
|
try:
|
|
body = self.read_json_body()
|
|
self.send_json(
|
|
{"ok": True, **self.application_service.save_trade_entry(body)},
|
|
HTTPStatus.CREATED,
|
|
)
|
|
except (ValueError, json.JSONDecodeError) as exc:
|
|
self.send_json({"error": str(exc)}, HTTPStatus.BAD_REQUEST)
|
|
|
|
def stream_assistant_chat(self) -> None:
|
|
try:
|
|
body = self.read_json_body()
|
|
stream = self.application_service.assistant_stream(body)
|
|
except (ValueError, json.JSONDecodeError) as exc:
|
|
self.send_json({"error": str(exc)}, HTTPStatus.BAD_REQUEST)
|
|
return
|
|
self.send_response(HTTPStatus.OK)
|
|
self.send_header("Content-Type", "application/x-ndjson; charset=utf-8")
|
|
self.send_header("Cache-Control", "no-cache, no-transform")
|
|
self.send_header("X-Accel-Buffering", "no")
|
|
self.send_header("Connection", "close")
|
|
self.end_headers()
|
|
try:
|
|
for chunk in stream:
|
|
self._write_stream_event({"type": "delta", "content": chunk})
|
|
self._write_stream_event({"type": "done"})
|
|
except (ValueError, ReviewAssistantError) as exc:
|
|
self._write_stream_event({"type": "error", "error": str(exc)})
|
|
except (BrokenPipeError, ConnectionResetError):
|
|
pass
|
|
finally:
|
|
self.close_connection = True
|
|
|
|
def save_watchlist(self) -> None:
|
|
try:
|
|
body = self.read_json_body()
|
|
code = validate_stock_code(str(body.get("code", "")))
|
|
name = validate_text(body.get("name"), "股票名称", 30, required=True)
|
|
sector = validate_text(body.get("sector"), "所属板块", 50)
|
|
color = str(body.get("color") or "red")
|
|
if color not in {"red", "blue", "green", "amber"}:
|
|
raise ValueError("标记颜色不支持。")
|
|
remark = validate_text(body.get("remark"), "跟踪备注", 240)
|
|
service = self.application_service
|
|
service.database.save_watchlist(
|
|
service.current_user_id, code, name, sector, color, remark
|
|
)
|
|
self.send_json(
|
|
{
|
|
"ok": True,
|
|
"items": service.database.list_watchlist(service.current_user_id),
|
|
}
|
|
)
|
|
except (ValueError, json.JSONDecodeError) as exc:
|
|
self.send_json({"error": str(exc)}, HTTPStatus.BAD_REQUEST)
|
|
|
|
def save_note(self) -> None:
|
|
try:
|
|
body = self.read_json_body()
|
|
code = str(body.get("code") or "").strip()
|
|
if code:
|
|
code = validate_stock_code(code)
|
|
stock_name = validate_text(body.get("stock_name"), "股票名称", 30)
|
|
trade_date = normalize_date(str(body.get("trade_date") or date.today().isoformat()))
|
|
summary = validate_text(body.get("summary"), "盘面摘要", 500)
|
|
content = validate_text(body.get("content"), "复盘内容", 5000)
|
|
plan = validate_text(body.get("plan"), "明日计划", 2000)
|
|
if not summary and not content and not plan:
|
|
raise ValueError("每日复盘内容不能全部为空。")
|
|
raw_id = body.get("id")
|
|
note_id = int(raw_id) if raw_id else None
|
|
service = self.application_service
|
|
saved_id = service.database.save_note(
|
|
service.current_user_id,
|
|
code,
|
|
stock_name,
|
|
trade_date,
|
|
content,
|
|
plan,
|
|
note_id,
|
|
summary=summary,
|
|
)
|
|
self.send_json({"ok": True, "id": saved_id})
|
|
except (ValueError, TypeError, json.JSONDecodeError) as exc:
|
|
self.send_json({"error": str(exc)}, HTTPStatus.BAD_REQUEST)
|