526 lines
19 KiB
Go
526 lines
19 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"net/http"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type adminRoleView struct {
|
|
ID int64 `json:"id"`
|
|
Code string `json:"code"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
BuiltIn bool `json:"builtIn"`
|
|
Status int `json:"status"`
|
|
Permissions []string `json:"permissions"`
|
|
}
|
|
|
|
var adminAccountPattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_.-]{2,49}$`)
|
|
var adminRoleCodePattern = regexp.MustCompile(`^[a-z][a-z0-9_]{2,49}$`)
|
|
|
|
func (a *App) adminAccess(ctx context.Context, adminID int64) ([]string, []string, error) {
|
|
roleRows, err := a.db.QueryContext(ctx, `SELECT DISTINCT r.role_code FROM admin_user_roles ur JOIN admin_roles r ON r.id=ur.role_id WHERE ur.admin_user_id=? AND r.status=1 ORDER BY r.role_code`, adminID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
roles := []string{}
|
|
for roleRows.Next() {
|
|
var code string
|
|
if err = roleRows.Scan(&code); err != nil {
|
|
_ = roleRows.Close()
|
|
return nil, nil, err
|
|
}
|
|
roles = append(roles, code)
|
|
}
|
|
if err = roleRows.Close(); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
permissionRows, err := a.db.QueryContext(ctx, `SELECT DISTINCT rp.permission_code
|
|
FROM admin_user_roles ur
|
|
JOIN admin_roles r ON r.id=ur.role_id AND r.status=1
|
|
JOIN admin_role_permissions rp ON rp.role_id=r.id
|
|
WHERE ur.admin_user_id=? ORDER BY rp.permission_code`, adminID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
permissions := []string{}
|
|
for permissionRows.Next() {
|
|
var code string
|
|
if err = permissionRows.Scan(&code); err != nil {
|
|
_ = permissionRows.Close()
|
|
return nil, nil, err
|
|
}
|
|
permissions = append(permissions, code)
|
|
}
|
|
err = permissionRows.Close()
|
|
return roles, permissions, err
|
|
}
|
|
|
|
func (a *App) hasAdminPermission(ctx context.Context, adminID int64, permission string) bool {
|
|
var allowed int
|
|
err := a.db.QueryRowContext(ctx, `SELECT EXISTS(
|
|
SELECT 1 FROM admin_user_roles ur
|
|
JOIN admin_roles r ON r.id=ur.role_id AND r.status=1
|
|
JOIN admin_role_permissions rp ON rp.role_id=r.id
|
|
WHERE ur.admin_user_id=? AND rp.permission_code=?)`, adminID, permission).Scan(&allowed)
|
|
return err == nil && allowed == 1
|
|
}
|
|
|
|
func (a *App) requireAdminPermission(permission string, next http.HandlerFunc) http.HandlerFunc {
|
|
return a.requireAuth("admin", func(w http.ResponseWriter, r *http.Request) {
|
|
if !a.hasAdminPermission(r.Context(), current(r).ID, permission) {
|
|
fail(w, http.StatusForbidden, 10006, "没有执行该操作的权限")
|
|
return
|
|
}
|
|
next(w, r)
|
|
})
|
|
}
|
|
|
|
func (a *App) adminCodes(w http.ResponseWriter, r *http.Request) {
|
|
_, permissions, err := a.adminAccess(r.Context(), current(r).ID)
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "读取权限失败")
|
|
return
|
|
}
|
|
reply(w, permissions)
|
|
}
|
|
|
|
func (a *App) adminRoleIDs(ctx context.Context, adminID int64) []int64 {
|
|
rows, err := a.db.QueryContext(ctx, `SELECT role_id FROM admin_user_roles WHERE admin_user_id=? ORDER BY role_id`, adminID)
|
|
if err != nil {
|
|
return []int64{}
|
|
}
|
|
defer rows.Close()
|
|
result := []int64{}
|
|
for rows.Next() {
|
|
var id int64
|
|
if rows.Scan(&id) == nil {
|
|
result = append(result, id)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (a *App) adminAccounts(w http.ResponseWriter, r *http.Request) {
|
|
page, size, offset := pagination(r)
|
|
keyword := strings.TrimSpace(r.URL.Query().Get("keyword"))
|
|
where := ""
|
|
args := []any{}
|
|
if keyword != "" {
|
|
where = ` WHERE username LIKE ? OR real_name LIKE ?`
|
|
like := "%" + keyword + "%"
|
|
args = append(args, like, like)
|
|
}
|
|
var total int64
|
|
if err := a.db.QueryRowContext(r.Context(), `SELECT COUNT(*) FROM admin_users`+where, args...).Scan(&total); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "查询管理员失败")
|
|
return
|
|
}
|
|
queryArgs := append(append([]any{}, args...), size, offset)
|
|
rows, err := a.db.QueryContext(r.Context(), `SELECT id,username,real_name,avatar_url,status,last_login_at,password_changed_at,created_at FROM admin_users`+where+` ORDER BY id LIMIT ? OFFSET ?`, queryArgs...)
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "查询管理员失败")
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
items := []map[string]any{}
|
|
for rows.Next() {
|
|
var id int64
|
|
var username, realName, avatar string
|
|
var status int
|
|
var lastLogin, passwordChanged sql.NullTime
|
|
var createdAt time.Time
|
|
if err = rows.Scan(&id, &username, &realName, &avatar, &status, &lastLogin, &passwordChanged, &createdAt); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "读取管理员失败")
|
|
return
|
|
}
|
|
roles, _, _ := a.adminAccess(r.Context(), id)
|
|
items = append(items, map[string]any{
|
|
"id": id, "username": username, "realName": realName, "avatar": avatar,
|
|
"status": status, "roles": roles, "roleIds": a.adminRoleIDs(r.Context(), id),
|
|
"lastLoginAt": nullableTime(lastLogin), "passwordChangedAt": nullableTime(passwordChanged), "createdAt": createdAt,
|
|
})
|
|
}
|
|
reply(w, pageResult{Items: items, Total: total, Page: page, Size: size})
|
|
}
|
|
|
|
func validateAdminRoleIDs(ctx context.Context, tx *sql.Tx, roleIDs []int64) error {
|
|
if len(roleIDs) == 0 {
|
|
return sql.ErrNoRows
|
|
}
|
|
seen := map[int64]bool{}
|
|
for _, roleID := range roleIDs {
|
|
if roleID <= 0 || seen[roleID] {
|
|
return sql.ErrNoRows
|
|
}
|
|
seen[roleID] = true
|
|
var status int
|
|
if err := tx.QueryRowContext(ctx, `SELECT status FROM admin_roles WHERE id=?`, roleID).Scan(&status); err != nil || status != 1 {
|
|
return sql.ErrNoRows
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func replaceAdminRoles(ctx context.Context, tx *sql.Tx, adminID int64, roleIDs []int64) error {
|
|
if err := validateAdminRoleIDs(ctx, tx, roleIDs); err != nil {
|
|
return err
|
|
}
|
|
if _, err := tx.ExecContext(ctx, `DELETE FROM admin_user_roles WHERE admin_user_id=?`, adminID); err != nil {
|
|
return err
|
|
}
|
|
for _, roleID := range roleIDs {
|
|
if _, err := tx.ExecContext(ctx, `INSERT INTO admin_user_roles(admin_user_id,role_id) VALUES(?,?)`, adminID, roleID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *App) adminCreateAccount(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Username string `json:"username"`
|
|
RealName string `json:"realName"`
|
|
Password string `json:"password"`
|
|
Avatar string `json:"avatar"`
|
|
RoleIDs []int64 `json:"roleIds"`
|
|
}
|
|
if decode(r, &req) != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "管理员资料格式错误")
|
|
return
|
|
}
|
|
req.Username = strings.TrimSpace(req.Username)
|
|
req.RealName = strings.TrimSpace(req.RealName)
|
|
if !adminAccountPattern.MatchString(req.Username) || req.RealName == "" || len([]rune(req.RealName)) > 50 {
|
|
fail(w, http.StatusBadRequest, 20001, "账号或姓名格式无效")
|
|
return
|
|
}
|
|
if req.Password == "" {
|
|
fail(w, http.StatusBadRequest, 20001, "请输入密码")
|
|
return
|
|
}
|
|
hash, err := hashPassword(req.Password)
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "密码加密失败")
|
|
return
|
|
}
|
|
tx, err := a.db.BeginTx(r.Context(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "创建管理员失败")
|
|
return
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
if err = validateAdminRoleIDs(r.Context(), tx, req.RoleIDs); err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "请至少选择一个有效角色")
|
|
return
|
|
}
|
|
result, err := tx.ExecContext(r.Context(), `INSERT INTO admin_users(username,password_hash,real_name,avatar_url,status,password_changed_at) VALUES(?,?,?,?,1,NOW(3))`, req.Username, hash, req.RealName, strings.TrimSpace(req.Avatar))
|
|
if err != nil {
|
|
fail(w, http.StatusConflict, 20001, "管理员账号已存在")
|
|
return
|
|
}
|
|
adminID, _ := result.LastInsertId()
|
|
if err = replaceAdminRoles(r.Context(), tx, adminID, req.RoleIDs); err != nil || tx.Commit() != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "保存管理员角色失败")
|
|
return
|
|
}
|
|
a.audit(r, "create", "admin_user", adminID, map[string]any{"username": req.Username, "roleIds": req.RoleIDs})
|
|
reply(w, map[string]any{"id": adminID, "success": true})
|
|
}
|
|
|
|
func containsInt64(values []int64, expected int64) bool {
|
|
for _, value := range values {
|
|
if value == expected {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (a *App) ensureSuperAdminRemains(ctx context.Context, tx *sql.Tx, targetID int64, newStatus int, roleIDs []int64) error {
|
|
var superRoleID int64
|
|
if err := tx.QueryRowContext(ctx, `SELECT id FROM admin_roles WHERE role_code='super_admin'`).Scan(&superRoleID); err != nil {
|
|
return err
|
|
}
|
|
var targetWasSuper int
|
|
if err := tx.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM admin_user_roles WHERE admin_user_id=? AND role_id=?)`, targetID, superRoleID).Scan(&targetWasSuper); err != nil {
|
|
return err
|
|
}
|
|
if targetWasSuper == 0 || (newStatus == 1 && containsInt64(roleIDs, superRoleID)) {
|
|
return nil
|
|
}
|
|
var remaining int
|
|
if err := tx.QueryRowContext(ctx, `SELECT COUNT(DISTINCT a.id) FROM admin_users a JOIN admin_user_roles ur ON ur.admin_user_id=a.id WHERE a.status=1 AND a.id<>? AND ur.role_id=?`, targetID, superRoleID).Scan(&remaining); err != nil {
|
|
return err
|
|
}
|
|
if remaining == 0 {
|
|
return sql.ErrNoRows
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *App) adminUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
|
adminID, err := pathID(r)
|
|
if err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "管理员 ID 无效")
|
|
return
|
|
}
|
|
var req struct {
|
|
RealName string `json:"realName"`
|
|
Avatar string `json:"avatar"`
|
|
Password string `json:"password"`
|
|
Status int `json:"status"`
|
|
RoleIDs []int64 `json:"roleIds"`
|
|
}
|
|
if decode(r, &req) != nil || strings.TrimSpace(req.RealName) == "" || (req.Status != 0 && req.Status != 1) {
|
|
fail(w, http.StatusBadRequest, 20001, "管理员资料格式错误")
|
|
return
|
|
}
|
|
if current(r).ID == adminID && req.Status != 1 {
|
|
fail(w, http.StatusBadRequest, 20001, "不能停用当前登录账号")
|
|
return
|
|
}
|
|
tx, err := a.db.BeginTx(r.Context(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "更新管理员失败")
|
|
return
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
var exists int64
|
|
if err = tx.QueryRowContext(r.Context(), `SELECT id FROM admin_users WHERE id=? FOR UPDATE`, adminID).Scan(&exists); err != nil {
|
|
fail(w, http.StatusNotFound, 30001, "管理员不存在")
|
|
return
|
|
}
|
|
if err = validateAdminRoleIDs(r.Context(), tx, req.RoleIDs); err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "请至少选择一个有效角色")
|
|
return
|
|
}
|
|
if err = a.ensureSuperAdminRemains(r.Context(), tx, adminID, req.Status, req.RoleIDs); err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "必须保留至少一个启用的超级管理员")
|
|
return
|
|
}
|
|
if _, err = tx.ExecContext(r.Context(), `UPDATE admin_users SET real_name=?,avatar_url=?,status=? WHERE id=?`, strings.TrimSpace(req.RealName), strings.TrimSpace(req.Avatar), req.Status, adminID); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "更新管理员失败")
|
|
return
|
|
}
|
|
if err = replaceAdminRoles(r.Context(), tx, adminID, req.RoleIDs); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "更新管理员角色失败")
|
|
return
|
|
}
|
|
passwordChanged := false
|
|
if req.Password != "" {
|
|
hash, hashErr := hashPassword(req.Password)
|
|
if hashErr != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "密码加密失败")
|
|
return
|
|
}
|
|
if _, err = tx.ExecContext(r.Context(), `UPDATE admin_users SET password_hash=?,token_version=token_version+1,password_changed_at=NOW(3) WHERE id=?`, hash, adminID); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "更新管理员密码失败")
|
|
return
|
|
}
|
|
passwordChanged = true
|
|
}
|
|
if req.Status != 1 || passwordChanged {
|
|
if _, err = tx.ExecContext(r.Context(), `UPDATE admin_sessions SET revoked_at=NOW(3) WHERE admin_user_id=? AND revoked_at IS NULL`, adminID); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "撤销管理员会话失败")
|
|
return
|
|
}
|
|
}
|
|
if err = tx.Commit(); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "更新管理员失败")
|
|
return
|
|
}
|
|
a.audit(r, "update", "admin_user", adminID, map[string]any{"roleIds": req.RoleIDs, "status": req.Status, "passwordChanged": passwordChanged})
|
|
reply(w, map[string]bool{"success": true})
|
|
}
|
|
|
|
func (a *App) rolePermissions(ctx context.Context, roleID int64) []string {
|
|
rows, err := a.db.QueryContext(ctx, `SELECT permission_code FROM admin_role_permissions WHERE role_id=? ORDER BY permission_code`, roleID)
|
|
if err != nil {
|
|
return []string{}
|
|
}
|
|
defer rows.Close()
|
|
permissions := []string{}
|
|
for rows.Next() {
|
|
var code string
|
|
if rows.Scan(&code) == nil {
|
|
permissions = append(permissions, code)
|
|
}
|
|
}
|
|
return permissions
|
|
}
|
|
|
|
func (a *App) adminRoles(w http.ResponseWriter, r *http.Request) {
|
|
rows, err := a.db.QueryContext(r.Context(), `SELECT id,role_code,role_name,description,built_in,status FROM admin_roles ORDER BY built_in DESC,id`)
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "查询角色失败")
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
items := []adminRoleView{}
|
|
for rows.Next() {
|
|
var item adminRoleView
|
|
var builtIn int
|
|
if err = rows.Scan(&item.ID, &item.Code, &item.Name, &item.Description, &builtIn, &item.Status); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "读取角色失败")
|
|
return
|
|
}
|
|
item.BuiltIn = builtIn == 1
|
|
item.Permissions = a.rolePermissions(r.Context(), item.ID)
|
|
items = append(items, item)
|
|
}
|
|
reply(w, map[string]any{"items": items})
|
|
}
|
|
|
|
func (a *App) adminPermissions(w http.ResponseWriter, r *http.Request) {
|
|
rows, err := a.db.QueryContext(r.Context(), `SELECT permission_code,permission_name,permission_group FROM admin_permissions ORDER BY permission_group,sort_order,permission_code`)
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "查询权限失败")
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
items := []map[string]string{}
|
|
for rows.Next() {
|
|
var code, name, group string
|
|
if rows.Scan(&code, &name, &group) == nil {
|
|
items = append(items, map[string]string{"code": code, "name": name, "group": group})
|
|
}
|
|
}
|
|
reply(w, map[string]any{"items": items})
|
|
}
|
|
|
|
func validatePermissionCodes(ctx context.Context, tx *sql.Tx, permissions []string) error {
|
|
if len(permissions) == 0 {
|
|
return sql.ErrNoRows
|
|
}
|
|
seen := map[string]bool{}
|
|
for _, permission := range permissions {
|
|
permission = strings.TrimSpace(permission)
|
|
if permission == "" || seen[permission] {
|
|
return sql.ErrNoRows
|
|
}
|
|
seen[permission] = true
|
|
var exists int
|
|
if err := tx.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM admin_permissions WHERE permission_code=?)`, permission).Scan(&exists); err != nil || exists != 1 {
|
|
return sql.ErrNoRows
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func replaceRolePermissions(ctx context.Context, tx *sql.Tx, roleID int64, permissions []string) error {
|
|
if err := validatePermissionCodes(ctx, tx, permissions); err != nil {
|
|
return err
|
|
}
|
|
if _, err := tx.ExecContext(ctx, `DELETE FROM admin_role_permissions WHERE role_id=?`, roleID); err != nil {
|
|
return err
|
|
}
|
|
sort.Strings(permissions)
|
|
for _, permission := range permissions {
|
|
if _, err := tx.ExecContext(ctx, `INSERT INTO admin_role_permissions(role_id,permission_code) VALUES(?,?)`, roleID, permission); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *App) adminCreateRole(w http.ResponseWriter, r *http.Request) {
|
|
a.adminSaveRole(w, r, 0)
|
|
}
|
|
|
|
func (a *App) adminUpdateRole(w http.ResponseWriter, r *http.Request) {
|
|
roleID, err := pathID(r)
|
|
if err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "角色 ID 无效")
|
|
return
|
|
}
|
|
a.adminSaveRole(w, r, roleID)
|
|
}
|
|
|
|
func (a *App) adminSaveRole(w http.ResponseWriter, r *http.Request, roleID int64) {
|
|
var req struct {
|
|
Code string `json:"code"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Permissions []string `json:"permissions"`
|
|
Status int `json:"status"`
|
|
}
|
|
if decode(r, &req) != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "角色资料格式错误")
|
|
return
|
|
}
|
|
req.Code = strings.TrimSpace(req.Code)
|
|
req.Name = strings.TrimSpace(req.Name)
|
|
if !adminRoleCodePattern.MatchString(req.Code) || req.Name == "" || len([]rune(req.Name)) > 80 || (req.Status != 0 && req.Status != 1) {
|
|
fail(w, http.StatusBadRequest, 20001, "角色编码、名称或状态无效")
|
|
return
|
|
}
|
|
tx, err := a.db.BeginTx(r.Context(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
|
if err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "保存角色失败")
|
|
return
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
if err = validatePermissionCodes(r.Context(), tx, req.Permissions); err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "请至少选择一个有效权限")
|
|
return
|
|
}
|
|
if roleID == 0 {
|
|
result, insertErr := tx.ExecContext(r.Context(), `INSERT INTO admin_roles(role_code,role_name,description,status) VALUES(?,?,?,?)`, req.Code, req.Name, strings.TrimSpace(req.Description), req.Status)
|
|
if insertErr != nil {
|
|
fail(w, http.StatusConflict, 20001, "角色编码已存在")
|
|
return
|
|
}
|
|
roleID, _ = result.LastInsertId()
|
|
} else {
|
|
var builtIn int
|
|
if err = tx.QueryRowContext(r.Context(), `SELECT built_in FROM admin_roles WHERE id=? FOR UPDATE`, roleID).Scan(&builtIn); err != nil {
|
|
fail(w, http.StatusNotFound, 30001, "角色不存在")
|
|
return
|
|
}
|
|
if builtIn == 1 {
|
|
fail(w, http.StatusBadRequest, 20001, "内置超级管理员角色不可修改")
|
|
return
|
|
}
|
|
if _, err = tx.ExecContext(r.Context(), `UPDATE admin_roles SET role_code=?,role_name=?,description=?,status=? WHERE id=?`, req.Code, req.Name, strings.TrimSpace(req.Description), req.Status, roleID); err != nil {
|
|
fail(w, http.StatusConflict, 20001, "角色编码已存在")
|
|
return
|
|
}
|
|
}
|
|
if err = replaceRolePermissions(r.Context(), tx, roleID, req.Permissions); err != nil || tx.Commit() != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "保存角色权限失败")
|
|
return
|
|
}
|
|
a.audit(r, "save", "admin_role", roleID, map[string]any{"code": req.Code, "permissions": req.Permissions, "status": req.Status})
|
|
reply(w, map[string]any{"id": roleID, "success": true})
|
|
}
|
|
|
|
func (a *App) adminDeleteRole(w http.ResponseWriter, r *http.Request) {
|
|
roleID, err := pathID(r)
|
|
if err != nil {
|
|
fail(w, http.StatusBadRequest, 20001, "角色 ID 无效")
|
|
return
|
|
}
|
|
var builtIn, assigned int
|
|
if err = a.db.QueryRowContext(r.Context(), `SELECT built_in,(SELECT COUNT(*) FROM admin_user_roles WHERE role_id=admin_roles.id) FROM admin_roles WHERE id=?`, roleID).Scan(&builtIn, &assigned); err != nil {
|
|
fail(w, http.StatusNotFound, 30001, "角色不存在")
|
|
return
|
|
}
|
|
if builtIn == 1 || assigned > 0 {
|
|
fail(w, http.StatusBadRequest, 20001, "内置角色或仍被管理员使用的角色不能删除")
|
|
return
|
|
}
|
|
if _, err = a.db.ExecContext(r.Context(), `DELETE FROM admin_roles WHERE id=?`, roleID); err != nil {
|
|
fail(w, http.StatusInternalServerError, 50001, "删除角色失败")
|
|
return
|
|
}
|
|
a.audit(r, "delete", "admin_role", roleID, nil)
|
|
reply(w, map[string]bool{"success": true})
|
|
}
|