二次开发手册
本文档旨在引导开发者如何在 Hex2077 启动模板的基础上进行二次开发,涵盖了从新增业务逻辑到自定义 UI 的全流程。
开发前准备
在开始开发之前,请确保你已经阅读了以下基础文档:
数据库扩展
本项目使用 Drizzle ORM,支持 PostgreSQL 和 SQLite。
新增表结构
-
定义 Schema:
- 如果使用 SQLite,修改
src/drizzle-sqlite-schema.ts - 如果使用 PostgreSQL,修改
src/drizzle-postgres-schema.ts - 注意:请保持两个文件的字段结构一致以便切换数据库
- 如果使用 SQLite,修改
-
生成并推送变更:
pnpm drizzle-kit generate
pnpm drizzle-kit push
- 更新类型声明: 如果在代码中引用了新表,确保在
src/drizzle-schema.ts中导出相关引用
业务逻辑开发 (Service Layer)
项目采用 Service 层封装业务逻辑,所有核心逻辑应放在 src/lib/services 目录下。
创建新服务
-
文件位置:
- 终端用户逻辑:
src/lib/services/client/ - 管理后台逻辑:
src/lib/services/admin/
- 终端用户逻辑:
-
编写模式:
// src/lib/services/client/my-service.ts
export class MyService {
async doSomething(userId: string, data: any) {
// 1. 业务逻辑处理
// 2. 数据库操作 (db.insert, db.select...)
// 3. 返回结果
}
}
export const myService = new MyService();
API 路由与权限
新增 API 路由
在 src/app/api 下创建对应的路由文件。
身份验证与权限校验
用户验证:
import { verifyUserInApiRoute } from '@/lib/server/admin-api-utils';
const session = await verifyUserInApiRoute(req);
管理员权限验证:
import { checkPermissionWithResponse } from '@/lib/server/admin-api-utils';
import { Permission } from '@/lib/constants/permissions';
await checkPermissionWithResponse(req, Permission.USER_VIEW);
前端界面开发
组件开发
- 基础组件: 使用
src/components/ui/下的 Shadcn UI 组件 - 业务组件: 放在
src/components/ - 样式: 必须使用 Tailwind CSS 4。禁止使用固定颜色,使用语义化变量(如
text-[var(--color-text-primary)])
国际化 (i18next)
-
添加词条: 修改
public/locales/{lang}/下的 JSON 文件 -
代码调用:
- Server Component:
const { t } = await useTranslation(lang, 'common') - Client Component:
const { t } = useTranslation('common')
- Server Component:
支付网关集成
如果需要新增支付方式或修改现有逻辑:
- Gateway 接口: 参考
src/lib/payment/types.ts中的PaymentGateway接口 - 新增实现: 在
src/lib/payment/gateways/下创建新的网关类 - 注册网关: 在
src/lib/payment/gateway-factory.ts中添加逻辑 - Webhook 处理: 在
src/lib/payment/webhook-handlers/中添加对应的归一化处理(Normalizer)
管理后台扩展
- 新增页面: 在
src/app/[lang]/admin/下创建子目录 - 菜单配置: 在管理员侧边栏组件中添加导航项
- 面包屑导航: 面包屑导航用于展示用户当前所在的页面路径,帮助用户快速导航
- 日志记录: 重要操作务必记录日志
import { adminOperationLogs } from '@/lib/services/admin/admin-service';
await adminOperationLogs.log({ action: 'UPDATE', module: 'USER', ... });
import { logSimpleOperation } from '@/lib/services/admin/admin-service';
await logSimpleOperation({ action: 'UPDATE', module: 'USER', ... });
常用工具函数
| 函数 | 说明 |
|---|---|
cn(...) | 类名合并 |
useDateFormat() | 日期本地化 |
formatPrice(amount, currency) | 价格格式化 |
fetchPost(url, body) | 封装好的网络请求 |
页面固定内容修改
本节介绍如何修改页面中的固定内容,包括语言/货币、公司信息、SEO 配置和社交媒体链接。
语言和货币配置
详细的语言和货币配置说明请参考 语言货币配置。
主要涉及的文件:
src/i18n/settings.ts: 语言配置src/lib/utils.ts: 币种-语言映射、价格格式化public/locales/: 多语言翻译文件目录- 管理后台设置页面:
src/app/[lang]/admin/system/settings/page.tsx
公司信息配置
公司信息用于隐私政策、服务条款等页面的展示。
配置方式
方式一:环境变量配置(推荐用于初始化)
在 .env 文件中设置:
NEXT_PUBLIC_COMPANY_NAME="Your Company Name"
NEXT_PUBLIC_COMPANY="Your Company"
NEXT_PUBLIC_PRIVACY_EMAIL="privacy@yourcompany.com"
NEXT_PUBLIC_SUPPORT_EMAIL="support@yourcompany.com"
NEXT_PUBLIC_WEBSITE="https://yourcompany.com"
NEXT_PUBLIC_ADDRESS="Your Company Address"
NEXT_PUBLIC_PHONE="+1 (555) 123-4567"
NEXT_PUBLIC_DPO_EMAIL="dpo@yourcompany.com"
NEXT_PUBLIC_DPO_ADDRESS="DPO Address"
方式二:管理后台配置(推荐用于运行时修改)
通过管理后台的系统设置页面修改,设置会存储在数据库的 admin_settings 表中,键名格式为 company.xxx。
代码中使用
import { getCompanySettings } from '@/lib/services/settings/company-settings-service'
const companySettings = await getCompanySettings()
console.log(companySettings.name) // 公司名称
console.log(companySettings.support_email) // 客服邮箱
console.log(companySettings.website) // 公司网站
SEO 配置
SEO 设置支持多语言配置,用于网站的元数据、Open Graph 和 Twitter Card。
配置方式
方式一:环境变量配置(推荐用于初始化)
在 .env 文件中设置:
NEXT_PUBLIC_SITE_NAME="Your Site Name"
NEXT_PUBLIC_SITE_DESCRIPTION="Your site description"
NEXT_PUBLIC_SITE_KEYWORDS="keyword1,keyword2,keyword3"
NEXT_PUBLIC_SITE_URL="https://yoursite.com"
NEXT_PUBLIC_OG_IMAGE="/og-image.png"
NEXT_PUBLIC_TWITTER_HANDLE="@yourhandle"
NEXT_PUBLIC_TWITTER_CARD="summary_large_image"
方式二:管理后台配置(推荐用于运行时修改)
通过管理后台的系统设置页面修改,支持为每种语言单独配置 SEO 信息。
代码中使用
import { getSEOSettings, getSEOSettingsForLang } from '@/lib/services/settings/seo-settings-service'
// 获取所有 SEO 设置
const seoSettings = await getSEOSettings()
console.log(seoSettings.siteUrl) // 网站 URL
console.log(seoSettings.localized['zh-CN']) // 中文 SEO 设置
// 获取指定语言的 SEO 设置
const zhSEO = await getSEOSettingsForLang('zh-CN')
console.log(zhSEO.siteName) // 中文站点名称
console.log(zhSEO.siteDescription) // 中文站点描述
Footer 社交媒体配置
Footer 底部的社交媒体链接在 src/components/Footer.tsx 中配置。
修改社交媒体链接
找到 socialPlatforms 配置对象,修改对应平台的 href 属性:
// src/components/Footer.tsx
const socialPlatforms = {
// 国际平台
international: [
{ name: 'X', icon: XLogoIcon, href: 'https://x.com/yourhandle', type: 'phosphor' },
{ name: 'GitHub', icon: GithubLogoIcon, href: 'https://github.com/yourorg', type: 'phosphor' },
{ name: 'Discord', icon: DiscordLogoIcon, href: 'https://discord.gg/yourinvite', type: 'phosphor' },
// ... 其他平台
],
// 中国平台
china: [
{ name: '微信', icon: WechatLogoIcon, href: '#', type: 'phosphor' },
{ name: 'B站', icon: SiBilibili, href: 'https://space.bilibili.com/yourid', type: 'si' },
// ... 其他平台
],
}
添加/删除社交平台
- 添加新平台:在对应数组中添加新对象
- 删除平台:从数组中移除对应对象
- 图标类型:
phosphor: 使用@phosphor-icons/react图标si: 使用react-icons/si图标text: 纯文本显示(无图标)
修改版权信息
在 Footer 组件底部修改版权文本:
<p className="text-sm text-[var(--color-text-muted)]">
© 2025 Your Company. All rights reserved.
</p>