refactor: reorganize modules and add Linux build tooling

This commit is contained in:
2026-08-09 12:41:08 +08:00
parent 86024fa81d
commit 5cc5fd92ed
1461 changed files with 4054 additions and 4724 deletions

View File

@@ -0,0 +1,89 @@
package application
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Create(c *gin.Context) {
var (
request = types.ApplicationRequest{}
data = models.MgtApplication{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, err)
return
}
// 判断应用是否已经存在(包括软删除的记录)
db := models.DBService.Model(&models.MgtApplication{})
if err = db.Where("title = ? or workspace = ?", request.Title, request.Workspace).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
// 未找到记录(包括软删除的),检查是否有软删除的记录
var deletedData models.MgtApplication
// 检查 workspace 是否被软删除workspace 有唯一索引)
if request.Workspace != "" {
if err = models.DBService.Unscoped().Where("workspace = ?", request.Workspace).First(&deletedData).Error; err == nil {
// 存在软删除的记录,先物理删除它,以便可以重新创建
if err = models.DBService.Unscoped().Delete(&deletedData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("已物理删除软删除的应用记录: ID=%d, workspace=%s", deletedData.ID, deletedData.Workspace)
}
}
// 检查 title 是否被软删除(如果 title 和 workspace 不同)
if request.Title != "" && request.Title != request.Workspace {
if err = models.DBService.Unscoped().Where("title = ?", request.Title).First(&deletedData).Error; err == nil {
// 存在软删除的记录,先物理删除它
if err = models.DBService.Unscoped().Delete(&deletedData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("已物理删除软删除的应用记录: ID=%d, title=%s", deletedData.ID, deletedData.Title)
}
}
} else {
// 找到未删除的记录,说明应用已存在
printer.Info("应用已存在: title=%s, workspace=%s", request.Title, request.Workspace)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
// 新增应用
var dataModel = models.MgtApplication{
Std_IICUDS: coreTypes.Std_IICUDS{Identity: utils.UUID(), Status: 1},
Title: request.Title,
Workspace: request.Workspace,
Description: request.Description,
}
if err = models.DBService.Model(&models.MgtApplication{}).Create(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("应用创建成功: ID=%d, workspace=%s", dataModel.ID, dataModel.Workspace)
infra.Response.Success(c, types.IdResp{Id: dataModel.ID, Identity: dataModel.Identity})
}

View File

@@ -0,0 +1,89 @@
package application
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Del(c *gin.Context) {
var (
request = types.ApplicationRequest{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("删除应用参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appId := request.ID
var dptIds []uint
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&models.MgtDepartment{}).Where("app_id = ?", appId).Pluck("id", &dptIds).Error; err != nil {
printer.Error("查询应用部门异常: %v", err)
return err
}
if len(dptIds) > 0 {
if err := tx.Where("dpt_id in ?", dptIds).Delete(&models.MgtLinkUserDpt{}).Error; err != nil {
return err
}
if err := tx.Where("dpt_id in ?", dptIds).Delete(&models.MgtLinkDptRole{}).Error; err != nil {
return err
}
if err := tx.Where("dpt_id in ?", dptIds).Delete(&models.MgtLinkDptPmn{}).Error; err != nil {
return err
}
if err := tx.Where("id in ?", dptIds).Delete(&models.MgtDepartment{}).Error; err != nil {
return err
}
}
if err := tx.Where("app_id = ?", appId).Delete(&models.MgtLinkUserApp{}).Error; err != nil {
return err
}
if err := tx.Where("app_id = ?", appId).Delete(&models.MgtLinkRoleApp{}).Error; err != nil {
return err
}
if err := tx.Where("app_id = ?", appId).Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
return err
}
if err := tx.Where("app_id = ?", appId).Delete(&models.MgtLinkRolePmn{}).Error; err != nil {
return err
}
if err := tx.Where("app_id = ?", appId).Delete(&models.MgtLinkDptPmn{}).Error; err != nil {
return err
}
if err := tx.Where("app_id = ?", appId).Delete(&models.MgtPermission{}).Error; err != nil {
return err
}
if err := tx.Delete(&models.MgtApplication{}, appId).Error; err != nil {
printer.Error("删除应用异常: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("应用删除成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID})
}

View File

@@ -0,0 +1,51 @@
package application
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Detail(c *gin.Context) {
var (
request = types.ApplicationRequest{}
data = types.ApplicationRsp{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("应用ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err = models.DBService.Model(&models.MgtApplication{}).Where("id = ?", request.ID).First(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,49 @@
package application
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
)
func Fetch(c *gin.Context) {
var (
request = types.ApplicationRequest{}
count int64 = 0
data = make([]types.ApplicationRsp, 0)
db = models.DBService.Model(&models.MgtApplication{})
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Keyword != "" {
likeStr := "%" + request.Keyword + "%"
db = db.Where("title like ? or code like ? ", likeStr)
}
if request.Status != 0 {
db = db.Where("status = ? ", request.Status)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,132 @@
package application
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// 获取应用用户列表
func UserFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = models.MgtApplication{}
db = models.DBService.Model(&models.MgtApplication{}).Select("id", "identity", "title")
count int64 = 0
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("Workspace不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Where("workspace = ?", request.Workspace).Preload("Users", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name", "account", "phone", "email", "avatar").Count(&count).Limit(size).Offset((page - 1) * size)
}).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}
// 获取应用角色列表
func RoleFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtApplication, 0)
db = models.DBService.Model(&models.MgtApplication{}).Select("id", "identity", "title")
count int64 = 0
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("Workspace不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Where("workspace = ?", request.Workspace).Preload("Roles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name")
}).Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}
// 获取应用权限列表
func PermissionFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtApplication, 0)
db = models.DBService.Model(&models.MgtApplication{}).Select("id", "identity", "title")
count int64 = 0
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("Workspace不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Where("workspace = ?", request.Workspace).Preload("Permissions").Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,71 @@
package application
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Midify(c *gin.Context) {
var (
request = types.ApplicationRequest{}
data = models.MgtApplication{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("更新应用参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 检查workspace是否已被其他应用使用
if request.Workspace != "" {
if err := models.DBService.Model(&models.MgtApplication{}).Where("workspace=? and id<>?", request.Workspace, request.ID).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if data.ID != 0 {
printer.Info("工作空间已被使用: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
}
var dataModel = models.MgtApplication{
Title: request.Title,
Workspace: request.Workspace,
Description: request.Description,
}
if request.Status != 0 {
dataModel.Status = request.Status
}
if err := models.DBService.Model(&models.MgtApplication{}).Where("id = ?", request.ID).Updates(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("应用更新成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID, Identity: dataModel.Identity})
}

View File

@@ -0,0 +1,103 @@
package department
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Create(c *gin.Context) {
var (
request = types.DptReq{}
data = models.MgtDepartment{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 创建时必填:应用维度(应用->部门)
if request.Workspace == "" {
printer.Error("新增部门参数异常: workspace 不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Name == "" {
printer.Error("新增部门参数异常: 部门名称不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到应用: workspace=%s, err=%v", request.Workspace, err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
// 同一应用下部门名称唯一
if err = models.DBService.Model(&models.MgtDepartment{}).
Where("app_id = ? and name = ?", appId, request.Name).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
} else {
printer.Info("该应用下部门已存在: workspace=%s, name=%s", request.Workspace, request.Name)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
// 若有父部门,校验父部门属于同一应用
if request.ParentID > 0 {
var parent models.MgtDepartment
if err = models.DBService.Select("id", "app_id").Where("id = ?", request.ParentID).First(&parent).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if parent.AppID != appId {
printer.Error("父部门不属于当前应用")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
}
// 新增部门(归属当前应用)
var dataModel = models.MgtDepartment{
Std_IICUDS: coreTypes.Std_IICUDS{Identity: utils.ULID(), Status: 1},
AppID: appId,
ParentID: request.ParentID,
LeaderID: request.LeaderID,
Name: request.Name,
}
if err = models.DBService.Create(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("部门创建成功: ID=%d, name=%s", dataModel.ID, dataModel.Name)
infra.Response.Success(c, types.IdResp{Id: dataModel.ID, Identity: dataModel.Identity})
}

View File

@@ -0,0 +1,96 @@
package department
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func getDepartmentIdsRecursive(db *gorm.DB, appId uint, rootDeptId uint) ([]uint, error) {
var allDepts []models.MgtDepartment
if err := db.Select("id", "parent_id").Where("app_id = ?", appId).Find(&allDepts).Error; err != nil {
return nil, err
}
childrenMap := make(map[uint][]uint, len(allDepts))
for _, dept := range allDepts {
if dept.ParentID > 0 {
childrenMap[dept.ParentID] = append(childrenMap[dept.ParentID], dept.ID)
}
}
result := make([]uint, 0, len(allDepts))
var collectChildren func(uint)
collectChildren = func(deptId uint) {
result = append(result, deptId)
for _, childId := range childrenMap[deptId] {
collectChildren(childId)
}
}
collectChildren(rootDeptId)
return result, nil
}
func Del(c *gin.Context) {
var (
request = types.DptReq{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
var dept models.MgtDepartment
if err := models.DBService.Select("id", "app_id").Where("id = ?", request.ID).First(&dept).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
allDeptIds, err := getDepartmentIdsRecursive(models.DBService, dept.AppID, request.ID)
if err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("dpt_id in ?", allDeptIds).Delete(&models.MgtLinkUserDpt{}).Error; err != nil {
return err
}
if err := tx.Where("dpt_id in ?", allDeptIds).Delete(&models.MgtLinkDptRole{}).Error; err != nil {
return err
}
if err := tx.Where("dpt_id in ?", allDeptIds).Delete(&models.MgtLinkDptPmn{}).Error; err != nil {
return err
}
if err := tx.Where("id in ?", allDeptIds).Delete(&models.MgtDepartment{}).Error; err != nil {
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.IdResp{Id: request.ID})
}

View File

@@ -0,0 +1,54 @@
package department
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Detail(c *gin.Context) {
var (
request = types.DptReq{}
data = models.MgtDepartment{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("部门ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err = models.DBService.Model(&models.MgtDepartment{}).
Select("id", "identity", "name", "app_id", "parent_id", "leader_id").Preload("Leader", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name")
}).Where("id = ?", request.ID).First(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,68 @@
package department
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
)
func Fetch(c *gin.Context) {
var (
request = types.DptReq{}
count int64 = 0
data = make([]models.MgtDepartment, 0)
db = models.DBService.Model(&models.MgtDepartment{}).Select("id", "identity", "name", "app_id", "parent_id")
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 应用->部门:列表必传 workspace只查询应用下部门
if request.Workspace == "" {
printer.Error("获取部门列表参数异常: workspace 不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到应用: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
db = db.Where("app_id = ?", appId)
if request.Keyword != "" {
db = db.Where("name like ?", "%"+request.Keyword+"%")
}
if request.Status != 0 {
db = db.Where("status = ? ", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
if request.ParentID != 0 {
db = db.Where("parent_id = ?", request.ParentID)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,105 @@
package department
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func PermissionsFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtDepartment, 0)
db = models.DBService.Model(&models.MgtDepartment{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
var appId uint
if request.Workspace != "" {
appModel := models.MgtApplication{Workspace: request.Workspace}
var err error
appId, err = appModel.WorkspaceToId()
if err != nil {
printer.Error("workspace not found: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
db = db.Where("app_id = ?", appId)
}
if request.Workspace != "" {
if err := db.Select("id", "identity", "name").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if len(data) == 0 {
infra.Response.Success(c, data)
return
}
deptIds := make([]uint, 0, len(data))
for _, d := range data {
deptIds = append(deptIds, d.ID)
}
var links []models.MgtLinkDptPmn
if err := models.DBService.Where("app_id = ? and dpt_id in ?", appId, deptIds).Find(&links).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
dptToPmn := make(map[uint][]uint)
pmnIdSet := make(map[uint]struct{})
for _, l := range links {
dptToPmn[l.DptId] = append(dptToPmn[l.DptId], l.PmnId)
pmnIdSet[l.PmnId] = struct{}{}
}
pmnIds := make([]uint, 0, len(pmnIdSet))
for id := range pmnIdSet {
pmnIds = append(pmnIds, id)
}
var perms []models.MgtPermission
if len(pmnIds) > 0 {
if err := models.DBService.Select("id", "identity", "title", "title_en", "sort_key", "parent_id").
Where("id in ?", pmnIds).Find(&perms).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
permMap := make(map[uint]models.MgtPermission)
for _, p := range perms {
permMap[p.ID] = p
}
for i := range data {
ids := dptToPmn[data[i].ID]
data[i].Permissions = make([]models.MgtPermission, 0, len(ids))
for _, pid := range ids {
if p, ok := permMap[pid]; ok {
data[i].Permissions = append(data[i].Permissions, p)
}
}
}
infra.Response.Success(c, data)
return
}
preloadPerm := func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "title", "title_en", "sort_key", "parent_id")
}
if err := db.Select("id", "identity", "name").Preload("Permissions", preloadPerm).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,129 @@
package department
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func PermissionsFetchTree(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtDepartment, 0)
db = models.DBService.Model(&models.MgtDepartment{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
var appId uint
if request.Workspace != "" {
appModel := models.MgtApplication{Workspace: request.Workspace}
var err error
appId, err = appModel.WorkspaceToId()
if err != nil {
printer.Error("workspace not found: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
db = db.Where("app_id = ?", appId)
}
if request.Workspace != "" {
if err := db.Select("id", "identity", "name").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if len(data) == 0 {
infra.Response.Success(c, data)
return
}
deptIds := make([]uint, 0, len(data))
for _, d := range data {
deptIds = append(deptIds, d.ID)
}
var links []models.MgtLinkDptPmn
if err := models.DBService.Where("app_id = ? and dpt_id in ?", appId, deptIds).Find(&links).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
dptToPmn := make(map[uint][]uint)
pmnIdSet := make(map[uint]struct{})
for _, l := range links {
dptToPmn[l.DptId] = append(dptToPmn[l.DptId], l.PmnId)
pmnIdSet[l.PmnId] = struct{}{}
}
pmnIds := make([]uint, 0, len(pmnIdSet))
for id := range pmnIdSet {
pmnIds = append(pmnIds, id)
}
var perms []models.MgtPermission
if len(pmnIds) > 0 {
if err := models.DBService.Select("id", "identity", "title", "title_en", "sort_key", "parent_id", "menu_path", "code", "component", "is_web_page", "is_new_tab", "web_url").
Where("id in ?", pmnIds).Order("sort_key").Find(&perms).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
for i := range data {
ids := dptToPmn[data[i].ID]
idSet := make(map[uint]struct{})
for _, id := range ids {
idSet[id] = struct{}{}
}
data[i].Permissions = buildPermTreeFilter(perms, 0, idSet)
}
infra.Response.Success(c, data)
return
}
permTreePreload := func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "title", "title_en", "sort_key", "parent_id", "menu_path", "code", "component", "is_web_page", "is_new_tab", "web_url").
Where("parent_id = 0").Order("sort_key").
Preload("Children", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "title", "title_en", "sort_key", "parent_id", "menu_path", "code", "component", "is_web_page", "is_new_tab", "web_url").Order("sort_key")
})
}
if err := db.Select("id", "identity", "name").Preload("Permissions", permTreePreload).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}
func buildPermTreeFilter(list []models.MgtPermission, parentId uint, idSet map[uint]struct{}) []models.MgtPermission {
var node []models.MgtPermission
for _, p := range list {
if _, ok := idSet[p.ID]; !ok {
continue
}
isRoot := parentId == 0
parentInSet := false
if p.ParentID != 0 {
_, parentInSet = idSet[p.ParentID]
}
matches := (p.ParentID == parentId) || (isRoot && p.ParentID != 0 && !parentInSet)
if !matches {
continue
}
children := buildPermTreeFilter(list, p.ID, idSet)
if len(children) > 0 {
p.Children = children
}
node = append(node, p)
}
return node
}

View File

@@ -0,0 +1,50 @@
package department
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
)
func FetchTree(c *gin.Context) {
var (
request = types.DptReq{}
data = make([]models.MgtDepartment, 0)
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("workspace is required: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("workspace not found: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
if err := models.DBService.Model(&models.MgtDepartment{}).
Where("app_id = ?", appId).
Select("id", "identity", "name", "app_id", "parent_id").
Order("parent_id asc").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
tree := buildDeptTree(data, 0)
infra.Response.Success(c, types.FetchResp{Data: tree})
}

View File

@@ -0,0 +1,82 @@
package department
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
)
// List 下拉用部门列表,必传 workspacetree=true 时返回树形
func List(c *gin.Context) {
var (
request = types.DptReq{}
data = make([]models.MgtDepartment, 0)
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("部门列表参数异常: workspace 不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到应用: workspace=%s", request.Workspace)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
db := models.DBService.Model(&models.MgtDepartment{}).
Where("app_id = ?", appId).
Select("id", "identity", "name", "app_id", "parent_id")
if request.Keyword != "" {
db = db.Where("name LIKE ?", "%"+request.Keyword+"%")
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
if request.Tree {
if err := db.Order("parent_id asc").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
tree := buildDeptTree(data, 0)
infra.Response.Success(c, types.FetchResp{Data: tree})
return
}
if err := db.Order("parent_id asc").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data})
}
// buildDeptTree 将扁平部门列表按 parent_id 建树parentId 为根节点父ID0 表示顶层)
func buildDeptTree(list []models.MgtDepartment, parentId uint) []models.MgtDepartment {
var node []models.MgtDepartment
for _, d := range list {
if d.ParentID != parentId {
continue
}
children := buildDeptTree(list, d.ID)
if len(children) > 0 {
d.Children = children
}
node = append(node, d)
}
return node
}

View File

@@ -0,0 +1,112 @@
package department
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Modify(c *gin.Context) {
var (
request = types.DptReq{}
data = models.MgtDepartment{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err = models.DBService.Select("id", "app_id", "identity").Where("id = ?", request.ID).First(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
appId := data.AppID
if request.ParentID > 0 {
if request.ParentID == request.ID {
printer.Error("self-reference is not allowed: id=%d", request.ID)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
descendantIds, err := getDepartmentIdsRecursive(models.DBService, appId, request.ID)
if err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
for _, id := range descendantIds {
if id == request.ParentID {
printer.Error("self-reference is not allowed: parent_id=%d", request.ParentID)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
}
}
if request.Name != "" {
var exist models.MgtDepartment
if err = models.DBService.Model(&models.MgtDepartment{}).
Where("app_id = ? and name = ? and id <> ?", appId, request.Name, request.ID).First(&exist).Error; err == nil {
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if request.ParentID > 0 {
var parent models.MgtDepartment
if err = models.DBService.Select("id", "app_id").Where("id = ?", request.ParentID).First(&parent).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if parent.AppID != appId {
printer.Error("parent department not found: parent_id=%d, app_id=%d", request.ParentID, appId)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
}
var dataModel = models.MgtDepartment{
Name: request.Name,
ParentID: request.ParentID,
LeaderID: request.LeaderID,
Std_IICUDS: coreTypes.Std_IICUDS{Status: request.Status},
}
if err := models.DBService.Model(&models.MgtDepartment{}).Where("id = ?", request.ID).Updates(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("department modified: id=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID, Identity: data.Identity})
}

View File

@@ -0,0 +1,199 @@
package department
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// SetPmn 给部门设置权限(应用->部门->权限,校验部门所属应用与 workspace 一致)
func SetPmn(c *gin.Context) {
var (
request = types.AddPmnRequest{}
pmnData = make([]models.MgtLinkDptPmn, 0)
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到应用: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
var dpt models.MgtDepartment
if err := models.DBService.Select("id", "app_id").Where("id = ?", request.Id).First(&dpt).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if dpt.AppID != appId {
printer.Error("部门不属于当前应用: dpt_id=%d", request.Id)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnData = append(pmnData, models.MgtLinkDptPmn{AppId: appId, PmnId: v, DptId: request.Id})
}
if err := models.DBService.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "dpt_id"}, {Name: "app_id"}, {Name: "pmn_id"}},
DoUpdates: clause.AssignmentColumns([]string{"dpt_id", "app_id", "pmn_id"}),
}).Create(&pmnData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("部门权限设置成功: dptId=%d", request.Id)
infra.Response.Success(c, "")
}
// ModifyPmn 按应用维度覆盖部门权限
func ModifyPmn(c *gin.Context) {
var (
request = types.AddPmnRequest{}
pmnData = make([]models.MgtLinkDptPmn, 0)
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到应用: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
var dpt models.MgtDepartment
if err := models.DBService.Select("id", "app_id").Where("id = ?", request.Id).First(&dpt).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if dpt.AppID != appId {
printer.Error("部门不属于当前应用: dpt_id=%d", request.Id)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnData = append(pmnData, models.MgtLinkDptPmn{AppId: appId, PmnId: v, DptId: request.Id})
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("dpt_id = ? and app_id = ?", request.Id, appId).Delete(&models.MgtLinkDptPmn{}).Error; err != nil {
return err
}
if len(pmnData) > 0 {
if err := tx.Create(&pmnData).Error; err != nil {
return err
}
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("部门权限修改成功: dptId=%d", request.Id)
infra.Response.Success(c, "")
}
// DelPmn 移除部门权限
func DelPmn(c *gin.Context) {
var request = types.AddPmnRequest{}
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if len(request.List) == 0 {
printer.Error("移除部门权限参数异常: list 不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到应用: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
var dpt models.MgtDepartment
if err := models.DBService.Select("id", "app_id").Where("id = ?", request.Id).First(&dpt).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if dpt.AppID != appId {
printer.Error("部门不属于当前应用: dpt_id=%d", request.Id)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnIds := make([]uint, 0, len(request.List))
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnIds = append(pmnIds, v)
}
if err := models.DBService.Where("dpt_id = ? AND app_id = ? AND pmn_id in ?", request.Id, appId, pmnIds).
Delete(&models.MgtLinkDptPmn{}).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("部门权限删除成功: dptId=%d", request.Id)
infra.Response.Success(c, "")
}

View File

@@ -0,0 +1,107 @@
package department
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// SetUser 将用户移入部门(可批量)
func SetUser(c *gin.Context) {
var request types.DptUserRequest
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
var dpt models.MgtDepartment
if err := models.DBService.Select("id").Where("id = ?", request.DptId).First(&dpt).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
list := make([]models.MgtLinkUserDpt, 0, len(request.UserIds))
for _, uid := range request.UserIds {
list = append(list, models.MgtLinkUserDpt{UserId: uid, DptId: request.DptId})
}
if err := models.DBService.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}, {Name: "dpt_id"}},
DoNothing: true,
}).Create(&list).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户移入部门成功: dptId=%d, count=%d", request.DptId, len(request.UserIds))
infra.Response.Success(c, "")
}
// DelUser 将用户移出部门(可批量)
func DelUser(c *gin.Context) {
var request types.DptUserRequest
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
result := models.DBService.Where("dpt_id = ? and user_id in ?", request.DptId, request.UserIds).
Delete(&models.MgtLinkUserDpt{})
if result.Error != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户移出部门成功: dptId=%d, count=%d", request.DptId, result.RowsAffected)
infra.Response.Success(c, "")
}
// UserFetch 获取部门下用户列表
func UserFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtDepartment, 0)
db = models.DBService.Model(&models.MgtDepartment{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("获取部门用户参数异常: 部门ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
db = db.Where("id = ?", request.ID)
if err := db.Select("id", "identity", "name").Preload("Users", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name")
}).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,35 @@
package hello
import (
"fmt"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
// Ping
func Ping(ctx *gin.Context) {
// file, err := ctx.FormFile("fileContent")
// if err != nil {
// fmt.Println("err", err)
// return
// }
// ctx.SaveUploadedFile(file, "./tmp/"+file.Filename)
// ctx.JSON(200, gin.H{
// "msg": file,
// })
ctx.JSON(200, gin.H{"message": "Pong"})
}
func SessionDemo(ctx *gin.Context) {
session := sessions.Default(ctx)
session.Set("key", "12345")
if err := session.Save(); err != nil {
ctx.JSON(500, gin.H{"error": err.Error()})
return
}
value := session.Get("key")
fmt.Println("session value: ", value)
ctx.JSON(200, gin.H{"message": "Pong"})
}

View File

@@ -0,0 +1,86 @@
package permission
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
coreutils "git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Create(c *gin.Context) {
var (
request = types.PermissionRequest{}
data = models.MgtPermission{}
app models.MgtApplication
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
app = models.MgtApplication{Workspace: request.Workspace}
appId, err := app.WorkspaceToId()
if err != nil {
printer.Error("??appId??: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
db := models.DBService.Model(&models.MgtPermission{})
if err = db.Where("code = ? AND app_id = ?", request.Code, appId).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
var deletedData models.MgtPermission
if err = models.DBService.Unscoped().Where("code = ? AND app_id = ?", request.Code, appId).First(&deletedData).Error; err == nil {
if err = models.DBService.Unscoped().Delete(&deletedData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
} else {
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
var dataModel = models.MgtPermission{
Std_IICUDS: coreTypes.Std_IICUDS{Identity: coreutils.UUID(), Status: 1},
Code: request.Code,
Title: request.Title,
TitleEn: request.TitleEn,
Description: request.Description,
Appid: appId,
ParentID: request.ParentID,
MenuPath: request.MenuPath,
MenuIcon: request.MenuIcon,
Type: request.Type,
Component: request.Component,
IsWebPage: request.IsWebPage,
IsNewTab: request.IsNewTab,
IsFull: request.IsFull,
HideMenu: request.HideMenu,
WebUrl: request.WebUrl,
}
if err = models.DBService.Model(&models.MgtPermission{}).Create(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.IdResp{Id: dataModel.ID, Identity: dataModel.Identity})
}

View File

@@ -0,0 +1,70 @@
package permission
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Del(c *gin.Context) {
var request = types.PermissionRequest{}
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("删除权限参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 删除权限时,同时删除关联数据
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
// 删除权限
if err = tx.Delete(&models.MgtPermission{}, request.ID).Error; err != nil {
printer.Error("删除权限异常: %v", err)
return err
}
// 删除用户权限关联
if err = tx.Where("pmn_id = ?", request.ID).Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
printer.Error("删除用户权限关联异常: %v", err)
return err
}
// 删除角色权限关联
if err = tx.Where("pmn_id = ?", request.ID).Delete(&models.MgtLinkRolePmn{}).Error; err != nil {
printer.Error("删除角色权限关联异常: %v", err)
return err
}
// 删除部门权限关联
if err = tx.Where("pmn_id = ?", request.ID).Delete(&models.MgtLinkDptPmn{}).Error; err != nil {
printer.Error("删除部门权限关联异常: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("权限删除成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID})
}

View File

@@ -0,0 +1,51 @@
package permission
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Detail(c *gin.Context) {
var (
request = types.PermissionRequest{}
data = types.PermissionRsp{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err = models.DBService.Model(&models.MgtPermission{}).Where("id = ?", request.ID).First(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,68 @@
package permission
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
)
func Fetch(c *gin.Context) {
var (
request = types.PermissionRequest{}
count int64 = 0
data = make([]types.PermissionRsp, 0)
db = models.DBService.Model(&models.MgtPermission{})
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Keyword != "" {
likeStr := "%" + request.Keyword + "%"
db = db.Where("title like ? or code like ? ", likeStr)
}
if request.Status != 0 {
db = db.Where("status = ? ", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
if request.Workspace != "" {
app := models.MgtApplication{Workspace: request.Workspace}
appId, err := app.WorkspaceToId()
if err != nil {
printer.Error("查询应用信息异常: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
db = db.Where("app_id = ?", appId)
}
// 按 sort_key 排序
if request.OrderDesc {
db = db.Order("sort_key desc")
} else {
db = db.Order("sort_key asc")
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,93 @@
package permission
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// 获取权限用户列表
func UserFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtPermission, 0)
db = models.DBService.Model(&models.MgtPermission{}).Select("id", "identity", "title")
count int64 = 0
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("Workspace不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Preload("Users", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name")
}).Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}
// 获取权限角色列表
func RoleFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtPermission, 0)
db = models.DBService.Model(&models.MgtPermission{}).Select("id", "identity", "title")
count int64 = 0
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace == "" {
printer.Error("Workspace不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Preload("Roles").Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,146 @@
package permission
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Midify(c *gin.Context) {
var (
request = types.PermissionRequest{}
data = models.MgtPermission{}
app models.MgtApplication
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("更新权限参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Workspace != "" {
app = models.MgtApplication{Workspace: request.Workspace}
appId, err := app.WorkspaceToId()
if err != nil {
printer.Error("获取appId异常: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
if request.Code != "" {
data = models.MgtPermission{}
if err := models.DBService.Model(&models.MgtPermission{}).Where("code = ? AND app_id = ? AND id <> ?", request.Code, appId, request.ID).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if data.ID != 0 {
printer.Info("权限code已存在: code=%s", request.Code)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
}
var dataModel = models.MgtPermission{
Std_IICUDS: coreTypes.Std_IICUDS{Status: request.Status},
Code: request.Code,
Title: request.Title,
Appid: appId,
ParentID: request.ParentID,
MenuPath: request.MenuPath,
MenuIcon: request.MenuIcon,
Type: request.Type,
Component: request.Component,
Description: request.Description,
IsWebPage: request.IsWebPage,
IsNewTab: request.IsNewTab,
IsFull: request.IsFull,
HideMenu: request.HideMenu,
WebUrl: request.WebUrl,
}
updateFields := []string{"status", "code", "title", "app_id", "parent_id", "menu_path", "menu_icon", "type", "component", "description", "is_web_page", "is_new_tab", "is_full", "hide_menu", "web_url"}
if err := models.DBService.Model(&models.MgtPermission{}).Where("id = ?", request.ID).Select(updateFields).Updates(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("权限更新成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID, Identity: dataModel.Identity})
return
}
var existing models.MgtPermission
if err := models.DBService.Select("app_id").Where("id = ?", request.ID).First(&existing).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if request.Code != "" {
data = models.MgtPermission{}
if err := models.DBService.Model(&models.MgtPermission{}).
Where("code = ? AND app_id = ? AND id <> ?", request.Code, existing.Appid, request.ID).
First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if data.ID != 0 {
printer.Info("权限code已存在: code=%s", request.Code)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
}
var dataModel = models.MgtPermission{
Std_IICUDS: coreTypes.Std_IICUDS{Status: request.Status},
Code: request.Code,
Title: request.Title,
ParentID: request.ParentID,
MenuPath: request.MenuPath,
MenuIcon: request.MenuIcon,
Type: request.Type,
Component: request.Component,
Description: request.Description,
IsWebPage: request.IsWebPage,
IsNewTab: request.IsNewTab,
IsFull: request.IsFull,
HideMenu: request.HideMenu,
WebUrl: request.WebUrl,
}
updateFields := []string{"status", "code", "title", "parent_id", "menu_path", "menu_icon", "type", "component", "description", "is_web_page", "is_new_tab", "is_full", "hide_menu", "web_url"}
if err := models.DBService.Model(&models.MgtPermission{}).Where("id = ?", request.ID).Select(updateFields).Updates(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("权限更新成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID, Identity: dataModel.Identity})
}

View File

@@ -0,0 +1,55 @@
package permission
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"github.com/gin-gonic/gin"
"gorm.io/gorm/clause"
)
func Sort(c *gin.Context) {
var (
request = make([]types.SortPmnRequest, 0)
data = make([]models.MgtPermission, 0)
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if len(request) == 0 {
printer.Error("排序参数不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
for _, v := range request {
if err := libs.ValidateStruct(&v); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
}
for _, v := range request {
data = append(data, models.MgtPermission{
Std_IICUDS: coreTypes.Std_IICUDS{ID: v.PmnId},
SortKey: int8(v.SortKey),
})
}
if err := models.DBService.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"sort_key"}),
}).Create(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("权限排序成功: 排序了 %d 个权限", len(request))
infra.Response.Success(c, "")
}

View File

@@ -0,0 +1,103 @@
package pub
import (
"errors"
"bsm/full/module/base/mgt/internal/impl"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreutils "git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// ForgetPwdBySms 忘记密码-通过短信更改
func ForgetPwdBySms(c *gin.Context) {
request := types.ForgetPwdRequest{}
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Code == "" {
printer.Error("验证码不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
var data models.MgtUser
if err := models.DBService.Model(&models.MgtUser{}).Where("account = ?", request.Account).First(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
if data.Phone != request.Phone {
printer.Error("账号与手机号不匹配: account=%s, phone=%s", request.Account, request.Phone)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
smsKey := types.KeyPrefix + request.Phone
storedCode, err := impl.RedisService.Client.Get(impl.RedisService.Ctx, smsKey).Result()
if err != nil {
if errors.Is(err, redis.Nil) {
printer.Error("验证码已过期或不存在: phone=%s", request.Phone)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
printer.Error("读取验证码异常: %v", err)
infra.Response.Error(c, errcode.ErrInternal)
return
}
if storedCode != request.Code {
printer.Error("验证码不正确: phone=%s", request.Phone)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if data.Salt == "" {
data.Salt = coreutils.UUID()
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.Password+data.Salt), bcrypt.DefaultCost)
if err != nil {
printer.Error("密码加密异常: %v", err)
infra.Response.Error(c, errcode.ErrInternal)
return
}
if err := models.DBService.Model(&models.MgtUser{}).Where("id = ?", data.ID).Updates(map[string]interface{}{
"password": string(hashedPassword),
"salt": data.Salt,
}).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if err := impl.RedisService.Client.Del(impl.RedisService.Ctx, smsKey).Err(); err != nil {
printer.Error("清除验证码缓存异常: %v", err)
}
printer.Info("密码重置成功: account=%s, phone=%s", request.Account, request.Phone)
infra.Response.Success(c, "密码重置成功")
}
// ForgetPwdByPwd 忘记密码-通过旧密码更改
func ForgetPwdByPwd(c *gin.Context) {
}

View File

@@ -0,0 +1,158 @@
package pub
import (
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"strconv"
"bsm/full/module/base/mgt/internal/impl"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/crypto/token"
"git.apinb.com/bsm-sdk/core/env"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/vars"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// Login 登录
// @Summary 登录
// @Description 登录
// @Tags 登录
// @Accept application/json
// @Produce application/json
// @Param role body types.LoginRequest true "登录请求"
// @Security ApiKeyAuth
// @Success 200 {object} types.LoginResp "新增成功,返回token"
// @Router /login [post]
func Login(c *gin.Context) {
var (
request types.LoginRequest
resp = types.LoginResp{}
user = models.MgtUser{}
extend = map[string]string{}
err error
account string
pwd string
)
err = c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if request.Account != "" && request.Password != "" {
account = request.Account
pwd = request.Password
} else if request.AppId != "" && request.AppKey != "" {
account = request.AppId
pwd = request.AppKey
} else {
printer.Error("登录参数异常: 账号密码或AppId/AppKey必须提供")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 获取用户信息
if err = models.DBService.Model(&models.MgtUser{}).Where("account=?", account).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
resp = types.LoginResp{Phone: user.Phone, Email: user.Email, Name: user.Name, UserId: user.ID, Account: user.Account, Avatar: user.Avatar, Identity: user.Identity}
// 检测Password与Status
if request.AppKey != "" {
err = checkAppKeyAndStatus(user.Password, pwd, int16(user.Status))
} else {
err = checkPwdAndStatus(user.Password, pwd, user.Salt, int16(user.Status))
}
if err != nil {
printer.Error("检测Password与Status异常: %v", err)
infra.Response.Error(c, err)
return
}
extend["id"] = strconv.Itoa(int(user.ID))
extend["Identity"] = user.Identity
extend["status"] = strconv.Itoa(int(user.Status))
extend["name"] = user.Name
// 获取token
resp.Token, err = token.New(env.Runtime.JwtSecretKey).GenerateJwt(uint(user.ID), user.Identity, c.ClientIP(), "", nil, extend)
if err != nil {
printer.Error("获取token异常: %v", err)
infra.Response.Error(c, err)
return
}
err = models.UpdateLastLoginDate(uint(user.ID))
if err != nil {
printer.Error("更新最后登录时间异常: %v", err)
infra.Response.Error(c, errcode.ErrInternal)
return
}
printer.Info("用户登录成功: account=%s, userId=%d", account, user.ID)
infra.Response.Success(c, resp)
}
func SaveToken(id int64, role, token string) error {
tokenKey := fmt.Sprintf("%d-%s-%s", uint(id), role, "token")
status := impl.RedisService.Client.Set(impl.RedisService.Ctx, tokenKey, token, 0)
if status.Val() != "OK" {
printer.Error("保存token失败: %v", errcode.ErrTokenAuthTokenChanged)
return errcode.ErrTokenAuthTokenChanged
}
return nil
}
func checkPwdAndStatus(userPwd, inPwd, salt string, stat int16) error {
// 账号是否禁用
if stat == vars.DisabledStatus {
printer.Error("检查账号状态异常: 账号已禁用")
return errcode.ErrUnavailable
}
err := bcrypt.CompareHashAndPassword([]byte(userPwd), []byte(inPwd+salt))
if err == nil {
return nil
}
// 如果bcrypt失败尝试MD5验证向后兼容旧用户
isBcrypt := len(userPwd) > 4 && (userPwd[0:4] == "$2a$" || userPwd[0:4] == "$2b$" || userPwd[0:4] == "$2y$")
if isBcrypt {
printer.Error("密码验证失败: bcrypt验证失败")
return errcode.ErrPassword
}
hash := md5.Sum([]byte(inPwd + salt))
md5Hash := hex.EncodeToString(hash[:])
if userPwd == md5Hash {
return nil
}
printer.Error("密码验证失败: 所有验证方式均失败")
return errcode.ErrPassword
}
func checkAppKeyAndStatus(userPwd, pwd string, stat int16) error {
if userPwd != pwd {
printer.Error("检查密码异常: 密码不匹配")
return errcode.ErrPassword
}
if stat == vars.DisabledStatus {
printer.Error("检查账号状态异常: 账号已禁用")
return errcode.ErrUnavailable
}
return nil
}

View File

@@ -0,0 +1,35 @@
package pub
import (
"git.apinb.com/bsm-sdk/core/crypto/token"
"git.apinb.com/bsm-sdk/core/env"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/service"
"github.com/gin-gonic/gin"
)
// Refresh 刷新token
// @Summary 刷新token
// @Description 刷新token
// @Tags 刷新token
// @Accept application/json
// @Produce application/json
// @Security ApiKeyAuth
// @Success 200 {object} coreTypes.LoginResp "刷新成功,返回token"
// @Router /pub/refresh [post]
func Refresh(c *gin.Context) {
claims, err := service.ParseMetaCtx(c, nil)
if err != nil {
infra.Response.Error(c, err)
return
}
// 将 ExpiresAt 转换为 time.Time 类型
token, err := token.New(env.Runtime.JwtSecretKey).GenerateJwt(claims.ID, claims.Identity, claims.Client, claims.Role, nil, claims.Extend)
if err != nil {
infra.Response.Error(c, errcode.ErrTokenGenerate)
return
}
infra.Response.Success(c, map[string]string{"token": token})
}

View File

@@ -0,0 +1,71 @@
package role
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
coreutils "git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Create(c *gin.Context) {
var (
request = types.RoleReq{}
data = models.MgtRole{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Name == "" {
printer.Error("role name is required: name=%s", request.Name)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
db := models.DBService.Model(&models.MgtRole{})
if err = db.Where("name = ? ", request.Name).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
var deletedData models.MgtRole
if err = models.DBService.Unscoped().Where("name = ?", request.Name).First(&deletedData).Error; err == nil {
if err = models.DBService.Unscoped().Delete(&deletedData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("role deleted: id=%d, name=%s", deletedData.ID, deletedData.Name)
}
} else {
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
var dataModel = models.MgtRole{
Description: request.Description,
Name: request.Name,
Std_IICUDS: coreTypes.Std_IICUDS{Identity: coreutils.UUID(), Status: 1},
}
if err = models.DBService.Model(&models.MgtRole{}).Create(&dataModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.IdResp{Id: dataModel.ID, Identity: dataModel.Identity})
}

View File

@@ -0,0 +1,94 @@
package role
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Del(c *gin.Context) {
var (
request = types.RoleReq{}
pmnId = []uint{}
userIds = []uint{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("删除角色参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err := models.DBService.Model(&models.MgtLinkRolePmn{}).Where("role_id=?", request.ID).Pluck("pmn_id", &pmnId).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if len(pmnId) > 0 {
if err := models.DBService.Model(&models.MgtLinkUserRole{}).
Where("role_id = ?", request.ID).
Pluck("user_id", &userIds).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if len(pmnId) > 0 && len(userIds) > 0 {
if err := tx.Where("user_id in ? and pmn_id in ?", userIds, pmnId).
Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
printer.Error("删除用户对应角色权限异常: %v", err)
return err
}
}
if err := tx.Delete(&models.MgtRole{}, request.ID).Error; err != nil {
printer.Error("删除角色异常: %v", err)
return err
}
if err := tx.Where("role_id = ?", request.ID).Delete(&models.MgtLinkRolePmn{}).Error; err != nil {
printer.Error("删除角色权限关联异常: %v", err)
return err
}
if err := tx.Where("role_id = ?", request.ID).Delete(&models.MgtLinkUserRole{}).Error; err != nil {
printer.Error("删除角色用户关联异常: %v", err)
return err
}
if err := tx.Where("role_id = ?", request.ID).Delete(&models.MgtLinkRoleApp{}).Error; err != nil {
printer.Error("删除角色应用关联异常: %v", err)
return err
}
if err := tx.Where("role_id = ?", request.ID).Delete(&models.MgtLinkDptRole{}).Error; err != nil {
printer.Error("删除部门角色关联异常: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("角色删除成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID})
}

View File

@@ -0,0 +1,51 @@
package role
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Detail(c *gin.Context) {
var (
request = types.RoleReq{}
data = models.MgtRole{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("角色ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err = models.DBService.Model(&models.MgtRole{}).Select("id", "identity", "name", "description").Where("id = ?", request.ID).First(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,51 @@
package role
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
)
func Fetch(c *gin.Context) {
var (
request = types.RoleReq{}
count int64 = 0
data = make([]models.MgtRole, 0)
db = models.DBService.Model(&models.MgtRole{}).Select("id", "identity", "name", "description")
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Keyword != "" {
db = db.Where("name like ?", "%"+request.Keyword+"%")
}
if request.Status != 0 {
db = db.Where("status = ? ", request.Status)
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,133 @@
package role
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func PermissionsFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtRole, 0)
db = models.DBService.Model(&models.MgtRole{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
if err := db.Select("id", "identity", "name").Preload("Permissions", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "title", "title_en", "sort_key", "parent_id")
}).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}
func PermissionsFetchTree(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtRole, 0)
db = models.DBService.Model(&models.MgtRole{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
if err := db.Select("id", "identity", "name").Preload("Application", func(db *gorm.DB) *gorm.DB {
db = db.Select("id", "identity", "title")
if request.Workspace != "" {
db = db.Where("workspace = ?", request.Workspace)
}
return db.Preload("RolesPmn", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "title", "title_en", "sort_key", "parent_id", "menu_path", "code", "component", "is_web_page", "is_new_tab", "web_url").
Where("parent_id=0").Order("sort_key").
Preload("Children", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "title", "title_en", "sort_key", "parent_id", "menu_path", "code", "component", "is_web_page", "is_new_tab", "web_url").Order("sort_key")
})
})
}).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}
func ApplicationsFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtRole, 0)
db = models.DBService.Model(&models.MgtRole{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
if err := db.Select("id", "identity", "name").Preload("Application").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}
func UserFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtRole, 0)
db = models.DBService.Model(&models.MgtRole{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID != 0 {
db = db.Where("id = ?", request.ID)
}
if err := db.Select("id", "identity", "name").Preload("Users", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name")
}).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,67 @@
package role
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Midify(c *gin.Context) {
var (
request = types.RoleReq{}
data = models.MgtRole{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("更新角色参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Name != "" {
db := models.DBService.Model(&models.MgtRole{})
if err = db.Where("name = ? and id<> ?", request.Name, request.ID).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if data.ID != 0 {
printer.Info("角色名称已存在: name=%s", request.Name)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
}
var roleModel = models.MgtRole{
Name: request.Name,
Description: request.Description,
Std_IICUDS: coreTypes.Std_IICUDS{Status: request.Status},
}
if err := models.DBService.Model(&models.MgtRole{}).Where("id = ?", request.ID).Updates(&roleModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("角色更新成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID, Identity: roleModel.Identity})
}

View File

@@ -0,0 +1,170 @@
package role
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// SetPmn 给角色设置权限
func SetPmn(c *gin.Context) {
var (
request = types.AddPmnRequest{}
pmnData = make([]models.MgtLinkRolePmn, 0)
Appdta = models.MgtLinkRoleApp{}
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到appid: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnData = append(pmnData, models.MgtLinkRolePmn{AppId: appId, PmnId: v, RoleId: request.Id})
Appdta = models.MgtLinkRoleApp{AppId: appId, RoleId: request.Id}
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "pmn_id"}, {Name: "role_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "pmn_id", "role_id"}),
}).Create(&pmnData).Error; err != nil {
printer.Error("创建角色权限关联失败: %v", err)
return err
}
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "role_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "role_id"}),
}).Create(&Appdta).Error; err != nil {
printer.Error("创建角色应用关联失败: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("角色权限设置成功: roleId=%d", request.Id)
infra.Response.Success(c, "")
}
// ModifyPmn 给角色修改权限
func ModifyPmn(c *gin.Context) {
var (
request = types.AddPmnRequest{}
pmnData = make([]models.MgtLinkRolePmn, 0)
Appdta = models.MgtLinkRoleApp{}
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到appid: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnData = append(pmnData, models.MgtLinkRolePmn{AppId: appId, PmnId: v, RoleId: request.Id})
Appdta = models.MgtLinkRoleApp{AppId: appId, RoleId: request.Id}
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("app_id = ? and role_id = ?", appId, request.Id).Delete(&models.MgtLinkRolePmn{}).Error; err != nil {
printer.Error("删除角色权限关联失败: %v", err)
return err
}
if err := tx.Where("app_id = ? and role_id = ?", appId, request.Id).Delete(&models.MgtLinkRoleApp{}).Error; err != nil {
printer.Error("删除角色应用关联失败: %v", err)
return err
}
if err := tx.Create(&pmnData).Error; err != nil {
printer.Error("创建角色权限关联失败: %v", err)
return err
}
if err := tx.Create(&Appdta).Error; err != nil {
printer.Error("创建角色应用关联失败: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("角色权限修改成功: roleId=%d", request.Id)
infra.Response.Success(c, "")
}
// DelPmn 角色权限移除
func DelPmn(c *gin.Context) {
var request = types.AddPmnRequest{}
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到appid: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
pmnIds := make([]uint, 0, len(request.List))
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnIds = append(pmnIds, v)
}
if err := models.DBService.Where("app_id = ? AND role_id = ? AND pmn_id in ?", appId, request.Id, pmnIds).
Delete(&models.MgtLinkRolePmn{}).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("角色权限删除成功: roleId=%d", request.Id)
infra.Response.Success(c, "")
}

View File

@@ -0,0 +1,14 @@
package tool
import "bsm/full/module/base/mgt/internal/types"
func GetSizeAndPage(page, size int) (int, int) {
if page <= 0 {
page = types.DefaultPage
}
if size <= 0 {
size = types.DefaultSize
}
return page, size
}

View File

@@ -0,0 +1,124 @@
package user
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/middleware"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
coreutils "git.apinb.com/bsm-sdk/core/utils"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
func Create(c *gin.Context) {
var (
request = types.UserRequest{}
data = models.MgtUser{}
linkData = models.MgtApplication{}
)
_, err := middleware.ParseAuth(c)
if err != nil {
infra.Response.Error(c, errcode.ErrTokenAuthNotFound)
return
}
err = c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Account == "" || request.Phone == "" || request.PassWord == "" {
printer.Error("account, phone and password are required: account=%s, phone=%s", request.Account, request.Phone)
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
db := models.DBService.Model(&models.MgtUser{})
if err = db.Where("phone = ? or account = ?", request.Phone, request.Account).First(&data).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
var deletedData models.MgtUser
if err = models.DBService.Unscoped().Where("phone = ?", request.Phone).First(&deletedData).Error; err == nil {
if err = models.DBService.Unscoped().Delete(&deletedData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("user deleted: id=%d, phone=%s", deletedData.ID, deletedData.Phone)
}
if request.Phone != request.Account {
if err = models.DBService.Unscoped().Where("account = ?", request.Account).First(&deletedData).Error; err == nil {
if err = models.DBService.Unscoped().Delete(&deletedData).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
}
} else {
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
if request.Workspace == "" {
request.Workspace = "def"
}
if err = models.DBService.Model(&models.MgtApplication{}).Where("workspace = ?", request.Workspace).First(&linkData).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
salt := coreutils.UUID()
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.PassWord+salt), bcrypt.DefaultCost)
if err != nil {
infra.Response.Error(c, errcode.ErrInternal)
return
}
var userModel = models.MgtUser{
Account: request.Account,
Name: request.Name,
Password: string(hashedPassword),
Phone: request.Phone,
Email: request.Email,
Avatar: request.Avatar,
Salt: salt,
Std_IICUDS: coreTypes.Std_IICUDS{Identity: coreutils.UUID(), Status: 1},
}
if err = models.DBService.Transaction(func(tx *gorm.DB) error {
if err = tx.Model(&models.MgtUser{}).Create(&userModel).Error; err != nil {
return err
}
userLink := models.MgtLinkUserApp{
AppId: linkData.ID,
UserId: userModel.ID,
}
if err = tx.Model(&models.MgtLinkUserApp{}).Create(&userLink).Error; err != nil {
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.IdResp{Id: userModel.ID, Identity: userModel.Identity})
}

View File

@@ -0,0 +1,76 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Del(c *gin.Context) {
var request = types.UserRequest{}
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("删除用户参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 删除用户时,同时删除关联数据
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
// 删除用户
if err = tx.Delete(&models.MgtUser{}, request.ID).Error; err != nil {
printer.Error("删除用户异常: %v", err)
return err
}
// 删除用户角色关联
if err = tx.Where("user_id = ?", request.ID).Delete(&models.MgtLinkUserRole{}).Error; err != nil {
printer.Error("删除用户角色关联异常: %v", err)
return err
}
// 删除用户应用关联
if err = tx.Where("user_id = ?", request.ID).Delete(&models.MgtLinkUserApp{}).Error; err != nil {
printer.Error("删除用户应用关联异常: %v", err)
return err
}
// 删除用户权限关联
if err = tx.Where("user_id = ?", request.ID).Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
printer.Error("删除用户权限关联异常: %v", err)
return err
}
// 删除用户部门关联
if err = tx.Where("user_id = ?", request.ID).Delete(&models.MgtLinkUserDpt{}).Error; err != nil {
printer.Error("删除用户部门关联异常: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户删除成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID})
}

View File

@@ -0,0 +1,51 @@
package user
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Detail(c *gin.Context) {
var (
request = types.UserRequest{}
userData = types.UserResp{}
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("用户ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err = models.DBService.Model(&models.MgtUser{}).Where("id = ?", request.ID).First(&userData).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, userData)
}

View File

@@ -0,0 +1,49 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/logic/tool"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
)
func Fetch(c *gin.Context) {
var (
request = types.UserRequest{}
count int64 = 0
data = make([]types.UserResp, 0)
db = models.DBService.Model(&models.MgtUser{})
)
// 解析JSON请求
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
// 参数验证
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Keyword != "" {
likeStr := "%" + request.Keyword + "%"
db = db.Where("name like ? or account like ? or phone like ?", likeStr, likeStr, likeStr)
}
if request.Status != 0 {
db = db.Where("status = ? ", request.Status)
}
page, size := tool.GetSizeAndPage(request.Page, request.Size)
if err := db.Count(&count).Limit(size).Offset((page - 1) * size).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data, Page: page, Size: size, Total: count})
}

View File

@@ -0,0 +1,49 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
mgtmw "bsm/full/module/base/mgt/internal/middleware"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/middleware"
"github.com/gin-gonic/gin"
)
func ApplicationsFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtUser, 0)
db = models.DBService.Model(&models.MgtUser{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
auth, err := middleware.ParseAuth(c)
if err != nil {
infra.Response.Error(c, errcode.ErrTokenAuthParseFail)
return
}
if request.ID == 0 {
request.ID = auth.ID
}
if !mgtmw.EnsureSelfOrAdmin(c, request.ID) {
return
}
db = db.Where("id = ?", request.ID)
if err := db.Select("id", "identity", "name").Preload("Application").Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,226 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
mgtmw "bsm/full/module/base/mgt/internal/middleware"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/middleware"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
const permissionPreloadSelect = "id,identity,title,title_en,sort_key,parent_id,menu_path,menu_icon,code,component,is_web_page,is_new_tab,is_full,hide_menu,web_url,app_id"
func permissionOrder(db *gorm.DB) *gorm.DB { return db.Order("sort_key asc") }
func pmnPreloadWithWorkspace(workspace string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
db = db.Select(permissionPreloadSelect)
if workspace != "" {
db = db.Where("app_id = (SELECT id FROM mgt_application WHERE workspace = ?)", workspace)
}
return permissionOrder(db)
}
}
func mergeEffectivePermissions(u *models.MgtUser) []models.MgtPermission {
seen := make(map[uint]struct{})
out := make([]models.MgtPermission, 0)
add := func(list []models.MgtPermission) {
for _, p := range list {
if p.ID == 0 {
continue
}
if _, ok := seen[p.ID]; ok {
continue
}
seen[p.ID] = struct{}{}
out = append(out, p)
}
}
add(u.Permissions)
for i := range u.Roles {
add(u.Roles[i].Permissions)
}
for i := range u.Departments {
add(u.Departments[i].Permissions)
for j := range u.Departments[i].Roles {
add(u.Departments[i].Roles[j].Permissions)
}
}
return out
}
func PermissionsFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtUser, 0)
db = models.DBService.Model(&models.MgtUser{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
auth, err := middleware.ParseAuth(c)
if err != nil {
infra.Response.Error(c, errcode.ErrTokenAuthParseFail)
return
}
if request.ID == 0 {
request.ID = auth.ID
}
if !mgtmw.EnsureSelfOrAdmin(c, request.ID) {
return
}
ws := request.Workspace
db = db.Where("id = ?", request.ID).Select("id", "identity", "name").
Preload("Permissions", pmnPreloadWithWorkspace(ws)).
Preload("Roles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name").Preload("Permissions", pmnPreloadWithWorkspace(ws))
}).
Preload("Departments", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name").
Preload("Permissions", pmnPreloadWithWorkspace(ws)).
Preload("Roles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name").Preload("Permissions", pmnPreloadWithWorkspace(ws))
})
})
if err := db.Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if len(data) == 0 {
infra.Response.Success(c, data)
return
}
merged := mergeEffectivePermissions(&data[0])
data[0].Permissions = merged
infra.Response.Success(c, data)
}
func PermissionsFetchTree(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtUser, 0)
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
auth, err := middleware.ParseAuth(c)
if err != nil {
infra.Response.Error(c, errcode.ErrTokenAuthParseFail)
return
}
if request.ID == 0 {
request.ID = auth.ID
}
if !mgtmw.EnsureSelfOrAdmin(c, request.ID) {
return
}
db := models.DBService.Model(&models.MgtUser{}).Where("id = ?", request.ID).
Select("id", "identity", "name").
Preload("Permissions", func(db *gorm.DB) *gorm.DB {
return db.Select(permissionPreloadSelect).Order("sort_key asc")
}).
Preload("Roles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name").Preload("Permissions", func(db *gorm.DB) *gorm.DB {
return db.Select(permissionPreloadSelect).Order("sort_key asc")
})
}).
Preload("Departments", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name").
Preload("Permissions", func(db *gorm.DB) *gorm.DB {
return db.Select(permissionPreloadSelect).Order("sort_key asc")
}).
Preload("Roles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name").Preload("Permissions", func(db *gorm.DB) *gorm.DB {
return db.Select(permissionPreloadSelect).Order("sort_key asc")
})
})
})
if err := db.Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if len(data) == 0 {
infra.Response.Success(c, data)
return
}
merged := mergeEffectivePermissions(&data[0])
pmnIdsByApp := make(map[uint][]uint)
for _, p := range merged {
if p.Appid == 0 {
continue
}
pmnIdsByApp[p.Appid] = append(pmnIdsByApp[p.Appid], p.ID)
}
appDb := models.DBService.Model(&models.MgtUser{}).Where("id = ?", request.ID).
Select("id", "identity", "name")
appDb = appDb.Preload("Application", func(db *gorm.DB) *gorm.DB {
db = db.Select("id", "identity", "title", "workspace")
if request.Workspace != "" {
db = db.Where("workspace = ?", request.Workspace)
}
return db
})
var userWithApp []models.MgtUser
if err := appDb.Find(&userWithApp).Error; err != nil || len(userWithApp) == 0 {
infra.Response.Success(c, data)
return
}
apps := userWithApp[0].Application
if len(apps) == 0 {
data[0].Application = nil
infra.Response.Success(c, data)
return
}
treeSelect := "id,identity,title,title_en,sort_key,parent_id,menu_path,menu_icon,code,component,is_web_page,is_new_tab,web_url,app_id"
for i := range apps {
ids := pmnIdsByApp[apps[i].ID]
if len(ids) == 0 {
apps[i].UsersPmn = nil
apps[i].RolesPmn = nil
continue
}
var tree []models.MgtPermission
err := models.DBService.Model(&models.MgtPermission{}).
Select(treeSelect).
Where("app_id = ? AND parent_id = 0 AND id IN ?", apps[i].ID, ids).
Order("sort_key asc").
Preload("Children", func(db *gorm.DB) *gorm.DB {
return db.Select(treeSelect).Where("id IN ?", ids).Order("sort_key asc")
}).
Find(&tree).Error
if err != nil {
apps[i].UsersPmn = nil
apps[i].RolesPmn = nil
continue
}
apps[i].UsersPmn = tree
}
data[0].Application = apps
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,53 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
mgtmw "bsm/full/module/base/mgt/internal/middleware"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/middleware"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func RoleFetch(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]models.MgtUser, 0)
db = models.DBService.Model(&models.MgtUser{})
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
auth, err := middleware.ParseAuth(c)
if err != nil {
infra.Response.Error(c, errcode.ErrTokenAuthParseFail)
return
}
if request.ID == 0 {
request.ID = auth.ID
}
if !mgtmw.EnsureSelfOrAdmin(c, request.ID) {
return
}
db = db.Where("id = ?", request.ID)
if err := db.Select("id", "identity", "name").Preload("Roles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "identity", "name")
}).Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, data)
}

View File

@@ -0,0 +1,40 @@
package user
import (
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"github.com/gin-gonic/gin"
)
func UserList(c *gin.Context) {
var (
request = types.FetchBase{}
data = make([]types.UserResp, 0)
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if request.Workspace == "" {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
db := models.DBService.Model(&models.MgtUser{}).
Where("id IN (SELECT user_id FROM mgt_link_user_app WHERE app_id = (SELECT id FROM mgt_application WHERE workspace = ? LIMIT 1))", request.Workspace)
if request.Keyword != "" {
likeStr := "%" + request.Keyword + "%"
db = db.Where("name LIKE ?", likeStr)
}
if request.Status != 0 {
db = db.Where("status = ?", request.Status)
}
if err := db.Find(&data).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, types.FetchResp{Data: data})
}

View File

@@ -0,0 +1,74 @@
package user
import (
"errors"
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
coreTypes "git.apinb.com/bsm-sdk/core/types"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Midify(c *gin.Context) {
var (
request = types.UserRequest{}
user = models.MgtUser{}
)
err := c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
request.PassWord = ""
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.ID == 0 {
printer.Error("更新用户参数异常: ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if request.Account != "" {
if err := models.DBService.Model(&models.MgtUser{}).Where("account = ? and id <>?", request.Account, request.ID).First(&user).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrDB)
return
}
}
if user.ID != 0 {
printer.Info("账号已被使用: account=%s", request.Account)
infra.Response.Error(c, errcode.ErrAlreadyExists)
return
}
}
var userModel = models.MgtUser{
Name: request.Name,
Phone: request.Phone,
Email: request.Email,
Avatar: request.Avatar,
Std_IICUDS: coreTypes.Std_IICUDS{Status: request.Status},
}
updateFields := []string{"name", "phone", "email", "avatar", "status"}
if request.Account != "" {
userModel.Account = request.Account
updateFields = append(updateFields, "account")
}
if err := models.DBService.Model(&models.MgtUser{}).Where("id = ?", request.ID).Select(updateFields).Updates(&userModel).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户更新成功: ID=%d", request.ID)
infra.Response.Success(c, types.IdResp{Id: request.ID, Identity: userModel.Identity})
}

View File

@@ -0,0 +1,171 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// SetPmn 给用户设置权限
func SetPmn(c *gin.Context) {
var (
request = types.AddPmnRequest{}
pmnData = make([]models.MgtLinkUserPmn, 0)
Appdta = models.MgtLinkUserApp{}
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到appid: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnData = append(pmnData, models.MgtLinkUserPmn{AppId: appId, PmnId: v, UserId: request.Id})
Appdta = models.MgtLinkUserApp{AppId: appId, UserId: request.Id}
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "pmn_id"}, {Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "pmn_id", "user_id"}),
}).Create(&pmnData).Error; err != nil {
return err
}
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "user_id"}),
}).Create(&Appdta).Error; err != nil {
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户权限设置成功: userId=%d", request.Id)
infra.Response.Success(c, "")
}
// ModifyPmn 给用户修改权限
func ModifyPmn(c *gin.Context) {
var (
request = types.AddPmnRequest{}
pmnData = make([]models.MgtLinkUserPmn, 0)
Appdta = models.MgtLinkUserApp{}
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到appid: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnData = append(pmnData, models.MgtLinkUserPmn{AppId: appId, PmnId: v, UserId: request.Id})
Appdta = models.MgtLinkUserApp{AppId: appId, UserId: request.Id}
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("app_id = ? and user_id = ?", appId, request.Id).Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
printer.Error("删除用户权限关联失败: %v", err)
return err
}
if len(pmnData) == 0 {
return nil
}
if err := tx.Create(&pmnData).Error; err != nil {
printer.Error("创建用户权限关联失败: %v", err)
return err
}
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "user_id"}),
}).Create(&Appdta).Error; err != nil {
printer.Error("创建用户应用关联失败: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户权限修改成功: userId=%d", request.Id)
infra.Response.Success(c, "")
}
// DelPmn 用户权限移除
func DelPmn(c *gin.Context) {
var request = types.AddPmnRequest{}
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
appModel := models.MgtApplication{Workspace: request.Workspace}
appId, err := appModel.WorkspaceToId()
if err != nil {
printer.Error("未获取到appid: %v", err)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
pmnIds := make([]uint, 0, len(request.List))
for _, v := range request.List {
if v == 0 {
printer.Error("权限ID不能为空")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
pmnIds = append(pmnIds, v)
}
if err := models.DBService.Where("app_id = ? AND user_id = ? AND pmn_id in ?", appId, request.Id, pmnIds).
Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
printer.Info("用户权限删除成功: userId=%d", request.Id)
infra.Response.Success(c, "")
}

View File

@@ -0,0 +1,188 @@
package user
import (
"bsm/full/module/base/mgt/internal/libs"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// SetRole 给用户设置角色
func SetRole(c *gin.Context) {
var (
request = types.UserRoleRequest{}
role = make([]models.MgtRole, 0)
pmnData = make([]models.MgtLinkUserPmn, 0)
roleData = make([]models.MgtLinkUserRole, 0)
appId = make([]uint, 0)
userApp = make([]models.MgtLinkUserApp, 0)
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err := models.DBService.Model(&models.MgtRole{}).Where("id in ?", request.RoleId).Preload("Permissions").Find(&role).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
for _, v := range role {
for _, vv := range v.Permissions {
pmnData = append(pmnData, models.MgtLinkUserPmn{
AppId: vv.Appid,
PmnId: vv.ID,
UserId: request.UserId,
})
}
roleData = append(roleData, models.MgtLinkUserRole{UserId: request.UserId, RoleId: v.ID})
}
if err := models.DBService.Model(&models.MgtLinkRoleApp{}).Select("app_id").Where("role_id in ?", request.RoleId).Find(&appId).Error; err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
if len(appId) == 0 {
printer.Info("角色未关联应用: roleIds=%v", request.RoleId)
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
for _, v := range appId {
userApp = append(userApp, models.MgtLinkUserApp{UserId: request.UserId, AppId: v})
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "pmn_id"}, {Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "pmn_id", "user_id"}),
}).Create(&pmnData).Error; err != nil {
printer.Error("创建用户权限关系失败: %v", err)
return err
}
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_id"}, {Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"app_id", "user_id"}),
}).Create(&userApp).Error; err != nil {
printer.Error("创建用户app关系失败: %v", err)
return err
}
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}, {Name: "role_id"}},
DoUpdates: clause.AssignmentColumns([]string{"user_id", "role_id"}),
}).Create(&roleData).Error; err != nil {
printer.Error("创建用户角色关系失败: %v", err)
return err
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, "")
}
// DelRole 给用户移除角色
func DelRole(c *gin.Context) {
var (
request = types.UserRoleRequest{}
)
if err := c.ShouldBindJSON(&request); err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if err := libs.ValidateStruct(&request); err != nil {
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
if err := models.DBService.Transaction(func(tx *gorm.DB) error {
var (
removedRolePmns []uint
userOtherRoles []uint
otherRolePmns []uint
toDeletePmns []uint
)
if err := tx.Model(&models.MgtLinkRolePmn{}).
Select("pmn_id").
Where("role_id in ?", request.RoleId).
Find(&removedRolePmns).Error; err != nil {
printer.Error("获取被移除角色权限异常: %v", err)
return err
}
if len(removedRolePmns) == 0 {
if err := tx.Where("user_id = ? and role_id in ?", request.UserId, request.RoleId).
Delete(&models.MgtLinkUserRole{}).Error; err != nil {
printer.Error("删除用户角色关联异常: %v", err)
return err
}
return nil
}
if err := tx.Model(&models.MgtLinkUserRole{}).
Select("role_id").
Where("user_id = ? and role_id not in ?", request.UserId, request.RoleId).
Find(&userOtherRoles).Error; err != nil {
printer.Error("获取用户其他角色异常: %v", err)
return err
}
if len(userOtherRoles) > 0 {
if err := tx.Model(&models.MgtLinkRolePmn{}).
Select("pmn_id").
Where("role_id in ?", userOtherRoles).
Find(&otherRolePmns).Error; err != nil {
printer.Error("获取用户其他角色权限异常: %v", err)
return err
}
}
otherPmnSet := make(map[uint]struct{}, len(otherRolePmns))
for _, pmnId := range otherRolePmns {
otherPmnSet[pmnId] = struct{}{}
}
toDeletePmns = make([]uint, 0, len(removedRolePmns))
for _, pmnId := range removedRolePmns {
if _, ok := otherPmnSet[pmnId]; !ok {
toDeletePmns = append(toDeletePmns, pmnId)
}
}
if err := tx.Where("user_id = ? and role_id in ?", request.UserId, request.RoleId).
Delete(&models.MgtLinkUserRole{}).Error; err != nil {
printer.Error("删除用户角色关联异常: %v", err)
return err
}
if len(toDeletePmns) > 0 {
if err := tx.Where("user_id = ? and pmn_id in ?", request.UserId, toDeletePmns).
Delete(&models.MgtLinkUserPmn{}).Error; err != nil {
printer.Error("删除用户权限关联异常: %v", err)
return err
}
printer.Info("删除用户权限关联: userId=%d, pmnIds=%v", request.UserId, toDeletePmns)
} else {
printer.Info("用户权限通过其他角色或直接拥有,无需删除: userId=%d", request.UserId)
}
return nil
}); err != nil {
infra.Response.Error(c, errcode.ErrDB)
return
}
infra.Response.Success(c, "")
}