签入版本

This commit is contained in:
2026-10-25 22:53:48 +08:00
parent e1ff172d27
commit 06a855c0f4
135 changed files with 2667 additions and 20 deletions
@@ -0,0 +1,5 @@
export declare const SITE_URL: string;
export declare const SITE_DESCRIPTION: string;
export declare function stripNumberPrefix(title: string): string;
export declare function extractDescription(md: string): string;
export declare function extractFaqs(md: string): { q: string; a: string }[];
@@ -0,0 +1,130 @@
/**
* SEO/GEO 共享工具(纯 ESM,零依赖)。
*
* 同时被两类调用方 import:
* 1. VitePress 配置(.ts,esbuild 打包时内联本文件)
* 2. scripts/generate-llms-txt.mjs(Node 直接运行)
*
* 注意:SITE_URL 是全站唯一 URL 真相源。
* docs 子域当前 TLS 证书为腾讯 CDN 默认证书(*.cdn.myqcloud.com,校验失败),
* 故暂用 http;证书修复后只需改这里一处。
*/
export const SITE_URL = "http://docs.tornado.antdvue.djangoadmin.cn";
export const SITE_DESCRIPTION =
"DjangoAdmin Tornado+AntDesign 企业级中台解决方案开发者文档";
/**
* 剥离 H1/标题中的章节编号前缀,用于 <title> 与 JSON-LD headline。
* 例:"5.10 字典模块" → "字典模块";"K. 贡献指南" → "贡献指南"。
* 页内 H1 与侧边栏编号保持不变,仅影响 SEO 输出。
*/
export function stripNumberPrefix(title) {
return String(title)
.replace(/^(?:\d+(?:\.\d+)+|[A-Z]\.)\s+/, "")
.trim();
}
/**
* 从页面 Markdown 提取 meta description:
* 1. 首选 `:::tip xxx` 容器内的首句(141/143 页的标准 opener)
* 2. 回退到 H1 后的第一个普通段落
* 输出压缩为单行、去除行内 Markdown,按中文句末标点截断(≤100 字符)。
*/
export function extractDescription(md) {
const src = String(md);
const fm = src.match(/^---\r?\n([\s\S]*?)\r?\n---/);
const body = fm ? src.slice(fm[0].length) : src;
let text = "";
// frontmatter description 显式声明优先(无 H1 页面的补齐手段)
const fmDesc = fm && fm[1].match(/^description:\s*(.+)$/m);
if (fmDesc) {
text = fmDesc[1].trim().replace(/^["']|["']$/g, "");
}
const tip = !text && body.match(/^:::\s*tip[^\n]*\n([\s\S]*?)\n:::/m);
if (tip) {
text = tip[1];
}
if (!text) {
const afterH1 = body.match(/^# (?!#)(.+)$/m);
if (afterH1) {
const rest = body.slice((afterH1.index ?? 0) + afterH1[0].length);
const para = rest
.split(/\n{2,}/)
.find(
(p) =>
p.trim() &&
!/^\s*#/.test(p) &&
!/^\s*:::/.test(p) &&
!/^\s*```/.test(p) &&
!/^\s*\|/.test(p),
);
text = para || "";
}
}
text = text
.replace(/\s+/g, " ")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
.trim();
if (!text) return "";
// 按中文句末标点截取首句,上限 100 字符;截断时避免切碎词
const m = text.match(/^.{1,100}?(?:[。!?!?]|$)/);
let out = m ? m[0] : text.slice(0, 100);
if (out.length < text.length && !/[。!?!?]$/.test(out)) {
out = out.replace(/[^\s,、;:,;:]+$/, "");
}
return out.trim() || text.slice(0, 100);
}
function cleanText(s) {
return s
.replace(/^:::[^\n]*$/gm, "")
.replace(/^```[^\n]*$/gm, "")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
.replace(/\s+/g, " ")
.trim();
}
/**
* 提取页面中的 Q/A 对(用于 FAQPage JSON-LD),支持两种站内格式:
* A. `### Q:问题` 标题式(如 zh/quickstart/faq.md)
* B. `**Q:问题**` 加粗式(如 zh/appendix/faq.md)
* 返回 [{ q, a }];无匹配返回 []。
*/
export function extractFaqs(md) {
const body = String(md).replace(/^---[\s\S]*?\n---\s*/, "");
let faqs = [];
// 格式 A:按 `### Q:` 切块,答案截断到下一个标题
for (const part of body.split(/(?=^###\s*Q[::])/m)) {
const m = part.match(/^###\s*Q[::]\s*([^\n]+)\n+([\s\S]*)/);
if (!m) continue;
let a = m[2];
const cut = a.search(/^#{1,4}\s/m);
if (cut >= 0) a = a.slice(0, cut);
faqs.push({ q: cleanText(m[1]), a: cleanText(a) });
}
// 格式 B:按 `**Q:` 切块
if (!faqs.length) {
for (const part of body.split(/(?=^\*\*Q[::])/m)) {
const m = part.match(/^\*\*Q[::]\s*([^*]+)\*\*\s*\n+([\s\S]*)/);
if (!m) continue;
let a = m[2];
const cut = a.search(/^(?:#{1,4}\s|\*\*Q)/m);
if (cut >= 0) a = a.slice(0, cut);
faqs.push({ q: cleanText(m[1]), a: cleanText(a) });
}
}
return faqs.filter((f) => f.q && f.a);
}
@@ -1,14 +1,22 @@
import { defineConfig } from "vitepress";
import { enConfig } from "./en";
import { createSeoHooks } from "./seo";
import { sharedConfig } from "./shared";
import { zhConfig } from "./zh";
const seo = createSeoHooks(zhConfig.themeConfig?.sidebar);
export default defineConfig({
...sharedConfig,
ignoreDeadLinks: [
(link) => link.includes('localhost'),
],
// SEO:页面级 title/description、canonical/OG/JSON-LD、sitemap+lastmod
transformPageData: seo.transformPageData,
transformHead: seo.transformHead,
sitemap: seo.sitemap,
locales: {
root: { label: "简体中文", lang: "zh-CN", link: "/", ...zhConfig },
// root: { label: "English", lang: "en-US", link: "/", ...enConfig },
@@ -0,0 +1,311 @@
/**
* SEO/GEO 构建期钩子(仅 Node 构建时运行,不进客户端 bundle):
* - transformPageData:每页独立 title(去编号前缀)与 description(取 :::tip 首句)
* - transformHead:canonical / Open Graph / twitter 卡片 / JSON-LD 结构化数据
* - sitemap.transformItems:注入 lastmod(git 最后提交时间)并过滤 404
*
* 由 config/index.ts 通过 createSeoHooks(zhConfig.themeConfig?.sidebar) 挂载。
* 依赖 extract.mjs(纯 ESM,同被 scripts/generate-llms-txt.mjs 复用)。
*/
import fs from "node:fs";
import path from "node:path";
import { execSync } from "node:child_process";
import {
SITE_URL,
extractDescription,
extractFaqs,
stripNumberPrefix,
} from "./extract.mjs";
/** 与 vitepress HeadConfig 同构:[tag, attrs, innerHTML?] */
type HeadTuple = [string, Record<string, any>, string?];
interface SideItem {
text?: string;
link?: string;
items?: SideItem[];
}
interface PageDataLike {
relativePath?: string;
title?: string;
description?: string;
frontmatter?: Record<string, any>;
[k: string]: any;
}
interface TransformCtx {
pageData?: PageDataLike;
title?: string;
description?: string;
content?: string;
siteConfig?: { root?: string; srcDir?: string };
}
const ORG = {
"@type": "Organization",
name: "DjangoAdmin",
url: "https://www.djangoadmin.cn",
logo: `${SITE_URL}/logo.png`,
sameAs: [
"https://gitee.com/djangoadmin/DjangoAdmin_Tornado_AntdVue",
"https://www.djangoadmin.cn",
],
};
/** 相对路径 → 站内路由(cleanUrls 关闭,正文页带 .html) */
function routeOf(relativePath: string): string {
if (relativePath === "index.md") return "/";
if (relativePath === "404.md") return "/404.html";
return `/${relativePath.replace(/\.md$/, ".html")}`;
}
/** 去掉侧边栏文案前导 emoji/符号,保留 "1.1 项目介绍" 这类编号文字 */
function cleanLabel(s?: string): string {
return (s || "").replace(/^[^\p{L}\p{N}]+/u, "").trim();
}
/** 从侧边栏构建 route → 面包屑文案链(含分组小节) */
function buildCrumbMap(sidebar: unknown): Map<string, string[]> {
const map = new Map<string, string[]>();
const groups: SideItem[] = Array.isArray(sidebar)
? sidebar
: ((sidebar as Record<string, SideItem[]>)?.["/zh/"] ?? []);
const walk = (items: SideItem[], trail: string[]) => {
for (const item of items) {
if (item.link && item.link.startsWith("/")) {
map.set(item.link, [...trail, cleanLabel(item.text)]);
} else if (item.items?.length) {
walk(item.items, [...trail, cleanLabel(item.text)]);
}
}
};
for (const group of groups) {
walk(group.items ?? [], [cleanLabel(group.text)].filter(Boolean));
}
return map;
}
/** 从渲染后的 HTML 提取 H1 纯文本(JSON-LD headline 须与页面 H1 一致) */
function h1OfHtml(html?: string): string {
const m = (html || "").match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
if (!m) return "";
return m[1]
.replace(/<[^>]+>/g, "")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.trim();
}
/** 从 Markdown 源提取 H1 纯文本(去除行内 markdown 标记) */
function h1OfMd(md: string): string {
// 围栏感知:跳过 ``` 代码块内的 # 注释,取真正的页面 H1
let inFence = false;
for (const line of md.split("\n")) {
if (line.startsWith("```")) { inFence = !inFence; continue; }
if (!inFence && /^# (?!#)/.test(line)) {
return line
.replace(/^# /, "")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.trim();
}
}
return "";
}
export function createSeoHooks(sidebar: unknown) {
const crumbs = buildCrumbMap(sidebar);
const projectDirs: string[] = [];
let gitDates: Map<string, string> | null = null;
const readMd = (relativePath: string, srcDir?: string): string => {
try {
return fs.readFileSync(
path.resolve(srcDir || process.cwd(), relativePath),
"utf8",
);
} catch {
return "";
}
};
/**
* 项目内每个文件最后一次 git 提交的 ISO 时间。
* 单次 git log 调用构建全量映射(key = 相对路径,与 pageData.relativePath 一致);
* git 不可用时静默降级(sitemap 无 lastmod、JSON-LD 无 dateModified)。
*/
const getGitDates = (root: string): Map<string, string> => {
if (gitDates) return gitDates;
gitDates = new Map<string, string>();
try {
const out = execSync(
"git log --format=:::%cI --name-only --relative -n 500 -- .",
{
cwd: root,
encoding: "utf8",
maxBuffer: 64 * 1024 * 1024,
stdio: ["ignore", "pipe", "ignore"],
},
);
let current = "";
for (const line of out.split(/\r?\n/)) {
if (line.startsWith(":::")) {
current = line.slice(3).trim();
continue;
}
const file = line.trim();
if (file && current && !gitDates.has(file)) {
gitDates.set(file, current);
}
}
} catch {
// 降级:无 lastmod / dateModified
}
return gitDates;
};
/** 每页独立 title(去 "5.10 " 编号前缀,关键词前置)与 description(:::tip 首句) */
function transformPageData(pageData: PageDataLike, ctx?: any): Record<string, unknown> {
const srcDir: string | undefined = ctx?.siteConfig?.srcDir;
const root: string | undefined = ctx?.siteConfig?.root;
if (root && !projectDirs.includes(root)) projectDirs.push(root);
const out: Record<string, unknown> = {};
const rel = pageData.relativePath;
if (rel && !pageData.frontmatter?.description) {
const desc = extractDescription(readMd(rel, srcDir));
if (desc) out.description = desc;
}
if (pageData.title) {
out.title = stripNumberPrefix(pageData.title);
}
return out;
}
/** 构建期注入 canonical / OG / twitter / JSON-LD */
function transformHead(ctx: TransformCtx): HeadTuple[] {
const rel = ctx?.pageData?.relativePath || "";
if (!rel || rel === "404.md") return [];
const route = routeOf(rel);
const url = `${SITE_URL}${route}`;
const title = ctx.pageData?.title || ctx.title || "";
const desc = ctx.description || "";
const isHome = rel === "index.md";
const image = `${SITE_URL}/logo.png`;
const heads: HeadTuple[] = [
["link", { rel: "canonical", href: url }],
["meta", { property: "og:type", content: isHome ? "website" : "article" }],
["meta", { property: "og:site_name", content: "DjangoAdmin" }],
["meta", { property: "og:locale", content: "zh_CN" }],
["meta", { property: "og:title", content: title }],
["meta", { property: "og:description", content: desc }],
["meta", { property: "og:url", content: url }],
["meta", { property: "og:image", content: image }],
["meta", { property: "twitter:title", content: title }],
["meta", { property: "twitter:description", content: desc }],
["meta", { property: "twitter:url", content: url }],
["meta", { property: "twitter:image", content: image }],
];
// ---- JSON-LD ----
const root = ctx.siteConfig?.root || projectDirs[0] || process.cwd();
const srcDir = ctx.siteConfig?.srcDir || root;
const lastMod = getGitDates(root).get(rel);
const blocks: Record<string, any>[] = [];
if (isHome) {
blocks.push({
"@context": "https://schema.org",
"@type": "WebSite",
name: "DjangoAdmin 开发文档",
url: `${SITE_URL}/`,
inLanguage: "zh-CN",
description: desc,
});
blocks.push({ "@context": "https://schema.org", ...ORG });
} else {
const md = readMd(rel, srcDir);
const article: Record<string, any> = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: h1OfMd(md) || h1OfHtml(ctx.content) || title,
description: desc,
url,
inLanguage: "zh-CN",
mainEntityOfPage: { "@type": "WebPage", "@id": url },
publisher: ORG,
};
if (lastMod) article.dateModified = lastMod;
blocks.push(article);
const trail = crumbs.get(route);
if (trail?.length) {
const items: Record<string, any>[] = [
{ "@type": "ListItem", position: 1, name: "首页", item: `${SITE_URL}/` },
];
trail.forEach((name, i) => {
const node: Record<string, any> = {
"@type": "ListItem",
position: i + 2,
name,
};
if (i === trail.length - 1) node.item = url;
items.push(node);
});
blocks.push({
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items,
});
}
const faqs = extractFaqs(md);
if (faqs.length) {
blocks.push({
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((f) => ({
"@type": "Question",
name: f.q,
acceptedAnswer: { "@type": "Answer", text: f.a },
})),
});
}
}
// 转义 "<",防止内容中出现 </script> 破坏注入
const json = JSON.stringify(blocks).replace(/</g, "\\u003c");
heads.push(["script", { type: "application/ld+json" }, json]);
return heads;
}
/** sitemap.xml:过滤 404/README,注入 git lastmod */
const sitemap = {
hostname: SITE_URL,
transformItems(items: { url: string; lastmod?: string }[]) {
const root = projectDirs[0] || process.cwd();
const dates = getGitDates(root);
const out: { url: string; lastmod?: string }[] = [];
for (const item of items) {
// ⚠ vitepress 传入的 item.url 是相对路径("404.html"、"zh/x.html"、""),
// 无前导斜杠也无域名——hostname 由 SitemapStream 事后拼接。
if (/(^|\/)404\.html$/.test(item.url)) continue; // 404 页不收录
if (/(^|\/)README\.html$/.test(item.url)) continue; // 仓库 README 非文档页
const p = item.url.replace(/^\//, "");
const rel =
p === "" || p === "index.html" ? "index.md" : p.replace(/\.html$/, ".md");
const lastmod = dates.get(rel);
out.push(lastmod ? { ...item, lastmod } : item);
}
return out;
},
};
return { transformPageData, transformHead, sitemap };
}
@@ -1,6 +1,6 @@
import { defineConfig, HeadConfig } from "vitepress";
export const META_IMAGE = "/social.png";
export const META_IMAGE = "/logo.png";
const rControl = /[\u0000-\u001f]/g;
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g;
@@ -23,6 +23,8 @@ export const slugify = (str: string): string =>
export const sharedConfig = defineConfig({
title: "小蚂蚁云",
// 页面 <title> 后缀:「环境准备 | DjangoAdmin 开发文档」(index.md frontmatter 可覆盖)
titleTemplate: "DjangoAdmin 开发文档",
appearance: "light",
markdown: {
@@ -1,8 +1,9 @@
import type { DefaultTheme, LocaleSpecificConfig } from "vitepress";
export const META_URL = "https://www.djangoadmin.cn";
export const META_TITLE = "企业级中台解决方案";
export const META_DESCRIPTION = "企业级中台解决方案";
export const META_URL = "http://docs.tornado.antdvue.djangoadmin.cn";
export const META_TITLE = "DjangoAdmin Tornado+AntDesign 开发文档";
export const META_DESCRIPTION =
"DjangoAdmin Tornado+AntDesign 企业级中台解决方案开发者文档";
export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
description: META_DESCRIPTION,
+12
View File
@@ -0,0 +1,12 @@
---
head:
- - meta
- name: robots
content: noindex
---
# 页面不存在
抱歉,您访问的页面不存在或已被移动。
[返回首页](/) · [浏览文档](/zh/introduction)
+2 -1
View File
@@ -5,7 +5,8 @@
"type": "module",
"scripts": {
"docs": "vitepress dev .",
"docs:build": "vitepress build .",
"docs:llms": "node scripts/generate-llms-txt.mjs",
"docs:build": "node scripts/generate-llms-txt.mjs && vitepress build .",
"docs:preview": "vitepress preview ."
},
"dependencies": {
+168
View File
@@ -0,0 +1,168 @@
# DjangoAdmin Tornado+AntDesign 开发文档
> DjangoAdmin Tornado+AntDesign 企业级中台解决方案开发者文档。内置 RBAC 权限体系、代码生成器等开箱即用功能,支持五种数据库一键切换。本文档共 138 篇,按章节组织。
## 1. 了解项目
- [更新日志](http://docs.tornado.antdvue.djangoadmin.cn/zh/changelog.html): 更新日志是项目演进的完整记录,帮助开发者和企业了解每个版本的能力变化与技术演进方向。
- [新手入门指南](http://docs.tornado.antdvue.djangoadmin.cn/zh/course.html): 详细的安装步骤请参考 环境准备 章节。
- [项目介绍](http://docs.tornado.antdvue.djangoadmin.cn/zh/introduction.html): DjangoAdmin 是企业级中台解决方案,后端基于 Tornado + SQLAlchemy,前端基于 Vue3 + AntDesign,内置 RBAC 权限、代码生成器等开箱即用功能,
- [项目目录结构](http://docs.tornado.antdvue.djangoadmin.cn/zh/struct.html): + 保持一致性:目录结构一旦确定,应保持一致性,避免随意更改。
- [为什么选择我们?](http://docs.tornado.antdvue.djangoadmin.cn/zh/why.html): 本框架提供开箱即用的企业级中台完整解决方案,免去从零搭建成本,助力中大型企业构建统一的数字化基座。
## 2. 快速入门
- [后端启动](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/backend.html): 启动后端前,请确保已完成 环境准备 章节中的所有软件安装。
- [数据库初始化](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/database.html): 数据库初始化前,请确保已完成以下准备工作: 1. 目标数据库服务已启动(如 MySQL 8.0+) 2. .env 文件中的 DB_DRIVER 和数据库连接信息已正确配置 3. Python
- [Docker 快速部署](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/docker.html): Docker 部署适合快速体验和生产环境。
- [环境变量速查表](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/env-vars.html): 1. .env.example 是环境变量模板文件,已提交到版本控制。
- [环境准备](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/environment.html): 1. 部分软件安装后需要设置本地环境变量,便于在终端中使用命令。
- [常见问题FAQ](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/faq.html): 遇到问题时,建议先查看终端日志输出和浏览器控制台(F12)的错误信息,通常能快速定位问题原因。
- [第一个接口调试](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/first-api.html): 开始调试前,请确保已完成以下步骤: 1. 后端服务已启动(参见 后端启动) 2. 数据库已初始化(参见 数据库初始化) 3. Redis 服务已启动
- [前端启动](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume1/frontend.html): 启动前端前,请确保已完成 环境准备 章节中的 Node.js 和 pnpm 安装,并已启动后端服务(参见 后端启动)。
## 3. 模块开发实战
- [完整代码清单](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/code-list.html): 本页汇总友链(Link)模块涉及的全部文件代码,开发时可快速查阅与复制对照。
- [示例模块(Example)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/example.html): src/modules/example/ 是一个完整的示例模块,演示了标准 CRUD 模块的六文件结构和开发模式。
- [常见问题与排错](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/faq.html): 本页汇总模块开发过程中的常见问题与排错方法,开发受阻时按症状索引即可定位。
- [前端 API 层](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/fe-api.html): 前端 API 层封装后端接口的 HTTP 请求,每个业务模块对应一个 API 文件,本页讲解基于 Axios 封装的编写规范。
- [前端路由与菜单](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/fe-router.html): 本页讲解前端路由与菜单机制——路由和菜单由后端动态返回,前端只需注册路由组件映射。
- [前端页面视图](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/fe-view.html): 前端页面视图是用户直接交互的界面,本页讲解 Vue3 + AntDesign 下一个完整 CRUD 页面的文件组成与编写方式。
- [请求处理器(Handler)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/handler.html): Handler 层是 HTTP 接口层,负责接收请求、校验权限与记录操作日志,编写新接口从本页开始。
- [数据模型(Model)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/model.html): BaseModel 是所有业务模型的基类,提供6个通用审计字段和 to_dict() 序列化方法。
- [概述与目标](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/overview.html): 其中 id、create_user、create_time、update_user、update_time、is_delete 由基类 BaseModel 自动提供,业务字段仅需定义 name、
- [权限节点配置](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/permission.html): 实际开发中,查询类权限(page、detail、list)通常合并为一个节点 sys:position:list,减少配置工作量。
- [Repository 层](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/repository.html): Repository 层是数据访问层,封装全部数据库查询操作,本页讲解基于 BaseRepository 基类的通用 CRUD 用法。
- [路由注册](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/router.html): 新模块只需两步: 1. 在 src/api/v1/ 下创建路由文件,定义 routes 列表 2. 在 router.py 的 _RESOURCE_MODULES 中追加模块导入 路由前缀
- [Pydantic Schema](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/schema.html): Schema 层负责请求数据校验,本页讲解基于 Pydantic v2 BaseModel 与 Field 的表单类定义规范。
- [Service 层](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume2/service.html): repo_cls 是 类属性,值为 Repository 类(不是实例)。
## 4. 架构设计
- [核心基类体系](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/base-classes.html): 四大基类各司其职,覆盖从数据模型到请求校验再到业务逻辑的完整链路。
- [缓存架构设计](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/cache.html): 采用两级缓存架构:第一级为进程内字典缓存(dict_util),第二级为 Redis 分布式缓存。
- [数据库设计](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/database-design.html): 所有业务表继承 base_model 基类,自动获得 6 个通用字段(id / create_user / create_time / update_user / update_time /
- [目录结构设计](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/directory.html): 目录结构遵循「按功能分组、按职责分层」的原则。
- [扩展性设计](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/extensibility.html): 遵循开闭原则(OCP):对扩展开放,对修改关闭。
- [分层架构设计](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/layer-arch.html): 采用经典的分层架构,核心原则是:依赖单向、职责单一、接口隔离。
- [整体架构图](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/overview.html): 本页从全局视角阐述系统整体技术架构,涵盖前端、后端、数据库与基础设施等核心组成,理解系统全貌时从本页开始。
- [请求完整链路](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/request-flow.html): 一个典型的 API 请求经过以下阶段:浏览器 → Nginx → Tornado → Handler 生命周期(set_default_headers → prepare →
- [安全架构设计](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/security.html): 纵深防御:认证、授权、限流、参数校验多层防护,单一防线失效时仍有其他机制兜底 最小权限:用户仅能访问已授权接口,角色权限按需分配,遵循"够用即可"原则 数据不落地:敏感信息(Token、
- [技术选型说明](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/tech-stack.html): Tornado 是 Python 生态中成熟的异步 Web 框架,基于自有事件循环实现非阻塞 I/O。
- [Tornado IOLoop 与异步模型](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume3/tornado-async.html): Tornado 基于单线程事件循环(IOLoop),Handler 方法默认为协程(async def)。
## 5. 开发指南
- [API 接口速查表](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/api-cheatsheet.html): API 接口速查表,按模块分组列出所有 RESTful 接口的请求方法、路径、说明和权限标识。
- [文章管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/article.html): 文章管理模块支持富文本编辑、分类关联、封面图片上传、图集管理等功能。
- [分类管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/category.html): 分类管理模块用于管理树形分类数据,如文章分类、产品分类等。
- [行政区划管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/city.html): 行政区划管理模块提供省市区三级行政区划数据的查询和管理能力,支持树形结构展示和下拉选择。
- [公告与链接管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/content.html): 公告、链接、城市是三个简单 CRUD 内容模块,均继承 BaseService,提供标准的增删改查接口。
- [部门/职级/岗位管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/dept.html): 部门、职级、岗位是组织架构管理的三个核心模块。
- [文件模板管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/file-template.html): 文件模板管理模块提供文件导入导出模板的管理能力,用于维护各种业务场景的 Excel/Word 等模板文件。
- [定时任务日志](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/job-log.html): 定时任务日志模块记录定时任务的执行历史,包括执行时间、执行结果、耗时等信息。
- [定时任务](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/job.html): 定时任务模块基于 APScheduler 实现,支持 Cron 表达式调度、任务执行日志、动态启停等功能。
- [菜单管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/menu.html): 菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。
- [参数管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/param.html): 参数管理模块用于管理动态系统参数,与系统配置模块类似但更侧重于业务参数的灵活配置。
- [角色管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/role.html): 角色是 RBAC 权限模型的核心,连接用户和菜单(权限)。
- [用户管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/business/user.html): 用户管理是系统的核心模块,属于复杂业务模块。
- [认证流程实战](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/auth-flow.html): 登录 → Token 获取 → 权限校验 → Token 刷新 → 退出登录的完整认证链路实战说明。
- [验证码机制](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/captcha.html): 验证码是防止自动化攻击的重要安全手段。
- [系统配置管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/config.html): 系统配置模块用于管理动态的键值对配置项,与数据字典类似但更偏向系统级参数。
- [CORS 跨域配置](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/cors.html): CORS(Cross-Origin Resource Sharing)跨域资源共享是前后端分离架构必须处理的问题。
- [演示模式](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/demo-mode.html): 演示模式用于在线体验环境,开启后所有写操作(新增、编辑、删除等)将被拦截,返回"演示环境,暂无操作权限"提示。
- [数据字典使用指南](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/dict-guide.html): 数据字典的完整使用流程,从新增字典类型到在代码中使用字典数据的实操指南。
- [字典模块](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/dict.html): 数据字典用于管理系统中的枚举值和配置项,如状态、类型、性别等。
- [异常与响应处理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/exception.html): 定义了统一的响应格式和全局异常处理机制。
- [优雅关闭](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/graceful-shutdown.html): 优雅关闭确保应用在收到终止信号时,能够完成正在处理的请求、关闭数据库连接、停止定时任务调度器后再退出,避免请求中断和资源泄漏。
- [用户认证(JWT)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/jwt.html): JWT(JSON Web Token)是的核心认证机制。
- [登录日志](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/login-log.html): 登录日志记录每次登录尝试(成功/失败),包含登录账号、IP地址、浏览器信息、登录时间等。
- [登录防爆破](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/login-protect.html): 登录防爆破机制基于 Redis 实现同一 IP + 用户名维度的连续失败计数与锁定,防止暴力破解密码。
- [操作日志](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/operation-log.html): 操作日志装饰器自动记录写请求(POST/PUT/DELETE/PATCH)的操作日志,无需手动埋点。
- [权限管理(RBAC)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/rbac.html): 采用基于角色的访问控制(RBAC)模型,通过用户-角色-菜单三层关系实现细粒度的权限管理。
- [链路追踪(TraceID)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/trace-id.html): 链路追踪通过为每个请求分配唯一的 TraceID,将同一请求的所有日志串联起来,方便问题排查和性能分析。
- [参数验证(Pydantic)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/core/validator.html): 使用 Pydantic v2 进行请求参数验证。
- [API 层开发](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/api.html): 前端 API 层位于 src/api/ 目录,封装所有与后端的 HTTP 请求。
- [前端构建](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/build.html): 前端使用 Vite 构建,支持开发服务器和生产构建。
- [公共组件库](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/components.html): 项目提供丰富的公共组件,位于 ui/src/components/ 目录,基于 AntDesign 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。
- [BasicForm 表单](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/form.html): BasicForm 位于 src/components/Form/src/BasicForm.vue,基于 AntDesign 的 a-form 二次封装,通过 schemas 配置化定义表单字段,
- [Hooks 组合式函数](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/hooks.html): 项目封装了一系列组合式函数(Hooks),位于 src/hooks/ 和 src/utils/ 目录,提供权限检查、异步 loading、防重复提交、实时时钟、项目配置等可复用逻辑。
- [国际化](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/i18n.html): 当前项目仅支持中文界面,AntDesign 使用中文语言包(zh-cn),所有界面文本直接硬编码在组件中。
- [Modal 弹窗](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/modal.html): 项目提供 BasicModal 组件(src/components/Modal/src/BasicModal.vue)和原生 a-modal 两种弹窗用法。
- [权限指令 v-perm](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/perm-directive.html): v-perm 和 v-perms 是项目自定义的 Vue 指令,用于按钮级别的权限控制,无权限时自动从 DOM 中移除元素。
- [路由与菜单](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/router.html): 前端路由基于 Vue Router 4 实现动态路由生成,菜单数据从后端获取,通过 generator-routers.ts 递归生成路由表,路由守卫控制登录状态和权限。
- [前端环境与结构](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/setup.html): 前端基于 Vue3 + AntDesign 构建,使用 pnpm 作为包管理工具,Vite 作为构建工具,TypeScript 作为开发语言。
- [Store 状态管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/store.html): 前端使用 Pinia 进行状态管理,核心模块包括 user(用户信息与权限)、asyncRoute(动态路由与菜单)、projectSetting(项目配置)、designSetting(主题设计)等。
- [样式与主题](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/style.html): 前端样式方案采用 Tailwind CSS + SCSS + AntDesign 主题覆盖,支持深色模式切换。
- [BasicTable 表格](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/table.html): BasicTable 位于 src/components/Table/src/Table.vue,基于 AntDesign 的 a-table 二次封装,提供列定义、数据请求、分页、操作列、多选、
- [工具函数](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/utils.html): 前端工具函数位于 src/utils/ 目录,提供认证管理、本地存储、日期格式化、树结构操作、文件下载等通用能力。
- [页面视图开发](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/frontend/view.html): 页面视图位于 src/views/ 目录,每个业务模块遵循 index.vue(列表页)+ edit.vue(编辑弹窗)的标准模式,辅以 columns.ts(列定义)和
- [接口响应规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/integration/api-contract.html): 后端所有接口统一返回 R.ok() / R.failed() 格式的 JSON 响应,前端通过 VAxios 的 transformRequestData 拦截器统一解包处理。
- [字典数据联调](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/integration/dict-sync.html): 字典数据通过后端字典 API 获取,前端在下拉选择器中渲染选项。
- [权限节点联动](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/integration/perm-sync.html): 前后端权限通过统一的权限节点字符串联动,后端 @permission_required 校验权限,前端 v-perm 指令和 TableAction 的 auth 属性控制按钮显示。
- [代理配置](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/integration/proxy.html): 开发环境通过 Vite 代理转发 API 请求到后端,生产环境通过 Nginx 代理。
- [文件上传联调](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/integration/upload-sync.html): 文件上传涉及前端 Upload 组件、后端上传接口和 URL 拼接三个环节,需要前后端协同配置。
- [请求上下文(ContextVar)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/context.html): src/core/context.py 使用 Python 标准库 contextvars 管理请求级别的上下文信息,包括当前用户 ID、用户名、姓名和链路追踪 ID。
- [数据库会话](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/db-session.html): 数据库会话通过 scoped_session 实现线程级隔离,Repository 层的写操作自动 commit,BaseHandler.on_finish() 兜底关闭会话。
- [字典缓存工具](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/dict-util.html): src/common/utils/dict_util.py 提供业务字典的缓存读取能力,从 Redis 缓存中获取字典数据,缓存未命中时从数据库加载。
- [导入导出](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/excel.html): 基于 openpyxl 实现 Excel 导入导出功能,支持通过 file_template 模块管理导入模板配置。
- [通用 Helper 函数](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/helpers.html): src/common/utils/helpers.py 提供项目中广泛使用的通用工具函数,包括 ID 解析、OS/浏览器检测、参数脱敏等。
- [IP 归属地解析](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/ip-region.html): src/common/utils/ip2region.py 基于 ip2region 离线数据库,提供 IP 地址到省市区的归属地查询能力,无需网络请求。
- [日志系统](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/logger.html): 通过 Python 标准库 logging 实现结构化日志,支持控制台 + 文件双输出,日志文件按大小自动轮转。
- [密码工具](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/password.html): 采用 bcrypt 双重加盐方案存储密码,源码位于 src/core/password.py。
- [权限缓存](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/perm-cache.html): 通过 Redis 实现用户权限和菜单的缓存,采用用户级缓存 + 全局版本号双层策略。
- [统一响应封装(R 对象)](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/response.html): src/core/response.py 提供统一的 API 响应封装,所有接口返回格式为 {code, data, msg, ok}。
- [富文本过滤](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/rich-text.html): 富文本编辑器内容存在存储型 XSS 风险,通过 bleach 库实现 HTML 白名单清洗,在落盘前统一过滤危险内容。
- [文件上传完整流程](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/upload-guide.html): 富文本编辑器(如 TinyMCE、CKEditor、WangEditor 等)的图片上传同样使用此接口。
- [文件上传](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume4/tools/upload.html): 提供本地文件上传功能,支持图片和通用文件上传,内置扩展名白名单、大小限制、魔术字节校验和路径遍历防护。
## 6. 代码生成器
- [CLI 命令行使用](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/cli.html): 代码生成器 CLI 模式的使用方法,包括单表生成、批量生成、预览模式和配置导出。
- [代码生成引擎](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/code-engine.html): 代码生成引擎的工作原理,包括配置解析、模板渲染、文件写入和自动注册的完整流程。
- [配置文件详解](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/config.html): 代码生成器 JSON 配置文件的格式说明,包括模块名称、字段属性、字典编码等配置项的详细解释。
- [实战案例](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/demo.html): 以 tornado_example 演示表为完整案例,演示从建表到生成到使用的全流程。
- [常见问题](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/faq.html): 代码生成器使用过程中常见问题的原因分析和解决方案。
- [概述与架构](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/overview.html): 代码生成器的整体架构、核心模块和生成流程概述。
- [表结构解析引擎](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/parser.html): 表结构解析引擎的工作原理,从数据库读取表元数据并智能推断字段用途的过程。
- [自定义模板](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/templates.html): 代码生成器模板的 Jinja2 语法说明,包括模板变量、字段属性和自定义模板的开发方法。
- [Web 管理界面](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume5/web-ui.html): 代码生成器 Web 管理界面的使用方法,通过可视化界面操作单表生成和批量生成。
## 7. 运维部署
- [备份与恢复](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/backup.html): - 3 份数据副本(1 份生产 + 2 份备份) - 2 种不同存储介质(如本地磁盘 + 远程存储) - 1 份异地备份(防机房级灾难)
- [跨库数据迁移](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/db-cross-migrate.html): 项目提供 scripts/migrate_db.py 脚本,支持在不同数据库之间迁移数据(如 MySQL → PostgreSQL / SQL Server / SQLite / Oracle)。
- [数据库迁移](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/db-migrate.html): - 结构迁移:Alembic(自动生成 DDL 差异) - 数据迁移:scripts/migrate_db.py(跨库数据泵) - 初始化:scripts/init_db.py(全新安装一键建库)
- [部署 Checklist](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/deploy-checklist.html): 从服务器准备到应用上线的完整部署操作清单,适用于生产环境首次部署和后续版本更新。
- [Docker容器化](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/docker.html): - 环境一致性:消除"在我机器上能跑"的问题 - 快速部署:一条命令启动完整服务 - 资源隔离:应用与宿主机环境解耦
- [环境规划](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/env-plan.html): 各环境之间保持代码一致,仅通过环境变量(.env)区分配置。
- [健康检查与监控](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/monitoring.html): `` 1. 应用层:健康检查端点、业务指标 2. 系统层:CPU/内存/磁盘/网络 3. 中间件:MySQL/Redis 连接状态 4. 日志:集中式日志收集与分析 ``
- [Nginx 反向代理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/nginx.html): `` 客户端 → Nginx(80/443) ├─ / → 前端静态资源(dist/) ├─ /api/v1/... → Tornado 后端(8041) └─ /api/file/ →
- [性能调优](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/performance.html): 1. 先测量,后优化:用数据说话,避免盲目调参 2. 瓶颈优先:找到最大瓶颈,集中优化 3. 渐进式:每次只调整一个参数,观察效果
- [安全加固](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/security-hardening.html): - 最小权限:只开放必要的端口和权限 - 纵深防御:多层安全措施叠加 - 默认安全:配置默认值应为安全选项
- [Supervisor进程管理](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/supervisor.html): - Linux 服务器直接部署(非 Docker) - 需要进程自动重启和日志轮转 - 管理多个相关进程
- [故障排查手册](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/troubleshooting.html): 1. 查看日志:tail -f logs/supervisor.out.log 或 docker-compose logs -f 2. 检查配置:.env 文件、数据库连接、Redis 连接 3.
- [版本升级流程](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume6/upgrade.html): 1. 先备份,后升级 2. 先测试环境,后生产环境 3. 每次升级可回滚 4. 低峰期执行升级
## 8. 规范标准
- [API响应规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/api-response.html): - 所有接口必须通过 R.ok() 或 R.failed() 返回,禁止直接返回 dict - 成功响应 code=0,失败响应 code=1 - 响应体始终包含 code、data、msg、ok
- [代码审查清单](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/code-review.html): - 每次提交必须经过至少一人审查 - 审查关注点:正确性 > 安全性 > 可维护性 > 性能 - 审查意见应具体、可操作,避免主观评价
- [删除规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/deletion.html): - is_delete = 0:正常记录(默认值) - is_delete = 1:已删除记录 - 所有查询方法自动过滤 is_delete = 0,业务层无需关心
- [文档规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/doc-standard.html): - 文档与代码同步更新 - 使用中文编写(项目约定) - 示例代码可直接运行
- [前端 API 规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/fe-api.html): - 每个后端模块对应一个 API 文件 - 函数命名统一使用 名词 + 动词 格式(与后端保持一致) - 请求路径使用相对路径(/position/page),不含 /api/v1 前缀(由
- [前端页面规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/fe-component.html): - Vue3(Composition API + <script setup>) - AntDesign - 自定义组件(BasicTable / BasicForm / TableAction /
- [Git提交规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/git.html): `` <type>(<scope>): <subject> <body> <footer> ``
- [Handler 层规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/handler.html): - Handler 只做请求分发,不含业务逻辑 - 通过装饰器控制权限和演示模式 - 委托 Service 处理业务逻辑 - 返回 R 封装的响应
- [分页规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/pagination.html): - 前端传递 pageNo(页码,从 1 开始)和 pageSize(每页条数) - 后端返回 {records, total, size, current, pages} 结构 - 默认每页 10
- [Pydantic Schema规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/pydantic.html): - 请求体参数必须声明为 Pydantic 模型(Form 类) - 字段约束使用 Field(...) 定义 - 自定义校验使用 @field_validator - 唯一性校验在 Schema
- [Repository层规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/repository.html): - Repository 只做数据访问原语,不处理 R 封装/序列化/业务逻辑 - 软删除由基类自动处理,子类无需关心 - 查询方法自动跳过 None 值条件
- [Service层规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/service.html): BaseService 采用模板方法模式:基类定义流程骨架,子类通过声明差异点和覆盖钩子定制行为。
- [测试规范](http://docs.tornado.antdvue.djangoadmin.cn/zh/volume7/testing.html): 1. 契约测试(Contract):验证路由、权限、表名等契约不变 2. 单元测试(Unit):验证单个函数/方法的逻辑正确性 3. 集成测试(Integration):验证模块间协作、真实数据库操作
## 附录
- [常见问题汇总](http://docs.tornado.antdvue.djangoadmin.cn/zh/faq.html): 本页汇总了项目各模块的常见问题及解决方案,按场景分类索引。
- [免责声明](http://docs.tornado.antdvue.djangoadmin.cn/zh/other/declare.html): 使用本产品前必读的法律声明,明确产品性质、使用限制与责任划分,一旦使用本产品即视为接受本声明全部条款。
- [广告赞助](http://docs.tornado.antdvue.djangoadmin.cn/zh/other/sponsor.html): 1. 投放广告不得与官方全系产品存在结构性冲突和竞争 2. 广告内容需符合国家法律法规,不得包含虚假、误导性信息 3. 广告素材需经官方审核通过后方可上线,审核周期 1-3 个工作日 4.
@@ -0,0 +1,33 @@
# DjangoAdmin Tornado+AntDesign 开发文档
User-agent: *
Allow: /
# 显式放行生成式引擎 / AI 爬虫(GEO)
User-agent: GPTBot
Allow: /
User-agent: ChatGPT-User
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: Claude-Web
Allow: /
User-agent: anthropic-ai
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: Bytespider
Allow: /
User-agent: bingbot
Allow: /
Sitemap: http://docs.tornado.antdvue.djangoadmin.cn/sitemap.xml
@@ -0,0 +1,122 @@
#!/usr/bin/env node
/**
* 生成 public/llms.txt(llmstxt.org 标准)——AI 爬虫的站点摘要索引。
*
* 数据源:zh 目录下全部 Markdown 的 H1 + :::tip 概述首句(与 meta
* description 同源,复用 .vitepress/config/extract.mjs)。按侧边栏章节分组。
*
* 用法:node scripts/generate-llms-txt.mjs
* 已挂入 package.json 的 docs:build 前置步骤。
*/
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
SITE_URL,
SITE_DESCRIPTION,
extractDescription,
} from "../.vitepress/config/extract.mjs";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const ZH = path.join(ROOT, "zh");
/** 顶层目录 → 侧边栏章节名(与 .vitepress/config/zh.ts 保持一致) */
const SECTIONS = [
["", "1. 了解项目"],
["volume1", "2. 快速入门"],
["volume2", "3. 模块开发实战"],
["volume3", "4. 架构设计"],
["volume4", "5. 开发指南"],
["volume5", "6. 代码生成器"],
["volume6", "7. 运维部署"],
["volume7", "8. 规范标准"],
["other", "附录"],
];
function walk(dir, out = []) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, entry.name);
if (entry.isDirectory()) walk(p, out);
else if (entry.name.endsWith(".md")) out.push(p);
}
return out;
}
/** 提取首个 H1 文本 */
function h1Of(md) {
// 围栏感知:跳过 ``` 代码块内的 # 注释,取真正的页面 H1
let inFence = false;
for (const line of md.split("\n")) {
if (line.startsWith("```")) { inFence = !inFence; continue; }
if (!inFence && line.startsWith("# ") && !line.startsWith("## ")) {
return line.slice(2).trim();
}
}
return "";
}
/** H1 编号排序键:"5.10 x" → [5,10];无编号排最后 */
function sortKey(h1) {
const m = h1.match(/^(\d+(?:\.\d+)*)/);
if (m) return m[1].split(".").map(Number);
const letter = h1.match(/^([A-Z])\./);
if (letter) return [1000 + letter[1].charCodeAt(0)];
return [9999];
}
function cmpKey(a, b) {
const len = Math.max(a.length, b.length);
for (let i = 0; i < len; i++) {
const x = a[i] ?? 0;
const y = b[i] ?? 0;
if (x !== y) return x - y;
}
return 0;
}
const files = walk(ZH);
const groups = new Map(SECTIONS.map(([dir, label]) => [label, []]));
for (const file of files) {
const rel = path.relative(ZH, file).replace(/\\/g, "/");
const topDir = rel.includes("/") ? rel.split("/")[0] : "";
const base = rel.split("/").pop();
const label = (topDir === "" && base === "faq.md") ? "附录"
: SECTIONS.find(([dir]) => dir === topDir)?.[1] ?? "附录";
const md = fs.readFileSync(file, "utf8");
const h1 = h1Of(md);
if (!h1) continue;
const desc = extractDescription(md);
const url = `${SITE_URL}/zh/${rel.replace(/\.md$/, ".html")}`;
groups.get(label).push({ h1, desc, url, key: sortKey(h1) });
}
const lines = [
"# DjangoAdmin Tornado+AntDesign 开发文档",
"",
`> ${SITE_DESCRIPTION}。内置 RBAC 权限体系、代码生成器等开箱即用功能,支持五种数据库一键切换。本文档共 ${files.length} 篇,按章节组织。`,
"",
];
// 按章节顺序输出,合并重复 label(appendix/other 同为「附录」,只输出一次)
const printed = new Set();
for (const [, label] of SECTIONS) {
if (printed.has(label)) continue;
const items = groups.get(label);
if (!items?.length) continue;
printed.add(label);
lines.push(`## ${label}`, "");
items.sort((a, b) => cmpKey(a.key, b.key));
for (const { h1, desc, url } of items) {
lines.push(desc ? `- [${h1}](${url}): ${desc}` : `- [${h1}](${url})`);
}
lines.push("");
}
const out = path.join(ROOT, "public", "llms.txt");
fs.writeFileSync(out, lines.join("\n"), "utf8");
console.log(
`llms.txt: ${files.length} 页 → ${path.relative(ROOT, out)} (${printed.size} 个章节)`,
);
+2
View File
@@ -1,3 +1,5 @@
# 新手入门指南
:::warning 特别提醒
官方精心制作本教程,目的在于方便用户快速掌握软件产品的使用和部署。通过此教程,刚入行的开发者也可以快速掌握并投入产品研发。本地部署时务必请耐心阅读文档操作。
:::
@@ -1,3 +1,9 @@
# 项目介绍
:::tip 概述
DjangoAdmin 是企业级中台解决方案,后端基于 Tornado + SQLAlchemy,前端基于 Vue3 + AntDesign,内置 RBAC 权限、代码生成器等开箱即用功能,支持五种数据库一键切换。
:::
<div align="center">
<p align="center">
<img src="/logo.png" height="150" alt="logo"/>
@@ -1,5 +1,9 @@
# 免责声明
:::tip 概述
使用本产品前必读的法律声明,明确产品性质、使用限制与责任划分,一旦使用本产品即视为接受本声明全部条款。
:::
:::danger 重要提示
任何用户在使用本团队研发的系列软件产品(以下简称「本产品」)前,请仔细阅读并透彻理解本声明。选择不使用本产品,可随时停止使用;一旦使用本产品,即视为对本声明全部内容的认可和接受。
:::
+2
View File
@@ -1,3 +1,5 @@
# 项目目录结构
## 目录说明
在软件开发过程中,优秀的软件目录结构对于项目的组织、开发、维护和扩展至关重要,合理的目录结构能够显著提升开发效率和代码可维护性。
@@ -1,5 +1,9 @@
# 完整代码清单
:::tip 概述
本页汇总友链(Link)模块涉及的全部文件代码,开发时可快速查阅与复制对照。
:::
本章汇总友链(Link)模块涉及的全部文件,便于开发时快速查阅和复制。
## 后端文件清单
@@ -1,5 +1,9 @@
# 常见问题与排错
:::tip 概述
本页汇总模块开发过程中的常见问题与排错方法,开发受阻时按症状索引即可定位。
:::
本章汇总模块开发过程中的常见问题和解决方案。
## 后端问题
@@ -1,5 +1,9 @@
# 前端 API 层
:::tip 概述
前端 API 层封装后端接口的 HTTP 请求,每个业务模块对应一个 API 文件,本页讲解基于 Axios 封装的编写规范。
:::
前端 API 层负责封装后端接口的 HTTP 请求。每个业务模块对应一个 API 文件,使用项目封装的 Axios 工具发送请求。
## 文件位置
@@ -1,5 +1,9 @@
# 前端路由与菜单
:::tip 概述
本页讲解前端路由与菜单机制——路由和菜单由后端动态返回,前端只需注册路由组件映射。
:::
前端路由定义页面的访问路径,菜单是路由的可视化展示。项目的路由和菜单由后端动态返回,前端只需注册路由组件映射。
## 路由配置
@@ -1,5 +1,9 @@
# 前端页面视图
:::tip 概述
前端页面视图是用户直接交互的界面,本页讲解 Vue3 + AntDesign 下一个完整 CRUD 页面的文件组成与编写方式。
:::
前端页面视图是用户直接交互的界面。项目使用 Vue3 + AntDesign + Vite 构建。一个完整的 CRUD 页面由 4 个文件组成:
```
@@ -1,5 +1,9 @@
# 请求处理器(Handler)
:::tip 概述
Handler 层是 HTTP 接口层,负责接收请求、校验权限与记录操作日志,编写新接口从本页开始。
:::
Handler 层是 HTTP 接口层,负责接收请求、校验权限、记录操作日志,然后委托给 Service 层处理。每个业务模块对应一个 `handlers.py` 文件,所有 Handler 继承 `BaseHandler`。
## 文件位置
@@ -1,5 +1,9 @@
# Repository 层
:::tip 概述
Repository 层是数据访问层,封装全部数据库查询操作,本页讲解基于 BaseRepository 基类的通用 CRUD 用法。
:::
Repository 层是数据访问层,封装所有数据库查询操作。项目通过 `BaseRepository` 基类提供通用 CRUD 能力,子类通常只需声明类名,无需编写任何代码。
## 文件位置
@@ -1,5 +1,9 @@
# Pydantic Schema
:::tip 概述
Schema 层负责请求数据校验,本页讲解基于 Pydantic v2 BaseModel 与 Field 的表单类定义规范。
:::
Schema 层负责请求数据的校验。项目使用 Pydantic v2 的 `BaseModel` 定义表单类,通过 `Field` 约束字段格式,Tornado 在接收请求体时自动执行校验。
## 文件位置
@@ -1,5 +1,9 @@
# 整体架构图
:::tip 概述
本页从全局视角阐述系统整体技术架构,涵盖前端、后端、数据库与基础设施等核心组成,理解系统全貌时从本页开始。
:::
本章从全局视角阐述系统的整体技术架构,涵盖前端、后端、数据库及基础设施等核心组成部分。
## 架构总览
@@ -1,3 +1,5 @@
# 文章管理
:::tip 文章管理
文章管理模块支持富文本编辑、分类关联、封面图片上传、图集管理等功能。适用于新闻、公告、帮助文档等内容管理场景。本模块是典型的「带文件处理」业务模块,涵盖了封面迁移、图集处理、富文本 XSS 清洗等核心机制。
:::
@@ -1,3 +1,5 @@
# 分类管理
:::tip 分类管理
分类管理模块用于管理树形分类数据,如文章分类、产品分类等。通过 `parent_id` 字段实现无限级分类。
:::
@@ -1,3 +1,5 @@
# 公告与链接管理
:::tip 公告/链接/城市
公告、链接、城市是三个简单 CRUD 内容模块,均继承 BaseService,提供标准的增删改查接口。适用于系统公告、友情链接、城市数据等场景。
:::
@@ -1,3 +1,5 @@
# 部门/职级/岗位管理
:::tip 部门/职级/岗位
部门、职级、岗位是组织架构管理的三个核心模块。部门采用树形结构,职级和岗位为简单 CRUD 模块,均继承 `BaseService`。用户表通过 `dept_id`、`position_id`、`level_id` 三个外键关联这三个模块。
:::
@@ -1,3 +1,5 @@
# 定时任务
:::tip 定时任务
定时任务模块基于 APScheduler 实现,支持 Cron 表达式调度、任务执行日志、动态启停等功能。包含 job(任务定义)、job_log(执行日志)、scheduler(调度器)三个子模块。
:::
@@ -1,3 +1,5 @@
# 菜单管理
:::tip 菜单管理
菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。支持目录/菜单(type=0)和权限按钮节点(type=1)两种类型,通过树形结构组织。权限节点由系统根据前端勾选自动生成,采用 `sys:module:action` 三段式命名。
:::
@@ -1,3 +1,5 @@
# 参数管理
:::tip 参数管理
参数管理模块用于管理动态系统参数,与系统配置模块类似但更侧重于业务参数的灵活配置。支持按参数键查询、修改,无需重启服务即可生效。本模块是典型的「BaseService 简单模块」,通过声明差异点即可获得完整 CRUD 能力。
:::
@@ -1,3 +1,5 @@
# 角色管理
:::tip 角色管理
角色是 RBAC 权限模型的核心,连接用户和菜单(权限)。通过为角色分配菜单权限,再将角色分配给用户,实现灵活的权限管理。角色删除时需清理关联的用户角色和角色菜单数据,并即时失效受影响用户的权限缓存。
:::
@@ -1,3 +1,5 @@
# 用户管理
:::tip 用户管理
用户管理是系统的核心模块,属于复杂业务模块。包含用户 CRUD、角色关联、密码加密、行政区划、Excel 导入导出等功能。与简单 CRUD 模块不同,用户管理使用自定义 service 函数而非继承 BaseService 的类方法。
:::
@@ -1,3 +1,5 @@
# 验证码机制
:::tip 验证码
验证码是防止自动化攻击的重要安全手段。使用 Pillow 生成图片验证码,存储在 Redis 中,支持防刷限流和万能验证码(演示模式)。源码位于 `src/common/utils/captcha.py`。
:::
@@ -1,3 +1,5 @@
# 系统配置管理
:::tip 系统配置
系统配置模块用于管理动态的键值对配置项,与数据字典类似但更偏向系统级参数。支持分组管理,可通过接口动态修改,无需重启服务。模块位于 `src/modules/config/` 和 `src/modules/config_item/`,路由位于 `src/api/v1/config.py` 和 `config_item.py`。
:::
@@ -1,3 +1,5 @@
# 演示模式
:::tip 演示模式
演示模式用于在线体验环境,开启后所有写操作(新增、编辑、删除等)将被拦截,返回"演示环境,暂无操作权限"提示。通过 `TORNADO_DEMO` 环境变量控制。装饰器位于 `src/common/middleware/access_decorators.py`。
:::
@@ -1,3 +1,5 @@
# 字典模块
:::tip 数据字典
数据字典用于管理系统中的枚举值和配置项,如状态、类型、性别等。通过字典编码统一管理,前端可通过 `useDictStore` 获取字典选项,避免硬编码。后端读取工具位于 `src/common/utils/dict_util.py`,内置进程内 TTL 缓存。
:::
@@ -1,3 +1,5 @@
# 异常与响应处理
:::tip 全局异常与响应
定义了统一的响应格式和全局异常处理机制。所有 API 返回 `{code, data, msg, ok}` 标准格式,异常被捕获后也转换为相同格式。响应函数位于 `src/core/response.py`,异常类型位于 `src/core/exceptions.py`。
:::
@@ -1,3 +1,5 @@
# 用户认证(JWT)
:::tip JWT认证
JWT(JSON Web Token)是的核心认证机制。用户登录成功后获取 Token,后续请求通过 `Authorization: Bearer <token>` 头携带 Token 进行身份验证。项目内置了 Token 生成、解析、刷新和黑名单管理功能,源码位于 `src/core/jwt.py`。
:::
@@ -1,3 +1,5 @@
# 登录日志
:::tip 登录日志
登录日志记录每次登录尝试(成功/失败),包含登录账号、IP地址、浏览器信息、登录时间等。支持 IP 地理位置解析(ip2region)和 User-Agent 解析。登录逻辑位于 `src/modules/auth/service.py`,日志记录位于 `src/modules/login_log/service.py`。
:::
@@ -1,3 +1,5 @@
# 操作日志
:::tip 操作日志
操作日志装饰器自动记录写请求(POST/PUT/DELETE/PATCH)的操作日志,无需手动埋点。源码位于 `src/common/middleware/operation_log.py`。
:::
@@ -1,3 +1,5 @@
# 权限管理(RBAC)
:::tip RBAC权限
采用基于角色的访问控制(RBAC)模型,通过用户-角色-菜单三层关系实现细粒度的权限管理。权限控制贯穿前后端:后端通过 `@permission_required` 装饰器校验(`src/common/middleware/access_decorators.py`),前端通过 `v-perm` 指令控制按钮显隐。
:::
@@ -1,3 +1,5 @@
# 参数验证(Pydantic)
:::tip 参数验证
使用 Pydantic v2 进行请求参数验证。所有请求体参数通过 Pydantic BaseModel 定义,Tornado 自动解析和验证 JSON 请求体,验证失败返回标准格式错误信息。BaseSchema 基类位于 `src/core/base_schema.py`。
:::
+1 -1
View File
@@ -1,7 +1,7 @@
# 为什么选择我们?
:::tip 核心价值
企业级数据中台不应从零搭建。本框架提供开箱即用的完整解决方案,为中大型企业构建统一的数字化基座。
本框架提供开箱即用的企业级中台完整解决方案,免去从零搭建成本,助力中大型企业构建统一的数字化基座。
:::
## 解决什么问题?
@@ -29,7 +29,7 @@
//
// themeConfig: {
// editLink: {
// pattern: "https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_EleVue",
// pattern: "https://gitee.com/djangoadmin/DjangoAdmin_Tornado_EleVue",
// text: "Suggest changes to this page",
// },
//
@@ -0,0 +1,5 @@
export declare const SITE_URL: string;
export declare const SITE_DESCRIPTION: string;
export declare function stripNumberPrefix(title: string): string;
export declare function extractDescription(md: string): string;
export declare function extractFaqs(md: string): { q: string; a: string }[];
@@ -0,0 +1,130 @@
/**
* SEO/GEO 共享工具(纯 ESM,零依赖)。
*
* 同时被两类调用方 import:
* 1. VitePress 配置(.ts,esbuild 打包时内联本文件)
* 2. scripts/generate-llms-txt.mjs(Node 直接运行)
*
* 注意:SITE_URL 是全站唯一 URL 真相源。
* docs 子域当前 TLS 证书为腾讯 CDN 默认证书(*.cdn.myqcloud.com,校验失败),
* 故暂用 http;证书修复后只需改这里一处。
*/
export const SITE_URL = "http://docs.tornado.elevue.djangoadmin.cn";
export const SITE_DESCRIPTION =
"DjangoAdmin Tornado+ElementPlus 企业级中台解决方案开发者文档";
/**
* 剥离 H1/标题中的章节编号前缀,用于 <title> 与 JSON-LD headline。
* 例:"5.10 字典模块" → "字典模块";"K. 贡献指南" → "贡献指南"。
* 页内 H1 与侧边栏编号保持不变,仅影响 SEO 输出。
*/
export function stripNumberPrefix(title) {
return String(title)
.replace(/^(?:\d+(?:\.\d+)+|[A-Z]\.)\s+/, "")
.trim();
}
/**
* 从页面 Markdown 提取 meta description:
* 1. 首选 `:::tip xxx` 容器内的首句(141/143 页的标准 opener)
* 2. 回退到 H1 后的第一个普通段落
* 输出压缩为单行、去除行内 Markdown,按中文句末标点截断(≤100 字符)。
*/
export function extractDescription(md) {
const src = String(md);
const fm = src.match(/^---\r?\n([\s\S]*?)\r?\n---/);
const body = fm ? src.slice(fm[0].length) : src;
let text = "";
// frontmatter description 显式声明优先(无 H1 页面的补齐手段)
const fmDesc = fm && fm[1].match(/^description:\s*(.+)$/m);
if (fmDesc) {
text = fmDesc[1].trim().replace(/^["']|["']$/g, "");
}
const tip = !text && body.match(/^:::\s*tip[^\n]*\n([\s\S]*?)\n:::/m);
if (tip) {
text = tip[1];
}
if (!text) {
const afterH1 = body.match(/^# (?!#)(.+)$/m);
if (afterH1) {
const rest = body.slice((afterH1.index ?? 0) + afterH1[0].length);
const para = rest
.split(/\n{2,}/)
.find(
(p) =>
p.trim() &&
!/^\s*#/.test(p) &&
!/^\s*:::/.test(p) &&
!/^\s*```/.test(p) &&
!/^\s*\|/.test(p),
);
text = para || "";
}
}
text = text
.replace(/\s+/g, " ")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
.trim();
if (!text) return "";
// 按中文句末标点截取首句,上限 100 字符;截断时避免切碎词
const m = text.match(/^.{1,100}?(?:[。!?!?]|$)/);
let out = m ? m[0] : text.slice(0, 100);
if (out.length < text.length && !/[。!?!?]$/.test(out)) {
out = out.replace(/[^\s,、;:,;:]+$/, "");
}
return out.trim() || text.slice(0, 100);
}
function cleanText(s) {
return s
.replace(/^:::[^\n]*$/gm, "")
.replace(/^```[^\n]*$/gm, "")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
.replace(/\s+/g, " ")
.trim();
}
/**
* 提取页面中的 Q/A 对(用于 FAQPage JSON-LD),支持两种站内格式:
* A. `### Q:问题` 标题式(如 zh/quickstart/faq.md)
* B. `**Q:问题**` 加粗式(如 zh/appendix/faq.md)
* 返回 [{ q, a }];无匹配返回 []。
*/
export function extractFaqs(md) {
const body = String(md).replace(/^---[\s\S]*?\n---\s*/, "");
let faqs = [];
// 格式 A:按 `### Q:` 切块,答案截断到下一个标题
for (const part of body.split(/(?=^###\s*Q[::])/m)) {
const m = part.match(/^###\s*Q[::]\s*([^\n]+)\n+([\s\S]*)/);
if (!m) continue;
let a = m[2];
const cut = a.search(/^#{1,4}\s/m);
if (cut >= 0) a = a.slice(0, cut);
faqs.push({ q: cleanText(m[1]), a: cleanText(a) });
}
// 格式 B:按 `**Q:` 切块
if (!faqs.length) {
for (const part of body.split(/(?=^\*\*Q[::])/m)) {
const m = part.match(/^\*\*Q[::]\s*([^*]+)\*\*\s*\n+([\s\S]*)/);
if (!m) continue;
let a = m[2];
const cut = a.search(/^(?:#{1,4}\s|\*\*Q)/m);
if (cut >= 0) a = a.slice(0, cut);
faqs.push({ q: cleanText(m[1]), a: cleanText(a) });
}
}
return faqs.filter((f) => f.q && f.a);
}
@@ -1,14 +1,22 @@
import { defineConfig } from "vitepress";
import { enConfig } from "./en";
import { createSeoHooks } from "./seo";
import { sharedConfig } from "./shared";
import { zhConfig } from "./zh";
const seo = createSeoHooks(zhConfig.themeConfig?.sidebar);
export default defineConfig({
...sharedConfig,
ignoreDeadLinks: [
(link) => link.includes('localhost'),
],
// SEO:页面级 title/description、canonical/OG/JSON-LD、sitemap+lastmod
transformPageData: seo.transformPageData,
transformHead: seo.transformHead,
sitemap: seo.sitemap,
locales: {
root: { label: "简体中文", lang: "zh-CN", link: "/", ...zhConfig },
// root: { label: "English", lang: "en-US", link: "/", ...enConfig },
@@ -0,0 +1,311 @@
/**
* SEO/GEO 构建期钩子(仅 Node 构建时运行,不进客户端 bundle):
* - transformPageData:每页独立 title(去编号前缀)与 description(取 :::tip 首句)
* - transformHead:canonical / Open Graph / twitter 卡片 / JSON-LD 结构化数据
* - sitemap.transformItems:注入 lastmod(git 最后提交时间)并过滤 404
*
* 由 config/index.ts 通过 createSeoHooks(zhConfig.themeConfig?.sidebar) 挂载。
* 依赖 extract.mjs(纯 ESM,同被 scripts/generate-llms-txt.mjs 复用)。
*/
import fs from "node:fs";
import path from "node:path";
import { execSync } from "node:child_process";
import {
SITE_URL,
extractDescription,
extractFaqs,
stripNumberPrefix,
} from "./extract.mjs";
/** 与 vitepress HeadConfig 同构:[tag, attrs, innerHTML?] */
type HeadTuple = [string, Record<string, any>, string?];
interface SideItem {
text?: string;
link?: string;
items?: SideItem[];
}
interface PageDataLike {
relativePath?: string;
title?: string;
description?: string;
frontmatter?: Record<string, any>;
[k: string]: any;
}
interface TransformCtx {
pageData?: PageDataLike;
title?: string;
description?: string;
content?: string;
siteConfig?: { root?: string; srcDir?: string };
}
const ORG = {
"@type": "Organization",
name: "DjangoAdmin",
url: "https://www.djangoadmin.cn",
logo: `${SITE_URL}/logo.png`,
sameAs: [
"https://gitee.com/djangoadmin/DjangoAdmin_Tornado_EleVue",
"https://www.djangoadmin.cn",
],
};
/** 相对路径 → 站内路由(cleanUrls 关闭,正文页带 .html) */
function routeOf(relativePath: string): string {
if (relativePath === "index.md") return "/";
if (relativePath === "404.md") return "/404.html";
return `/${relativePath.replace(/\.md$/, ".html")}`;
}
/** 去掉侧边栏文案前导 emoji/符号,保留 "1.1 项目介绍" 这类编号文字 */
function cleanLabel(s?: string): string {
return (s || "").replace(/^[^\p{L}\p{N}]+/u, "").trim();
}
/** 从侧边栏构建 route → 面包屑文案链(含分组小节) */
function buildCrumbMap(sidebar: unknown): Map<string, string[]> {
const map = new Map<string, string[]>();
const groups: SideItem[] = Array.isArray(sidebar)
? sidebar
: ((sidebar as Record<string, SideItem[]>)?.["/zh/"] ?? []);
const walk = (items: SideItem[], trail: string[]) => {
for (const item of items) {
if (item.link && item.link.startsWith("/")) {
map.set(item.link, [...trail, cleanLabel(item.text)]);
} else if (item.items?.length) {
walk(item.items, [...trail, cleanLabel(item.text)]);
}
}
};
for (const group of groups) {
walk(group.items ?? [], [cleanLabel(group.text)].filter(Boolean));
}
return map;
}
/** 从渲染后的 HTML 提取 H1 纯文本(JSON-LD headline 须与页面 H1 一致) */
function h1OfHtml(html?: string): string {
const m = (html || "").match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
if (!m) return "";
return m[1]
.replace(/<[^>]+>/g, "")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.trim();
}
/** 从 Markdown 源提取 H1 纯文本(去除行内 markdown 标记) */
function h1OfMd(md: string): string {
// 围栏感知:跳过 ``` 代码块内的 # 注释,取真正的页面 H1
let inFence = false;
for (const line of md.split("\n")) {
if (line.startsWith("```")) { inFence = !inFence; continue; }
if (!inFence && /^# (?!#)/.test(line)) {
return line
.replace(/^# /, "")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.trim();
}
}
return "";
}
export function createSeoHooks(sidebar: unknown) {
const crumbs = buildCrumbMap(sidebar);
const projectDirs: string[] = [];
let gitDates: Map<string, string> | null = null;
const readMd = (relativePath: string, srcDir?: string): string => {
try {
return fs.readFileSync(
path.resolve(srcDir || process.cwd(), relativePath),
"utf8",
);
} catch {
return "";
}
};
/**
* 项目内每个文件最后一次 git 提交的 ISO 时间。
* 单次 git log 调用构建全量映射(key = 相对路径,与 pageData.relativePath 一致);
* git 不可用时静默降级(sitemap 无 lastmod、JSON-LD 无 dateModified)。
*/
const getGitDates = (root: string): Map<string, string> => {
if (gitDates) return gitDates;
gitDates = new Map<string, string>();
try {
const out = execSync(
"git log --format=:::%cI --name-only --relative -n 500 -- .",
{
cwd: root,
encoding: "utf8",
maxBuffer: 64 * 1024 * 1024,
stdio: ["ignore", "pipe", "ignore"],
},
);
let current = "";
for (const line of out.split(/\r?\n/)) {
if (line.startsWith(":::")) {
current = line.slice(3).trim();
continue;
}
const file = line.trim();
if (file && current && !gitDates.has(file)) {
gitDates.set(file, current);
}
}
} catch {
// 降级:无 lastmod / dateModified
}
return gitDates;
};
/** 每页独立 title(去 "5.10 " 编号前缀,关键词前置)与 description(:::tip 首句) */
function transformPageData(pageData: PageDataLike, ctx?: any): Record<string, unknown> {
const srcDir: string | undefined = ctx?.siteConfig?.srcDir;
const root: string | undefined = ctx?.siteConfig?.root;
if (root && !projectDirs.includes(root)) projectDirs.push(root);
const out: Record<string, unknown> = {};
const rel = pageData.relativePath;
if (rel && !pageData.frontmatter?.description) {
const desc = extractDescription(readMd(rel, srcDir));
if (desc) out.description = desc;
}
if (pageData.title) {
out.title = stripNumberPrefix(pageData.title);
}
return out;
}
/** 构建期注入 canonical / OG / twitter / JSON-LD */
function transformHead(ctx: TransformCtx): HeadTuple[] {
const rel = ctx?.pageData?.relativePath || "";
if (!rel || rel === "404.md") return [];
const route = routeOf(rel);
const url = `${SITE_URL}${route}`;
const title = ctx.pageData?.title || ctx.title || "";
const desc = ctx.description || "";
const isHome = rel === "index.md";
const image = `${SITE_URL}/logo.png`;
const heads: HeadTuple[] = [
["link", { rel: "canonical", href: url }],
["meta", { property: "og:type", content: isHome ? "website" : "article" }],
["meta", { property: "og:site_name", content: "DjangoAdmin" }],
["meta", { property: "og:locale", content: "zh_CN" }],
["meta", { property: "og:title", content: title }],
["meta", { property: "og:description", content: desc }],
["meta", { property: "og:url", content: url }],
["meta", { property: "og:image", content: image }],
["meta", { property: "twitter:title", content: title }],
["meta", { property: "twitter:description", content: desc }],
["meta", { property: "twitter:url", content: url }],
["meta", { property: "twitter:image", content: image }],
];
// ---- JSON-LD ----
const root = ctx.siteConfig?.root || projectDirs[0] || process.cwd();
const srcDir = ctx.siteConfig?.srcDir || root;
const lastMod = getGitDates(root).get(rel);
const blocks: Record<string, any>[] = [];
if (isHome) {
blocks.push({
"@context": "https://schema.org",
"@type": "WebSite",
name: "DjangoAdmin 开发文档",
url: `${SITE_URL}/`,
inLanguage: "zh-CN",
description: desc,
});
blocks.push({ "@context": "https://schema.org", ...ORG });
} else {
const md = readMd(rel, srcDir);
const article: Record<string, any> = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: h1OfMd(md) || h1OfHtml(ctx.content) || title,
description: desc,
url,
inLanguage: "zh-CN",
mainEntityOfPage: { "@type": "WebPage", "@id": url },
publisher: ORG,
};
if (lastMod) article.dateModified = lastMod;
blocks.push(article);
const trail = crumbs.get(route);
if (trail?.length) {
const items: Record<string, any>[] = [
{ "@type": "ListItem", position: 1, name: "首页", item: `${SITE_URL}/` },
];
trail.forEach((name, i) => {
const node: Record<string, any> = {
"@type": "ListItem",
position: i + 2,
name,
};
if (i === trail.length - 1) node.item = url;
items.push(node);
});
blocks.push({
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items,
});
}
const faqs = extractFaqs(md);
if (faqs.length) {
blocks.push({
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((f) => ({
"@type": "Question",
name: f.q,
acceptedAnswer: { "@type": "Answer", text: f.a },
})),
});
}
}
// 转义 "<",防止内容中出现 </script> 破坏注入
const json = JSON.stringify(blocks).replace(/</g, "\\u003c");
heads.push(["script", { type: "application/ld+json" }, json]);
return heads;
}
/** sitemap.xml:过滤 404/README,注入 git lastmod */
const sitemap = {
hostname: SITE_URL,
transformItems(items: { url: string; lastmod?: string }[]) {
const root = projectDirs[0] || process.cwd();
const dates = getGitDates(root);
const out: { url: string; lastmod?: string }[] = [];
for (const item of items) {
// ⚠ vitepress 传入的 item.url 是相对路径("404.html"、"zh/x.html"、""),
// 无前导斜杠也无域名——hostname 由 SitemapStream 事后拼接。
if (/(^|\/)404\.html$/.test(item.url)) continue; // 404 页不收录
if (/(^|\/)README\.html$/.test(item.url)) continue; // 仓库 README 非文档页
const p = item.url.replace(/^\//, "");
const rel =
p === "" || p === "index.html" ? "index.md" : p.replace(/\.html$/, ".md");
const lastmod = dates.get(rel);
out.push(lastmod ? { ...item, lastmod } : item);
}
return out;
},
};
return { transformPageData, transformHead, sitemap };
}
@@ -1,6 +1,6 @@
import { defineConfig, HeadConfig } from "vitepress";
export const META_IMAGE = "/social.png";
export const META_IMAGE = "/logo.png";
const rControl = /[\u0000-\u001f]/g;
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g;
@@ -23,6 +23,8 @@ export const slugify = (str: string): string =>
export const sharedConfig = defineConfig({
title: "小蚂蚁云",
// 页面 <title> 后缀:「环境准备 | DjangoAdmin 开发文档」(index.md frontmatter 可覆盖)
titleTemplate: "DjangoAdmin 开发文档",
appearance: "light",
markdown: {
@@ -1,8 +1,9 @@
import type { DefaultTheme, LocaleSpecificConfig } from "vitepress";
export const META_URL = "https://www.djangoadmin.cn";
export const META_TITLE = "企业级中台解决方案";
export const META_DESCRIPTION = "企业级中台解决方案";
export const META_URL = "http://docs.tornado.elevue.djangoadmin.cn";
export const META_TITLE = "DjangoAdmin Tornado+ElementPlus 开发文档";
export const META_DESCRIPTION =
"DjangoAdmin Tornado+ElementPlus 企业级中台解决方案开发者文档";
export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
description: META_DESCRIPTION,
+12
View File
@@ -0,0 +1,12 @@
---
head:
- - meta
- name: robots
content: noindex
---
# 页面不存在
抱歉,您访问的页面不存在或已被移动。
[返回首页](/) · [浏览文档](/zh/introduction)
+2 -1
View File
@@ -5,7 +5,8 @@
"type": "module",
"scripts": {
"docs": "vitepress dev .",
"docs:build": "vitepress build .",
"docs:llms": "node scripts/generate-llms-txt.mjs",
"docs:build": "node scripts/generate-llms-txt.mjs && vitepress build .",
"docs:preview": "vitepress preview ."
},
"dependencies": {
+168
View File
@@ -0,0 +1,168 @@
# DjangoAdmin Tornado+ElementPlus 开发文档
> DjangoAdmin Tornado+ElementPlus 企业级中台解决方案开发者文档。内置 RBAC 权限体系、代码生成器等开箱即用功能,支持五种数据库一键切换。本文档共 138 篇,按章节组织。
## 1. 了解项目
- [更新日志](http://docs.tornado.elevue.djangoadmin.cn/zh/changelog.html): 更新日志是项目演进的完整记录,帮助开发者和企业了解每个版本的能力变化与技术演进方向。
- [新手入门指南](http://docs.tornado.elevue.djangoadmin.cn/zh/course.html): 详细的安装步骤请参考 环境准备 章节。
- [项目介绍](http://docs.tornado.elevue.djangoadmin.cn/zh/introduction.html): DjangoAdmin 是企业级中台解决方案,后端基于 Tornado + SQLAlchemy,前端基于 Vue3 + ElementPlus,内置 RBAC 权限、代码生成器等开箱即用功能,
- [项目目录结构](http://docs.tornado.elevue.djangoadmin.cn/zh/struct.html): + 保持一致性:目录结构一旦确定,应保持一致性,避免随意更改。
- [为什么选择我们?](http://docs.tornado.elevue.djangoadmin.cn/zh/why.html): 本框架提供开箱即用的企业级中台完整解决方案,免去从零搭建成本,助力中大型企业构建统一的数字化基座。
## 2. 快速入门
- [后端启动](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/backend.html): 启动后端前,请确保已完成 环境准备 章节中的所有软件安装。
- [数据库初始化](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/database.html): 数据库初始化前,请确保已完成以下准备工作: 1. 目标数据库服务已启动(如 MySQL 8.0+) 2. .env 文件中的 DB_DRIVER 和数据库连接信息已正确配置 3. Python
- [Docker 快速部署](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/docker.html): Docker 部署适合快速体验和生产环境。
- [环境变量速查表](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/env-vars.html): 1. .env.example 是环境变量模板文件,已提交到版本控制。
- [环境准备](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/environment.html): 1. 部分软件安装后需要设置本地环境变量,便于在终端中使用命令。
- [常见问题FAQ](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/faq.html): 遇到问题时,建议先查看终端日志输出和浏览器控制台(F12)的错误信息,通常能快速定位问题原因。
- [第一个接口调试](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/first-api.html): 开始调试前,请确保已完成以下步骤: 1. 后端服务已启动(参见 后端启动) 2. 数据库已初始化(参见 数据库初始化) 3. Redis 服务已启动
- [前端启动](http://docs.tornado.elevue.djangoadmin.cn/zh/volume1/frontend.html): 启动前端前,请确保已完成 环境准备 章节中的 Node.js 和 pnpm 安装,并已启动后端服务(参见 后端启动)。
## 3. 模块开发实战
- [完整代码清单](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/code-list.html): 本页汇总友链(Link)模块涉及的全部文件代码,开发时可快速查阅与复制对照。
- [示例模块(Example)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/example.html): src/modules/example/ 是一个完整的示例模块,演示了标准 CRUD 模块的六文件结构和开发模式。
- [常见问题与排错](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/faq.html): 本页汇总模块开发过程中的常见问题与排错方法,开发受阻时按症状索引即可定位。
- [前端 API 层](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/fe-api.html): 前端 API 层封装后端接口的 HTTP 请求,每个业务模块对应一个 API 文件,本页讲解基于 Axios 封装的编写规范。
- [前端路由与菜单](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/fe-router.html): 本页讲解前端路由与菜单机制——路由和菜单由后端动态返回,前端只需注册路由组件映射。
- [前端页面视图](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/fe-view.html): 前端页面视图是用户直接交互的界面,本页讲解 Vue3 + ElementPlus 下一个完整 CRUD 页面的文件组成与编写方式。
- [请求处理器(Handler)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/handler.html): Handler 层是 HTTP 接口层,负责接收请求、校验权限与记录操作日志,编写新接口从本页开始。
- [数据模型(Model)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/model.html): 数据模型是模块开发的第一步,讲解如何用 SQLAlchemy ORM 与基类定义数据库表结构,新建模块时从本页开始。
- [概述与目标](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/overview.html): 其中 id、create_user、create_time、update_user、update_time、is_delete 由基类 BaseModel 自动提供,业务字段仅需定义 name、
- [权限节点配置](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/permission.html): 实际开发中,查询类权限(page、detail、list)通常合并为一个节点 sys:position:list,减少配置工作量。
- [Repository 层](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/repository.html): Repository 层是数据访问层,封装全部数据库查询操作,本页讲解基于 BaseRepository 基类的通用 CRUD 用法。
- [路由注册](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/router.html): 新模块只需两步: 1. 在 src/api/v1/ 下创建路由文件,定义 routes 列表 2. 在 router.py 的 _RESOURCE_MODULES 中追加模块导入 路由前缀
- [Pydantic Schema](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/schema.html): Schema 层负责请求数据校验,本页讲解基于 Pydantic v2 BaseModel 与 Field 的表单类定义规范。
- [Service 层](http://docs.tornado.elevue.djangoadmin.cn/zh/volume2/service.html): repo_cls 是 类属性,值为 Repository 类(不是实例)。
## 4. 架构设计
- [核心基类体系](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/base-classes.html): 四大基类各司其职,覆盖从数据模型到请求校验再到业务逻辑的完整链路。
- [缓存架构设计](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/cache.html): 采用两级缓存架构:第一级为进程内字典缓存(dict_util),第二级为 Redis 分布式缓存。
- [数据库设计](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/database-design.html): 所有业务表继承 base_model 基类,自动获得 6 个通用字段(id / create_user / create_time / update_user / update_time /
- [目录结构设计](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/directory.html): 目录结构遵循「按功能分组、按职责分层」的原则。
- [扩展性设计](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/extensibility.html): 遵循开闭原则(OCP):对扩展开放,对修改关闭。
- [分层架构设计](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/layer-arch.html): 采用经典的分层架构,核心原则是:依赖单向、职责单一、接口隔离。
- [整体架构图](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/overview.html): 本页从全局视角阐述系统整体技术架构,涵盖前端、后端、数据库与基础设施等核心组成,理解系统全貌时从本页开始。
- [请求完整链路](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/request-flow.html): 一个典型的 API 请求经过以下阶段:浏览器 → Nginx → Tornado → Handler 生命周期(set_default_headers → prepare →
- [安全架构设计](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/security.html): 纵深防御:认证、授权、限流、参数校验多层防护,单一防线失效时仍有其他机制兜底 最小权限:用户仅能访问已授权接口,角色权限按需分配,遵循"够用即可"原则 数据不落地:敏感信息(Token、
- [技术选型说明](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/tech-stack.html): Tornado 是 Python 生态中成熟的异步 Web 框架,基于自有事件循环实现非阻塞 I/O。
- [Tornado IOLoop 与异步模型](http://docs.tornado.elevue.djangoadmin.cn/zh/volume3/tornado-async.html): Tornado 基于单线程事件循环(IOLoop),Handler 方法默认为协程(async def)。
## 5. 开发指南
- [API 接口速查表](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/api-cheatsheet.html): API 接口速查表,按模块分组列出所有 RESTful 接口的请求方法、路径、说明和权限标识。
- [文章管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/article.html): 文章管理模块支持富文本编辑、分类关联、封面图片上传、图集管理等功能。
- [分类管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/category.html): 分类管理模块用于管理树形分类数据,如文章分类、产品分类等。
- [行政区划管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/city.html): 行政区划管理模块提供省市区三级行政区划数据的查询和管理能力,支持树形结构展示和下拉选择。
- [公告与链接管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/content.html): 公告、链接、城市是三个简单 CRUD 内容模块,均继承 BaseService,提供标准的增删改查接口。
- [部门/职级/岗位管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/dept.html): 部门、职级、岗位是组织架构管理的三个核心模块。
- [文件模板管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/file-template.html): 文件模板管理模块提供文件导入导出模板的管理能力,用于维护各种业务场景的 Excel/Word 等模板文件。
- [定时任务日志](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/job-log.html): 定时任务日志模块记录定时任务的执行历史,包括执行时间、执行结果、耗时等信息。
- [定时任务](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/job.html): 定时任务模块基于 APScheduler 实现,支持 Cron 表达式调度、任务执行日志、动态启停等功能。
- [菜单管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/menu.html): 菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。
- [参数管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/param.html): 参数管理模块用于管理动态系统参数,与系统配置模块类似但更侧重于业务参数的灵活配置。
- [角色管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/role.html): 角色是 RBAC 权限模型的核心,连接用户和菜单(权限)。
- [用户管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/business/user.html): 用户管理是系统的核心模块,属于复杂业务模块。
- [认证流程实战](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/auth-flow.html): 登录 → Token 获取 → 权限校验 → Token 刷新 → 退出登录的完整认证链路实战说明。
- [验证码机制](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/captcha.html): 验证码是防止自动化攻击的重要安全手段。
- [系统配置管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/config.html): 系统配置模块用于管理动态的键值对配置项,与数据字典类似但更偏向系统级参数。
- [CORS 跨域配置](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/cors.html): CORS(Cross-Origin Resource Sharing)跨域资源共享是前后端分离架构必须处理的问题。
- [演示模式](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/demo-mode.html): 演示模式用于在线体验环境,开启后所有写操作(新增、编辑、删除等)将被拦截,返回"演示环境,暂无操作权限"提示。
- [数据字典使用指南](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/dict-guide.html): 数据字典的完整使用流程,从新增字典类型到在代码中使用字典数据的实操指南。
- [字典模块](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/dict.html): 数据字典用于管理系统中的枚举值和配置项,如状态、类型、性别等。
- [异常与响应处理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/exception.html): 定义了统一的响应格式和全局异常处理机制。
- [优雅关闭](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/graceful-shutdown.html): 优雅关闭确保应用在收到终止信号时,能够完成正在处理的请求、关闭数据库连接、停止定时任务调度器后再退出,避免请求中断和资源泄漏。
- [用户认证(JWT)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/jwt.html): JWT(JSON Web Token)是的核心认证机制。
- [登录日志](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/login-log.html): 登录日志记录每次登录尝试(成功/失败),包含登录账号、IP地址、浏览器信息、登录时间等。
- [登录防爆破](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/login-protect.html): 登录防爆破机制基于 Redis 实现同一 IP + 用户名维度的连续失败计数与锁定,防止暴力破解密码。
- [操作日志](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/operation-log.html): 操作日志装饰器自动记录写请求(POST/PUT/DELETE/PATCH)的操作日志,无需手动埋点。
- [权限管理(RBAC)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/rbac.html): 采用基于角色的访问控制(RBAC)模型,通过用户-角色-菜单三层关系实现细粒度的权限管理。
- [链路追踪(TraceID)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/trace-id.html): 链路追踪通过为每个请求分配唯一的 TraceID,将同一请求的所有日志串联起来,方便问题排查和性能分析。
- [参数验证(Pydantic)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/core/validator.html): 使用 Pydantic v2 进行请求参数验证。
- [API 层开发](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/api.html): 前端 API 层位于 src/api/ 目录,封装所有与后端的 HTTP 请求。
- [前端构建](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/build.html): 前端使用 Vite 构建,支持开发服务器和生产构建。
- [公共组件库](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/components.html): 项目提供丰富的公共组件,位于 ui/src/components/ 目录,基于 ElementPlus 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。
- [BasicForm 表单](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/form.html): BasicForm 位于 src/components/Form/src/BasicForm.vue,基于 ElementPlus 的 el-form 二次封装,通过 schemas
- [Hooks 组合式函数](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/hooks.html): 项目封装了一系列组合式函数(Hooks),位于 src/hooks/ 和 src/utils/ 目录,提供权限检查、异步 loading、防重复提交、实时时钟、项目配置等可复用逻辑。
- [国际化](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/i18n.html): 当前项目仅支持中文界面,ElementPlus 使用中文语言包(zh-cn),所有界面文本直接硬编码在组件中。
- [Modal 弹窗](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/modal.html): 项目提供 BasicModal 组件(src/components/Modal/src/BasicModal.vue)和原生 el-dialog 两种弹窗用法。
- [权限指令 v-perm](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/perm-directive.html): v-perm 和 v-perms 是项目自定义的 Vue 指令,用于按钮级别的权限控制,无权限时自动从 DOM 中移除元素。
- [路由与菜单](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/router.html): 前端路由基于 Vue Router 4 实现动态路由生成,菜单数据从后端获取,通过 generator-routers.ts 递归生成路由表,路由守卫控制登录状态和权限。
- [前端环境与结构](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/setup.html): 前端基于 Vue3 + ElementPlus 构建,使用 pnpm 作为包管理工具,Vite 作为构建工具,TypeScript 作为开发语言。
- [Store 状态管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/store.html): 前端使用 Pinia 进行状态管理,核心模块包括 user(用户信息与权限)、asyncRoute(动态路由与菜单)、projectSetting(项目配置)、designSetting(主题设计)等。
- [样式与主题](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/style.html): 前端样式方案采用 Tailwind CSS + SCSS + ElementPlus 主题覆盖,支持深色模式切换。
- [BasicTable 表格](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/table.html): BasicTable 位于 src/components/Table/src/Table.vue,基于 ElementPlus 的 el-table 二次封装,提供列定义、数据请求、分页、操作列、
- [工具函数](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/utils.html): 前端工具函数位于 src/utils/ 目录,提供认证管理、本地存储、日期格式化、树结构操作、文件下载等通用能力。
- [页面视图开发](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/frontend/view.html): 页面视图位于 src/views/ 目录,每个业务模块遵循 index.vue(列表页)+ edit.vue(编辑弹窗)的标准模式,辅以 columns.ts(列定义)和
- [接口响应规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/integration/api-contract.html): 后端所有接口统一返回 R.ok() / R.failed() 格式的 JSON 响应,前端通过 VAxios 的 transformRequestData 拦截器统一解包处理。
- [字典数据联调](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/integration/dict-sync.html): 字典数据通过后端字典 API 获取,前端在下拉选择器中渲染选项。
- [权限节点联动](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/integration/perm-sync.html): 前后端权限通过统一的权限节点字符串联动,后端 @permission_required 校验权限,前端 v-perm 指令和 TableAction 的 auth 属性控制按钮显示。
- [代理配置](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/integration/proxy.html): 开发环境通过 Vite 代理转发 API 请求到后端,生产环境通过 Nginx 代理。
- [文件上传联调](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/integration/upload-sync.html): 文件上传涉及前端 Upload 组件、后端上传接口和 URL 拼接三个环节,需要前后端协同配置。
- [请求上下文(ContextVar)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/context.html): src/core/context.py 使用 Python 标准库 contextvars 管理请求级别的上下文信息,包括当前用户 ID、用户名、姓名和链路追踪 ID。
- [数据库会话](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/db-session.html): 数据库会话通过 scoped_session 实现线程级隔离,Repository 层的写操作自动 commit,BaseHandler.on_finish() 兜底关闭会话。
- [字典缓存工具](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/dict-util.html): src/common/utils/dict_util.py 提供业务字典的缓存读取能力,从 Redis 缓存中获取字典数据,缓存未命中时从数据库加载。
- [导入导出](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/excel.html): 基于 openpyxl 实现 Excel 导入导出功能,支持通过 file_template 模块管理导入模板配置。
- [通用 Helper 函数](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/helpers.html): src/common/utils/helpers.py 提供项目中广泛使用的通用工具函数,包括 ID 解析、OS/浏览器检测、参数脱敏等。
- [IP 归属地解析](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/ip-region.html): src/common/utils/ip2region.py 基于 ip2region 离线数据库,提供 IP 地址到省市区的归属地查询能力,无需网络请求。
- [日志系统](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/logger.html): 通过 Python 标准库 logging 实现结构化日志,支持控制台 + 文件双输出,日志文件按大小自动轮转。
- [密码工具](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/password.html): 采用 bcrypt 双重加盐方案存储密码,源码位于 src/core/password.py。
- [权限缓存](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/perm-cache.html): 通过 Redis 实现用户权限和菜单的缓存,采用用户级缓存 + 全局版本号双层策略。
- [统一响应封装(R 对象)](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/response.html): src/core/response.py 提供统一的 API 响应封装,所有接口返回格式为 {code, data, msg, ok}。
- [富文本过滤](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/rich-text.html): 富文本编辑器内容存在存储型 XSS 风险,通过 bleach 库实现 HTML 白名单清洗,在落盘前统一过滤危险内容。
- [文件上传完整流程](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/upload-guide.html): 富文本编辑器(如 TinyMCE、CKEditor、WangEditor 等)的图片上传同样使用此接口。
- [文件上传](http://docs.tornado.elevue.djangoadmin.cn/zh/volume4/tools/upload.html): 提供本地文件上传功能,支持图片和通用文件上传,内置扩展名白名单、大小限制、魔术字节校验和路径遍历防护。
## 6. 代码生成器
- [CLI 命令行使用](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/cli.html): 代码生成器 CLI 模式的使用方法,包括单表生成、批量生成、预览模式和配置导出。
- [代码生成引擎](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/code-engine.html): 代码生成引擎的工作原理,包括配置解析、模板渲染、文件写入和自动注册的完整流程。
- [配置文件详解](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/config.html): 代码生成器 JSON 配置文件的格式说明,包括模块名称、字段属性、字典编码等配置项的详细解释。
- [实战案例](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/demo.html): 以 tornado_example 演示表为完整案例,演示从建表到生成到使用的全流程。
- [常见问题](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/faq.html): 代码生成器使用过程中常见问题的原因分析和解决方案。
- [概述与架构](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/overview.html): 代码生成器的整体架构、核心模块和生成流程概述。
- [表结构解析引擎](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/parser.html): 表结构解析引擎的工作原理,从数据库读取表元数据并智能推断字段用途的过程。
- [自定义模板](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/templates.html): 代码生成器模板的 Jinja2 语法说明,包括模板变量、字段属性和自定义模板的开发方法。
- [Web 管理界面](http://docs.tornado.elevue.djangoadmin.cn/zh/volume5/web-ui.html): 代码生成器 Web 管理界面的使用方法,通过可视化界面操作单表生成和批量生成。
## 7. 运维部署
- [备份与恢复](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/backup.html): - 3 份数据副本(1 份生产 + 2 份备份) - 2 种不同存储介质(如本地磁盘 + 远程存储) - 1 份异地备份(防机房级灾难)
- [跨库数据迁移](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/db-cross-migrate.html): 项目提供 scripts/migrate_db.py 脚本,支持在不同数据库之间迁移数据(如 MySQL → PostgreSQL / SQL Server / SQLite / Oracle)。
- [数据库迁移](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/db-migrate.html): - 结构迁移:Alembic(自动生成 DDL 差异) - 数据迁移:scripts/migrate_db.py(跨库数据泵) - 初始化:scripts/init_db.py(全新安装一键建库)
- [部署 Checklist](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/deploy-checklist.html): 从服务器准备到应用上线的完整部署操作清单,适用于生产环境首次部署和后续版本更新。
- [Docker容器化](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/docker.html): - 环境一致性:消除"在我机器上能跑"的问题 - 快速部署:一条命令启动完整服务 - 资源隔离:应用与宿主机环境解耦
- [环境规划](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/env-plan.html): 各环境之间保持代码一致,仅通过环境变量(.env)区分配置。
- [健康检查与监控](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/monitoring.html): `` 1. 应用层:健康检查端点、业务指标 2. 系统层:CPU/内存/磁盘/网络 3. 中间件:MySQL/Redis 连接状态 4. 日志:集中式日志收集与分析 ``
- [Nginx 反向代理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/nginx.html): `` 客户端 → Nginx(80/443) ├─ / → 前端静态资源(dist/) ├─ /api/v1/... → Tornado 后端(8041) └─ /api/file/ →
- [性能调优](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/performance.html): 1. 先测量,后优化:用数据说话,避免盲目调参 2. 瓶颈优先:找到最大瓶颈,集中优化 3. 渐进式:每次只调整一个参数,观察效果
- [安全加固](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/security-hardening.html): - 最小权限:只开放必要的端口和权限 - 纵深防御:多层安全措施叠加 - 默认安全:配置默认值应为安全选项
- [Supervisor进程管理](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/supervisor.html): - Linux 服务器直接部署(非 Docker) - 需要进程自动重启和日志轮转 - 管理多个相关进程
- [故障排查手册](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/troubleshooting.html): 1. 查看日志:tail -f logs/supervisor.out.log 或 docker-compose logs -f 2. 检查配置:.env 文件、数据库连接、Redis 连接 3.
- [版本升级流程](http://docs.tornado.elevue.djangoadmin.cn/zh/volume6/upgrade.html): 1. 先备份,后升级 2. 先测试环境,后生产环境 3. 每次升级可回滚 4. 低峰期执行升级
## 8. 规范标准
- [API响应规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/api-response.html): - 所有接口必须通过 R.ok() 或 R.failed() 返回,禁止直接返回 dict - 成功响应 code=0,失败响应 code=1 - 响应体始终包含 code、data、msg、ok
- [代码审查清单](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/code-review.html): - 每次提交必须经过至少一人审查 - 审查关注点:正确性 > 安全性 > 可维护性 > 性能 - 审查意见应具体、可操作,避免主观评价
- [删除规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/deletion.html): - is_delete = 0:正常记录(默认值) - is_delete = 1:已删除记录 - 所有查询方法自动过滤 is_delete = 0,业务层无需关心
- [文档规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/doc-standard.html): - 文档与代码同步更新 - 使用中文编写(项目约定) - 示例代码可直接运行
- [前端 API 规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/fe-api.html): - 每个后端模块对应一个 API 文件 - 函数命名统一使用 名词 + 动词 格式(与后端保持一致) - 请求路径使用相对路径(/position/page),不含 /api/v1 前缀(由
- [前端页面规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/fe-component.html): - Vue3(Composition API + <script setup>) - ElementPlus - 自定义组件(BasicTable / BasicForm / TableAction
- [Git提交规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/git.html): `` <type>(<scope>): <subject> <body> <footer> ``
- [Handler 层规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/handler.html): - Handler 只做请求分发,不含业务逻辑 - 通过装饰器控制权限和演示模式 - 委托 Service 处理业务逻辑 - 返回 R 封装的响应
- [分页规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/pagination.html): - 前端传递 pageNo(页码,从 1 开始)和 pageSize(每页条数) - 后端返回 {records, total, size, current, pages} 结构 - 默认每页 10
- [Pydantic Schema规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/pydantic.html): - 请求体参数必须声明为 Pydantic 模型(Form 类) - 字段约束使用 Field(...) 定义 - 自定义校验使用 @field_validator - 唯一性校验在 Schema
- [Repository层规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/repository.html): - Repository 只做数据访问原语,不处理 R 封装/序列化/业务逻辑 - 软删除由基类自动处理,子类无需关心 - 查询方法自动跳过 None 值条件
- [Service层规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/service.html): BaseService 采用模板方法模式:基类定义流程骨架,子类通过声明差异点和覆盖钩子定制行为。
- [测试规范](http://docs.tornado.elevue.djangoadmin.cn/zh/volume7/testing.html): 1. 契约测试(Contract):验证路由、权限、表名等契约不变 2. 单元测试(Unit):验证单个函数/方法的逻辑正确性 3. 集成测试(Integration):验证模块间协作、真实数据库操作
## 附录
- [常见问题汇总](http://docs.tornado.elevue.djangoadmin.cn/zh/faq.html): 本页汇总了项目各模块的常见问题及解决方案,按场景分类索引。
- [免责声明](http://docs.tornado.elevue.djangoadmin.cn/zh/other/declare.html): 使用本产品前必读的法律声明,明确产品性质、使用限制与责任划分,一旦使用本产品即视为接受本声明全部条款。
- [广告赞助](http://docs.tornado.elevue.djangoadmin.cn/zh/other/sponsor.html): 1. 投放广告不得与官方全系产品存在结构性冲突和竞争 2. 广告内容需符合国家法律法规,不得包含虚假、误导性信息 3. 广告素材需经官方审核通过后方可上线,审核周期 1-3 个工作日 4.
@@ -0,0 +1,33 @@
# DjangoAdmin Tornado+ElementPlus 开发文档
User-agent: *
Allow: /
# 显式放行生成式引擎 / AI 爬虫(GEO)
User-agent: GPTBot
Allow: /
User-agent: ChatGPT-User
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: Claude-Web
Allow: /
User-agent: anthropic-ai
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: Bytespider
Allow: /
User-agent: bingbot
Allow: /
Sitemap: http://docs.tornado.elevue.djangoadmin.cn/sitemap.xml
@@ -0,0 +1,122 @@
#!/usr/bin/env node
/**
* 生成 public/llms.txt(llmstxt.org 标准)——AI 爬虫的站点摘要索引。
*
* 数据源:zh 目录下全部 Markdown 的 H1 + :::tip 概述首句(与 meta
* description 同源,复用 .vitepress/config/extract.mjs)。按侧边栏章节分组。
*
* 用法:node scripts/generate-llms-txt.mjs
* 已挂入 package.json 的 docs:build 前置步骤。
*/
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
SITE_URL,
SITE_DESCRIPTION,
extractDescription,
} from "../.vitepress/config/extract.mjs";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const ZH = path.join(ROOT, "zh");
/** 顶层目录 → 侧边栏章节名(与 .vitepress/config/zh.ts 保持一致) */
const SECTIONS = [
["", "1. 了解项目"],
["volume1", "2. 快速入门"],
["volume2", "3. 模块开发实战"],
["volume3", "4. 架构设计"],
["volume4", "5. 开发指南"],
["volume5", "6. 代码生成器"],
["volume6", "7. 运维部署"],
["volume7", "8. 规范标准"],
["other", "附录"],
];
function walk(dir, out = []) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, entry.name);
if (entry.isDirectory()) walk(p, out);
else if (entry.name.endsWith(".md")) out.push(p);
}
return out;
}
/** 提取首个 H1 文本 */
function h1Of(md) {
// 围栏感知:跳过 ``` 代码块内的 # 注释,取真正的页面 H1
let inFence = false;
for (const line of md.split("\n")) {
if (line.startsWith("```")) { inFence = !inFence; continue; }
if (!inFence && line.startsWith("# ") && !line.startsWith("## ")) {
return line.slice(2).trim();
}
}
return "";
}
/** H1 编号排序键:"5.10 x" → [5,10];无编号排最后 */
function sortKey(h1) {
const m = h1.match(/^(\d+(?:\.\d+)*)/);
if (m) return m[1].split(".").map(Number);
const letter = h1.match(/^([A-Z])\./);
if (letter) return [1000 + letter[1].charCodeAt(0)];
return [9999];
}
function cmpKey(a, b) {
const len = Math.max(a.length, b.length);
for (let i = 0; i < len; i++) {
const x = a[i] ?? 0;
const y = b[i] ?? 0;
if (x !== y) return x - y;
}
return 0;
}
const files = walk(ZH);
const groups = new Map(SECTIONS.map(([dir, label]) => [label, []]));
for (const file of files) {
const rel = path.relative(ZH, file).replace(/\\/g, "/");
const topDir = rel.includes("/") ? rel.split("/")[0] : "";
const base = rel.split("/").pop();
const label = (topDir === "" && base === "faq.md") ? "附录"
: SECTIONS.find(([dir]) => dir === topDir)?.[1] ?? "附录";
const md = fs.readFileSync(file, "utf8");
const h1 = h1Of(md);
if (!h1) continue;
const desc = extractDescription(md);
const url = `${SITE_URL}/zh/${rel.replace(/\.md$/, ".html")}`;
groups.get(label).push({ h1, desc, url, key: sortKey(h1) });
}
const lines = [
"# DjangoAdmin Tornado+ElementPlus 开发文档",
"",
`> ${SITE_DESCRIPTION}。内置 RBAC 权限体系、代码生成器等开箱即用功能,支持五种数据库一键切换。本文档共 ${files.length} 篇,按章节组织。`,
"",
];
// 按章节顺序输出,合并重复 label(appendix/other 同为「附录」,只输出一次)
const printed = new Set();
for (const [, label] of SECTIONS) {
if (printed.has(label)) continue;
const items = groups.get(label);
if (!items?.length) continue;
printed.add(label);
lines.push(`## ${label}`, "");
items.sort((a, b) => cmpKey(a.key, b.key));
for (const { h1, desc, url } of items) {
lines.push(desc ? `- [${h1}](${url}): ${desc}` : `- [${h1}](${url})`);
}
lines.push("");
}
const out = path.join(ROOT, "public", "llms.txt");
fs.writeFileSync(out, lines.join("\n"), "utf8");
console.log(
`llms.txt: ${files.length} 页 → ${path.relative(ROOT, out)} (${printed.size} 个章节)`,
);
+2
View File
@@ -1,3 +1,5 @@
# 新手入门指南
:::warning 特别提醒
官方精心制作本教程,目的在于方便用户快速掌握软件产品的使用和部署。通过此教程,刚入行的开发者也可以快速掌握并投入产品研发。本地部署时务必请耐心阅读文档操作。
:::
@@ -1,3 +1,9 @@
# 项目介绍
:::tip 概述
DjangoAdmin 是企业级中台解决方案,后端基于 Tornado + SQLAlchemy,前端基于 Vue3 + ElementPlus,内置 RBAC 权限、代码生成器等开箱即用功能,支持五种数据库一键切换。
:::
<div align="center">
<p align="center">
<img src="/logo.png" height="150" alt="logo"/>
@@ -1,5 +1,9 @@
# 免责声明
:::tip 概述
使用本产品前必读的法律声明,明确产品性质、使用限制与责任划分,一旦使用本产品即视为接受本声明全部条款。
:::
:::danger 重要提示
任何用户在使用本团队研发的系列软件产品(以下简称「本产品」)前,请仔细阅读并透彻理解本声明。选择不使用本产品,可随时停止使用;一旦使用本产品,即视为对本声明全部内容的认可和接受。
:::
+2
View File
@@ -1,3 +1,5 @@
# 项目目录结构
## 目录说明
在软件开发过程中,优秀的软件目录结构对于项目的组织、开发、维护和扩展至关重要,合理的目录结构能够显著提升开发效率和代码可维护性。
@@ -1,5 +1,9 @@
# 完整代码清单
:::tip 概述
本页汇总友链(Link)模块涉及的全部文件代码,开发时可快速查阅与复制对照。
:::
本章汇总友链(Link)模块涉及的全部文件,便于开发时快速查阅和复制。
## 后端文件清单
@@ -1,5 +1,9 @@
# 常见问题与排错
:::tip 概述
本页汇总模块开发过程中的常见问题与排错方法,开发受阻时按症状索引即可定位。
:::
本章汇总模块开发过程中的常见问题和解决方案。
## 后端问题
@@ -1,5 +1,9 @@
# 前端 API 层
:::tip 概述
前端 API 层封装后端接口的 HTTP 请求,每个业务模块对应一个 API 文件,本页讲解基于 Axios 封装的编写规范。
:::
前端 API 层负责封装后端接口的 HTTP 请求。每个业务模块对应一个 API 文件,使用项目封装的 Axios 工具发送请求。
## 文件位置
@@ -1,5 +1,9 @@
# 前端路由与菜单
:::tip 概述
本页讲解前端路由与菜单机制——路由和菜单由后端动态返回,前端只需注册路由组件映射。
:::
前端路由定义页面的访问路径,菜单是路由的可视化展示。项目的路由和菜单由后端动态返回,前端只需注册路由组件映射。
## 路由配置
@@ -1,5 +1,9 @@
# 前端页面视图
:::tip 概述
前端页面视图是用户直接交互的界面,本页讲解 Vue3 + ElementPlus 下一个完整 CRUD 页面的文件组成与编写方式。
:::
前端页面视图是用户直接交互的界面。项目使用 Vue3 + ElementPlus + Vite 构建。一个完整的 CRUD 页面由 4 个文件组成:
```
@@ -1,5 +1,9 @@
# 请求处理器(Handler)
:::tip 概述
Handler 层是 HTTP 接口层,负责接收请求、校验权限与记录操作日志,编写新接口从本页开始。
:::
Handler 层是 HTTP 接口层,负责接收请求、校验权限、记录操作日志,然后委托给 Service 层处理。每个业务模块对应一个 `handlers.py` 文件,所有 Handler 继承 `BaseHandler`。
## 文件位置
@@ -1,5 +1,9 @@
# 数据模型(Model)
:::tip 概述
数据模型是模块开发的第一步,讲解如何用 SQLAlchemy ORM 与基类定义数据库表结构,新建模块时从本页开始。
:::
数据模型是模块开发的第一步,用于定义数据库表结构。项目使用 `SQLAlchemy ORM`,模型类通过继承 `BaseModel` 自动获得通用字段和方法。
## 文件位置
@@ -1,5 +1,9 @@
# Repository 层
:::tip 概述
Repository 层是数据访问层,封装全部数据库查询操作,本页讲解基于 BaseRepository 基类的通用 CRUD 用法。
:::
Repository 层是数据访问层,封装所有数据库查询操作。项目通过 `BaseRepository` 基类提供通用 CRUD 能力,子类通常只需声明类名,无需编写任何代码。
## 文件位置
@@ -1,5 +1,9 @@
# Pydantic Schema
:::tip 概述
Schema 层负责请求数据校验,本页讲解基于 Pydantic v2 BaseModel 与 Field 的表单类定义规范。
:::
Schema 层负责请求数据的校验。项目使用 Pydantic v2 的 `BaseModel` 定义表单类,通过 `Field` 约束字段格式,Tornado 在接收请求体时自动执行校验。
## 文件位置
@@ -1,5 +1,9 @@
# 整体架构图
:::tip 概述
本页从全局视角阐述系统整体技术架构,涵盖前端、后端、数据库与基础设施等核心组成,理解系统全貌时从本页开始。
:::
本章从全局视角阐述系统的整体技术架构,涵盖前端、后端、数据库及基础设施等核心组成部分。
## 架构总览
@@ -1,3 +1,5 @@
# 文章管理
:::tip 文章管理
文章管理模块支持富文本编辑、分类关联、封面图片上传、图集管理等功能。适用于新闻、公告、帮助文档等内容管理场景。本模块是典型的「带文件处理」业务模块,涵盖了封面迁移、图集处理、富文本 XSS 清洗等核心机制。
:::
@@ -1,3 +1,5 @@
# 分类管理
:::tip 分类管理
分类管理模块用于管理树形分类数据,如文章分类、产品分类等。通过 `parent_id` 字段实现无限级分类。
:::
@@ -1,3 +1,5 @@
# 公告与链接管理
:::tip 公告/链接/城市
公告、链接、城市是三个简单 CRUD 内容模块,均继承 BaseService,提供标准的增删改查接口。适用于系统公告、友情链接、城市数据等场景。
:::
@@ -1,3 +1,5 @@
# 部门/职级/岗位管理
:::tip 部门/职级/岗位
部门、职级、岗位是组织架构管理的三个核心模块。部门采用树形结构,职级和岗位为简单 CRUD 模块,均继承 `BaseService`。用户表通过 `dept_id`、`position_id`、`level_id` 三个外键关联这三个模块。
:::
@@ -1,3 +1,5 @@
# 定时任务
:::tip 定时任务
定时任务模块基于 APScheduler 实现,支持 Cron 表达式调度、任务执行日志、动态启停等功能。包含 job(任务定义)、job_log(执行日志)、scheduler(调度器)三个子模块。
:::
@@ -1,3 +1,5 @@
# 菜单管理
:::tip 菜单管理
菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。支持目录/菜单(type=0)和权限按钮节点(type=1)两种类型,通过树形结构组织。权限节点由系统根据前端勾选自动生成,采用 `sys:module:action` 三段式命名。
:::
@@ -1,3 +1,5 @@
# 参数管理
:::tip 参数管理
参数管理模块用于管理动态系统参数,与系统配置模块类似但更侧重于业务参数的灵活配置。支持按参数键查询、修改,无需重启服务即可生效。本模块是典型的「BaseService 简单模块」,通过声明差异点即可获得完整 CRUD 能力。
:::
@@ -1,3 +1,5 @@
# 角色管理
:::tip 角色管理
角色是 RBAC 权限模型的核心,连接用户和菜单(权限)。通过为角色分配菜单权限,再将角色分配给用户,实现灵活的权限管理。角色删除时需清理关联的用户角色和角色菜单数据,并即时失效受影响用户的权限缓存。
:::
@@ -1,3 +1,5 @@
# 用户管理
:::tip 用户管理
用户管理是系统的核心模块,属于复杂业务模块。包含用户 CRUD、角色关联、密码加密、行政区划、Excel 导入导出等功能。与简单 CRUD 模块不同,用户管理使用自定义 service 函数而非继承 BaseService 的类方法。
:::
@@ -1,3 +1,5 @@
# 验证码机制
:::tip 验证码
验证码是防止自动化攻击的重要安全手段。使用 Pillow 生成图片验证码,存储在 Redis 中,支持防刷限流和万能验证码(演示模式)。源码位于 `src/common/utils/captcha.py`。
:::
@@ -1,3 +1,5 @@
# 系统配置管理
:::tip 系统配置
系统配置模块用于管理动态的键值对配置项,与数据字典类似但更偏向系统级参数。支持分组管理,可通过接口动态修改,无需重启服务。模块位于 `src/modules/config/` 和 `src/modules/config_item/`,路由位于 `src/api/v1/config.py` 和 `config_item.py`。
:::
@@ -1,3 +1,5 @@
# 演示模式
:::tip 演示模式
演示模式用于在线体验环境,开启后所有写操作(新增、编辑、删除等)将被拦截,返回"演示环境,暂无操作权限"提示。通过 `TORNADO_DEMO` 环境变量控制。装饰器位于 `src/common/middleware/access_decorators.py`。
:::
@@ -1,3 +1,5 @@
# 字典模块
:::tip 数据字典
数据字典用于管理系统中的枚举值和配置项,如状态、类型、性别等。通过字典编码统一管理,前端可通过 `useDictStore` 获取字典选项,避免硬编码。后端读取工具位于 `src/common/utils/dict_util.py`,内置进程内 TTL 缓存。
:::
@@ -1,3 +1,5 @@
# 异常与响应处理
:::tip 全局异常与响应
定义了统一的响应格式和全局异常处理机制。所有 API 返回 `{code, data, msg, ok}` 标准格式,异常被捕获后也转换为相同格式。响应函数位于 `src/core/response.py`,异常类型位于 `src/core/exceptions.py`。
:::
@@ -1,3 +1,5 @@
# 用户认证(JWT)
:::tip JWT认证
JWT(JSON Web Token)是的核心认证机制。用户登录成功后获取 Token,后续请求通过 `Authorization: Bearer <token>` 头携带 Token 进行身份验证。项目内置了 Token 生成、解析、刷新和黑名单管理功能,源码位于 `src/core/jwt.py`。
:::
@@ -1,3 +1,5 @@
# 登录日志
:::tip 登录日志
登录日志记录每次登录尝试(成功/失败),包含登录账号、IP地址、浏览器信息、登录时间等。支持 IP 地理位置解析(ip2region)和 User-Agent 解析。登录逻辑位于 `src/modules/auth/service.py`,日志记录位于 `src/modules/login_log/service.py`。
:::
@@ -1,3 +1,5 @@
# 操作日志
:::tip 操作日志
操作日志装饰器自动记录写请求(POST/PUT/DELETE/PATCH)的操作日志,无需手动埋点。源码位于 `src/common/middleware/operation_log.py`。
:::
@@ -1,3 +1,5 @@
# 权限管理(RBAC)
:::tip RBAC权限
采用基于角色的访问控制(RBAC)模型,通过用户-角色-菜单三层关系实现细粒度的权限管理。权限控制贯穿前后端:后端通过 `@permission_required` 装饰器校验(`src/common/middleware/access_decorators.py`),前端通过 `v-perm` 指令控制按钮显隐。
:::
@@ -1,3 +1,5 @@
# 参数验证(Pydantic)
:::tip 参数验证
使用 Pydantic v2 进行请求参数验证。所有请求体参数通过 Pydantic BaseModel 定义,Tornado 自动解析和验证 JSON 请求体,验证失败返回标准格式错误信息。BaseSchema 基类位于 `src/core/base_schema.py`。
:::
+1 -1
View File
@@ -1,7 +1,7 @@
# 为什么选择我们?
:::tip 核心价值
企业级数据中台不应从零搭建。本框架提供开箱即用的完整解决方案,为中大型企业构建统一的数字化基座。
本框架提供开箱即用的企业级中台完整解决方案,免去从零搭建成本,助力中大型企业构建统一的数字化基座。
:::
## 解决什么问题?
@@ -29,7 +29,7 @@
//
// themeConfig: {
// editLink: {
// pattern: "https://gitee.com/djangoadmin/DjangoAdmin_Fastapi_EleVue",
// pattern: "https://gitee.com/djangoadmin/DjangoAdmin_Tornado_NaiveVue",
// text: "Suggest changes to this page",
// },
//
@@ -0,0 +1,5 @@
export declare const SITE_URL: string;
export declare const SITE_DESCRIPTION: string;
export declare function stripNumberPrefix(title: string): string;
export declare function extractDescription(md: string): string;
export declare function extractFaqs(md: string): { q: string; a: string }[];
@@ -0,0 +1,130 @@
/**
* SEO/GEO 共享工具(纯 ESM,零依赖)。
*
* 同时被两类调用方 import:
* 1. VitePress 配置(.ts,esbuild 打包时内联本文件)
* 2. scripts/generate-llms-txt.mjs(Node 直接运行)
*
* 注意:SITE_URL 是全站唯一 URL 真相源。
* docs 子域当前 TLS 证书为腾讯 CDN 默认证书(*.cdn.myqcloud.com,校验失败),
* 故暂用 http;证书修复后只需改这里一处。
*/
export const SITE_URL = "http://docs.tornado.naivevue.djangoadmin.cn";
export const SITE_DESCRIPTION =
"DjangoAdmin Tornado+NaiveUI 企业级中台解决方案开发者文档";
/**
* 剥离 H1/标题中的章节编号前缀,用于 <title> 与 JSON-LD headline。
* 例:"5.10 字典模块" → "字典模块";"K. 贡献指南" → "贡献指南"。
* 页内 H1 与侧边栏编号保持不变,仅影响 SEO 输出。
*/
export function stripNumberPrefix(title) {
return String(title)
.replace(/^(?:\d+(?:\.\d+)+|[A-Z]\.)\s+/, "")
.trim();
}
/**
* 从页面 Markdown 提取 meta description:
* 1. 首选 `:::tip xxx` 容器内的首句(141/143 页的标准 opener)
* 2. 回退到 H1 后的第一个普通段落
* 输出压缩为单行、去除行内 Markdown,按中文句末标点截断(≤100 字符)。
*/
export function extractDescription(md) {
const src = String(md);
const fm = src.match(/^---\r?\n([\s\S]*?)\r?\n---/);
const body = fm ? src.slice(fm[0].length) : src;
let text = "";
// frontmatter description 显式声明优先(无 H1 页面的补齐手段)
const fmDesc = fm && fm[1].match(/^description:\s*(.+)$/m);
if (fmDesc) {
text = fmDesc[1].trim().replace(/^["']|["']$/g, "");
}
const tip = !text && body.match(/^:::\s*tip[^\n]*\n([\s\S]*?)\n:::/m);
if (tip) {
text = tip[1];
}
if (!text) {
const afterH1 = body.match(/^# (?!#)(.+)$/m);
if (afterH1) {
const rest = body.slice((afterH1.index ?? 0) + afterH1[0].length);
const para = rest
.split(/\n{2,}/)
.find(
(p) =>
p.trim() &&
!/^\s*#/.test(p) &&
!/^\s*:::/.test(p) &&
!/^\s*```/.test(p) &&
!/^\s*\|/.test(p),
);
text = para || "";
}
}
text = text
.replace(/\s+/g, " ")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
.trim();
if (!text) return "";
// 按中文句末标点截取首句,上限 100 字符;截断时避免切碎词
const m = text.match(/^.{1,100}?(?:[。!?!?]|$)/);
let out = m ? m[0] : text.slice(0, 100);
if (out.length < text.length && !/[。!?!?]$/.test(out)) {
out = out.replace(/[^\s,、;:,;:]+$/, "");
}
return out.trim() || text.slice(0, 100);
}
function cleanText(s) {
return s
.replace(/^:::[^\n]*$/gm, "")
.replace(/^```[^\n]*$/gm, "")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
.replace(/\s+/g, " ")
.trim();
}
/**
* 提取页面中的 Q/A 对(用于 FAQPage JSON-LD),支持两种站内格式:
* A. `### Q:问题` 标题式(如 zh/quickstart/faq.md)
* B. `**Q:问题**` 加粗式(如 zh/appendix/faq.md)
* 返回 [{ q, a }];无匹配返回 []。
*/
export function extractFaqs(md) {
const body = String(md).replace(/^---[\s\S]*?\n---\s*/, "");
let faqs = [];
// 格式 A:按 `### Q:` 切块,答案截断到下一个标题
for (const part of body.split(/(?=^###\s*Q[::])/m)) {
const m = part.match(/^###\s*Q[::]\s*([^\n]+)\n+([\s\S]*)/);
if (!m) continue;
let a = m[2];
const cut = a.search(/^#{1,4}\s/m);
if (cut >= 0) a = a.slice(0, cut);
faqs.push({ q: cleanText(m[1]), a: cleanText(a) });
}
// 格式 B:按 `**Q:` 切块
if (!faqs.length) {
for (const part of body.split(/(?=^\*\*Q[::])/m)) {
const m = part.match(/^\*\*Q[::]\s*([^*]+)\*\*\s*\n+([\s\S]*)/);
if (!m) continue;
let a = m[2];
const cut = a.search(/^(?:#{1,4}\s|\*\*Q)/m);
if (cut >= 0) a = a.slice(0, cut);
faqs.push({ q: cleanText(m[1]), a: cleanText(a) });
}
}
return faqs.filter((f) => f.q && f.a);
}
@@ -1,14 +1,22 @@
import { defineConfig } from "vitepress";
import { enConfig } from "./en";
import { createSeoHooks } from "./seo";
import { sharedConfig } from "./shared";
import { zhConfig } from "./zh";
const seo = createSeoHooks(zhConfig.themeConfig?.sidebar);
export default defineConfig({
...sharedConfig,
ignoreDeadLinks: [
(link) => link.includes('localhost'),
],
// SEO:页面级 title/description、canonical/OG/JSON-LD、sitemap+lastmod
transformPageData: seo.transformPageData,
transformHead: seo.transformHead,
sitemap: seo.sitemap,
locales: {
root: { label: "简体中文", lang: "zh-CN", link: "/", ...zhConfig },
// root: { label: "English", lang: "en-US", link: "/", ...enConfig },
@@ -0,0 +1,311 @@
/**
* SEO/GEO 构建期钩子(仅 Node 构建时运行,不进客户端 bundle):
* - transformPageData:每页独立 title(去编号前缀)与 description(取 :::tip 首句)
* - transformHead:canonical / Open Graph / twitter 卡片 / JSON-LD 结构化数据
* - sitemap.transformItems:注入 lastmod(git 最后提交时间)并过滤 404
*
* 由 config/index.ts 通过 createSeoHooks(zhConfig.themeConfig?.sidebar) 挂载。
* 依赖 extract.mjs(纯 ESM,同被 scripts/generate-llms-txt.mjs 复用)。
*/
import fs from "node:fs";
import path from "node:path";
import { execSync } from "node:child_process";
import {
SITE_URL,
extractDescription,
extractFaqs,
stripNumberPrefix,
} from "./extract.mjs";
/** 与 vitepress HeadConfig 同构:[tag, attrs, innerHTML?] */
type HeadTuple = [string, Record<string, any>, string?];
interface SideItem {
text?: string;
link?: string;
items?: SideItem[];
}
interface PageDataLike {
relativePath?: string;
title?: string;
description?: string;
frontmatter?: Record<string, any>;
[k: string]: any;
}
interface TransformCtx {
pageData?: PageDataLike;
title?: string;
description?: string;
content?: string;
siteConfig?: { root?: string; srcDir?: string };
}
const ORG = {
"@type": "Organization",
name: "DjangoAdmin",
url: "https://www.djangoadmin.cn",
logo: `${SITE_URL}/logo.png`,
sameAs: [
"https://gitee.com/djangoadmin/DjangoAdmin_Tornado_NaiveVue",
"https://www.djangoadmin.cn",
],
};
/** 相对路径 → 站内路由(cleanUrls 关闭,正文页带 .html) */
function routeOf(relativePath: string): string {
if (relativePath === "index.md") return "/";
if (relativePath === "404.md") return "/404.html";
return `/${relativePath.replace(/\.md$/, ".html")}`;
}
/** 去掉侧边栏文案前导 emoji/符号,保留 "1.1 项目介绍" 这类编号文字 */
function cleanLabel(s?: string): string {
return (s || "").replace(/^[^\p{L}\p{N}]+/u, "").trim();
}
/** 从侧边栏构建 route → 面包屑文案链(含分组小节) */
function buildCrumbMap(sidebar: unknown): Map<string, string[]> {
const map = new Map<string, string[]>();
const groups: SideItem[] = Array.isArray(sidebar)
? sidebar
: ((sidebar as Record<string, SideItem[]>)?.["/zh/"] ?? []);
const walk = (items: SideItem[], trail: string[]) => {
for (const item of items) {
if (item.link && item.link.startsWith("/")) {
map.set(item.link, [...trail, cleanLabel(item.text)]);
} else if (item.items?.length) {
walk(item.items, [...trail, cleanLabel(item.text)]);
}
}
};
for (const group of groups) {
walk(group.items ?? [], [cleanLabel(group.text)].filter(Boolean));
}
return map;
}
/** 从渲染后的 HTML 提取 H1 纯文本(JSON-LD headline 须与页面 H1 一致) */
function h1OfHtml(html?: string): string {
const m = (html || "").match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
if (!m) return "";
return m[1]
.replace(/<[^>]+>/g, "")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.trim();
}
/** 从 Markdown 源提取 H1 纯文本(去除行内 markdown 标记) */
function h1OfMd(md: string): string {
// 围栏感知:跳过 ``` 代码块内的 # 注释,取真正的页面 H1
let inFence = false;
for (const line of md.split("\n")) {
if (line.startsWith("```")) { inFence = !inFence; continue; }
if (!inFence && /^# (?!#)/.test(line)) {
return line
.replace(/^# /, "")
.replace(/`([^`]+)`/g, "$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.trim();
}
}
return "";
}
export function createSeoHooks(sidebar: unknown) {
const crumbs = buildCrumbMap(sidebar);
const projectDirs: string[] = [];
let gitDates: Map<string, string> | null = null;
const readMd = (relativePath: string, srcDir?: string): string => {
try {
return fs.readFileSync(
path.resolve(srcDir || process.cwd(), relativePath),
"utf8",
);
} catch {
return "";
}
};
/**
* 项目内每个文件最后一次 git 提交的 ISO 时间。
* 单次 git log 调用构建全量映射(key = 相对路径,与 pageData.relativePath 一致);
* git 不可用时静默降级(sitemap 无 lastmod、JSON-LD 无 dateModified)。
*/
const getGitDates = (root: string): Map<string, string> => {
if (gitDates) return gitDates;
gitDates = new Map<string, string>();
try {
const out = execSync(
"git log --format=:::%cI --name-only --relative -n 500 -- .",
{
cwd: root,
encoding: "utf8",
maxBuffer: 64 * 1024 * 1024,
stdio: ["ignore", "pipe", "ignore"],
},
);
let current = "";
for (const line of out.split(/\r?\n/)) {
if (line.startsWith(":::")) {
current = line.slice(3).trim();
continue;
}
const file = line.trim();
if (file && current && !gitDates.has(file)) {
gitDates.set(file, current);
}
}
} catch {
// 降级:无 lastmod / dateModified
}
return gitDates;
};
/** 每页独立 title(去 "5.10 " 编号前缀,关键词前置)与 description(:::tip 首句) */
function transformPageData(pageData: PageDataLike, ctx?: any): Record<string, unknown> {
const srcDir: string | undefined = ctx?.siteConfig?.srcDir;
const root: string | undefined = ctx?.siteConfig?.root;
if (root && !projectDirs.includes(root)) projectDirs.push(root);
const out: Record<string, unknown> = {};
const rel = pageData.relativePath;
if (rel && !pageData.frontmatter?.description) {
const desc = extractDescription(readMd(rel, srcDir));
if (desc) out.description = desc;
}
if (pageData.title) {
out.title = stripNumberPrefix(pageData.title);
}
return out;
}
/** 构建期注入 canonical / OG / twitter / JSON-LD */
function transformHead(ctx: TransformCtx): HeadTuple[] {
const rel = ctx?.pageData?.relativePath || "";
if (!rel || rel === "404.md") return [];
const route = routeOf(rel);
const url = `${SITE_URL}${route}`;
const title = ctx.pageData?.title || ctx.title || "";
const desc = ctx.description || "";
const isHome = rel === "index.md";
const image = `${SITE_URL}/logo.png`;
const heads: HeadTuple[] = [
["link", { rel: "canonical", href: url }],
["meta", { property: "og:type", content: isHome ? "website" : "article" }],
["meta", { property: "og:site_name", content: "DjangoAdmin" }],
["meta", { property: "og:locale", content: "zh_CN" }],
["meta", { property: "og:title", content: title }],
["meta", { property: "og:description", content: desc }],
["meta", { property: "og:url", content: url }],
["meta", { property: "og:image", content: image }],
["meta", { property: "twitter:title", content: title }],
["meta", { property: "twitter:description", content: desc }],
["meta", { property: "twitter:url", content: url }],
["meta", { property: "twitter:image", content: image }],
];
// ---- JSON-LD ----
const root = ctx.siteConfig?.root || projectDirs[0] || process.cwd();
const srcDir = ctx.siteConfig?.srcDir || root;
const lastMod = getGitDates(root).get(rel);
const blocks: Record<string, any>[] = [];
if (isHome) {
blocks.push({
"@context": "https://schema.org",
"@type": "WebSite",
name: "DjangoAdmin 开发文档",
url: `${SITE_URL}/`,
inLanguage: "zh-CN",
description: desc,
});
blocks.push({ "@context": "https://schema.org", ...ORG });
} else {
const md = readMd(rel, srcDir);
const article: Record<string, any> = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: h1OfMd(md) || h1OfHtml(ctx.content) || title,
description: desc,
url,
inLanguage: "zh-CN",
mainEntityOfPage: { "@type": "WebPage", "@id": url },
publisher: ORG,
};
if (lastMod) article.dateModified = lastMod;
blocks.push(article);
const trail = crumbs.get(route);
if (trail?.length) {
const items: Record<string, any>[] = [
{ "@type": "ListItem", position: 1, name: "首页", item: `${SITE_URL}/` },
];
trail.forEach((name, i) => {
const node: Record<string, any> = {
"@type": "ListItem",
position: i + 2,
name,
};
if (i === trail.length - 1) node.item = url;
items.push(node);
});
blocks.push({
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items,
});
}
const faqs = extractFaqs(md);
if (faqs.length) {
blocks.push({
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((f) => ({
"@type": "Question",
name: f.q,
acceptedAnswer: { "@type": "Answer", text: f.a },
})),
});
}
}
// 转义 "<",防止内容中出现 </script> 破坏注入
const json = JSON.stringify(blocks).replace(/</g, "\\u003c");
heads.push(["script", { type: "application/ld+json" }, json]);
return heads;
}
/** sitemap.xml:过滤 404/README,注入 git lastmod */
const sitemap = {
hostname: SITE_URL,
transformItems(items: { url: string; lastmod?: string }[]) {
const root = projectDirs[0] || process.cwd();
const dates = getGitDates(root);
const out: { url: string; lastmod?: string }[] = [];
for (const item of items) {
// ⚠ vitepress 传入的 item.url 是相对路径("404.html"、"zh/x.html"、""),
// 无前导斜杠也无域名——hostname 由 SitemapStream 事后拼接。
if (/(^|\/)404\.html$/.test(item.url)) continue; // 404 页不收录
if (/(^|\/)README\.html$/.test(item.url)) continue; // 仓库 README 非文档页
const p = item.url.replace(/^\//, "");
const rel =
p === "" || p === "index.html" ? "index.md" : p.replace(/\.html$/, ".md");
const lastmod = dates.get(rel);
out.push(lastmod ? { ...item, lastmod } : item);
}
return out;
},
};
return { transformPageData, transformHead, sitemap };
}
@@ -1,6 +1,6 @@
import { defineConfig, HeadConfig } from "vitepress";
export const META_IMAGE = "/social.png";
export const META_IMAGE = "/logo.png";
const rControl = /[\u0000-\u001f]/g;
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g;
@@ -23,6 +23,8 @@ export const slugify = (str: string): string =>
export const sharedConfig = defineConfig({
title: "小蚂蚁云",
// 页面 <title> 后缀:「环境准备 | DjangoAdmin 开发文档」(index.md frontmatter 可覆盖)
titleTemplate: "DjangoAdmin 开发文档",
appearance: "light",
markdown: {
@@ -1,8 +1,9 @@
import type { DefaultTheme, LocaleSpecificConfig } from "vitepress";
export const META_URL = "https://www.djangoadmin.cn";
export const META_TITLE = "企业级中台解决方案";
export const META_DESCRIPTION = "企业级中台解决方案";
export const META_URL = "http://docs.tornado.naivevue.djangoadmin.cn";
export const META_TITLE = "DjangoAdmin Tornado+NaiveUI 开发文档";
export const META_DESCRIPTION =
"DjangoAdmin Tornado+NaiveUI 企业级中台解决方案开发者文档";
export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
description: META_DESCRIPTION,
+12
View File
@@ -0,0 +1,12 @@
---
head:
- - meta
- name: robots
content: noindex
---
# 页面不存在
抱歉,您访问的页面不存在或已被移动。
[返回首页](/) · [浏览文档](/zh/introduction)
+2 -1
View File
@@ -5,7 +5,8 @@
"type": "module",
"scripts": {
"docs": "vitepress dev .",
"docs:build": "vitepress build .",
"docs:llms": "node scripts/generate-llms-txt.mjs",
"docs:build": "node scripts/generate-llms-txt.mjs && vitepress build .",
"docs:preview": "vitepress preview ."
},
"dependencies": {
@@ -0,0 +1,168 @@
# DjangoAdmin Tornado+NaiveUI 开发文档
> DjangoAdmin Tornado+NaiveUI 企业级中台解决方案开发者文档。内置 RBAC 权限体系、代码生成器等开箱即用功能,支持五种数据库一键切换。本文档共 138 篇,按章节组织。
## 1. 了解项目
- [更新日志](http://docs.tornado.naivevue.djangoadmin.cn/zh/changelog.html): 更新日志是项目演进的完整记录,帮助开发者和企业了解每个版本的能力变化与技术演进方向。
- [新手入门指南](http://docs.tornado.naivevue.djangoadmin.cn/zh/course.html): 详细的安装步骤请参考 环境准备 章节。
- [项目介绍](http://docs.tornado.naivevue.djangoadmin.cn/zh/introduction.html): DjangoAdmin 是企业级中台解决方案,后端基于 Tornado + SQLAlchemy,前端基于 Vue3 + NaiveUI,内置 RBAC 权限、代码生成器等开箱即用功能,
- [项目目录结构](http://docs.tornado.naivevue.djangoadmin.cn/zh/struct.html): + 保持一致性:目录结构一旦确定,应保持一致性,避免随意更改。
- [为什么选择我们?](http://docs.tornado.naivevue.djangoadmin.cn/zh/why.html): 本框架提供开箱即用的企业级中台完整解决方案,免去从零搭建成本,助力中大型企业构建统一的数字化基座。
## 2. 快速入门
- [后端启动](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/backend.html): 启动后端前,请确保已完成 环境准备 章节中的所有软件安装。
- [数据库初始化](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/database.html): 数据库初始化前,请确保已完成以下准备工作: 1. 目标数据库服务已启动(如 MySQL 8.0+) 2. .env 文件中的 DB_DRIVER 和数据库连接信息已正确配置 3. Python
- [Docker 快速部署](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/docker.html): Docker 部署适合快速体验和生产环境。
- [环境变量速查表](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/env-vars.html): 1. .env.example 是环境变量模板文件,已提交到版本控制。
- [环境准备](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/environment.html): 1. 部分软件安装后需要设置本地环境变量,便于在终端中使用命令。
- [常见问题FAQ](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/faq.html): 遇到问题时,建议先查看终端日志输出和浏览器控制台(F12)的错误信息,通常能快速定位问题原因。
- [第一个接口调试](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/first-api.html): 开始调试前,请确保已完成以下步骤: 1. 后端服务已启动(参见 后端启动) 2. 数据库已初始化(参见 数据库初始化) 3. Redis 服务已启动
- [前端启动](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume1/frontend.html): 启动前端前,请确保已完成 环境准备 章节中的 Node.js 和 pnpm 安装,并已启动后端服务(参见 后端启动)。
## 3. 模块开发实战
- [完整代码清单](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/code-list.html): 本页汇总友链(Link)模块涉及的全部文件代码,开发时可快速查阅与复制对照。
- [示例模块(Example)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/example.html): src/modules/example/ 是一个完整的示例模块,演示了标准 CRUD 模块的六文件结构和开发模式。
- [常见问题与排错](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/faq.html): 本页汇总模块开发过程中的常见问题与排错方法,开发受阻时按症状索引即可定位。
- [前端 API 层](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/fe-api.html): 前端 API 层封装后端接口的 HTTP 请求,每个业务模块对应一个 API 文件,本页讲解基于 Axios 封装的编写规范。
- [前端路由与菜单](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/fe-router.html): 本页讲解前端路由与菜单机制——路由和菜单由后端动态返回,前端只需注册路由组件映射。
- [前端页面视图](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/fe-view.html): 前端页面视图是用户直接交互的界面,本页讲解 Vue3 + NaiveUI 下一个完整 CRUD 页面的文件组成与编写方式。
- [请求处理器(Handler)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/handler.html): Handler 层是 HTTP 接口层,负责接收请求、校验权限与记录操作日志,编写新接口从本页开始。
- [数据模型(Model)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/model.html): BaseModel 是所有业务模型的基类,提供6个通用审计字段和 to_dict() 序列化方法。
- [概述与目标](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/overview.html): 其中 id、create_user、create_time、update_user、update_time、is_delete 由基类 BaseModel 自动提供,业务字段仅需定义 name、
- [权限节点配置](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/permission.html): 实际开发中,查询类权限(page、detail、list)通常合并为一个节点 sys:position:list,减少配置工作量。
- [Repository 层](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/repository.html): Repository 层是数据访问层,封装全部数据库查询操作,本页讲解基于 BaseRepository 基类的通用 CRUD 用法。
- [路由注册](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/router.html): 新模块只需两步: 1. 在 src/api/v1/ 下创建路由文件,定义 routes 列表 2. 在 router.py 的 _RESOURCE_MODULES 中追加模块导入 路由前缀
- [Pydantic Schema](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/schema.html): Schema 层负责请求数据校验,本页讲解基于 Pydantic v2 BaseModel 与 Field 的表单类定义规范。
- [Service 层](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume2/service.html): repo_cls 是 类属性,值为 Repository 类(不是实例)。
## 4. 架构设计
- [核心基类体系](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/base-classes.html): 四大基类各司其职,覆盖从数据模型到请求校验再到业务逻辑的完整链路。
- [缓存架构设计](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/cache.html): 采用两级缓存架构:第一级为进程内字典缓存(dict_util),第二级为 Redis 分布式缓存。
- [数据库设计](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/database-design.html): 所有业务表继承 base_model 基类,自动获得 6 个通用字段(id / create_user / create_time / update_user / update_time /
- [目录结构设计](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/directory.html): 目录结构遵循「按功能分组、按职责分层」的原则。
- [扩展性设计](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/extensibility.html): 遵循开闭原则(OCP):对扩展开放,对修改关闭。
- [分层架构设计](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/layer-arch.html): 采用经典的分层架构,核心原则是:依赖单向、职责单一、接口隔离。
- [整体架构图](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/overview.html): 本页从全局视角阐述系统整体技术架构,涵盖前端、后端、数据库与基础设施等核心组成,理解系统全貌时从本页开始。
- [请求完整链路](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/request-flow.html): 一个典型的 API 请求经过以下阶段:浏览器 → Nginx → Tornado → Handler 生命周期(set_default_headers → prepare →
- [安全架构设计](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/security.html): 纵深防御:认证、授权、限流、参数校验多层防护,单一防线失效时仍有其他机制兜底 最小权限:用户仅能访问已授权接口,角色权限按需分配,遵循"够用即可"原则 数据不落地:敏感信息(Token、
- [技术选型说明](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/tech-stack.html): Tornado 是 Python 生态中成熟的异步 Web 框架,基于自有事件循环实现非阻塞 I/O。
- [Tornado IOLoop 与异步模型](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume3/tornado-async.html): Tornado 基于单线程事件循环(IOLoop),Handler 方法默认为协程(async def)。
## 5. 开发指南
- [API 接口速查表](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/api-cheatsheet.html): API 接口速查表,按模块分组列出所有 RESTful 接口的请求方法、路径、说明和权限标识。
- [文章管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/article.html): 文章管理模块支持富文本编辑、分类关联、封面图片上传、图集管理等功能。
- [分类管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/category.html): 分类管理模块用于管理树形分类数据,如文章分类、产品分类等。
- [行政区划管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/city.html): 行政区划管理模块提供省市区三级行政区划数据的查询和管理能力,支持树形结构展示和下拉选择。
- [公告与链接管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/content.html): 公告、链接、城市是三个简单 CRUD 内容模块,均继承 BaseService,提供标准的增删改查接口。
- [部门/职级/岗位管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/dept.html): 部门、职级、岗位是组织架构管理的三个核心模块。
- [文件模板管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/file-template.html): 文件模板管理模块提供文件导入导出模板的管理能力,用于维护各种业务场景的 Excel/Word 等模板文件。
- [定时任务日志](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/job-log.html): 定时任务日志模块记录定时任务的执行历史,包括执行时间、执行结果、耗时等信息。
- [定时任务](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/job.html): 定时任务模块基于 APScheduler 实现,支持 Cron 表达式调度、任务执行日志、动态启停等功能。
- [菜单管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/menu.html): 菜单管理是 RBAC 权限系统的基础,负责管理系统的导航菜单和权限节点。
- [参数管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/param.html): 参数管理模块用于管理动态系统参数,与系统配置模块类似但更侧重于业务参数的灵活配置。
- [角色管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/role.html): 角色是 RBAC 权限模型的核心,连接用户和菜单(权限)。
- [用户管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/business/user.html): 用户管理是系统的核心模块,属于复杂业务模块。
- [认证流程实战](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/auth-flow.html): 登录 → Token 获取 → 权限校验 → Token 刷新 → 退出登录的完整认证链路实战说明。
- [验证码机制](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/captcha.html): 验证码是防止自动化攻击的重要安全手段。
- [系统配置管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/config.html): 系统配置模块用于管理动态的键值对配置项,与数据字典类似但更偏向系统级参数。
- [CORS 跨域配置](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/cors.html): CORS(Cross-Origin Resource Sharing)跨域资源共享是前后端分离架构必须处理的问题。
- [演示模式](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/demo-mode.html): 演示模式用于在线体验环境,开启后所有写操作(新增、编辑、删除等)将被拦截,返回"演示环境,暂无操作权限"提示。
- [数据字典使用指南](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/dict-guide.html): 数据字典的完整使用流程,从新增字典类型到在代码中使用字典数据的实操指南。
- [字典模块](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/dict.html): 数据字典用于管理系统中的枚举值和配置项,如状态、类型、性别等。
- [异常与响应处理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/exception.html): 定义了统一的响应格式和全局异常处理机制。
- [优雅关闭](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/graceful-shutdown.html): 优雅关闭确保应用在收到终止信号时,能够完成正在处理的请求、关闭数据库连接、停止定时任务调度器后再退出,避免请求中断和资源泄漏。
- [用户认证(JWT)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/jwt.html): JWT(JSON Web Token)是的核心认证机制。
- [登录日志](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/login-log.html): 登录日志记录每次登录尝试(成功/失败),包含登录账号、IP地址、浏览器信息、登录时间等。
- [登录防爆破](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/login-protect.html): 登录防爆破机制基于 Redis 实现同一 IP + 用户名维度的连续失败计数与锁定,防止暴力破解密码。
- [操作日志](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/operation-log.html): 操作日志装饰器自动记录写请求(POST/PUT/DELETE/PATCH)的操作日志,无需手动埋点。
- [权限管理(RBAC)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/rbac.html): 采用基于角色的访问控制(RBAC)模型,通过用户-角色-菜单三层关系实现细粒度的权限管理。
- [链路追踪(TraceID)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/trace-id.html): 链路追踪通过为每个请求分配唯一的 TraceID,将同一请求的所有日志串联起来,方便问题排查和性能分析。
- [参数验证(Pydantic)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/core/validator.html): 使用 Pydantic v2 进行请求参数验证。
- [API 层开发](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/api.html): 前端 API 层位于 src/api/ 目录,封装所有与后端的 HTTP 请求。
- [前端构建](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/build.html): 前端使用 Vite 构建,支持开发服务器和生产构建。
- [公共组件库](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/components.html): 项目提供丰富的公共组件,位于 ui/src/components/ 目录,基于 NaiveUI 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。
- [BasicForm 表单](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/form.html): BasicForm 位于 src/components/Form/src/BasicForm.vue,基于 NaiveUI 的 n-form 二次封装,通过 schemas 配置化定义表单字段,
- [Hooks 组合式函数](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/hooks.html): 项目封装了一系列组合式函数(Hooks),位于 src/hooks/ 和 src/utils/ 目录,提供权限检查、异步 loading、防重复提交、实时时钟、项目配置等可复用逻辑。
- [国际化](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/i18n.html): 当前项目仅支持中文界面,NaiveUI 默认使用中文,所有界面文本直接硬编码在组件中。
- [Modal 弹窗](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/modal.html): 项目提供 BasicModal 组件(src/components/Modal/src/BasicModal.vue)和 useModal hook 管理弹窗状态。
- [权限指令 v-perm](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/perm-directive.html): v-perm 和 v-perms 是项目自定义的 Vue 指令,用于按钮级别的权限控制,无权限时自动从 DOM 中移除元素。
- [路由与菜单](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/router.html): 前端路由基于 Vue Router 4 实现动态路由生成,菜单数据从后端获取,通过 generator-routers.ts 递归生成路由表,路由守卫控制登录状态和权限。
- [前端环境与结构](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/setup.html): 前端基于 Vue3 + NaiveUI 构建,使用 pnpm 作为包管理工具,Vite 作为构建工具,TypeScript 作为开发语言。
- [Store 状态管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/store.html): 前端使用 Pinia 进行状态管理,核心模块包括 user(用户信息与权限)、asyncRoute(动态路由与菜单)、projectSetting(项目配置)、designSetting(主题设计)等。
- [样式与主题](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/style.html): 前端样式方案采用 Tailwind CSS + SCSS + NaiveUI 主题覆盖,支持深色模式切换。
- [BasicTable 表格](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/table.html): BasicTable 位于 src/components/Table/src/Table.vue,基于 NaiveUI 的 n-data-table 二次封装,提供列定义、数据请求、分页、操作列、
- [工具函数](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/utils.html): 前端工具函数位于 src/utils/ 目录,提供认证管理、本地存储、日期格式化、树结构操作、文件下载等通用能力。
- [页面视图开发](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/frontend/view.html): 页面视图位于 src/views/ 目录,每个业务模块遵循 index.vue(列表页)+ edit.vue(编辑弹窗)的标准模式,辅以 columns.ts(列定义)和
- [接口响应规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/integration/api-contract.html): 后端所有接口统一返回 R.ok() / R.failed() 格式的 JSON 响应,前端通过 VAxios 的 transformRequestData 拦截器统一解包处理。
- [字典数据联调](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/integration/dict-sync.html): 字典数据通过后端字典 API 获取,前端在下拉选择器中渲染选项。
- [权限节点联动](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/integration/perm-sync.html): 前后端权限通过统一的权限节点字符串联动,后端 @permission_required 校验权限,前端 v-perm 指令和 TableAction 的 auth 属性控制按钮显示。
- [代理配置](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/integration/proxy.html): 开发环境通过 Vite 代理转发 API 请求到后端,生产环境通过 Nginx 代理。
- [文件上传联调](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/integration/upload-sync.html): 文件上传涉及前端 Upload 组件、后端上传接口和 URL 拼接三个环节,需要前后端协同配置。
- [请求上下文(ContextVar)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/context.html): src/core/context.py 使用 Python 标准库 contextvars 管理请求级别的上下文信息,包括当前用户 ID、用户名、姓名和链路追踪 ID。
- [数据库会话](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/db-session.html): 数据库会话通过 scoped_session 实现线程级隔离,Repository 层的写操作自动 commit,BaseHandler.on_finish() 兜底关闭会话。
- [字典缓存工具](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/dict-util.html): src/common/utils/dict_util.py 提供业务字典的缓存读取能力,从 Redis 缓存中获取字典数据,缓存未命中时从数据库加载。
- [导入导出](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/excel.html): 基于 openpyxl 实现 Excel 导入导出功能,支持通过 file_template 模块管理导入模板配置。
- [通用 Helper 函数](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/helpers.html): src/common/utils/helpers.py 提供项目中广泛使用的通用工具函数,包括 ID 解析、OS/浏览器检测、参数脱敏等。
- [IP 归属地解析](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/ip-region.html): src/common/utils/ip2region.py 基于 ip2region 离线数据库,提供 IP 地址到省市区的归属地查询能力,无需网络请求。
- [日志系统](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/logger.html): 通过 Python 标准库 logging 实现结构化日志,支持控制台 + 文件双输出,日志文件按大小自动轮转。
- [密码工具](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/password.html): 采用 bcrypt 双重加盐方案存储密码,源码位于 src/core/password.py。
- [权限缓存](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/perm-cache.html): 通过 Redis 实现用户权限和菜单的缓存,采用用户级缓存 + 全局版本号双层策略。
- [统一响应封装(R 对象)](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/response.html): src/core/response.py 提供统一的 API 响应封装,所有接口返回格式为 {code, data, msg, ok}。
- [富文本过滤](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/rich-text.html): 富文本编辑器内容存在存储型 XSS 风险,通过 bleach 库实现 HTML 白名单清洗,在落盘前统一过滤危险内容。
- [文件上传完整流程](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/upload-guide.html): 富文本编辑器(如 TinyMCE、CKEditor、WangEditor 等)的图片上传同样使用此接口。
- [文件上传](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume4/tools/upload.html): 提供本地文件上传功能,支持图片和通用文件上传,内置扩展名白名单、大小限制、魔术字节校验和路径遍历防护。
## 6. 代码生成器
- [CLI 命令行使用](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/cli.html): 代码生成器 CLI 模式的使用方法,包括单表生成、批量生成、预览模式和配置导出。
- [代码生成引擎](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/code-engine.html): 代码生成引擎的工作原理,包括配置解析、模板渲染、文件写入和自动注册的完整流程。
- [配置文件详解](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/config.html): 代码生成器 JSON 配置文件的格式说明,包括模块名称、字段属性、字典编码等配置项的详细解释。
- [实战案例](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/demo.html): 以 tornado_example 演示表为完整案例,演示从建表到生成到使用的全流程。
- [常见问题](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/faq.html): 代码生成器使用过程中常见问题的原因分析和解决方案。
- [概述与架构](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/overview.html): 代码生成器的整体架构、核心模块和生成流程概述。
- [表结构解析引擎](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/parser.html): 表结构解析引擎的工作原理,从数据库读取表元数据并智能推断字段用途的过程。
- [自定义模板](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/templates.html): 代码生成器模板的 Jinja2 语法说明,包括模板变量、字段属性和自定义模板的开发方法。
- [Web 管理界面](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume5/web-ui.html): 代码生成器 Web 管理界面的使用方法,通过可视化界面操作单表生成和批量生成。
## 7. 运维部署
- [备份与恢复](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/backup.html): - 3 份数据副本(1 份生产 + 2 份备份) - 2 种不同存储介质(如本地磁盘 + 远程存储) - 1 份异地备份(防机房级灾难)
- [跨库数据迁移](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/db-cross-migrate.html): 项目提供 scripts/migrate_db.py 脚本,支持在不同数据库之间迁移数据(如 MySQL → PostgreSQL / SQL Server / SQLite / Oracle)。
- [数据库迁移](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/db-migrate.html): - 结构迁移:Alembic(自动生成 DDL 差异) - 数据迁移:scripts/migrate_db.py(跨库数据泵) - 初始化:scripts/init_db.py(全新安装一键建库)
- [部署 Checklist](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/deploy-checklist.html): 从服务器准备到应用上线的完整部署操作清单,适用于生产环境首次部署和后续版本更新。
- [Docker容器化](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/docker.html): - 环境一致性:消除"在我机器上能跑"的问题 - 快速部署:一条命令启动完整服务 - 资源隔离:应用与宿主机环境解耦
- [环境规划](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/env-plan.html): 各环境之间保持代码一致,仅通过环境变量(.env)区分配置。
- [健康检查与监控](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/monitoring.html): `` 1. 应用层:健康检查端点、业务指标 2. 系统层:CPU/内存/磁盘/网络 3. 中间件:MySQL/Redis 连接状态 4. 日志:集中式日志收集与分析 ``
- [Nginx 反向代理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/nginx.html): `` 客户端 → Nginx(80/443) ├─ / → 前端静态资源(dist/) ├─ /api/v1/... → Tornado 后端(8041) └─ /api/file/ →
- [性能调优](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/performance.html): 1. 先测量,后优化:用数据说话,避免盲目调参 2. 瓶颈优先:找到最大瓶颈,集中优化 3. 渐进式:每次只调整一个参数,观察效果
- [安全加固](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/security-hardening.html): - 最小权限:只开放必要的端口和权限 - 纵深防御:多层安全措施叠加 - 默认安全:配置默认值应为安全选项
- [Supervisor进程管理](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/supervisor.html): - Linux 服务器直接部署(非 Docker) - 需要进程自动重启和日志轮转 - 管理多个相关进程
- [故障排查手册](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/troubleshooting.html): 1. 查看日志:tail -f logs/supervisor.out.log 或 docker-compose logs -f 2. 检查配置:.env 文件、数据库连接、Redis 连接 3.
- [版本升级流程](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume6/upgrade.html): 1. 先备份,后升级 2. 先测试环境,后生产环境 3. 每次升级可回滚 4. 低峰期执行升级
## 8. 规范标准
- [API响应规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/api-response.html): - 所有接口必须通过 R.ok() 或 R.failed() 返回,禁止直接返回 dict - 成功响应 code=0,失败响应 code=1 - 响应体始终包含 code、data、msg、ok
- [代码审查清单](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/code-review.html): - 每次提交必须经过至少一人审查 - 审查关注点:正确性 > 安全性 > 可维护性 > 性能 - 审查意见应具体、可操作,避免主观评价
- [删除规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/deletion.html): - is_delete = 0:正常记录(默认值) - is_delete = 1:已删除记录 - 所有查询方法自动过滤 is_delete = 0,业务层无需关心
- [文档规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/doc-standard.html): - 文档与代码同步更新 - 使用中文编写(项目约定) - 示例代码可直接运行
- [前端 API 规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/fe-api.html): - 每个后端模块对应一个 API 文件 - 函数命名统一使用 名词 + 动词 格式(与后端保持一致) - 请求路径使用相对路径(/position/page),不含 /api/v1 前缀(由
- [前端页面规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/fe-component.html): - Vue3(Composition API + <script setup>) - NaiveUI - 自定义组件(BasicTable / BasicForm / TableAction /
- [Git提交规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/git.html): `` <type>(<scope>): <subject> <body> <footer> ``
- [Handler 层规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/handler.html): - Handler 只做请求分发,不含业务逻辑 - 通过装饰器控制权限和演示模式 - 委托 Service 处理业务逻辑 - 返回 R 封装的响应
- [分页规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/pagination.html): - 前端传递 pageNo(页码,从 1 开始)和 pageSize(每页条数) - 后端返回 {records, total, size, current, pages} 结构 - 默认每页 10
- [Pydantic Schema规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/pydantic.html): - 请求体参数必须声明为 Pydantic 模型(Form 类) - 字段约束使用 Field(...) 定义 - 自定义校验使用 @field_validator - 唯一性校验在 Schema
- [Repository层规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/repository.html): - Repository 只做数据访问原语,不处理 R 封装/序列化/业务逻辑 - 软删除由基类自动处理,子类无需关心 - 查询方法自动跳过 None 值条件
- [Service层规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/service.html): BaseService 采用模板方法模式:基类定义流程骨架,子类通过声明差异点和覆盖钩子定制行为。
- [测试规范](http://docs.tornado.naivevue.djangoadmin.cn/zh/volume7/testing.html): 1. 契约测试(Contract):验证路由、权限、表名等契约不变 2. 单元测试(Unit):验证单个函数/方法的逻辑正确性 3. 集成测试(Integration):验证模块间协作、真实数据库操作
## 附录
- [常见问题汇总](http://docs.tornado.naivevue.djangoadmin.cn/zh/faq.html): 本页汇总了项目各模块的常见问题及解决方案,按场景分类索引。
- [免责声明](http://docs.tornado.naivevue.djangoadmin.cn/zh/other/declare.html): 使用本产品前必读的法律声明,明确产品性质、使用限制与责任划分,一旦使用本产品即视为接受本声明全部条款。
- [广告赞助](http://docs.tornado.naivevue.djangoadmin.cn/zh/other/sponsor.html): 1. 投放广告不得与官方全系产品存在结构性冲突和竞争 2. 广告内容需符合国家法律法规,不得包含虚假、误导性信息 3. 广告素材需经官方审核通过后方可上线,审核周期 1-3 个工作日 4.

Some files were not shown because too many files have changed in this diff Show More