CHG: 移除管理员管理菜单、新增日志管理菜单(008_menu_rbac_v4.sql)

This commit is contained in:
夏犀麟 2026-08-26 22:23:21 +08:00
parent 2ed31f012e
commit 78756a849f
15 changed files with 633 additions and 0 deletions

View File

@ -0,0 +1,74 @@
// Package admin_admin_admin 定义后台管理接口(管理员账号管理),路由前缀 /admin。
package admin_admin_admin
import "github.com/gogf/gf/v2/frame/g"
// AdminItem 管理员列表中的一行。
type AdminItem struct {
Id uint64 `json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Status int `json:"status"`
RoleIds []uint64 `json:"roleIds"`
RoleNames []string `json:"roleNames"`
CreatedAt string `json:"createdAt"`
}
// AdminListReq 分页查询管理员。
type AdminListReq struct {
g.Meta `path:"/admin/list" method:"post" tags:"Admin/Admin" summary:"管理员列表"`
Page int `json:"page" d:"1" v:"min:1"`
Size int `json:"size" d:"10" v:"min:1|max:100"`
Keyword string `json:"keyword"` // 匹配用户名或昵称
}
// AdminListRes 是 AdminListReq 的响应。
type AdminListRes struct {
List []*AdminItem `json:"list"`
Total int `json:"total"`
}
// AdminCreateReq 创建管理员。
type AdminCreateReq struct {
g.Meta `path:"/admin/create" method:"post" tags:"Admin/Admin" summary:"创建管理员"`
Username string `json:"username" v:"required"`
Password string `json:"password" v:"required|min-length:6#password required|password too short"`
Nickname string `json:"nickname"`
RoleIds []uint64 `json:"roleIds"`
}
// AdminCreateRes 是 AdminCreateReq 的响应。
type AdminCreateRes struct {
Id uint64 `json:"id"`
}
// AdminUpdateReq 更新管理员的资料、状态与角色。
type AdminUpdateReq struct {
g.Meta `path:"/admin/update/{id}" method:"post" tags:"Admin/Admin" summary:"更新管理员"`
Id uint64 `json:"id" in:"path" v:"required"`
Nickname string `json:"nickname"`
Status int `json:"status"`
RoleIds []uint64 `json:"roleIds"`
}
// AdminUpdateRes 是 AdminUpdateReq 的响应。
type AdminUpdateRes struct{}
// AdminResetPwdReq 重置管理员密码。
type AdminResetPwdReq struct {
g.Meta `path:"/admin/resetPwd/{id}" method:"post" tags:"Admin/Admin" summary:"重置管理员密码"`
Id uint64 `json:"id" in:"path" v:"required"`
Password string `json:"password" v:"required|min-length:6#password required|password too short"`
}
// AdminResetPwdRes 是 AdminResetPwdReq 的响应。
type AdminResetPwdRes struct{}
// AdminDeleteReq 删除管理员(软删除)。
type AdminDeleteReq struct {
g.Meta `path:"/admin/delete/{id}" method:"post" tags:"Admin/Admin" summary:"删除管理员"`
Id uint64 `json:"id" in:"path" v:"required"`
}
// AdminDeleteRes 是 AdminDeleteReq 的响应。
type AdminDeleteRes struct{}

View File

@ -0,0 +1,33 @@
// Package admin_system_menu 定义菜单路由接口(当前管理员可见菜单树),路由前缀 /admin。
package admin_system_menu
import "github.com/gogf/gf/v2/frame/g"
// RouteMeta 与 vben 后台动态路由元数据对应。
type RouteMeta struct {
Title string `json:"title"`
Icon string `json:"icon"`
Order int `json:"order"`
Authority []string `json:"authority,omitempty"` // 角色码
HideInMenu bool `json:"hideInMenu,omitempty"`
KeepAlive bool `json:"keepAlive,omitempty"`
}
// RouteItem vben 路由树中的一个节点。
type RouteItem struct {
Name string `json:"name"`
Path string `json:"path"`
Component string `json:"component,omitempty"`
Meta RouteMeta `json:"meta"`
Children []*RouteItem `json:"children,omitempty"`
}
// MenuRoutesReq 获取当前管理员的菜单树(后端鉴权模式,登录后即可访问)。
type MenuRoutesReq struct {
g.Meta `path:"/system/menu/routes" method:"post" tags:"Admin/System/Menu" summary:"当前管理员菜单路由"`
}
// MenuRoutesRes 是 MenuRoutesReq 的响应。
type MenuRoutesRes struct {
Routes []*RouteItem `json:"routes"`
}

View File

@ -0,0 +1,78 @@
// Package admin_system_menu_manage 定义菜单管理接口(菜单 + 按钮权限树),路由前缀 /admin。
package admin_system_menu_manage
import "github.com/gogf/gf/v2/frame/g"
// MenuItem 完整菜单树中的一个节点type 1 菜单 / 2 按钮)。
type MenuItem struct {
Id uint64 `json:"id"`
ParentId uint64 `json:"parentId"`
Name string `json:"name"`
Icon string `json:"icon"`
Type int `json:"type"` // 1 菜单, 2 按钮/接口
Path string `json:"path"`
Component string `json:"component"`
Permission string `json:"permission"`
Sort int `json:"sort"`
Status int `json:"status"`
Hidden bool `json:"hidden"`
Children []*MenuItem `json:"children,omitempty"`
}
// MenuTreeReq 获取完整菜单树(供管理)。
type MenuTreeReq struct {
g.Meta `path:"/system/menu/tree" method:"post" tags:"Admin/System/Menu" summary:"完整菜单树"`
}
// MenuTreeRes 是 MenuTreeReq 的响应。
type MenuTreeRes struct {
Tree []*MenuItem `json:"tree"`
}
// MenuCreateReq 创建菜单或按钮节点。
type MenuCreateReq struct {
g.Meta `path:"/system/menu/create" method:"post" tags:"Admin/System/Menu" summary:"创建菜单"`
ParentId uint64 `json:"parentId"`
Name string `json:"name" v:"required"`
Icon string `json:"icon"`
Type int `json:"type" v:"in:1,2#type must be 1 or 2"`
Path string `json:"path"`
Component string `json:"component"`
Permission string `json:"permission" v:"required"`
Sort int `json:"sort"`
Status int `json:"status"`
Hidden bool `json:"hidden"`
}
// MenuCreateRes 是 MenuCreateReq 的响应。
type MenuCreateRes struct {
Id uint64 `json:"id"`
}
// MenuUpdateReq 更新菜单或按钮节点。
type MenuUpdateReq struct {
g.Meta `path:"/system/menu/update/{id}" method:"post" tags:"Admin/System/Menu" summary:"更新菜单"`
Id uint64 `json:"id" in:"path" v:"required"`
ParentId uint64 `json:"parentId"`
Name string `json:"name" v:"required"`
Icon string `json:"icon"`
Type int `json:"type" v:"in:1,2#type must be 1 or 2"`
Path string `json:"path"`
Component string `json:"component"`
Permission string `json:"permission" v:"required"`
Sort int `json:"sort"`
Status int `json:"status"`
Hidden bool `json:"hidden"`
}
// MenuUpdateRes 是 MenuUpdateReq 的响应。
type MenuUpdateRes struct{}
// MenuDeleteReq 删除菜单节点(软删除)。
type MenuDeleteReq struct {
g.Meta `path:"/system/menu/delete/{id}" method:"post" tags:"Admin/System/Menu" summary:"删除菜单"`
Id uint64 `json:"id" in:"path" v:"required"`
}
// MenuDeleteRes 是 MenuDeleteReq 的响应。
type MenuDeleteRes struct{}

View File

@ -0,0 +1,63 @@
// Package admin_system_role 定义角色管理接口(角色及其菜单绑定),路由前缀 /admin。
package admin_system_role
import "github.com/gogf/gf/v2/frame/g"
// RoleItem 角色列表中的一行。
type RoleItem struct {
Id uint64 `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
Status int `json:"status"`
MenuIds []uint64 `json:"menuIds"` // 已绑定的菜单 id编辑用
CreatedAt string `json:"createdAt"`
}
// RoleListReq 分页查询角色。
type RoleListReq struct {
g.Meta `path:"/system/role/list" method:"post" tags:"Admin/System/Role" summary:"角色列表"`
Page int `json:"page" d:"1" v:"min:1"`
Size int `json:"size" d:"10" v:"min:1|max:100"`
Keyword string `json:"keyword"` // 匹配角色码或名称
}
// RoleListRes 是 RoleListReq 的响应。
type RoleListRes struct {
List []*RoleItem `json:"list"`
Total int `json:"total"`
}
// RoleCreateReq 创建角色并绑定菜单。
type RoleCreateReq struct {
g.Meta `path:"/system/role/create" method:"post" tags:"Admin/System/Role" summary:"创建角色"`
Code string `json:"code" v:"required"`
Name string `json:"name" v:"required"`
Status int `json:"status"`
MenuIds []uint64 `json:"menuIds"`
}
// RoleCreateRes 是 RoleCreateReq 的响应。
type RoleCreateRes struct {
Id uint64 `json:"id"`
}
// RoleUpdateReq 更新角色并重新绑定菜单。
type RoleUpdateReq struct {
g.Meta `path:"/system/role/update/{id}" method:"post" tags:"Admin/System/Role" summary:"更新角色"`
Id uint64 `json:"id" in:"path" v:"required"`
Name string `json:"name" v:"required"`
Status int `json:"status"`
MenuIds []uint64 `json:"menuIds"`
}
// RoleUpdateRes 是 RoleUpdateReq 的响应。
type RoleUpdateRes struct{}
// RoleDeleteReq 删除角色(软删除)。
type RoleDeleteReq struct {
g.Meta `path:"/system/role/delete/{id}" method:"post" tags:"Admin/System/Role" summary:"删除角色"`
Id uint64 `json:"id" in:"path" v:"required"`
}
// RoleDeleteRes 是 RoleDeleteReq 的响应。
type RoleDeleteRes struct{}

15
api/open/tools/doc.go Normal file
View File

@ -0,0 +1,15 @@
// Package open_tools 承载开放工具接口GET/POST /tools/*)。
//
// 命名规则tools/ 下每个子功能一个独立目录目录名即包名Req/Res 契约放在
// 目录内以功能命名的文件中,例如:
//
// api/open/tools/uuid/uuid.go - POST /tools/uuid
// api/open/tools/md5/md5.go - POST /tools/md5
// api/open/tools/random/random.go - POST /tools/random
// api/open/tools/time/time.go - POST /tools/time
// api/open/tools/ip/ip.go - POST /tools/ip
//
// 功能增长时,在其目录内新增文件(如 ocr/ 下加 idcard.go、invoice.go
// 而不是把起始文件写大。新增功能:创建 tools/<name>/<name>.go 并在
// internal/controller/open/<name>.go 中增加对应方法,路由自动绑定。
package open_tools

15
api/open/tools/ip/ip.go Normal file
View File

@ -0,0 +1,15 @@
// Package open_tools_ip 定义 POST /tools/ip 接口契约。
package open_tools_ip
import "github.com/gogf/gf/v2/frame/g"
// IPReq 获取调用方 IP 信息。
type IPReq struct {
g.Meta `path:"/tools/ip" method:"post" tags:"Open/Tools" summary:"客户端 IP 信息"`
}
// IPRes 是 IPReq 的响应。
type IPRes struct {
IP string `json:"ip"`
Internal bool `json:"internal"` // 是否为内网/私网地址
}

15
api/open/tools/md5/md5.go Normal file
View File

@ -0,0 +1,15 @@
// Package open_tools_md5 定义 POST /tools/md5 接口契约。
package open_tools_md5
import "github.com/gogf/gf/v2/frame/g"
// MD5Req 计算文本的 MD5 摘要。
type MD5Req struct {
g.Meta `path:"/tools/md5" method:"post" tags:"Open/Tools" summary:"计算 MD5 摘要"`
Text string `json:"text" v:"required#text required"`
}
// MD5Res 是 MD5Req 的响应。
type MD5Res struct {
MD5 string `json:"md5"`
}

View File

@ -0,0 +1,16 @@
// Package open_tools_random 定义 POST /tools/random 接口契约。
package open_tools_random
import "github.com/gogf/gf/v2/frame/g"
// RandomReq 生成随机字符串。
type RandomReq struct {
g.Meta `path:"/tools/random" method:"post" tags:"Open/Tools" summary:"生成随机字符串"`
Length int `json:"length" d:"16" v:"min:1|max:128#length must be 1-128"`
Type string `json:"type" d:"alnum" v:"in:alnum,digits,letters#unsupported type"`
}
// RandomRes 是 RandomReq 的响应。
type RandomRes struct {
Value string `json:"value"`
}

View File

@ -0,0 +1,16 @@
// Package open_tools_time 定义 POST /tools/time 接口契约。
package open_tools_time
import "github.com/gogf/gf/v2/frame/g"
// TimeReq 获取当前服务器时间。
type TimeReq struct {
g.Meta `path:"/tools/time" method:"post" tags:"Open/Tools" summary:"当前服务器时间"`
}
// TimeRes 是 TimeReq 的响应。
type TimeRes struct {
Timestamp int64 `json:"timestamp"` // Unix 秒
DateTime string `json:"dateTime"` // 2006-01-02 15:04:05
Date string `json:"date"` // 2006-01-02
}

View File

@ -0,0 +1,15 @@
// Package open_tools_uuid 定义 POST /tools/uuid 接口契约。
package open_tools_uuid
import "github.com/gogf/gf/v2/frame/g"
// UUIDReq 生成唯一 ID。
type UUIDReq struct {
g.Meta `path:"/tools/uuid" method:"post" tags:"Open/Tools" summary:"生成唯一 ID"`
Short bool `json:"short"` // true: 8 位短码false: 32 位 ID
}
// UUIDRes 是 UUIDReq 的响应。
type UUIDRes struct {
UUID string `json:"uuid"`
}

33
api/user/auth/auth.go Normal file
View File

@ -0,0 +1,33 @@
// Package user_auth 定义用户端认证接口(登录/刷新),路由前缀 /api。
package user_auth
import "github.com/gogf/gf/v2/frame/g"
// LoginReq 用户登录请求(支持微信/手机验证码/账号密码三种方式)。
type LoginReq struct {
g.Meta `path:"/auth/login" method:"post" tags:"User/Auth" summary:"用户登录"`
LoginType string `json:"loginType" v:"required|in:wechat,mobile,password#login type required|unsupported login type"`
Code string `json:"code"` // 微信授权码
Mobile string `json:"mobile"` // 手机号mobile 登录方式)
VerifyCode string `json:"verifyCode"` // 短信验证码
Account string `json:"account"` // 账号password 登录方式)
Password string `json:"password"` // 密码
Terminal string `json:"terminal" v:"required|in:mini,h5,app#terminal required|invalid terminal"`
}
// LoginRes 用户登录响应。
type LoginRes struct {
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
ExpiresIn int64 `json:"expiresIn"`
UserID uint64 `json:"userId"`
}
// RefreshReq 使用刷新令牌轮换用户令牌对。
type RefreshReq struct {
g.Meta `path:"/auth/refresh" method:"post" tags:"User/Auth" summary:"轮换用户令牌"`
RefreshToken string `json:"refreshToken" v:"required"`
}
// RefreshRes 是 RefreshReq 的响应。
type RefreshRes LoginRes

View File

@ -0,0 +1,158 @@
package admin_admin_admin
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"golang.org/x/crypto/bcrypt"
"service.xpcool.com/internal/consts"
"service.xpcool.com/internal/dao"
"service.xpcool.com/internal/library/response"
"service.xpcool.com/internal/model/do"
"service.xpcool.com/internal/model/dto"
"service.xpcool.com/internal/model/entity"
)
// IAdminManage 管理后台管理员账号。
type IAdminManage interface {
List(context.Context, dto.PageQuery) ([]*dto.AdminItem, int, error)
Create(context.Context, dto.AdminCreateInput) (uint64, error)
Update(context.Context, dto.AdminUpdateInput) error
ResetPassword(context.Context, uint64, string) error
Delete(context.Context, uint64) error
}
type adminManage struct{}
var localAdminManage IAdminManage
func NewAdminManage() IAdminManage { return &adminManage{} }
func AdminManage() IAdminManage {
if localAdminManage == nil {
panic("AdminManage implementation not registered")
}
return localAdminManage
}
func RegisterAdminManage(i IAdminManage) { localAdminManage = i }
// ---------------------- Admin manage ----------------------
func (s *adminManage) List(ctx context.Context, q dto.PageQuery) ([]*dto.AdminItem, int, error) {
total, err := dao.AdminUser.Ctx(ctx).Count()
if err != nil {
return nil, 0, gerror.Wrap(err, "count admins")
}
if total == 0 {
return nil, 0, nil
}
var list []entity.AdminUser
if err = dao.AdminUser.Ctx(ctx).Page(q.Page, q.Size).OrderDesc("id").Scan(&list); err != nil {
return nil, 0, gerror.Wrap(err, "query admins")
}
items := make([]*dto.AdminItem, 0, len(list))
for i := range list {
a := &list[i]
roleIds, roleNames, err := s.roles(ctx, a.Id)
if err != nil {
return nil, 0, err
}
items = append(items, &dto.AdminItem{
Id: a.Id, Username: a.Username, Nickname: a.Nickname, Status: a.Status,
RoleIds: roleIds, RoleNames: roleNames,
CreatedAt: a.CreatedAt.Layout("2006-01-02 15:04:05"),
})
}
return items, total, nil
}
func (s *adminManage) roles(ctx context.Context, adminID uint64) ([]uint64, []string, error) {
var rels []entity.AdminUserRole
if err := dao.AdminUserRole.Ctx(ctx).Where(do.AdminUserRole{AdminUserId: adminID}).Scan(&rels); err != nil {
return nil, nil, gerror.Wrap(err, "query admin roles")
}
roleIds := make([]uint64, 0, len(rels))
for _, r := range rels {
roleIds = append(roleIds, r.RoleId)
}
if len(roleIds) == 0 {
return roleIds, nil, nil
}
var roles []entity.AdminRole
if err := dao.AdminRole.Ctx(ctx).WhereIn("id", roleIds).Scan(&roles); err != nil {
return nil, nil, gerror.Wrap(err, "query roles")
}
names := make([]string, 0, len(roles))
for _, r := range roles {
names = append(names, r.Name)
}
return roleIds, names, nil
}
func (s *adminManage) Create(ctx context.Context, in dto.AdminCreateInput) (uint64, error) {
count, err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Username: in.Username}).Count()
if err != nil {
return 0, gerror.Wrap(err, "check username")
}
if count > 0 {
return 0, response.Error(consts.CodeInvalidParam, "username already exists")
}
hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
if err != nil {
return 0, gerror.Wrap(err, "hash password")
}
id, err := dao.AdminUser.Ctx(ctx).Data(do.AdminUser{
Username: in.Username, PasswordHash: string(hash), Nickname: in.Nickname, Status: 1,
}).InsertAndGetId()
if err != nil {
return 0, gerror.Wrap(err, "insert admin")
}
if err = bindAdminRoles(ctx, uint64(id), in.RoleIds); err != nil {
return 0, err
}
return uint64(id), nil
}
func (s *adminManage) Update(ctx context.Context, in dto.AdminUpdateInput) error {
data := do.AdminUser{Nickname: in.Nickname, Status: in.Status}
if _, err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: in.Id}).Data(data).Update(); err != nil {
return gerror.Wrap(err, "update admin")
}
return bindAdminRoles(ctx, in.Id, in.RoleIds)
}
func (s *adminManage) ResetPassword(ctx context.Context, id uint64, password string) error {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return gerror.Wrap(err, "hash password")
}
if _, err = dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: id}).Data(do.AdminUser{PasswordHash: string(hash)}).Update(); err != nil {
return gerror.Wrap(err, "reset password")
}
return nil
}
func (s *adminManage) Delete(ctx context.Context, id uint64) error {
if _, err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: id}).Delete(); err != nil {
return gerror.Wrap(err, "delete admin")
}
// 关联关系物理删除,避免唯一键残留。
if _, err := dao.AdminUserRole.Ctx(ctx).Unscoped().Where(do.AdminUserRole{AdminUserId: id}).Delete(); err != nil {
return gerror.Wrap(err, "delete admin role bindings")
}
return nil
}
// bindAdminRoles 全量重绑管理员-角色关系。
func bindAdminRoles(ctx context.Context, adminID uint64, roleIds []uint64) error {
if _, err := dao.AdminUserRole.Ctx(ctx).Unscoped().Where(do.AdminUserRole{AdminUserId: adminID}).Delete(); err != nil {
return gerror.Wrap(err, "clear admin roles")
}
for _, rid := range roleIds {
if _, err := dao.AdminUserRole.Ctx(ctx).Data(do.AdminUserRole{AdminUserId: adminID, RoleId: rid}).Insert(); err != nil {
return gerror.Wrap(err, "bind admin role")
}
}
return nil
}

View File

@ -0,0 +1,43 @@
-- 004b_fill_seed.sql
-- 补齐 004_seed.sql 中断后的缺失数据admin_menu 菜单树 + super_admin 全绑定 + admin 用户角色绑定
-- 背景服务器首次导入 004 时因缺 icon 列在 admin_menu 处中断仅写入了 admin_user/admin_role
-- 003 补列后再导入 004 会因 admin_user id=1 已存在而中断本脚本以 INSERT IGNORE 幂等补齐
-- 依赖003_schema_ext.sql 已执行icon/component/hidden 列存在007_menu_permissions_v3.sql 已执行 id=63
-- 1) 菜单树type=1 菜单 + type=2 按钮 004_seed.sql 一致
INSERT IGNORE INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES
-- 系统管理
(1, 0, '系统管理', 'mdi:settings-outline', 1, '/system', '', 'system', 1, 1, 0, NOW(), NOW()),
-- 管理员管理
(2, 1, '管理员管理', 'mdi:account-group-outline', 1, 'admin', 'system/admin/index', 'system:admin', 1, 1, 0, NOW(), NOW()),
(21, 2, '查询', '', 2, '', '', 'system:admin:list', 1, 1, 0, NOW(), NOW()),
(22, 2, '新增', '', 2, '', '', 'system:admin:create', 2, 1, 0, NOW(), NOW()),
(23, 2, '编辑', '', 2, '', '', 'system:admin:update', 3, 1, 0, NOW(), NOW()),
(24, 2, '删除', '', 2, '', '', 'system:admin:delete', 4, 1, 0, NOW(), NOW()),
(25, 2, '重置密码', '', 2, '', '', 'system:admin:resetPwd', 5, 1, 0, NOW(), NOW()),
-- 角色管理
(3, 1, '角色管理', 'mdi:shield-account-outline', 1, 'role', 'system/role/index', 'system:role', 2, 1, 0, NOW(), NOW()),
(31, 3, '查询', '', 2, '', '', 'system:role:list', 1, 1, 0, NOW(), NOW()),
(32, 3, '新增', '', 2, '', '', 'system:role:create', 2, 1, 0, NOW(), NOW()),
(33, 3, '编辑', '', 2, '', '', 'system:role:update', 3, 1, 0, NOW(), NOW()),
(34, 3, '删除', '', 2, '', '', 'system:role:delete', 4, 1, 0, NOW(), NOW()),
(35, 3, '分配菜单', '', 2, '', '', 'system:role:assignMenu', 5, 1, 0, NOW(), NOW()),
-- 菜单管理
(4, 1, '菜单管理', 'mdi:menu-outline', 1, 'menu', 'system/menu/index', 'system:menu', 3, 1, 0, NOW(), NOW()),
(41, 4, '查询', '', 2, '', '', 'system:menu:list', 1, 1, 0, NOW(), NOW()),
(42, 4, '新增', '', 2, '', '', 'system:menu:create', 2, 1, 0, NOW(), NOW()),
(43, 4, '编辑', '', 2, '', '', 'system:menu:update', 3, 1, 0, NOW(), NOW()),
(44, 4, '删除', '', 2, '', '', 'system:menu:delete', 4, 1, 0, NOW(), NOW()),
-- 系统监控
(5, 0, '系统监控', 'mdi:monitor-dashboard', 1, '/monitor', '', 'monitor', 2, 1, 0, NOW(), NOW()),
-- 服务器日志
(6, 5, '服务器日志', 'mdi:file-document-outline', 1, 'log', 'monitor/log/index', 'monitor:log', 1, 1, 0, NOW(), NOW()),
(61, 6, '查看', '', 2, '', '', 'monitor:log:view', 1, 1, 0, NOW(), NOW()),
(62, 6, '实时监控', '', 2, '', '', 'monitor:log:tail', 2, 1, 0, NOW(), NOW());
-- 2) super_admin 绑定全部菜单 007 新增的 63
INSERT IGNORE INTO admin_role_menu (role_id, menu_id, created_at, updated_at)
SELECT 1, id, NOW(), NOW() FROM admin_menu WHERE deleted_at IS NULL;
-- 3) admin 用户绑定 super_admin 角色
INSERT IGNORE INTO admin_user_role (admin_user_id, role_id, created_at, updated_at) VALUES (1, 1, NOW(), NOW());

View File

@ -0,0 +1,36 @@
-- 007_menu_permissions_v3.sql
-- 适配 2026-08-26 统一 API 前缀 /api/service + POST 重构后的权限映射
-- 背景005/006 path 仍为旧的 /admin/v1 路径与最新路由不符
-- 本脚本将 admin_menu type=2 行的 path 全部更新为METHOD /api/service/admin/...格式
-- 并补充系统日志 system:log:list 权限映射菜单 id=63挂服务器日志 id=6
-- 依赖先执行 004_seed.sql表结构已就绪时仅需 004 + 本脚本
-- 幂等性UPDATE 幂等INSERT 63 INSERT IGNORE可重复执行
-- ============ 1) 权限路径映射type=2 按钮行 ============
-- 管理员管理 2
UPDATE admin_menu SET path='POST /api/service/admin/admin/list' WHERE id=21; -- system:admin:list
UPDATE admin_menu SET path='POST /api/service/admin/admin/create' WHERE id=22; -- system:admin:create
UPDATE admin_menu SET path='POST /api/service/admin/admin/update/{id}' WHERE id=23; -- system:admin:update
UPDATE admin_menu SET path='POST /api/service/admin/admin/delete/{id}' WHERE id=24; -- system:admin:delete
UPDATE admin_menu SET path='POST /api/service/admin/admin/resetPwd/{id}' WHERE id=25; -- system:admin:resetPwd
-- 角色管理 3
UPDATE admin_menu SET path='POST /api/service/admin/system/role/list' WHERE id=31; -- system:role:list
UPDATE admin_menu SET path='POST /api/service/admin/system/role/create' WHERE id=32; -- system:role:create
UPDATE admin_menu SET path='POST /api/service/admin/system/role/update/{id}' WHERE id=33; -- system:role:update
UPDATE admin_menu SET path='POST /api/service/admin/system/role/delete/{id}' WHERE id=34; -- system:role:delete
UPDATE admin_menu SET path='' WHERE id=35; -- system:role:assignMenu无独立端点
-- 菜单管理 4查询走 tree 接口
UPDATE admin_menu SET path='POST /api/service/admin/system/menu/tree' WHERE id=41; -- system:menu:list
UPDATE admin_menu SET path='POST /api/service/admin/system/menu/create' WHERE id=42; -- system:menu:create
UPDATE admin_menu SET path='POST /api/service/admin/system/menu/update/{id}' WHERE id=43; -- system:menu:update
UPDATE admin_menu SET path='POST /api/service/admin/system/menu/delete/{id}' WHERE id=44; -- system:menu:delete
-- 服务器日志 6
UPDATE admin_menu SET path='POST /api/service/admin/base/log/files' WHERE id=61; -- monitor:log:view
UPDATE admin_menu SET path='POST /api/service/admin/base/log/tail' WHERE id=62; -- monitor:log:tail
-- ============ 2) 系统日志权限id=63 6system:log:list ============
INSERT IGNORE INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at)
VALUES (63, 6, '操作日志', '', 2, 'POST /api/service/admin/system/log', '', 'system:log:list', 3, 1, 0, NOW(), NOW());
-- super_admin 绑定新权限
INSERT IGNORE INTO admin_role_menu (role_id, menu_id, created_at, updated_at) VALUES (1, 63, NOW(), NOW());

View File

@ -0,0 +1,23 @@
-- 008_menu_rbac_v4.sql
-- 2026-08-26 RBAC 界面调整
-- 1) 移除"管理员管理"菜单id=2 及按钮行 21-25软删 + 清理角色绑定
-- 后端 /admin/* 接口与权限码 system:admin:* 保留仅前端不再出现该菜单
-- 2) 系统管理下新增"日志管理"菜单id=7type=1组件 system/log/index
-- 原按钮行 63system:log:listPOST /api/service/admin/system/log改挂到 id=7
-- 依赖先执行 004_seed.sql + 007_menu_permissions_v3.sql
-- 幂等性UPDATE/INSERT IGNORE 可重复执行
-- ============ 1) 移除管理员管理菜单 ============
UPDATE admin_menu SET deleted_at=NOW() WHERE id IN (2,21,22,23,24,25) AND deleted_at IS NULL;
DELETE FROM admin_role_menu WHERE menu_id IN (2,21,22,23,24,25);
-- ============ 2) 新增日志管理菜单 ============
INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at)
VALUES (7, 1, '日志管理', 'mdi:clipboard-text-outline', 1, 'log', 'system/log/index', 'system:log', 4, 1, 0, NOW(), NOW())
ON DUPLICATE KEY UPDATE deleted_at=NULL, parent_id=1, name='日志管理', icon='mdi:clipboard-text-outline', path='log', component='system/log/index', permission='system:log', sort=4, status=1, hidden=0;
-- 原按钮行 63 挂到日志管理下
UPDATE admin_menu SET parent_id=7, name='操作日志', sort=1, deleted_at=NULL WHERE id=63;
-- 超管绑定新菜单与按钮
INSERT IGNORE INTO admin_role_menu (role_id, menu_id, created_at, updated_at) VALUES (1, 7, NOW(), NOW()), (1, 63, NOW(), NOW());