Gemini 3.5 Flash全栈博客开发实战
Gemini 3.5 Flash 全栈教程:从零搭建个人技术博客系统(含后台与前端)
本文详细讲解如何利用 Gemini 3.5 Flash 高效完成从后端API开发、前端页面搭建到Docker容器化部署的全栈博客系统构建。通过实战案例展示其在高频、轻量、重复性开发场景下的超高性价比,提供完整的代码示例和配置方案,帮助开发者快速掌握全栈开发流程与最佳实践。
一、前言
前段时间想把自己散落在各个平台的技术笔记整合一下,搭一套个人技术博客。后端接口、后台管理、前端展示、评论系统——需求不复杂,但全栈从头写一遍工作量不小。
正好在 大模型(01gpt.cn) 上把 Gemini 3.5 Flash 接入了日常开发流,284 token/s 的生成速率和不到 GPT-4 一半的单价这种“高频、轻量、重复性高”的全栈开发场景下性价比极高。花了两天时间从零搭完整套系统,以下是完整的实战教程。
二、技术选型与环境初始化
后端选 FastAPI,异步支持好、自动生成 OpenAPI 文档;前端选 React + Ant Design,组件生态成熟;数据库用 PostgreSQL,博文内容用 JSON 格式存储。先把项目骨架搭起来——建目录、配置虚拟环境、安装依赖。
Gemini 3.5 Flash 在批量生成 CRUD 接口时效率极高,把数据库 Schema 描述清楚后,几分钟就能把全部接口生成完毕。
三、数据库设计与模型定义
博客系统的核心实体就四张表:文章表、分类表、标签表、评论表。文章和标签是多对多关系,评论和文章是一对多。把需求描述清楚,Gemini 3.5 Flash 给出了完整的 SQLAlchemy 模型定义。字段类型、约束、默认值、索引策略全齐了。
文章表有个细节:content 字段用 JSON 类型存储,方便后续支持富文本和 Markdown 混排。Gemini 3.5 Flash 还在模型文件里主动加了 created_at 和 updated_at 的自动时间戳,以及软删除字段,工程习惯很到位。
四、后端接口开发
FastAPI 的 CRUD 接口很标准,Gemini 3.5 Flash 批量生成的速度让人印象深刻——十几个接口几分钟全出来。代码如同毛坯房,功能完整但需要进一步优化。参数校验漏了几个空值判断,异常处理覆盖不全,让它修改一轮即可满足要求。
下面是一个典型的文章列表接口实现,带分页和分类筛选:
@router.get("/articles")
async def list_articles(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
category_id: Optional[int] = None,
db: AsyncSession = Depends(get_db)
):
"""分页获取文章列表,支持按分类筛选"""
query = select(Article).where(Article.is_deleted == False)
if category_id:
query = query.where(Article.category_id == category_id)
query = query.order_by(Article.created_at.desc())
total = (await db.execute(select(func.count()).select_from(query.subquery()))).scalar()
articles = (await db.execute(query.offset((page - 1) * size).limit(size))).scalars().all()
return {
"items": [ArticleSchema.from_orm(a) for a in articles],
"total": total, "page": page, "size": size
}
下面是一个完整的文章创建接口(POST /articles)实战代码示例,包含参数校验、异常处理和数据库事务:
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.exc import IntegrityError
from pydantic import BaseModel, Field, validator
from typing import List, Optional
from datetime import datetime
import logging
# 定义请求模型
class ArticleCreateRequest(BaseModel):
"""文章创建请求模型"""
title: str = Field(..., min_length=1, max_length=200, description="文章标题")
content: dict = Field(..., description="文章内容,JSON格式存储富文本/Markdown")
summary: Optional[str] = Field(None, max_length=500, description="文章摘要")
category_id: int = Field(..., gt=0, description="分类ID")
tag_ids: List[int] = Field(default_factory=list, description="标签ID列表")
is_published: bool = Field(default=True, description="是否立即发布")
@validator('title')
def validate_title_not_empty(cls, v):
"""验证标题非空且去除首尾空格"""
if not v or not v.strip():
raise ValueError('标题不能为空')
return v.strip()
@validator('content')
def validate_content_structure(cls, v):
"""验证内容结构,确保包含必要的字段"""
if not isinstance(v, dict):
raise ValueError('内容必须是JSON对象')
if 'type' not in v or 'body' not in v:
raise ValueError('内容必须包含type和body字段')
return v
# 定义响应模型
class ArticleCreateResponse(BaseModel):
"""文章创建响应模型"""
id: int
title: str
slug: str
created_at: datetime
message: str
@router.post("/articles",
response_model=ArticleCreateResponse,
status_code=status.HTTP_201_CREATED,
summary="创建新文章",
description="创建一篇新文章,支持富文本内容、标签关联和事务处理")
async def create_article(
article_data: ArticleCreateRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user) # 假设有用户认证中间件
):
"""
创建新文章接口
关键特性:
1. 完整的参数校验(Pydantic模型 + 自定义验证器)
2. 数据库事务保证数据一致性
3. 异常处理覆盖所有可能错误场景
4. 日志记录便于问题排查
5. 返回标准化的响应格式
"""
logger = logging.getLogger(__name__)
try:
# 1. 参数预处理和业务校验
logger.info(f"开始创建文章,标题:{article_data.title[:50]}...")
# 检查分类是否存在
category = await db.execute(
select(Category).where(
Category.id == article_data.category_id,
Category.is_deleted == False
)
)
category = category.scalar_one_or_none()
if not category:
logger.warning(f"分类不存在:{article_data.category_id}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"分类ID {article_data.category_id} 不存在"
)
# 检查标签是否存在(如果提供了标签)
if article_data.tag_ids:
tags = await db.execute(
select(Tag).where(
Tag.id.in_(article_data.tag_ids),
Tag.is_deleted == False
)
)
existing_tags = tags.scalars().all()
existing_tag_ids = {tag.id for tag in existing_tags}
# 找出不存在的标签ID
non_existing_ids = set(article_data.tag_ids) - existing_tag_ids
if non_existing_ids:
logger.warning(f"部分标签不存在:{non_existing_ids}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"以下标签ID不存在:{list(non_existing_ids)}"
)
# 2. 生成文章slug(URL友好格式)
from slugify import slugify
base_slug = slugify(article_data.title)
slug = base_slug
# 检查slug是否已存在,如果存在则添加数字后缀
counter = 1
while True:
existing = await db.execute(
select(Article).where(Article.slug == slug)
)
if not existing.scalar_one_or_none():
break
slug = f"{base_slug}-{counter}"
counter += 1
if counter > 10: # 安全限制
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="无法生成唯一的文章slug,请修改标题"
)
# 3. 开始数据库事务
async with db.begin():
try:
# 创建文章记录
new_article = Article(
title=article_data.title,
slug=slug,
content=article_data.content,
summary=article_data.summary or article_data.content.get('body', '')[:200],
category_id=article_data.category_id,
author_id=current_user.id,
is_published=article_data.is_published,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
db.add(new_article)
await db.flush() # 获取生成的ID
# 关联标签(如果提供了标签)
if article_data.tag_ids:
# 批量创建文章-标签关联记录
article_tags = [
ArticleTag(
article_id=new_article.id,
tag_id=tag_id
)
for tag_id in article_data.tag_ids
]
db.add_all(article_tags)
# 提交事务
await db.commit()
logger.info(f"文章创建成功,ID:{new_article.id},标题:{new_article.title}")
# 4. 返回标准化响应
return ArticleCreateResponse(
id=new_article.id,
title=new_article.title,
slug=new_article.slug,
created_at=new_article.created_at,
message="文章创建成功"
)
except IntegrityError as e:
# 数据库完整性错误(如唯一约束冲突)
await db.rollback()
logger.error(f"数据库完整性错误:{str(e)}")
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="数据冲突,请检查输入数据"
)
except Exception as e:
# 其他数据库错误
await db.rollback()
logger.error(f"数据库操作失败:{str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="服务器内部错误,请稍后重试"
)
except HTTPException:
# 重新抛出已处理的HTTP异常
raise
except ValueError as e:
# 业务逻辑验证错误
logger.warning(f"业务验证失败:{str(e)}")
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=str(e)
)
except Exception as e:
# 捕获所有未预期的异常
logger.error(f"创建文章时发生未预期错误:{str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="服务器内部错误,请稍后重试"
)
代码关键逻辑说明:
-
参数校验层:
- 使用Pydantic模型进行基础类型和范围校验
- 自定义验证器确保标题非空、内容结构正确
- 业务层校验分类和标签是否存在
-
数据库事务管理:
- 使用
async with db.begin()开启事务上下文 - 文章和标签关联在同一个事务中提交
- 异常时自动回滚,保证数据一致性
- 使用
-
异常处理策略:
HTTPException:业务逻辑错误(404、409、422等)IntegrityError:数据库约束冲突(唯一性、外键等)ValueError:业务验证失败- 通用异常:未预期的服务器错误
-
日志记录:
- 不同级别日志(INFO、WARNING、ERROR)
- 记录关键操作和错误堆栈
- 便于问题排查和监控
-
安全考虑:
- 用户认证(依赖
get_current_user) - 输入清理(标题去除首尾空格)
- 防重复slug生成机制
- 事务隔离防止并发问题
- 用户认证(依赖
-
性能优化:
- 批量插入标签关联记录
- 异步数据库操作
- 合理的错误消息,避免信息泄露
这个实现展示了生产级API应该具备的完整特性,包括参数校验、异常处理、事务管理和日志记录,可以直接集成到现有的博客系统中。
生成时还自动加了排序和软删除过滤,这些细节人工写容易漏掉。但有些地方需要调整——比如默认值、注释规范等细节让它修改一轮即可满足要求。
五、前端页面搭建
前端用 React + Ant Design,Gemini 3.5 Flash 搭页面骨架很快。首页文章列表、分类导航、文章详情页、评论组件,几个核心页面几分钟就出来了。
不过样式细节需要人工调——按钮间距不统一、移动端适配漏了断点、加载态和空状态没做区分。按照 Ant Design 规范统一调整后,效果符合预期。评论组件生成的代码包含回复嵌套和分页加载,逻辑完整,但需要人工确认用户体验细节。
六、部署上线
部署环节是 Gemini 1.5 Flash 的强项。几分钟给出完整配置——Docker Compose、Nginx 反代、HTTPS 证书自动续期、数据库备份脚本全有。第一次启动报端口冲突,它自己查到冲突进程然后调整了端口分配,第二次直接跑通。整个过程只手动确认了域名配置和备份策略。
部署流程概览
以下是博客系统从代码到线上服务的完整部署流程图,涵盖了构建、容器化、网络配置、Nginx代理等关键步骤:
流程关键步骤说明:
- 代码准备:准备好前后端源代码、配置文件、Dockerfile等
- 环境配置:设置环境变量、数据库连接信息、密钥等
- Docker镜像构建:分别构建后端和前端Docker镜像
- 容器编排:通过Docker Compose定义和编排所有服务
- 网络配置:配置内部网络和端口映射,确保服务间通信
- Nginx反向代理:配置Nginx作为统一入口,处理请求路由
- SSL/TLS配置:配置HTTPS证书,启用安全传输
- 服务启动与监控:启动所有服务并监控运行状态
- 线上服务就绪:系统完全部署完成,可对外提供服务
该流程图清晰地展示了从代码到线上服务的完整部署链路,帮助读者理解各组件之间的依赖关系和执行顺序。
1. Docker Compose 配置文件
以下是完整的 docker-compose.yml 配置文件,包含后端服务、前端服务、PostgreSQL 数据库和 Redis 缓存:
version: '3.8'
services:
# PostgreSQL 数据库服务
postgres:
image: postgres:15-alpine
container_name: blog-postgres
restart: unless-stopped
environment:
POSTGRES_DB: blog_db # 数据库名称
POSTGRES_USER: blog_user # 数据库用户名
POSTGRES_PASSWORD: ${DB_PASSWORD} # 密码从环境变量读取
volumes:
- postgres_data:/var/lib/postgresql/data # 数据持久化
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化脚本
ports:
- "5432:5432" # 主机端口:容器端口
networks:
- blog-network
# Redis 缓存服务
redis:
image: redis:7-alpine
container_name: blog-redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD} # 设置密码
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- blog-network
# 后端 FastAPI 服务
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: blog-backend
restart: unless-stopped
environment:
DATABASE_URL: postgresql://blog_user:${DB_PASSWORD}@postgres:5432/blog_db
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0
SECRET_KEY: ${SECRET_KEY}
CORS_ORIGINS: ${CORS_ORIGINS}
depends_on:
- postgres
- redis
ports:
- "8000:8000" # FastAPI 默认端口
volumes:
- ./backend:/app # 开发时挂载代码热重载
- ./logs:/app/logs # 日志目录
networks:
- blog-network
# 前端 React 服务
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: blog-frontend
restart: unless-stopped
environment:
REACT_APP_API_URL: ${REACT_APP_API_URL:-http://localhost:8000/api}
depends_on:
- backend
ports:
- "3000:3000" # React 开发服务器端口
networks:
- blog-network
# Nginx 反向代理(生产环境)
nginx:
image: nginx:alpine
container_name: blog-nginx
restart: unless-stopped
depends_on:
- backend
- frontend
ports:
- "80:80"
- "443:443" # HTTPS 端口
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf # Nginx 配置
- ./nginx/conf.d:/etc/nginx/conf.d # 站点配置
- ./ssl:/etc/nginx/ssl # SSL 证书目录
- ./logs/nginx:/var/log/nginx # Nginx 日志
networks:
- blog-network
# 数据卷定义
volumes:
postgres_data:
driver: local
redis_data:
driver: local
# 网络定义
networks:
blog-network:
driver: bridge
关键配置项说明:
- 环境变量文件:创建
.env文件存储敏感信息(DB_PASSWORD、REDIS_PASSWORD、SECRET_KEY等) - 数据持久化:PostgreSQL 和 Redis 数据通过 volumes 持久化,避免容器重启数据丢失
- 服务依赖:使用
depends_on确保服务启动顺序,后端依赖数据库,前端依赖后端 - 网络隔离:所有服务在
blog-network内通信,提高安全性 - 端口映射:开发环境映射到主机端口,生产环境可只暴露 Nginx 端口
2. Nginx 反向代理配置
以下是生产环境的 Nginx 配置文件 nginx.conf:
# 全局配置
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
# 基础设置
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 100M; # 支持大文件上传
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/atom+xml image/svg+xml;
# 上游服务定义
upstream backend {
server backend:8000; # 指向 Docker Compose 中的 backend 服务
keepalive 32;
}
upstream frontend {
server frontend:3000; # 指向 Docker Compose 中的 frontend 服务
keepalive 32;
}
# HTTP 到 HTTPS 重定向(生产环境启用)
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# 重定向到 HTTPS
return 301 https://$server_name$request_uri;
}
# HTTPS 服务器配置
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL 证书配置
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 前端静态文件服务
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 缓存静态资源
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# 后端 API 代理
location /api/ {
proxy_pass http://backend/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 跨域支持
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always;
add_header Access-Control-Allow-Credentials true always;
# 预检请求处理
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# 静态文件服务(可选)
location /static/ {
alias /app/static/;
expires 30d;
add_header Cache-Control "public";
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
关键配置项说明:
- SSL/TLS 配置:生产环境必须启用 HTTPS,配置证书路径和加密协议
- 反向代理:Nginx 作为统一入口,将请求分发到前端和后端服务
- 性能优化:启用 Gzip 压缩、keepalive 连接、静态资源缓存
- 安全头:添加 X-Frame-Options、X-Content-Type-Options 等安全头
- 跨域处理:后端 API 配置完整的 CORS 支持,包括预检请求处理
- WebSocket 支持:配置 WebSocket 代理,支持实时功能
- 健康检查:提供
/health端点供监控系统使用
3. 环境变量文件示例
创建 .env 文件存储敏感配置:
# 数据库配置
DB_PASSWORD=your_secure_password_here
POSTGRES_DB=blog_db
POSTGRES_USER=blog_user
# Redis 配置
REDIS_PASSWORD=your_redis_password_here
# 后端配置
SECRET_KEY=your_secret_key_for_jwt_tokens
CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
# 前端配置
REACT_APP_API_URL=https://yourdomain.com/api
# 域名配置
DOMAIN=yourdomain.com
4. 部署命令
# 1. 创建必要的目录
mkdir -p nginx/conf.d nginx/ssl logs
# 2. 复制 SSL 证书到 nginx/ssl/ 目录
# 证书文件:fullchain.pem 和 privkey.pem
# 3. 启动所有服务
docker-compose up -d
# 4. 查看服务状态
docker-compose ps
# 5. 查看日志
docker-compose logs -f backend
docker-compose logs -f frontend
docker-compose logs -f nginx
# 6. 停止服务
docker-compose down
# 7. 停止并清理数据(谨慎使用)
docker-compose down -v
5. HTTPS 证书自动续期
使用 Certbot 自动管理 Let’s Encrypt 证书:
# 安装 Certbot
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
# 获取证书(需要先暂停 Nginx 容器)
docker-compose stop nginx
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com
# 配置自动续期
sudo crontab -e
# 添加以下行(每月1号凌晨3点续期)
0 3 1 * * /usr/bin/certbot renew --quiet && docker-compose restart nginx
这些配置提供了完整的生产级部署方案,涵盖了容器编排、反向代理、SSL 证书管理和自动化运维等关键环节。
七、总结
Gemini 3.5 Flash 在全栈开发这种“高频、轻量、重复性高”的场景下,性价比很高。它承担了大部分重复性工作——CRUD 生成、页面骨架、部署配置。但核心逻辑、安全策略、用户体验细节,还是得人来把关。用对场景,效率翻倍不止。
八、后续优化方向
当前博客系统已具备基础功能,但仍有进一步扩展和优化的空间。以下是几个值得考虑的后续优化方向:
-
接入搜索引擎优化(SEO)
- 为每篇文章自动生成 SEO 友好的 URL(slug)和元标签(meta tags)
- 实现站点地图(sitemap.xml)自动生成与提交
- 添加结构化数据(JSON-LD)提升搜索引擎收录效果
-
用户认证与权限系统
- 实现完整的用户注册、登录、密码重置流程
- 基于角色的访问控制(RBAC),区分管理员、编辑、普通用户
- JWT Token 自动刷新机制,提升用户体验和安全性
-
数据统计与分析
- 集成 Google Analytics 或自建访问统计系统
- 文章阅读量、点赞数、分享数等基础数据统计
- 用户行为分析,为内容优化提供数据支持
-
内容推荐与个性化
- 基于用户浏览历史的文章推荐算法
- 相关文章推荐(基于标签、分类相似度)
- 热门文章、最新文章、编辑推荐等多样化内容展示
-
性能优化与缓存策略
- Redis 缓存文章列表、热门文章等高频访问数据
- CDN 集成,加速静态资源加载
- 数据库查询优化,添加合适的索引提升查询性能
-
多语言与国际化的支持
- 支持中英文等多语言内容管理
- 根据用户浏览器语言自动切换界面语言
- 文章内容的多语言版本管理
这些优化方向可以根据实际需求逐步实现,进一步提升博客系统的功能性、用户体验和可维护性。
更多推荐


所有评论(0)