三十的博客

GVM 脚手架探险记08:跟着 GVM 学习项目开发规范

发布时间
阅读量 加载中...

今天阅读 GVM 的官方文档,偶然发现了一个神仙栏目–介绍的是项目开发规范。我仔细阅读了遍,内容都和我这些年的摸爬滚打积累的经验有一定的相似之处。早些年我拥有这些规范资料会少走好多弯路…

我截取了 GVM 官方开发规范 中的部分我认为针对个人开发比较有价值的内容。

代码规范

Go 代码规范

命名规范

go
// 包名:小写,简短,有意义
package user
package system

// 常量:大写,下划线分隔
const (
    MAX_RETRY_COUNT = 3
    DEFAULT_TIMEOUT = 30
    API_VERSION     = "v1"
)

// 变量:驼峰命名
var (
    userService    *UserService
    configFilePath string
    isDebugMode    bool
)

// 函数:大写开头(公开),小写开头(私有)
func GetUserList() []User {}
func createUser() error {}

// 结构体:大写开头,驼峰命名
type UserService struct {
    db    *gorm.DB
    redis *redis.Client
}

// 接口:以 -er 结尾或描述性名称
type UserRepository interface {
    Create(user *User) error
    GetByID(id uint) (*User, error)
}

type Validator interface {
    Validate() error
}

注释规范

go
// Package user 提供用户管理相关功能
// 包括用户的增删改查、权限验证等操作
package user

// UserService 用户服务,负责处理用户相关的业务逻辑
type UserService struct {
    db    *gorm.DB
    redis *redis.Client
}

// NewUserService 创建用户服务实例
// 参数:
//   db: 数据库连接
//   redis: Redis连接
// 返回:
//   *UserService: 用户服务实例
func NewUserService(db *gorm.DB, redis *redis.Client) *UserService {
    return &UserService{
        db:    db,
        redis: redis,
    }
}

// GetUserByID 根据用户ID获取用户信息
// 该方法会先从缓存中查找,如果缓存中不存在则从数据库查询
// 参数:
//   id: 用户ID
// 返回:
//   *User: 用户信息,如果用户不存在则返回nil
//   error: 错误信息
func (s *UserService) GetUserByID(id uint) (*User, error) {
    // 实现逻辑...
}

错误处理规范

go
// 自定义错误类型
type UserError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Data  string `json:"data,omitempty"`
}

func (e *UserError) Error() string {
    return e.Message
}

// 错误常量定义
var (
    ErrUserNotFound     = &UserError{Code: 1001, Message: "用户不存在"}
    ErrUserExists       = &UserError{Code: 1002, Message: "用户已存在"}
    ErrInvalidPassword  = &UserError{Code: 1003, Message: "密码格式不正确"}
    ErrPermissionDenied = &UserError{Code: 1004, Message: "权限不足"}
)

// 错误处理示例
func (s *UserService) CreateUser(user *User) error {
    // 验证用户是否已存在
    exists, err := s.userExists(user.Username)
    if err != nil {
        return fmt.Errorf("检查用户是否存在失败: %w", err)
    }
    if exists {
        return ErrUserExists
    }

    // 验证密码强度
    if err := s.validatePassword(user.Password); err != nil {
        return fmt.Errorf("密码验证失败: %w", err)
    }

    // 创建用户
    if err := s.db.Create(user).Error; err != nil {
        return fmt.Errorf("创建用户失败: %w", err)
    }

    return nil
}

结构体标签规范

go
type User struct {
    ID        uint      `json:"id" gorm:"primarykey;comment:用户ID"`
    Username  string    `json:"username" gorm:"uniqueIndex;size:64;not null;comment:用户名" validate:"required,min=3,max=64"`
    Password  string    `json:"-" gorm:"size:255;not null;comment:密码" validate:"required,min=8"`
    Email     string    `json:"email" gorm:"uniqueIndex;size:128;comment:邮箱" validate:"email"`
    Phone     string    `json:"phone" gorm:"size:20;comment:手机号" validate:"phone"`
    Status    int       `json:"status" gorm:"default:1;comment:状态(1:启用 0:禁用)"`
    CreatedAt time.Time `json:"createdAt" gorm:"comment:创建时间"`
    UpdatedAt time.Time `json:"updatedAt" gorm:"comment:更新时间"`
    DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
}

// TableName 指定表名
func (User) TableName() string {
    return "sys_users"
}

Js/Vue 代码规范

命名规范

js
// 常量:大写,下划线分隔
const API_BASE_URL = "http://localhost:8888";
const MAX_FILE_SIZE = 10 * 1024 * 1024;

// 变量:驼峰命名
const userName = "admin";
const isLoggedIn = true;
const userList = [];

// 函数:驼峰命名,动词开头
function getUserList() {}
function createUser() {}
function validateForm() {}
function handleSubmit() {}

// 类:大写开头,驼峰命名
class UserService {
  constructor() {}

  async getUsers() {}
  async createUser(userData) {}
}

// 组件:大写开头,驼峰命名
const UserList = {
  name: "UserList",
  // ...
};

Vue 组件规范

vue
<template>
  <!-- 使用语义化的HTML标签 -->
  <div class="user-list-container">
    <!-- 条件渲染使用 v-if/v-show -->
    <div v-if="loading" class="loading-wrapper">
      <el-loading />
    </div>

    <!-- 列表渲染使用 v-for必须添加 key -->
    <div
      v-for="user in userList"
      :key="user.id"
      class="user-item"
      @click="handleUserClick(user)"
    >
      {{ user.username }}
    </div>

    <!-- 事件处理使用方法名不使用内联表达式 -->
    <el-button @click="handleAddUser">添加用户</el-button>
  </div>
</template>

<script>
import { ref, reactive, computed, onMounted } from "vue";
import { ElMessage } from "element-plus";
import { getUserList, createUser } from "@/api/user";

export default {
  name: "UserList",

  // Props 定义要详细
  props: {
    departmentId: {
      type: [String, Number],
      required: true,
      validator: (value) => value > 0,
    },
    showActions: {
      type: Boolean,
      default: true,
    },
  },

  // Emits 声明
  emits: ["user-selected", "user-created"],

  setup(props, { emit }) {
    // 响应式数据
    const loading = ref(false);
    const userList = ref([]);
    const searchForm = reactive({
      keyword: "",
      status: 1,
    });

    // 计算属性
    const filteredUsers = computed(() => {
      return userList.value.filter((user) =>
        user.username.includes(searchForm.keyword)
      );
    });

    // 方法
    const fetchUserList = async () => {
      try {
        loading.value = true;
        const { data } = await getUserList({
          departmentId: props.departmentId,
          ...searchForm,
        });
        userList.value = data.list;
      } catch (error) {
        ElMessage.error("获取用户列表失败");
        console.error("获取用户列表失败:", error);
      } finally {
        loading.value = false;
      }
    };

    const handleUserClick = (user) => {
      emit("user-selected", user);
    };

    const handleAddUser = () => {
      // 处理添加用户逻辑
    };

    // 生命周期
    onMounted(() => {
      fetchUserList();
    });

    return {
      loading,
      userList,
      searchForm,
      filteredUsers,
      fetchUserList,
      handleUserClick,
      handleAddUser,
    };
  },
};
</script>

<style lang="scss" scoped>
// 使用 BEM 命名规范
.user-list-container {
  padding: 20px;

  .loading-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 200px;
  }

  .user-item {
    padding: 10px;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    margin-bottom: 10px;
    cursor: pointer;
    transition: all 0.3s;

    &:hover {
      background-color: #f5f7fa;
      border-color: #409eff;
    }

    &--active {
      background-color: #ecf5ff;
      border-color: #409eff;
    }
  }
}
</style>

API 调用规范

js
// api/user.js
import request from "@/utils/request";

// API 函数命名:动词 + 资源名
export function getUserList(params) {
  return request({
    url: "/user/getUserList",
    method: "post",
    data: params,
  });
}

export function createUser(data) {
  return request({
    url: "/user/register",
    method: "post",
    data,
  });
}

export function updateUser(id, data) {
  return request({
    url: `/user/setUserInfo`,
    method: "put",
    data: { ...data, ID: id },
  });
}

export function deleteUser(id) {
  return request({
    url: "/user/deleteUser",
    method: "delete",
    data: { ID: id },
  });
}

// 在组件中使用
import { getUserList, createUser } from "@/api/user";

export default {
  setup() {
    const fetchUsers = async () => {
      try {
        const response = await getUserList({ page: 1, pageSize: 10 });
        if (response.code === 0) {
          userList.value = response.data.list;
        } else {
          ElMessage.error(response.msg || "获取用户列表失败");
        }
      } catch (error) {
        ElMessage.error("网络错误,请稍后重试");
        console.error("API调用失败:", error);
      }
    };

    return { fetchUsers };
  },
};

Git 工作流

分支管理

bash
# 主要分支
main/master    # 主分支,用于生产环境
develop        # 开发分支,用于集成开发

# 功能分支
feature/user-management     # 功能开发分支
feature/api-optimization    # 功能开发分支

# 修复分支
hotfix/login-bug           # 紧急修复分支
bugfix/user-validation     # 普通修复分支

# 发布分支
release/v1.2.0             # 发布准备分支

提交规范

提交信息格式
txt
<type>(<scope>): <subject>

<body>

<footer>
类型说明
bash
# 功能相关
feat:     新功能
fix:      修复bug
perf:     性能优化

# 代码相关
refactor: 重构代码
style:    代码格式调整

# 文档和测试
docs:     文档更新
test:     测试相关

# 构建和配置
build:    构建系统或依赖更新
ci:       CI配置更新
chore:    其他杂项

# 版本相关
revert:   回滚提交
提交示例
bash
# 新功能
git commit -m "feat(user): 添加用户批量导入功能

- 支持Excel文件导入
- 添加数据验证
- 支持导入进度显示

Closes #123"

# 修复bug
git commit -m "fix(auth): 修复JWT token过期时间计算错误

修复了token过期时间计算不准确的问题,
现在使用UTC时间进行计算。

Fixes #456"

# 重构
git commit -m "refactor(api): 重构用户API响应结构

BREAKING CHANGE: 用户API响应格式发生变化
- 将user_info改为userInfo
- 添加了权限信息字段"

分支操作流程

功能开发流程
bash
# 1. 从develop分支创建功能分支
git checkout develop
git pull origin develop
git checkout -b feature/user-management

# 2. 开发功能
# ... 编写代码 ...

# 3. 提交代码
git add .
git commit -m "feat(user): 添加用户管理功能"

# 4. 推送到远程
git push origin feature/user-management

# 5. 创建Pull Request
# 在GitHub/GitLab上创建PR,请求合并到develop分支

# 6. 代码审查通过后合并
# 删除功能分支
git checkout develop
git pull origin develop
git branch -d feature/user-management
git push origin --delete feature/user-management
紧急修复流程
bash
# 1. 从main分支创建hotfix分支
git checkout main
git pull origin main
git checkout -b hotfix/login-bug

# 2. 修复问题
# ... 修复代码 ...

# 3. 提交修复
git add .
git commit -m "fix(auth): 修复登录验证失败问题"

# 4. 合并到main和develop
git checkout main
git merge hotfix/login-bug
git push origin main

git checkout develop
git merge hotfix/login-bug
git push origin develop

# 5. 删除hotfix分支
git branch -d hotfix/login-bug
git push origin --delete hotfix/login-bug

# 6. 创建版本标签
git tag -a v1.1.1 -m "修复登录验证问题"
git push origin v1.1.1

VSCode 配置

编辑器配置

json
{
  "go.formatTool": "goimports",
  "go.lintTool": "golangci-lint",
  "go.testFlags": ["-v"],
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.organizeImports": true
  },
  "eslint.validate": ["javascript", "javascriptreact", "vue"],
  "vetur.format.defaultFormatter.html": "prettier",
  "vetur.format.defaultFormatter.js": "prettier",
  "vetur.format.defaultFormatter.css": "prettier"
}

推荐扩展

json
{
  "recommendations": [
    "golang.go",
    "vue.volar",
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "bradlc.vscode-tailwindcss",
    "ms-vscode.vscode-json",
    "redhat.vscode-yaml"
  ]
}

eslintrc.js 参考

js
module.exports = {
  root: true,
  env: {
    node: true,
    browser: true,
    es2021: true,
  },
  extends: ["plugin:vue/vue3-essential", "@vue/standard"],
  parserOptions: {
    ecmaVersion: 2021,
    sourceType: "module",
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    "vue/multi-word-component-names": "off",
    "space-before-function-paren": ["error", "never"],
    "comma-dangle": ["error", "never"],
  },
};

Git Hooks 参考

pre-commit

bash
#!/bin/sh
# .git/hooks/pre-commit

echo "Running pre-commit checks..."

# 检查Go代码格式
if ! gofmt -l . | grep -q '^$'; then
    echo "Go code is not formatted. Please run 'gofmt -w .'"
    exit 1
fi

# 运行Go测试
if ! go test ./...; then
    echo "Go tests failed"
    exit 1
fi

# 检查前端代码格式
cd web
if ! npm run lint; then
    echo "Frontend linting failed"
    exit 1
fi

echo "Pre-commit checks passed!"

commit-msg

bash
#!/bin/sh
# .git/hooks/commit-msg

commit_regex='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,50}'

if ! grep -qE "$commit_regex" "$1"; then
    echo "Invalid commit message format!"
    echo "Format: <type>(<scope>): <subject>"
    echo "Example: feat(user): add user registration"
    exit 1
fi
#Gvm #开发规范 #Golang