Procházet zdrojové kódy

feat: migrate MySQL to SQLite with system data seed

lq před 1 měsícem
rodič
revize
220a7e13c1
6 změnil soubory, kde provedl 514 přidání a 28 odebrání
  1. 27 23
      README.md
  2. 3 3
      config.yaml
  3. 1 1
      doc/PROJECT.md
  4. 25 1
      server/initialize/gorm_sqlite.go
  5. 457 0
      server/initialize/seed.go
  6. 1 0
      server/main.go

+ 27 - 23
README.md

@@ -1,19 +1,18 @@
 # 智慧停车管理系统 (Smart Parking)
 
-基于 Wails v2 构建的单进程桌面应用。
+基于 Wails v2 构建的单进程桌面应用,零外部依赖
 
 ## 技术栈
 
 - **桌面框架**:Wails v2.13
 - **后端**:Go 1.25 + Gin + GORM + Casbin + JWT
 - **前端**:Vue 3 + Vite 4 + Element Plus + Pinia
-- **数据库**:MySQL 5.7+(外部
+- **数据库**:SQLite(`%APPDATA%/smart-parking/lc_garage.db`
 
 ## 环境要求
 
 - Go >= 1.25
 - Node.js >= 16
-- MySQL 5.7+
 - Wails CLI
 - Windows 10 build 1809+(WebView2 运行时)
 
@@ -23,19 +22,9 @@
 go install github.com/wailsapp/wails/v2/cmd/wails@latest
 ```
 
-## 初始化数据库
-
-```bash
-mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS lc_garage DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"
-mysql -uroot -proot lc_garage < lc_garage.sql
-```
-
-修改 `config.yaml` 中的数据库连接信息(默认 `root/root@127.0.0.1:3306/lc_garage`)。
-
 ## 安装依赖
 
 ```bash
-# 安装前端依赖
 cd frontend && npm install && cd ..
 ```
 
@@ -48,8 +37,9 @@ wails dev
 
 Wails 自动:
 1. 编译 Go 后端,Gin 在 goroutine 中启动(:8888)
-2. 启动 Vite 开发服务器(:5173)
-3. 打开 WebView2 窗口,支持热更新
+2. 创建 SQLite 数据库并写入系统初始数据(首次启动)
+3. 启动 Vite 开发服务器(:5173)
+4. 打开 WebView2 窗口,支持热更新
 
 ## 生产构建
 
@@ -65,16 +55,30 @@ wails build
 - 账号:`admin`
 - 密码:`123456`
 
+首次启动自动创建系统数据(用户、角色、菜单、API、Casbin 规则、字典),后续重启跳过。
+
+## 数据库
+
+- 文件位置:`%APPDATA%/smart-parking/lc_garage.db`
+- 可通过 `config.yaml` 的 `sqlite.path` 自定义路径(空 = 默认)
+- 查询工具:[DB Browser for SQLite](https://sqlitebrowser.org/) 或 `sqlite3` 命令行
+
 ## 项目结构
 
 ```
 lc_garage/
-├── main.go            # Wails 入口
-├── wails.json         # Wails 配置
-├── config.yaml        # 后端配置
-├── server/            # Go 后端
-├── frontend/          # Vue 3 前端
-└── build/bin/         # 构建产物
+├── main.go               # Wails 入口(反向代理 /api → Gin :8888)
+├── wails.json             # Wails 配置
+├── go.mod                 # 根模块 (wails-app)
+├── config.yaml            # 后端配置
+├── server/                # Go 后端(独立模块 "server")
+│   ├── main.go            #   InitBackend() + SeedSystemData()
+│   ├── initialize/        #   初始化(GORM/Redis/Timer/UHF/Seed)
+│   ├── api/               #   API 控制器
+│   ├── service/           #   业务逻辑
+│   └── dao/               #   数据访问
+├── frontend/              # Vue 3 前端
+└── build/bin/             # 构建产物
 ```
 
 ## 常见问题
@@ -82,8 +86,8 @@ lc_garage/
 **双击 exe 打不开?**
 确保 `config.yaml` 与 `smart-parking.exe` 在同一目录。
 
-**接口报 404?**
-检查 MySQL 是否运行,确认 `lc_garage` 数据库已初始化
+**超级管理员菜单不显示?**
+可能是数据库种子数据异常,删除 `%APPDATA%/smart-parking/lc_garage.db` 后重新启动即可重建
 
 **wails dev 卡住?**
 确保 `cd frontend && npm install` 已执行过。

+ 3 - 3
config.yaml

@@ -183,18 +183,18 @@ sqlite:
     prefix: ""
     port: ""
     config: ""
-    db-name: ""
+    db-name: lc_garage
     username: ""
     password: ""
     path: ""
     engine: ""
-    log-mode: ""
+    log-mode: error
     max-idle-conns: 10
     max-open-conns: 100
     singular: false
     log-zap: false
 system:
-    db-type: mysql
+    db-type: sqlite
     oss-type: local
     router-prefix: ""
     addr: 8888

+ 1 - 1
doc/PROJECT.md

@@ -26,7 +26,7 @@ Wails .exe
 | 桌面框架 | Wails v2.13 |
 | 后端 | Go 1.25 + Gin + GORM + Casbin + JWT |
 | 前端 | Vue 3 + Vite 4 + Element Plus + Pinia + ECharts |
-| 数据库 | MySQL 5.7+(外部) |
+| 数据库 | SQLite(`%APPDATA%/smart-parking/lc_garage.db`) |
 
 ---
 

+ 25 - 1
server/initialize/gorm_sqlite.go

@@ -1,6 +1,10 @@
 package initialize
 
 import (
+	"fmt"
+	"os"
+	"path/filepath"
+
 	"github.com/glebarez/sqlite"
 	"gorm.io/gorm"
 	"server/config"
@@ -15,7 +19,27 @@ func GormSqlite() *gorm.DB {
 		return nil
 	}
 
-	if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
+	// Resolve path: empty = %APPDATA%/smart-parking/
+	if s.Path == "" {
+		if appData := os.Getenv("APPDATA"); appData != "" {
+			s.Path = appData
+		} else {
+			// Fallback: exe directory
+			if exePath, err := os.Executable(); err == nil {
+				s.Path = filepath.Dir(exePath)
+			}
+		}
+		s.Path = filepath.Join(s.Path, "smart-parking")
+	}
+
+	// Ensure directory exists
+	if err := os.MkdirAll(s.Path, 0755); err != nil {
+		panic(fmt.Errorf("failed to create SQLite db directory %s: %w", s.Path, err))
+	}
+
+	dsn := filepath.Join(s.Path, s.Dbname+".db")
+
+	if db, err := gorm.Open(sqlite.Open(dsn), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
 		panic(err)
 	} else {
 		sqlDB, _ := db.DB()

+ 457 - 0
server/initialize/seed.go

@@ -0,0 +1,457 @@
+package initialize
+
+import (
+	"time"
+
+	"server/dao"
+	"server/global"
+
+	"github.com/gofrs/uuid/v5"
+)
+
+// SeedSystemData seeds system base data if not already present.
+// Idempotent: checks if admin user exists, skips if so.
+func SeedSystemData() {
+	db := global.GVA_DB
+	if db == nil {
+		return
+	}
+
+	var count int64
+	db.Model(&dao.SysUser{}).Where("username = ?", "admin").Count(&count)
+	if count > 0 {
+		return
+	}
+
+	now := time.Date(2024, 5, 5, 9, 33, 22, 0, time.UTC)
+
+	// --- sys_users ---
+	users := []dao.SysUser{
+		{
+			UUID:        uuid.Must(uuid.FromString("07c9149c-6a9a-4126-8d8a-dd11c7b914a1")),
+			Username:    "admin",
+			Password:    "$2a$10$WTDQCqEQzEi/FU2UKejgGuWDK3PtHT0yb6xwY.yhx4ugjAmS6iTNa",
+			NickName:    "龙弛管理员",
+			SideMode:    "dark",
+			HeaderImg:   "uploads/file/70b1e321d2ec7357cb1b948f5bde35a6_20240606094912.jpg",
+			BaseColor:   "#fff",
+			ActiveColor: "#1890ff",
+			AuthorityId: 888,
+			Phone:       "17611111111",
+			Email:       "333333333@qq.com",
+			Enable:      1,
+		},
+		{
+			UUID:        uuid.Must(uuid.FromString("0a5db918-c3cc-4034-a32a-6c15f37e78cb")),
+			Username:    "xuwenhao",
+			Password:    "$2a$10$EL7QgO5cVNdB6N2rYqAQNOLj8w3mSh9noKpjmPfFO98ip0GyR9x3y",
+			NickName:    "许文浩",
+			SideMode:    "dark",
+			HeaderImg:   "uploads/file/c67b3af7ed83579cc083089fc0bbefd0_20240505115511.jpg",
+			BaseColor:   "#fff",
+			ActiveColor: "#1890ff",
+			AuthorityId: 888,
+			Phone:       "17365743261",
+			Email:       "sn3115614529@163.com",
+			Enable:      1,
+		},
+		{
+			UUID:        uuid.Must(uuid.FromString("9fce89d6-8552-4637-9cd1-9ffd6c8d4dbd")),
+			Username:    "chengqian",
+			Password:    "$2a$10$0qbv0aeavXAxdqUNIAhIC.TK/hfgC8Ky.sP0dK9fAw9lIsR9cIive",
+			NickName:    "程潜",
+			SideMode:    "dark",
+			HeaderImg:   "https://qmplusimg.henrongyi.top/1576554439myAvatar.png",
+			BaseColor:   "#fff",
+			ActiveColor: "#1890ff",
+			AuthorityId: 618,
+			Phone:       "18574363259",
+			Email:       "",
+			Enable:      1,
+		},
+	}
+	for i := range users {
+		users[i].CreatedAt = now
+		users[i].UpdatedAt = now
+	}
+	db.Create(&users)
+
+	// --- sys_authorities ---
+	authorities := []dao.SysAuthority{
+		{AuthorityId: 618, AuthorityName: "普通用户", ParentId: ptr(uint(0)), DefaultRouter: "dashboard", CreatedAt: now, UpdatedAt: now},
+		{AuthorityId: 888, AuthorityName: "管理员", ParentId: ptr(uint(0)), DefaultRouter: "dashboard", CreatedAt: now, UpdatedAt: now},
+		{AuthorityId: 9527, AuthorityName: "开发", ParentId: ptr(uint(0)), DefaultRouter: "dashboard", CreatedAt: now, UpdatedAt: now},
+	}
+	db.Create(&authorities)
+
+	// --- sys_user_authority ---
+	db.Create(&dao.SysUserAuthority{SysUserId: 1, SysAuthorityAuthorityId: 888})
+	db.Create(&dao.SysUserAuthority{SysUserId: 3, SysAuthorityAuthorityId: 888})
+	db.Create(&dao.SysUserAuthority{SysUserId: 4, SysAuthorityAuthorityId: 618})
+
+	// --- sys_base_menus (active only, skipping soft-deleted; ParentId refs = new seq IDs) ---
+	// New ID seq: 1=dashboard, 2=superAdmin, 3=authority, 4=menu, 5=api, 6=user, 7=dictionary, 8=operation,
+	// 9=person, 10=systemTools, 11=autoCode, 12=formCreate, 13=system, 14=autoCodeAdmin,
+	// 15=autoCodeEdit, 16=autoPkg, 17=state, 18=plugin, 19=exportTemplate
+	menus := []dao.SysBaseMenu{
+		{MenuLevel: 0, ParentId: 0, Path: "dashboard", Name: "dashboard", Hidden: false, Component: "view/dashboard/index.vue", Sort: 1, Meta: dao.Meta{Title: "仪表盘", Icon: "odometer"}},
+		{MenuLevel: 0, ParentId: 0, Path: "admin", Name: "superAdmin", Hidden: false, Component: "view/superAdmin/index.vue", Sort: 3, Meta: dao.Meta{Title: "超级管理员", Icon: "user"}},
+		{MenuLevel: 0, ParentId: 2, Path: "authority", Name: "authority", Hidden: false, Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: dao.Meta{Title: "角色管理", Icon: "avatar"}},
+		{MenuLevel: 0, ParentId: 2, Path: "menu", Name: "menu", Hidden: false, Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: dao.Meta{KeepAlive: true, Title: "菜单管理", Icon: "tickets"}},
+		{MenuLevel: 0, ParentId: 2, Path: "api", Name: "api", Hidden: false, Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: dao.Meta{KeepAlive: true, Title: "api管理", Icon: "platform"}},
+		{MenuLevel: 0, ParentId: 2, Path: "user", Name: "user", Hidden: false, Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: dao.Meta{Title: "用户管理", Icon: "coordinate"}},
+		{MenuLevel: 0, ParentId: 2, Path: "dictionary", Name: "dictionary", Hidden: false, Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: dao.Meta{Title: "字典管理", Icon: "notebook"}},
+		{MenuLevel: 0, ParentId: 2, Path: "operation", Name: "operation", Hidden: false, Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: dao.Meta{Title: "操作历史", Icon: "pie-chart"}},
+		{MenuLevel: 0, ParentId: 0, Path: "person", Name: "person", Hidden: true, Component: "view/person/person.vue", Sort: 4, Meta: dao.Meta{Title: "个人信息", Icon: "message"}},
+		{MenuLevel: 0, ParentId: 0, Path: "systemTools", Name: "systemTools", Hidden: false, Component: "view/systemTools/index.vue", Sort: 5, Meta: dao.Meta{Title: "系统工具", Icon: "tools"}},
+		{MenuLevel: 0, ParentId: 10, Path: "autoCode", Name: "autoCode", Hidden: false, Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: dao.Meta{KeepAlive: true, Title: "代码生成器", Icon: "cpu"}},
+		{MenuLevel: 0, ParentId: 10, Path: "formCreate", Name: "formCreate", Hidden: false, Component: "view/systemTools/formCreate/index.vue", Sort: 2, Meta: dao.Meta{KeepAlive: true, Title: "表单生成器", Icon: "magic-stick"}},
+		{MenuLevel: 0, ParentId: 10, Path: "system", Name: "system", Hidden: false, Component: "view/systemTools/system/system.vue", Sort: 3, Meta: dao.Meta{Title: "系统配置", Icon: "operation"}},
+		{MenuLevel: 0, ParentId: 10, Path: "autoCodeAdmin", Name: "autoCodeAdmin", Hidden: false, Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: dao.Meta{Title: "自动化代码管理", Icon: "magic-stick"}},
+		{MenuLevel: 0, ParentId: 10, Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Hidden: true, Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: dao.Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}},
+		{MenuLevel: 0, ParentId: 10, Path: "autoPkg", Name: "autoPkg", Hidden: false, Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: dao.Meta{Title: "自动化package", Icon: "folder"}},
+		{MenuLevel: 0, ParentId: 0, Path: "state", Name: "state", Hidden: false, Component: "view/system/state.vue", Sort: 8, Meta: dao.Meta{Title: "服务器状态", Icon: "cloudy"}},
+		{MenuLevel: 0, ParentId: 0, Path: "plugin", Name: "plugin", Hidden: false, Component: "view/routerHolder.vue", Sort: 6, Meta: dao.Meta{Title: "插件系统", Icon: "cherry"}},
+		{MenuLevel: 0, ParentId: 10, Path: "exportTemplate", Name: "exportTemplate", Hidden: false, Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 10, Meta: dao.Meta{Title: "表格模板", Icon: "reading"}},
+	}
+	for i := range menus {
+		menus[i].CreatedAt = now
+		menus[i].UpdatedAt = now
+	}
+	db.Create(&menus)
+
+	// --- sys_authority_menus (new seq IDs: 1=dashboard,2=superAdmin,3-8=subs,9=person) ---
+	authMenus := []dao.SysAuthorityMenu{
+		{MenuId: "1", AuthorityId: "618"},
+		{MenuId: "1", AuthorityId: "888"},
+		{MenuId: "1", AuthorityId: "9527"},
+		{MenuId: "2", AuthorityId: "888"},
+		{MenuId: "2", AuthorityId: "9527"},
+		{MenuId: "3", AuthorityId: "888"},
+		{MenuId: "3", AuthorityId: "9527"},
+		{MenuId: "4", AuthorityId: "888"},
+		{MenuId: "4", AuthorityId: "9527"},
+		{MenuId: "5", AuthorityId: "888"},
+		{MenuId: "5", AuthorityId: "9527"},
+		{MenuId: "6", AuthorityId: "888"},
+		{MenuId: "6", AuthorityId: "9527"},
+		{MenuId: "7", AuthorityId: "888"},
+		{MenuId: "7", AuthorityId: "9527"},
+		{MenuId: "8", AuthorityId: "888"},
+		{MenuId: "8", AuthorityId: "9527"},
+		{MenuId: "9", AuthorityId: "618"},
+		{MenuId: "9", AuthorityId: "888"},
+		{MenuId: "9", AuthorityId: "9527"},
+	}
+	db.Create(&authMenus)
+
+	// --- sys_apis (active only, skipping soft-deleted ids 44-66,84,90-98) ---
+	apis := []dao.SysApi{
+		{Path: "/jwt/jsonInBlacklist", Description: "jwt加入黑名单(退出,必选)", ApiGroup: "jwt", Method: "POST"},
+		{Path: "/user/deleteUser", Description: "删除用户", ApiGroup: "系统用户", Method: "DELETE"},
+		{Path: "/user/admin_register", Description: "用户注册", ApiGroup: "系统用户", Method: "POST"},
+		{Path: "/user/getUserList", Description: "获取用户列表", ApiGroup: "系统用户", Method: "POST"},
+		{Path: "/user/setUserInfo", Description: "设置用户信息", ApiGroup: "系统用户", Method: "PUT"},
+		{Path: "/user/setSelfInfo", Description: "设置自身信息(必选)", ApiGroup: "系统用户", Method: "PUT"},
+		{Path: "/user/getUserInfo", Description: "获取自身信息(必选)", ApiGroup: "系统用户", Method: "GET"},
+		{Path: "/user/setUserAuthorities", Description: "设置权限组", ApiGroup: "系统用户", Method: "POST"},
+		{Path: "/user/changePassword", Description: "修改密码(建议选择)", ApiGroup: "系统用户", Method: "POST"},
+		{Path: "/user/setUserAuthority", Description: "修改用户角色(必选)", ApiGroup: "系统用户", Method: "POST"},
+		{Path: "/user/resetPassword", Description: "重置用户密码", ApiGroup: "系统用户", Method: "POST"},
+		{Path: "/api/createApi", Description: "创建api", ApiGroup: "api", Method: "POST"},
+		{Path: "/api/deleteApi", Description: "删除Api", ApiGroup: "api", Method: "POST"},
+		{Path: "/api/updateApi", Description: "更新Api", ApiGroup: "api", Method: "POST"},
+		{Path: "/api/getApiList", Description: "获取api列表", ApiGroup: "api", Method: "POST"},
+		{Path: "/api/getAllApis", Description: "获取所有api", ApiGroup: "api", Method: "POST"},
+		{Path: "/api/getApiById", Description: "获取api详细信息", ApiGroup: "api", Method: "POST"},
+		{Path: "/api/deleteApisByIds", Description: "批量删除api", ApiGroup: "api", Method: "DELETE"},
+		{Path: "/authority/copyAuthority", Description: "拷贝角色", ApiGroup: "角色", Method: "POST"},
+		{Path: "/authority/createAuthority", Description: "创建角色", ApiGroup: "角色", Method: "POST"},
+		{Path: "/authority/deleteAuthority", Description: "删除角色", ApiGroup: "角色", Method: "POST"},
+		{Path: "/authority/updateAuthority", Description: "更新角色信息", ApiGroup: "角色", Method: "PUT"},
+		{Path: "/authority/getAuthorityList", Description: "获取角色列表", ApiGroup: "角色", Method: "POST"},
+		{Path: "/authority/setDataAuthority", Description: "设置角色资源权限", ApiGroup: "角色", Method: "POST"},
+		{Path: "/casbin/updateCasbin", Description: "更改角色api权限", ApiGroup: "casbin", Method: "POST"},
+		{Path: "/casbin/getPolicyPathByAuthorityId", Description: "获取权限列表", ApiGroup: "casbin", Method: "POST"},
+		{Path: "/menu/addBaseMenu", Description: "新增菜单", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/getMenu", Description: "获取菜单树(必选)", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/deleteBaseMenu", Description: "删除菜单", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/updateBaseMenu", Description: "更新菜单", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/getBaseMenuById", Description: "根据id获取菜单", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/getMenuList", Description: "分页获取基础menu列表", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/getBaseMenuTree", Description: "获取用户动态路由", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/getMenuAuthority", Description: "获取指定角色menu", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/menu/addMenuAuthority", Description: "增加menu和角色关联关系", ApiGroup: "菜单", Method: "POST"},
+		{Path: "/fileUploadAndDownload/findFile", Description: "寻找目标文件(秒传)", ApiGroup: "分片上传", Method: "GET"},
+		{Path: "/fileUploadAndDownload/breakpointContinue", Description: "断点续传", ApiGroup: "分片上传", Method: "POST"},
+		{Path: "/fileUploadAndDownload/breakpointContinueFinish", Description: "断点续传完成", ApiGroup: "分片上传", Method: "POST"},
+		{Path: "/fileUploadAndDownload/removeChunk", Description: "上传完成移除文件", ApiGroup: "分片上传", Method: "POST"},
+		{Path: "/fileUploadAndDownload/upload", Description: "文件上传示例", ApiGroup: "文件上传与下载", Method: "POST"},
+		{Path: "/fileUploadAndDownload/deleteFile", Description: "删除文件", ApiGroup: "文件上传与下载", Method: "POST"},
+		{Path: "/fileUploadAndDownload/editFileName", Description: "文件名或者备注编辑", ApiGroup: "文件上传与下载", Method: "POST"},
+		{Path: "/fileUploadAndDownload/getFileList", Description: "获取上传文件列表", ApiGroup: "文件上传与下载", Method: "POST"},
+		{Path: "/sysDictionaryDetail/updateSysDictionaryDetail", Description: "更新字典内容", ApiGroup: "系统字典详情", Method: "PUT"},
+		{Path: "/sysDictionaryDetail/createSysDictionaryDetail", Description: "新增字典内容", ApiGroup: "系统字典详情", Method: "POST"},
+		{Path: "/sysDictionaryDetail/deleteSysDictionaryDetail", Description: "删除字典内容", ApiGroup: "系统字典详情", Method: "DELETE"},
+		{Path: "/sysDictionaryDetail/findSysDictionaryDetail", Description: "根据ID获取字典内容", ApiGroup: "系统字典详情", Method: "GET"},
+		{Path: "/sysDictionaryDetail/getSysDictionaryDetailList", Description: "获取字典内容列表", ApiGroup: "系统字典详情", Method: "GET"},
+		{Path: "/sysDictionary/createSysDictionary", Description: "新增字典", ApiGroup: "系统字典", Method: "POST"},
+		{Path: "/sysDictionary/deleteSysDictionary", Description: "删除字典", ApiGroup: "系统字典", Method: "DELETE"},
+		{Path: "/sysDictionary/updateSysDictionary", Description: "更新字典", ApiGroup: "系统字典", Method: "PUT"},
+		{Path: "/sysDictionary/findSysDictionary", Description: "根据ID获取字典", ApiGroup: "系统字典", Method: "GET"},
+		{Path: "/sysDictionary/getSysDictionaryList", Description: "获取字典列表", ApiGroup: "系统字典", Method: "GET"},
+		{Path: "/sysOperationRecord/createSysOperationRecord", Description: "新增操作记录", ApiGroup: "操作记录", Method: "POST"},
+		{Path: "/sysOperationRecord/findSysOperationRecord", Description: "根据ID获取操作记录", ApiGroup: "操作记录", Method: "GET"},
+		{Path: "/sysOperationRecord/getSysOperationRecordList", Description: "获取操作记录列表", ApiGroup: "操作记录", Method: "GET"},
+		{Path: "/sysOperationRecord/deleteSysOperationRecord", Description: "删除操作记录", ApiGroup: "操作记录", Method: "DELETE"},
+		{Path: "/sysOperationRecord/deleteSysOperationRecordByIds", Description: "批量删除操作历史", ApiGroup: "操作记录", Method: "DELETE"},
+		{Path: "/simpleUploader/upload", Description: "插件版分片上传", ApiGroup: "断点续传(插件版)", Method: "POST"},
+		{Path: "/simpleUploader/checkFileMd5", Description: "文件完整度验证", ApiGroup: "断点续传(插件版)", Method: "GET"},
+		{Path: "/email/emailTest", Description: "发送测试邮件", ApiGroup: "email", Method: "POST"},
+		{Path: "/email/emailSend", Description: "发送邮件示例", ApiGroup: "email", Method: "POST"},
+		{Path: "/authorityBtn/setAuthorityBtn", Description: "设置按钮权限", ApiGroup: "按钮权限", Method: "POST"},
+		{Path: "/authorityBtn/getAuthorityBtn", Description: "获取已有按钮权限", ApiGroup: "按钮权限", Method: "POST"},
+		{Path: "/authorityBtn/canRemoveAuthorityBtn", Description: "删除按钮", ApiGroup: "按钮权限", Method: "POST"},
+	}
+	for i := range apis {
+		apis[i].CreatedAt = now
+		apis[i].UpdatedAt = now
+	}
+	db.Create(&apis)
+
+	// --- casbin_rule ---
+	type casbinRule struct {
+		Ptype string `gorm:"column:ptype"`
+		V0    string `gorm:"column:v0"`
+		V1    string `gorm:"column:v1"`
+		V2    string `gorm:"column:v2"`
+		V3    string `gorm:"column:v3"`
+		V4    string `gorm:"column:v4"`
+		V5    string `gorm:"column:v5"`
+	}
+	casbinRules := []casbinRule{
+		// 618 (普通用户)
+		{"p", "618", "/base/login", "POST", "", "", ""},
+		{"p", "618", "/jwt/jsonInBlacklist", "POST", "", "", ""},
+		{"p", "618", "/menu/getMenu", "POST", "", "", ""},
+		{"p", "618", "/user/admin_register", "POST", "", "", ""},
+		{"p", "618", "/user/changePassword", "POST", "", "", ""},
+		{"p", "618", "/user/getUserInfo", "GET", "", "", ""},
+		{"p", "618", "/user/setUserAuthority", "POST", "", "", ""},
+		{"p", "618", "/user/setUserInfo", "PUT", "", "", ""},
+		// 888 (管理员)
+		{"p", "888", "/user/admin_register", "POST", "", "", ""},
+		{"p", "888", "/api/createApi", "POST", "", "", ""},
+		{"p", "888", "/api/getApiList", "POST", "", "", ""},
+		{"p", "888", "/api/getApiById", "POST", "", "", ""},
+		{"p", "888", "/api/deleteApi", "POST", "", "", ""},
+		{"p", "888", "/api/updateApi", "POST", "", "", ""},
+		{"p", "888", "/api/getAllApis", "POST", "", "", ""},
+		{"p", "888", "/api/deleteApisByIds", "DELETE", "", "", ""},
+		{"p", "888", "/authority/copyAuthority", "POST", "", "", ""},
+		{"p", "888", "/authority/updateAuthority", "PUT", "", "", ""},
+		{"p", "888", "/authority/createAuthority", "POST", "", "", ""},
+		{"p", "888", "/authority/deleteAuthority", "POST", "", "", ""},
+		{"p", "888", "/authority/getAuthorityList", "POST", "", "", ""},
+		{"p", "888", "/authority/setDataAuthority", "POST", "", "", ""},
+		{"p", "888", "/menu/getMenu", "POST", "", "", ""},
+		{"p", "888", "/menu/getMenuList", "POST", "", "", ""},
+		{"p", "888", "/menu/addBaseMenu", "POST", "", "", ""},
+		{"p", "888", "/menu/getBaseMenuTree", "POST", "", "", ""},
+		{"p", "888", "/menu/addMenuAuthority", "POST", "", "", ""},
+		{"p", "888", "/menu/getMenuAuthority", "POST", "", "", ""},
+		{"p", "888", "/menu/deleteBaseMenu", "POST", "", "", ""},
+		{"p", "888", "/menu/updateBaseMenu", "POST", "", "", ""},
+		{"p", "888", "/menu/getBaseMenuById", "POST", "", "", ""},
+		{"p", "888", "/user/getUserInfo", "GET", "", "", ""},
+		{"p", "888", "/user/setUserInfo", "PUT", "", "", ""},
+		{"p", "888", "/user/setSelfInfo", "PUT", "", "", ""},
+		{"p", "888", "/user/getUserList", "POST", "", "", ""},
+		{"p", "888", "/user/deleteUser", "DELETE", "", "", ""},
+		{"p", "888", "/user/changePassword", "POST", "", "", ""},
+		{"p", "888", "/user/setUserAuthority", "POST", "", "", ""},
+		{"p", "888", "/user/setUserAuthorities", "POST", "", "", ""},
+		{"p", "888", "/user/resetPassword", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/findFile", "GET", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/breakpointContinueFinish", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/breakpointContinue", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/removeChunk", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/upload", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/deleteFile", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/editFileName", "POST", "", "", ""},
+		{"p", "888", "/fileUploadAndDownload/getFileList", "POST", "", "", ""},
+		{"p", "888", "/casbin/updateCasbin", "POST", "", "", ""},
+		{"p", "888", "/casbin/getPolicyPathByAuthorityId", "POST", "", "", ""},
+		{"p", "888", "/jwt/jsonInBlacklist", "POST", "", "", ""},
+		{"p", "888", "/sysDictionaryDetail/findSysDictionaryDetail", "GET", "", "", ""},
+		{"p", "888", "/sysDictionaryDetail/updateSysDictionaryDetail", "PUT", "", "", ""},
+		{"p", "888", "/sysDictionaryDetail/createSysDictionaryDetail", "POST", "", "", ""},
+		{"p", "888", "/sysDictionaryDetail/getSysDictionaryDetailList", "GET", "", "", ""},
+		{"p", "888", "/sysDictionaryDetail/deleteSysDictionaryDetail", "DELETE", "", "", ""},
+		{"p", "888", "/sysDictionary/findSysDictionary", "GET", "", "", ""},
+		{"p", "888", "/sysDictionary/updateSysDictionary", "PUT", "", "", ""},
+		{"p", "888", "/sysDictionary/createSysDictionary", "POST", "", "", ""},
+		{"p", "888", "/sysDictionary/deleteSysDictionary", "DELETE", "", "", ""},
+		{"p", "888", "/sysDictionary/getSysDictionaryList", "GET", "", "", ""},
+		{"p", "888", "/sysOperationRecord/findSysOperationRecord", "GET", "", "", ""},
+		{"p", "888", "/sysOperationRecord/updateSysOperationRecord", "PUT", "", "", ""},
+		{"p", "888", "/sysOperationRecord/createSysOperationRecord", "POST", "", "", ""},
+		{"p", "888", "/sysOperationRecord/getSysOperationRecordList", "GET", "", "", ""},
+		{"p", "888", "/sysOperationRecord/deleteSysOperationRecord", "DELETE", "", "", ""},
+		{"p", "888", "/sysOperationRecord/deleteSysOperationRecordByIds", "DELETE", "", "", ""},
+		{"p", "888", "/simpleUploader/upload", "POST", "", "", ""},
+		{"p", "888", "/simpleUploader/checkFileMd5", "GET", "", "", ""},
+		{"p", "888", "/email/emailTest", "POST", "", "", ""},
+		{"p", "888", "/authorityBtn/setAuthorityBtn", "POST", "", "", ""},
+		{"p", "888", "/authorityBtn/getAuthorityBtn", "POST", "", "", ""},
+		{"p", "888", "/authorityBtn/canRemoveAuthorityBtn", "POST", "", "", ""},
+		// 9527 (开发)
+		{"p", "9527", "/jwt/jsonInBlacklist", "POST", "", "", ""},
+		{"p", "9527", "/user/deleteUser", "DELETE", "", "", ""},
+		{"p", "9527", "/user/admin_register", "POST", "", "", ""},
+		{"p", "9527", "/user/getUserList", "POST", "", "", ""},
+		{"p", "9527", "/user/setUserInfo", "PUT", "", "", ""},
+		{"p", "9527", "/user/setSelfInfo", "PUT", "", "", ""},
+		{"p", "9527", "/user/getUserInfo", "GET", "", "", ""},
+		{"p", "9527", "/user/setUserAuthorities", "POST", "", "", ""},
+		{"p", "9527", "/user/changePassword", "POST", "", "", ""},
+		{"p", "9527", "/user/setUserAuthority", "POST", "", "", ""},
+		{"p", "9527", "/user/resetPassword", "POST", "", "", ""},
+		{"p", "9527", "/api/createApi", "POST", "", "", ""},
+		{"p", "9527", "/api/deleteApi", "POST", "", "", ""},
+		{"p", "9527", "/api/updateApi", "POST", "", "", ""},
+		{"p", "9527", "/api/getApiList", "POST", "", "", ""},
+		{"p", "9527", "/api/getAllApis", "POST", "", "", ""},
+		{"p", "9527", "/api/getApiById", "POST", "", "", ""},
+		{"p", "9527", "/api/deleteApisByIds", "DELETE", "", "", ""},
+		{"p", "9527", "/authority/copyAuthority", "POST", "", "", ""},
+		{"p", "9527", "/authority/createAuthority", "POST", "", "", ""},
+		{"p", "9527", "/authority/deleteAuthority", "POST", "", "", ""},
+		{"p", "9527", "/authority/updateAuthority", "PUT", "", "", ""},
+		{"p", "9527", "/authority/getAuthorityList", "POST", "", "", ""},
+		{"p", "9527", "/authority/setDataAuthority", "POST", "", "", ""},
+		{"p", "9527", "/casbin/updateCasbin", "POST", "", "", ""},
+		{"p", "9527", "/casbin/getPolicyPathByAuthorityId", "POST", "", "", ""},
+		{"p", "9527", "/menu/addBaseMenu", "POST", "", "", ""},
+		{"p", "9527", "/menu/getMenu", "POST", "", "", ""},
+		{"p", "9527", "/menu/deleteBaseMenu", "POST", "", "", ""},
+		{"p", "9527", "/menu/updateBaseMenu", "POST", "", "", ""},
+		{"p", "9527", "/menu/getBaseMenuById", "POST", "", "", ""},
+		{"p", "9527", "/menu/getMenuList", "POST", "", "", ""},
+		{"p", "9527", "/menu/getBaseMenuTree", "POST", "", "", ""},
+		{"p", "9527", "/menu/getMenuAuthority", "POST", "", "", ""},
+		{"p", "9527", "/menu/addMenuAuthority", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/findFile", "GET", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/breakpointContinue", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/breakpointContinueFinish", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/removeChunk", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/upload", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/deleteFile", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/editFileName", "POST", "", "", ""},
+		{"p", "9527", "/fileUploadAndDownload/getFileList", "POST", "", "", ""},
+		{"p", "9527", "/sysDictionaryDetail/updateSysDictionaryDetail", "PUT", "", "", ""},
+		{"p", "9527", "/sysDictionaryDetail/createSysDictionaryDetail", "POST", "", "", ""},
+		{"p", "9527", "/sysDictionaryDetail/deleteSysDictionaryDetail", "DELETE", "", "", ""},
+		{"p", "9527", "/sysDictionaryDetail/findSysDictionaryDetail", "GET", "", "", ""},
+		{"p", "9527", "/sysDictionaryDetail/getSysDictionaryDetailList", "GET", "", "", ""},
+		{"p", "9527", "/sysDictionary/createSysDictionary", "POST", "", "", ""},
+		{"p", "9527", "/sysDictionary/deleteSysDictionary", "DELETE", "", "", ""},
+		{"p", "9527", "/sysDictionary/updateSysDictionary", "PUT", "", "", ""},
+		{"p", "9527", "/sysDictionary/findSysDictionary", "GET", "", "", ""},
+		{"p", "9527", "/sysDictionary/getSysDictionaryList", "GET", "", "", ""},
+		{"p", "9527", "/sysOperationRecord/createSysOperationRecord", "POST", "", "", ""},
+		{"p", "9527", "/sysOperationRecord/findSysOperationRecord", "GET", "", "", ""},
+		{"p", "9527", "/sysOperationRecord/getSysOperationRecordList", "GET", "", "", ""},
+		{"p", "9527", "/sysOperationRecord/deleteSysOperationRecord", "DELETE", "", "", ""},
+		{"p", "9527", "/sysOperationRecord/deleteSysOperationRecordByIds", "DELETE", "", "", ""},
+		{"p", "9527", "/simpleUploader/upload", "POST", "", "", ""},
+		{"p", "9527", "/simpleUploader/checkFileMd5", "GET", "", "", ""},
+		{"p", "9527", "/email/emailTest", "POST", "", "", ""},
+		{"p", "9527", "/email/emailSend", "POST", "", "", ""},
+		{"p", "9527", "/authorityBtn/setAuthorityBtn", "POST", "", "", ""},
+		{"p", "9527", "/authorityBtn/getAuthorityBtn", "POST", "", "", ""},
+		{"p", "9527", "/authorityBtn/canRemoveAuthorityBtn", "POST", "", "", ""},
+	}
+	// Ensure casbin_rule table exists before seeding
+	db.Exec(`CREATE TABLE IF NOT EXISTS casbin_rule (
+		id INTEGER PRIMARY KEY AUTOINCREMENT,
+		ptype TEXT,
+		v0 TEXT,
+		v1 TEXT,
+		v2 TEXT,
+		v3 TEXT,
+		v4 TEXT,
+		v5 TEXT
+	)`)
+
+	// Use raw SQL to insert casbin rules
+	for _, r := range casbinRules {
+		db.Exec("INSERT INTO casbin_rule (ptype, v0, v1, v2, v3, v4, v5) VALUES (?, ?, ?, ?, ?, ?, ?)",
+			r.Ptype, r.V0, r.V1, r.V2, r.V3, r.V4, r.V5)
+	}
+
+	// --- sys_dictionaries (active only) ---
+	dictionaries := []dao.SysDictionary{
+		{Name: "性别", Type: "sex", Status: ptr(true), Desc: "性别字典", SysDictionaryDetails: []dao.SysDictionaryDetail{
+			{Label: "男", Value: "1", Status: ptr(true), Sort: 1},
+			{Label: "女", Value: "2", Status: ptr(true), Sort: 2},
+		}},
+		{Name: "数据库int类型", Type: "int", Status: ptr(true), Desc: "int类型对应的数据库类型", SysDictionaryDetails: []dao.SysDictionaryDetail{
+			{Label: "smallint", Value: "1", Extend: "mysql", Status: ptr(true), Sort: 1},
+			{Label: "mediumint", Value: "2", Extend: "mysql", Status: ptr(true), Sort: 2},
+			{Label: "int", Value: "3", Extend: "mysql", Status: ptr(true), Sort: 3},
+			{Label: "bigint", Value: "4", Extend: "mysql", Status: ptr(true), Sort: 4},
+			{Label: "int2", Value: "5", Extend: "pgsql", Status: ptr(true), Sort: 5},
+			{Label: "int4", Value: "6", Extend: "pgsql", Status: ptr(true), Sort: 6},
+			{Label: "int6", Value: "7", Extend: "pgsql", Status: ptr(true), Sort: 7},
+			{Label: "int8", Value: "8", Extend: "pgsql", Status: ptr(true), Sort: 8},
+		}},
+		{Name: "数据库时间日期类型", Type: "time.Time", Status: ptr(true), Desc: "数据库时间日期类型", SysDictionaryDetails: []dao.SysDictionaryDetail{
+			{Label: "date", Value: "", Status: ptr(true), Sort: 0},
+			{Label: "time", Value: "1", Extend: "mysql", Status: ptr(true), Sort: 1},
+			{Label: "year", Value: "2", Extend: "mysql", Status: ptr(true), Sort: 2},
+			{Label: "datetime", Value: "3", Extend: "mysql", Status: ptr(true), Sort: 3},
+			{Label: "timestamp", Value: "5", Extend: "mysql", Status: ptr(true), Sort: 5},
+			{Label: "timestamptz", Value: "6", Extend: "pgsql", Status: ptr(true), Sort: 5},
+		}},
+		{Name: "数据库浮点型", Type: "float64", Status: ptr(true), Desc: "数据库浮点型", SysDictionaryDetails: []dao.SysDictionaryDetail{
+			{Label: "float", Value: "", Status: ptr(true), Sort: 0},
+			{Label: "double", Value: "1", Extend: "mysql", Status: ptr(true), Sort: 1},
+			{Label: "decimal", Value: "2", Extend: "mysql", Status: ptr(true), Sort: 2},
+			{Label: "numeric", Value: "3", Extend: "pgsql", Status: ptr(true), Sort: 3},
+			{Label: "smallserial", Value: "4", Extend: "pgsql", Status: ptr(true), Sort: 4},
+		}},
+		{Name: "数据库字符串", Type: "string", Status: ptr(true), Desc: "数据库字符串", SysDictionaryDetails: []dao.SysDictionaryDetail{
+			{Label: "char", Value: "", Status: ptr(true), Sort: 0},
+			{Label: "varchar", Value: "1", Extend: "mysql", Status: ptr(true), Sort: 1},
+			{Label: "tinyblob", Value: "2", Extend: "mysql", Status: ptr(true), Sort: 2},
+			{Label: "tinytext", Value: "3", Extend: "mysql", Status: ptr(true), Sort: 3},
+			{Label: "text", Value: "4", Extend: "mysql", Status: ptr(true), Sort: 4},
+			{Label: "blob", Value: "5", Extend: "mysql", Status: ptr(true), Sort: 5},
+			{Label: "mediumblob", Value: "6", Extend: "mysql", Status: ptr(true), Sort: 6},
+			{Label: "mediumtext", Value: "7", Extend: "mysql", Status: ptr(true), Sort: 7},
+			{Label: "longblob", Value: "8", Extend: "mysql", Status: ptr(true), Sort: 8},
+			{Label: "longtext", Value: "9", Extend: "mysql", Status: ptr(true), Sort: 9},
+		}},
+		{Name: "数据库bool类型", Type: "bool", Status: ptr(true), Desc: "数据库bool类型", SysDictionaryDetails: []dao.SysDictionaryDetail{
+			{Label: "tinyint", Value: "1", Extend: "mysql", Status: ptr(true), Sort: 0},
+			{Label: "bool", Value: "2", Extend: "pgsql", Status: ptr(true), Sort: 0},
+		}},
+	}
+	for i := range dictionaries {
+		dictionaries[i].CreatedAt = now
+		dictionaries[i].UpdatedAt = now
+		for j := range dictionaries[i].SysDictionaryDetails {
+			dictionaries[i].SysDictionaryDetails[j].CreatedAt = now
+			dictionaries[i].SysDictionaryDetails[j].UpdatedAt = now
+		}
+	}
+	db.Create(&dictionaries)
+
+	global.GVA_LOG.Info("system data seeded successfully")
+}
+
+// ptr returns a pointer to the given value.
+func ptr[T any](v T) *T {
+	return &v
+}

+ 1 - 0
server/main.go

@@ -22,6 +22,7 @@ func InitBackend() {
 	initialize.DBList()
 	if global.GVA_DB != nil {
 		initialize.RegisterTables()
+		initialize.SeedSystemData()
 	}
 	initialize.InitUHFDevices()
 	go core.RunWindowsServer()