Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ jobs:
- run: python -c "from app.main import app; print('import ok')"
- name: Init test DB
run: |
cd ..
python scripts/init_db.py
python scripts/seed_data.py
python scripts/compute_baseline.py
python -c "from app.database import init_database; from app.main import DB_PATH; init_database(DB_PATH)"
- run: python -m pytest tests/ -v --tb=short

frontend:
Expand Down
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

# 一键初始化数据库并生成 baseline
data:
python3 scripts/init_db.py
python3 scripts/seed_data.py
python3 scripts/compute_baseline.py
@echo "数据库表 / 种子数据 / 基线指标 已由 init_database() 自动处理"

# 安装所有依赖
install:
Expand Down
106 changes: 106 additions & 0 deletions backend/app/database/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import sqlite3
from pathlib import Path


def init_database(db_path: Path):
"""创建数据库表结构(如不存在)"""
db_path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(db_path)

conn.execute("""
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT NOT NULL,
title TEXT NOT NULL,
title_length INTEGER,
content TEXT,
tags TEXT,
publish_hour INTEGER,
likes INTEGER DEFAULT 0,
collects INTEGER DEFAULT 0,
comments INTEGER DEFAULT 0,
followers INTEGER DEFAULT 0,
is_viral INTEGER DEFAULT 0,
cover_has_face INTEGER DEFAULT 0,
cover_text_ratio REAL DEFAULT 0,
cover_saturation REAL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_notes_category ON notes(category)")
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_notes_viral ON notes(category, is_viral)"
)

conn.execute("""
CREATE TABLE IF NOT EXISTS baseline_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT NOT NULL,
metric_name TEXT NOT NULL,
metric_value REAL,
metric_json TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(category, metric_name)
)
""")

conn.execute("""
CREATE TABLE IF NOT EXISTS diagnosis_history (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
category TEXT NOT NULL,
overall_score REAL,
grade TEXT,
report_json TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_history_created
ON diagnosis_history(created_at DESC)
""")

conn.execute("""
CREATE TABLE IF NOT EXISTS usage_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOT NULL,
action TEXT NOT NULL DEFAULT 'diagnose',
title TEXT DEFAULT '',
category TEXT DEFAULT '',
total_tokens INTEGER DEFAULT 0,
duration_sec REAL DEFAULT 0,
status TEXT DEFAULT 'ok',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_usage_created ON usage_log(created_at DESC)"
)
conn.execute("CREATE INDEX IF NOT EXISTS idx_usage_ip ON usage_log(ip)")

conn.execute("""
CREATE TABLE IF NOT EXISTS visit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
visitor_hash TEXT NOT NULL,
user_agent_hash TEXT DEFAULT '',
path TEXT NOT NULL,
referrer TEXT DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_visit_created ON visit_log(created_at DESC)"
)
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_visit_visitor ON visit_log(visitor_hash)"
)
conn.execute("CREATE INDEX IF NOT EXISTS idx_visit_path ON visit_log(path)")

conn.commit()
conn.close()

from app.database.seed_data import seed_data
from app.database.baseline import compute_baseline

seed_data(db_path)
compute_baseline(db_path)
23 changes: 7 additions & 16 deletions scripts/compute_baseline.py → backend/app/database/baseline.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
"""
基于 notes 表数据,预计算各垂类的 baseline 统计指标并写入 baseline_stats 表。

Usage:
python scripts/compute_baseline.py
"""
import sqlite3
import json
import os
from collections import Counter

DB_PATH = os.path.join(os.path.dirname(__file__), "..", "backend", "data", "baseline.db")
from pathlib import Path


def upsert_stat(cursor, category, metric_name, metric_value=None, metric_json=None):
Expand Down Expand Up @@ -174,20 +166,19 @@ def compute_for_category(cursor, category):
print(f" [{category}] 已计算 baseline 指标(含粉丝分层与标签分桶)")


def main():
def compute_baseline(db_path: Path):
"""计算所有垂类的 baseline 统计指标"""
conn = sqlite3.connect(DB_PATH)
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

cursor.execute("DELETE FROM baseline_stats")
cursor.execute("SELECT COUNT(*) FROM baseline_stats")
if cursor.fetchone()[0] > 0:
conn.close()
return

for cat in ["food", "fashion", "tech", "travel", "beauty", "fitness", "lifestyle", "home"]:
compute_for_category(cursor, cat)

conn.commit()
conn.close()
print("所有 baseline 统计指标已计算完毕")


if __name__ == "__main__":
main()
27 changes: 9 additions & 18 deletions scripts/seed_data.py → backend/app/database/seed_data.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
"""
生成模拟 baseline 种子数据用于开发和演示。
实际比赛前应替换为真实采集的小红书笔记数据。

Usage:
python scripts/seed_data.py
"""
import sqlite3
import json
import random
import os
from pathlib import Path

DB_PATH = os.path.join(os.path.dirname(__file__), "..", "backend", "data", "baseline.db")

FOOD_TITLES = [
"手把手教你做日式溏心蛋!零失败!", "一周减脂餐分享|好吃不胖",
Expand Down Expand Up @@ -41,7 +33,7 @@
"这个APP改变了我的学习方式", "数码产品年度盘点|好用到哭",
"iPad学习法|从学渣到学霸", "耳机横评|千元内最值得买的5款",
"NAS入门指南|打造私人云存储", "手机摄影技巧|拍出电影质感",
"机械键盘入坑指南|新手必看", "二手数码避坑指南‼️",
"机械键盘入坑指南|新手必看", "二手数码避坑指南‼️",
"AI工具合集|效率提升10倍", "极简桌面布置|打造高效工作台",
]

Expand Down Expand Up @@ -153,12 +145,15 @@ def generate_notes(category, titles, tags_pool, count=500):
return notes


def seed():
"""写入种子数据"""
conn = sqlite3.connect(DB_PATH)
def seed_data(db_path: Path):
"""若 notes 表为空,则填充种子数据"""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

cursor.execute("DELETE FROM notes")
cursor.execute("SELECT COUNT(*) FROM notes")
if cursor.fetchone()[0] > 0:
conn.close()
return

all_notes = []
all_notes.extend(generate_notes("food", FOOD_TITLES, FOOD_TAGS, 500))
Expand All @@ -181,7 +176,3 @@ def seed():
conn.commit()
print(f"已插入 {len(all_notes)} 条种子数据")
conn.close()


if __name__ == "__main__":
seed()
3 changes: 2 additions & 1 deletion backend/app/local_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
import logging
import os
from datetime import datetime
from pathlib import Path

logger = logging.getLogger("noterx.local_memory")

_DATA_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "data"))
_DATA_ROOT = Path(__file__).parent.parent.parent / "data"
WORKSPACE_ROOT = os.path.join(_DATA_ROOT, "noterx_workspace")
MEMORY_MD = os.path.join(WORKSPACE_ROOT, "MEMORY.md")
MEMORY_DIR = os.path.join(WORKSPACE_ROOT, "memory")
Expand Down
66 changes: 6 additions & 60 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import logging
import os
import sqlite3
from pathlib import Path
from contextlib import asynccontextmanager

from fastapi import FastAPI
Expand All @@ -13,69 +13,17 @@

from app.api.routes import router as api_router
from app import local_memory
from app.database import init_database

DB_PATH = Path(__file__).parent.parent.parent / "data" / "baseline.db"
FRONTEND_DIST = os.path.join(os.path.dirname(__file__), "..", "..", "frontend", "dist")

DB_PATH = os.path.join(os.path.dirname(__file__), "..", "data", "baseline.db")


def _ensure_history_table():
"""启动时自动创建 diagnosis_history 表(如不存在)"""
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS diagnosis_history (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
category TEXT NOT NULL,
overall_score REAL,
grade TEXT,
report_json TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_history_created
ON diagnosis_history(created_at DESC)
""")
# Usage tracking table
conn.execute("""
CREATE TABLE IF NOT EXISTS usage_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOT NULL,
action TEXT NOT NULL DEFAULT 'diagnose',
title TEXT DEFAULT '',
category TEXT DEFAULT '',
total_tokens INTEGER DEFAULT 0,
duration_sec REAL DEFAULT 0,
status TEXT DEFAULT 'ok',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_usage_created ON usage_log(created_at DESC)")
conn.execute("CREATE INDEX IF NOT EXISTS idx_usage_ip ON usage_log(ip)")
conn.execute("""
CREATE TABLE IF NOT EXISTS visit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
visitor_hash TEXT NOT NULL,
user_agent_hash TEXT DEFAULT '',
path TEXT NOT NULL,
referrer TEXT DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_visit_created ON visit_log(created_at DESC)")
conn.execute("CREATE INDEX IF NOT EXISTS idx_visit_visitor ON visit_log(visitor_hash)")
conn.execute("CREATE INDEX IF NOT EXISTS idx_visit_path ON visit_log(path)")
conn.commit()
conn.close()
local_memory.ensure_memory_md()


@asynccontextmanager
async def lifespan(_app: FastAPI):
"""应用生命周期:启动时自动建表"""
_ensure_history_table()
init_database(DB_PATH)
local_memory.ensure_memory_md()
yield

logging.basicConfig(
Expand Down Expand Up @@ -178,12 +126,10 @@ async def serve_app():
async def health():
"""详细健康检查,含数据库探测"""
import sqlite3
import os
db_path = os.path.join(os.path.dirname(__file__), "..", "data", "baseline.db")
db_ok = False
note_count = 0
try:
conn = sqlite3.connect(db_path)
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM notes")
note_count = cur.fetchone()[0]
Expand Down
5 changes: 1 addition & 4 deletions deploy_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ def run(ssh, cmd, check=True):
run(ssh, f"{REMOTE_DIR}/backend/venv/bin/pip install -r {REMOTE_DIR}/backend/requirements.txt")

# Init DB
print(" Initializing database...")
run(ssh, f"cd {REMOTE_DIR} && {REMOTE_DIR}/backend/venv/bin/python scripts/init_db.py", check=False)
run(ssh, f"cd {REMOTE_DIR} && {REMOTE_DIR}/backend/venv/bin/python scripts/seed_data.py", check=False)
run(ssh, f"cd {REMOTE_DIR} && {REMOTE_DIR}/backend/venv/bin/python scripts/compute_baseline.py", check=False)
print(" Database tables / seed data / baseline will be handled by app startup")

# Upload .env
print(" Uploading .env...")
Expand Down
Loading