初始化 MengStack 官方网站仓库

包含 VitePress 文档站完整源码及全新自定义首页设计:
- 自定义首页组件 (Home.vue):Hero + 终端动效 + 核心能力 + 架构展示 + 快速开始 + CTA
- 自定义 Layout.vue:首页/文档页布局切换
- 金铜色品牌主题样式
- 完整文档页面(指南、API、更新日志、演示、社区)
- SEO 配置(sitemap、robots、meta 标签)
This commit is contained in:
SoftUnis 2026-10-02 23:37:25 +08:00
commit ecb29e0aa4
34 changed files with 5734 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
node_modules/
.vitepress/dist/
.vitepress/cache/
*.local
.DS_Store
Thumbs.db

172
.vitepress/config.ts Normal file
View File

@ -0,0 +1,172 @@
import { defineConfig } from 'vitepress'
const seoPages: Record<string, { title: string; description: string; keywords: string }> = {
'/': {
title: 'MengStack - 企业级Go语言微服务开发框架 | 开源模块化开发底座',
description: 'MengStack是软盟SoftUnis开源的企业级Go语言微服务开发框架,基于Gin+GORM+uber-go/fx构建,内置三层隔离架构、多租户支持、JWT认证、API文档、Docker部署等能力,开箱即用,帮助开发者快速搭建企业级后端服务。',
keywords: 'Go微服务框架,Go企业级开发框架,MengStack,开源Go框架,Go模块化开发',
},
'/guide/what-is-mengstack': {
title: '什么是MengStack | 架构设计与核心特性介绍 - MengStack官方文档',
description: 'MengStack入门指南,详细介绍框架设计理念、三层隔离 Kernel/App/Modules 架构、四层模块模式、核心特性清单、适用场景与完整技术栈选型,帮助开发者快速了解MengStack框架。',
keywords: 'MengStack教程,MengStack架构,Go框架架构设计,DDD四层架构,Go微服务入门',
},
'/guide/getting-started': {
title: '快速开始 | 5分钟搭建MengStack项目环境 - MengStack官方文档',
description: 'MengStack快速开始教程,包含环境要求、项目克隆、依赖安装、配置修改、服务启动完整步骤,帮助开发者5分钟跑通第一个MengStack项目,快速体验框架能力。',
keywords: 'MengStack快速开始,MengStack安装,Go项目搭建,Go框架入门教程',
},
'/guide/project-structure': {
title: '项目结构说明 | MengStack目录规范详解 - MengStack官方文档',
description: '详细介绍MengStack标准项目目录结构,各目录职责划分、模块组织规范、依赖注入配置方式,帮助开发者遵循统一规范开发,降低团队协作成本。',
keywords: 'MengStack项目结构,Go项目目录规范,Go项目架构,模块化开发规范',
},
'/api/overview': {
title: 'API接口概览 | 统一响应格式与错误码说明 - MengStack官方文档',
description: 'MengStack API接口参考文档,包含接口基础信息、统一响应格式规范、HTTP状态码与错误码说明、JWT认证方式说明、完整接口列表,以及在线Swagger UI调试入口。',
keywords: 'MengStack API,Go接口文档,RESTful API规范,JWT认证接口,Swagger接口文档',
},
'/changelog': {
title: '更新日志 | 版本迭代记录与路线图 - MengStack开源框架',
description: 'MengStack各版本更新记录,包含v0.1.0首发版本技术栈说明、功能特性清单,以及后续版本路线图规划,跟进MengStack框架最新功能动态。',
keywords: 'MengStack更新日志,MengStack版本,Go框架更新,开源项目路线图',
},
'/demo': {
title: '在线演示 | 免费体验MengStack接口服务 - MengStack官方',
description: 'MengStack官方在线演示环境,提供公开API服务地址、Swagger UI在线调试入口、健康检查地址,附快速体验步骤指引,无需本地部署即可直接体验框架接口能力。',
keywords: 'MengStack在线演示,Go框架体验,Swagger在线调试,API接口测试',
},
'/community': {
title: '开发者社区 | 交流渠道与贡献指南 - MengStack开源社区',
description: 'MengStack开源开发者社区,提供代码仓库地址、问题反馈渠道、功能建议入口,以及Bug报告、代码提交、代码规范等贡献指引,欢迎所有开发者参与MengStack共建。',
keywords: 'MengStack社区,Go开源社区,Go开发者交流,开源项目贡献',
},
}
function getPagePath(relativePath: string) {
let p = '/' + relativePath.replace(/\.md$/, '').replace(/\/index$/, '')
if (p === '/index') p = '/'
return p
}
function getSeo(pagePath: string) {
return seoPages[pagePath] || seoPages['/']
}
export default defineConfig({
title: 'MengStack',
titleTemplate: ':title',
description: 'MengStack - 企业级 Go 语言微服务开发框架',
lang: 'zh-CN',
head: [
['link', { rel: 'icon', href: '/favicon.png' }],
['meta', { property: 'og:image', content: '/og-image.png' }],
['meta', { name: 'theme-color', content: '#C8963E' }],
],
transformHead({ pageData }) {
const path = getPagePath(pageData.relativePath)
const seo = getSeo(path)
const url = `https://mengstack.softunis.com${path === '/' ? '/' : path + '.html'}`
const head: [string, Record<string, string>][] = [
['meta', { name: 'description', content: seo.description }],
['meta', { name: 'keywords', content: seo.keywords }],
['link', { rel: 'canonical', href: url }],
['meta', { property: 'og:title', content: seo.title }],
['meta', { property: 'og:description', content: seo.description }],
['meta', { property: 'og:url', content: url }],
]
return head
},
themeConfig: {
logo: {
light: '/logo-light.png',
dark: '/logo-dark.png',
alt: 'MengStack 企业级Go语言微服务开发框架',
},
siteTitle: 'MengStack',
nav: [
{ text: '首页', link: '/' },
{ text: '指南', link: '/guide/what-is-mengstack' },
{ text: 'API 参考', link: '/api/overview' },
{ text: '更新日志', link: '/changelog' },
{ text: '在线演示', link: '/demo' },
{ text: '社区', link: '/community' },
],
sidebar: {
'/guide/': [
{
text: '入门',
items: [
{ text: '什么是 MengStack', link: '/guide/what-is-mengstack' },
{ text: '快速开始', link: '/guide/getting-started' },
{ text: '项目结构', link: '/guide/project-structure' },
],
},
{
text: '架构设计',
items: [
{ text: '三层隔离架构', link: '/guide/architecture' },
{ text: '模块四层模式', link: '/guide/module-pattern' },
{ text: '依赖注入', link: '/guide/dependency-injection' },
],
},
{
text: '核心功能',
items: [
{ text: '认证与授权', link: '/guide/auth' },
{ text: '多租户系统', link: '/guide/multi-tenancy' },
{ text: '配置管理', link: '/guide/configuration' },
],
},
{
text: '部署',
items: [
{ text: 'Docker 部署', link: '/guide/docker-deploy' },
{ text: 'CI/CD 流水线', link: '/guide/cicd' },
],
},
],
'/api/': [
{
text: 'API 参考',
items: [
{ text: '概览', link: '/api/overview' },
{ text: '认证接口', link: '/api/auth' },
{ text: '系统接口', link: '/api/system' },
],
},
],
},
socialLinks: [
{ icon: 'github', link: 'https://gitea.softunis.com/mengstack' },
],
footer: {
message: 'Released under the MIT License.',
copyright: `Copyright &copy; 2026 <a href="https://www.softunis.com/" target="_blank" rel="noopener">软盟 SoftUnis</a><br><a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">粤ICP备2023054965号</a> &nbsp;|&nbsp; <a href="https://beian.mps.gov.cn/" target="_blank" rel="noopener">粤公网安备 44030502010466号</a>`,
},
search: {
provider: 'local',
},
outline: {
label: '页面导航',
},
docFooter: {
prev: '上一页',
next: '下一页',
},
lastUpdated: {
text: '最后更新',
},
},
})

796
.vitepress/theme/Home.vue Normal file
View File

@ -0,0 +1,796 @@
<template>
<div class="home-new">
<!-- ============ Navbar ============ -->
<header class="nav" :class="{ scrolled }" ref="navEl">
<div class="nav-inner">
<a class="brand" href="#top" aria-label="MengStack 首页">
<img class="light-logo" src="/logo-light.png" alt="" width="30" height="30" />
<img class="dark-logo" src="/logo-dark.png" alt="" width="30" height="30" />
MengStack
</a>
<nav class="nav-links" aria-label="主导航">
<a href="#top" class="active">首页</a>
<a href="/guide/what-is-mengstack.html">指南</a>
<a href="/api/overview.html">API 参考</a>
<a href="/changelog.html">更新日志</a>
<a href="/demo.html">在线演示</a>
<a href="/community.html">社区</a>
</nav>
<div class="nav-actions">
<button class="icon-btn" @click="toggleTheme" aria-label="切换明暗主题" title="切换主题">
<svg aria-hidden="true" class="sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="4" /><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" /></svg>
<svg aria-hidden="true" class="moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z" /></svg>
</button>
<button class="icon-btn hamburger" ref="menuBtn" aria-label="展开菜单" aria-expanded="false" @click="toggleMenu">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 7h16M4 12h16M4 17h16" /></svg>
</button>
</div>
</div>
</header>
<main id="top">
<!-- ============ Hero ============ -->
<section class="hero">
<div class="wrap hero-grid">
<div>
<span class="badge"><span class="dot"></span>云原生 × AI 原生 · MIT 开源</span>
<h1><span class="grad">MengStack</span><br />企业级 Go 语言微服务开发框架</h1>
<p class="sub">面向云原生与 AI 时代的一站式全栈开发底座</p>
<p class="tagline">组件化集成、零侵入依赖注入、全链路国产化适配——开箱即用,让团队专注核心业务,不再重复造轮子。</p>
<div class="hero-actions">
<a class="btn btn-primary btn-lg" href="/guide/getting-started.html">
快速开始
<svg aria-hidden="true" viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
</a>
<a class="btn btn-ghost btn-lg" href="/demo.html">在线演示</a>
<a class="btn btn-ghost btn-lg" href="https://gitea.softunis.com/mengstack" target="_blank" rel="noopener noreferrer">
<svg aria-hidden="true" viewBox="0 0 24 24" width="17" height="17" fill="currentColor"><path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.55v-2.15c-3.2.7-3.87-1.36-3.87-1.36-.52-1.33-1.28-1.69-1.28-1.69-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.23-1.28-5.23-5.68 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.12 3.05.74.81 1.18 1.83 1.18 3.09 0 4.41-2.69 5.38-5.25 5.67.41.36.78 1.06.78 2.14v3.17c0 .3.21.67.8.55A11.51 11.51 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z" /></svg>
GitHub
</a>
</div>
</div>
<div class="terminal" aria-label="代码示例终端">
<div class="terminal-bar"><i></i><i></i><i></i><span>main.go — mengstack</span></div>
<div class="terminal-body">
<div class="ln"><span class="kw">package</span> main</div>
<div class="ln">&nbsp;</div>
<div class="ln"><span class="kw">import</span> <span class="str">"github.com/mengstack/mengstack"</span></div>
<div class="ln">&nbsp;</div>
<div class="ln"><span class="kw">func</span> <span class="fn">main</span>() {</div>
<div class="ln"> app := mengstack.<span class="fn">New</span>()</div>
<div class="ln"> app.<span class="fn">Run</span>(<span class="num">":2222"</span>) <span class="cursor"></span></div>
<div class="ln">}</div>
</div>
</div>
</div>
</section>
<!-- ============ Trust strip ============ -->
<div class="trust">
<div class="trust-inner">
<span class="trust-label">运行环境</span>
<span class="chip">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 9V3m0 18v-2M3 12h2m14 0h2M6 6l1.5 1.5M16.5 16.5 18 18M18 6l-1.5 1.5M7.5 16.5 6 18" /><circle cx="12" cy="12" r="3.2" /></svg>
Go 1.23+
</span>
<span class="chip">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="6" rx="8" ry="3" /><path d="M4 6v6c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6" /></svg>
PostgreSQL 14+
</span>
<span class="chip">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0-6 6v3a6 6 0 0 0 12 0V9" /><path d="M9 21h6M12 18v3" /></svg>
Redis 7+
</span>
<span class="chip">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="3" y="9" width="4" height="4" /><rect x="10" y="9" width="4" height="4" /><rect x="17" y="9" width="4" height="4" /><rect x="3" y="15.5" width="4" height="4" /><rect x="10" y="15.5" width="4" height="4" /><rect x="17" y="15.5" width="4" height="4" /><path d="M7 11h3m4 0h3M7 17.5h3m4 0h3" /></svg>
Docker Compose
</span>
<span class="chip">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2 4 5v6c0 5 3.4 8.5 8 11 4.6-2.5 8-6 8-11V5z" /></svg>
MIT 开源
</span>
</div>
</div>
<!-- ============ Features ============ -->
<section class="section features" id="features">
<div class="wrap">
<div class="section-head reveal">
<span class="eyebrow">Core Capabilities</span>
<h2>七大核心能力,覆盖企业级开发全链路</h2>
<p>从架构搭建到 AI 集成,生产验证的模块开箱即用,大幅降低中大型项目的架构成本。</p>
</div>
<div class="feature-grid stagger">
<article class="feature reveal" style="--i:0">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.5" /><rect x="14" y="3" width="7" height="7" rx="1.5" /><rect x="3" y="14" width="7" height="7" rx="1.5" /><path d="M17.5 14v7M14 17.5h7" /></svg>
</div>
<h3>全栈组件化集成</h3>
<p>内置前端模板、API 网关、分布式缓存、消息队列、定时任务等模块,生产验证,开箱即用。</p>
<a class="more" href="/guide/what-is-mengstack.html">了解架构<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg></a>
</article>
<article class="feature reveal" style="--i:1">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="currentColor"><path d="M13 2 4.5 13.5H11l-1 8.5 8.5-11.5H12z" /></svg>
</div>
<h3>零侵入依赖注入</h3>
<p>兼容 Wire 编译时注入与 uber-go/fx 运行时管理,按需切换,兼顾性能与便捷。</p>
<a class="more" href="/guide/dependency-injection.html">查看文档<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg></a>
</article>
<article class="feature reveal" style="--i:2">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="5" width="14" height="14" rx="3" /><path d="M9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3" /><circle cx="12" cy="12" r="3" /></svg>
</div>
<h3>AI 原生开发支持</h3>
<p>内置大模型调用、Agent 内存管理、向量数据库集成,快速落地智能业务场景。</p>
</article>
<article class="feature reveal" style="--i:3">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2 4 5v6c0 5 3.4 8.5 8 11 4.6-2.5 8-6 8-11V5z" /><path d="m9 12 2 2 4-4" /></svg>
</div>
<h3>全链路国产化适配</h3>
<p>兼容国产操作系统、数据库、中间件,满足政企金融等保合规要求。</p>
</article>
<article class="feature reveal" style="--i:4">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5a2 2 0 0 1 2-2h13v16H6a2 2 0 0 0-2 2z" /><path d="M8 7h7M8 11h7M4 5v14" /></svg>
</div>
<h3>极低学习门槛</h3>
<p>统一代码规范与完善中文文档,新成员接入周期缩短 70% 以上。</p>
<a class="more" href="/guide/getting-started.html">快速开始<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg></a>
</article>
<article class="feature reveal" style="--i:5">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19V5" /><path d="M4 19h16" /><rect x="7" y="11" width="3" height="5" /><rect x="12" y="7" width="3" height="9" /><rect x="17" y="13" width="3" height="3" /></svg>
</div>
<h3>生产级可观测</h3>
<p>内置链路追踪、指标监控、日志聚合,无需额外搭建监控体系。</p>
</article>
<article class="feature feature--wide reveal" style="--i:6">
<div class="ficon">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="11" width="14" height="9" rx="2" /><path d="M8 11V8a4 4 0 0 1 7.7-1.5" /></svg>
</div>
<div class="wtext">
<h3>完全开源开放</h3>
<p>核心代码 MIT 协议开源,无商业限制,社区持续迭代,可自由定制扩展。</p>
</div>
<a class="btn btn-primary wcta" href="/community.html">加入社区</a>
</article>
</div>
</div>
</section>
<!-- ============ Architecture ============ -->
<section class="section arch" id="architecture">
<div class="wrap">
<div class="section-head reveal">
<span class="eyebrow">Architecture</span>
<h2>三层隔离架构,全链路一站打通</h2>
<p>覆盖从前端交互、后端服务、数据治理到 AI 能力集成的全链路开发场景,层间解耦、演进自由。</p>
</div>
<div class="arch-layers stagger">
<div class="layer reveal" style="--i:0">
<div class="layer-num">LAYER 01</div>
<h3>前端交互层</h3>
<p>内置前端模板,统一交互规范,与后端接口无缝对接。</p>
<ul><li>前端模板</li><li>API 网关</li><li>认证鉴权</li></ul>
</div>
<div class="layer reveal" style="--i:1">
<div class="layer-num">LAYER 02</div>
<h3>后端服务层</h3>
<p>模块四层模式配合零侵入依赖注入,业务边界清晰,按需装配。</p>
<ul><li>模块四层模式</li><li>Wire / fx 注入</li><li>消息队列 · 定时任务</li></ul>
</div>
<div class="layer reveal" style="--i:2">
<div class="layer-num">LAYER 03</div>
<h3>数据与 AI 层</h3>
<p>数据治理与智能能力一体集成,并完成全链路国产化适配。</p>
<ul><li>分布式缓存</li><li>向量数据库</li><li>大模型 · Agent</li></ul>
</div>
</div>
<div class="arch-flow reveal">
前端交互
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
后端服务
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
数据治理 · AI 能力
</div>
</div>
</section>
<!-- ============ Quick start ============ -->
<section class="section qs" id="quickstart">
<div class="wrap">
<div class="section-head reveal">
<span class="eyebrow">Quick Start</span>
<h2>三步启动,5 分钟跑起项目环境</h2>
<p>推荐使用 Docker Compose 一键拉起 API、PostgreSQL 与 Redis,无需手动配置依赖。</p>
</div>
<div class="qs-grid stagger">
<div class="qs-step reveal" style="--i:0">
<h3><span class="step-no">1</span>克隆项目</h3>
<p>从 Gitea 仓库拉取最新代码。</p>
<div class="codeblock">
<button class="copy-btn" aria-label="复制命令" @click="copyCode($event)"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2" /><path d="M5 15V5a2 2 0 0 1 2-2h10" /></svg></button>
<pre>git clone https://gitea.softunis.com/mengstack/mengstack.git
cd mengstack</pre>
</div>
</div>
<div class="qs-step reveal" style="--i:1">
<h3><span class="step-no">2</span>启动服务</h3>
<p>一条命令启动全部依赖服务。</p>
<div class="codeblock">
<button class="copy-btn" aria-label="复制命令" @click="copyCode($event)"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2" /><path d="M5 15V5a2 2 0 0 1 2-2h10" /></svg></button>
<pre><span class="c-comment"># 启动所有服务</span>
docker-compose up -d
<span class="c-comment"># 查看日志</span>
docker-compose logs -f app</pre>
</div>
</div>
<div class="qs-step reveal" style="--i:2">
<h3><span class="step-no">3</span>验证访问</h3>
<p>打开浏览器或调用健康检查接口。</p>
<div class="codeblock">
<button class="copy-btn" aria-label="复制命令" @click="copyCode($event)"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2" /><path d="M5 15V5a2 2 0 0 1 2-2h10" /></svg></button>
<pre>API http://localhost:2222
Docs /swagger/index.html
Check /health</pre>
</div>
</div>
</div>
</div>
</section>
<!-- ============ Stats ============ -->
<section class="stats">
<div class="wrap stats-grid">
<div class="stat reveal">
<div class="v">5<small style="font-size:.55em">分钟</small></div>
<div class="l">完成项目环境搭建</div>
</div>
<div class="stat reveal">
<div class="v">70%+</div>
<div class="l">新成员接入周期缩短</div>
</div>
<div class="stat reveal">
<div class="v">MIT</div>
<div class="l">核心代码开源协议 · 无商业限制</div>
</div>
</div>
</section>
<!-- ============ About ============ -->
<section class="section about" id="about">
<div class="wrap">
<div class="section-head reveal">
<span class="eyebrow">About</span>
<h2>关于 MengStack</h2>
</div>
<div class="about-card reveal">
<p>MengStack 是面向云原生与 AI 时代的一站式全栈开发底座,整合主流开源技术能力,为开发者提供开箱即用的组件化开发体验,大幅降低中大型项目的架构搭建成本,让团队专注于核心业务逻辑落地,无需重复造轮子。</p>
<p>我们坚持全栈开源、无厂商锁定的设计理念,覆盖从前端交互、后端服务、数据治理到 AI 能力集成的全链路开发场景,适配国产化软硬件生态,为企业级项目提供稳定、高效、可扩展的技术支撑。</p>
<div class="about-points">
<div>
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
全栈开源,无厂商锁定
</div>
<div>
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
组件开箱即用
</div>
<div>
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
国产化生态适配
</div>
</div>
</div>
</div>
</section>
<!-- ============ CTA ============ -->
<section class="cta-band">
<div class="wrap">
<div class="cta-card reveal">
<h2>现在开始,用 MengStack 搭建你的下一个项目</h2>
<p>完善中文文档与活跃社区,陪你从第一行代码走到生产上线。</p>
<div class="hero-actions">
<a class="btn btn-primary btn-lg" href="/guide/getting-started.html">5 分钟快速开始</a>
<a class="btn btn-ghost btn-lg" href="https://gitea.softunis.com/mengstack" target="_blank" rel="noopener noreferrer">访问 Gitea 仓库</a>
</div>
</div>
</div>
</section>
</main>
<!-- ============ Footer ============ -->
<footer class="footer">
<div class="wrap footer-inner">
<div>
<p>Released under the MIT License.</p>
<p>Copyright &copy; 2026 <a href="https://www.softunis.com/" target="_blank" rel="noopener noreferrer">软盟 SoftUnis</a><br />
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">粤ICP备2023054965号</a> &nbsp;|&nbsp;
<a href="https://beian.mps.gov.cn/" target="_blank" rel="noopener noreferrer">粤公网安备 44030502010466号</a></p>
</div>
<nav class="footer-links" aria-label="页脚导航">
<a href="/guide/what-is-mengstack.html">指南</a>
<a href="/api/overview.html">API 参考</a>
<a href="/demo.html">在线演示</a>
<a href="/community.html">社区</a>
</nav>
</div>
</footer>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useData } from 'vitepress/data'
const { isDark } = useData()
const navEl = ref(null)
const menuBtn = ref(null)
const scrolled = ref(false)
let scrollHandler = null
let observer = null
let mutationObserver = null
function toggleTheme() {
const el = document.documentElement
el.classList.toggle('dark')
const dark = el.classList.contains('dark')
isDark.value = dark
try { localStorage.setItem('ms-theme', dark ? 'dark' : 'light') } catch (e) {}
}
function toggleMenu() {
const nav = navEl.value
if (!nav) return
const open = nav.classList.toggle('nav-menu-open')
menuBtn.value?.setAttribute('aria-expanded', open ? 'true' : 'false')
}
function copyCode(event) {
const btn = event.currentTarget
const pre = btn.parentElement.querySelector('pre')
const text = pre ? pre.innerText : ''
const done = () => {
btn.classList.add('copied')
btn.innerHTML = '<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>'
setTimeout(() => {
btn.classList.remove('copied')
btn.innerHTML = '<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>'
}, 1600)
}
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(done).catch(done)
} else { done() }
} catch (e) { done() }
}
onMounted(() => {
const root = document.documentElement
try {
const saved = localStorage.getItem('ms-theme')
if (saved === 'dark' || (!saved && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
root.classList.add('dark')
isDark.value = true
} else {
root.classList.remove('dark')
isDark.value = false
}
} catch (e) {}
mutationObserver = new MutationObserver(() => {
isDark.value = root.classList.contains('dark')
})
mutationObserver.observe(root, { attributes: true, attributeFilter: ['class'] })
scrollHandler = () => { scrolled.value = window.scrollY > 8 }
window.addEventListener('scroll', scrollHandler, { passive: true })
scrollHandler()
const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (reduce || !('IntersectionObserver' in window)) {
root.querySelectorAll('.reveal').forEach(el => el.classList.add('in'))
} else {
observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('in')
observer.unobserve(entry.target)
}
})
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' })
root.querySelectorAll('.reveal').forEach(el => observer.observe(el))
}
})
onUnmounted(() => {
if (scrollHandler) window.removeEventListener('scroll', scrollHandler)
if (observer) observer.disconnect()
if (mutationObserver) mutationObserver.disconnect()
})
</script>
<style>
/* ============ Design Tokens ============ */
:root{
--ms-gold:#C8963E;
--ms-gold-light:#E0B86A;
--ms-gold-dark:#A07830;
--ms-bronze:#B87333;
--ms-bronze-dark:#8B5A2B;
--ms-cream:#FDF8EF;
--ms-cream-dark:#F5EDD8;
--home-bg:#ffffff;
--home-bg-soft:#FAF7F1;
--home-bg-elev:#ffffff;
--home-ink:#211d16;
--home-ink-soft:#5c564b;
--home-ink-faint:#8a8275;
--home-border:#ece5d8;
--home-border-strong:#ddd2bc;
--home-code-bg:#1d1a14;
--home-code-ink:#f3ead8;
--home-hero-glow:rgba(200,150,62,.16);
--home-shadow:0 1px 2px rgba(60,45,15,.05),0 12px 32px -12px rgba(120,86,30,.16);
--home-shadow-lg:0 2px 4px rgba(60,45,15,.06),0 24px 56px -16px rgba(120,86,30,.22);
--home-radius:16px;
--home-radius-sm:12px;
--home-maxw:1180px;
--home-font:"Inter",ui-sans-serif,system-ui,-apple-system,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
--home-mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,"Liberation Mono",monospace;
}
html.dark{
--home-bg:#16140f;
--home-bg-soft:#1d1a13;
--home-bg-elev:#221e16;
--home-ink:#f4ede0;
--home-ink-soft:#bfb5a2;
--home-ink-faint:#8d8472;
--home-border:#312b20;
--home-border-strong:#443b2a;
--home-hero-glow:rgba(200,150,62,.22);
--home-shadow:0 1px 2px rgba(0,0,0,.3),0 12px 32px -12px rgba(0,0,0,.55);
--home-shadow-lg:0 2px 4px rgba(0,0,0,.35),0 24px 56px -16px rgba(0,0,0,.65);
}
/* ============ Hide VitePress nav/footer on homepage ============ */
.home-layout .VPNav,
.home-layout .VPFooter {
display: none !important;
}
.home-layout .VPContent {
padding-top: 0 !important;
}
/* ============ Home Page Base ============ */
.home-new{
font-family:var(--home-font);
color:var(--home-ink);
background:var(--home-bg);
line-height:1.7;
font-size:16px;
-webkit-font-smoothing:antialiased;
overflow-x:hidden;
}
.home-new *,.home-new *::before,.home-new *::after{box-sizing:border-box}
.home-new a{color:inherit;text-decoration:none}
.home-new img{max-width:100%;display:block}
.home-new h1,.home-new h2,.home-new h3{line-height:1.25;margin:0;font-weight:800;letter-spacing:-.01em}
.home-new p{margin:0}
.home-new .wrap{max-width:var(--home-maxw);margin:0 auto;padding:0 24px}
/* ============ Navbar ============ */
.home-new .nav{
position:fixed;top:0;left:0;right:0;z-index:100;
background:color-mix(in srgb,var(--home-bg) 82%,transparent);
backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
border-bottom:1px solid transparent;transition:border-color .25s;
}
.home-new .nav.scrolled{border-bottom-color:var(--home-border)}
.home-new .nav-inner{max-width:var(--home-maxw);margin:0 auto;padding:0 24px;height:64px;display:flex;align-items:center;gap:28px}
.home-new .brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:18px;letter-spacing:-.01em}
.home-new .brand img{width:30px;height:30px}
.home-new .brand .dark-logo{display:none}
html.dark .home-new .brand .light-logo{display:none}
html.dark .home-new .brand .dark-logo{display:block}
.home-new .nav-links{display:flex;gap:4px;margin-left:auto}
.home-new .nav-links a{
padding:7px 13px;border-radius:8px;font-size:14.5px;font-weight:500;color:var(--home-ink-soft);
transition:color .2s,background .2s;
}
.home-new .nav-links a:hover{color:var(--ms-gold-dark);background:var(--home-bg-soft)}
html.dark .home-new .nav-links a:hover{color:var(--ms-gold-light)}
.home-new .nav-links a.active{color:var(--ms-gold-dark);font-weight:600}
html.dark .home-new .nav-links a.active{color:var(--ms-gold-light)}
.home-new .nav-actions{display:flex;align-items:center;gap:8px}
.home-new .icon-btn{
width:38px;height:38px;border-radius:10px;border:1px solid var(--home-border);background:var(--home-bg);
display:grid;place-items:center;cursor:pointer;color:var(--home-ink-soft);transition:all .2s;
}
.home-new .icon-btn:hover{border-color:var(--home-border-strong);color:var(--ms-gold-dark);transform:translateY(-1px)}
.home-new .icon-btn .moon{display:none}
html.dark .home-new .icon-btn .sun{display:none}
html.dark .home-new .icon-btn .moon{display:block}
.home-new .hamburger{display:none}
/* ============ Buttons ============ */
.home-new .btn{
display:inline-flex;align-items:center;gap:8px;padding:12px 22px;border-radius:12px;
font-size:15px;font-weight:600;cursor:pointer;border:1px solid transparent;
transition:transform .2s,box-shadow .2s,background .2s,border-color .2s;white-space:nowrap;
}
.home-new .btn:hover{transform:translateY(-2px)}
.home-new .btn-primary{
background:linear-gradient(135deg,var(--ms-gold) 0%,var(--ms-bronze) 100%);
color:#fff;box-shadow:0 6px 20px -6px rgba(200,150,62,.55);
}
.home-new .btn-primary:hover{box-shadow:0 10px 28px -6px rgba(200,150,62,.65)}
.home-new .btn-ghost{background:var(--home-bg-elev);border-color:var(--home-border-strong);color:var(--home-ink)}
.home-new .btn-ghost:hover{border-color:var(--ms-gold);color:var(--ms-gold-dark)}
html.dark .home-new .btn-ghost:hover{color:var(--ms-gold-light)}
.home-new .btn-lg{padding:14px 28px;font-size:16px}
/* ============ Hero ============ */
.home-new .hero{position:relative;padding:148px 0 88px;overflow:hidden}
.home-new .hero::before{
content:"";position:absolute;inset:0;pointer-events:none;
background:
radial-gradient(640px 380px at 82% -8%,var(--home-hero-glow),transparent 70%),
radial-gradient(520px 360px at 8% 12%,rgba(184,115,51,.08),transparent 70%);
}
.home-new .hero::after{
content:"";position:absolute;inset:0;pointer-events:none;opacity:.5;
background-image:linear-gradient(var(--home-border) 1px,transparent 1px),linear-gradient(90deg,var(--home-border) 1px,transparent 1px);
background-size:56px 56px;
-webkit-mask-image:radial-gradient(720px 420px at 70% 0%,#000 0%,transparent 72%);
mask-image:radial-gradient(720px 420px at 70% 0%,#000 0%,transparent 72%);
}
.home-new .hero-grid{position:relative;display:grid;grid-template-columns:1.05fr .95fr;gap:56px;align-items:center}
.home-new .badge{
display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border-radius:999px;
background:var(--ms-cream);border:1px solid var(--home-border-strong);color:var(--ms-bronze-dark);
font-size:13.5px;font-weight:600;margin-bottom:22px;
}
html.dark .home-new .badge{background:rgba(200,150,62,.12);color:var(--ms-gold-light);border-color:var(--home-border-strong)}
.home-new .badge .dot{width:7px;height:7px;border-radius:50%;background:var(--ms-gold);box-shadow:0 0 0 4px rgba(200,150,62,.2)}
.home-new .hero h1{font-size:clamp(34px,4.6vw,54px)}
.home-new .hero h1 .grad{
background:linear-gradient(120deg,var(--ms-gold) 10%,var(--ms-bronze) 55%,var(--ms-gold-dark) 95%);
-webkit-background-clip:text;background-clip:text;color:transparent;
}
.home-new .hero .sub{font-size:clamp(17px,2vw,21px);color:var(--home-ink-soft);margin-top:14px;font-weight:500}
.home-new .hero .tagline{font-size:15.5px;color:var(--home-ink-faint);margin-top:12px;max-width:520px}
.home-new .hero-actions{display:flex;flex-wrap:wrap;gap:14px;margin-top:34px}
/* Terminal */
.home-new .terminal{
background:var(--home-code-bg);border-radius:14px;overflow:hidden;box-shadow:var(--home-shadow-lg);
border:1px solid #3a3326;font-family:var(--home-mono);
}
.home-new .terminal-bar{display:flex;align-items:center;gap:8px;padding:13px 16px;background:#26211a;border-bottom:1px solid #3a3326}
.home-new .terminal-bar i{width:11px;height:11px;border-radius:50%;display:block}
.home-new .terminal-bar i:nth-child(1){background:#ff5f57}
.home-new .terminal-bar i:nth-child(2){background:#febc2e}
.home-new .terminal-bar i:nth-child(3){background:#28c840}
.home-new .terminal-bar span{margin-left:8px;font-size:12.5px;color:#8d8472;font-family:var(--home-font)}
.home-new .terminal-body{padding:20px 20px 22px;font-size:13.5px;line-height:1.85;color:var(--home-code-ink);overflow-x:auto}
.home-new .terminal-body .ln{white-space:pre;opacity:0;transform:translateY(6px);animation:termIn .45s ease forwards}
.home-new .terminal-body .cmt{color:#8d8472}
.home-new .terminal-body .prompt{color:var(--ms-gold-light);user-select:none}
.home-new .terminal-body .ok{color:#7fd49c}
.home-new .terminal-body .kw{color:#c792ea}
.home-new .terminal-body .fn{color:#82b4ff}
.home-new .terminal-body .str{color:#a5d6a0}
.home-new .terminal-body .num{color:#f0b97a}
.home-new .terminal-body .ln:nth-child(1){animation-delay:.15s}
.home-new .terminal-body .ln:nth-child(2){animation-delay:.35s}
.home-new .terminal-body .ln:nth-child(3){animation-delay:.55s}
.home-new .terminal-body .ln:nth-child(4){animation-delay:.85s}
.home-new .terminal-body .ln:nth-child(5){animation-delay:1.1s}
.home-new .terminal-body .ln:nth-child(6){animation-delay:1.5s}
.home-new .terminal-body .ln:nth-child(7){animation-delay:1.9s}
.home-new .terminal-body .ln:nth-child(8){animation-delay:2.25s}
.home-new .terminal-body .cursor{display:inline-block;width:8px;height:16px;background:var(--ms-gold-light);vertical-align:-2px;animation:blink 1.1s steps(1) infinite}
@keyframes termIn{to{opacity:1;transform:none}}
@keyframes blink{50%{opacity:0}}
/* ============ Trust strip ============ */
.home-new .trust{border-top:1px solid var(--home-border);border-bottom:1px solid var(--home-border);background:var(--home-bg-soft)}
.home-new .trust-inner{padding:22px 24px;max-width:var(--home-maxw);margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:14px 30px}
.home-new .trust-label{font-size:13.5px;color:var(--home-ink-faint);font-weight:600;letter-spacing:.04em}
.home-new .chip{
display:inline-flex;align-items:center;gap:7px;font-size:14px;font-weight:600;color:var(--home-ink-soft);
padding:6px 14px;border-radius:999px;border:1px solid var(--home-border);background:var(--home-bg);
}
.home-new .chip svg{width:15px;height:15px;color:var(--ms-gold)}
/* ============ Sections ============ */
.home-new .section{padding:92px 0}
.home-new .section-head{max-width:680px;margin:0 auto 56px;text-align:center}
.home-new .eyebrow{
display:inline-block;font-size:13px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
color:var(--ms-bronze);margin-bottom:14px;
}
html.dark .home-new .eyebrow{color:var(--ms-gold-light)}
.home-new .section-head h2{font-size:clamp(26px,3.2vw,38px)}
.home-new .section-head p{margin-top:14px;color:var(--home-ink-soft);font-size:16.5px}
/* ============ Features ============ */
.home-new .features{background:var(--home-bg)}
.home-new .feature-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:20px}
.home-new .feature{
grid-column:span 4;position:relative;padding:30px 26px;border-radius:var(--home-radius);
background:var(--home-bg-elev);border:1px solid var(--home-border);box-shadow:var(--home-shadow);
transition:transform .25s ease,box-shadow .25s ease,border-color .25s;
display:flex;flex-direction:column;
}
.home-new .feature:hover{transform:translateY(-5px);box-shadow:var(--home-shadow-lg);border-color:var(--home-border-strong)}
.home-new .feature:hover .ficon{transform:scale(1.08) rotate(-3deg)}
.home-new .ficon{
width:48px;height:48px;border-radius:13px;display:grid;place-items:center;margin-bottom:18px;
background:linear-gradient(135deg,var(--ms-cream),var(--ms-cream-dark));
color:var(--ms-bronze);transition:transform .3s ease;flex:none;
}
html.dark .home-new .ficon{background:rgba(200,150,62,.14);color:var(--ms-gold-light)}
.home-new .ficon svg{width:24px;height:24px}
.home-new .feature h3{font-size:18px;margin-bottom:9px}
.home-new .feature p{font-size:14.5px;color:var(--home-ink-soft)}
.home-new .feature .more{margin-top:auto;padding-top:16px;font-size:13.5px;font-weight:600;color:var(--ms-gold-dark);display:inline-flex;align-items:center;gap:6px}
html.dark .home-new .feature .more{color:var(--ms-gold-light)}
.home-new .feature .more svg{width:14px;height:14px;transition:transform .2s}
.home-new .feature:hover .more svg{transform:translateX(4px)}
.home-new .feature--wide{grid-column:span 12;flex-direction:row;align-items:center;gap:28px;padding:34px 38px;
background:linear-gradient(120deg,var(--ms-cream) 0%,var(--home-bg-elev) 62%)}
html.dark .home-new .feature--wide{background:linear-gradient(120deg,rgba(200,150,62,.13) 0%,var(--home-bg-elev) 62%)}
.home-new .feature--wide .ficon{margin-bottom:0}
.home-new .feature--wide .wtext{flex:1}
.home-new .feature--wide .wcta{flex:none}
/* ============ Architecture ============ */
.home-new .arch{background:var(--home-bg-soft)}
.home-new .arch-layers{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.home-new .layer{
position:relative;padding:32px 28px;border-radius:var(--home-radius);background:var(--home-bg-elev);
border:1px solid var(--home-border);box-shadow:var(--home-shadow);
}
.home-new .layer-num{
font-family:var(--home-mono);font-size:13px;font-weight:700;color:var(--ms-gold);
display:flex;align-items:center;gap:10px;margin-bottom:16px;
}
.home-new .layer-num::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--home-border-strong),transparent)}
.home-new .layer h3{font-size:19px;margin-bottom:10px}
.home-new .layer p{font-size:14.5px;color:var(--home-ink-soft);margin-bottom:16px}
.home-new .layer ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.home-new .layer li{
font-size:12.5px;font-weight:600;padding:5px 12px;border-radius:8px;
background:var(--home-bg-soft);border:1px solid var(--home-border);color:var(--home-ink-soft);
}
.home-new .arch-flow{
margin-top:34px;display:flex;align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
font-size:14px;color:var(--home-ink-faint);font-weight:600;
}
.home-new .arch-flow svg{width:18px;height:18px;color:var(--ms-gold)}
/* ============ Quick start ============ */
.home-new .qs{background:var(--home-bg)}
.home-new .qs-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;align-items:stretch}
.home-new .qs-step{
padding:26px;border-radius:var(--home-radius);background:var(--home-bg-elev);border:1px solid var(--home-border);
box-shadow:var(--home-shadow);display:flex;flex-direction:column;
}
.home-new .qs-step h3{font-size:16.5px;margin:14px 0 6px;display:flex;align-items:center;gap:10px}
.home-new .step-no{
width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex:none;
background:linear-gradient(135deg,var(--ms-gold),var(--ms-bronze));color:#fff;
font-family:var(--home-mono);font-size:13.5px;font-weight:700;
}
.home-new .qs-step>p{font-size:13.5px;color:var(--home-ink-faint);margin-bottom:14px}
.home-new .codeblock{margin-top:auto;position:relative;background:var(--home-code-bg);border-radius:10px;padding:14px 16px;overflow-x:auto}
.home-new .codeblock pre{margin:0;font-family:var(--home-mono);font-size:12.5px;line-height:1.7;color:var(--home-code-ink);white-space:pre}
.home-new .codeblock .c-comment{color:#8d8472}
.home-new .copy-btn{
position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:7px;border:1px solid #4a4232;
background:#2c271e;color:#b3a88e;display:grid;place-items:center;cursor:pointer;opacity:0;
transition:opacity .2s,color .2s;
}
.home-new .codeblock:hover .copy-btn,.home-new .copy-btn:focus-visible{opacity:1}
.home-new .copy-btn:hover{color:var(--ms-gold-light)}
.home-new .copy-btn svg{width:14px;height:14px}
.home-new .copy-btn.copied{color:#7fd49c;opacity:1}
/* ============ Stats ============ */
.home-new .stats{background:var(--home-bg-soft);border-top:1px solid var(--home-border);border-bottom:1px solid var(--home-border)}
.home-new .stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;padding:64px 0}
.home-new .stat{text-align:center;padding:0 20px}
.home-new .stat .v{font-size:clamp(34px,4vw,46px);font-weight:800;letter-spacing:-.02em;
background:linear-gradient(120deg,var(--ms-gold),var(--ms-bronze));
-webkit-background-clip:text;background-clip:text;color:transparent}
.home-new .stat .l{margin-top:8px;font-size:15px;color:var(--home-ink-soft);font-weight:500}
/* ============ About ============ */
.home-new .about .about-card{
max-width:880px;margin:0 auto;padding:48px 52px;border-radius:20px;
background:var(--home-bg-elev);border:1px solid var(--home-border);box-shadow:var(--home-shadow);
}
.home-new .about-card p{color:var(--home-ink-soft);font-size:16px}
.home-new .about-card p+p{margin-top:18px}
.home-new .about-points{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:32px}
.home-new .about-points div{display:flex;gap:11px;align-items:flex-start;font-size:14px;font-weight:600;color:var(--home-ink)}
.home-new .about-points svg{width:19px;height:19px;color:var(--ms-gold);flex:none;margin-top:3px}
/* ============ CTA ============ */
.home-new .cta-band{padding:0 0 96px}
.home-new .cta-card{
position:relative;overflow:hidden;text-align:center;padding:64px 32px;border-radius:22px;
background:linear-gradient(130deg,#2b2315 0%,#3d2f17 55%,#2b2315 100%);
border:1px solid #4d3f23;
}
.home-new .cta-card::before{
content:"";position:absolute;inset:0;
background:radial-gradient(520px 240px at 50% -30%,rgba(224,184,106,.32),transparent 70%);
}
.home-new .cta-card h2{position:relative;color:#fbf3e2;font-size:clamp(25px,3vw,34px)}
.home-new .cta-card p{position:relative;color:#c9bca0;margin-top:12px}
.home-new .cta-card .hero-actions{justify-content:center;margin-top:30px}
.home-new .cta-card .btn-ghost{background:transparent;border-color:#6b5a38;color:#f0e6d0}
.home-new .cta-card .btn-ghost:hover{border-color:var(--ms-gold-light);color:var(--ms-gold-light)}
/* ============ Footer ============ */
.home-new .footer{border-top:1px solid var(--home-border);background:var(--home-bg-soft);padding:44px 0}
.home-new .footer-inner{display:flex;flex-wrap:wrap;gap:20px;align-items:center;justify-content:space-between}
.home-new .footer p{font-size:13.5px;color:var(--home-ink-faint);line-height:1.9}
.home-new .footer a{color:var(--home-ink-soft)}
.home-new .footer a:hover{color:var(--ms-gold-dark)}
html.dark .home-new .footer a:hover{color:var(--ms-gold-light)}
.home-new .footer-links{display:flex;gap:18px;flex-wrap:wrap}
.home-new .footer-links a{font-size:13.5px;font-weight:500}
/* ============ Reveal animation ============ */
.home-new .reveal{opacity:0;transform:translateY(22px);transition:opacity .7s ease,transform .7s ease}
.home-new .reveal.in{opacity:1;transform:none}
.home-new .stagger>*{transition-delay:calc(var(--i,0)*70ms)}
/* ============ Responsive ============ */
@media (max-width:980px){
.home-new .hero{padding:120px 0 64px}
.home-new .hero-grid{grid-template-columns:1fr;gap:44px}
.home-new .feature{grid-column:span 6}
.home-new .feature--wide{grid-column:span 12;flex-direction:column;align-items:flex-start;gap:18px}
.home-new .arch-layers,.home-new .qs-grid,.home-new .stats-grid,.home-new .about-points{grid-template-columns:1fr}
.home-new .stat{padding:10px 0}
.home-new .nav-links{display:none}
.home-new .hamburger{display:grid}
.home-new .nav-menu-open .nav-links{
display:flex;flex-direction:column;position:absolute;top:64px;left:0;right:0;
background:var(--home-bg);border-bottom:1px solid var(--home-border);padding:12px 20px 18px;gap:2px;
box-shadow:var(--home-shadow);
}
.home-new .about .about-card{padding:32px 24px}
}
@media (max-width:560px){
.home-new .feature{grid-column:span 12}
.home-new .section{padding:68px 0}
.home-new .hero-actions .btn{width:100%;justify-content:center}
.home-new .footer-inner{flex-direction:column;align-items:flex-start}
}
@media (prefers-reduced-motion:reduce){
*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
.home-new .reveal{opacity:1;transform:none}
.home-new .terminal-body .ln{opacity:1;transform:none}
}
</style>

View File

@ -0,0 +1,27 @@
<script setup>
import DefaultTheme from 'vitepress/theme'
import { useRoute } from 'vitepress'
import { computed, onMounted, onUnmounted } from 'vue'
import Home from './Home.vue'
const route = useRoute()
const isHome = computed(() => route.path === '/' || route.path === '/index.html')
function applyLayout() {
if (isHome.value) {
document.documentElement.classList.add('home-layout')
} else {
document.documentElement.classList.remove('home-layout')
}
}
onMounted(applyLayout)
onUnmounted(() => {
document.documentElement.classList.remove('home-layout')
})
</script>
<template>
<Home v-if="isHome" />
<DefaultTheme.Layout v-else />
</template>

554
.vitepress/theme/custom.css Normal file
View File

@ -0,0 +1,554 @@
/* ========================================
MengStack Brand Theme — Gold & Bronze
======================================== */
:root {
/* Brand palette */
--ms-gold: #C8963E;
--ms-gold-light: #E0B86A;
--ms-gold-dark: #A07830;
--ms-bronze: #B87333;
--ms-bronze-dark: #8B5A2B;
--ms-cream: #FDF8EF;
--ms-cream-dark: #F5EDD8;
/* Override VitePress brand colors */
--vp-c-brand-1: var(--ms-gold);
--vp-c-brand-2: var(--ms-gold-dark);
--vp-c-brand-3: var(--ms-bronze);
--vp-c-brand-soft: rgba(200, 150, 62, 0.14);
--vp-c-brand-soft-2: rgba(200, 150, 62, 0.25);
}
/* ============ NAV BAR ============ */
.VPNav {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.VPNavBar {
border-bottom: 1px solid rgba(200, 150, 62, 0.1) !important;
background: rgba(255, 255, 255, 0.85) !important;
}
.dark .VPNavBar {
background: rgba(27, 27, 30, 0.88) !important;
border-bottom-color: rgba(200, 150, 62, 0.12) !important;
}
.VPNavBarTitle a.title {
font-weight: 700;
}
.VPNavBarTitle a.title span {
background: linear-gradient(135deg, var(--ms-gold-dark), var(--ms-gold-light));
-webkit-background-clip: text;
background-clip: text;
color: transparent !important;
}
.VPNavBar .VPNavBarMenuLink,
.VPNavBar .VPNavBarMenuGroup .title {
font-weight: 500;
transition: color 0.25s;
}
.VPNavBar .VPNavBarMenuLink:hover,
.VPNavBar .VPNavBarMenuGroup .title:hover {
color: var(--ms-gold) !important;
}
.VPNavBar .VPNavBarMenuLink.active {
color: var(--ms-gold) !important;
}
.VPNavBar .VPNavBarMenuLink.active::after {
background: var(--ms-gold) !important;
}
/* Social icon */
.VPNavBar .VPSocialLink:hover {
color: var(--ms-gold) !important;
transform: scale(1.1);
}
.VPNavBar .VPSocialLink {
transition: color 0.25s, transform 0.25s;
}
/* ============ HERO SECTION ============ */
.VPHero {
padding-top: 48px;
padding-bottom: 40px;
position: relative;
overflow: hidden;
}
.VPHero::before {
content: '';
position: absolute;
top: -40%;
left: -20%;
width: 140%;
height: 180%;
background: radial-gradient(ellipse at 30% 20%, rgba(200, 150, 62, 0.06) 0%, transparent 60%),
radial-gradient(ellipse at 70% 80%, rgba(184, 115, 51, 0.04) 0%, transparent 50%);
pointer-events: none;
z-index: 0;
}
.dark .VPHero::before {
background: radial-gradient(ellipse at 30% 20%, rgba(200, 150, 62, 0.08) 0%, transparent 60%),
radial-gradient(ellipse at 70% 80%, rgba(184, 115, 51, 0.05) 0%, transparent 50%);
}
.VPHero .main {
position: relative;
z-index: 1;
}
/* Hide empty image column, make content full-width */
.VPHero .image {
display: none !important;
}
.VPHero .container {
grid-template-columns: 1fr !important;
gap: 0 !important;
max-width: 100% !important;
width: 100% !important;
flex-grow: 1 !important;
}
.VPHero .content {
max-width: 100% !important;
flex: 1 1 100% !important;
}
/* Hero name — premium gold gradient text */
.VPHero .heading {
margin-bottom: 8px;
}
.VPHero .heading .name {
font-size: 4.2rem !important;
line-height: 1.1 !important;
letter-spacing: -0.03em;
font-weight: 800;
background: linear-gradient(135deg, #B8862D 0%, #D4A84B 25%, #F0D078 50%, #D4A84B 75%, #A07020 100%);
-webkit-background-clip: text !important;
background-clip: text !important;
color: transparent !important;
text-shadow: none;
position: relative;
display: inline-block;
}
/* Decorative accent line under the name */
.VPHero .heading .name::after {
content: '';
position: absolute;
bottom: -6px;
left: 0;
width: 80px;
height: 3px;
background: linear-gradient(90deg, var(--ms-gold), var(--ms-gold-light), transparent);
border-radius: 2px;
}
.VPHero .text {
font-size: 1.6rem !important;
line-height: 1.4 !important;
font-weight: 600;
margin-top: 20px;
}
.dark .VPHero .text {
color: rgba(255, 255, 255, 0.88) !important;
}
.VPHero .tagline {
font-size: 1.1rem !important;
color: var(--ms-gold-dark) !important;
font-weight: 400;
letter-spacing: 0.03em;
margin-top: 12px;
}
.dark .VPHero .tagline {
color: var(--ms-gold-light) !important;
}
/* Hero buttons */
.VPHero .actions .action a.VPButton.brand {
background: linear-gradient(135deg, var(--ms-gold-dark), var(--ms-gold)) !important;
border: none !important;
color: #fff !important;
font-weight: 600;
border-radius: 10px !important;
padding: 0 28px !important;
height: 44px !important;
line-height: 44px !important;
font-size: 0.95rem !important;
box-shadow: 0 4px 14px rgba(200, 150, 62, 0.3);
transition: all 0.3s ease;
}
.VPHero .actions .action a.VPButton.brand:hover {
box-shadow: 0 6px 20px rgba(200, 150, 62, 0.45);
transform: translateY(-2px);
}
.VPHero .actions .action a.VPButton.alt {
border: 1.5px solid var(--ms-gold) !important;
color: var(--ms-gold-dark) !important;
border-radius: 10px !important;
padding: 0 28px !important;
height: 44px !important;
line-height: 42px !important;
font-size: 0.95rem !important;
font-weight: 600;
background: transparent !important;
transition: all 0.3s ease;
}
.dark .VPHero .actions .action a.VPButton.alt {
color: var(--ms-gold-light) !important;
border-color: var(--ms-gold-light) !important;
}
.VPHero .actions .action a.VPButton.alt:hover {
background: rgba(200, 150, 62, 0.08) !important;
transform: translateY(-2px);
}
/* ============ FEATURES SECTION ============ */
.VPFeatures {
padding: 48px 0 56px !important;
}
.VPFeatures .VPFeature .box {
border: 1px solid rgba(200, 150, 62, 0.12) !important;
border-radius: 14px !important;
padding: 28px 24px 24px !important;
background: rgba(255, 255, 255, 0.7) !important;
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.dark .VPFeatures .VPFeature .box {
background: rgba(40, 40, 44, 0.6) !important;
border-color: rgba(200, 150, 62, 0.1) !important;
}
.VPFeatures .VPFeature .box::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--ms-gold-dark), var(--ms-gold-light), var(--ms-bronze));
opacity: 0;
transition: opacity 0.35s;
}
.VPFeatures .VPFeature:hover .box {
border-color: rgba(200, 150, 62, 0.3) !important;
box-shadow: 0 8px 30px rgba(200, 150, 62, 0.1), 0 2px 8px rgba(0, 0, 0, 0.04);
transform: translateY(-4px);
}
.dark .VPFeatures .VPFeature:hover .box {
border-color: rgba(200, 150, 62, 0.25) !important;
box-shadow: 0 8px 30px rgba(200, 150, 62, 0.08), 0 2px 8px rgba(0, 0, 0, 0.2);
}
.VPFeatures .VPFeature:hover .box::before {
opacity: 1;
}
/* Feature icon */
.VPFeatures .VPFeature .icon {
width: 52px !important;
height: 52px !important;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
background: linear-gradient(135deg, rgba(200, 150, 62, 0.08), rgba(184, 115, 51, 0.05)) !important;
border: 1px solid rgba(200, 150, 62, 0.12);
border-radius: 12px !important;
margin-bottom: 4px;
}
.dark .VPFeatures .VPFeature .icon {
background: linear-gradient(135deg, rgba(200, 150, 62, 0.12), rgba(184, 115, 51, 0.08)) !important;
border-color: rgba(200, 150, 62, 0.15);
}
/* Feature title */
.VPFeatures .VPFeature .title {
font-size: 1.05rem !important;
font-weight: 700 !important;
color: var(--ms-bronze-dark) !important;
letter-spacing: 0.01em;
}
.dark .VPFeatures .VPFeature .title {
color: var(--ms-gold-light) !important;
}
/* Feature details — prevent truncation */
.VPFeatures .details {
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.65;
font-size: 0.88rem !important;
color: var(--vp-c-text-2) !important;
}
/* Feature link arrow */
.VPFeatures .VPFeature .link-text {
color: var(--ms-gold) !important;
font-weight: 600;
}
/* ============ ABOUT SECTION ============ */
.VPDoc:has(.content-container) .main .content .content-body {
max-width: none;
}
.VPHome .VPHomeContent {
max-width: 1152px;
margin: 0 auto;
padding: 0 24px;
}
/* Style the "关于 MengStack" section */
.VPHome .vp-doc h2 {
font-size: 1.8rem;
font-weight: 700;
text-align: center;
margin: 48px 0 24px;
background: linear-gradient(135deg, var(--ms-gold-dark), var(--ms-bronze));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
position: relative;
}
.VPHome .vp-doc h2::after {
content: '';
display: block;
width: 60px;
height: 3px;
background: linear-gradient(90deg, var(--ms-gold), var(--ms-bronze));
margin: 12px auto 0;
border-radius: 2px;
}
.VPHome .vp-doc p {
font-size: 1.05rem;
line-height: 1.85;
color: var(--vp-c-text-1);
max-width: 720px;
margin: 0 auto 16px;
text-align: center;
}
/* ============ FOOTER ============ */
.VPFooter {
border-top: 1px solid rgba(200, 150, 62, 0.1) !important;
background: rgba(250, 247, 240, 0.6) !important;
padding: 28px 0 !important;
}
.dark .VPFooter {
background: rgba(20, 20, 22, 0.8) !important;
border-top-color: rgba(200, 150, 62, 0.08) !important;
}
.VPFooter .message {
color: var(--ms-gold-dark) !important;
font-weight: 500;
}
.dark .VPFooter .message {
color: var(--ms-gold-light) !important;
}
.VPFooter a {
color: var(--vp-c-text-2);
text-decoration: none;
transition: color 0.25s;
}
.VPFooter a:hover {
color: var(--ms-gold) !important;
text-decoration: none;
}
.footer-extra {
margin-top: 8px;
font-size: 12px;
color: var(--vp-c-text-2);
}
.footer-extra a {
color: var(--vp-c-text-2);
text-decoration: none;
transition: color 0.25s;
}
.footer-extra a:hover {
color: var(--ms-gold);
text-decoration: underline;
}
/* ============ DOC PAGES ============ */
/* Sidebar active item */
.VPSidebarItem.is-active > .item .link .link-text {
color: var(--ms-gold) !important;
font-weight: 600;
}
.VPSidebarItem.is-active > .item .link {
background: rgba(200, 150, 62, 0.06) !important;
border-left-color: var(--ms-gold) !important;
}
/* Sidebar hover */
.VPSidebarItem .item .link:hover {
background: rgba(200, 150, 62, 0.04) !important;
}
/* Inline code */
.vp-doc code {
background: rgba(200, 150, 62, 0.08) !important;
color: var(--ms-bronze-dark) !important;
border: 1px solid rgba(200, 150, 62, 0.1);
border-radius: 5px;
padding: 2px 6px;
font-size: 0.88em;
}
.dark .vp-doc code {
background: rgba(200, 150, 62, 0.1) !important;
color: var(--ms-gold-light) !important;
border-color: rgba(200, 150, 62, 0.12);
}
/* Blockquote / custom containers */
.vp-doc blockquote {
border-left: 3px solid var(--ms-gold) !important;
background: rgba(200, 150, 62, 0.04);
padding: 12px 16px;
border-radius: 0 8px 8px 0;
color: var(--vp-c-text-2);
}
/* Links in content */
.vp-doc a {
color: var(--ms-gold) !important;
font-weight: 500;
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-color 0.25s;
}
.vp-doc a:hover {
border-bottom-color: var(--ms-gold);
}
/* Headings in doc pages */
.vp-doc h1, .vp-doc h2, .vp-doc h3, .vp-doc h4 {
color: var(--ms-bronze-dark);
font-weight: 700;
}
.dark .vp-doc h1, .dark .vp-doc h2, .dark .vp-doc h3, .dark .vp-doc h4 {
color: var(--ms-gold-light);
}
.vp-doc h2 {
border-bottom: 1px solid rgba(200, 150, 62, 0.15) !important;
}
/* Table styling */
.vp-doc table {
border-collapse: collapse;
width: 100%;
}
.vp-doc table th {
background: rgba(200, 150, 62, 0.06) !important;
color: var(--ms-bronze-dark);
font-weight: 600;
}
.dark .vp-doc table th {
background: rgba(200, 150, 62, 0.08) !important;
color: var(--ms-gold-light);
}
.vp-doc table tr {
border-bottom: 1px solid rgba(200, 150, 62, 0.08);
}
/* ============ SEARCH ============ */
.VPLocalSearchBox .filter {
background: rgba(200, 150, 62, 0.08) !important;
border-color: rgba(200, 150, 62, 0.15) !important;
color: var(--ms-gold-dark);
}
/* ============ SCROLLBAR ============ */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(200, 150, 62, 0.2);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(200, 150, 62, 0.35);
}
/* ============ SELECTION ============ */
::selection {
background: rgba(200, 150, 62, 0.2);
color: inherit;
}
/* ============ TRANSITIONS ============ */
.VPNavBar, .VPFeatures .VPFeature, .VPHero .actions a, .VPFooter {
transition-property: background, border-color, box-shadow, transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
/* ============ RESPONSIVE ============ */
@media (max-width: 768px) {
.VPHero .heading .name {
font-size: 2.8rem !important;
}
.VPHero .text {
font-size: 1.3rem !important;
}
.VPFeatures .VPFeature .box {
padding: 22px 18px 18px !important;
}
}

View File

@ -0,0 +1,8 @@
import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'
import './custom.css'
export default {
extends: DefaultTheme,
Layout
}

181
api/auth.md Normal file
View File

@ -0,0 +1,181 @@
# 认证接口
## 用户注册
注册新用户并返回 JWT 令牌。
**`POST /api/v1/auth/register`**
### 请求体
```json
{
"email": "user@example.com",
"username": "johndoe",
"nickname": "John Doe",
"password": "securepass123"
}
```
| 字段 | 类型 | 必填 | 约束 |
|------|------|------|------|
| `email` | string | 是 | 有效邮箱格式 |
| `username` | string | 是 | 3-64 字符 |
| `nickname` | string | 否 | - |
| `password` | string | 是 | 8-128 字符 |
### 响应
```json
{
"code": 0,
"message": "success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 7200
},
"trace_id": "req-abc-123"
}
```
### 错误
| 状态码 | 说明 |
|--------|------|
| 400 | 参数校验失败 |
| 409 | 邮箱或用户名已存在 |
---
## 用户登录
使用邮箱和密码登录。
**`POST /api/v1/auth/login`**
### 请求体
```json
{
"email": "user@example.com",
"password": "securepass123"
}
```
| 字段 | 类型 | 必填 |
|------|------|------|
| `email` | string | 是 |
| `password` | string | 是 |
### 响应
同注册接口,返回 TokenPair。
### 错误
| 状态码 | 说明 |
|--------|------|
| 400 | 参数校验失败 |
| 401 | 邮箱或密码错误 |
---
## 刷新令牌
使用 refresh_token 获取新的令牌对。
**`POST /api/v1/auth/refresh`**
### 请求体
```json
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
```
### 响应
同注册接口,返回新的 TokenPair。
### 错误
| 状态码 | 说明 |
|--------|------|
| 400 | 参数校验失败 |
| 401 | Refresh Token 无效或过期 |
---
## 修改密码
修改当前用户密码,需要认证。
**`POST /api/v1/password`**
### 请求头
```
Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id>
```
### 请求体
```json
{
"old_password": "oldpass123",
"new_password": "newpass456"
}
```
| 字段 | 类型 | 必填 | 约束 |
|------|------|------|------|
| `old_password` | string | 是 | - |
| `new_password` | string | 是 | 8-128 字符 |
### 响应
```json
{
"code": 0,
"message": "success",
"data": null,
"trace_id": "req-abc-123"
}
```
---
## 获取当前用户
获取当前认证用户的个人资料。
**`GET /api/v1/profile`**
### 请求头
```
Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id>
```
### 响应
```json
{
"code": 0,
"message": "success",
"data": {
"id": 1,
"email": "user@example.com",
"username": "johndoe",
"nickname": "John Doe",
"avatar": "",
"status": 1,
"tenant_id": "tenant-001"
},
"trace_id": "req-abc-123"
}
```

73
api/overview.md Normal file
View File

@ -0,0 +1,73 @@
---
title: API接口概览 | 统一响应格式与错误码说明 - MengStack官方文档
---
# API 概览
MengStack API 基于 RESTful 设计,所有接口统一前缀 `/api/v1`。
## 基础信息
| 项目 | 值 |
|------|------|
| Base URL | `http://localhost:2222/api/v1` |
| 数据格式 | JSON |
| 认证方式 | Bearer Token (JWT) |
| API 文档 | `http://localhost:2222/swagger/index.html` |
## 统一响应格式
所有接口返回统一格式:
```json
{
"code": 0,
"message": "success",
"data": {},
"trace_id": "req-abc-123"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int | 状态码,0 表示成功 |
| `message` | string | 状态描述 |
| `data` | any | 响应数据 |
| `trace_id` | string | 请求追踪 ID |
## 错误码
| HTTP 状态码 | 说明 |
|------------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未认证或 Token 过期 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 资源冲突(如邮箱已注册) |
| 500 | 服务器内部错误 |
## 认证
需要认证的接口,在请求头中携带:
```
Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id>
```
## 接口列表
| 模块 | 端点 | 说明 |
|------|------|------|
| 认证 | `POST /auth/register` | 用户注册,详见[认证接口](/api/auth) |
| 认证 | `POST /auth/login` | 用户登录,详见[认证接口](/api/auth) |
| 认证 | `POST /auth/refresh` | 刷新令牌,详见[认证接口](/api/auth) |
| 认证 | `POST /password` | 修改密码,详见[认证接口](/api/auth) |
| 认证 | `GET /profile` | 获取当前用户,详见[认证接口](/api/auth) |
| 系统 | `GET /health` | 健康检查,详见[系统接口](/api/system) |
| 系统 | `GET /ping` | 连通性测试,详见[系统接口](/api/system) |
## Swagger UI
启动服务后访问 `http://localhost:2222/swagger/index.html` 查看交互式 API 文档。

66
api/system.md Normal file
View File

@ -0,0 +1,66 @@
# 系统接口
## 健康检查
检查 PostgreSQL 和 Redis 连接状态。
**`GET /health`**
无需认证。
### 响应
```json
{
"status": "ok",
"database": "connected",
"redis": "connected",
"timestamp": 1727856000,
"version": "0.1.0",
"trace_id": "req-abc-123"
}
```
### 状态说明
| 字段 | 可能值 |
|------|--------|
| `status` | `ok` / `degraded` / `down` |
| `database` | `connected` / `disconnected` |
| `redis` | `connected` / `disconnected` |
---
## 连通性测试
返回 pong,用于验证认证和多租户中间件是否正常工作。
**`GET /api/v1/ping`**
需要认证和租户头。
### 请求头
```
Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id>
```
### 响应
```json
{
"code": 0,
"message": "success",
"data": {
"message": "pong"
},
"trace_id": "req-abc-123"
}
```
### 用途
- 验证 Token 是否有效
- 验证多租户中间件是否正常
- 客户端心跳检测

81
changelog.md Normal file
View File

@ -0,0 +1,81 @@
---
title: 更新日志 | 版本迭代记录与路线图 - MengStack开源框架
---
# 更新日志
## v0.1.0 (2026-10-02)
**初始版本** — 项目骨架 + 认证模块
### 新增
- **三层隔离架构**:Kernel / App / Modules 层间依赖方向单一
- **模块四层模式**:Domain / Application / Infrastructure / Interfaces
- **依赖注入**:基于 uber-go/fx 的自动依赖解析和生命周期管理
- **配置管理**:Viper 分层配置,支持 YAML + 环境变量覆盖
- **结构化日志**:Zap 结构化输出,请求级 trace_id
- **多租户支持**:基于 X-Tenant-ID 的租户隔离中间件
- **认证模块**:
- 用户注册(邮箱 + 用户名 + 密码)
- 用户登录(邮箱 + 密码)
- JWT 双 Token 机制(Access + Refresh)
- Token 刷新
- 密码修改
- 用户资料查询
- **统一响应格式**:`{code, message, data, trace_id}`
- **Swagger API 文档**:swaggo 注解驱动,自动生成 Swagger UI
- **Docker Compose**:PostgreSQL + Redis + 应用一键启动
- **健康检查**:PostgreSQL + Redis 连接状态检测
- **CORS 中间件**:跨域请求支持
- **请求追踪**:每个请求自动分配 trace_id
### 技术栈
| 组件 | 版本 |
|------|------|
| Go | 1.23 |
| Gin | 1.10 |
| GORM | 1.25 |
| PostgreSQL | 14+ |
| Redis | 7+ |
| uber-go/fx | 1.23 |
| Zap | 1.27 |
| Viper | 1.19 |
| golang-jwt | 5.2 |
---
## 路线图
### v0.2.0 — RBAC 权限模型
- Role / Permission 数据模型
- 用户角色分配
- RBAC 中间件
- 权限校验装饰器
### v0.3.0 — 组织管理 + 审计日志
- 组织 CRUD
- 用户-组织关系
- 操作审计日志
### v0.4.0 — 配置管理增强
- 运行时配置 API
- 全局默认 + 租户覆盖
- 配置变更历史
### v0.5.0 — 用户示例模块
- 完整用户 CRUD
- 单元测试 + 集成测试
- 示例代码
### v1.0.0 — 正式发布
- 完整测试覆盖
- 安全审计
- 性能基准测试
- 生产部署文档

56
community.md Normal file
View File

@ -0,0 +1,56 @@
---
title: 开发者社区 | 交流渠道与贡献指南 - MengStack开源社区
---
# 社区
MengStack 正在积极建设中,欢迎参与!
## 交流渠道
| 渠道 | 链接 |
|------|------|
| Gitea 仓库 | [mengstackgit.softunis.com/mengstack](https://mengstackgit.softunis.com/mengstack) |
| 问题反馈 | [Gitea Issues](https://mengstackgit.softunis.com/mengstack/issues) |
| 功能建议 | [Gitea Issues](https://mengstackgit.softunis.com/mengstack/issues/new) |
## 参与贡献
### 报告 Bug
1. 在 Gitea 上创建 Issue
2. 描述复现步骤
3. 附上错误日志
4. 标注环境信息(Go 版本、OS、数据库版本)
### 提交代码
1. Fork 仓库
2. 创建功能分支:`git checkout -b feature/my-feature`
3. 提交变更:`git commit -am 'Add my feature'`
4. 推送分支:`git push origin feature/my-feature`
5. 创建 Pull Request
### 代码规范
- 遵循项目三层隔离架构
- 新模块遵循四层模式(Domain / Application / Infrastructure / Interfaces)
- 所有公开函数添加 swaggo 注解
- 提交前运行 `make lint` 和 `make test`
## 问答社区(即将上线)
我们正在筹备在线问答社区,届时将提供:
- 技术问答
- 最佳实践分享
- 版本发布公告
- 开发者交流
敬请期待!
## 关于软盟
MengStack 由 [软盟 SoftUnis](https://www.softunis.com) 团队开发和维护。
软盟专注于企业级解决方案,致力于开源生态建设。

69
demo.md Normal file
View File

@ -0,0 +1,69 @@
---
title: 在线演示 | 免费体验MengStack接口服务 - MengStack官方
---
# 在线演示
## 演示地址
| 服务 | 地址 |
|------|------|
| API 服务 | `https://mengstackdemo.softunis.com` |
| Swagger UI | `https://mengstackdemo.softunis.com/swagger/index.html` |
| 健康检查 | `https://mengstackdemo.softunis.com/health` |
::: warning 注意
演示数据会定期重置,请勿存储重要数据。
:::
## 快速体验
### 1. 注册账号
```bash
curl -X POST https://mengstackdemo.softunis.com/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "demo@example.com",
"username": "demo_user",
"nickname": "Demo User",
"password": "demo12345"
}'
```
### 2. 登录获取 Token
```bash
curl -X POST https://mengstackdemo.softunis.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "demo@example.com",
"password": "demo12345"
}'
```
### 3. 访问受保护接口
```bash
curl https://mengstackdemo.softunis.com/api/v1/profile \
-H "Authorization: Bearer <your_access_token>" \
-H "X-Tenant-ID: demo-tenant"
```
## Swagger UI 交互
打开 [Swagger UI](https://mengstackdemo.softunis.com/swagger/index.html),可以:
- 浏览所有 API 端点
- 在线发送请求测试
- 查看请求/响应模型
- 下载 OpenAPI 规范文件
### 使用步骤
1. 打开 Swagger UI
2. 点击 `/auth/register` 端点
3. 点击 "Try it out"
4. 填写请求参数
5. 点击 "Execute"
6. 查看响应结果

73
guide/architecture.md Normal file
View File

@ -0,0 +1,73 @@
# 三层隔离架构
MengStack 采用三层隔离架构,从外到内依次为:**Kernel → App → Modules**。
## 架构总览
```
┌─────────────────────────────────────────────────┐
│ Modules │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Auth │ │ Org │ │ User │ ... │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
├───────┼───────────┼───────────┼──────────────────┤
│ └───────────┼───────────┘ │
│ │ │
│ App │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Database │ │ Cache │ │Middleware│ ... │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
├───────┼────────────┼────────────┼────────────────┤
│ └────────────┼────────────┘ │
│ │ │
│ Kernel │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Response │ │ Error │ │ Paginator│ ... │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
└──────────────────────────────────────────────────┘
```
## 依赖规则
**只能从上到下依赖,禁止反向依赖。**
| 层 | 可以依赖 | 禁止依赖 |
|---|---|---|
| Modules | App, Kernel | 其他 Modules |
| App | Kernel | Modules |
| Kernel | 标准库 | App, Modules |
## Kernel 层
零业务语义的基础组件。判断标准:**这个组件是否知道"用户"、"租户"、"订单"等业务概念?** 如果知道,它就不属于 Kernel。
Kernel 提供的都是纯粹的技术工具:
- 统一响应格式
- 通用错误类型
- 分页工具
- 字符串/时间工具
## App 层
应用基础设施,负责连接外部世界:
- 数据库连接(GORM + PostgreSQL)
- 缓存连接(Redis)
- 全局中间件
- 健康检查
App 层可以引用 Kernel,但不知道任何业务模块的存在。
## Modules 层
业务模块,每个模块独立自包含。模块间不能直接引用,只能通过接口或事件通信。
## 为什么这样分?
1. **Kernel 可复用**:换一个项目,Kernel 可以直接搬过去
2. **App 可替换**:换数据库、换缓存,只改 App 层
3. **Modules 可删除**:删除一个业务模块,不影响其他模块和基础设施

107
guide/auth.md Normal file
View File

@ -0,0 +1,107 @@
# 认证与授权
MengStack 内置完整的认证授权系统,基于 JWT 双 Token 机制。
## 认证流程
```
┌────────┐ POST /auth/register ┌────────┐
│ Client │ ──────────────────────────→ │ Server │
│ │ ←────────────────────────── │ │
│ │ { access_token, refresh } │ │
│ │ │ │
│ │ POST /auth/login │ │
│ │ ──────────────────────────→ │ │
│ │ ←────────────────────────── │ │
│ │ { access_token, refresh } │ │
│ │ │ │
│ │ GET /api/v1/profile │ │
│ │ Authorization: Bearer xxx │ │
│ │ ──────────────────────────→ │ │
│ │ ←────────────────────────── │ │
│ │ { user data } │ │
│ │ │ │
│ │ POST /auth/refresh │ │
│ │ { refresh_token } │ │
│ │ ──────────────────────────→ │ │
│ │ ←────────────────────────── │ │
│ │ { new token pair } │ │
└────────┘ └────────┘
```
## API 端点
| 端点 | 方法 | 说明 | 认证 |
|------|------|------|------|
| `/api/v1/auth/register` | POST | 用户注册 | 无 |
| `/api/v1/auth/login` | POST | 用户登录 | 无 |
| `/api/v1/auth/refresh` | POST | 刷新令牌 | 无 |
| `/api/v1/password` | POST | 修改密码 | Bearer |
| `/api/v1/profile` | GET | 获取当前用户 | Bearer |
## 注册
```bash
curl -X POST http://localhost:2222/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"username": "johndoe",
"nickname": "John Doe",
"password": "securepass123"
}'
```
响应:
```json
{
"code": 0,
"message": "success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 7200
},
"trace_id": "abc-123-def"
}
```
## 登录
```bash
curl -X POST http://localhost:2222/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securepass123"
}'
```
## 使用 Access Token
在请求头中携带 `Authorization: Bearer <access_token>`:
```bash
curl http://localhost:2222/api/v1/profile \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```
## 刷新 Token
Access Token 过期后,使用 Refresh Token 获取新的令牌对:
```bash
curl -X POST http://localhost:2222/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}'
```
## 安全特性
- **密码哈希**:使用 bcrypt 加密存储,永不明文
- **双 Token**:Access Token 短期有效(2h),Refresh Token 长期有效(7d)
- **Token 轮换**:每次刷新都生成全新的令牌对
- **验证约束**:用户名 3-64 字符,密码 8-128 字符,邮箱格式校验

53
guide/cicd.md Normal file
View File

@ -0,0 +1,53 @@
# CI/CD 流水线
MengStack 使用 Gitea Actions 实现持续集成和部署。
## 流水线概览
```
代码推送 → 代码检查 → 构建 → 测试 → Docker 镜像 → 部署
```
## Gitea Actions 配置
仓库地址:`https://mengstackgit.softunis.com/mengstack`
### 流水线阶段
| 阶段 | 说明 |
|------|------|
| Lint | 代码风格检查 |
| Build | 编译 Go 二进制 |
| Test | 运行单元测试和集成测试 |
| Docker | 构建 Docker 镜像 |
| Deploy | 部署到目标服务器 |
## 本地验证
在推送代码前,本地运行检查:
```bash
# 代码检查
make lint
# 运行测试
make test
# 构建
make build
```
## 部署策略
当前采用单服务器部署:
1. 代码推送到 Gitea 仓库
2. CI 流水线自动触发
3. 通过 SSH 部署到目标服务器
4. Docker Compose 重启服务
## 后续规划
- **多环境支持**:dev / staging / production 环境隔离
- **蓝绿部署**:零停机更新
- **自动回滚**:部署失败自动回退

102
guide/configuration.md Normal file
View File

@ -0,0 +1,102 @@
# 配置管理
MengStack 使用 [Viper](https://github.com/spf13/viper) 进行配置管理,支持多来源、分层配置。
## 配置优先级
从高到低:
1. 环境变量(`MENGSTACK_` 前缀)
2. `.env` 文件
3. `configs/config.yaml`
4. 默认值
## 配置文件
### `configs/config.yaml`
```yaml
server:
port: 2222
mode: debug # debug / release / test
database:
host: localhost
port: 5432
name: mengstack
user: postgres
password: ""
sslmode: disable
redis:
host: localhost
port: 6379
password: ""
db: 0
jwt:
secret: "change-me-in-production"
access_ttl: 7200 # 2 hours
refresh_ttl: 604800 # 7 days
log:
level: debug # debug / info / warn / error
format: console # console / json
```
### 环境变量覆盖
所有配置项都可以通过环境变量覆盖,规则:
- 前缀 `MENGSTACK_`
- 大写
- 用 `_` 连接层级
```bash
# 等价于 server.port: 3000
export MENGSTACK_SERVER_PORT=3000
# 等价于 database.host: db.example.com
export MENGSTACK_DB_HOST=db.example.com
# 等价于 jwt.secret: my-secret
export MENGSTACK_JWT_SECRET=my-secret
```
## 代码中使用
```go
type Config struct {
Server ServerConfig
Database DatabaseConfig
Redis RedisConfig
JWT JWTConfig
Log LogConfig
}
func Load() (*Config, error) {
v := viper.New()
v.SetConfigFile("configs/config.yaml")
v.SetEnvPrefix("MENGSTACK")
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := v.ReadInConfig(); err != nil {
return nil, err
}
var cfg Config
if err := v.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
```
## 最佳实践
1. **不要硬编码**:所有可变配置走 Viper
2. **生产环境用环境变量**:敏感信息(密码、密钥)不写入代码仓库
3. **提供合理默认值**:开发环境零配置即可启动
4. **使用 `.env.example`**:提供配置模板,方便新人上手

View File

@ -0,0 +1,111 @@
# 依赖注入
MengStack 使用 [uber-go/fx](https://github.com/uber-go/fx) 进行依赖注入,自动管理组件的生命周期和依赖关系。
## 为什么用依赖注入?
没有 DI 框架时,组装应用需要手动 wiring:
```go
// 手动 wiring — 容易遗漏,难以维护
db := database.New(cfg.DB)
rdb := cache.New(cfg.Redis)
repo := infrastructure.NewUserRepo(db)
svc := application.NewService(repo, rdb, cfg.JWT)
handler := interfaces.NewHandler(svc)
```
使用 fx 后,依赖关系由框架自动解析:
```go
// fx 自动解析依赖
fx.Provide(
database.New,
cache.New,
application.NewService,
interfaces.NewHandler,
)
```
## fx.Module 组织
每个模块通过 `fx.Module` 声明自己的依赖:
```go
// internal/modules/auth/interfaces/module.go
var Module = fx.Module("auth",
fx.Provide(
infrastructure.NewUserRepository,
infrastructure.NewTokenRepository,
application.NewService,
NewHandler,
NewAuthMiddleware,
SetupRoutes,
),
)
```
主应用组装所有模块:
```go
// internal/app/app.go
func NewApp() *fx.App {
return fx.New(
fx.Provide(config.Load, logger.New),
database.Module,
cache.Module,
authinterfaces.Module,
Module,
)
}
```
## 生命周期管理
fx 自动管理组件的启动和停止顺序:
```go
func newServer(lc fx.Lifecycle, engine *gin.Engine) *http.Server {
srv := &http.Server{Addr: ":2222", Handler: engine}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
go srv.ListenAndServe()
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}
```
fx 会确保:
- **OnStart** 按依赖顺序执行(先数据库,后应用)
- **OnStop** 按逆序执行(先应用,后数据库)
## 参数注入
通过函数参数声明依赖,fx 自动注入:
```go
func newEngine(
cfg *config.Config, // fx 自动提供
log *zap.Logger, // fx 自动提供
db *gorm.DB, // fx 自动提供
rdb *redis.Client, // fx 自动提供
authHandler *Handler, // fx 自动提供
authMW gin.HandlerFunc, // fx 自动提供
) *gin.Engine {
// ...
}
```
## 最佳实践
1. **构造函数注入**:通过 `fx.Provide` 注册构造函数
2. **接口传递**:尽量传递接口而非具体实现
3. **模块隔离**:每个业务模块声明自己的 `fx.Module`
4. **避免全局状态**:所有依赖通过注入获取

72
guide/docker-deploy.md Normal file
View File

@ -0,0 +1,72 @@
# Docker 部署
MengStack 提供开箱即用的 Docker Compose 配置,一键启动完整开发/生产环境。
## 快速启动
```bash
# 启动所有服务(后台运行)
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f app
```
## 服务组成
```yaml
services:
app: # MengStack 应用(端口 2222)
postgres: # PostgreSQL 数据库(端口 5432)
redis: # Redis 缓存(端口 6379)
```
## 常用命令
```bash
# 启动
docker-compose up -d
# 停止
docker-compose down
# 重新构建并启动
docker-compose up -d --build
# 查看应用日志
docker-compose logs -f app
# 进入应用容器
docker-compose exec app sh
# 进入数据库
docker-compose exec postgres psql -U postgres -d mengstack
```
## 数据持久化
PostgreSQL 和 Redis 数据通过 Docker Volume 持久化:
```yaml
volumes:
postgres_data:
redis_data:
```
清除数据重新开始:
```bash
docker-compose down -v
docker-compose up -d
```
## 生产部署建议
1. **修改默认密码**:`docker-compose.yml` 中的数据库密码
2. **设置 release 模式**:`MENGSTACK_SERVER_MODE=release`
3. **配置 HTTPS**:在反向代理(Nginx/Caddy)层处理
4. **限制端口暴露**:数据库和 Redis 不对外暴露
5. **定期备份**:PostgreSQL 数据定时备份

111
guide/getting-started.md Normal file
View File

@ -0,0 +1,111 @@
---
title: 快速开始 | 5分钟搭建MengStack项目环境 - MengStack官方文档
---
# 快速开始
## 环境要求
- Go 1.23+
- PostgreSQL 14+
- Redis 7+
- Docker & Docker Compose(可选)
## 使用 Docker Compose 启动(推荐)
最简单的方式是使用 Docker Compose 一键启动所有服务:
```bash
# 克隆项目
git clone https://gitea.softunis.com/mengstack/mengstack.git
cd mengstack
# 启动所有服务
docker-compose up -d
# 查看日志
docker-compose logs -f app
```
服务启动后:
- API 服务:`http://localhost:2222`
- Swagger UI:`http://localhost:2222/swagger/index.html`
- 健康检查:`http://localhost:2222/health`
## 本地开发
### 1. 安装依赖
```bash
# 下载 Go 模块
go mod download
# 确保 PostgreSQL 和 Redis 已启动
```
### 2. 配置环境变量
复制 `.env.example` 为 `.env` 并修改:
```bash
cp .env.example .env
```
关键配置项:
```env
MENGSTACK_SERVER_PORT=2222
# PostgreSQL
MENGSTACK_DB_HOST=localhost
MENGSTACK_DB_PORT=5432
MENGSTACK_DB_NAME=mengstack
MENGSTACK_DB_USER=postgres
MENGSTACK_DB_PASSWORD=your_password
# Redis
MENGSTACK_REDIS_HOST=localhost
MENGSTACK_REDIS_PORT=6379
# JWT
MENGSTACK_JWT_SECRET=your_jwt_secret_key
```
### 3. 启动服务
```bash
# 直接运行
go run cmd/server/main.go
# 或使用 Makefile
make run
```
### 4. 验证
```bash
# 健康检查
curl http://localhost:2222/health
# 注册新用户
curl -X POST http://localhost:2222/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"username": "admin",
"password": "password123"
}'
```
## 项目命令
| 命令 | 说明 |
|------|------|
| `make run` | 启动开发服务 |
| `make build` | 编译二进制文件 |
| `make test` | 运行测试 |
| `make swagger` | 重新生成 API 文档 |
| `make lint` | 代码检查 |
| `make docker-up` | Docker 启动所有服务 |
| `make docker-down` | Docker 停止所有服务 |

112
guide/module-pattern.md Normal file
View File

@ -0,0 +1,112 @@
# 模块四层模式
每个业务模块内部采用四层结构:**Domain → Application → Infrastructure → Interfaces**。
## 四层结构
```
┌─────────────────────────────────────┐
│ Interfaces │ ← HTTP Handler, 路由注册
├─────────────────────────────────────┤
│ Application │ ← 业务逻辑编排
├─────────────────────────────────────┤
│ Infrastructure │ ← GORM Model, Redis 实现
├─────────────────────────────────────┤
│ Domain │ ← 实体, 值对象, 接口定义
└─────────────────────────────────────┘
```
## 依赖方向
**从上到下依赖,Domain 在最底层,不依赖任何外部包。**
| 层 | 职责 | 可以依赖 | 禁止依赖 |
|---|---|---|---|
| Interfaces | HTTP 入口 | Application, Domain | Infrastructure |
| Application | 业务编排 | Domain | Infrastructure, Interfaces |
| Infrastructure | 数据持久化 | Domain | Application, Interfaces |
| Domain | 业务核心 | 标准库 | 所有外部包 |
## Domain 层
定义业务的核心概念和契约:
```go
// domain/entity.go
type User struct {
ID uint
Email string
Username string
Password string
TenantID string
}
// domain/repository.go — 定义接口,不定义实现
type UserRepository interface {
Create(ctx context.Context, user *User) error
FindByEmail(ctx context.Context, email string) (*User, error)
}
```
Domain 层**零外部依赖**,只使用 Go 标准库。这使得业务逻辑可以独立于框架和数据库进行单元测试。
## Application 层
编排业务流程,调用 Domain 层定义的接口:
```go
// application/service.go
type Service struct {
repo domain.UserRepository
hasher domain.PasswordHasher
jwt domain.TokenManager
}
func (s *Service) Register(ctx context.Context, req domain.RegisterRequest) (*domain.TokenPair, error) {
// 1. 验证邮箱唯一性
// 2. 密码哈希
// 3. 创建用户
// 4. 生成 JWT
// 5. 返回 Token
}
```
Application 层不知道数据是怎么存的(GORM?Redis?),它只依赖 Domain 层定义的接口。
## Infrastructure 层
提供 Domain 层接口的具体实现:
```go
// infrastructure/repository.go
type GormUserRepository struct {
db *gorm.DB
}
func (r *GormUserRepository) Create(ctx context.Context, user *domain.User) error {
// GORM 实现
}
```
Infrastructure 层实现 Domain 层定义的接口,但 Domain 层不知道 Infrastructure 的存在。
## Interfaces 层
HTTP 入口,负责请求解析和响应:
```go
// interfaces/handler.go
func (h *Handler) Register(c *gin.Context) {
var req domain.RegisterRequest
c.ShouldBindJSON(&req)
tokens, err := h.svc.Register(c.Request.Context(), req)
response.Success(c, tokens)
}
```
## 好处
1. **可测试**:Domain 层可以脱离数据库进行单元测试
2. **可替换**:换数据库只需写新的 Infrastructure 实现
3. **边界清晰**:每层职责明确,新人容易理解
4. **防止腐败**:Gin 的 `c *gin.Context` 不会渗透到业务逻辑中

58
guide/multi-tenancy.md Normal file
View File

@ -0,0 +1,58 @@
# 多租户系统
MengStack 内置多租户支持,通过 `X-Tenant-ID` 请求头实现租户隔离。
## 工作原理
```
┌─────────┐ ┌─────────┐
│ Tenant A │──X-Tenant-ID: A──→│ │──WHERE tenant_id='A'──→ DB
├─────────┤ │ MengStack │
│ Tenant B │──X-Tenant-ID: B──→│ Server │──WHERE tenant_id='B'──→ DB
├─────────┤ │ │
│ Tenant C │──X-Tenant-ID: C──→│ │──WHERE tenant_id='C'──→ DB
└─────────┘ └─────────┘
```
每个请求必须携带 `X-Tenant-ID` 头,中间件自动提取并注入到上下文中。
## 使用方式
### 请求示例
```bash
curl http://localhost:2222/api/v1/profile \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-ID: tenant-001"
```
### 中间件处理
认证中间件自动验证:
1. 解析 JWT Token 获取用户信息
2. 验证 `X-Tenant-ID` 头是否存在
3. 将 `user_id` 和 `tenant_id` 注入到 Gin Context
4. 下游 Handler 可直接使用 `c.GetUint("user_id")` 和 `c.GetString("tenant_id")`
### 数据层隔离
Repository 层自动添加租户过滤:
```go
func (r *GormUserRepository) FindByTenant(ctx context.Context, tenantID string) ([]*domain.User, error) {
var users []*domain.User
err := r.db.Where("tenant_id = ?", tenantID).Find(&users).Error
return users, err
}
```
## 租户注册
租户管理是独立模块(M4 里程碑),当前版本通过手动在数据库创建租户记录。
## 安全考虑
- 中间件强制校验 `X-Tenant-ID`,缺失则返回 401
- 数据查询自动过滤,防止跨租户数据泄露
- JWT Token 中包含租户信息,服务端二次验证

View File

@ -0,0 +1,75 @@
---
title: 项目结构说明 | MengStack目录规范详解 - MengStack官方文档
---
# 项目结构
```
mengstack/
├── cmd/
│ └── server/
│ └── main.go # 应用入口,Swagger 注解
├── configs/
│ └── config.yaml # 默认配置文件
├── docs/ # Swagger 自动生成文档
│ ├── docs.go
│ ├── swagger.json
│ └── swagger.yaml
├── internal/
│ ├── kernel/ # 第一层:零业务语义基础组件
│ │ └── response/ # 统一响应格式
│ ├── app/ # 第二层:应用基础设施
│ │ ├── cache/ # Redis 缓存模块
│ │ ├── database/ # PostgreSQL 数据库模块
│ │ ├── health/ # 健康检查
│ │ ├── middleware/ # 全局中间件(CORS、请求ID、日志)
│ │ └── app.go # fx 容器组装
│ ├── config/ # 配置加载(Viper)
│ ├── logger/ # 日志初始化(Zap)
│ └── modules/ # 第三层:业务模块
│ └── auth/ # 认证模块
│ ├── domain/ # 领域层:实体、值对象、接口定义
│ ├── application/ # 应用层:业务逻辑编排
│ ├── infrastructure/ # 基础设施层:GORM/Redis 实现
│ └── interfaces/ # 接口层:HTTP Handler + 路由
├── migrations/ # 数据库迁移文件
├── test/ # 集成测试
├── docker-compose.yml # Docker 编排
├── Makefile # 构建命令
├── AGENTS.md # AI 辅助开发指南
└── go.mod # Go 模块定义
```
## 目录职责
### `cmd/server/`
应用入口,仅负责组装和启动。包含 Swagger 全局注解。
### `internal/kernel/`
**零业务语义**的基础组件。不包含任何业务概念,只提供通用工具:
- `response/` — 统一响应格式 `{code, message, data, trace_id}`
- 后续可扩展:`paginator/`、`errors/` 等
### `internal/app/`
应用基础设施,连接 kernel 和外部世界:
- `database/` — GORM + PostgreSQL 初始化和生命周期
- `cache/` — Redis 客户端初始化和生命周期
- `middleware/` — 请求 ID、请求日志、CORS
- `health/` — 健康检查端点
### `internal/modules/`
业务模块目录,每个模块独立自包含,遵循四层模式。
### `configs/`
默认配置文件,运行时可通过环境变量覆盖。
### `migrations/`
数据库迁移文件,使用 golang-migrate 管理。

View File

@ -0,0 +1,68 @@
---
title: 什么是MengStack | 架构设计与核心特性介绍 - MengStack官方文档
---
# 什么是 MengStack
MengStack 是一个基于 Go 语言的企业级微服务开发框架,采用模块化架构设计,提供多租户、认证授权、配置管理等开箱即用的基础能力。作为面向云原生与 AI 时代的一站式全栈开发底座,MengStack 整合主流开源技术能力,帮助开发者快速搭建企业级后端服务。
## 设计理念
### 为什么选择 Go?
Go 语言以其简洁的语法、高效的并发模型和快速的编译速度,成为构建现代后端服务的理想选择。MengStack 充分利用 Go 的生态和特性:
- **高性能**:Go 的 goroutine 和 channel 提供轻量级并发
- **强类型**:编译期发现错误,减少运行时意外
- **标准库丰富**:net/http、encoding/json 等开箱即用
- **生态成熟**:Gin、GORM、Zap 等优秀第三方库
### 为什么模块化?
传统的分层架构(Controller → Service → Repository)在项目规模增长后容易出现:
- 模块间边界模糊,耦合严重
- 删除一个功能需要改动多个层
- 难以独立测试和替换
MengStack 的[三层隔离架构](/guide/architecture)让每个业务模块都是自包含的,模块间通过明确的接口通信。每个模块遵循[四层模式](/guide/module-pattern)(Domain / Application / Infrastructure / Interfaces),确保依赖方向单一。
## 核心特性
| 特性 | 说明 |
|------|------|
| 三层隔离 | Kernel / App / Modules 层间依赖方向单一,详见[架构设计](/guide/architecture) |
| 四层模块 | Domain / Application / Infrastructure / Interfaces,详见[模块模式](/guide/module-pattern) |
| 多租户 | 基于 Header 的租户隔离,数据自动过滤,详见[多租户系统](/guide/multi-tenancy) |
| JWT 认证 | Access Token + Refresh Token 双令牌机制,详见[认证与授权](/guide/auth) |
| 依赖注入 | uber-go/fx 自动管理组件生命周期,详见[依赖注入](/guide/dependency-injection) |
| API 文档 | swaggo 注解驱动,自动生成 Swagger UI |
| 配置管理 | Viper 分层配置,支持环境变量覆盖,详见[配置管理](/guide/configuration) |
| 结构化日志 | Zap 结构化输出,请求级 trace_id |
| Docker 部署 | 开箱即用的 docker-compose,详见[Docker 部署](/guide/docker-deploy) |
## 适用场景
- **SaaS 平台**:[多租户隔离](/guide/multi-tenancy)开箱即用
- **中后台系统**:[认证](/guide/auth)、权限、审计日志完备
- **微服务集群**:模块化设计便于拆分和独立部署
- **开源项目**:清晰的架构便于社区贡献
## 技术栈
| 组件 | 选型 | 用途 |
|------|------|------|
| Web 框架 | Gin | HTTP 路由和中间件 |
| ORM | GORM | 数据库操作 |
| 数据库 | PostgreSQL | 主数据存储 |
| 缓存 | Redis | 缓存和会话管理 |
| 认证 | golang-jwt | JWT 令牌生成和验证 |
| DI | uber-go/fx | 依赖注入 |
| 配置 | Viper | 配置加载和管理 |
| 日志 | Zap | 结构化日志 |
| API 文档 | swaggo | Swagger 文档生成 |
| 容器化 | Docker Compose | 开发和部署环境 |
## 快速开始
准备好开始使用 MengStack?查看[快速开始指南](/guide/getting-started),5 分钟即可跑通第一个项目。也可以访问[在线演示](/demo)直接体验 API 接口能力。

5
index.md Normal file
View File

@ -0,0 +1,5 @@
---
layout: home
editLink: false
lastUpdated: false
---

2565
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "mengstack-website",
"version": "0.1.0",
"description": "MengStack 官方网站",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
},
"keywords": ["mengstack", "golang", "framework"],
"author": "SoftUnis",
"license": "MIT",
"type": "module",
"devDependencies": {
"vitepress": "^1.6.4",
"vue": "^3.5.43"
}
}

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
public/logo-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
public/logo-hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
public/logo-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
public/og-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

4
public/robots.txt Normal file
View File

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Disallow: /404.html
Sitemap: https://mengstack.softunis.com/sitemap.xml

111
public/sitemap.xml Normal file
View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://mengstack.softunis.com/</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/what-is-mengstack.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/getting-started.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/project-structure.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/architecture.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/module-pattern.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/dependency-injection.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/auth.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/multi-tenancy.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/configuration.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/docker-deploy.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/guide/cicd.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/api/overview.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/api/auth.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/api/system.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/changelog.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/demo.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://mengstack.softunis.com/community.html</loc>
<lastmod>2026-10-02</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
</urlset>