首页后端开发其他后端知识golang中标准库template的代码生成步骤是什么?

golang中标准库template的代码生成步骤是什么?

时间2024-03-26 13:58:03发布访客分类其他后端知识浏览899
导读:这篇文章给大家分享的是“golang中标准库template的代码生成步骤是什么?”,文中的讲解内容简单清晰,对大家学习和理解有一定的参考价值和帮助,有这方面学习需要的朋友,接下来就跟随小编一起学习一下“golang中标准库template...
这篇文章给大家分享的是“golang中标准库template的代码生成步骤是什么?”,文中的讲解内容简单清晰,对大家学习和理解有一定的参考价值和帮助,有这方面学习需要的朋友,接下来就跟随小编一起学习一下“golang中标准库template的代码生成步骤是什么?”吧。


 

curd-gen 项目

curd-gen 项目的创建本来是为了做为 illuminant 项目的一个工具,用来生成前端增删改查页面中的基本代码。

最近,随着 antd Pro v5 的升级,将项目进行了升级,现在生成的都是 ts 代码。这个项目的自动生成代码都是基于 golang 的标准库 template 的,所以这篇博客也算是对使用 template 库的一次总结。

自动生成的配置

curd-gen 项目的自动代码生成主要是3部分:

  • 类型定义:用于API请求和页面显示的各个类型
  • API请求:graphql 请求语句和函数
  • 页面:列表页面,新增页面和编辑页面。新增和编辑是用弹出 modal 框的方式。

根据要生成的内容,定义了一个json格式文件,做为代码生成的基础。 json文件的说明在:https://gitee.com/wangyubin/curd-gen#curdjson

生成类型定义

类型是API请求和页面显示的基础,一般开发流程也是先根据业务定义类型,才开始API和页面的开发的。 ​

自动生成类型定义就是根据 json 文件中的字段列表,生成 ts 的类型定义。模板定义如下:

const TypeDTmpl = `// @ts-ignore
/* eslint-disable */

declare namespace API {

  type {
{
.Model.Name}
}
Item = {

    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

    {
{
- if .IsRequired}
}

    {
{
.Name}
}
: {
{
.ConvertTypeForTs}
}
    ;

    {
{
- else}
}

    {
{
.Name}
}
?: {
{
.ConvertTypeForTs}
}
    ;

    {
{
- end}
}
{
{
- /* end for if .IsRequired */}
}

    {
{
- end}
}
{
{
- /* end for range */}
}

    {
{
- end}
}
{
{
- /* end for with .Model.Fields */}
}

  }
    ;


  type {
{
.Model.Name}
}
    ListResult = CommonResponse &
 {

    data: {

      {
{
.Model.GraphqlName}
}
: {
{
.Model.Name}
}
    Item[];

      {
{
.Model.GraphqlName}
}
_aggregate: {

        aggregate: {
    
          count: number;

        }
    ;

      }
    ;

    }
    ;

  }
    ;


  type Create{
{
.Model.Name}
}
    Result = CommonResponse &
 {

    data: {

      insert_{
{
.Model.GraphqlName}
}
: {
    
        affected_rows: number;

      }
    ;

    }
    ;

  }
    ;


  type Update{
{
.Model.Name}
}
    Result = CommonResponse &
 {

    data: {

      update_{
{
.Model.GraphqlName}
}
_by_pk: {
    
        id: string;

      }
    ;

    }
    ;

  }
    ;


  type Delete{
{
.Model.Name}
}
    Result = CommonResponse &
 {

    data: {

      delete_{
{
.Model.GraphqlName}
}
_by_pk: {
    
        id: string;

      }
    ;

    }
    ;

  }
    ;

}
`

除了主要的类型,还包括了增删改查 API 返回值的定义。 ​

其中用到 text/template 库相关的知识点有:

  1. 通过 **with **限制访问范围,这样,在 { { - with xxx} } 和 { { - end} } 的代码中,不用每个字段前再加 .Model.Fields 前缀了
  2. 通过 range 循环访问数组,根据数组中每个元素来生成相应的代码
  3. 通过 if 判断,根据json文件中的属性的不同的定义生成不同的代码
  4. 自定义函数 **ConvertTypeForTs **,这个函数是将json中定义的 graphql type 转换成 typescript 中对应的类型。用自定义函数是为了避免在模板中写过多的逻辑代码

生成API

这里只生成 graphql 请求的 API,是为了配合 illuminant 项目。 API的参数和返回值用到的对象就在上面自动生成的类型定义中。 ​

const APITmpl = `// @ts-ignore
/* eslint-disable */
import {
 Graphql }
     from '../utils';


const gqlGet{
{
.Model.Name}
}
List = ` + "`" + `query get_item_list($limit: Int = 10, $offset: Int = 0{
{
- with .Model.Fields}
}
{
{
- range .}
}
{
{
- if .IsSearch}
}
, ${
{
.Name}
}
: {
{
.Type}
}
{
{
- end}
}
{
{
- end}
}
{
{
- end}
}
) {

  {
{
.Model.GraphqlName}
}
(order_by: {
updated_at: desc}
, limit: $limit, offset: $offset{
{
.Model.GenGraphqlSearchWhere false}
}
) {

    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

    {
{
.Name}
}

    {
{
- end}
}

    {
{
- end}
}

  }

  {
{
.Model.GraphqlName}
}
_aggregate({
{
.Model.GenGraphqlSearchWhere true}
}
) {

    aggregate {

      count
    }

  }

}
    ` + "`" + `;


const gqlCreate{
{
.Model.Name}
}
 = ` + "`" + `mutation create_item({
{
.Model.GenGraphqlInsertParamDefinations}
}
) {

  insert_{
{
.Model.GraphqlName}
}
(objects: {
 {
{
.Model.GenGraphqlInsertParams}
}
 }
) {

    affected_rows
  }

}
    ` + "`" + `;


const gqlUpdate{
{
.Model.Name}
}
 = ` + "`" + `mutation update_item_by_pk($id: uuid!, {
{
.Model.GenGraphqlUpdateParamDefinations}
}
) {

  update_{
{
.Model.GraphqlName}
}
_by_pk(pk_columns: {
id: $id}
, _set: {
 {
{
.Model.GenGraphqlUpdateParams}
}
 }
) {

    id
  }

}
    ` + "`" + `;


const gqlDelete{
{
.Model.Name}
}
 = ` + "`" + `mutation delete_item_by_pk($id: uuid!) {

  delete_{
{
.Model.GraphqlName}
}
_by_pk(id: $id) {

    id
  }

}
    ` + "`" + `;


export async function get{
{
.Model.Name}
}
List(params: API.{
{
.Model.Name}
}
    Item &
 API.PageInfo) {

  const gqlVar = {
    
    limit: params.pageSize ? params.pageSize : 10,
    offset: params.current &
    &
 params.pageSize ? (params.current - 1) * params.pageSize : 0,
    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

    {
{
- if .IsSearch}
}

    {
{
.Name}
}
: params.{
{
.Name}
}
 ? '%' + params.{
{
.Name}
}
 + '%' : '%%',
    {
{
- end}
}

    {
{
- end}
}

    {
{
- end}
}

  }
    ;


  return GraphqlAPI.{
{
.Model.Name}
}
    ListResult>
(gqlGet{
{
.Model.Name}
}
    List, gqlVar);

}


export async function create{
{
.Model.Name}
}
(params: API.{
{
.Model.Name}
}
Item) {

  const gqlVar = {

    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

    {
{
- if not .NotInsert}
}

    {
{
- if .IsPageRequired}
}

    {
{
.Name}
}
: params.{
{
.Name}
}
,
    {
{
- else}
}

    {
{
.Name}
}
: params.{
{
.Name}
}
 ? params.{
{
.Name}
}
 : null,
    {
{
- end}
}

    {
{
- end}
}

    {
{
- end}
}

    {
{
- end}
}

  }
    ;


  return GraphqlAPI.Create{
{
.Model.Name}
}
    Result>
(gqlCreate{
{
.Model.Name}
}
    , gqlVar);

}


export async function update{
{
.Model.Name}
}
(params: API.{
{
.Model.Name}
}
Item) {

  const gqlVar = {

    id: params.id,
    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

    {
{
- if not .NotUpdate}
}

    {
{
- if .IsPageRequired}
}

    {
{
.Name}
}
: params.{
{
.Name}
}
,
    {
{
- else}
}

    {
{
.Name}
}
: params.{
{
.Name}
}
 ? params.{
{
.Name}
}
 : null,
    {
{
- end}
}

    {
{
- end}
}

    {
{
- end}
}

    {
{
- end}
}

  }
    ;


  return GraphqlAPI.Update{
{
.Model.Name}
}
    Result>
(gqlUpdate{
{
.Model.Name}
}
    , gqlVar);

}


export async function delete{
{
.Model.Name}
}
(id: string) {

  return GraphqlAPI.Delete{
{
.Model.Name}
}
    Result>
(gqlDelete{
{
.Model.Name}
}
, {
 id }
    );

}
`

这个模板中也使用了几个自定义函数,GenGraphqlSearchWhere,GenGraphqlInsertParams,**GenGraphqlUpdateParams **等等。

生成列表页面,新增和编辑页面

最后一步,就是生成页面。列表页面是主要页面:

const PageListTmpl = `import {
 useRef, useState }
     from 'react';

import {
 PageContainer }
     from '@ant-design/pro-layout';

import {
 Button, Modal, Popconfirm, message }
     from 'antd';

import {
 PlusOutlined }
     from '@ant-design/icons';

import type {
 ActionType, ProColumns }
     from '@ant-design/pro-table';
    
import ProTable from '@ant-design/pro-table';

import {
 get{
{
.Model.Name}
}
List, create{
{
.Model.Name}
}
, update{
{
.Model.Name}
}
, delete{
{
.Model.Name}
}
 }
 from '{
{
.Page.ApiImport}
}
    ';

import {
{
.Model.Name}
}
Add from './{
{
.Model.Name}
}
    Add';

import {
{
.Model.Name}
}
Edit from './{
{
.Model.Name}
}
    Edit';
    

export default () =>
 {
    
  const tableRef = useRefActionType>
    ();
    
  const [modalAddVisible, setModalAddVisible] = useState(false);
    
  const [modalEditVisible, setModalEditVisible] = useState(false);

  const [record, setRecord] = useStateAPI.{
{
.Model.Name}
}
    Item>
({
}
    );


  const columns: ProColumnsAPI.{
{
.Model.Name}
}
    Item>
[] = [
    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

    {
{
- if .IsColumn}
}

    {

      title: '{
{
.Title}
}
',
      dataIndex: '{
{
.Name}
}
',
    {
{
- if not .IsSearch}
}

      hideInSearch: true,
    {
{
- end}
}

    }
,
    {
{
- end }
}
{
{
- /* end for if .IsColumn */}
}

    {
{
- end }
}
{
{
- /* end for range . */}
}

    {
{
- end }
}
{
{
- /* end for with */}
}

    {
    
      title: '操作',
      valueType: 'option',
      render: (_, rd) =>
 [
        Button
          type="primary"
          size="small"
          key="edit"
          onClick={
    () =>
 {
    
            setModalEditVisible(true);
    
            setRecord(rd);

          }
}
    
        >
    
          修改
        /Button>
,
        Popconfirm
          placement="topRight"
          title="是否删除?"
          okText="Yes"
          cancelText="No"
          key="delete"
          onConfirm={
    async () =>
 {

            const response = await delete{
{
.Model.Name}
}
    (rd.id as string);

            if (response.code === 10000) message.info(` + "`" + `TODO: 【${
rd.TODO}
    】 删除成功` + "`" + `);

            else message.warn(` + "`" + `TODO: 【${
rd.TODO}
    】 删除失败` + "`" + `);
    
            tableRef.current?.reload();

          }
}
    
        >
    
          Button danger size="small">
    
            删除
          /Button>
    
        /Popconfirm>
,
      ],
    }
    ,
  ];
    

  const addItem = async (values: any) =>
 {
    
    console.log(values);

    const response = await create{
{
.Model.Name}
}
    (values);

    if (response.code !== 10000) {
    
      message.error('创建TODO失败');

    }


    if (response.code === 10000) {
    
      setModalAddVisible(false);
    
      tableRef.current?.reload();

    }

  }
    ;
    

  const editItem = async (values: any) =>
 {
    
    values.id = record.id;
    
    console.log(values);

    const response = await update{
{
.Model.Name}
}
    (values);

    if (response.code !== 10000) {
    
      message.error('编辑TODO失败');

    }


    if (response.code === 10000) {
    
      setModalEditVisible(false);
    
      tableRef.current?.reload();

    }

  }
    ;


  return (
    PageContainer fixedHeader header={
{
"{
{
"}
}
 title: '{
{
.Page.Title}
}
' }
}
    >

      ProTableAPI.{
{
.Model.Name}
}
    Item>

        columns={
columns}

        rowKey="id"
        actionRef={
tableRef}

        search={
{
"{
{
"}
}

          labelWidth: 'auto',
        }
}

        toolBarRender={
    () =>
 [
          Button
            key="button"
            icon={
    PlusOutlined />
}

            type="primary"
            onClick={
    () =>
 {
    
              setModalAddVisible(true);

            }
}
    
          >
    
            新建
          /Button>
,
        ]}

        request={
async (params: API.{
{
.Model.Name}
}
    Item &
     API.PageInfo) =>
 {

          const resp = await get{
{
.Model.Name}
}
    List(params);

          return {

            data: resp.data.{
{
.Model.GraphqlName}
}
,
            total: resp.data.{
{
.Model.GraphqlName}
}
_aggregate.aggregate.count,
          }
    ;

        }
}
    
      />

      Modal
        destroyOnClose
        title="新增"
        visible={
modalAddVisible}

        footer={
null}

        onCancel={
    () =>
 setModalAddVisible(false)}
    
      >

        {
{
.Model.Name}
}
Add onFinish={
addItem}
     />
    
      /Modal>

      Modal
        destroyOnClose
        title="编辑"
        visible={
modalEditVisible}

        footer={
null}

        onCancel={
    () =>
 setModalEditVisible(false)}
    
      >

        {
{
.Model.Name}
}
Edit onFinish={
editItem}
 record={
record}
     />
    
      /Modal>
    
    /PageContainer>
    
  );

}
    ;
`

新增页面和编辑页面差别不大,分开定义是为了以后能分别扩展。新增页面:

const PageAddTmpl = `import ProForm, {
{
.Model.GenPageImportCtrls}
}

import {
 formLayout }
     from '@/common';

import {
 Row, Col, Space }
     from 'antd';
    

export default (props: any) =>
 {

  return (
    ProForm
      {
...formLayout}

      layout="horizontal"
      onFinish={
props.onFinish}

      submitter={
{
"{
{
"}
}

        // resetButtonProps: {
 style: {
 display: 'none' }
 }
    ,
        render: (_, dom) =>
     (
          Row>

            Col offset={
10}
    >
    
              Space>
{
dom}
    /Space>
    
            /Col>
    
          /Row>

        ),
      }
}
    
    >

    {
{
- with .Model.Fields}
}

    {
{
- range .}
}

{
{
- .GenPageCtrl}
}

    {
{
- end}
}

    {
{
- end}
}
    
    /ProForm>
    
  );

}
    ;
`

页面生成中有个地方困扰了我一阵,就是页面中有个和 text/template 标记冲突的地方,也就是 { { 的显示。比如上面的 submitter={ { "{ { "} } ,页面中需要直接显示 { { 2个字符,但 { { } } 框住的部分是模板中需要替换的部分。

所以,模板中需要显示 { { 的地方,可以用 { { "{ { "} } 代替。

总结

上面的代码生成虽然需要配合 illuminant 项目一起使用,但是其思路可以参考。

代码生成无非就是找出重复代码的规律,将其中变化的部分定义出来,然后通过模板来生成不同的代码。通过模板来生成代码,跟拷贝相似代码来修改相比,可以有效减少很多人为造成的混乱,比如拷贝过来后漏改,或者有些多余代码未删除等等。



通过以上内容的阐述,相信大家对“golang中标准库template的代码生成步骤是什么?”已经有了进一步的了解,更多相关的问题,欢迎关注网络或到官网咨询客服。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

golang

若转载请注明出处: golang中标准库template的代码生成步骤是什么?
本文地址: https://pptw.com/jishu/653544.html
PHP中强制类型转换怎样实现?一文带你看懂 详解node的redis集群配置是怎样,如何用redis集群功能

游客 回复需填写必要信息