xorm 代码生成

在实际开发中, 每添加一个表时,一般就会有对应的增删改查操作, 而这些操作代码又高度相似,copy 代码时枯燥乏味。因此在插件中添加了 xorm 基本代码生成功能。让编码更聚焦核心功能。

如何使用

插件仅在 niuhe/.model.niuhe 中读取表定义并生成代码。

#mode 可选 默认值为 api,
#niuhe 可选, 默认值为 False, 在生成对应的 niuhe 是否覆盖已存在的 niuhe 文件内容
#dao 可选, 默认值为 False, 在生成对应的 dao 是否覆盖已存在的 dao 文件内容
#service 可选, 默认值为 False, 在生成对应的 service 是否覆盖已存在的 service 文件内容
#model 可选, 默认值为 False, 在生成对应的 model 是否覆盖已存在的 model 文件内容
#vite 可选, 默认值为 False, 在生成对应的 vite 是否覆盖已存在的 vite 文件内容, 需在 .config.json5 中 langs 中添加 "vite"
#这里是做示例用, 实际开发中直接写 class Config():即可
class Config(mode='api', niuhe=True, dao=True, service=True, model=True, vite=True):
    '''系统配置表'''
    name = required.String(desc='配置名称', index=True, search=True, len=255, notnull=True)# index 加索引, len varchar 最大长度, notnull 是否为空 search 分页查询时是否出现在参数中
    value = required.Long(desc='配置值', search=True)

上述参数说明

参数类型默认值必须描述
modestr'api'可选生成代码的在哪个mode下
niuheboolFalse可选是否覆盖已存在的 niuhe 文件内容
daoboolFalse可选是否覆盖已存在的 dao 文件内容
serviceboolFalse可选是否覆盖已存在的 service 文件内容
modelboolFalse可选是否覆盖已存在的 model 文件内容
viteboolFalse可选是否覆盖已存在的 vite 文件内容, 需要配置 .config.json5 中的 langs 添加 "vite"

当首次生成代码时, 会生成以下几个文件(以 #app=demo)

  • niuhe/api_config.niuhe (需要手动 include 到 all.niuhe 中)
  • src/demo/xorm/models/config.go
  • src/demo/xorm/daos/config_dao.go
  • src/demo/xorm/services/config_svc.go
  • src/demo/app/api/views/config_views.go (include 后才会生成)
  • src/demo/app/api/views/gen_config_views.go (include 后才会生成)
  • vite/api_config.vue (需要在 .config.json5 中 langs 添加 "vite")

除上述文件外, 如配置了 docs, ts 等语言的生成,也会更新对应的文件内容. 各个生成的文件内容如下:

本节以下文件内容均为插件生成

niuhe/api_config.niuhe


class ConfigItem():
	'''系统配置表'''
	id = optional.Long(desc='id')
	name = required.StringField(desc='配置名称')
	value = required.LongField(desc='配置值')
	create_at = optional.String(desc='创建时间')
	update_at = optional.String(desc='更新时间')

class ConfigFormReq():
	'''请求 Config 信息'''
	id = required.Long()

class ConfigPageReq():
	'''分页查询 Config 信息'''
	page = required.Integer(desc='页码')
	size = required.Integer(desc='每页数量')
	value = required.LongField(desc='配置值')

class ConfigPageRsp():
	'''分页查询 Config 信息'''
	total = required.Long(desc='总数')
	items = repeated.Message(cls=ConfigItem, desc='Config信息')

class ConfigDeleteReq():
	'''批量删除 Config 信息'''
	ids = repeated.Long(desc='记录id列表')

class ConfigNoneRsp():
	'''Config 无数据返回'''
	pass

with services():
	GET('分页查询获取 Config 信息', '/api/config/page/', ConfigPageReq, ConfigPageRsp)
	GET('查询获取 Config 信息', '/api/config/form/', ConfigFormReq, ConfigItem)
	POST('添加 Config 信息', '/api/config/add/', ConfigItem, ConfigItem)
	POST('更新 Config 信息', '/api/config/update/', ConfigItem, ConfigItem)
	DELETE('删除 Config 信息', '/api/config/delete/', ConfigDeleteReq, ConfigNoneRsp)

model 表定义

src/demo/xorm/models/config.go

package models

// Generated by niuhe.idl

// 如要同步表结构, 需要手动将 Config 手动添加到 models.go 文件的 GetSyncModels 数组中

import (
	"demo/app/api/protos"

	"time"
)

// 系统配置表
type Config struct {
	Id       int64     `xorm:"NOT NULL PK AUTOINCR INT(11)"`
	Name     string    `xorm:"VARCHAR(255) COMMENT('配置名称')"` // 配置名称
	Value    int64     `xorm:"INT COMMENT('配置值')"`           // 配置值
	CreateAt time.Time `xorm:"created"`                      // 创建时间
	UpdateAt time.Time `xorm:"updated"`                      // 更新时间
	DeleteAt time.Time `xorm:"deleted"`                      // 删除时间
}

func (row *Config) ToProto(item *protos.ConfigItem) *protos.ConfigItem {
	if item == nil {
		item = &protos.ConfigItem{}
	}
	item.Id = row.Id
	item.Name = row.Name
	item.Value = row.Value
	item.CreateAt = row.CreateAt.Format(time.DateTime)
	item.UpdateAt = row.UpdateAt.Format(time.DateTime)
	return item
}

dao 表定义

src/demo/xorm/daos/config_dao.go

package daos

// Generated by niuhe.idl

import (
	"demo/xorm/models"
	"github.com/ma-guo/niuhe"
)

// 系统配置表
type _ConfigDao struct {
	*Dao
}

// 系统配置表
func (dao *Dao) Config() *_ConfigDao {
	return &_ConfigDao{Dao: dao}
}

// 根据ID获取数据
func (dao *_ConfigDao) GetByIds(ids ...int64) ([]*models.Config, error) {
	rows := []*models.Config{}
	session := dao.db()
	err := session.In("id", ids).Desc("`id`").Find(&rows)
	if err != nil {
		niuhe.LogInfo("GetByIds Config error: %v", err)
		return nil, err
	}
	return rows, nil
}

// 分页获取数据
func (dao *_ConfigDao) GetPage(page, size int, value int64) ([]*models.Config, int64, error) {
	rows := make([]*models.Config, 0)
	session := dao.db()
	dao.Like(session, "`value`", value)
	dao.Limit(session, page, size)
	total, err := session.Desc("`id`").FindAndCount(&rows)
	if err != nil {
		niuhe.LogInfo("GetPage Config error: %v", err)
		return nil, 0, err
	}
	return rows, total, nil
}

service 表定义

src/demo/xorm/services/config_svc.go

package services

// Generated by niuhe.idl

import (
	"github.com/ma-guo/niuhe"

	"demo/xorm/models"
)

// 系统配置表
type _ConfigSvc struct {
	*_Svc
}

// 系统配置表
func (svc *_Svc) Config() *_ConfigSvc {
	return &_ConfigSvc{svc}
}

// 获取单个数据
func (svc *_ConfigSvc) GetById(id int64) (*models.Config, bool, error) {
	if id <= 0 {
		return nil, false, nil
	}
	row := &models.Config{Id: id}
	has, err := svc.dao().GetBy(row)
	if err != nil {
		niuhe.LogInfo("GetById Config error: %v", err)
	}
	return row, has, err
}

// 获取单个数据
func (svc *_ConfigSvc) GetBy(row *models.Config) (bool, error) {
	has, err := svc.dao().GetBy(row)
	if err != nil {
		niuhe.LogInfo("GetBy Config error: %v", err)
	}
	return has, err
}

// 更新数据
func (svc *_ConfigSvc) Update(row *models.Config) (bool, error) {
	has, err := svc.dao().Update(row.Id, row)
	if err != nil {
		niuhe.LogInfo("Update Config error: %v", err)
	}
	return has, err
}

// 插入数据
func (svc *_ConfigSvc) Insert(rows ...*models.Config) error {
	if len(rows) == 0 {
		return nil
	}
	// 2000条是经验值, 可根据自己需要更改
	batchSize := 2000
	for i := 0; i < len(rows); i += batchSize {
		end := i + batchSize
		if end > len(rows) {
			end = len(rows)
		}
		_, err := svc.dao().Insert(rows[i:end])
		if err != nil {
			niuhe.LogInfo("Insert Config error: %v", err)
			return err
		}
	}
	return nil
}

// 删除数据
func (svc *_ConfigSvc) Delete(rows []*models.Config) error {
	if len(rows) == 0 {
		return nil
	}
	_, err := svc.dao().Delete(rows)
	if err != nil {
		niuhe.LogInfo("Delete Config error: %v", err)
	}
	return err
}

// 根据 id 获取 map 数据
func (svc *_ConfigSvc) GetByIds(ids ...int64) (map[int64]*models.Config, error) {
	rowsMap := make(map[int64]*models.Config, 0)
	if len(ids) == 0 {
		return rowsMap, nil
	}
	rows, err := svc.dao().Config().GetByIds(ids...)
	if err != nil {
		niuhe.LogInfo("GetByIds Config error: %v", err)
		return nil, err
	}
	for _, row := range rows {
		rowsMap[row.Id] = row
	}
	return rowsMap, nil
}

// 分页获取数据
func (svc *_ConfigSvc) GetPage(page, size int, value int64) ([]*models.Config, int64, error) {
	rows, total, err := svc.dao().Config().GetPage(page, size, value)
	if err != nil {
		niuhe.LogInfo("GetPage Config error: %v", err)
	}
	return rows, total, nil
}

config_views 定义

src/demo/xorm/models/config_views.go

package services

// Generated by niuhe.idl

import (
	"github.com/ma-guo/niuhe"

	"demo/xorm/models"
)

// 系统配置表
type _ConfigSvc struct {
	*_Svc
}

// 系统配置表
func (svc *_Svc) Config() *_ConfigSvc {
	return &_ConfigSvc{svc}
}

// 获取单个数据
func (svc *_ConfigSvc) GetById(id int64) (*models.Config, bool, error) {
	if id <= 0 {
		return nil, false, nil
	}
	row := &models.Config{Id: id}
	has, err := svc.dao().GetBy(row)
	if err != nil {
		niuhe.LogInfo("GetById Config error: %v", err)
	}
	return row, has, err
}

// 获取单个数据
func (svc *_ConfigSvc) GetBy(row *models.Config) (bool, error) {
	has, err := svc.dao().GetBy(row)
	if err != nil {
		niuhe.LogInfo("GetBy Config error: %v", err)
	}
	return has, err
}

// 更新数据
func (svc *_ConfigSvc) Update(row *models.Config) (bool, error) {
	has, err := svc.dao().Update(row.Id, row)
	if err != nil {
		niuhe.LogInfo("Update Config error: %v", err)
	}
	return has, err
}

// 插入数据
func (svc *_ConfigSvc) Insert(rows ...*models.Config) error {
	if len(rows) == 0 {
		return nil
	}
	// 2000条是经验值, 可根据自己需要更改
	batchSize := 2000
	for i := 0; i < len(rows); i += batchSize {
		end := i + batchSize
		if end > len(rows) {
			end = len(rows)
		}
		_, err := svc.dao().Insert(rows[i:end])
		if err != nil {
			niuhe.LogInfo("Insert Config error: %v", err)
			return err
		}
	}
	return nil
}

// 删除数据
func (svc *_ConfigSvc) Delete(rows []*models.Config) error {
	if len(rows) == 0 {
		return nil
	}
	_, err := svc.dao().Delete(rows)
	if err != nil {
		niuhe.LogInfo("Delete Config error: %v", err)
	}
	return err
}

// 根据 id 获取 map 数据
func (svc *_ConfigSvc) GetByIds(ids ...int64) (map[int64]*models.Config, error) {
	rowsMap := make(map[int64]*models.Config, 0)
	if len(ids) == 0 {
		return rowsMap, nil
	}
	rows, err := svc.dao().Config().GetByIds(ids...)
	if err != nil {
		niuhe.LogInfo("GetByIds Config error: %v", err)
		return nil, err
	}
	for _, row := range rows {
		rowsMap[row.Id] = row
	}
	return rowsMap, nil
}

// 分页获取数据
func (svc *_ConfigSvc) GetPage(page, size int, value int64) ([]*models.Config, int64, error) {
	rows, total, err := svc.dao().Config().GetPage(page, size, value)
	if err != nil {
		niuhe.LogInfo("GetPage Config error: %v", err)
	}
	return rows, total, nil
}

新增的 protos 定义

src/demo/api/protos/gen_protos.go


// 系统配置表
type ConfigItem struct {
	Id       int64  `json:"id" zpf_name:"id"`                       //	id
	Name     string `json:"name" zpf_name:"name" zpf_reqd:"true"`   //	配置名称
	Value    int64  `json:"value" zpf_name:"value" zpf_reqd:"true"` //	配置值
	CreateAt string `json:"create_at" zpf_name:"create_at"`         //	创建时间
	UpdateAt string `json:"update_at" zpf_name:"update_at"`         //	更新时间
}

// 请求,Config,信息
type ConfigFormReq struct {
	Id int64 `json:"id" zpf_name:"id" zpf_reqd:"true"`
}

// 分页查询,Config,信息
type ConfigPageReq struct {
	Page  int   `json:"page" zpf_name:"page" zpf_reqd:"true"`   //	页码
	Size  int   `json:"size" zpf_name:"size" zpf_reqd:"true"`   //	每页数量
	Value int64 `json:"value" zpf_name:"value" zpf_reqd:"true"` //	配置值
}

// 分页查询,Config,信息
type ConfigPageRsp struct {
	Total int64         `json:"total" zpf_name:"total" zpf_reqd:"true"` //	总数
	Items []*ConfigItem `json:"items" zpf_name:"items"`                 //	Config信息
}

// 批量删除,Config,信息
type ConfigDeleteReq struct {
	Ids []int64 `json:"ids" zpf_name:"ids"` //	记录id列表
}

// Config,无数据返回
type ConfigNoneRsp struct {
}

vite 定义

文件路径 vite/api_config.vue。vite 需结合 vue3-element-admin 库使用, 是生成的增删改查 api 管理页面内容。

<template>
  <div class="app-container">
    <div class="search-container">
      <el-form ref="queryFormRef" :model="queryParams" :inline="true">
        <el-form-item prop="value" label="配置值">
          <el-input v-model="queryParams.value" placeholder="配置值" clearable @keyup.enter="fetchPage" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="fetchPage"><i-ep-search />搜索</el-button>
          <el-button @click="resetQuery"><i-ep-refresh />重置</el-button>
        </el-form-item>
      </el-form>
    </div>

    <el-card shadow="never" class="table-container">
      <template #header>
        <el-button @click="openDialogWithAdd()" type="success"><i-ep-plus />新增</el-button>
        <el-button type="danger" :disabled="state.ids.length === 0"
          @click="bantchDelete()"><i-ep-delete />删除</el-button>
      </template>
      <el-table ref="dataTableRef" v-loading="state.loading" :data="configItems" highlight-current-row border
        @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column label="ID" prop="id" align="center" />
        <el-table-column label="配置名称" prop="name" align="center" />
        <el-table-column label="配置值" prop="value" align="center" />
        <el-table-column fixed="right" label="操作" width="140" align="center">
          <template #default="{ row }">
            <el-button type="primary" size="small" link @click="openDialogWithEdit(row.id)">
              <i-ep-edit />编辑
            </el-button>
            <el-button type="primary" size="small" link @click="handleDelete(row.id)">
              <i-ep-delete />删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>

      <pagination v-if="state.total > 0" v-model:total="state.total" v-model:page="queryParams.page"
        v-model:limit="queryParams.size" @pagination="fetchPage" />
    </el-card>

    <!-- Config 表单弹窗 -->
    <el-dialog v-model="state.dialogVisible" :title="state.dialogTitle" @close="closeDialog">
      <el-form ref="configFormRef" :model="formData" :rules="rules" label-width="100px">
        <el-form-item label="ID" prop="id" v-if="formData.id > 0">
          <el-input v-model="formData.id" disabled placeholder="" />
        </el-form-item>
        <el-form-item prop="name" label="配置名称">
          <el-input v-model="formData.name" placeholder="配置名称" clearable />
        </el-form-item>
        <el-form-item prop="value" label="配置值">
          <el-input v-model="formData.value" placeholder="配置值" clearable />
        </el-form-item>
      </el-form>

      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确 定</el-button>
          <el-button @click="closeDialog">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">

import { setConfigAdd } from "@/api/demo/api";
import { setConfigUpdate } from "@/api/demo/api";
import { deleteConfigDelete } from "@/api/demo/api";
import { getConfigForm } from "@/api/demo/api";
import { getConfigPage } from "@/api/demo/api";

defineOptions({
  name: "config",
  inheritAttrs: false,
});

const queryFormRef = ref(ElForm);

const configFormRef = ref(ElForm);
const configItems = ref<Demo.ConfigItem[]>();
const state = reactive({
  loading: false,
  total: 0,
  ids: [] as number[],
  dialogVisible: false,
  dialogTitle: "",
});

const queryParams = reactive<Demo.ConfigPageReq>({
  page: 1,
  size: 10,
  value: 0,
});

const formData = reactive<Demo.ConfigItem>({
  id: 0,
  name: "",
  value: 0,
  create_at: "",
  update_at: ""
});

// 根据需要添加校验规则
const rules = reactive({
  //   name: [{ required: true, message: "本字段必填", trigger: "blur" }],
});

/** 查询 */
const fetchPage = async () => {
  state.loading = true;
  const rsp = await getConfigPage(queryParams);
  state.loading = false;
  if (rsp.result == 0) {
    configItems.value = rsp.data.items;
    state.total = rsp.data.total;
  }
}
/** 重置查询 */
function resetQuery() {
  queryFormRef.value.resetFields();
  queryParams.page = 1;
  fetchPage();
}

/** 行checkbox 选中事件 */
function handleSelectionChange(selection: any) {
  state.ids = selection.map((item: any) => item.id);
}

/** 打开添加弹窗 */
function openDialogWithAdd() {
  state.dialogVisible = true;
  state.dialogTitle = "添加Config";
  resetForm();
}
/** 打开编辑弹窗 */
const openDialogWithEdit = async (roleId: number) => {
  state.dialogVisible = true;
  state.dialogTitle = "修改Config";
  state.loading = true;
  const rsp = await getConfigForm({ id: roleId });
  state.loading = false;
  if (rsp.result == 0) {
    Object.assign(formData, rsp.data);
  }
}

/** 保存提交 */
function handleSubmit() {
  configFormRef.value.validate((valid: any) => {
    if (valid) {
      if (formData.id) {
        updateRowRecord();
      } else {
        addRowRecord();
      }
    }
  });
}
/** 新增记录 */
const addRowRecord = async () => {
  state.loading = true;
  const rsp = await setConfigAdd(formData);
  state.loading = false
  if (rsp.result == 0) {
    ElMessage.success("添加成功");
    closeDialog();
    resetQuery();
  }
}
/** 修改记录 */
const updateRowRecord = async () => {
  state.loading = true;
  const rsp = await setConfigUpdate(formData);
  state.loading = false
  if (rsp.result == 0) {
    ElMessage.success("修改成功");
    closeDialog();
    resetQuery();
  }
}
/** 关闭表单弹窗 */
function closeDialog() {
  state.dialogVisible = false;
  resetForm();
}

/** 重置表单 */
function resetForm() {
  const value = configFormRef.value;
  if (value) {
    value.resetFields();
    value.clearValidate();
  }
  formData.id = 0;
  formData.name = "";
  formData.value = 0;
  formData.create_at = "";
  formData.update_at = "";
}

/** 删除 Config */
function handleDelete(id: number) {
  ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(async () => {
    state.loading = true;
    const rsp = await deleteConfigDelete({ ids: [id] });
    state.loading = false;
    if (rsp.result == 0) {
      ElMessage.success("删除成功");
      resetQuery();
    }
  });
}

/** 批量删除 */
const bantchDelete = () => {
  if (state.ids.length <= 0) {
    ElMessage.warning("请勾选删除项");
    return;
  }
  ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(async () => {
    state.loading = true;
    const rsp = await deleteConfigDelete({ ids: state.ids });
    state.loading = false;
    if (rsp.result == 0) {
      ElMessage.success("删除成功");
    }
    resetQuery();
  });
};

onMounted(() => {
  fetchPage();
});
</script>