Zurl 是一个短链接管理系统,使用以下技术栈:
- 后端: Python 3 + FastAPI + SQLAlchemy + SQLite + Redis
- 前端: Vue 3 + Element Plus + Pinia + Vite
- 部署: Docker
zurl/
├── app/ # 后端代码
│ ├── api/ # API 业务逻辑层
│ ├── config.py # 配置管理
│ ├── main.py # FastAPI 应用入口
│ ├── middleware/ # 中间件(认证、点击统计等)
│ ├── models/ # SQLAlchemy 数据模型
│ ├── routers/ # 路由定义
│ └── utils/ # 工具函数
├── frontend/ # 前端代码
│ ├── src/
│ │ ├── components/ # Vue 组件
│ │ ├── stores/ # Pinia 状态管理
│ │ ├── utils/ # 工具函数
│ │ └── views/ # 页面视图
│ └── vite.config.js
└── docker-compose.yaml
# 安装依赖
pip install -r app/requirements.txt
# 启动开发服务器
uvicorn app.main:app --reload --host 0.0.0.0 --port 3080
# 数据库迁移
alembic upgrade headcd frontend
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 构建生产版本
pnpm build# 构建并启动
docker-compose up -d
# 查看日志
docker-compose logs -f当前项目没有自动化测试。如需添加测试:
# 后端测试(如添加 pytest)
pip install pytest pytest-asyncio
pytest tests/
# 前端测试(如添加 vitest)
pnpm add -D vitest
pnpm test- 标准库导入
- 第三方库导入
- 本地应用导入
import time
import re
from datetime import datetime
from fastapi import APIRouter, Form, Request, Depends
from pydantic import BaseModel
from sqlalchemy import desc
from app.models.conn import get_db
from app.utils.helper import show_json, md5- 函数/变量:
snake_case(如get_client_ip,short_url) - 类名:
PascalCase(如UrlAPI,UserItem) - 常量:
UPPER_SNAKE_CASE(如DENY_SHORT_URLS,DB_FILE_PATH) - 数据库表名:
zurl_前缀(如zurl_urls,zurl_sessions)
使用 show_json 统一返回格式:
from app.utils.helper import show_json
return show_json(200, "success", data)
return show_json(400, "error.message", {})
return show_json(404, "not.found", {})- 使用 HTTPException 抛出认证错误
- 业务逻辑错误通过
show_json返回错误码 - 数据库操作使用 try/finally 确保连接关闭
db = next(get_db())
try:
# 数据库操作
result = db.query(Model).filter(...).first()
return show_json(200, "success", result)
finally:
db.close()定义在对应 API 文件顶部:
from pydantic import BaseModel, HttpUrl, EmailStr
class UrlItem(BaseModel):
short_url: str = None
long_url: str
title: str = None
ttl_days: int = 0使用 <script setup> 语法糖,按以下顺序组织:
<template><script setup><style scoped>
- Vue 相关(ref, onMounted 等)
- 第三方库(Element Plus, axios 等)
- 本地组件/stores/utils
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import req from '@/utils/req'
import { useSiteStore } from '@/stores/site'
import MyComponent from '@/components/MyComponent.vue'
</script>- 组件文件:
PascalCase.vue(如AdminView.vue,list.vue) - 变量/函数:
camelCase(如shortUrl,getPosts) - Pinia stores:
camelCase(如site.js,base.js) - 常量:
UPPER_CASE
使用 Pinia,定义在 stores/ 目录:
import { defineStore } from "pinia";
export const useSiteStore = defineStore('site', {
state: () => ({
// 状态
}),
actions: {
// 方法
}
})使用封装的 axios 实例:
import req, { toForm } from '@/utils/req'
// GET 请求
req.get("/api/urls?page=1&limit=10")
// POST JSON
req.post("/api/search", { filter: "short_url", keyword: "test" })
// POST FormData
req.post("/api/delete/url", toForm({ short_url: "test" }))使用 vue-i18n,文案使用 $t() 或 t():
<template>
<span>{{ $t('link.list') }}</span>
</template>
<script setup>
const { t } = useI18n()
ElMessage.success(t('success'))
</script>- 使用 SQLAlchemy ORM
- 时间戳存储为 Unix 时间戳(整数)
- 数据库文件路径:
app/data/db/zurl.db - 配置文件路径:
app/data/config.toml
- 在
app/api/创建/修改 API 类 - 定义 Pydantic 请求模型(如需要)
- 在
app/routers/routers.py添加路由 - 使用
get_current_session依赖保护需要认证的接口
- 在
src/views/创建视图组件 - 在
src/router/index.js添加路由配置 - 在管理后台则修改
src/components/admin/组件
- 后端代码使用中文注释是可接受的
- 前端需要支持中英双语(使用 vue-i18n)
- 敏感配置不要提交到仓库
- 修改配置后需要重启服务