feat: 机器脚本新增分配、组件属性类型不匹配警告调整

This commit is contained in:
meilin.huang
2025-06-16 20:13:03 +08:00
parent cc66fcddf5
commit 7eb4d064ea
35 changed files with 162 additions and 64 deletions

View File

@@ -11,8 +11,8 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.15",
"@logicflow/extension": "^2.0.20",
"@logicflow/core": "^2.0.16",
"@logicflow/extension": "^2.0.21",
"@vueuse/core": "^13.3.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-search": "^0.15.0",
@@ -24,7 +24,7 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.6.0",
"element-plus": "^2.10.1",
"element-plus": "^2.10.2",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"mitt": "^3.0.1",
@@ -45,7 +45,7 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.6",
"@tailwindcss/vite": "^4.1.9",
"@types/crypto-js": "^4.2.2",
"@types/node": "^18.14.0",
"@types/nprogress": "^0.2.0",
@@ -58,13 +58,13 @@
"code-inspector-plugin": "^0.20.9",
"dotenv": "^16.3.1",
"eslint": "^9.27.0",
"eslint-plugin-vue": "^10.1.0",
"eslint-plugin-vue": "^10.2.0",
"postcss": "^8.5.4",
"prettier": "^3.5.3",
"sass": "^1.89.1",
"tailwindcss": "^4.1.8",
"sass": "^1.89.2",
"tailwindcss": "^4.1.9",
"typescript": "^5.8.2",
"vite": "^6.3.5",
"vite": "npm:rolldown-vite@latest",
"vite-plugin-progress": "0.0.7",
"vue-eslint-parser": "^10.1.3"
},

View File

@@ -15,7 +15,7 @@ const config = {
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
// 系统版本
version: 'v1.10.0',
version: 'v1.10.1',
};
export default config;

View File

@@ -42,4 +42,5 @@ export function exportFile(filename: string, content: string) {
link.setAttribute('download', `${filename}`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // 下载完成后移除元素
}

View File

@@ -12,8 +12,9 @@ const props = defineProps({
required: true,
},
value: {
type: [Object, String, Number],
type: [Object, String, Number, null],
required: true,
default: () => null,
},
});
@@ -40,7 +41,7 @@ onMounted(() => {
});
const convert = (value: any) => {
const enumValue = EnumValue.getEnumByValue(props.enums, value) as any;
const enumValue = EnumValue.getEnumByValue(props.enums, value);
if (!enumValue) {
state.enumLabel = '-';
state.type = 'danger';
@@ -50,8 +51,8 @@ const convert = (value: any) => {
state.enumLabel = enumValue?.label || '';
if (enumValue.tag) {
state.color = enumValue.tag.color;
state.type = enumValue.tag.type;
state.color = enumValue.tag.color || '';
state.type = enumValue.tag.type || defaultType;
} else {
state.type = defaultType;
}

View File

@@ -68,7 +68,7 @@
trigger="click"
>
<div v-for="(item, index) in tableColumns" :key="index">
<el-checkbox v-model="item.show" :label="$t(item.label)" :true-value="true" :false-value="false" />
<el-checkbox v-model="item.show" :label="$t(item.label)" :true-value="1" :false-value="0" />
</div>
<template #reference>
<el-button icon="Operation" circle :size="props.size"></el-button>

View File

@@ -71,9 +71,9 @@ export class TableColumn {
formatFunc: Function;
/**
* 是否显示该列
* 是否显示该列,1显示 0不显示
*/
show: boolean = true;
show: number = 1;
/**
* 是否展示美化按钮主要用于美化json文本等

View File

@@ -87,6 +87,8 @@ export default {
scriptResultEnumRealTime: 'Real-time',
scriptTypeEnumPrivate: 'Private',
scriptTypeEnumPublic: 'Public',
category: 'Category',
categoryTips: 'support input new category and selection',
// security
cmdConfig: 'Command Config',

View File

@@ -27,7 +27,6 @@ export default {
menuCodePlaceholder: `A menu that does not begin with '/' will automatically concatenate the parent menu path`,
routerNameTips:
'For component caching to work, the key for route.ts in the frontend module should match the vue component name, such as ResourceList',
componentPathTips: 'Access path components, such as: ` system/resource/ResourceList `, default in ` views ` directory',
isCacheTips: `If yes is selected, it will be 'keepalive' cached (reentering the page without refreshing the page and requesting data again), and needs the route name to match the vue component name`,
isHideTips:
'Select Hide and the route will not appear in the menu bar, but it will still be accessible. Disabled will not be able to access and operate',

View File

@@ -88,6 +88,8 @@ export default {
scriptResultEnumRealTime: '实时交互',
scriptTypeEnumPrivate: '私有',
scriptTypeEnumPublic: '公共',
category: '分类',
categoryTips: '支持输入新分类并选择',
// security
cmdConfig: '命令配置',

View File

@@ -26,7 +26,6 @@ export default {
menuCodeTips: `菜单类型则为访问路径(若菜单路径不以'/'开头则访问地址会自动拼接父菜单路径)、否则为资源唯一编码`,
menuCodePlaceholder: `菜单不以'/'开头则自动拼接父菜单路径`,
routerNameTips: '前端模块下route.ts中对应的key与vue的组件名一致才可使组件缓存生效如ResourceList',
componentPathTips: '访问的组件路径,如:`system/resource/ResourceList`,默认在`views`目录下',
isCacheTips: '选择是则会被`keep-alive`缓存(重新进入页面不会刷新页面及重新请求数据)需要路由名与vue的组件名一致',
isHideTips: '选择隐藏则路由将不会出现在菜单栏中,但仍然可以访问。禁用则不可访问与操作',
externalLinkTips: '内嵌: 以iframe展示、外链: 新标签打开',

View File

@@ -2,7 +2,7 @@
<div class="layout-navbars-breadcrumb" v-show="themeConfig.isBreadcrumb">
<SvgIcon class="layout-navbars-breadcrumb-icon" :name="themeConfig.isCollapse ? 'expand' : 'fold'" @click="onThemeConfigChange" />
<el-breadcrumb class="layout-navbars-breadcrumb-hide">
<transition-group name="breadcrumb" mode="out-in">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="v.meta.title">
<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />

View File

@@ -83,9 +83,8 @@ type RouterConvCallbackFunc = (router: any) => void;
* @param name ==> title路由标题 相当于route.meta.title
*
* @param meta ==> 路由菜单元信息
* @param meta.routeName ==> route.name -> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选)
* @param meta.routeName ==> route.name -> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选) -> 对应模块下route.ts字段key
* @param meta.redirect ==> route.redirect -> 路由重定向地址
* @param meta.component ==> 文件路径
* @param meta.icon ==> 菜单和面包屑对应的图标
* @param meta.isHide ==> 是否在菜单中隐藏 (通常列表详情页需要隐藏)
* @param meta.isFull ==> 菜单是否全屏 (示例:数据大屏页面)

View File

@@ -21,7 +21,7 @@
<enum-tag :enums="FlowBizType" :value="procinst.bizType"></enum-tag>
</el-descriptions-item>
<el-descriptions-item :span="1" :label="$t('flow.initiator')">
<AccountInfo :username="procinst.creator" />
<AccountInfo :username="procinst.creator || ''" />
</el-descriptions-item>
<el-descriptions-item :span="1" :label="$t('flow.procinstStatus')">

View File

@@ -10,7 +10,7 @@
<el-table-column :label="$t('flow.approver')" min-width="100">
<template #default="scope">
<AccountInfo :username="scope.row.handler" />
<AccountInfo :username="scope.row.handler || ''" />
</template>
</el-table-column>

View File

@@ -19,6 +19,7 @@ const props = defineProps({
authCerts: {
type: [Array<any>],
required: true,
default: () => [],
},
});

View File

@@ -26,6 +26,7 @@ const props = defineProps({
tags: {
type: [Array<any>],
required: true,
default: () => [],
},
});
</script>

View File

@@ -19,10 +19,9 @@
:default-expanded-keys="props.defaultExpandedKeys"
>
<template #default="{ node, data }">
<span
<div
:id="node.key"
@dblclick="treeNodeDblclick(data, node)"
class="node-container flex items-center cursor-pointer select-none"
class="w-full node-container flex items-center cursor-pointer select-none"
:class="data.type.nodeDblclickFunc ? 'select-none' : ''"
>
<span v-if="data.type.value == TagTreeNode.TagPath">
@@ -44,7 +43,7 @@
<span class="absolute right-2.5 mt-0.5 text-[10px] text-gray-400">
<slot :node="node" :data="data" name="suffix"></slot>
</span>
</span>
</div>
</template>
</el-tree>
@@ -153,7 +152,16 @@ const loadNode = async (node: any, resolve: (data: any) => void, reject: () => v
return resolve(nodes);
};
const treeNodeClick = async (data: any) => {
let lastNodeClickTime = 0;
const treeNodeClick = async (data: any, node: any) => {
const currentClickNodeTime = Date.now();
if (currentClickNodeTime - lastNodeClickTime < 300) {
treeNodeDblclick(data, node);
return;
}
lastNodeClickTime = currentClickNodeTime;
if (!data.disabled && !data.type.nodeDblclickFunc && data.type.nodeClickFunc) {
emit('nodeClick', data);
await data.type.nodeClickFunc(data);
@@ -170,7 +178,6 @@ const treeNodeDblclick = (data: any, node: any) => {
node.expand();
}
// emit('nodeDblick', data);
if (!data.disabled && data.type.nodeDblclickFunc) {
data.type.nodeDblclickFunc(data);
}

View File

@@ -89,7 +89,7 @@ import { Rules } from '@/common/rule';
const props = defineProps({
instance: {
type: [Boolean, Object],
type: [Boolean, Object, null],
},
db: {
type: [Boolean, Object],

View File

@@ -365,7 +365,7 @@ const editDb = (data: any) => {
state.dbEditDialog.data = { ...data };
} else {
state.dbEditDialog.data = {
instanceId: props.instance.id,
instanceId: props.instance?.id,
};
}
state.dbEditDialog.title = data ? useI18nEditTitle('db.db') : useI18nCreateTitle('db.db');
@@ -373,7 +373,7 @@ const editDb = (data: any) => {
};
const confirmEditDb = async (db: any) => {
db.instanceId = props.instance.id;
db.instanceId = props.instance?.id;
await dbApi.saveDb.request(db);
useI18nSaveSuccessMsg();
search();

View File

@@ -155,7 +155,7 @@ const state = reactive({
},
dbEditDialog: {
visible: false,
instance: null as any,
instance: {},
title: '',
},
});

View File

@@ -61,10 +61,8 @@
</el-form>
<template #footer>
<div>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</template>
</el-drawer>
</div>

View File

@@ -1,15 +1,20 @@
<template>
<div>
<el-dialog
<el-drawer
:title="title"
v-model="dialogVisible"
:close-on-click-modal="false"
:before-close="cancel"
:before-close="onCancel"
:show-close="true"
:destroy-on-close="true"
width="1000px"
size="1000px"
header-class="!mb-1"
>
<el-form :model="form" :rules="rules" ref="scriptForm" label-width="auto">
<template #header>
<DrawerHeader :header="title" :back="onCancel" />
</template>
<el-form :model="form" :rules="rules" ref="scriptForm" label-position="top">
<el-form-item prop="name" :label="$t('common.name')" required>
<el-input v-model="form.name"></el-input>
</el-form-item>
@@ -22,6 +27,12 @@
<EnumSelect :enums="ScriptResultEnum" v-model="form.type" default-first-option />
</el-form-item>
<el-form-item prop="category" :label="$t('machine.category')">
<el-select v-model="form.category" filterable allow-create :placeholder="$t('machine.categoryTips')">
<el-option v-for="item in categorys" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item class="!w-full">
<template #label>
<el-tooltip placement="top">
@@ -43,14 +54,12 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="btnOk">
{{ $t('common.save') }}
</el-button>
</div>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="onConfirm">
{{ $t('common.save') }}
</el-button>
</template>
</el-dialog>
</el-drawer>
</div>
</template>
@@ -64,6 +73,7 @@ import SvgIcon from '@/components/svgIcon/index.vue';
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import { useI18nFormValidate, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
import { Rules } from '@/common/rule';
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
const props = defineProps({
data: {
@@ -93,6 +103,7 @@ const rules = {
const { isCommon, machineId } = toRefs(props);
const scriptForm: any = ref(null);
const categorys = ref([]);
const state = reactive({
params: [] as any,
@@ -104,6 +115,7 @@ const state = reactive({
script: '',
params: '',
type: null,
category: '',
},
btnLoading: false,
});
@@ -114,6 +126,9 @@ watch(props, (newValue: any) => {
if (!dialogVisible.value) {
return;
}
machineApi.scriptCategorys.request().then((res: any) => {
categorys.value = res;
});
if (newValue.data) {
state.form = { ...newValue.data };
if (state.form.params) {
@@ -125,7 +140,7 @@ watch(props, (newValue: any) => {
}
});
const btnOk = async () => {
const onConfirm = async () => {
state.form.machineId = isCommon.value ? 9999999 : (machineId?.value as any);
await useI18nFormValidate(scriptForm);
if (state.params) {
@@ -134,11 +149,11 @@ const btnOk = async () => {
machineApi.saveScript.request(state.form).then(() => {
useI18nSaveSuccessMsg();
emit('submitSuccess');
cancel();
onCancel();
});
};
const cancel = () => {
const onCancel = () => {
dialogVisible.value = false;
emit('cancel');
state.params = [];

View File

@@ -99,6 +99,7 @@ import { DynamicFormDialog } from '@/components/dynamic-form';
import { SearchItem } from '@/components/SearchForm';
import { useI18n } from 'vue-i18n';
import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nEditTitle } from '@/hooks/useI18n';
import { OptionsApi } from '@/components/SearchForm/index';
const { t } = useI18n();
@@ -117,11 +118,24 @@ const pageTableRef: Ref<any> = ref(null);
const state = reactive({
selectionData: [],
searchItems: [SearchItem.select('type', 'common.type').withEnum(ScriptTypeEnum)],
searchItems: [
SearchItem.select('type', 'common.type').withEnum(ScriptTypeEnum),
SearchItem.select('category', 'machine.category').withOptionsApi(
OptionsApi.new(machineApi.scriptCategorys, {}).withConvertFn((res) => {
return res.map((x: any) => {
return {
label: x,
value: x,
};
});
})
),
],
columns: [
TableColumn.new('name', 'common.name'),
TableColumn.new('description', 'common.remark'),
TableColumn.new('type', 'common.type').typeTag(ScriptResultEnum),
TableColumn.new('category', 'machine.category'),
TableColumn.new('action', 'common.operation').isSlot().setMinWidth(140).alignCenter(),
],
query: {

View File

@@ -23,6 +23,7 @@ export const machineApi = {
// 删除机器
del: Api.newDelete('/machines/{id}'),
scripts: Api.newGet('/machines/{machineId}/scripts'),
scriptCategorys: Api.newGet('/machines/scripts/categorys'),
runScript: Api.newGet('/machines/scripts/{scriptId}/{ac}/run'),
saveScript: Api.newPost('/machines/{machineId}/scripts'),
deleteScript: Api.newDelete('/machines/{machineId}/scripts/{scriptId}'),

View File

@@ -117,6 +117,7 @@
</el-tab-pane>
<el-tab-pane
class="h-full"
:disabled="currentTag.type != TagResourceTypeEnum.Tag.value"
:label="`Redis (${resourceCount.redis || 0})`"
:name="RedisTag"

View File

@@ -2,7 +2,7 @@
<div class="h-full">
<page-table :page-api="logApi.list" :search-items="searchItems" v-model:query-form="query" :columns="columns">
<template #creator="{ data }">
<account-info :username="data.creator" />
<account-info :username="data.creator || ''" />
</template>
</page-table>
</div>

View File

@@ -58,11 +58,21 @@ const viteConfig: UserConfig = {
entryFileNames: `assets/[name]-[hash].js`,
chunkFileNames: `assets/[name]-[hash].js`,
assetFileNames: `assets/[name]-[hash].[ext]`,
compact: true,
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
echarts: ['echarts'],
monaco: ['monaco-editor'],
advancedChunks: {
groups: [
{
name: 'vue',
test: /(vue|vue-router|pinia)/i,
},
{
name: 'echarts',
test: /(echarts)/i,
},
{
name: 'monaco',
test: /(monaco-editor)/i,
},
],
},
},
},

View File

@@ -68,7 +68,7 @@ func (d *dbAppImpl) GetPageList(condition *entity.DbQuery, orderBy ...string) (*
func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
// 查找是否存在
oldDb := &entity.Db{Name: dbEntity.Name, InstanceId: dbEntity.InstanceId}
oldDb := &entity.Db{Name: dbEntity.Name, InstanceId: dbEntity.InstanceId, AuthCertName: dbEntity.AuthCertName}
authCert, err := d.resourceAuthCertApp.GetAuthCert(dbEntity.AuthCertName)
if err != nil {

View File

@@ -32,6 +32,7 @@ type MachineScriptForm struct {
Name string `json:"name" binding:"required"`
MachineId uint64 `json:"machineId" binding:"required"`
Type int `json:"type" binding:"required"`
Category string `json:"category"`
Description string `json:"description" binding:"required"`
Params string `json:"params"`
Script string `json:"script" binding:"required"`

View File

@@ -28,6 +28,8 @@ func (ms *MachineScript) ReqConfs() *req.Confs {
// 获取指定机器脚本列表
req.NewGet(":machineId/scripts", ms.MachineScripts),
req.NewGet("/scripts/categorys", ms.MachineScriptCategorys),
req.NewPost(":machineId/scripts", ms.SaveMachineScript).Log(req.NewLogSave("机器-保存脚本")).RequiredPermissionCode("machine:script:save"),
req.NewDelete(":machineId/scripts/:scriptId", ms.DeleteMachineScript).Log(req.NewLogSave("机器-删除脚本")).RequiredPermissionCode("machine:script:del"),
@@ -39,12 +41,18 @@ func (ms *MachineScript) ReqConfs() *req.Confs {
}
func (m *MachineScript) MachineScripts(rc *req.Ctx) {
condition := &entity.MachineScript{MachineId: GetMachineId(rc)}
condition := &entity.MachineScript{MachineId: GetMachineId(rc), Category: rc.Query("category")}
res, err := m.machineScriptApp.GetPageList(condition, rc.GetPageParam())
biz.ErrIsNil(err)
rc.ResData = model.PageResultConv[*entity.MachineScript, *vo.MachineScriptVO](res)
}
func (m *MachineScript) MachineScriptCategorys(rc *req.Ctx) {
res, err := m.machineScriptApp.GetScriptCategorys(rc.MetaCtx)
biz.ErrIsNil(err)
rc.ResData = res
}
func (m *MachineScript) SaveMachineScript(rc *req.Ctx) {
form, machineScript := req.BindJsonAndCopyTo[*form.MachineScriptForm, *entity.MachineScript](rc)

View File

@@ -49,6 +49,7 @@ type MachineScriptVO struct {
Name *string `json:"name"`
Script *string `json:"script"`
Type *int `json:"type"`
Category string `json:"category"`
Description *string `json:"description"`
Params *string `json:"params"`
MachineId *uint64 `json:"machineId"`

View File

@@ -7,6 +7,7 @@ import (
"mayfly-go/pkg/base"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils/collx"
)
type MachineScript interface {
@@ -15,11 +16,16 @@ type MachineScript interface {
// 分页获取机器脚本信息列表
GetPageList(condition *entity.MachineScript, pageParam model.PageParam, orderBy ...string) (*model.PageResult[*entity.MachineScript], error)
// GetScriptCategorys 获取脚本分类
GetScriptCategorys(ctx context.Context) ([]string, error)
Save(ctx context.Context, entity *entity.MachineScript) error
Delete(ctx context.Context, id uint64)
}
var _ (MachineScript) = (*machineScriptAppImpl)(nil)
type machineScriptAppImpl struct {
base.AppImpl[*entity.MachineScript, repository.MachineScript]
@@ -33,6 +39,15 @@ func (m *machineScriptAppImpl) GetPageList(condition *entity.MachineScript, page
return m.GetRepo().GetPageList(condition, pageParam, orderBy...)
}
func (m *machineScriptAppImpl) GetScriptCategorys(ctx context.Context) ([]string, error) {
scripts, err := m.ListByCond(new(entity.MachineScript), "category")
if err != nil {
return nil, err
}
return collx.ArrayRemoveBlank(collx.ArrayDeduplicate(collx.ArrayMap(scripts, func(script *entity.MachineScript) string { return script.Category }))), nil
}
// 保存机器脚本
func (m *machineScriptAppImpl) Save(ctx context.Context, ms *entity.MachineScript) error {
// 如果机器id不为公共脚本id则校验机器是否存在

View File

@@ -8,7 +8,8 @@ type MachineScript struct {
Name string `json:"name" gorm:"not null;size:255;comment:脚本名"` // 脚本名
MachineId uint64 `json:"machineId" gorm:"not null;comment:机器id[0:公共]"` // 机器id
Type int `json:"type" gorm:"comment:脚本类型[1: 有结果2无结果3实时交互]"` // 脚本类型[1: 有结果2无结果3实时交互]
Description string `json:"description" gorm:"size:255;comment:脚本描述"` // 脚本描述
Params string `json:"params" gorm:"size:500;comment:脚本入参"` // 参数列表json
Script string `json:"script" gorm:"type:text;comment:脚本内容"` // 脚本内容
Category string `json:"category" gorm:"size:20;comment:分类"`
Description string `json:"description" gorm:"size:255;comment:脚本描述"` // 脚本描述
Params string `json:"params" gorm:"size:500;comment:脚本入参"` // 参数列表json
Script string `json:"script" gorm:"type:text;comment:脚本内容"` // 脚本内容
}

View File

@@ -4,7 +4,7 @@ import "fmt"
const (
AppName = "mayfly-go"
Version = "v1.10.0"
Version = "v1.10.1"
)
func GetAppInfo() string {

View File

@@ -3,6 +3,7 @@ package migrations
import (
esentity "mayfly-go/internal/es/domain/entity"
flowentity "mayfly-go/internal/flow/domain/entity"
machineentity "mayfly-go/internal/machine/domain/entity"
sysentity "mayfly-go/internal/sys/domain/entity"
"mayfly-go/pkg/model"
"time"
@@ -14,6 +15,7 @@ import (
func V1_10() []*gormigrate.Migration {
var migrations []*gormigrate.Migration
migrations = append(migrations, V1_10_0()...)
migrations = append(migrations, V1_10_1()...)
return migrations
}
@@ -132,3 +134,22 @@ func V1_10_0() []*gormigrate.Migration {
},
}
}
func V1_10_1() []*gormigrate.Migration {
return []*gormigrate.Migration{
{
ID: "20250610-v1.10.1",
Migrate: func(tx *gorm.DB) error {
if !tx.Migrator().HasColumn(&machineentity.MachineScript{}, "category") {
if err := tx.Migrator().AddColumn(&machineentity.MachineScript{}, "category"); err != nil {
return err
}
}
return nil
},
Rollback: func(tx *gorm.DB) error {
return nil
},
},
}
}