coding-standards.md 16 KB

FranklinWH-ToolBox AI 编程规范与操作规范

项目概览

本项目是 FranklinWH 内部工具箱平台,采用 monorepo 结构(pnpm workspace),包含前端 Web 应用和后端 API 服务。

  • 前端:Vue 3 + TypeScript + Vite (rolldown-vite)
  • 后端:Go 1.25+ + Fiber v2
  • 包管理:pnpm(前端)、Go Modules(后端)

一、项目结构规范

前端目录结构(根目录)

src/
├── assets/          # 静态资源(CSS、图片、数据文件)
├── components/      # 全局通用组件
├── composable/      # Vue Composables(可组合函数)
├── event/           # 全局事件系统(EventEmitter)
├── injection/       # Vue provide/inject 相关
├── layout/          # 布局组件(header、aside、footer、main)
├── router/          # 路由配置、守卫、辅助函数
├── service/         # API 请求层(按业务模块分目录)
├── stores/          # Pinia 状态管理
├── theme/           # 主题配置(light/dark/common)
├── types/           # TypeScript 类型声明
├── typings/         # 全局类型扩展(window.d.ts、vue-router.d.ts)
├── utils/           # 工具函数
├── views/           # 页面视图组件(按业务模块分目录)
├── App.vue          # 根组件
└── main.ts          # 入口文件

后端目录结构(service/)

service/
├── cmd/             # 脚本(部署、构建、日志查看)
├── cache/           # 缓存层(Redis)
├── config/          # 配置加载(Viper)
├── controllers/     # 控制器层(请求处理、参数校验)
├── database/        # 数据库初始化(MySQL、PostgreSQL、MongoDB)
├── domain/          # 领域模型/实体定义
├── dto/             # 数据传输对象
├── docs/            # Swagger 文档
├── function/        # 功能模块(文件处理等)
├── jobs/            # 后台定时任务
├── logger/          # 日志配置(Zap)
├── middleware/      # 中间件(认证等)
├── models/          # 数据库模型(GORM)
├── networkEvents/   # 网络事件(Evio)
├── repositories/    # 数据访问层(MySQL/PgSQL/MongoDB/Redis/MQTT/API)
├── routes/          # 路由注册
├── scheduler/       # 定时调度器(Cron)
├── services/        # 业务逻辑层
├── task/            # 异步任务队列(Asynq)
├── test/            # 测试文件
└── utils/           # 工具包

二、前端编码规范

2.1 通用格式

项目 规范
缩进 2 空格
分号 不使用分号
引号 单引号
行宽 100 字符
行尾 LF
尾逗号 ES5 风格
文件末尾 保留空行

2.2 TypeScript

  • 启用严格模式(strict: true)
  • 使用路径别名 @/ 指向 src/
  • 优先使用 interface 定义对象类型,type 用于联合类型/工具类型
  • 避免 any,必要时使用 unknown 并进行类型收窄
  • 导出类型时使用 export type / export interface

2.3 Vue 组件

<template>
  <!-- 使用 UnoCSS 原子类 -->
  <div class="flex items-center gap-4 p-4">
    <NButton @click="handleClick">{{ displayTitle }}</NButton>
  </div>
</template>

<script setup lang="ts">
// 1. 第三方库导入
import { NButton } from 'naive-ui'
import { ref, computed, onMounted } from 'vue'

// 2. 内部模块导入
import { useXxxStore } from '@/stores/xxx'
import type { SomeType } from '@/service/xxx/interface'

// 3. 组件选项
defineOptions({
  name: 'ComponentName',
})

// 4. Props & Emits
const props = defineProps<{
  title: string
  count?: number
}>()

const emit = defineEmits<{
  update: [value: string]
}>()

// 5. 响应式状态
const loading = ref(false)

// 6. 计算属性
const displayTitle = computed(() => `${props.title} (${props.count ?? 0})`)

// 7. 方法
function handleClick() {
  emit('update', 'new value')
}

// 8. 生命周期
onMounted(() => {
  // ...
})
</script>

关键约定:

  • 使用 <script setup lang="ts"> + Composition API
  • 使用 defineOptions({ name: 'XxxPage' }) 定义组件名
  • Props 使用 defineProps<T>() 泛型语法
  • Emits 使用 defineEmits<T>() 泛型语法
  • 不在 template 中使用复杂表达式,提取为 computed 或方法
  • 模板中组件使用 PascalCase(如 <NButton>)

2.4 Pinia Store

import { useStorage } from '@vueuse/core'
import { acceptHMRUpdate, defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useXxxStore = defineStore('xxxStore', () => {
  // 持久化状态使用 useStorage
  const data = useStorage<DataType>('storageKey', defaultValue)

  // 非持久化状态使用 ref
  const loading = ref(false)

  // Getters 使用 computed
  const isEmpty = computed(() => data.value.length === 0)

  // Actions 使用普通函数
  function reset() {
    data.value = defaultValue
  }

  return {
    data,
    loading,
    isEmpty,
    reset,
  }
})

// HMR 支持(必须)
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useXxxStore, import.meta.hot))
}

2.5 Service 层(API 请求)

每个业务模块在 src/service/ 下创建独立目录,包含:

  • index.ts — API 函数导出
  • interface.ts — 请求/响应类型定义

    // src/service/moduleName/interface.ts
    export interface ModuleItem {
    id: string
    name: string
    createdAt: string
    }
    
    // src/service/moduleName/index.ts
    import http from '@/service/serverReq'
    import type { ModuleItem } from './interface'
    
    const API_List = {
    list: '/moduleName/list',
    detail: '/moduleName/detail',
    }
    
    export const getModuleList = async (page: number, size: number): Promise<ModuleItem[]> => {
    return await http.get<ModuleItem[]>(API_List.list, {
    params: { page, size },
    })
    }
    
    export const getModuleDetail = async (id: string): Promise<ModuleItem> => {
    return await http.get<ModuleItem>(API_List.detail, {
    params: { id },
    })
    }
    

2.6 样式规范

  • 优先使用 UnoCSS 原子类,避免自定义 CSS
  • 暗色模式使用 dark: 前缀变体
  • 响应式使用 sm: / md: / lg: / xl: 前缀
  • CSS 变量引用使用 var(--xxx) 语法(如 bg-[var(--card-color)])
  • 图标使用 UnoCSS Icons(如 i-ph:arrow-up、i-mdi:close)
  • 过渡动画使用项目自定义 transition-[property]-duration 规则

2.7 路由

  • 路由记录定义在 src/router/record.ts
  • 懒加载页面组件:component: () => import('@/views/xxx/index.vue')
  • 页面视图目录使用 kebab-case 命名(如 site-info-manage)
  • 路由 name 使用 camelCase(如 siteInfoManage)

三、后端编码规范(Go)

3.1 分层职责

层级 职责 命名规范
routes 路由注册 xxxRoutes.go
controllers 请求处理、参数校验、响应格式化 xxxControllers.go
services 业务逻辑编排 xxxServices.go
repositories 数据访问(DB/Cache/API/MQTT) 按数据源分子目录
domain 领域实体结构定义 xxxDomain.go
dto 请求/响应数据传输对象 xxxDTO.go

3.2 路由文件

package routes

import (
    "fwh-toolbox-server/controllers"
    "github.com/gofiber/fiber/v2"
)

func XxxSetup(api fiber.Router) {
    api.Get("/xxx/list", controllers.GetXxxList)
    api.Post("/xxx/create", controllers.CreateXxx)
}

3.3 Controller

package controllers

import (
    "fwh-toolbox-server/services"
    "github.com/gofiber/fiber/v2"
    "go.uber.org/zap"
)

func GetXxxList(c *fiber.Ctx) error {
    page := c.QueryInt("page", 1)
    size := c.QueryInt("size", 20)

    data, err := services.GetXxxList(uint(page), uint(size))
    if err != nil {
        zap.L().Error("GetXxxList failed.", zap.String("err", err.Error()))
        return Error(c, "Get xxx list failed.")
    }
    return Success(c, data)
}

关键约定:

  • 使用统一响应函数:Success、SuccessPage、Fail、Error、NotFound、Unauthorized、Forbidden
  • 参数校验在 Controller 层完成
  • 错误日志使用 zap.L() 结构化日志
  • 业务错误返回中文/英文描述,不暴露内部错误细节

3.4 Service

package services

import (
    "fwh-toolbox-server/repositories"
    "go.uber.org/zap"
)

func GetXxxList(page, size uint) (*SomeResult, error) {
    data, err := repositories.GetXxxList(page, size)
    if err != nil {
        zap.L().Error("repositories.GetXxxList failed.",
            zap.String("err", err.Error()),
            zap.Uint("page", page),
            zap.Uint("size", size),
        )
        return nil, err
    }
    // 业务逻辑处理...
    return result, nil
}

3.5 日志规范

  • 使用 go.uber.org/zap 结构化日志
  • 日志级别:Debug < Info < Warn < Error < Fatal
  • 错误日志必须包含错误信息和关键上下文参数
  • 格式:zap.L().Error("描述", zap.String("key", value), ...)

3.6 配置管理

  • 使用 Viper 读取配置(支持 .env 文件 + 环境变量)
  • 配置结构体定义在 config/config.go
  • 密码等敏感信息通过环境变量注入,不硬编码

四、通用开发规范

4.1 命名规范

类型 前端 后端
文件名 camelCase.ts / kebab-case.vue camelCase.go
变量 camelCase camelCase
常量 UPPER_SNAKE_CASE 或 camelCase UPPER_SNAKE_CASE 或 camelCase
函数 camelCase PascalCase(导出)/ camelCase(私有)
类型/接口 PascalCase PascalCase
组件 PascalCase(.vue 文件名可 index.vue 或 camelCase) —
CSS 类 UnoCSS 原子类 / kebab-case —
路由路径 kebab-case camelCase
目录名 kebab-case(views)/ camelCase(service、stores) camelCase

4.2 Git 提交规范

<type>(<scope>): <subject>

type: feat | fix | docs | style | refactor | perf | test | chore | ci
scope: 可选,影响范围(如 frontend, backend, router, store)
subject: 简短描述,不超过 50 字符

4.3 注释规范

  • 前端:重要逻辑使用 // 行注释,中文或英文均可
  • 后端:Controller 函数头部使用 // 功能描述 注释
  • 路由文件头部注释 API 清单:

    /*
    * Module Name API List
    * /module/action1 - GET 描述
    * /module/action2 - POST 描述
    */
    

五、工具链与命令

前端命令

命令 用途
pnpm dev 启动开发服务器
pnpm build 类型检查 + 生产构建
pnpm test:unit 运行 Vitest 单元测试
pnpm test:e2e 运行 Playwright E2E 测试
pnpm lint ESLint 检查
pnpm lint:fix ESLint 自动修复
pnpm format:check Prettier 格式检查
pnpm format:fix Prettier 自动格式化

后端命令

命令 用途
go run main.go 启动服务(在 service/ 目录执行)
go build -o fwh-toolbox-server 编译(在 service/ 目录执行)
go test ./... 运行测试
./cmd/deploy.sh 部署脚本(在 service/ 目录执行)

ESLint 配置要点

  • 基于 @antfu/eslint-config
  • 禁用的规则:perfectionist/sort-imports、vue/block-order、no-console(开发期)
  • 强制 Vue SFC 使用 <script lang="ts"> 或 <script lang="tsx">
  • 启用 UnoCSS、Vitest、Playwright 插件支持

六、AI 操作规范

6.1 代码生成

  • 生成前端代码时,遵循本项目的 Composition API + <script setup> 风格
  • 使用 Naive UI 组件库,不引入其他 UI 框架
  • 样式优先使用 UnoCSS 原子类
  • API 请求使用 @/service/serverReq 中封装的 http 实例
  • 新页面按 src/views/module-name/index.vue 结构创建
  • 新 API 模块按 src/service/moduleName/{index.ts, interface.ts} 结构创建
  • Store 使用 setup function 风格 + HMR 热更新支持
  • 后端新增接口遵循 routes → controllers → services → repositories 分层

6.2 代码修改

  • 修改前先读取相关文件,理解现有实现
  • 保持与周围代码一致的风格
  • 不随意删除注释掉的代码(可能是有意保留的备选方案)
  • 修改前需详细说明修改或创建的内容,获得同意后再写入文件
  • 修改后确保通过 pnpm lint 检查

6.3 依赖管理

  • 前端使用 pnpm 安装依赖,锁定具体版本
  • 后端使用 go get 添加依赖
  • 不引入与现有技术栈冲突的库(如不使用 Axios、Element UI、Tailwind CSS)
  • 工具函数优先使用 es-toolkit(替代 lodash)
  • 日期处理使用 dayjs
  • 状态持久化使用 @vueuse/core 的 useStorage

6.4 类型安全

  • 所有新增 API 响应必须定义 TypeScript 接口
  • 后端 DTO 结构体必须添加 JSON tag
  • 避免使用 any、interface{}(Go 中优先使用泛型或具体类型)

6.5 测试

  • 单元测试文件放在 src/**/__tests__/ 下
  • 使用 Vitest 编写测试,测试文件后缀 .test.ts
  • E2E 测试放在 e2e/ 目录,使用 Playwright
  • 后端测试放在 service/test/ 目录

6.6 安全注意事项

  • 不在代码中硬编码密钥、Token、密码
  • .env 文件中的敏感配置不应被提交到仓库
  • API 请求自动附带 Authorization header(通过拦截器)
  • 后端使用 middleware 进行认证校验

6.7 文件操作

  • 编辑前端文件时,工作目录为项目根目录
  • 编辑后端文件时,注意文件在 service/ 子目录内
  • Go 模块路径为 fwh-toolbox-server
  • Go import 使用完整模块路径(如 fwh-toolbox-server/controllers)

七、技术栈速查

前端核心依赖

库 用途 版本要求
Vue 3 框架 ^3.5
Naive UI UI 组件库 ^2.44
Pinia 状态管理 ^3.0
Vue Router 路由 ^5.0
VueUse 组合式工具 ^14.x
UnoCSS 原子 CSS ^66.x
@antv/g2 图表 ^5.x
es-toolkit 工具函数 ^1.x
dayjs 日期处理 ^1.x
monaco-editor 代码编辑器 ^0.55

后端核心依赖

库 用途
Fiber v2 Web 框架
GORM ORM(MySQL/PostgreSQL)
mongo-driver MongoDB 驱动
go-redis/v9 Redis 客户端
zap 结构化日志
viper 配置管理
asynq 异步任务队列
cron/v3 定时任务
minio-go 对象存储
paho.mqtt MQTT 客户端
casdoor-go-sdk SSO 认证
excelize Excel 处理
swag Swagger 文档生成