Compare commits

..

20 Commits

Author SHA1 Message Date
5462ecc8af feat:更新路由注释 2025-12-11 14:02:39 +08:00
49d634b877 feat:更新数据模型定义 2025-12-10 19:50:13 +08:00
8650f92259 feat:增加分页条件查询博客接口 2025-11-04 15:55:42 +08:00
a698267d9e fix:修复查询未发布博客无阅读次数的问题 2025-11-03 14:29:08 +08:00
f22dc7c35f feat:更新博客排序规则 2025-10-29 16:40:55 +08:00
8ad63f9a78 feat:增加博客统计接口 2025-10-27 15:04:47 +08:00
02983a882d feat:更新博客统计模块 2025-10-26 20:00:56 +08:00
f6d1096dbc feat:增加上传博客图片模块 2025-10-25 22:53:46 +08:00
6512c536dc feat:增加博客草稿功能 2025-10-23 19:58:31 +08:00
398ad56933 feat:更改配置文件 2025-10-20 15:43:03 +08:00
132fbb13b7 feat:增加查询博客相邻记录功能 2025-10-12 22:56:31 +08:00
38610aef67 fix:修复fluentbit端口错误的问题 2025-10-09 23:48:12 +08:00
8fc915a6e3 feat:更新host地址 2025-10-09 22:58:49 +08:00
0d32c9b629 fix:解决数据库断线的问题 2025-10-09 09:33:51 +08:00
37b16dc5e5 feat:增加fluentbit日志输出 2025-09-29 14:20:04 +08:00
46ea187d55 fix:修复日志不显示问题 2025-09-29 10:44:56 +08:00
b6617d3db2 feat:更新博客访客记录 2025-09-28 18:42:08 +08:00
bd1d10e0f7 fix:修复博客评论接口 2025-09-26 17:13:01 +08:00
a8fdd04c41 feat:增加fluent日志 2025-09-25 23:33:50 +08:00
5e3dc21ed1 feat:增加博客搜索功能 2025-09-25 17:31:25 +08:00
24 changed files with 1815 additions and 317 deletions

View File

@@ -1,5 +1,7 @@
FROM python:3.12-slim FROM python:3.12-slim
WORKDIR /app WORKDIR /app
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
COPY ./packages /app/packages COPY ./packages /app/packages
COPY requirements.txt /app/ COPY requirements.txt /app/
RUN pip install --no-cache-dir --no-index --find-links=/app/packages -r requirements.txt RUN pip install --no-cache-dir --no-index --find-links=/app/packages -r requirements.txt

View File

@@ -1,7 +1,9 @@
from fastapi import FastAPI from fastapi import FastAPI
from .blog import router as blog_router from .blog import router as blog_router
from .blog_stats import router as blog_stats_router
from .session import router as session_router from .session import router as session_router
def register_routers(app: FastAPI): def register_routers(app: FastAPI):
app.include_router(blog_router, prefix="") app.include_router(blog_router, prefix="")
app.include_router(blog_stats_router, prefix="")
app.include_router(session_router, prefix="") app.include_router(session_router, prefix="")

View File

@@ -1,14 +1,16 @@
from typing import List from typing import List
from fastapi import APIRouter, Depends, Query, Request from fastapi import APIRouter, Depends, Query, Request, UploadFile, File, Path, Body
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from config.auth import verify_token from config.auth import verify_token
from schemas.blog import BlogResponse, BlogQuery, BlogCategoryResponse, BlogStatsResponse, \ from schemas.blog import BlogResponse, BlogQuery, BlogCategoryResponse, BlogStatsResponse, \
BlogLatestResponse, BlogCommentCreate, BlogCommentResponse, BlogVisitResponse, BlogCreate, BlogUpdate BlogLatestResponse, BlogCommentCreate, BlogCommentResponse, BlogVisitResponse, BlogCreate, BlogUpdate, \
BlogAdjacentResponse
from schemas.blog_elastic import BlogSearch
from schemas.pagination import PageResult from schemas.pagination import PageResult
from config.database import get_db from config.database import get_db
from service import blog_service from service import blog_service, blog_elastic_service, file_service
router = APIRouter( router = APIRouter(
prefix="/blog", prefix="/blog",
@@ -17,39 +19,114 @@ router = APIRouter(
) )
@router.post("/file/upload", summary="上传博客图片", response_model=str)
async def sync_all_blog(md5: str = Query(..., description="文件MD5值"),
file: UploadFile = File(..., description="要上传的图片文件")):
return await file_service.upload_file(md5, file)
@router.get("/sync", summary="同步博客到elastic", response_model=bool)
def sync_all_blog(db: Session = Depends(get_db)):
return blog_elastic_service.sync_all_blog(db)
@router.get("/search", summary="搜索elastic", response_model=List[BlogSearch])
def search_blog(keyword: str = Query(..., description="搜索关键词")):
return blog_elastic_service.search_blog(keyword)
@router.get("/page", summary="分页查询博客", response_model=PageResult[BlogResponse]) @router.get("/page", summary="分页查询博客", response_model=PageResult[BlogResponse])
def query_blog_by_page(current_page: int = Query(1, ge=1, alias="currentPage", description="当前页码"), def query_blog_by_page(
page_size: int = Query(10, ge=1, le=100, alias="pageSize", description="每页数量"), current_page: int = Query(1, ge=1, alias="currentPage", description="当前页码从1开始"),
db: Session = Depends(get_db)): page_size: int = Query(10, ge=1, le=100, alias="pageSize", description="每页显示数量1-100之间"),
db: Session = Depends(get_db)
):
return blog_service.query_blog_by_page(db, current_page, page_size) return blog_service.query_blog_by_page(db, current_page, page_size)
@router.get("/condition", summary="条件查询博客", response_model=List[BlogResponse]) @router.get("/condition", summary="条件查询博客", response_model=List[BlogResponse])
def query_blog_by_condition(query: BlogQuery = Depends(), def query_blog_by_condition(query: BlogQuery = Depends(), db: Session = Depends(get_db)):
db: Session = Depends(get_db)):
return blog_service.query_blog_by_condition(db, query) return blog_service.query_blog_by_condition(db, query)
@router.get("/content/{blog_id}", summary="查询博客内容", response_model=BlogResponse) @router.get("/condition/page", summary="条件分页查询博客", response_model=PageResult[BlogResponse])
def query_blog_by_id(blog_id: int, db: Session = Depends(get_db)): def query_blog_by_condition_page(
query: BlogQuery = Depends(),
current_page: int = Query(1, ge=1, alias="currentPage", description="当前页码从1开始"),
page_size: int = Query(10, ge=1, le=100, alias="pageSize", description="每页显示数量1-100之间"),
db: Session = Depends(get_db)
):
return blog_service.query_blog_by_condition_page(db, query, current_page, page_size)
@router.get("/unapproved", summary="查询待发布博客", response_model=List[BlogResponse])
def query_unapproved_blog(db: Session = Depends(get_db), _=Depends(verify_token)):
return blog_service.query_unapproved_blog(db)
@router.get("/{blog_id}/content", summary="查询博客内容", response_model=BlogResponse)
def query_blog_by_id(blog_id: int = Path(..., ge=1, description="博客ID"), db: Session = Depends(get_db)):
return blog_service.query_blog_by_id(db, blog_id) return blog_service.query_blog_by_id(db, blog_id)
@router.get("/all/page", summary="分页查询博客", response_model=PageResult[BlogResponse])
def query_blog_by_page(
current_page: int = Query(1, ge=1, alias="currentPage", description="当前页码从1开始"),
page_size: int = Query(10, ge=1, le=100, alias="pageSize", description="每页显示数量1-100之间"),
db: Session = Depends(get_db),
_=Depends(verify_token)
):
return blog_service.query_blog_by_page(db, current_page, page_size, True)
@router.post("", summary="新增博客内容", response_model=bool) @router.post("", summary="新增博客内容", response_model=bool)
def add_blog(blog: BlogCreate, db: Session = Depends(get_db), _ = Depends(verify_token)): def add_blog(
blog: BlogCreate = Body(..., description="博客创建数据"),
db: Session = Depends(get_db),
_=Depends(verify_token)
):
return blog_service.add_blog(db, blog) return blog_service.add_blog(db, blog)
@router.put("/{blog_id}", summary="更新博客内容", response_model=bool) @router.put("/{blog_id}", summary="更新博客内容", response_model=bool)
def update_blog(blog_id: int, blog: BlogUpdate, db: Session = Depends(get_db), _ = Depends(verify_token)): def update_blog(
blog_id: int = Path(..., ge=1, description="博客ID"),
blog: BlogUpdate = Body(..., description="博客更新数据"),
db: Session = Depends(get_db),
_=Depends(verify_token)
):
return blog_service.update_blog(db, blog_id, blog) return blog_service.update_blog(db, blog_id, blog)
@router.delete("/{blog_id}", summary="删除博客内容", response_model=bool) @router.delete("/{blog_id}", summary="删除博客内容", response_model=bool)
def delete_blog(blog_id: int, db: Session = Depends(get_db), _ = Depends(verify_token)): def delete_blog(
blog_id: int = Path(..., ge=1, description="博客ID"),
db: Session = Depends(get_db),
_=Depends(verify_token)
):
return blog_service.delete_blog(db, blog_id) return blog_service.delete_blog(db, blog_id)
@router.get("/{blog_id}/adjacent", summary="查询相邻博客", response_model=List[BlogAdjacentResponse])
def query_blog_latest(blog_id: int = Path(..., ge=1, description="博客ID"), db: Session = Depends(get_db)):
return blog_service.query_blog_adjacent(db, blog_id)
@router.put("/{blog_id}/comment", summary="新增博客评论", response_model=bool)
def add_blog_comment(
request: Request,
blog_id: int = Path(..., ge=1, description="博客ID"),
blog_comment: BlogCommentCreate = Body(..., description="博客评论数据"),
db: Session = Depends(get_db)
):
return blog_service.add_blog_comment(db, request, blog_id, blog_comment)
@router.get("/{blog_id}/comment", summary="查询博客评论", response_model=list[BlogCommentResponse])
def query_blog_comment(blog_id: int = Path(..., ge=1, description="博客ID"), db: Session = Depends(get_db)):
return blog_service.query_blog_comment(db, blog_id)
@router.get("/category", summary="查询博客分类", response_model=List[BlogCategoryResponse]) @router.get("/category", summary="查询博客分类", response_model=List[BlogCategoryResponse])
def query_blog_category(db: Session = Depends(get_db)): def query_blog_category(db: Session = Depends(get_db)):
return blog_service.query_blog_category(db) return blog_service.query_blog_category(db)
@@ -61,22 +138,14 @@ def query_blog_stats(db: Session = Depends(get_db)):
@router.get("/visit", summary="查询博客访问信息", response_model=PageResult[BlogVisitResponse]) @router.get("/visit", summary="查询博客访问信息", response_model=PageResult[BlogVisitResponse])
def query_blog_visit(current_page: int = Query(1, ge=1, alias="currentPage", description="当前页码"), def query_blog_visit(
page_size: int = Query(10, ge=1, le=100, alias="pageSize", description="每页数量"), current_page: int = Query(1, ge=1, alias="currentPage", description="当前页码从1开始"),
db: Session = Depends(get_db)): page_size: int = Query(10, ge=1, le=100, alias="pageSize", description="每页显示数量1-100之间"),
db: Session = Depends(get_db)
):
return blog_service.query_blog_visit(db, current_page, page_size) return blog_service.query_blog_visit(db, current_page, page_size)
@router.get("/latest", summary="查询近期博客", response_model=list[BlogLatestResponse]) @router.get("/latest", summary="查询近期博客", response_model=list[BlogLatestResponse])
def query_blog_latest(db: Session = Depends(get_db)): def query_blog_latest(db: Session = Depends(get_db)):
return blog_service.query_blog_latest(db) return blog_service.query_blog_latest(db)
@router.put("/comment", summary="新增博客评论", response_model=bool)
def add_blog_comment(blog_comment: BlogCommentCreate, request: Request, db: Session = Depends(get_db)):
return blog_service.add_blog_comment(db, request, blog_comment)
@router.get("/comment/{blog_id}", summary="查询博客评论", response_model=list[BlogCommentResponse])
def query_blog_comment(blog_id: int, db: Session = Depends(get_db)):
return blog_service.query_blog_comment(db, blog_id)

51
api/blog_stats.py Normal file
View File

@@ -0,0 +1,51 @@
from typing import List
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from schemas.blog_stats import BlogOverview, BlogChartStats
from config.database import get_db
from service import blog_stats_service
router = APIRouter(
prefix="/stats",
tags=["博客统计"],
responses={404: {"description": "Not found"}}
)
@router.get("/overview", summary="统计数据", response_model=BlogOverview)
def query_blog_overview(db: Session = Depends(get_db)):
return blog_stats_service.query_blog_overview(db)
@router.get("/category", summary="博客分类统计", response_model=List[BlogChartStats])
def query_blog_category(db: Session = Depends(get_db)):
return blog_stats_service.query_blog_category(db)
@router.get("/approved/monthly", summary="每月博客发布统计", response_model=List[BlogChartStats])
def query_blog_monthly(year: int, db: Session = Depends(get_db)):
return blog_stats_service.query_blog_approved_monthly(db, year)
@router.get("/visit/monthly", summary="每月博客访问统计", response_model=List[BlogChartStats])
def query_blog_visit_monthly(year: int, db: Session = Depends(get_db)):
return blog_stats_service.query_blog_visit_monthly(db, year)
@router.get("/visit/rank", summary="博客访问数量排行", response_model=List[BlogChartStats])
def query_blog_visit_rank(db: Session = Depends(get_db)):
return blog_stats_service.query_blog_visit_rank(db)
@router.get("/read/rank", summary="博客阅读时长排行", response_model=List[BlogChartStats])
def query_blog_read_rank(db: Session = Depends(get_db)):
return blog_stats_service.query_blog_read_rank(db)
@router.get("/comment/rank", summary="博客评论数量排行", response_model=List[BlogChartStats])
def query_blog_comment_rank(db: Session = Depends(get_db)):
return blog_stats_service.query_blog_comment_rank(db)
# 分类统计饼图

157
blog.sql Normal file
View File

@@ -0,0 +1,157 @@
-- MySQL dump 10.13 Distrib 8.0.26, for Win64 (x86_64)
--
-- Host: localhost Database: blog
-- ------------------------------------------------------
-- Server version 8.0.27
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `blog`
--
DROP TABLE IF EXISTS `blog`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blog` (
`id` bigint NOT NULL,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '博客标题',
`top_value` int NOT NULL COMMENT '置顶值 越大越靠前',
`is_great` tinyint NOT NULL COMMENT '是否是精品 0否1是',
`category_id` bigint NOT NULL COMMENT '博客类别',
`summary` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '博客内容概要',
`content_id` bigint NOT NULL COMMENT '博客内容',
`word_count` int NOT NULL COMMENT '字数统计',
`read_duration` decimal(10,2) NOT NULL COMMENT '阅读时长',
`is_approved` tinyint NOT NULL COMMENT '是否发布',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '发布时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '发布人',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='博客记录';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `blog_category`
--
DROP TABLE IF EXISTS `blog_category`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blog_category` (
`id` bigint NOT NULL,
`name` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '类别名称',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='博客类别';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `blog_comment`
--
DROP TABLE IF EXISTS `blog_comment`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blog_comment` (
`id` bigint NOT NULL COMMENT 'id',
`blog_id` bigint NOT NULL COMMENT '博客id',
`parent_id` bigint NOT NULL COMMENT '父评论id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '评论人昵称',
`website` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '评论人网站',
`ip_address` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '评论人ip',
`user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '评论人浏览器信息',
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '评论内容',
`is_approved` tinyint NOT NULL COMMENT '是否通过',
`create_time` datetime DEFAULT NULL COMMENT '评论时间',
`create_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='博客评论';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `blog_content`
--
DROP TABLE IF EXISTS `blog_content`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blog_content` (
`id` bigint NOT NULL,
`content` mediumblob NOT NULL COMMENT '博客内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='博客内容';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `blog_like`
--
DROP TABLE IF EXISTS `blog_like`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blog_like` (
`id` bigint NOT NULL COMMENT 'id',
`user_id` bigint DEFAULT NULL COMMENT '点赞人',
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '点赞类型(博客、评论)',
`target_id` bigint DEFAULT NULL COMMENT '点赞目标的id',
`status` tinyint DEFAULT NULL COMMENT '点赞状态 0取消点赞/1点赞',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='博客点赞';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `blog_visit`
--
DROP TABLE IF EXISTS `blog_visit`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blog_visit` (
`id` bigint NOT NULL,
`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ip地址',
`os` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作系统',
`browser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '浏览器',
`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '路径',
`blog_id` bigint DEFAULT NULL COMMENT '博客id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='博客访问记录';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping routines for database 'blog'
--
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2025-10-23 19:56:43

View File

@@ -22,7 +22,7 @@ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="session") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="session")
# 请求上下文 # 请求上下文
context_sub: ContextVar[str] = ContextVar('sub') context_sub: ContextVar[str] = ContextVar('sub', default='')
# 获取数据库 加密密码 # 获取数据库 加密密码

View File

@@ -4,9 +4,9 @@ from sqlalchemy.orm import sessionmaker
from config.setting import settings from config.setting import settings
DATABASE_URL = f"mysql+pymysql://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}" DATABASE_URL = f"mysql+pymysql://root:{settings.DB_PASSWORD}@{settings.DB_HOST}:3306/blog"
engine = create_engine(url=DATABASE_URL) engine = create_engine(url=DATABASE_URL, pool_pre_ping=True, pool_recycle=3600)
# 会话工厂 # 会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

View File

@@ -1,16 +1,20 @@
from elasticsearch import Elasticsearch from elasticsearch import Elasticsearch
from config.setting import settings
from schemas.blog_elastic import BLOG_MAPPING from schemas.blog_elastic import BLOG_MAPPING
from config.logging import logger from config.logging import logger
ES_HOSTS = "http://14.103.235.151:9200" ES_HOST = f"http://{settings.ES_HOST}:9200"
ES_USER = "elastic" ES_USER = "elastic"
ES_PASSWORD = "19940822Cxx" ES_PASSWORD = "19940822Cxx"
BLOG_INDEX = "blog" BLOG_INDEX = "blog"
# 注意Python的elasticsearch的版本要和服务器的一致 # 注意Python的elasticsearch的版本要和服务器的一致
es = Elasticsearch(ES_HOSTS, http_auth=(ES_USER, ES_PASSWORD)) try:
es = Elasticsearch(ES_HOST, http_auth=(ES_USER, ES_PASSWORD))
if not es.indices.exists(index=BLOG_INDEX): if not es.indices.exists(index=BLOG_INDEX):
es.indices.create(index=BLOG_INDEX, body=BLOG_MAPPING) es.indices.create(index=BLOG_INDEX, body=BLOG_MAPPING)
logger.info(f"Elastic索引 {BLOG_INDEX} 创建成功") logger.info(f"Elastic索引 {BLOG_INDEX} 创建成功")
except Exception as e:
logger.error(f"elastic异常: {str(e)}")

View File

@@ -1,11 +1,14 @@
import atexit
import sys
from fluent import sender
from loguru import logger from loguru import logger
from pathlib import Path
from config.setting import settings from config.setting import settings
# 日志目录 FLUENTD_HOST = settings.FLUENTD_HOST
LOG_DIR = Path(__file__).parent.parent.parent / "logs" FLUENTD_PORT = 24224
LOG_DIR.mkdir(exist_ok=True) TOPIC_TAG = 'blog-service'
# 日志级别 # 日志级别
LOG_LEVEL = settings.LOG_LEVEL.upper() LOG_LEVEL = settings.LOG_LEVEL.upper()
@@ -24,42 +27,64 @@ FILE_FORMAT = (
"{name}:{function}:{line} - {message}" "{name}:{function}:{line} - {message}"
) )
fluent_sender = sender.FluentSender(
tag=TOPIC_TAG,
host=FLUENTD_HOST,
port=FLUENTD_PORT,
buffer_max_size=8 * 1024 * 1024,
timeout=3.0,
retry_timeout=60
)
def log_to_fluent(message):
try:
record = message.record
# 构建结构化日志数据
log_data = {
'topic': TOPIC_TAG,
'timestamp': record['time'].timestamp(),
'level': record['level'].name.lower(),
'message': record['message'],
'source': f"{record['file'].path}:{record['line']}",
'module': record['module'],
'function': record['function'],
'process_id': record['process'].id,
'thread_id': record['thread'].id,
**record['extra']
}
if not fluent_sender.emit(TOPIC_TAG, log_data):
print(f"Fluentd 发送失败: {fluent_sender.last_error}")
except Exception as e:
print(f"日志处理异常: {str(e)}")
# 移除默认处理器 # 移除默认处理器
logger.remove() logger.remove()
# 添加控制台处理器 # 添加控制台处理器
logger.add( logger.add(
sink="sys.stdout", sink=sys.stdout,
level=LOG_LEVEL, level=LOG_LEVEL,
format=STDOUT_FORMAT, format=STDOUT_FORMAT,
colorize=True, colorize=True,
backtrace=True, # 显示完整异常堆栈 backtrace=True, # 显示完整异常堆栈
diagnose=True, # 显示详细异常信息 diagnose=True, # 显示详细异常信息
) )
# 添加文件处理器 - 常规日志 if settings.ENVIRONMENT == 'docker':
logger.add( logger.add(
sink=LOG_DIR / "app.log", log_to_fluent,
level="INFO", level=LOG_LEVEL, # 处理 INFO 及以上级别
format=FILE_FORMAT, format="{message}", # 原始消息(实际使用结构化数据)
rotation="10 MB", # 日志文件大小达到10MB时自动分割 backtrace=True, # 启用堆栈回溯
retention="7 days", # 保留7天的日志 diagnose=True # 显示诊断信息
compression="zip", # 归档时压缩为zip )
enqueue=True, # 异步写入
serialize=False, # 不使用JSON格式
)
# 添加文件处理器 - 错误日志 atexit.register(fluent_sender.close)
logger.add(
sink=LOG_DIR / "error.log",
level="ERROR",
format=FILE_FORMAT,
rotation="10 MB",
retention="30 days",
compression="zip",
enqueue=True,
serialize=False,
)
# 导出配置好的logger # 导出配置好的logger
__all__ = ["logger"] __all__ = ["logger"]

15
config/rustfs.py Normal file
View File

@@ -0,0 +1,15 @@
import boto3
from botocore.client import Config
from config.setting import settings
access_key = 'jRxroVX8PUuSOia71qE4'
secret_access = 'RZE82VATN1Gqj9xy3d5OzFvHoDBgwJ4PSf7IKuei'
s3 = boto3.client('s3',
endpoint_url=f'http://{settings.RUSTFS_HOST}:{settings.RUSTFS_PORT}',
aws_access_key_id=access_key,
aws_secret_access_key=secret_access,
config=Config(signature_version='s3v4'),
region_name='cn-east-1'
)

View File

@@ -2,13 +2,14 @@ from pydantic_settings import BaseSettings
class Settings(BaseSettings): class Settings(BaseSettings):
DB_HOST: str ENVIRONMENT: str
DB_PORT: int
DB_USER: str
DB_PASSWORD: str
DB_NAME: str
LOG_LEVEL: str LOG_LEVEL: str
DB_HOST: str
DB_PASSWORD: str
ES_HOST: str
FLUENTD_HOST: str
RUSTFS_HOST:str
RUSTFS_PORT: str
class Config: class Config:
env_file = ".env" # 指定.env文件路径 env_file = ".env" # 指定.env文件路径

View File

@@ -9,8 +9,9 @@ from middleware.exceptions import AppException
async def blog_visit_middleware(request: Request, call_next): async def blog_visit_middleware(request: Request, call_next):
# 检查路径是否匹配 /blog/content/{blog_id} # 检查路径是否匹配
if match := re.match(r"^/blog/content/(\d+)$", request.url.path): if match := re.match(r"^/blog/(\d+)/content$", request.url.path):
# 获取客户端信息 # 获取客户端信息
ip = get_client_ip(request) ip = get_client_ip(request)
user_agent = request.headers.get("User-Agent", "") user_agent = request.headers.get("User-Agent", "")

View File

@@ -35,13 +35,13 @@ async def global_exception_handler(request: Request, call_next):
except SQLAlchemyError as e: except SQLAlchemyError as e:
# 记录数据库异常 # 记录数据库异常
logger.critical(f"数据库异常: {str(e)}", exc_info=True) logger.critical(f"数据库异常: {str(e)}")
return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"message": "数据库操作失败", "details": str(e)}) content={"message": "数据库操作失败", "details": str(e)})
except Exception as e: except Exception as e:
# 记录未知异常(带堆栈信息) # 记录未知异常(带堆栈信息)
logger.critical(f"未知异常: {str(e)}", exc_info=True) logger.critical(f"未知异常: {str(e)}")
return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"code": 500, "message": "服务器内部错误", "details": str(e)}) content={"code": 500, "message": "服务器内部错误", "details": str(e)})

View File

@@ -1,99 +1,129 @@
from sqlalchemy import ( from sqlalchemy import (BigInteger, Boolean, Column, Integer, DECIMAL, String, LargeBinary)
BigInteger,
Boolean,
Column,
Integer,
DECIMAL,
SMALLINT,
String,
LargeBinary
)
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from models.base import AuditBase, IdBase from models.base import AuditBase, IdBase
class Blog(AuditBase): class Blog(AuditBase):
title = Column(String(255), nullable=True, comment="博客标题") title = Column(String(255), nullable=False, comment="博客标题")
top_value = Column(Integer, nullable=True, comment="置顶值 越大越靠前") top_value = Column(Integer, nullable=False, default=0, comment="置顶值 越大越靠前")
is_great = Column(Boolean, nullable=True, comment="是否是精品 0否1是") is_great = Column(Boolean, nullable=False, default=False, comment="是否是精品")
category_id = Column(BigInteger, nullable=True, comment="博客类别") category_id = Column(BigInteger, nullable=False, comment="博客类别")
summary = Column(String(255), nullable=True, comment="博客内容概要") summary = Column(String(255), nullable=False, comment="博客内容概要")
content_id = Column(BigInteger, nullable=True, comment="博客内容") content_id = Column(BigInteger, nullable=False, comment="博客内容")
word_count = Column(Integer, nullable=True, comment="字数统计") word_count = Column(Integer, nullable=False, default=0, comment="字数统计")
read_duration = Column(DECIMAL(10, 2), nullable=True, comment="阅读时长") read_duration = Column(DECIMAL(10, 2), nullable=False, default=0.00, comment="阅读时长")
is_approved = Column(Boolean, nullable=False, default=False, comment="是否发布")
# 分类关系(多对一)
category = relationship( category = relationship(
"BlogCategory", "BlogCategory",
# 与BlogCategory的blogs属性建立双向关系
back_populates="blogs", back_populates="blogs",
# 只级联保存和合并操作,不级联删除(删除博客不应删除分类
cascade="save-update, merge",
# 明确指定连接条件
# 如果数据库设置了外键可以省略
primaryjoin="foreign(Blog.category_id) == BlogCategory.id" primaryjoin="foreign(Blog.category_id) == BlogCategory.id"
) )
# 内容关系(一对一)
content = relationship( content = relationship(
"BlogContent", "BlogContent",
# 与BlogContent的blog属性建立双向关系
back_populates="blog", back_populates="blog",
# 完全级联操作:保存、合并、刷新、删除等所有操作都会级联
cascade="all, delete-orphan",
# 设置为False表示一对一关系返回单个对象而不是列表
uselist=False,
# 确保内容只有一个父博客与delete-orphan配合使用
single_parent=True,
# 明确指定连接条件
primaryjoin="foreign(Blog.content_id) == BlogContent.id" primaryjoin="foreign(Blog.content_id) == BlogContent.id"
) )
# 访问记录(一对多)
visits = relationship( visits = relationship(
"BlogVisit", "BlogVisit",
# 与BlogVisit的blog属性建立双向关系
back_populates="blog", back_populates="blog",
# 完全级联操作:博客删除时自动删除所有访问记录
cascade="all, delete-orphan",
# 明确指定连接条件
primaryjoin="Blog.id == foreign(BlogVisit.blog_id)" primaryjoin="Blog.id == foreign(BlogVisit.blog_id)"
) )
# 4. 评论(一对多)
comments = relationship( comments = relationship(
"BlogComment", "BlogComment",
# 与BlogComment的blog属性建立双向关系
back_populates="blog", back_populates="blog",
# 完全级联操作:博客删除时自动删除所有评论
cascade="all, delete-orphan",
# 明确指定连接条件
primaryjoin="Blog.id == foreign(BlogComment.blog_id)" primaryjoin="Blog.id == foreign(BlogComment.blog_id)"
) )
class BlogCategory(AuditBase): class BlogCategory(AuditBase):
name = Column(String(45), nullable=True, comment="类别名称") name = Column(String(45), nullable=False, comment="类别名称")
blogs = relationship( blogs = relationship(
"Blog", "Blog",
# 与Blog的category属性建立双向关系
back_populates="category", back_populates="category",
# 完全级联操作:分类删除时自动删除所有关联的博客
# 警告:这会级联删除分类下的所有博客,包括博客的内容、访问记录和评论
cascade="all, delete-orphan",
# 明确指定连接条件
primaryjoin="BlogCategory.id == foreign(Blog.category_id)" primaryjoin="BlogCategory.id == foreign(Blog.category_id)"
) )
class BlogContent(IdBase): class BlogContent(IdBase):
content = Column(LargeBinary, comment="博客内容") content = Column(LargeBinary, nullable=False, comment="博客内容")
blog = relationship( blog = relationship(
"Blog", "Blog",
# 与Blog的content属性建立双向关系
back_populates="content", back_populates="content",
# 设置为False表示一对一关系
uselist=False,
# 明确指定连接条件
primaryjoin="BlogContent.id == foreign(Blog.content_id)" primaryjoin="BlogContent.id == foreign(Blog.content_id)"
) )
class BlogVisit(AuditBase): class BlogVisit(AuditBase):
ip = Column(String(255), nullable=True, comment="ip地址") ip = Column(String(255), nullable=False, comment="IP地址")
os = Column(String(255), nullable=True, comment="操作系统") os = Column(String(255), nullable=False, comment="操作系统")
browser = Column(String(255), nullable=True, comment="浏览器") browser = Column(String(255), nullable=False, comment="浏览器")
uri = Column(String(255), nullable=True, comment="路径") uri = Column(String(255), nullable=False, comment="路径")
blog_id = Column(BigInteger, nullable=True, comment="博客id") blog_id = Column(BigInteger, nullable=True, comment="博客ID")
blog = relationship( blog = relationship(
"Blog", "Blog",
# 与Blog的visits属性建立双向关系
back_populates="visits", back_populates="visits",
# 明确指定连接条件
primaryjoin="foreign(BlogVisit.blog_id) == Blog.id" primaryjoin="foreign(BlogVisit.blog_id) == Blog.id"
) )
class BlogComment(AuditBase): class BlogComment(AuditBase):
blog_id = Column(BigInteger, comment="博客id") blog_id = Column(BigInteger, nullable=False, comment="博客ID")
parent_id = Column(BigInteger, comment="父评论id") parent_id = Column(BigInteger, nullable=False, comment="父评论ID")
name = Column(String(255), comment="评论人昵称") name = Column(String(255), nullable=False, comment="评论人昵称")
website = Column(String(255), nullable=True, comment="评论人网站") website = Column(String(255), nullable=True, comment="评论人网站")
ip_address = Column(String(45), nullable=True, comment="评论人ip") ip_address = Column(String(45), nullable=False, comment="评论人IP")
user_agent = Column(String(255), nullable=True, comment="评论人浏览器信息") user_agent = Column(String(255), nullable=False, comment="评论人浏览器信息")
content = Column(String(512), comment="评论内容") content = Column(String(255), nullable=False, comment="评论内容")
is_approved = Column(SMALLINT, comment="是否通过") is_approved = Column(Boolean, nullable=False, default=False, comment="是否通过")
blog = relationship( blog = relationship(
"Blog", "Blog",
# 与Blog的comments属性建立双向关系
back_populates="comments", back_populates="comments",
# 明确指定连接条件
primaryjoin="foreign(BlogComment.blog_id) == Blog.id" primaryjoin="foreign(BlogComment.blog_id) == Blog.id"
) )

View File

@@ -1 +1,15 @@
fastapi~=0.115.12 fastapi~=0.115.12
sqlalchemy~=2.0.41
pydantic~=2.11.4
PyJWT~=2.10.1
passlib~=1.7.4
loguru~=0.7.3
pydantic-settings~=2.9.1
pymysql~=1.1.1
python-multipart~=0.0.20
uvicorn~=0.23.0
elasticsearch~=8.12.0
fluent-logger~=0.10.0
boto3~=1.40.59
botocore~=1.40.59
# pip download -r requirements.txt -d ./packages --only-binary=:all: --platform manylinux2014_x86_64 -i https://pypi.tuna.tsinghua.edu.cn/simple

View File

@@ -1,24 +1,42 @@
from typing import Optional from typing import Optional
from pydantic import BaseModel, Field from pydantic import BaseModel, Field, field_validator, ConfigDict
from datetime import datetime from datetime import datetime
class BlogQuery(BaseModel): class BlogQuery(BaseModel):
# 类别 """博客查询参数"""
category: Optional[str] = None category: Optional[str] = Field(None, description="分类名称")
# 标题 title: Optional[str] = Field(None, description="标题关键词")
title: Optional[str] = None year: Optional[int] = Field(None, description="发布年份", ge=2000, le=datetime.now().year)
# 年份
year: Optional[int] = None @field_validator('year')
def validate_year(cls, v):
if v is not None and v > datetime.now().year:
raise ValueError('年份不能超过当前年份')
return v
class BlogBase(BaseModel): class BlogBase(BaseModel):
title: str """博客基础模型"""
topValue: int title: str = Field(..., min_length=1, max_length=255, description="博客标题")
isGreat: bool top_value: int = Field(default=0, ge=0, description="置顶值,越大越靠前", alias="topValue")
category: str is_great: bool = Field(default=False, description="是否是精品", alias="isGreat")
content: Optional[str] = None category: str = Field(..., min_length=1, max_length=45, description="分类名称")
content: Optional[str] = Field(None, description="博客内容")
is_approved: bool = Field(default=False, description="是否已发布", alias="isApproved")
@field_validator('title')
def title_not_empty(cls, v):
if not v or not v.strip():
raise ValueError('标题不能为空')
return v.strip()
@field_validator('category')
def category_not_empty(cls, v):
if not v or not v.strip():
raise ValueError('分类不能为空')
return v.strip()
class BlogCreate(BlogBase): class BlogCreate(BlogBase):
@@ -30,76 +48,105 @@ class BlogUpdate(BlogBase):
class BlogResponse(BlogBase): class BlogResponse(BlogBase):
id: Optional[int] = None id: int = Field(..., description="博客ID")
summary: Optional[str] = None summary: Optional[str] = Field(None, description="内容摘要")
wordCount: Optional[int] = None word_count: Optional[int] = Field(None, description="字数统计", alias="wordCount")
readDuration: Optional[float] = None read_duration: Optional[float] = Field(None, description="阅读时长", alias="readDuration")
visitCount: Optional[int] = None visit_count: Optional[int] = Field(0, description="访问次数", alias="visitCount")
createTime: Optional[datetime] = None create_time: datetime = Field(..., description="创建时间", alias="createTime")
updateTime: Optional[datetime] = None update_time: datetime = Field(..., description="更新时间", alias="updateTime")
class Config: model_config = ConfigDict(
from_attributes = True from_attributes=True,
json_encoders = { populate_by_name=True,
# 自定义 datetime 类型的序列化格式 json_encoders={
datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S') datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S')
} }
)
class BlogCategoryResponse(BaseModel): class BlogCategoryResponse(BaseModel):
name: str name: str = Field(..., description="分类名称")
count: int count: int = Field(..., description="博客数量")
class Config: model_config = ConfigDict(
from_attributes = True from_attributes=True,
populate_by_name=True
)
class BlogStatsResponse(BaseModel): class BlogStatsResponse(BaseModel):
blogCount: int blog_count: int = Field(..., description="博客总数", alias="blogCount")
categoryCount: int category_count: int = Field(..., description="分类总数", alias="categoryCount")
wordCount: int word_count: int = Field(..., description="总字数", alias="wordCount")
class BlogVisitResponse(BaseModel): class BlogVisitResponse(BaseModel):
ip: str ip: str = Field(..., description="IP地址")
os: str os: str = Field(..., description="操作系统")
browser: str browser: str = Field(..., description="浏览器")
uri: str uri: str = Field(..., description="访问路径")
title: Optional[str] = None title: str = Field(None, description="博客标题")
visitTime: datetime visit_time: datetime = Field(..., description="访问时间", alias="visitTime")
class Config: model_config = ConfigDict(
from_attributes = True from_attributes=True,
json_encoders = { populate_by_name=True,
json_encoders={
datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S') datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S')
} }
)
class BlogLatestResponse(BaseModel): class BlogLatestResponse(BaseModel):
id: int id: int = Field(..., description="博客ID")
title: str title: str = Field(..., description="博客标题")
class Config: model_config = ConfigDict(
from_attributes = True from_attributes=True,
populate_by_name=True
)
class BlogAdjacentResponse(BaseModel):
id: int = Field(..., description="博客ID")
title: str = Field(..., description="博客标题")
model_config = ConfigDict(
from_attributes=True,
populate_by_name=True
)
class BlogCommentCreate(BaseModel): class BlogCommentCreate(BaseModel):
blogId: int parent_id: int = Field(default=0, ge=0, description="父评论ID0表示顶级评论", alias="parentId")
parentId: int name: str = Field(..., min_length=1, max_length=50, description="评论人昵称")
name: str website: Optional[str] = Field(None, description="评论人网站")
website: Optional[str] = None content: str = Field(..., min_length=1, max_length=1000, description="评论内容")
content: str
@field_validator('name')
def name_not_empty(cls, v):
if not v or not v.strip():
raise ValueError('昵称不能为空')
return v.strip()
@field_validator('content')
def content_not_empty(cls, v):
if not v or not v.strip():
raise ValueError('评论内容不能为空')
return v.strip()
class BlogCommentResponse(BlogCommentCreate): class BlogCommentResponse(BlogCommentCreate):
id: int id: int = Field(..., description="评论ID")
ipAddress: str ip_address: str = Field(..., description="IP地址", alias="ipAddress")
userAgent: str user_agent: str = Field(..., description="浏览器信息", alias="userAgent")
createTime: datetime create_time: datetime = Field(..., description="创建时间", alias="createTime")
class Config: model_config = ConfigDict(
from_attributes = True from_attributes=True,
json_encoders = { populate_by_name=True,
# 自定义 datetime 类型的序列化格式 json_encoders={
datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S') datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S')
} }
)

View File

@@ -9,9 +9,23 @@ class BlogElastic(BaseModel):
title: str title: str
content: str content: str
category: str category: str
isApproved: int
createTime: Optional[Union[datetime, str]] = None createTime: Optional[Union[datetime, str]] = None
updateTime: Optional[Union[datetime, str]] = None updateTime: Optional[Union[datetime, str]] = None
class Config:
from_attributes = True
class BlogSearch(BaseModel):
id: int
title: str
content: str
highlight: str
class Config:
from_attributes = True
BLOG_MAPPING = { BLOG_MAPPING = {
"mappings": { "mappings": {

16
schemas/blog_stats.py Normal file
View File

@@ -0,0 +1,16 @@
from pydantic import BaseModel, ConfigDict
class BlogOverview(BaseModel):
blogCount: int
categoryCount: int
wordCount: int
greatCount: int
visitCount: int
class BlogChartStats(BaseModel):
name: str
value: int
model_config = ConfigDict(from_attributes=True)

View File

@@ -1,10 +1,68 @@
from datetime import datetime from datetime import datetime
from typing import List
from config.logging import logger
from elasticsearch.helpers import bulk
from sqlalchemy import select
from sqlalchemy.orm import Session
from config.elastic import es, BLOG_INDEX from config.elastic import es, BLOG_INDEX
from schemas.blog_elastic import BlogElastic from config.setting import settings
from models.blog import Blog, BlogCategory, BlogContent
from schemas.blog_elastic import BlogElastic, BlogSearch
def sync_all_blog(db: Session) -> bool:
if settings.ENVIRONMENT == 'dev':
return True
stmt = select(
Blog.id,
Blog.title,
BlogCategory.name.label("category"),
BlogContent.content.label("content"),
Blog.is_approved.label("isApproved"),
Blog.create_time.label("createTime"),
Blog.update_time.label("updateTime"),
).select_from(Blog).outerjoin(
BlogCategory, Blog.category_id == BlogCategory.id
).outerjoin(
BlogContent, Blog.content_id == BlogContent.id
)
results = db.execute(stmt).fetchall()
actions = [
{
"_op_type": "index", # 如果存在则更新,不存在则创建
"_index": BLOG_INDEX,
"_id": blog.id,
"_source": {
"title": blog.title,
"category": blog.category,
"content": blog.content.decode("utf-8"),
"isApproved": blog.isApproved,
"createTime": blog.createTime.strftime("%Y-%m-%d %H:%M:%S"),
"updateTime": blog.updateTime.strftime("%Y-%m-%d %H:%M:%S")
}
}
for blog in results
]
success_count, errors = bulk(es, actions=actions)
if errors:
logger.error(f"部分文档同步失败,成功同步 {success_count}")
return False
logger.info(f"成功同步 {success_count} 条博客到 Elasticsearch")
return True
def add_blog_elastic(blog: BlogElastic) -> bool: def add_blog_elastic(blog: BlogElastic) -> bool:
if settings.ENVIRONMENT == 'dev':
return True
now = datetime.now() now = datetime.now()
blog.createTime = now.strftime("%Y-%m-%d %H:%M:%S") blog.createTime = now.strftime("%Y-%m-%d %H:%M:%S")
blog.updateTime = now.strftime("%Y-%m-%d %H:%M:%S") blog.updateTime = now.strftime("%Y-%m-%d %H:%M:%S")
@@ -15,10 +73,14 @@ def add_blog_elastic(blog: BlogElastic) -> bool:
body=blog.model_dump() body=blog.model_dump()
) )
logger.info(f"成功新增博客 {blog.id} 到 Elasticsearch")
return True return True
def update_blog_elastic(blog: BlogElastic) -> bool: def update_blog_elastic(blog: BlogElastic) -> bool:
if settings.ENVIRONMENT == 'dev':
return True
now = datetime.now() now = datetime.now()
blog.updateTime = now.strftime("%Y-%m-%d %H:%M:%S") blog.updateTime = now.strftime("%Y-%m-%d %H:%M:%S")
@@ -28,10 +90,80 @@ def update_blog_elastic(blog: BlogElastic) -> bool:
body=blog.model_dump() body=blog.model_dump()
) )
logger.info(f"成功修改博客 {blog.id} 到 Elasticsearch")
return True return True
def delete_blog(blog_id: int) -> bool: def delete_blog(blog_id: int) -> bool:
if settings.ENVIRONMENT == 'dev':
return True
es.delete(index=BLOG_INDEX, id=blog_id) es.delete(index=BLOG_INDEX, id=blog_id)
logger.info(f"成功删除博客 {blog_id} 到 Elasticsearch")
return True return True
def search_blog(keyword: str) -> List[BlogSearch]:
# 构造查询请求
body = {
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": keyword,
"fields": ["title^3", "content"],
"type": "phrase"
}
}
],
"filter": [
{
"term": {
"isApproved": 1
}
}
]
}
},
"highlight": {
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"],
"fields": {
"title": {},
"content": {
"fragment_size": 150,
"number_of_fragments": 2,
"no_match_size": 100
}
}
},
"_source": ["title", "content"]
}
response = es.search(index=BLOG_INDEX, body=body)
results = []
for hit in response["hits"]["hits"]:
highlight = hit.get("highlight", {})
# 合并所有高亮片段(标题+内容)
all_highlights = []
if "title" in highlight:
all_highlights.extend(highlight["title"])
if "content" in highlight:
all_highlights.extend(highlight["content"])
# 优先使用高亮内容,否则截取内容开头
displayed_content = " ".join(all_highlights) if all_highlights \
else hit["_source"].get("content", "")[:200] + "..."
results.append(BlogSearch(
id=hit["_id"],
title=hit["_source"]["title"],
content=displayed_content,
highlight=" | ".join(all_highlights) # 合并所有高亮片段
))
return results

View File

@@ -1,64 +1,74 @@
from typing import List from typing import List
from fastapi import Request from fastapi import Request
from sqlalchemy import select, func, desc, and_ from sqlalchemy import select, func, desc, and_, asc
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from models.blog import Blog, BlogCategory, BlogVisit, BlogContent, BlogComment from models.blog import Blog, BlogCategory, BlogVisit, BlogContent, BlogComment
from schemas.blog import BlogResponse, BlogQuery, BlogCategoryResponse, BlogStatsResponse, \ from schemas.blog import BlogResponse, BlogQuery, BlogCategoryResponse, BlogStatsResponse, \
BlogLatestResponse, BlogCommentCreate, BlogCommentResponse, BlogVisitResponse, BlogCreate, BlogUpdate BlogLatestResponse, BlogCommentCreate, BlogCommentResponse, BlogVisitResponse, BlogCreate, BlogUpdate, \
BlogAdjacentResponse
from schemas.blog_elastic import BlogElastic from schemas.blog_elastic import BlogElastic
from schemas.pagination import PageResult from schemas.pagination import PageResult
from schemas.paginate_query import paginate_query from schemas.paginate_query import paginate_query
from middleware.exceptions import AppException from middleware.exceptions import AppException
from service.blog_elastic_service import add_blog_elastic from service.blog_elastic_service import add_blog_elastic
from service.blog_stats_service import query_blog_overview
from utils.blog_utils import get_blog_summary, get_word_count, get_read_duration from utils.blog_utils import get_blog_summary, get_word_count, get_read_duration
def query_blog_by_page(db: Session, current_page: int = 1, page_size: int = 10) -> PageResult[BlogResponse]: def query_blog_by_page(db: Session, current_page: int = 1, page_size: int = 10,
query = select( is_all=False) -> PageResult[BlogResponse]:
stmt = (select(
Blog.id, Blog.id,
Blog.title, Blog.title,
Blog.top_value.label("topValue"), Blog.top_value,
Blog.is_great.label("isGreat"), Blog.is_great,
BlogCategory.name.label("category"), BlogCategory.name.label("category"),
Blog.summary, Blog.summary,
Blog.word_count.label("wordCount"), Blog.word_count,
Blog.read_duration.label("readDuration"), Blog.read_duration,
func.count(BlogVisit.id).label("visitCount"), func.count(BlogVisit.id).label("visitCount"),
Blog.create_time.label("createTime"), Blog.is_approved,
Blog.update_time.label("updateTime") Blog.create_time,
).select_from(Blog).outerjoin( Blog.update_time
BlogCategory, Blog.category_id == BlogCategory.id ).outerjoin(BlogCategory, Blog.category_id == BlogCategory.id)
).outerjoin( .outerjoin(BlogVisit, Blog.id == BlogVisit.blog_id)
BlogVisit, Blog.id == BlogVisit.blog_id .group_by(Blog.id, BlogCategory.name)
).group_by( .order_by(desc(Blog.is_great), desc(Blog.top_value), desc(Blog.update_time)))
Blog.id, BlogCategory.name
).order_by(
desc(Blog.top_value), desc(Blog.update_time)
)
return paginate_query(db, query, current_page, page_size) if not is_all:
stmt = stmt.where(Blog.is_approved == 1)
return paginate_query(db, stmt, current_page, page_size)
def query_blog_by_condition(db: Session, blog_query: BlogQuery) -> List[BlogResponse]: def query_blog_by_condition(db: Session, blog_query: BlogQuery) -> List[BlogResponse]:
query = select( stmt = get_query_blog_by_condition_stmt(blog_query)
results = db.execute(stmt).fetchall()
return [BlogResponse.model_validate(result) for result in results]
def get_query_blog_by_condition_stmt(blog_query: BlogQuery):
stmt = (select(
Blog.id, Blog.id,
Blog.title, Blog.title,
Blog.top_value.label("topValue"), Blog.top_value,
Blog.is_great.label("isGreat"), Blog.is_great,
BlogCategory.name.label("category"), BlogCategory.name.label("category"),
Blog.summary, Blog.summary,
BlogContent.content.label("content"), BlogContent.content,
Blog.word_count.label("wordCount"), Blog.word_count,
Blog.read_duration.label("readDuration"), Blog.read_duration,
Blog.create_time.label("createTime"), func.count(BlogVisit.id).label("visitCount"),
Blog.update_time.label("updateTime") Blog.is_approved,
).select_from(Blog).outerjoin( Blog.create_time,
BlogCategory, Blog.category_id == BlogCategory.id Blog.update_time
).outerjoin( ).where(Blog.is_approved == 1)
BlogContent, Blog.content_id == BlogContent.id .outerjoin(BlogCategory, Blog.category_id == BlogCategory.id)
) .outerjoin(BlogContent, Blog.content_id == BlogContent.id)
.outerjoin(BlogVisit, Blog.id == BlogVisit.blog_id)
.group_by(Blog.id))
conditions = [] conditions = []
@@ -72,42 +82,71 @@ def query_blog_by_condition(db: Session, blog_query: BlogQuery) -> List[BlogResp
conditions.append(func.extract('year', Blog.create_time) == blog_query.year) conditions.append(func.extract('year', Blog.create_time) == blog_query.year)
if conditions: if conditions:
query = query.where(and_(*conditions)) stmt = stmt.where(and_(*conditions))
query = query.order_by(desc(Blog.update_time)) stmt = stmt.order_by(desc(Blog.is_great), desc(Blog.update_time))
results = db.execute(query).fetchall() return stmt
return [BlogResponse.from_orm(result) for result in results]
def query_blog_by_condition_page(db: Session, blog_query: BlogQuery,
current_page: int = 1, page_size: int = 10) -> PageResult[BlogResponse]:
stmt = get_query_blog_by_condition_stmt(blog_query)
return paginate_query(db, stmt, current_page, page_size)
def query_unapproved_blog(db: Session) -> List[BlogResponse]:
stmt = ((select(
Blog.id,
Blog.title,
Blog.top_value,
Blog.is_great,
BlogCategory.name.label("category"),
Blog.summary,
BlogContent.content,
Blog.word_count,
Blog.read_duration,
func.count(BlogVisit.id),
Blog.is_approved,
Blog.create_time,
Blog.update_time
).where(Blog.is_approved == 0)
.outerjoin(BlogCategory, Blog.category_id == BlogCategory.id)
.outerjoin(BlogContent, Blog.content_id == BlogContent.id))
.outerjoin(BlogVisit, Blog.id == BlogVisit.blog_id)
.group_by(Blog.id)
.order_by(desc(Blog.update_time)))
results = db.execute(stmt).fetchall()
return [BlogResponse.model_validate(result) for result in results]
def query_blog_by_id(db: Session, blog_id: int) -> BlogResponse: def query_blog_by_id(db: Session, blog_id: int) -> BlogResponse:
query = db.query( check_blog_exist(db, blog_id)
Blog.id.label("id"),
Blog.title.label("title"), stmt = (select(
Blog.top_value.label("topValue"), Blog.id,
Blog.is_great.label("isGreat"), Blog.title,
Blog.top_value,
Blog.is_great,
BlogCategory.name.label("category"), BlogCategory.name.label("category"),
Blog.summary.label("summary"), Blog.summary,
BlogContent.content.label("content"), BlogContent.content,
Blog.word_count.label("wordCount"), Blog.word_count,
Blog.read_duration.label("readDuration"), Blog.read_duration,
func.count(BlogVisit.id).label("visitCount"), func.count(BlogVisit.id).label("visitCount"),
Blog.create_time.label("createTime"), Blog.is_approved,
Blog.update_time.label("updateTime") Blog.create_time,
).select_from(Blog).outerjoin( Blog.update_time
BlogCategory, Blog.category_id == BlogCategory.id ).where(Blog.id == blog_id)
).outerjoin( .outerjoin(BlogCategory, Blog.category_id == BlogCategory.id)
BlogContent, Blog.content_id == BlogContent.id .outerjoin(BlogContent, Blog.content_id == BlogContent.id)
).outerjoin( .outerjoin(BlogVisit, Blog.id == BlogVisit.blog_id))
BlogVisit, Blog.id == BlogVisit.blog_id
).filter(Blog.id == blog_id)
blog = query.first() blog = db.execute(stmt).first()
if blog[0] is None:
raise AppException("博客不存在")
return BlogResponse.from_orm(blog) return BlogResponse.model_validate(blog)
def add_blog(db: Session, blog: BlogCreate) -> bool: def add_blog(db: Session, blog: BlogCreate) -> bool:
@@ -115,15 +154,18 @@ def add_blog(db: Session, blog: BlogCreate) -> bool:
db_blog = Blog( db_blog = Blog(
title=blog.title, title=blog.title,
top_value=blog.topValue, top_value=blog.top_value,
is_great=blog.isGreat, is_great=blog.is_great,
category_id=add_blog_category(db, blog.category), category_id=add_blog_category(db, blog.category),
content_id=add_blog_content(db, blog.content),
summary=get_blog_summary(blog.content), summary=get_blog_summary(blog.content),
word_count=word_count, word_count=word_count,
read_duration=get_read_duration(word_count) read_duration=get_read_duration(word_count),
is_approved=blog.is_approved
) )
# 级联新增
db_blog.content = BlogContent(content=blog.content.encode('utf-8'))
db.add(db_blog) db.add(db_blog)
db.commit() db.commit()
db.refresh(db_blog) db.refresh(db_blog)
@@ -132,7 +174,8 @@ def add_blog(db: Session, blog: BlogCreate) -> bool:
id=db_blog.id, id=db_blog.id,
title=db_blog.title, title=db_blog.title,
content=blog.content, content=blog.content,
category=blog.category category=blog.category,
isApproved=blog.isApproved
) )
add_blog_elastic(blog_elastic) add_blog_elastic(blog_elastic)
@@ -140,7 +183,8 @@ def add_blog(db: Session, blog: BlogCreate) -> bool:
def add_blog_category(db: Session, category: str) -> int: def add_blog_category(db: Session, category: str) -> int:
db_blog_category = db.query(BlogCategory).filter(BlogCategory.name == category).first() db_blog_category = db.execute(select(BlogCategory).where(BlogCategory.name == category)).scalar_one_or_none()
if db_blog_category is None: if db_blog_category is None:
db_new_blog_category = BlogCategory(name=category) db_new_blog_category = BlogCategory(name=category)
db.add(db_new_blog_category) db.add(db_new_blog_category)
@@ -152,32 +196,22 @@ def add_blog_category(db: Session, category: str) -> int:
return category_id return category_id
def add_blog_content(db: Session, content: str) -> int:
db_blog_content = BlogContent(content=content.encode('utf-8'))
db.add(db_blog_content)
db.commit()
return db_blog_content.id
def update_blog(db: Session, blog_id: int, blog: BlogUpdate) -> bool: def update_blog(db: Session, blog_id: int, blog: BlogUpdate) -> bool:
db_blog = db.query(Blog).filter(Blog.id == blog_id).first() db_blog = check_blog_exist(db, blog_id)
if not db_blog:
raise AppException("博客不存在")
update_data = blog.model_dump(exclude_unset=True) db_blog.title = blog.title
db_blog.top_value = blog.topValue
update_blog_content(db, db_blog.content_id, blog.content) db_blog.is_great = blog.isGreat
db_blog.title = update_data["title"]
db_blog.top_value = update_data["topValue"]
db_blog.is_great = update_data["isGreat"]
db_blog.category_id = update_blog_category(db, blog.category) db_blog.category_id = update_blog_category(db, blog.category)
# 级联更新
db_blog.content = BlogContent(content=blog.content.encode('utf-8'))
word_count = get_word_count(blog.content) word_count = get_word_count(blog.content)
db_blog.summary = get_blog_summary(blog.content), db_blog.summary = get_blog_summary(blog.content),
db_blog.word_count = word_count db_blog.word_count = word_count
db_blog.read_duration = get_read_duration(word_count) db_blog.read_duration = get_read_duration(word_count)
db_blog.is_approved = blog.isApproved
db.commit() db.commit()
db.refresh(db_blog) db.refresh(db_blog)
@@ -186,7 +220,7 @@ def update_blog(db: Session, blog_id: int, blog: BlogUpdate) -> bool:
def update_blog_category(db: Session, category: str) -> int: def update_blog_category(db: Session, category: str) -> int:
db_blog_category = db.query(BlogCategory).filter(BlogCategory.name == category).first() db_blog_category = db.execute(select(BlogCategory).where(BlogCategory.name == category)).scalar_one_or_none()
if db_blog_category is None: if db_blog_category is None:
return add_blog_category(db, category) return add_blog_category(db, category)
@@ -194,75 +228,67 @@ def update_blog_category(db: Session, category: str) -> int:
return db_blog_category.id return db_blog_category.id
def update_blog_content(db: Session, blog_content_id: int, blog_content: str) -> bool:
db_blog_content = db.query(BlogContent).filter(BlogContent.id == blog_content_id).first()
if not db_blog_content:
raise AppException("博客内容不存在")
db_blog_content.content = blog_content.encode('utf-8')
db.commit()
db.refresh(db_blog_content)
return True
def delete_blog(db: Session, blog_id: int) -> bool: def delete_blog(db: Session, blog_id: int) -> bool:
db_blog = db.query(Blog).filter(Blog.id == blog_id).first() db_blog = check_blog_exist(db, blog_id)
if not db_blog:
raise AppException("博客不存在")
delete_blog_content(db, db_blog.content_id)
# 级联删除
db.delete(db_blog) db.delete(db_blog)
db.commit() db.commit()
return True return True
def delete_blog_content(db: Session, blog_content_id: int) -> bool:
db_blog_content = db.query(BlogContent).filter(BlogContent.id == blog_content_id).first()
if not db_blog_content:
raise AppException("博客内容不存在")
db.delete(db_blog_content)
db.commit()
return True
def query_blog_category(db: Session) -> List[BlogCategoryResponse]: def query_blog_category(db: Session) -> List[BlogCategoryResponse]:
query = select( query = (select(BlogCategory.name, func.count(Blog.id).label("count"))
BlogCategory.name, .where(Blog.is_approved == 1)
func.count(Blog.id).label("count") .outerjoin(BlogCategory, Blog.category_id == BlogCategory.id)
).select_from(Blog).outerjoin( .group_by(BlogCategory.name))
BlogCategory, Blog.category_id == BlogCategory.id
).group_by(
BlogCategory.name
)
results = db.execute(query).fetchall() results = db.execute(query).fetchall()
return [BlogCategoryResponse.from_orm(result) for result in results] return [BlogCategoryResponse.model_validate(result) for result in results]
def query_blog_stats(db: Session) -> BlogStatsResponse: def query_blog_stats(db: Session) -> BlogStatsResponse:
blog_stats = query_blog_overview(db)
return BlogStatsResponse( return BlogStatsResponse(
blogCount=db.query(Blog).count(), blogCount=blog_stats.blogCount,
categoryCount=db.query(BlogCategory).count(), categoryCount=blog_stats.categoryCount,
wordCount=db.query(func.sum(Blog.word_count)).scalar() wordCount=blog_stats.wordCount
) )
def query_blog_latest(db: Session) -> List[BlogLatestResponse]: def query_blog_latest(db: Session) -> List[BlogLatestResponse]:
query = select( stmt = select(Blog.id, Blog.title).where(Blog.is_approved == 1).order_by(desc(Blog.update_time)).limit(5)
Blog.id, results = db.execute(stmt).fetchall()
Blog.title
).select_from(Blog).order_by(desc(Blog.update_time)).limit(5)
results = db.execute(query).fetchall() return [BlogLatestResponse.model_validate(result) for result in results]
return [BlogLatestResponse.from_orm(result) for result in results]
def query_blog_adjacent(db: Session, blog_id: int) -> List[BlogAdjacentResponse]:
check_blog_exist(db, blog_id)
prev_stmt = (select(Blog.id, Blog.title)
.where(Blog.id < blog_id, Blog.is_approved == 1)
.order_by(desc(Blog.id)))
prev_result = db.execute(prev_stmt).first()
next_stmt = (select(Blog.id, Blog.title)
.where(Blog.id > blog_id, Blog.is_approved == 1)
.order_by(asc(Blog.id)))
next_result = db.execute(next_stmt).first()
return [
BlogAdjacentResponse(
id=prev_result[0] if prev_result else 0,
title=prev_result[1] if prev_result else ""
),
BlogAdjacentResponse(
id=next_result[0] if next_result else 0,
title=next_result[1] if next_result else ""
)
]
def add_blog_visit(db: Session, blog_visit: BlogVisit): def add_blog_visit(db: Session, blog_visit: BlogVisit):
@@ -287,46 +313,45 @@ def query_blog_visit(db: Session, current_page: int = 1, page_size: int = 10) ->
def query_blog_comment(db: Session, blog_id: int) -> List[BlogCommentResponse]: def query_blog_comment(db: Session, blog_id: int) -> List[BlogCommentResponse]:
query = select( stmt = select(
BlogComment.id, BlogComment.id,
BlogComment.blog_id.label("blogId"), BlogComment.blog_id,
BlogComment.parent_id.label("parentId"), BlogComment.parent_id,
BlogComment.name, BlogComment.name,
BlogComment.website, BlogComment.website,
BlogComment.ip_address.label("ipAddress"), BlogComment.ip_address,
BlogComment.user_agent.label("userAgent"), BlogComment.user_agent,
BlogComment.content, BlogComment.content,
BlogComment.is_approved.label("isApproved"), BlogComment.is_approved,
BlogComment.create_time.label("createTime") BlogComment.create_time
).select_from(BlogComment).where(BlogComment.blog_id == blog_id) ).where(BlogComment.blog_id == blog_id, BlogComment.is_approved == 1).order_by(desc(BlogComment.create_time))
results = db.execute(query).fetchall() results = db.execute(stmt).fetchall()
return [BlogCommentResponse.from_orm(result) for result in results] return [BlogCommentResponse.model_validate(result) for result in results]
def add_blog_comment(db: Session, request: Request, blog_comment: BlogCommentCreate) -> bool: def add_blog_comment(db: Session, request: Request, blog_id: int, blog_comment: BlogCommentCreate) -> bool:
blog = db.query(Blog).filter(Blog.id == blog_comment.blogId).first() check_blog_exist(db, blog_id)
if not blog:
raise AppException("博客不存在")
if blog_comment.parentId != 0: if blog_comment.parentId != 0:
parent_comment = db.query(BlogComment).filter( parent_comment = db.execute(
BlogComment.id == blog_comment.parentId, select(BlogComment)
BlogComment.blog_id == blog_comment.blogId .where(BlogComment.id == blog_comment.parentId)
).first() .where(BlogComment.blog_id == blog_id)
).scalar_one_or_none()
if not parent_comment: if not parent_comment:
raise AppException("父评论不存在") raise AppException("父评论不存在")
db_comment = BlogComment( db_comment = BlogComment(
blog_id=blog_comment.blogId, blog_id=blog_id,
parent_id=blog_comment.parentId, parent_id=blog_comment.parentId,
name=blog_comment.name, name=blog_comment.name,
website=blog_comment.website, website=blog_comment.website,
content=blog_comment.content, content=blog_comment.content,
ip_address=request.client.host, ip_address=request.client.host,
user_agent=request.headers.get("user-agent"), user_agent=request.headers.get("user-agent"),
is_approved=1 is_approved=True
) )
db.add(db_comment) db.add(db_comment)
@@ -334,3 +359,20 @@ def add_blog_comment(db: Session, request: Request, blog_comment: BlogCommentCre
db.refresh(db_comment) db.refresh(db_comment)
return True return True
def check_blog_exist(db: Session, blog_id: int) -> Blog:
result = db.execute(select(Blog).where(Blog.id == blog_id)).scalar_one_or_none()
if not result:
raise AppException("博客不存在")
return result
def check_blog_content_exist(db: Session, blog_content_id: int) -> BlogContent:
result = db.execute(select(BlogContent).where(BlogContent.id == blog_content_id)).scalar_one_or_none()
if not result:
raise AppException("博客内容不存在")
return result

View File

@@ -0,0 +1,143 @@
from datetime import datetime
from typing import List
from sqlalchemy import select, func, distinct, extract
from sqlalchemy.orm import Session
from models.blog import Blog, BlogVisit, BlogComment, BlogCategory
from schemas.blog_stats import BlogOverview, BlogChartStats
def query_blog_overview(db: Session) -> BlogOverview:
blog_count_stmt = select(func.count(Blog.id)).where(Blog.is_approved == 1)
blog_count = db.execute(blog_count_stmt).scalar() or 0
word_count_stmt = select(func.sum(Blog.word_count)).where(Blog.is_approved == 1)
word_count = db.execute(word_count_stmt).scalar() or 0
category_count_stmt = select(func.count(distinct(Blog.category_id))).where(Blog.is_approved == 1)
category_count = db.execute(category_count_stmt).scalar() or 0
great_count_stmt = select(func.count(Blog.id)).where(Blog.is_approved == 1, Blog.is_great == 1)
great_count = db.execute(great_count_stmt).scalar() or 0
visit_count_stmt = select(func.count(BlogVisit.id))
visit_count = db.execute(visit_count_stmt).scalar() or 0
return BlogOverview(
blogCount=blog_count,
categoryCount=category_count,
wordCount=word_count,
greatCount=great_count,
visitCount=visit_count
)
def query_blog_category(db: Session) -> List[BlogChartStats]:
query = (
select(
BlogCategory.name.label("name"),
func.count(Blog.id).label("value")
)
.where(Blog.is_approved == 1)
.outerjoin(BlogCategory, Blog.category_id == BlogCategory.id)
.group_by(BlogCategory.name))
results = db.execute(query).fetchall()
return [BlogChartStats.model_validate(result) for result in results]
def query_blog_approved_monthly(db: Session, year: int = None) -> List[BlogChartStats]:
current_year = datetime.now().year
target_year = year if year else current_year
# extract():返回日期/时间的单独部分
stmt = (
select(
func.concat(extract('month', Blog.create_time), '').label('name'),
func.count(Blog.id).label('value')
)
.where(
Blog.is_approved == 1,
extract('year', Blog.create_time) == target_year
)
.group_by('name')
)
results = db.execute(stmt).fetchall()
return [BlogChartStats.model_validate(result) for result in results]
def query_blog_visit_monthly(db: Session, year: int = None) -> List[BlogChartStats]:
current_year = datetime.now().year
target_year = year if year else current_year
stmt = (
select(
func.concat(extract('month', BlogVisit.create_time), '').label('name'),
func.count(BlogVisit.id).label('value')
)
.where(
extract('year', BlogVisit.create_time) == target_year
)
.group_by('name')
)
results = db.execute(stmt).fetchall()
return [BlogChartStats.model_validate(result) for result in results]
def query_blog_visit_rank(db: Session, limit: int = 5) -> List[BlogChartStats]:
stmt = (
select(
Blog.title.label('name'),
func.count(BlogVisit.id).label('value')
)
.join(BlogVisit, Blog.id == BlogVisit.blog_id)
.where(Blog.is_approved == 1)
.group_by(Blog.title)
.order_by(func.count(BlogVisit.id).desc())
.limit(limit)
)
results = db.execute(stmt).fetchall()
return [BlogChartStats.model_validate(result) for result in results]
def query_blog_read_rank(db: Session, limit: int = 5) -> List[BlogChartStats]:
stmt = (
select(
Blog.title.label('name'),
func.round(func.sum(Blog.read_duration)).label('value')
)
.where(Blog.is_approved == 1)
.group_by(Blog.title)
.order_by(func.sum(Blog.read_duration).desc())
.limit(limit)
)
results = db.execute(stmt).fetchall()
return [BlogChartStats.model_validate(result) for result in results]
def query_blog_comment_rank(db: Session, limit: int = 5) -> List[BlogChartStats]:
stmt = (
select(
Blog.title.label('name'),
func.count(BlogComment.id).label('value')
)
.join(BlogComment, Blog.id == BlogComment.blog_id)
.where(Blog.is_approved == 1)
.group_by(Blog.title)
.order_by(func.count(BlogComment.id).desc())
.limit(limit)
)
results = db.execute(stmt).fetchall()
return [BlogChartStats.model_validate(result) for result in results]

40
service/file_service.py Normal file
View File

@@ -0,0 +1,40 @@
from fastapi import UploadFile, File, HTTPException
from config.rustfs import s3
from config.setting import settings
ALLOWED_IMAGE_TYPES = [
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/svg+xml",
]
BUCKET = 'blog'
NGINX_PROXY = 'rustfs'
async def upload_file(md5: str, file: UploadFile = File(...)) -> str:
# 校验文件类型是否是图片
if file.content_type not in ALLOWED_IMAGE_TYPES:
raise HTTPException(
status_code=400,
detail="只允许上传图片文件 (JPEG, PNG, GIF, WEBP, SVG)"
)
file_ext = file.filename.split('.')[-1]
unique_filename = f"{md5}.{file_ext}"
file_content = await file.read()
# 上传到S3
s3.put_object(
Bucket=BUCKET,
Key=unique_filename,
Body=file_content,
ContentType=file.content_type
)
# 返回文件url
return f"{NGINX_PROXY}/{BUCKET}/{unique_filename}"

View File

@@ -18,6 +18,3 @@ def create(db: Session, username: str, password: str) -> SessionResponse:
access_token=create_token(create_payload(username)), access_token=create_token(create_payload(username)),
token_type="Bearer" token_type="Bearer"
) )

View File

@@ -858,3 +858,699 @@ category
2025-09-25 00:06:40.224 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/blog 2025-09-25 00:06:40.224 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/blog
2025-09-25 00:07:13.887 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/blog 2025-09-25 00:07:13.887 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/blog
2025-09-25 00:07:15.655 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/blog 2025-09-25 00:07:15.655 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/blog
2025-09-25 11:44:15.070 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/
2025-09-25 11:44:15.071 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-25 11:44:15.173 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/favicon.ico
2025-09-25 11:44:15.174 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-25 11:44:20.341 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-25 11:44:21.109 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-25 11:44:24.250 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 11:44:24.410 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 1 validation error for BlogElastic
Input should be a valid dictionary or instance of BlogElastic [type=model_type, input_value=(591259203186757, 'MyBati...xe3\x80\x82</font>\n\n'), input_type=Row]
For further information visit https://errors.pydantic.dev/2.11/v/model_type
2025-09-25 11:45:40.490 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 11:45:40.555 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: You must set the config attribute `from_attributes=True` to use from_orm
2025-09-25 11:46:36.849 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 11:46:36.917 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: list.count() takes exactly one argument (0 given)
2025-09-25 11:47:47.735 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:02:12.322 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:07:05.987 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:09:49.942 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:11:29.949 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:12:05.262 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:20:22.224 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:20:22.403 | INFO  | service.blog_elastic_service:sync_all_blog:50 - 成功同步 13 条博客到 Elasticsearch
2025-09-25 13:21:18.623 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/sync
2025-09-25 13:21:18.753 | INFO  | service.blog_elastic_service:sync_all_blog:50 - 成功同步 13 条博客到 Elasticsearch
2025-09-25 13:26:43.033 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-25 13:26:43.260 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-25 13:26:51.271 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:26:51.272 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:27:28.435 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:27:28.436 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:28:02.588 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:28:02.589 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:28:26.051 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:28:26.051 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:29:14.437 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:29:14.438 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:30:29.031 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:30:29.031 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:34:01.586 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:34:01.587 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:34:20.735 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:34:20.735 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:38:11.466 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:38:11.466 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:38:15.478 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-25 13:38:15.663 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-25 13:38:20.698 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:38:20.699 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:40:27.142 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 13:40:27.142 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 13:43:19.045 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=Spring
2025-09-25 13:43:19.045 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': 'Spring'}
2025-09-25 13:47:02.900 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:47:02.946 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 13:47:02.946 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 13:47:03.063 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 13:48:14.267 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:48:14.271 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 13:48:14.272 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 13:48:14.291 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 13:51:19.419 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 13:51:19.420 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 13:51:21.402 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/category
2025-09-25 13:55:21.251 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:55:21.252 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/category
2025-09-25 13:55:21.285 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 13:55:22.087 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 13:55:22.087 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 13:56:07.890 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:56:07.891 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 13:56:07.891 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 13:56:07.916 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 13:56:10.251 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:56:10.252 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 13:56:10.252 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 13:56:10.276 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 13:57:22.388 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:57:22.389 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 13:57:22.390 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 13:57:22.428 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 13:57:23.517 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 13:57:23.519 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 13:57:23.519 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 13:57:23.553 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:06:40.887 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:06:40.923 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:06:42.166 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:06:42.194 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:06:47.716 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:06:47.762 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:07:28.984 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:07:29.031 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:27:09.163 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:27:09.192 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:27:26.494 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 14:27:26.494 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 14:29:40.269 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:29:40.286 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:30:40.527 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:30:40.556 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:30:40.637 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:30:40.652 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:31:44.236 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:31:44.249 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:34:54.316 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:34:54.333 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:34:58.160 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:34:58.198 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:35:07.966 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=dd
2025-09-25 14:35:07.967 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': 'dd'}
2025-09-25 14:35:22.188 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:35:22.188 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:35:40.918 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:35:40.956 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:35:48.222 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:35:48.222 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:38:13.931 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:38:13.970 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:39:00.228 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:39:00.242 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:39:06.442 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:39:06.491 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:39:16.760 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:39:16.861 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:39:19.369 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:39:19.370 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:39:26.761 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:39:26.822 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:41:32.350 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E5%8D%95%E7%8B%AC
2025-09-25 14:41:32.350 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '单独'}
2025-09-25 14:41:35.148 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E5%8D%95
2025-09-25 14:41:35.148 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '单'}
2025-09-25 14:41:35.643 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=1
2025-09-25 14:41:35.644 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '1'}
2025-09-25 14:41:47.057 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:41:47.111 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:41:48.341 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E9%97%AE%E9%97%AE
2025-09-25 14:41:48.341 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '问问'}
2025-09-25 14:41:49.268 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E9%97%AE
2025-09-25 14:41:49.269 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '问'}
2025-09-25 14:41:49.447 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=
2025-09-25 14:41:49.447 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': ''}
2025-09-25 14:41:49.875 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%88%91
2025-09-25 14:41:49.876 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '我'}
2025-09-25 14:41:51.018 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=
2025-09-25 14:41:51.018 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': ''}
2025-09-25 14:41:52.360 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:41:52.360 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:41:53.388 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86
2025-09-25 14:41:53.389 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框'}
2025-09-25 14:41:54.484 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=
2025-09-25 14:41:54.484 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': ''}
2025-09-25 14:42:07.744 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:42:07.744 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:42:09.517 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=
2025-09-25 14:42:09.518 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': ''}
2025-09-25 14:42:22.609 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E7%9A%84
2025-09-25 14:42:22.609 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '的'}
2025-09-25 14:45:38.620 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%88%91
2025-09-25 14:45:38.620 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '我'}
2025-09-25 14:45:40.520 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E4%BD%A0
2025-09-25 14:45:40.520 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '你'}
2025-09-25 14:46:17.291 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:46:17.292 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:46:26.237 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:46:26.256 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:46:29.766 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:46:29.766 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:46:33.114 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:46:33.135 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:46:34.508 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:46:34.508 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:46:57.691 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:46:57.737 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:46:58.594 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:46:58.595 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:47:04.608 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:47:04.627 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:47:06.837 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:47:06.896 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:47:08.113 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:47:08.113 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:48:59.162 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:48:59.223 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:49:00.880 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:49:00.880 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:51:02.407 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:51:02.433 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:51:03.966 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:51:03.966 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:53:00.197 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:53:00.251 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:53:01.530 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:53:01.530 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:54:26.078 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:54:26.186 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:54:30.854 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:54:30.854 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:54:42.278 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:54:42.306 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:54:43.851 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%98%AF
2025-09-25 14:54:43.851 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '是'}
2025-09-25 14:56:17.101 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:56:17.102 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:56:22.995 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/:591259203186757
2025-09-25 14:56:22.997 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-25 14:56:30.426 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 14:56:30.426 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 14:56:54.085 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:56:54.159 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:56:56.193 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:56:56.193 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:57:03.178 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:57:03.194 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:57:04.483 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 14:57:04.483 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 14:57:07.503 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-25 14:57:07.541 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-25 14:57:29.779 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:57:29.780 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 14:57:29.780 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 14:57:29.801 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:57:31.697 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-25 14:57:31.716 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-25 14:57:39.093 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E6%A1%86%E6%9E%B6
2025-09-25 14:57:39.093 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '框架'}
2025-09-25 14:57:40.678 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/676185644712005
2025-09-25 14:57:40.693 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/676185644712005
2025-09-25 14:58:06.963 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E4%BD%A0
2025-09-25 14:58:06.963 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '你'}
2025-09-25 14:59:27.179 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:59:27.241 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:59:31.417 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 14:59:31.418 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 14:59:34.828 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E4%BD%A0%E5%A5%BD
2025-09-25 14:59:34.829 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '你好'}
2025-09-25 14:59:35.815 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E4%BD%A0
2025-09-25 14:59:35.815 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '你'}
2025-09-25 14:59:37.358 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 14:59:37.359 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 14:59:49.964 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-25 14:59:50.045 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-25 14:59:50.680 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 14:59:50.680 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 14:59:52.852 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=%E4%BD%A0
2025-09-25 14:59:52.853 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '你'}
2025-09-25 14:59:54.375 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/condition?year=2025
2025-09-25 14:59:54.375 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'year': '2025'}
2025-09-25 15:00:09.535 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 15:00:09.536 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:49:58.584 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/
2025-09-25 22:50:00.691 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-25 22:50:02.808 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-25 22:50:09.497 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-25 22:50:16.478 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:50:18.553 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:50:20.723 | CRITICAL | middleware.exceptions:global_exception_handler:38 - 数据库异常: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on 'host.docker.internal' ([Errno 11001] getaddrinfo failed)")
(Background on this error at: https://sqlalche.me/e/20/e3q8)
2025-09-25 22:50:51.042 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:50:53.126 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:50:55.241 | CRITICAL | middleware.exceptions:global_exception_handler:38 - 数据库异常: (pymysql.err.OperationalError) (1045, "Access denied for user 'root'@'localhost' (using password: YES)")
(Background on this error at: https://sqlalche.me/e/20/e3q8)
2025-09-25 22:51:09.586 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:51:11.676 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:52:58.339 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:53:01.352 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:54:16.612 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:54:16.622 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:57:08.357 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:57:08.357 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 22:57:58.433 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 22:57:58.433 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:03:57.445 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:03:57.445 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:04:29.198 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:04:29.198 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:11:27.170 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:11:27.171 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:17:14.162 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:17:14.164 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:18:42.029 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:18:42.029 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:20:09.630 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:20:09.668 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-25 23:22:00.941 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-25 23:22:00.942 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 16:03:42.517 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 16:03:42.537 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 16:03:42.537 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 16:03:42.703 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 16:09:59.841 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 16:09:59.842 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 16:09:59.842 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 16:09:59.888 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 16:11:17.113 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 16:11:17.180 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-26 16:11:40.642 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 16:11:40.702 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 16:11:40.714 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-26 16:11:40.814 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 16:13:45.814 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:13:45.816 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 401
2025-09-26 16:13:46.054 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 16:13:46.057 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 16:13:46.058 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 16:13:46.071 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 16:13:52.974 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 16:13:53.166 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-26 16:14:06.274 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:14:06.275 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 401
2025-09-26 16:14:06.935 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 16:14:06.944 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 16:14:06.945 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 16:14:06.963 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 16:14:57.178 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 16:14:57.374 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-26 16:17:51.796 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-26 16:17:52.677 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-26 16:19:00.119 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:19:00.122 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 401
2025-09-26 16:21:45.316 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 16:21:45.316 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 16:24:29.117 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:24:29.138 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 401
2025-09-26 16:28:18.003 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-26 16:28:18.241 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-26 16:28:28.641 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:28:28.644 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 401
2025-09-26 16:29:07.075 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/0
2025-09-26 16:29:15.253 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/comment/631305371107398
2025-09-26 16:34:33.316 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-26 16:34:33.446 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-26 16:34:34.795 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-26 16:34:34.853 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-26 16:34:35.378 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-26 16:34:35.437 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-26 16:34:38.667 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:34:38.670 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 401
2025-09-26 16:36:52.062 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:36:52.106 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:37:21.688 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:37:21.724 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:51:46.676 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:51:46.678 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:52:11.046 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-26 16:52:11.200 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-26 16:52:17.101 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:52:17.106 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:52:44.449 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:52:44.466 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:53:10.411 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:53:10.449 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:54:22.147 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:54:22.193 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 16:54:23.444 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/comment
2025-09-26 16:54:23.447 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 422
2025-09-26 17:04:46.164 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:04:46.298 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 17:04:46.300 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-26 17:04:46.371 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:04:46.425 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 17:04:46.426 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-26 17:04:46.428 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:04:46.449 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:05:24.904 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:05:24.955 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 17:05:24.956 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-26 17:05:24.970 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:05:55.692 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:05:55.745 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 17:05:55.746 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-26 17:05:55.761 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:06:06.166 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:06:06.219 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-26 17:06:06.220 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-26 17:06:06.233 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:06:19.470 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:06:19.476 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:06:19.522 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:06:19.548 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:06:27.196 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:06:27.276 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:06:27.286 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:07:38.586 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 17:07:38.586 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 17:07:40.042 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:07:40.043 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-26 17:07:40.044 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-26 17:07:40.062 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:07:42.579 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:07:42.831 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:07:47.930 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:07:48.043 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:07:48.053 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:08:11.749 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:08:11.808 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:08:11.817 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:08:36.260 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:08:36.355 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:08:36.365 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:10:27.472 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:10:27.473 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:10:27.495 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:10:27.498 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:11:25.564 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-26 17:11:25.565 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:11:25.645 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:11:25.646 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-26 17:11:33.777 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:11:33.850 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:11:33.859 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:11:52.370 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: PUT http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-26 17:11:52.449 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-26 17:11:52.459 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 16:45:56.908 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:45:56.937 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:45:56.938 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:45:57.069 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:46:03.071 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/category
2025-09-28 16:46:03.776 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:46:03.777 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:46:05.951 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:46:05.987 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 16:46:31.595 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:46:31.595 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:46:33.006 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:46:33.006 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:46:33.007 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:46:33.022 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:47:12.257 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:47:12.258 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:47:12.258 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:47:12.303 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:47:14.784 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:47:14.902 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 16:47:18.050 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:47:18.051 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:47:19.668 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:47:19.669 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:47:19.670 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:47:19.692 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:47:29.187 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:47:29.203 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:47:29.204 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:47:29.272 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:47:31.419 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:47:31.443 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 16:47:59.666 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:47:59.941 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:48:07.270 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:48:07.271 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:48:08.526 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:48:08.527 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:48:08.527 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:48:08.545 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:50:19.796 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:50:19.895 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:51:05.805 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:51:05.920 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:51:05.940 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:51:05.943 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 16:51:18.654 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 16:51:20.310 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:51:20.310 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:51:24.671 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-28 16:51:24.672 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-28 16:51:32.384 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:51:32.384 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:51:34.502 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:51:34.502 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:51:35.825 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/content/631305371107398
2025-09-28 16:51:35.825 | WARNING  | middleware.exceptions:global_exception_handler:26 - 响应: 404
2025-09-28 16:51:44.254 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:51:44.255 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:52:52.544 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:52:52.544 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:53:05.540 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:53:05.541 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:53:08.758 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:53:08.769 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 16:53:20.253 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 16:53:21.837 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:53:21.838 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:53:23.683 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:53:23.694 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 16:53:27.278 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:53:27.278 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:53:29.139 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:53:29.139 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:53:32.161 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 16:53:32.207 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:53:32.228 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 16:53:32.229 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 16:54:07.019 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 16:54:13.084 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:54:13.084 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 16:54:15.572 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 16:54:15.584 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 16:54:18.198 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 16:54:18.198 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 17:00:01.301 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/stats
2025-09-28 17:00:01.411 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 17:00:01.436 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/latest
2025-09-28 17:00:01.438 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/comment
2025-09-28 17:00:17.224 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 17:00:18.548 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 17:00:18.549 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 17:00:20.049 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 17:00:20.060 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 17:00:22.313 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 17:00:22.313 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 17:30:42.553 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 17:31:07.575 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 17:31:10.589 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 17:31:13.605 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 17:31:16.615 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:18:58.591 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:18:58.603 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:19:51.206 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 18:19:55.103 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:19:55.103 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:19:56.439 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 18:19:56.451 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 18:19:58.707 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:19:58.708 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:20:39.477 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 18:20:39.494 | ERROR  | middleware.exceptions:global_exception_handler:32 - 业务异常: 密码错误 - 详情: None
2025-09-28 18:20:43.789 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: POST http://127.0.0.1:8082/session
2025-09-28 18:20:45.916 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:20:45.916 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:20:47.327 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/631305371107398/content
2025-09-28 18:20:47.338 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 18:20:48.979 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:20:48.980 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:24:43.315 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/category
2025-09-28 18:24:44.236 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:24:44.237 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:24:46.075 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/694197446615109/content
2025-09-28 18:24:46.118 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/0/comment
2025-09-28 18:24:47.897 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:24:47.898 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:25:50.326 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:25:50.326 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:25:51.641 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:25:51.642 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:26:29.481 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:26:29.482 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:27:28.567 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:27:28.567 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:28:41.164 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/visit?currentPage=1&pageSize=15
2025-09-28 18:28:41.165 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '15'}
2025-09-28 18:28:43.293 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/visit?currentPage=2&pageSize=15
2025-09-28 18:28:43.293 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '2', 'pageSize': '15'}
2025-09-28 18:28:44.483 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/visit?currentPage=1&pageSize=15
2025-09-28 18:28:44.483 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '15'}
2025-09-28 18:28:47.358 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/visit?currentPage=5&pageSize=15
2025-09-28 18:28:47.358 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '5', 'pageSize': '15'}
2025-09-28 18:28:50.937 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/visit?currentPage=1&pageSize=15
2025-09-28 18:28:50.937 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '15'}
2025-09-28 18:28:56.655 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:28:56.656 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:29:01.109 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:29:01.110 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:29:02.991 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:29:02.992 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:29:04.513 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:29:04.513 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:29:08.015 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:29:08.016 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:33:59.860 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:33:59.861 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:34:01.710 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:34:01.710 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:35:56.072 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 18:35:56.083 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 18:35:56.116 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:19:14.537 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-28 19:19:14.734 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-28 19:19:18.138 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:19:18.139 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:19:18.148 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:22:51.280 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:22:51.281 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:22:51.284 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:25:30.370 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:25:30.371 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:25:30.374 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:28:28.382 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:28:28.382 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:28:28.385 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:32:53.616 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:32:53.617 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:32:53.620 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:34:44.707 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:34:44.707 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:34:44.710 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:38:22.706 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:38:22.707 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:38:22.728 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:39:50.001 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:39:50.001 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:39:50.005 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:41:24.798 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:41:24.799 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:41:24.826 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:43:01.412 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:43:01.412 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:43:01.418 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:44:15.925 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:44:15.925 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:44:15.928 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:44:27.896 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:44:27.897 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:44:27.901 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-28 19:44:28.915 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-28 19:44:28.915 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-28 19:44:28.933 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
2025-09-29 09:24:08.763 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-29 09:24:08.998 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-29 09:24:17.059 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:24:17.060 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:46:13.912 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:46:13.927 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:47:38.224 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:47:38.237 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:50:21.389 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:50:21.402 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:50:21.403 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:50:35.920 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/docs
2025-09-29 09:50:36.069 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/openapi.json
2025-09-29 09:50:38.521 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:50:38.521 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:50:38.523 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:51:10.682 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:51:10.692 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:51:10.695 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:51:13.397 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:51:13.397 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:51:13.399 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:51:54.908 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:51:54.930 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:51:54.933 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:51:57.821 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:51:57.821 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:51:57.823 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:52:17.305 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:52:17.329 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:52:17.332 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:52:17.333 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:52:18.672 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:52:18.672 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:52:18.674 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:52:18.674 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:54:43.930 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:54:43.930 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:54:43.933 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:54:43.933 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:54:46.330 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:54:46.331 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:54:46.333 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:54:46.333 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:55:05.250 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:55:05.250 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:55:05.252 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:55:05.252 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:55:06.964 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:55:06.964 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:55:06.966 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:55:06.966 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:55:07.783 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:55:07.784 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:55:07.786 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:55:07.786 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:55:08.244 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/page?currentPage=1&pageSize=10
2025-09-29 09:55:08.244 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'currentPage': '1', 'pageSize': '10'}
2025-09-29 09:55:08.246 | INFO  | service.blog_service:query_blog_by_page:20 - Cxxx
2025-09-29 09:55:08.246 | ERROR  | service.blog_service:query_blog_by_page:21 - C213123xxx
2025-09-29 09:55:26.661 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:55:26.661 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:55:26.679 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: BadRequestError(400, 'search_phase_execution_exception', 'failed to create query: Cannot invoke "org.wltea.analyzer.dic.DictSegment.match(char[], int, int)" because "org.wltea.analyzer.dic.Dictionary.singleton._StopWords" is null')
2025-09-29 09:56:20.140 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:56:20.140 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:56:22.378 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:56:22.378 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:56:57.739 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:56:57.739 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:56:57.755 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: BadRequestError(400, 'search_phase_execution_exception', 'failed to create query: Cannot invoke "org.wltea.analyzer.dic.DictSegment.match(char[], int, int)" because "org.wltea.analyzer.dic.Dictionary.singleton._StopWords" is null')
2025-09-29 09:57:24.317 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:57:24.317 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:57:24.334 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: BadRequestError(400, 'search_phase_execution_exception', 'failed to create query: Cannot invoke "org.wltea.analyzer.dic.DictSegment.match(char[], int, int)" because "org.wltea.analyzer.dic.Dictionary.singleton._StopWords" is null')
2025-09-29 09:57:25.184 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:57:25.184 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:57:25.199 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: BadRequestError(400, 'search_phase_execution_exception', 'failed to create query: Cannot invoke "org.wltea.analyzer.dic.DictSegment.match(char[], int, int)" because "org.wltea.analyzer.dic.Dictionary.singleton._StopWords" is null')
2025-09-29 09:58:14.518 | INFO  | middleware.exceptions:global_exception_handler:18 - 请求: GET http://127.0.0.1:8082/blog/search?keyword=21323
2025-09-29 09:58:14.518 | INFO  | middleware.exceptions:global_exception_handler:20 - 查询参数: {'keyword': '21323'}
2025-09-29 09:58:14.531 | CRITICAL | middleware.exceptions:global_exception_handler:44 - 未知异常: BadRequestError(400, 'search_phase_execution_exception', 'failed to create query: Cannot invoke "org.wltea.analyzer.dic.DictSegment.match(char[], int, int)" because "org.wltea.analyzer.dic.Dictionary.singleton._StopWords" is null')