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)
上述参数说明
参数 | 类型 | 默认值 | 必须 | 描述 |
---|---|---|---|---|
mode | str | 'api' | 可选 | 生成代码的在哪个mode下 |
niuhe | bool | False | 可选 | 是否覆盖已存在的 niuhe 文件内容 |
dao | bool | False | 可选 | 是否覆盖已存在的 dao 文件内容 |
service | bool | False | 可选 | 是否覆盖已存在的 service 文件内容 |
model | bool | False | 可选 | 是否覆盖已存在的 model 文件内容 |
vite | bool | False | 可选 | 是否覆盖已存在的 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>