阅读时间 4 分钟最后更新: 2025-12-31 12:12

二次开发手册

本文档旨在引导开发者如何在 Hex2077 启动模板的基础上进行二次开发,涵盖了从新增业务逻辑到自定义 UI 的全流程。

开发前准备

在开始开发之前,请确保你已经阅读了以下基础文档:

数据库扩展

本项目使用 Drizzle ORM,支持 PostgreSQL 和 SQLite。

新增表结构

  1. 定义 Schema:

    • 如果使用 SQLite,修改 src/drizzle-sqlite-schema.ts
    • 如果使用 PostgreSQL,修改 src/drizzle-postgres-schema.ts
    • 注意:请保持两个文件的字段结构一致以便切换数据库
  2. 生成并推送变更:

pnpm drizzle-kit generate
pnpm drizzle-kit push
  1. 更新类型声明: 如果在代码中引用了新表,确保在 src/drizzle-schema.ts 中导出相关引用

业务逻辑开发 (Service Layer)

项目采用 Service 层封装业务逻辑,所有核心逻辑应放在 src/lib/services 目录下。

创建新服务

  1. 文件位置:

    • 终端用户逻辑:src/lib/services/client/
    • 管理后台逻辑:src/lib/services/admin/
  2. 编写模式:

// 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)

  1. 添加词条: 修改 public/locales/{lang}/ 下的 JSON 文件

  2. 代码调用:

    • Server Component: const { t } = await useTranslation(lang, 'common')
    • Client Component: const { t } = useTranslation('common')

支付网关集成

如果需要新增支付方式或修改现有逻辑:

  1. Gateway 接口: 参考 src/lib/payment/types.ts 中的 PaymentGateway 接口
  2. 新增实现: 在 src/lib/payment/gateways/ 下创建新的网关类
  3. 注册网关: 在 src/lib/payment/gateway-factory.ts 中添加逻辑
  4. Webhook 处理: 在 src/lib/payment/webhook-handlers/ 中添加对应的归一化处理(Normalizer)

管理后台扩展

  1. 新增页面: 在 src/app/[lang]/admin/ 下创建子目录
  2. 菜单配置: 在管理员侧边栏组件中添加导航项
  3. 面包屑导航: 面包屑导航用于展示用户当前所在的页面路径,帮助用户快速导航
  4. 日志记录: 重要操作务必记录日志
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 底部的社交媒体链接在 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' },
    // ... 其他平台
  ],
}

添加/删除社交平台

  1. 添加新平台:在对应数组中添加新对象
  2. 删除平台:从数组中移除对应对象
  3. 图标类型
    • 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>