Compare commits

...

3 Commits

Author SHA1 Message Date
sutong 5efcaaae9f feat: add article about architecture evolution from static to front-back separation
- cover image from hello-world
2026-07-23 21:35:37 +08:00
sutong 20180085cc docs: add React Go platform selection 2026-07-19 19:08:14 +08:00
sutong 18b74a9d51 chore: prepare open source archive 2026-07-19 18:49:14 +08:00
20 changed files with 529 additions and 1943 deletions
-3
View File
@@ -1,3 +0,0 @@
# 备案号(必填,否则底部不显示)
NUXT_PUBLIC_ICP=蜀ICP备XXXXXXXXXX号
NUXT_PUBLIC_PS_BEIAN=51015602001952
+2
View File
@@ -11,3 +11,5 @@ Thumbs.db
.env
.env.local
config/site.ts
public/sponsors/qrcode/*
!public/sponsors/qrcode/.gitkeep
+13 -7
View File
@@ -1,8 +1,6 @@
# SuBlog
尼克的小窝 — 个人技术博客
🔗 官方网站:[https://nixus.top/](https://nixus.top/)
基于 Nuxt 3 的个人技术博客模板。
## 为什么做这个
@@ -35,6 +33,9 @@
## 快速开始
```bash
# 创建本地站点配置(该文件不会提交到 Git)
cp config/site.ts.example config/site.ts
# 安装依赖
pnpm install
@@ -60,7 +61,8 @@ pnpm new-post <文章名称>
├── components/
│ ├── layout/ # 导航栏、页脚、主题切换、3D 技术栈头像
│ └── blog/ # 文章卡片、目录、评论
├── config/site.ts # 全站配置(改这一个文件就行)
├── config/site.ts.example # 可提交的安全配置模板
├── config/site.ts # 本地私有站点配置(Git 忽略)
├── content/posts/ # Markdown 文章
├── pages/ # 页面路由
├── public/ # 静态资源
@@ -70,7 +72,11 @@ pnpm new-post <文章名称>
## 配置
所有站点配置集中在 `config/site.ts`站点名称、头像、社交链接、导航菜单、友链、赞助、Giscus 都在这里改,改完自动生效。首页头像使用 `components/layout/AvatarOrbit.client.vue` 渲染,技术栈图标、颜色、动画节奏在该组件内维护
所有个性化信息集中在本地的 `config/site.ts`站点资料、社交链接、项目反馈地址、友链、赞助名单、收款码路径、备案信息和 Giscus 标识。该文件已被 Git 忽略,仓库只提交 `config/site.ts.example`
收款二维码请放在 `public/sponsors/qrcode/` 后,再在 `config/site.ts` 填写路径;该目录同样不会提交到 Git。Giscus、收款码和备案信息留空时,对应页面内容会自动隐藏。
更多归档前检查项见 [开源配置说明](docs/open-source-configuration.md)。首页头像使用 `components/layout/AvatarOrbit.client.vue` 渲染,技术栈图标、颜色、动画节奏在该组件内维护。
## 部署
@@ -80,6 +86,6 @@ pnpm generate
产物在 `.output/public` 目录,直接丢到 Nginx、Vercel、Netlify 或者 1Panel 都行。
## 开源计划
## 开源归档
目前代码还不够整洁,等整理好了会考虑开源。如果你对这个项目感兴趣,欢迎先访问 [官方网站](https://nixus.top/) 看看效果
提交前请确认 `config/site.ts``.env*``public/sponsors/qrcode/` 没有被强制加入 Git;仓库内不应包含个人账号、备案信息或收款码
+12 -2
View File
@@ -3,6 +3,14 @@ import { siteConfig } from '~/config/site'
const colorMode = useColorMode()
const container = ref<HTMLElement>()
const isEnabled = computed(() =>
Boolean(
siteConfig.giscus.repo
&& siteConfig.giscus.repoId
&& siteConfig.giscus.category
&& siteConfig.giscus.categoryId
)
)
function loadGiscus() {
if (!container.value) return
@@ -29,10 +37,12 @@ function loadGiscus() {
}
onMounted(() => {
loadGiscus()
if (isEnabled.value) loadGiscus()
})
watch(() => colorMode.value, () => {
if (!isEnabled.value) return
const frame = document.querySelector<HTMLIFrameElement>('iframe.giscus-frame')
if (frame?.contentWindow) {
frame.contentWindow.postMessage(
@@ -44,7 +54,7 @@ watch(() => colorMode.value, () => {
</script>
<template>
<div>
<div v-if="isEnabled">
<h2 class="text-xl font-semibold mb-4">评论</h2>
<div ref="container" />
</div>
+6 -8
View File
@@ -1,25 +1,23 @@
<script setup lang="ts">
import { siteConfig } from '~/config/site'
const config = useRuntimeConfig()
const icp = config.public.icp
const psBeian = config.public.psBeian
const { filing } = siteConfig
</script>
<template>
<footer class="py-6 text-center text-sm border-t" style="color: var(--color-text-secondary); border-color: var(--color-border);">
<p>
&copy; {{ new Date().getFullYear() }} {{ siteConfig.siteName }}
<template v-if="icp">
<template v-if="filing.icp">
&nbsp;·&nbsp;
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer" class="hover:opacity-70 whitespace-nowrap" style="border-bottom: 1px solid var(--color-text-secondary); padding-bottom: 1px;">{{ icp }}</a>
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer" class="hover:opacity-70 whitespace-nowrap" style="border-bottom: 1px solid var(--color-text-secondary); padding-bottom: 1px;">{{ filing.icp }}</a>
</template>
<template v-if="psBeian">
<template v-if="filing.publicSecurity.code">
&nbsp;·&nbsp;
<a :href="`https://beian.mps.gov.cn/#/query/webSearch?code=${psBeian}`" rel="noreferrer" target="_blank" class="hover:opacity-70 whitespace-nowrap" style="border-bottom: 1px solid var(--color-text-secondary); padding-bottom: 1px;">
<a :href="`https://beian.mps.gov.cn/#/query/webSearch?code=${filing.publicSecurity.code}`" rel="noreferrer" target="_blank" class="hover:opacity-70 whitespace-nowrap" style="border-bottom: 1px solid var(--color-text-secondary); padding-bottom: 1px;">
<span class="inline-flex items-center gap-0.5">
<img src="@/assets/备案图标.png" alt="" class="w-3 h-3" />
<span>川公网安备{{ psBeian }}</span>
<span>{{ filing.publicSecurity.label }}{{ filing.publicSecurity.code }}</span>
</span>
</a>
</template>
+18 -2
View File
@@ -25,6 +25,11 @@ export const siteConfig = {
{ name: 'GitHub', icon: 'simple-icons:github', url: 'https://github.com/yourname', color: '#333333' },
],
// 项目仓库(首页公告中的反馈入口)
repository: {
issuesUrl: 'https://github.com/yourname/your-repository/issues',
},
// 导航菜单
navLinks: [
{ label: '博客', icon: 'mdi:post-outline', href: '/posts' },
@@ -39,15 +44,26 @@ export const siteConfig = {
// 赞助
sponsors: {
qrcode: {
wechat: '/sponsors/qrcode/wechat.png',
// 收款码放在 public/sponsors/qrcode/,该目录默认不会提交到 Git
wechat: '',
},
list: [] as { name: string; date: string; amount: string; avatar?: string; text?: string }[],
},
// 备案信息。留空时页脚不显示。
filing: {
icp: '',
publicSecurity: {
label: '公网安备',
code: '',
},
},
// Giscus 评论配置
giscus: {
repo: 'owner/repo',
repoId: '',
category: 'Announcements',
categoryId: '',
},}
},
}
+4 -4
View File
@@ -1,5 +1,5 @@
---
title: Hello World - 欢迎来到尼克的小窝
title: Hello World - 欢迎使用 SuBlog
published: 2026-05-31T08:00:00
updated: 2026-05-31
description: 博客上线啦!这篇文章介绍本站的技术架构、功能特性和搭建过程中的一些思考。
@@ -14,7 +14,7 @@ difficulty: 入门
## 博客上线啦!
Hello World!欢迎来到**尼克的小窝**,这是的第一篇博客文章。
Hello World!欢迎使用 **SuBlog**,这是站点的第一篇示例文章。
经过一段时间的折腾,这个基于 Nuxt 3 的个人博客终于上线了。在这里记录一下这个项目的技术选型和功能特性。
@@ -53,8 +53,8 @@ Hello World!欢迎来到**尼克的小窝**,这是我的第一篇博客文
```typescript
// config/site.ts
export const siteConfig = {
siteName: '尼克的小窝',
url: 'https://nixus.top/',
siteName: '我的博客',
url: 'https://example.com/',
avatar: 'https://q2.qlogo.cn/headimg_dl?dst_uin=xxx&spec=100',
// ... 改这一个文件,全站生效
}
+4 -4
View File
@@ -10,7 +10,7 @@ difficulty: 入门
## jira_cli 是什么
[jira_cli](https://github.com/wishesl/jira_cli) 是一个面向企业 JiraServer / Data Center)的本地命令行工具,Go 语言编写,MIT 协议开源。它同时提供 **Cobra CLI****MCP stdio server** 两种入口,既能当日常命令行用,又能直接接入 AI 编程工具。
[jira_cli](https://github.com/yourname/jira_cli) 是一个面向企业 JiraServer / Data Center)的本地命令行工具,Go 语言编写,MIT 协议开源。它同时提供 **Cobra CLI****MCP stdio server** 两种入口,既能当日常命令行用,又能直接接入 AI 编程工具。
```bash
# CLI 日常使用
@@ -39,10 +39,10 @@ jira_cli mcp --user=work
```bash
# 从源码安装(要求 Go 1.26.3+)
go install github.com/wishesl/jira_cli@latest
go install github.com/yourname/jira_cli@latest
# 或下载预编译二进制
# https://github.com/wishesl/jira_cli/releases
# https://github.com/yourname/jira_cli/releases
```
Windows 用户下载 zip 包解压后双击 `install.bat` 即可全局安装,自动加入 PATH。
@@ -258,4 +258,4 @@ main.go
- 需要管理多个 Jira 实例和多套账号
- Windows 用户想要无摩擦的 CLI 体验
GitHub[github.com/wishesl/jira_cli](https://github.com/wishesl/jira_cli) | MIT 协议 | Go 1.26.3+
GitHub[github.com/yourname/jira_cli](https://github.com/yourname/jira_cli) | MIT 协议 | Go 1.26.3+
@@ -5,7 +5,7 @@ updated: 2026-05-31
image: /posts/nuxt-tailwind-underline-fix/img/cover.png
description: 记录 Nuxt 3 配合 Tailwind CSS 时,客户端导航导致链接文字出现下划线闪烁的问题及解决方案。
tags: ['Nuxt', 'Tailwind CSS', 'CSS', '踩坑']
author: Nixus
author: SuBlog
series: Nuxt 博客开发
seriesOrder: 3
difficulty: 进阶
@@ -76,4 +76,4 @@ a:not(.prose a) {
这是 Nuxt 3 + Tailwind CSS 的一个已知问题,本质上是 CSS 加载时序导致的 FOUC。`!important` 虽然不够优雅,但在这个场景下是务实的解决方案。
如果你有更好的方案,欢迎在 [Issues](https://github.com/wishesl/SuBlog/issues) 提出!
如果你有更好的方案,欢迎在项目仓库的 Issues 提出!
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,46 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630" style="background-color:#ffffff">
<defs>
<linearGradient id="accent" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#e94560"/>
<stop offset="100%" stop-color="#059669"/>
</linearGradient>
</defs>
<!-- 白底 -->
<rect width="1200" height="630" fill="#ffffff"/>
<!-- 极简装饰:一条渐变线 -->
<rect x="0" y="80" width="1200" height="2" fill="url(#accent)" opacity="0.3"/>
<!-- 左侧垂直线 -->
<rect x="80" y="0" width="2" height="630" fill="#e94560" opacity="0.08"/>
<!-- 标题 -->
<text x="120" y="200" font-family="system-ui,-apple-system,sans-serif" font-size="48" font-weight="700" fill="#0f172a" letter-spacing="2">
尼克的小窝
</text>
<text x="120" y="260" font-family="system-ui,-apple-system,sans-serif" font-size="48" font-weight="700" fill="#0f172a" letter-spacing="2">
架构变迁
</text>
<!-- 副标题 -->
<text x="120" y="320" font-family="system-ui,-apple-system,sans-serif" font-size="18" fill="#94a3b8" letter-spacing="6">
从纯静态到前后端分离
</text>
<!-- 小标签 -->
<rect x="120" y="365" width="180" height="32" rx="16" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<text x="210" y="386" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#64748b">Nuxt → React + Go</text>
<!-- 右侧极简装饰:两个小方块 -->
<rect x="980" y="180" width="140" height="140" rx="8" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="1050" y="238" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" font-weight="600" fill="#e94560">静态</text>
<text x="1050" y="260" text-anchor="middle" font-family="system-ui,sans-serif" font-size="11" fill="#94a3b8">Monolith</text>
<rect x="980" y="340" width="140" height="140" rx="8" fill="#ecfdf5" stroke="#6ee7b7" stroke-width="1"/>
<text x="1050" y="398" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" font-weight="600" fill="#059669">分离</text>
<text x="1050" y="420" text-anchor="middle" font-family="system-ui,sans-serif" font-size="11" fill="#94a3b8">Front-end + Back-end</text>
<!-- 两个方块之间的箭头 -->
<text x="1050" y="325" text-anchor="middle" font-family="system-ui,sans-serif" font-size="16" fill="#cbd5e1"></text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+159
View File
@@ -0,0 +1,159 @@
---
title: 尼克的小窝 架构变迁:从纯静态到前后端分离
published: 2026-07-23T12:00:00
updated: 2026-07-23
description: 记录尼克的小窝从 Nuxt 3 纯静态博客演进到 React + Go 前后端分离架构的全过程,包括架构设计的思考、技术选型的变化,以及新增的账号注册功能。
image: /posts/sublog-evolution/img/cover.jpg
tags: ['架构', 'React', 'Next.js', 'Go']
draft: false
series: 尼克的小窝 开发记录
seriesOrder: 1
difficulty: 入门
---
## 📝 写在前面
SuBlog 最初只是一个简单的技术博客——Nuxt 3 静态生成,Markdown 写文章,部署到静态托管完事。但随着功能需求的增长,纯静态的架构开始显露出局限性。
这篇文章记录从 **纯静态****前后端分离** 的演进过程,带你了解每一步背后的思考。
## 📦 第一代:纯静态(Nuxt 3 + @nuxt/content
### 🏗️ 架构图
```text
┌─────────────────────────────────────┐
│ 静态托管 (Nginx/Vercel) │
│ │
│ ┌───────────────────────────────┐ │
│ │ Nuxt 3 SSG 产物 │ │
│ │ │ │
│ │ pages/ + content/ + public/ │ │
│ └───────────────────────────────┘ │
│ │
│ Giscus (GitHub Discussions 评论) │
└─────────────────────────────────────┘
```
### 🔧 做了什么
- **Nuxt 3** + **TypeScript** + **Tailwind CSS v4** 搭建前端
- **@nuxt/content** 管理 Markdown 文章,编译时生成静态页面
- **Giscus** 嵌入评论,依赖 GitHub Discussions
- 部署通过 `nuxt generate` 输出 `.output/public`,丢到 Nginx 或 Vercel
### ✅ 优点
- 部署极简,一个 `nuxt generate` 搞定
- 加载飞快,全是 HTML,没有服务端开销
- 无需后端,零运维成本
### ❌ 局限
随着时间的推移,纯静态的问题越来越明显:
| 问题 | 具体表现 |
|------|----------|
| 🐢 **内容更新慢** | 改一篇文章要重新构建、重新部署,不能实时发布 |
| 👤 **没有用户体系** | 无法区分读者和管理员,所有内容对所有人一致可见 |
| 🔒 **评论受限** | Giscus 需要 GitHub 账号才能评论,门槛高 |
| 🚫 **无法做动态功能** | 点赞、收藏、阅读统计、个性化推荐……统统做不了 |
| 🔗 **前端锁死** | 被 Nuxt/Vue 生态绑定,想换技术栈得重写 |
## ⚡ 第二代:前后端分离(React + Go)
### 🏗️ 架构图
```text
┌───────────────────────┐ ┌────────────────────┐
│ 静态托管 (Vercel) │ │ Go 服务端 │
│ │ │ │
│ ┌─────────────────┐ │ │ ┌──────────────┐ │
│ │ sublog-react │ │ API │ │ HTTP API │ │
│ │ (Next.js SSG) │──┼───────┼─>│ │ │
│ │ │ │ │ │ 文章 / 用户 │ │
│ │ 首页 / 文章 / │ │ │ └──────────────┘ │
│ │ 用户中心 │ │ │ │
│ └─────────────────┘ │ │ ┌──────────────┐ │
│ │ │ │ 数据库 │ │
└───────────────────────┘ │ └──────────────┘ │
└────────────────────┘
```
### 🖥️ 前端:sublog-react
选用 **Next.js**App Router + **React 19**,理由是:
- 🎨 **React 生态**——组件库成熟,开发效率高
- 📐 **App Router**——支持布局嵌套,页面组织清晰
- 📦 **静态导出**——仍可部署到静态托管
- ⚙️ **运行时配置**——API 地址可动态注入,切换环境无需重新构建
前端不再直接读取 Markdown 文件,而是通过 HTTP API 获取文章数据。静态导出 + 客户端数据获取的组合,既保留了首屏加载速度,又实现了内容的动态更新。
### ⚙️ 后端:sublog-s
后端使用 **Go** + **Gin**,按业务逻辑分层组织代码。
核心技术选型:
| 技术 | 用途 |
|------|------|
| 🚀 **Gin** | HTTP 框架 |
| 🗄️ **GORM** | ORM,数据库操作 |
| 🔐 **JWT** | 用户认证 |
## 📊 前后对比
| 维度 | 第一代(纯静态) | 第二代(前后端分离) |
|------|-----------------|---------------------|
| 🎨 **前端框架** | Nuxt 3 (Vue) | Next.js (React 19) |
| 🖥️ **后端** | 无 | Go + Gin |
| 📄 **内容来源** | Markdown 文件 | API 数据库 |
| 🚀 **发布方式** | 全量重新构建 | API 实时发布 |
| 👤 **用户系统** | ❌ 无 | ✅ 注册/登录 |
| 💬 **评论** | Giscus(需 GitHub | 自有评论系统(规划中) |
| 📦 **部署** | 1 个静态目录 | 前端静态 + 后端二进制 |
| ⚡ **更新体验** | 改文章 → 构建 → 部署 | 改文章 → 立即生效 |
## 🎉 新功能:注册账号
伴随着架构升级,**用户账号系统**也上线了。
### 📋 目前支持
现在你可以在尼克的小窝 **注册一个账号**
```
1. ✏️ 填写用户名、邮箱、密码
2. 📧 查收激活邮件
3. 🔗 点击链接激活账号
4. 🚪 登录后查看和管理个人信息
```
## 🗓️ 后续计划
正在开发或规划中的功能:
- [x] 用户注册/登录(已上线 🎉)
- [x] 个人资料编辑(已上线 🎉)
- [ ] 💬 站内评论系统
- [ ] 🔖 文章收藏
- [ ] 📚 阅读历史
- [ ] 👍 文章点赞
- [ ] 🔔 关注通知
- [ ] 🎨 个人首页定制
## 💡 结语
从 Nuxt 3 纯静态到 React + Go 前后端分离,SuBlog 的架构变迁本质上是一个**功能驱动**的过程。静态博客有它的位置——写写文章、展示内容完全够用。但当你想做的不仅仅是"展示"时,后端的能力就不可或缺了。
这次重构最大的感触是:**架构选型不是越复杂越好,而是刚好够用最好**。纯静态阶段用 Nuxt 3 一点问题没有,直到功能需求超出了静态的边界,才自然推动到前后端分离。
如果你也在纠结"要不要上后端",不妨先问问自己:**你要做的是"展示内容"还是"提供服务"**
前者,静态就够了;后者,来吧,后端欢迎你。😄
---
**🎉 目前注册功能已经可以用了,后续更多好玩的功能正在路上。部分功能可能需要登录才能体验,快注册一个账号吧!**
+28
View File
@@ -0,0 +1,28 @@
# 开源配置说明
## 目标
本项目将站点身份和可关联个人的信息从可提交代码中分离,避免公开仓库包含个人域名、社交账号、备案号、评论仓库标识、赞助记录及收款码。
## 配置边界
`config/site.ts` 是唯一的本地私有配置,已被 `.gitignore` 忽略。首次使用时复制 `config/site.ts.example` 创建该文件。
| 内容 | 保存位置 | 是否提交 |
| --- | --- | --- |
| 站点名称、域名、头像、社交账号、友链 | `config/site.ts` | 否 |
| 项目反馈地址、Giscus 标识、备案号、赞助记录 | `config/site.ts` | 否 |
| 收款二维码 | `public/sponsors/qrcode/` | 否 |
| 可复用的字段结构与默认值 | `config/site.ts.example` | 是 |
`siteConfig.giscus``siteConfig.sponsors.qrcode.wechat``siteConfig.filing` 留空时,评论区、收款入口和备案信息不会渲染。项目反馈链接也由 `siteConfig.repository.issuesUrl` 统一提供。
## 归档流程
1. 复制并填写本地配置:`cp config/site.ts.example config/site.ts`
2. 将收款二维码放入 `public/sponsors/qrcode/`,并在本地配置中填写资源路径。
3. 执行 `pnpm generate`,确认未配置项不会显示且构建成功。
4. 使用 `git status --ignored` 检查 `config/site.ts` 和收款码目录仍处于忽略状态。
5. 提交前搜索个人域名、账号、备案号和二维码文件,确认没有被意外加入版本控制。
文章中的公开项目链接和技术引用属于内容资产,不会自动改写;如需匿名发布,请在提交前单独审阅 `content/posts/`
+230
View File
@@ -0,0 +1,230 @@
# React + Go 内容平台技术选型
> 状态:提案
>
> 更新时间:2026-07-19
>
> 适用范围:SuBlog 从 Markdown 静态博客演进为可运营的内容系统。
## 1. 目标与边界
### 目标
- 提供公开的文章阅读、搜索、归档、标签、系列、SEO 和评论体验。
- 提供受权限控制的运营后台,支持文章草稿、发布、定时发布、修订、媒体上传和用户管理。
- 保留当前博客的视觉识别:技术图标环绕头像、主题切换动画、轻量内容阅读体验。
- 将业务规则、鉴权和数据一致性收敛到 Go 服务,前端不持有业务写入逻辑。
### 不在第一阶段解决
- 多租户、计费、复杂工作流和实时协同编辑。
- 微服务、消息队列和 gRPC。
- 直接把 Nuxt/Vue 组件转换为 React 组件。
首版采用模块化单体:一个 Next.js 应用、一个 Go API、一个 PostgreSQL 实例和一个 S3 兼容对象存储。先保证内容链路可靠,再按真实运营需求拆分。
## 2. 结论
采用以下组合:
| 层级 | 选型 | 决策 |
| --- | --- | --- |
| Web 前端 | Next.js App Router + React + TypeScript | 采用 |
| 样式与基础组件 | Tailwind CSS + shadcn/ui | 采用 |
| 视觉动效 | Magic UI | 采用,仅用于体验增强 |
| 后台服务 | Go + Gin | 采用 |
| 数据库 | PostgreSQL | 采用 |
| 媒体存储 | S3 协议对象存储,开发环境使用 MinIO | 采用 |
| 内容编辑器 | 首版 Markdown;富文本需求明确后引入 Tiptap | 延后 |
| API 契约 | OpenAPI 3.1 + TypeScript 客户端生成 | 采用 |
| 服务端状态 | TanStack Query,仅用于后台的客户端交互 | 采用 |
Next.js 的 App Router 默认使用服务端组件,适合将公开文章页在服务端从 Go API 获取数据后输出 HTML,同时把编辑器、筛选和管理表单限制在客户端组件中。[Next.js Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components)
shadcn/ui 的组件代码进入项目而非被黑盒依赖包封装,适合作为长期维护的后台设计系统;Magic UI 则只用于图标云、主题切换等差异化效果,不作为后台基础组件。[shadcn/ui Introduction](https://ui.shadcn.com/docs) [Magic UI Icon Cloud](https://magicui.design/docs/components/icon-cloud)
## 3. 总体架构
```mermaid
flowchart LR
Visitor["读者"] --> Web["Next.js Web\n公开站点与运营后台"]
Operator["运营人员"] --> Web
Web -->|"SSR / HTTPS API"| API["Go API\n鉴权与业务规则"]
API --> DB[("PostgreSQL")]
API --> Storage[("S3 / MinIO\n媒体资源")]
API --> Events["内容发布事件"]
Events --> Revalidate["Next.js 缓存失效端点"]
Revalidate --> Web
```
生产环境使用同一主域名,反向代理按路径分流:
```text
https://example.com/ -> Next.js
https://example.com/admin/ -> Next.js
https://example.com/api/v1/ -> Go API
```
这样浏览器访问 API 不需要 CORS,Go 可使用 `HttpOnly``Secure``SameSite=Lax` 的会话 Cookie。Next.js 仅负责渲染、路由和 API 调用适配;文章发布、权限判断、文件签名和数据写入只能由 Go 服务完成。
## 4. React 前端设计
### 4.1 框架职责
| 区域 | 渲染方式 | 数据方式 | 说明 |
| --- | --- | --- | --- |
| 首页、文章页、归档、标签、系列 | Server Components 优先 | Next 服务端请求 Go API | SEO、首屏和分享信息优先 |
| 搜索、评论、主题切换、头像图标云 | Client Components | 按需调用 API | 仅加载必需的浏览器逻辑 |
| `/admin` | Client Components 为主 | TanStack Query 调用 Go API | 表格、筛选、编辑和乐观更新 |
| 登录页 | Server + Client Components | Go 会话 API | 不在浏览器保存访问令牌 |
TanStack Query 管理后台的远程数据缓存、重新获取和写入后的失效;公开页面不为此引入客户端状态层,直接使用 Next 的服务端数据获取和缓存策略。[TanStack Query Queries](https://tanstack.com/query/latest/docs/framework/react/guides/queries)
### 4.2 UI 与视觉系统
以 Tailwind CSS 的语义变量作为唯一主题来源,先定义 `background``foreground``card``muted``border``primary` 等 token,再由 shadcn/ui 和业务组件共同消费。不要将页面色值散落在组件中。
Magic UI 的定位是“效果层”,使用范围限定为首页品牌区、空状态和少量内容提示;文章正文、后台表格、表单和弹窗保持克制、信息密度优先。
#### 现有视觉资产迁移
| 现有能力 | 当前实现 | React 迁移方案 | 可复用范围 |
| --- | --- | --- | --- |
| 技术图标环绕头像 | `vue-icon-cloud` + CSS | Magic UI `IconCloud` | 图标 slug、图片 URL、尺寸与视觉参数可复用;Vue 组件代码不可复用 |
| 主题切换 | Vue `useColorMode` + View Transitions API | `next-themes` 管理主题状态,Magic UI `AnimatedThemeToggler` 负责动画 | 动画原点、时长、`prefers-reduced-motion` 降级和 CSS token 可复用 |
| 深浅色样式 | CSS 变量 | `globals.css` 的 Tailwind/shadcn token | 色彩语义和视觉值可复用,选择器需重写 |
现有项目并没有直接使用 Magic UI:头像云是 Vue 专用组件,主题切换是自行封装的 View Transitions API。React 端可直接采用 Magic UI 对应组件的实现路径,官方提供了 `IconCloud` 和基于 View Transitions API 的主题切换组件。[Magic UI Theme Toggler](https://magicui.design/docs/components/animated-theme-toggler)
### 4.3 内容编辑策略
首版以 Markdown 为正文规范,延续现有文章资产并降低导入风险:
- 后台编辑器保存 `body_markdown`,预览由与公开页面一致的渲染器生成。
- 文章的标题、摘要、标签、发布时间、封面和状态使用结构化字段保存。
- 每次保存创建修订快照,发布操作产生不可变的发布记录。
当存在非技术作者、复杂嵌入内容或协同编辑的明确需求时,再评估 Tiptap。Tiptap 提供 React 集成和可组合扩展,但其 Markdown 能力仍处于 beta 文档范围,因此不将它作为首版 Markdown 数据规范的前提。[Tiptap Editor](https://tiptap.dev/docs/editor/getting-started/overview)
## 5. Go 后端设计
### 5.1 模块边界
```text
apps/api/
├── cmd/api/ # 程序入口、依赖装配、优雅退出
├── internal/
│ ├── identity/ # 用户、角色、会话、登录限制
│ ├── content/ # 文章、标签、系列、草稿、修订、发布
│ ├── media/ # 上传策略、文件元数据、对象存储
│ ├── site/ # 站点设置、导航、主题等运营配置
│ ├── audit/ # 管理操作审计日志
│ ├── platform/ # 数据库、缓存、对象存储、时钟、ID
│ └── transport/http/ # Gin 路由、鉴权中间件、请求/响应 DTO
├── migrations/ # 仅追加的数据库迁移
└── openapi/ # OpenAPI 源文件与生成配置
```
业务模块拥有自己的服务、仓储接口和测试;HTTP handler 只做参数绑定、鉴权调用和响应映射。禁止 handler 直接拼接 SQL,禁止前端绕过 Go 直接访问数据库或对象存储。
### 5.2 首版数据模型
| 实体 | 关键字段 | 说明 |
| --- | --- | --- |
| `users` | `id``email``status` | 运营用户身份 |
| `roles` / `user_roles` | `role``permission` | RBAC,首版至少管理员、编辑、只读 |
| `sessions` | `token_hash``expires_at``user_id` | 服务端会话,不将访问令牌写入 localStorage |
| `posts` | `slug``title``summary``body_markdown``status``published_at` | 文章当前版本 |
| `post_revisions` | `post_id``content``created_by` | 草稿与发布前可回滚快照 |
| `tags` / `post_tags` | `slug``name` | 标签关系 |
| `media` | `object_key``mime_type``size``created_by` | 文件元数据,文件本体在对象存储 |
| `audit_logs` | `actor_id``action``target``metadata` | 发布、删除、授权等关键操作记录 |
文章状态固定为 `draft``scheduled``published``archived`。状态流转在 `content` 模块校验,数据库事务内同时更新文章、修订和审计日志。
### 5.3 API 与契约
- 使用 `/api/v1/public/*` 提供公开只读接口,例如文章详情、归档、标签、站点配置。
- 使用 `/api/v1/admin/*` 提供后台接口,并按角色授权。
- 以 OpenAPI 3.1 为唯一接口契约,从契约生成 React TypeScript 客户端;Go handler 的集成测试验证响应符合契约。
- 发布、下线、删除、媒体上传等写入接口要求会话、CSRF 防护、审计日志和幂等键。
- 发布成功后,Go 使用签名事件调用 Next.js 缓存失效端点;不得依赖固定时间等待缓存过期。
## 6. 仓库结构
迁移完成后采用 pnpm workspace 与 Go 模块并存的单仓库:
```text
sublog/
├── apps/
│ ├── web/ # Next.js
│ │ ├── app/
│ │ │ ├── (public)/
│ │ │ └── admin/
│ │ ├── components/
│ │ │ ├── ui/ # shadcn/ui 受控源码
│ │ │ ├── effects/ # Magic UI 及品牌动效
│ │ │ └── content/
│ │ ├── lib/api/ # 生成的 API client 与查询封装
│ │ └── styles/
│ └── api/ # Go 服务
├── contracts/openapi/ # API 契约
├── deploy/ # Compose、反向代理、生产部署说明
├── scripts/migrate-content/ # 现有 Markdown 导入工具
└── docs/
```
现有 `content/posts/<slug>/index.md` 仅作为迁移输入和归档备份;迁移完成后,运行时内容的唯一来源是 Go API + PostgreSQL。
## 7. 分阶段迁移
### 阶段 0:技术验证
完成一条最小闭环:Go 提供一篇文章的公开读取接口,Next.js 服务端渲染文章详情,Magic UI 图标云和主题切换在 React 中正确运行。
验收:SEO 元信息可见、首次加载无主题闪烁、禁用动效偏好下主题可正常切换。
### 阶段 1:公开内容迁移
1. 建立 PostgreSQL 迁移、`posts``tags``post_revisions` 和公开 API。
2. 编写一次性导入脚本,读取现有 frontmatter 和 Markdown 正文。
3. 重建首页、文章详情、列表、归档、标签和系列页面。
4. 保留原 URL,缺失页面使用 301 重定向,逐页比对标题、描述、封面和正文。
验收:全部已发布文章可访问;迁移前后的 URL、SEO 字段和图片链接无回归。
### 阶段 2:运营后台
1. 建立登录、会话、RBAC 与审计日志。
2. 实现文章列表、草稿编辑、预览、发布、定时发布和修订回滚。
3. 接入对象存储的预签名上传与媒体选择器。
4. 接入内容发布事件与 Next.js 缓存失效。
验收:编辑只能操作授权资源;发布后公开页与 sitemap 在可预期时间内更新;审计记录可追踪。
### 阶段 3:运营能力完善
- 后台搜索、内容质量检查、死链检测与站点设置。
- 评论审核、内容推荐和访问统计。
- 基于实际作者需求决定是否引入 Tiptap、协同编辑或内容审批流。
## 8. 关键风险与验证
| 风险 | 影响 | MVP 验证 |
| --- | --- | --- |
| Markdown 导入丢失格式 | 历史内容不可逆受损 | 保留原文件,导入后逐篇 hash 与人工抽检 |
| Next 缓存未失效 | 发布后读者仍看到旧内容 | 发布接口后调用签名失效端点,编写端到端测试 |
| 前后端职责混杂 | 规则分散且难以审计 | 代码审查禁止 Next Route Handler 承载业务写入 |
| 富文本过早引入 | 数据格式和编辑器维护成本暴涨 | 首版只验证 Markdown 编辑与预览 |
| 动效影响后台可用性 | 性能与无障碍下降 | Magic UI 仅在品牌区域使用,尊重 `prefers-reduced-motion` |
## 9. 测试与上线标准
- Go:领域服务单元测试、HTTP + PostgreSQL 集成测试、迁移升级测试、权限矩阵测试。
- Web:组件测试覆盖主题与编辑表单;Playwright 覆盖登录、草稿、发布、撤回、文章访问和上传。
- 契约:CI 生成 TypeScript API client,并检测 OpenAPI 变更。
- 发布:健康检查、数据库备份、对象存储生命周期规则、错误监控和审计日志保留策略齐备后才接入真实运营数据。
## 10. 最终决策
React + Go 适合本项目向内容平台演进,但必须以“Next.js 负责体验与渲染、Go 负责全部业务与数据”为边界。优先迁移公开阅读链路,再实现后台闭环;不要在第一阶段同时重写全部页面、接入富文本和实现复杂工作流。
+2 -2
View File
@@ -205,8 +205,8 @@ const secondaryNavLinks = computed(() =>
<p class="announcement-text text-sm leading-relaxed">
<strong>欢迎来到{{ siteConfig.siteName }}~ 用代码创造快乐用热爱点亮生活 (゚ヮ゚)</strong>
</p>
<p class="announcement-text mt-1 text-sm leading-relaxed">
遇到问题的话可以去 <a href="https://github.com/wishesl/SuBlog/issues" target="_blank" rel="noopener noreferrer">Issues</a> 提反馈哦会尽快修的
<p v-if="siteConfig.repository.issuesUrl" class="announcement-text mt-1 text-sm leading-relaxed">
遇到问题的话可以去 <a :href="siteConfig.repository.issuesUrl" target="_blank" rel="noopener noreferrer">Issues</a> 提反馈哦会尽快修的
</p>
</div>
</div>
+2 -1
View File
@@ -7,6 +7,7 @@ useSeoMeta({
})
const route = useRoute()
const hasWechatQrcode = computed(() => Boolean(siteConfig.sponsors.qrcode.wechat))
const ratio = computed(() => {
const v = Number(route.query.ratio)
@@ -30,7 +31,7 @@ function formatAmount(amount: string): string {
</div>
<!-- 微信支付 -->
<div class="mb-8 rounded-xl border p-6 text-center anim-fade-in-up anim-delay-1" style="border-color: var(--color-border); background-color: var(--color-bg-card);">
<div v-if="hasWechatQrcode" class="mb-8 rounded-xl border p-6 text-center anim-fade-in-up anim-delay-1" style="border-color: var(--color-border); background-color: var(--color-bg-card);">
<h2 class="mb-2 text-xl font-semibold">微信支付</h2>
<p class="mb-4 text-sm text-muted-foreground">扫描下方二维码向我赞助</p>
<div class="flex justify-center py-4">
+1
View File
@@ -0,0 +1 @@
-867
View File
@@ -1,867 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="540" height="540" viewBox="0 0 540 540" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="540" height="540" fill="#ffffff"/>
<defs>
<rect id="p" width="12" height="12" />
</defs>
<g>
<use x="24" y="24" xlink:href="#p" fill="#000000" />
<use x="36" y="24" xlink:href="#p" fill="#000000" />
<use x="48" y="24" xlink:href="#p" fill="#000000" />
<use x="60" y="24" xlink:href="#p" fill="#000000" />
<use x="72" y="24" xlink:href="#p" fill="#000000" />
<use x="84" y="24" xlink:href="#p" fill="#000000" />
<use x="96" y="24" xlink:href="#p" fill="#000000" />
<use x="120" y="24" xlink:href="#p" fill="#000000" />
<use x="156" y="24" xlink:href="#p" fill="#000000" />
<use x="180" y="24" xlink:href="#p" fill="#000000" />
<use x="204" y="24" xlink:href="#p" fill="#000000" />
<use x="216" y="24" xlink:href="#p" fill="#000000" />
<use x="228" y="24" xlink:href="#p" fill="#000000" />
<use x="252" y="24" xlink:href="#p" fill="#000000" />
<use x="276" y="24" xlink:href="#p" fill="#000000" />
<use x="288" y="24" xlink:href="#p" fill="#000000" />
<use x="300" y="24" xlink:href="#p" fill="#000000" />
<use x="336" y="24" xlink:href="#p" fill="#000000" />
<use x="360" y="24" xlink:href="#p" fill="#000000" />
<use x="372" y="24" xlink:href="#p" fill="#000000" />
<use x="384" y="24" xlink:href="#p" fill="#000000" />
<use x="396" y="24" xlink:href="#p" fill="#000000" />
<use x="432" y="24" xlink:href="#p" fill="#000000" />
<use x="444" y="24" xlink:href="#p" fill="#000000" />
<use x="456" y="24" xlink:href="#p" fill="#000000" />
<use x="468" y="24" xlink:href="#p" fill="#000000" />
<use x="480" y="24" xlink:href="#p" fill="#000000" />
<use x="492" y="24" xlink:href="#p" fill="#000000" />
<use x="504" y="24" xlink:href="#p" fill="#000000" />
<use x="24" y="36" xlink:href="#p" fill="#000000" />
<use x="96" y="36" xlink:href="#p" fill="#000000" />
<use x="132" y="36" xlink:href="#p" fill="#000000" />
<use x="192" y="36" xlink:href="#p" fill="#000000" />
<use x="216" y="36" xlink:href="#p" fill="#000000" />
<use x="240" y="36" xlink:href="#p" fill="#000000" />
<use x="252" y="36" xlink:href="#p" fill="#000000" />
<use x="300" y="36" xlink:href="#p" fill="#000000" />
<use x="312" y="36" xlink:href="#p" fill="#000000" />
<use x="324" y="36" xlink:href="#p" fill="#000000" />
<use x="348" y="36" xlink:href="#p" fill="#000000" />
<use x="360" y="36" xlink:href="#p" fill="#000000" />
<use x="384" y="36" xlink:href="#p" fill="#000000" />
<use x="432" y="36" xlink:href="#p" fill="#000000" />
<use x="504" y="36" xlink:href="#p" fill="#000000" />
<use x="24" y="48" xlink:href="#p" fill="#000000" />
<use x="48" y="48" xlink:href="#p" fill="#000000" />
<use x="60" y="48" xlink:href="#p" fill="#000000" />
<use x="72" y="48" xlink:href="#p" fill="#000000" />
<use x="96" y="48" xlink:href="#p" fill="#000000" />
<use x="132" y="48" xlink:href="#p" fill="#000000" />
<use x="156" y="48" xlink:href="#p" fill="#000000" />
<use x="192" y="48" xlink:href="#p" fill="#000000" />
<use x="216" y="48" xlink:href="#p" fill="#000000" />
<use x="240" y="48" xlink:href="#p" fill="#000000" />
<use x="252" y="48" xlink:href="#p" fill="#000000" />
<use x="264" y="48" xlink:href="#p" fill="#000000" />
<use x="276" y="48" xlink:href="#p" fill="#000000" />
<use x="288" y="48" xlink:href="#p" fill="#000000" />
<use x="348" y="48" xlink:href="#p" fill="#000000" />
<use x="372" y="48" xlink:href="#p" fill="#000000" />
<use x="384" y="48" xlink:href="#p" fill="#000000" />
<use x="432" y="48" xlink:href="#p" fill="#000000" />
<use x="456" y="48" xlink:href="#p" fill="#000000" />
<use x="468" y="48" xlink:href="#p" fill="#000000" />
<use x="480" y="48" xlink:href="#p" fill="#000000" />
<use x="504" y="48" xlink:href="#p" fill="#000000" />
<use x="24" y="60" xlink:href="#p" fill="#000000" />
<use x="48" y="60" xlink:href="#p" fill="#000000" />
<use x="60" y="60" xlink:href="#p" fill="#000000" />
<use x="72" y="60" xlink:href="#p" fill="#000000" />
<use x="96" y="60" xlink:href="#p" fill="#000000" />
<use x="120" y="60" xlink:href="#p" fill="#000000" />
<use x="132" y="60" xlink:href="#p" fill="#000000" />
<use x="144" y="60" xlink:href="#p" fill="#000000" />
<use x="168" y="60" xlink:href="#p" fill="#000000" />
<use x="180" y="60" xlink:href="#p" fill="#000000" />
<use x="192" y="60" xlink:href="#p" fill="#000000" />
<use x="216" y="60" xlink:href="#p" fill="#000000" />
<use x="240" y="60" xlink:href="#p" fill="#000000" />
<use x="252" y="60" xlink:href="#p" fill="#000000" />
<use x="264" y="60" xlink:href="#p" fill="#000000" />
<use x="288" y="60" xlink:href="#p" fill="#000000" />
<use x="300" y="60" xlink:href="#p" fill="#000000" />
<use x="312" y="60" xlink:href="#p" fill="#000000" />
<use x="324" y="60" xlink:href="#p" fill="#000000" />
<use x="360" y="60" xlink:href="#p" fill="#000000" />
<use x="372" y="60" xlink:href="#p" fill="#000000" />
<use x="384" y="60" xlink:href="#p" fill="#000000" />
<use x="432" y="60" xlink:href="#p" fill="#000000" />
<use x="456" y="60" xlink:href="#p" fill="#000000" />
<use x="468" y="60" xlink:href="#p" fill="#000000" />
<use x="480" y="60" xlink:href="#p" fill="#000000" />
<use x="504" y="60" xlink:href="#p" fill="#000000" />
<use x="24" y="72" xlink:href="#p" fill="#000000" />
<use x="48" y="72" xlink:href="#p" fill="#000000" />
<use x="60" y="72" xlink:href="#p" fill="#000000" />
<use x="72" y="72" xlink:href="#p" fill="#000000" />
<use x="96" y="72" xlink:href="#p" fill="#000000" />
<use x="132" y="72" xlink:href="#p" fill="#000000" />
<use x="204" y="72" xlink:href="#p" fill="#000000" />
<use x="240" y="72" xlink:href="#p" fill="#000000" />
<use x="276" y="72" xlink:href="#p" fill="#000000" />
<use x="288" y="72" xlink:href="#p" fill="#000000" />
<use x="348" y="72" xlink:href="#p" fill="#000000" />
<use x="384" y="72" xlink:href="#p" fill="#000000" />
<use x="396" y="72" xlink:href="#p" fill="#000000" />
<use x="432" y="72" xlink:href="#p" fill="#000000" />
<use x="456" y="72" xlink:href="#p" fill="#000000" />
<use x="468" y="72" xlink:href="#p" fill="#000000" />
<use x="480" y="72" xlink:href="#p" fill="#000000" />
<use x="504" y="72" xlink:href="#p" fill="#000000" />
<use x="24" y="84" xlink:href="#p" fill="#000000" />
<use x="96" y="84" xlink:href="#p" fill="#000000" />
<use x="180" y="84" xlink:href="#p" fill="#000000" />
<use x="192" y="84" xlink:href="#p" fill="#000000" />
<use x="204" y="84" xlink:href="#p" fill="#000000" />
<use x="216" y="84" xlink:href="#p" fill="#000000" />
<use x="228" y="84" xlink:href="#p" fill="#000000" />
<use x="240" y="84" xlink:href="#p" fill="#000000" />
<use x="264" y="84" xlink:href="#p" fill="#000000" />
<use x="276" y="84" xlink:href="#p" fill="#000000" />
<use x="288" y="84" xlink:href="#p" fill="#000000" />
<use x="312" y="84" xlink:href="#p" fill="#000000" />
<use x="324" y="84" xlink:href="#p" fill="#000000" />
<use x="336" y="84" xlink:href="#p" fill="#000000" />
<use x="348" y="84" xlink:href="#p" fill="#000000" />
<use x="360" y="84" xlink:href="#p" fill="#000000" />
<use x="372" y="84" xlink:href="#p" fill="#000000" />
<use x="396" y="84" xlink:href="#p" fill="#000000" />
<use x="432" y="84" xlink:href="#p" fill="#000000" />
<use x="504" y="84" xlink:href="#p" fill="#000000" />
<use x="24" y="96" xlink:href="#p" fill="#000000" />
<use x="36" y="96" xlink:href="#p" fill="#000000" />
<use x="48" y="96" xlink:href="#p" fill="#000000" />
<use x="60" y="96" xlink:href="#p" fill="#000000" />
<use x="72" y="96" xlink:href="#p" fill="#000000" />
<use x="84" y="96" xlink:href="#p" fill="#000000" />
<use x="96" y="96" xlink:href="#p" fill="#000000" />
<use x="120" y="96" xlink:href="#p" fill="#000000" />
<use x="144" y="96" xlink:href="#p" fill="#000000" />
<use x="168" y="96" xlink:href="#p" fill="#000000" />
<use x="192" y="96" xlink:href="#p" fill="#000000" />
<use x="216" y="96" xlink:href="#p" fill="#000000" />
<use x="240" y="96" xlink:href="#p" fill="#000000" />
<use x="264" y="96" xlink:href="#p" fill="#000000" />
<use x="288" y="96" xlink:href="#p" fill="#000000" />
<use x="312" y="96" xlink:href="#p" fill="#000000" />
<use x="336" y="96" xlink:href="#p" fill="#000000" />
<use x="360" y="96" xlink:href="#p" fill="#000000" />
<use x="384" y="96" xlink:href="#p" fill="#000000" />
<use x="408" y="96" xlink:href="#p" fill="#000000" />
<use x="432" y="96" xlink:href="#p" fill="#000000" />
<use x="444" y="96" xlink:href="#p" fill="#000000" />
<use x="456" y="96" xlink:href="#p" fill="#000000" />
<use x="468" y="96" xlink:href="#p" fill="#000000" />
<use x="480" y="96" xlink:href="#p" fill="#000000" />
<use x="492" y="96" xlink:href="#p" fill="#000000" />
<use x="504" y="96" xlink:href="#p" fill="#000000" />
<use x="132" y="108" xlink:href="#p" fill="#000000" />
<use x="168" y="108" xlink:href="#p" fill="#000000" />
<use x="180" y="108" xlink:href="#p" fill="#000000" />
<use x="192" y="108" xlink:href="#p" fill="#000000" />
<use x="252" y="108" xlink:href="#p" fill="#000000" />
<use x="264" y="108" xlink:href="#p" fill="#000000" />
<use x="276" y="108" xlink:href="#p" fill="#000000" />
<use x="300" y="108" xlink:href="#p" fill="#000000" />
<use x="312" y="108" xlink:href="#p" fill="#000000" />
<use x="336" y="108" xlink:href="#p" fill="#000000" />
<use x="348" y="108" xlink:href="#p" fill="#000000" />
<use x="360" y="108" xlink:href="#p" fill="#000000" />
<use x="372" y="108" xlink:href="#p" fill="#000000" />
<use x="384" y="108" xlink:href="#p" fill="#000000" />
<use x="408" y="108" xlink:href="#p" fill="#000000" />
<use x="48" y="120" xlink:href="#p" fill="#000000" />
<use x="72" y="120" xlink:href="#p" fill="#000000" />
<use x="84" y="120" xlink:href="#p" fill="#000000" />
<use x="96" y="120" xlink:href="#p" fill="#000000" />
<use x="120" y="120" xlink:href="#p" fill="#000000" />
<use x="144" y="120" xlink:href="#p" fill="#000000" />
<use x="168" y="120" xlink:href="#p" fill="#000000" />
<use x="180" y="120" xlink:href="#p" fill="#000000" />
<use x="192" y="120" xlink:href="#p" fill="#000000" />
<use x="204" y="120" xlink:href="#p" fill="#000000" />
<use x="216" y="120" xlink:href="#p" fill="#000000" />
<use x="228" y="120" xlink:href="#p" fill="#000000" />
<use x="240" y="120" xlink:href="#p" fill="#000000" />
<use x="288" y="120" xlink:href="#p" fill="#000000" />
<use x="300" y="120" xlink:href="#p" fill="#000000" />
<use x="336" y="120" xlink:href="#p" fill="#000000" />
<use x="360" y="120" xlink:href="#p" fill="#000000" />
<use x="372" y="120" xlink:href="#p" fill="#000000" />
<use x="420" y="120" xlink:href="#p" fill="#000000" />
<use x="468" y="120" xlink:href="#p" fill="#000000" />
<use x="504" y="120" xlink:href="#p" fill="#000000" />
<use x="36" y="132" xlink:href="#p" fill="#000000" />
<use x="48" y="132" xlink:href="#p" fill="#000000" />
<use x="60" y="132" xlink:href="#p" fill="#000000" />
<use x="72" y="132" xlink:href="#p" fill="#000000" />
<use x="84" y="132" xlink:href="#p" fill="#000000" />
<use x="108" y="132" xlink:href="#p" fill="#000000" />
<use x="132" y="132" xlink:href="#p" fill="#000000" />
<use x="168" y="132" xlink:href="#p" fill="#000000" />
<use x="180" y="132" xlink:href="#p" fill="#000000" />
<use x="192" y="132" xlink:href="#p" fill="#000000" />
<use x="204" y="132" xlink:href="#p" fill="#000000" />
<use x="264" y="132" xlink:href="#p" fill="#000000" />
<use x="276" y="132" xlink:href="#p" fill="#000000" />
<use x="288" y="132" xlink:href="#p" fill="#000000" />
<use x="312" y="132" xlink:href="#p" fill="#000000" />
<use x="360" y="132" xlink:href="#p" fill="#000000" />
<use x="384" y="132" xlink:href="#p" fill="#000000" />
<use x="408" y="132" xlink:href="#p" fill="#000000" />
<use x="420" y="132" xlink:href="#p" fill="#000000" />
<use x="432" y="132" xlink:href="#p" fill="#000000" />
<use x="444" y="132" xlink:href="#p" fill="#000000" />
<use x="456" y="132" xlink:href="#p" fill="#000000" />
<use x="468" y="132" xlink:href="#p" fill="#000000" />
<use x="480" y="132" xlink:href="#p" fill="#000000" />
<use x="492" y="132" xlink:href="#p" fill="#000000" />
<use x="24" y="144" xlink:href="#p" fill="#000000" />
<use x="60" y="144" xlink:href="#p" fill="#000000" />
<use x="96" y="144" xlink:href="#p" fill="#000000" />
<use x="108" y="144" xlink:href="#p" fill="#000000" />
<use x="144" y="144" xlink:href="#p" fill="#000000" />
<use x="180" y="144" xlink:href="#p" fill="#000000" />
<use x="192" y="144" xlink:href="#p" fill="#000000" />
<use x="204" y="144" xlink:href="#p" fill="#000000" />
<use x="240" y="144" xlink:href="#p" fill="#000000" />
<use x="264" y="144" xlink:href="#p" fill="#000000" />
<use x="276" y="144" xlink:href="#p" fill="#000000" />
<use x="288" y="144" xlink:href="#p" fill="#000000" />
<use x="300" y="144" xlink:href="#p" fill="#000000" />
<use x="312" y="144" xlink:href="#p" fill="#000000" />
<use x="324" y="144" xlink:href="#p" fill="#000000" />
<use x="360" y="144" xlink:href="#p" fill="#000000" />
<use x="372" y="144" xlink:href="#p" fill="#000000" />
<use x="384" y="144" xlink:href="#p" fill="#000000" />
<use x="420" y="144" xlink:href="#p" fill="#000000" />
<use x="456" y="144" xlink:href="#p" fill="#000000" />
<use x="480" y="144" xlink:href="#p" fill="#000000" />
<use x="24" y="156" xlink:href="#p" fill="#000000" />
<use x="60" y="156" xlink:href="#p" fill="#000000" />
<use x="84" y="156" xlink:href="#p" fill="#000000" />
<use x="108" y="156" xlink:href="#p" fill="#000000" />
<use x="120" y="156" xlink:href="#p" fill="#000000" />
<use x="132" y="156" xlink:href="#p" fill="#000000" />
<use x="156" y="156" xlink:href="#p" fill="#000000" />
<use x="180" y="156" xlink:href="#p" fill="#000000" />
<use x="192" y="156" xlink:href="#p" fill="#000000" />
<use x="204" y="156" xlink:href="#p" fill="#000000" />
<use x="240" y="156" xlink:href="#p" fill="#000000" />
<use x="252" y="156" xlink:href="#p" fill="#000000" />
<use x="288" y="156" xlink:href="#p" fill="#000000" />
<use x="348" y="156" xlink:href="#p" fill="#000000" />
<use x="360" y="156" xlink:href="#p" fill="#000000" />
<use x="372" y="156" xlink:href="#p" fill="#000000" />
<use x="384" y="156" xlink:href="#p" fill="#000000" />
<use x="408" y="156" xlink:href="#p" fill="#000000" />
<use x="420" y="156" xlink:href="#p" fill="#000000" />
<use x="432" y="156" xlink:href="#p" fill="#000000" />
<use x="456" y="156" xlink:href="#p" fill="#000000" />
<use x="492" y="156" xlink:href="#p" fill="#000000" />
<use x="504" y="156" xlink:href="#p" fill="#000000" />
<use x="24" y="168" xlink:href="#p" fill="#000000" />
<use x="36" y="168" xlink:href="#p" fill="#000000" />
<use x="96" y="168" xlink:href="#p" fill="#000000" />
<use x="108" y="168" xlink:href="#p" fill="#000000" />
<use x="120" y="168" xlink:href="#p" fill="#000000" />
<use x="132" y="168" xlink:href="#p" fill="#000000" />
<use x="168" y="168" xlink:href="#p" fill="#000000" />
<use x="180" y="168" xlink:href="#p" fill="#000000" />
<use x="192" y="168" xlink:href="#p" fill="#000000" />
<use x="276" y="168" xlink:href="#p" fill="#000000" />
<use x="288" y="168" xlink:href="#p" fill="#000000" />
<use x="336" y="168" xlink:href="#p" fill="#000000" />
<use x="348" y="168" xlink:href="#p" fill="#000000" />
<use x="360" y="168" xlink:href="#p" fill="#000000" />
<use x="372" y="168" xlink:href="#p" fill="#000000" />
<use x="396" y="168" xlink:href="#p" fill="#000000" />
<use x="420" y="168" xlink:href="#p" fill="#000000" />
<use x="468" y="168" xlink:href="#p" fill="#000000" />
<use x="492" y="168" xlink:href="#p" fill="#000000" />
<use x="504" y="168" xlink:href="#p" fill="#000000" />
<use x="36" y="180" xlink:href="#p" fill="#000000" />
<use x="48" y="180" xlink:href="#p" fill="#000000" />
<use x="60" y="180" xlink:href="#p" fill="#000000" />
<use x="84" y="180" xlink:href="#p" fill="#000000" />
<use x="156" y="180" xlink:href="#p" fill="#000000" />
<use x="180" y="180" xlink:href="#p" fill="#000000" />
<use x="192" y="180" xlink:href="#p" fill="#000000" />
<use x="204" y="180" xlink:href="#p" fill="#000000" />
<use x="240" y="180" xlink:href="#p" fill="#000000" />
<use x="264" y="180" xlink:href="#p" fill="#000000" />
<use x="288" y="180" xlink:href="#p" fill="#000000" />
<use x="300" y="180" xlink:href="#p" fill="#000000" />
<use x="324" y="180" xlink:href="#p" fill="#000000" />
<use x="348" y="180" xlink:href="#p" fill="#000000" />
<use x="360" y="180" xlink:href="#p" fill="#000000" />
<use x="384" y="180" xlink:href="#p" fill="#000000" />
<use x="396" y="180" xlink:href="#p" fill="#000000" />
<use x="420" y="180" xlink:href="#p" fill="#000000" />
<use x="468" y="180" xlink:href="#p" fill="#000000" />
<use x="492" y="180" xlink:href="#p" fill="#000000" />
<use x="504" y="180" xlink:href="#p" fill="#000000" />
<use x="48" y="192" xlink:href="#p" fill="#000000" />
<use x="96" y="192" xlink:href="#p" fill="#000000" />
<use x="108" y="192" xlink:href="#p" fill="#000000" />
<use x="132" y="192" xlink:href="#p" fill="#000000" />
<use x="144" y="192" xlink:href="#p" fill="#000000" />
<use x="180" y="192" xlink:href="#p" fill="#000000" />
<use x="204" y="192" xlink:href="#p" fill="#000000" />
<use x="240" y="192" xlink:href="#p" fill="#000000" />
<use x="276" y="192" xlink:href="#p" fill="#000000" />
<use x="288" y="192" xlink:href="#p" fill="#000000" />
<use x="312" y="192" xlink:href="#p" fill="#000000" />
<use x="336" y="192" xlink:href="#p" fill="#000000" />
<use x="348" y="192" xlink:href="#p" fill="#000000" />
<use x="360" y="192" xlink:href="#p" fill="#000000" />
<use x="432" y="192" xlink:href="#p" fill="#000000" />
<use x="444" y="192" xlink:href="#p" fill="#000000" />
<use x="468" y="192" xlink:href="#p" fill="#000000" />
<use x="480" y="192" xlink:href="#p" fill="#000000" />
<use x="492" y="192" xlink:href="#p" fill="#000000" />
<use x="36" y="204" xlink:href="#p" fill="#000000" />
<use x="60" y="204" xlink:href="#p" fill="#000000" />
<use x="120" y="204" xlink:href="#p" fill="#000000" />
<use x="228" y="204" xlink:href="#p" fill="#000000" />
<use x="264" y="204" xlink:href="#p" fill="#000000" />
<use x="276" y="204" xlink:href="#p" fill="#000000" />
<use x="288" y="204" xlink:href="#p" fill="#000000" />
<use x="312" y="204" xlink:href="#p" fill="#000000" />
<use x="324" y="204" xlink:href="#p" fill="#000000" />
<use x="336" y="204" xlink:href="#p" fill="#000000" />
<use x="360" y="204" xlink:href="#p" fill="#000000" />
<use x="372" y="204" xlink:href="#p" fill="#000000" />
<use x="384" y="204" xlink:href="#p" fill="#000000" />
<use x="396" y="204" xlink:href="#p" fill="#000000" />
<use x="432" y="204" xlink:href="#p" fill="#000000" />
<use x="444" y="204" xlink:href="#p" fill="#000000" />
<use x="24" y="216" xlink:href="#p" fill="#000000" />
<use x="48" y="216" xlink:href="#p" fill="#000000" />
<use x="96" y="216" xlink:href="#p" fill="#000000" />
<use x="120" y="216" xlink:href="#p" fill="#000000" />
<use x="180" y="216" xlink:href="#p" fill="#000000" />
<use x="204" y="216" xlink:href="#p" fill="#000000" />
<use x="228" y="216" xlink:href="#p" fill="#000000" />
<use x="252" y="216" xlink:href="#p" fill="#000000" />
<use x="264" y="216" xlink:href="#p" fill="#000000" />
<use x="288" y="216" xlink:href="#p" fill="#000000" />
<use x="300" y="216" xlink:href="#p" fill="#000000" />
<use x="324" y="216" xlink:href="#p" fill="#000000" />
<use x="348" y="216" xlink:href="#p" fill="#000000" />
<use x="360" y="216" xlink:href="#p" fill="#000000" />
<use x="372" y="216" xlink:href="#p" fill="#000000" />
<use x="408" y="216" xlink:href="#p" fill="#000000" />
<use x="432" y="216" xlink:href="#p" fill="#000000" />
<use x="444" y="216" xlink:href="#p" fill="#000000" />
<use x="468" y="216" xlink:href="#p" fill="#000000" />
<use x="504" y="216" xlink:href="#p" fill="#000000" />
<use x="24" y="228" xlink:href="#p" fill="#000000" />
<use x="60" y="228" xlink:href="#p" fill="#000000" />
<use x="84" y="228" xlink:href="#p" fill="#000000" />
<use x="108" y="228" xlink:href="#p" fill="#000000" />
<use x="120" y="228" xlink:href="#p" fill="#000000" />
<use x="156" y="228" xlink:href="#p" fill="#000000" />
<use x="168" y="228" xlink:href="#p" fill="#000000" />
<use x="180" y="228" xlink:href="#p" fill="#000000" />
<use x="216" y="228" xlink:href="#p" fill="#000000" />
<use x="228" y="228" xlink:href="#p" fill="#000000" />
<use x="240" y="228" xlink:href="#p" fill="#000000" />
<use x="252" y="228" xlink:href="#p" fill="#000000" />
<use x="264" y="228" xlink:href="#p" fill="#000000" />
<use x="276" y="228" xlink:href="#p" fill="#000000" />
<use x="288" y="228" xlink:href="#p" fill="#000000" />
<use x="324" y="228" xlink:href="#p" fill="#000000" />
<use x="336" y="228" xlink:href="#p" fill="#000000" />
<use x="372" y="228" xlink:href="#p" fill="#000000" />
<use x="432" y="228" xlink:href="#p" fill="#000000" />
<use x="480" y="228" xlink:href="#p" fill="#000000" />
<use x="492" y="228" xlink:href="#p" fill="#000000" />
<use x="504" y="228" xlink:href="#p" fill="#000000" />
<use x="24" y="240" xlink:href="#p" fill="#000000" />
<use x="84" y="240" xlink:href="#p" fill="#000000" />
<use x="96" y="240" xlink:href="#p" fill="#000000" />
<use x="120" y="240" xlink:href="#p" fill="#000000" />
<use x="132" y="240" xlink:href="#p" fill="#000000" />
<use x="144" y="240" xlink:href="#p" fill="#000000" />
<use x="180" y="240" xlink:href="#p" fill="#000000" />
<use x="204" y="240" xlink:href="#p" fill="#000000" />
<use x="228" y="240" xlink:href="#p" fill="#000000" />
<use x="240" y="240" xlink:href="#p" fill="#000000" />
<use x="252" y="240" xlink:href="#p" fill="#000000" />
<use x="276" y="240" xlink:href="#p" fill="#000000" />
<use x="348" y="240" xlink:href="#p" fill="#000000" />
<use x="360" y="240" xlink:href="#p" fill="#000000" />
<use x="384" y="240" xlink:href="#p" fill="#000000" />
<use x="420" y="240" xlink:href="#p" fill="#000000" />
<use x="456" y="240" xlink:href="#p" fill="#000000" />
<use x="468" y="240" xlink:href="#p" fill="#000000" />
<use x="480" y="240" xlink:href="#p" fill="#000000" />
<use x="492" y="240" xlink:href="#p" fill="#000000" />
<use x="504" y="240" xlink:href="#p" fill="#000000" />
<use x="24" y="252" xlink:href="#p" fill="#000000" />
<use x="48" y="252" xlink:href="#p" fill="#000000" />
<use x="60" y="252" xlink:href="#p" fill="#000000" />
<use x="108" y="252" xlink:href="#p" fill="#000000" />
<use x="132" y="252" xlink:href="#p" fill="#000000" />
<use x="144" y="252" xlink:href="#p" fill="#000000" />
<use x="168" y="252" xlink:href="#p" fill="#000000" />
<use x="264" y="252" xlink:href="#p" fill="#000000" />
<use x="312" y="252" xlink:href="#p" fill="#000000" />
<use x="324" y="252" xlink:href="#p" fill="#000000" />
<use x="360" y="252" xlink:href="#p" fill="#000000" />
<use x="372" y="252" xlink:href="#p" fill="#000000" />
<use x="384" y="252" xlink:href="#p" fill="#000000" />
<use x="408" y="252" xlink:href="#p" fill="#000000" />
<use x="432" y="252" xlink:href="#p" fill="#000000" />
<use x="444" y="252" xlink:href="#p" fill="#000000" />
<use x="480" y="252" xlink:href="#p" fill="#000000" />
<use x="492" y="252" xlink:href="#p" fill="#000000" />
<use x="24" y="264" xlink:href="#p" fill="#000000" />
<use x="36" y="264" xlink:href="#p" fill="#000000" />
<use x="48" y="264" xlink:href="#p" fill="#000000" />
<use x="84" y="264" xlink:href="#p" fill="#000000" />
<use x="96" y="264" xlink:href="#p" fill="#000000" />
<use x="132" y="264" xlink:href="#p" fill="#000000" />
<use x="144" y="264" xlink:href="#p" fill="#000000" />
<use x="168" y="264" xlink:href="#p" fill="#000000" />
<use x="180" y="264" xlink:href="#p" fill="#000000" />
<use x="192" y="264" xlink:href="#p" fill="#000000" />
<use x="228" y="264" xlink:href="#p" fill="#000000" />
<use x="252" y="264" xlink:href="#p" fill="#000000" />
<use x="288" y="264" xlink:href="#p" fill="#000000" />
<use x="300" y="264" xlink:href="#p" fill="#000000" />
<use x="348" y="264" xlink:href="#p" fill="#000000" />
<use x="384" y="264" xlink:href="#p" fill="#000000" />
<use x="396" y="264" xlink:href="#p" fill="#000000" />
<use x="408" y="264" xlink:href="#p" fill="#000000" />
<use x="420" y="264" xlink:href="#p" fill="#000000" />
<use x="432" y="264" xlink:href="#p" fill="#000000" />
<use x="444" y="264" xlink:href="#p" fill="#000000" />
<use x="456" y="264" xlink:href="#p" fill="#000000" />
<use x="480" y="264" xlink:href="#p" fill="#000000" />
<use x="504" y="264" xlink:href="#p" fill="#000000" />
<use x="24" y="276" xlink:href="#p" fill="#000000" />
<use x="36" y="276" xlink:href="#p" fill="#000000" />
<use x="48" y="276" xlink:href="#p" fill="#000000" />
<use x="60" y="276" xlink:href="#p" fill="#000000" />
<use x="84" y="276" xlink:href="#p" fill="#000000" />
<use x="120" y="276" xlink:href="#p" fill="#000000" />
<use x="132" y="276" xlink:href="#p" fill="#000000" />
<use x="144" y="276" xlink:href="#p" fill="#000000" />
<use x="168" y="276" xlink:href="#p" fill="#000000" />
<use x="180" y="276" xlink:href="#p" fill="#000000" />
<use x="192" y="276" xlink:href="#p" fill="#000000" />
<use x="204" y="276" xlink:href="#p" fill="#000000" />
<use x="216" y="276" xlink:href="#p" fill="#000000" />
<use x="228" y="276" xlink:href="#p" fill="#000000" />
<use x="240" y="276" xlink:href="#p" fill="#000000" />
<use x="276" y="276" xlink:href="#p" fill="#000000" />
<use x="312" y="276" xlink:href="#p" fill="#000000" />
<use x="336" y="276" xlink:href="#p" fill="#000000" />
<use x="372" y="276" xlink:href="#p" fill="#000000" />
<use x="384" y="276" xlink:href="#p" fill="#000000" />
<use x="408" y="276" xlink:href="#p" fill="#000000" />
<use x="420" y="276" xlink:href="#p" fill="#000000" />
<use x="432" y="276" xlink:href="#p" fill="#000000" />
<use x="444" y="276" xlink:href="#p" fill="#000000" />
<use x="468" y="276" xlink:href="#p" fill="#000000" />
<use x="492" y="276" xlink:href="#p" fill="#000000" />
<use x="504" y="276" xlink:href="#p" fill="#000000" />
<use x="84" y="288" xlink:href="#p" fill="#000000" />
<use x="96" y="288" xlink:href="#p" fill="#000000" />
<use x="108" y="288" xlink:href="#p" fill="#000000" />
<use x="168" y="288" xlink:href="#p" fill="#000000" />
<use x="180" y="288" xlink:href="#p" fill="#000000" />
<use x="192" y="288" xlink:href="#p" fill="#000000" />
<use x="204" y="288" xlink:href="#p" fill="#000000" />
<use x="228" y="288" xlink:href="#p" fill="#000000" />
<use x="264" y="288" xlink:href="#p" fill="#000000" />
<use x="288" y="288" xlink:href="#p" fill="#000000" />
<use x="300" y="288" xlink:href="#p" fill="#000000" />
<use x="312" y="288" xlink:href="#p" fill="#000000" />
<use x="324" y="288" xlink:href="#p" fill="#000000" />
<use x="348" y="288" xlink:href="#p" fill="#000000" />
<use x="360" y="288" xlink:href="#p" fill="#000000" />
<use x="372" y="288" xlink:href="#p" fill="#000000" />
<use x="384" y="288" xlink:href="#p" fill="#000000" />
<use x="420" y="288" xlink:href="#p" fill="#000000" />
<use x="444" y="288" xlink:href="#p" fill="#000000" />
<use x="456" y="288" xlink:href="#p" fill="#000000" />
<use x="480" y="288" xlink:href="#p" fill="#000000" />
<use x="492" y="288" xlink:href="#p" fill="#000000" />
<use x="504" y="288" xlink:href="#p" fill="#000000" />
<use x="48" y="300" xlink:href="#p" fill="#000000" />
<use x="60" y="300" xlink:href="#p" fill="#000000" />
<use x="84" y="300" xlink:href="#p" fill="#000000" />
<use x="132" y="300" xlink:href="#p" fill="#000000" />
<use x="144" y="300" xlink:href="#p" fill="#000000" />
<use x="168" y="300" xlink:href="#p" fill="#000000" />
<use x="180" y="300" xlink:href="#p" fill="#000000" />
<use x="192" y="300" xlink:href="#p" fill="#000000" />
<use x="228" y="300" xlink:href="#p" fill="#000000" />
<use x="240" y="300" xlink:href="#p" fill="#000000" />
<use x="252" y="300" xlink:href="#p" fill="#000000" />
<use x="264" y="300" xlink:href="#p" fill="#000000" />
<use x="300" y="300" xlink:href="#p" fill="#000000" />
<use x="324" y="300" xlink:href="#p" fill="#000000" />
<use x="336" y="300" xlink:href="#p" fill="#000000" />
<use x="348" y="300" xlink:href="#p" fill="#000000" />
<use x="360" y="300" xlink:href="#p" fill="#000000" />
<use x="372" y="300" xlink:href="#p" fill="#000000" />
<use x="408" y="300" xlink:href="#p" fill="#000000" />
<use x="420" y="300" xlink:href="#p" fill="#000000" />
<use x="444" y="300" xlink:href="#p" fill="#000000" />
<use x="468" y="300" xlink:href="#p" fill="#000000" />
<use x="24" y="312" xlink:href="#p" fill="#000000" />
<use x="72" y="312" xlink:href="#p" fill="#000000" />
<use x="96" y="312" xlink:href="#p" fill="#000000" />
<use x="108" y="312" xlink:href="#p" fill="#000000" />
<use x="120" y="312" xlink:href="#p" fill="#000000" />
<use x="168" y="312" xlink:href="#p" fill="#000000" />
<use x="180" y="312" xlink:href="#p" fill="#000000" />
<use x="192" y="312" xlink:href="#p" fill="#000000" />
<use x="204" y="312" xlink:href="#p" fill="#000000" />
<use x="240" y="312" xlink:href="#p" fill="#000000" />
<use x="264" y="312" xlink:href="#p" fill="#000000" />
<use x="276" y="312" xlink:href="#p" fill="#000000" />
<use x="300" y="312" xlink:href="#p" fill="#000000" />
<use x="324" y="312" xlink:href="#p" fill="#000000" />
<use x="336" y="312" xlink:href="#p" fill="#000000" />
<use x="360" y="312" xlink:href="#p" fill="#000000" />
<use x="372" y="312" xlink:href="#p" fill="#000000" />
<use x="384" y="312" xlink:href="#p" fill="#000000" />
<use x="396" y="312" xlink:href="#p" fill="#000000" />
<use x="408" y="312" xlink:href="#p" fill="#000000" />
<use x="432" y="312" xlink:href="#p" fill="#000000" />
<use x="456" y="312" xlink:href="#p" fill="#000000" />
<use x="480" y="312" xlink:href="#p" fill="#000000" />
<use x="36" y="324" xlink:href="#p" fill="#000000" />
<use x="48" y="324" xlink:href="#p" fill="#000000" />
<use x="84" y="324" xlink:href="#p" fill="#000000" />
<use x="120" y="324" xlink:href="#p" fill="#000000" />
<use x="144" y="324" xlink:href="#p" fill="#000000" />
<use x="156" y="324" xlink:href="#p" fill="#000000" />
<use x="204" y="324" xlink:href="#p" fill="#000000" />
<use x="216" y="324" xlink:href="#p" fill="#000000" />
<use x="252" y="324" xlink:href="#p" fill="#000000" />
<use x="360" y="324" xlink:href="#p" fill="#000000" />
<use x="420" y="324" xlink:href="#p" fill="#000000" />
<use x="432" y="324" xlink:href="#p" fill="#000000" />
<use x="492" y="324" xlink:href="#p" fill="#000000" />
<use x="504" y="324" xlink:href="#p" fill="#000000" />
<use x="48" y="336" xlink:href="#p" fill="#000000" />
<use x="60" y="336" xlink:href="#p" fill="#000000" />
<use x="96" y="336" xlink:href="#p" fill="#000000" />
<use x="132" y="336" xlink:href="#p" fill="#000000" />
<use x="204" y="336" xlink:href="#p" fill="#000000" />
<use x="216" y="336" xlink:href="#p" fill="#000000" />
<use x="240" y="336" xlink:href="#p" fill="#000000" />
<use x="252" y="336" xlink:href="#p" fill="#000000" />
<use x="300" y="336" xlink:href="#p" fill="#000000" />
<use x="312" y="336" xlink:href="#p" fill="#000000" />
<use x="336" y="336" xlink:href="#p" fill="#000000" />
<use x="384" y="336" xlink:href="#p" fill="#000000" />
<use x="432" y="336" xlink:href="#p" fill="#000000" />
<use x="444" y="336" xlink:href="#p" fill="#000000" />
<use x="480" y="336" xlink:href="#p" fill="#000000" />
<use x="492" y="336" xlink:href="#p" fill="#000000" />
<use x="504" y="336" xlink:href="#p" fill="#000000" />
<use x="24" y="348" xlink:href="#p" fill="#000000" />
<use x="60" y="348" xlink:href="#p" fill="#000000" />
<use x="84" y="348" xlink:href="#p" fill="#000000" />
<use x="108" y="348" xlink:href="#p" fill="#000000" />
<use x="120" y="348" xlink:href="#p" fill="#000000" />
<use x="144" y="348" xlink:href="#p" fill="#000000" />
<use x="156" y="348" xlink:href="#p" fill="#000000" />
<use x="180" y="348" xlink:href="#p" fill="#000000" />
<use x="192" y="348" xlink:href="#p" fill="#000000" />
<use x="216" y="348" xlink:href="#p" fill="#000000" />
<use x="228" y="348" xlink:href="#p" fill="#000000" />
<use x="240" y="348" xlink:href="#p" fill="#000000" />
<use x="252" y="348" xlink:href="#p" fill="#000000" />
<use x="288" y="348" xlink:href="#p" fill="#000000" />
<use x="300" y="348" xlink:href="#p" fill="#000000" />
<use x="312" y="348" xlink:href="#p" fill="#000000" />
<use x="336" y="348" xlink:href="#p" fill="#000000" />
<use x="348" y="348" xlink:href="#p" fill="#000000" />
<use x="372" y="348" xlink:href="#p" fill="#000000" />
<use x="408" y="348" xlink:href="#p" fill="#000000" />
<use x="456" y="348" xlink:href="#p" fill="#000000" />
<use x="492" y="348" xlink:href="#p" fill="#000000" />
<use x="504" y="348" xlink:href="#p" fill="#000000" />
<use x="84" y="360" xlink:href="#p" fill="#000000" />
<use x="96" y="360" xlink:href="#p" fill="#000000" />
<use x="120" y="360" xlink:href="#p" fill="#000000" />
<use x="156" y="360" xlink:href="#p" fill="#000000" />
<use x="192" y="360" xlink:href="#p" fill="#000000" />
<use x="204" y="360" xlink:href="#p" fill="#000000" />
<use x="228" y="360" xlink:href="#p" fill="#000000" />
<use x="252" y="360" xlink:href="#p" fill="#000000" />
<use x="264" y="360" xlink:href="#p" fill="#000000" />
<use x="276" y="360" xlink:href="#p" fill="#000000" />
<use x="300" y="360" xlink:href="#p" fill="#000000" />
<use x="384" y="360" xlink:href="#p" fill="#000000" />
<use x="396" y="360" xlink:href="#p" fill="#000000" />
<use x="408" y="360" xlink:href="#p" fill="#000000" />
<use x="432" y="360" xlink:href="#p" fill="#000000" />
<use x="468" y="360" xlink:href="#p" fill="#000000" />
<use x="492" y="360" xlink:href="#p" fill="#000000" />
<use x="504" y="360" xlink:href="#p" fill="#000000" />
<use x="48" y="372" xlink:href="#p" fill="#000000" />
<use x="60" y="372" xlink:href="#p" fill="#000000" />
<use x="144" y="372" xlink:href="#p" fill="#000000" />
<use x="156" y="372" xlink:href="#p" fill="#000000" />
<use x="168" y="372" xlink:href="#p" fill="#000000" />
<use x="204" y="372" xlink:href="#p" fill="#000000" />
<use x="252" y="372" xlink:href="#p" fill="#000000" />
<use x="264" y="372" xlink:href="#p" fill="#000000" />
<use x="324" y="372" xlink:href="#p" fill="#000000" />
<use x="336" y="372" xlink:href="#p" fill="#000000" />
<use x="348" y="372" xlink:href="#p" fill="#000000" />
<use x="384" y="372" xlink:href="#p" fill="#000000" />
<use x="396" y="372" xlink:href="#p" fill="#000000" />
<use x="408" y="372" xlink:href="#p" fill="#000000" />
<use x="468" y="372" xlink:href="#p" fill="#000000" />
<use x="480" y="372" xlink:href="#p" fill="#000000" />
<use x="492" y="372" xlink:href="#p" fill="#000000" />
<use x="24" y="384" xlink:href="#p" fill="#000000" />
<use x="48" y="384" xlink:href="#p" fill="#000000" />
<use x="72" y="384" xlink:href="#p" fill="#000000" />
<use x="96" y="384" xlink:href="#p" fill="#000000" />
<use x="108" y="384" xlink:href="#p" fill="#000000" />
<use x="132" y="384" xlink:href="#p" fill="#000000" />
<use x="156" y="384" xlink:href="#p" fill="#000000" />
<use x="192" y="384" xlink:href="#p" fill="#000000" />
<use x="204" y="384" xlink:href="#p" fill="#000000" />
<use x="216" y="384" xlink:href="#p" fill="#000000" />
<use x="228" y="384" xlink:href="#p" fill="#000000" />
<use x="240" y="384" xlink:href="#p" fill="#000000" />
<use x="252" y="384" xlink:href="#p" fill="#000000" />
<use x="312" y="384" xlink:href="#p" fill="#000000" />
<use x="396" y="384" xlink:href="#p" fill="#000000" />
<use x="408" y="384" xlink:href="#p" fill="#000000" />
<use x="468" y="384" xlink:href="#p" fill="#000000" />
<use x="480" y="384" xlink:href="#p" fill="#000000" />
<use x="36" y="396" xlink:href="#p" fill="#000000" />
<use x="72" y="396" xlink:href="#p" fill="#000000" />
<use x="108" y="396" xlink:href="#p" fill="#000000" />
<use x="120" y="396" xlink:href="#p" fill="#000000" />
<use x="132" y="396" xlink:href="#p" fill="#000000" />
<use x="144" y="396" xlink:href="#p" fill="#000000" />
<use x="156" y="396" xlink:href="#p" fill="#000000" />
<use x="180" y="396" xlink:href="#p" fill="#000000" />
<use x="204" y="396" xlink:href="#p" fill="#000000" />
<use x="228" y="396" xlink:href="#p" fill="#000000" />
<use x="240" y="396" xlink:href="#p" fill="#000000" />
<use x="252" y="396" xlink:href="#p" fill="#000000" />
<use x="264" y="396" xlink:href="#p" fill="#000000" />
<use x="276" y="396" xlink:href="#p" fill="#000000" />
<use x="300" y="396" xlink:href="#p" fill="#000000" />
<use x="312" y="396" xlink:href="#p" fill="#000000" />
<use x="324" y="396" xlink:href="#p" fill="#000000" />
<use x="348" y="396" xlink:href="#p" fill="#000000" />
<use x="408" y="396" xlink:href="#p" fill="#000000" />
<use x="420" y="396" xlink:href="#p" fill="#000000" />
<use x="432" y="396" xlink:href="#p" fill="#000000" />
<use x="456" y="396" xlink:href="#p" fill="#000000" />
<use x="468" y="396" xlink:href="#p" fill="#000000" />
<use x="492" y="396" xlink:href="#p" fill="#000000" />
<use x="24" y="408" xlink:href="#p" fill="#000000" />
<use x="48" y="408" xlink:href="#p" fill="#000000" />
<use x="60" y="408" xlink:href="#p" fill="#000000" />
<use x="72" y="408" xlink:href="#p" fill="#000000" />
<use x="96" y="408" xlink:href="#p" fill="#000000" />
<use x="108" y="408" xlink:href="#p" fill="#000000" />
<use x="144" y="408" xlink:href="#p" fill="#000000" />
<use x="180" y="408" xlink:href="#p" fill="#000000" />
<use x="192" y="408" xlink:href="#p" fill="#000000" />
<use x="216" y="408" xlink:href="#p" fill="#000000" />
<use x="228" y="408" xlink:href="#p" fill="#000000" />
<use x="240" y="408" xlink:href="#p" fill="#000000" />
<use x="252" y="408" xlink:href="#p" fill="#000000" />
<use x="264" y="408" xlink:href="#p" fill="#000000" />
<use x="288" y="408" xlink:href="#p" fill="#000000" />
<use x="300" y="408" xlink:href="#p" fill="#000000" />
<use x="324" y="408" xlink:href="#p" fill="#000000" />
<use x="336" y="408" xlink:href="#p" fill="#000000" />
<use x="360" y="408" xlink:href="#p" fill="#000000" />
<use x="396" y="408" xlink:href="#p" fill="#000000" />
<use x="408" y="408" xlink:href="#p" fill="#000000" />
<use x="420" y="408" xlink:href="#p" fill="#000000" />
<use x="432" y="408" xlink:href="#p" fill="#000000" />
<use x="444" y="408" xlink:href="#p" fill="#000000" />
<use x="456" y="408" xlink:href="#p" fill="#000000" />
<use x="492" y="408" xlink:href="#p" fill="#000000" />
<use x="120" y="420" xlink:href="#p" fill="#000000" />
<use x="168" y="420" xlink:href="#p" fill="#000000" />
<use x="180" y="420" xlink:href="#p" fill="#000000" />
<use x="204" y="420" xlink:href="#p" fill="#000000" />
<use x="252" y="420" xlink:href="#p" fill="#000000" />
<use x="264" y="420" xlink:href="#p" fill="#000000" />
<use x="288" y="420" xlink:href="#p" fill="#000000" />
<use x="312" y="420" xlink:href="#p" fill="#000000" />
<use x="396" y="420" xlink:href="#p" fill="#000000" />
<use x="408" y="420" xlink:href="#p" fill="#000000" />
<use x="456" y="420" xlink:href="#p" fill="#000000" />
<use x="480" y="420" xlink:href="#p" fill="#000000" />
<use x="504" y="420" xlink:href="#p" fill="#000000" />
<use x="24" y="432" xlink:href="#p" fill="#000000" />
<use x="36" y="432" xlink:href="#p" fill="#000000" />
<use x="48" y="432" xlink:href="#p" fill="#000000" />
<use x="60" y="432" xlink:href="#p" fill="#000000" />
<use x="72" y="432" xlink:href="#p" fill="#000000" />
<use x="84" y="432" xlink:href="#p" fill="#000000" />
<use x="96" y="432" xlink:href="#p" fill="#000000" />
<use x="156" y="432" xlink:href="#p" fill="#000000" />
<use x="168" y="432" xlink:href="#p" fill="#000000" />
<use x="192" y="432" xlink:href="#p" fill="#000000" />
<use x="204" y="432" xlink:href="#p" fill="#000000" />
<use x="240" y="432" xlink:href="#p" fill="#000000" />
<use x="252" y="432" xlink:href="#p" fill="#000000" />
<use x="264" y="432" xlink:href="#p" fill="#000000" />
<use x="288" y="432" xlink:href="#p" fill="#000000" />
<use x="300" y="432" xlink:href="#p" fill="#000000" />
<use x="336" y="432" xlink:href="#p" fill="#000000" />
<use x="348" y="432" xlink:href="#p" fill="#000000" />
<use x="360" y="432" xlink:href="#p" fill="#000000" />
<use x="372" y="432" xlink:href="#p" fill="#000000" />
<use x="408" y="432" xlink:href="#p" fill="#000000" />
<use x="432" y="432" xlink:href="#p" fill="#000000" />
<use x="456" y="432" xlink:href="#p" fill="#000000" />
<use x="492" y="432" xlink:href="#p" fill="#000000" />
<use x="504" y="432" xlink:href="#p" fill="#000000" />
<use x="24" y="444" xlink:href="#p" fill="#000000" />
<use x="96" y="444" xlink:href="#p" fill="#000000" />
<use x="120" y="444" xlink:href="#p" fill="#000000" />
<use x="144" y="444" xlink:href="#p" fill="#000000" />
<use x="156" y="444" xlink:href="#p" fill="#000000" />
<use x="180" y="444" xlink:href="#p" fill="#000000" />
<use x="216" y="444" xlink:href="#p" fill="#000000" />
<use x="228" y="444" xlink:href="#p" fill="#000000" />
<use x="252" y="444" xlink:href="#p" fill="#000000" />
<use x="288" y="444" xlink:href="#p" fill="#000000" />
<use x="300" y="444" xlink:href="#p" fill="#000000" />
<use x="312" y="444" xlink:href="#p" fill="#000000" />
<use x="324" y="444" xlink:href="#p" fill="#000000" />
<use x="348" y="444" xlink:href="#p" fill="#000000" />
<use x="360" y="444" xlink:href="#p" fill="#000000" />
<use x="396" y="444" xlink:href="#p" fill="#000000" />
<use x="408" y="444" xlink:href="#p" fill="#000000" />
<use x="456" y="444" xlink:href="#p" fill="#000000" />
<use x="24" y="456" xlink:href="#p" fill="#000000" />
<use x="48" y="456" xlink:href="#p" fill="#000000" />
<use x="60" y="456" xlink:href="#p" fill="#000000" />
<use x="72" y="456" xlink:href="#p" fill="#000000" />
<use x="96" y="456" xlink:href="#p" fill="#000000" />
<use x="120" y="456" xlink:href="#p" fill="#000000" />
<use x="216" y="456" xlink:href="#p" fill="#000000" />
<use x="240" y="456" xlink:href="#p" fill="#000000" />
<use x="252" y="456" xlink:href="#p" fill="#000000" />
<use x="300" y="456" xlink:href="#p" fill="#000000" />
<use x="324" y="456" xlink:href="#p" fill="#000000" />
<use x="336" y="456" xlink:href="#p" fill="#000000" />
<use x="348" y="456" xlink:href="#p" fill="#000000" />
<use x="408" y="456" xlink:href="#p" fill="#000000" />
<use x="420" y="456" xlink:href="#p" fill="#000000" />
<use x="432" y="456" xlink:href="#p" fill="#000000" />
<use x="444" y="456" xlink:href="#p" fill="#000000" />
<use x="456" y="456" xlink:href="#p" fill="#000000" />
<use x="24" y="468" xlink:href="#p" fill="#000000" />
<use x="48" y="468" xlink:href="#p" fill="#000000" />
<use x="60" y="468" xlink:href="#p" fill="#000000" />
<use x="72" y="468" xlink:href="#p" fill="#000000" />
<use x="96" y="468" xlink:href="#p" fill="#000000" />
<use x="144" y="468" xlink:href="#p" fill="#000000" />
<use x="156" y="468" xlink:href="#p" fill="#000000" />
<use x="180" y="468" xlink:href="#p" fill="#000000" />
<use x="192" y="468" xlink:href="#p" fill="#000000" />
<use x="204" y="468" xlink:href="#p" fill="#000000" />
<use x="216" y="468" xlink:href="#p" fill="#000000" />
<use x="240" y="468" xlink:href="#p" fill="#000000" />
<use x="252" y="468" xlink:href="#p" fill="#000000" />
<use x="264" y="468" xlink:href="#p" fill="#000000" />
<use x="288" y="468" xlink:href="#p" fill="#000000" />
<use x="312" y="468" xlink:href="#p" fill="#000000" />
<use x="336" y="468" xlink:href="#p" fill="#000000" />
<use x="348" y="468" xlink:href="#p" fill="#000000" />
<use x="408" y="468" xlink:href="#p" fill="#000000" />
<use x="420" y="468" xlink:href="#p" fill="#000000" />
<use x="456" y="468" xlink:href="#p" fill="#000000" />
<use x="480" y="468" xlink:href="#p" fill="#000000" />
<use x="24" y="480" xlink:href="#p" fill="#000000" />
<use x="48" y="480" xlink:href="#p" fill="#000000" />
<use x="60" y="480" xlink:href="#p" fill="#000000" />
<use x="72" y="480" xlink:href="#p" fill="#000000" />
<use x="96" y="480" xlink:href="#p" fill="#000000" />
<use x="120" y="480" xlink:href="#p" fill="#000000" />
<use x="132" y="480" xlink:href="#p" fill="#000000" />
<use x="144" y="480" xlink:href="#p" fill="#000000" />
<use x="168" y="480" xlink:href="#p" fill="#000000" />
<use x="204" y="480" xlink:href="#p" fill="#000000" />
<use x="228" y="480" xlink:href="#p" fill="#000000" />
<use x="276" y="480" xlink:href="#p" fill="#000000" />
<use x="288" y="480" xlink:href="#p" fill="#000000" />
<use x="336" y="480" xlink:href="#p" fill="#000000" />
<use x="360" y="480" xlink:href="#p" fill="#000000" />
<use x="372" y="480" xlink:href="#p" fill="#000000" />
<use x="396" y="480" xlink:href="#p" fill="#000000" />
<use x="408" y="480" xlink:href="#p" fill="#000000" />
<use x="444" y="480" xlink:href="#p" fill="#000000" />
<use x="456" y="480" xlink:href="#p" fill="#000000" />
<use x="504" y="480" xlink:href="#p" fill="#000000" />
<use x="24" y="492" xlink:href="#p" fill="#000000" />
<use x="96" y="492" xlink:href="#p" fill="#000000" />
<use x="144" y="492" xlink:href="#p" fill="#000000" />
<use x="156" y="492" xlink:href="#p" fill="#000000" />
<use x="192" y="492" xlink:href="#p" fill="#000000" />
<use x="204" y="492" xlink:href="#p" fill="#000000" />
<use x="240" y="492" xlink:href="#p" fill="#000000" />
<use x="276" y="492" xlink:href="#p" fill="#000000" />
<use x="336" y="492" xlink:href="#p" fill="#000000" />
<use x="348" y="492" xlink:href="#p" fill="#000000" />
<use x="372" y="492" xlink:href="#p" fill="#000000" />
<use x="396" y="492" xlink:href="#p" fill="#000000" />
<use x="408" y="492" xlink:href="#p" fill="#000000" />
<use x="432" y="492" xlink:href="#p" fill="#000000" />
<use x="444" y="492" xlink:href="#p" fill="#000000" />
<use x="480" y="492" xlink:href="#p" fill="#000000" />
<use x="492" y="492" xlink:href="#p" fill="#000000" />
<use x="24" y="504" xlink:href="#p" fill="#000000" />
<use x="36" y="504" xlink:href="#p" fill="#000000" />
<use x="48" y="504" xlink:href="#p" fill="#000000" />
<use x="60" y="504" xlink:href="#p" fill="#000000" />
<use x="72" y="504" xlink:href="#p" fill="#000000" />
<use x="84" y="504" xlink:href="#p" fill="#000000" />
<use x="96" y="504" xlink:href="#p" fill="#000000" />
<use x="132" y="504" xlink:href="#p" fill="#000000" />
<use x="168" y="504" xlink:href="#p" fill="#000000" />
<use x="180" y="504" xlink:href="#p" fill="#000000" />
<use x="192" y="504" xlink:href="#p" fill="#000000" />
<use x="204" y="504" xlink:href="#p" fill="#000000" />
<use x="216" y="504" xlink:href="#p" fill="#000000" />
<use x="228" y="504" xlink:href="#p" fill="#000000" />
<use x="240" y="504" xlink:href="#p" fill="#000000" />
<use x="252" y="504" xlink:href="#p" fill="#000000" />
<use x="300" y="504" xlink:href="#p" fill="#000000" />
<use x="324" y="504" xlink:href="#p" fill="#000000" />
<use x="360" y="504" xlink:href="#p" fill="#000000" />
<use x="372" y="504" xlink:href="#p" fill="#000000" />
<use x="408" y="504" xlink:href="#p" fill="#000000" />
<use x="420" y="504" xlink:href="#p" fill="#000000" />
<use x="432" y="504" xlink:href="#p" fill="#000000" />
<use x="444" y="504" xlink:href="#p" fill="#000000" />
<use x="456" y="504" xlink:href="#p" fill="#000000" />
<use x="480" y="504" xlink:href="#p" fill="#000000" />
<use x="492" y="504" xlink:href="#p" fill="#000000" />
<use x="504" y="504" xlink:href="#p" fill="#000000" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 55 KiB