Softhub软件下载站实战开发(四):代码生成器设计与实现

文章目录

    • Softhub软件下载站实战开发(四):代码生成器设计与实现
    • 1.前言
    • 2.技术选型
    • 3.架构概览 ️
      • 3.1 架构概览
      • 3.2 工作流程详解
    • 4.核心功能实现 ⏳
      • 4.1 配置管理系统
      • 4.2 数据库表结构解析
      • 4.3 模板渲染引擎
      • 4.4 智能类型转换
      • 4.5 动态文件生成
      • 4.6 智能覆盖策略
      • 4.7 运行
    • 5.附录 ℹ️
      • 5.1 生成器代码
      • 5.2 后端模板
      • 5.3 前端模板

Softhub软件下载站实战开发(四):代码生成器设计与实现

1.前言

在上篇文章中我们以platform模块为例,详细记录了从创建model到编写页面的全过程。相信读者已经对编写流程有了一个大致的了解。
不难发现,整个项目分为多层:controller、service、dao等等,并且存在着大量模板代码,手动一个个文件编写费事费力,还容易出错。GoFrame其实为我们提供了cli工具,可以方便快捷的生成例如model、service等代码。
对整个项目而言,其实这还远远不够,我们还有例如controller、service、前端等模板代码需要编写,这时候编写一个简单的代码生成器就会方便很多。

2.技术选型

因为本项目代码生成器不是重点,因此不考虑项目中集成,仅是作为一个工具存在。因为方便快捷作为第一考虑因素。
因此我们选用python作为主语言、结合jinja模板引擎快速生成模板代码。

系统核心组件包括:

  1. 数据库元数据提取 - 通过SQLAlchemy分析表结构
  2. 模板引擎 - 使用Jinja2渲染代码模板
  3. 配置管理 - YAML配置文件支持
  4. 文件生成器 - 自动化文件创建和写入

3.架构概览 ️

3.1 架构概览

输出
核心处理
输入
前端代码生成
后端代码生成
配置读取模块
数据库连接模块
表结构解析模块
模板渲染引擎
配置文件 application.yml
前端项目
后端项目

3.2 工作流程详解

Main ConfigUtil DB Inspector Template Engine File Writer 读取application.yml 返回配置数据 连接数据库 获取表结构信息 返回表元数据 加载前端模板 渲染模板 返回渲染结果 写入前端文件 alt [生成前端代码] 加载后端模板 渲染模板 返回渲染结果 写入后端文件 alt [生成后端代码] loop [遍历每个表配置] Main ConfigUtil DB Inspector Template Engine File Writer

4.核心功能实现 ⏳

4.1 配置管理系统

database:
  host: 127.0.0.1
  port: 3306
  user: root
  password: 123456
  dbname: softhub

code_generator:
  # 生成类型:frontend-前端, backend-后端, all-全部
  generate_type: all
  
  # 表配置
  tables:
    - name: ds_platform  # 表名
      comment: "平台管理"  # 表注释
      generate_type: all  # 可选:frontend, backend, all
      frontend:
        template_name: "gfast前端模板2"
        output_path: "D:/output/frontend"
        primary_name: "name"  # 主键名称
      backend:
        template_name: "gfast模板"
        output_path: "D:/output/backend"
        package: "platform"  # 包名
        package_path: "github.com/tiger1103/gfast/v3"  # 包路径前缀

配置模块管理

class ConfigUtil:
    def __init__(self, config_path: Path):
        self.config_path = config_path

    def read_config(self) -> Dict[str, Any]:
        with open(self.config_path, 'r', encoding='utf-8') as f:
            return yaml.safe_load(f)

使用YAML配置文件管理:

  1. 数据库连接信息
  2. 模板路径配置
  3. 包名和导入路径
  4. 代码生成选项

4.2 数据库表结构解析

class InspectUtils:
    def __init__(self, engine):
        self.engine = engine
        self.inspector = inspect(engine)
        
    def get_infos(self, table_name: str) -> Dict[str, Any]:
        columns = self.inspector.get_columns(table_name)
        # 处理字段信息...
        return {
            'table_name': table_name,
            'columns': columns,
            # 其他元数据...
        }
  • 使用SQLAlchemy的Inspector获取表元数据
  • 自动将SQL类型转换为Go类型
  • 生成驼峰命名等符合编程规范的字段名

4.3 模板渲染引擎

# 设置模板路径
template_base_path = Path('template')
template_path = template_base_path.joinpath(template_name)

# 渲染模板
templates = Environment(loader=FileSystemLoader(template_path), 
                       lstrip_blocks=True, 
                       trim_blocks=True)
tl = templates.get_template(template)
render_str = tl.render(infos)
  • 支持模板继承和宏定义
  • 自动处理缩进和空白字符
  • 动态生成文件路径和文件名

4.4 智能类型转换

def _get_go_type(self, sql_type: str) -> str:
    """将SQL类型转换为Go类型"""
    sql_type = sql_type.lower()
    if 'int' in sql_type:
        return 'int'
    elif 'varchar' in sql_type or 'char' in sql_type or 'text' in sql_type:
        return 'string'
    elif 'time' in sql_type or 'date' in sql_type:
        return '*gtime.Time'
    # 其他类型处理...

4.5 动态文件生成

# 渲染相对路径
relative_path = Template(str(item_template_path.parent
                         .relative_to(template_path))
                         .render(file_info)
# 渲染文件名
template_stem = item_template_path.stem
if '.' in template_stem:
    name_part, ext = template_stem.rsplit('.', 1)
    file_name = Template(name_part).render(file_info) + '.' + ext

4.6 智能覆盖策略

# 跳过已存在的router.go和logic.go文件
if Path(file_path).exists() and 
   (str(item_template_path.stem) in ["router.go", "logic.go"]):
    continue

4.7 运行

修改config\application.yml配置

python code_generator.py

输出类似如下
Softhub软件下载站实战开发(四):代码生成器设计与实现_第1张图片

5.附录 ℹ️

5.1 生成器代码

import logging as log
import os
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, List

from jinja2 import Environment, FileSystemLoader, Template
import sqlalchemy as sa
from sqlalchemy import inspect
import yaml

# 配置日志
log.basicConfig(level=log.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

class ConfigUtil:
    def __init__(self, config_path: Path):
        self.config_path = config_path

    def read_config(self) -> Dict[str, Any]:
        with open(self.config_path, 'r', encoding='utf-8') as f:
            return yaml.safe_load(f)

class InspectUtils:
    def __init__(self, engine):
        self.engine = engine
        self.inspector = inspect(engine)
        self.tables = self.inspector.get_table_names()

    def get_infos(self, table_name: str) -> Dict[str, Any]:
        columns = self.inspector.get_columns(table_name)
        primary_keys = self.inspector.get_pk_constraint(table_name)
        foreign_keys = self.inspector.get_foreign_keys(table_name)
        
        # 处理字段信息
        field_info = []
        for column in columns:
            # 获取字段类型
            type_name = str(column['type'])
            go_type = self._get_go_type(type_name)
            
            # 处理字段名
            name = column['name']
            camel_name = ''.join(word.capitalize() for word in name.split('_'))
            if camel_name and camel_name[0].isupper():
                camel_name = camel_name[0].lower() + camel_name[1:]
            
            field_info.append({
                'name': name,
                'camelName': camel_name,
                'firstUpperName': ''.join(word.capitalize() for word in name.split('_')),
                'type': go_type,
                'comment': column.get('comment', ''),
                'nullable': column.get('nullable', False)
            })
        
        return {
            'table_name': table_name,
            'columns': columns,
            'primary_keys': primary_keys,
            'foreign_keys': foreign_keys,
            'field_info': field_info
        }

    def _get_go_type(self, sql_type: str) -> str:
        """将SQL类型转换为Go类型"""
        sql_type = sql_type.lower()
        if 'int' in sql_type:
            return 'int'
        elif 'varchar' in sql_type or 'char' in sql_type or 'text' in sql_type:
            return 'string'
        elif 'float' in sql_type or 'double' in sql_type or 'decimal' in sql_type:
            return 'float64'
        elif 'bool' in sql_type:
            return 'bool'
        elif 'time' in sql_type or 'date' in sql_type:
            return '*gtime.Time'
        else:
            return 'string'

    def get_file_info(self, table_name: str) -> Dict[str, str]:
        first_upper_name = ''.join(word.capitalize() for word in table_name.split('_'))
        return {
            'table_name': table_name,
            'table_name_camel': ''.join(word.capitalize() for word in table_name.split('_')),
            'table_name_lower': table_name.lower(),
            'name': table_name,
            'first_upper_name': first_upper_name
        }

def get_connection(config: Dict[str, Any]):
    return sa.create_engine(
            f"mysql+pymysql://{config['user']}:{config['password']}@{config['host']}:{config['port']}/{config['dbname']}"
        )

def generate_code(config_map: Dict[str, Any], table_config: Dict[str, Any]):
    template_name = config_map['template_name']
    table_name = config_map['table_name']
    output_path = config_map['output_path']
    package = config_map.get('package', 'template')
    package_path = config_map.get('package_path', 'github.com/tiger1103/gfast/v3')

    # 读取配置
    config_path = Path('config/application.yml').resolve()
    config_util = ConfigUtil(config_path)
    config = config_util.read_config()
    
    # 连接数据库
    engine = get_connection(config['database'])
    iu = InspectUtils(engine)

    # 获取表信息
    infos = iu.get_infos(table_name)
    
    # 添加前端信息
    front_info = {
        'search_columns': [
            {"camelName": "name", "comment": "变量名称", "type": "string"}
        ]
    }
    if "primary_name" in config_map:
        front_info['primaryName'] = config_map['primary_name']
    infos['front_info'] = front_info

    # 添加包信息
    package_info = {
        'api': f"{package_path}/api/v1/{package}",
        'service': f"{package_path}/internal/app/{package}/service",
        'dao': f"{package_path}/internal/app/{package}/dao",
        'liberr': f"{package_path}/library/liberr",
        'libUtils': f"{package_path}/library/libUtils",
        'consts': f"{package_path}/internal/app/system/consts",
        'model': f"{package_path}/internal/app/{package}/model",
        'entity': f"{package_path}/internal/app/{package}/model/entity",
        'utils': f"{package_path}/internal/app/{package}/utils",
        'common': f"{package_path}/internal/app/{package}/common",
        'commonDao': f"{package_path}/internal/app/common/dao",
        'commonEntity': f"{package_path}/internal/app/common/model/entity",
        'commonApi': f"{package_path}/api/v1/common",
        'do': f"{package_path}/internal/app/{package}/model/do",
        'SystemS': f"{package_path}/internal/app/system/service",
        'dao_internal': f"{package_path}/internal/app/{package}/dao/internal",
        'package': package,
        'packageFirstUpper': package.capitalize()
    }
    infos['package_info'] = package_info

    # 获取文件信息
    file_info = iu.get_file_info(table_name)
    file_info['package'] = package
    file_info['packageFirstUpper'] = package.capitalize()

    # 添加表信息
    table_info = {
        'name': table_name,
        'lowerName': table_name.lower(),
        'upperName': table_name.upper(),
        'firstUpperName': ''.join(word.capitalize() for word in table_name.split('_')),
        'camelName': ''.join(word.capitalize() for word in table_name.split('_'))[0].lower() + ''.join(word.capitalize() for word in table_name.split('_'))[1:],
        'comment': table_config.get('comment', ''),
        'columns': infos['columns'],
        'primaryKeys': infos['primary_keys'],
        'foreignKeys': infos['foreign_keys']
    }
    infos['table_info'] = table_info

    # 设置模板路径
    template_base_path = Path('template')
    template_path = template_base_path.joinpath(template_name)

    # 设置输出路径
    output_path = Path(output_path).resolve()
    if not output_path.exists():
        output_path.mkdir(parents=True)

    # 渲染模板
    templates = Environment(loader=FileSystemLoader(template_path), lstrip_blocks=True, trim_blocks=True)
    for template in templates.list_templates():
        tl = templates.get_template(template)
        item_template_path = Path(tl.filename)

        # 渲染相对路径
        relative_path = Template(str(item_template_path.parent.relative_to(template_path))).render(file_info)
        log.info(f"Processing template: {relative_path}")

        # 渲染文件名(保持原始扩展名)
        template_stem = item_template_path.stem  # 获取不带.j2的文件名
        if '.' in template_stem:  # 如果文件名中包含扩展名
            name_part, ext = template_stem.rsplit('.', 1)  # 分离名称和扩展名
            file_name = Template(name_part).render(file_info) + '.' + ext
        else:
            file_name = Template(template_stem).render(file_info)
        
        render_str = tl.render(infos)
        parent_output = output_path.joinpath(relative_path)
        if not parent_output.exists():
            parent_output.mkdir(parents=True)
            
        file_path = parent_output.joinpath(file_name)
        log.info(f"Generating file: {file_path}")
        
        # 跳过已存在的router.go和logic.go文件
        if Path(file_path).exists() and (str(item_template_path.stem) in ["router.go", "logic.go"]):
            continue
            
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(render_str)

def main():
    # 读取配置
    config_path = Path('config/application.yml').resolve()
    config_util = ConfigUtil(config_path)
    config = config_util.read_config()
    
    # 获取生成器配置
    generator_config = config['code_generator']
    generate_type = generator_config['generate_type']
    
    # 处理每个表的配置
    for table_config in generator_config['tables']:
        table_name = table_config['name']
        table_generate_type = table_config.get('generate_type', generate_type)
        
        # 生成前端代码
        if table_generate_type in ['frontend', 'all']:
            frontend_config = table_config['frontend']
            config_map = {
                'template_name': frontend_config['template_name'],
                'table_name': table_name,
                'output_path': frontend_config['output_path'],
                'primary_name': frontend_config['primary_name']
            }
            generate_code(config_map, table_config)
        
        # 生成后端代码
        if table_generate_type in ['backend', 'all']:
            backend_config = table_config['backend']
            config_map = {
                'template_name': backend_config['template_name'],
                'table_name': table_name,
                'output_path': backend_config['output_path'],
                'package': backend_config['package'],
                'package_path': backend_config['package_path']
            }
            generate_code(config_map, table_config)

if __name__ == "__main__":
    main() 

5.2 后端模板

项目结构

gfast模板:
- api
- api\v1
- api\v1\{{package}}
- api\v1\{{package}}\{{name}}.go.j2
- internal
- internal\app
- internal\app\{{package}}
- internal\app\{{package}}\controller
- internal\app\{{package}}\controller\{{name}}.go.j2
- internal\app\{{package}}\dao
- internal\app\{{package}}\dao\internal
- internal\app\{{package}}\dao\internal\{{name}}.go.j2
- internal\app\{{package}}\dao\{{name}}.go.j2
- internal\app\{{package}}\logic
- internal\app\{{package}}\logic\logic.go.j2
- internal\app\{{package}}\logic\{{name}}
- internal\app\{{package}}\logic\{{name}}\{{name}}.go.j2
- internal\app\{{package}}\model
- internal\app\{{package}}\model\do
- internal\app\{{package}}\model\do\{{name}}.go.j2
- internal\app\{{package}}\model\entity
- internal\app\{{package}}\model\entity\{{name}}.go.j2
- internal\app\{{package}}\model\{{name}}.go.j2
- internal\app\{{package}}\router
- internal\app\{{package}}\router\router.go.j2
- internal\app\{{package}}\service
- internal\app\{{package}}\service\{{name}}.go.j2

代码内容

api\v1{{package}}{{name}}.go.j2

package template

import (
	"github.com/gogf/gf/v2/frame/g"
	commonApi "{{package_info.commonApi}}"
	model "{{package_info.model}}"

)

type {{table_info.firstUpperName}}AddReq struct {
	g.Meta `path:"/{{table_info.camelName}}/add" method:"post" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-新增"`
	{% for item in field_info %}
	{% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}
    {{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}" v:"required#{{ item.comment }}不能为空"`
    {% endif %}
    {% endfor %}
}

type {{table_info.firstUpperName}}AddRes struct {
	g.Meta `mime:"application/json" example:"string"`
}

type {{table_info.firstUpperName}}DelReq struct {
	g.Meta `path:"/{{table_info.camelName}}/del" method:"delete" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-删除"`
	Id uint `json:"id" v:"required#id不能为空"`
}

type {{table_info.firstUpperName}}DelRes struct {
	g.Meta `mime:"application/json" example:"string"`
}

type {{table_info.firstUpperName}}BatchDelReq struct {
	g.Meta `path:"/{{table_info.camelName}}/batchdel" method:"delete" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-批量删除"`
	Ids []uint `json:"id" v:"required#id不能为空"`
}

type {{table_info.firstUpperName}}BatchDelRes struct {
	g.Meta `mime:"application/json" example:"string"`
}

type {{table_info.firstUpperName}}EditReq struct {
	g.Meta `path:"/{{table_info.camelName}}/edit" method:"put" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-修改"`
	{% for item in field_info %}
	{% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}
    {{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}" v:"required#{{ item.comment }}不能为空"`
    {% endif %}
    {% endfor %}
}

type {{table_info.firstUpperName}}EditRes struct {
	g.Meta `mime:"application/json" example:"string"`
}

type {{table_info.firstUpperName}}ListReq struct {
	g.Meta `path:"/{{table_info.camelName}}/list" method:"get" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-列表"`
	commonApi.PageReq
    {% for item in field_info %}
    {% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}
    {{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}" v:"required#{{ item.comment }}不能为空"`
    {% endif %}
    {% endfor %}
}

type {{table_info.firstUpperName}}ListRes struct {
	g.Meta `mime:"application/json" example:"string"`
	commonApi.ListRes
	{{table_info.firstUpperName}}List []*model.{{table_info.firstUpperName}}Info `json:"{{table_info.camelName}}List"`
}

type {{table_info.firstUpperName}}DetailReq struct {
    g.Meta `path:"/{{table_info.camelName}}/detail" method:"get" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-详情"`
    Id uint `json:"id" v:"required#id不能为空"`
}

type {{table_info.firstUpperName}}DetailRes struct {
	g.Meta `mime:"application/json" example:"string"`
	*model.{{table_info.firstUpperName}}Info
}

internal\app{{package}}\controller{{name}}.go.j2

package controller

import (
	"context"
	api "{{package_info.api}}"
	service "{{package_info.service}}"
	consts "{{package_info.consts}}"
)



var {{table_info.firstUpperName}} = {{table_info.camelName}}Controller{}

type {{table_info.camelName}}Controller struct {
	BaseController
}

func (c *{{table_info.camelName}}Controller) Add(ctx context.Context, req *api.{{table_info.firstUpperName}}AddReq) (res *api.{{table_info.firstUpperName}}AddRes, err error) {
	res = new(api.{{table_info.firstUpperName}}AddRes)
	err = service.{{table_info.firstUpperName}}().Add(ctx, req)
	return
}

func (c *{{table_info.camelName}}Controller) List(ctx context.Context, req *api.{{table_info.firstUpperName}}ListReq) (res *api.{{table_info.firstUpperName}}ListRes, err error) {
	res = new(api.{{table_info.firstUpperName}}ListRes)
	if req.PageSize == 0 {
		req.PageSize = consts.PageSize
	}
	if req.PageNum == 0 {
		req.PageNum = 1
	}
	total, {{table_info.camelName}}s, err := service.{{table_info.firstUpperName}}().List(ctx, req)
	res.Total = total
	res.CurrentPage = req.PageNum
	res.{{table_info.firstUpperName}}List = {{table_info.camelName}}s
	return
}

func (c *{{table_info.camelName}}Controller) Get(ctx context.Context, req *api.{{table_info.firstUpperName}}DetailReq) (res *api.{{table_info.firstUpperName}}DetailRes, err error) {
	res = new(api.{{table_info.firstUpperName}}DetailRes)
	service.{{table_info.firstUpperName}}().GetById(ctx, req.Id)
	return
}


func (c *{{table_info.camelName}}Controller) Edit(ctx context.Context, req *api.{{table_info.firstUpperName}}EditReq) (res *api.{{table_info.firstUpperName}}EditRes, err error) {
	err = service.{{table_info.firstUpperName}}().Edit(ctx, req)
	return
}

func (c *{{table_info.camelName}}Controller) Delete(ctx context.Context, req *api.{{table_info.firstUpperName}}DelReq) (res *api.{{table_info.firstUpperName}}DelRes, err error) {
	err = service.{{table_info.firstUpperName}}().Delete(ctx, req.Id)
	return
}

func (c *{{table_info.camelName}}Controller) BatchDelete(ctx context.Context, req *api.{{table_info.firstUpperName}}BatchDelReq) (res *api.{{table_info.firstUpperName}}BatchDelRes, err error) {
	err = service.{{table_info.firstUpperName}}().BatchDelete(ctx, req.Ids)
	return
}

internal\app{{package}}\dao\internal{{name}}.go.j2

// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================

package internal

import (
	"context"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
)

// {{table_info.firstUpperName}}Dao is the data access object for table cts_voice.
type {{table_info.firstUpperName}}Dao struct {
	table   string          // table is the underlying table name of the DAO.
	group   string          // group is the database configuration group name of current DAO.
	columns {{table_info.firstUpperName}}Columns // columns contains all the column names of Table for convenient usage.
}

// {{table_info.firstUpperName}}Columns defines and stores column names for table {{table_info.Name}}.
type {{table_info.firstUpperName}}Columns struct {
    {% for item in field_info %}
    {{ item.firstUpperName }} string //{{ item.comment }}
    {% endfor %}
}

// {{table_info.camelName}}Columns holds the columns for table {{table_info.Name}}.
var {{table_info.camelName}}Columns = {{table_info.firstUpperName}}Columns{
    {% for item in field_info %}
    {{ item.firstUpperName }}: "{{ item.name }}",
    {% endfor %}
}

// New{{table_info.firstUpperName}}Dao creates and returns a new DAO object for table data access.
func New{{table_info.firstUpperName}}Dao() *{{table_info.firstUpperName}}Dao {
	return &{{table_info.firstUpperName}}Dao{
		group:   "default",
		table:   "{{table_info.name}}",
		columns: {{table_info.camelName}}Columns,
	}
}

// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *{{table_info.firstUpperName}}Dao) DB() gdb.DB {
	return g.DB(dao.group)
}

// Table returns the table name of current dao.
func (dao *{{table_info.firstUpperName}}Dao) Table() string {
	return dao.table
}

// Columns returns all column names of current dao.
func (dao *{{table_info.firstUpperName}}Dao) Columns() {{table_info.firstUpperName}}Columns {
	return dao.columns
}

// Group returns the configuration group name of database of current dao.
func (dao *{{table_info.firstUpperName}}Dao) Group() string {
	return dao.group
}

// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *{{table_info.firstUpperName}}Dao) Ctx(ctx context.Context) *gdb.Model {
	return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}

// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *{{table_info.firstUpperName}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
	return dao.Ctx(ctx).Transaction(ctx, f)
}

internal\app{{package}}\dao{{name}}.go.j2

// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================

package dao

import (
	internal "{{package_info.dao_internal}}"
)

// internal{{table_info.firstUpperName}}Dao is internal type for wrapping internal DAO implements.
type internal{{table_info.firstUpperName}}Dao = *internal.{{table_info.firstUpperName}}Dao

// {{table_info.camelName}}Dao is the data access object for table {{table_info.Name}}.
// You can define custom methods on it to extend its functionality as you wish.
type {{table_info.camelName}}Dao struct {
	internal{{table_info.firstUpperName}}Dao
}

var (
	// {{table_info.firstUpperName}}Dao is globally public accessible object for table cts_voice operations.
	{{table_info.firstUpperName}} = {{table_info.camelName}}Dao{
		internal.New{{table_info.firstUpperName}}Dao(),
	}
)

// Fill with you ideas below.

internal\app{{package}}\logic\logic.go.j2

package logic

import _ "github.com/tiger1103/gfast/v3/internal/app/{{package_info.package}}/logic/{{table_info.name}}"

internal\app{{package}}\logic{{name}}{{name}}.go.j2

package {{table_info.name}}

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	api "{{package_info.api}}"
	dao "{{package_info.dao}}"
	model "{{package_info.model}}"
	do "{{package_info.do}}"
	service "{{package_info.service}}"
	SystemS "{{package_info.SystemS}}"
	liberr "{{package_info.liberr}}"
)

func init() {
	service.Register{{table_info.firstUpperName}}(New())
}

func New() *s{{table_info.firstUpperName}} {
	return &s{{table_info.firstUpperName}}{}
}

type s{{table_info.firstUpperName}} struct {
}

func (s s{{table_info.firstUpperName}}) List(ctx context.Context, req *api.{{table_info.firstUpperName}}ListReq) (total interface{}, {{table_info.camelName}}List []*model.{{table_info.firstUpperName}}Info, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		m := dao.{{table_info.firstUpperName}}.Ctx(ctx)
		columns := dao.{{table_info.firstUpperName}}.Columns()
		//TODO 根据实际情况修改
		{% for item in field_info %}
        {% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}
        if req.{{ item.firstUpperName }} != "" {
           m = m.Where(columns.{{ item.firstUpperName }}+" = ?", req.{{ item.firstUpperName }})
           // like
           //m = m.Where(fmt.Sprintf("%s like ?", columns.{{ item.firstUpperName }}), "%"+req.{{ item.firstUpperName }}+"%")
        }
        {% endif %}
        {% endfor %}

		total, err = m.Count()
		liberr.ErrIsNil(ctx, err, "获取{{table_info.comment}}列表失败")
		orderBy := req.OrderBy
		if orderBy == "" {
			orderBy = "created_at desc"
		}
		err = m.Page(req.PageNum, req.PageSize).Order(orderBy).Scan(&{{table_info.camelName}}List)
		liberr.ErrIsNil(ctx, err, "获取{{table_info.comment}}列表失败")
	})
	return
}

func (s s{{table_info.firstUpperName}}) Add(ctx context.Context, req *api.{{table_info.firstUpperName}}AddReq) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		// TODO 查询是否已经存在

		// add
		_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).Insert(do.{{table_info.firstUpperName}}{
		    {% for item in field_info %}
            {% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}
            {{ item.firstUpperName }}:  req.{{ item.firstUpperName }},    // {{ item.comment }}
            {% endif %}
            {% endfor %}
            CreatedBy: SystemS.Context().GetUserId(ctx),
            UpdatedBy: SystemS.Context().GetUserId(ctx),
		})
		liberr.ErrIsNil(ctx, err, "新增{{table_info.comment}}失败")
	})
	return
}

func (s s{{table_info.firstUpperName}}) Edit(ctx context.Context, req *api.{{table_info.firstUpperName}}EditReq) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		_, err = s.GetById(ctx, req.Id)
		liberr.ErrIsNil(ctx, err, "获取{{table_info.comment}}失败")
		//TODO 根据名称等查询是否存在

		//编辑
		_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).WherePri(req.Id).Update(do.{{table_info.firstUpperName}}{
		    {% for item in field_info %}
            {% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}
            {{ item.firstUpperName }}:  req.{{ item.firstUpperName }},    // {{ item.comment }}
            {% endif %}
            {% endfor %}
		})
		liberr.ErrIsNil(ctx, err, "修改{{table_info.comment}}失败")
	})
	return
}

func (s s{{table_info.firstUpperName}}) Delete(ctx context.Context, id uint) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).WherePri(id).Delete()
		liberr.ErrIsNil(ctx, err, "删除{{table_info.comment}}失败")
	})
	return
}

func (s s{{table_info.firstUpperName}}) BatchDelete(ctx context.Context, ids []uint) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).Where(dao.{{table_info.firstUpperName}}.Columns().Id+" in(?)", ids).Delete()
		liberr.ErrIsNil(ctx, err, "批量删除{{table_info.comment}}失败")
	})
	return
}

func (s s{{table_info.firstUpperName}}) GetById(ctx context.Context, id uint) (res *model.{{table_info.firstUpperName}}Info, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		err = dao.{{table_info.firstUpperName}}.Ctx(ctx).Where(fmt.Sprintf("%s=?", dao.{{table_info.firstUpperName}}.Columns().Id), id).Scan(&res)
		liberr.ErrIsNil(ctx, err, "获取{{table_info.comment}}失败")
	})
	return
}

internal\app{{package}}\model\do{{name}}.go.j2

// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================

package do

import (
	"github.com/gogf/gf/v2/frame/g"
)

// {{table_info.firstUpperName}} is the golang structure of table {{table_info.name}} for DAO operations like Where/Data.
type {{table_info.firstUpperName}} struct {
    g.Meta    `orm:"table:{{table_info.name}}, do:true"`
	{% for item in field_info %}
    {{ item.firstUpperName }} interface{} //{{ item.comment }}
    {% endfor %}
}

internal\app{{package}}\model\entity{{name}}.go.j2

// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================

package entity

import "github.com/gogf/gf/v2/os/gtime"

// {{table_info.firstUpperName}} is the golang structure for table {{table_info.name}}.
type {{table_info.firstUpperName}} struct {
    {% for item in field_info %}
    {{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}"   description:"{{ item.comment }}"`
    {% endfor %}
}

internal\app{{package}}\model{{name}}.go.j2

package model

import "github.com/gogf/gf/v2/os/gtime"

type {{table_info.firstUpperName}}Info struct {
    {% for item in field_info %}
    {{ item.firstUpperName }} {{ item.type }} `orm:"{{item.name}}"  json:"{{item.camelName}}"` // {{ item.comment }}
    {% endfor %}
}

internal\app{{package}}\router\router.go.j2

/*
* @desc:后台路由
* @company:云南奇讯科技有限公司
* @Author: yixiaohu
* @Date:   2022/2/18 17:34
 */

package router

import (
	"context"
	"github.com/gogf/gf/v2/net/ghttp"
	_ "github.com/tiger1103/gfast/v3/internal/app/{{package_info.package}}/logic"
	"github.com/tiger1103/gfast/v3/internal/app/{{package_info.package}}/controller"
	"github.com/tiger1103/gfast/v3/internal/app/system/service"
	"github.com/tiger1103/gfast/v3/library/libRouter"
)

var R = new(Router)

type Router struct{}

func (router *Router) BindController(ctx context.Context, group *ghttp.RouterGroup) {
	group.Group("/{{package_info.package}}", func(group *ghttp.RouterGroup) {
		//登录验证拦截
		service.GfToken().Middleware(group)
		//context拦截器
		group.Middleware(service.Middleware().Ctx, service.Middleware().Auth)
		//后台操作日志记录
		group.Hook("/*", ghttp.HookAfterOutput, service.OperateLog().OperationLog)
		group.Bind(
			controller.{{table_info.firstUpperName}},
		)
		//自动绑定定义的控制器
		if err := libRouter.RouterAutoBind(ctx, router, group); err != nil {
			panic(err)
		}
	})
}

internal\app{{package}}\service{{name}}.go.j2

package service

import (
	"context"
	api "{{package_info.api}}"
	model "{{package_info.model}}"
)

type I{{table_info.firstUpperName}} interface {
	List(ctx context.Context, req *api.{{table_info.firstUpperName}}ListReq) (total interface{}, res []*model.{{table_info.firstUpperName}}Info, err error)
	Add(ctx context.Context, req *api.{{table_info.firstUpperName}}AddReq) (err error)
	Edit(ctx context.Context, req *api.{{table_info.firstUpperName}}EditReq) (err error)
	Delete(ctx context.Context, id uint) (err error)
	BatchDelete(ctx context.Context, ids []uint) (err error)
	GetById(ctx context.Context, id uint) (res *model.{{table_info.firstUpperName}}Info, err error)
}

var local{{table_info.firstUpperName}} I{{table_info.firstUpperName}}

func {{table_info.firstUpperName}}() I{{table_info.firstUpperName}} {
	if local{{table_info.firstUpperName}} == nil {
		panic("implement not found for interface I{{table_info.firstUpperName}}, forgot register?")
	}
	return local{{table_info.firstUpperName}}
}

func Register{{table_info.firstUpperName}}(i I{{table_info.firstUpperName}}) {
	local{{table_info.firstUpperName}} = i
}

5.3 前端模板

项目结构

gfast前端模板2:
- src
- src\api
- src\api\{{package}}
- src\api\{{package}}\{{camel_name}}
- src\api\{{package}}\{{camel_name}}\index.ts.j2
- src\views
- src\views\{{package}}
- src\views\{{package}}\{{camel_name}}
- src\views\{{package}}\{{camel_name}}\component
- src\views\{{package}}\{{camel_name}}\component\edit{{first_upper_name}}.vue.j2
- src\views\{{package}}\{{camel_name}}\index.vue.j2

代码内容

src\api{{package}}{{camel_name}}\index.ts.j2

import request from '/@/utils/request';

export function get{{table_info.firstUpperName}}List(query?:Object) {
    return request({
        url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/list',
        method: 'get',
        params:query
    })
}


export function add{{table_info.firstUpperName}}(data:object) {
    return request({
        url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/add',
        method: 'post',
        data:data
    })
}


export function edit{{table_info.firstUpperName}}(data:object) {
    return request({
        url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/edit',
        method: 'put',
        data:data
    })
}


export function delete{{table_info.firstUpperName}}(id:number) {
    return request({
        url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/del',
        method: 'delete',
        data:{id}
    })
}

export function batchDelete{{table_info.firstUpperName}}(ids:number[]) {
    return request({
        url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/batchdel',
        method: 'delete',
        data:{ids}
    })
}

src\views{{package}}{{camel_name}}\component\edit{{first_upper_name}}.vue.j2






src\views{{package}}{{camel_name}}\index.vue.j2




你可能感兴趣的:(softHub,python,低代码,mysql)