feat(init): 完成 Phase 1 基础架构搭建
- 完成 T-001A: 前端项目初始化 (Next.js 14 + TypeScript + Tailwind CSS) - 完成 T-001B: 后端项目初始化 (FastAPI + SQLAlchemy + asyncpg) - 完成 T-002: 数据库配置 (KolVideo 模型 + 索引 + 测试) - 完成 T-003: 基础 UI 框架 (Header/Footer 组件 + 品牌色系) - 完成 T-004: 环境变量配置 (前后端环境变量) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from typing import List
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Application settings."""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
)
|
||||
|
||||
# Database
|
||||
DATABASE_URL: str = "postgresql+asyncpg://user:password@localhost:5432/yuntu_kol"
|
||||
|
||||
# CORS
|
||||
CORS_ORIGINS: List[str] = ["http://localhost:3000"]
|
||||
|
||||
# Brand API
|
||||
BRAND_API_BASE_URL: str = "https://api.internal.intelligrow.cn"
|
||||
|
||||
# API Settings
|
||||
MAX_QUERY_LIMIT: int = 1000
|
||||
BRAND_API_TIMEOUT: float = 3.0
|
||||
BRAND_API_CONCURRENCY: int = 10
|
||||
|
||||
|
||||
settings = Settings()
|
||||
@@ -0,0 +1,34 @@
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from app.config import settings
|
||||
|
||||
# 创建异步引擎
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
echo=False,
|
||||
pool_pre_ping=True,
|
||||
pool_size=10,
|
||||
max_overflow=20,
|
||||
)
|
||||
|
||||
# 创建异步会话工厂
|
||||
async_session_maker = async_sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""Base class for all models."""
|
||||
pass
|
||||
|
||||
|
||||
async def get_db() -> AsyncSession:
|
||||
"""Dependency to get database session."""
|
||||
async with async_session_maker() as session:
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.close()
|
||||
@@ -0,0 +1,31 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.config import settings
|
||||
|
||||
app = FastAPI(
|
||||
title="KOL Insight API",
|
||||
description="KOL 视频数据查询与分析 API",
|
||||
version="1.0.0",
|
||||
)
|
||||
|
||||
# CORS 配置
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.CORS_ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Root endpoint."""
|
||||
return {"message": "KOL Insight API", "version": "1.0.0"}
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
"""Health check endpoint."""
|
||||
return {"status": "healthy"}
|
||||
@@ -0,0 +1,3 @@
|
||||
from app.models.kol_video import KolVideo
|
||||
|
||||
__all__ = ["KolVideo"]
|
||||
@@ -0,0 +1,52 @@
|
||||
from sqlalchemy import Column, String, Integer, Float, DateTime, Index
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class KolVideo(Base):
|
||||
"""KOL 视频数据模型."""
|
||||
|
||||
__tablename__ = "kol_videos"
|
||||
|
||||
# 主键
|
||||
item_id = Column(String, primary_key=True)
|
||||
|
||||
# 基础信息
|
||||
title = Column(String, nullable=True)
|
||||
viral_type = Column(String, nullable=True)
|
||||
video_url = Column(String, nullable=True)
|
||||
star_id = Column(String, nullable=False)
|
||||
star_unique_id = Column(String, nullable=False)
|
||||
star_nickname = Column(String, nullable=False)
|
||||
publish_time = Column(DateTime, nullable=True)
|
||||
|
||||
# 曝光指标
|
||||
natural_play_cnt = Column(Integer, default=0)
|
||||
heated_play_cnt = Column(Integer, default=0)
|
||||
total_play_cnt = Column(Integer, default=0)
|
||||
|
||||
# 互动指标
|
||||
total_interact = Column(Integer, default=0)
|
||||
like_cnt = Column(Integer, default=0)
|
||||
share_cnt = Column(Integer, default=0)
|
||||
comment_cnt = Column(Integer, default=0)
|
||||
|
||||
# 效果指标
|
||||
new_a3_rate = Column(Float, nullable=True)
|
||||
after_view_search_uv = Column(Integer, default=0)
|
||||
return_search_cnt = Column(Integer, default=0)
|
||||
|
||||
# 商业信息
|
||||
industry_id = Column(String, nullable=True)
|
||||
industry_name = Column(String, nullable=True)
|
||||
brand_id = Column(String, nullable=True)
|
||||
estimated_video_cost = Column(Float, default=0)
|
||||
|
||||
# 索引定义
|
||||
__table_args__ = (
|
||||
Index("idx_star_id", "star_id"),
|
||||
Index("idx_star_unique_id", "star_unique_id"),
|
||||
Index("idx_star_nickname", "star_nickname"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<KolVideo(item_id={self.item_id}, title={self.title})>"
|
||||
Reference in New Issue
Block a user