# -*- coding: utf-8 -*- """ 成交/未成交记录 - 用于日报与数据分析 """ import sqlite3 import os from datetime import datetime from typing import List, Dict, Optional _DB_PATH = os.path.join(os.path.dirname(__file__), "deal_outcome_db", "outcomes.db") _DB_TYPE = os.getenv("DB_TYPE", "sqlite").lower() _MYSQL_HOST = os.getenv("MYSQL_HOST", "127.0.0.1") _MYSQL_PORT = int(os.getenv("MYSQL_PORT", "3306")) _MYSQL_USER = os.getenv("MYSQL_USER", "root") _MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "") _MYSQL_DATABASE = os.getenv("MYSQL_DATABASE", "ai_cs") class _CompatResult: def __init__(self, rows=None, rowcount: int = 0, lastrowid: int = 0): self._rows = rows or [] self.rowcount = rowcount self.lastrowid = lastrowid def fetchall(self): return self._rows def fetchone(self): return self._rows[0] if self._rows else None class _PyMySQLCompatConn: def __init__(self, conn): self._conn = conn def __enter__(self): return self def __exit__(self, exc_type, exc, tb): if exc_type: try: self._conn.rollback() except Exception: pass self._conn.close() def execute(self, query: str, args=None): cur = self._conn.cursor() cur.execute(query, args or ()) rows = cur.fetchall() if cur.description else [] res = _CompatResult(rows=rows, rowcount=cur.rowcount, lastrowid=getattr(cur, "lastrowid", 0)) cur.close() return res def commit(self): self._conn.commit() def _is_mysql() -> bool: return _DB_TYPE in ("mysql", "mariadb") def _sql(query: str) -> str: return query.replace("?", "%s") if _is_mysql() else query def _get_conn() -> sqlite3.Connection: if _is_mysql(): import pymysql conn = pymysql.connect( host=_MYSQL_HOST, port=_MYSQL_PORT, user=_MYSQL_USER, password=_MYSQL_PASSWORD, database=_MYSQL_DATABASE, charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor, autocommit=False, ) return _PyMySQLCompatConn(conn) os.makedirs(os.path.dirname(_DB_PATH), exist_ok=True) conn = sqlite3.connect(_DB_PATH) conn.row_factory = sqlite3.Row return conn def _init_db(): with _get_conn() as conn: if _is_mysql(): conn.execute(""" CREATE TABLE IF NOT EXISTS deal_outcomes ( id INTEGER PRIMARY KEY AUTO_INCREMENT, customer_id VARCHAR(128) NOT NULL, customer_name VARCHAR(255) DEFAULT '', acc_id VARCHAR(128) DEFAULT '', platform VARCHAR(64) DEFAULT '', date DATE NOT NULL, outcome VARCHAR(16) NOT NULL, reason TEXT, order_id VARCHAR(128) DEFAULT '', amount REAL DEFAULT 0, discount_given INTEGER DEFAULT 0, timestamp DATETIME NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 """) idx_rows = conn.execute("SHOW INDEX FROM deal_outcomes").fetchall() exists = {str(r.get("Key_name", "")) for r in idx_rows} if "idx_deal_date" not in exists: conn.execute("CREATE INDEX idx_deal_date ON deal_outcomes(date)") if "idx_deal_customer" not in exists: conn.execute("CREATE INDEX idx_deal_customer ON deal_outcomes(customer_id)") if "idx_deal_acc" not in exists: conn.execute("CREATE INDEX idx_deal_acc ON deal_outcomes(acc_id)") if "idx_deal_outcome" not in exists: conn.execute("CREATE INDEX idx_deal_outcome ON deal_outcomes(outcome)") else: conn.execute(""" CREATE TABLE IF NOT EXISTS deal_outcomes ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id TEXT NOT NULL, customer_name TEXT DEFAULT '', acc_id TEXT DEFAULT '', platform TEXT DEFAULT '', date TEXT NOT NULL, outcome TEXT NOT NULL CHECK(outcome IN ('成交','未成交')), reason TEXT DEFAULT '', order_id TEXT DEFAULT '', amount REAL DEFAULT 0, discount_given INTEGER DEFAULT 0, timestamp TEXT NOT NULL ) """) conn.execute("CREATE INDEX IF NOT EXISTS idx_deal_date ON deal_outcomes(date)") conn.execute("CREATE INDEX IF NOT EXISTS idx_deal_customer ON deal_outcomes(customer_id)") conn.execute("CREATE INDEX IF NOT EXISTS idx_deal_acc ON deal_outcomes(acc_id)") conn.execute("CREATE INDEX IF NOT EXISTS idx_deal_outcome ON deal_outcomes(outcome)") conn.commit() _init_db() def record_deal( customer_id: str, outcome: str, reason: str = "", customer_name: str = "", acc_id: str = "", platform: str = "", order_id: str = "", amount: float = 0, discount_given: bool = False, ): """记录一笔成交或未成交""" ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") date = datetime.now().strftime("%Y-%m-%d") with _get_conn() as conn: conn.execute( _sql("""INSERT INTO deal_outcomes (customer_id, customer_name, acc_id, platform, date, outcome, reason, order_id, amount, discount_given, timestamp) VALUES (?,?,?,?,?,?,?,?,?,?,?)"""), ( customer_id, customer_name or "", acc_id or "", platform or "", date, outcome, reason or "", order_id or "", amount, 1 if discount_given else 0, ts, ), ) conn.commit() def get_daily_outcomes(date: str = "") -> List[Dict]: """获取指定日期的成交/未成交记录,用于日报""" if not date: date = datetime.now().strftime("%Y-%m-%d") with _get_conn() as conn: rows = conn.execute( _sql(""" SELECT customer_id, customer_name, acc_id, outcome, reason, order_id, amount, discount_given, timestamp FROM deal_outcomes WHERE date = ? ORDER BY timestamp ASC """), (date,), ).fetchall() return [dict(r) for r in rows] def get_daily_summary(date: str = "") -> Dict: """获取指定日期的成交/未成交汇总统计""" outcomes = get_daily_outcomes(date) success = [o for o in outcomes if o["outcome"] == "成交"] fail = [o for o in outcomes if o["outcome"] == "未成交"] # 按原因分组 fail_by_reason: Dict[str, int] = {} for o in fail: r = o.get("reason") or "其他" fail_by_reason[r] = fail_by_reason.get(r, 0) + 1 return { "date": date or datetime.now().strftime("%Y-%m-%d"), "成交数": len(success), "未成交数": len(fail), "成交金额": sum(o.get("amount") or 0 for o in success), "成交明细": success, "未成交明细": fail, "未成交原因分布": fail_by_reason, } def export_for_analysis(start_date: str = "", end_date: str = "") -> List[Dict]: """ 导出成交/未成交记录,供数据库分析。 日期格式 YYYY-MM-DD,留空则查全部。 """ with _get_conn() as conn: if start_date and end_date: rows = conn.execute( _sql("""SELECT * FROM deal_outcomes WHERE date BETWEEN ? AND ? ORDER BY date, timestamp"""), (start_date, end_date), ).fetchall() elif start_date: rows = conn.execute( _sql("""SELECT * FROM deal_outcomes WHERE date >= ? ORDER BY date, timestamp"""), (start_date,), ).fetchall() elif end_date: rows = conn.execute( _sql("""SELECT * FROM deal_outcomes WHERE date <= ? ORDER BY date, timestamp"""), (end_date,), ).fetchall() else: rows = conn.execute( """SELECT * FROM deal_outcomes ORDER BY date, timestamp""" ).fetchall() return [dict(r) for r in rows]