π Code Generator β
Gin-Vue-Admin provides a powerful code generator that supports automatically generating complete CRUD code based on database table structure, including backend APIs, frontend pages, route configuration, etc., greatly improving development efficiency.
π― Feature Overview β
Generated Content β
- Backend Code: Model, Service, API, Router
- Frontend Code: Vue pages, API interfaces, route configuration
- Database: Auto table creation, field validation
- Permission Configuration: API permissions, menu permissions
Supported Features β
- π CRUD Operations: Complete CRUD operations
- π Conditional Queries: Support multi-field condition filtering
- π Pagination Queries: Automatic pagination processing
- π File Upload: Support file field processing
- π Join Queries: Support table join operations
- π¨ Custom Templates: Customizable code templates
π οΈ Usage Methods β
1. Web Interface Generation β
Access System Management β Code Generator, configure through visual interface:
System Tools β Code Generator β Add NewConfiguration Steps β
Basic Information Configuration
- Table name
- Table description
- Struct name
- Package name
- File name
Field Configuration
- Field name
- Field type
- Database type
- Field description
- Required or not
- Query conditions
- Dictionary type
Generation Options
- Generation module
- Generation path
- Whether to overwrite
2. Command Line Generation β
# Enter project directory
cd server
# Run code generator
go run main.go -c=config.yaml -gva=gen3. API Interface Generation β
# Use curl to call generation interface
curl -X POST "http://localhost:8888/autoCode/createTemp" \
-H "Content-Type: application/json" \
-d '{
"structName": "User",
"tableName": "sys_users",
"packageName": "system",
"fields": [
{
"fieldName": "Name",
"fieldDesc": "Username",
"fieldType": "string",
"dataType": "varchar(255)",
"fieldJson": "name",
"require": true,
"errorText": "Please enter username"
}
]
}'π Configuration Details β
Basic Configuration Structure β
type AutoCodeStruct struct {
StructName string `json:"structName"` // Struct name
TableName string `json:"tableName"` // Table name
PackageName string `json:"packageName"` // Package name
HumpPackageName string `json:"humpPackageName"` // Camel case package name
Abbreviation string `json:"abbreviation"` // Abbreviation
Description string `json:"description"` // Description
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // Automatically create API
AutoCreateResource bool `json:"autoCreateResource"` // Automatically create resources
AutoMoveFile bool `json:"autoMoveFile"` // Automatically move files
Fields []Field `json:"fields"` // Field list
DictTypes []string `json:"dictTypes"` // Dictionary types
Package string `json:"package"` // Full package path
PackageT string `json:"packageT"` // Template package path
NeedValid bool `json:"needValid"` // Validation required
HasTimer bool `json:"hasTimer"` // Includes time fields
NeedSort bool `json:"needSort"` // Sorting required
HasSearchTimer bool `json:"hasSearchTimer"` // Includes search time
HasFile bool `json:"hasFile"` // Includes file fields
HasDataSource bool `json:"hasDataSource"` // Includes data source
HasRichText bool `json:"hasRichText"` // Includes rich text
HasPic bool `json:"hasPic"` // Includes pictures
LogicDelete bool `json:"logicDelete"` // Logical deletion
MenuID string `json:"menuID"` // Menu ID
}Field Configuration Structure β
type Field struct {
FieldName string `json:"fieldName"` // Field name
FieldDesc string `json:"fieldDesc"` // Field description
FieldType string `json:"fieldType"` // Go field type
FieldJson string `json:"fieldJson"` // JSON tag
DataType string `json:"dataType"` // Database field type
ColumnName string `json:"columnName"` // Database column name
Comment string `json:"comment"` // Comment
Require bool `json:"require"` // Required
ErrorText string `json:"errorText"` // Error message
Clearable bool `json:"clearable"` // Clearable
Sort bool `json:"sort"` // Sort field
PrimaryKey bool `json:"primaryKey"` // Primary key
DefaultValue string `json:"defaultValue"` // Default value
DictType string `json:"dictType"` // Dictionary type
Front bool `json:"front"` // Frontend display
Desc bool `json:"desc"` // Descending order
SearchType string `json:"searchType"` // Search type
FieldSearchType string `json:"fieldSearchType"` // Field search type
FieldSearchHide bool `json:"fieldSearchHide"` // Hide search
FieldIndexType string `json:"fieldIndexType"` // Index type
CheckDataSource bool `json:"checkDataSource"` // Check data source
DataSource struct {
Association int `json:"association"` // Association type
Table string `json:"table"` // Associated table
Label string `json:"label"` // Display field
Value string `json:"value"` // Value field
} `json:"dataSource"` // Data source configuration
}π¨ Template System β
Template Directory Structure β
server/resource/template/
βββ web/
β βββ api.js.tpl # Frontend API template
β βββ form.vue.tpl # Form page template
β βββ table.vue.tpl # List page template
β βββ ...
βββ server/
βββ api.go.tpl # Backend API template
βββ model.go.tpl # Model template
βββ request.go.tpl # Request structure template
βββ router.go.tpl # Router template
βββ service.go.tpl # Service template
βββ ...Custom Templates β
Backend Template Example β
// model.go.tpl
package {{.PackageName}}
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
{{- if .HasTimer}}
"time"
{{- end}}
)
// {{.StructName}} structure {{.Description}}
type {{.StructName}} struct {
global.GVA_MODEL {{- range .Fields}}
{{- if .PrimaryKey}}
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"primarykey{{- if .Comment}};comment:{{.Comment}}{{- end}}"`
{{- else}}
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .ColumnName}}column:{{.ColumnName}};{{- end}}{{- if .Comment}}comment:{{.Comment}};{{- end}}{{- if .DataType}}type:{{.DataType}};{{- end}}{{- if .DefaultValue}}default:{{.DefaultValue}};{{- end}}"`
{{- end}}{{- end}}
}
// TableName {{.StructName}} table name
func ({{.StructName}}) TableName() string {
return "{{.TableName}}"
}Frontend Template Example β
<!-- table.vue.tpl -->
<template>
<div>
<div class="gva-search-box">
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline" :rules="searchRule" @keyup.enter="onSubmit">
{{- range .Fields}}
{{- if .FieldSearchType}}
<el-form-item label="{{.FieldDesc}}" prop="{{.FieldJson}}">
{{- if eq .FieldSearchType "LIKE" }}
<el-input v-model="searchInfo.{{.FieldJson}}" placeholder="Search condition" />
{{- else if eq .FieldSearchType "BETWEEN" }}
<template v-if="searchInfo.{{.FieldJson}} && searchInfo.{{.FieldJson}}.length === 2">
<el-date-picker
v-model="searchInfo.{{.FieldJson}}"
type="datetimerange"
range-separator="to"
start-placeholder="Start date"
end-placeholder="End date"
/>
</template>
{{- end}}
</el-form-item>
{{- end}}
{{- end}}
<el-form-item>
<el-button type="primary" icon="search" @click="onSubmit">Search</el-button>
<el-button icon="refresh" @click="onReset">Reset</el-button>
</el-form-item>
</el-form>
</div>
<div class="gva-table-box">
<div class="gva-btn-list">
<el-button type="primary" icon="plus" @click="openDialog">Add</el-button>
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">Delete</el-button>
</div>
<el-table
ref="multipleTable"
style="width: 100%"
tooltip-effect="dark"
:data="tableData"
row-key="ID"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
{{- range .Fields}}
{{- if .Front}}
<el-table-column align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- end}}
{{- end}}
<el-table-column align="left" label="Actions">
<template #default="scope">
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)"><icon name="edit" />Edit</el-button>
<el-button type="primary" link class="table-button" @click="deleteRow(scope.row)"><icon name="delete" />Delete</el-button>
</template>
</el-table-column>
</el-table>
<div class="gva-pagination">
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:current-page="page"
:page-size="pageSize"
:page-sizes="[10, 30, 50, 100]"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</div>
</div>
</template>π§ Core Code of the Generator β
Code Generation Service β
type AutoCodeService struct{}
// CreateTemp creates code
func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, ids ...uint) (err error) {
// 1. Create base directory
basePath := "autocode_template/"
if autoCode.AutoMoveFile {
basePath = ""
}
// 2. Generate backend code
for _, value := range autoCode.Fields {
if value.FieldType == "time.Time" {
autoCode.HasTimer = true
}
if value.FieldSearchType != "" {
autoCode.NeedSort = true
}
if value.DictType != "" {
autoCode.DictTypes = append(autoCode.DictTypes, value.DictType)
}
}
// 3. Parse templates and generate files
templates := []string{
"server/api.go.tpl",
"server/model.go.tpl",
"server/request.go.tpl",
"server/router.go.tpl",
"server/service.go.tpl",
"web/api.js.tpl",
"web/table.vue.tpl",
"web/form.vue.tpl",
}
for _, tmpl := range templates {
if err = autoCodeService.generateFile(tmpl, autoCode, basePath); err != nil {
return err
}
}
// 4. Automatically inject routes and APIs
if autoCode.AutoCreateApiToSql {
if err = autoCodeService.AutoCreateApi(&autoCode); err != nil {
return err
}
}
return nil
}
// generateFile generates a single file
func (autoCodeService *AutoCodeService) generateFile(templatePath string, data system.AutoCodeStruct, basePath string) error {
// Read template file
templateContent, err := ioutil.ReadFile("resource/template/" + templatePath)
if err != nil {
return err
}
// Parse template
tmpl, err := template.New("autocode").Parse(string(templateContent))
if err != nil {
return err
}
// Generate target file path
outputPath := autoCodeService.getOutputPath(templatePath, data, basePath)
// Create directory
if err = os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
return err
}
// Generate file
file, err := os.Create(outputPath)
if err != nil {
return err
}
defer file.Close()
// Execute template
return tmpl.Execute(file, data)
}Automatic Injection Feature β
// AutoCreateApi automatically creates API
func (autoCodeService *AutoCodeService) AutoCreateApi(autoCode *system.AutoCodeStruct) error {
apis := []system.SysApi{
{
Path: "/" + autoCode.Abbreviation + "/create" + autoCode.StructName,
Description: "Add " + autoCode.Description,
ApiGroup: autoCode.Description,
Method: "POST",
},
{
Path: "/" + autoCode.Abbreviation + "/delete" + autoCode.StructName,
Description: "Delete " + autoCode.Description,
ApiGroup: autoCode.Description,
Method: "DELETE",
},
{
Path: "/" + autoCode.Abbreviation + "/delete" + autoCode.StructName + "ByIds",
Description: "Batch delete " + autoCode.Description,
ApiGroup: autoCode.Description,
Method: "DELETE",
},
{
Path: "/" + autoCode.Abbreviation + "/update" + autoCode.StructName,
Description: "Update " + autoCode.Description,
ApiGroup: autoCode.Description,
Method: "PUT",
},
{
Path: "/" + autoCode.Abbreviation + "/find" + autoCode.StructName,
Description: "Get " + autoCode.Description + " by ID",
ApiGroup: autoCode.Description,
Method: "GET",
},
{
Path: "/" + autoCode.Abbreviation + "/get" + autoCode.StructName + "List",
Description: "Get " + autoCode.Description + " list",
ApiGroup: autoCode.Description,
Method: "GET",
},
}
for _, api := range apis {
if err := apiService.CreateApi(api); err != nil {
return err
}
}
return nil
}
// AutoCreateMenu automatically creates menu
func (autoCodeService *AutoCodeService) AutoCreateMenu(autoCode *system.AutoCodeStruct) error {
menu := system.SysBaseMenu{
ParentId: "0",
Path: autoCode.Abbreviation,
Name: autoCode.StructName,
Component: "view/" + autoCode.PackageName + "/" + autoCode.PackageName + ".vue",
Sort: 999,
Meta: system.Meta{
Title: autoCode.Description,
Icon: "setting",
},
}
return menuService.AddBaseMenu(menu)
}π― Advanced Features β
1. Associated Table Generation β
// One-to-many association configuration
type Association struct {
Type string `json:"type"` // Association type: hasOne, hasMany, belongsTo
ForeignKey string `json:"foreignKey"` // Foreign key
References string `json:"references"` // Referenced field
Table string `json:"table"` // Associated table
StructName string `json:"structName"` // Associated structure
}
// Generate association query code
func generateAssociationCode(associations []Association) string {
var code strings.Builder
for _, assoc := range associations {
switch assoc.Type {
case "hasMany":
code.WriteString(fmt.Sprintf(
"db.Preload(\"%s\").Find(&result)\n",
assoc.StructName,
))
case "belongsTo":
code.WriteString(fmt.Sprintf(
"db.Preload(\"%s\").Find(&result)\n",
assoc.StructName,
))
}
}
return code.String()
}2. Dictionary Type Support β
// Dictionary field processing
func processDictField(field Field) string {
if field.DictType != "" {
return fmt.Sprintf(`
<el-select v-model="formData.%s" placeholder="Please select %s">
<el-option
v-for="item in %sOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>`,
field.FieldJson,
field.FieldDesc,
field.DictType,
)
}
return fmt.Sprintf(`<el-input v-model="formData.%s" placeholder="Please enter %s" />`,
field.FieldJson, field.FieldDesc)
}3. File Upload Field β
// File upload field template
const fileUploadTemplate = `
<el-upload
class="upload-demo"
:action="path"
:headers="{ 'x-token': userStore.token }"
:on-success="handleFileSuccess"
:before-upload="beforeFileUpload"
>
<el-button type="primary">Click to upload</el-button>
<template #tip>
<div class="el-upload__tip">Only jpg/png files can be uploaded, and the size should not exceed 500kb</div>
</template>
</el-upload>
`
// Generate file upload handling functions
func generateFileUploadMethods(fields []Field) string {
var methods strings.Builder
for _, field := range fields {
if field.FieldType == "file" {
methods.WriteString(fmt.Sprintf(`
// %s file upload success callback
const handle%sSuccess = (response) => {
formData.value.%s = response.data.file.url
}
// %s file upload pre-check
const before%sUpload = (file) => {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
ElMessage.error('Uploaded avatar image can only be in JPG/PNG format!')
}
if (!isLt2M) {
ElMessage.error('Uploaded avatar image size cannot exceed 2MB!')
}
return isJPG && isLt2M
}`,
strings.Title(field.FieldJson),
field.FieldJson,
field.FieldDesc,
strings.Title(field.FieldJson),
))
}
}
return methods.String()
}π Generation Statistics β
Code Generation Records β
type AutoCodeHistory struct {
ID uint `json:"ID" gorm:"primarykey"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
StructName string `json:"structName" gorm:"comment:Structure name"`
TableName string `json:"tableName" gorm:"comment:Table name"`
RequestMeta string `json:"requestMeta" gorm:"type:text;comment:Request meta information"`
AutoCodePath string `json:"autoCodePath" gorm:"comment:Automatically generated code path"`
InjectionMeta string `json:"injectionMeta" gorm:"type:text;comment:Injection meta information"`
ApiIDs string `json:"apiIDs" gorm:"comment:API table registration content"`
Flag int `json:"flag" gorm:"comment:Indicates the corresponding status 0 represents creation, 1 represents rollback ...."`
}
// Record generation history
func (autoCodeService *AutoCodeService) CreateAutoCodeHistory(meta, path, injectionMeta, apiIDs string, autoCode system.AutoCodeStruct) error {
return global.GVA_DB.Create(&system.SysAutoCodeHistory{
RequestMeta: meta,
AutoCodePath: path,
InjectionMeta: injectionMeta,
StructName: autoCode.StructName,
TableName: autoCode.TableName,
ApiIDs: apiIDs,
Flag: 0,
}).Error
}π Code Rollback β
// RollBack rollback automatically generated code
func (autoCodeService *AutoCodeService) RollBack(id uint) error {
var history system.SysAutoCodeHistory
if err := global.GVA_DB.First(&history, id).Error; err != nil {
return err
}
// Delete generated files
var meta system.AutoCodeStruct
if err := json.Unmarshal([]byte(history.RequestMeta), &meta); err != nil {
return err
}
// Delete backend files
serverFiles := []string{
fmt.Sprintf("app/%s/model/%s.go", meta.PackageName, meta.PackageName),
fmt.Sprintf("app/%s/request/%s.go", meta.PackageName, meta.PackageName),
fmt.Sprintf("app/%s/service/%s.go", meta.PackageName, meta.PackageName),
fmt.Sprintf("app/%s/api/%s.go", meta.PackageName, meta.PackageName),
fmt.Sprintf("app/%s/router/%s.go", meta.PackageName, meta.PackageName),
}
for _, file := range serverFiles {
if err := os.Remove(file); err != nil && !os.IsNotExist(err) {
return err
}
}
// Delete frontend files
webFiles := []string{
fmt.Sprintf("../web/src/api/%s.js", meta.PackageName),
fmt.Sprintf("../web/src/view/%s/%s.vue", meta.PackageName, meta.PackageName),
fmt.Sprintf("../web/src/view/%s/%sForm.vue", meta.PackageName, meta.PackageName),
}
for _, file := range webFiles {
if err := os.Remove(file); err != nil && !os.IsNotExist(err) {
return err
}
}
// Delete API records
var apiIDs []uint
if err := json.Unmarshal([]byte(history.ApiIDs), &apiIDs); err == nil {
global.GVA_DB.Delete(&system.SysApi{}, apiIDs)
}
// Update history record status
return global.GVA_DB.Model(&history).Update("flag", 1).Error
}π¨ Frontend Integration β
Code Generator Page β
<template>
<div class="auto-code">
<el-card>
<template #header>
<div class="card-header">
<span>Code Generator</span>
</div>
</template>
<el-form ref="autoCodeForm" :model="form" :rules="rules" label-width="120px">
<el-form-item label="Structure Name" prop="structName">
<el-input v-model="form.structName" placeholder="Please enter structure name" />
</el-form-item>
<el-form-item label="Table Name" prop="tableName">
<el-input v-model="form.tableName" placeholder="Please enter table name" />
</el-form-item>
<el-form-item label="Package Name" prop="packageName">
<el-input v-model="form.packageName" placeholder="Please enter package name" />
</el-form-item>
<el-form-item label="Structure Description" prop="description">
<el-input v-model="form.description" placeholder="Please enter structure description" />
</el-form-item>
<!-- Field configuration -->
<el-form-item label="Field Configuration">
<el-table :data="form.fields" border>
<el-table-column label="Field Name" prop="fieldName" />
<el-table-column label="Field Description" prop="fieldDesc" />
<el-table-column label="Field Type" prop="fieldType" />
<el-table-column label="Database Type" prop="dataType" />
<el-table-column label="Actions">
<template #default="scope">
<el-button @click="editField(scope.$index)">Edit</el-button>
<el-button @click="deleteField(scope.$index)">Delete</el-button>
</template>
</el-table-column>
</el-table>
<el-button @click="addField" style="margin-top: 10px;">Add Field</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="generateCode">Generate Code</el-button>
<el-button @click="resetForm">Reset</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { createTemp } from '@/api/autoCode'
import { ElMessage } from 'element-plus'
const form = reactive({
structName: '',
tableName: '',
packageName: '',
description: '',
fields: []
})
const rules = {
structName: [{ required: true, message: 'Please enter structure name', trigger: 'blur' }],
tableName: [{ required: true, message: 'Please enter table name', trigger: 'blur' }],
packageName: [{ required: true, message: 'Please enter package name', trigger: 'blur' }]
}
const generateCode = async () => {
try {
const res = await createTemp(form)
if (res.code === 0) {
ElMessage.success('Code generated successfully')
}
} catch (error) {
ElMessage.error('Code generation failed')
}
}
</script>π Frequently Asked Questions β
Q: The generated code fails to compile? β
A: Check if the field type configuration is correct, ensuring that the Go type and database type match.
Q: The frontend page displays abnormally? β
A: Check the frontend display configuration of the fields, ensuring that the necessary fields are set to be displayed on the frontend.
Q: How to customize the generation template? β
A: Modify the template files in the server/resource/template/ directory and restart the service to take effect.
Q: How to add custom logic after generation? β
A: Add custom methods on top of the generated code, avoiding direct modification of the core CRUD methods that are generated.


