Skip to content

Commit 89f4647

Browse files
committed
[feat]: 实现文献库和跨域
1 parent 8bba2bc commit 89f4647

File tree

9 files changed

+125
-16
lines changed

9 files changed

+125
-16
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""实现多需求合并
2+
3+
Revision ID: 4b9d22943860
4+
Revises: 48b09347ef95
5+
Create Date: 2025-04-28 23:52:46.462144
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = '4b9d22943860'
16+
down_revision: Union[str, None] = '48b09347ef95'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
"""Upgrade schema."""
23+
# ### commands auto generated by Alembic - please adjust! ###
24+
op.create_table('enter_application',
25+
sa.Column('user_id', sa.Integer(), nullable=False),
26+
sa.Column('group_id', sa.Integer(), nullable=False),
27+
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
28+
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
29+
sa.PrimaryKeyConstraint('user_id', 'group_id')
30+
)
31+
op.add_column('groups', sa.Column('description', sa.String(length=200), nullable=False))
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade() -> None:
36+
"""Downgrade schema."""
37+
# ### commands auto generated by Alembic - please adjust! ###
38+
op.drop_column('groups', 'description')
39+
op.drop_table('enter_application')
40+
# ### end Alembic commands ###

app/api/v1/endpoints/articleDB.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
from sqlalchemy.ext.asyncio import AsyncSession
33
from app.utils.get_db import get_db
44
from app.schemas.articleDB import UploadArticle, GetArticle, DeLArticle, GetResponse
5-
from app.curd.articleDB import create_article_in_db, get_article_in_db
6-
5+
from app.curd.articleDB import create_article_in_db, get_article_in_db, get_article_in_db_by_id
6+
from app.core.config import settings
7+
import os
8+
import uuid
9+
from fastapi.responses import FileResponse
10+
from urllib.parse import quote
711
router = APIRouter()
812

913
@router.put("/upload", response_model=dict)
@@ -17,8 +21,19 @@ async def upload_article(
1721
"""
1822
Upload an article to the database.
1923
"""
24+
# 将文件保存到指定目录
25+
if not os.path.exists(settings.UPLOAD_FOLDER):
26+
os.makedirs(settings.UPLOAD_FOLDER)
27+
28+
# 生成文件名,可以使用 UUID 或者其他方式来确保文件名唯一
29+
file_name = f"{uuid.uuid4()}.pdf"
30+
file_path = os.path.join(settings.UPLOAD_FOLDER, file_name)
2031
try:
21-
await create_article_in_db(db=db, upload_article=UploadArticle(title=title, author=author, url=url))
32+
with open(file_path, "wb") as f:
33+
while chunk := await file.read(1024): # 每次读取 1024 字节
34+
f.write(chunk)
35+
36+
await create_article_in_db(db=db, upload_article=UploadArticle(title=title, author=author, url=url, file_path=file_path))
2237
return {"msg": "Article uploaded successfully"}
2338
except Exception as e:
2439
raise HTTPException(status_code=500, detail=str(e))
@@ -36,4 +51,30 @@ async def get_article(get_article: GetArticle = Depends(), db: AsyncSession = De
3651
"total_count": total_count
3752
},
3853
"articles": [articles.model_dump() for articles in articles]
39-
}
54+
}
55+
56+
@router.get("/download/{article_id}", response_model=dict)
57+
async def download_article(article_id: int, db: AsyncSession = Depends(get_db)):
58+
"""
59+
Download an article file by its ID.
60+
"""
61+
# 根据 ID 查询文章信息
62+
article = await get_article_in_db_by_id(db=db, article_id=article_id)
63+
if not article or not article.file_path:
64+
raise HTTPException(status_code=404, detail="File not found")
65+
66+
if not os.path.exists(article.file_path):
67+
raise HTTPException(status_code=404, detail="File not found on server")
68+
69+
# 从文件路径获取文件名
70+
file_name = os.path.basename(article.file_path)
71+
72+
# 设置原始文件名,如果有标题,使用标题作为文件名
73+
download_filename = f"{article.title}.pdf" if article.title else file_name
74+
75+
# 返回文件,并设置文件名(使用 quote 处理中文文件名)
76+
return FileResponse(
77+
path=article.file_path,
78+
filename=quote(download_filename),
79+
media_type="application/pdf"
80+
)

app/api/v1/endpoints/auth.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from app.curd.article import crud_self_create_folder, crud_article_statistic
1717
from app.utils.get_db import get_db
1818
from app.utils.redis import get_redis_client
19+
from app.curd.note import find_recent_notes_in_db
1920

2021
router = APIRouter()
2122

@@ -151,4 +152,11 @@ async def send_code(user_send_code: UserSendCode):
151152
@router.get("/articleStatistic", response_model="dict")
152153
async def article_statistic(db: AsyncSession = Depends(get_db)):
153154
articles = await crud_article_statistic(db)
154-
return {"articles": articles}
155+
return {"articles": articles}
156+
157+
@router.get("/recent", response_model=dict)
158+
async def get_recent_notes(db: AsyncSession = Depends(get_db)):
159+
notes = await find_recent_notes_in_db(db)
160+
return {
161+
"notes": notes
162+
}

app/api/v1/endpoints/note.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from sqlalchemy.ext.asyncio import AsyncSession
33
from app.schemas.note import NoteCreate, NoteUpdate, NoteFind
44
from app.utils.get_db import get_db
5-
from app.curd.note import create_note_in_db, delete_note_in_db, update_note_in_db, find_notes_in_db, find_notes_title_in_db, find_recent_notes_in_db
5+
from app.curd.note import create_note_in_db, delete_note_in_db, update_note_in_db, find_notes_in_db, find_notes_title_in_db
66
from typing import Optional
77

88
router = APIRouter()
@@ -52,11 +52,3 @@ async def get_notes_title(note_find: NoteFind = Depends(), db: AsyncSession = De
5252
},
5353
"notes": notes
5454
}
55-
56-
57-
@router.get("/recent", response_model=dict)
58-
async def get_recent_notes(db: AsyncSession = Depends(get_db)):
59-
notes = await find_recent_notes_in_db(db)
60-
return {
61-
"notes": notes
62-
}

app/core/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Settings:
1616
SENDER_EMAIL : str = "[email protected]"
1717
SENDER_PASSWORD: str = os.getenv("SENDER_PASSWORD", "default_password") # 发件人邮箱密码
1818
KIMI_API_KEY: str = os.getenv("KIMI_API_KEY", "default_kimi_api_key") # KIMI API密钥
19+
UPLOAD_FOLDER: str = "/lhcos_data/acticleDB"
1920

2021

2122
settings = Settings()

app/curd/articleDB.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ async def create_article_in_db(db: AsyncSession, upload_article: UploadArticle):
88
"""
99
Create a new article in the database.
1010
"""
11-
article =ArticleDB(title=upload_article.title, url=upload_article.url, author=upload_article.author)
11+
article =ArticleDB(title=upload_article.title, url=upload_article.url, author=upload_article.author, file_path=upload_article.file_path)
1212
db.add(article)
1313
await db.commit()
1414
await db.refresh(article)
@@ -37,3 +37,10 @@ async def get_article_in_db(db: AsyncSession, get_article: GetArticle):
3737

3838
return [GetResponse.model_validate(article) for article in articles], total_count
3939

40+
async def get_article_in_db_by_id(db: AsyncSession, article_id: int):
41+
"""
42+
Get an article by its ID.
43+
"""
44+
result = await db.execute(select(ArticleDB).where(ArticleDB.id == article_id))
45+
article = result.scalars().first()
46+
return article

app/main.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from app.routers.router import include_routers
33
from fastapi_pagination import add_pagination
44
from loguru import logger
5+
from fastapi.middleware.cors import CORSMiddleware
56

67
app = FastAPI()
78

@@ -27,4 +28,13 @@ async def log_requests(request: Request, call_next):
2728
logger.info(f"Request: {request.method} {request.url}")
2829
response = await call_next(request)
2930
logger.info(f"Response status: {response.status_code}")
30-
return response
31+
return response
32+
33+
# 配置 CORS
34+
app.add_middleware(
35+
CORSMiddleware,
36+
allow_origins=["*"], # 允许的前端来源
37+
allow_credentials=True, # 允许发送凭据(如 Cookies 或 Authorization 头)
38+
allow_methods=["*"], # 允许的 HTTP 方法
39+
allow_headers=["*"], # 允许的请求头
40+
)

app/models/model.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
Column('is_admin', Boolean, default=False)
1212
)
1313

14+
enter_application = Table(
15+
'enter_application', Base.metadata,
16+
Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
17+
Column('group_id', Integer, ForeignKey('groups.id'), primary_key=True),
18+
)
19+
1420
class User(Base):
1521
__tablename__ = 'users'
1622

@@ -32,6 +38,7 @@ class Group(Base):
3238
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
3339
leader = Column(Integer)
3440
name = Column(String(30), nullable=False)
41+
description = Column(String(200), nullable=False)
3542
create_time = Column(DateTime, default=func.now(), nullable=False) # 创建时间
3643
update_time = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False) # 更新时间
3744
users = relationship('User', secondary=user_group, back_populates='groups')
@@ -109,6 +116,7 @@ class ArticleDB(Base):
109116
title = Column(String(200), nullable=False)
110117
url = Column(String(200), nullable=False)
111118
author = Column(String(100), nullable=False)
119+
file_path = Column(String(200), nullable=False)
112120

113121
create_time = Column(DateTime, default=func.now(), nullable=False) # 创建时间
114122
update_time = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False) # 更新时间

app/schemas/articleDB.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class UploadArticle(BaseModel):
55
title: str
66
author: str
77
url: str
8+
file_path: str
89

910
class GetArticle(BaseModel):
1011
id: int | None = None
@@ -20,6 +21,7 @@ class GetResponse(BaseModel):
2021
url: str
2122
create_time: datetime
2223
update_time: datetime
24+
file_path: str
2325

2426
class Config:
2527
from_attributes = True

0 commit comments

Comments
 (0)